162 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
package telegram
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/NicoNex/echotron/v3"
 | 
						|
	"github.com/labstack/gommon/log"
 | 
						|
	"github.com/ngoduykhanh/wireguard-ui/store"
 | 
						|
)
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
	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("You can only request your configs once per %d minutes", FloodWait),
 | 
						|
					userid,
 | 
						|
					&echotron.MessageOptions{
 | 
						|
						ReplyToMessageID: 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{
 | 
						|
						ReplyToMessageID: 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("this client already got their config less than %d minutes ago", 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")
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func updateFloodWait() {
 | 
						|
	thresholdTS := time.Now().Unix() - 60*int64(FloodWait)
 | 
						|
	for userid, ts := range floodWait {
 | 
						|
		if ts < thresholdTS {
 | 
						|
			delete(floodWait, userid)
 | 
						|
			delete(floodMessageSent, userid)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |