From 15bfada07d4dba1d60a10474754d40dfdafea4d3 Mon Sep 17 00:00:00 2001 From: Ryan Hitchman Date: Sat, 14 Mar 2009 21:06:36 -0600 Subject: [PATCH] lots of stuff fixed --- bot.py | 120 +++++++++++++++++++++++++-------------------- irc.py | 34 ++++++++----- irc.pyc | Bin 4366 -> 0 bytes plugins/filter.py | 18 +++---- plugins/weather.py | 68 ++++++++----------------- 5 files changed, 121 insertions(+), 119 deletions(-) delete mode 100644 irc.pyc diff --git a/bot.py b/bot.py index 70eb50c..be435b7 100755 --- a/bot.py +++ b/bot.py @@ -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.listens = {} - self.filters = {} + self.commands = [] # fn, name, func, args + self.listens = {} + 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) + 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))) == ""] + 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() diff --git a/irc.py b/irc.py index d7e7cec..b864236 100644 --- a/irc.py +++ b/irc.py @@ -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') + print ">>> %r" % line + 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): diff --git a/irc.pyc b/irc.pyc deleted file mode 100644 index 55f544e0ae35353299160231d05a8acd7c0cb49b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4366 zcmb7HYjYIG6}_|X(E~8HA;d8(7^ha)mPk@%yA&*8%NP`gg|vuMk!_}ynOSMj?#wdX z8&H+>iTs#+&)>+eN#%QT&h3536cuEx`tI$=^xS*ybN~78<%R$J84foksYu$8tR;P1 zSe3tvycGHCZYXIW*@7fBd5M=TPN#L9E={Kmoi0zOO`V>Rq$TNDy)m5Kc#cZ!bf{VO zU;K4l3v65t`jLxAGtC1meuZIw50XM>)sq?qyo%~ol!0z4E)}ckN<)&mGEFf%odz%Y z8suItPX?)N<1OvD$n!LIS&?JD-*a5BnRV@=-yXaEC!e{MsK^34>kt3iMNPsVZ3PCx6n3zWp(=&B(KudJ7d4g|oB@4wZNVceSNnTcP zS)fOk<;Xt|R0a4^h_0=GE-&EyBAv4!ie=9p<}sAQO(HspbV{6~D9bY!MeYnHHg%Cp z%VC!HTv0+SktgGRKP_DoY%$^M;Gd1tajFN3Ih%QQ8T)aW_FNj-B0fl+o|~||=*w@S z?e3jNZV~*>gRO6)ok!-*m-k%_^Zmj)WkwJ{mC~6_2Yo|@%sG$_h9~xy;MVqwVY*fx z_VWETTgGco3b&S(@#^T%@JhbYo(GZOT(BI3_$=Xb0pBsbii7B>`PRV@cTW{+*s)(x z(>Vl3O}F7&uqLbl-7VKrY<9=Up2-fPq89!4Z8I>F3X?68uF$p%J?Ju8ob2_J^gV2WeDI{I$ac zRq9bWPEE)e<=BJo@Zr(e?ki|%9v4a4p`5C<2Lh3KYW2;#c=jTOWiUvsD$U?5J%M-r zME|$30PSjYg+M;5M4DuG6!NYIp76aQOwx0`Qw!rLOk4~GNIi(dxIpCXMZBVo4FyMD zW0^HrDHGrcp_|zykVeo5!o{#2hGEreMp?bFxzg@T>587}tejY&&vZ7|A0Kr# zZ++2TS?R2=-Z=XHar?&`EA|rh);BxfKfZb!)2rmj+}_0VD+(*pQH(?SM`XXkusa~> zPb(Q~7|JT_=xZ2)%GWR?9ACpw%o>womV}Zt^*orCE-tXb5ll3#lu8hZqzGns`=H&0yC*PU!J<>MR{O8 z;$=U-uQhpHS`bG%Oo)3!IciDq zYgiTYs$61{_tKo7t--=qa4?_Pen~R4CXgNe1GhoBtjiIEhJ&D^31u(zZUWb+%hQS! z7roD*jln5+i?6G!9`g#~eT4X}Yv$5M5ovE?{D<4!yXrv?A9U|(-(wh(W`#ukgl&Ra zTx+dm|K`}IATFa(nf9|6D7ElukzvopzS!)Q#du^Li!qPo#rz8ME>PxvmM6Wzz}x_D+9V%?Xn^|qMeiKiJuPnj`bo<$93po8GB331iK^q+rls~0 zuqH|)cL(THbvEc(rz`U~c~f5e1ee^xu$Mt3U_CsKKIvNJa&Q@yu@St7`DOG?%de@C zOo|M6{UHLQPmHH=%{1rZy1TvgtuL6W{k*a;x=t@CPv6Iw)nT!!r6hvAhM4JM(mKYU$K8Pm3&nh+C%;@`W6;3`>k>5<)x*S%Hv< z$Z0zCLehC2n(goI@7#S=P9E^i!^(};aoQ!h^u>w%_$Irv(PaU8Rn{4r+6f~*k$1ev zloyC%Ao6^+*G4ZtbK-<{9!%@r{0z~y@(Pf}?4-0Vs*U?nj;uV8lUsFN9%-j<>{r`#w9=S%@jd`Ziji^ zTqF5_? zt!#81duUOK@e?4`a;Z7*Py97!n8C~i67HK8i8>pY=`We#C>x~acij4v -- queries google weather API for weather data" - q = input.re.groups()[0] - cond = goog_weather(q) + '''.weather -- 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