lots of stuff fixed

This commit is contained in:
Ryan Hitchman 2009-03-14 21:06:36 -06:00
parent 25133138c1
commit 15bfada07d
5 changed files with 121 additions and 119 deletions

118
bot.py
View File

@ -1,71 +1,79 @@
#!/usr/bin/python #!/usr/bin/python
network = "irc.synirc.net" network = "localhost"
nickname = "skybot" nickname = "skybot"
channel = "#mongbot" channel = "#skybot"
import sys, os, glob, imp, re import sys
import thread, Queue, copy import os
import irc, yaml import glob
import imp
import re
import thread
import Queue
import copy
import irc
import yaml
os.chdir(sys.path[0]) #do stuff relative to the installation directory os.chdir(sys.path[0]) #do stuff relative to the installation directory
class Empty(object): #this is used to store attributes
pass
class Bot(object): class Bot(object):
def __init__(self): def __init__(self):
self.plugins = {} self.plugins = {}
self.commands = {} self.commands = [] # fn, name, func, args
self.listens = {} self.listens = {}
self.filters = {} self.filters = [] #fn, name, func
self.daemons = {} self.daemons = {}
def command(self, name, func, **filterargs):
self.commands[name] = (func, filterargs)
def listen(self, name, func):
self.listens[name] = func
def filter(self, name, func):
self.filters[name] = func
def daemon(self, name, func):
self.daemons[name] = func
bot = Bot() bot = Bot()
bot.nickname = nickname
bot.channel = channel
bot.network = network
print 'Loading plugins' print 'Loading plugins'
magic_re = re.compile(r'^\s*#(command|filter)(?:: +(\S+) *(\S.*)?)?\s*$')
for filename in glob.glob("plugins/*.py"): for filename in glob.glob("plugins/*.py"):
shortname = os.path.splitext(os.path.basename(filename))[0] shortname = os.path.splitext(os.path.basename(filename))[0]
try: try:
bot.plugins[shortname] = imp.load_source(shortname,filename) bot.plugins[shortname] = imp.load_source(shortname, filename)
plugin = bot.plugins[shortname]
source = open(filename).read().split('\n')
#this is a nasty hack, but it simplifies registration
funcs = [x for x in dir(plugin)
if str(type(getattr(plugin,x))) == "<type 'function'>"]
for func in funcs:
#read the line before the function definition, looking for a
# comment that tells the bot about what it does
func = getattr(plugin, func)
lineno = func.func_code.co_firstlineno
if lineno == 1:
continue #can't have a line before the first line...
m = magic_re.match(source[lineno-2])
if m:
typ, nam, rest = m.groups()
if nam is None:
nam = func.__name__
if rest is None:
rest = '\s*(.*)'
if typ == 'command':
args = {'name': nam, 'hook': nam + rest}
bot.commands.append((filename, nam, func, args))
if typ == 'filter':
bot.filters.append((filename, nam, func))
except Exception, e: except Exception, e:
print e print e
print bot.plugins
print 'Registering plugins'
for name, plugin in bot.plugins.iteritems():
if hasattr(plugin, 'setup'):
try:
plugin.setup(bot)
except Exception, e:
print e
print 'Connecting to IRC'
bot.irc = irc.irc(network, nickname)
bot.irc.join(channel)
bot.commandprefix = '^(?:\.|'+nickname+'[:,]*\s*)'
if bot.daemons: if bot.daemons:
print 'Running daemons' print 'Running daemons'
for daemon in bot.daemons.itervalues(): for daemon in bot.daemons.itervalues():
thread.start_new_thread(daemon, ()) thread.start_new_thread(daemon, ())
print 'Connecting to IRC'
bot.irc = irc.irc(network, nickname)
bot.irc.join(channel)
bot.commandprefix = '^(?:\.|'+nickname+'[:,]*\s*)'
print 'Running main loop' print 'Running main loop'
class Input(object): class Input(object):
@ -82,9 +90,14 @@ class Input(object):
self.msg = msg self.msg = msg
class FakeBot(object): class FakeBot(object):
def __init__(self, bot, input): def __init__(self, bot, input, func):
self.bot = bot self.bot = bot
self.input = input self.input = input
self.msg = bot.irc.msg
self.cmd = bot.irc.cmd
self.func = func
if input.command == "PRIVMSG":
self.chan = input.paraml[0]
def say(self, msg): def say(self, msg):
self.bot.irc.msg(input.paraml[0], msg) self.bot.irc.msg(input.paraml[0], msg)
@ -92,22 +105,25 @@ class FakeBot(object):
def reply(self, msg): def reply(self, msg):
self.say(input.nick + ': ' + msg) self.say(input.nick + ': ' + msg)
def run(self):
out = self.func(self, self.input)
if out is not None:
self.reply(unicode(out))
print bot.commands print bot.commands
print bot.filters
while True: while True:
try: try:
out = bot.irc.out.get(timeout=1) out = bot.irc.out.get(timeout=1)
#print repr(out) for fn, name, func, args in bot.commands:
for func, args in bot.commands.itervalues():
input = Input(*out) input = Input(*out)
for filt in bot.filters.itervalues(): for fn, nam, filt in bot.filters:
input = filt(bot, func, args, input) input = filt(bot, func, args, input)
if input == False: if input == None:
break break
if input == False: if input == None:
continue break
thread.start_new_thread(func,(FakeBot(bot, input), input)) thread.start_new_thread(FakeBot(bot, input, func).run, ())
except Queue.Empty: except Queue.Empty:
pass pass
#except KeyboardInterrupt:
# sys.exit()

32
irc.py
View File

@ -1,7 +1,21 @@
import sys, re, thread, Queue import sys
import socket, asyncore, asynchat import re
import socket
import thread
import asyncore
import asynchat
import Queue
queue = Queue.Queue queue = Queue.Queue
def decode(txt, codecs=['utf-8', 'iso-8859-1', 'shift_jis', 'cp1252']):
if len(codecs) == 0:
return txt.decode('utf-8', 'ignore')
try:
return txt.decode(codecs[0])
except UnicodeDecodeError:
return decode(txt, codecs[1:])
class crlf_tcp(asynchat.async_chat): class crlf_tcp(asynchat.async_chat):
"Handles tcp connections that consist of utf-8 lines ending with crlf" "Handles tcp connections that consist of utf-8 lines ending with crlf"
def __init__(self, host, port): def __init__(self, host, port):
@ -17,30 +31,28 @@ class crlf_tcp(asynchat.async_chat):
def run(self): def run(self):
self.connect((self.host, self.port)) self.connect((self.host, self.port))
thread.start_new_thread(self.queue_read_loop,())
asyncore.loop() asyncore.loop()
def handle_connect(self):
thread.start_new_thread(self.queue_read_loop,())
def queue_read_loop(self): def queue_read_loop(self):
#this is an attempt at making this thread-safe
#at least with this, only TWO things could be modifying the output
#buffer at the same time
while True: while True:
line = self.oqueue.get() line = self.oqueue.get()
print ">>> %r" % line print ">>> %r" % line
self.push(line.encode('utf-8')+'\r\n') self.push(line.encode('utf-8','replace')+'\r\n')
def collect_incoming_data(self, data): def collect_incoming_data(self, data):
self.buffer += data self.buffer += data
def found_terminator(self): def found_terminator(self):
line = self.buffer line = self.buffer
# print repr(line) self.iqueue.put(decode(line))
self.iqueue.put(line.encode('utf-8'))
self.buffer = '' self.buffer = ''
irc_prefix_re = re.compile(r'(.*?) (.*?) (.*)') irc_prefix_re = re.compile(r'(.*?) (.*?) (.*)')
irc_noprefix_re = re.compile(r'()(.*?) (.*)') irc_noprefix_re = re.compile(r'()(.*?) (.*)')
irc_param_re = re.compile(r'(?:^|(?<= ))(:.*|[^ ]*)') irc_param_re = re.compile(r'(?:^|(?<= ))(:.*|[^ ]+)')
irc_netmask_re = re.compile(r':?([^!@]*)!?([^@]*)@?(.*)') irc_netmask_re = re.compile(r':?([^!@]*)!?([^@]*)@?(.*)')
class irc(object): class irc(object):

BIN
irc.pyc

Binary file not shown.

View File

@ -1,13 +1,12 @@
import re import re
def setup(bot): #filter
bot.filter("default_filters", filter_suite)
def filter_suite(bot, func, args, input): def filter_suite(bot, func, args, input):
args.setdefault('events', ['PRIVMSG']) args.setdefault('events', ['PRIVMSG'])
if input.command not in args['events']: if input.command not in args['events']:
return False if args['events'] != '*':
return None
args.setdefault('hook', r'(.*)') args.setdefault('hook', r'(.*)')
args.setdefault('prefix', True) args.setdefault('prefix', True)
@ -16,9 +15,10 @@ def filter_suite(bot, func, args, input):
if args['prefix']: if args['prefix']:
hook = bot.commandprefix + args['hook'] hook = bot.commandprefix + args['hook']
m = re.match(hook, input.msg) input.re = re.match(hook, input.msg)
if not m: if input.re is None:
return False return None
input.inp = ' '.join(input.re.groups())
input.re = m
return input return input

View File

@ -1,54 +1,28 @@
#!/usr/bin/python
"weather, thanks to google" "weather, thanks to google"
import urllib, re, pickle, os import urllib
from xml.dom import minidom from xml.etree import ElementTree
WEATHER_URL = 'http://www.google.com/ig/api'
def setup(bot):
bot.command("weather", weather, hook=r"weather (.*)")
def goog_weather(query):
data = urllib.urlencode({'weather':query})
url = WEATHER_URL + "?" + data
#print url
dom = minidom.parse(urllib.urlopen(url))
if len(dom.getElementsByTagName('problem_cause')):
return {'error': True}
place = dom.getElementsByTagName('city')[0].getAttribute('data')
temp = dom.getElementsByTagName('temp_f')[0].getAttribute('data')
conditions = dom.getElementsByTagName('current_conditions')[0]
condition = conditions.getElementsByTagName('condition')[0].getAttribute('data')
wind = conditions.getElementsByTagName('wind_condition')[0].getAttribute('data')
humidity = conditions.getElementsByTagName('humidity')[0].getAttribute('data')
forecast = dom.getElementsByTagName('forecast_conditions')[0]
high = forecast.getElementsByTagName('high')[0].getAttribute('data')
low = forecast.getElementsByTagName('low')[0].getAttribute('data')
return {
'place': place,
'temp': temp,
'high': high,
'low': low,
'condition': condition,
'wind': wind,
'humidity': humidity
}
#command: weather
def weather(bot, input): def weather(bot, input):
".weather <location> -- queries google weather API for weather data" '''.weather <location> -- queries the google weather API for weather data'''
q = input.re.groups()[0]
cond = goog_weather(q)
if cond.has_key('error'): if not input.inp.strip(): # blank line
bot.reply(u'Couldn\'t fetch weather data for "%s", try using a zip/postal code' % (q)) return "welp"
return
format = u'%s %sF (%s/%s/%s) (h:%sF,l:%sF)' data = urllib.urlencode({'weather':input.inp.encode('utf-8')})
args = (cond['place'],cond['temp'],cond['condition'],cond['wind'],cond['humidity'],cond['high'],cond['low']) url = 'http://www.google.com/ig/api?' + data
w = ElementTree.parse(urllib.urlopen(url)).find('weather')
bot.reply(format.encode('utf-8') % args) if w.find('problem_cause') is not None:
return "Couldn't fetch weather data for '%s', try using a zip or " \
"postal code." % input.inp
info = dict((e.tag, e.get('data')) for e in w.find('current_conditions'))
info['city'] = w.find('forecast_information/city').get('data')
info['high'] = w.find('forecast_conditions/high').get('data')
info['low'] = w.find('forecast_conditions/low').get('data')
return '%(city)s: %(condition)s, %(temp_f)sF/%(temp_c)sC (H:%(high)sF, ' \
'L:%(low)sF), %(humidity)s, %(wind_condition)s.' % info