Files
avandClaude Opus 4.8 f75d6b1f91 tgbot: выбор кандидата метабазы inline-кнопками в карточке ревью
Когда для распознавания сохранены кандидаты метабазы, карточка подтверждения
бота показывает кнопку «🗂 База (N)». По ней двухшагово (как delete/dismiss)
разворачивается список кандидатов inline-кнопками; выбор пиннит источник через
worker.ChooseCandidate (ручной матч, без авто-раскладки) и обновляет карточку.
Веб остаётся точкой точных правок (ручной ввод id/URL, «без базы»).

Безопасность границы: id кандидата из callback_data валидируется как ULID
(ident.Parse) до доменного вызова, как в вебе. Текст inline-кнопок Telegram не
парсится как HTML — название кандидата в подписи не экранируется.

SDD: change telegram-vybor-nahodok — дельта notifications (ADDED «Выбор
кандидата метабазы из карточки подтверждения бота») + review (MODIFIED
«Разделение труда транспортов»: быстрый выбор кандидата — Telegram-действие).
Влито в specs, change заархивирован. Миграций БД нет (кандидаты уже в БД).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 16:02:47 +03:00

2.3 KiB
Raw Permalink Blame History

1. Интерфейс транспорта

  • 1.1 internal/tgbot/bot.go: добавить в интерфейс Reviewer метод ChooseCandidate(ctx context.Context, id, candidateID string) error.
  • 1.2 internal/tgbot/bot_test.go: дополнить fakeReviewer методом ChooseCandidate (запоминает выбор для проверки).

2. Клавиатуры (render.go)

  • 2.1 reviewKeyboard: когда len(rd.Candidates) > 0, добавить кнопку «🗂 База (N)» с callback sources:<id>.
  • 2.2 Новая candidatesKeyboard(rd): по кнопке на кандидата (дедуп по provider:provider_id, потолок), текст provider · Title (Year), у Chosen — префикс «✓ », callback pick:<id>:<candidateID>; нижняя кнопка «← Назад» с callback srcback:<id>.

3. Обработка callback (bot.go)

  • 3.1 handleCallback: ветки sources (answer + editMarkup списком кандидатов, перечитав ReviewData; домен не трогаем) и srcback (answer «Отменено» + refreshCard).
  • 3.2 Ветка pick: валидировать val (id кандидата) через ident.Parse (невалидный → answer «Кнопка устарела…», состояние не меняем), затем reviewer.ChooseCandidate(ctx, id, candidateID); успех → answer «Источник выбран» + refreshCard; ошибка — общая ветка ошибки.

4. Тесты

  • 4.1 internal/tgbot: карточка ревью с кандидатами содержит кнопку sources:<id>; без кандидатов — не содержит.
  • 4.2 sources разворачивает клавиатуру с кнопками pick:<id>:<candID> и «← Назад»; выбранный кандидат помечен «✓ ».
  • 4.3 pick с валидным id зовёт ChooseCandidate с этим id и обновляет карточку; pick с невалидным id — не зовёт домен.

5. Спека и проверки

  • 5.1 openspec validate --strict telegram-vybor-nahodok.
  • 5.2 task test и task lint — зелёные.