Files
jellybit/internal/tgbot/render.go
T

183 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tgbot
import (
"fmt"
"path/filepath"
"strconv"
"strings"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"git.vakhrushev.me/av/jellybit/internal/store"
"git.vakhrushev.me/av/jellybit/internal/worker"
)
// renderCard строит текст и клавиатуру карточки по состоянию задачи.
func (b *Bot) renderCard(rd *worker.ReviewData) (string, *tgbotapi.InlineKeyboardMarkup) {
id := rd.Download.ID
state := rd.Download.State
switch state {
case store.StateReview, store.StateDeferred:
return b.reviewCard(rd)
case store.StateRecognizing:
return "⏳ Распознаю #" + itoa(id) + "…", b.webOnly(id)
case store.StateLinking:
return "⏳ Раскладываю #" + itoa(id) + "…", nil
case store.StateDone:
return b.renderDone(rd), b.webOnly(id)
default:
text := fmt.Sprintf("Задача #%d — %s.", id, state)
if msg := rd.Download.ErrorMsg.String; msg != "" {
text += "\n" + msg
}
return text, b.webOnly(id)
}
}
func (b *Bot) reviewCard(rd *worker.ReviewData) (string, *tgbotapi.InlineKeyboardMarkup) {
id := rd.Download.ID
var sb strings.Builder
fmt.Fprintf(&sb, "🟡 Нужно подтверждение #%d\n", id)
if src := contextOrSource(rd); src != "" {
fmt.Fprintf(&sb, "Источник: %s\n", shorten(src, 80))
}
fmt.Fprintf(&sb, "Похоже на: %s\n", guessLine(rd))
if base := baseLine(rd.Recognition); base != "" {
fmt.Fprintf(&sb, "База: %s\n", base)
}
if reasons := rd.Recognition.ReasonList(); len(reasons) > 0 {
fmt.Fprintf(&sb, "Причины: %s\n", strings.Join(reasons, " · "))
}
if n := len(rd.Preview); n > 0 {
fmt.Fprintf(&sb, "План: %d файлов → %s", n, tailPath(rd.Preview[0].Dst))
}
return strings.TrimRight(sb.String(), "\n"), b.reviewKeyboard(rd)
}
func (b *Bot) reviewKeyboard(rd *worker.ReviewData) *tgbotapi.InlineKeyboardMarkup {
id := rd.Download.ID
sid := itoa(id)
var row1 []tgbotapi.InlineKeyboardButton
if len(rd.Preview) > 0 {
row1 = append(row1, tgbotapi.NewInlineKeyboardButtonData("✅ Применить", "apply:"+sid))
}
row1 = append(row1, tgbotapi.NewInlineKeyboardButtonData("📺↔🎬 Тип", "type:"+sid+":"+oppositeType(string(rd.Plan.Type))))
row2 := tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("🔁 Уточнить", "refine:"+sid),
tgbotapi.NewInlineKeyboardButtonData("🕗 Позже", "defer:"+sid),
)
var row3 []tgbotapi.InlineKeyboardButton
if url := b.reviewURL(id); url != "" {
row3 = append(row3, tgbotapi.NewInlineKeyboardButtonURL("🌐 В вебе", url))
}
row3 = append(row3, tgbotapi.NewInlineKeyboardButtonData("❌ Отклонить", "reject:"+sid))
kb := tgbotapi.NewInlineKeyboardMarkup(row1, row2, row3)
return &kb
}
// renderDone — короткое сообщение о готовности.
func (b *Bot) renderDone(rd *worker.ReviewData) string {
title := rd.Plan.Title
if title == "" {
title = "#" + itoa(rd.Download.ID)
}
n := len(rd.Preview)
if n == 0 {
return fmt.Sprintf("✅ Готово: «%s» разложен.", title)
}
return fmt.Sprintf("✅ Готово: «%s» — разложено файлов: %d.", title, n)
}
func (b *Bot) webOnly(id int64) *tgbotapi.InlineKeyboardMarkup {
url := b.reviewURL(id)
if url == "" {
return nil
}
kb := tgbotapi.NewInlineKeyboardMarkup(tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonURL("🌐 Открыть в вебе", url),
))
return &kb
}
func (b *Bot) reviewURL(id int64) string {
if b.webBase == "" {
return ""
}
return b.webBase + "/review/" + itoa(id)
}
// --- мелкие хелперы ---
func guessLine(rd *worker.ReviewData) string {
emoji, kind := "🎬", "фильм"
if rd.Plan.Type == "series" {
emoji, kind = "📺", "сериал"
}
title := rd.Plan.Title
if title == "" {
title = "не распознано"
}
s := fmt.Sprintf("%s %s «%s»", emoji, kind, title)
if rd.Plan.Year != 0 {
s += fmt.Sprintf(" (%d)", rd.Plan.Year)
}
return s
}
func baseLine(rec *store.Recognition) string {
if rec == nil || !rec.Provider.Valid || rec.Provider.String == "" || rec.Provider.String == "none" {
return "нет матча"
}
if rec.ProviderID.Valid && rec.ProviderID.String != "" {
return rec.Provider.String + " " + rec.ProviderID.String
}
return rec.Provider.String
}
func contextOrSource(rd *worker.ReviewData) string {
if c := strings.TrimSpace(rd.Download.Context); c != "" {
return firstLine(c)
}
return rd.Download.SourceRef
}
func oppositeType(t string) string {
if t == "series" {
return "movie"
}
return "series"
}
func firstLine(s string) string {
if i := strings.IndexByte(s, '\n'); i >= 0 {
return s[:i]
}
return s
}
func tailPath(p string) string {
dir, file := filepath.Split(p)
parent := filepath.Base(strings.TrimRight(dir, "/"))
if parent == "." || parent == "/" || parent == "" {
return file
}
return parent + "/" + file
}
func shorten(s string, n int) string {
r := []rune(s)
if len(r) <= n {
return s
}
return string(r[:n]) + "…"
}
func itoa(n int64) string { return strconv.FormatInt(n, 10) }