From: John Runyon Date: Tue, 16 Oct 2012 08:54:09 +0000 (-0500) Subject: Initialize repo X-Git-Url: https://jfr.im/git/z_archive/Ophion.git/commitdiff_plain/b069ba10b2669134c5e566c2a1f7e79d9ea0be6d Initialize repo --- b069ba10b2669134c5e566c2a1f7e79d9ea0be6d diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5aca241 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +*.log +.* diff --git a/README b/README new file mode 100644 index 0000000..6a13f54 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +Ophion multibot. + +Config: + bot.py (top) + classes.py (class Cache) diff --git a/bot.py b/bot.py new file mode 100755 index 0000000..be850a8 --- /dev/null +++ b/bot.py @@ -0,0 +1,281 @@ +#!/usr/bin/python + +### +### Ophion Bot +### v1.00 +### Copyright 2012 John Runyon +### +### + +## CONFIG +dbhost = 'localhost' +dbuser = 'bot' +dbpass = 'roboticism' +dbname = 'bot' +rootdir = '/home/ophion' +logfile = rootdir+'/output.log' +excfile = rootdir+'/exception.log' + +oidfile = '/home/ophion/.oidentd.conf' +identprefix = 'jobbig' # ident will be + +import socket, select, sys, os, signal, time +from threading import * +from traceback import print_exc + +import MySQLdb, MySQLdb.cursors + +from classes import * +from util import * + +cache = Cache() + +def quit(reason, restart=False): + cache.quitting = True + cache.mainBot.cmsg('fatal', "Shutting down because: %s" % (reason)) + time.sleep(1) + for bot in cache.bots.values(): + bot.disconnect() + for tkey in cache.timers.keys(): + cache.timers[tkey].cancel() + del cache.timers[tkey] + for modf in cache.modules: + unloadmod(modf, cache, True) + + if restart: + os.execv(sys.argv[0], sys.argv) + sys.exit(0) +def sigHandle(signum, stack): + quit("Signal: %d" % (signum)) +def online(bid): + return (bid in cache.bots and cache.bots[bid].online) +def toofew(bot, nick): # like: if len(params) < NEEDED: return toofew(bot, nick) + bot.msg(nick, "Too few parameters for that command.") + return False + +def sendRaws(): + for bot in cache.bots.values(): + bot.sendRaws() + if not cache.quitting: + t = Timer(1, sendRaws) + t.daemon = True + if 'raws' in cache.timers: del cache.timers['raws'] + cache.timers['raws'] = t + t.start() + +def connectBots(): + curs = cache.dbc.cursor() + curs.execute("SELECT 1") + curs.close() + + connected = 0 + for bot in cache.bots.values(): + if not bot.online: + ident = "%s%d" % (identprefix, bot.id) + oid = open(oidfile, 'w') + oid.write("global {\n\treply \"%s\"\n}" % ident) + oid.close() + if bot.connect(ident): + bot.cmsg('info', "Connected") + connected += 1 + if connected == 2: + break + + if not cache.quitting: + if connected < 2: interval = 300 + else: interval = 60 + t = Timer(interval, connectBots) + t.daemon = True + if 'conn' in cache.timers: del cache.timers['conn'] + cache.timers['conn'] = t + t.start() + + return connected +def makeBots(): + curs = cache.dbc.cursor() + curs.execute("SELECT id FROM bots") + row = curs.fetchone() + while row is not None: + bot = Bot(row['id']) + if row['id'] == 0: + cache.mainBot = bot + bot.mainbot = True + cache.bots[row['id']] = bot + row = curs.fetchone() + +def fillCache(): + curs = cache.dbc.cursor() + curs.execute("SELECT username, level FROM admins") + row = curs.fetchone() + while row is not None: + cache.admins[row['username']] = row['level'] + row = curs.fetchone() + curs.execute("SELECT chname FROM chans ORDER BY id LIMIT 2") + rows = curs.fetchall() + cache.ctrl = rows[0]['chname'] + cache.home = rows[1]['chname'] + curs.close() + makeBots() + +def parseCmd(bot, line, linepieces, msg, iscm): + hostmask = linepieces[0][1:] + fromnick = hostmask.split('!')[0] + target = linepieces[2] + pieces = msg.split() + cmd = pieces[0].upper() + if len(pieces) == 1: params = '' + else: params = ' '.join(pieces[1:]) + + ret = "" + + if cmd not in cache.cmds: + noaccess(bot, fromnick) + return ret + else: ci = cache.cmds[cmd] + + if not ci['isadmin']: + if fromnick not in cache.users: + cache.users[fromnick] = User(fromnick) + auth = cache.users[fromnick].auth + if not iscm: + if ci['reqchan']: + target = pieces.pop(1).lower() + params = ' '.join(pieces[1:]) + else: + target = fromnick + if target not in cache.chans: + bot.msg(fromnick, "No such channel.") + return ret + chan = cache.chans[target] + if auth not in chan.alist or chan.alist[auth] < ci['level']: + noaccess(bot, fromnick, True) + return ret + elif len(pieces)-1 < cache.cmds[cmd]['params']: + toofew(bot, fromnick) + return ret + else: + try: ret = cache.cmds[cmd]['func'](fromnick, target, params, chan.bot, cache) + except Exception as e: + print_exc(None, cache.excfile) + bot.msg(fromnick, "An error occurred, sorry! Try again later.") + cache.mainBot.cmsg('warn', "EXCEPTION! %r Caused by <%s> %s" % (e, fromnick, line)) + + else: # admin/global command + if fromnick in cache.users: glblevel = cache.users[fromnick].access + else: glblevel = 0 + if glblevel < ci['level']: + noaccess(bot, fromnick) + elif len(pieces)-1 < cache.cmds[cmd]['params']: + toofew(bot, fromnick) + else: + try: ret = cache.cmds[cmd]['func'](fromnick, target, params, bot, cache) + except Exception as e: + print_exc(None, cache.excfile) + bot.msg(fromnick, "An error occurred, sorry! Try again later.") + cache.mainBot.cmsg('warn', "EXCEPTION! %r Caused by <%s> %s" % (e, fromnick, line)) + return ret + +def _parse(bot, line): + pieces = line.split() + if pieces[1] == "PRIVMSG": + target = pieces[2] + msg = ' '.join(pieces[3:]).lstrip(':') + if msg[0] == cache.trigger: + return parseCmd(bot, line, pieces, msg[1:], True) + elif target == bot.nick: + return parseCmd(bot, line, pieces, msg, False) + elif pieces[1] == "JOIN": + nick = pieces[0][1:].split('!')[0] + host = pieces[0][1:].split('@')[1] + chname = pieces[2].lower() + if chname in cache.chans: + bot.joined(chname, nick) + elif pieces[1] == "PART": + nick = pieces[0][1:].split('!')[0] + chname = pieces[2].lower() + if chname in cache.chans: + bot.parted(chname, nick) + elif pieces[1] == "QUIT": + nick = pieces[0][1:].split('!')[0] + bot.quit(nick) + elif pieces[1] == "NICK": + fromnick = pieces[0][1:].split('!')[0] + tonick = pieces[2][1:] + cache.users[tonick] = cache.users[fromnick] + del cache.users[fromnick] + cache.users[tonick].nick = tonick + elif pieces[0] == "PING": + bot.rawnow("PONG %s" % (pieces[1])) + elif pieces[0] == "ERROR": + try: bot.disconnect() + except: pass + elif pieces[1] in cache.nums: + for fn in cache.nums[pieces[1]]: + try: fn(line, bot) + except Exception as e: + print_exc(None, cache.excfile) + bot.msg(fromnick, "An error occurred, sorry! Try again later.") + cache.mainBot.cmsg('warn', "EXCEPTION! %r Caused by <%s> %s" % (e, fromnick, line)) + +def parse(bot): + line = bot.get().strip() + return _parse(bot, line) + + +def loop(): + while True: + ready = cache.poll.poll() + for fde in ready: + ret = "" + if fde[0] == sys.stdin.fileno(): + line = sys.stdin.readline().strip() + pieces = line.split(None, 2) + if len(pieces) == 3 and pieces[1].isdigit(): + mode = pieces[0] + bid = int(pieces[1]) + line = pieces[2] + if mode.upper() == "IN": ret = _parse(cache.bots[bid], line) + elif mode.upper() == "OUT": cache.bots[bid].rawnow(line) + else: print "ERROR! <'IN'|'OUT'>::" + else: print "ERROR! <'IN'|'OUT'>::" + elif fde[0] in cache.botsByFD: # it's a bot + bot = cache.botsByFD[fde[0]] + if fde[1] & select.POLLERR or fde[1] & select.POLLHUP or fde[1] & select.POLLNVAL: + bot.disconnect() + else: + ret = parse(bot) + if ret == "QUIT": break + else: continue + break # if the "for" was broken, break the while as well. + + +signal.signal(signal.SIGHUP, signal.SIG_IGN) +signal.signal(signal.SIGINT, sigHandle) + +cache.excfile = open(excfile, 'a') + +sys.path.append(rootdir+'/modules') +sys.path.append(rootdir+'/modules/autoload') +for modf in os.listdir(rootdir+'/modules/autoload'): + if modf[-3:] == ".py": + loadmod(modf[:-3], cache) + +cache.dbc = MySQLdb.connect(host=dbhost, user=dbuser, passwd=dbpass, db=dbname, cursorclass=MySQLdb.cursors.DictCursor) +fillCache() + +cache.poll = select.poll() + +cache.poll.register(sys.stdin.fileno(), select.POLLIN) + +t = Timer(1, sendRaws) +t.daemon = True +cache.timers['raws'] = t +t.start() + +connectBots() +t = Timer(60, connectBots) +t.daemon = True +cache.timers['conn'] = t +t.start() + +loop() diff --git a/classes.py b/classes.py new file mode 100644 index 0000000..3c0838a --- /dev/null +++ b/classes.py @@ -0,0 +1,263 @@ +import socket, random, select +from collections import deque + +from util import * + +class User: + def __init__(self, nick): + self.nick = nick + self.auth = '' + self.access = 0 + self.alist = [] + self.commonchans = [] + self.host = None + def __str__(self): return self.nick + def __repr__(self): return "" % (self.nick, self.access) + def authed(self, username): + self.auth = username + if username in cache.admins: + self.access = cache.admins[username] + def joined(self, chan): + self.commonchans.append(chan) + def parted(self, chan): + if chan in self.commonchans: self.commonchans.remove(chan) + if self.commonchans == []: + self.auth = '' + self.access = 0 + +class Channel: + def __init__(self, name, bot, id): + self.id = id + self.name = name + self.bot = bot + + self.triggers = {} + + self.alist = {} + curs = cache.dbc.cursor() + curs.execute("SELECT authname, level FROM chusers WHERE chid = %s", (self.id,)) + row = curs.fetchone() + while row is not None: + self.alist[row['authname']] = row['level'] + row = curs.fetchone() + + self.ops = [] + self.voices = [] + self.users = [] + def joined(self, user): + self.users.append(user) + user.joined(self.name) + def parted(self, user): + if user in self.users: self.users.remove(user) + if user in self.ops: self.ops.remove(user) + if user in self.voices: self.voices.remove(user) + user.parted(self.name) + def opped(self, user): + self.ops.append(user) + def voiced(self, user): + self.voices.append(user) + def __str__(self): return self.name + def __repr__(self): return "" % (self.name) + +class Bot: + def __init__(self, id): + self.id = id + self.online = False + self.chans = {} + self.mainbot = False + self.rawqueue = deque() + + def get(self): + buf = "" + chin = self.s.recv(1) + while chin != "\n": + buf += chin + chin = self.s.recv(1) + buf = buf.strip() + print "<%d<%s" % (self.id, buf) + return buf + + def sendRaws(self, count=2): + if self.online: + for i in range(count): + try: line = self.rawqueue.popleft() + except IndexError: return + self.rawnow(line) + def raw(self, line): + self.rawqueue.append(line) + def rawnow(self, line): + self.s.sendall(line+"\r\n") + print ">%d>%s" % (self.id, line) + + def msg(self, target, msg): + msgs = msg.split("\n") + for msg in msgs: + if target[0] == '#': + self.raw("PRIVMSG %s :%s" % (target, msg)) + else: + self.raw("NOTICE %s :%s" % (target, msg)) + def cmsg(self, cmtype, msg, id=None): + if id is None: id = self.id + self.msg(cache.ctrl, cache.cmsgs[cmtype] % {'id': id, 'msg': msg}) + + def joined(self, chname, nick, chlevel=None): + chname = chname.lower() + if chname not in self.chans and chname not in cache.chans: + return + + self.raw("WHO %s n%%nah" % (nick)) + if nick not in cache.users: cache.users[nick] = User(nick) + self.chans[chname].joined(cache.users[nick]) + if chlevel == "@": + self.chans[chname].opped(cache.users[nick]) + if chlevel == "+": + self.chans[chname].voiced(cache.users[nick]) + def parted(self, chname, nick): + chname = chname.lower() + if nick == self.nick: + del self.chans[chname] + del cache.chans[chname] + elif chname in self.chans: + self.chans[chname].parted(cache.users[nick]) + def quit(self, nick): + if nick in cache.users: + user = cache.users[nick] + for ch in user.commonchans: + cache.chans[ch].parted(user) + del cache.users[nick] + + def disconnect(self): + try: cache.mainBot.cmsg('warn', 'Disconnected!', self.id) + except: pass + try: self.rawnow("QUIT :Disconnected") + except: pass + try: self.s.shutdown(socket.SHUT_RDWR) + except: pass + try: self.s.close() + except: pass + for ch in self.chans.values(): + for user in ch.users: + ch.parted(user) + cache.poll.unregister(self.s.fileno()) + self.online = False + + def joinChans(self): + curs = cache.dbc.cursor() + curs.execute("SELECT id, chname FROM chans WHERE botid = %s", (self.id,)) + row = curs.fetchone() + while row is not None: + self.raw("JOIN %s" % (row['chname'])) + chan = Channel(row['chname'].lower(), self, row['id']) + self.chans[row['chname'].lower()] = chan + cache.chans[row['chname']] = chan + row = curs.fetchone() + curs.close() + def connect(self, ident): + curs = cache.dbc.cursor() + curs.execute("SELECT irchost, ircport, ircpass, nick, vhost, realname, authname, authpass FROM bots WHERE id = %s", (self.id,)) + row = curs.fetchone() + self.nick = row['nick'] + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.s.bind((row['vhost'], 0)) + self.s.connect((row['irchost'], row['ircport'])) + self.rawnow("NICK %s" % (self.nick)) + self.rawnow("USER %s * * :%s" % (ident, row['realname'])) + curs.close() + while True: + line = self.get() + pieces = line.split() + if pieces[0] == "PING": + self.rawnow("PONG %s" % (pieces[1])) + elif pieces[1] == "433": + self.nick = self.nick+str(random.randrange(0,9)) + self.rawnow("NICK %s" % (self.nick)) + elif pieces[0] == "ERROR": + self.online = False + return False + elif pieces[1] == "376" or pieces[1] == "422": + break + if row['authname'] is not None and row['authpass'] is not None: + self.rawnow("AUTH %s %s" % (row['authname'], row['authpass'])) + self.rawnow("MODE %s +ix" % (self.nick)) + cache.poll.register(self.s.fileno(), select.POLLIN) + cache.botsByFD[self.s.fileno()] = self + cache.botsByNick[self.nick] = self + self.joinChans() + self.online = True + return True + def __str__(self): return self.nick + def __repr__(self): return "" % (self.id, self.nick) + +class Cache: + dbc = None + ls = None + admins = {} + bots = {} + botsByFD = {} + botsByNick = {} + mainBot = None + home = None # homechan + ctrl = None # ctrlchan + modules = {} + unmodules = {} # unloaded + timers = {} + quitting = False + + currmod = None + cmds = {} + nums = {} + + users = {} + chans = {} + + ## CONFIG + lshost = '0.0.0.0' + lsport = 13245 + triviapath = '/home/ophion/modules/trivia/' + trigger = '!' + cmsgs = { + 'debug': "\00303[\037DEBUG\037][%(id)d]: %(msg)s", + 'info': "\00312[\037INFO\037][%(id)d]: %(msg)s", + 'warn': "\00306[\037WARN\037][%(id)d]: %(msg)s", + 'fatal': "\00304[\037FATAL\037][%(id)d]: %(msg)s", + } + + def __init__(self): + global cache + cache = self + + def hooknum(self, num, func): + try: self.nums[num].append(func) + except: self.nums[num] = [func] + def unhooknum(self, num, func): + try: self.nums[num].remove(func) + except: return False + else: return True + + def hookcmd(self, cmd, level, func, params, helpfunc, isadmin=False, reqchan=True): + self.cmds[cmd.upper()] = {'module': cache.currmod, 'func': func, 'level': level, 'params': params, 'helpfunc': helpfunc, 'isadmin': isadmin, 'reqchan': reqchan} + def unhookcmd(self, cmd): + try: del self.cmds[cmd] + except: return False + else: return True + + def gethelp(self, cmd, nick=None, user=None, access=None): + if cmd in self.cmds: + if self.cmds[cmd]['level'] == 0: + return self.cmds[cmd]['helpfunc']() + if nick is None and user is None: + return self.cmds[cmd]['helpfunc']() + elif nick is not None and nick in self.users and self.users[nick].access > self.cmds[cmd]['level']: + return self.cmds[cmd]['helpfunc']() + elif user is not None and user.access > self.cmds[cmd]['level']: + return self.cmds[cmd]['helpfunc']() + elif access is not None and access > self.cmds[cmd]['level']: + return self.cmds[cmd]['helpfunc']() + return None + + def isbot(self, bid): + return (toint(bid) in self.bots) + def isonline(self, bid): + bid = toint(bid) + return (bid in self.bots and self.bots[bid].online) diff --git a/modules/autoload/adminmsg.py b/modules/autoload/adminmsg.py new file mode 100644 index 0000000..109585e --- /dev/null +++ b/modules/autoload/adminmsg.py @@ -0,0 +1,43 @@ +from classes import * +from util import * + +name = 'admin messaging' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('ADMINSEND', 2, send, 3, helpsend, isadmin=True) + cache.hookcmd('ADMINACT', 2, act, 3, helpact, isadmin=True) +def deinit(cache): + cache.currmod = __name__ + cache.unhookcmd('ADMINSEND') + cache.unhookcmd('ADMINACT') + +def send(nick, target, params, bot, cache): + pieces = params.split() + sbid = pieces[0] + bid = toint(sbid) + target = pieces[1] + msg = ' '.join(pieces[2:]) + if sbid == "*": + for bot in cache.bots.values(): + if bot.online: bot.msg(target, msg) + elif cache.isonline(bid): + cache.bots[bid].msg(target, msg) + else: + bot.msg(nick, "No such bot, or offline.") +def act(nick, target, params, bot, cache): + pieces = params.split() + sbid = pieces[0] + bid = toint(sbid) + target = pieces[1] + msg = ' '.join(pieces[2:]) + if sbid == "*": + for bot in cache.bots.values(): + if bot.online: bot.raw("PRIVMSG %s :\1ACTION %s\1" % (target, msg)) + elif cache.isonline(bid): + cache.bots[bid].raw("PRIVMSG %s :\1ACTION %s\1" % (target, msg)) + else: + bot.msg(nick, "No such bot, or offline.") + + +def helpsend(): return ['ADMINSEND ', 'Sends a PRIVMSG (#channel) or NOTICE (nick).'] +def helpact(): return ['ADMINACT ', 'Sends an ACTION (/me).'] diff --git a/modules/autoload/auth.py b/modules/autoload/auth.py new file mode 100644 index 0000000..76c10aa --- /dev/null +++ b/modules/autoload/auth.py @@ -0,0 +1,119 @@ +from classes import * +from util import * + +name = 'auth' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('AUTH', 0, auth, 0, helpauth, isadmin=True) + cache.hookcmd('WHOIS', 1, whois, 1, helpwhois, isadmin=True) + cache.hookcmd('WHOAMI', 0, whoami, 0, helpwhoami, isadmin=True) + cache.hooknum('354', rep_who) + cache.hooknum('353', rep_names) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('AUTH') + cache.unhookcmd('WHOIS') + cache.unhookcmd('WHOAMI') + cache.unhooknum('354', rep_who) + cache.unhooknum('353', rep_names) + +def auth(nick, target, params, bot, cache): + bot.raw("WHO %s n%%hna" % (nick)) + bot.msg(nick, "Looking up your auth...") + +def whois(nick, target, params, bot, cache): + lookup = params.split()[0] + if lookup[0] == '*': + if cache.users[nick].access >= 1: + lookup = lookup[1:] + bid = toint(lookup) + bot2 = None + if bid is None: + if lookup in cache.botsByNick: + bot2 = cache.botsByNick[lookup] + else: + if bid in bot2s: + bot2 = bot2s[bid] + if bot2 is not None: + bot.msg(nick, "Bot #%d ..." % (bot2.id)) + + if bot2.nick: bot.msg(nick, "- nick: %s" % (bot2.nick)) + else: bot.msg(nick, "- doesn't know it's nick") + + if bot2.online: bot.msg(nick, "- is online") + else: bot.msg(nick, "- is offline") + + if bot2.mainbot: bot.msg(nick, "- is the mainbot") + else: bot.msg(nick, "- is not the mainbot") + + if len(bot2.rawqueue) != 0: bot.msg(nick, "- has %d lines in queue" % (len(bot2.rawqueue))) + else: bot.msg(nick, "- has an empty queue") + + if len(bot2.chans) != 0: bot.msg(nick, "- in %s" % (' '.join([chan.name for chan in bot2.chans.values()]))) + else: bot.msg(nick, "- is in no channels.") + + bot.msg(nick, "End info for bot #%d" % (bot2.id)) + else: + bot.msg(nick, "No such bot %s" % (lookup)) + elif lookup[0] != '#': + if lookup in cache.users: + auth = cache.users[lookup].auth + access = cache.users[lookup].access + bot.msg(nick, "%s is #%s (access: %d)" % (lookup, auth, access)) + else: + bot.msg(nick, "%s is not a known user." % (lookup)) + return + else: + auth = lookup[1:] + curs = cache.dbc.cursor() + curs.execute("SELECT level FROM admins WHERE username = %s", (auth,)) + row = curs.fetchone() + if row is not None: + bot.msg(nick, "%s (access: %d)" % (lookup, row['level'])) + else: + bot.msg(nick, "%s is unknown." % (lookup)) + +def whoami(nick, target, params, bot, cache): + if nick in cache.users and cache.users[nick].access != -1: + bot.msg(nick, "You are %s (#%s access: %d)" % (nick, cache.users[nick].auth, cache.users[nick].access)) + + curs = cache.dbc.cursor() + curs.execute("SELECT chans.chname AS ch, chusers.level AS level FROM chans, chusers WHERE chans.id = chusers.chid AND chusers.authname = %s", (cache.users[nick].auth,)) + rows = curs.fetchall() + if len(rows) != 0: + bot.msg(nick, "-- CHANNELS:") + for row in rows: + bot.msg(nick, "%s - level %d" % (row['ch'], row['level'])) + bot.msg(nick, "-- END OF CHANNELS") + else: + bot.msg(nick, "You are %s (unknown auth)" % (nick)) + +def rep_who(line, bot): # : 354 + pieces = line.split() + host = pieces[3] + nick = pieces[4] + auth = pieces[5] + if nick not in cache.users: + cache.users[nick] = User(nick) + user = cache.users[nick] + user.host = host + cache.users[nick].authed(auth) + +def rep_names(line, bot): # : 353 = <#chan> :jrunyon @Ophion +BiohZn @Q +DimeCadmium + pieces = line.split() + chan = pieces[4] + nicksidx = line.find(':', 1) + nicks = line[nicksidx+1:].split() + for nick in nicks: + mode = nick[0] + if mode == '@' or mode == '+': + chlevel = mode + nick = nick[1:] + else: + chlevel = None + if nick != bot.nick: bot.joined(chan, nick, chlevel) + + +def helpauth(): return ['AUTH', 'Requests the bot to look up your account.'] +def helpwhois(): return ['WHOIS ', 'Shows info about an account with the bot.'] +def helpwhoami(): return ['WHOAMI', 'Shows info about your account with the bot.'] diff --git a/modules/autoload/chkick.py b/modules/autoload/chkick.py new file mode 100644 index 0000000..a443bb6 --- /dev/null +++ b/modules/autoload/chkick.py @@ -0,0 +1,26 @@ +from classes import * +from util import * + +name = 'channel kick' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('KICK', 3, kick, 1, helpkick) + cache.hookcmd('BAN', 3, ban, 1, helpban) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('KICK') + cache.unhookcmd('BAN') + +def kick(nick, target, params, bot, cache): + pieces = params.split(None, 1) + reason = "Requested by %s" % (nick) + if len(pieces) == 2: + reason = "%s (%s)" % (pieces[1], reason) + bot.raw("KICK %s %s :%s" % (target, pieces[0], reason)) + +def ban(nick, target, params, bot, cache): + bot.raw("MODE %s +b %s" % (target, pieces[0])) + bot.msg(nick, "Done.") + +def helpkick(): return ['KICK <#channel> []', 'Kicks '] +def helpban(): return ['BAN ', 'Bans . (Doesn\'t kick)'] diff --git a/modules/autoload/chlevel.py b/modules/autoload/chlevel.py new file mode 100644 index 0000000..57155f0 --- /dev/null +++ b/modules/autoload/chlevel.py @@ -0,0 +1,70 @@ +from classes import * +from util import * + +name = 'channel modes' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('CHANLEV', 1, chanlev, 0, helpchanlev) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('CHANLEV') + +def chanlev(nick, target, params, bot, cache): + pieces = params.split() + user = cache.users[nick] + chan = cache.chans[target] + + if len(pieces) == 0: + bot.msg(nick, "-- ACCESS LIST FOR %s" % (target)) + for authname, access in chan.alist.items(): + bot.msg(nick, "%-15s %s" % (authname, access)) + bot.msg(nick, "-- END OF ACCESS LIST") + elif len(pieces) == 1: + if pieces[0][0] == '#': + auth = pieces[0][1:] + bot.msg(nick, "Auth %s has level %s on %s" % (auth, chan.alist[auth], target)) + elif pieces[0] in cache.users: + auth = cache.users[pieces[0]].auth + bot.msg(nick, "Nick %s has level %s on %s" % (pieces[0], chan.alist[auth], target)) + else: + bot.msg(nick, "%s is unknown." % (pieces[0])) + elif len(pieces) == 2: + auth = user.auth + if auth in chan.alist and chan.alist[auth] >= 4: + level = chan.alist[auth] + if pieces[0][0] == '#': + targauth = pieces[0][1:] + elif pieces[0] in cache.users: + targauth = cache.users[pieces[0]].auth + else: + bot.msg(nick, "%s is unknown." % (pieces[0])) + return + targlev = toint(pieces[1]) + if targlev is None or targlev > 5: + bot.msg(nick, "Invalid level %d." % (targlev)) + return + if level != 5: + if targauth in chan.alist and chan.alist[targauth] >= level: + noaccess(bot, nick, True) + return + if targlev >= level: + noaccess(bot, nick, True) + return + if targlev != 0: + chan.alist[targauth] = targlev + curs = cache.dbc.cursor() + curs.execute("REPLACE INTO chusers(chid, authname, level) VALUES (%s, %s, %s)", (chan.id, targauth, targlev)) + curs.close() + elif targauth in chan.alist: + del chan.alist[targauth] + curs = cache.dbc.cursor() + curs.execute("DELETE FROM chusers WHERE chid = %s AND authname = '%s'" % (chan.id, targauth)) + curs.close() + bot.msg(nick, "Done.") + else: + noaccess(bot, nick, True) + else: + bot.msg(nick, "Invalid syntax. Usage: CHANLEV <#channel> [ []]") + +def helpchanlev(): + return ['CHANLEV <#channel> [ []]', 'Change or view access.'] diff --git a/modules/autoload/chmode.py b/modules/autoload/chmode.py new file mode 100644 index 0000000..ca137eb --- /dev/null +++ b/modules/autoload/chmode.py @@ -0,0 +1,125 @@ +from classes import * +from util import * + +name = 'channel modes' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('OP', 3, op, 0, helpop) + cache.hookcmd('VOICE', 2, voice, 0, helpvoice) + cache.hookcmd('DEOP', 0, deop, 0, helpdeop) + cache.hookcmd('DEVOICE', 0, devoice, 0, helpdevoice) + cache.hookcmd('DOWN', 0, down, 0, helpdown) + cache.hooknum('MODE', modehook) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('OP') + cache.unhookcmd('VOICE') + cache.unhookcmd('DEOP') + cache.unhookcmd('DEVOICE') + cache.unhookcmd('DOWN') + +def op(nick, target, params, bot, cache): + if params == '': opnick = nick + elif cache.chans[target].alist[cache.users[nick].auth] >= 4: opnick = params + else: + noaccess(bot, nick, True) + return + + user = cache.users[nick] + if user in cache.chans[target].ops: + bot.msg(nick, "%s is already opped on %s" % (opnick, target)) + elif user in cache.chans[target].users: + bot.raw("MODE %s +o %s" % (target, opnick)) + bot.msg(nick, "Done.") + else: + bot.msg(nick, "%s isn't on %s" % (opnick, target)) +def voice(nick, target, params, bot, cache): + if params == '': vnick = nick + elif cache.chans[target].alist[cache.users[nick].auth] >= 3: vnick = params + else: + noaccess(bot, nick, True) + return + + user = cache.users[nick] + if user in cache.chans[target].voices: + bot.msg(nick, "%s is already voiced on %s" % (vnick, target)) + elif user in cache.chans[target].users: + bot.raw("MODE %s +v %s" % (target, vnick)) + bot.msg(nick, "Done.") + else: + bot.msg(nick, "%s isn't on %s" % (vnick, target)) +def deop(nick, target, params, bot, cache): + if params == '': opnick = nick + elif cache.chans[target].alist[cache.users[nick].auth] >= 4: opnick = params + else: + noaccess(bot, nick, True) + return + + user = cache.users[nick] + if user in cache.chans[target].ops: + bot.raw("MODE %s -o %s" % (target, opnick)) + bot.msg(nick, "Done.") + elif user in cache.chans[target].users: + bot.msg(nick, "%s isn't opped on %s" % (opnick, target)) + else: + bot.msg(nick, "%s isn't on %s" % (opnick, target)) +def devoice(nick, target, params, bot, cache): + if params == '': vnick = nick + elif cache.chans[target].alist[cache.users[nick].auth] >= 3: vnick = params + else: + noaccess(bot, nick, True) + return + + user = cache.users[nick] + if user in cache.chans[target].voices: + bot.raw("MODE %s -v %s" % (target, vnick)) + bot.msg(nick, "Done.") + elif user in cache.chans[target].users: + bot.msg(nick, "%s isn't voiced on %s" % (vnick, target)) + else: + bot.msg(nick, "%s isn't on %s" % (vnick, target)) + +def down(nick, target, params, bot, cache): + user = cache.users[nick] + if user in cache.chans[target].users: + bot.raw("MODE %s -ov %s %s" % (target, nick, nick)) + else: + bot.msg(nick, "You aren't on %s" % (target)) + + +def modehook(line, bot): # :U!I@H MODE #chan +o-v DimeCadmium DimeCadmium + pieces = deque(line.split()[2:]) # #chan +o-v DimeCadmium DimeCadmium + chname = pieces.popleft().lower() + if chname[0] != '#': return # usermode change + if chname not in cache.chans: + return + chan = cache.chans[chname] # +o-v DimeCadmium DimeCadmium + modes = pieces.popleft() # DimeCadmium DimeCadmium + for i in modes: + if i == '+': + adding = True + elif i == '-': + adding = False + elif i == 'o': + nick = pieces.popleft() + if nick not in cache.users: cache.users[nick] = User(nick) + user = cache.users[nick] + if adding: chan.ops.append(user) + elif user in chan.ops: chan.ops.remove(user) + elif i == 'v': + nick = pieces.popleft() + if nick not in cache.users: cache.users[nick] = User(nick) + user = cache.users[nick] + if adding: chan.voices.append(user) + elif user in chan.voices: chan.voices.remove(user) + +def helpop(): + return ['OP <#channel> []', 'Ops you, or .'] +def helpvoice(): + return ['VOICE <#channel> []', 'Voices you, or .'] +def helpdeop(): + return ['DEOP <#channel> []', 'Deops you, or .'] +def helpdevoice(): + return ['DEVOICE <#channel> []', 'Devoices you, or .'] +def helpdown(): + return ['DOWN <#channel>', 'Removes your op/voice.'] diff --git a/modules/autoload/chmsg.py b/modules/autoload/chmsg.py new file mode 100644 index 0000000..cf2a062 --- /dev/null +++ b/modules/autoload/chmsg.py @@ -0,0 +1,21 @@ +from classes import * +from util import * + +name = 'channel messaging' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('SAY', 3, say, 1, helpsay) + cache.hookcmd('ACT', 3, act, 1, helpact) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('SAY') + cache.unhookcmd('ACT') + +def say(nick, target, params, bot, cache): + bot.msg(target, params) + +def act(nick, target, params, bot, cache): + bot.msg(target, "\001ACTION %s\001" % (params)) + +def helpsay(): return ['SAY <#channel> ', 'Does a /ME.'] +def helpact(): return ['ACT <#channel> ', 'Does a /ME .'] diff --git a/modules/autoload/debug.py b/modules/autoload/debug.py new file mode 100644 index 0000000..d9765b0 --- /dev/null +++ b/modules/autoload/debug.py @@ -0,0 +1,40 @@ +from classes import * +from util import * + +name = 'debug' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('EXEC', 8, doexec, 1, helpexec, isadmin=True) + cache.hookcmd('EVAL', 8, doeval, 1, helpeval, isadmin=True) + cache.hookcmd('RAW', 8, raw, 2, helpraw, isadmin=True) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('EXEC') + cache.unhookcmd('EVAL') + cache.unhookcmd('RAW') + +def doexec(nick, target, params, bot, cache): + try: exec(params) + except Exception as e: bot.msg(nick, "Exception! "+str(e)) + else: bot.msg(nick, "Done.") +def doeval(nick, target, params, bot, cache): + ret = None + try: ret = eval(params) + except Exception as e: bot.msg(nick, "Exception! "+str(e)) + else: bot.msg(nick, "Return: %s" % (ret)) +def raw(nick, target, params, bot, cache): + pieces = params.split() + if pieces[0] == "*": + for bot in cache.bots.values(): + if bot.online: bot.raw(' '.join(pieces[1:])) + else: + bid = toint(pieces[0]) + if cache.isonline(bid): + cache.bots[bid].raw(' '.join(pieces[1:])) + else: + bot.msg(nick, "No such bot, or offline.") + + +def helpexec(): return ['EXEC ', 'Runs Python in exec, and prints the exception (if any).'] +def helpeval(): return ['EVAL ', 'Runs Python in eval, and prints the return or exception.'] +def helpraw(): return ['RAW ', 'Sends to the server from bot BID.'] diff --git a/modules/autoload/help.py b/modules/autoload/help.py new file mode 100644 index 0000000..7b5d544 --- /dev/null +++ b/modules/autoload/help.py @@ -0,0 +1,45 @@ +from classes import * +from util import * + +name = 'help' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('HELP', 0, dohelp, 0, helphelp, isadmin=True) + cache.hookcmd('SHOWCOMMANDS', 0, showcommands, 0, helpshowcommands, isadmin=True) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('HELP') + cache.unhookcmd('SHOWCOMMANDS') + +def dohelp(nick, target, params, bot, cache): + if len(params) == 0: showcommands(nick, target, params, bot, cache) + else: + thehelp = cache.gethelp(params.upper(), nick) + if thehelp is not None: + bot.msg(nick, '-- HELP for %s' % (params)) + bot.msg(nick, 'Syntax: %s' % (thehelp[0])) + for theline in thehelp[1:]: + bot.msg(nick, theline) + bot.msg(nick, '-- End of HELP for %s' % (params)) + else: + bot.msg(nick, "HELP for %s not available: no such command or not enough access." % (params)) + +def showcommands(nick, target, params, bot, cache): + helps = [] + if nick in cache.users: + access = cache.users[nick].access + else: + access = 0 + bot.msg(nick, '-- COMMAND LIST') + for key in sorted(cache.cmds): + cmd = cache.cmds[key] + if access >= cmd['level']: + cmdhelp = cmd['helpfunc']() + if len(cmdhelp) == 3 and access >= 1: + bot.msg(nick, "%-20s %s [module: %s]" % (cmdhelp[0], cmdhelp[1], cmdhelp[2])) + else: + bot.msg(nick, "%-20s %s" % (cmdhelp[0], cmdhelp[1])) + bot.msg(nick, '-- End of COMMAND LIST') + +def helphelp(): return ['HELP []', 'Requests help for a command, or shows a command list.'] +def helpshowcommands(): return ['SHOWCOMMANDS', 'Shows a command list.'] diff --git a/modules/autoload/modctrl.py b/modules/autoload/modctrl.py new file mode 100644 index 0000000..cba4f8c --- /dev/null +++ b/modules/autoload/modctrl.py @@ -0,0 +1,49 @@ +from classes import * +from util import * + +name = 'module control' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('LOADMOD', 9, doloadmod, 1, helploadmod, isadmin=True) + cache.hookcmd('UNLOADMOD', 9, dounloadmod, 1, helpunloadmod, isadmin=True) + cache.hookcmd('RELOADMOD', 9, doreloadmod, 1, helpreloadmod, isadmin=True) + cache.hookcmd('LISTMODS', 8, dolistmods, 0, helplistmods, isadmin=True) +def deinit(cache, reloading): + if reloading: + cache.currmod = __name__ + cache.unhookcmd('LOADMOD') + cache.unhookcmd('UNLOADMOD') + cache.unhookcmd('RELOADMOD') + cache.unhookcmd('LISTMODS') + else: + return True # block unload + +def doloadmod(nick, target, params, bot, cache): + modf = params.split()[0] + retcode = loadmod(modf, cache) + if retcode == 0: bot.msg(nick, "Loaded %s successfully." % (modf)) + elif retcode == 1: bot.msg(nick, "Error loading %s: import error." % (modf)) + elif retcode == 2: bot.msg(nick, "Error loading %s: already loaded." % (modf)) +def dounloadmod(nick, target, params, bot, cache): + modf = params.split()[0] + retcode = unloadmod(modf, cache) + if retcode == 0: bot.msg(nick, "Unloaded %s successfully." % (modf)) + elif retcode == 1: bot.msg(nick, "Error unloading %s: module refused unload." % (modf)) + elif retcode == 2: bot.msg(nick, "Error unloading %s: not loaded." % (modf)) +def doreloadmod(nick, target, params, bot, cache): + modf = params.split()[0] + retcode = reloadmod(modf, cache) + if retcode == 0: bot.msg(nick, "Reloaded %s successfully." % (modf)) + else: bot.msg(nick, "Error reloading %s: %d" % (modf, retcode)) + +def dolistmods(nick, target, params, bot, cache): + bot.msg(nick, "-- LISTMODS") + for modf in cache.modules.keys(): + mod = cache.modules[modf] + bot.msg(nick, "%-20s - %s" % (modf, mod.name)) + bot.msg(nick, "-- END LISTMODS") + +def helploadmod(): return ['LOADMOD ', 'Loads "modules/.py".', 'modctrl'] +def helpunloadmod(): return ['UNLOADMOD ', 'Unloads "modules/.py".'] +def helpreloadmod(): return ['RELOADMOD ', 'Reloads "modules/.py".'] +def helplistmods(): return ['LISTMODS', 'List loaded modules.'] diff --git a/modules/autoload/quit.py b/modules/autoload/quit.py new file mode 100644 index 0000000..ab4abf2 --- /dev/null +++ b/modules/autoload/quit.py @@ -0,0 +1,15 @@ +from classes import * +from util import * + +name = 'quit' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('DIE', 8, die, 0, helpdie, isadmin=True) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('DIE') + +def die(nick, target, params, bot, cache): + return "QUIT" + +def helpdie(): return ['DIE', 'Kills the bots.'] diff --git a/modules/autoload/weather.py b/modules/autoload/weather.py new file mode 100644 index 0000000..452ba5b --- /dev/null +++ b/modules/autoload/weather.py @@ -0,0 +1,36 @@ +from classes import * +from util import * + +import httplib, json + +apikey = '6117a56225f311c3' + +name = 'weather underground' +def init(cache): + cache.currmod = __name__ + cache.hookcmd('WEATHER', 0, weather, 1, helpweather, reqchan=False) + cache.hookcmd('W', 0, weather, 1, helpw, reqchan=False) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('WEATHER') + cache.unhookcmd('W') + +def weather(nick, target, params, bot, cache): + location = params.strip().replace(' ', '_') + conn = httplib.HTTPConnection("api.wunderground.com") + conn.request("GET", "/api/%s/conditions/q/%s.json" % (apikey, location)) + res = conn.getresponse() + if res.status == 200: + data = res.read() + wz = json.loads(data) + if 'current_observation' in wz: + wz = wz['current_observation'] + buf = "Weather for %s, %s: %s - Feels like: %s - Conditions: %s - Humidity: %s. %s" % (wz['display_location']['full'], wz['display_location']['country_iso3166'], wz['temperature_string'], wz['feelslike_string'], wz['weather'], wz['relative_humidity'], wz['observation_time']) + bot.msg(target, buf) + return + bot.msg(target, "Error retrieving weather.") + +def helpweather(): + return ['WEATHER ', 'Gets weather data from Weather Underground'] +def helpw(): + return ['W ', 'Alias for WEATHER.'] diff --git a/modules/sample.py b/modules/sample.py new file mode 100644 index 0000000..dd43f2b --- /dev/null +++ b/modules/sample.py @@ -0,0 +1,38 @@ +from classes import * +from util import * + +name = 'foobar' +author = 'John Runyon' +version = '1' +def init(cache): + cache.currmod = __name__ +#cache.hookcmd('COMMAND',level,cmdfn,params,helpfn,isadmin=False,reqchan=True) + cache.hookcmd('FOO', 0, foo, 0, helpfoo) + cache.hookcmd('BAR', 1, bar, 0, helpbar, isadmin=True) + cache.hookcmd('BAZ', 0, baz, 0, helpbaz, reqchan=False) + cache.hookcmd('HELLO', 0, hello, 1, helphello) +def deinit(cache, reloading=False): + cache.currmod = __name__ + cache.unhookcmd('FOO') + cache.unhookcmd('BAR') + cache.unhookcmd('BAZ') + cache.unhookcmd('HELLO') + +def foo(nick, target, params, bot, cache): + bot.msg(nick, "Foo to you too! (nick)") + bot.msg(target, "Foo to you too! (target)") +def bar(nick, target, params, bot, cache): + bot.msg(nick, "Bar to you too! (nick)") + bot.msg(target, "Bar to you too! (target)") +def baz(nick, target, params, bot, cache): + bot.msg(nick, "Baz to you too! (nick)") + bot.msg(target, "Baz to you too! (target)") +def hello(nick, target, params, bot, cache): + bot.msg(nick, "Hello, %s! (nick)" % (params)) + bot.msg(target, "Hello, %s! (target" % (params)) + +# ['COMMAND ', 'Help description'] +def helpfoo(): return ['FOO <#channel>', "'FOO', 0, foo, 0, helpfoo"] +def helpbar(): return ['BAR', "'BAR', 1, bar, 0, helpbar, isadmin=True"] +def helpbaz(): return ['BAZ', "'BAZ', 0, baz, 0, helpbaz, reqchan=False"] +def helphello(): return ['HELLO', "'HELLO', 0, hello, 1, helphello"] diff --git a/modules/trivia.py b/modules/trivia.py new file mode 100644 index 0000000..0cb89f5 --- /dev/null +++ b/modules/trivia.py @@ -0,0 +1,119 @@ +from classes import * +from util import * + +import time, os, random, json +from threading import Timer +from traceback import print_exc + +name = 'trivia' +author = 'John Runyon' +version = '1' + +questions = [] +points = {} + +aqinterval = 10 # seconds from question end -> next question +qhinterval = 30 # seconds from question prompt -> hint +hainterval = 30 # seconds from hint -> question end + +def init(cache): + global questions, points + cache.currmod = __name__ + cache.trivia = {} + + try: + qfile = open(cache.triviapath+"/questions.txt", 'r') + except IOError as e: + print_exc(None, cache.excfile) + return True + questions = qfile.readlines() + qfile.close() + + try: + ptsfile = open(cache.triviapath+"/points.json", 'r') + except IOError as e: + print_exc(None, cache.excfile) + return True + points = json.load(ptsfile) + ptsfile.close() + + cache.hookcmd('TRIVIA', 1, trivia, 1, helptrivia) + cache.hookcmd('T', 0, t, 1, helpt) +def deinit(cache, reloading=False): + global questions, points + cache.currmod = __name__ + + ptsfile = open(cache.triviapath+"/points.json", 'w') + json.dump(points, ptsfile, indent=4) + ptsfile.close() + + del questions + del points + del cache.trivia + cache.unhookcmd('TRIVIA') + +def _loadQuestion(): + tdict = {} + line = random.choice(questions) + parts = line.split('~', 2) + tdict['question'] = parts[0] + tdict['answer'] = parts[1].lower() + if len(parts) == 3: tdict['flags'] = parts[2].split('~') + else: tdict['flags'] = [] + return tdict +def _sendQuestion(cache, chan): + tdict = cache.trivia[chan]['tdict'] + cache.chans[chan].bot.msg(chan, "Next question: %s" % (tdict['question'])) + timer = Timer(qhinterval, _sendHint, kwargs={'cache': cache, 'chan': chan}) + cache.trivia[chan]['timer'] = timer + cache.trivia[chan]['timertype'] = '_sendHint' +def _sendHint(cache, chan): + tdict = cache.trivia[chan]['tdict'] + pieces = tdict['answer'].split(' ') + hintpieces = [] + for piece in pieces: + if piece == '': continue + plen = len(piece) + reveal = int(round(plen*0.40)) + if reveal < 2: reveal = 2 + if reveal > plen: reveal = plen + + revealpos = [] + for i in range(reveal): + pos = random.randint(0, plen-1) + while pos in revealpos: + pos = random.randint(0, plen-1) + revealpos.append(pos) + hiddenpieces = [] + for i in range(plen): + if i in revealpos: + hiddenpieces.append(piece[i]) + elif not piece[i].isalnum(): + hiddenpieces.append(piece[i]) + else: + hiddenpieces.append('*') + hintpieces.append(''.join(hiddenpieces)) + hint = ' '.join(hintpieces) + cache.chans[chan].bot.msg(chan, "Hint: %s" % (hint)) + +def trivia(nick, target, params, bot, cache): + chan = target.lower() + if params == 'start': + tdict = _loadQuestion() + timer = Timer(aqinterval, _sendQuestion, kwargs={'cache': cache, 'chan': chan}) + cache.trivia[chan] = {'tdict': tdict, 'timer': timer, 'timertype': '_sendQuestion'} + elif params == 'stop': + cache.trivia[chan]['timer'].cancel() + del cache.trivia[chan] +def t(nick, target, params, bot, cache): + chan = target.lower() + guess = params.lower() + if chan not in cache.trivia: + bot.msg(nick, "Trivia isn't running in %s!" % (chan)) + elif nick not in cache.users or cache.users[nick] == '': + bot.msg(nick, "You must be AUTH'ed and recognized by me! Try !AUTH") + else: + pass #TODO + +def helptrivia(): return ['TRIVIA <#channel> start|stop', 'Starts or stops trivia.'] +def helpt(): return ['T <#channel> ', 'Attempt to answer a trivia question. You must be authed with the bot. If no reply, guess was incorrect.'] diff --git a/modules/trivia/points.json b/modules/trivia/points.json new file mode 100644 index 0000000..9344ab1 --- /dev/null +++ b/modules/trivia/points.json @@ -0,0 +1,6 @@ +{ + "#ophion": { + "BiohZn": 4, + "DimeCadmium": 5 + } +} \ No newline at end of file diff --git a/modules/trivia/questions.txt b/modules/trivia/questions.txt new file mode 100644 index 0000000..3d364b8 --- /dev/null +++ b/modules/trivia/questions.txt @@ -0,0 +1,2 @@ +Where was DimeCadmium born? (State postal abbreviation)~TX +Where does BiohZn live? (City, Country)~Vaasa, Finland diff --git a/util.py b/util.py new file mode 100644 index 0000000..84a5bc0 --- /dev/null +++ b/util.py @@ -0,0 +1,79 @@ +from traceback import print_exc + +def toint(s): + i = None + try: i = int(s) + except: pass + return i + +def noaccess(bot, nick, chcmd=False): + if chcmd: + bot.msg(nick, "You don't have enough access to do that.") + else: + bot.msg(nick, "No such command or not enough access.") + +# except Exception as e: +# print_exc(None, cache.excfile) +# bot.msg(fromnick, "An error occurred, sorry! Try again later.") +# cache.mainBot.cmsg('warn', "EXCEPTION! %r Caused by <%s> %s" % (e, fromnick, line)) +def loadmod(modf, cache, reloading=False): + # return: 0=success, 1=import error, 2=already loaded, 3=reload() failed + if modf in cache.modules: + return 2 + elif modf in cache.unmodules: + mod = cache.unmodules[modf] + try: + reload(mod) + except Exception as e: + print "! reload" + print_exc(None, cache.excfile) + return 1 + try: + if mod.init(cache): + print "not mod.init(cache)" + cache.excfile.write("%s refused init - returned True\n" % (modf)) + return 1 + except Exception as e: + print "! init" + print_exc(None, cache.excfile) + return 1 + del cache.unmodules[modf] + else: + try: + mod = __import__(modf) + except Exception as e: + print "! import" + print_exc(None, cache.excfile) + return 1 + try: + if mod.init(cache): + cache.excfile.write("%s refused init - returned True\n" % (modf)) + except Exception as e: + print "! init" + print_exc(None, cache.excfile) + return 1 + cache.modules[modf] = mod + return 0 +def unloadmod(modf, cache, reloading=False): + # return: 0=success, 1=refused unload, 2=not loaded + if modf not in cache.modules: + return 2 + else: + try: + ret = cache.modules[modf].deinit(cache, reloading) + except Exception as e: + print_exc(None, cache.excfile) + return 1 + if ret: + return 1 + else: + cache.unmodules[modf] = cache.modules[modf] + del cache.modules[modf] + return 0 +def reloadmod(modf, cache): + # return: 0 = success; 1,2=unload error; 3,4,5=load error + unloadret = unloadmod(modf, cache, True) + if unloadret != 0: return unloadret + loadret = loadmod(modf, cache, True) + if loadret != 0: return loadret+2 + return 0