feat: paste detailed help via FlaskPaste for !help command

!help <cmd> now pastes the command's docstring and appends the URL.
!help <plugin> pastes detail for all plugin commands.
!help (no args) pastes a full reference grouped by plugin.
Falls back gracefully when flaskpaste is not loaded or paste fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
user
2026-02-23 22:51:55 +01:00
parent c851e82990
commit 69976196cd
2 changed files with 213 additions and 3 deletions

View File

@@ -1,11 +1,36 @@
"""Core plugin: ping, help, version, plugin management."""
import asyncio
import textwrap
from collections import Counter
from derp import __version__
from derp.plugin import command
def _build_cmd_detail(handler, prefix: str) -> str:
"""Extract and format a command's docstring into a detail block."""
doc = textwrap.dedent(handler.callback.__doc__ or "").strip()
if not doc:
return ""
header = f"{prefix}{handler.name}"
if handler.help:
header += f" -- {handler.help}"
return f"{header}\n{doc}"
async def _paste(bot, text: str) -> str | None:
"""Create a paste via FlaskPaste. Returns URL or None."""
fp = bot.registry._modules.get("flaskpaste")
if not fp:
return None
loop = asyncio.get_running_loop()
try:
return await loop.run_in_executor(None, fp.create_paste, bot, text)
except Exception:
return None
@command("ping", help="Check if the bot is alive")
async def cmd_ping(bot, message):
"""Respond with pong."""
@@ -27,7 +52,13 @@ async def cmd_help(bot, message):
handler = bot.registry.commands.get(name)
if handler and bot._plugin_allowed(handler.plugin, channel):
help_text = handler.help or "No help available."
await bot.reply(message, f"{bot.prefix}{name} -- {help_text}")
reply = f"{bot.prefix}{name} -- {help_text}"
detail = _build_cmd_detail(handler, bot.prefix)
if detail:
url = await _paste(bot, detail)
if url:
reply += f" | {url}"
await bot.reply(message, reply)
return
# Check plugin
@@ -41,7 +72,19 @@ async def cmd_help(bot, message):
lines = [f"{name} -- {desc}" if desc else name]
if cmds:
lines.append(f"Commands: {', '.join(bot.prefix + c for c in cmds)}")
await bot.reply(message, " | ".join(lines))
reply = " | ".join(lines)
# Build detail block for all plugin commands
blocks = []
for cmd_name in cmds:
h = bot.registry.commands[cmd_name]
blk = _build_cmd_detail(h, bot.prefix)
if blk:
blocks.append(blk)
if blocks:
url = await _paste(bot, "\n\n".join(blocks))
if url:
reply += f" | {url}"
await bot.reply(message, reply)
return
await bot.reply(message, f"Unknown command or plugin: {name}")
@@ -52,7 +95,37 @@ async def cmd_help(bot, message):
k for k, v in bot.registry.commands.items()
if bot._plugin_allowed(v.plugin, channel)
)
await bot.reply(message, ", ".join(names))
reply = ", ".join(names)
# Build full reference grouped by plugin
plugins: dict[str, list[str]] = {}
for cmd_name in names:
h = bot.registry.commands[cmd_name]
plugins.setdefault(h.plugin, []).append(cmd_name)
blocks = []
for plugin_name in sorted(plugins):
mod = bot.registry._modules.get(plugin_name)
desc = (getattr(mod, "__doc__", "") or "").split("\n")[0].strip() if mod else ""
header = f"[{plugin_name}]"
if desc:
header += f" {desc}"
cmd_lines = []
for cmd_name in plugins[plugin_name]:
h = bot.registry.commands[cmd_name]
detail = _build_cmd_detail(h, bot.prefix)
if detail:
cmd_lines.append(detail)
else:
line = f"{bot.prefix}{cmd_name}"
if h.help:
line += f" -- {h.help}"
cmd_lines.append(line)
blocks.append(header + "\n" + "\n\n".join(cmd_lines))
if blocks:
url = await _paste(bot, "\n\n".join(blocks))
if url:
reply += f" | {url}"
await bot.reply(message, reply)
@command("version", help="Show bot version")