Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

15 changed files with 151 additions and 296 deletions

View file

@ -1,23 +0,0 @@
name: CI
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.11']
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install '.[dev]'
- run: tox

2
.gitignore vendored
View file

@ -3,8 +3,6 @@
__pycache__/
/.tox/
/build/
/dist/
/venv/
bot.db/
teams_bot_data/
team_bot_data/

View file

@ -1,4 +1,4 @@
# Team Bot
# Teams Bot
This bot connects your team to the outside
and makes it addressable.
@ -22,8 +22,8 @@ To install this bot,
run:
```
git clone https://github.com/deltachat-bot/team-bot
cd team-bot
git clone https://git.0x90.space/missytake/teams-bot
cd teams-bot
pip install .
```
@ -32,19 +32,19 @@ with an email address
you want to use as a team:
```
team-bot init --email helpdesk@example.org --password p455w0rD
teams-bot init --email helpdesk@example.org --password p455w0rD
```
This command will show a QR code;
scan it with Delta Chat
to become part of the "team",
the verified group which manages the Team Bot.
the verified group which manages the Teams Bot.
Now to run it,
simply execute:
```
team-bot run -v
teams-bot run -v
```
The bot only works as long as this command is running.
@ -59,9 +59,9 @@ you can deploy this bot with it.
Just import it into your [deploy.py file](https://docs.pyinfra.com/en/2.x/getting-started.html#create-a-deploy) like this:
```
from team_bot.pyinfra import deploy_team_bot
from teams_bot.pyinfra import deploy_teams_bot
deploy_team_bot(
deploy_teams_bot(
unix_user='root', # an existing UNIX user (doesn't need root or sudo privileges)
bot_email='helpdesk@example.org', # the email address your team wants to use
bot_passwd='p4ssw0rd', # the password to the email account
@ -79,7 +79,7 @@ login to the user with ssh
and run:
```
export $(cat ~/.env | xargs) && ~/.local/lib/team-bot.venv/bin/team-bot init
export $(cat ~/.env | xargs) && ~/.local/lib/teams-bot.venv/bin/teams-bot init
```
Then,
@ -88,11 +88,11 @@ and keep it running in the background,
run:
```
systemctl --user enable --now team-bot
systemctl --user enable --now teams-bot
```
You can view the log output
with `journalctl --user -fu team-bot`
with `journalctl --user -fu teams-bot`
to confirm that it works.
## Development Environment
@ -103,6 +103,7 @@ run:
```
python3 -m venv venv
. venv/bin/activate
pip install -e .[dev]
pip install pytest tox black pytest-xdist pytest-timeout
pip install -e .
tox
```

View file

@ -1,17 +1,17 @@
[metadata]
name = team_bot
version = 1.1.0
name = teams-bot
version = 0.0.1
author = missytake
author_email = missytake@systemli.org
description = This bot connects your team to the outside and makes it addressable.
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/deltachat-bot/team-bot
url = https://git.0x90.space/missytake/teams-bot
project_urls =
Bug Tracker = https://github.com/deltachat-bot/team-bot/issues
Bug Tracker = https://git.0x90.space/missytake/teams-bot/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
License :: OSI Approved :: ISC License (ISCL)
Operating System :: OS Independent
[options]
@ -22,24 +22,16 @@ python_requires = >=3.8
install_requires =
click
pyinfra
pickleDB==0.9
pickleDB
qrcode
deltachat>=1.142.7
[options.extras_require]
dev =
pytest
tox
black
pytest-xdist
pytest-timeout
deltachat
[options.packages.find]
where = src
[options.entry_points]
console_scripts =
team-bot = team_bot.cli:main
teams-bot = teams_bot.cli:main
[tox:tox]
envlist = lint, py310

View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
python3 -m venv ~/.local/lib/team-bot.venv
source ~/.local/lib/team-bot.venv/bin/activate
pip install -U pip wheel

View file

@ -11,7 +11,6 @@ from .commands import (
crew_help,
set_display_name,
set_avatar,
generate_invite,
start_chat,
outside_help,
set_outside_help,
@ -41,7 +40,6 @@ class SetupPlugin:
class RelayPlugin:
def __init__(self, account: deltachat.Account, kvstore: pickledb.PickleDB):
self.account = account
self.account.set_config("bcc_self", "1")
self.kvstore = kvstore
self.crew = account.get_chat_by_id(kvstore.get("crew_id"))
if not kvstore.get("relays"):
@ -66,14 +64,15 @@ class RelayPlugin:
relay_group = self.get_relay_group(message.chat.id)
relay_group.send_text(f"Sending Message failed:\n\n{error}")
@account_hookimpl
def ac_member_removed(self, chat, contact, actor, message):
if chat == self.crew:
self.offboard(contact)
@account_hookimpl
def ac_incoming_message(self, message: deltachat.Message):
"""This method is called on every incoming message and decides what to do with it."""
logging.info(
"New message from %s in chat %s: %s",
message.get_sender_contact().addr,
message.chat.get_name(),
message.text,
)
if message.is_system_message():
if message.chat.id == self.crew.id:
@ -114,9 +113,6 @@ class RelayPlugin:
if arguments[0] == "/set_avatar":
result = set_avatar(self.account, message, self.crew)
self.reply(message.chat, result, quote=message)
if arguments[0] == "/generate-invite":
text = generate_invite(self.account)
self.reply(message.chat, text, quote=message)
if arguments[0] == "/start_chat":
outside_chat, result = start_chat(
self.account,
@ -156,7 +152,6 @@ class RelayPlugin:
else:
logging.debug("Ignoring message, just the crew chatting")
else:
self.mark_last_messages_read(message.chat)
logging.debug("Ignoring message, just the crew chatting")
else:
@ -199,11 +194,7 @@ class RelayPlugin:
)
return
""":TODO don't forward if message is the explanation message"""
try:
outside_chat.send_msg(message)
except Exception as e:
self.reply(message.chat, "Sending message failed.", quote=message)
raise e
def forward_to_relay_group(self, message: deltachat.Message, started_by_crew=False):
"""forward a request to a relay group; create one if it doesn't exist yet."""
@ -240,10 +231,21 @@ class RelayPlugin:
def is_relay_group(self, chat: deltachat.Chat) -> bool:
"""Check whether a chat is a relay group."""
for mapping in self.kvstore.get("relays"):
if mapping[1] == chat.id:
if not chat.get_name().startswith(
"[%s] " % (self.account.get_config("addr").split("@")[0],)
):
return False # all relay groups' names begin with a [tag] with the localpart of the teamsbot's address
if (
chat.get_messages()[0].get_sender_contact()
!= self.account.get_self_contact()
):
return False # all relay groups were started by the teamsbot
if chat.is_protected():
return False # relay groups don't need to be protected, so they are not
for crew_member in self.crew.get_contacts():
if crew_member not in chat.get_contacts():
return False # all crew members have to be in any relay group
return True
return False
def get_outside_chat(self, relay_group_id: int) -> deltachat.Chat:
"""Get the corresponding outside chat for the ID of a relay group.
@ -268,22 +270,3 @@ class RelayPlugin:
if mapping[0] == outside_id:
return self.account.get_chat_by_id(mapping[1])
return None
def offboard(self, ex_admin: deltachat.Contact) -> None:
"""Remove a former crew member from all relay groups they are part of.
:param ex_admin: a contact which just got removed from the crew.
"""
for mapping in self.kvstore.get("relays"):
relay_group = self.account.get_chat_by_id(mapping[1])
if ex_admin in relay_group.get_contacts():
relay_group.remove_contact(ex_admin)
def mark_last_messages_read(self, relay_group: deltachat.Chat) -> None:
"""Mark the last incoming messages as read for a corresponding relay group.
:param relay_group: the relay group in which the messages which should marked read were forwarded.
"""
outside_chat = self.get_outside_chat(relay_group.id)
for msg in outside_chat.get_messages():
msg.mark_seen()

View file

@ -29,11 +29,11 @@ def set_log_level(verbose: int, db: str):
cls=click.Group, context_settings={"help_option_names": ["-h", "--help"]}
)
@click.pass_context
def team_bot(ctx):
def teams_bot(ctx):
"""This bot connects your team to the outside and makes it addressable."""
@team_bot.command()
@teams_bot.command()
@click.option("--email", type=str, default=None, help="the email account for the bot")
@click.option(
"--password", type=str, default=None, help="the password of the email account"
@ -41,7 +41,7 @@ def team_bot(ctx):
@click.option(
"--dbdir",
type=str,
default="team_bot_data",
default="teams_bot_data",
help="path to the bot's database",
envvar="TEAMS_DBDIR",
)
@ -59,9 +59,9 @@ def init(ctx, email: str, password: str, dbdir: str, verbose: int):
set_log_level(verbose, delta_db)
ac = deltachat.Account(delta_db)
ac.run_account(addr=email, password=password, show_ffi=verbose)
ac.set_config("mvbox_move", "1")
ac.set_config("sentbox_watch", "0")
ac.run_account(addr=email, password=password, show_ffi=verbose)
crew_id_old = kvstore.get("crew_id")
@ -123,11 +123,11 @@ def init(ctx, email: str, password: str, dbdir: str, verbose: int):
logging.info("Successfully changed crew ID to the new group.")
@team_bot.command()
@teams_bot.command()
@click.option(
"--dbdir",
type=str,
default="team_bot_data",
default="teams_bot_data",
help="path to the bot's database",
envvar="TEAMS_DBDIR",
)
@ -157,11 +157,11 @@ def run(ctx, dbdir: str, verbose: int):
ac.wait_shutdown()
@team_bot.command()
@teams_bot.command()
@click.option(
"--dbdir",
type=str,
default="team_bot_data",
default="teams_bot_data",
help="path to the bot's database",
envvar="TEAMS_DBDIR",
)
@ -191,7 +191,7 @@ def verify_crypto(ctx, dbdir: str, verbose: int):
def main():
team_bot(auto_envvar_prefix="TEAMS")
teams_bot(auto_envvar_prefix="TEAMS")
if __name__ == "__main__":

View file

@ -15,7 +15,6 @@ def crew_help() -> str:
Start a chat:\t/start_chat alice@example.org,bob@example.org Chat_Title Hello friends!
Change the bot's name:\t/set_name Name
Change the bot's avatar:\t/set_avatar <attach image>
Generate invite link:\t\t/generate-invite
Show this help text:\t\t/help
Change the help message for outsiders:\t/set_outside_help Hello outsider
"""
@ -64,14 +63,6 @@ def set_avatar(
return "Avatar changed to this image."
def generate_invite(account: deltachat.Account) -> str:
"""Return a https://i.delta.chat invite link for chatting with the bot.
:return: the invite link, e.g.: https://i.delta.chat
"""
return account.get_setup_contact_qr()
def start_chat(
ac: deltachat.Account,
command: deltachat.Message,

View file

@ -6,38 +6,47 @@ from pyinfra import host
from pyinfra.facts.systemd import SystemdStatus
def deploy_team_bot(unix_user: str, bot_email: str, bot_passwd: str, dbdir: str = None):
def deploy_teams_bot(
unix_user: str, bot_email: str, bot_passwd: str, dbdir: str = None
):
"""Deploy TeamsBot to a UNIX user, with specified credentials
:param unix_user: the existing UNIX user of the bot
:param bot_email: the email address for the bot account
:param bot_passwd: the password for the bot's email account
:param dbdir: the directory where the bot's data will be stored. default: ~/.config/team-bot/email@example.org
:param dbdir: the directory where the bot's data will be stored. default: ~/.config/teams-bot/email@example.org
"""
clone_repo = git.repo(
name="Pull the team-bot repository",
src="https://github.com/deltachat-bot/team-bot",
dest=f"/home/{unix_user}/team-bot",
name="Pull the teams-bot repository",
src="https://git.0x90.space/missytake/teams-bot",
dest=f"/home/{unix_user}/teams-bot",
rebase=True,
_su_user=unix_user,
_use_su_login=True,
)
if clone_repo.changed:
server.script(
name="Setup virtual environment for teams-bot",
src=importlib.resources.files(__package__)
/ "pyinfra_assets"
/ "setup-venv.sh",
_su_user=unix_user,
_use_su_login=True,
)
server.shell(
name="Compile team-bot",
name="Compile teams-bot",
commands=[
"python3 -m venv ~/.local/lib/team-bot.venv",
". ~/.local/lib/team-bot.venv/bin/activate && pip install -U pip wheel",
f". .local/lib/team-bot.venv/bin/activate && cd /home/{unix_user}/team-bot && pip install ."
f". .local/lib/teams-bot.venv/bin/activate && cd /home/{unix_user}/teams-bot && pip install ."
],
_su_user=unix_user,
_use_su_login=True,
)
if not dbdir:
dbdir = f"/home/{unix_user}/.config/team_bot/{bot_email}/"
dbdir = f"/home/{unix_user}/.config/teams_bot/{bot_email}/"
secrets = [
f"TEAMS_DBDIR={dbdir}",
f"TEAMS_INIT_EMAIL={bot_email}",
@ -61,18 +70,23 @@ def deploy_team_bot(unix_user: str, bot_email: str, bot_passwd: str, dbdir: str
)
files.template(
name="upload team-bot systemd unit",
name="upload teams-bot systemd unit",
src=importlib.resources.files(__package__)
/ "pyinfra_assets"
/ "team-bot.service.j2",
dest=f"/home/{unix_user}/.config/systemd/user/team-bot.service",
/ "teams-bot.service.j2",
dest=f"/home/{unix_user}/.config/systemd/user/teams-bot.service",
user=unix_user,
unix_user=unix_user,
bot_email=bot_email,
)
server.shell(
name=f"enable {unix_user}'s systemd units to auto-start at boot",
commands=[f"loginctl enable-linger {unix_user}"],
)
systemd.daemon_reload(
name=f"{unix_user}: load team-bot systemd service",
name=f"{unix_user}: load teams-bot systemd service",
user_name=unix_user,
user_mode=True,
_su_user=unix_user,
@ -92,10 +106,10 @@ def deploy_team_bot(unix_user: str, bot_email: str, bot_passwd: str, dbdir: str
_use_su_login=True,
)
try:
if services["team-bot.service"]:
if services["teams-bot.service"]:
systemd.service(
name=f"{unix_user}: restart team-bot systemd service",
service="team-bot.service",
name=f"{unix_user}: restart teams-bot systemd service",
service="teams-bot.service",
running=True,
restarted=True,
user_mode=True,

View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
python3 -m venv ~/.local/lib/teams-bot.venv
source ~/.local/lib/teams-bot.venv/bin/activate
pip install -U pip wheel

View file

@ -1,8 +1,8 @@
[Unit]
Description=run deltachat team-bot: {{ bot_email }}
Description=run deltachat teams-bot: {{ bot_email }}
[Service]
ExecStart=/home/{{ unix_user }}/.local/lib/team-bot.venv/bin/team-bot run -v
ExecStart=/home/{{ unix_user }}/.local/lib/teams-bot.venv/bin/teams-bot run -v
EnvironmentFile=/home/{{ unix_user }}/.env
Restart=on-failure
RestartSec=5s

View file

@ -7,7 +7,7 @@ import deltachat
import pytest
from _pytest.pytester import LineMatcher
from team_bot.bot import RelayPlugin
from teams_bot.bot import RelayPlugin
class ClickRunner:
@ -65,9 +65,9 @@ def _perform_match(output, fnl):
@pytest.fixture
def cmd():
"""invoke a command line subcommand."""
from team_bot.cli import team_bot
from teams_bot.cli import teams_bot
return ClickRunner(team_bot)
return ClickRunner(teams_bot)
@pytest.fixture
@ -80,7 +80,7 @@ def tmp_file_path(request, tmpdir):
@pytest.fixture
def relaycrew(crew) -> deltachat.Chat:
def relaycrew(crew):
crew.bot.relayplugin = RelayPlugin(crew.bot, crew.kvstore)
crew.bot.add_account_plugin(crew.bot.relayplugin)
assert not crew.bot.relayplugin.is_relay_group(crew)
@ -88,24 +88,28 @@ def relaycrew(crew) -> deltachat.Chat:
@pytest.fixture
def crew(team_bot, team_user, tmpdir) -> deltachat.Chat:
from team_bot.bot import SetupPlugin
def crew(teams_bot, teams_user, tmpdir):
from teams_bot.bot import SetupPlugin
crew = team_bot.create_group_chat(
f"Team: {team_bot.get_config('addr')}", verified=True
crew = teams_bot.create_group_chat(
f"Team: {teams_bot.get_config('addr')}", verified=True
)
setupplugin = SetupPlugin(crew.id)
team_bot.add_account_plugin(setupplugin)
teams_bot.add_account_plugin(setupplugin)
qr = crew.get_join_qr()
team_user.qr_join_chat(qr)
teams_user.qr_join_chat(qr)
setupplugin.member_added.wait(timeout=30)
crew.user = team_user
crew.bot = team_bot
crew.user = teams_user
crew.bot = teams_bot
crew.bot.setupplugin = setupplugin
# wait until old user is properly added to crew
team_user._evtracker.wait_securejoin_joiner_progress(1000)
team_user._evtracker.wait_next_incoming_message() # member added message
last_message = teams_user.wait_next_incoming_message().text
while (
f"Member Me ({teams_user.get_config('addr')}) added by bot" not in last_message
):
print("User received message:", last_message)
last_message = teams_user.wait_next_incoming_message().text
crew.kvstore = pickledb.load(tmpdir + "pickle.db", True)
crew.kvstore.set("crew_id", crew.id)
@ -113,7 +117,7 @@ def crew(team_bot, team_user, tmpdir) -> deltachat.Chat:
@pytest.fixture
def team_bot(tmpdir) -> deltachat.Account:
def teams_bot(tmpdir):
ac = account(tmpdir + "/bot.sqlite", show_ffi=True)
yield ac
ac.shutdown()
@ -121,7 +125,7 @@ def team_bot(tmpdir) -> deltachat.Account:
@pytest.fixture
def team_user(tmpdir) -> deltachat.Account:
def teams_user(tmpdir):
ac = account(tmpdir + "/user.sqlite")
yield ac
ac.shutdown()
@ -129,20 +133,19 @@ def team_user(tmpdir) -> deltachat.Account:
@pytest.fixture
def outsider(tmpdir) -> deltachat.Account:
def outsider(tmpdir):
ac = account(tmpdir + "/outsider.sqlite")
yield ac
ac.shutdown()
ac.wait_shutdown()
def account(db_path, show_ffi=False) -> deltachat.Account:
def account(db_path, show_ffi=False):
token = os.environ.get(
"DCC_NEW_TMP_EMAIL", "https://nine.testrun.org/cgi-bin/newemail.py"
)
print(token)
ac = deltachat.Account(str(db_path))
ac._evtracker = ac.add_account_plugin(deltachat.events.FFIEventTracker(ac))
credentials = requests.post(token).json()
email = credentials["email"]
password = credentials["password"]

View file

@ -6,90 +6,46 @@ import pytest
from deltachat.capi import lib as dclib
TIMEOUT = 40
TIMEOUT = 20
def get_user_crew(crewuser: deltachat.Account, id=11) -> deltachat.Chat:
"""Get the Team chat from the team member's point of view.
:param crewuser: the account object of the team member
:return: the chat object of the team chat
"""
for chat in crewuser.get_chats():
print(chat.id, chat.get_name())
user_crew = crewuser.get_chat_by_id(id)
assert user_crew.get_name().startswith("Team")
return user_crew
@pytest.mark.timeout(TIMEOUT)
def test_not_relay_groups(relaycrew, outsider, lp):
@pytest.mark.timeout(60)
def test_not_relay_groups(relaycrew, outsider):
bot = relaycrew.bot
user = relaycrew.user
def find_msg(ac, text):
for chat in ac.get_chats():
for msg in chat.get_messages():
if msg.text == text:
return msg
text = "outsider -> bot 1:1 chat"
lp.sec(text)
# bot <-> outsider 1:1 chat
outsider_botcontact = outsider.create_contact(bot.get_config("addr"))
outsider_outside_chat = outsider.create_chat(outsider_botcontact)
outsider_outside_chat.send_text(text)
lp.sec("receiving message from outsider in 1:1 chat")
bot_message_from_outsider = bot._evtracker.wait_next_incoming_message()
outsider_outside_chat.send_text("test 1:1 message to bot")
bot_message_from_outsider = bot.wait_next_incoming_message()
bot_outside_chat = bot_message_from_outsider.chat
assert bot_message_from_outsider.text == text
assert not bot.relayplugin.is_relay_group(bot_outside_chat)
lp.sec("leave relay group with user")
relayed_msg = find_msg(user, text)
if not relayed_msg:
relayed_msg = user._evtracker.wait_next_incoming_message()
relayed_msg.chat.remove_contact(user.get_config("addr"))
leave_msg = bot._evtracker.wait_next_incoming_message()
assert bot.relayplugin.is_relay_group(leave_msg.chat)
text = "outsider -> bot group chat"
lp.sec(text)
# bot <-> outsider group chat
outsider_bot_group = outsider.create_group_chat(
"test with outsider", contacts=[outsider_botcontact]
)
outsider_bot_group.send_text(text)
lp.sec("receiving message from outsider in group chat")
bot_message_from_outsider = bot._evtracker.wait_next_incoming_message()
assert bot_message_from_outsider.text == text
outsider_bot_group.send_text("test message to outsider group")
bot_message_from_outsider = bot.wait_next_incoming_message()
assert not bot.relayplugin.is_relay_group(bot_message_from_outsider.chat)
text = "user -> bot 1:1 chat"
lp.sec(text)
# bot <-> user 1:1 chat
user_botcontact = user.create_contact(bot.get_config("addr"))
user_to_bot = user.create_chat(user_botcontact)
user_to_bot.send_text(text)
lp.sec("receiving message from user in 1:1 chat")
# somehow the message doesn't trigger DC_EVENT_INCOMING_MSG
# bot._evtracker.wait_next_incoming_message()
bot_message_from_user = find_msg(bot, text)
while not bot_message_from_user:
bot_message_from_user = find_msg(bot, text)
time.sleep(1)
assert bot_message_from_user.text == text
user_to_bot.send_text("test message to bot")
bot_message_from_user = bot.wait_next_incoming_message()
assert not bot.relayplugin.is_relay_group(bot_message_from_user.chat)
text = "user -> bot group chat"
lp.sec(text)
# bot <-> user group chat
user_group = user.create_group_chat("test with user", contacts=[user_botcontact])
user_group.send_text(text)
lp.sec("receiving message from user in group chat")
bot_message_from_user = bot._evtracker.wait_next_incoming_message()
assert bot_message_from_user.text == text
user_group.send_text("testing message to user group")
bot_message_from_user = bot.wait_next_incoming_message()
assert not bot.relayplugin.is_relay_group(bot_message_from_user.chat)
@pytest.mark.timeout(TIMEOUT)
@pytest.mark.timeout(60)
def test_relay_group_forwarding(relaycrew, outsider):
bot = relaycrew.bot
user = relaycrew.user
@ -100,18 +56,18 @@ def test_relay_group_forwarding(relaycrew, outsider):
outsider_outside_chat.send_text("test 1:1 message to bot")
# get outside chat
message_from_outsider = bot._evtracker.wait_next_incoming_message()
message_from_outsider = bot.wait_next_incoming_message()
bot_outside_chat = message_from_outsider.chat
assert not bot.relayplugin.is_relay_group(bot_outside_chat)
assert message_from_outsider.is_in_fresh()
# get relay group
user_forwarded_message_from_outsider = user._evtracker.wait_next_incoming_message()
user.wait_next_incoming_message() # group added message
user_forwarded_message_from_outsider = user.wait_next_incoming_message()
user_relay_group = user_forwarded_message_from_outsider.create_chat()
user_relay_group.send_text(
"Chatter in relay group"
) # send normal reply, not forwarded
bot_chatter_in_relay_group = bot._evtracker.wait_next_incoming_message()
bot_chatter_in_relay_group = bot.wait_next_incoming_message()
bot_relay_group = bot_chatter_in_relay_group.chat
# check if relay group has relay group properties
@ -133,10 +89,9 @@ def test_relay_group_forwarding(relaycrew, outsider):
user._dc_context, user_relay_group.id, user_direct_reply._dc_msg
)
assert sent_id == user_direct_reply.id
assert message_from_outsider.is_in_seen()
# check that direct reply was forwarded to outsider
outsider_direct_reply = outsider._evtracker.wait_next_incoming_message()
outsider_direct_reply = outsider.wait_next_incoming_message()
assert outsider_direct_reply.text == "This should be forwarded to the outsider"
assert outsider_direct_reply.chat == outsider_outside_chat
assert outsider_direct_reply.get_sender_contact() == outsider_botcontact
@ -150,7 +105,7 @@ def test_relay_group_forwarding(relaycrew, outsider):
outsider_outside_chat.send_text("Second message by outsider")
# check that outsider's reply ends up in the same chat
user_second_message_from_outsider = user._evtracker.wait_next_incoming_message()
user_second_message_from_outsider = user.wait_next_incoming_message()
assert user_second_message_from_outsider.chat == user_relay_group
# check that relay group explanation is not forwarded to outsider
@ -159,44 +114,6 @@ def test_relay_group_forwarding(relaycrew, outsider):
assert "This is the relay group for" not in msg.text
@pytest.mark.timeout(TIMEOUT)
def test_offboarding(team_bot, relaycrew, outsider, team_user):
# outsider sends message, creates relay group
outsider_botcontact = outsider.create_contact(team_bot.get_config("addr"))
outsider_outside_chat = outsider.create_chat(outsider_botcontact)
outsider_outside_chat.send_text("test 1:1 message to bot")
# get relay group
user_relay_group = team_user._evtracker.wait_next_incoming_message().chat
bot_relay_group = team_bot.get_chats()[-1]
# outsider gets added to crew
qr = relaycrew.get_join_qr()
outsider.qr_join_chat(qr)
outsider._evtracker.wait_securejoin_joiner_progress(1000)
# user kicks outsider from crew
user_crew = get_user_crew(team_user)
user_crew.remove_contact(team_user.create_contact(outsider))
team_bot._evtracker.wait_next_incoming_message()
# user leaves crew
user_crew.remove_contact(team_user)
# make sure they are also offboarded from relay group
team_bot._evtracker.wait_next_incoming_message()
team_user._evtracker.wait_next_incoming_message()
team_user._evtracker.wait_next_incoming_message()
team_user._evtracker.wait_next_incoming_message()
for contact in bot_relay_group.get_contacts():
assert team_user.get_config("addr") != contact.addr
# make sure there is no message in relay group that outsider was kicked
for msg in user_relay_group.get_messages():
print(msg.text)
assert outsider.get_config("addr") + " removed by " not in msg.text
@pytest.mark.timeout(TIMEOUT)
def test_default_outside_help(relaycrew, outsider):
bot = relaycrew.bot
user = relaycrew.user
@ -207,7 +124,7 @@ def test_default_outside_help(relaycrew, outsider):
outsider_outside_chat.send_text("/help")
# get response
outside_help_message = outsider._evtracker.wait_next_incoming_message()
outside_help_message = outsider.wait_next_incoming_message()
assert "I forward messages to the " in outside_help_message.text
# assert no relay group was created
@ -215,7 +132,6 @@ def test_default_outside_help(relaycrew, outsider):
assert len(user.get_chats()) == 1
@pytest.mark.timeout(TIMEOUT)
def test_empty_outside_help(relaycrew, outsider):
bot = relaycrew.bot
user = relaycrew.user
@ -227,7 +143,7 @@ def test_empty_outside_help(relaycrew, outsider):
assert user_crew.get_name().startswith("Team")
user_crew.send_text("/set_outside_help")
# ensure /set_outside_help arrives before sending /help
bot._evtracker.wait_next_incoming_message()
bot.wait_next_incoming_message()
# create outside chat
outsider_botcontact = outsider.create_contact(bot.get_config("addr"))
@ -235,13 +151,12 @@ def test_empty_outside_help(relaycrew, outsider):
outsider_outside_chat.send_text("/help")
# get forwarded /help message
user._evtracker.wait_next_incoming_message() # "Removed help message for outsiders"
user._evtracker.wait_next_incoming_message() # explanation message
user_forwarded_message_from_outsider = user._evtracker.wait_next_incoming_message()
user.wait_next_incoming_message() # group added message
user.wait_next_incoming_message() # explanation message
user_forwarded_message_from_outsider = user.wait_next_incoming_message()
assert user_forwarded_message_from_outsider.text == "/help"
@pytest.mark.timeout(TIMEOUT)
def test_changed_outside_help(relaycrew, outsider):
bot = relaycrew.bot
user = relaycrew.user
@ -254,7 +169,7 @@ def test_changed_outside_help(relaycrew, outsider):
outside_help_text = "Hi friend :) send me messages to chat with the team"
user_crew.send_text("/set_outside_help " + outside_help_text)
# ensure /set_outside_help arrives before sending /help
bot._evtracker.wait_next_incoming_message()
bot.wait_next_incoming_message()
# create outside chat
outsider_botcontact = outsider.create_contact(bot.get_config("addr"))
@ -262,7 +177,7 @@ def test_changed_outside_help(relaycrew, outsider):
outsider_outside_chat.send_text("/help")
# get response
outside_help_message = outsider._evtracker.wait_next_incoming_message()
outside_help_message = outsider.wait_next_incoming_message()
assert outside_help_message.text == outside_help_text
# assert no relay group was created
@ -270,7 +185,6 @@ def test_changed_outside_help(relaycrew, outsider):
assert len(user.get_chats()) == 1
@pytest.mark.timeout(TIMEOUT)
def test_change_avatar(relaycrew):
bot = relaycrew.bot
user = relaycrew.user
@ -288,23 +202,25 @@ def test_change_avatar(relaycrew):
pytest.skip(f"example image not available: {example_png_path}")
# set avatar to example image
user_crew = get_user_crew(user)
for chat in user.get_chats():
print(chat.id, chat.get_name())
user_crew = user.get_chat_by_id(11)
assert user_crew.get_name().startswith("Team")
msg = deltachat.Message.new_empty(user, "image")
msg.set_text("/set_avatar")
msg.set_file(example_png_path)
sent_id = dclib.dc_send_msg(user._dc_context, user_crew.id, msg._dc_msg)
assert sent_id == msg.id
group_avatar_changed_msg = user._evtracker.wait_next_incoming_message()
group_avatar_changed_msg = user.wait_next_incoming_message()
assert "Group image changed" in group_avatar_changed_msg.text
assert user_crew.get_profile_image()
confirmation_msg = user._evtracker.wait_next_incoming_message()
confirmation_msg = user.wait_next_incoming_message()
assert confirmation_msg.text == "Avatar changed to this image."
assert botcontact.get_profile_image()
@pytest.mark.timeout(TIMEOUT * 2)
def test_forward_sending_errors_to_relay_group(relaycrew):
usercrew = relaycrew.user.get_chats()[-1]
usercrew.send_text("/start_chat alice@example.org This_Message_will_fail test")
@ -321,9 +237,7 @@ def test_forward_sending_errors_to_relay_group(relaycrew):
while len(relaycrew.user.get_chats()) < 2 and int(time.time()) < begin + TIMEOUT:
time.sleep(0.1)
for chat in relaycrew.user.get_chats():
if "This Message will fail" in chat.get_name():
relay_group = chat
relay_group = relaycrew.user.get_chats()[-2]
while len(relay_group.get_messages()) < 3 and int(time.time()) < begin + TIMEOUT:
print(relay_group.get_messages()[-1].text)
@ -336,21 +250,3 @@ def test_forward_sending_errors_to_relay_group(relaycrew):
"Invalid unencrypted mail to <alice@example.org>"
in relay_group.get_messages()[-1].text
)
@pytest.mark.timeout(TIMEOUT)
def test_public_invite(relaycrew, outsider):
crew = get_user_crew(relaycrew.user)
crew.send_text("/generate-invite")
result = relaycrew.user._evtracker.wait_next_incoming_message()
# assert result.filename
# assert result.text.startswith("https://i.delta.chat")
# qr = result.filename
# invite = "OPENPGP4FPR:" + result.text[22::]
chat = outsider.qr_setup_contact(result.text)
outsider._evtracker.wait_securejoin_joiner_progress(1000)
while not chat.is_protected():
print(chat.get_messages()[-1].text)
time.sleep(1)

View file

@ -2,7 +2,7 @@ def test_help(cmd):
cmd.run_ok(
[],
"""
Usage: team-bot [OPTIONS] COMMAND [ARGS]...
Usage: teams-bot [OPTIONS] COMMAND [ARGS]...
* -h, --help Show this message and exit.
* init Scan a QR code to create a crew and join it
* run Run the bot, so it relays messages from and to the outside