Skip to content

Module examples

Every example is loaded in tests (tests/test_examples.py), so the code works with the current version. Install one by replying to the file with .lm or with .dlm asykixd/uroboros/examples/<name>.

counter

# meta developer: @uroboros
# requires_uroboros: 0.3
"""Inline form: buttons, text input, confirmation and an inline command."""

from uroboros import Module, command, inline_handler


class Counter(Module):
    """Counter with buttons"""

    @command("counter")
    async def counter(self, message):
        """— counter with buttons"""
        await self.inline.form(message, self._text(0), self._buttons(0))

    @staticmethod
    def _text(value):
        return f"🔢 <b>Count:</b> <code>{value}</code>"

    def _buttons(self, value):
        return [
            [
                {"text": "−", "callback": self._add, "args": (value, -1)},
                {"text": "+", "callback": self._add, "args": (value, 1)},
            ],
            [{"text": "✍️ Set", "input": "New value", "handler": self._typed}],
            [
                {"text": "Reset", "callback": self._add, "args": (0, 0), "confirm": "Reset the counter?"},
                {"text": "✖ Close", "action": "close"},
            ],
        ]

    async def _add(self, call, value, delta):
        value += delta
        await call.edit(self._text(value), self._buttons(value))

    async def _typed(self, call, text):
        if not text.lstrip("-").isdigit():
            await call.edit("❌ Integer required", self._buttons(0))
            return
        await call.edit(self._text(int(text)), self._buttons(int(text)))

    @inline_handler()
    async def count_inline_handler(self, query):
        """<number> — send a counter to any chat"""
        value = int(query.args) if query.args.lstrip("-").isdigit() else 0
        return {
            "title": f"Counter from {value}",
            "message": self._text(value),
            "buttons": self._buttons(value),
        }

hello

# meta developer: @uroboros
# meta version: 1.0
# meta permissions: none
# requires_uroboros: 1.0
"""Minimal module: a command, a setting, strings and a card reply."""

from uroboros import ConfigValue, Module, ModuleConfig, command, utils, validators


class Hello(Module):
    """Says hello"""

    strings = {
        "hello": "👋 <b>Hello, {name}!</b>",
        "count": "🔁 Greeted <code>{n}</code> times",
    }

    def __init__(self):
        self.config = ModuleConfig(
            ConfigValue("name", "world", "Default name to greet", validators.String(max_len=64)),
        )

    @command("hello", aliases=["hi"], emoji="👋")
    async def hello(self, message):
        """[name] — say hello"""
        name = utils.get_args_raw(message) or self.config["name"]
        self.set("count", self.get("count", 0) + 1)
        await utils.answer(
            message,
            utils.card(
                self.strings("hello", name=name),
                self.strings("count", n=self.get("count")),
                hint="change the default name: <code>.cfg hello name</code>",
            ),
        )

reminder

# meta developer: @uroboros
# requires_uroboros: 0.2
"""Background task: checks reminders every minute and sends them to Saved Messages."""

import time

from uroboros import Module, command, loop, utils


class Reminder(Module):
    """Reminders in Saved Messages"""

    @command("remind", no_reply=True)
    async def remind(self, message):
        """<minutes> <text> — remind in N minutes"""
        args = utils.get_args_raw(message).split(maxsplit=1)
        if len(args) != 2 or not args[0].isdigit():
            await utils.answer(message, "❌ Usage: <code>remind 10 text</code>")
            return
        reminders = self.get("reminders", [])
        reminders.append([time.time() + int(args[0]) * 60, args[1]])
        self.set("reminders", reminders)
        await utils.answer(message, f"✅ Reminder in {int(args[0])} min")

    @loop(interval=60)
    async def check(self):
        now = time.time()
        reminders = self.get("reminders", [])
        due = [text for at, text in reminders if at <= now]
        if not due:
            return
        self.set("reminders", [[at, text] for at, text in reminders if at > now])
        for text in due:
            await self.client.send_message("me", f"⏰ {utils.escape_html(text)}", parse_mode="html")

shout

# requires_uroboros: 0.2
"""A module that uses the textlib.py library."""

from uroboros import Module, command, utils

TEXTLIB = "https://raw.githubusercontent.com/asykixd/uroboros/HEAD/examples/textlib.py"


class Shout(Module):
    """Shouts text"""

    async def on_load(self):
        self.textlib = await self.import_lib(TEXTLIB)

    @command("shout")
    async def shout(self, message):
        """<text> — shout"""
        await utils.answer(message, utils.escape_html(self.textlib.shout(utils.get_args_raw(message))))

textlib

# requires_uroboros: 0.2
"""Library: code shared by several modules. Imported with self.import_lib(url)."""

from uroboros import Library


class TextLib(Library):
    async def on_load(self):
        self.calls = 0

    def shout(self, text: str) -> str:
        self.calls += 1
        return text.upper() + "!"

who

# meta developer: @uroboros
# requires_uroboros: 0.2
"""Command filters and user lookup: by reply, @username or private chat."""

from uroboros import Module, command, utils


class Who(Module):
    """User info"""

    @command("who")
    async def who(self, message):
        """[@username | id] — who is this (or reply to a message)"""
        user = await utils.get_target(message)
        if user is None:
            await utils.answer(message, "❌ Reply to a message or pass @username")
            return
        name = getattr(user, "first_name", None) or getattr(user, "title", "")
        await utils.answer(
            message,
            f"👤 <b>{utils.escape_html(name)}</b>\n" + utils.quote(f"id: <code>{user.id}</code>"),
        )

    @command("pmonly", only_pm=True)
    async def pmonly(self, message):
        """— works in private chats only"""
        await utils.answer(message, "✅ This is a private chat")