feat: команда /help — справка по всем командам бота
All checks were successful
CI / test (push) Successful in 47s
CI / build-and-package (amd64, linux) (push) Successful in 41s
CI / build-and-package (amd64, windows) (push) Successful in 41s

This commit is contained in:
Hermes
2026-08-16 20:05:11 +05:00
parent 0142686592
commit 91e91ac3ce
2 changed files with 58 additions and 4 deletions

View File

@@ -198,8 +198,11 @@ func (a *App) handleIncoming(inc chat.Incoming) {
// update/status — команды бинаря вне машины состояний задач.
// /status перехватываем только без числового аргумента (/status N — статус задачи).
if cmdName(text) == "/update" || (cmdName(text) == "/status" && !hasArg(text)) {
a.handleUpdateCommand(ctx, uid, text)
// /help — справка по всем командам (тоже команда бинаря, вне задач).
if cmdName(text) == "/help" ||
cmdName(text) == "/update" ||
(cmdName(text) == "/status" && !hasArg(text)) {
a.handleBinaryCommand(ctx, uid, text)
return
}
@@ -342,16 +345,37 @@ func (a *App) autoCheckOnce(ctx context.Context) {
a.send(ctx, uid, msg)
}
// handleUpdateCommand — маршрутизация /update и /status.
func (a *App) handleUpdateCommand(ctx context.Context, uid chat.UserID, text string) {
// handleBinaryCommand — маршрутизация бинарных команд: /help, /update, /status.
func (a *App) handleBinaryCommand(ctx context.Context, uid chat.UserID, text string) {
switch cmdName(text) {
case "/status":
a.handleStatusCmd(ctx, uid)
case "/update":
a.handleUpdateCmd(ctx, uid)
case "/help":
a.handleHelpCmd(ctx, uid)
}
}
// helpText — текст команды /help.
const helpText = "⚡ **Ratatoskr** — помогу продумать задачу и запущу агента.\n\n" +
"**Команды:**\n" +
"`/start` — новая задача\n" +
"`/cancel` — отменить текущую\n" +
"`/skip` — хватит вопросов, предложить черновик\n" +
"`/retry N` — перезапустить задачу `N`\n" +
"`/status N` — статус задачи `N`\n" +
"`/continue N` — продолжить задачу `N`\n" +
"`/status` — статус бинаря и обновления\n" +
"`/update` — применить обновление\n" +
"`/help` — эта справка\n\n" +
"Просто опишите задачу — я помогу её продумать."
// handleHelpCmd отвечает списком команд.
func (a *App) handleHelpCmd(ctx context.Context, uid chat.UserID) {
a.send(ctx, uid, helpText)
}
// handleStatusCmd отвечает текущей версией и результатом последней проверки.
func (a *App) handleStatusCmd(ctx context.Context, uid chat.UserID) {
if a.Config.Update.Enabled && a.Updater != nil {

View File

@@ -163,3 +163,33 @@ func TestNew_UpdateWiring(t *testing.T) {
t.Errorf("Token = %q, want embedded-update-token (вшитый приоритетнее)", a2.Updater.Token)
}
}
// TestHelp_IsBinaryCommand проверяет, что /help распознаётся cmdName и, значит,
// в handleIncoming попадёт в бинарную ветку (не в машину состояний задач).
// Косвенно: /help не должен иметь аргумента-номера задачи (как /status N).
func TestHelp_IsBinaryCommand(t *testing.T) {
if got := cmdName("/help"); got != "/help" {
t.Errorf("cmdName(/help) = %q, want /help", got)
}
if hasArg("/help") {
t.Errorf("/help не должен требовать аргумент")
}
if got := cmdName("/help foo"); got != "/help" {
t.Errorf("cmdName(/help foo) = %q, want /help", got)
}
}
// TestHelpText_ContainsCommands проверяет, что справка упоминает все команды.
func TestHelpText_ContainsCommands(t *testing.T) {
for _, cmd := range []string{
"/start", "/cancel", "/skip", "/retry", "/status", "/continue",
"/update", "/help",
} {
if !strings.Contains(helpText, cmd) {
t.Errorf("helpText не упоминает команду %s", cmd)
}
}
if !strings.Contains(helpText, "Ratatoskr") {
t.Errorf("helpText не содержит имени бота")
}
}