From bdd3a11cec65983249f1788d05e2104d191c62a5 Mon Sep 17 00:00:00 2001 From: Irene Sheen Date: Mon, 4 May 2026 18:25:48 +0200 Subject: [PATCH] Add core structure for DeltaChat port --- .python-version | 1 + pyproject.toml | 19 +++++ src/quadnite_bot/__init__.py | 3 + src/quadnite_bot/__main__.py | 11 +++ src/quadnite_bot/command.py | 51 ++++++++++++ src/quadnite_bot/command_registry.py | 6 ++ src/quadnite_bot/commands/__init__.py | 0 src/quadnite_bot/commands/echo.py | 8 ++ uv.lock | 112 ++++++++++++++++++++++++++ 9 files changed, 211 insertions(+) create mode 100644 .python-version create mode 100644 pyproject.toml create mode 100644 src/quadnite_bot/__init__.py create mode 100644 src/quadnite_bot/__main__.py create mode 100644 src/quadnite_bot/command.py create mode 100644 src/quadnite_bot/command_registry.py create mode 100644 src/quadnite_bot/commands/__init__.py create mode 100644 src/quadnite_bot/commands/echo.py create mode 100644 uv.lock diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..91e4f5b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "quadnite-bot" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [ + { name = "Irene Sheen", email = "ceda_ei@webionite.com" } +] +requires-python = ">=3.13" +dependencies = [ + "deltabot-cli>=8.1.0", +] + +[project.scripts] +quadnite-bot = "quadnite_bot:main" + +[build-system] +requires = ["uv_build>=0.11.7,<0.12.0"] +build-backend = "uv_build" diff --git a/src/quadnite_bot/__init__.py b/src/quadnite_bot/__init__.py new file mode 100644 index 0000000..d8bc1e3 --- /dev/null +++ b/src/quadnite_bot/__init__.py @@ -0,0 +1,3 @@ +from quadnite_bot.__main__ import main + +__all__ = ["main"] diff --git a/src/quadnite_bot/__main__.py b/src/quadnite_bot/__main__.py new file mode 100644 index 0000000..6502057 --- /dev/null +++ b/src/quadnite_bot/__main__.py @@ -0,0 +1,11 @@ +from deltabot_cli import BotCli +from deltachat2 import events + +from quadnite_bot.command_registry import dispatcher + +def main(): + cli = BotCli("quadnite-bot") + + cli.on(events.NewMessage)(dispatcher) + + cli.start() diff --git a/src/quadnite_bot/command.py b/src/quadnite_bot/command.py new file mode 100644 index 0000000..c153de5 --- /dev/null +++ b/src/quadnite_bot/command.py @@ -0,0 +1,51 @@ +from abc import ABC, abstractmethod +import re +from deltachat2 import Bot, MsgData, NewMsgEvent + +class Context: + def __init__(self, bot: Bot, accid: int, event: NewMsgEvent) -> None: + self.bot = bot + self.accid = accid + self.event = event + + def reply(self, text: str): + self.bot.rpc.send_msg( + self.accid, + self.event.msg.chat_id, + MsgData(text), + ) + + +class Command(ABC): + command: str|None = None + regex: re.Pattern|None = None + run_next: bool = False + + def handles_message(self, event: NewMsgEvent) -> bool: + if self.command: + return event.msg.text.lower().startswith(f"/{self.command.lower()} ") + if self.regex: + return self.regex.search(event.msg) is not None + raise NotImplementedError(f"The command has neither 'command' nor 'regex' set. Required one of the two.") + + @abstractmethod + def process_event(self, ctx: Context, event: NewMsgEvent) -> None: + pass + + def __call__(self, bot: Bot, accid: int, event: NewMsgEvent) -> None: + self.process_event(Context(bot, accid, event), event) + + +class CommandDispatcher: + def __init__(self): + self._commands: list[Command] = [] + + def add_command(self, command: Command): + self._commands.append(command) + + def __call__(self, bot: Bot, accid: int, event: NewMsgEvent) -> None: + for command in self._commands: + if command.handles_message(event): + command(bot, accid, event) + if not command.run_next: + break diff --git a/src/quadnite_bot/command_registry.py b/src/quadnite_bot/command_registry.py new file mode 100644 index 0000000..cf2abc5 --- /dev/null +++ b/src/quadnite_bot/command_registry.py @@ -0,0 +1,6 @@ +from quadnite_bot.command import CommandDispatcher +from quadnite_bot.commands.echo import EchoCommand + +dispatcher = CommandDispatcher() + +dispatcher.add_command(EchoCommand()) diff --git a/src/quadnite_bot/commands/__init__.py b/src/quadnite_bot/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/quadnite_bot/commands/echo.py b/src/quadnite_bot/commands/echo.py new file mode 100644 index 0000000..3f2e82d --- /dev/null +++ b/src/quadnite_bot/commands/echo.py @@ -0,0 +1,8 @@ +from deltachat2 import NewMsgEvent +from quadnite_bot.command import Command, Context + +class EchoCommand(Command): + command = "echo" + + def process_event(self, ctx: Context, event: NewMsgEvent) -> None: + ctx.reply(event.msg.text) diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..4fc2830 --- /dev/null +++ b/uv.lock @@ -0,0 +1,112 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, +] + +[[package]] +name = "deltabot-cli" +version = "8.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appdirs" }, + { name = "deltachat2", extra = ["full"] }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/3c/b380bc6c2bf3d76d7d39faec57e022d25447d0503c04109f5bc1ca0c779a/deltabot_cli-8.1.0.tar.gz", hash = "sha256:53bf983d9a76381deb23506d232de619993b82642d76ead201a1beef27ebe98e", size = 14886, upload-time = "2026-04-14T02:09:42.214Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/64/cfecb6303fd90be9897ba6bb3184955996368ceff02a49853a31fff54b92/deltabot_cli-8.1.0-py3-none-any.whl", hash = "sha256:691bf62266628f6ef25788cba2054ab6fb2774e1918c8a7fb66be6e3e135fa3a", size = 12694, upload-time = "2026-04-14T02:09:41.303Z" }, +] + +[[package]] +name = "deltachat-rpc-server" +version = "2.49.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/5c/5cc080e47555116f34c9c14c8c8bad14568499b148bcbb1c736447b9afa5/deltachat_rpc_server-2.49.0-py3-none-android_21_arm64_v8a.whl", hash = "sha256:a5a6aed57436766794764aa90897eddd700bffca979c924725a3dff3b40ccca3", size = 11455996, upload-time = "2026-04-13T09:11:05.039Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/1641b807f7a880a495c58f76f6f333e0db46a1a891cea155d1077e66a831/deltachat_rpc_server-2.49.0-py3-none-android_21_armeabi_v7a.whl", hash = "sha256:100379ca2c29aaa433dd536038ee5603f3ab36517777b0d5923a2d4bd1e4b701", size = 9712072, upload-time = "2026-04-13T09:11:07.88Z" }, + { url = "https://files.pythonhosted.org/packages/2a/1f/c5c61a1ffd3c2369848db9cd1e83989257b3b5836b07d61c0de9b51c515d/deltachat_rpc_server-2.49.0-py3-none-linux_armv6l.whl", hash = "sha256:4a0719575130ea8d96eeceb4b0c2eca4de276c9b0aa42d6a048438754107003f", size = 10558722, upload-time = "2026-04-13T09:11:10.355Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ab/65d7f305cca0762a17bcd0686650d33e5b8c2dd66e9a304633066677344e/deltachat_rpc_server-2.49.0-py3-none-linux_armv7l.manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:449c9b714a520b60f843735bdd3fb4be891dd08c15db3e6e1bebb643a7be9178", size = 10457845, upload-time = "2026-04-13T09:11:12.749Z" }, + { url = "https://files.pythonhosted.org/packages/6d/14/f47884b55bf267115e89826db02ff65a4b409658fc1190c9b793dcd65c7f/deltachat_rpc_server-2.49.0-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:7c6897c9beb0b75ecbbddd9417f05906a5646e61fe7f6f4bec0e1392055c6871", size = 9929478, upload-time = "2026-04-13T09:11:15.567Z" }, + { url = "https://files.pythonhosted.org/packages/a3/e1/8a0c2657d53b9c463d94b749481cd616a4ba9f8a9b67daa5b506019fd9b3/deltachat_rpc_server-2.49.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b62592f0217ff2de23fd363c0fb47b17a0de94fb4e7f21e2aa5a7196cd87d6eb", size = 9743447, upload-time = "2026-04-13T09:11:18.209Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f6/59f4e09dbd6f6f9ada62436ad0a7d16072412bce65f071fa20723c9b024b/deltachat_rpc_server-2.49.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:687315e0e9a753c42ae1ad241013b72e863fe267297e17d26f141b277733452a", size = 11172948, upload-time = "2026-04-13T09:11:21.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f1/507556562cf52606b726b2deafb052d90161a3ebf195f15448b807a9311f/deltachat_rpc_server-2.49.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:84e1b89cfcfd0be07c9d43fa830f9ebdd84eb6d5c71e558c19ee81d18d7c23c1", size = 11409855, upload-time = "2026-04-13T09:11:23.72Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e1/b6b9defefd8d5e5896993ec6be5dafeb3e5c44da6db1b60d671553d8a618/deltachat_rpc_server-2.49.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:2aa8f41aebe7a502bd30e72f8009a199f4f91948deffcfab90b71c5dbbee9691", size = 11745522, upload-time = "2026-04-13T09:11:26.426Z" }, + { url = "https://files.pythonhosted.org/packages/df/e9/ca3978f05a4edb450da387275fa0401bd26f80d22e339b0b7e0107ab76ea/deltachat_rpc_server-2.49.0-py3-none-win32.whl", hash = "sha256:a80cb2171685060d7c48f90013ecf5e0239fa5b1b6c3598c37d593903438a4d9", size = 10527871, upload-time = "2026-04-13T09:11:29.359Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d3/3eb5b1ad0dd193288ced6ac44b9d3741768b17ffdca8cc13bd53cc994802/deltachat_rpc_server-2.49.0-py3-none-win_amd64.whl", hash = "sha256:324e480f201a9477b045571aa9baf08ee07b5eb53ef7ae3b64efb689efa124fd", size = 10546684, upload-time = "2026-04-13T09:11:32.141Z" }, +] + +[[package]] +name = "deltachat2" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/c1/f025ab95da941fdaf49a47978974c3666d681625b5f584b2782c14ca1f31/deltachat2-0.10.0.tar.gz", hash = "sha256:b27f2f46cfb378b276a530f7f8eb475aa113339d2136825539332823cbb5a389", size = 19563, upload-time = "2026-04-13T23:44:31.639Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/ad/0eb9c72275c8178a2668da906f2635c90a97a7c5cecbfb2624cdc7cc1048/deltachat2-0.10.0-py3-none-any.whl", hash = "sha256:b20ff04b295ddebd981bb9be9185868ce5448c83b0ddfb064bef77706a3dbd45", size = 19062, upload-time = "2026-04-13T23:44:30.492Z" }, +] + +[package.optional-dependencies] +full = [ + { name = "deltachat-rpc-server" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "quadnite-bot" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "deltabot-cli" }, +] + +[package.metadata] +requires-dist = [{ name = "deltabot-cli", specifier = ">=8.1.0" }] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +]