From 4a2b9efbe78d110fbab67550c6726da695e1368d Mon Sep 17 00:00:00 2001 From: "Sean B. Palmer" Date: Wed, 23 Feb 2011 21:32:37 +0000 Subject: [PATCH 1/5] Added reminders module. --- modules/calc.py | 4 +++- modules/head.py | 2 ++ modules/oblique.py | 6 +++--- modules/tell.py | 34 +++++++--------------------------- modules/wikipedia.py | 2 +- 5 files changed, 16 insertions(+), 32 deletions(-) diff --git a/modules/calc.py b/modules/calc.py index ab0f3fa..88ac814 100755 --- a/modules/calc.py +++ b/modules/calc.py @@ -78,6 +78,8 @@ def c(phenny, input): answer = [p for p in parts if p.startswith('rhs: "')][0][6:] if answer: answer = answer.decode('unicode-escape') + answer = ''.join(chr(ord(c)) for c in answer) + answer = answer.decode('utf-8') answer = answer.replace(u'\xc2\xa0', ',') answer = answer.replace('', '^(') answer = answer.replace('', ')') @@ -97,7 +99,7 @@ def py(phenny, input): py.commands = ['py'] def wa(phenny, input): - query = input.group(2) + query = input.group(2).encode('utf-8') uri = 'http://tumbolia.appspot.com/wa/' answer = web.get(uri + web.urllib.quote(query)) if answer: diff --git a/modules/head.py b/modules/head.py index 384b8df..1008628 100755 --- a/modules/head.py +++ b/modules/head.py @@ -26,6 +26,7 @@ def head(phenny, input): if not uri.startswith('htt'): uri = 'http://' + uri + # uri = uri.replace('#!', '?_escaped_fragment_=') try: info = web.head(uri) except IOError: return phenny.say("Can't connect to %s" % uri) @@ -80,6 +81,7 @@ def f_title(self, origin, match, args): if not ':' in uri: uri = 'http://' + uri + uri = uri.replace('#!', '?_escaped_fragment_=') try: redirects = 0 diff --git a/modules/oblique.py b/modules/oblique.py index 3622b4a..6d6156c 100755 --- a/modules/oblique.py +++ b/modules/oblique.py @@ -30,9 +30,9 @@ def mappings(uri): def service(phenny, input, command, args): t = o.services[command] - template = t.replace('${args}', urllib.quote(args.encode('utf-8'))) - template = template.replace('${nick}', urllib.quote(input.nick)) - uri = template.replace('${sender}', urllib.quote(input.sender)) + template = t.replace('${args}', urllib.quote(args.encode('utf-8'), '')) + template = template.replace('${nick}', urllib.quote(input.nick, '')) + uri = template.replace('${sender}', urllib.quote(input.sender, '')) info = web.head(uri) if isinstance(info, list): diff --git a/modules/tell.py b/modules/tell.py index 741c58d..5e61007 100755 --- a/modules/tell.py +++ b/modules/tell.py @@ -82,13 +82,13 @@ def f_remind(phenny, input): if not phenny.reminders.has_key(tellee): phenny.reminders[tellee] = [(teller, verb, timenow, msg)] else: - if len(phenny.reminders[tellee]) >= maximum: - warn = True + # if len(phenny.reminders[tellee]) >= maximum: + # warn = True phenny.reminders[tellee].append((teller, verb, timenow, msg)) # @@ Stephanie's augmentation response = "I'll pass that on when %s is around." % tellee_original - if warn: response += (" I'll have to use a pastebin, though, so " + - "your message may get lost.") + # if warn: response += (" I'll have to use a pastebin, though, so " + + # "your message may get lost.") rand = random.random() if rand > 0.9999: response = "yeah, yeah" @@ -138,29 +138,9 @@ def message(phenny, input): phenny.say(line) if reminders[maximum:]: - try: - if origin.sender in lispchannels: - chan = origin.sender - else: chan = 'None' - - result = web.post('http://paste.lisp.org/submit', - {'channel': chan, - 'username': phenny.nick, - 'title': 'Further Messages for %s' % tellee, - 'colorize': 'None', - 'text': '\n'.join(reminders[maximum:]) + '\n', - 'captcha': 'lisp', - 'captchaid': 'bdf447484f62a3e8b23816f9acee79d9' - } - ) - uris = re.findall('http://paste.lisp.org/display/\d+', result) - uri = list(reversed(uris)).pop() - if not origin.sender in lispchannels: - message = '%s: see %s for further messages' % (tellee, uri) - phenny.say(message) - except: - error = '[Sorry, some messages were elided and lost...]' - phenny.say(error) + phenny.say('Further messages sent privately') + for line in reminders[maximum:]: + phenny.msg(tellee, line) if len(phenny.reminders.keys()) != remkeys: dumpReminders(phenny.tell_filename, phenny.reminders) # @@ tell diff --git a/modules/wikipedia.py b/modules/wikipedia.py index 0542067..30a23f3 100755 --- a/modules/wikipedia.py +++ b/modules/wikipedia.py @@ -24,7 +24,7 @@ r_redirect = re.compile( abbrs = ['etc', 'ca', 'cf', 'Co', 'Ltd', 'Inc', 'Mt', 'Mr', 'Mrs', 'Dr', 'Ms', 'Rev', 'Fr', 'St', 'Sgt', 'pron', 'approx', 'lit', - 'syn', 'transl', 'sess', 'fl', 'Op'] \ + 'syn', 'transl', 'sess', 'fl', 'Op', 'Dec'] \ + list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') \ + list('abcdefghijklmnopqrstuvwxyz') t_sentence = r'^.{5,}?(? Date: Thu, 24 Feb 2011 17:30:07 +0000 Subject: [PATCH 2/5] Added the reminders module. --- modules/remind.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100755 modules/remind.py diff --git a/modules/remind.py b/modules/remind.py new file mode 100755 index 0000000..ec1a4d1 --- /dev/null +++ b/modules/remind.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +""" +remind.py - Phenny Reminder Module +Copyright 2011, Sean B. Palmer, inamidst.com +Licensed under the Eiffel Forum License 2. + +http://inamidst.com/phenny/ +""" + +import os, re, time, threading + +def filename(self): + name = self.nick + '-' + self.config.host + '.reminders.db' + return os.path.join(os.path.expanduser('~/.phenny'), name) + +def load_database(name): + data = {} + if os.path.isfile(name): + f = open(name, 'rb') + for line in f: + unixtime, channel, nick, message = line.split('\t') + message = message.rstrip('\n') + t = int(unixtime) + reminder = (channel, nick, message) + try: data[t].append(reminder) + except KeyError: data[t] = [reminder] + f.close() + return data + +def dump_database(name, data): + f = open(name, 'wb') + for unixtime, reminders in data.iteritems(): + for channel, nick, message in reminders: + f.write('%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message)) + f.close() + +def setup(phenny): + phenny.rfn = filename(phenny) + phenny.rdb = load_database(phenny.rfn) + + def monitor(phenny): + time.sleep(5) + while True: + now = int(time.time()) + unixtimes = [int(key) for key in phenny.rdb] + oldtimes = [t for t in unixtimes if t <= now] + if oldtimes: + for oldtime in oldtimes: + for (channel, nick, message) in phenny.rdb[oldtime]: + if message: + phenny.msg(channel, nick + ': ' + message) + else: phenny.msg(channel, nick + '!') + del phenny.rdb[oldtime] + dump_database(phenny.rfn, phenny.rdb) + time.sleep(2.5) + + targs = (phenny,) + t = threading.Thread(target=monitor, args=targs) + t.start() + +scaling = { + 'years': 365.25 * 24 * 3600, + 'year': 365.25 * 24 * 3600, + 'yrs': 365.25 * 24 * 3600, + 'y': 365.25 * 24 * 3600, + + 'months': 29.53059 * 24 * 3600, + 'month': 29.53059 * 24 * 3600, + 'mo': 29.53059 * 24 * 3600, + + 'weeks': 7 * 24 * 3600, + 'week': 7 * 24 * 3600, + 'wks': 7 * 24 * 3600, + 'wk': 7 * 24 * 3600, + 'w': 7 * 24 * 3600, + + 'days': 24 * 3600, + 'day': 24 * 3600, + 'd': 24 * 3600, + + 'hours': 3600, + 'hour': 3600, + 'hrs': 3600, + 'hr': 3600, + 'h': 3600, + + 'minutes': 60, + 'minute': 60, + 'mins': 60, + 'min': 60, + 'm': 60, + + 'seconds': 1, + 'second': 1, + 'secs': 1, + 'sec': 1, + 's': 1 +} + +periods = '|'.join(scaling.keys()) +p_command = r'\.in ([0-9]+(?:\.[0-9]+)?)\s?((?:%s)\b)?:?\s?(.*)' % periods +r_command = re.compile(p_command) + +def remind(phenny, input): + m = r_command.match(input.bytes) + if not m: + return phenny.reply("Sorry, didn't understand the input.") + length, scale, message = m.groups() + + length = float(length) + factor = scaling.get(scale, 60) + duration = length * factor + + if duration % 1: + duration = int(duration) + 1 + else: duration = int(duration) + + t = int(time.time()) + duration + reminder = (input.sender, input.nick, message) + + try: phenny.rdb[t].append(reminder) + except KeyError: phenny.rdb[t] = [reminder] + + dump_database(phenny.rfn, phenny.rdb) + + if duration >= 60: + w = '' + if duration >= 3600 * 12: + w += time.strftime(' on %d %b %Y', time.gmtime(t)) + w += time.strftime(' at %H:%MZ', time.gmtime(t)) + phenny.reply('Okay, will remind%s' % w) + else: phenny.reply('Okay, will remind in %s secs' % duration) +remind.commands = ['in'] + +if __name__ == '__main__': + print __doc__.strip() From ecb8af1bbea0dee31903b1e962c0b731f2d36318 Mon Sep 17 00:00:00 2001 From: "Sean B. Palmer" Date: Thu, 24 Feb 2011 18:57:21 +0000 Subject: [PATCH 3/5] Better reload function. --- modules/reload.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/reload.py b/modules/reload.py index fc508a6..50e1a56 100755 --- a/modules/reload.py +++ b/modules/reload.py @@ -7,6 +7,7 @@ Licensed under the Eiffel Forum License 2. http://inamidst.com/phenny/ """ +import sys, os.path, time, imp import irc def f_reload(phenny, input): @@ -21,18 +22,23 @@ def f_reload(phenny, input): phenny.setup() return phenny.reply('done') - try: module = getattr(__import__('modules.' + name), name) - except ImportError: - module = getattr(__import__('opt.' + name), name) - reload(module) + if not sys.modules.has_key(name): + return phenny.reply('%s: no such module!' % name) + + # Thanks to moot for prodding me on this + path = sys.modules[name].__file__ + if path.endswith('.pyc') or path.endswith('.pyo'): + path = path[:-1] + if not os.path.isfile(path): + return phenny.reply('Found %s, but not the source file' % name) + + module = imp.load_source(name, path) + sys.modules[name] = module if hasattr(module, 'setup'): module.setup(phenny) - if hasattr(module, '__file__'): - import os.path, time - mtime = os.path.getmtime(module.__file__) - modified = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(mtime)) - else: modified = 'unknown' + mtime = os.path.getmtime(module.__file__) + modified = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(mtime)) phenny.register(vars(module)) phenny.bind_commands() From 18a24a81174d84941e74ddbf5864fb1b8d2948bc Mon Sep 17 00:00:00 2001 From: David Moore Date: Sat, 5 Mar 2011 15:02:22 -0600 Subject: [PATCH 4/5] add cookie support to head.py, for e.g. nytimes urls --- modules/head.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/head.py b/modules/head.py index 1008628..5231edb 100755 --- a/modules/head.py +++ b/modules/head.py @@ -7,11 +7,15 @@ Licensed under the Eiffel Forum License 2. http://inamidst.com/phenny/ """ -import re, urllib, urllib2, httplib, urlparse, time +import re, urllib, urllib2, httplib, urlparse, time, cookielib from htmlentitydefs import name2codepoint import web from tools import deprecated +cj = cookielib.LWPCookieJar() +opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) +urllib2.install_opener(opener) + def head(phenny, input): """Provide HTTP HEAD information.""" uri = input.group(2) From ff2434db414603251cbcbe5f965c58c7e775ea92 Mon Sep 17 00:00:00 2001 From: David Moore Date: Sat, 5 Mar 2011 19:51:52 -0600 Subject: [PATCH 5/5] added uri snarfing with automatic title reading --- modules/head.py | 58 +++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/modules/head.py b/modules/head.py index 5231edb..096b9a9 100755 --- a/modules/head.py +++ b/modules/head.py @@ -9,6 +9,7 @@ http://inamidst.com/phenny/ import re, urllib, urllib2, httplib, urlparse, time, cookielib from htmlentitydefs import name2codepoint +from string import join import web from tools import deprecated @@ -82,7 +83,32 @@ def f_title(self, origin, match, args): uri = self.last_seen_uri.get(origin.sender) if not uri: return self.msg(origin.sender, 'I need a URI to give the title of...') + title = gettitle(uri) + if title: + self.msg(origin.sender, origin.nick + ': ' + title) + else: self.msg(origin.sender, origin.nick + ': No title found') +f_title.commands = ['title'] +def noteuri(phenny, input): + uri = input.group(1).encode('utf-8') + if not hasattr(phenny.bot, 'last_seen_uri'): + phenny.bot.last_seen_uri = {} + phenny.bot.last_seen_uri[input.sender] = uri +noteuri.rule = r'.*(http[s]?://[^<> "\x01]+)[,.]?' +noteuri.priority = 'low' + +titlecommands = r'(?:' + join(f_title.commands, r'|') + r')' +def snarfuri(phenny, input): + if re.match(r'(?i)' + phenny.config.prefix + titlecommands, input.group()): + return + uri = input.group(1).encode('utf-8') + title = gettitle(uri) + if title: + phenny.msg(input.sender, '[ ' + title + ' ]') +snarfuri.rule = r'.*(http[s]?://[^<> "\x01]+)[,.]?' +snarfuri.priority = 'low' + +def gettitle(uri): if not ':' in uri: uri = 'http://' + uri uri = uri.replace('#!', '?_escaped_fragment_=') @@ -98,7 +124,6 @@ def f_title(self, origin, match, args): u = urllib2.urlopen(req) info = u.info() u.close() - # info = web.head(uri) if not isinstance(info, list): status = '200' @@ -111,23 +136,19 @@ def f_title(self, origin, match, args): redirects += 1 if redirects >= 25: - self.msg(origin.sender, origin.nick + ": Too many redirects") - return + return None try: mtype = info['content-type'] except: - err = ": Couldn't get the Content-Type, sorry" - return self.msg(origin.sender, origin.nick + err) - if not (('/html' in mtype) or ('/xhtml' in mtype)): - self.msg(origin.sender, origin.nick + ": Document isn't HTML") - return + return None + if not (('/html' in mtype) or ('/xhtml' in mtype)): + return None u = urllib2.urlopen(req) bytes = u.read(262144) u.close() except IOError: - self.msg(origin.sender, "Can't connect to %s" % uri) return m = r_title.search(bytes) @@ -161,21 +182,10 @@ def f_title(self, origin, match, args): try: title = title.decode('iso-8859-1').encode('utf-8') except: title = title.decode('cp1252').encode('utf-8') else: pass - else: title = '[The title is empty.]' - - title = title.replace('\n', '') - title = title.replace('\r', '') - self.msg(origin.sender, origin.nick + ': ' + title) - else: self.msg(origin.sender, origin.nick + ': No title found') -f_title.commands = ['title'] - -def noteuri(phenny, input): - uri = input.group(1).encode('utf-8') - if not hasattr(phenny.bot, 'last_seen_uri'): - phenny.bot.last_seen_uri = {} - phenny.bot.last_seen_uri[input.sender] = uri -noteuri.rule = r'.*(http[s]?://[^<> "\x01]+)[,.]?' -noteuri.priority = 'low' + title = title.replace('\n', '') + title = title.replace('\r', '') + else: title = None + return title if __name__ == '__main__': print __doc__.strip()