commit
12035fee94
|
@ -9,8 +9,8 @@ class Input(dict):
|
||||||
def __init__(self, conn, raw, prefix, command, params,
|
def __init__(self, conn, raw, prefix, command, params,
|
||||||
nick, user, host, paraml, msg):
|
nick, user, host, paraml, msg):
|
||||||
|
|
||||||
chan = paraml[0].lower()
|
chan = paraml[0]
|
||||||
if chan == conn.nick: # is a PM
|
if chan.lower() == conn.nick.lower(): # is a PM
|
||||||
chan = nick
|
chan = nick
|
||||||
|
|
||||||
def say(msg):
|
def say(msg):
|
||||||
|
@ -24,6 +24,9 @@ class Input(dict):
|
||||||
|
|
||||||
def pm(msg):
|
def pm(msg):
|
||||||
conn.msg(nick, msg)
|
conn.msg(nick, msg)
|
||||||
|
|
||||||
|
def set_nick(nick):
|
||||||
|
conn.set_nick(nick)
|
||||||
|
|
||||||
def me(msg):
|
def me(msg):
|
||||||
conn.msg(chan, "\x01%s %s\x01" % ("ACTION", msg))
|
conn.msg(chan, "\x01%s %s\x01" % ("ACTION", msg))
|
||||||
|
@ -35,7 +38,7 @@ class Input(dict):
|
||||||
params=params, nick=nick, user=user, host=host,
|
params=params, nick=nick, user=user, host=host,
|
||||||
paraml=paraml, msg=msg, server=conn.server, chan=chan,
|
paraml=paraml, msg=msg, server=conn.server, chan=chan,
|
||||||
notice=notice, say=say, reply=reply, pm=pm, bot=bot,
|
notice=notice, say=say, reply=reply, pm=pm, bot=bot,
|
||||||
me=me, lastparam=paraml[-1])
|
me=me, set_nick=set_nick, lastparam=paraml[-1])
|
||||||
|
|
||||||
# make dict keys accessible as attributes
|
# make dict keys accessible as attributes
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
All plugins need to 'from util import hook' if they want to be callable.
|
||||||
|
|
||||||
|
There are three ways to set when a plugin is called using
|
||||||
|
decorators. @hook.command causes it to be callable using normal command
|
||||||
|
syntax; an argument will register it under that name (so if my function is
|
||||||
|
called foo and I use @hook.command, .foo will work; if I use
|
||||||
|
@hook.command("bar"), .bar will work but not .foo). The first argument, inp,
|
||||||
|
will be the text that occurs after the command. (e.g., "bar" in ".foo bar").
|
||||||
|
|
||||||
|
@hook.regex takes an argument corresponding to the regex string (not the
|
||||||
|
compiled regex), followed by optional flags. It will attempt to match the regex
|
||||||
|
on all inputs; if so, the hooked function will be called with the match object.
|
||||||
|
|
||||||
|
@hook.event requires a parameter; if it's '*", it will trigger on every line. If
|
||||||
|
it's 'PRIVMSG', it'll trigger on only actual lines of chat (not
|
||||||
|
nick-changes). The first argument in these cases will be a two-element list of
|
||||||
|
the form ["#channel", "text"]; I don't know what it's like for NICK or other
|
||||||
|
'commands'.
|
||||||
|
|
||||||
|
@hook.singlethread indicates that the command should run in its own thread; this
|
||||||
|
means that you can't use the existing database connection object!
|
||||||
|
|
||||||
|
In addition to the standard argument, plugins can take other arguments; db is
|
||||||
|
the database object; input corresponds to the triggering line of text, and bot
|
||||||
|
is the bot itself.
|
||||||
|
|
||||||
|
TODO: describe what can be done with db, input, and bot.
|
|
@ -9,51 +9,79 @@ from util import hook
|
||||||
|
|
||||||
|
|
||||||
whitespace_re = re.compile(r'\s+')
|
whitespace_re = re.compile(r'\s+')
|
||||||
valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d\d+)([+-](\d+|\d*d\d+))*$',
|
valid_diceroll = r'^([+-]?(?:\d+|\d*d(?:\d+|F))(?:[+-](?:\d+|\d*d(?:\d+|F)))*)( .+)?$'
|
||||||
re.I)
|
valid_diceroll_re = re.compile(valid_diceroll, re.I)
|
||||||
sign_re = re.compile(r'[+-]?(?:\d*d)?\d+', re.I)
|
sign_re = re.compile(r'[+-]?(?:\d*d)?(?:\d+|F)', re.I)
|
||||||
split_re = re.compile(r'([\d+-]*)d?(\d*)', re.I)
|
split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I)
|
||||||
|
|
||||||
|
|
||||||
def nrolls(count, n):
|
def nrolls(count, n):
|
||||||
"roll an n-sided die count times"
|
"roll an n-sided die count times"
|
||||||
|
if n == "F":
|
||||||
|
return [random.randint(-1,1) for x in xrange(count)]
|
||||||
if n < 2: # it's a coin
|
if n < 2: # it's a coin
|
||||||
if count < 5000:
|
if count < 5000:
|
||||||
return sum(random.randint(0, 1) for x in xrange(count))
|
return [random.randint(0, 1) for x in xrange(count)]
|
||||||
else: # fake it
|
else: # fake it
|
||||||
return int(random.normalvariate(.5*count, (.75*count)**.5))
|
return [int(random.normalvariate(.5*count, (.75*count)**.5))]
|
||||||
else:
|
else:
|
||||||
if count < 5000:
|
if count < 5000:
|
||||||
return sum(random.randint(1, n) for x in xrange(count))
|
return [random.randint(1, n) for x in xrange(count)]
|
||||||
else: # fake it
|
else: # fake it
|
||||||
return int(random.normalvariate(.5*(1+n)*count,
|
return [int(random.normalvariate(.5*(1+n)*count,
|
||||||
(((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))
|
(((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))]
|
||||||
|
|
||||||
|
|
||||||
@hook.command('roll')
|
@hook.command('roll')
|
||||||
|
@hook.regex(valid_diceroll, re.I)
|
||||||
@hook.command
|
@hook.command
|
||||||
def dice(inp):
|
def dice(inp):
|
||||||
".dice <diceroll> -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \
|
".dice <diceroll> -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \
|
||||||
"D20s, subtract 1D5, add 4"
|
"D20s, subtract 1D5, add 4"
|
||||||
|
desc = None
|
||||||
|
try: # if inp is a re.match object...
|
||||||
|
(inp, desc) = inp.groups()
|
||||||
|
except AttributeError:
|
||||||
|
pass # we got called via hook.command, inp is already the roll
|
||||||
|
if desc == None: (inp, desc) = valid_diceroll_re.match(inp).groups()
|
||||||
|
if "d" not in inp: return
|
||||||
spec = whitespace_re.sub('', inp)
|
spec = whitespace_re.sub('', inp)
|
||||||
if not valid_diceroll_re.match(spec):
|
if not valid_diceroll_re.match(spec):
|
||||||
return "Invalid diceroll"
|
return "Invalid diceroll"
|
||||||
sum = 0
|
|
||||||
groups = sign_re.findall(spec)
|
groups = sign_re.findall(spec)
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
rolls = []
|
||||||
|
|
||||||
for roll in groups:
|
for roll in groups:
|
||||||
count, side = split_re.match(roll).groups()
|
count, side = split_re.match(roll).groups()
|
||||||
if side == "":
|
count = int(count) if count not in " +-" else 1
|
||||||
sum += int(count)
|
if side.upper() == "F": # fudge dice are basically 1d3-2
|
||||||
|
for fudge in nrolls(count, "F"):
|
||||||
|
if fudge == 1:
|
||||||
|
rolls.append("\x033+\x0F")
|
||||||
|
elif fudge == -1:
|
||||||
|
rolls.append("\x034-\x0F")
|
||||||
|
else:
|
||||||
|
rolls.append("0")
|
||||||
|
total += fudge
|
||||||
|
elif side == "":
|
||||||
|
total += count
|
||||||
else:
|
else:
|
||||||
count = int(count) if count not in" +-" else 1
|
|
||||||
side = int(side)
|
side = int(side)
|
||||||
try:
|
try:
|
||||||
if count > 0:
|
if count > 0:
|
||||||
sum += nrolls(count, side)
|
dice = nrolls(count, side)
|
||||||
|
rolls += map(str, dice)
|
||||||
|
total += sum(dice)
|
||||||
else:
|
else:
|
||||||
sum -= nrolls(abs(count), side)
|
dice = nrolls(-count, side)
|
||||||
|
rolls += [str(-x) for x in dice]
|
||||||
|
total -= sum(dice)
|
||||||
except OverflowError:
|
except OverflowError:
|
||||||
return "Thanks for overflowing a float, jerk >:["
|
return "Thanks for overflowing a float, jerk >:["
|
||||||
|
|
||||||
return str(sum)
|
if desc:
|
||||||
|
return "%s: %d (%s=%s)" % (desc.strip(), total, inp, ", ".join(rolls))
|
||||||
|
else:
|
||||||
|
return "%d (%s=%s)" % (total, inp, ", ".join(rolls))
|
||||||
|
|
|
@ -12,7 +12,7 @@ def db_init(db):
|
||||||
|
|
||||||
|
|
||||||
def get_memory(db, chan, word):
|
def get_memory(db, chan, word):
|
||||||
row = db.execute("select data from memory where chan=? and word=lower(?)",
|
row = db.execute("select data from memory where chan=lower(?) and word=lower(?)",
|
||||||
(chan, word)).fetchone()
|
(chan, word)).fetchone()
|
||||||
if row:
|
if row:
|
||||||
return row[0]
|
return row[0]
|
||||||
|
|
Loading…
Reference in New Issue