Skip to content

Commit

Permalink
update to new deltabot-cli API
Browse files Browse the repository at this point in the history
  • Loading branch information
adbenitez committed Feb 24, 2024
1 parent fe6e831 commit c1391d6
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 98 deletions.
38 changes: 19 additions & 19 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ jobs:
matrix:
python-version: ['3.8', '3.11']
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install ".[dev]"
- name: Check code with black
run: |
black --check .
- name: Lint code
run: |
pylama
- name: Test with pytest
run: |
#pytest
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install '.[dev]'
- name: Check code with black
run: |
black --check .
- name: Lint code
run: |
pylama
- name: Test with pytest
run: |
#pytest
deploy:
needs: test
Expand All @@ -51,6 +51,6 @@ jobs:
uses: casperdcl/deploy-pypi@v2
with:
password: ${{ secrets.PYPI_TOKEN }}
pip: true
build: true
# only upload if a tag is pushed (otherwise just build & check)
upload: ${{ github.event_name == 'push' && steps.check-tag.outputs.match == 'true' }}
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@ Configure the bot's Delta Chat account:

```sh
matterdelta init bot@example.com PASSWORD
# optional:
```

You can run `matterdelta init` several times to add multiple different accounts to the bot
so it can be reached in more than one email address.

The bot's display name, avatar and status/signature can also be tweaked:

```
matterdelta config selfavatar "/path/to/avatar.png"
matterdelta config displayname "Bridge Bot"
matterdelta config selfstatus "Hi, I am a Delta Chat bot"
```

Running the bot:
To run the bot so it starts processing messages:

