From 6ced549deaecb42b9bb93ea9efcb4c1bbaabe8a4 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Sat, 20 Sep 2025 09:35:50 +0200 Subject: docs: add comments for all functions --- web/service/tgbot.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) (limited to 'web/service/tgbot.go') diff --git a/web/service/tgbot.go b/web/service/tgbot.go index 44e4af28..e575bb28 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -65,14 +65,18 @@ var ( var userStates = make(map[int64]string) +// LoginStatus represents the result of a login attempt. type LoginStatus byte +// Login status constants const ( - LoginSuccess LoginStatus = 1 - LoginFail LoginStatus = 0 - EmptyTelegramUserID = int64(0) + LoginSuccess LoginStatus = 1 // Login was successful + LoginFail LoginStatus = 0 // Login failed + EmptyTelegramUserID = int64(0) // Default value for empty Telegram user ID ) +// Tgbot provides business logic for Telegram bot integration. +// It handles bot commands, user interactions, and status reporting via Telegram. type Tgbot struct { inboundService InboundService settingService SettingService @@ -81,18 +85,22 @@ type Tgbot struct { lastStatus *Status } +// NewTgbot creates a new Tgbot instance. func (t *Tgbot) NewTgbot() *Tgbot { return new(Tgbot) } +// I18nBot retrieves a localized message for the bot interface. func (t *Tgbot) I18nBot(name string, params ...string) string { return locale.I18n(locale.Bot, name, params...) } +// GetHashStorage returns the hash storage instance for callback queries. func (t *Tgbot) GetHashStorage() *global.HashStorage { return hashStorage } +// Start initializes and starts the Telegram bot with the provided translation files. func (t *Tgbot) Start(i18nFS embed.FS) error { // Initialize localizer err := locale.InitLocalizer(i18nFS, &t.settingService) @@ -173,6 +181,7 @@ func (t *Tgbot) Start(i18nFS embed.FS) error { return nil } +// NewBot creates a new Telegram bot instance with optional proxy and API server settings. func (t *Tgbot) NewBot(token string, proxyUrl string, apiServerUrl string) (*telego.Bot, error) { if proxyUrl == "" && apiServerUrl == "" { return telego.NewBot(token) @@ -209,10 +218,12 @@ func (t *Tgbot) NewBot(token string, proxyUrl string, apiServerUrl string) (*tel return telego.NewBot(token, telego.WithAPIServer(apiServerUrl)) } +// IsRunning checks if the Telegram bot is currently running. func (t *Tgbot) IsRunning() bool { return isRunning } +// SetHostname sets the hostname for the bot. func (t *Tgbot) SetHostname() { host, err := os.Hostname() if err != nil { @@ -223,6 +234,7 @@ func (t *Tgbot) SetHostname() { hostname = host } +// Stop stops the Telegram bot and cleans up resources. func (t *Tgbot) Stop() { if botHandler != nil { botHandler.Stop() @@ -232,6 +244,7 @@ func (t *Tgbot) Stop() { adminIds = nil } +// encodeQuery encodes the query string if it's longer than 64 characters. func (t *Tgbot) encodeQuery(query string) string { // NOTE: we only need to hash for more than 64 chars if len(query) <= 64 { @@ -241,6 +254,7 @@ func (t *Tgbot) encodeQuery(query string) string { return hashStorage.SaveHash(query) } +// decodeQuery decodes a hashed query string back to its original form. func (t *Tgbot) decodeQuery(query string) (string, error) { if !hashStorage.IsMD5(query) { return query, nil @@ -254,6 +268,7 @@ func (t *Tgbot) decodeQuery(query string) (string, error) { return decoded, nil } +// OnReceive starts the message receiving loop for the Telegram bot. func (t *Tgbot) OnReceive() { params := telego.GetUpdatesParams{ Timeout: 10, @@ -430,6 +445,7 @@ func (t *Tgbot) OnReceive() { botHandler.Start() } +// answerCommand processes incoming command messages from Telegram users. func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin bool) { msg, onlyMessage := "", false @@ -505,7 +521,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo } } -// Helper function to send the message based on onlyMessage flag. +// sendResponse sends the response message based on the onlyMessage flag. func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool) { if onlyMessage { t.SendMsgToTgbot(chatId, msg) @@ -514,6 +530,7 @@ func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool } } +// randomLowerAndNum generates a random string of lowercase letters and numbers. func (t *Tgbot) randomLowerAndNum(length int) string { charset := "abcdefghijklmnopqrstuvwxyz0123456789" bytes := make([]byte, length) @@ -524,6 +541,7 @@ func (t *Tgbot) randomLowerAndNum(length int) string { return string(bytes) } +// randomShadowSocksPassword generates a random password for Shadowsocks. func (t *Tgbot) randomShadowSocksPassword() string { array := make([]byte, 32) _, err := rand.Read(array) @@ -533,6 +551,7 @@ func (t *Tgbot) randomShadowSocksPassword() string { return base64.StdEncoding.EncodeToString(array) } +// answerCallback processes callback queries from inline keyboards. func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) { chatId := callbackQuery.Message.GetChat().ID @@ -1815,6 +1834,7 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool } } +// BuildInboundClientDataMessage builds a message with client data for the given inbound and protocol. func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol model.Protocol) (string, error) { var message string @@ -1864,6 +1884,7 @@ func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol mo return message, nil } +// BuildJSONForProtocol builds a JSON string for the given protocol with client data. func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string, error) { var jsonString string @@ -1942,6 +1963,7 @@ func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string, error) { return jsonString, nil } +// SubmitAddClient submits the client addition request to the inbound service. func (t *Tgbot) SubmitAddClient() (bool, error) { inbound, err := t.inboundService.GetInbound(receiver_inbound_ID) @@ -1964,6 +1986,7 @@ func (t *Tgbot) SubmitAddClient() (bool, error) { return t.inboundService.AddInboundClient(newInbound) } +// checkAdmin checks if the given Telegram ID is an admin. func checkAdmin(tgId int64) bool { for _, adminId := range adminIds { if adminId == tgId { @@ -1973,6 +1996,7 @@ func checkAdmin(tgId int64) bool { return false } +// SendAnswer sends a response message with an inline keyboard to the specified chat. func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) { numericKeyboard := tu.InlineKeyboard( tu.InlineKeyboardRow( @@ -2028,6 +2052,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) { t.SendMsgToTgbot(chatId, msg, ReplyMarkup) } +// SendMsgToTgbot sends a message to the Telegram bot with optional reply markup. func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.ReplyMarkup) { if !isRunning { return @@ -2143,6 +2168,7 @@ func (t *Tgbot) buildSubscriptionURLs(email string) (string, string, error) { return subURL, subJsonURL, nil } +// sendClientSubLinks sends the subscription links for the client to the chat. func (t *Tgbot) sendClientSubLinks(chatId int64, email string) { subURL, subJsonURL, err := t.buildSubscriptionURLs(email) if err != nil { @@ -2338,6 +2364,7 @@ func (t *Tgbot) sendClientQRLinks(chatId int64, email string) { } } +// SendMsgToTgbotAdmins sends a message to all admin Telegram chats. func (t *Tgbot) SendMsgToTgbotAdmins(msg string, replyMarkup ...telego.ReplyMarkup) { if len(replyMarkup) > 0 { for _, adminId := range adminIds { @@ -2350,6 +2377,7 @@ func (t *Tgbot) SendMsgToTgbotAdmins(msg string, replyMarkup ...telego.ReplyMark } } +// SendReport sends a periodic report to admin chats. func (t *Tgbot) SendReport() { runTime, err := t.settingService.GetTgbotRuntime() if err == nil && len(runTime) > 0 { @@ -2371,6 +2399,7 @@ func (t *Tgbot) SendReport() { } } +// SendBackupToAdmins sends a database backup to admin chats. func (t *Tgbot) SendBackupToAdmins() { if !t.IsRunning() { return @@ -2380,6 +2409,7 @@ func (t *Tgbot) SendBackupToAdmins() { } } +// sendExhaustedToAdmins sends notifications about exhausted clients to admins. func (t *Tgbot) sendExhaustedToAdmins() { if !t.IsRunning() { return @@ -2389,6 +2419,7 @@ func (t *Tgbot) sendExhaustedToAdmins() { } } +// getServerUsage retrieves and formats server usage information. func (t *Tgbot) getServerUsage(chatId int64, messageID ...int) string { info := t.prepareServerUsageInfo() @@ -2410,6 +2441,7 @@ func (t *Tgbot) sendServerUsage() string { return info } +// prepareServerUsageInfo prepares the server usage information string. func (t *Tgbot) prepareServerUsageInfo() string { info, ipv4, ipv6 := "", "", "" @@ -2459,6 +2491,7 @@ func (t *Tgbot) prepareServerUsageInfo() string { return info } +// UserLoginNotify sends a notification about user login attempts to admins. func (t *Tgbot) UserLoginNotify(username string, password string, ip string, time string, status LoginStatus) { if !t.IsRunning() { return @@ -2490,6 +2523,7 @@ func (t *Tgbot) UserLoginNotify(username string, password string, ip string, tim t.SendMsgToTgbotAdmins(msg) } +// getInboundUsages retrieves and formats inbound usage information. func (t *Tgbot) getInboundUsages() string { info := "" // get traffic @@ -2515,6 +2549,8 @@ func (t *Tgbot) getInboundUsages() string { } return info } + +// getInbounds creates an inline keyboard with all inbounds. func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) { inbounds, err := t.inboundService.GetAllInbounds() if err != nil { @@ -2546,8 +2582,7 @@ func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) { return keyboard, nil } -// getInboundsFor builds an inline keyboard of inbounds where each button leads to a custom next action -// nextAction should be one of: get_clients_for_sub|get_clients_for_individual|get_clients_for_qr +// getInboundsFor builds an inline keyboard of inbounds for a custom next action. func (t *Tgbot) getInboundsFor(nextAction string) (*telego.InlineKeyboardMarkup, error) { inbounds, err := t.inboundService.GetAllInbounds() if err != nil { @@ -2614,6 +2649,7 @@ func (t *Tgbot) getInboundClientsFor(inboundID int, action string) (*telego.Inli return keyboard, nil } +// getInboundsAddClient creates an inline keyboard for adding clients to inbounds. func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) { inbounds, err := t.inboundService.GetAllInbounds() if err != nil { @@ -2656,6 +2692,7 @@ func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) { return keyboard, nil } +// getInboundClients creates an inline keyboard with clients of a specific inbound. func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error) { inbound, err := t.inboundService.GetInbound(id) if err != nil { @@ -2690,6 +2727,7 @@ func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error) return keyboard, nil } +// clientInfoMsg formats client information message based on traffic and flags. func (t *Tgbot) clientInfoMsg( traffic *xray.ClientTraffic, printEnabled bool, @@ -2796,6 +2834,7 @@ func (t *Tgbot) clientInfoMsg( return output } +// getClientUsage retrieves and sends client usage information to the chat. func (t *Tgbot) getClientUsage(chatId int64, tgUserID int64, email ...string) { traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID) if err != nil { @@ -2838,6 +2877,7 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserID int64, email ...string) { t.SendAnswer(chatId, output, false) } +// searchClientIps searches and sends client IP addresses for the given email. func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) { ips, err := t.inboundService.GetInboundClientIps(email) if err != nil || len(ips) == 0 { @@ -2865,6 +2905,7 @@ func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) { } } +// clientTelegramUserInfo retrieves and sends Telegram user info for the client. func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ...int) { traffic, client, err := t.inboundService.GetClientByEmail(email) if err != nil { @@ -2917,6 +2958,7 @@ func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ... } } +// searchClient searches for a client by email and sends the information. func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) { traffic, err := t.inboundService.GetClientTrafficByEmail(email) if err != nil { @@ -2962,6 +3004,7 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) { } } +// addClient handles the process of adding a new client to an inbound. func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) { inbound, err := t.inboundService.GetInbound(receiver_inbound_ID) if err != nil { @@ -3058,6 +3101,7 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) { } +// searchInbound searches for inbounds by remark and sends the results. func (t *Tgbot) searchInbound(chatId int64, remark string) { inbounds, err := t.inboundService.SearchInbounds(remark) if err != nil { @@ -3095,6 +3139,7 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) { } } +// getExhausted retrieves and sends information about exhausted clients. func (t *Tgbot) getExhausted(chatId int64) { trDiff := int64(0) exDiff := int64(0) @@ -3191,6 +3236,7 @@ func (t *Tgbot) getExhausted(chatId int64) { } } +// notifyExhausted sends notifications for exhausted clients. func (t *Tgbot) notifyExhausted() { trDiff := int64(0) exDiff := int64(0) @@ -3262,6 +3308,7 @@ func (t *Tgbot) notifyExhausted() { } } +// int64Contains checks if an int64 slice contains a specific item. func int64Contains(slice []int64, item int64) bool { for _, s := range slice { if s == item { @@ -3271,6 +3318,7 @@ func int64Contains(slice []int64, item int64) bool { return false } +// onlineClients retrieves and sends information about online clients. func (t *Tgbot) onlineClients(chatId int64, messageID ...int) { if !p.IsRunning() { return @@ -3305,6 +3353,7 @@ func (t *Tgbot) onlineClients(chatId int64, messageID ...int) { } } +// sendBackup sends a backup of the database and configuration files. func (t *Tgbot) sendBackup(chatId int64) { output := t.I18nBot("tgbot.messages.backupTime", "Time=="+time.Now().Format("2006-01-02 15:04:05")) t.SendMsgToTgbot(chatId, output) @@ -3344,6 +3393,7 @@ func (t *Tgbot) sendBackup(chatId int64) { } } +// sendBanLogs sends the ban logs to the specified chat. func (t *Tgbot) sendBanLogs(chatId int64, dt bool) { if dt { output := t.I18nBot("tgbot.messages.datetime", "DateTime=="+time.Now().Format("2006-01-02 15:04:05")) @@ -3393,6 +3443,7 @@ func (t *Tgbot) sendBanLogs(chatId int64, dt bool) { } } +// sendCallbackAnswerTgBot answers a callback query with a message. func (t *Tgbot) sendCallbackAnswerTgBot(id string, message string) { params := telego.AnswerCallbackQueryParams{ CallbackQueryID: id, @@ -3403,6 +3454,7 @@ func (t *Tgbot) sendCallbackAnswerTgBot(id string, message string) { } } +// editMessageCallbackTgBot edits the reply markup of a message. func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, inlineKeyboard *telego.InlineKeyboardMarkup) { params := telego.EditMessageReplyMarkupParams{ ChatID: tu.ID(chatId), @@ -3414,6 +3466,7 @@ func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, inlineKeyb } } +// editMessageTgBot edits the text and reply markup of a message. func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlineKeyboard ...*telego.InlineKeyboardMarkup) { params := telego.EditMessageTextParams{ ChatID: tu.ID(chatId), @@ -3429,6 +3482,7 @@ func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlin } } +// SendMsgToTgbotDeleteAfter sends a message and deletes it after a specified delay. func (t *Tgbot) SendMsgToTgbotDeleteAfter(chatId int64, msg string, delayInSeconds int, replyMarkup ...telego.ReplyMarkup) { // Determine if replyMarkup was passed; otherwise, set it to nil var replyMarkupParam telego.ReplyMarkup @@ -3455,6 +3509,7 @@ func (t *Tgbot) SendMsgToTgbotDeleteAfter(chatId int64, msg string, delayInSecon }() } +// deleteMessageTgBot deletes a message from the chat. func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) { params := telego.DeleteMessageParams{ ChatID: tu.ID(chatId), @@ -3467,6 +3522,7 @@ func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) { } } +// isSingleWord checks if the text contains only a single word. func (t *Tgbot) isSingleWord(text string) bool { text = strings.TrimSpace(text) re := regexp.MustCompile(`\s+`) -- cgit v1.2.3