forked from missytake/team-bot
added pickleDB to store crew.id #2
This commit is contained in:
parent
ebbc93d945
commit
17a3fce45b
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ __pycache__/
|
||||||
/build/
|
/build/
|
||||||
/venv/
|
/venv/
|
||||||
bot.db/
|
bot.db/
|
||||||
|
teams_bot_data/
|
||||||
|
|
|
@ -21,6 +21,7 @@ packages = find:
|
||||||
python_requires = >=3.8
|
python_requires = >=3.8
|
||||||
install_requires =
|
install_requires =
|
||||||
click
|
click
|
||||||
|
pickleDB
|
||||||
qrcode
|
qrcode
|
||||||
deltachat
|
deltachat
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import logging
|
import logging
|
||||||
from threading import Event
|
from threading import Event
|
||||||
|
|
||||||
|
import pickledb
|
||||||
import deltachat
|
import deltachat
|
||||||
from deltachat import account_hookimpl
|
from deltachat import account_hookimpl
|
||||||
from deltachat.capi import lib as dclib
|
from deltachat.capi import lib as dclib
|
||||||
|
|
||||||
from .commands import help_message, set_display_name, set_avatar, get_crew_id
|
from .commands import help_message, set_display_name, set_avatar
|
||||||
|
|
||||||
|
|
||||||
class SetupPlugin:
|
class SetupPlugin:
|
||||||
|
@ -29,8 +30,10 @@ class SetupPlugin:
|
||||||
|
|
||||||
|
|
||||||
class RelayPlugin:
|
class RelayPlugin:
|
||||||
def __init__(self, account: deltachat.Account):
|
def __init__(self, account: deltachat.Account, kvstore: pickledb.PickleDB):
|
||||||
self.account = account
|
self.account = account
|
||||||
|
self.kvstore = kvstore
|
||||||
|
self.crew = account.get_chat_by_id(kvstore.get("crew_id"))
|
||||||
|
|
||||||
@account_hookimpl
|
@account_hookimpl
|
||||||
def ac_incoming_message(self, message: deltachat.Message):
|
def ac_incoming_message(self, message: deltachat.Message):
|
||||||
|
@ -47,7 +50,7 @@ class RelayPlugin:
|
||||||
""":TODO handle chat name changes"""
|
""":TODO handle chat name changes"""
|
||||||
return
|
return
|
||||||
|
|
||||||
if message.chat.id == get_crew_id(self.account):
|
if message.chat.id == self.crew.id:
|
||||||
if message.text.startswith("/"):
|
if message.text.startswith("/"):
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"handling command by %s: %s",
|
"handling command by %s: %s",
|
||||||
|
@ -64,7 +67,7 @@ class RelayPlugin:
|
||||||
quote=message,
|
quote=message,
|
||||||
)
|
)
|
||||||
if arguments[0] == "/set_avatar":
|
if arguments[0] == "/set_avatar":
|
||||||
result = set_avatar(self.account, message)
|
result = set_avatar(self.account, message, self.crew)
|
||||||
self.reply(message.chat, result, quote=message)
|
self.reply(message.chat, result, quote=message)
|
||||||
else:
|
else:
|
||||||
logging.debug("Ignoring message, just the crew chatting")
|
logging.debug("Ignoring message, just the crew chatting")
|
||||||
|
@ -119,9 +122,7 @@ class RelayPlugin:
|
||||||
def forward_to_relay_group(self, message: deltachat.Message):
|
def forward_to_relay_group(self, message: deltachat.Message):
|
||||||
"""forward a request to a relay group; create one if it doesn't exist yet."""
|
"""forward a request to a relay group; create one if it doesn't exist yet."""
|
||||||
outsider = message.get_sender_contact().addr
|
outsider = message.get_sender_contact().addr
|
||||||
crew_members = self.account.get_chat_by_id(
|
crew_members = self.crew.get_contacts()
|
||||||
get_crew_id(self.account)
|
|
||||||
).get_contacts()
|
|
||||||
crew_members.remove(self.account.get_self_contact())
|
crew_members.remove(self.account.get_self_contact())
|
||||||
group_name = "[%s] %s" % (
|
group_name = "[%s] %s" % (
|
||||||
self.account.get_config("addr").split("@")[0],
|
self.account.get_config("addr").split("@")[0],
|
||||||
|
@ -157,9 +158,7 @@ class RelayPlugin:
|
||||||
return False # all relay groups were started by the teamsbot
|
return False # all relay groups were started by the teamsbot
|
||||||
if chat.is_protected():
|
if chat.is_protected():
|
||||||
return False # relay groups don't need to be protected, so they are not
|
return False # relay groups don't need to be protected, so they are not
|
||||||
for crew_member in self.account.get_chat_by_id(
|
for crew_member in self.crew.get_contacts():
|
||||||
get_crew_id(self.account)
|
|
||||||
).get_contacts():
|
|
||||||
if crew_member not in chat.get_contacts():
|
if crew_member not in chat.get_contacts():
|
||||||
return False # all crew members have to be in any relay group
|
return False # all crew members have to be in any relay group
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import logging
|
import logging
|
||||||
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import pickledb
|
||||||
import click
|
import click
|
||||||
import qrcode
|
import qrcode
|
||||||
import deltachat
|
import deltachat
|
||||||
|
|
||||||
from .bot import SetupPlugin, RelayPlugin, get_crew_id
|
from .bot import SetupPlugin, RelayPlugin
|
||||||
|
|
||||||
|
|
||||||
def set_log_level(verbose: int, db: str):
|
def set_log_level(verbose: int, db: str):
|
||||||
|
@ -37,22 +39,27 @@ def teams_bot(ctx):
|
||||||
"--password", type=str, default=None, help="the password of the email account"
|
"--password", type=str, default=None, help="the password of the email account"
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--db", type=str, default="bot.db/db.sqlite", help="path to the bot's database"
|
"--db_dir", type=str, default="teams_bot_data", help="path to the bot's database"
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-v", "--verbose", count=True, help="show low level delta chat ffi events"
|
"-v", "--verbose", count=True, help="show low level delta chat ffi events"
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def init(ctx, email: str, password: str, db: str, verbose: int):
|
def init(ctx, email: str, password: str, db_dir: str, verbose: int):
|
||||||
"""Configure bot; create crew; add user to crew by scanning a QR code."""
|
"""Configure bot; create crew; add user to crew by scanning a QR code."""
|
||||||
set_log_level(verbose, db)
|
db_dir = pathlib.Path(db_dir)
|
||||||
|
delta_db = db_dir.joinpath("delta.sqlite")
|
||||||
|
pickle_path = db_dir.joinpath("pickle.db")
|
||||||
|
kvstore = pickledb.load(pickle_path, True)
|
||||||
|
|
||||||
ac = deltachat.Account(db)
|
set_log_level(verbose, delta_db)
|
||||||
|
|
||||||
|
ac = deltachat.Account(str(delta_db))
|
||||||
ac.run_account(addr=email, password=password, show_ffi=verbose)
|
ac.run_account(addr=email, password=password, show_ffi=verbose)
|
||||||
ac.set_config("mvbox_move", "1")
|
ac.set_config("mvbox_move", "1")
|
||||||
ac.set_config("sentbox_watch", "0")
|
ac.set_config("sentbox_watch", "0")
|
||||||
|
|
||||||
crew_id_old = get_crew_id(ac)
|
crew_id_old = kvstore.get("crew_id")
|
||||||
|
|
||||||
chat = ac.create_group_chat(
|
chat = ac.create_group_chat(
|
||||||
"Team: {}".format(ac.get_config("addr")), contacts=[], verified=True
|
"Team: {}".format(ac.get_config("addr")), contacts=[], verified=True
|
||||||
|
@ -87,13 +94,20 @@ def init(ctx, email: str, password: str, db: str, verbose: int):
|
||||||
|
|
||||||
if crew_id_old:
|
if crew_id_old:
|
||||||
setupplugin.message_sent.clear()
|
setupplugin.message_sent.clear()
|
||||||
|
old_crew = ac.get_chat_by_id(crew_id_old)
|
||||||
|
old_crew.set_name(f"Old Team: {ac.get_config('addr')}")
|
||||||
|
new_crew = [contact.addr for contact in chat.get_contacts()]
|
||||||
|
new_crew_emails = " or ".join(new_crew)
|
||||||
|
quit_message = f"There is a new Group for the Team now; you can ask {new_crew_emails} to add you to it."
|
||||||
|
logging.debug(
|
||||||
|
"Sending quit message to old crew with ID %s: %s",
|
||||||
|
old_crew.id,
|
||||||
|
quit_message,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
new_crew_id = get_crew_id(
|
old_crew.send_text(quit_message)
|
||||||
ac, setupplugin
|
setupplugin.outgoing_messages += 1
|
||||||
) # notify old crew about who created the new crew
|
old_crew.remove_contact(ac.get_self_contact())
|
||||||
assert (
|
|
||||||
new_crew_id == chat.id
|
|
||||||
), f"Bot found different 'new crew' than the one we just created; consider deleting {db}"
|
|
||||||
setupplugin.message_sent.wait()
|
setupplugin.message_sent.wait()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logging.warning("Could not notify the old crew: %s", str(e))
|
logging.warning("Could not notify the old crew: %s", str(e))
|
||||||
|
@ -101,22 +115,31 @@ def init(ctx, email: str, password: str, db: str, verbose: int):
|
||||||
sys.stdout.flush() # flush stdout to actually show the messages above
|
sys.stdout.flush() # flush stdout to actually show the messages above
|
||||||
ac.shutdown()
|
ac.shutdown()
|
||||||
|
|
||||||
|
kvstore.set("crew_id", chat.id)
|
||||||
|
logging.info("Successfully changed crew ID to the new group.")
|
||||||
|
|
||||||
|
|
||||||
@teams_bot.command()
|
@teams_bot.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--db", type=str, default="bot.db/db.sqlite", help="path to the bot's database"
|
"--db_dir", type=str, default="teams_bot_data", help="path to the bot's database"
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-v", "--verbose", count=True, help="show low level delta chat ffi events"
|
"-v", "--verbose", count=True, help="show low level delta chat ffi events"
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def run(ctx, db: str, verbose: int):
|
def run(ctx, db_dir: str, verbose: int):
|
||||||
"""Run the bot, so it relays messages between the crew and the outside."""
|
"""Run the bot, so it relays messages between the crew and the outside."""
|
||||||
set_log_level(verbose, db)
|
db_dir = pathlib.Path(db_dir)
|
||||||
|
delta_db = db_dir.joinpath("delta.sqlite")
|
||||||
|
pickle_path = db_dir.joinpath("pickle.db")
|
||||||
|
kvstore = pickledb.load(pickle_path, True)
|
||||||
|
|
||||||
ac = deltachat.Account(db)
|
logging.debug("delta_db: %s", type(delta_db))
|
||||||
|
set_log_level(verbose, delta_db)
|
||||||
|
|
||||||
|
ac = deltachat.Account(str(delta_db))
|
||||||
display_name = ac.get_config("displayname")
|
display_name = ac.get_config("displayname")
|
||||||
ac.run_account(account_plugins=[RelayPlugin(ac)], show_ffi=verbose)
|
ac.run_account(account_plugins=[RelayPlugin(ac, kvstore)], show_ffi=verbose)
|
||||||
ac.set_config("displayname", display_name)
|
ac.set_config("displayname", display_name)
|
||||||
try:
|
try:
|
||||||
ac.wait_shutdown()
|
ac.wait_shutdown()
|
||||||
|
|
|
@ -25,7 +25,9 @@ def set_display_name(account: deltachat.Account, display_name: str) -> str:
|
||||||
return "Display name changed to " + display_name
|
return "Display name changed to " + display_name
|
||||||
|
|
||||||
|
|
||||||
def set_avatar(account: deltachat.Account, message: deltachat.Message) -> str:
|
def set_avatar(
|
||||||
|
account: deltachat.Account, message: deltachat.Message, crew: deltachat.Chat
|
||||||
|
) -> str:
|
||||||
"""Set the avatar of the bot.
|
"""Set the avatar of the bot.
|
||||||
|
|
||||||
:return: a success/failure message
|
:return: a success/failure message
|
||||||
|
@ -34,54 +36,5 @@ def set_avatar(account: deltachat.Account, message: deltachat.Message) -> str:
|
||||||
return "Please attach an image so the avatar can be changed."
|
return "Please attach an image so the avatar can be changed."
|
||||||
logging.debug("Found file with MIMEtype %s", message.filemime)
|
logging.debug("Found file with MIMEtype %s", message.filemime)
|
||||||
account.set_avatar(message.filename)
|
account.set_avatar(message.filename)
|
||||||
crew = account.get_chat_by_id(get_crew_id(account))
|
|
||||||
crew.set_profile_image(message.filename)
|
crew.set_profile_image(message.filename)
|
||||||
return "Avatar changed to this image."
|
return "Avatar changed to this image."
|
||||||
|
|
||||||
|
|
||||||
def get_crew_id(ac: deltachat.Account, setupplugin=None) -> int:
|
|
||||||
"""Get the group ID of the crew group if it exists; warn old crews if they might still believe they are the crew.
|
|
||||||
|
|
||||||
:param ac: the account object of the bot.
|
|
||||||
:param setupplugin: only if this function is run during `teams-bot init`.
|
|
||||||
:return: the chat ID of the crew group, if there is none, return 0.
|
|
||||||
"""
|
|
||||||
crew_id = 0
|
|
||||||
for chat in reversed(ac.get_chats()):
|
|
||||||
if (
|
|
||||||
chat.is_protected()
|
|
||||||
and chat.num_contacts() > 1
|
|
||||||
and chat.get_name() == f"Team: {ac.get_config('addr')}"
|
|
||||||
):
|
|
||||||
logging.debug(
|
|
||||||
"Chat with ID %s and title %s could be a crew", chat.id, chat.get_name()
|
|
||||||
)
|
|
||||||
if crew_id > 0:
|
|
||||||
old_crew = ac.get_chat_by_id(crew_id)
|
|
||||||
old_crew.set_name(f"Old Team: {ac.get_config('addr')}")
|
|
||||||
new_crew = [contact.addr for contact in chat.get_contacts()]
|
|
||||||
new_crew_emails = " or ".join(new_crew)
|
|
||||||
quit_message = f"There is a new Group for the Team now; you can ask {new_crew_emails} to add you to it."
|
|
||||||
logging.debug(
|
|
||||||
"Sending quit message to old crew with ID %s: %s",
|
|
||||||
old_crew.id,
|
|
||||||
quit_message,
|
|
||||||
)
|
|
||||||
old_crew.send_text(quit_message)
|
|
||||||
if setupplugin:
|
|
||||||
setupplugin.outgoing_messages += 1
|
|
||||||
old_crew.remove_contact(ac.get_self_contact())
|
|
||||||
crew_id = chat.id
|
|
||||||
else:
|
|
||||||
logging.debug(
|
|
||||||
"Chat with ID %s and title %s is not a crew.", chat.id, chat.get_name()
|
|
||||||
)
|
|
||||||
if crew_id:
|
|
||||||
crew_members = [
|
|
||||||
contact.addr for contact in ac.get_chat_by_id(crew_id).get_contacts()
|
|
||||||
]
|
|
||||||
crew_emails = " or ".join(crew_members)
|
|
||||||
logging.debug("The current crew has ID %s and members %s", crew_id, crew_emails)
|
|
||||||
else:
|
|
||||||
logging.debug("Currently there is no crew")
|
|
||||||
return crew_id
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import pickledb
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import deltachat
|
import deltachat
|
||||||
|
@ -79,14 +81,14 @@ def tmp_file_path(request, tmpdir):
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def relaycrew(crew):
|
def relaycrew(crew):
|
||||||
crew.bot.relayplugin = RelayPlugin(crew.bot)
|
crew.bot.relayplugin = RelayPlugin(crew.bot, crew.kvstore)
|
||||||
crew.bot.add_account_plugin(crew.bot.relayplugin)
|
crew.bot.add_account_plugin(crew.bot.relayplugin)
|
||||||
assert not crew.bot.relayplugin.is_relay_group(crew)
|
assert not crew.bot.relayplugin.is_relay_group(crew)
|
||||||
yield crew
|
yield crew
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def crew(teams_bot, teams_user):
|
def crew(teams_bot, teams_user, tmpdir):
|
||||||
from teams_bot.bot import SetupPlugin
|
from teams_bot.bot import SetupPlugin
|
||||||
|
|
||||||
crew = teams_bot.create_group_chat(
|
crew = teams_bot.create_group_chat(
|
||||||
|
@ -109,6 +111,8 @@ def crew(teams_bot, teams_user):
|
||||||
print("User received message:", last_message)
|
print("User received message:", last_message)
|
||||||
last_message = teams_user.wait_next_incoming_message().text
|
last_message = teams_user.wait_next_incoming_message().text
|
||||||
|
|
||||||
|
crew.kvstore = pickledb.load(tmpdir + "pickle.db", True)
|
||||||
|
crew.kvstore.set("crew_id", crew.id)
|
||||||
yield crew
|
yield crew
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import deltachat
|
import deltachat
|
||||||
from deltachat.capi import lib as dclib
|
from deltachat.capi import lib as dclib
|
||||||
|
|
||||||
from teams_bot.commands import get_crew_id
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_relay_groups(relaycrew, outsider):
|
def test_not_relay_groups(relaycrew, outsider):
|
||||||
bot = relaycrew.bot
|
bot = relaycrew.bot
|
||||||
|
@ -71,10 +69,7 @@ def test_relay_group_forwarding(relaycrew, outsider):
|
||||||
bot_relay_group.get_messages()[0].get_sender_contact() == bot.get_self_contact()
|
bot_relay_group.get_messages()[0].get_sender_contact() == bot.get_self_contact()
|
||||||
)
|
)
|
||||||
assert not bot_relay_group.is_protected()
|
assert not bot_relay_group.is_protected()
|
||||||
assert (
|
assert relaycrew.get_contacts() == bot_relay_group.get_contacts()
|
||||||
bot.get_chat_by_id(get_crew_id(bot)).get_contacts()
|
|
||||||
== bot_relay_group.get_contacts()
|
|
||||||
)
|
|
||||||
assert bot.relayplugin.is_relay_group(bot_relay_group)
|
assert bot.relayplugin.is_relay_group(bot_relay_group)
|
||||||
|
|
||||||
# send direct reply, should be forwarded
|
# send direct reply, should be forwarded
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
from teams_bot.commands import get_crew_id
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_crew_id(crew):
|
|
||||||
"""Test if crew is properly found in delta chat database."""
|
|
||||||
assert crew.id == get_crew_id(crew.bot)
|
|
||||||
|
|
||||||
|
|
||||||
def test_disable_old_crew(crew, outsider):
|
|
||||||
"""Test if crew is properly disabled if someone else creates a new crew on the command line."""
|
|
||||||
old_crew_id = get_crew_id(crew.bot)
|
|
||||||
|
|
||||||
# outsider fires up the command line and creates a new crew
|
|
||||||
new_crew = crew.bot.create_group_chat(
|
|
||||||
f"Team: {crew.bot.get_config('addr')}", verified=True
|
|
||||||
)
|
|
||||||
assert new_crew.id != old_crew_id
|
|
||||||
qr = new_crew.get_join_qr()
|
|
||||||
|
|
||||||
# prepare setupplugin for waiting on second group join
|
|
||||||
crew.bot.setupplugin.member_added.clear()
|
|
||||||
crew.bot.setupplugin.crew_id = new_crew.id
|
|
||||||
|
|
||||||
# outsider joins new crew
|
|
||||||
outsider.qr_join_chat(qr)
|
|
||||||
crew.bot.setupplugin.member_added.wait(timeout=30)
|
|
||||||
assert len(new_crew.get_contacts()) == 2
|
|
||||||
assert new_crew.get_name() == f"Team: {crew.bot.get_config('addr')}"
|
|
||||||
assert new_crew.is_protected()
|
|
||||||
assert new_crew.id == get_crew_id(crew.bot, crew.bot.setupplugin)
|
|
||||||
|
|
||||||
# old user receives disable warning
|
|
||||||
crew.user.wait_next_incoming_message()
|
|
||||||
quit_message = crew.user.wait_next_incoming_message()
|
|
||||||
assert "There is a new Group for the Team now" in quit_message.text
|
|
||||||
assert outsider.get_config("addr") in quit_message.text
|
|
Loading…
Reference in a new issue