disable !command, enable fuzzy command matching, make help automatic, add disabled_command (for fuzzy command match disabling), misc bugs

This commit is contained in:
Ryan Hitchman 2010-05-07 17:16:44 -06:00
parent e5b623cb68
commit 6452c1169c
36 changed files with 67 additions and 127 deletions

6
bot.py
View File

@ -25,6 +25,10 @@ eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(),
os.path.join('core', 'reload.py'), 'exec'))
reload(init=True)
config()
if not hasattr(bot, 'config'):
exit()
print 'Connecting to IRC'
bot.conns = {}
@ -39,7 +43,7 @@ try:
bot.conns[name] = IRC(conf['server'], conf['nick'], conf=conf,
port=conf.get('port', 6667), channels=conf['channels'])
except Exception, e:
print 'ERROR: malformed config file', Exception, e
print 'ERROR: malformed config file', e
sys.exit()
bot.persist_dir = os.path.abspath('persist')

View File

@ -3,10 +3,6 @@ import json
import os
def load():
return
def save(conf):
json.dump(conf, open('config', 'w'), sort_keys=True, indent=2)
@ -22,14 +18,20 @@ if not os.path.exists('config'):
"nick": "skybot",
"channels": ["#test"]
}
}
},
"disabled_plugins": [],
"disabled_commands": [],
"acls": {}
}''') + '\n')
bot.config = json.load(open('config'))
bot._config_mtime = os.stat('config').st_mtime
def config():
# reload config from file if file has changed
if bot._config_mtime != os.stat('config').st_mtime:
try:
bot.config = json.load(open('config'))
except ValueError, e:
print 'ERROR: malformed config!', e
bot._config_mtime = 0

View File

@ -105,18 +105,33 @@ class Handler(object):
self.input_queue.put(value)
def dispatch(input, kind, func, args):
def dispatch(input, kind, func, args, autohelp=False):
for sieve, in bot.plugs['sieve']:
input = do_sieve(sieve, bot, input, func, kind, args)
if input == None:
return
if autohelp and args.get('autohelp', True) and not input.inp:
input.reply(func.__doc__)
return
if func._thread:
bot.threads[func].put(input)
else:
thread.start_new_thread(run, (func, input))
def match_command(command):
commands = list(bot.commands)
# do some fuzzy matching
prefix = filter(lambda x: x.startswith(command), commands)
if len(prefix) == 1:
return prefix[0]
return command
def main(conn, out):
inp = Input(conn, *out)
@ -127,9 +142,9 @@ def main(conn, out):
if inp.command == 'PRIVMSG':
# COMMANDS
if inp.chan == inp.nick: # private message, no command prefix
prefix = r'^(?:[.!]?|'
prefix = r'^(?:[.]?|'
else:
prefix = r'^(?:[.!]|'
prefix = r'^(?:[.]|'
command_re = prefix + inp.conn.nick
command_re += r'[:,]*\s+)(\w+)(?:$|\s+)(.*)'
@ -137,14 +152,17 @@ def main(conn, out):
m = re.match(command_re, inp.lastparam)
if m:
command = m.group(1).lower()
trigger = m.group(1).lower()
command = match_command(trigger)
if command in bot.commands:
input = Input(conn, *out)
input.trigger = trigger
input.inp_unstripped = m.group(2)
input.inp = m.group(2).strip()
input.inp = input.inp_unstripped.strip()
func, args = bot.commands[command]
dispatch(input, "command", func, args)
dispatch(input, "command", func, args, autohelp=True)
# REGEXES
for func, args in bot.plugs['regex']:

View File

@ -55,9 +55,6 @@ def babel_gen(inp):
def babel(inp):
".babel <sentence> -- translates <sentence> through multiple languages"
if not inp:
return babel.__doc__
try:
return list(babel_gen(inp))[-1][2]
except IOError, e:
@ -68,9 +65,6 @@ def babel(inp):
def babelext(inp):
".babelext <sentence> -- like .babel, but with more detailed output"
if not inp:
return babelext.__doc__
try:
babels = list(babel_gen(inp))
except IOError, e:

View File

@ -15,9 +15,6 @@ MAX_STEPS = 1000000
def bf(inp):
".bf <prog> -- executes brainfuck program <prog>"""
if not inp:
return bf.__doc__
program = re.sub('[^][<>+-.,]', '', inp)
# create a dict of brackets pairs, for speed later on

View File

@ -5,9 +5,6 @@ from util import hook, http
def bam(inp):
".bam [basic|magic|pepsi|jprdy] <message> -- creates a big ass message"
if not inp:
return bam.__doc__
host = 'http://bigassmessage.com/'
path = 'dsx_BAM/boe.php?'
params = {'action': 'saveMsg', 'theStyle': 'basic', 'theMessage': inp}

View File

@ -8,9 +8,6 @@ from util import hook
def choose(inp):
".choose <choice1>, <choice2>, ... <choicen> -- makes a decision"
if not inp:
return choose.__doc__
c = re.findall(r'([^,]+)', inp)
if len(c) == 1:
c = re.findall(r'(\S+)', inp)

View File

@ -30,13 +30,11 @@ def nrolls(count, n):
(((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))
@hook.command
@hook.command('roll')
@hook.command
def dice(inp):
".dice <diceroll> -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \
"D20s, subtract 1D5, add 4"
if not inp:
return dice.__doc__
spec = whitespace_re.sub('', inp)
if not valid_diceroll_re.match(spec):

View File

@ -7,8 +7,6 @@ from util import hook, http
@hook.command
def urban(inp):
'''.u/.urban <phrase> -- looks up <phrase> on urbandictionary.com'''
if not inp:
return urban.__doc__
url = 'http://www.urbandictionary.com/define.php'
page = http.get_html(url, term=inp)
@ -28,13 +26,10 @@ def urban(inp):
# define plugin by GhettoWizard & Scaevolus
@hook.command('dict')
@hook.command('dictionary')
@hook.command
def define(inp):
".define/.dict <word> -- fetches definition of <word>"
if not inp:
return define.__doc__
".define/.dictionary <word> -- fetches definition of <word>"
url = 'http://ninjawords.com/'

View File

@ -47,9 +47,6 @@ def dotnetpad(lang, code, timeout=10):
def fs(inp):
".fs -- post a F# code snippet to dotnetpad.net and print the results"
if not inp:
return fs.__doc__
return dotnetpad('fsharp', inp)
@ -57,9 +54,6 @@ def fs(inp):
def cs(snippet):
".cs -- post a C# code snippet to dotnetpad.net and print the results"
if not snippet:
return cs.__doc__
file_template = ('using System; '
'using System.Linq; '
'using System.Collections.Generic; '

View File

@ -7,9 +7,6 @@ from util import hook, http
def down(inp):
'''.down <url> -- checks to see if the site is down'''
if not inp:
return down.__doc__
if 'http://' not in inp:
inp = 'http://' + inp

View File

@ -12,8 +12,6 @@ ed_url = "http://encyclopediadramatica.com/"
def drama(inp):
'''.drama <phrase> -- gets first paragraph of Encyclopedia Dramatica ''' \
'''article on <phrase>'''
if not inp:
return drama.__doc__
j = http.get_json(api_url, search=inp)
if not j[1]:

View File

@ -5,8 +5,6 @@ from pycparser.cdecl import explain_c_declaration
@hook.command
def explain(inp):
".explain <c expression> -- gives an explanation of C expression"
if not inp:
return explain.__doc__
inp = inp.encode('utf8', 'ignore')

View File

@ -6,8 +6,6 @@ from util import hook, http
@hook.command
def calc(inp):
'''.calc <term> -- returns Google Calculator result'''
if not inp:
return calc.__doc__
page = http.get('http://www.google.com/search', q=inp)

View File

@ -12,8 +12,6 @@ def api_get(kind, query):
@hook.command
def gis(inp):
'''.gis <term> -- returns first google image result (safesearch off)'''
if not inp:
return gis.__doc__
parsed = api_get('images', inp)
if not 200 <= parsed['responseStatus'] < 300:
@ -25,12 +23,10 @@ def gis(inp):
['unescapedUrl'] # squares is dumb
@hook.command
@hook.command('g')
@hook.command
def google(inp):
'''.g/.google <query> -- returns first google search result'''
if not inp:
return google.__doc__
parsed = api_get('web', inp)
if not 200 <= parsed['responseStatus'] < 300:

View File

@ -1,7 +1,7 @@
from util import hook
@hook.command
@hook.command(autohelp=False)
def help(inp, bot=None, pm=None):
".help [command] -- gives a list of commands/help for a command"

View File

@ -4,7 +4,7 @@ import re
from util import hook
@hook.command
@hook.command(autohelp=False)
def mem(inp):
".mem -- returns bot's current memory usage -- linux/windows only"

View File

@ -7,9 +7,6 @@ from util import hook, http
def mtg(inp):
".mtg <name> -- gets information about Magic the Gathering card <name>"
if not inp:
return mtg.__doc__
url = 'http://magiccards.info/query?v=card&s=cname'
h = http.get_html(url, q=inp)

View File

@ -6,8 +6,6 @@ from util import hook
@hook.command
def profile(inp):
".profile <username> -- links to <username>'s profile on SA"
if not inp:
return profile.__doc__
return 'http://forums.somethingawful.com/member.php?action=getinfo' + \
'&username=' + '+'.join(inp.split())

View File

@ -7,11 +7,8 @@ re_lineends = re.compile(r'[\r\n]*')
@hook.command
def py(inp):
".py <prog> -- executes python code <prog>"
if not inp:
return py.__doc__
def python(inp):
".python <prog> -- executes python code <prog>"
res = http.get("http://eval.appspot.com/eval", statement=inp).splitlines()

View File

@ -83,5 +83,3 @@ def quote(inp, nick='', chan='', db=None):
selected_quote = quotes[num - 1]
return format_quote(selected_quote, num, n_quotes)
else:
return quote.__doc__

View File

@ -6,9 +6,6 @@ from util import hook, http
def bible(inp):
".bible <passage> -- gets <passage> from the Bible (ESV)"
if not inp:
return bible.__doc__
base_url = ('http://www.esvapi.org/v2/rest/passageQuery?key=IP&'
'output-format=plain-text&include-heading-horizontal-lines&'
'include-headings=false&include-passage-horizontal-lines=false&'
@ -31,9 +28,6 @@ def bible(inp):
def koran(inp): # Koran look-up plugin by Ghetto Wizard
".koran <chapter.verse> -- gets <chapter.verse> from the Koran"
if not inp:
return koran.__doc__
url = 'http://quod.lib.umich.edu/cgi/k/koran/koran-idx?type=simple'
results = http.get_html(url, q1=inp).xpath('//li')

View File

@ -44,8 +44,6 @@ def remember(inp, nick='', chan='', db=None):
@hook.command
def forget(inp, chan='', db=None):
".forget <word> -- forgets the mapping that word had"
if not inp:
return forget.__doc__
db_init(db)
data = get_memory(db, chan, inp)

View File

@ -26,9 +26,6 @@ def seeninput(paraml, input=None, db=None, bot=None):
def seen(inp, nick='', chan='', db=None):
".seen <nick> -- Tell when a nickname was last in active in irc"
if not inp:
return seen.__doc__
if inp.lower() == nick.lower():
return "Have you looked in a mirror lately?"

View File

@ -5,8 +5,15 @@ from util import hook
@hook.sieve
def sieve_suite(bot, input, func, kind, args):
if input.command == 'PRIVMSG' and input.nick.lower()[-3:] == 'bot' \
and args.get('ignorebots', True):
if input.command == 'PRIVMSG'
if input.nick.lower()[-3:] == 'bot' and args.get('ignorebots', True):
return None
elif input.trigger in bot.config.get('disabled_commands', []):
return None
fn = re.match(r'^plugins.(.+).py$', func._filename)
disabled = bot.config.get('disabled_plugins', [])
if fn and fn.group(1).lower() in disabled:
return None
acl = bot.config.get('acls', {}).get(func.__name__)
@ -20,9 +27,4 @@ def sieve_suite(bot, input, func, kind, args):
if input.chan.lower() in denied_channels:
return None
fn = re.match(r'^plugins.(.+).py$', func._filename)
disabled = bot.config.get('disabled_plugins', {})
if fn and fn.group(1).lower() in disabled:
return None
return input

View File

@ -8,8 +8,6 @@ from util import hook, http
@hook.command
def suggest(inp, inp_unstripped=''):
".suggest [#n] <phrase> -- gets a random/the nth suggested google search"
if not inp:
return suggest.__doc__
inp = inp_unstripped
m = re.match('^#(\d+) (.+)$', inp)

View File

@ -100,9 +100,6 @@ def tag(inp, chan='', db=None):
nick, subject = add.groups()
return add_tag(db, chan, nick, subject)
else:
if not inp:
return tag.__doc__
tags = get_tags_by_nick(db, chan, inp)
if not tags:

View File

@ -47,7 +47,7 @@ def tellinput(paraml, input=None, db=None, bot=None):
input.notice(reply)
@hook.command
@hook.command(autohelp=False)
def showtells(inp, nick='', chan='', notice=None, db=None):
".showtells -- view all pending tell messages (sent in PM)."
@ -75,7 +75,7 @@ def tell(inp, nick='', chan='', db=None):
query = inp.split(' ', 1)
if not inp or len(query) != 2:
if len(query) != 2:
return tell.__doc__
user_to = query[0].lower()

View File

@ -11,9 +11,6 @@ from util import hook, http
def tf(inp):
""".tf/.hats <SteamID> -- Shows items waiting to be received in TF2."""
if not inp:
return tf.__doc__
if inp.isdigit():
link = 'profiles'
else:

View File

@ -31,9 +31,6 @@ def twitter(inp):
"tweet from <user>/gets tweet <id>/gets random tweet with #<hashtag>/"\
"gets replied tweet from @<user>"
if not inp:
return twitter.__doc__
def add_reply(reply_name, reply_id):
if len(history) == history_max_size:
history.pop()

View File

@ -46,7 +46,7 @@ def sieve(func):
return func
def command(arg, **kwargs):
def command(arg=None, **kwargs):
args = {}
def command_wrapper(func):

View File

@ -7,14 +7,9 @@ by Vladi
from util import hook, http
@hook.command('val')
@hook.command('valid')
@hook.command
def validate(inp):
".val/.valid/.validate <url> -- runs url through w3c markup validator"
if not inp:
return validate.__doc__
".validate <url> -- runs url through w3c markup validator"
if not inp.startswith('http://'):
inp = 'http://' + inp

View File

@ -3,7 +3,7 @@
from util import hook, http
@hook.command
@hook.command(autohelp=False)
def weather(inp, nick='', server='', reply=None, db=None):
".weather <location> [dontsave] -- gets weather data from Google"

View File

@ -18,9 +18,6 @@ def wiki(inp):
'''.w/.wiki <phrase> -- gets first sentence of wikipedia ''' \
'''article on <phrase>'''
if not inp:
return wiki.__doc__
x = http.get_xml(search_url, search=inp)
ns = '{http://opensearch.org/searchsuggest2}'

View File

@ -3,15 +3,12 @@ import re
from util import hook, http
@hook.command
@hook.command('wa')
@hook.command
def wolframalpha(inp):
".wa/.wolframalpha <query> -- scrapes Wolfram Alpha's" \
"results for <query>"
if not inp:
return wolframalpha.__doc__
url = "http://www.wolframalpha.com/input/?asynchronous=false"
h = http.get_html(url, i=inp)

View File

@ -61,8 +61,6 @@ def youtube_url(match):
@hook.command
def youtube(inp):
'.youtube <query> -- returns the first YouTube search result for <query>'
if not inp:
return youtube.__doc__
j = http.get_json(search_api_url, q=inp)