diff --git a/bot.py b/bot.py index 4d5836a..2d41b9a 100755 --- a/bot.py +++ b/bot.py @@ -20,7 +20,7 @@ print 'Loading plugins' # bootstrap the reloader eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(), - os.path.join('core', 'reload.py'), 'exec')) + os.path.join('core', 'reload.py'), 'exec')) reload(init=True) config() @@ -35,11 +35,11 @@ try: for name, conf in bot.config['connections'].iteritems(): if conf.get('ssl'): 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)) + 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'], conf=conf, - port=conf.get('port', 6667), channels=conf['channels']) + port=conf.get('port', 6667), channels=conf['channels']) except Exception, e: print 'ERROR: malformed config file', e sys.exit() diff --git a/core/irc.py b/core/irc.py index 340b2ce..a98a105 100644 --- a/core/irc.py +++ b/core/irc.py @@ -27,6 +27,7 @@ def censor(text): class crlf_tcp(object): + "Handles tcp connections that consist of utf-8 lines ending with crlf" def __init__(self, host, port, timeout=300): @@ -94,15 +95,17 @@ class crlf_tcp(object): 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_NONE if self.ignore_cert_errors else - CERT_REQUIRED) + cert_reqs=CERT_NONE if self.ignore_cert_errors else + CERT_REQUIRED) def recv_from_socket(self, nbytes): return self.socket.read(nbytes) @@ -123,8 +126,10 @@ irc_param_ref = re.compile(r'(?:^|(?<= ))(:.*|[^ ]+)').findall class IRC(object): + "handles the IRC protocol" - #see the docs/ folder for more information on the protocol + # see the docs/ folder for more information on the protocol + def __init__(self, server, nick, port=6667, channels=[], conf={}): self.channels = channels self.conf = conf @@ -148,8 +153,8 @@ class IRC(object): self.set_pass(self.conf.get('server_password')) self.set_nick(self.nick) self.cmd("USER", - [conf.get('user', 'skybot'), "3", "*", conf.get('realname', - 'Python bot - http://github.com/rmmh/skybot')]) + [conf.get('user', 'skybot'), "3", "*", conf.get('realname', + 'Python bot - http://github.com/rmmh/skybot')]) def parse_loop(self): while True: @@ -171,7 +176,7 @@ class IRC(object): paramlist[-1] = paramlist[-1][1:] lastparam = paramlist[-1] self.out.put([msg, prefix, command, params, nick, user, host, - paramlist, lastparam]) + paramlist, lastparam]) if command == "PING": self.cmd("PONG", paramlist) @@ -200,6 +205,7 @@ class IRC(object): class FakeIRC(IRC): + def __init__(self, server, nick, port=6667, channels=[], conf={}, fn=""): self.channels = channels self.conf = conf @@ -233,7 +239,7 @@ class FakeIRC(IRC): paramlist[-1] = paramlist[-1][1:] lastparam = paramlist[-1] self.out.put([msg, prefix, command, params, nick, user, host, - paramlist, lastparam]) + paramlist, lastparam]) if command == "PING": self.cmd("PONG", [params]) @@ -242,6 +248,7 @@ class FakeIRC(IRC): class SSLIRC(IRC): + def __init__(self, server, nick, port=6667, channels=[], conf={}, ignore_certificate_errors=True): self.ignore_cert_errors = ignore_certificate_errors diff --git a/core/main.py b/core/main.py index cafc3d3..bf93f9c 100644 --- a/core/main.py +++ b/core/main.py @@ -6,8 +6,9 @@ thread.stack_size(1024 * 512) # reduce vm size class Input(dict): + def __init__(self, conn, raw, prefix, command, params, - nick, user, host, paraml, msg): + nick, user, host, paraml, msg): chan = paraml[0].lower() if chan == conn.nick.lower(): # is a PM @@ -35,10 +36,10 @@ class Input(dict): conn.cmd('NOTICE', [nick, msg]) 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, - notice=notice, say=say, reply=reply, pm=pm, bot=bot, - me=me, set_nick=set_nick, lastparam=paraml[-1]) + params=params, nick=nick, user=user, host=host, + paraml=paraml, msg=msg, server=conn.server, chan=chan, + notice=notice, say=say, reply=reply, pm=pm, bot=bot, + me=me, set_nick=set_nick, lastparam=paraml[-1]) # make dict keys accessible as attributes def __getattr__(self, key): @@ -80,7 +81,9 @@ def do_sieve(sieve, bot, input, func, type, args): class Handler(object): + '''Runs plugins in their own threads (ensures order)''' + def __init__(self, func): self.func = func self.input_queue = Queue.Queue() @@ -121,7 +124,7 @@ def dispatch(input, kind, func, args, autohelp=False): return if autohelp and args.get('autohelp', True) and not input.inp \ - and func.__doc__ is not None: + and func.__doc__ is not None: input.reply(func.__doc__) return diff --git a/core/reload.py b/core/reload.py index f952fc4..cd0e01d 100644 --- a/core/reload.py +++ b/core/reload.py @@ -49,7 +49,7 @@ def reload(init=False): try: eval(compile(open(filename, 'U').read(), filename, 'exec'), - globals()) + globals()) except Exception: traceback.print_exc() if init: # stop if there's an error (syntax?) in a core @@ -111,7 +111,7 @@ def reload(init=False): if not init: print '### new plugin (type: %s) loaded:' % \ - type, format_plug(data) + type, format_plug(data) if changed: bot.commands = {} @@ -119,7 +119,7 @@ def reload(init=False): name = plug[1]['name'].lower() if not re.match(r'^\w+$', name): print '### ERROR: invalid command name "%s" (%s)' % (name, - format_plug(plug)) + format_plug(plug)) continue if name in bot.commands: print "### ERROR: command '%s' already registered (%s, %s)" % \ diff --git a/plugins/bf.py b/plugins/bf.py index 9e7d66c..8e86832 100644 --- a/plugins/bf.py +++ b/plugins/bf.py @@ -44,7 +44,7 @@ def bf(inp): # the main program loop: while ip < len(program): c = program[ip] - if c == '+': + if c == '+': memory[mp] = memory[mp] + 1 % 256 elif c == '-': memory[mp] = memory[mp] - 1 % 256 diff --git a/plugins/dice.py b/plugins/dice.py index 0279fb7..20ea1c0 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -23,13 +23,13 @@ def nrolls(count, n): if count < 5000: return [random.randint(0, 1) for x in xrange(count)] else: # fake it - return [int(random.normalvariate(.5*count, (.75*count)**.5))] + return [int(random.normalvariate(.5 * count, (.75 * count) ** .5))] else: if count < 5000: return [random.randint(1, n) for x in xrange(count)] else: # fake it - return [int(random.normalvariate(.5*(1+n)*count, - (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))] + return [int(random.normalvariate(.5 * (1 + n) * count, + (((n + 1) * (2 * n + 1) / 6. - (.5 * (1 + n)) ** 2) * count) ** .5))] @hook.command('roll') diff --git a/plugins/dictionary.py b/plugins/dictionary.py index d593085..d6f3c25 100755 --- a/plugins/dictionary.py +++ b/plugins/dictionary.py @@ -15,7 +15,7 @@ def urban(inp): if page['result_type'] == 'no_results': return 'not found.' - out = defs[0]['word'] + ': ' + defs[0]['definition'].replace('\r\n',' ') + out = defs[0]['word'] + ': ' + defs[0]['definition'].replace('\r\n', ' ') if len(out) > 400: out = out[:out.rfind(' ', 0, 400)] + '...' @@ -61,7 +61,7 @@ def define(inp): result += article[0] if len(article) > 2: result += ' '.join('%d. %s' % (n + 1, section) - for n, section in enumerate(article[1:])) + for n, section in enumerate(article[1:])) else: result += article[1] + ' ' diff --git a/plugins/drama.py b/plugins/drama.py index 7715da7..d728cb3 100644 --- a/plugins/drama.py +++ b/plugins/drama.py @@ -11,7 +11,7 @@ ed_url = "http://encyclopediadramatica.se/" @hook.command def drama(inp): '''.drama -- gets first paragraph of Encyclopedia Dramatica ''' \ - '''article on ''' + '''article on ''' j = http.get_json(api_url, search=inp) if not j[1]: diff --git a/plugins/google.py b/plugins/google.py index af38e8f..a78e10a 100644 --- a/plugins/google.py +++ b/plugins/google.py @@ -5,7 +5,7 @@ from util import hook, http def api_get(query, key, is_image=None, num=1): url = ('https://www.googleapis.com/customsearch/v1?cx=007629729846476161907:ud5nlxktgcw' - '&fields=items(title,link,snippet)&safe=off' + ('&searchType=image' if is_image else '')) + '&fields=items(title,link,snippet)&safe=off' + ('&searchType=image' if is_image else '')) return http.get_json(url, key=key, q=query, num=num) diff --git a/plugins/hash.py b/plugins/hash.py index 99a2b84..1ba2c12 100644 --- a/plugins/hash.py +++ b/plugins/hash.py @@ -17,4 +17,4 @@ def sha1(inp): def hash(inp): ".hash -- returns hashes of " return ', '.join(x + ": " + getattr(hashlib, x)(inp).hexdigest() - for x in 'md5 sha1 sha256'.split()) + for x in 'md5 sha1 sha256'.split()) diff --git a/plugins/lastfm.py b/plugins/lastfm.py index b87d02b..7a41dbd 100644 --- a/plugins/lastfm.py +++ b/plugins/lastfm.py @@ -7,6 +7,7 @@ from util import hook, http api_url = "http://ws.audioscrobbler.com/2.0/?format=json" + @hook.api_key('lastfm') @hook.command(autohelp=False) def lastfm(inp, nick='', say=None, api_key=None): diff --git a/plugins/log.py b/plugins/log.py index 7a9d601..4a9c896 100644 --- a/plugins/log.py +++ b/plugins/log.py @@ -15,15 +15,15 @@ log_fds = {} # '%(net)s %(chan)s' : (filename, fd) timestamp_format = '%H:%M:%S' formats = {'PRIVMSG': '<%(nick)s> %(msg)s', - 'PART': '-!- %(nick)s [%(user)s@%(host)s] has left %(chan)s', - 'JOIN': '-!- %(nick)s [%(user)s@%(host)s] has joined %(param0)s', - 'MODE': '-!- mode/%(chan)s [%(param_tail)s] by %(nick)s', - 'KICK': '-!- %(param1)s was kicked from %(chan)s by %(nick)s [%(msg)s]', - 'TOPIC': '-!- %(nick)s changed the topic of %(chan)s to: %(msg)s', - 'QUIT': '-!- %(nick)s has quit [%(msg)s]', - 'PING': '', - 'NOTICE': '' -} + 'PART': '-!- %(nick)s [%(user)s@%(host)s] has left %(chan)s', + 'JOIN': '-!- %(nick)s [%(user)s@%(host)s] has joined %(param0)s', + 'MODE': '-!- mode/%(chan)s [%(param_tail)s] by %(nick)s', + 'KICK': '-!- %(param1)s was kicked from %(chan)s by %(nick)s [%(msg)s]', + 'TOPIC': '-!- %(nick)s changed the topic of %(chan)s to: %(msg)s', + 'QUIT': '-!- %(nick)s has quit [%(msg)s]', + 'PING': '', + 'NOTICE': '' + } ctcp_formats = {'ACTION': '* %(nick)s %(ctcpmsg)s'} @@ -32,7 +32,7 @@ irc_color_re = re.compile(r'(\x03(\d+,\d+|\d)|[\x0f\x02\x16\x1f])') def get_log_filename(dir, server, chan): return os.path.join(dir, 'log', gmtime('%Y'), server, - (gmtime('%%s.%m-%d.log') % chan).lower()) + (gmtime('%%s.%m-%d.log') % chan).lower()) def gmtime(format): @@ -57,8 +57,8 @@ def beautify(input): ctcp += [''] args['ctcpcmd'], args['ctcpmsg'] = ctcp format = ctcp_formats.get(args['ctcpcmd'], - '%(nick)s [%(user)s@%(host)s] requested unknown CTCP ' - '%(ctcpcmd)s from %(chan)s: %(ctcpmsg)s') + '%(nick)s [%(user)s@%(host)s] requested unknown CTCP ' + '%(ctcpcmd)s from %(chan)s: %(ctcpmsg)s') return format % args diff --git a/plugins/metacritic.py b/plugins/metacritic.py index c401dee..ec05dcb 100644 --- a/plugins/metacritic.py +++ b/plugins/metacritic.py @@ -9,7 +9,7 @@ from util import hook, http @hook.command('mc') def metacritic(inp): '.mc [all|movie|tv|album|x360|ps3|pc|ds|3ds|wii|psv] -- gets rating for'\ - ' <title> from metacritic on the specified medium' + ' <title> from metacritic on the specified medium' # if the results suck, it's metacritic's fault @@ -130,6 +130,6 @@ def metacritic(inp): score = None return '[%s] %s - %s, %s -- %s' % (plat.upper(), name, - score or 'no score', - 'release: %s' % release if release else 'unreleased', - link) + score or 'no score', + 'release: %s' % release if release else 'unreleased', + link) diff --git a/plugins/misc.py b/plugins/misc.py index ce9908d..e1981fa 100644 --- a/plugins/misc.py +++ b/plugins/misc.py @@ -1,4 +1,3 @@ -import re import socket import subprocess import time @@ -18,12 +17,12 @@ def get_version(): shorthash = stdout.split(None, 1)[0] http.ua_skybot = 'Skybot/r%d %s (http://github.com/rmmh/skybot)' \ - % (revnumber, shorthash) + % (revnumber, shorthash) return shorthash, revnumber -#autorejoin channels +# autorejoin channels @hook.event('KICK') def rejoin(paraml, conn=None): if paraml[1] == conn.nick: @@ -31,7 +30,7 @@ def rejoin(paraml, conn=None): conn.join(paraml[0]) -#join channels when invited +# join channels when invited @hook.event('INVITE') def invite(paraml, conn=None): conn.join(paraml[-1]) diff --git a/plugins/mtg.py b/plugins/mtg.py index 6160970..9c74deb 100644 --- a/plugins/mtg.py +++ b/plugins/mtg.py @@ -33,7 +33,7 @@ def mtg(inp): printing_out = ', '.join('%s (%s)' % (set_abbrevs.get(x[0], x[0]), rarity_abbrevs.get(x[1], x[1])) - for x in printings) + for x in printings) name.make_links_absolute(base_url=url) link = name.attrib['href'] diff --git a/plugins/profile.py b/plugins/profile.py index 7f9f06e..3c9d27f 100644 --- a/plugins/profile.py +++ b/plugins/profile.py @@ -8,4 +8,4 @@ def profile(inp): ".profile <username> -- links to <username>'s profile on SA" return 'http://forums.somethingawful.com/member.php?action=getinfo' + \ - '&username=' + '+'.join(inp.split()) + '&username=' + '+'.join(inp.split()) diff --git a/plugins/quote.py b/plugins/quote.py index 37c69d7..a07a2b8 100644 --- a/plugins/quote.py +++ b/plugins/quote.py @@ -8,7 +8,7 @@ from util import hook def add_quote(db, chan, nick, add_nick, msg): db.execute('''insert or fail into quote (chan, nick, add_nick, msg, time) values(?,?,?,?,?)''', - (chan, nick, add_nick, msg, time.time())) + (chan, nick, add_nick, msg, time.time())) db.commit() @@ -20,19 +20,19 @@ def del_quote(db, chan, nick, add_nick, msg): 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() + "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() + "and chan=? order by time", (chan,)).fetchall() def format_quote(q, num, n_quotes): ctime, nick, msg = q return "[%d/%d] %s <%s> %s" % (num, n_quotes, - time.strftime("%Y-%m-%d", time.gmtime(ctime)), nick, msg) + time.strftime("%Y-%m-%d", time.gmtime(ctime)), nick, msg) @hook.command('q') @@ -42,8 +42,8 @@ def quote(inp, nick='', chan='', db=None): "random or [#n]th quote by <nick> or from <#chan>/adds quote" db.execute("create table if not exists quote" - "(chan, nick, add_nick, msg, time real, deleted default 0, " - "primary key (chan, nick, msg))") + "(chan, nick, add_nick, msg, time real, deleted default 0, " + "primary key (chan, nick, msg))") db.commit() add = re.match(r"add[^\w@]+(\S+?)>?\s+(.*)", inp, re.I) @@ -61,9 +61,7 @@ def quote(inp, nick='', chan='', db=None): elif retrieve: select, num = retrieve.groups() - by_chan = False if select.startswith('#'): - by_chan = True quotes = get_quotes_by_chan(db, select) else: quotes = get_quotes_by_nick(db, chan, select) @@ -85,7 +83,7 @@ def quote(inp, nick='', chan='', db=None): if num: if num > n_quotes or (num < 0 and num < -n_quotes): return "I only have %d quote%s for %s" % (n_quotes, - ('s', '')[n_quotes == 1], select) + ('s', '')[n_quotes == 1], select) elif num < 0: selected_quote = quotes[num] num = n_quotes + num + 1 diff --git a/plugins/religion.py b/plugins/religion.py index 39e2b30..1a48c04 100644 --- a/plugins/religion.py +++ b/plugins/religion.py @@ -7,11 +7,11 @@ def bible(inp): ".bible <passage> -- gets <passage> from the Bible (ESV)" base_url = ('http://www.esvapi.org/v2/rest/passageQuery?key=IP&' - 'output-format=plain-text&include-heading-horizontal-lines&' - 'include-headings=false&include-passage-horizontal-lines=false&' - 'include-passage-references=false&include-short-copyright=false&' - 'include-footnotes=false&line-length=0&' - 'include-heading-horizontal-lines=false') + 'output-format=plain-text&include-heading-horizontal-lines&' + 'include-headings=false&include-passage-horizontal-lines=false&' + 'include-passage-references=false&include-short-copyright=false&' + 'include-footnotes=false&line-length=0&' + 'include-heading-horizontal-lines=false') text = http.get(base_url, passage=inp) diff --git a/plugins/remember.py b/plugins/remember.py index fc8e708..91d7cb9 100644 --- a/plugins/remember.py +++ b/plugins/remember.py @@ -13,7 +13,7 @@ def db_init(db): def get_memory(db, chan, word): row = db.execute("select data from memory where chan=? and word=lower(?)", - (chan, word)).fetchone() + (chan, word)).fetchone() if row: return row[0] else: @@ -56,7 +56,7 @@ def remember(inp, nick='', chan='', db=None): return "appending %s to %s" % (new, data.replace('"', "''")) else: return 'forgetting "%s", remembering this instead.' % \ - data.replace('"', "''") + data.replace('"', "''") else: return 'done.' diff --git a/plugins/rottentomatoes.py b/plugins/rottentomatoes.py index 691dfb5..c405ab7 100644 --- a/plugins/rottentomatoes.py +++ b/plugins/rottentomatoes.py @@ -25,11 +25,13 @@ def rottentomatoes(inp, api_key=None): if critics_score == -1: return - reviews = http.get_json(movie_reviews_url % id, apikey=api_key, review_type='all') + reviews = http.get_json(movie_reviews_url % + id, apikey=api_key, review_type='all') review_count = reviews['total'] fresh = critics_score * review_count / 100 rotten = review_count - fresh return u"%s - critics: \x02%d%%\x02 (%d\u2191%d\u2193)" \ - " audience: \x02%d%%\x02 - %s" % (title, critics_score, fresh, rotten, audience_score, url) + " audience: \x02%d%%\x02 - %s" % (title, critics_score, + fresh, rotten, audience_score, url) diff --git a/plugins/seen.py b/plugins/seen.py index 7d0d5b0..3e76396 100644 --- a/plugins/seen.py +++ b/plugins/seen.py @@ -8,7 +8,7 @@ from util import hook, timesince def db_init(db): "check to see that our db has the the seen table and return a connection." db.execute("create table if not exists seen(name, time, quote, chan, " - "primary key(name, chan))") + "primary key(name, chan))") db.commit() @@ -17,8 +17,8 @@ def db_init(db): def seeninput(paraml, input=None, db=None, bot=None): db_init(db) db.execute("insert or replace into seen(name, time, quote, chan)" - "values(?,?,?,?)", (input.nick.lower(), time.time(), input.msg, - input.chan)) + "values(?,?,?,?)", (input.nick.lower(), time.time(), input.msg, + input.chan)) db.commit() @@ -44,11 +44,11 @@ def seen(inp, nick='', chan='', db=None, input=None): reltime = timesince.timesince(last_seen[1]) if last_seen[0] != inp.lower(): # for glob matching inp = last_seen[0] - if last_seen[2][0:1]=="\x01": + if last_seen[2][0:1] == "\x01": return '%s was last seen %s ago: *%s %s*' % \ - (inp, reltime, inp, last_seen[2][8:-1]) + (inp, reltime, inp, last_seen[2][8:-1]) else: return '%s was last seen %s ago saying: %s' % \ - (inp, reltime, last_seen[2]) + (inp, reltime, last_seen[2]) else: return "I've never seen %s" % inp diff --git a/plugins/snopes.py b/plugins/snopes.py index 75adfd3..74f0d87 100644 --- a/plugins/snopes.py +++ b/plugins/snopes.py @@ -26,7 +26,7 @@ def snopes(inp): status = status.group(0).strip() else: # new-style statuses status = "Status: %s." % re.search(r"FALSE|TRUE|MIXTURE|UNDETERMINED", - snopes_text).group(0).title() + snopes_text).group(0).title() claim = re.sub(r"[\s\xa0]+", " ", claim) # compress whitespace status = re.sub(r"[\s\xa0]+", " ", status) diff --git a/plugins/somethingawful.py b/plugins/somethingawful.py index 4d638d9..1d73291 100644 --- a/plugins/somethingawful.py +++ b/plugins/somethingawful.py @@ -16,7 +16,7 @@ def login(user, password): user = http.quote(user) password = http.quote(password) http.get("http://forums.somethingawful.com/account.php", cookies=True, - post_data="action=login&username=%s&password=%s" % (user, password)) + post_data="action=login&username=%s&password=%s" % (user, password)) @hook.api_key('somethingawful') @@ -49,8 +49,8 @@ def forum_link(inp, api_key=None): num_posts = int(num_posts[0].rsplit('=', 1)[1]) return '\x02%s\x02 > \x02%s\x02 by \x02%s\x02, %s post%s' % ( - forum_title, thread_title, poster, num_posts, - 's' if num_posts > 1 else '') + forum_title, thread_title, poster, num_posts, + 's' if num_posts > 1 else '') forum_abbrevs = { diff --git a/plugins/stock.py b/plugins/stock.py index 388bcbe..4229716 100644 --- a/plugins/stock.py +++ b/plugins/stock.py @@ -1,5 +1,3 @@ -import random - from util import hook, http diff --git a/plugins/suggest.py b/plugins/suggest.py index 3d30195..183cde1 100644 --- a/plugins/suggest.py +++ b/plugins/suggest.py @@ -19,7 +19,8 @@ def suggest(inp, inp_unstripped=''): else: num = 0 - page = http.get('http://google.com/complete/search', output='json', client='hp', q=inp) + page = http.get('http://google.com/complete/search', + output='json', client='hp', q=inp) page_json = page.split('(', 1)[1][:-1] suggestions = json.loads(page_json)[1] if not suggestions: diff --git a/plugins/tag.py b/plugins/tag.py index 0e83020..b55acb8 100644 --- a/plugins/tag.py +++ b/plugins/tag.py @@ -20,7 +20,9 @@ def munge(inp, munge_count=0): break return inp + class PaginatingWinnower(object): + def __init__(self): self.lock = threading.Lock() self.last_input = [] @@ -48,7 +50,8 @@ class PaginatingWinnower(object): if inp in inputs: inputs.remove(inp) else: - inputs.remove(random.choice([inp for inp in inputs if inp in self.recent])) + inputs.remove( + random.choice([inp for inp in inputs if inp in self.recent])) else: if ordered: inputs.pop() @@ -61,6 +64,7 @@ class PaginatingWinnower(object): winnow = PaginatingWinnower().winnow + def add_tag(db, chan, nick, subject): match = db.execute('select * from tag where lower(nick)=lower(?) and' ' chan=? and lower(subject)=lower(?)', @@ -95,7 +99,6 @@ def get_tag_counts_by_chan(db, chan): tags.sort(key=lambda x: x[1], reverse=True) if not tags: return 'no tags in %s' % chan - ret = '%s tags: ' % chan return winnow(['%s (%d)' % row for row in tags], ordered=True) @@ -105,7 +108,7 @@ def get_tags_by_nick(db, chan, nick): " order by lower(subject)", (nick, chan)).fetchall() if tags: return 'tags for "%s": ' % munge(nick, 1) + winnow([ - tag[0] for tag in tags]) + tag[0] for tag in tags]) else: return '' @@ -151,6 +154,7 @@ def tag(inp, chan='', db=None): else: return tag.__doc__ + @hook.command def untag(inp, chan='', db=None): '.untag <nick> <tag> -- unmarks <nick> as <tag> {related: .tag, .tags, .tagged}' @@ -163,6 +167,7 @@ def untag(inp, chan='', db=None): else: return untag.__doc__ + @hook.command def tags(inp, chan='', db=None): '.tags <nick>/list -- get list of tags for <nick>, or a list of tags {related: .tag, .untag, .tagged}' @@ -182,6 +187,7 @@ def tagged(inp, chan='', db=None): return get_nicks_by_tagset(db, chan, inp) + def distance(lat1, lon1, lat2, lon2): deg_to_rad = math.pi / 180 lat1 *= deg_to_rad @@ -189,17 +195,18 @@ def distance(lat1, lon1, lat2, lon2): lon1 *= deg_to_rad lon2 *= deg_to_rad - R = 6371 # km - d = math.acos(math.sin(lat1)*math.sin(lat2) + - math.cos(lat1)*math.cos(lat2) * - math.cos(lon2-lon1)) * R + R = 6371 # km + d = math.acos(math.sin(lat1) * math.sin(lat2) + + math.cos(lat1) * math.cos(lat2) * + math.cos(lon2 - lon1)) * R return d @hook.command(autohelp=False) def near(inp, nick='', chan='', db=None): try: - loc = db.execute("select lat, lon from location where chan=? and nick=lower(?)", (chan, nick)).fetchone() + loc = db.execute("select lat, lon from location where chan=? and nick=lower(?)", + (chan, nick)).fetchone() except db.OperationError: loc = None @@ -210,10 +217,9 @@ def near(inp, nick='', chan='', db=None): db.create_function('distance', 4, distance) nearby = db.execute("select nick, distance(lat, lon, ?, ?) as dist from location where chan=?" - " and nick != lower(?) order by dist limit 20", (lat, lon, chan, nick)).fetchall() + " and nick != lower(?) order by dist limit 20", (lat, lon, chan, nick)).fetchall() out = '(km) ' - last_dist = 10 while nearby and len(out) < 200: nick, dist = nearby.pop(0) out += '%s:%.0f ' % (munge(nick, 1), dist) diff --git a/plugins/tell.py b/plugins/tell.py index bdbedda..db13593 100644 --- a/plugins/tell.py +++ b/plugins/tell.py @@ -9,8 +9,8 @@ from util import hook, timesince def db_init(db): "check to see that our db has the tell table and return a dbection." db.execute("create table if not exists tell" - "(user_to, user_from, message, chan, time," - "primary key(user_to, message))") + "(user_to, user_from, message, chan, time," + "primary key(user_to, message))") db.commit() return db @@ -18,8 +18,8 @@ def db_init(db): def get_tells(db, user_to): return db.execute("select user_from, message, time, chan from tell where" - " user_to=lower(?) order by time", - (user_to.lower(),)).fetchall() + " user_to=lower(?) order by time", + (user_to.lower(),)).fetchall() @hook.singlethread @@ -42,7 +42,7 @@ def tellinput(paraml, input=None, db=None, bot=None): reply += " (+%d more, .showtells to view)" % (len(tells) - 1) db.execute("delete from tell where user_to=lower(?) and message=?", - (input.nick, message)) + (input.nick, message)) db.commit() input.notice(reply) @@ -65,7 +65,7 @@ def showtells(inp, nick='', chan='', notice=None, db=None): notice("%s said %s ago in %s: %s" % (user_from, past, chan, message)) db.execute("delete from tell where user_to=lower(?)", - (nick,)) + (nick,)) db.commit() @@ -91,13 +91,13 @@ def tell(inp, nick='', chan='', db=None): db_init(db) if db.execute("select count() from tell where user_to=?", - (user_to,)).fetchone()[0] >= 5: + (user_to,)).fetchone()[0] >= 5: return "That person has too many things queued." try: db.execute("insert into tell(user_to, user_from, message, chan," - "time) values(?,?,?,?,?)", (user_to, user_from, message, - chan, time.time())) + "time) values(?,?,?,?,?)", (user_to, user_from, message, + chan, time.time())) db.commit() except db.IntegrityError: return "Message has already been queued." diff --git a/plugins/translate.py b/plugins/translate.py index efdb54e..46324de 100644 --- a/plugins/translate.py +++ b/plugins/translate.py @@ -40,13 +40,14 @@ def unescape(text): def goog_trans(text, slang, tlang): url = 'https://www.googleapis.com/language/translate/v2' - parsed = http.get_json(url, key=api_key, q=text, source=slang, target=tlang) + parsed = http.get_json( + url, key=api_key, q=text, source=slang, target=tlang) if not 200 <= parsed['responseStatus'] < 300: raise IOError('error with the translation server: %d: %s' % ( - parsed['responseStatus'], parsed['responseDetails'])) + parsed['responseStatus'], parsed['responseDetails'])) if not slang: return unescape('(%(detectedSourceLanguage)s) %(translatedText)s' % - (parsed['responseData']['data']['translations'][0])) + (parsed['responseData']['data']['translations'][0])) return unescape('%(translatedText)s' % parsed['responseData']['data']['translations'][0]) @@ -66,8 +67,8 @@ def match_language(fragment): @hook.command def translate(inp, bot=None): '.translate [source language [target language]] <sentence> -- translates' \ - ' <sentence> from source language (default autodetect) to target' \ - ' language (default English) using Google Translate' + ' <sentence> from source language (default autodetect) to target' \ + ' language (default English) using Google Translate' if not hasapikey(bot): return None @@ -141,6 +142,7 @@ def babelext(inp, bot=None): return out + def hasapikey(bot): api_key = bot.config.get("api_keys", {}).get("googletranslate", None) return api_key diff --git a/plugins/tvdb.py b/plugins/tvdb.py index 81f023a..4e664f3 100644 --- a/plugins/tvdb.py +++ b/plugins/tvdb.py @@ -5,7 +5,6 @@ modified by rmmh 2010, 2013 import datetime -from lxml import etree from util import hook, http, timesince @@ -31,7 +30,8 @@ def get_episodes_for_series(seriesname): series_id = series_id[0] try: - series = http.get_xml(base_url + '%s/series/%s/all/en.xml' % (api_key, series_id)) + series = http.get_xml(base_url + '%s/series/%s/all/en.xml' % + (api_key, series_id)) except http.URLError: res["error"] = "error contacting thetvdb.com" return res @@ -97,12 +97,13 @@ def tv_next(inp): (episode_air_date, airdate, episode_desc) = ep_info if airdate > today: - next_eps = ['%s (%s) (%s)' % (episode_air_date, timesince.timeuntil(datetime.datetime.strptime(episode_air_date, "%Y-%m-%d")), episode_desc)] + next_eps = ['%s (%s) (%s)' % (episode_air_date, timesince.timeuntil( + datetime.datetime.strptime(episode_air_date, "%Y-%m-%d")), episode_desc)] elif airdate == today: next_eps = ['Today (%s)' % episode_desc] + next_eps else: - #we're iterating in reverse order with newest episodes last - #so, as soon as we're past today, break out of loop + # we're iterating in reverse order with newest episodes last + # so, as soon as we're past today, break out of loop break if not next_eps: @@ -140,8 +141,8 @@ def tv_last(inp): (episode_air_date, airdate, episode_desc) = ep_info if airdate < today: - #iterating in reverse order, so the first episode encountered - #before today was the most recently aired + # iterating in reverse order, so the first episode encountered + # before today was the most recently aired prev_ep = '%s (%s)' % (episode_air_date, episode_desc) break diff --git a/plugins/twitter.py b/plugins/twitter.py index 49e7c76..b01699b 100644 --- a/plugins/twitter.py +++ b/plugins/twitter.py @@ -5,14 +5,15 @@ from urllib import quote from util import hook, http + @hook.api_key('twitter') @hook.command def twitter(inp, api_key=None): ".twitter <user>/<user> <n>/<id>/#<search>/#<search> <n> -- " \ - "get <user>'s last/<n>th tweet/get tweet <id>/do <search>/get <n>th <search> result" + "get <user>'s last/<n>th tweet/get tweet <id>/do <search>/get <n>th <search> result" if not isinstance(api_key, dict) or any(key not in api_key for key in - ('consumer', 'consumer_secret', 'access', 'access_secret')): + ('consumer', 'consumer_secret', 'access', 'access_secret')): return "error: api keys not set" getting_id = False @@ -44,13 +45,13 @@ def twitter(inp, api_key=None): tweet = http.get_json(request_url, oauth=True, oauth_keys=api_key) except http.HTTPError, e: errors = {400: 'bad request (ratelimited?)', - 401: 'unauthorized', - 403: 'forbidden', - 404: 'invalid user/id', - 500: 'twitter is broken', - 502: 'twitter is down ("getting upgraded")', - 503: 'twitter is overloaded (lol, RoR)', - 410: 'twitter shut off api v1.' } + 401: 'unauthorized', + 403: 'forbidden', + 404: 'invalid user/id', + 500: 'twitter is broken', + 502: 'twitter is down ("getting upgraded")', + 503: 'twitter is overloaded (lol, RoR)', + 410: 'twitter shut off api v1.'} if e.code == 404: return 'error: invalid ' + ['username', 'tweet id'][getting_id] if e.code in errors: @@ -75,11 +76,13 @@ def twitter(inp, api_key=None): screen_name = tweet["user"]["screen_name"] time = tweet["created_at"] - time = strftime('%Y-%m-%d %H:%M:%S', strptime(time, '%a %b %d %H:%M:%S +0000 %Y')) + time = strftime('%Y-%m-%d %H:%M:%S', + strptime(time, '%a %b %d %H:%M:%S +0000 %Y')) return "%s \x02%s\x02: %s" % (time, screen_name, text) + @hook.api_key('twitter') @hook.regex(r'https?://twitter.com/(#!/)?([_0-9a-zA-Z]+)/status/(\d+)') def show_tweet(match, api_key=None): - return twitter(match.group(3),api_key) + return twitter(match.group(3), api_key) diff --git a/plugins/urlhistory.py b/plugins/urlhistory.py index d5470cd..37c1f4b 100644 --- a/plugins/urlhistory.py +++ b/plugins/urlhistory.py @@ -1,5 +1,4 @@ import math -import re import time from util import hook, urlnorm, timesince @@ -12,22 +11,21 @@ ignored_urls = [urlnorm.normalize("http://google.com")] def db_init(db): db.execute("create table if not exists urlhistory" - "(chan, url, nick, time)") + "(chan, url, nick, time)") db.commit() 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())) + "values(?,?,?,?)", (chan, url, nick, time.time())) db.commit() def get_history(db, chan, url): db.execute("delete from urlhistory where time < ?", - (time.time() - expiration_period,)) + (time.time() - expiration_period,)) return db.execute("select nick, time from urlhistory where " - "chan=? and url=? order by time desc", (chan, url)).fetchall() + "chan=? and url=? order by time desc", (chan, url)).fetchall() def nicklist(nicks): @@ -60,7 +58,7 @@ def format_reply(history): 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, - hour_span, nicklist(history), last) + hour_span, nicklist(history), last) @hook.regex(r'([a-zA-Z]+://|www\.)[^ ]+') diff --git a/plugins/util/hook.py b/plugins/util/hook.py index a20b265..349eac6 100644 --- a/plugins/util/hook.py +++ b/plugins/util/hook.py @@ -22,7 +22,7 @@ def _hook_add(func, add, name=''): n_args -= 1 if n_args != 1: err = '%ss must take 1 non-keyword argument (%s)' % (name, - func.__name__) + func.__name__) raise ValueError(err) args = [] @@ -41,7 +41,7 @@ def _hook_add(func, add, name=''): def sieve(func): if func.func_code.co_argcount != 5: raise ValueError( - 'sieves must take 5 arguments: (bot, input, func, type, args)') + 'sieves must take 5 arguments: (bot, input, func, type, args)') _hook_add(func, ['sieve', (func,)]) return func diff --git a/plugins/util/http.py b/plugins/util/http.py index 8e70f96..2bedd19 100644 --- a/plugins/util/http.py +++ b/plugins/util/http.py @@ -10,7 +10,7 @@ import urllib import urllib2 import urlparse -from hashlib import sha1 +from hashlib import sha1 from urllib import quote, quote_plus as _quote_plus from urllib2 import HTTPError, URLError @@ -69,14 +69,16 @@ def open(url, query_params=None, user_agent=None, referer=None, post_data=None, nonce = oauth_nonce() timestamp = oauth_timestamp() api_url, req_data = string.split(url, "?") - unsigned_request = oauth_unsigned_request(nonce, timestamp, req_data, oauth_keys['consumer'], oauth_keys['access']) - - signature = oauth_sign_request("GET", api_url, req_data, unsigned_request, oauth_keys['consumer_secret'], oauth_keys['access_secret']) - - header = oauth_build_header(nonce, signature, timestamp, oauth_keys['consumer'], oauth_keys['access']) + unsigned_request = oauth_unsigned_request( + nonce, timestamp, req_data, oauth_keys['consumer'], oauth_keys['access']) + + signature = oauth_sign_request("GET", api_url, req_data, unsigned_request, oauth_keys[ + 'consumer_secret'], oauth_keys['access_secret']) + + header = oauth_build_header( + nonce, signature, timestamp, oauth_keys['consumer'], oauth_keys['access']) request.add_header('Authorization', header) - if cookies: opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar)) else: @@ -91,7 +93,7 @@ def prepare_url(url, queries): query = dict(urlparse.parse_qsl(query)) query.update(queries) query = urllib.urlencode(dict((to_utf8(key), to_utf8(value)) - for key, value in query.iteritems())) + for key, value in query.iteritems())) url = urlparse.urlunsplit((scheme, netloc, path, query, fragment)) @@ -108,48 +110,53 @@ def to_utf8(s): def quote_plus(s): return _quote_plus(to_utf8(s)) + def oauth_nonce(): return ''.join([str(random.randint(0, 9)) for i in range(8)]) + def oauth_timestamp(): return str(int(time.time())) -def oauth_unsigned_request(nonce, timestamp, req, consumer, token): - d = { 'oauth_consumer_key':consumer, - 'oauth_nonce':nonce, - 'oauth_signature_method':'HMAC-SHA1', - 'oauth_timestamp':timestamp, - 'oauth_token':token, - 'oauth_version':'1.0' } - k,v = string.split(req, "=") +def oauth_unsigned_request(nonce, timestamp, req, consumer, token): + d = {'oauth_consumer_key': consumer, + 'oauth_nonce': nonce, + 'oauth_signature_method': 'HMAC-SHA1', + 'oauth_timestamp': timestamp, + 'oauth_token': token, + 'oauth_version': '1.0'} + + k, v = string.split(req, "=") d[k] = v - + unsigned_req = '' - + for x in sorted(d, key=lambda key: key): unsigned_req += x + "=" + d[x] + "&" - + unsigned_req = quote(unsigned_req[:-1]) return unsigned_req -def oauth_build_header(nonce, signature, timestamp, consumer, token): - d = { 'oauth_consumer_key':consumer, - 'oauth_nonce':nonce, - 'oauth_signature':signature, - 'oauth_signature_method':'HMAC-SHA1', - 'oauth_timestamp':timestamp, - 'oauth_token':token, - 'oauth_version':'1.0' } - header='OAuth ' - +def oauth_build_header(nonce, signature, timestamp, consumer, token): + d = {'oauth_consumer_key': consumer, + 'oauth_nonce': nonce, + 'oauth_signature': signature, + 'oauth_signature_method': 'HMAC-SHA1', + 'oauth_timestamp': timestamp, + 'oauth_token': token, + 'oauth_version': '1.0'} + + header = 'OAuth ' + for x in sorted(d, key=lambda key: key): header += x + '="' + d[x] + '", ' return header[:-1] + def oauth_sign_request(method, url, params, unsigned_request, consumer_secret, token_secret): key = consumer_secret + "&" + token_secret @@ -158,9 +165,10 @@ def oauth_sign_request(method, url, params, unsigned_request, consumer_secret, t hash = hmac.new(key, base, sha1) signature = quote(binascii.b2a_base64(hash.digest())[:-1]) - + return signature + def unescape(s): if not s.strip(): return s diff --git a/plugins/validate.py b/plugins/validate.py index 7cdb13c..b9da5b0 100644 --- a/plugins/validate.py +++ b/plugins/validate.py @@ -22,4 +22,4 @@ def validate(inp): errorcount = info['x-w3c-validator-errors'] warningcount = info['x-w3c-validator-warnings'] return "%s was found to be %s with %s errors and %s warnings." \ - " see: %s" % (inp, status, errorcount, warningcount, url) + " see: %s" % (inp, status, errorcount, warningcount, url) diff --git a/plugins/weather.py b/plugins/weather.py index 170ef2e..4898818 100644 --- a/plugins/weather.py +++ b/plugins/weather.py @@ -1,23 +1,21 @@ "weather, thanks to wunderground" -import math - from util import hook, http - @hook.api_key('wunderground') @hook.command(autohelp=False) def weather(inp, chan='', nick='', reply=None, db=None, api_key=None): ".weather <location> [dontsave] -- gets weather data from Wunderground "\ - "http://wunderground.com/weather/api" + "http://wunderground.com/weather/api" if not api_key: return None # this database is used by other plugins interested in user's locations, # like .near in tag.py - db.execute("create table if not exists location(chan, nick, loc, lat, lon, primary key(chan, nick))") + db.execute( + "create table if not exists location(chan, nick, loc, lat, lon, primary key(chan, nick))") loc = inp @@ -25,10 +23,10 @@ def weather(inp, chan='', nick='', reply=None, db=None, api_key=None): if dontsave: loc = loc[:-9].strip().lower() - if not loc: # blank line - loc = db.execute("select loc from location where chan=? and nick=lower(?)", - (chan, nick)).fetchone() + loc = db.execute( + "select loc from location where chan=? and nick=lower(?)", + (chan, nick)).fetchone() if not loc: try: # grab from old-style weather database @@ -104,10 +102,10 @@ def weather(inp, chan='', nick='', reply=None, db=None, api_key=None): info['l_c'] = sf['low']['celsius'] info['humid'] = obs['relative_humidity'] info['wind'] = 'Wind: {mph}mph/{kph}kph' \ - .format(mph=obs['wind_mph'], kph=obs['wind_kph']) - reply('{city}: {weather}, {t_f}F/{t_c}C' \ - '(H:{h_f}F/{h_c}C L:{l_f}F/{l_c}C)' \ - ', Humidity: {humid}, {wind}'.format(**info)) + .format(mph=obs['wind_mph'], kph=obs['wind_kph']) + reply('{city}: {weather}, {t_f}F/{t_c}C' + '(H:{h_f}F/{h_c}C L:{l_f}F/{l_c}C)' + ', Humidity: {humid}, {wind}'.format(**info)) lat = float(obs['display_location']['latitude']) lon = float(obs['display_location']['longitude']) @@ -116,5 +114,3 @@ def weather(inp, chan='', nick='', reply=None, db=None, api_key=None): db.execute("insert or replace into location(chan, nick, loc, lat, lon) " "values (?, ?, ?, ?,?)", (chan, nick.lower(), inp, lat, lon)) db.commit() - - diff --git a/plugins/wikipedia.py b/plugins/wikipedia.py index a83cd43..d99e08b 100644 --- a/plugins/wikipedia.py +++ b/plugins/wikipedia.py @@ -16,7 +16,7 @@ paren_re = re.compile('\s*\(.*\)$') @hook.command def wiki(inp): '''.w/.wiki <phrase> -- gets first sentence of wikipedia ''' \ - '''article on <phrase>''' + '''article on <phrase>''' x = http.get_xml(search_url, search=inp) @@ -31,7 +31,7 @@ def wiki(inp): def extract(item): return [item.find(ns + x).text for x in - ('Text', 'Description', 'Url')] + ('Text', 'Description', 'Url')] title, desc, url = extract(items[0]) diff --git a/plugins/yahooanswers.py b/plugins/yahooanswers.py index f6b23ab..97353af 100644 --- a/plugins/yahooanswers.py +++ b/plugins/yahooanswers.py @@ -1,6 +1,7 @@ from util import hook, http from random import choice + @hook.api_key('yahoo') @hook.command def answer(inp, api_key=None): @@ -9,10 +10,10 @@ def answer(inp, api_key=None): url = "http://answers.yahooapis.com/AnswersService/V1/questionSearch" result = http.get_json(url, - query=inp, - search_in="question", - output="json", - appid=api_key) + query=inp, + search_in="question", + output="json", + appid=api_key) questions = result.get("all", {}).get("questions", []) answered = filter(lambda x: x.get("ChosenAnswer", ""), questions) @@ -25,4 +26,3 @@ def answer(inp, api_key=None): response = "%s -- %s" % (link, answer) return " ".join(response.split()) - diff --git a/plugins/youtube.py b/plugins/youtube.py index 2d4f179..ede38c7 100644 --- a/plugins/youtube.py +++ b/plugins/youtube.py @@ -39,17 +39,17 @@ def get_video_description(vid_id): if 'rating' in j: out += ' - rated \x02%.2f/5.0\x02 (%d)' % (j['rating'], - j['ratingCount']) + j['ratingCount']) # The use of str.decode() prevents UnicodeDecodeError with some locales # See http://stackoverflow.com/questions/4082645/ if 'viewCount' in j: out += ' - \x02%s\x02 views' % locale.format('%d', - j['viewCount'], 1).decode(locale.getlocale()[1]) + j['viewCount'], 1).decode(locale.getlocale()[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)) + time.strftime("%Y.%m.%d", upload_time)) if 'contentRating' in j: out += ' - \x034NSFW\x02'