tgbot: показывать запись матча метабазы (провайдер+id+ссылка)

В карточке подтверждения и уведомлении о готовности бот теперь показывает
запись матча метабазы — провайдер, id и кликабельную ссылку на страницу
записи, — как веб-страница /download/{id} и экран ревью. Так ошибочную
привязку видно и из Telegram.

Билдер URL записи (providerURL/matchURL) вынесен из internal/httpapi в ядро
internal/worker (worker.ProviderURL + метод (*ReviewData).MatchURL()), чтобы
оба транспорта строили ссылку одинаково; httpapi делегирует туда. baseLine
переведён на эффективные provider/id (с учётом ручных правок), URL в href
экранируется escHref (сверх esc закрывает кавычку — иначе изготовленный id
разорвал бы атрибут и Telegram отклонил бы сообщение). При отсутствии матча
карточка ревью показывает «нет матча», уведомление о готовности строку
опускает.

Capability notifications: ADDED «Показ записи матча метабазы» + MODIFIED
требования об экранировании (id матча и URL, контекст href).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-07-18 15:41:28 +03:00
co-authored by Claude Opus 4.8
parent 4a58b0bda0
commit 10a6348d39
15 changed files with 614 additions and 123 deletions
+43
View File
@@ -957,6 +957,49 @@ type ReviewData struct {
Overrides map[string]string
}
// MatchURL — ссылка на подтверждённую запись метабазы для этой загрузки. Общий
// билдер для всех транспортов (веб и Telegram строят ссылку одинаково). Приоритет
// — URL выбранного кандидата, но только если его provider+id совпадают с
// эффективными (человек мог выбрать кандидата, затем вручную переопределить id —
// тогда кандидат указывает на другую запись). Иначе строим канонический URL по
// эффективным provider/id и типу медиа плана; если не удаётся — пусто (транспорт
// покажет матч текстом).
func (rd *ReviewData) MatchURL() string {
for _, c := range rd.Candidates {
if c.Chosen && c.Provider == rd.Provider && c.ProviderID == rd.ProviderID &&
c.URL.Valid && c.URL.String != "" {
return c.URL.String
}
}
return ProviderURL(rd.Provider, rd.ProviderID, string(rd.Plan.Type))
}
// ProviderURL строит канонический URL записи метабазы с учётом типа медиа.
// Пустой id или неизвестный провайдер → пусто.
func ProviderURL(provider, id, mediaType string) string {
if id == "" {
return ""
}
switch provider {
case "tmdb":
kind := "movie"
if mediaType == "series" {
kind = "tv"
}
return "https://www.themoviedb.org/" + kind + "/" + id
case "tvdb":
kind := "series"
if mediaType == "movie" {
kind = "movie"
}
return "https://www.thetvdb.com/dereferrer/" + kind + "/" + id
case "imdb":
return "https://www.imdb.com/title/" + id
default:
return ""
}
}
// SourceKind — вид источника в едином списке ревью.
type SourceKind string