Files
comfy-discord-web/commands/help_command.py
Khoa (Revenovich) Tran Gia 1ed3c9ec4b Initial commit — ComfyUI Discord bot + web UI
Full source for the-third-rev: Discord bot (discord.py), FastAPI web UI
(React/TS/Vite/Tailwind), ComfyUI integration, generation history DB,
preset manager, workflow inspector, and all supporting modules.

Excluded from tracking: .env, invite_tokens.json, *.db (SQLite),
current-workflow-changes.json, user_settings/, presets/, logs/,
web-static/ (build output), frontend/node_modules/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 09:55:48 +07:00

135 lines
4.7 KiB
Python

"""
commands/help_command.py
========================
Custom help command for the Discord ComfyUI bot.
Replaces discord.py's default help with a categorised listing that
automatically includes every registered command.
How it works
------------
Each ``@bot.command()`` decorator should carry an ``extras`` dict with a
``"category"`` key:
@bot.command(name="my-command", extras={"category": "Generation"})
async def my_command(ctx):
\"""One-line brief shown in the listing.
Longer description shown in ttr!help my-command.
\"""
The first line of the docstring becomes the brief shown in the main
listing. The full docstring is shown when the user asks for per-command
detail. Commands without a category appear under **Other**.
Usage
-----
ttr!help — list all commands grouped by category
ttr!help <command> — detailed help for a specific command
"""
from __future__ import annotations
from collections import defaultdict
from typing import List, Mapping, Optional
from discord.ext import commands
# Order in which categories appear in the full help listing.
# Any category not listed here appears at the end, sorted alphabetically.
CATEGORY_ORDER = ["Generation", "Workflow", "Upload", "History", "Presets", "Utility"]
def _category_sort_key(name: str) -> tuple:
"""Return a sort key that respects CATEGORY_ORDER, then alphabetical."""
try:
return (CATEGORY_ORDER.index(name), name)
except ValueError:
return (len(CATEGORY_ORDER), name)
class CustomHelpCommand(commands.HelpCommand):
"""
Categorised help command.
Groups commands by the ``"category"`` value in their ``extras`` dict.
Commands that omit this appear under **Other**.
Adding a new command to the help output requires no changes here —
just set ``extras={"category": "..."}`` on the decorator and write a
descriptive docstring.
"""
# ------------------------------------------------------------------
# Main listing — ttr!help
# ------------------------------------------------------------------
async def send_bot_help(
self,
mapping: Mapping[Optional[commands.Cog], List[commands.Command]],
) -> None:
"""Send the full command listing grouped by category."""
# Collect all visible commands across every cog / None bucket
all_commands: List[commands.Command] = []
for cmds in mapping.values():
filtered = await self.filter_commands(cmds)
all_commands.extend(filtered)
# Group by category
categories: dict[str, list[commands.Command]] = defaultdict(list)
for cmd in all_commands:
cat = cmd.extras.get("category", "Other")
categories[cat].append(cmd)
prefix = self.context.prefix
lines: list[str] = [f"**Commands** — prefix: `{prefix}`\n"]
for cat in sorted(categories.keys(), key=_category_sort_key):
cmds = sorted(categories[cat], key=lambda c: c.name)
lines.append(f"**{cat}**")
for cmd in cmds:
aliases = (
f" ({', '.join(cmd.aliases)})" if cmd.aliases else ""
)
brief = cmd.short_doc or "No description."
lines.append(f" `{cmd.name}`{aliases}{brief}")
lines.append("")
lines.append(
f"Use `{prefix}help <command>` for details on a specific command."
)
await self.get_destination().send("\n".join(lines))
# ------------------------------------------------------------------
# Per-command detail — ttr!help <command>
# ------------------------------------------------------------------
async def send_command_help(self, command: commands.Command) -> None:
"""Send detailed help for a single command."""
prefix = self.context.prefix
header = f"`{prefix}{command.name}`"
if command.aliases:
alias_list = ", ".join(f"`{a}`" for a in command.aliases)
header += f" (aliases: {alias_list})"
category = command.extras.get("category", "Other")
lines: list[str] = [header, f"Category: **{category}**", ""]
if command.help:
lines.append(command.help.strip())
else:
lines.append("No description available.")
await self.get_destination().send("\n".join(lines))
# ------------------------------------------------------------------
# Error — unknown command name
# ------------------------------------------------------------------
async def send_error_message(self, error: str) -> None:
"""Forward the error text to the channel."""
await self.get_destination().send(error)