From d34421bd233999868dad764a505ce3d01c711d9c Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Sun, 22 Aug 2010 20:07:27 -0400 Subject: [PATCH 01/12] change VERSION string use git describe --always to return a 'version number' --- plugins/misc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/misc.py b/plugins/misc.py index 3ce29cf..710ea4b 100644 --- a/plugins/misc.py +++ b/plugins/misc.py @@ -45,10 +45,10 @@ def onjoin(paraml, conn=None): @hook.regex(r'^\x01VERSION\x01$') def version(inp, notice=None): - p = subprocess.Popen(['hg', 'id', '-n'], stdout=subprocess.PIPE) + p = subprocess.Popen(['git', 'describe', '--always'], stdout=subprocess.PIPE) stdout, _ = p.communicate() p.wait() - ret = 'r' + stdout[:-2] - notice('\x01VERSION skybot %s - http://bitbucket.org/Scaevolus/' + ret = stdout.strip() + notice('\x01VERSION skybot %s - http://github.com/rmmh/' 'skybot/\x01' % ret) From 219fe44d61d61dc3beb89bc9423edab7676a00d3 Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Sat, 14 Aug 2010 17:07:17 -0400 Subject: [PATCH 02/12] add fudge dice fudge dice are dF's, and are either -1, 0, or +1 --- plugins/dice.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/dice.py b/plugins/dice.py index 0ed6c03..c7f8649 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -9,10 +9,10 @@ from util import hook whitespace_re = re.compile(r'\s+') -valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d\d+)([+-](\d+|\d*d\d+))*$', +valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d(\d+|F))([+-](\d+|\d*d(\d+|F)))*$', re.I) -sign_re = re.compile(r'[+-]?(?:\d*d)?\d+', re.I) -split_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?(F|\d*)', re.I) def nrolls(count, n): @@ -43,10 +43,15 @@ def dice(inp): groups = sign_re.findall(spec) for roll in groups: count, side = split_re.match(roll).groups() - if side == "": - sum += int(count) + count = int(count) if count not in " +-" else 1 + if side.lower() == "f": + if count > 0: + sum += nrolls(count, 3) - 2 * count + else: + sum -= nrolls(count, 3) + 2 * count + elif side == "": + sum += count else: - count = int(count) if count not in" +-" else 1 side = int(side) try: if count > 0: From 4cc12512ff7e7f08c2e5cbfcf273e99a5e8d4d9b Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Sat, 14 Aug 2010 17:27:09 -0400 Subject: [PATCH 03/12] allow dice to be rolled without explicit command anything that looks like a dice roll will be rolled as such --- plugins/dice.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/dice.py b/plugins/dice.py index c7f8649..6005cf5 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -9,8 +9,8 @@ from util import hook whitespace_re = re.compile(r'\s+') -valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d(\d+|F))([+-](\d+|\d*d(\d+|F)))*$', - re.I) +valid_diceroll = r'^([+-]?(\d+|\d*d(\d+|F))([+-](\d+|\d*d(\d+|F)))*)$' +valid_diceroll_re = re.compile(valid_diceroll, re.I) sign_re = re.compile(r'[+-]?(?:\d*d)?(?:\d+|F)', re.I) split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I) @@ -31,11 +31,15 @@ def nrolls(count, n): @hook.command('roll') +@hook.regex(valid_diceroll, re.I) @hook.command def dice(inp): ".dice -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \ "D20s, subtract 1D5, add 4" - + try: + inp = inp.groups()[0] # try to grab the roll + except AttributeError: + pass # we got called via hook.command, inp is already the roll spec = whitespace_re.sub('', inp) if not valid_diceroll_re.match(spec): return "Invalid diceroll" From 91124d1f8a6329ad396cc23200ffe4db6a163a54 Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Mon, 16 Aug 2010 04:36:47 -0400 Subject: [PATCH 04/12] allow nick changing in commands now you can change nick via input.set_nick --- core/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/main.py b/core/main.py index aedb0e3..c05c703 100644 --- a/core/main.py +++ b/core/main.py @@ -24,6 +24,9 @@ class Input(dict): def pm(msg): conn.msg(nick, msg) + + def set_nick(nick): + conn.set_nick(nick) def me(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, paraml=paraml, msg=msg, server=conn.server, chan=chan, 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 def __getattr__(self, key): From 7876fd57acc357a0899743594914ff2e7c26c232 Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Mon, 16 Aug 2010 05:32:28 -0400 Subject: [PATCH 05/12] dicerolls now have descriptions --- plugins/dice.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/dice.py b/plugins/dice.py index 6005cf5..54445ab 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -9,7 +9,7 @@ from util import hook whitespace_re = re.compile(r'\s+') -valid_diceroll = r'^([+-]?(\d+|\d*d(\d+|F))([+-](\d+|\d*d(\d+|F)))*)$' +valid_diceroll = r'^([+-]?(?:\d+|\d*d(?:\d+|F))(?:[+-](?:\d+|\d*d(?:\d+|F)))*)( .+)?$' valid_diceroll_re = re.compile(valid_diceroll, re.I) sign_re = re.compile(r'[+-]?(?:\d*d)?(?:\d+|F)', re.I) split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I) @@ -36,10 +36,13 @@ def nrolls(count, n): def dice(inp): ".dice -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \ "D20s, subtract 1D5, add 4" - try: - inp = inp.groups()[0] # try to grab the roll + 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() + desc = desc.strip() spec = whitespace_re.sub('', inp) if not valid_diceroll_re.match(spec): return "Invalid diceroll" @@ -65,4 +68,4 @@ def dice(inp): except OverflowError: return "Thanks for overflowing a float, jerk >:[" - return str(sum) + return "%s: %d" % (desc, sum) From b70c703af518dc474316e3e7679b384c495bf835 Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Tue, 17 Aug 2010 13:00:41 -0400 Subject: [PATCH 06/12] explicitly show each die roll --- plugins/dice.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/dice.py b/plugins/dice.py index 54445ab..0119116 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -17,17 +17,19 @@ split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I) def nrolls(count, n): "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 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 - return int(random.normalvariate(.5*count, (.75*count)**.5)) + return [int(random.normalvariate(.5*count, (.75*count)**.5))] else: 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 - return int(random.normalvariate(.5*(1+n)*count, - (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5)) + return [int(random.normalvariate(.5*(1+n)*count, + (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))] @hook.command('roll') @@ -46,26 +48,25 @@ def dice(inp): spec = whitespace_re.sub('', inp) if not valid_diceroll_re.match(spec): return "Invalid diceroll" - sum = 0 groups = sign_re.findall(spec) + + rolls = [] + bias = 0 for roll in groups: count, side = split_re.match(roll).groups() count = int(count) if count not in " +-" else 1 - if side.lower() == "f": - if count > 0: - sum += nrolls(count, 3) - 2 * count - else: - sum -= nrolls(count, 3) + 2 * count + if side.lower() == "f": # fudge dice are basically 1d3-2 + rolls += nrolls(count, "F") elif side == "": - sum += count + bias += count else: side = int(side) try: if count > 0: - sum += nrolls(count, side) + rolls += nrolls(count, side) else: - sum -= nrolls(abs(count), side) + rolls += [-x for x in nrolls(abs(count), side)] except OverflowError: return "Thanks for overflowing a float, jerk >:[" - return "%s: %d" % (desc, sum) + return "%s: %d (%s=%s)" % (desc, sum(rolls)+bias, inp, str(rolls).strip("[]")) From 32aaf97c3441d7d21363050aa5ee11a04f73a2ed Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Tue, 17 Aug 2010 13:02:59 -0400 Subject: [PATCH 07/12] allow empty descriptions for dice rolls --- plugins/dice.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/dice.py b/plugins/dice.py index 0119116..70c4ef6 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -44,7 +44,7 @@ def dice(inp): 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() - desc = desc.strip() + spec = whitespace_re.sub('', inp) if not valid_diceroll_re.match(spec): return "Invalid diceroll" @@ -69,4 +69,7 @@ def dice(inp): except OverflowError: return "Thanks for overflowing a float, jerk >:[" - return "%s: %d (%s=%s)" % (desc, sum(rolls)+bias, inp, str(rolls).strip("[]")) + if desc: + return "%s: %d (%s=%s)" % (desc.strip(), sum(rolls)+bias, inp, str(rolls).strip("[]")) + else: + return "%d (%s=%s)" % (sum(rolls)+bias, inp, str(rolls).strip("[]")) From c35f15b9810e27e638928461060fe689515eb607 Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Tue, 17 Aug 2010 13:54:47 -0400 Subject: [PATCH 08/12] color fudge dice rolls when rolling fudge dice, use + and - instead. + is green, - is red. --- plugins/dice.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/plugins/dice.py b/plugins/dice.py index 70c4ef6..21cde86 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -50,26 +50,38 @@ def dice(inp): return "Invalid diceroll" groups = sign_re.findall(spec) + total = 0 rolls = [] - bias = 0 + for roll in groups: count, side = split_re.match(roll).groups() count = int(count) if count not in " +-" else 1 - if side.lower() == "f": # fudge dice are basically 1d3-2 - rolls += nrolls(count, "F") + 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 == "": - bias += count + total += count else: side = int(side) try: if count > 0: - rolls += nrolls(count, side) + dice = nrolls(count, side) + rolls += map(str, dice) + total += sum(dice) else: - rolls += [-x for x in nrolls(abs(count), side)] + dice = nrolls(-count, side) + rolls += [str(-x) for x in dice] + total -= sum(dice) except OverflowError: return "Thanks for overflowing a float, jerk >:[" if desc: - return "%s: %d (%s=%s)" % (desc.strip(), sum(rolls)+bias, inp, str(rolls).strip("[]")) + return "%s: %d (%s=%s)" % (desc.strip(), total, inp, ", ".join(rolls)) else: - return "%d (%s=%s)" % (sum(rolls)+bias, inp, str(rolls).strip("[]")) + return "%d (%s=%s)" % (total, inp, ", ".join(rolls)) From ee79576569c7d99c3723c297ccadfc790d22dca7 Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Tue, 17 Aug 2010 18:43:51 -0400 Subject: [PATCH 09/12] take a first stab at writing documentation document the @hook stuff and how you can take input, bot, and db as arguments. --- docs/plugins | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 docs/plugins diff --git a/docs/plugins b/docs/plugins new file mode 100644 index 0000000..430d1b0 --- /dev/null +++ b/docs/plugins @@ -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. \ No newline at end of file From 3f326dcc2257af2a884a0d267f88aef02a639ccd Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Tue, 17 Aug 2010 23:14:28 -0400 Subject: [PATCH 10/12] fix stupid bug involving channel case and ?remember --- plugins/remember.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/remember.py b/plugins/remember.py index 97f035c..55841f8 100644 --- a/plugins/remember.py +++ b/plugins/remember.py @@ -12,7 +12,7 @@ def db_init(db): 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() if row: return row[0] From c5f1fccd297ab3a26abcc093cbf2d8776a9674dc Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Sun, 22 Aug 2010 21:33:24 -0400 Subject: [PATCH 11/12] fix stupid PM bug the bot wouldn't respond in PMs to people with uppercase letters in their name --- core/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/main.py b/core/main.py index c05c703..da35066 100644 --- a/core/main.py +++ b/core/main.py @@ -9,8 +9,8 @@ class Input(dict): def __init__(self, conn, raw, prefix, command, params, nick, user, host, paraml, msg): - chan = paraml[0].lower() - if chan == conn.nick: # is a PM + chan = paraml[0] + if chan.lower() == conn.nick.lower(): # is a PM chan = nick def say(msg): From 434ff989f256c5f90a1c303766cdb969aab0fd6d Mon Sep 17 00:00:00 2001 From: Patrick Hurst Date: Mon, 23 Aug 2010 02:19:40 -0400 Subject: [PATCH 12/12] don't roll on lines like '15 minutes later' any die-initial roll line has to actually contain dice --- plugins/dice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dice.py b/plugins/dice.py index 21cde86..ea5eb27 100644 --- a/plugins/dice.py +++ b/plugins/dice.py @@ -44,7 +44,7 @@ def dice(inp): 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) if not valid_diceroll_re.match(spec): return "Invalid diceroll"