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>
135 lines
4.7 KiB
Python
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)
|