diff --git a/bot.py b/bot.py index cdd5bb3..2f5f37c 100755 --- a/bot.py +++ b/bot.py @@ -5,9 +5,9 @@ import Queue import sys import time -sys.path += ['plugins'] # so 'import hook' works without duplication +sys.path += ['plugins'] # so 'import hook' works without duplication sys.path += ['lib'] -os.chdir(sys.path[0] or '.') # do stuff relative to the installation directory +os.chdir(sys.path[0] or '.') # do stuff relative to the install directory class Bot(object): @@ -19,7 +19,7 @@ bot = Bot() print 'Loading plugins' # bootstrap the reloader -eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(), +eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(), os.path.join('core', 'reload.py'), 'exec')) reload(init=True) @@ -30,12 +30,12 @@ bot.conns = {} try: for name, conf in bot.config['connections'].iteritems(): if conf.get('ssl'): - bot.conns[name] = SSLIRC(conf['server'], conf['nick'], - port=conf.get('port', 6667), channels=conf['channels'], conf=conf, + bot.conns[name] = SSLIRC(conf['server'], conf['nick'], conf=conf, + port=conf.get('port', 6667), channels=conf['channels'], ignore_certificate_errors=conf.get('ignore_cert', True)) else: - bot.conns[name] = IRC(conf['server'], conf['nick'], - port=conf.get('port', 6667), channels=conf['channels'], conf=conf) + bot.conns[name] = IRC(conf['server'], conf['nick'], conf=conf, + port=conf.get('port', 6667), channels=conf['channels']) except Exception, e: print 'ERROR: malformed config file', Exception, e sys.exit() @@ -47,8 +47,8 @@ if not os.path.exists(bot.persist_dir): print 'Running main loop' while True: - reload() # these functions only do things - config() # if changes have occured + reload() # these functions only do things + config() # if changes have occured for conn in bot.conns.itervalues(): try: diff --git a/core/config.py b/core/config.py index 3eb51a5..6f87ed3 100644 --- a/core/config.py +++ b/core/config.py @@ -2,8 +2,10 @@ import inspect import json import os + def load(): - return + return + def save(conf): json.dump(conf, open('config', 'w'), sort_keys=True, indent=2) @@ -26,7 +28,8 @@ if not os.path.exists('config'): bot.config = json.load(open('config')) bot._config_mtime = os.stat('config').st_mtime + def config(): # reload config from file if file has changed if bot._config_mtime != os.stat('config').st_mtime: - bot.config = json.load(open('config')) + bot.config = json.load(open('config')) diff --git a/core/db.py b/core/db.py index 4230139..1f7209f 100644 --- a/core/db.py +++ b/core/db.py @@ -1,9 +1,10 @@ import os import sqlite3 + def get_db_connection(server, name='skybot.%s.db'): "returns an sqlite3 connection to a persistent database" filename = os.path.join(bot.persist_dir, name % server) return sqlite3.connect(filename, timeout=10) -bot.get_db_connection = get_db_connection \ No newline at end of file +bot.get_db_connection = get_db_connection diff --git a/core/irc.py b/core/irc.py index 8a0e124..8533cc5 100644 --- a/core/irc.py +++ b/core/irc.py @@ -7,6 +7,7 @@ import Queue from ssl import wrap_socket, CERT_NONE, CERT_REQUIRED, SSLError + def decode(txt): for codec in ('utf-8', 'iso-8859-1', 'shift_jis', 'cp1252'): try: @@ -15,14 +16,15 @@ def decode(txt): continue return txt.decode('utf-8', 'ignore') + class crlf_tcp(object): "Handles tcp connections that consist of utf-8 lines ending with crlf" def __init__(self, host, port, timeout=300): self.ibuffer = "" self.obuffer = "" - self.oqueue = Queue.Queue() # lines to be sent out - self.iqueue = Queue.Queue() # lines that were received + self.oqueue = Queue.Queue() # lines to be sent out + self.iqueue = Queue.Queue() # lines that were received self.socket = self.create_socket() self.host = host self.port = port @@ -30,7 +32,7 @@ class crlf_tcp(object): def create_socket(self): return socket.socket(socket.AF_INET, socket.TCP_NODELAY) - + def run(self): self.socket.connect((self.host, self.port)) thread.start_new_thread(self.recv_loop, ()) @@ -38,10 +40,10 @@ class crlf_tcp(object): def recv_from_socket(self, nbytes): return self.socket.recv(nbytes) - + def get_timeout_exception_type(self): return socket.timeout - + def handle_receive_exception(self, error, last_timestamp): if time.time() - last_timestamp > self.timeout: self.iqueue.put(StopIteration) @@ -81,28 +83,30 @@ class crlf_tcp(object): sent = self.socket.send(self.obuffer) self.obuffer = self.obuffer[sent:] + class crlf_ssl_tcp(crlf_tcp): "Handles ssl tcp connetions that consist of utf-8 lines ending with crlf" def __init__(self, host, port, ignore_cert_errors, timeout=300): self.ignore_cert_errors = ignore_cert_errors crlf_tcp.__init__(self, host, port, timeout) - + def create_socket(self): - return wrap_socket(crlf_tcp.create_socket(self), server_side=False, - cert_reqs = [CERT_REQUIRED, CERT_NONE][self.ignore_cert_errors]) - + return wrap_socket(crlf_tcp.create_socket(self), server_side=False, + cert_reqs=CERT_NONE if self.ignore_cert_errors else + CERT_REQUIRED) + def recv_from_socket(self, nbytes): return self.socket.read(nbytes) def get_timeout_exception_type(self): return SSLError - + def handle_receive_exception(self, error, last_timestamp): # this is terrible if not "timed out" in error.args[0]: raise return crlf_tcp.handle_receive_exception(self, error, last_timestamp) - + irc_prefix_rem = re.compile(r'(.*?) (.*?) (.*)').match irc_noprefix_rem = re.compile(r'()(.*?) (.*)').match irc_netmask_rem = re.compile(r':?([^!@]*)!?([^@]*)@?(.*)').match @@ -119,7 +123,7 @@ class IRC(object): self.port = port self.nick = nick - self.out = Queue.Queue() #responses from the server are placed here + self.out = Queue.Queue() # responses from the server are placed here # format: [rawline, prefix, command, params, # nick, user, host, paramlist, msg] self.connect() @@ -128,7 +132,7 @@ class IRC(object): def create_connection(self): return crlf_tcp(self.server, self.port) - + def connect(self): self.conn = self.create_connection() thread.start_new_thread(self.conn.run, ()) @@ -146,7 +150,7 @@ class IRC(object): self.connect() continue - if msg.startswith(":"): #has a prefix + if msg.startswith(":"): # has a prefix prefix, command, params = irc_prefix_rem(msg).groups() else: prefix, command, params = irc_noprefix_rem(msg).groups() @@ -178,13 +182,14 @@ class IRC(object): def cmd(self, command, params=None): if params: params[-1] = ':' + params[-1] - self.send(command+' '+' '.join(params)) + self.send(command + ' ' + ' '.join(params)) else: self.send(command) def send(self, str): self.conn.oqueue.put(str) + class FakeIRC(IRC): def __init__(self, server, nick, port=6667, channels=[], conf={}, fn=""): self.channels = channels @@ -193,7 +198,7 @@ class FakeIRC(IRC): self.port = port self.nick = nick - self.out = Queue.Queue() #responses from the server are placed here + self.out = Queue.Queue() # responses from the server are placed here self.f = open(fn, 'rb') @@ -207,7 +212,7 @@ class FakeIRC(IRC): print "!!!!DONE READING FILE!!!!" return - if msg.startswith(":"): #has a prefix + if msg.startswith(":"): # has a prefix prefix, command, params = irc_prefix_rem(msg).groups() else: prefix, command, params = irc_noprefix_rem(msg).groups() @@ -225,12 +230,13 @@ class FakeIRC(IRC): def cmd(self, command, params=None): pass - + + class SSLIRC(IRC): - def __init__(self, server, nick, port=6667, channels=[], conf={}, + def __init__(self, server, nick, port=6667, channels=[], conf={}, ignore_certificate_errors=True): self.ignore_cert_errors = ignore_certificate_errors IRC.__init__(self, server, nick, port, channels, conf) - + def create_connection(self): return crlf_ssl_tcp(self.server, self.port, self.ignore_cert_errors) diff --git a/core/main.py b/core/main.py index 70026eb..8ca6f60 100644 --- a/core/main.py +++ b/core/main.py @@ -1,12 +1,13 @@ import thread import traceback + class Input(dict): def __init__(self, conn, raw, prefix, command, params, nick, user, host, paraml, msg): - + chan = paraml[0].lower() - if chan == conn.nick: # is a PM + if chan == conn.nick: # is a PM chan = nick def say(msg): @@ -20,9 +21,9 @@ class Input(dict): dict.__init__(self, conn=conn, raw=raw, prefix=prefix, command=command, params=params, nick=nick, user=user, host=host, - paraml=paraml, msg=msg, server=conn.server, chan=chan, + paraml=paraml, msg=msg, server=conn.server, chan=chan, say=say, reply=reply, pm=pm, bot=bot) - self.__dict__ = self # permits attribute access to values + self.__dict__ = self # permits attribute access to values def run(func, input): diff --git a/core/reload.py b/core/reload.py index 80510bf..54ee9b0 100644 --- a/core/reload.py +++ b/core/reload.py @@ -10,12 +10,14 @@ if 'mtimes' not in globals(): if 'lastfiles' not in globals(): lastfiles = set() + def format_plug(plug, lpad=0, width=40): out = ' ' * lpad + '%s:%s:%s' % (plug[0]) if len(plug) == 3 and 'hook' in plug[2]: out += '%s%s' % (' ' * (width - len(out)), plug[2]['hook']) return out + def reload(init=False): if init: bot.plugs = collections.defaultdict(lambda: []) @@ -25,12 +27,12 @@ def reload(init=False): if mtime != mtimes.get(filename): mtimes[filename] = mtime try: - eval(compile(open(filename, 'U').read(), filename, 'exec'), + eval(compile(open(filename, 'U').read(), filename, 'exec'), globals()) except Exception: traceback.print_exc(Exception) if init: # stop if there's a syntax error in a core - sys.exit() # script on startup + sys.exit() # script on startup continue if filename == os.path.join('core', 'reload.py'): @@ -38,7 +40,7 @@ def reload(init=False): return fileset = set(glob.glob(os.path.join('plugins', '*py'))) - for name, data in bot.plugs.iteritems(): # remove deleted/moved plugins + for name, data in bot.plugs.iteritems(): # remove deleted/moved plugins bot.plugs[name] = filter(lambda x: x[0][0] in fileset, data) for filename in fileset: @@ -56,7 +58,7 @@ def reload(init=False): # remove plugins already loaded from this filename for name, data in bot.plugs.iteritems(): - if name == 'tee': # signal tee trampolines to stop + if name == 'tee': # signal tee trampolines to stop for csig, func, args in data: if csig[0] == filename: func._iqueue.put(StopIteration) @@ -64,7 +66,7 @@ def reload(init=False): bot.plugs[name] = filter(lambda x: x[0][0] != filename, data) for obj in namespace.itervalues(): - if hasattr(obj, '_skybot_hook'): #check for magic + if hasattr(obj, '_skybot_hook'): # check for magic for type, data in obj._skybot_hook: bot.plugs[type] += [data] diff --git a/plugins/babel.py b/plugins/babel.py index f12c3a6..ea3b222 100644 --- a/plugins/babel.py +++ b/plugins/babel.py @@ -7,8 +7,8 @@ from util import hook ########### from http://effbot.org/zone/re-sub.htm#unescape-html ############# -def unescape(text): +def unescape(text): def fixup(m): text = m.group(0) if text[:2] == "&#": @@ -26,7 +26,7 @@ def unescape(text): text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) except KeyError: pass - return text # leave as is + return text # leave as is return re.sub("&#?\w+;", fixup, text) diff --git a/plugins/bf.py b/plugins/bf.py index 610fc19..42a135f 100644 --- a/plugins/bf.py +++ b/plugins/bf.py @@ -21,8 +21,8 @@ def bf(inp): program = re.sub('[^][<>+-.,]', '', inp) # create a dict of brackets pairs, for speed later on - brackets={} - open_brackets=[] + brackets = {} + open_brackets = [] for pos in range(len(program)): if program[pos] == '[': open_brackets.append(pos) @@ -40,9 +40,9 @@ def bf(inp): ip = 0 # instruction pointer mp = 0 # memory pointer steps = 0 - memory = [0] * BUFFER_SIZE #initial memory area + memory = [0] * BUFFER_SIZE # initial memory area rightmost = 0 - output = "" #we'll save the output here + output = "" # we'll save the output here # the main program loop: while ip < len(program): @@ -57,7 +57,7 @@ def bf(inp): rightmost = mp if mp >= len(memory): # no restriction on memory growth! - memory.extend([0]*BUFFER_SIZE) + memory.extend([0] * BUFFER_SIZE) elif c == '<': mp = mp - 1 % len(memory) elif c == '.': diff --git a/plugins/choose.py b/plugins/choose.py index 3c6788e..971397b 100644 --- a/plugins/choose.py +++ b/plugins/choose.py @@ -3,13 +3,14 @@ import random from util import hook + @hook.command def choose(inp): ".choose , , ... -- makes a decision" if not inp: return choose.__doc__ - + c = re.findall(r'([^,]+)', inp) if len(c) == 1: c = re.findall(r'(\S+)', inp) diff --git a/plugins/dice.py b/plugins/dice.py index 4c7b716..25c3f2c 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -9,7 +9,7 @@ from util import hook whitespace_re = re.compile(r'\s+') -valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d\d+)([+-](\d+|\d*d\d+))*$', +valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d\d+)([+-](\d+|\d*d\d+))*$', re.I) sign_re = re.compile(r'[+-]?(?:\d*d)?\d+', re.I) split_re = re.compile(r'([\d+-]*)d?(\d*)', re.I) @@ -17,15 +17,15 @@ split_re = re.compile(r'([\d+-]*)d?(\d*)', re.I) def nrolls(count, n): "roll an n-sided die count times" - if n < 2: #it's a coin + if n < 2: # it's a coin if count < 5000: return sum(random.randint(0, 1) for x in xrange(count)) - else: #fake it + else: # fake it return int(random.normalvariate(.5*count, (.75*count)**.5)) else: if count < 5000: return sum(random.randint(1, n) for x in xrange(count)) - else: #fake it + else: # fake it return int(random.normalvariate(.5*(1+n)*count, (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5)) diff --git a/plugins/dotnetpad.py b/plugins/dotnetpad.py index b1e9561..edb22f3 100644 --- a/plugins/dotnetpad.py +++ b/plugins/dotnetpad.py @@ -59,38 +59,39 @@ def cs(snippet): if not snippet: return cs.__doc__ - + file_template = ('using System; ' 'using System.Linq; ' 'using System.Collections.Generic; ' 'using System.Text; ' - '%(class)s') + '%s') class_template = ('public class Default ' - '{ ' - ' %(main)s ' + '{' + ' %s ' '}') main_template = ('public static void Main(String[] args) ' - '{ ' - ' %(snippet)s ' + '{' + ' %s ' '}') # There are probably better ways to do the following, but I'm feeling lazy - # if no main is found in the snippet, then we use the template with Main in it + # if no main is found in the snippet, use the template with Main in it if 'public static void Main' not in snippet: - code = main_template % { 'snippet': snippet } - code = class_template % { 'main': code } - code = file_template % { 'class' : code } + code = main_template % snippet + code = class_template % code + code = file_template % code - # if Main is found, check for class and see if we need to use the classed template + # if Main is found, check for class and see if we need to use the + # classed template elif 'class' not in snippet: - code = class_template % { 'main': snippet } - code = file_template % { 'class' : code } + code = class_template % snippet + code = file_template % code return 'Error using dotnetpad' # if we found class, then use the barebones template else: - code = file_template % { 'class' : snippet } + code = file_template % snippet return dotnetpad('csharp', code) diff --git a/plugins/down.py b/plugins/down.py index 4842242..7db4690 100644 --- a/plugins/down.py +++ b/plugins/down.py @@ -3,6 +3,7 @@ import urlparse from util import hook + @hook.command def down(inp): '''.down -- checks to see if the site is down''' diff --git a/plugins/explain.py b/plugins/explain.py index 6d65082..d72927a 100755 --- a/plugins/explain.py +++ b/plugins/explain.py @@ -1,6 +1,7 @@ from util import hook from pycparser.cdecl import explain_c_declaration + @hook.command def explain(inp): ".explain -- gives an explanation of C expression" @@ -9,7 +10,7 @@ def explain(inp): inp = inp.encode('utf8', 'ignore') - try: + try: return explain_c_declaration(inp) except Exception, e: return 'error: %s' % e diff --git a/plugins/gcalc.py b/plugins/gcalc.py index d92260c..1193a37 100644 --- a/plugins/gcalc.py +++ b/plugins/gcalc.py @@ -5,6 +5,7 @@ from lxml import html from util import hook + @hook.command def calc(inp): '''.calc -- returns Google Calculator result''' @@ -22,7 +23,7 @@ def calc(inp): if m is None: return "could not calculate " + inp - result = m.group(1).replace(" ",",") - result = result.replace(" × 10","E").replace("","") - result = result.replace("\xa0",",") + result = m.group(1).replace(" ", ",") + result = result.replace(" × 10", "E").replace("", "") + result = result.replace("\xa0", ",") return result diff --git a/plugins/google.py b/plugins/google.py index e90c874..85b3855 100644 --- a/plugins/google.py +++ b/plugins/google.py @@ -27,8 +27,8 @@ def gis(inp): parsed['responseStatus'], '')) if not parsed['responseData']['results']: return 'no images found' - return random.choice(parsed['responseData']['results'][:10] - )['unescapedUrl'] # squares is dumb + return random.choice(parsed['responseData']['results'][:10]) \ + ['unescapedUrl'] # squares is dumb @hook.command diff --git a/plugins/goonsay.py b/plugins/goonsay.py index 42c8eb9..4d5eff8 100644 --- a/plugins/goonsay.py +++ b/plugins/goonsay.py @@ -1,6 +1,8 @@ from util import hook -#Scaevolus: factormystic if you commit a re-enabled goonsay I'm going to revoke your commit access + +#Scaevolus: factormystic if you commit a re-enabled goonsay I'm +# going to revoke your commit access #@hook.command def goonsay(inp, say=None): say(' __________ /') diff --git a/plugins/help.py b/plugins/help.py index c616a9e..a9fbde4 100644 --- a/plugins/help.py +++ b/plugins/help.py @@ -1,9 +1,10 @@ from util import hook + @hook.command def help(inp, bot=None, pm=None): ".help [command] -- gives a list of commands/help for a command" - + funcs = {} for csig, func, args in bot.plugs['command']: if args['hook'] != r'(.*)': diff --git a/plugins/log.py b/plugins/log.py index 400fca5..2ade656 100644 --- a/plugins/log.py +++ b/plugins/log.py @@ -12,7 +12,7 @@ from util import hook lock = thread.allocate_lock() -log_fds = {} # '%(net)s %(chan)s' : (filename, fd) +log_fds = {} # '%(net)s %(chan)s' : (filename, fd) timestamp_format = '%H:%M:%S' @@ -69,8 +69,8 @@ def get_log_fd(dir, server, chan): cache_key = '%s %s' % (server, chan) filename, fd = log_fds.get(cache_key, ('', 0)) - if fn != filename: # we need to open a file for writing - if fd != 0: # is a valid fd + if fn != filename: # we need to open a file for writing + if fd != 0: # is a valid fd fd.flush() fd.close() dir = os.path.split(fn)[0] @@ -90,14 +90,14 @@ def log(bot, input): fd = get_log_fd(bot.persist_dir, input.server, 'raw') fd.write(timestamp + ' ' + input.raw + '\n') - if input.command == 'QUIT': # these are temporary fixes until proper - input.chan = 'quit' # presence tracking is implemented + if input.command == 'QUIT': # these are temporary fixes until proper + input.chan = 'quit' # presence tracking is implemented if input.command == 'NICK': input.chan = 'nick' beau = beautify(input) - if beau == '': # don't log this + if beau == '': # don't log this return if input.chan: diff --git a/plugins/misc.py b/plugins/misc.py index 8a94cb1..1bde4ae 100644 --- a/plugins/misc.py +++ b/plugins/misc.py @@ -1,7 +1,7 @@ from util import hook import socket -socket.setdefaulttimeout(5) # global setting +socket.setdefaulttimeout(5) # global setting #autorejoin channels @@ -11,12 +11,14 @@ def rejoin(inp, paraml=[], conn=None): if paraml[0].lower() in conn.channels: conn.join(paraml[0]) + #join channels when invited @hook.event('INVITE') def invite(inp, command='', conn=None): if command == 'INVITE': conn.join(inp) + #join channels when server says hello & identify bot @hook.event('004') def onjoin(inp, conn=None): diff --git a/plugins/mtg.py b/plugins/mtg.py index 2ea9d63..efdb8c9 100644 --- a/plugins/mtg.py +++ b/plugins/mtg.py @@ -8,7 +8,7 @@ from util import hook @hook.command def mtg(inp): - ".mtg -- gets information about Magic the Gathering card " + ".mtg -- gets information about Magic the Gathering card " url = 'http://magiccards.info/query.php?cardname=' url += urllib2.quote(inp, safe='') h = html.parse(url) @@ -20,11 +20,11 @@ def mtg(inp): type = text.text text = text.find('b').text_content() - text = re.sub(r'\(.*?\)', '', text) # strip parenthetical explanations - text = re.sub(r'\.(\S)', r'. \1', text) # fix spacing + text = re.sub(r'\(.*?\)', '', text) # strip parenthetical explanations + text = re.sub(r'\.(\S)', r'. \1', text) # fix spacing printings = card.find('table/tr/td/img').getparent().text_content() - printings = re.findall(r'\s*(.+?(?: \([^)]+\))*) \((.*?)\)', + printings = re.findall(r'\s*(.+?(?: \([^)]+\))*) \((.*?)\)', ' '.join(printings.split())) printing_out = ', '.join('%s (%s)' % (set_abbrevs.get(x[0], x[0]), rarity_abbrevs.get(x[1], x[1])) diff --git a/plugins/quote.py b/plugins/quote.py index 3a1b0a8..597ee9f 100644 --- a/plugins/quote.py +++ b/plugins/quote.py @@ -8,15 +8,17 @@ from util import hook def add_quote(db, chan, nick, add_nick, msg): now = time.time() db.execute('''insert or fail into quote (chan, nick, add_nick, - msg, time) values(?,?,?,?,?)''', + msg, time) values(?,?,?,?,?)''', (chan, nick, add_nick, msg, now)) db.commit() + def get_quotes_by_nick(db, chan, nick): return db.execute("select time, nick, msg from quote where deleted!=1 " "and chan=? and lower(nick)=lower(?) order by time", (chan, nick)).fetchall() + def get_quotes_by_chan(db, chan): return db.execute("select time, nick, msg from quote where deleted!=1 " "and chan=? order by time", (chan,)).fetchall() @@ -24,9 +26,10 @@ def get_quotes_by_chan(db, chan): def format_quote(q, num, n_quotes): ctime, nick, msg = q - return "[%d/%d] %s <%s> %s" % (num, n_quotes, + return "[%d/%d] %s <%s> %s" % (num, n_quotes, time.strftime("%Y-%m-%d", time.gmtime(ctime)), nick, msg) + @hook.command('q') @hook.command def quote(inp, nick='', chan='', db=None): @@ -46,12 +49,12 @@ def quote(inp, nick='', chan='', db=None): quoted_nick, msg = add.groups() try: add_quote(db, chan, quoted_nick, nick, msg) - except db.IntegrityError: + except db.IntegrityError: return "message already stored, doing nothing." return "quote added." elif retrieve: select, num = retrieve.groups() - + by_chan = False if select.startswith('#'): by_chan = True @@ -69,7 +72,7 @@ def quote(inp, nick='', chan='', db=None): if num: if num > n_quotes: - return "I only have %d quote%s for %s" % (n_quotes, + return "I only have %d quote%s for %s" % (n_quotes, ('s', '')[n_quotes == 1], select) else: selected_quote = quotes[num - 1] diff --git a/plugins/regular.py b/plugins/regular.py index 47d9ffa..511c350 100644 --- a/plugins/regular.py +++ b/plugins/regular.py @@ -1,24 +1,23 @@ -''' -regular.py - -skybot plugin for testing regular expressions -by Ipsum -''' - -import re - -from util import hook - - -@hook.command('re') -def reg(inp): - ".re -- matches regular expression in given "\ - "(leave 2 spaces between)" - - query = inp.split(" ", 1) - - if not inp or len(query) != 2: - return reg.__doc__ - - return '|'.join(re.findall(query[0], query[1])) - +''' +regular.py + +skybot plugin for testing regular expressions +by Ipsum +''' + +import re + +from util import hook + + +@hook.command('re') +def reg(inp): + ".re -- matches regular expression in given "\ + "(leave 2 spaces between)" + + query = inp.split(" ", 1) + + if not inp or len(query) != 2: + return reg.__doc__ + + return '|'.join(re.findall(query[0], query[1])) diff --git a/plugins/remember.py b/plugins/remember.py index 840539a..b464efc 100644 --- a/plugins/remember.py +++ b/plugins/remember.py @@ -4,11 +4,13 @@ remember.py: written by Scaevolus 2010 from util import hook + def db_init(db): db.execute("create table if not exists memory(chan, word, data, nick," " primary key(chan, word))") db.commit() + def get_memory(db, chan, word): row = db.execute("select data from memory where chan=? and word=lower(?)", (chan, word)).fetchone() @@ -17,6 +19,7 @@ def get_memory(db, chan, word): else: return None + @hook.command def remember(inp, nick='', chan='', db=None): ".remember -- maps word to data in the memory" @@ -37,6 +40,7 @@ def remember(inp, nick='', chan='', db=None): else: return 'done.' + @hook.command def forget(inp, chan='', db=None): ".forget -- forgets the mapping that word had" @@ -57,6 +61,7 @@ def forget(inp, chan='', db=None): else: return "I don't know about that." + @hook.command(hook='\?(.+)', prefix=False) def question(inp, chan='', say=None, db=None): "? -- shows what data is associated with word" diff --git a/plugins/seen.py b/plugins/seen.py index a917dc5..3da8f1d 100644 --- a/plugins/seen.py +++ b/plugins/seen.py @@ -13,7 +13,7 @@ def seeninput(bot, input): db = bot.get_db_connection(input.server) db_init(db) db.execute("insert or replace into seen(name, time, quote, chan)" - "values(?,?,?,?)", (input.nick.lower(), time.time(), input.msg, + "values(?,?,?,?)", (input.nick.lower(), time.time(), input.msg, input.chan)) db.commit() @@ -35,7 +35,7 @@ def seen(inp, nick='', chan='', db=None): if last_seen: reltime = timesince.timesince(last_seen[1]) - if last_seen[0] != inp.lower(): # for glob matching + if last_seen[0] != inp.lower(): # for glob matching inp = last_seen[0] return '%s was last seen %s ago saying: %s' % \ (inp, reltime, last_seen[2]) diff --git a/plugins/sieve.py b/plugins/sieve.py index b283094..c45782a 100644 --- a/plugins/sieve.py +++ b/plugins/sieve.py @@ -17,7 +17,7 @@ def sieve_suite(bot, input, func, args): hook = args.get('hook', r'(.*)') if args.get('prefix', True): - if input.chan == input.nick: # private message, prefix not required + if input.chan == input.nick: # private message, prefix not required prefix = r'^(?:[.!]?|' else: prefix = r'^(?:[.!]|' diff --git a/plugins/suggest.py b/plugins/suggest.py index 5be7a26..ec39c81 100644 --- a/plugins/suggest.py +++ b/plugins/suggest.py @@ -6,12 +6,13 @@ import json from util import hook + @hook.command def suggest(inp, inp_unstripped=''): ".suggest [#n] -- gets a random/the nth suggested google search" if not inp: return suggest.__doc__ - + inp = inp_unstripped m = re.match('^#(\d+) (.+)$', inp) if m: diff --git a/plugins/tell.py b/plugins/tell.py index 8353173..ff06820 100644 --- a/plugins/tell.py +++ b/plugins/tell.py @@ -5,9 +5,10 @@ import time from util import hook, timesince + def get_tells(db, user_to, chan): return db.execute("select user_from, message, time from tell where" - " user_to=lower(?) and chan=? order by time", + " user_to=lower(?) and chan=? order by time", (user_to.lower(), chan)).fetchall() @@ -18,7 +19,7 @@ def tellinput(bot, input): if 'showtells' in input.msg.lower(): return - + db = bot.get_db_connection(input.server) db = db_init(db) @@ -37,14 +38,15 @@ def tellinput(bot, input): db.commit() input.reply(reply) + @hook.command def showtells(inp, nick='', chan='', pm=None, db=None): ".showtells -- view all pending tell messages (sent in PM)." - + db_init(db) tells = get_tells(db, nick, chan) - + if not tells: pm("You have no pending tells.") return @@ -53,11 +55,12 @@ def showtells(inp, nick='', chan='', pm=None, db=None): user_from, message, time = tell reltime = timesince.timesince(time) pm("%s said %s ago: %s" % (user_from, reltime, message)) - + db.execute("delete from tell where user_to=lower(?) and chan=?", (nick, chan)) db.commit() + @hook.command def tell(inp, nick='', chan='', db=None): ".tell -- relay to when is around" @@ -70,7 +73,7 @@ def tell(inp, nick='', chan='', db=None): user_to = query[0].lower() message = query[1].strip() user_from = nick - + if user_to == user_from.lower(): return "No." @@ -97,5 +100,5 @@ def db_init(db): "(user_to, user_from, message, chan, time," "primary key(user_to, message))") db.commit() - + return db diff --git a/plugins/twitter.py b/plugins/twitter.py index 9338f38..6eec832 100644 --- a/plugins/twitter.py +++ b/plugins/twitter.py @@ -26,10 +26,12 @@ def unescape_xml(string): history = [] history_max_size = 250 + @hook.command def twitter(inp): - ".twitter / //#/@ -- gets last/th tweet from"\ - "/gets tweet /gets random tweet with #/gets replied tweet from @" + ".twitter / //#/@ -- gets last/th "\ + "tweet from /gets tweet /gets random tweet with #/"\ + "gets replied tweet from @" if not inp: return twitter.__doc__ @@ -89,7 +91,7 @@ def twitter(inp): try: xml = urllib2.urlopen(url).read() except urllib2.HTTPError, e: - errors = {400 : 'bad request (ratelimited?)', + errors = {400: 'bad request (ratelimited?)', 401: 'tweet is private', 404: 'invalid user/id', 500: 'twitter is broken', @@ -125,10 +127,11 @@ def twitter(inp): reply_name = tweet.find(reply_name).text reply_id = tweet.find(reply_id).text reply_user = tweet.find(reply_user).text - if reply_name is not None and (reply_id is not None or reply_user is not None): + if reply_name is not None and (reply_id is Not None or + reply_user is not None): add_reply(reply_name, reply_id if reply_id else -1) - time = strftime('%Y-%m-%d %H:%M:%S', + time = strftime('%Y-%m-%d %H:%M:%S', strptime(time.text, '%a %b %d %H:%M:%S +0000 %Y')) text = unescape_xml(tweet.find(text).text.replace('\n', '')) diff --git a/plugins/urlhistory.py b/plugins/urlhistory.py index b32e4e0..abbcc07 100644 --- a/plugins/urlhistory.py +++ b/plugins/urlhistory.py @@ -6,10 +6,11 @@ from util import hook, urlnorm, timesince url_re = re.compile(r'([a-zA-Z]+://|www\.)[^ ]*') -expiration_period = 60 * 60 * 24 # 1 day +expiration_period = 60 * 60 * 24 # 1 day ignored_urls = [urlnorm.normalize("http://google.com")] + def db_connect(bot, server): "check to see that our db has the the seen table and return a dbection." db = bot.get_db_connection(server) @@ -18,18 +19,21 @@ def db_connect(bot, server): db.commit() return db + def insert_history(db, chan, url, nick): now = time.time() db.execute("insert into urlhistory(chan, url, nick, time) " "values(?,?,?,?)", (chan, url, nick, time.time())) db.commit() + def get_history(db, chan, url): - db.execute("delete from urlhistory where time < ?", + db.execute("delete from urlhistory where time < ?", (time.time() - expiration_period,)) return db.execute("select nick, time from urlhistory where " "chan=? and url=? order by time desc", (chan, url)).fetchall() + def nicklist(nicks): nicks = sorted(dict(nicks), key=unicode.lower) if len(nicks) <= 2: @@ -37,6 +41,7 @@ def nicklist(nicks): else: return ', and '.join((', '.join(nicks[:-1]), nicks[-1])) + def format_reply(history): if not history: return @@ -57,10 +62,11 @@ def format_reply(history): last = "last linked %s ago" % last_time else: last = "last linked by %s %s ago" % (last_nick, last_time) - - return "that url has been posted %s in the past %s by %s (%s)." % (ordinal, + + return "that url has been posted %s in the past %s by %s (%s)." % (ordinal, hour_span, nicklist(history), last) - + + @hook.command(hook=r'(.*)', prefix=False) def urlinput(inp, nick='', chan='', server='', reply=None, bot=None): m = url_re.search(inp.encode('utf8')) diff --git a/plugins/util/hook.py b/plugins/util/hook.py index 22963f4..ca8c780 100644 --- a/plugins/util/hook.py +++ b/plugins/util/hook.py @@ -3,6 +3,7 @@ import thread import traceback import Queue + def _isfunc(x): if type(x) == type(_isfunc): return True @@ -24,7 +25,7 @@ def _hook_add(func, add, name=''): if argspec.varargs: n_args -= 1 if n_args != 1: - err = '%ss must take 1 non-keyword argument (%s)' % (name, + err = '%ss must take 1 non-keyword argument (%s)' % (name, func.__name__) raise ValueError(err) @@ -34,9 +35,10 @@ def _hook_add(func, add, name=''): args.extend(argspec.args[-len(argspec.defaults): end if end else None]) if argspec.keywords: - args.append(0) # means kwargs present + args.append(0) # means kwargs present func._skybot_args = args + def _make_sig(f): return f.func_code.co_filename, f.func_name, f.func_code.co_firstlineno diff --git a/plugins/util/timesince.py b/plugins/util/timesince.py index bbe7671..846d2a7 100644 --- a/plugins/util/timesince.py +++ b/plugins/util/timesince.py @@ -1,34 +1,35 @@ # Copyright (c) Django Software Foundation and individual contributors. # All rights reserved. # -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: # -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. # -# 3. Neither the name of Django nor the names of its contributors may be used -# to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of Django nor the names of its contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"AND +#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +#ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import datetime import time + def timesince(d, now=None): """ Takes two datetime objects and returns the time between d and now @@ -86,11 +87,12 @@ def timesince(d, now=None): count2 = (since - (seconds * count)) // seconds2 if count2 != 0: if count2 == 1: - s += ', %(number)d %(type)s' % {'number': count2, 'type': name2[0]} + s += ', %d %s' % (count2, name2[0]) else: - s += ', %(number)d %(type)s' % {'number': count2, 'type': name2[1]} + s += ', %d %s' % (count2, name2[1]) return s + def timeuntil(d, now=None): """ Like timesince, but returns a string measuring the time until diff --git a/plugins/weather.py b/plugins/weather.py index 6c9481e..c60acc2 100644 --- a/plugins/weather.py +++ b/plugins/weather.py @@ -8,7 +8,7 @@ from util import hook @hook.command def weather(inp, nick='', server='', reply=None, db=None): - ".weather [dontsave] -- queries the google weather API for weather data" + ".weather [dontsave] -- gets weather data from Google" loc = inp @@ -18,7 +18,7 @@ def weather(inp, nick='', server='', reply=None, db=None): db.execute("create table if not exists weather(nick primary key, loc)") - if not loc: # blank line + if not loc: # blank line loc = db.execute("select loc from weather where nick=lower(?)", (nick,)).fetchone() if not loc: diff --git a/plugins/wikipedia.py b/plugins/wikipedia.py index ec6e9f3..22f9d8f 100644 --- a/plugins/wikipedia.py +++ b/plugins/wikipedia.py @@ -26,7 +26,8 @@ def wiki(inp): q = search_url % (urllib2.quote(inp, safe='')) request = urllib2.Request(q) - request.add_header('User-Agent', 'Skybot/1.0 http://bitbucket.org/Scaevolus/skybot/') + request.add_header('User-Agent', + 'Skybot/1.0 http://bitbucket.org/Scaevolus/skybot/') opener = urllib2.build_opener() xml = opener.open(request).read() x = etree.fromstring(xml) @@ -54,7 +55,7 @@ def wiki(inp): if title.lower() not in desc.lower(): desc = title + desc - desc = re.sub('\s+', ' ', desc).strip() #remove excess spaces + desc = re.sub('\s+', ' ', desc).strip() # remove excess spaces if len(desc) > 300: desc = desc[:300] + '...' diff --git a/plugins/wolframalpha.py b/plugins/wolframalpha.py index 491fe0d..affa749 100644 --- a/plugins/wolframalpha.py +++ b/plugins/wolframalpha.py @@ -5,6 +5,7 @@ from lxml import html from util import hook + @hook.command @hook.command('wa') def wolframalpha(inp): @@ -21,7 +22,7 @@ def wolframalpha(inp): pods = h.xpath("//div[@class='pod ']") pod_texts = [] - for pod in pods: + for pod in pods: heading = pod.find('h1/span') if heading is not None: heading = heading.text_content().strip() @@ -40,7 +41,7 @@ def wolframalpha(inp): if results: pod_texts.append(heading + ' ' + '|'.join(results)) - ret = '. '.join(pod_texts) # first pod is the input + ret = '. '.join(pod_texts) if not pod_texts: return 'no results' @@ -52,7 +53,7 @@ def wolframalpha(inp): ret = re.sub(r'\\:([0-9a-z]{4})', unicode_sub, ret) - if len(ret) > 430: + if len(ret) > 430: ret = ret[:ret.rfind(' ', 0, 430)] ret = re.sub(r'\W+$', '', ret) + '...' diff --git a/plugins/youtube.py b/plugins/youtube.py index a3a3502..99a5a21 100644 --- a/plugins/youtube.py +++ b/plugins/youtube.py @@ -17,11 +17,12 @@ url = base_url + 'videos/%s?v=2&alt=jsonc' search_api_url = base_url + 'videos?v=2&alt=jsonc&max-results=1&q=%s' video_url = "http://youtube.com/watch?v=%s" + def get_video_description(vid_id): j = json.load(urllib2.urlopen(url % vid_id)) if j.get('error'): - return + return j = j['data'] @@ -29,7 +30,7 @@ def get_video_description(vid_id): out += ' - length \x02' length = j['duration'] - if length / 3600: # > 1 hour + if length / 3600: # > 1 hour out += '%dh ' % (length / 3600) if length / 60: out += '%dm ' % (length / 60 % 60) @@ -38,12 +39,11 @@ def get_video_description(vid_id): if 'rating' in j: out += ' - rated \x02%.2f/5.0\x02 (%d)' % (j['rating'], j['ratingCount']) - if 'viewCount' in j: out += ' - \x02%s\x02 views' % locale.format('%d', j['viewCount'], 1) - + upload_time = time.strptime(j['uploaded'], "%Y-%m-%dT%H:%M:%S.000Z") out += ' - \x02%s\x02 on \x02%s\x02' % (j['uploader'], time.strftime("%Y.%m.%d", upload_time)) @@ -52,7 +52,7 @@ def get_video_description(vid_id): out += ' - \x034NSFW\x02' return out - + @hook.command(hook=r'(.*)', prefix=False) def youtube_url(inp):