telegram-to-discord/tgBot/handler.go

211 lines
4.7 KiB
Go
Raw Permalink Normal View History

2025-03-23 15:35:24 +03:00
package tgBot
import (
2025-03-29 22:00:58 +03:00
"bytes"
2025-03-23 15:35:24 +03:00
"context"
"fmt"
"github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
log "github.com/sirupsen/logrus"
2025-03-29 22:00:58 +03:00
"io"
"net/http"
"path/filepath"
"sync"
2025-03-23 15:35:24 +03:00
"tg-disc-bot/config"
"tg-disc-bot/dto"
2025-03-29 22:00:58 +03:00
"time"
2025-03-23 15:35:24 +03:00
)
type TgBot struct {
2025-03-29 22:00:58 +03:00
ctx context.Context
bot *bot.Bot
chatID int64
token string
msgChan chan dto.TelegramDTO
mediaGroupCache map[string][]dto.Image
cacheMutex sync.Mutex
2025-03-23 15:35:24 +03:00
}
func NewTgBot(ctx context.Context, config config.TelegramConfig) (*TgBot, error) {
2025-03-29 22:00:58 +03:00
tgBot := &TgBot{
ctx: ctx,
chatID: config.ChatID,
token: config.Token,
msgChan: make(chan dto.TelegramDTO, 100),
mediaGroupCache: make(map[string][]dto.Image, 10),
cacheMutex: sync.Mutex{},
}
2025-03-23 15:35:24 +03:00
var err error
tgBot.bot, err = bot.New(config.Token)
if err != nil {
return nil, err
}
return tgBot, nil
}
func (b *TgBot) Start(fromDiscord chan dto.DiscordDTO) chan dto.TelegramDTO {
2025-03-29 22:00:58 +03:00
b.bot.RegisterHandler(bot.HandlerTypeMessageText, "", bot.MatchTypeContains, b.mainHandler)
2025-03-23 15:35:24 +03:00
go func() {
log.Info("Starting telegram bot...")
b.bot.Start(b.ctx)
}()
go func() {
for msg := range fromDiscord {
log.WithField("content", msg).Debug("TG | Message from Discord")
2026-03-05 16:20:28 +03:00
m := fmt.Sprintf("*\\[ %s \\]*\n%s", msg.AuthorName, msg.Content)
2025-03-23 15:35:24 +03:00
b.bot.SendMessage(b.ctx, &bot.SendMessageParams{
2026-03-05 16:20:28 +03:00
ChatID: b.chatID,
Text: m,
ParseMode: "",
2025-03-23 15:35:24 +03:00
})
2025-03-29 22:00:58 +03:00
if msg.Images != nil {
var mediaGroup []models.InputMedia
for _, img := range *msg.Images {
photo := &models.InputMediaPhoto{
2026-03-05 16:20:28 +03:00
Media: fmt.Sprintf("attach://%s", img.Filename),
//Caption: "test caption",
2025-03-29 22:00:58 +03:00
MediaAttachment: bytes.NewReader(img.Data),
}
mediaGroup = append(mediaGroup, photo)
}
_, err := b.bot.SendMediaGroup(b.ctx, &bot.SendMediaGroupParams{
ChatID: b.chatID,
Media: mediaGroup,
})
if err != nil {
log.Error("TG | Failed to send media group")
}
}
2025-03-23 15:35:24 +03:00
}
}()
2025-03-29 22:00:58 +03:00
return b.msgChan
}
func (b *TgBot) mainHandler(ctx context.Context, bt *bot.Bot, update *models.Update) {
if update.Message != nil && !update.Message.From.IsBot {
mediaGroupID := update.Message.MediaGroupID
if mediaGroupID != "" {
b.processMediaGroup(update, mediaGroupID, b.msgChan)
} else {
b.proccessSimple(update, b.msgChan)
}
}
}
func (b *TgBot) getImage(photo models.PhotoSize) *dto.Image {
fileID := &bot.GetFileParams{FileID: photo.FileID}
file, err := b.bot.GetFile(b.ctx, fileID)
if err != nil {
log.Errorf("GetFile error: %s", err)
return nil
}
if file.FilePath == "" {
log.Errorf("FilePath is empty for fileID: %s", photo.FileID)
return nil
}
resp, err := http.Get(fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", b.token, file.FilePath))
if err != nil {
log.Errorf("Download error: %s", err)
return nil
}
defer resp.Body.Close()
imgBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Errorf("ReadAll error: %s", err)
return nil
}
image := dto.Image{
Filename: filepath.Base(file.FilePath),
Data: imgBytes,
}
return &image
}
func (b *TgBot) proccessSimple(update *models.Update, msgChan chan<- dto.TelegramDTO) {
var images []dto.Image
content := update.Message.Text
if update.Message.Photo != nil {
largest := update.Message.Photo[len(update.Message.Photo)-1]
images = append(images, *b.getImage(largest))
content = update.Message.Caption
}
msg := dto.TelegramDTO{
2026-03-05 16:20:28 +03:00
AuthorName: b.getName(update),
2025-03-29 22:00:58 +03:00
Content: content,
Images: &images,
}
msgChan <- msg
}
func (b *TgBot) processMediaGroup(update *models.Update, mediaGroupID string, msgChan chan<- dto.TelegramDTO) {
b.cacheMutex.Lock()
defer b.cacheMutex.Unlock()
if update.Message.Photo != nil {
largest := update.Message.Photo[len(update.Message.Photo)-1]
image := b.getImage(largest)
b.mediaGroupCache[mediaGroupID] = append(b.mediaGroupCache[mediaGroupID], *image)
}
var content string
if update.Message.Caption != "" {
content = update.Message.Caption
} else {
content = update.Message.Text
}
go func() {
time.Sleep(2 * time.Second)
b.cacheMutex.Lock()
defer b.cacheMutex.Unlock()
images, exists := b.mediaGroupCache[mediaGroupID]
if !exists || len(images) == 0 {
return
}
delete(b.mediaGroupCache, mediaGroupID)
msg := dto.TelegramDTO{
2026-03-05 16:20:28 +03:00
AuthorName: b.getName(update),
2025-03-29 22:00:58 +03:00
Content: content,
Images: &images,
}
msgChan <- msg
}()
2025-03-23 15:35:24 +03:00
}
2026-03-05 16:20:28 +03:00
func (b *TgBot) getName(update *models.Update) string {
switch {
case update.Message.From.Username != "":
return update.Message.From.Username
case update.Message.From.FirstName != "":
return update.Message.From.FirstName
case update.Message.From.LastName != "":
return update.Message.From.LastName
default:
return "No name"
}
}