def init(bot): global mappings # oops! Bad things are going to happen # you should very much not do the following. This relies on knowing how the internals of dispatcher setup work! for command, output in bot.getOption("commands", module="simplecommands"): mappings.append( Mapping(command=command, function=partial(echothis, output))) return True
def funicode(event, bot): """ unicode [character(s)/description/hex]. Displays information about provided characters (limit of 3,) or does a search on the character description or provides information on the character indexed by the given hexidecimal.""" arg = event.argument if not arg: return bot.say(functionHelp(funicode)) if REGHEX.match(arg): i = int(arg, 16) u = unichr(i) return bot.say(RPLFORMAT % (i, _getname(u), u)) elif len(arg) <= 3: output = [] for u in arg: output.append(RPLFORMAT % (ord(u), _getname(u), u)) return bot.say(", ".join(output)) else: output = [] for i, entry in CHARACTER_DESC: if len(output) > 8: break # could be lowered to improve performance if arg.lower() in entry.lower(): output.append(RPLFORMAT % (i, entry, unichr(i))) if output: return bot.say(", ".join(output)) else: return bot.say("No characters found.") mappings = (Mapping(command=("u", "unicode"), function=funicode),)
match = None for command, output in commands: if isinstance(command, list) and arg1 in command: match = (command, output) if not match: return bot.say('(%s) is not a known simplecommand.' % arg1) bot.say('Simplecommand (%s): %s' % (', '.join(match[0]), match[1])) def echo_this(text, event, bot): bot.say(text) # for abuse in init: mappings = [ Mapping(command=("simplecommands", "simplecommand", "sc"), function=simplecommands) ] def init(bot): global mappings # oops! Bad things are going to happen # you should very much not do the following. This relies on knowing how the internals of dispatcher setup work! for command, output in bot.getOption("commands", module="pbm_simplecommands"): mappings.append( Mapping(command=command, function=partial(echo_this, output), hidden=True)) return True
if today: day = "Today" else: day = data['date']['weekday'] days.append( FORECAST_DAY % (day, data['conditions'], data['low']['fahrenheit'], data['high']['fahrenheit'], data['low']['celsius'], data['high']['celsius'], data['pop'], data['avehumidity'])) today = False return bot.say(FORECAST_RPL % (_build_locname(loc), ", ".join(days))) def init(bot): global WUAPI_MODULE # oh nooooooooooooooooo global LOC_MODULE # oh nooooooooooooooooo WUAPI_MODULE = bot.getModule("wuapi") LOC_MODULE = bot.getModule("location") return True #mappings to methods mappings = ( Mapping(command="weather", function=weather), Mapping(command="forecast", function=forecast), )
o.addheaders = [('User-agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36')] f = o.open(GDQ_URL) # http://stackoverflow.com/a/9920703 page = parse(f) rows = page.xpath("body/table/tbody")[0].findall("tr") data = [] for row in rows: data.append([c.text for c in row.getchildren()]) # find current upcoming = None # try searching for incorrect name in timetable because bads... for igametitle in (ngame, ngame.replace(":", ""), ngame.split(":")[0], ngame.split(u"\u2013")[0].strip(), ngame.replace("two", "2"), ngame.replace(":", "").replace("two", "2")): upcoming, eta = _searchGame(data, igametitle) if upcoming: break else: if ngame[:4] == "the ": upcoming, eta = _searchGame(data, ngame[4:]) if eta: curr = timegm(gmtime()) neta = timegm(strptime(eta, "%H:%M:%S")) - timegm(strptime("0:00:00", "%H:%M:%S")) eta = "%s/%s" % (eta.lstrip("0:")[:-3], (neta - (curr-gstart))/60) else: eta = "?" if not upcoming: upcoming = ["Don't know"] bot.say(RPL % (game, eta, "http://www.twitch.tv/gamesdonequick/popout", "https://gamesdonequick.com/schedule"), strins=", ".join(upcoming)) mappings = (Mapping(command=("gdq", "agdq"), function=agdq),)
#~ print podnames, results if not results: if input: return bot.say("WolframAlpha doesn't know [%s]." % input) else: return bot.say( "WolframAlpha doesn't know and doesn't understand your input." ) for entry in results: if isinstance(entry, list): entry[0] = POD_PRIORITY.get(entry[0][0], entry[0][1]) results.sort() msg = "[%s] {0}" % (input, ) #~ print msg, results bot.say(msg, strins=[x[1] for x in results], fcfs=True, joinsep="\x02,\x02 ") else: bot.say("Dunno.") def init(bot): global API_KEY # oh nooooooooooooooooo API_KEY = bot.getOption("API_KEY", module="calc") return True #mappings to methods mappings = (Mapping(command=("calc", "c"), function=calc), )
pass else: # have desired nick, delete check timers try: Timers.deltimer("NICKTOOLS_%s" % bot.network) except TimerNotFound: pass # TODO: like below, when state can track +r, this should be checked before attempting identify identify(bot, snick) def init(bot): # we could dynamically add the nickChanged mapping event here to mappings depending on setting # sort of like what simplecommands does # TODO: When state can track usermode (+r in particular), this should be used to know whether to reident or not on nick reclaim #~ if not bot.getOption("enablestate"): #~ raise ConfigException('nicktools module requires "enablestate" option') return True def unload(): Timers._delPrefix( "NICKTOOLS_" ) # use non blocking call version since unload is called in the reactor mappings = ( Mapping(types=["preJoin"], function=preJoin), Mapping(types=["signedOn"], function=nickCheckAndRecover), Mapping(types=["nickChanged"], function=nickChanged), )
corechange = True check_output([gitpath, "merge", "origin/master"]) if corechange: print "RESTARTING BOT" #restart bot blockingCallFromThread(reactor, Settings.shutdown, True) elif modchange: #reload if bot.isModuleAvailable("pbm_reload"): bot.getModule("pbm_reload").admin_reload_bot(event, bot) else: bot.say( "Module(s) updated but can't reload. reload module not available." ) else: bot.say("Already up-to date.") def local_update(event, bot): if not bot.getOption('debug'): return bot.say('Debug must be enabled for localupdate.') print "RESTARTING BOT" bot.say('Restarting...') blockingCallFromThread(reactor, Settings.shutdown, True) mappings = (Mapping(command="update", function=update, admin=True), Mapping(command="localupdate", function=local_update, admin=True))
print "CHANGES:", changes corechange = False modchange = False for line in changes.splitlines(): if line.lstrip("M\t").startswith("modules/") or line.lstrip( "A\t").startswith("modules/"): modchange = True elif line.endswith(".py"): corechange = True check_output([gitpath, "merge", "origin/master"]) if corechange: print "RESTARTING BOT" #restart bot blockingCallFromThread(reactor, Settings.shutdown, True) elif modchange: #reload if bot.isModuleAvailable("core"): bot.getModule("core").reloadbot(event, bot) else: bot.say( "Module(s) updated but can't reload. core module not available." ) else: bot.say("Already up-to date.") #mappings to methods mappings = (Mapping(command="update", function=update), )
def init(bot): global USERS_MODULE # oh nooooooooooooooooo bot.dbCheckCreateTable("alias", '''CREATE TABLE alias( alias TEXT PRIMARY KEY COLLATE NOCASE, user TEXT COLLATE NOCASE );''') bot.dbCheckCreateTable("aliasgrp", '''CREATE TABLE aliasgrp( grp TEXT COLLATE NOCASE, user TEXT COLLATE NOCASE );''') #index alias column #Unique does this for us #but should index nick column so can do fast "get all alias for nick" queries... # Consider going back to using integer IDs for this? I dunno if indexing integers is faster than TEXT in SQLite # if not exists: bot.dbCheckCreateTable("alias_user_idx", '''CREATE INDEX alias_user_idx ON alias(user);''') bot.dbCheckCreateTable("alias_group_idx", '''CREATE INDEX alias_group_idx ON aliasgrp(grp,user);''') # cache user module. # NOTE: you should only call getModule in init() if you have preloaded it first using "REQUIRES" USERS_MODULE = bot.getModule("users") # add backreference to alias module for fast lookup # this probably shouldn't normally be done. USERS_MODULE.ALIAS_MODULE = modules[__name__] return True #mappings to methods mappings = (Mapping(command="alias", function=alias),)
command, args = commandSplit(event.argument) if command == "show": bot.say("Timers:") for timer in Timers.getTimers().itervalues(): bot.say(" - %s: reps = %s, delay = %s, f = %s" % (timer.name, timer.reps, timer.interval, timer.f)) elif command == "add": args = argumentSplit(args, 4) #add timername delay reps msg if not args: bot.say( "Not enough arguments. Need: timername delay reps message (reps <= 0 means forever)" ) return msg = Timers.addtimer(args[0], float(args[1]), timercallback, reps=int(args[2]), msg=args[3], bot=bot, channel=event.target)[1] bot.say("%s (%s)" % (msg, args[0])) elif command == "stop": bot.say(Timers.deltimer(args)[1]) #mappings to methods mappings = (Mapping(command="timers", function=timers), )
TABLEUPDATES.setdefault(network, []).append(func) else: EXTERNALUPDATES.setdefault(network, []).append(func) #init should always be here to setup needed DB tables or objects or whatever def init(bot): """Do startup module things. This just checks if table exists. If not, creates it.""" bot.dbCheckCreateTable( 'user', '''CREATE TABLE user( user TEXT PRIMARY KEY COLLATE NOCASE, host TEXT, lastseen INTEGER, seenwhere TEXT, lastmsg TEXT );''') #should probably index nick column #unique does this for us #but should probably index lastseen so can ez-tells: # if not exists: bot.dbCheckCreateTable( "user_lastseen_idx", '''CREATE INDEX user_lastseen_idx ON user(lastseen);''') return True #mappings to methods mappings = (Mapping(types=["privmsged"], function=user_update), Mapping(command="seen", function=user_seen))
#sendwaiteventexample.py #example on how to send and then wait on events from util import Mapping, TimeoutException from twisted.internet import reactor from twisted.internet.threads import blockingCallFromThread def waitexample(event, bot): count = 0 try: for event in bot.send_and_wait("noticed", f=bot.notice, fargs=(event.nick, "sending...")): bot.say("Recieved: %s" % event.msg) count += 1 if count > 1: raise Exception() except TimeoutException: print "TIMEOUT!" print "bailed generator" def printwaits(s): print s.dispatcher.waitmap # never ever do something like this please, please. This is debugging example. def waitlist(event, bot): blockingCallFromThread(reactor, printwaits, bot._settings) bot.say("done.") #mappings to methods mappings = (Mapping(types=["privmsged"], command="waitexample", function=waitexample), Mapping(types=["privmsged"], command="waitlist", function=waitlist))
iproxy = INDEX_PROXIES.get(network) if iproxy: iproxy.rename(old, new) def init(bot): global INDEX_PROXIES global USERS_MODULE USERS_MODULE = bot.getModule("pbm_users") if bot.network not in INDEX_PROXIES: proxy = IndexProxy( bot.network, bot.getOption("indexdir", module="pbm_logindexsearch"), bot.getOption("commandprefix")) INDEX_PROXIES[bot.network] = proxy proxy.start() USERS_MODULE.REGISTER_UPDATE(bot.network, _user_rename, external=True) else: print "WARNING: Already have log proxy for (%s) network." % bot.network return True def unload(): for lproc in INDEX_PROXIES.itervalues(): lproc.stop() mappings = (Mapping(types=["privmsged"], function=logmsg), Mapping(command="log", function=logsearch), Mapping(command="logstats", function=logstats))
""" #do some things command, args = commandSplit(event.argument) if command == "something": if args: bot.say("%s %s" % (command, args)) else: bot.say("%s" % command) elif command == "dothing": if args: bot.say("%s %s" % (args, command)) else: bot.say("%s" % command) else: bot.say(functionHelp(samplecommand)) #init should always be here to setup needed DB tables or objects or whatever def init(bot): """Do startup module things. This sample just checks if table exists. If not, creates it.""" bot.dbCheckCreateTable( 'sample_table', '''create table sample_table( columnA, columnB );''') return True #mappings to methods mappings = ( Mapping(types=["privmsged"], regex=recompile(r"\|.*"), function=repeater), Mapping(command="samplecommand", function=samplecommand), Mapping(command="print", function=printer), )
return bot.say(". ".join(output)) def synonym(event, bot): """ synonym [query]. Returns synonyms for query.""" if not event.argument: return bot.say(functionHelp(synonym)) syns = WORD_API.word_synonyms(event.argument) if syns is None: return spelling(event, bot, skipSearch=True) elif not syns: return bot.say("No synonyms found for \x02%s\x02" % event.argument) else: return bot.say("Synonyms for (%s): %s" % (event.argument, ", ".join(syns))) def init(bot): global WORD_API # oh nooooooooooooooooo WORD_API = bot.getModule("pbm_wordsapi") return True mappings = ( Mapping(command=("dict", "d", "dictionary"), function=dictionary), Mapping(command=("spell", "sp", "spelling"), function=spelling), Mapping(command=("syn", "synonym", "thesaurus"), function=synonym), )
bot.say("Successfully sent action to (%s)." % chan_or_user) def admin_rage(event, bot): msg = event.argument if event.isPM(): chan_or_user, msg = commandSplit(msg) if not chan_or_user: return bot.say(".%s #channel FURIOUS_MESSAGE" % event.command) else: chan_or_user = event.target if not msg: msg = 'A' * randint(200, 400) if send_msg_and_wait(bot, chan_or_user, AAA(msg)) and event.isPM(): bot.say("Successfully sent FURIOUS_MESSAGE to (%s)." % chan_or_user) mappings = (Mapping(command="join", function=admin_join, admin=True), Mapping(command="part", function=admin_part, admin=True), Mapping(command="kick", function=admin_kick, admin=True), Mapping(command=("msg", "message", "say", "pm"), function=admin_msg, admin=True), Mapping(command=("action", "me"), function=admin_action, admin=True), Mapping(command=("rage", "fury"), function=admin_rage, admin=True))
def _user_rename(old, new): return (('''UPDATE tell SET user=? WHERE user=?;''', (new, old)),) def init(bot): global USERS_MODULE # oh nooooooooooooooooo bot.dbCheckCreateTable("tell", '''CREATE TABLE tell( id INTEGER PRIMARY KEY, delivered INTEGER DEFAULT 0, user TEXT COLLATE NOCASE, telltime INTEGER, origintime INTEGER, toldtime INTEGER, remind INTEGER DEFAULT 0, source TEXT, msg TEXT );''') # I am bad at indexes. bot.dbCheckCreateTable("tell_deliv_idx", '''CREATE INDEX tell_deliv_idx ON tell(user, delivered, telltime);''') # cache user module. # NOTE: you should only call getModule in init() if you have preloaded it first using "REQUIRES" USERS_MODULE = bot.getModule("pbm_users") # Modules storing "users" in their own tables should register to be notified when a username is changed (by the alias module) USERS_MODULE.REGISTER_UPDATE(bot.network, _user_rename) return True mappings = (Mapping(types=["privmsged"], function=deliver_tell), Mapping(command=("tell", "ask"), function=tell), Mapping(command="remind", function=remind),)
return \ ("""INSERT OR REPLACE INTO location (id, name, lat, lon) SELECT ?, name, lat, lon FROM location WHERE id == ?""", (new, old)),\ ("""DELETE FROM location WHERE id == ?""", (old,)) def init(bot): global USERS_MODULE # oh nooooooooooooooooo global GAPI_MODULE # oh nooooooooooooooooo #id is id from user table bot.dbCheckCreateTable( "location", '''CREATE TABLE location( id TEXT PRIMARY KEY COLLATE NOCASE, name TEXT, lat REAL, lon REAL );''') # cache user module. # NOTE: you should only call getModule in init() if you have preloaded it first using "REQUIRES" USERS_MODULE = bot.getModule("pbm_users") GAPI_MODULE = bot.getModule("pbm_googleapi") # Modules storing "users" in their own tables should register to be notified when a username is changed (by the alias module) USERS_MODULE.REGISTER_UPDATE(bot.network, _user_rename) return True mappings = (Mapping(command="location", function=location), )
#run db query from util import Mapping def dbquery(event, bot): if bot.isadmin(): query = event.argument bot.say("Running: %s" % query) try: result = bot.dbQuery(query) except Exception as e: return bot.say("Error with query: %s" % e) if not result: return bot.say("No error, but nothing to display.") print "GOOD" #good for row in result: nrow = [] for key in row.keys(): nrow.append((key, row[key])) bot.say(repr(nrow)) else: bot.say("uwish.") mappings = (Mapping(command="dbquery", function=dbquery), )
# reload module from util import Mapping ### Modules should not import this! Unless they have a very good reason to. from util.settings import Settings ### This is only something that modules that know what they are doing should do: from twisted.internet import reactor from twisted.internet.threads import blockingCallFromThread ### def _reallyReload(): Settings.reloadStage1() Settings.reloadStage2() def admin_reload_bot(event, bot): #reload settings, important to do only from within reactor #also refresh dispatchers blockingCallFromThread(reactor, _reallyReload) # may never get sent if bot is disconnecting from this server after reload return bot.say("Done.") mappings = (Mapping(command="reload", function=admin_reload_bot, admin=True), )
channel=channel, module=module) except Exception as e: return bot.say("Error: %s" % e) blockingCallFromThread(reactor, Settings.saveOptions) bot.say(msg % (opt, servchan, dumps(value))) #get value else: try: value = dumps( bot.getOption(opt, server=server, channel=channel, module=module)) except Exception as e: return bot.say("Error: %s" % e) if module and opt in modopts: t, desc, default = modopts[opt] bot.say( "Setting for %s(%s) is %s. %s Type: %s, Default: %s" % (opt, servchan, value, desc, t.__name__, dumps(default))) else: bot.say("Setting for %s(%s) is %s" % (opt, servchan, value)) else: return bot.say(functionHelp(config)) #mappings to methods mappings = (Mapping(command="config", function=config, admin=True), )
if results: entries = [] # TODO: the following should probably be handled in the smart unicode cropping thing # or in a smarter generic result splitter thing. # TODO: (also) this is basically double iterating over the results. Griff fix later please, thanks. for item in results: if entries: entries.append(RESULT_IMG2 % (item[0], item[1])) else: entries.append(RESULT_IMG % (item[0], item[1])) if len(entries) < NUM_IMGS: entries = entries+[""]*(NUM_IMGS-len(l)) if spelling: bot.say(RESULTS_SPELL_IMG % spelling, fcfs=True, strins=entries) else: bot.say(RESULTS_IMG, fcfs=True, strins=entries) else: if spelling: bot.say("(SP: %s) No results found." % spelling) else: bot.say("No results found.") def init(bot): global GAPI_MODULE # oh nooooooooooooooooo GAPI_MODULE = bot.getModule("pbm_googleapi") return True #mappings to methods mappings = (Mapping(command=("google", "g"), function=google),Mapping(command="gis", function=google_image),)
errors = [] attempt = 0 p = None try: p = page( result[0] ) # use preload=True when it's fixed: https://github.com/goldsmith/Wikipedia/issues/78 except DisambiguationError as e: errors.append("Random disambig page: ") while attempt < 3: try: p = page(choice(e.options)) except DisambiguationError: pass attempt += 1 if not p: return bot.say( "Gave up looking for disambiguous entry from disambiguous page.") if result[1]: errors.append("(SP: %s?) " % result[1]) content = p.content[:800].replace("\n", " ").replace("====", "").replace( "===", "").replace("==", "") bot.say(RESULT_RPL % ("".join(errors), p.url), strins=[p.title, content], fcfs=True) mappings = (Mapping(command=("wiki", "w", "wikipedia"), function=wiki), )
bot.say(rpl, fcfs=False, strins=titles) else: bot.say("(%s) No results found." % numresults) def init(bot): global GAPI_MODULE # oh nooooooooooooooooo bot.dbCheckCreateTable( "youtubeseen", '''CREATE TABLE youtubeseen( source TEXT PRIMARY KEY COLLATE NOCASE, id TEXT );''') GAPI_MODULE = bot.getModule("googleapi") return True #mappings to methods mappings = ( Mapping(command=("youtube", "yt"), function=youtube), Mapping(types=["privmsged"], regex=recompile( r"\bhttps?\://(?:www\.)?youtu\.be\/[a-zA-Z0-9_-]{11}.*\b"), function=seen_video), Mapping( types=["privmsged"], regex=recompile( r"\bhttps?\://(?:www\.)?youtube\.com\/.*v\=[a-zA-Z0-9_-]{11}.*\b"), function=seen_video), )
print_exc() def init(bot): global CHAT_THREADS # oh nooooooooooooooooo if bot.getOption("enablestate"): if bot.network not in CHAT_THREADS: CHAT_THREADS[bot.network] = SteamChat( bot.container, bot.getOption("commandprefix"), bot.getOption( "allowedModules", module="pbm_steamchat")) # bit silly, but whatever else: print "WARNING: Already have thread for (%s) network." % bot.network else: raise ConfigException('steamchat module requires "enablestate" option') return True def unload(): for cthread in CHAT_THREADS.itervalues(): cthread.stop() mappings = ( Mapping(types=["privmsged"], function=relaymsg), Mapping(types=("kickedFrom", "left"), function=doleft), Mapping(command=("steamchat", "sc"), function=steamchatcmd), Mapping(["sendmsg"], function=processBotSendmsg), )
bot.say("Users on channel (%s): %s" % (args, ", ".join(chan.users))) else: bot.say("lol dunno channel %s" % args) elif command == "bans": if not args: for chan in bot.state.channels.itervalues(): bot.say("Bans on channel (%s): %s" % (chan.name, ", ".join(chan.banlist.iterkeys()))) else: chan = bot.state.channels.get(args, None) if chan: bot.say("Bans on channel (%s): %s" % (args, ", ".join(chan.banlist.iterkeys()))) else: bot.say("lol dunno channel %s" % args) elif command == "network": bot.say("Known users on network: %s" % ", ".join(bot.state.users.keys())) elif command == "channels": bot.say("On channels: %s" % ", ".join(bot.state.channels.keys())) else: bot.say("state: channel, network, channels") #mappings to methods mappings = (Mapping(command="state", function=statecommand), )
butt = 'Butt' actual_word = actual_word[:l] + butt + actual_word[l + r:] if len(hyphenated_parts) > 5 and random.randint(0, (4 - butt_pass)) == 1: butt_pass += 1 actual_word = _butt_word(actual_word, butt_pass=butt_pass) return lp + actual_word + rp def _weighted_butt_words(sortedlist): weight = len(sortedlist) weighted_butt_words = [] for word in sortedlist: weighted_butt_words.extend([word] * (weight**2)) weight -= 1 return weighted_butt_words def init(bot): bot.dbCheckCreateTable( "butts", '''CREATE TABLE butts( id INTEGER PRIMARY KEY, butt TEXT );''') return True mappings = (Mapping(command="butt", function=butt), Mapping(command="butts", function=butts), Mapping(types=["privmsged"], function=rand_butt))
for func, command in cmds: if arg: h = functionHelp(func, arg) if h: bot.say(h) else: bot.say("No help for (%s) available." % cmd) else: h = functionHelp(func) if h: if isIterable(command): bot.say("%s Aliases: %s" % (h, ", ".join(command))) else: bot.say(h) else: bot.say("No help for (%s) available." % cmd) else: bot.say("Command %s not found." % cmd) else: cmds = bot._settings.dispatcher.getCommands() try: cmds.remove("eval") except ValueError: pass cmds.sort() bot.say(" ".join(cmds)) mappings = (Mapping(command="reload", function=reloadbot), Mapping(command="help", function=help))
def fdecode(event, bot): """ decode encoding content. content will be decoded according to provided encoding. Will be displayed using python's repr if not unicode. Available encodings: https://docs.python.org/2/library/codecs.html#standard-encodings . Append |repr to the method if you are supplying escaped ascii. """ method, content = argumentSplit(event.argument, 2) if not (method and content): return bot.say(functionHelp(fdecode)) # some crazy voodoo try: try: if method.endswith("|repr"): o = content.decode("string_escape").decode(method[:-5]) else: o = content.decode(method) if isinstance(o, unicode): bot.say(o) else: bot.say(repr(o)) except (UnicodeEncodeError, UnicodeDecodeError): if method.endswith("|repr"): o = content.encode("utf-8").decode("string_escape").decode(method[:-5]) else: o = content.encode("utf-8").decode(method) if isinstance(o, unicode): bot.say(o) else: bot.say(repr(o)) except LookupError: bot.say("Unknown encoding. Available encodings: https://docs.python.org/2/library/codecs.html#standard-encodings") except (UnicodeEncodeError, UnicodeDecodeError): bot.say("Can't decode.") #mappings to methods mappings = (Mapping(command="hash", function=hash), Mapping(command="md5", function=md5), Mapping(command="rot13", function=rot13), Mapping(command="crc", function=crc), Mapping(command="unquote", function=funquote), Mapping(command="quote", function=fquote), Mapping(command="encode", function=fencode), Mapping(command="decode", function=fdecode),)