started removing bullshit magic

This commit is contained in:
Luke Rogers 2013-10-02 13:33:17 +13:00
parent c6fd9e1f55
commit 7e4ea3a9ff
6 changed files with 44 additions and 59 deletions

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# we import bot as _bot for now, for legacy reasons # we import bot as _bot for now, for legacy reasons
from core import bot as _bot from core import bot as _bot
from core import loader, main
import os import os
import Queue import Queue
@ -16,20 +17,14 @@ print 'CloudBot REFRESH <http://git.io/cloudbotirc>'
bot = _bot.Bot("cloudbot") bot = _bot.Bot("cloudbot")
bot.logger.debug("Bot initalized.") bot.logger.debug("Bot initalized.")
# bootstrap the reloader
bot.logger.debug("Bootstrapping reloader.")
eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(),
os.path.join('core', 'reload.py'), 'exec'))
reload(init=True)
bot.logger.debug("Starting main loop.") bot.logger.debug("Starting main loop.")
while True: while True:
reload() # these functions only do things loader.reload(bot) # these functions only do things
for connection in bot.connections.itervalues(): for connection in bot.connections.itervalues():
try: try:
out = connection.out.get_nowait() out = connection.out.get_nowait()
main(connection, out) main.main(bot, connection, out)
except Queue.Empty: except Queue.Empty:
pass pass
while all(connection.out.empty() for connection in bot.connections.itervalues()): while all(connection.out.empty() for connection in bot.connections.itervalues()):

View file

@ -9,7 +9,7 @@
"ssl": false, "ssl": false,
"ignore_cert": true "ignore_cert": true
}, },
"nick": "MyCloudBot", "nick": "MyCloueqerdBot",
"user": "cloudbot", "user": "cloudbot",
"real_name": "CloudBot - http://git.io/cloudbotirc", "real_name": "CloudBot - http://git.io/cloudbotirc",
"channels": ["#cloudbot", "#cloudbot2"], "channels": ["#cloudbot", "#cloudbot2"],
@ -52,5 +52,6 @@
"rdio_key": "", "rdio_key": "",
"rdio_secret": "", "rdio_secret": "",
"steam_key": "" "steam_key": ""
} },
"disabled_plugins": []
} }

View file

@ -3,7 +3,7 @@ import logging
import re import re
import os import os
from core import config, irc from core import config, irc, loader
def clean_name(n): def clean_name(n):
@ -25,6 +25,11 @@ class Bot(object):
self.connections = {} self.connections = {}
self.connect() self.connect()
# run plugin loader
self.logger.debug("Bootstrapping reloader.")
loader.reload(self, init=True)
def connect(self): def connect(self):
"""connect to all the networks defined in the bot config""" """connect to all the networks defined in the bot config"""
for conf in self.config['connections']: for conf in self.config['connections']:

49
core/reload.py → core/loader.py Executable file → Normal file
View file

