From 9714464d2095c77999f7e627fe5b5443179ecaaa Mon Sep 17 00:00:00 2001 From: nquidox Date: Sat, 29 Mar 2025 22:00:58 +0300 Subject: [PATCH] added: image send --- discordBot/handler.go | 25 +++++- discordBot/messages.go | 47 +++++++++++ tgBot/handler.go | 171 +++++++++++++++++++++++++++++++++++------ 3 files changed, 218 insertions(+), 25 deletions(-) diff --git a/discordBot/handler.go b/discordBot/handler.go index 6c5c95d..1fa98ed 100644 --- a/discordBot/handler.go +++ b/discordBot/handler.go @@ -1,6 +1,7 @@ package discordBot import ( + "bytes" "context" "fmt" "github.com/disgoorg/disgo" @@ -18,11 +19,16 @@ type DiscordBot struct { client bot.Client guildID snowflake.ID channelID snowflake.ID + token string } func NewDiscordBot(ctx context.Context, config config.DiscordConfig) (*DiscordBot, error) { var err error - dsBot := &DiscordBot{ctx: ctx, channelID: config.ChannelID} + dsBot := &DiscordBot{ + ctx: ctx, + channelID: config.ChannelID, + token: config.Token, + } dsBot.client, err = disgo.New(config.Token, bot.WithGatewayConfigOpts( @@ -40,7 +46,7 @@ func NewDiscordBot(ctx context.Context, config config.DiscordConfig) (*DiscordBo return dsBot, nil } -func (d *DiscordBot) Start(fromTelegram chan dto.TelegramDTO) chan dto.DiscordDTO { +func (d *DiscordBot) Start(fromTelegram <-chan dto.TelegramDTO) chan dto.DiscordDTO { log.Info("Starting discord bot...") msgChan := make(chan dto.DiscordDTO, 100) @@ -50,11 +56,26 @@ func (d *DiscordBot) Start(fromTelegram chan dto.TelegramDTO) chan dto.DiscordDT for msg := range fromTelegram { log.WithField("content", msg).Debug("DS | Message from Telegram") + var files []*discord.File + for _, media := range *(msg.Images) { + file := &discord.File{ + Name: media.Filename, + Reader: bytes.NewReader(media.Data), + } + files = append(files, file) + } + m := discord.MessageCreate{Content: fmt.Sprintf("[%s]\n%s", msg.AuthorName, msg.Content)} + if len(files) > 0 { + m.Files = append(m.Files, files...) + + } + _, err := d.client.Rest().CreateMessage(d.channelID, m) if err != nil { log.Errorf("Failed to send message to Discord: %v", err) + continue } } }() diff --git a/discordBot/messages.go b/discordBot/messages.go index 0322a30..697082f 100644 --- a/discordBot/messages.go +++ b/discordBot/messages.go @@ -2,8 +2,11 @@ package discordBot import ( "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" log "github.com/sirupsen/logrus" + "io" + "net/http" "tg-disc-bot/dto" ) @@ -14,9 +17,20 @@ type messageHandler struct { func (m *messageHandler) OnEvent(event bot.Event) { if e, ok := event.(*events.MessageCreate); ok { if !e.Message.Author.Bot { + attachments := e.Message.Attachments + var images *[]dto.Image + var err error + if len(attachments) > 0 { + images, err = getImages(attachments) + if err != nil { + log.Warnf("could not get images from attachments: %v", err) + } + } + message := dto.DiscordDTO{ AuthorName: e.Message.Author.Username, Content: e.Message.Content, + Images: images, } m.msgChan <- message @@ -26,3 +40,36 @@ func (m *messageHandler) OnEvent(event bot.Event) { } } + +func getImages(a []discord.Attachment) (*[]dto.Image, error) { + var images []dto.Image + for _, item := range a { + if (*item.ContentType == "image/png") || (*item.ContentType == "image/jpeg") { + imageData, err := downloadImage(item.URL) + if err != nil { + log.Errorf("Error downloading image: %s", err) + return nil, err + } + + images = append(images, dto.Image{ + Data: imageData, + Filename: item.Filename, + }) + } + } + return &images, nil +} + +func downloadImage(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return data, nil +} diff --git a/tgBot/handler.go b/tgBot/handler.go index ce963cf..40c52c0 100644 --- a/tgBot/handler.go +++ b/tgBot/handler.go @@ -1,23 +1,40 @@ package tgBot import ( + "bytes" "context" "fmt" "github.com/go-telegram/bot" "github.com/go-telegram/bot/models" log "github.com/sirupsen/logrus" + "io" + "net/http" + "path/filepath" + "sync" "tg-disc-bot/config" "tg-disc-bot/dto" + "time" ) type TgBot struct { - ctx context.Context - bot *bot.Bot - chatID int64 + ctx context.Context + bot *bot.Bot + chatID int64 + token string + msgChan chan dto.TelegramDTO + mediaGroupCache map[string][]dto.Image + cacheMutex sync.Mutex } func NewTgBot(ctx context.Context, config config.TelegramConfig) (*TgBot, error) { - tgBot := &TgBot{ctx: ctx, chatID: config.ChatID} + 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{}, + } var err error tgBot.bot, err = bot.New(config.Token) @@ -30,24 +47,7 @@ func NewTgBot(ctx context.Context, config config.TelegramConfig) (*TgBot, error) func (b *TgBot) Start(fromDiscord chan dto.DiscordDTO) chan dto.TelegramDTO { - msgChan := make(chan dto.TelegramDTO, 100) - - b.bot.RegisterHandler( - bot.HandlerTypeMessageText, - "", - bot.MatchTypeContains, - func(ctx context.Context, bt *bot.Bot, update *models.Update) { - if update.Message != nil || !update.Message.From.IsBot { - msg := dto.TelegramDTO{ - AuthorName: update.Message.From.Username, - Content: update.Message.Text, - } - - fmt.Println(update.Message.Chat.ID) - - msgChan <- msg - } - }) + b.bot.RegisterHandler(bot.HandlerTypeMessageText, "", bot.MatchTypeContains, b.mainHandler) go func() { log.Info("Starting telegram bot...") @@ -63,8 +63,133 @@ func (b *TgBot) Start(fromDiscord chan dto.DiscordDTO) chan dto.TelegramDTO { ChatID: b.chatID, Text: m, }) + + if msg.Images != nil { + var mediaGroup []models.InputMedia + for _, img := range *msg.Images { + photo := &models.InputMediaPhoto{ + Media: fmt.Sprintf("attach://%s", img.Filename), + Caption: "test caption", + 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") + } + } } }() - return msgChan + 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{ + AuthorName: update.Message.From.Username, + 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{ + AuthorName: update.Message.From.Username, + Content: content, + Images: &images, + } + + msgChan <- msg + }() }