diff --git a/core/main.py b/core/main.py index 7aaf512..883effd 100644 --- a/core/main.py +++ b/core/main.py @@ -24,7 +24,7 @@ class Input(dict): def pm(msg): conn.msg(nick, msg) - + def set_nick(nick): conn.set_nick(nick) @@ -137,7 +137,7 @@ def match_command(command): return prefix[0] elif prefix and command not in prefix: return prefix - + return command @@ -166,7 +166,7 @@ def main(conn, out): if isinstance(command, list): # multiple potential matches input = Input(conn, *out) - input.reply("did you mean %s or %s?" % + input.reply("did you mean %s or %s?" % (', '.join(command[:-1]), command[-1])) elif command in bot.commands: input = Input(conn, *out) diff --git a/plugins/dice.py b/plugins/dice.py index 02fd231..0279fb7 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -18,7 +18,7 @@ split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I) def nrolls(count, n): "roll an n-sided die count times" if n == "F": - return [random.randint(-1,1) for x in xrange(count)] + return [random.randint(-1, 1) for x in xrange(min(count, 100))] if n < 2: # it's a coin if count < 5000: return [random.randint(0, 1) for x in xrange(count)] @@ -38,13 +38,15 @@ def nrolls(count, n): def dice(inp): ".dice -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \ "D20s, subtract 1D5, add 4" - desc = None - try: # if inp is a re.match object... + + try: # if inp is a re.match object... (inp, desc) = inp.groups() except AttributeError: - pass # we got called via hook.command, inp is already the roll - if desc == None: (inp, desc) = valid_diceroll_re.match(inp).groups() - if "d" not in inp: return + (inp, desc) = valid_diceroll_re.match(inp).groups() + + if "d" not in inp: + return + spec = whitespace_re.sub('', inp) if not valid_diceroll_re.match(spec): return "Invalid diceroll" @@ -56,7 +58,7 @@ def dice(inp): for roll in groups: count, side = split_re.match(roll).groups() count = int(count) if count not in " +-" else 1 - if side.upper() == "F": # fudge dice are basically 1d3-2 + if side.upper() == "F": # fudge dice are basically 1d3-2 for fudge in nrolls(count, "F"): if fudge == 1: rolls.append("\x033+\x0F") diff --git a/plugins/dictionary.py b/plugins/dictionary.py index 755e98e..8c22175 100644 --- a/plugins/dictionary.py +++ b/plugins/dictionary.py @@ -7,7 +7,7 @@ from util import hook, http @hook.command def urban(inp): '''.u/.urban -- looks up on urbandictionary.com''' - + url = 'http://www.urbandictionary.com/define.php' page = http.get_html(url, term=inp) words = page.xpath("//td[@class='word']") @@ -44,7 +44,7 @@ def define(inp): def format_output(show_examples): result = '%s: ' % h.xpath('//dt[@class="title-word"]/a/text()')[0] - + correction = h.xpath('//span[@class="correct-word"]/text()') if correction: result = 'definition for "%s": ' % correction[0] @@ -70,7 +70,7 @@ def define(inp): synonyms = h.xpath('//dd[@class="synonyms"]') if synonyms: result += synonyms[0].text_content() - + result = re.sub(r'\s+', ' ', result) result = re.sub('\xb0', '', result) return result @@ -101,7 +101,7 @@ def etymology(inp): return 'No etymology found for ' + inp etym = etym[0].text_content() - + etym = ' '.join(etym.split()) if len(etym) > 400: diff --git a/plugins/drama.py b/plugins/drama.py index 681c3d8..7ad3631 100644 --- a/plugins/drama.py +++ b/plugins/drama.py @@ -12,7 +12,7 @@ ed_url = "http://encyclopediadramatica.com/" def drama(inp): '''.drama -- gets first paragraph of Encyclopedia Dramatica ''' \ '''article on ''' - + j = http.get_json(api_url, search=inp) if not j[1]: return 'no results found' diff --git a/plugins/google.py b/plugins/google.py index 34d0b64..caa9bb5 100644 --- a/plugins/google.py +++ b/plugins/google.py @@ -37,14 +37,13 @@ def google(inp): result = parsed['responseData']['results'][0] - title = result['titleNoFormatting'] - content = result['content'] + content = result['content'] if len(content) == 0: - content = "No description available" + content = "No description available" else: - content = http.html.fromstring(content).text_content() + content = http.html.fromstring(content).text_content() out = '%s -- \x02%s\x02: "%s"' % (result['unescapedUrl'], title, content) out = ' '.join(out.split()) diff --git a/plugins/misc.py b/plugins/misc.py index 8ca04e1..7094a76 100644 --- a/plugins/misc.py +++ b/plugins/misc.py @@ -48,7 +48,7 @@ def version(inp, notice=None): p = subprocess.Popen(['git', 'log', '--oneline'], stdout=subprocess.PIPE) stdout, _ = p.communicate() p.wait() - + revnumber = len(stdout.splitlines()) ret = stdout.split(None, 1)[0] diff --git a/plugins/profile.py b/plugins/profile.py index d556689..7f9f06e 100644 --- a/plugins/profile.py +++ b/plugins/profile.py @@ -6,6 +6,6 @@ from util import hook @hook.command def profile(inp): ".profile -- links to 's profile on SA" - + return 'http://forums.somethingawful.com/member.php?action=getinfo' + \ '&username=' + '+'.join(inp.split()) diff --git a/plugins/somethingawful.py b/plugins/somethingawful.py index d332ad8..fda1755 100644 --- a/plugins/somethingawful.py +++ b/plugins/somethingawful.py @@ -4,6 +4,7 @@ from util import hook, http thread_re = r"(?i)forums\.somethingawful\.com/\S+threadid=(\d+)" showthread = "http://forums.somethingawful.com/showthread.php?noseen=1" + def login(user, password): http.jar.clear_expired_cookies() if any(cookie.domain == 'forums.somethingawful.com' and @@ -13,7 +14,7 @@ def login(user, password): return assert("malformed cookie jar") 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.regex(thread_re) @@ -24,7 +25,7 @@ def forum_link(inp, bot=None): login(bot.config['sa_user'], bot.config['sa_password']) - thread = http.get_html(showthread, threadid=inp.group(1), perpage='1', + thread = http.get_html(showthread, threadid=inp.group(1), perpage='1', cookies=True) breadcrumbs = thread.xpath('//div[@class="breadcrumbs"]//a/text()') @@ -46,8 +47,8 @@ def forum_link(inp, bot=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/suggest.py b/plugins/suggest.py index cff9a8d..f3e69bc 100644 --- a/plugins/suggest.py +++ b/plugins/suggest.py @@ -8,7 +8,7 @@ from util import hook, http @hook.command def suggest(inp, inp_unstripped=''): ".suggest [#n] -- gets a random/the nth suggested google search" - + inp = inp_unstripped m = re.match('^#(\d+) (.+)$', inp) if m: diff --git a/plugins/tvdb.py b/plugins/tvdb.py index bd24060..a12480b 100644 --- a/plugins/tvdb.py +++ b/plugins/tvdb.py @@ -15,7 +15,7 @@ api_key = "D1EBA6781E2572BB" @hook.command def tv_next(inp): ".tv_next -- get the next episode of from thetvdb.com" - + # http://thetvdb.com/wiki/index.php/API:GetSeries query = http.get_xml(base_url + 'GetSeries.php', seriesname=inp) series_id = query.xpath('//seriesid/text()') @@ -34,19 +34,19 @@ def tv_next(inp): next_eps = [] today = datetime.date.today() - + for episode in reversed(series.xpath('//Episode')): - first_aired = episode.findtext("FirstAired") + first_aired = episode.findtext("FirstAired") try: airdate = datetime.date(*map(int, first_aired.split('-'))) except (ValueError, TypeError): continue - + episode_name = episode.findtext("EpisodeName") or "No Title Yet" episode_num = "S%02dE%02d" % (int(episode.findtext("SeasonNumber")), int(episode.findtext("EpisodeNumber"))) episode_desc = '%s "%s"' % (episode_num, episode_name) - + if airdate > today: next_eps = ['%s (%s)' % (first_aired, episode_desc)] elif airdate == today: @@ -55,11 +55,12 @@ def tv_next(inp): #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: return "there are no new episodes scheduled for %s" % series_name - + if len(next_eps) == 1: return "the next episode of %s airs %s" % (series_name, next_eps[0]) else: - return "the next episodes of %s: %s" % (series_name, ", ".join(next_eps)) + next_eps = ', '.join(next_eps) + return "the next episodes of %s: %s" % (series_name, next_eps) diff --git a/plugins/twitter.py b/plugins/twitter.py index c4d2ebf..8fd8dc9 100644 --- a/plugins/twitter.py +++ b/plugins/twitter.py @@ -122,7 +122,7 @@ def twitter(inp): 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): - add_reply(reply_name, reply_id if reply_id else -1) + add_reply(reply_name, reply_id or -1) time = strftime('%Y-%m-%d %H:%M:%S', strptime(time.text, diff --git a/plugins/util/urlnorm.py b/plugins/util/urlnorm.py index 024fc32..4089710 100644 --- a/plugins/util/urlnorm.py +++ b/plugins/util/urlnorm.py @@ -23,13 +23,16 @@ inspired by: __license__ = "Python" -import re, unicodedata, urlparse +import re +import unicodedata +import urlparse from urllib import quote, unquote default_port = { 'http': 80, } + class Normalizer(object): def __init__(self, regex, normalize_func): self.regex = regex @@ -37,84 +40,92 @@ class Normalizer(object): normalizers = ( Normalizer( re.compile(r'(?:https?://)?(?:[a-zA-Z0-9\-]+\.)?(?:amazon|amzn){1}\.(?P[a-zA-Z\.]{2,})\/(gp/(?:product|offer-listing|customer-media/product-gallery)/|exec/obidos/tg/detail/-/|o/ASIN/|dp/|(?:[A-Za-z0-9\-]+)/dp/)?(?P[0-9A-Za-z]{10})'), lambda m: r'http://amazon.%s/dp/%s' % (m.group('tld'), m.group('ASIN'))), - Normalizer( re.compile(r'.*waffleimages\.com.*/([0-9a-fA-F]{40})'), + Normalizer( re.compile(r'.*waffleimages\.com.*/([0-9a-fA-F]{40})'), lambda m: r'http://img.waffleimages.com/%s' % m.group(1) ), Normalizer( re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-z0-9]+)'), lambda m: r'http://youtube.com/watch?v=%s' % m.group(1) ), ) + def normalize(url): """Normalize a URL.""" scheme, auth, path, query, fragment = urlparse.urlsplit(url.strip()) - userinfo, host, port=re.search('([^@]*@)?([^:]*):?(.*)', auth).groups() + userinfo, host, port = re.search('([^@]*@)?([^:]*):?(.*)', auth).groups() # Always provide the URI scheme in lowercase characters. scheme = scheme.lower() # Always provide the host, if any, in lowercase characters. host = host.lower() - if host and host[-1] == '.': host = host[:-1] - if host and host.startswith("www."): - if not scheme: scheme = "http" + if host and host[-1] == '.': + host = host[:-1] + if host and host.startswith("www."): + if not scheme: + scheme = "http" host = host[4:] elif path and path.startswith("www."): - if not scheme: scheme = "http" + if not scheme: + scheme = "http" path = path[4:] # Only perform percent-encoding where it is essential. # Always use uppercase A-through-F characters when percent-encoding. # All portions of the URI must be utf-8 encoded NFC from Unicode strings def clean(string): - string=unicode(unquote(string), 'utf-8', 'replace') + string = unicode(unquote(string), 'utf-8', 'replace') return unicodedata.normalize('NFC', string).encode('utf-8') - path=quote(clean(path), "~:/?#[]@!$&'()*+,;=") - fragment=quote(clean(fragment), "~") + path = quote(clean(path), "~:/?#[]@!$&'()*+,;=") + fragment = quote(clean(fragment), "~") # note care must be taken to only encode & and = characters as values - query="&".join(["=".join([quote(clean(t), "~:/?#[]@!$'()*+,;=") + query = "&".join(["=".join([quote(clean(t), "~:/?#[]@!$'()*+,;=") for t in q.split("=", 1)]) for q in query.split("&")]) # Prevent dot-segments appearing in non-relative URI paths. if scheme in ["", "http", "https", "ftp", "file"]: - output=[] + output = [] for input in path.split('/'): - if input=="": - if not output: output.append(input) - elif input==".": + if input == "": + if not output: + output.append(input) + elif input == ".": pass - elif input=="..": - if len(output)>1: output.pop() + elif input == "..": + if len(output) > 1: + output.pop() else: output.append(input) - if input in ["", ".", ".."]: output.append("") - path='/'.join(output) + if input in ["", ".", ".."]: + output.append("") + path = '/'.join(output) # For schemes that define a default authority, use an empty authority if # the default is desired. - if userinfo in ["@", ":@"]: userinfo="" + if userinfo in ["@", ":@"]: + userinfo = "" # For schemes that define an empty path to be equivalent to a path of "/", # use "/". - if path=="" and scheme in ["http", "https", "ftp", "file"]: - path="/" + if path == "" and scheme in ["http", "https", "ftp", "file"]: + path = "/" # For schemes that define a port, use an empty port if the default is # desired if port and scheme in default_port.keys(): if port.isdigit(): - port=str(int(port)) - if int(port)==default_port[scheme]: + port = str(int(port)) + if int(port) == default_port[scheme]: port = '' # Put it all back together again - auth=(userinfo or "") + host - if port: - auth+=":"+port - if url.endswith("#") and query == "" and fragment == "": + auth = (userinfo or "") + host + if port: + auth += ":" + port + if url.endswith("#") and query == "" and fragment == "": path += "#" - normal_url = urlparse.urlunsplit((scheme, auth, path, query, fragment)).replace( - "http:///", "http://") + normal_url = urlparse.urlunsplit((scheme, auth, path, query, + fragment)).replace("http:///", "http://") for norm in normalizers: m = norm.regex.match(normal_url) if m: