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

116
bot.py
View File

@ -1,71 +1,79 @@
#!/usr/bin/python
network = "irc.synirc.net"
network = "localhost"
nickname = "skybot"
channel = "#mongbot"
channel = "#skybot"
import sys, os, glob, imp, re
import thread, Queue, copy
import irc, yaml
import sys
import os
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
class Empty(object): #this is used to store attributes
pass
class Bot(object):
def __init__(self):
self.plugins = {}
self.commands = {}
self.commands = [] # fn, name, func, args
self.listens = {}
self.filters = {}
self.filters = [] #fn, name, func
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.nickname = nickname
bot.channel = channel
bot.network = network
print 'Loading plugins'
magic_re = re.compile(r'^\s*#(command|filter)(?:: +(\S+) *(\S.*)?)?\s*$')
for filename in glob.glob("plugins/*.py"):
shortname = os.path.splitext(os.path.basename(filename))[0]
try:
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:
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:
print 'Running daemons'
for daemon in bot.daemons.itervalues():
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'
class Input(object):
@ -82,9 +90,14 @@ class Input(object):
self.msg = msg
class FakeBot(object):
def __init__(self, bot, input):
def __init__(self, bot, input, func):
self.bot = bot
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):
self.bot.irc.msg(input.paraml[0], msg)
@ -92,22 +105,25 @@ class FakeBot(object):
def reply(self, 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.filters
while True:
try:
out = bot.irc.out.get(timeout=1)
#print repr(out)
for func, args in bot.commands.itervalues():
for fn, name, func, args in bot.commands:
input = Input(*out)
for filt in bot.filters.itervalues():
for fn, nam, filt in bot.filters:
input = filt(bot, func, args, input)
if input == False:
if input == None:
break
if input == False:
continue
thread.start_new_thread(func,(FakeBot(bot, input), input))
if input == None:
break
thread.start_new_thread(FakeBot(bot, input, func).run, ())
except Queue.Empty:
pass
#except KeyboardInterrupt:
# sys.exit()

32
irc.py
View File

@ -1,7 +1,21 @@
import sys, re, thread, Queue
import socket, asyncore, asynchat
import sys
import re
import socket
import thread
import asyncore
import asynchat
import 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):
"Handles tcp connections that consist of utf-8 lines ending with crlf"
def __init__(self, host, port):
@ -17,30 +31,28 @@ class crlf_tcp(asynchat.async_chat):
def run(self):
self.connect((self.host, self.port))
thread.start_new_thread(self.queue_read_loop,())
asyncore.loop()
def handle_connect(self):
thread.start_new_thread(self.queue_read_loop,())
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:
line = self.oqueue.get()
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):
self.buffer += data
def found_terminator(self):
line = self.buffer
# print repr(line)
self.iqueue.put(line.encode('utf-8'))
self.iqueue.put(decode(line))
self.buffer = ''
irc_prefix_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':?([^!@]*)!?([^@]*)@?(.*)')
class irc(object):

BIN
irc.pyc

Binary file not shown.

View File

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

View File

@ -1,54 +1,28 @@
#!/usr/bin/python
"weather, thanks to google"
import urllib, re, pickle, os
from xml.dom import minidom
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
}
import urllib
from xml.etree import ElementTree
#command: weather
def weather(bot, input):
".weather <location> -- queries google weather API for weather data"
q = input.re.groups()[0]
cond = goog_weather(q)
'''.weather <location> -- queries the google weather API for weather data'''
if cond.has_key('error'):
bot.reply(u'Couldn\'t fetch weather data for "%s", try using a zip/postal code' % (q))
return
if not input.inp.strip(): # blank line
return "welp"
format = u'%s %sF (%s/%s/%s) (h:%sF,l:%sF)'
args = (cond['place'],cond['temp'],cond['condition'],cond['wind'],cond['humidity'],cond['high'],cond['low'])
data = urllib.urlencode({'weather':input.inp.encode('utf-8')})
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