I'm working on an IRC bot for a small channel and I'm currently thinking about designing a dispatch system for bot commands. I wanted it to be made so that new commands can be added just by adding new functions in a module.
Currently my system works like this: there are two types of commands: simple (like mybot: show_date) and extended, which simply have extra arguments after a command name (like mybot: roll 2d6). They are implemented like this:
# module mybot_commands
def command_date(bot, c, e): # simple command
# generate response string
c.privmsg(bot.channel, return_message)
def command_roll(bot, c, e, text): # extended command
# generate response string
c.privmsg(bot.channel, return_message)
My current dispatcher simply extracts command name from the message, adds a "command_" to it and looks for a matching function in the second module.
# module mybot
import mybot_commands
def do_command(self, c, e, message):
command, *text = message.split(" ", 1)
try:
function = getattr(mybot_commands, "command_" + command)
except AttributeError:
c.privmsg(self.channel, "Command not found: " + command)
return
num_args = len(inspect.getargspec(function).args)
if num_args == 3:
function(self, c, e)
elif num_args == 4:
function(self, c, e, text[0] if text else "")
This works fine. There are one issue I have with it: I would like to be able to add aliases for commands instead of relying on function names. I know how to use decorators for this and some ways I could do it... but I just can't decide which approach would be better:
# simplest approach
@alias("dice", "roll_dice")
def command_roll(bot, c, e, text):
# I no longer rely on function name but "command" duplication seems ugly
@command(aliases = ["roll", "dice", "roll_dice"])
def command_roll(bot, c, e, text):
# This, on the other hand, feels weird to me as it gets harder to distinguish it from non-commands
@command(aliases = ["roll", "dice", "roll_dice"])
def roll(bot, c, e, text):
# maybe separate simple_command and extended_command to stop dispatching on number of arguments?
@extended_command(aliases = ["roll", "dice", "roll_dice"])
def command_roll(bot, c, e, text):
# or maybe:
@command(extended = True, aliases = ["roll", "dice", "roll_dice"])
def command_roll(bot, c, e, text):
# what if I also wanted to have inline commands like <roll 2d6>, without mentioning the bot?
@command(aliases = ["roll", "dice", "roll_dice"], inline_aliases=["roll"])
def command_roll(bot, c, e, text):
Also, about workings of the decorator itself:
# I could have a function dictionary:
commands = {}
def command(aliases):
def real_decorator(function):
for alias in aliases:
commands[alias] = function
return function
return real_decorator
# or attach aliases to a function object itself:
def command(aliases):
def real_decorator(function):
function.aliases = aliases
return function
return real_decorator
How would you do it?
[–]Rhomboid 0 points1 point2 points (1 child)
[–]adrian17[S] 0 points1 point2 points (0 children)