signalbot icon indicating copy to clipboard operation
signalbot copied to clipboard

HelpCommand Class

Open Kariton opened this issue 1 year ago • 1 comments

Hey,

when creating a command class the examples (and the code) define self.describe(). https://github.com/filipre/signalbot/blob/33d0f3106be7f39cee6aff5e3c29a01dc81f90c2/signalbot/command.py#L35-L37 what is the use of this additional infromation?

i was not able do figure out where this is acutally used. after that i created a short command myself which might fit right into the examples itself.

from signalbot import Command, Context


class HelpCommand(Command):
    def prefix(self) -> str:
        return "help"

    def describe(self) -> str:
        return "📚 Display a list of available commands or get detailed help for a command."

    def explain(self) -> str:
        return f"{self.prefix()} [COMMAND] - get help message for defined command"

    async def handle(self, c: Context):
        if c.message.text.startswith(self.prefix()):
            # Start typing indicator
            await c.start_typing()

            command_text = c.message.text[len(self.prefix()):].strip()

            if not command_text:
                # Filter available commands based on user access
                available_commands = []
                for cmd, contacts, groups in c.bot.commands:
                    if c.bot._should_react(c.message, contacts, groups):
                        available_commands.append(cmd)

                help_text = "Available commands:\n\n"
                for cmd in available_commands:
                    cmd_prefix = cmd.prefix() if hasattr(cmd, 'prefix') and cmd.prefix() is not None else 'Unknown command prefix'
                    cmd_description = cmd.describe() if hasattr(cmd, 'describe') and cmd.describe() is not None else 'No description available'
                    help_text += f"{cmd_prefix} - {cmd_description}\n"

                help_text += "\nTo get detailed informaition for a given command use: `help [COMMAND]`"

                # Stop typing indicator
                await c.stop_typing()

                await c.send(help_text)
            else:
                for cmd, _, _ in c.bot.commands:
                    if hasattr(cmd, 'prefix') and cmd.prefix() == command_text:
                        # Build man-like help message
                        description = cmd.describe() if hasattr(cmd, 'describe') else 'No description available'
                        usage = f"\nUsage:\n    {cmd.explain()}" if hasattr(cmd, 'explain') else ''

                        # Build the help message with sections
                        help_sections = [section for section in [description, usage] if section]
                        help_message = '\n'.join(help_sections)
                        
                        # Stop typing indicator
                        await c.stop_typing()
                        
                        await c.send(help_message)
                        break
                else:
                    await c.send("Command not found or no detailed help available.")

            return

another command definition:

class ChatGPTCommand(Command):
    def __init__(self, api_key):
        self.api_key = api_key
        self.ongoing_conversations = []

    def prefix(self) -> str:
        return "gpt"

    def describe(self) -> str:
        return "🤖 Engage in a historical conversation with ChatGPT. [GPT 3.5 turbo]"

    def explain(self) -> str:
        return (
            f"{self.prefix()} [MESSAGE] - send message to ChatGPT.\n"
            f"    {self.prefix()} start - start an ongoing ChatGPT conversation; '{self.prefix()}' prefix is no longer nessesary.\n"
            f"    {self.prefix()} end - end an ongoing ChatGPT conversation; '{self.prefix()}' prefix is nessesary again.\n"
            f"    {self.prefix()} status - check if you're in an ongoing ChatGPT conversation.\n"
            f"    {self.prefix()} history - recive ChatGPT conversation history.\n"
            f"    {self.prefix()} clear - clear the ChatGPT conversation history."
        )
[...]

self.prefix() - well, the command itself / message prefix self.describe()- short describtion of the command self.explain() - instruction / usage

in signal:

me

help

bot

Available commands:

help - 📚 Display a list of available commands or get detailed help for a command. ping - 🏓 Ping Command: Listen for a ping. friday - 🦀 Congratulations sailor, you made it to friday! reply - 💬 Auto-reply to messages containing 'reply'. gpt - 🤖 Engage in a historical conversation with ChatGPT. [GPT 3.5 turbo]

To get detailed informaition for a given command use: help [COMMAND]

me

help help

bot (__ to replace 4 spaces)

📚 Display a list of available commands or get detailed help for a command.

Usage: __help [COMMAND] - get help message for defined command

me (gpt command will be uploaded to github in the future)

help gpt

bot (__ to replace 4 spaces)

🤖 Engage in a historical conversation with ChatGPT. [GPT 3.5 turbo]

Usage: __gpt [MESSAGE] - send message to ChatGPT. __gpt start - start an ongoing ChatGPT conversation; 'gpt' prefix is no longer nessesary. __gpt end - end an ongoing ChatGPT conversation; 'gpt' prefix is nessesary again. __gpt status - check if you're in an ongoing ChatGPT conversation. __gpt history - recive ChatGPT conversation history. __gpt clear - clear the ChatGPT conversation history.

me

help ping

bot

🏓 Ping Command: Listen for a ping.

here my few thoughts: with the modularity and ability to share snippets to represend rather complex bot actions it might be helpul do define some "standard models".

because those commads are so modular an emedded help command might not be feasable. but i think everybody should be advises to include the three functions in their command class (prefix, describe, explain)

i know that the name "prefix" does not fit nicely with stuff like:

if "reply" in c.message.text.lower():

but it works.

class ReplyCommand(Command):
    def prefix(self) -> str:
        return "reply"

    def describe(self) -> str:
        return f"💬 Auto-reply to messages containing '{self.prefix()}'."

    async def handle(self, c: Context):
        if self.prefix() in c.message.text.lower():
            await c.reply(
                "i ain't reading all that. i'm happy for u tho or sorry that happened"
            )

furthermore might a globaly configurable "pre_prefix" be useful. if everybody defines their prefixes alphanumeric a gloabal prefix is added and the command will fit whatever the user wants. /cmd, !cmd, .cmd

renaming prefix to prefix_trigger and an additional cmd_trigger, which does not get the "pre_prefix", might be a good way to handle the different requirements. and then there is the actual @trigger... which i dont use and havent tested yet.

and last but not least: with defined prefix warnings can be returned if two commands share the same.

Kariton avatar Aug 23 '23 05:08 Kariton