Commit c18694ac authored by Guillaume BINET's avatar Guillaume BINET

major refactoring in the framework to make it more readable

parent 77e72a0b
import inspect
import logging
import os
import shelve
from config import BOT_DATA_DIR
from utils import PLUGINS_SUBDIR
import holder
# this class handle the basic needs of bot plugins like loading, unloading and creating a storage
class BotPlugin(object):
......@@ -15,21 +17,33 @@ class BotPlugin(object):
logging.debug('Init shelf for %s' % classname)
filename = BOT_DATA_DIR + os.sep + PLUGINS_SUBDIR + os.sep + classname + '.db'
logging.debug('Loading %s' % filename)
if hasattr(self.__class__, '_shelf'):
self.__class__._shelf.close()
self.__class__._shelf = shelve.DbfilenameShelf(filename)
BotPlugin.botbase_class.__bases__ += ( self.__class__,) # I use a class parameter to avoid a circular dependency to errBot
self.shelf = shelve.DbfilenameShelf(filename)
for name, value in inspect.getmembers(self, inspect.ismethod):
if getattr(value, '_jabberbot_command', False):
name = getattr(value, '_jabberbot_command_name')
logging.debug('Adding command to %s : %s -> %s' % (holder.bot, name, value))
holder.bot.commands[name] = value
self.is_activated = True
def deactivate(self):
BotPlugin.botbase_class.__bases__ = filter(lambda e: e != self.__class__, BotPlugin.botbase_class.__bases__)
logging.debug('Closing shelf %s' % self.shelf)
self.shelf.close()
for name, value in inspect.getmembers(self, inspect.ismethod):
if getattr(value, '_jabberbot_command', False):
name = getattr(value, '_jabberbot_command_name')
del(holder.bot.commands[name])
self.is_activated = False
@property
def shelf(self):
return self.__class__._shelf
# Proxyfy some useful tools from the motherbot
def send(self, user, text, in_reply_to=None, message_type='chat'):
return holder.bot.send(user, text, in_reply_to, message_type)
def connect(self):
return holder.bot.connect()
def join_room(self, room, username=None, password=None):
return holder.bot.join_room(room, username, password)
......@@ -19,29 +19,29 @@ import os
from config import BOT_IDENTITY,BOT_LOG_LEVEL,BOT_DATA_DIR
from utils import PLUGINS_SUBDIR
logging.basicConfig(format='%(levelname)s:%(message)s')
logging.getLogger('').setLevel(BOT_LOG_LEVEL)
d = os.path.dirname(BOT_DATA_DIR)
if not os.path.exists(d):
raise Exception('The data directory %s for the bot does not exist' % BOT_DATA_DIR)
if not os.access(BOT_DATA_DIR, os.W_OK):
raise Exception('The data directory %s should be writable for the bot' % BOT_DATA_DIR)
# make the plugins subdir to store the plugin shelves
d = BOT_DATA_DIR + os.sep + PLUGINS_SUBDIR
if not os.path.exists(d):
os.makedirs(d)
from errBot import ErrBot
from botplugin import BotPlugin
import holder
holder.bot = ErrBot(**BOT_IDENTITY)
def main():
logging.basicConfig(format='%(levelname)s:%(message)s')
logging.getLogger('').setLevel(BOT_LOG_LEVEL)
__author__ = 'gbin'
d = os.path.dirname(BOT_DATA_DIR)
if not os.path.exists(d):
raise Exception('The data directory %s for the bot does not exist' % BOT_DATA_DIR)
if not os.access(BOT_DATA_DIR, os.W_OK):
raise Exception('The data directory %s should be writable for the bot' % BOT_DATA_DIR)
# make the plugins subdir to store the plugin shelves
d = BOT_DATA_DIR + os.sep + PLUGINS_SUBDIR
if not os.path.exists(d):
os.makedirs(d)
# Set the class to extend in the BotPlugin (trick to avoid weird circular dependencies)
BotPlugin.botbase_class = ErrBot
holder.bot.update_dynamic_plugins()
logging.debug('serve from %s' % holder.bot)
holder.bot.serve_forever()
bot = ErrBot(**BOT_IDENTITY)
bot.serve_forever()
if __name__ == "__main__":
main()
\ No newline at end of file
......@@ -26,7 +26,7 @@ from urllib2 import urlopen
from config import BOT_DATA_DIR, BOT_ADMINS
from jabberbot import JabberBot, botcmd
from plugin_manager import get_all_active_plugin_names, activate_plugin, deactivate_plugin, activate_all_plugins, deactivate_all_plugins, update_plugin_places, init_plugin_manager
from plugin_manager import get_all_active_plugin_names, activate_plugin, deactivate_plugin, activate_all_plugins, deactivate_all_plugins, update_plugin_places, init_plugin_manager, get_all_active_plugin_objects
from utils import get_jid_from_message, PLUGINS_SUBDIR, human_name_for_git_url
from repos import KNOWN_PUBLIC_REPOS
......@@ -49,43 +49,41 @@ class ErrBot(JabberBot):
""" Commands related to the bot administration """
MSG_ERROR_OCCURRED = 'Computer says nooo. '
MSG_UNKNOWN_COMMAND = 'Unknown command: "%(command)s". '
shelf = shelve.DbfilenameShelf(BOT_DATA_DIR + os.sep + 'core.db')
internal_shelf = shelve.DbfilenameShelf(BOT_DATA_DIR + os.sep + 'core.db')
def add_repo(self, name, url):
repos = self.shelf.get('repos', {})
repos = self.internal_shelf.get('repos', {})
repos[name] = url
self.shelf['repos'] = repos
self.shelf.sync()
self.internal_shelf['repos'] = repos
self.internal_shelf.sync()
# this will load the plugins the admin has setup at runtime
def update_dynamic_plugins(self):
update_plugin_places([PLUGIN_DIR + os.sep + d for d in self.shelf.get('repos', {}).keys()])
update_plugin_places([PLUGIN_DIR + os.sep + d for d in self.internal_shelf.get('repos', {}).keys()])
def __init__(self, username, password, res=None, debug=False,
privatedomain=False, acceptownmsgs=False, handlers=None):
JabberBot.__init__(self, username, password, res, debug, privatedomain, acceptownmsgs, handlers)
self.update_dynamic_plugins()
def callback_message(self, conn, mess):
for cls in ErrBot.__bases__:
if hasattr(cls, 'callback_message'):
for bot in get_all_active_plugin_objects():
if hasattr(bot, 'callback_message'):
try:
cls.callback_message(self, conn, mess)
bot.callback_message(conn, mess)
except:
logging.exception("Probably a type error")
def activate_non_started_plugins(self):
logging.info('Activating all the plugins...')
activate_all_plugins()
logging.info('Refreshing command list...')
self.refresh_command_list()
def signal_connect_to_all_plugins(self):
for cls in ErrBot.__bases__:
if hasattr(cls, 'callback_connect'):
for bot in get_all_active_plugin_objects():
if hasattr(bot, 'callback_connect'):
try:
cls.callback_connect(self)
bot.callback_connect()
except:
logging.exception("callback_connect failed for %s" % cls)
logging.exception("callback_connect failed for %s" % bot)
def connect(self):
if not self.conn:
self.conn = JabberBot.connect(self)
......@@ -98,7 +96,7 @@ class ErrBot(JabberBot):
def shutdown(self):
logging.info('Shutting down... deactivating all the plugins.')
deactivate_all_plugins()
self.shelf.close()
self.internal_shelf.close()
logging.info('Bye.')
@botcmd
......@@ -119,7 +117,7 @@ class ErrBot(JabberBot):
"""load a plugin"""
admin_only(mess)
result = activate_plugin(args)
self.refresh_command_list()
#self.refresh_command_list()
return result
@botcmd
......@@ -127,7 +125,7 @@ class ErrBot(JabberBot):
"""unload a plugin"""
admin_only(mess)
result = deactivate_plugin(args)
self.refresh_command_list()
#self.refresh_command_list()
return result
@botcmd
......@@ -135,7 +133,7 @@ class ErrBot(JabberBot):
"""reload a plugin"""
admin_only(mess)
result = deactivate_plugin(args) + " / " + activate_plugin(args)
self.refresh_command_list()
#self.refresh_command_list()
return result
@botcmd
......@@ -175,13 +173,13 @@ class ErrBot(JabberBot):
admin_only(mess)
if not args.strip():
return "You should have a repo name as argument"
repos = self.shelf.get('repos', {})
repos = self.internal_shelf.get('repos', {})
if not repos.has_key(args):
return "This repo is not installed check with !repos the list of installed ones"
shutil.rmtree(PLUGIN_DIR + os.sep + args)
repos.pop(args)
self.shelf['repos'] = repos
self.shelf.sync()
self.internal_shelf['repos'] = repos
self.internal_shelf.sync()
self.quit(-1337)
return "Done, restarting"
......@@ -192,7 +190,7 @@ class ErrBot(JabberBot):
max_width = max([len(name) for name,(_,_) in KNOWN_PUBLIC_REPOS.iteritems()])
answer = 'Public repos : \n' + '\n'.join(['%s %s'%(name.ljust(max_width), desc) for name,(url,desc) in KNOWN_PUBLIC_REPOS.iteritems()])
answer += '\n' + '-'* 40 + '\n\nInstalled repos :\n'
repos = self.shelf.get('repos', {})
repos = self.internal_shelf.get('repos', {})
if not len(repos):
answer += 'No plugin repo has been installed, use !install to add one.'
return answer
......@@ -244,7 +242,7 @@ class ErrBot(JabberBot):
admin_only(mess)
directories = set()
args = args.split(' ')
repos = self.shelf.get('repos', {})
repos = self.internal_shelf.get('repos', {})
if 'all' in args or 'core' in args:
directories.add(os.path.dirname(__file__))
......
# This class just hold the global singleton for the bot outside of the plugin framework to avoid spurrious modules reloads
bot = None
\ No newline at end of file
......@@ -2,6 +2,7 @@ from itertools import chain
import logging
import sys
from botplugin import BotPlugin
from config import BOT_EXTRA_PLUGIN_DIR
__author__ = 'gbin'
......@@ -30,8 +31,9 @@ def update_plugin_places(list):
def activate_all_plugins():
for pluginInfo in simplePluginManager.getAllPlugins():
try:
logging.info('Activate plugin %s' % pluginInfo.name)
simplePluginManager.activatePluginByName(pluginInfo.name, "bots")
if not pluginInfo.is_activated:
logging.info('Activate plugin %s' % pluginInfo.name)
simplePluginManager.activatePluginByName(pluginInfo.name, "bots")
except:
logging.exception("Error loading %s" % pluginInfo.name)
......@@ -39,12 +41,14 @@ def activate_all_plugins():
def get_all_plugins():
return simplePluginManager.getAllPlugins()
def get_all_active_plugin_objects():
return [plug.plugin_object for plug in simplePluginManager.getAllPlugins() if plug.is_activated]
def get_all_active_plugin_names():
return map(lambda p:p.name, filter(lambda p:p.is_activated, simplePluginManager.getAllPlugins()))
def get_active_plugin_names():
def get_all_plugin_names():
return map(lambda p:p.name, simplePluginManager.getAllPlugins())
......@@ -58,7 +62,7 @@ def deactivate_plugin(name):
def activate_plugin(name):
if name in get_all_active_plugin_names():
return "Plugin already in active list"
if name not in get_active_plugin_names():
if name not in get_all_plugin_names():
return "I don't know this %s plugin" % name
simplePluginManager.activatePluginByName(name, "bots")
return "Plugin %s activated" % name
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment