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>
This commit is contained in:
134
commands/help_command.py
Normal file
134
commands/help_command.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user