Files
wireguard-ui/telegram/bot.go
Артём Грачёв 1b833124c6
Some checks failed
Build container images / build-image (push) Has been cancelled
Lint / Lint (push) Has been cancelled
update packages
updates data and links
2026-03-29 02:56:35 +03:00

189 lines
4.9 KiB
Go

package telegram
import (
"fmt"
"sync"
"time"
"github.com/NicoNex/echotron/v3"
"github.com/gra4art/wireguard-ui/store"
"github.com/labstack/gommon/log"
"github.com/skip2/go-qrcode"
)
type SendRequestedConfigsToTelegram func(db store.IStore, userid int64) []string
type TgBotInitDependencies struct {
DB store.IStore
SendRequestedConfigsToTelegram SendRequestedConfigsToTelegram
}
var (
Token string
AllowConfRequest bool
FloodWait int
LogLevel log.Lvl
MTProxyLink string // ДОБАВИЛИ НАШУ ССЫЛКУ
Bot *echotron.API
BotMutex sync.RWMutex
floodWait = make(map[int64]int64)
floodMessageSent = make(map[int64]struct{})
)
func Start(initDeps TgBotInitDependencies) (err error) {
ticker := time.NewTicker(time.Minute)
defer func() {
if err != nil {
BotMutex.Lock()
Bot = nil
BotMutex.Unlock()
ticker.Stop()
}
if r := recover(); r != nil {
err = fmt.Errorf("[PANIC] recovered from panic: %v", r)
}
}()
token := Token
if token == "" || len(token) < 30 {
return
}
bot := echotron.NewAPI(token)
res, err := bot.GetMe()
if !res.Ok || err != nil {
log.Warnf("[Telegram] Unable to connect to bot.\n%v\n%v", res.Description, err)
return
}
BotMutex.Lock()
Bot = &bot
BotMutex.Unlock()
if LogLevel <= log.INFO {
fmt.Printf("[Telegram] Authorized as %s\n", res.Result.Username)
}
go func() {
for range ticker.C {
updateFloodWait()
}
}()
if !AllowConfRequest {
return
}
updatesChan := echotron.PollingUpdatesOptions(token, false, echotron.UpdateOptions{AllowedUpdates: []echotron.UpdateType{echotron.MessageUpdate}})
for update := range updatesChan {
if update.Message != nil {
userid := update.Message.Chat.ID
if _, wait := floodWait[userid]; wait {
if _, notified := floodMessageSent[userid]; notified {
continue
}
floodMessageSent[userid] = struct{}{}
_, err := bot.SendMessage(
fmt.Sprintf("Подождите %d секунд, защита от флуда", FloodWait),
userid,
&echotron.MessageOptions{
ReplyParameters: echotron.ReplyParameters{
MessageID: update.Message.ID,
},
})
if err != nil {
log.Errorf("Failed to send telegram message. Error %v", err)
}
continue
}
floodWait[userid] = time.Now().Unix()
failed := initDeps.SendRequestedConfigsToTelegram(initDeps.DB, userid)
if len(failed) > 0 {
messageText := "Failed to send configs:\n"
for _, f := range failed {
messageText += f + "\n"
}
_, err := bot.SendMessage(
messageText,
userid,
&echotron.MessageOptions{
ReplyParameters: echotron.ReplyParameters{
MessageID: update.Message.ID,
},
})
if err != nil {
log.Errorf("Failed to send telegram message. Error %v", err)
}
}
}
}
return err
}
func SendConfig(userid int64, clientName string, confData, qrData []byte, ignoreFloodWait bool) error {
BotMutex.RLock()
defer BotMutex.RUnlock()
if Bot == nil {
return fmt.Errorf("telegram bot is not configured or not available")
}
if _, wait := floodWait[userid]; wait && !ignoreFloodWait {
return fmt.Errorf("Этот клиент будет готов получить конфиг через %d секунд", FloodWait)
}
if !ignoreFloodWait {
floodWait[userid] = time.Now().Unix()
}
qrAttachment := echotron.NewInputFileBytes("qr.png", qrData)
_, err := Bot.SendPhoto(qrAttachment, userid, &echotron.PhotoOptions{Caption: clientName})
if err != nil {
log.Error(err)
return fmt.Errorf("unable to send qr picture")
}
confAttachment := echotron.NewInputFileBytes(clientName+".conf", confData)
_, err = Bot.SendDocument(confAttachment, userid, nil)
if err != nil {
log.Error(err)
return fmt.Errorf("unable to send conf file")
}
if MTProxyLink != "" {
// Генерируем QR-код (размер 512x512, средний уровень коррекции ошибок)
mtQRData, err := qrcode.Encode(MTProxyLink, qrcode.Medium, 512)
if err == nil {
// Превращаем байты в картинку для Телеграма
mtAttachment := echotron.NewInputFileBytes("mtproxy.png", mtQRData)
// Формируем красивое сообщение (можешь поменять текст)
caption := "🚀 Твой MTProxy для обхода блокировок: \n\n" + MTProxyLink
// Отправляем!
_, err = Bot.SendPhoto(mtAttachment, userid, &echotron.PhotoOptions{Caption: caption})
if err != nil {
log.Errorf("Failed to send MTProxy to user %d: %v", userid, err)
}
} else {
log.Errorf("Failed to generate MTProxy QR: %v", err)
}
}
// --- КОНЕЦ НАШЕГО БЛОКА ---
return nil
}
func updateFloodWait() {
thresholdTS := time.Now().Unix() - int64(FloodWait)
for userid, ts := range floodWait {
if ts < thresholdTS {
delete(floodWait, userid)
delete(floodMessageSent, userid)
}
}
}