mirror of
https://github.com/BEARlogin/max-telegram-bridge-bot.git
synced 2026-04-28 03:39:46 +00:00
Two issues reported by a user via the bridge: 1. Links vanish on TG channel → MAX crosspost. formatTgCrosspostCaption returned raw text and forwardTgToMax dropped markdown format for the crosspost branch, so text_link entities became plain text. Now formatTgCrosspostCaption runs tgEntitiesToMarkdown, and forwardTgToMax always uses markdown. Media group flusher also skips the second conversion for crosspost items (caption is already markdown) and keeps markdown format. 2. When a channel video is too big for Bot API getFile (e.g. HD > 2 GB on local server), the whole post was dropped — even the caption text was lost. Video upload failure no longer returns from forwardTgToMax; the fallback branch appends a "[Видео]" marker so subscribers in MAX at least get the text and know a video was attached but not relayed. The fallback condition now checks presence of any media instead of only msg.Text == "" (it missed video-with-caption posts) and also covers Animation and Photo. Tradeoff: crosspost replacements now operate on markdown text. URL- and phrase-level replacements keep working; regex rules touching markdown characters (_, *, [, ]) may need adjustment. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
147 lines
3.9 KiB
Go
147 lines
3.9 KiB
Go
package main
|
||
|
||
import (
|
||
"strings"
|
||
|
||
maxschemes "github.com/max-messenger/max-bot-api-client-go/schemes"
|
||
)
|
||
|
||
func tgName(msg *TGMessage) string {
|
||
if msg.From == nil {
|
||
if msg.SenderChat != nil {
|
||
return msg.SenderChat.Title
|
||
}
|
||
return "Unknown"
|
||
}
|
||
name := msg.From.FirstName
|
||
if msg.From.LastName != "" {
|
||
name += " " + msg.From.LastName
|
||
}
|
||
return name
|
||
}
|
||
|
||
// formatAttribution собирает строку "Имя: текст" или "Имя:\nтекст" в зависимости от настройки.
|
||
func formatAttribution(name, text string, newline bool) string {
|
||
if newline {
|
||
return name + ":\n" + text
|
||
}
|
||
return name + ": " + text
|
||
}
|
||
|
||
// formatAttributionMD собирает строку с жирным именем в markdown: "**Имя**: текст".
|
||
func formatAttributionMD(name, text string, newline bool) string {
|
||
bold := "**" + name + "**"
|
||
if newline {
|
||
return bold + ":\n" + text
|
||
}
|
||
return bold + ": " + text
|
||
}
|
||
|
||
// formatTgCaption — для пересылки (текст или caption)
|
||
func formatTgCaption(msg *TGMessage, prefix, newline bool) string {
|
||
name := tgName(msg)
|
||
text := msg.Text
|
||
if text == "" {
|
||
text = msg.Caption
|
||
}
|
||
if prefix {
|
||
return formatAttribution("[TG] "+name, text, newline)
|
||
}
|
||
return formatAttribution(name, text, newline)
|
||
}
|
||
|
||
// formatTgMessage — для edit (полный формат)
|
||
func formatTgMessage(msg *TGMessage, prefix, newline bool) string {
|
||
name := tgName(msg)
|
||
text := msg.Text
|
||
if text == "" {
|
||
text = msg.Caption
|
||
}
|
||
if text == "" {
|
||
return ""
|
||
}
|
||
if prefix {
|
||
return formatAttribution("[TG] "+name, text, newline)
|
||
}
|
||
return formatAttribution(name, text, newline)
|
||
}
|
||
|
||
func maxName(upd *maxschemes.MessageCreatedUpdate) string {
|
||
name := upd.Message.Sender.Name
|
||
if name == "" {
|
||
name = upd.Message.Sender.Username
|
||
}
|
||
return name
|
||
}
|
||
|
||
// formatMaxCaption — для пересылки
|
||
func formatMaxCaption(upd *maxschemes.MessageCreatedUpdate, prefix, newline bool) string {
|
||
name := maxName(upd)
|
||
text := upd.Message.Body.Text
|
||
if prefix {
|
||
return formatAttribution("[MAX] "+name, text, newline)
|
||
}
|
||
return formatAttribution(name, text, newline)
|
||
}
|
||
|
||
// formatTgCrosspostCaption — для кросспостинга каналов (без attribution и префиксов).
|
||
// Конвертирует entities в markdown, чтобы ссылки и форматирование сохранились
|
||
// при пересылке в MAX. Replacements, если настроены, применяются поверх markdown.
|
||
func formatTgCrosspostCaption(msg *TGMessage) string {
|
||
text := msg.Text
|
||
entities := msg.Entities
|
||
if text == "" {
|
||
text = msg.Caption
|
||
entities = msg.CaptionEntities
|
||
}
|
||
return tgEntitiesToMarkdown(text, entities)
|
||
}
|
||
|
||
// formatMaxCrosspostCaption — для кросспостинга каналов (без attribution и префиксов)
|
||
func formatMaxCrosspostCaption(upd *maxschemes.MessageCreatedUpdate) string {
|
||
return upd.Message.Body.Text
|
||
}
|
||
|
||
// mimeToFilename генерирует имя файла из MIME-типа, если оригинальное имя отсутствует.
|
||
func mimeToFilename(base, mime string) string {
|
||
ext := ""
|
||
// sub = часть после "/" в mime type
|
||
if i := strings.Index(mime, "/"); i >= 0 {
|
||
sub := mime[i+1:]
|
||
switch sub {
|
||
case "mp4":
|
||
ext = ".mp4"
|
||
case "webm":
|
||
ext = ".webm"
|
||
case "x-matroska":
|
||
ext = ".mkv"
|
||
case "quicktime":
|
||
ext = ".mov"
|
||
case "mpeg":
|
||
ext = ".mpeg"
|
||
case "ogg":
|
||
ext = ".ogg"
|
||
case "pdf":
|
||
ext = ".pdf"
|
||
case "gif":
|
||
ext = ".gif"
|
||
default:
|
||
ext = "." + sub
|
||
}
|
||
}
|
||
return base + ext
|
||
}
|
||
|
||
// fileNameFromURL извлекает имя файла из URL, fallback "file".
|
||
func fileNameFromURL(rawURL string) string {
|
||
if idx := strings.LastIndex(rawURL, "/"); idx >= 0 {
|
||
name := rawURL[idx+1:]
|
||
if q := strings.Index(name, "?"); q >= 0 {
|
||
name = name[:q]
|
||
}
|
||
if name != "" {
|
||
return name
|
||
}
|
||
}
|
||
return "file"
|
||
}
|