diff --git a/examples/echo_client.py b/examples/echo_client.py index 9820aa6..16df8ca 100644 --- a/examples/echo_client.py +++ b/examples/echo_client.py @@ -11,6 +11,7 @@ """ import os +import re import sys import asyncio import logging @@ -27,6 +28,10 @@ from omemo.exceptions import MissingBundleException log = logging.getLogger(__name__) +# Used by the EchoBot +LEVEL_DEBUG = 0 +LEVEL_ERROR = 1 + class EchoBot(ClientXMPP): @@ -39,10 +44,15 @@ class EchoBot(ClientXMPP): """ eme_ns = 'eu.siacs.conversations.axolotl' + cmd_prefix = '!' + debug_level: int = LEVEL_DEBUG # or LEVEL_ERROR def __init__(self, jid, password): ClientXMPP.__init__(self, jid, password) + self.prefix_re: re.Pattern = re.compile('^%s' % self.cmd_prefix) + self.cmd_re: re.Pattern = re.compile('^%s(?P\w+)(?:\s+(?P.*))?' % self.cmd_prefix) + self.add_event_handler("session_start", self.start) self.add_event_handler("message", self.message_handler) @@ -62,6 +72,46 @@ class EchoBot(ClientXMPP): self.send_presence() self.get_roster() + def is_command(self, body: str) -> bool: + return self.prefix_re.match(body) is not None + + async def handle_command(self, mto: JID, mtype: str, body: str) -> None: + match = self.cmd_re.match(body) + if match is None: + return None + + groups = match.groupdict() + cmd = groups['command'] + # args = groups['args'] + + if cmd == 'help': + await self.cmd_help(mto, mtype) + elif cmd == 'verbose': + await self.cmd_verbose(mto, mtype) + elif cmd == 'error': + await self.cmd_error(mto, mtype) + + return None + + async def cmd_help(self, mto: JID, mtype: str) -> None: + body = ( + 'I\'m the slixmpp-omemo echo bot! ' + 'The following commands are available:\n' + '{prefix}verbose Send message or reply with log messages\n' + '{prefix}error Send message or reply only on error\n' + ).format(prefix=self.cmd_prefix) + return await self.encrypted_reply(mto, mtype, body) + + async def cmd_verbose(self, mto: JID, mtype: str) -> None: + self.debug_level = LEVEL_DEBUG + body = '''Debug level set to 'verbose'.''' + return await self.encrypted_reply(mto, mtype, body) + + async def cmd_error(self, mto: JID, mtype: str) -> None: + self.debug_level = LEVEL_ERROR + body = '''Debug level set to 'error'.''' + return await self.encrypted_reply(mto, mtype, body) + def message_handler(self, msg: Message) -> None: asyncio.ensure_future(self.message(msg)) @@ -84,14 +134,18 @@ class EchoBot(ClientXMPP): return None if not self['xep_0384'].is_encrypted(msg): - await self.plain_reply(mto, mtype, 'Echo unencrypted message:%(body)s' % msg) + if self.debug_level == LEVEL_DEBUG: + await self.plain_reply(mto, mtype, 'Echo unencrypted message:%(body)s' % msg) return None try: encrypted = msg['omemo_encrypted'] body = self['xep_0384'].decrypt_message(encrypted, mfrom, allow_untrusted) decoded = body.decode('utf8') - await self.encrypted_reply(mto, mtype, 'Echo: %s' % decoded) + if self.is_command(decoded): + await self.handle_command(mto, mtype, decoded) + elif self.debug_level == LEVEL_DEBUG: + await self.encrypted_reply(mto, mtype, 'Echo: %s' % decoded) return None except (MissingOwnKey,): # The message is missing our own key, it was not encrypted for