--- /dev/null
+*.pyc
+*.log
+.*
--- /dev/null
+Ophion multibot.
+
+Config:
+ bot.py (top)
+ classes.py (class Cache)
--- /dev/null
+#!/usr/bin/python
+
+###
+### Ophion Bot
+### v1.00
+### Copyright 2012 John Runyon
+### <http://inaccurate.us/ophion/>
+###
+
+## 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 <identprefix><bot ID#>
+
+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'>:<bid>:<line>"
+ else: print "ERROR! <'IN'|'OUT'>:<bid>:<line>"
+ 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()
--- /dev/null
+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 "<User: %s (%d)>" % (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 "<Chan: %s>" % (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 "<Bot%d: %s>" % (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)
--- /dev/null
+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 <bid> <nick|#channel> <msg>', 'Sends a PRIVMSG (#channel) or NOTICE (nick).']
+def helpact(): return ['ADMINACT <bid> <nick|#channel> <msg>', 'Sends an ACTION (/me).']
--- /dev/null
+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): # :<server.tld> 354 <self> <host> <nick> <auth>
+ 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): # :<server.tld> 353 <self> = <#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 <nick|#auth>', 'Shows info about an account with the bot.']
+def helpwhoami(): return ['WHOAMI', 'Shows info about your account with the bot.']
--- /dev/null
+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> [<reason>]', 'Kicks <user>']
+def helpban(): return ['BAN <hostmask>', 'Bans <hostmask>. (Doesn\'t kick)']
--- /dev/null
+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> [<nick|#auth> [<level>]]")
+
+def helpchanlev():
+ return ['CHANLEV <#channel> [<nick|#auth> [<level>]]', 'Change or view access.']
--- /dev/null
+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> [<nick>]', 'Ops you, or <nick>.']
+def helpvoice():
+ return ['VOICE <#channel> [<nick>]', 'Voices you, or <nick>.']
+def helpdeop():
+ return ['DEOP <#channel> [<nick>]', 'Deops you, or <nick>.']
+def helpdevoice():
+ return ['DEVOICE <#channel> [<nick>]', 'Devoices you, or <nick>.']
+def helpdown():
+ return ['DOWN <#channel>', 'Removes your op/voice.']
--- /dev/null
+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> <line>', 'Does a /ME.']
+def helpact(): return ['ACT <#channel> <line>', 'Does a /ME <line>.']
--- /dev/null
+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 <code>', 'Runs Python <code> in exec, and prints the exception (if any).']
+def helpeval(): return ['EVAL <code>', 'Runs Python <code> in eval, and prints the return or exception.']
+def helpraw(): return ['RAW <bid> <line>', 'Sends <line> to the server from bot BID.']
--- /dev/null
+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 [<command>]', 'Requests help for a command, or shows a command list.']
+def helpshowcommands(): return ['SHOWCOMMANDS', 'Shows a command list.']
--- /dev/null
+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 <module>', 'Loads "modules/<module>.py".', 'modctrl']
+def helpunloadmod(): return ['UNLOADMOD <module>', 'Unloads "modules/<module>.py".']
+def helpreloadmod(): return ['RELOADMOD <module>', 'Reloads "modules/<module>.py".']
+def helplistmods(): return ['LISTMODS', 'List loaded modules.']
--- /dev/null
+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.']
--- /dev/null
+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 <location>', 'Gets weather data from Weather Underground']
+def helpw():
+ return ['W <location>', 'Alias for WEATHER.']
--- /dev/null
+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 <params>', '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"]
--- /dev/null
+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> <answer>', 'Attempt to answer a trivia question. You must be authed with the bot. If no reply, guess was incorrect.']
--- /dev/null
+{
+ "#ophion": {
+ "BiohZn": 4,
+ "DimeCadmium": 5
+ }
+}
\ No newline at end of file
--- /dev/null
+Where was DimeCadmium born? (State postal abbreviation)~TX
+Where does BiohZn live? (City, Country)~Vaasa, Finland
--- /dev/null
+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