# Copyright (C) 2007-2012 Thomas Perl <thp.io/about>
# Copyright (C) 2010, 2011 Anaël Verrier <elghinn@free.fr>
# Copyright (C) 2007-2012 Thomas Perl <thp.io/about>
# Copyright (C) 2010, 2011 Anaël Verrier <elghinn@free.fr>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
- """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)
def decorate(func, hidden=False, name=None):
setattr(func, '_bot_command', True)
-class MUCBot(sleekxmpp.ClientXMPP):
+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
+ """
prefix = '!'
def __init__(self, jid, password, room, nick, log_file=None,
prefix = '!'
def __init__(self, jid, password, room, nick, log_file=None,
- self.register_plugin('xep_0030') # Service Discovery
- self.register_plugin('xep_0045') # Multi-User Chat
- self.register_plugin('xep_0199') # self Ping
+ 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
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
- self.add_event_handler("groupchat_message", self.muc_message)
+ self.add_event_handler('message', self.message)
+ self.add_event_handler('got_online', self._view)
# Discover bot internal command (ie. help)
for name, value in inspect.getmembers(self):
if inspect.ismethod(value) and getattr(value, '_bot_command', False):
name = getattr(value, '_bot_command_name')
# Discover bot internal command (ie. help)
for name, value in inspect.getmembers(self):
if inspect.ismethod(value) and getattr(value, '_bot_command', False):
name = getattr(value, '_bot_command_name')
self.commands[name] = value
def __set_logger(self, log_file=None, log_level=logging.INFO):
self.commands[name] = value
def __set_logger(self, log_file=None, log_level=logging.INFO):
log_fd = open(log_file, 'w') if log_file else None
chandler = logging.StreamHandler(log_fd)
formatter = logging.Formatter(
log_fd = open(log_file, 'w') if log_file else None
chandler = logging.StreamHandler(log_fd)
formatter = logging.Formatter(
chandler.setFormatter(formatter)
self.log.addHandler(chandler)
self.log.setLevel(log_level)
chandler.setFormatter(formatter)
self.log.addHandler(chandler)
self.log.setLevel(log_level)
- def muc_message(self, msg):
- # ignore message from self
- body = msg['body'].strip()
- mucfrom = msg['mucnic']
+ def message(self, msg):
+ """Messages handler
+
+ Parses message received to detect :py:obj:`prefix`
+ """
+ if msg['type'] not in ('groupchat', 'chat'):
+ self.log.warning('Unhandled message')
+ return
try:
self.commands[cmd](msg, args)
except Exception as err:
reply = ''.join(traceback.format_exc())
try:
self.commands[cmd](msg, args)
except Exception as err:
reply = ''.join(traceback.format_exc())
- self.log.exception('An error occurred processing: {0}: {1}'.format(body, reply))
- if self.log.level < 20 and reply:
+ self.log.exception('An error occurred processing: %s: %s', body, reply)
+ if self.log.level < 10 and reply:
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))
+ :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):
name = getattr(value, '_bot_command_name')
for name, value in inspect.getmembers(self.plugins[-1]):
if inspect.ismethod(value) and getattr(value, '_bot_command',
False):
name = getattr(value, '_bot_command_name')
self.commands[name] = value
def foreach_plugin(self, method, *args, **kwds):
for plugin in self.plugins:
self.commands[name] = value
def foreach_plugin(self, method, *args, **kwds):
for plugin in self.plugins:
getattr(plugin, method)(*args, **kwds)
def shutdown_plugins(self):
# TODO: why can't use event session_end|disconnected?
self.log.info('shuting down')
for plugin in self.plugins:
getattr(plugin, method)(*args, **kwds)
def shutdown_plugins(self):
# TODO: why can't use event session_end|disconnected?
self.log.info('shuting down')
for plugin in self.plugins:
Automatically assigned to the "help" command."""
help_cmd = ('Type {}help <command name>'.format(self.prefix) +
Automatically assigned to the "help" command."""
help_cmd = ('Type {}help <command name>'.format(self.prefix) +
text = '{}\n\n{}'.format(description, usage)
else:
if args[0] in self.commands.keys():
text = '{}\n\n{}'.format(description, usage)
else:
if args[0] in self.commands.keys():
- self.send_message(mto=message['from'].bare, mbody=text, mtype='groupchat')
+ if message['type'] == 'groupchat':
+ to = message['from'].bare
+ else:
+ to = message['from']
+ self.send_message(mto=to, mbody=text, mtype=message['type'])