diff -Naur phenny-2005-09-08/config.py phenny-2005-10-03/config.py --- phenny-2005-09-08/config.py 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/config.py 2005-10-03 03:37:48.000000000 +0100 @@ -0,0 +1,30 @@ +#!/usr/bin/env python +""" +config.py - Configuration File +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +# Default nickname for phenny +nick = 'phenny' + +# Host to run phenny on +host = 'irc.freenode.net' + +# Directory to store all the data. Must be writeable +datadir = '/home/sbp/phenny/data' + +# Channels for phenny to join +channels = ( + '#swhack', '#swig', '#foaf', '#validator', '#julie', + '#rowr', '#joiito', '#ud', '#d8uv.com', '#wordpress', + '#wikidb', '#svg', '#suwcharman', '#esp', '#jeanniecool', + '#4suite', '#cym' +) + +# Channels for phenny to join in test mode +testchannels = ( + '#d8uv.com', '#inamidst' +) + +# [EOF] diff -Naur phenny-2005-09-08/irc.py phenny-2005-10-03/irc.py --- phenny-2005-09-08/irc.py 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/irc.py 2005-10-03 03:37:22.000000000 +0100 @@ -0,0 +1,193 @@ +#!/usr/bin/env python +""" +ircbot.py - A Utility IRC Bot +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +import sys, re, time, threading +import socket, asyncore, asynchat + +class Origin(object): + def __init__(self, origin): + self.origin = origin + self.split(origin) + + def __str__(self): + return self.origin + + def split(self, origin): + if origin and '!' in origin: + self.nick, userHost = origin.split('!', 1) + if '@' in userHost: + self.user, self.host = userHost.split('@', 1) + else: self.user, self.host = userHost, None + else: self.nick, self.user, self.host = origin, None, None + + def setSender(self, nickname, args): + if (not args) or (len(args) < 2): return + target = args[1] + if target == nickname: + self.sender = self.nick + else: self.sender = target + +class Bot(asynchat.async_chat): + def __init__(self, nick, **kargs): + asynchat.async_chat.__init__(self) + self.buffer = '' + self.set_terminator('\r\n') + + self.nick, self.uid, self.name = nick, nick, nick + self.channels = kargs.get('channels', ['#test']) + self.protect = kargs.get('protect', False) + if kargs.has_key('stats') and not kargs['stats']: + self.stats = None + else: self.stats = {} + self.msgstack = [] + self.bindrules() + + def run(self, host, port=6667): + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + print >> sys.stderr, "Connecting to %s:%s..." % (host, port), + self.connect((host, port)) + asyncore.loop() + + def write(self, args, text=None): + if text is not None: + self.push(' '.join(args) + ' :' + text + '\r\n') + else: self.push(' '.join(args) + '\r\n') + + def handle_connect(self): + print >> sys.stderr, "connected!" + self.write(('NICK', self.nick)) + self.write(('USER', self.uid, '+iw', self.nick), self.name) + for channel in self.channels: + self.write(('JOIN', channel)) + + def collect_incoming_data(self, data): + self.buffer += data + + def found_terminator(self): + line = self.buffer + self.buffer = '' + + if line.startswith(':'): + origin, line = line[1:].split(' ', 1) + origin = Origin(origin) + else: origin = None + + if ' :' in line: + args, text = line.split(' :', 1) + else: args, text = line, '' + args = args.split() + + if origin: origin.setSender(self.nick, args) + args = tuple([text] + args) + self.dispatch(origin, args) + + def bindrules(self): + self.rules = {} + self.doc = {} + + for name in dir(self): + method = getattr(self, name) + if hasattr(method, 'rule'): + method.rule = method.rule.replace('$nick', self.nick) + if hasattr(method, '__doc__'): + if method.__doc__: + self.doc[name] = method.__doc__ + thread = hasattr(method, 'thread') + + if hasattr(method, 'command'): + command = method.command + else: command = 'PRIVMSG' + + args = (name, method, re.compile(method.rule), thread) + if self.rules.has_key(command): + self.rules[command].append(args) + else: self.rules[command] = [args] + + def dispatch(self, origin, args): + if self.rules.has_key(args[1]): + for (name, method, pattern, thread) in self.rules[args[1]]: + match = pattern.search(args[0]) + if match: + if thread: + def target(self, method, origin, match, args): + time.sleep(0.15) # An odd threading bug + try: + target.completed or \ + time.sleep(0.15) or \ + method(origin, match, args) + except Exception, e: + self.msg(origin.sender, "%s: %s" % (e.__class__, e)) + target.completed = True + time.sleep(0.5) + target.completed = False + + targs = (self, method, origin, match, args) + t = threading.Thread(target=target, args=targs) + t.start() + elif self.protect: + try: method(origin, match, args) + except Exception, e: + self.msg(origin.sender, "%s: %s" % (e.__class__, e)) + else: method(origin, match, args) + + if self.stats is not None: + self.stats.setdefault(name, {}) + self.stats[name].setdefault(origin.sender, 1) + self.stats[name][origin.sender] += 1 + + if args[1] == 'PING': + self.write(('PONG', args[0])) + + def msg(self, dest, text): + # Flood protection + if self.msgstack.count(text) >= 5: + text = '...' + if self.msgstack.count('...') >= 2: + if text == '...': return + if len(''.join(self.msgstack[:-3])) > 200: + time.sleep(2) + + self.msgstack = self.msgstack[-9:] + self.msgstack.append(text) + self.write(('PRIVMSG', dest), text) + + def notice(self, dest, text): + self.write(('NOTICE', dest), text) + + def msglines(self, channel, lines): + for line in lines: + if line: self.msg(channel, line) + time.sleep(1) + if len(line) > 50: time.sleep(0.7) + +def register(bot, variables): + for name, obj in variables.iteritems(): + if hasattr(obj, 'rule'): + def wrapper(origin, match, args, bot=bot, obj=obj): + obj(bot, origin, match, args) + for key, value in vars(obj).iteritems(): + setattr(wrapper, key, value) + if hasattr(obj, '__doc__'): + wrapper.__doc__ = obj.__doc__ + setattr(bot, name, wrapper) + +class TestBot(Bot): + def f_ping(self, origin, match, args): + delay = m.group(1) + if delay is not None: + import time + time.sleep(int(delay)) + self.msg(origin.sender, 'pong (%s)' % delay) + else: self.msg(origin.sender, 'pong') + f_ping.rule = r'^\.ping(?:[ \t]+(\d+))?$' + +def main(): + bot = TestBot('testbot', ['#d8uv.com']) + bot.run('irc.freenode.net') + +if __name__=="__main__": + main() diff -Naur phenny-2005-09-08/ircbot.py phenny-2005-10-03/ircbot.py --- phenny-2005-09-08/ircbot.py 2005-06-27 20:22:49.000000000 +0100 +++ phenny-2005-10-03/ircbot.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -#!/usr/bin/env python -""" -ircbot.py - A Utility IRC Bot -Author: Sean B. Palmer, inamidst.com -""" - -import sys, re, time, threading -import socket, asyncore, asynchat - -class Origin(object): - def __init__(self, origin): - self.origin = origin - self.split(origin) - - def __str__(self): - return self.origin - - def split(self, origin): - if origin and '!' in origin: - self.nick, userHost = origin.split('!', 1) - if '@' in userHost: - self.user, self.host = userHost.split('@', 1) - else: self.user, self.host = userHost, None - else: self.nick, self.user, self.host = origin, None, None - - def setSender(self, nickname, args): - if (not args) or (len(args) < 2): return - target = args[1] - if target == nickname: - self.sender = self.nick - else: self.sender = target - -class IRCBot(asynchat.async_chat): - def __init__(self, nick, **kargs): - asynchat.async_chat.__init__(self) - self.buffer = '' - self.set_terminator('\r\n') - - self.nick, self.uid, self.name = nick, nick, nick - self.channels = kargs.get('channels', ['#test']) - self.protect = kargs.get('protect', False) - if kargs.has_key('stats') and not kargs['stats']: - self.stats = None - else: self.stats = {} - self.msgstack = [] - self.bindrules() - - def run(self, host, port=6667): - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - print >> sys.stderr, "Connecting to %s:%s..." % (host, port), - self.connect((host, port)) - asyncore.loop() - - def write(self, args, text=None): - if text is not None: - self.push(' '.join(args) + ' :' + text + '\r\n') - else: self.push(' '.join(args) + '\r\n') - - def handle_connect(self): - print >> sys.stderr, "connected!" - self.write(('NICK', self.nick)) - self.write(('USER', self.uid, '+iw', self.nick), self.name) - for channel in self.channels: - self.write(('JOIN', channel)) - - def collect_incoming_data(self, data): - self.buffer += data - - def found_terminator(self): - line = self.buffer - self.buffer = '' - - if line.startswith(':'): - origin, line = line[1:].split(' ', 1) - origin = Origin(origin) - else: origin = None - - if ' :' in line: - args, text = line.split(' :', 1) - else: args, text = line, '' - args = args.split() - - if origin: origin.setSender(self.nick, args) - args = tuple([text] + args) - self.dispatch(origin, args) - - def bindrules(self): - self.rules = {} - self.doc = {} - - for name in dir(self): - method = getattr(self, name) - if hasattr(method, 'rule'): - method.rule = method.rule.replace('$nick', self.nick) - if hasattr(method, '__doc__'): - if method.__doc__: - self.doc[name] = method.__doc__ - thread = hasattr(method, 'thread') - - if hasattr(method, 'command'): - command = method.command - else: command = 'PRIVMSG' - - args = (name, method, re.compile(method.rule), thread) - if self.rules.has_key(command): - self.rules[command].append(args) - else: self.rules[command] = [args] - - def dispatch(self, origin, args): - if self.rules.has_key(args[1]): - for (name, method, pattern, thread) in self.rules[args[1]]: - match = pattern.search(args[0]) - if match: - if thread: - def target(self, method, origin, match, args): - time.sleep(0.15) # An odd threading bug - try: - target.completed or \ - time.sleep(0.15) or \ - method(origin, match, args) - except Exception, e: - self.msg(origin.sender, "%s: %s" % (e.__class__, e)) - target.completed = True - time.sleep(0.5) - target.completed = False - - targs = (self, method, origin, match, args) - t = threading.Thread(target=target, args=targs) - t.start() - elif self.protect: - try: method(origin, match, args) - except Exception, e: - self.msg(origin.sender, "%s: %s" % (e.__class__, e)) - else: method(origin, match, args) - - if self.stats is not None: - self.stats.setdefault(name, {}) - self.stats[name].setdefault(origin.sender, 1) - self.stats[name][origin.sender] += 1 - - if args[1] == 'PING': - self.write(('PONG', args[0])) - - def msg(self, dest, text): - # Flood protection - if self.msgstack.count(text) >= 5: - text = '...' - if self.msgstack.count('...') >= 2: - if text == '...': return - if len(''.join(self.msgstack[:-3])) > 200: - time.sleep(2) - - self.msgstack = self.msgstack[-9:] - self.msgstack.append(text) - self.write(('PRIVMSG', dest), text) - - def notice(self, dest, text): - self.write(('NOTICE', dest), text) - - def msglines(self, channel, lines): - for line in lines: - if line: self.msg(channel, line) - time.sleep(1) - if len(line) > 50: time.sleep(0.7) - -def register(bot, variables): - for name, obj in variables.iteritems(): - if hasattr(obj, 'rule'): - def wrapper(origin, match, args, bot=bot, obj=obj): - obj(bot, origin, match, args) - for key, value in vars(obj).iteritems(): - setattr(wrapper, key, value) - if hasattr(obj, '__doc__'): - wrapper.__doc__ = obj.__doc__ - setattr(bot, name, wrapper) - -class TestBot(IRCBot): - def f_ping(self, origin, match, args): - delay = m.group(1) - if delay is not None: - import time - time.sleep(int(delay)) - self.msg(origin.sender, 'pong (%s)' % delay) - else: self.msg(origin.sender, 'pong') - f_ping.rule = r'^\.ping(?:[ \t]+(\d+))?$' - -def main(): - bot = TestBot('testbot', ['#d8uv.com']) - bot.run('irc.freenode.net') - -if __name__=="__main__": - main() diff -Naur phenny-2005-09-08/manifest phenny-2005-10-03/manifest --- phenny-2005-09-08/manifest 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/manifest 2005-10-03 03:20:10.000000000 +0100 @@ -0,0 +1,3 @@ +phenny/*.py +phenny/manifest +phenny/modules/*.py diff -Naur phenny-2005-09-08/modules/__init__.py phenny-2005-10-03/modules/__init__.py --- phenny-2005-09-08/modules/__init__.py 2005-06-20 05:54:36.000000000 +0100 +++ phenny-2005-10-03/modules/__init__.py 2005-10-03 03:38:36.000000000 +0100 @@ -0,0 +1,9 @@ +#!/usr/bin/env python +""" +__init__.py - Initialisation Module +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +if __name__=="__main__": + print __doc__ diff -Naur phenny-2005-09-08/modules/admin.py phenny-2005-10-03/modules/admin.py --- phenny-2005-09-08/modules/admin.py 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/modules/admin.py 2005-10-03 03:38:54.000000000 +0100 @@ -0,0 +1,65 @@ +#!/usr/bin/env python +""" +admin.py - Administration Tools +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +admins = ['sbp', 'd8uv', 'AaronSw', 'deltab', 'xover', 'Morbus'] +admins = frozenset(admins) + +def f_join(self, origin, match, args): + if origin.nick in admins: + self.write(['JOIN'], match.group(1)) +f_join.rule = r'^\.join (.*)$' + +def f_part(self, origin, match, args): + if origin.nick in admins: + self.write(['PART'], match.group(1)) +f_part.rule = r'^\.part (.*)$' + +def f_tell(self, origin, match, args): + if origin.nick in admins: + self.msg(match.group(1), match.group(2)) +f_tell.rule = r'^\.msg ([^ ]+) (.*)' + +def f_act(self, origin, match, args): + if origin.nick in admins: + self.msg(match.group(1), '\x01ACTION %s\x01' % match.group(2)) +f_act.rule = r'^\.act ([^ ]+) (.*)' + +def f_channels(self, origin, match, args): + if origin.nick in admins: + self.msg(origin.sender, str(self.channels)) +f_channels.rule = r'^\.channels$' + +def f_stats(self, origin, match, args): + if origin.nick in admins: + if self.stats: + name = match.group(1) + if not name: + import textwrap + results = [] + for fname in self.stats.iterkeys(): + total = sum(self.stats[fname].itervalues()) + results.append((total, fname)) + arg = ['%s: %s' % (q, p) for (p, q) in reversed(sorted(results))] + lines = textwrap.wrap(', '.join(arg), 150) + self.msglines(origin.sender, lines) + else: self.msg(origin.sender, str(self.stats[name])) + else: self.msg(origin.sender, "Sorry, not recording stats.") +f_stats.rule = r'^\.stats(?:[ \t]+(\S+))?$' +f_stats.thread = True + +# @@ f_stats.private = True + +def f_eval(self, origin, match, args): + phenny = self + if origin.nick in admins: + import textwrap + result = '%r' % eval(match.group(1)) + self.msglines(origin.sender, textwrap.wrap(result, 150)) +f_eval.rule = r'^\.eval (.*)$' + +# @@ exec +# @@ login diff -Naur phenny-2005-09-08/modules/codepoint.py phenny-2005-10-03/modules/codepoint.py --- phenny-2005-09-08/modules/codepoint.py 2005-06-27 20:23:55.000000000 +0100 +++ phenny-2005-10-03/modules/codepoint.py 2005-10-03 07:13:07.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +codepoint.py - Unicode Utilities +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import re @@ -46,3 +51,34 @@ self.f_codepoint(origin, match, args) f_podecoint.rule = r'^\.(?:pc|podecoint) (.*)$' f_podecoint.thread = True + +def f_charinfo(self, origin, match, args): + """.char - Get info about a sequence.""" + import unicodedata + input = match.group(1) + + chars = { + '\xa3': u'\u00A3', + 'E/': u'\u00C9', + 'ae': u'\u00E6', + 'e/': u'\u00E9', + 'i/': u'\u00ED', + 'o^': u'\u00F4', + '|': u'\u017F', + 'long s': u'\u017F', + 'long-s': u'\u017F', + '--': u'\u2014' + } + + if chars.has_key(input): + char = chars[input] + else: char = unicode(input, 'utf-8')[:1] + + if char: + cp = ord(char) + name = unicodedata.name(char).title() + msg = '&#x%04X; - %s (%s)' % (cp, name, char.encode('utf-8')) + self.msg(origin.sender, msg) + else: self.msg(origin.sender, '%s: sorry, no such char' % origin.nick) +f_charinfo.rule = r'^\.char(?:info)? (.*)$' +f_charinfo.thread = True diff -Naur phenny-2005-09-08/modules/cym.py phenny-2005-10-03/modules/cym.py --- phenny-2005-09-08/modules/cym.py 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/modules/cym.py 2005-10-03 03:39:39.000000000 +0100 @@ -0,0 +1,77 @@ +#!/usr/bin/python +""" +cym.py - Welsh Utilities +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +import sys, re, urllib +import web + +channels = ('#cym', '#inamidst') + +r_tag = re.compile(r"<[^>]+>") +r_whitespace = re.compile(r"[\t\r\n ]+") +r_bbc = re.compile(r"(?ims)Search results:(.*?)") +r_geir = re.compile("(?ims)Search Results.*? (.*?)") + +def text(html): + html = r_tag.sub('', html) + html = r_whitespace.sub(' ', html) + return html.strip(', ') + +def cyenBBC(term): + uri = ("http://www.bbc.co.uk/cgi-bin/wales/learnwelsh/" + + "welsh_dictionary.pl?i=c&gair=%s") % term + data = web.get(uri) + m = r_bbc.search(data) + if m: line = text(m.group(1)) + else: line = 'No result.' + return line + ' (BBC)' + +def encyBBC(term): + uri = ("http://www.bbc.co.uk/cgi-bin/wales/learnwelsh/" + + "welsh_dictionary.pl?gair=%s") % term + data = web.get(uri) + m = r_bbc.search(data) + if m: line = text(m.group(1)) + else: line = 'No result.' + return line + ' (BBC)' + +def cyenGeir(term): + uri = "http://www.geiriadur.net/atebion.php?prefLang=en" + query = {'term': term, 'direction': 'we', + 'type': 'all', 'whichpart': 'exact'} + data = web.post(uri, query) + m = r_geir.search(data) + if m: line = text(m.group(1)) + else: line = 'No result.' + return line + ' (Geiriadur)' + +def encyGeir(term): + uri = "http://www.geiriadur.net/atebion.php?prefLang=en" + query = {'term': term, 'direction': 'ew', + 'type': 'all', 'whichpart': 'exact'} + data = web.post(uri, query) + m = r_geir.search(data) + if m: line = text(m.group(1)) + else: line = 'No result.' + return line + ' (Geiriadur)' + +def f_cyen(self, origin, match, args): + if origin.sender not in channels: + return + term = match.group(1) + self.msg(origin.sender, cyenBBC(term)) + self.msg(origin.sender, cyenGeir(term)) +f_cyen.rule = r'^\.cy (\S+)$' +f_cyen.thread = True + +def f_ency(self, origin, match, args): + if origin.sender not in channels: + return + term = match.group(1) + self.msg(origin.sender, encyBBC(term)) + self.msg(origin.sender, encyGeir(term)) +f_ency.rule = r'^\.en (\S+)$' +f_ency.thread = True diff -Naur phenny-2005-09-08/modules/devoice.py phenny-2005-10-03/modules/devoice.py --- phenny-2005-09-08/modules/devoice.py 2005-06-20 10:54:15.000000000 +0100 +++ phenny-2005-10-03/modules/devoice.py 2005-10-03 03:39:49.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +devoice.py - For devoicing folk +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" nicks = ['sbp', 'xover', 'd8uv', 'AaronSw', 'kandinski', 'datum'] nicks = frozenset(nicks) diff -Naur phenny-2005-09-08/modules/etymology.py phenny-2005-10-03/modules/etymology.py --- phenny-2005-09-08/modules/etymology.py 2005-06-27 20:24:30.000000000 +0100 +++ phenny-2005-10-03/modules/etymology.py 2005-10-03 03:40:04.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +etymology.py - Lookup Etymologies +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import re import web diff -Naur phenny-2005-09-08/modules/google.py phenny-2005-10-03/modules/google.py --- phenny-2005-09-08/modules/google.py 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/modules/google.py 2005-10-03 03:40:17.000000000 +0100 @@ -0,0 +1,125 @@ +#!/usr/bin/env python +""" +google.py - Perform Google Searches +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +import re, time +import web + +uri_google = "http://www.google.com/search?btnI=1&q=" +uri_gimage = ("http://images.google.com/images?q=%s&hl=en" + + "&lr=&c2coff=1&safe=off&sa=N&tab=wi") +uri_googlec = "http://www.google.com/search?q=%s&oe=UTF-8" + +def formatnumber(n): + parts = list(str(n)) + for i in range((len(parts) - 3), 0, -3): + parts.insert(i, ',') + return ''.join(parts) + +def google(query): + uri = uri_google + web.urllib.quote(query) + info = web.head(uri) + if isinstance(info, list): + return info[0].get('Location') + else: return None + +r_imgurl = re.compile(r'(?i)imgurl=(http://[^&]+)') + +def image(query): + uri = uri_gimage % web.urllib.quote(query) + s = web.get(uri) + results = r_imgurl.findall(s) + if results: + return web.urllib.unquote(results[0]) + else: return None + +r_googlecount = re.compile(r'(?i)(?:of|of about)\s+(\d+(?:,\d+)*)') + +def googlecount(query): + uri = uri_googlec % web.urllib.quote(query) + s = web.get(uri) + m = r_googlecount.search(s) + if m: return m.group(1) + else: return None + +r_googlecalc = re.compile(r'(?i)(.+?)') +r_tag = re.compile(r'<[^>]+?>') +def text(s): + s = s.replace(' × 10', 'e') + return r_tag.sub('', s) + +def googlecalc(query): + uri = uri_googlec % web.urllib.quote(query) + s = web.get(uri) + for line in s.splitlines(): + if '/images/calc' in line: + m = r_googlecalc.search(line) + if m: return text(m.group(1)) + else: break + return None + +def f_google(self, origin, match, args): + """.g - Google for and return the top result.""" + q = args[0][3:] + result = google(q) + if result is not None: + self.msg(origin.sender, q + ': ' + result) + else: self.msg(origin.sender, '%s: sorry, no results were found.' % q) +f_google.rule = r'^\.g (.*)' + +def f_gimage(self, origin, match, args): + """.img - Google image search for .""" + q = match.group(1) + result = image(q) + if result is not None: + self.msg(origin.sender, q + ': ' + result) + else: self.msg(origin.sender, '%s: sorry, no results were found.' % q) +f_gimage.rule = r'^\.(?:img|image) (.*)' + +def f_googlecount(self, origin, match, args): + """.gc - Return the Googlecount for .""" + q = match.group(1) + result = googlecount(q) + if result is not None: + self.msg(origin.sender, q + ': ' + result) + else: self.msg(origin.sender, '%s: 0' % q) +f_googlecount.rule = r'^\.(?:gc|mg|gcount|googlecount) (.*)' + +def f_rate(self, origin, match, args): + """.rate - Return the rocks/sucks rating for .""" + def gc(query): + result = googlecount(query) + if result: return int(result.replace(',', '')) + return 0 + term = match.group(1).replace('"', '') + rocks = gc('"%s rocks"' % term); time.sleep(1) + rules = gc('"%s rules"' % term); time.sleep(1) + sucks = gc('"%s sucks"' % term); time.sleep(1) + blows = gc('"%s blows"' % term) + + total = rocks + rules + sucks + blows + if total: rating = (float(rocks + rules) / total) * 100 + else: rating = 50 + + rating = round(rating, 2) + rocks = formatnumber(rocks) + rules = formatnumber(rules) + sucks = formatnumber(sucks) + blows = formatnumber(blows) + + msg = '"%s": %s%% (%s rocks; %s rules; %s sucks; %s blows)' + self.msg(origin.sender, msg % (term, rating, rocks, rules, sucks, blows)) +f_rate.rule = r'^\.rate (.*)' +f_rate.thread = True + +def f_googlecalc(self, origin, match, args): + """.calc - Return the Googlecalc for .""" + q = match.group(1) + result = googlecalc(q) + if result is not None: + self.msg(origin.sender, result) + else: self.msg(origin.sender, "%s: Sorry, no result." % origin.nick) +f_googlecalc.rule = r'^\.(?:calc|gcalc|googlecalc) (.*)' diff -Naur phenny-2005-09-08/modules/head.py phenny-2005-10-03/modules/head.py --- phenny-2005-09-08/modules/head.py 2005-06-27 20:24:56.000000000 +0100 +++ phenny-2005-10-03/modules/head.py 2005-10-03 03:40:30.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +head.py - HTTP HEAD Utilities +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import web diff -Naur phenny-2005-09-08/modules/help.py phenny-2005-10-03/modules/help.py --- phenny-2005-09-08/modules/help.py 2005-06-20 11:50:31.000000000 +0100 +++ phenny-2005-10-03/modules/help.py 2005-10-03 03:40:40.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +help.py - Help Facilities +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" def f_help(self, origin, match, args): result = [] diff -Naur phenny-2005-09-08/modules/hi.py phenny-2005-10-03/modules/hi.py --- phenny-2005-09-08/modules/hi.py 2005-09-01 00:03:03.000000000 +0100 +++ phenny-2005-10-03/modules/hi.py 2005-10-03 03:40:52.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +hi.py - Perform Greetings +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import random diff -Naur phenny-2005-09-08/modules/monty.py phenny-2005-10-03/modules/monty.py --- phenny-2005-09-08/modules/monty.py 2005-06-20 08:02:24.000000000 +0100 +++ phenny-2005-10-03/modules/monty.py 2005-10-03 03:41:03.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +monty.py - Converse with Monty +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" def f_tauntmonty(self, origin, match, args): text = args[0] diff -Naur phenny-2005-09-08/modules/mw.py phenny-2005-10-03/modules/mw.py --- phenny-2005-09-08/modules/mw.py 2005-06-27 20:25:25.000000000 +0100 +++ phenny-2005-10-03/modules/mw.py 2005-10-03 03:41:14.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +mw.py - Interface M-W +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import re import web diff -Naur phenny-2005-09-08/modules/ping.py phenny-2005-10-03/modules/ping.py --- phenny-2005-09-08/modules/ping.py 2005-06-20 07:51:30.000000000 +0100 +++ phenny-2005-10-03/modules/ping.py 2005-10-03 03:41:31.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +ping.py - Ping Functionality +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import random diff -Naur phenny-2005-09-08/modules/prefix.py phenny-2005-10-03/modules/prefix.py --- phenny-2005-09-08/modules/prefix.py 2005-06-20 08:30:10.000000000 +0100 +++ phenny-2005-10-03/modules/prefix.py 2005-10-03 03:41:44.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +prefix.py - Munge Prefixes Dynamically +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" def f_prefix(self, origin, match, args): name, prefix = match.group(1), match.group(2) diff -Naur phenny-2005-09-08/modules/pwkedit.py phenny-2005-10-03/modules/pwkedit.py --- phenny-2005-09-08/modules/pwkedit.py 1970-01-01 00:00:00.000000000 +0000 +++ phenny-2005-10-03/modules/pwkedit.py 2005-10-03 03:42:02.000000000 +0100 @@ -0,0 +1,119 @@ +#!/usr/bin/env python +""" +pwkedit.py - Eph Editing Capabilities +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" + +import sys, os +sys.path.append('/web/inamidst.com/eph') +try: import pwk, pwkblog +except ImportError: + pwk = None + pwkblog = None + +eph = "http://inamidst.com/eph/" +allowed = frozenset(['sbp', 'crschmidt', 'd8uv', 'xover']) + +def post(wikiname, text, append=True): + pwd = os.getcwd() + os.chdir('/web/miscoranda.com/wiki') + try: pwk.doPost(wikiname, text, append=append) + except "WikiParseError": + os.chdir(pwd) + return False + os.chdir(pwd) + return True + +def f_edit(self, origin, match, args): + if origin.nick not in allowed: + self.msg(origin.sender, "You don't smell like I know you...") + return + channel = args[2] + wikiname = match.group(1) + if not hasattr(self, 'editinfo'): + self.editinfo = {} + if self.editinfo.has_key(origin.nick): + if self.editinfo[origin.nick].has_key(channel): + self.msg(origin.sender, "You're already editing a document here.") + return + self.editinfo[origin.nick][channel] = [wikiname, []] + else: self.editinfo[origin.nick] = {channel: [wikiname, []]} + self.msg(origin.sender, "Opened %s%s for appending." % (eph, wikiname)) +f_edit.rule = r'^$nick: <<< ([A-Za-z0-9-]+)$' + +def f_readline(self, origin, match, args): + if origin.nick not in allowed: return + if not hasattr(self, 'editinfo'): + self.editinfo = {} + if not self.editinfo.has_key(origin.nick): return + text = args[0] + if text.startswith(self.nick + ': '): return + if text.startswith(':: '): return + channel = args[2] + if not self.editinfo[origin.nick].has_key(channel): return + sig = '--by %s on %s' % (origin.nick, args[2]) + text = text.replace('{$sig}', sig) + if text == '..': text = '' + if text in ('{{{', '[[['): + self.editinfo[origin.nick][channel][1].append('') + self.editinfo[origin.nick][channel][1].append(text) + if text in ('}}}', ']]]'): + self.editinfo[origin.nick][channel][1].append('') +f_readline.rule = r'.*' + +def f_write(self, origin, match, args): + if origin.nick not in allowed: + self.msg(origin.sender, "You don't smell like I know you...") + return + if not hasattr(self, 'editinfo'): + self.editinfo = {} + if not self.editinfo.has_key(origin.nick): + self.msg(origin.sender, "You don't currently have a document open.") + return + channel = args[2] + if not self.editinfo[origin.nick].has_key(channel): + channels = self.editinfo[origin.nick].keys() + self.msg(origin.sender, "You don't have a document open here.") + if channels: + self.msg(origin.sender, "Try one of: %s" % ', '.join(channels)) + wikiname, lines = tuple(self.editinfo[origin.nick][channel]) + text = '\n%s\n' % '\n'.join(lines) + result = post(wikiname, text) + del self.editinfo[origin.nick][channel] + if not self.editinfo[origin.nick]: + del self.editinfo[origin.nick] + args = (len(text), eph, wikiname) + if result: + self.msg(origin.sender, "Queued writing %s bytes to %s%s" % args) + else: self.msg(origin.sender, "Sorry, I got a parse error.") +f_write.rule = r'^$nick: >>>' + +def f_writeline(self, origin, match, args): + wikiname, text = tuple(match.groups()) + sig = '--by %s on %s' % (origin.nick, args[2]) + text = text.replace('{$sig}', sig) + if not text.endswith('\n'): text = text + '\n' + post(wikiname, text) + self.msg(origin.sender, "Queued to eph:%s" % wikiname) +f_writeline.rule = r'^$nick: post ([A-Za-z0-9-]+)\s+(?:-\s+)?(.*)$' + +# def f_blog(m, origin, args, text, p=p): +# wikiname = m.group(1) +# description = m.group(2) +# pwkblog.blog(wikiname, description, '/web/sbp/wiki') +# p.msg(origin.sender, "Blogged about %s to %s" % (wikiname, eph)) +# p.rule(f_blog, r'^%s: blog ([A-Za-z0-9-]+) (.*)$' % p.nick) + +def f_reloadpwk(self, origin, match, args): + try: reload(pwk) + except Exception, e: + self.msg(origin.sender, "Sorry, got: %s" % e) + else: self.msg(origin.sender, "Reloaded pwk, version: %s" % pwk.__version__) + try: reload(pwkblog) + except Exception, e: + self.msg(origin.sender, "Sorry, got: %s" % e) + else: + msg = "Reloaded pwkblog, version: %s" % pwkblog.__version__ + self.msg(origin.sender, msg) +f_reloadpwk.rule = r'^$nick: reloadpwk$' diff -Naur phenny-2005-09-08/modules/reload.py phenny-2005-10-03/modules/reload.py --- phenny-2005-09-08/modules/reload.py 2005-06-20 05:58:42.000000000 +0100 +++ phenny-2005-10-03/modules/reload.py 2005-10-03 03:42:13.000000000 +0100 @@ -1,12 +1,17 @@ #!/usr/bin/env python +""" +reload.py - Reload Modules Dynamically +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" -import ircbot +import irc def f_reload(self, origin, match, text): name = match.group(1) module = getattr(__import__('modules.' + name), name) reload(module) - ircbot.register(self, vars(module)) + irc.register(self, vars(module)) self.bindrules() self.msg(origin.sender, 'Reloaded "%s".' % name) f_reload.rule = r"^\.reload (\S+)$" diff -Naur phenny-2005-09-08/modules/remind.py phenny-2005-10-03/modules/remind.py --- phenny-2005-09-08/modules/remind.py 2005-09-08 00:16:34.000000000 +0100 +++ phenny-2005-10-03/modules/remind.py 2005-10-03 03:42:26.000000000 +0100 @@ -1,7 +1,41 @@ #!/usr/bin/env python +""" +remind.py - The All Important Reminder Function +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import os, time, random -import tell + +def loadReminders(fn): + result = {} + f = open(fn) + for line in f: + line = line.strip() + if line: + tellee, teller, verb, timenow, msg = line.split('\t', 4) + result.setdefault(tellee, []).append((teller, verb, timenow, msg)) + f.close() + return result + +def dumpReminders(fn, data): + f = open(fn, 'w') + for tellee in data.iterkeys(): + for remindon in data[tellee]: + line = '\t'.join((tellee,) + remindon) + f.write(line + '\n') + f.close() + return True + +def initialize(self): + self.remindersFilename = os.path.join(self.datadir, 'reminders.db') + if not os.path.exists(self.remindersFilename): + try: f = open(self.remindersFilename, 'w') + except OSError: pass + else: + f.write('') + f.close() + self.reminders = loadReminders(self.remindersFilename) # @@ tell def f_remind(self, origin, match, args): """tell - I'll remind nick about blargh.""" @@ -42,7 +76,7 @@ self.msg(origin.sender, "You can %s yourself that." % verb) else: self.msg(origin.sender, "Hey, I'm not as stupid as Monty you know!") - tell.dumpReminders(self.remindersFilename, self.reminders) + dumpReminders(self.remindersFilename, self.reminders) # @@ tell # The + is specifically for jill's double-spacing ways # I.e. to make phenny love jill as the rest of us do. # Cf. http://swhack.com/logs/2004-06-30#T06-59-10 @@ -80,5 +114,5 @@ elif tellee.lower().startswith(remkey.rstrip('*')): self.f_doReminder(channel, remkey, tellee) if len(self.reminders.keys()) != remkeys: - tell.dumpReminders(self.remindersFilename, self.reminders) + dumpReminders(self.remindersFilename, self.reminders) # @@ tell f_privremind.rule = r'(.*)' diff -Naur phenny-2005-09-08/modules/repres.py phenny-2005-10-03/modules/repres.py --- phenny-2005-09-08/modules/repres.py 2005-06-20 07:41:44.000000000 +0100 +++ phenny-2005-10-03/modules/repres.py 2005-10-03 03:42:39.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +repres.py - Get Byte Representations +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" def f_representation(self, origin, match, args): """.repres - Return the representation of .""" diff -Naur phenny-2005-09-08/modules/seen.py phenny-2005-10-03/modules/seen.py --- phenny-2005-09-08/modules/seen.py 2005-06-20 08:19:32.000000000 +0100 +++ phenny-2005-10-03/modules/seen.py 2005-10-03 03:42:49.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +seen.py - Tell Who's Been Online +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import time diff -Naur phenny-2005-09-08/modules/streetmap.py phenny-2005-10-03/modules/streetmap.py --- phenny-2005-09-08/modules/streetmap.py 2005-06-27 20:26:58.000000000 +0100 +++ phenny-2005-10-03/modules/streetmap.py 2005-10-03 03:43:03.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +streetmap.py - Streetmap Interface +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import re import web diff -Naur phenny-2005-09-08/modules/swhack.py phenny-2005-10-03/modules/swhack.py --- phenny-2005-09-08/modules/swhack.py 2005-06-20 07:15:42.000000000 +0100 +++ phenny-2005-10-03/modules/swhack.py 2005-10-03 03:43:17.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +swhack.py - Swhack Log Tailer +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" def f_swhacktail(self, origin, match, args): """.swhack - Search for in Swhack's logs.""" diff -Naur phenny-2005-09-08/modules/thanks.py phenny-2005-10-03/modules/thanks.py --- phenny-2005-09-08/modules/thanks.py 2005-06-20 07:01:42.000000000 +0100 +++ phenny-2005-10-03/modules/thanks.py 2005-10-03 03:43:29.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +thanks.py - Thankfulness Functionality +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import random diff -Naur phenny-2005-09-08/modules/thesaurus.py phenny-2005-10-03/modules/thesaurus.py --- phenny-2005-09-08/modules/thesaurus.py 2005-06-27 20:27:19.000000000 +0100 +++ phenny-2005-10-03/modules/thesaurus.py 2005-10-03 03:43:43.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +thesaurus.py - Thesaurus Functionality +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import re, textwrap import web diff -Naur phenny-2005-09-08/modules/timer.py phenny-2005-10-03/modules/timer.py --- phenny-2005-09-08/modules/timer.py 2005-06-27 20:27:30.000000000 +0100 +++ phenny-2005-10-03/modules/timer.py 2005-10-03 03:43:53.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +timer.py - Timer Functionality +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import time diff -Naur phenny-2005-09-08/modules/timing.py phenny-2005-10-03/modules/timing.py --- phenny-2005-09-08/modules/timing.py 2005-06-20 07:03:36.000000000 +0100 +++ phenny-2005-10-03/modules/timing.py 2005-10-03 03:44:02.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +timing.py - Show The Time +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import time diff -Naur phenny-2005-09-08/modules/validate.py phenny-2005-10-03/modules/validate.py --- phenny-2005-09-08/modules/validate.py 2005-06-27 20:27:44.000000000 +0100 +++ phenny-2005-10-03/modules/validate.py 2005-10-03 03:44:14.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +validate.py - Validate a URI +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import web diff -Naur phenny-2005-09-08/modules/wordnet.py phenny-2005-10-03/modules/wordnet.py --- phenny-2005-09-08/modules/wordnet.py 2005-06-27 20:27:59.000000000 +0100 +++ phenny-2005-10-03/modules/wordnet.py 2005-10-03 03:44:27.000000000 +0100 @@ -1,4 +1,9 @@ #!/usr/bin/env python +""" +wordnet.py - Wordnet Interface +Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ +""" import re import web diff -Naur phenny-2005-09-08/phenny.py phenny-2005-10-03/phenny.py --- phenny-2005-09-08/phenny.py 2005-09-01 00:03:25.000000000 +0100 +++ phenny-2005-10-03/phenny.py 2005-10-03 03:37:28.000000000 +0100 @@ -2,27 +2,21 @@ """ phenny.py - Phenny Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ """ import sys, os, optparse -import ircbot, tell +import irc home = os.getcwd() -class Phenny(ircbot.IRCBot): +class Phenny(irc.Bot): def __init__(self, *args, **kargs): - ircbot.IRCBot.__init__(self, *args, **kargs) + irc.Bot.__init__(self, *args, **kargs) self.initialize() def initialize(self): - self.remindersFilename = os.path.join(datadir, 'reminders.db') - if not os.path.exists(self.remindersFilename): - try: f = open(self.remindersFilename, 'w') - except OSError: pass - else: - f.write('') - f.close() - self.reminders = tell.loadReminders(self.remindersFilename) + self.datadir = datadir modules = [] moduledir = os.path.join(home, 'modules') @@ -30,14 +24,17 @@ if filename.endswith('.py') and not filename.startswith('_'): name, ext = os.path.splitext(os.path.basename(filename)) module = getattr(__import__('modules.' + name), name) - ircbot.register(self, vars(module)) + + if hasattr(module, 'initialize'): + module.initialize(self) + irc.register(self, vars(module)) modules.append(name) if modules: print >> sys.stderr, 'Registered modules:', ', '.join(modules) self.bindrules() def data(self, fn): - path = os.path.join(datadir, fn) + path = os.path.join(self.datadir, fn) if os.path.exists(path): f = open(path, 'rb') bytes = f.read() @@ -58,7 +55,7 @@ pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: - barf("fork #1 failed: %d (%s)" % (e.errno, e.strerror)) + raise OSError("fork #1 failed: %d (%s)" % (e.errno, e.strerror)) os.chdir("/") os.setsid() os.umask(0) @@ -68,7 +65,7 @@ print "Daemon PID %d" % pid sys.exit(0) except OSError, e: - barf("fork #2 failed: %d (%s)" % (e.errno, e.strerror)) + raise OSError("fork #2 failed: %d (%s)" % (e.errno, e.strerror)) def run(host, nick, channels, protect): phenny = Phenny(nick, channels=channels, protect=protect) @@ -81,37 +78,47 @@ parser.add_option("-t", "--test", dest="test", action="store_true", default=False, help="enter testing mode") + parser.add_option("-p", "--protect", dest="protect", + action="store_true", default=False, + help="protect from errors") parser.add_option("-d", "--data", dest="data", default=False, help="provides a nickname for the bot") options, args = parser.parse_args(argv) + import config global datadir - nick = options.nick or 'phenny' - datadir = options.data or '/home/sbp/phenny/data' + + nick = options.nick or config.nick + datadir = options.data or config.datadir if not os.path.isdir(datadir): raise IOError("%s is not a directory" % datadir) + # Augment the nickname when testing if options.test: nick += 'test' - # protect = False - # else: - protect = True - - channels = [] - if args: - for arg in args: - if not arg.startswith('#'): arg = '#' + arg - channels.append(arg) - elif not options.test: - channels.extend(('#swhack', '#swig', '#foaf')) - channels.extend(('#validator', '#julie', '#rowr')) - channels.extend(('#joiito', '#ud', '#d8uv.com')) - channels.extend(('#wordpress', '#wikidb', '#svg', '#suwcharman')) - channels.extend(('#esp', '#jeanniecool', '#4suite', '#cym')) - else: channels.extend(('#inamidst', '#d8uv.com')) + protect = options.protect + else: protect = True + + # Get the channels from args and config + channels = set() + + for channel in args: + if not channel.startswith('#'): + channel = '#' + channel + channels.add(channel) + + if not options.test: + channels.update(config.channels) + else: channels.update(config.testchannels) + + channels = list(channels) + + # Do the double fork trick to prevent having to nohup + if not options.test: + try: doubleFork() + except OSError: pass - if not options.test: doubleFork() - run('irc.freenode.net', nick, channels, protect) + run(config.host, nick, channels, protect) if __name__=="__main__": main() diff -Naur phenny-2005-09-08/tell.py phenny-2005-10-03/tell.py --- phenny-2005-09-08/tell.py 2005-06-20 07:27:32.000000000 +0100 +++ phenny-2005-10-03/tell.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/usr/bin/env python -""" -tell.py - Tell Facilities -Author: Sean B. Palmer, inamidst.com -""" - -def loadReminders(fn): - result = {} - f = open(fn) - for line in f: - line = line.strip() - if line: - tellee, teller, verb, timenow, msg = line.split('\t', 4) - result.setdefault(tellee, []).append((teller, verb, timenow, msg)) - f.close() - return result - -def dumpReminders(fn, data): - f = open(fn, 'w') - for tellee in data.iterkeys(): - for remindon in data[tellee]: - line = '\t'.join((tellee,) + remindon) - f.write(line + '\n') - f.close() - return True diff -Naur phenny-2005-09-08/web.py phenny-2005-10-03/web.py --- phenny-2005-09-08/web.py 2005-06-20 06:36:42.000000000 +0100 +++ phenny-2005-10-03/web.py 2005-10-03 03:37:33.000000000 +0100 @@ -2,6 +2,7 @@ """ web.py - Web Facilities Author: Sean B. Palmer, inamidst.com +About: http://inamidst.com/phenny/ """ import urllib