159 lines
3.7 KiB
Go
159 lines
3.7 KiB
Go
package telegram
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.vakhrushev.me/av/remembos/internal/memos"
|
|
"git.vakhrushev.me/av/remembos/internal/search"
|
|
)
|
|
|
|
const (
|
|
maxMessageLen = 4096
|
|
maxCaptionLen = 1024
|
|
)
|
|
|
|
// formatMemory returns the full message text formatted as HTML for Telegram.
|
|
// The text is NOT truncated — the caller is responsible for splitting if needed.
|
|
func formatMemory(mem *search.Memory, publicURL string) string {
|
|
var b strings.Builder
|
|
|
|
// Header: date and "ago" text
|
|
b.WriteString(fmt.Sprintf("<b>%s</b>", formatDate(mem.Date)))
|
|
if ago := agoText(mem.Date); ago != "" {
|
|
b.WriteString(fmt.Sprintf(" <i>(%s)</i>", ago))
|
|
}
|
|
b.WriteString("\n\n")
|
|
|
|
// Content
|
|
b.WriteString(escapeHTML(mem.Memo.Content))
|
|
|
|
// Link to original
|
|
memoURL := fmt.Sprintf("%s/%s", publicURL, mem.Memo.Name)
|
|
b.WriteString("\n\n<a href=\"" + memoURL + "\">Оригинал</a>")
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// mediaAttachments splits memo attachments into images, videos, and audios.
|
|
func mediaAttachments(memo *memos.Memo) (images, videos, audios []memos.Attachment) {
|
|
for _, att := range memo.Attachments {
|
|
switch {
|
|
case att.IsImage():
|
|
images = append(images, att)
|
|
case att.IsVideo():
|
|
videos = append(videos, att)
|
|
case att.IsAudio():
|
|
audios = append(audios, att)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// splitText splits text into chunks of at most maxLen bytes,
|
|
// breaking at paragraph (\n\n), line (\n), or word boundaries.
|
|
func splitText(text string, maxLen int) []string {
|
|
if len(text) <= maxLen {
|
|
return []string{text}
|
|
}
|
|
|
|
var parts []string
|
|
remaining := text
|
|
|
|
for len(remaining) > maxLen {
|
|
chunk := remaining[:maxLen]
|
|
|
|
// Try to break at a paragraph boundary
|
|
idx := strings.LastIndex(chunk, "\n\n")
|
|
if idx <= 0 {
|
|
// Try a line boundary
|
|
idx = strings.LastIndex(chunk, "\n")
|
|
}
|
|
if idx <= 0 {
|
|
// Try a word boundary
|
|
idx = strings.LastIndexByte(chunk, ' ')
|
|
}
|
|
if idx <= 0 {
|
|
// Hard cut as last resort
|
|
idx = maxLen
|
|
}
|
|
|
|
parts = append(parts, remaining[:idx])
|
|
remaining = strings.TrimLeft(remaining[idx:], "\n ")
|
|
}
|
|
|
|
if remaining != "" {
|
|
parts = append(parts, remaining)
|
|
}
|
|
|
|
return parts
|
|
}
|
|
|
|
// escapeHTML escapes special HTML characters for Telegram HTML parse mode.
|
|
func escapeHTML(s string) string {
|
|
s = strings.ReplaceAll(s, "&", "&")
|
|
s = strings.ReplaceAll(s, "<", "<")
|
|
s = strings.ReplaceAll(s, ">", ">")
|
|
return s
|
|
}
|
|
|
|
var months = [...]string{
|
|
1: "января", 2: "февраля", 3: "марта",
|
|
4: "апреля", 5: "мая", 6: "июня",
|
|
7: "июля", 8: "августа", 9: "сентября",
|
|
10: "октября", 11: "ноября", 12: "декабря",
|
|
}
|
|
|
|
func formatDate(t time.Time) string {
|
|
return fmt.Sprintf("%d %s %d", t.Day(), months[t.Month()], t.Year())
|
|
}
|
|
|
|
func agoText(t time.Time) string {
|
|
now := time.Now()
|
|
years := now.Year() - t.Year()
|
|
monthsDiff := int(now.Month()) - int(t.Month())
|
|
if monthsDiff < 0 {
|
|
years--
|
|
monthsDiff += 12
|
|
}
|
|
|
|
if years > 0 {
|
|
return fmt.Sprintf("%s назад", pluralYears(years))
|
|
}
|
|
if monthsDiff > 0 {
|
|
return fmt.Sprintf("%s назад", pluralMonths(monthsDiff))
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func pluralYears(n int) string {
|
|
mod10 := n % 10
|
|
mod100 := n % 100
|
|
switch {
|
|
case mod100 >= 11 && mod100 <= 14:
|
|
return fmt.Sprintf("%d лет", n)
|
|
case mod10 == 1:
|
|
return fmt.Sprintf("%d год", n)
|
|
case mod10 >= 2 && mod10 <= 4:
|
|
return fmt.Sprintf("%d года", n)
|
|
default:
|
|
return fmt.Sprintf("%d лет", n)
|
|
}
|
|
}
|
|
|
|
func pluralMonths(n int) string {
|
|
mod10 := n % 10
|
|
mod100 := n % 100
|
|
switch {
|
|
case mod100 >= 11 && mod100 <= 14:
|
|
return fmt.Sprintf("%d месяцев", n)
|
|
case mod10 == 1:
|
|
return fmt.Sprintf("%d месяц", n)
|
|
case mod10 >= 2 && mod10 <= 4:
|
|
return fmt.Sprintf("%d месяца", n)
|
|
default:
|
|
return fmt.Sprintf("%d месяцев", n)
|
|
}
|
|
}
|