@ -3,8 +3,11 @@ import glob
import os import os
import re import re
import sys import sys
import Queue
import traceback import traceback
from core import main
if 'mtimes' not in globals(): if 'mtimes' not in globals():
mtimes = {} mtimes = {}
@ -31,40 +34,18 @@ def format_plug(plug, kind='', lpad=0):
return out return out
def reload(init=False): def reload(bot, init=False):
changed = False changed = False
if init: if init:
bot.plugs = collections.defaultdict(list) bot.plugins = collections.defaultdict(list)
bot.threads = {} bot.threads = {}
core_fileset = set(glob.glob(os.path.join("core", "*.py")))
for filename in core_fileset:
mtime = os.stat(filename).st_mtime
if mtime != mtimes.get(filename):
mtimes[filename] = mtime
changed = True
try:
eval(compile(open(filename, 'U').read(), filename, 'exec'),
globals())
except Exception:
traceback.print_exc()
if init: # stop if there's an error (syntax?) in a core
sys.exit() # script on startup
continue
if filename == os.path.join('core', 'reload.py'):
reload(init=init)
return
fileset = set(glob.glob(os.path.join('plugins', '*.py'))) fileset = set(glob.glob(os.path.join('plugins', '*.py')))
# remove deleted/moved plugins # remove deleted/moved plugins
for name, data in bot.plugs.iteritems(): for name, data in bot.plugins.iteritems():
bot.plugs[name] = [x for x in data if x[0]._filename in fileset] bot.plugins[name] = [x for x in data if x[0]._filename in fileset]
for filename in list(mtimes): for filename in list(mtimes):
if filename not in fileset and filename not in core_fileset: if filename not in fileset and filename not in core_fileset:
@ -72,7 +53,7 @@ def reload(init=False):
for func, handler in list(bot.threads.iteritems()): for func, handler in list(bot.threads.iteritems()):
if func._filename not in fileset: if func._filename not in fileset:
handler.stop() main.handler.stop()
del bot.threads[func] del bot.threads[func]
# compile new plugins # compile new plugins
@ -92,8 +73,8 @@ def reload(init=False):
continue continue
# remove plugins already loaded from this filename # remove plugins already loaded from this filename
for name, data in bot.plugs.iteritems(): for name, data in bot.plugins.iteritems():
bot.plugs[name] = [x for x in data bot.plugins[name] = [x for x in data
if x[0]._filename != filename] if x[0]._filename != filename]
for func, handler in list(bot.threads.iteritems()): for func, handler in list(bot.threads.iteritems()):
@ -104,10 +85,10 @@ def reload(init=False):
for obj in namespace.itervalues(): for obj in namespace.itervalues():
if hasattr(obj, '_hook'): # check for magic if hasattr(obj, '_hook'): # check for magic
if obj._thread: if obj._thread:
bot.threads[obj] = Handler(obj) bot.threads[obj] = main.Handler(bot, obj)
for type, data in obj._hook: for type, data in obj._hook:
bot.plugs[type] += [data] bot.plugins[type] += [data]
if not init: if not init:
print '### new plugin (type: %s) loaded:' % \ print '### new plugin (type: %s) loaded:' % \
@ -115,7 +96,7 @@ def reload(init=False):
if changed: if changed:
bot.commands = {} bot.commands = {}
for plug in bot.plugs['command']: for plug in bot.plugins['command']:
name = plug[1]['name'].lower() name = plug[1]['name'].lower()
if not re.match(r'^\w+$', name): if not re.match(r'^\w+$', name):
print '### ERROR: invalid command name "{}" ({})'.format(name, format_plug(plug)) print '### ERROR: invalid command name "{}" ({})'.format(name, format_plug(plug))
@ -128,7 +109,7 @@ def reload(init=False):
bot.commands[name] = plug bot.commands[name] = plug
bot.events = collections.defaultdict(list) bot.events = collections.defaultdict(list)
for func, args in bot.plugs['event']: for func, args in bot.plugins['event']:
for event in args['events']: for event in args['events']:
bot.events[event].append((func, args)) bot.events[event].append((func, args))
@ -151,7 +132,7 @@ def reload(init=False):
out += ' ' * (50 - len(out)) + ', '.join(names) out += ' ' * (50 - len(out)) + ', '.join(names)
print out print out
for kind, plugs in sorted(bot.plugs.iteritems()): for kind, plugs in sorted(bot.plugins.iteritems()):
if kind == 'command': if kind == 'command':
continue continue
print ' {}:'.format(kind) print ' {}:'.format(kind)

View file