```sh
matterdelta serve
Expand Down Expand Up @@ -65,7 +72,7 @@ Add these to your existing Matterbridge config to set up an API instance that Ma
```
{
"gateways": [
{"gateway": "gateway1", "chatId": 1234}
{"gateway": "gateway1", "accountId": 1, "chatId": 1234}
],
"api": {
"url": "http://127.0.0.1:4242",
Expand All @@ -78,6 +85,6 @@ Add these to your existing Matterbridge config to set up an API instance that Ma
This file should be in Matterdelta's configuration directory, usually `~/.config/matterdelta/`
in Linux-based systems.

To get the `chatId` of the chat you want to bridge, run the bot and add its address to your Delta Chat group,
then send `/id`in the group, the bot will reply with the chat id, then edit the configuration file
and restart the bot.
To get the `accountId` and `chatId` of the chat you want to bridge,
run the bot and add its address to your Delta Chat group, then send `/id` in the group,
the bot will reply with the account and chat id, then edit the configuration file and restart the bot.
1 change: 1 addition & 0 deletions matterdelta/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Matterdelta bot."""

from .hooks import cli


Expand Down
56 changes: 28 additions & 28 deletions matterdelta/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Matterbridge API interaction"""

import base64
import json
import logging
Expand All @@ -8,11 +9,11 @@
from threading import Thread

import requests
from deltabot_cli import AttrDict, Bot, const
from deltabot_cli import AttrDict, Bot, ViewType

mb_config = {}
id2gateway = {}
gateway2id = {}
chat2gateway = {}
gateway2chat = {}


def init_api(bot: Bot, config_dir: str) -> None:
Expand All @@ -23,24 +24,27 @@ def init_api(bot: Bot, config_dir: str) -> None:
mb_config.update(json.load(config))

for gateway in mb_config.get("gateways") or []:
gateway2id[gateway["gateway"]] = gateway["chatId"]
id2gateway[gateway["chatId"]] = gateway["gateway"]
chat = (gateway["accountId"], gateway["chatId"])
gateway2chat[gateway["gateway"]] = chat
chat2gateway[chat] = gateway["gateway"]

if len(mb_config.get("gateways") or []):
Thread(target=listen_to_matterbridge, args=(bot,)).start()


def dc2mb(msg: AttrDict) -> None:
def dc2mb(bot: Bot, accid: int, msg: AttrDict) -> None:
"""Send a Delta Chat message to the matterbridge side."""
gateway = id2gateway.get(msg.chat_id)
gateway = chat2gateway.get((accid, msg.chat_id))
if gateway:
if not msg.text and not msg.file: # ignore buggy empty messages
return
api_url = mb_config["api"]["url"]
token = mb_config["api"].get("token", "")
headers = {"Authorization": f"Bearer {token}"} if token else None
sender = msg.sender.get_snapshot()
username = msg.override_sender_name or sender.display_name
if not msg.text and not msg.file:
return
username = (
msg.override_sender_name
or bot.rpc.get_contact(accid, msg.sender.id).display_name
)
text = msg.text
if text and text.split(maxsplit=1)[0] == "/me":
event = "user_action"
Expand All @@ -49,9 +53,7 @@ def dc2mb(msg: AttrDict) -> None:
event = ""
if msg.quote and mb_config.get("quoteFormat"):
quotenick = (
msg.quote.get("override_sender_name")
or msg.quote.get("author_display_name")
or ""
msg.quote.override_sender_name or msg.quote.author_display_name or ""
)
text = mb_config["quoteFormat"].format(
MESSAGE=text,
Expand All @@ -73,32 +75,30 @@ def mb2dc(bot: Bot, msg: dict) -> None:
"""Send a message from matterbridge to the bridged Delta Chat group"""
if msg["event"] not in ("", "user_action"):
return
chat_id = gateway2id.get(msg["gateway"])
if not chat_id:
accid, chat_id = gateway2chat.get(msg["gateway"]) or (0, 0)
if not accid or not chat_id:
return
chat = bot.account.get_chat_by_id(chat_id)
text = msg.get("text") or ""
if msg["event"] == "user_action":
text = "/me " + text
reply = {
"text": text,
"overrideSenderName": msg["username"],
}
file = ((msg.get("Extra") or {}).get("file") or [{}])[0]
if file:
if text == file["Name"]:
text = ""
with tempfile.TemporaryDirectory() as tmp_dir:
filename = os.path.join(tmp_dir, file["Name"])
reply["file"] = os.path.join(tmp_dir, file["Name"])
data = base64.decodebytes(file["Data"].encode())
with open(filename, mode="wb") as attachment:
with open(reply["file"], mode="wb") as attachment:
attachment.write(data)
is_sticker = file["Name"].endswith((".tgs", ".webp"))
viewtype = const.ViewType.STICKER if is_sticker else None
chat.send_message(
text=text,
file=filename,
viewtype=viewtype,
override_sender_name=msg["username"],
)
if file["Name"].endswith((".tgs", ".webp")):
reply["viewtype"] = ViewType.STICKER
bot.rpc.send_msg(accid, chat_id, reply)
elif text:
chat.send_message(text=text, override_sender_name=msg["username"])
bot.rpc.send_msg(accid, chat_id, reply)


def listen_to_matterbridge(bot: Bot) -> None:
Expand Down
92 changes: 53 additions & 39 deletions matterdelta/hooks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
"""Event Hooks"""
import logging

from argparse import Namespace

from deltabot_cli import AttrDict, Bot, BotCli, EventType, const, events
from deltabot_cli import (
AttrDict,
Bot,
BotCli,
ChatType,
EventType,
events,
is_not_known_command,
)

from .api import dc2mb, init_api
from .util import get_log_level
Expand All @@ -12,55 +20,61 @@

@cli.on_init
def _on_init(bot: Bot, _args: Namespace) -> None:
if not bot.account.get_config("displayname"):
bot.account.set_config("displayname", "Matterbridge Bot")
status = "I am a Delta Chat bot, send me /help for more info"
bot.account.set_config("selfstatus", status)
for accid in bot.rpc.get_all_account_ids():
if not bot.rpc.get_config(accid, "displayname"):
bot.rpc.set_config(accid, "displayname", "Matterbridge Bot")
status = "I am a Delta Chat bot, send me /help for more info"
bot.rpc.set_config(accid, "selfstatus", status)
bot.rpc.set_config(accid, "delete_server_after", "1")


@cli.on_start
def _on_start(bot: Bot, args: Namespace) -> None:
system_info = bot.account.manager.get_system_info()
addr = bot.account.get_config("addr")
logging.info(
"Delta Chat %s listening at: %s", system_info.deltachat_core_version, addr
)

init_api(bot, args.config_dir)


@cli.on(events.RawEvent)
def _log_event(event: AttrDict) -> None:
def _log_event(bot: Bot, accid: int, event: AttrDict) -> None:
if event.kind == EventType.INFO:
logging.info(event.msg)
bot.logger.info(event.msg)
elif event.kind == EventType.WARNING:
logging.warning(event.msg)
bot.logger.warning(event.msg)
elif event.kind == EventType.ERROR:
logging.error(event.msg)
bot.logger.error(event.msg)
elif event.kind == EventType.SECUREJOIN_INVITER_PROGRESS:
if event.progress == 1000:
bot.logger.debug("QR scanned by contact id=%s", event.contact_id)
chatid = bot.rpc.create_chat_by_contact_id(accid, event.contact_id)
send_help(bot, accid, chatid)


@cli.on(events.NewMessage(command="/id"))
def _id(event: AttrDict) -> None:
msg = event.message_snapshot
chat = msg.chat.get_basic_snapshot()
if chat.chat_type == const.ChatType.SINGLE:
msg.chat.send_message(
text="Can't use /id command here, add me to a group and use the command there",
quoted_msg=msg.id,
)
@cli.on(events.NewMessage(is_info=False, is_bot=None, func=is_not_known_command))
def _bridge(bot: Bot, accid: int, event: AttrDict) -> None:
msg = event.msg
chat = bot.rpc.get_basic_chat_info(accid, msg.chat_id)
if chat.chat_type == ChatType.SINGLE and not msg.is_bot:
bot.rpc.markseen_msgs(accid, [msg.id])
send_help(bot, accid, msg.chat_id)
else:
msg.chat.send_text(str(msg.chat_id))


@cli.on(events.NewMessage(is_info=False, is_bot=None, func=cli.is_not_known_command))
def _bridge(event: AttrDict) -> None:
msg = event.message_snapshot
chat = msg.chat.get_basic_snapshot()
if chat.chat_type == const.ChatType.SINGLE and not msg.is_bot:
text = (
"**Available commands**\n\n"
"/id - send me this command in a group to get its chatId."
)
msg.chat.send_text(text)
dc2mb(bot, accid, msg)


@cli.on(events.NewMessage(command="/id"))
def _id(bot: Bot, accid: int, event: AttrDict) -> None:
msg = event.msg
bot.rpc.markseen_msgs(accid, [msg.id])
chat = bot.rpc.get_basic_chat_info(accid, msg.chat_id)
if chat.chat_type == ChatType.SINGLE:
text = "You can't use /id command here, add me to a group and use the command there"
bot.rpc.send_msg(accid, msg.chat_id, {"text": text, "quotedMessageId": msg.id})
else:
dc2mb(msg)
reply = {"text": f"accountId: {accid}\nchatId: {msg.chat_id}"}
bot.rpc.send_msg(accid, msg.chat_id, reply)


def send_help(bot: Bot, accid: int, chatid: int) -> None:
text = (
"**Available commands**\n\n"
"/id - send me this command in a group to get its ID."
)
bot.rpc.send_msg(accid, chatid, {"text": text})
14 changes: 8 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
[build-system]
requires = ["setuptools"]
requires = ["setuptools>=64", "setuptools_scm>=8"]
build-backend = "setuptools.build_meta"

[project]
version = "0.2.2"
name = "matterdelta"
description = "Matterbridge API plugin for Delta Chat"
dynamic = ["version"]
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = {file = "LICENSE.txt"}
keywords = ["deltachat", "bot", "matterbridge", "bridge"]
authors = [
{name = "adbenitez", email = "adbenitez@hispanilandia.net"},
{name = "adbenitez", email = "adb@merlinux.eu"},
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python"
]
dependencies = [
"deltabot-cli>=0.1.0",
"deltachat-rpc-server>=1.127.0",
"deltabot-cli>=3.0.0,<4.0",
"requests",
]

Expand All @@ -40,6 +39,9 @@ dev = [
[project.scripts]
matterdelta = "matterdelta:main"

[tool.setuptools_scm]
# can be empty if no extra settings are needed, presence enables setuptools_scm

[tool.isort]
profile = "black"

Expand Down

0 comments on commit c1391d6

Please sign in to comment.