Compare commits

..

No commits in common. "d9e56de17f1374b0bfc05bde6c3947157c368731" and "7c610d04771cb1b0916b2e12e49dd3e90b5e7d25" have entirely different histories.

14 changed files with 41 additions and 526 deletions

1
.gitignore vendored
View file

@ -1,2 +1 @@
.idea .idea
config/dev_config.go

View file

@ -1,21 +0,0 @@
FROM golang:1.25.7-alpine3.23 AS builder
WORKDIR /build
COPY go.* ./
RUN go mod tidy
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app ./
FROM alpine:latest
RUN mkdir -p /home
COPY --from=builder /build/app /home/app
CMD ["./home/app"]

View file

@ -8,10 +8,9 @@ import (
) )
type Config struct { type Config struct {
AppConf AppConfig AppConf AppConfig
TgConf TelegramConfig TgConf TelegramConfig
DsConf DiscordConfig DsConf DiscordConfig
HttpConf HttpConfig
} }
type AppConfig struct { type AppConfig struct {
@ -29,17 +28,10 @@ type DiscordConfig struct {
ChannelID snowflake.ID ChannelID snowflake.ID
} }
type HttpConfig struct {
Host string
Port string
GinMode string
}
// prod config
func NewConfig() *Config { func NewConfig() *Config {
return &Config{ return &Config{
AppConfig{ AppConfig{
LogLvl: getEnv("APP_LOG_LEVEL", "debug"), LogLvl: "info",
}, },
TelegramConfig{ TelegramConfig{
Token: getEnv("TELEGRAM_TOKEN", ""), Token: getEnv("TELEGRAM_TOKEN", ""),
@ -50,12 +42,6 @@ func NewConfig() *Config {
GuildID: convertID(getEnv("GUILD_ID", "")), GuildID: convertID(getEnv("GUILD_ID", "")),
ChannelID: convertID(getEnv("CHANNEL_ID", "")), ChannelID: convertID(getEnv("CHANNEL_ID", "")),
}, },
HttpConfig{
Host: getEnv("HTTP_HOST", "0.0.0.0"),
Port: getEnv("HTTP_PORT", "8080"),
GinMode: getEnv("GIN_MODE", "debug"),
},
} }
} }

View file

@ -1,7 +1,6 @@
package discordBot package discordBot
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"github.com/disgoorg/disgo" "github.com/disgoorg/disgo"
@ -19,16 +18,11 @@ type DiscordBot struct {
client bot.Client client bot.Client
guildID snowflake.ID guildID snowflake.ID
channelID snowflake.ID channelID snowflake.ID
token string
} }
func NewDiscordBot(ctx context.Context, config config.DiscordConfig) (*DiscordBot, error) { func NewDiscordBot(ctx context.Context, config config.DiscordConfig) (*DiscordBot, error) {
var err error var err error
dsBot := &DiscordBot{ dsBot := &DiscordBot{ctx: ctx, channelID: config.ChannelID}
ctx: ctx,
channelID: config.ChannelID,
token: config.Token,
}
dsBot.client, err = disgo.New(config.Token, dsBot.client, err = disgo.New(config.Token,
bot.WithGatewayConfigOpts( bot.WithGatewayConfigOpts(
@ -46,7 +40,7 @@ func NewDiscordBot(ctx context.Context, config config.DiscordConfig) (*DiscordBo
return dsBot, nil 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...") log.Info("Starting discord bot...")
msgChan := make(chan dto.DiscordDTO, 100) msgChan := make(chan dto.DiscordDTO, 100)
@ -56,26 +50,11 @@ func (d *DiscordBot) Start(fromTelegram <-chan dto.TelegramDTO) chan dto.Discord
for msg := range fromTelegram { for msg := range fromTelegram {
log.WithField("content", msg).Debug("DS | Message from Telegram") log.WithField("content", msg).Debug("DS | Message from Telegram")
var files []*discord.File m := discord.MessageCreate{Content: fmt.Sprintf("[%s]\n%s", msg.AuthorName, msg.Content)}
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) _, err := d.client.Rest().CreateMessage(d.channelID, m)
if err != nil { if err != nil {
log.Errorf("Failed to send message to Discord: %v", err) log.Errorf("Failed to send message to Discord: %v", err)
continue
} }
} }
}() }()

View file

@ -2,11 +2,8 @@ package discordBot
import ( import (
"github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io"
"net/http"
"tg-disc-bot/dto" "tg-disc-bot/dto"
) )
@ -17,20 +14,9 @@ type messageHandler struct {
func (m *messageHandler) OnEvent(event bot.Event) { func (m *messageHandler) OnEvent(event bot.Event) {
if e, ok := event.(*events.MessageCreate); ok { if e, ok := event.(*events.MessageCreate); ok {
if !e.Message.Author.Bot { 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{ message := dto.DiscordDTO{
AuthorName: e.Message.Author.Username, AuthorName: e.Message.Author.Username,
Content: e.Message.Content, Content: e.Message.Content,
Images: images,
} }
m.msgChan <- message m.msgChan <- message
@ -40,36 +26,3 @@ 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
}

View file

@ -3,5 +3,4 @@ package dto
type DiscordDTO struct { type DiscordDTO struct {
AuthorName string AuthorName string
Content string Content string
Images *[]Image
} }

View file

@ -1,6 +0,0 @@
package dto
type Image struct {
Data []byte
Filename string
}

View file

@ -3,5 +3,4 @@ package dto
type TelegramDTO struct { type TelegramDTO struct {
AuthorName string AuthorName string
Content string Content string
Images *[]Image
} }

42
go.mod
View file

@ -1,54 +1,18 @@
module tg-disc-bot module tg-disc-bot
go 1.25.0 go 1.24.1
require ( require (
github.com/disgoorg/disgo v0.18.15 github.com/disgoorg/disgo v0.18.15
github.com/disgoorg/snowflake/v2 v2.0.3 github.com/disgoorg/snowflake/v2 v2.0.3
github.com/gin-gonic/gin v1.12.0
github.com/go-telegram/bot v1.14.1 github.com/go-telegram/bot v1.14.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/zsais/go-gin-prometheus v1.0.3
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/disgoorg/json v1.2.0 // indirect github.com/disgoorg/json v1.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/crypto v0.36.0 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect golang.org/x/sys v0.31.0 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
golang.org/x/arch v0.22.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
) )

103
go.sum
View file

@ -1,15 +1,3 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -19,106 +7,25 @@ github.com/disgoorg/json v1.2.0 h1:6e/j4BCfSHIvucG1cd7tJPAOp1RgnnMFSqkvZUtEd1Y=
github.com/disgoorg/json v1.2.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA= github.com/disgoorg/json v1.2.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA=
github.com/disgoorg/snowflake/v2 v2.0.3 h1:3B+PpFjr7j4ad7oeJu4RlQ+nYOTadsKapJIzgvSI2Ro= github.com/disgoorg/snowflake/v2 v2.0.3 h1:3B+PpFjr7j4ad7oeJu4RlQ+nYOTadsKapJIzgvSI2Ro=
github.com/disgoorg/snowflake/v2 v2.0.3/go.mod h1:W6r7NUA7DwfZLwr00km6G4UnZ0zcoLBRufhkFWgAc4c= github.com/disgoorg/snowflake/v2 v2.0.3/go.mod h1:W6r7NUA7DwfZLwr00km6G4UnZ0zcoLBRufhkFWgAc4c=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-telegram/bot v1.14.1 h1:ySVCITvYsvBSiChOmr6GolLUcWX2T/ugykc2rjIaaQg= github.com/go-telegram/bot v1.14.1 h1:ySVCITvYsvBSiChOmr6GolLUcWX2T/ugykc2rjIaaQg=
github.com/go-telegram/bot v1.14.1/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM= github.com/go-telegram/bot v1.14.1/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad h1:qIQkSlF5vAUHxEmTbaqt1hkJ/t6skqEGYiMag343ucI= github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad h1:qIQkSlF5vAUHxEmTbaqt1hkJ/t6skqEGYiMag343ucI=
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s= github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/zsais/go-gin-prometheus v1.0.3 h1:NIYXItaoGNiyDWXqrIzfQHWcRnen+iwgAw4sX/UieiM=
github.com/zsais/go-gin-prometheus v1.0.3/go.mod h1:avQI7yOKIhpOi4QJxFZdmZb47AEjmS4MTC4Z6PsNmiA=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

23
main.go
View file

@ -3,16 +3,13 @@ package main
import ( import (
"context" "context"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"tg-disc-bot/config" "tg-disc-bot/config"
"tg-disc-bot/discordBot" "tg-disc-bot/discordBot"
"tg-disc-bot/dto" "tg-disc-bot/dto"
"tg-disc-bot/router"
"tg-disc-bot/tgBot" "tg-disc-bot/tgBot"
"time"
) )
func main() { func main() {
@ -41,33 +38,13 @@ func main() {
tgMsgs := tgb.Start(fromDiscord) tgMsgs := tgb.Start(fromDiscord)
dsMsgs := dsb.Start(fromTelegram) dsMsgs := dsb.Start(fromTelegram)
r := router.NewHandler(router.Deps{
Addr: net.JoinHostPort(c.HttpConf.Host, c.HttpConf.Port),
GinMode: c.HttpConf.GinMode,
})
log.Info("App is now running. Press CTRL-C to exit.") log.Info("App is now running. Press CTRL-C to exit.")
errChan := make(chan error, 10)
go func() {
if err = r.Run(); err != nil {
errChan <- err
}
}()
for { for {
select { select {
case sig := <-shutdown: case sig := <-shutdown:
{ {
shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 15*time.Second)
if err = r.Shutdown(shutdownCtx); err != nil {
log.WithError(err).Error("Error shutting down router")
}
log.WithField("type", sig).Info("terminating, close app") log.WithField("type", sig).Info("terminating, close app")
shutdownCancel()
os.Exit(0) os.Exit(0)
} }
case <-ctx.Done(): case <-ctx.Done():

View file

@ -1,67 +0,0 @@
package router
import (
"context"
"errors"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
ginprometheus "github.com/zsais/go-gin-prometheus"
"net/http"
)
type Handler struct {
srv *http.Server
}
type Deps struct {
Addr string
GinMode string
}
const pkgLogHeader string = "Router |"
func NewHandler(deps Deps) *Handler {
engine := gin.Default()
if deps.GinMode == "release" {
gin.SetMode(gin.ReleaseMode)
err := engine.SetTrustedProxies([]string{"172.20.0.0/16"})
if err != nil {
log.WithError(err).Errorf("%v Set proxies failed", pkgLogHeader)
return nil
}
}
engine.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{"msg": "v2"}) })
p := ginprometheus.NewPrometheus("gin")
p.Use(engine)
engine.Use(gin.LoggerWithConfig(gin.LoggerConfig{
Skip: func(c *gin.Context) bool {
return c.Request.URL.Path == "/metrics"
},
}))
srv := http.Server{
Addr: deps.Addr,
Handler: engine,
}
return &Handler{
srv: &srv,
}
}
func (h *Handler) Run() error {
log.Infof("Starting server on %s", h.srv.Addr)
if err := h.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.WithError(err).Errorf("%v ListenAndServe failed", pkgLogHeader)
return err
}
return nil
}
func (h *Handler) Shutdown(ctx context.Context) error {
return h.srv.Shutdown(ctx)
}

View file

@ -1,14 +0,0 @@
#APP
APP_LOG_LEVEL=info
HTTP_HOST=0.0.0.0
HTTP_PORT=8080
GIN_MODE=release
#Telegram
TELEGRAM_TOKEN=
TELEGRAM_CHANNEL_ID=
#Discord
DISCORD_TOKEN=
GUILD_ID=
CHANNEL_ID=

View file

@ -1,40 +1,23 @@
package tgBot package tgBot
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"github.com/go-telegram/bot" "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models" "github.com/go-telegram/bot/models"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io"
"net/http"
"path/filepath"
"sync"
"tg-disc-bot/config" "tg-disc-bot/config"
"tg-disc-bot/dto" "tg-disc-bot/dto"
"time"
) )
type TgBot struct { type TgBot struct {
ctx context.Context ctx context.Context
bot *bot.Bot bot *bot.Bot
chatID int64 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) { func NewTgBot(ctx context.Context, config config.TelegramConfig) (*TgBot, error) {
tgBot := &TgBot{ tgBot := &TgBot{ctx: ctx, chatID: config.ChatID}
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 var err error
tgBot.bot, err = bot.New(config.Token) tgBot.bot, err = bot.New(config.Token)
@ -47,7 +30,24 @@ func NewTgBot(ctx context.Context, config config.TelegramConfig) (*TgBot, error)
func (b *TgBot) Start(fromDiscord chan dto.DiscordDTO) chan dto.TelegramDTO { func (b *TgBot) Start(fromDiscord chan dto.DiscordDTO) chan dto.TelegramDTO {
b.bot.RegisterHandler(bot.HandlerTypeMessageText, "", bot.MatchTypeContains, b.mainHandler) 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
}
})
go func() { go func() {
log.Info("Starting telegram bot...") log.Info("Starting telegram bot...")
@ -57,154 +57,14 @@ func (b *TgBot) Start(fromDiscord chan dto.DiscordDTO) chan dto.TelegramDTO {
go func() { go func() {
for msg := range fromDiscord { for msg := range fromDiscord {
log.WithField("content", msg).Debug("TG | Message from Discord") log.WithField("content", msg).Debug("TG | Message from Discord")
m := fmt.Sprintf("[%s]\n%s", msg.AuthorName, msg.Content)
m := fmt.Sprintf("*\\[ %s \\]*\n%s", msg.AuthorName, msg.Content)
b.bot.SendMessage(b.ctx, &bot.SendMessageParams{ b.bot.SendMessage(b.ctx, &bot.SendMessageParams{
ChatID: b.chatID, ChatID: b.chatID,
Text: m, Text: m,
ParseMode: "",
}) })
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 b.msgChan return 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: b.getName(update),
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: b.getName(update),
Content: content,
Images: &images,
}
msgChan <- msg
}()
}
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"
}
} }