diff --git a/sopel/bot.py b/sopel/bot.py index b0d13331cc..016488f802 100644 --- a/sopel/bot.py +++ b/sopel/bot.py @@ -62,6 +62,8 @@ def __init__(self, config, daemon=False): key in version *3.2* onward. Prior to *3.2*, the name of the function as declared in the source code was used. """ + self.commands = collections.defaultdict(list) + """A mapping of module names to a list of commands in it.""" self.stats = {} """ A dictionary which maps a tuple of a function name and where it was @@ -173,6 +175,12 @@ def register(self, callables, jobs, shutdowns): for callbl in callables: for rule in callbl.rule: self._callables[callbl.priority][rule].append(callbl) + if hasattr(callbl, 'commands'): + module_name = callbl.__module__.rsplit('.', 1)[-1] + # TODO doc and make decorator for this. Not sure if this is how + # it should work yet, so not making it public for 6.0. + category = getattr(callbl, 'category', module_name) + self.commands[category].append(callbl.commands[0]) for command, docs in callbl._docs.items(): self.doc[command] = docs for func in jobs: diff --git a/sopel/modules/help.py b/sopel/modules/help.py index 05acc8d5c8..ff3ac31f64 100644 --- a/sopel/modules/help.py +++ b/sopel/modules/help.py @@ -9,19 +9,19 @@ """ from __future__ import unicode_literals +import textwrap + +from sopel.formatting import bold from sopel.module import commands, rule, example, priority -from sopel.tools import iterkeys @rule('$nick' '(?i)(help|doc) +([A-Za-z]+)(?:\?+)?$') @example('.help tell') -@commands('help') +@commands('help', 'commands') @priority('low') def help(bot, trigger): """Shows a command's documentation, and possibly an example.""" - if not trigger.group(2): - bot.reply('Say .help (for example .help c) to get help for a command, or .commands for a list of commands.') - else: + if trigger.group(2): name = trigger.group(2) name = name.lower() @@ -40,18 +40,24 @@ def help(bot, trigger): msgfun(line) if bot.doc[name][1]: msgfun('e.g. ' + bot.doc[name][1]) + else: + if not trigger.is_privmsg: + bot.reply("I'm sending you a list of my commands in a private message!") + bot.say( + 'You can see more info about any of these commands by doing .help ' + ' (e.g. .help time)', + trigger.nick + ) - -@commands('commands') -@priority('low') -def commands(bot, trigger): - """Return a list of bot's commands""" - names = ', '.join(sorted(iterkeys(bot.doc))) - if not trigger.is_privmsg: - bot.reply("I am sending you a private message of all my commands!") - bot.msg(trigger.nick, 'Commands I recognise: ' + names + '.', max_messages=10) - bot.msg(trigger.nick, ("For help, do '%s: help example' where example is the " + - "name of the command you want help for.") % bot.nick) + name_length = max(6, max(len(k) for k in bot.commands.keys())) + for category, cmds in bot.commands.items(): + category = category.upper().ljust(name_length) + cmds = ' '.join(cmds) + msg = bold(category) + ' ' + cmds + indent = ' ' * (name_length + 2) + msg = textwrap.wrap(msg, subsequent_indent=indent) + for line in msg: + bot.say(line, trigger.nick) @rule('$nick' r'(?i)help(?:[?!]+)?$')