more work
This commit is contained in:
parent
15bfada07d
commit
92345146c7
97
bot.py
97
bot.py
|
@ -20,11 +20,8 @@ os.chdir(sys.path[0]) #do stuff relative to the installation directory
|
||||||
|
|
||||||
class Bot(object):
|
class Bot(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.plugins = {}
|
|
||||||
self.commands = [] # fn, name, func, args
|
self.commands = [] # fn, name, func, args
|
||||||
self.listens = {}
|
|
||||||
self.filters = [] #fn, name, func
|
self.filters = [] #fn, name, func
|
||||||
self.daemons = {}
|
|
||||||
|
|
||||||
bot = Bot()
|
bot = Bot()
|
||||||
bot.nickname = nickname
|
bot.nickname = nickname
|
||||||
|
@ -33,41 +30,49 @@ bot.network = network
|
||||||
|
|
||||||
print 'Loading plugins'
|
print 'Loading plugins'
|
||||||
magic_re = re.compile(r'^\s*#(command|filter)(?:: +(\S+) *(\S.*)?)?\s*$')
|
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
|
|
||||||
|
|
||||||
if bot.daemons:
|
def reload_plugins(mtime=[0]):
|
||||||
print 'Running daemons'
|
new_mtime = os.stat('plugins')
|
||||||
for daemon in bot.daemons.itervalues():
|
if new_mtime == mtime[0]:
|
||||||
thread.start_new_thread(daemon, ())
|
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))) == "<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
|
||||||
|
|
||||||
|
mtime[0] = new_mtime
|
||||||
|
|
||||||
|
reload_plugins()
|
||||||
|
|
||||||
print 'Connecting to IRC'
|
print 'Connecting to IRC'
|
||||||
bot.irc = irc.irc(network, nickname)
|
bot.irc = irc.irc(network, nickname)
|
||||||
|
@ -88,27 +93,32 @@ class Input(object):
|
||||||
self.host = host
|
self.host = host
|
||||||
self.paraml = paraml
|
self.paraml = paraml
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
self.doreply = True
|
||||||
|
|
||||||
class FakeBot(object):
|
class FakeBot(object):
|
||||||
def __init__(self, bot, input, func):
|
def __init__(self, bot, input, fn, func):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.input = input
|
self.input = input
|
||||||
self.msg = bot.irc.msg
|
self.msg = bot.irc.msg
|
||||||
self.cmd = bot.irc.cmd
|
self.cmd = bot.irc.cmd
|
||||||
|
self.fn = func
|
||||||
self.func = func
|
self.func = func
|
||||||
if input.command == "PRIVMSG":
|
if input.command == "PRIVMSG":
|
||||||
self.chan = input.paraml[0]
|
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(self.input.paraml[0], msg)
|
||||||
|
|
||||||
def reply(self, msg):
|
def reply(self, msg):
|
||||||
self.say(input.nick + ': ' + msg)
|
self.say(self.input.nick + ': ' + msg)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
out = self.func(self, self.input)
|
out = self.func(self, self.input)
|
||||||
if out is not None:
|
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.commands
|
||||||
print bot.filters
|
print bot.filters
|
||||||
|
@ -116,6 +126,7 @@ print bot.filters
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
out = bot.irc.out.get(timeout=1)
|
out = bot.irc.out.get(timeout=1)
|
||||||
|
reload_plugins()
|
||||||
for fn, name, func, args in bot.commands:
|
for fn, name, func, args in bot.commands:
|
||||||
input = Input(*out)
|
input = Input(*out)
|
||||||
for fn, nam, filt in bot.filters:
|
for fn, nam, filt in bot.filters:
|
||||||
|
@ -123,7 +134,7 @@ while True:
|
||||||
if input == None:
|
if input == None:
|
||||||
break
|
break
|
||||||
if input == None:
|
if input == None:
|
||||||
break
|
continue
|
||||||
thread.start_new_thread(FakeBot(bot, input, func).run, ())
|
thread.start_new_thread(FakeBot(bot, input, fn, func).run, ())
|
||||||
except Queue.Empty:
|
except Queue.Empty:
|
||||||
pass
|
pass
|
||||||
|
|
1
irc.py
1
irc.py
|
@ -72,6 +72,7 @@ class irc(object):
|
||||||
def parse_loop(self):
|
def parse_loop(self):
|
||||||
while True:
|
while True:
|
||||||
msg = self.conn.iqueue.get()
|
msg = self.conn.iqueue.get()
|
||||||
|
print '>>>', msg
|
||||||
if msg.startswith(":"): #has a prefix
|
if msg.startswith(":"): #has a prefix
|
||||||
prefix, command, params = irc_prefix_re.match(msg).groups()
|
prefix, command, params = irc_prefix_re.match(msg).groups()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -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 <diceroll> - 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)
|
Binary file not shown.
Binary file not shown.
|
@ -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 <user> - gets most recent tweet from <user>'''
|
||||||
|
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()))
|
Binary file not shown.
|
@ -6,7 +6,7 @@ from xml.etree import ElementTree
|
||||||
|
|
||||||
#command: weather
|
#command: weather
|
||||||
def weather(bot, input):
|
def weather(bot, input):
|
||||||
'''.weather <location> -- queries the google weather API for weather data'''
|
".weather <location> -- queries the google weather API for weather data"
|
||||||
|
|
||||||
if not input.inp.strip(): # blank line
|
if not input.inp.strip(): # blank line
|
||||||
return "welp"
|
return "welp"
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue