X-Git-Url: http://git.kaliko.me/?a=blobdiff_plain;f=sid%2Fsid.py;h=a3ca555e891602c839ababae1097635676a08e75;hb=ca957c6dc76e96e46d8fc05fbb0c7dff1cb1437c;hp=0df5478de958e31dc1f268602b604196f8a828a1;hpb=685ca532c5f30b41da003878cbfca8183b4965b7;p=sid.git diff --git a/sid/sid.py b/sid/sid.py index 0df5478..a3ca555 100644 --- a/sid/sid.py +++ b/sid/sid.py @@ -23,9 +23,15 @@ import traceback import slixmpp +from sid import __url__ + def botcmd(*args, **kwargs): - """Decorator for bot command functions""" + """Decorator for bot command functions + + :param bool hidden: is the command hidden in global help + :param str name: command name, default to decorated function name + """ def decorate(func, hidden=False, name=None): setattr(func, '_bot_command', True) @@ -42,13 +48,25 @@ def botcmd(*args, **kwargs): class MUCBot(slixmpp.ClientXMPP): - + """ + :param str jid: jid to log with + :param str password: jid password + :param str room: conference room to join + :param str nick: Nickname to use in the room + """ + + #: Class attribute to define bot's command prefix + #: + #: Defaults to "!" prefix = '!' def __init__(self, jid, password, room, nick, log_file=None, log_level=logging.INFO): super(MUCBot, self).__init__(jid, password) + # Clean sphinx autodoc for self documentation + # (cf. MUCBot.help) + self.__doc__ = None self.log = logging.getLogger(__package__) self.plugins = list() self.commands = dict() @@ -56,10 +74,10 @@ class MUCBot(slixmpp.ClientXMPP): self.nick = nick self.__set_logger(log_file, log_level) self.__seen = dict() - self.register_plugin('xep_0030') # Service Discovery - self.register_plugin('xep_0045') # Multi-User Chat - self.register_plugin('xep_0071') # xhtml-im - self.register_plugin('xep_0199') # self Ping + self.register_plugin('xep_0030') # Service Discovery + self.register_plugin('xep_0045') # Multi-User Chat + self.register_plugin('xep_0071') # xhtml-im + self.register_plugin('xep_0199') # self Ping # The session_start event will be triggered when # the bot establishes its connection with the server @@ -72,9 +90,13 @@ class MUCBot(slixmpp.ClientXMPP): self.add_event_handler('message', self.message) self.add_event_handler('got_online', self._view) + # Handles disconnection + self.add_event_handler('disconnected', self.disconn) + # Discover bot internal command (ie. help) for name, value in inspect.getmembers(self): - if inspect.ismethod(value) and getattr(value, '_bot_command', False): + if inspect.ismethod(value) and \ + getattr(value, '_bot_command', False): name = getattr(value, '_bot_command_name') self.log.debug('Registered command: %s', name) self.commands[name] = value @@ -91,8 +113,17 @@ class MUCBot(slixmpp.ClientXMPP): self.log.setLevel(log_level) self.log.debug('set logger, log level : %s', log_level) + def disconn(self, event): + """disconnected handler""" + msg = ": %s" % event if event else "‽" + self.log.info('Disconnected from server%s', msg) + self.connect() + def message(self, msg): - """Messages handler""" + """Messages handler + + Parses message received to detect :py:obj:`prefix` + """ if msg['type'] not in ('groupchat', 'chat'): self.log.warning('Unhandled message') return @@ -101,9 +132,11 @@ class MUCBot(slixmpp.ClientXMPP): body = msg['body'].strip() if not body.startswith(MUCBot.prefix): return - if msg['from'] not in self.__seen: - self.log.warning('Will not handle message from unseen jid: %s', msg['from']) - #return + self.log.debug(msg['from']) + if msg['from'] not in self.__seen and msg['type'] == 'chat': + self.log.warning('Will not handle direct message' + 'from unseen jid: %s', msg['from']) + return args = body[1:].split() cmd = args.pop(0) if cmd not in self.commands: @@ -117,7 +150,8 @@ class MUCBot(slixmpp.ClientXMPP): reply = ''.join(traceback.format_exc()) self.log.exception('An error occurred processing: %s: %s', body, reply) if self.log.level < 10 and reply: - self.send_message(mto=msg['from'].bare, mbody=reply, mtype='groupchat') + self.send_message(mto=msg['from'].bare, mbody=reply, + mtype='groupchat') def _view(self, pres): """Track known nick""" @@ -125,7 +159,7 @@ class MUCBot(slixmpp.ClientXMPP): status = (pres['type'], pres['status']) self.__seen.update({nick: status}) - def start(self, event): + async def start(self, event): """ Process the session_start event. @@ -133,35 +167,37 @@ class MUCBot(slixmpp.ClientXMPP): requesting the roster and broadcasting an initial presence stanza. - Arguments: - event -- An empty dictionary. The session_start - event does not provide any additional - data. + :param dict event: An empty dictionary. The session_start + event does not provide any additional data. """ - self.get_roster() + await self.get_roster() self.send_presence() self.plugin['xep_0045'].join_muc(self.room, - self.nick, - # If a room password is needed, use: - # password=the_room_password, - wait=True) + self.nick, + # If a room password is needed, use: + # password=the_room_password, + wait=True) - def register_bot_plugin(self, plugin_class): - self.plugins.append(plugin_class(self)) + def register_bot_plugin(self, plugin_cls): + """Registers plugin, takes a class, the method instanciates the plugin + + :param `sid.plugin.Plugin` plugin_cls: A :py:obj:`sid.plugin.Plugin` class + """ + self.plugins.append(plugin_cls(self)) for name, value in inspect.getmembers(self.plugins[-1]): - if inspect.ismethod(value) and getattr(value, '_bot_command', - False): + if inspect.ismethod(value) and \ + getattr(value, '_bot_command', False): name = getattr(value, '_bot_command_name') self.log.debug('Registered command: %s', name) self.commands[name] = value def foreach_plugin(self, method, *args, **kwds): for plugin in self.plugins: - self.log.debug('shuting down %s', plugin.__str__) + self.log.debug('calling %s for %s', method, plugin) getattr(plugin, method)(*args, **kwds) def shutdown_plugins(self): - # TODO: why can't use event session_end|disconnected? + # TODO: also use event session_end|disconnected? self.log.info('shuting down') for plugin in self.plugins: self.log.debug('shuting down %s', plugin) @@ -173,7 +209,8 @@ class MUCBot(slixmpp.ClientXMPP): Automatically assigned to the "help" command.""" help_cmd = ('Type {}help '.format(self.prefix) + - ' to get more info about that specific command.') + ' to get more info about that specific command.\n\n' + + f'SRC: {__url__}') if not args: if self.__doc__: description = self.__doc__.strip() @@ -192,7 +229,8 @@ class MUCBot(slixmpp.ClientXMPP): text = '{}\n\n{}'.format(description, usage) else: if args[0] in self.commands.keys(): - text = self.commands[args[0]].__doc__.strip() or 'undocumented' + text = self.commands[args[0]].__doc__ or 'undocumented' + text = inspect.cleandoc(text) else: text = 'That command is not defined.' if message['type'] == 'groupchat':