From 92345146c77f184feacf6343398bb9558d205f95 Mon Sep 17 00:00:00 2001 From: Ryan Hitchman Date: Sat, 14 Mar 2009 22:14:07 -0600 Subject: [PATCH] more work --- bot.py | 97 ++++++++++++++++++++++++-------------------- irc.py | 1 + plugins/dice.py | 55 +++++++++++++++++++++++++ plugins/dice.pyc | Bin 0 -> 2148 bytes plugins/filter.pyc | Bin 0 -> 708 bytes plugins/twitter.py | 23 +++++++++++ plugins/twitter.pyc | Bin 0 -> 1097 bytes plugins/weather.py | 2 +- plugins/weather.pyc | Bin 0 -> 1450 bytes 9 files changed, 134 insertions(+), 44 deletions(-) create mode 100755 plugins/dice.py create mode 100644 plugins/dice.pyc create mode 100644 plugins/filter.pyc create mode 100644 plugins/twitter.py create mode 100644 plugins/twitter.pyc create mode 100644 plugins/weather.pyc diff --git a/bot.py b/bot.py index be435b7..a6c05d4 100755 --- a/bot.py +++ b/bot.py @@ -20,11 +20,8 @@ os.chdir(sys.path[0]) #do stuff relative to the installation directory class Bot(object): def __init__(self): - self.plugins = {} self.commands = [] # fn, name, func, args - self.listens = {} self.filters = [] #fn, name, func - self.daemons = {} bot = Bot() bot.nickname = nickname @@ -33,41 +30,49 @@ 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))) == ""] - 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 -if bot.daemons: - print 'Running daemons' - for daemon in bot.daemons.itervalues(): - thread.start_new_thread(daemon, ()) +def reload_plugins(mtime=[0]): + new_mtime = os.stat('plugins') + if new_mtime == mtime[0]: + return + + bot.commands = [] + bot.filters = [] + + + for filename in glob.glob("plugins/*.py"): + shortname = os.path.splitext(os.path.basename(filename))[0] + try: + plugin = imp.load_source(shortname, filename) + 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 + + mtime[0] = new_mtime + +reload_plugins() print 'Connecting to IRC' bot.irc = irc.irc(network, nickname) @@ -88,27 +93,32 @@ class Input(object): self.host = host self.paraml = paraml self.msg = msg + self.doreply = True class FakeBot(object): - def __init__(self, bot, input, func): + def __init__(self, bot, input, fn, func): self.bot = bot self.input = input self.msg = bot.irc.msg self.cmd = bot.irc.cmd + self.fn = func self.func = func if input.command == "PRIVMSG": self.chan = input.paraml[0] def say(self, msg): - self.bot.irc.msg(input.paraml[0], msg) + self.bot.irc.msg(self.input.paraml[0], msg) def reply(self, msg): - self.say(input.nick + ': ' + msg) + self.say(self.input.nick + ': ' + msg) def run(self): out = self.func(self, self.input) if out is not None: - self.reply(unicode(out)) + if self.doreply: + self.reply(unicode(out)) + else: + self.say(unicode(out)) print bot.commands print bot.filters @@ -116,6 +126,7 @@ print bot.filters while True: try: out = bot.irc.out.get(timeout=1) + reload_plugins() for fn, name, func, args in bot.commands: input = Input(*out) for fn, nam, filt in bot.filters: @@ -123,7 +134,7 @@ while True: if input == None: break if input == None: - break - thread.start_new_thread(FakeBot(bot, input, func).run, ()) + continue + thread.start_new_thread(FakeBot(bot, input, fn, func).run, ()) except Queue.Empty: pass diff --git a/irc.py b/irc.py index b864236..aad2ddb 100644 --- a/irc.py +++ b/irc.py @@ -72,6 +72,7 @@ class irc(object): def parse_loop(self): while True: msg = self.conn.iqueue.get() + print '>>>', msg if msg.startswith(":"): #has a prefix prefix, command, params = irc_prefix_re.match(msg).groups() else: diff --git a/plugins/dice.py b/plugins/dice.py new file mode 100755 index 0000000..560dd47 --- /dev/null +++ b/plugins/dice.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +""" +dice.py written by Scaevolus 2008, updated 2009 +simulates dicerolls +""" +import re +import random + +whitespace_re = re.compile(r'\s+') +valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d\d+)([+-](\d+|\d*d\d+))*$') +sign_re = re.compile(r'[+-]?(?:\d*d)?\d+') +split_re = re.compile(r'([\d+-]*)d?(\d*)') + +def nrolls(count, n): + "roll an n-sided die count times" + if n < 2: #it's a coin + if count < 5000: + return sum(random.randint(0,1) for x in xrange(count)) + else: #fake it + return int(random.normalvariate(.5*count,(.75*count)**.5)) + else: + if count < 5000: + return sum(random.randint(1,n) for x in xrange(count)) + else: #fake it + return int(random.normalvariate(.5*(1+n)*count, + (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5)) + +#command +def dice(bot, input): + ".dice - simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \ + "D20s, subtract 1D5, add 4" + if not input.inp.strip(): + return dice.__doc__ + + spec = whitespace_re.sub('', input.inp) + if not valid_diceroll_re.match(spec): + return "Invalid diceroll" + sum = 0 + groups = sign_re.findall(spec) + for roll in groups: + count, side = split_re.match(roll).groups() + if side == "": + sum += int(count) + else: + count = int(count) if count not in " +-" else 1 + side = int(side) + try: + if count > 0: + sum += nrolls(count, side) + else: + sum -= nrolls(abs(count), side) + except OverflowError: + return "Thanks for overflowing a float, jerk >:[" + + return str(sum) diff --git a/plugins/dice.pyc b/plugins/dice.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd8f62fccc987e75b70b51ccf7070b7c7ff67b20 GIT binary patch literal 2148 zcma)7&2Ah;5U!s6vp3n;;0=yI34JhV{EIP(2qChAa1Oa7VvGoiZLIP3c+<}8%w)RP zb|hOu3L<$QuDk$89smh$oOuEGs%BS7Bu8TGuBqy(ssF0|<)2Rb#qqEAdNg|Kc;CTs z1%M%X1gOXa^hD7kAQc|aqks}cwnnB#&%vsX$-0n@G1(BZIVPJzw#c;TCn%pFGa<+% zo7f5`+45b0wz0irrFf=~OY41->HTMVs~0Cnc{+4@ZFTkLk{%W&_K9KYi?*|aVG7b| z{<6%|)V1xuc<#Fz^zFDs7vlZH_TuvHMz~`ZPj}3M!FxXBt2b`u7v|hM5Uyl5Zn5P2 z24vkdZo_S0%exEnhV?AW_X1q#ZU?x$kHi!Bl04H0=na-A-HK>QNiSFgY%xiB4 ziL;y=`*Mb;r+_iVP3HJ~1aNYVBix;(GeyXaMI4elg!`R(+N6il8Jt*)!+fIqA*=CC zfHKZZKJc7;Ms4OR9t*6jBFC^00+0YV1*}MieVe&YMm^@Z>u&dxEIBF4^>;xy{&j@UinL~JtRC_}yzzq9}8@+>|Wxhw`o z?q0cbKWwlrY0BY1(shC)^%J>5s>BK3fl!v0gE&2kON$|@GRWUb9V4z+l|%MT$nz2@ z1~QqwOOTUkp`fEW>RRwI-gChfCDSqiE}ySi>PzT6#VV1~K1X#fNV{USY-I{)oos{m zP9=k!o?%3qWLtD915r$n;uc|H4AxnV`~=y_5%;W4C%@Baoi=xWqq7G2wtOjT$zCAX z+kwU=ok1PelA^|26dkrHM?&ymy0_Si?*ShAX@ZBm zG(zrtw+pbs{j6_~_usl+*5_=+C7rDFS9B%3X4Y1h&5gzDT84N{Z?3JnCGCd$zKnZb zf4X^NNyozK?@Mek)tacX(Yt34}IDN9x_E=&K(rv3JDoIP>+ATM=(l#~Z)er&V8 zjy15@FX_Xie4^KHZHK&RNBvlM?m6d6TcGUN0x%(yy|-ub-rk;c>hWWXHDAQNWUs6m zncZbA;@95TFXpnkLF{{vMT@ijjBhz(2R1Wtnxa);ut=?E2Je%8nGXvWu}s7(PF#!k zo!EXbisWut=4CZkz6@E15c|0olQt`cULtTs(u>%Oh=)|JIMWfYoJs&IGX(MW^%U_e zUBz(=ELWRW7lO-bT1}{?no=!lst+(>-9W30ss?N}xT-FxP9O_9;(dqT6fDQ28g>q> zmk$b?CK3OfMZEB$E`#?NaLi)zl~;nDH}H-lzdgv!FiqB_bgZ!nM=l0aa1MXL00W^IFP!d_B z5m@^R&n)W!#yAo>#ql>VTTM0;qpVQ0)1F#PU_BSdH~i9jJ+Zq{b@Ik98`8&t(Ji b3CzDH0sX&|m$kp#xV$aRE7`Iakx}#;aEXB> literal 0 HcmV?d00001 diff --git a/plugins/twitter.py b/plugins/twitter.py new file mode 100644 index 0000000..2b83896 --- /dev/null +++ b/plugins/twitter.py @@ -0,0 +1,23 @@ +""" +twitter.py: written by Scaevolus 2009 +retrieves most recent tweets +""" + +import urllib +from xml.etree import ElementTree + +#command +def twitter(bot, input): + '''.twitter - gets most recent tweet from ''' + if not input.inp.strip(): + return twitter.__doc__ + + url = "http://twitter.com/statuses/user_timeline/%s.xml?count=1" \ + % urllib.quote(input.inp) + tweet = ElementTree.parse(urllib.urlopen(url)) + + if tweet.find('error') is not None: + return "can't find that username" + + tweet = tweet.find('status') + bot.say(': '.join(tweet.find(x).text for x in 'user/name created_at text'.split())) diff --git a/plugins/twitter.pyc b/plugins/twitter.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39609361b7fb6d928cd43999209e5195ef810861 GIT binary patch literal 1097 zcmaJ=&2G~`5T3QuG(Rm=AhqR!_EO;sN%%QHRSJi4;F3dvK&pfy*WR|<+Fo~e+$K_c zD&prwcmb|F6)ylYYnKZG%O0Mlz5>Ox}N?;*EkYHhkY`yGnJG3tyL-s=sTo`1M=H_BXcQ0 z184!)0A(Kp2B1Tj7J||gfGyBV4dzjDcIo~K@P$~qToHYy9XRQN z=@3W5)p%PC_>#bVNS8pbz-NF%I?{$uq}np*RZeBE<|QkLY#Mbr-=|~-jYx8*a?0+? zhw_N({#y!pXieUL+%r0Oj96|Dhc_B0CLcPCkyzZ2sb;8imFYqax7=W!XD<^|75L&2 zvRukqW8EgLlDK$)@=zD4#N!wxA1&fskt~#f8&S`QwK;N3g;K7=2&mDZ!kiRF|XUrdHN}cXaVjHnKG-6 zbm9IKT`n98xpSlnH81V%1_PO?3+^7hxR@bVBNMA)ROyT=Q@q{s@DkjYAD;%3b`8Y0K_>B{z zi#lMLX^cFO&4EF`P5D&lPY{E(5q(?q}ydQ!!(GNm#S8N9Bq7&Q}{+FEa t?=x8;gRB`%#DIQ0RafB|>R{x9|0>=gz)o(`DpR}MOvjVb4pzik_#3qm?ehQt literal 0 HcmV?d00001 diff --git a/plugins/weather.py b/plugins/weather.py index 509d0ad..6d2f115 100755 --- a/plugins/weather.py +++ b/plugins/weather.py @@ -6,7 +6,7 @@ from xml.etree import ElementTree #command: weather def weather(bot, input): - '''.weather -- queries the google weather API for weather data''' + ".weather -- queries the google weather API for weather data" if not input.inp.strip(): # blank line return "welp" diff --git a/plugins/weather.pyc b/plugins/weather.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b073fc95faa7054daf94fcfc986214dc2fa7dac7 GIT binary patch literal 1450 zcmah}Ur!T35TCtkODQ1e184$qO(a|+^n5g=K@CDAF^wri6B5%LciX#_>+Ny7mx3wr z0l$&2ek#9!&TJ2eA!^&5o7w%%zu)Zj`_F~e(bpdbAyl6lK40O|K8gt77$t+qfl~&@ zsNm0qQwLHPj$L3;4Zb0$MudVgQ!Kyqz=&>bSsbfJ7)_wXZP-$EkZY1q6o`}-UA|JA4R9)mUmMX z21crEhi`51vr-gNAk&GcsOHze*Y6MbP!%_J6qvx!oW~-~HL+cq;noW?W4G+!^cw1k zG5NOdkH_QAba7-B!l2Yb@8Qm_D$^+2FnlOXnEa8_*08az zH}F;z7rfLmi+RAm$ed$wxzZ*`d8i`M3F&&!ZJ;7Gt!F6CpTY#-(;8eNtM*oq#Bl); zH+0|wJ8Qs4x*#4o+ZL(U-qdtyFNh62V_~M71nfLFlu&pmzzR)l=V=+sO#2m$o%}-2 z;&wL{nK;jjon