@ -1,12 +1,14 @@
import thread import thread
import traceback import traceback
import Queue
import re
thread.stack_size(1024 * 512) # reduce vm size thread.stack_size(1024 * 512) # reduce vm size
class Input(dict): class Input(dict):
def __init__(self, conn, raw, prefix, command, params, def __init__(self, bot, conn, raw, prefix, command, params,
nick, user, host, mask, paraml, msg): nick, user, host, mask, paraml, msg):
chan = paraml[0].lower() chan = paraml[0].lower()
@ -84,8 +86,9 @@ def do_sieve(sieve, bot, input, func, type, args):
class Handler(object): class Handler(object):
"""Runs plugins in their own threads (ensures order)""" """Runs plugins in their own threads (ensures order)"""
def __init__(self, func): def __init__(self, func, bot):
self.func = func self.func = func
self.bot = bot
self.input_queue = Queue.Queue() self.input_queue = Queue.Queue()
thread.start_new_thread(self.start, ()) thread.start_new_thread(self.start, ())
@ -101,7 +104,7 @@ class Handler(object):
if uses_db: if uses_db:
db = db_conns.get(input.conn) db = db_conns.get(input.conn)
if db is None: if db is None:
db = bot.get_db_connection(input.conn) db = self.bot.get_db_connection(input.conn)
db_conns[input.conn] = db db_conns[input.conn] = db
input.db = db input.db = db
@ -119,8 +122,8 @@ class Handler(object):
self.input_queue.put(value) self.input_queue.put(value)
def dispatch(input, kind, func, args, autohelp=False): def dispatch(bot, input, kind, func, args, autohelp=False):
for sieve, in bot.plugs['sieve']: for sieve, in bot.plugins['sieve']:
input = do_sieve(sieve, bot, input, func, kind, args) input = do_sieve(sieve, bot, input, func, kind, args)
if input is None: if input is None:
return return
@ -135,7 +138,7 @@ def dispatch(input, kind, func, args, autohelp=False):
thread.start_new_thread(run, (func, input)) thread.start_new_thread(run, (func, input))
def match_command(command): def match_command(bot, command):
commands = list(bot.commands) commands = list(bot.commands)
# do some fuzzy matching # do some fuzzy matching
@ -148,13 +151,13 @@ def match_command(command):
return command return command
def main(conn, out): def main(bot, conn, out):
inp = Input(conn, *out) inp = Input(bot, conn, *out)
command_prefix = conn.conf.get('command_prefix', '.') command_prefix = conn.conf.get('command_prefix', '.')
# EVENTS # EVENTS
for func, args in bot.events[inp.command] + bot.events['*']: for func, args in bot.events[inp.command] + bot.events['*']:
dispatch(Input(conn, *out), "event", func, args) dispatch(bot, Input(bot, conn, *out), "event", func, args)
if inp.command == 'PRIVMSG': if inp.command == 'PRIVMSG':
# COMMANDS # COMMANDS
@ -170,23 +173,23 @@ def main(conn, out):
if m: if m:
trigger = m.group(1).lower() trigger = m.group(1).lower()
command = match_command(trigger) command = match_command(bot, trigger)
if isinstance(command, list): # multiple potential matches if isinstance(command, list): # multiple potential matches
input = Input(conn, *out) input = Input(conn, *out)
input.notice("Did you mean {} or {}?".format input.notice("Did you mean {} or {}?".format
(', '.join(command[:-1]), command[-1])) (', '.join(command[:-1]), command[-1]))
elif command in bot.commands: elif command in bot.commands:
input = Input(conn, *out) input = Input(bot, conn, *out)
input.trigger = trigger input.trigger = trigger
input.inp_unstripped = m.group(2) input.inp_unstripped = m.group(2)
input.inp = input.inp_unstripped.strip() input.inp = input.inp_unstripped.strip()
func, args = bot.commands[command] func, args = bot.commands[command]
dispatch(input, "command", func, args, autohelp=True) dispatch(bot, input, "command", func, args, autohelp=True)
# REGEXES # REGEXES
for func, args in bot.plugs['regex']: for func, args in bot.plugins['regex']:
m = args['re'].search(inp.lastparam) m = args['re'].search(inp.lastparam)
if m: if m:
input = Input(conn, *out) input = Input(conn, *out)

View file

@ -1,6 +1,6 @@
from util import hook, http from util import hook, http
from lib import pygeoip
import os.path import os.path
import pygeoip
import json import json
import gzip import gzip
from StringIO import StringIO from StringIO import StringIO