diff --git a/setup.cfg b/setup.cfg index a5da691..056f833 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ packages = find: python_requires = >=3.8 install_requires = click + qrcode deltachat [options.packages.find] diff --git a/src/teams_bot/bot.py b/src/teams_bot/bot.py new file mode 100644 index 0000000..a5c50fd --- /dev/null +++ b/src/teams_bot/bot.py @@ -0,0 +1,73 @@ +import logging +from threading import Event + +import deltachat +from deltachat import account_hookimpl + + +class SetupPlugin: + def __init__(self, admingrpid): + self.member_added = Event() + self.admingrpid = admingrpid + self.message_sent = Event() + self.outgoing_messages = 0 + + @account_hookimpl + def ac_member_added(self, chat: deltachat.Chat, contact, actor, message): + if chat.id == self.admingrpid and chat.num_contacts() == 2: + self.member_added.set() + + @account_hookimpl + def ac_message_delivered(self, message: deltachat.Message): + if not message.is_system_message(): + self.outgoing_messages -= 1 + if self.outgoing_messages < 1: + self.message_sent.set() + + +def get_crew_id(ac: deltachat.Account, setupplugin: 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 diff --git a/src/teams_bot/cli.py b/src/teams_bot/cli.py index 3a55a80..8aa8f2c 100644 --- a/src/teams_bot/cli.py +++ b/src/teams_bot/cli.py @@ -1,5 +1,11 @@ -import click import logging +import sys + +import click +import qrcode +import deltachat + +from .bot import SetupPlugin, get_crew_id def set_log_level(verbose: int, db: str): @@ -41,6 +47,60 @@ def init(ctx, email: str, password: str, db: str, verbose: int): """Configure bot; create crew; add user to crew by scanning a QR code.""" set_log_level(verbose, db) + ac = deltachat.Account(db) + ac.run_account(addr=email, password=password, show_ffi=verbose) + ac.set_config("mvbox_move", "1") + ac.set_config("sentbox_watch", "0") + + crew_id_old = get_crew_id(ac) + + chat = ac.create_group_chat( + "Team: {}".format(ac.get_config("addr")), contacts=[], verified=True + ) + + setupplugin = SetupPlugin(chat.id) + ac.add_account_plugin(setupplugin) + + chatinvite = chat.get_join_qr() + qr = qrcode.QRCode() + qr.add_data(chatinvite) + print( + "\nPlease scan this qr code with Delta Chat to join the verified crew group:\n\n" + ) + qr.print_ascii(invert=True) + print( + "\nAlternatively, copy-paste this invite to your Delta Chat desktop client:", + chatinvite, + ) + + print("\nWaiting until you join the chat") + sys.stdout.flush() # flush stdout to actually show the messages above + setupplugin.member_added.wait() + setupplugin.message_sent.clear() + + chat.send_text( + "Welcome to the %s crew! Type /help to see the existing commands." + % (ac.get_config("addr"),) + ) + print("Welcome message sent.") + setupplugin.message_sent.wait() + + if crew_id_old: + setupplugin.message_sent.clear() + try: + new_crew_id = get_crew_id( + ac, setupplugin + ) # notify old crew about who created the new crew + 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() + except ValueError as e: + logging.warning("Could not notify the old crew: %s", str(e)) + print("The old crew was deactivated.") + sys.stdout.flush() # flush stdout to actually show the messages above + ac.shutdown() + @teams_bot.command() @click.option(