diff options
| author | nistootsin <104831639+nistootsin@users.noreply.github.com> | 2025-05-06 19:27:17 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-06 19:27:17 +0300 |
| commit | d39ccf4b8f77f99d4468580085e9d89e8b5f0b1c (patch) | |
| tree | 90ca065b6aed3171771f9c87ab3b6371eb91f1ea /web/service | |
| parent | 1aed2d8cdcd7b971d3bc055d428a7958a71c9226 (diff) | |
Added 3 new buttons to telegram bot (#2965)
* Add a new button to but : Reset All Clients
* handel translation for `Reset All Clients` button
* refactoring
* add a new button to telegram bot >> `Sorted Traffic Usage Report`
* - refactoring
* add ip limit conifg on new client adding time
Diffstat (limited to 'web/service')
| -rw-r--r-- | web/service/inbound.go | 35 | ||||
| -rw-r--r-- | web/service/tgbot.go | 201 |
2 files changed, 233 insertions, 3 deletions
diff --git a/web/service/inbound.go b/web/service/inbound.go index fce01634..f2646dbb 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -3,6 +3,7 @@ package service import ( "encoding/json" "fmt" + "sort" "strconv" "strings" "time" @@ -2025,3 +2026,37 @@ func (s *InboundService) MigrateDB() { func (s *InboundService) GetOnlineClients() []string { return p.GetOnlineClients() } + +func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, []string, error) { + db := database.GetDB() + + // Step 1: Get ClientTraffic records for emails in the input list + var clients []xray.ClientTraffic + err := db.Where("email IN ?", emails).Find(&clients).Error + if err != nil && err != gorm.ErrRecordNotFound { + return nil, nil, err + } + + // Step 2: Sort clients by (Up + Down) descending + sort.Slice(clients, func(i, j int) bool { + return (clients[i].Up + clients[i].Down) > (clients[j].Up + clients[j].Down) + }) + + // Step 3: Extract sorted valid emails and track found ones + validEmails := make([]string, 0, len(clients)) + found := make(map[string]bool) + for _, client := range clients { + validEmails = append(validEmails, client.Email) + found[client.Email] = true + } + + // Step 4: Identify emails that were not found in the database + extraEmails := make([]string, 0) + for _, email := range emails { + if !found[email] { + extraEmails = append(extraEmails, email) + } + } + + return validEmails, extraEmails, nil +} diff --git a/web/service/tgbot.go b/web/service/tgbot.go index 2c572192..2f36697e 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -1069,6 +1069,83 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool } t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation")) t.searchClient(chatId, email, callbackQuery.Message.GetMessageID()) + case "add_client_ip_limit_c": + if len(dataArray) == 2 { + count, _ := strconv.Atoi(dataArray[1]) + client_LimitIP = count + } + + messageId := callbackQuery.Message.GetMessageID() + inbound, err := t.inboundService.GetInbound(receiver_inbound_ID) + if err != nil { + t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error()) + return + } + message_text, err := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + + t.addClient(chatId, message_text, messageId) + t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation")) + case "add_client_ip_limit_in": + if len(dataArray) >= 2 { + oldInputNumber, err := strconv.Atoi(dataArray[1]) + inputNumber := oldInputNumber + if err == nil { + if len(dataArray) == 3 { + num, err := strconv.Atoi(dataArray[2]) + if err == nil { + if num == -2 { + inputNumber = 0 + } else if num == -1 { + if inputNumber > 0 { + inputNumber = (inputNumber / 10) + } + } else { + inputNumber = (inputNumber * 10) + num + } + } + if inputNumber == oldInputNumber { + t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation")) + return + } + if inputNumber >= 999999 { + t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation")) + return + } + } + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_ip_limit")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmNumber", "Num=="+strconv.Itoa(inputNumber))).WithCallbackData(t.encodeQuery("add_client_ip_limit_c "+strconv.Itoa(inputNumber))), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 1")), + tu.InlineKeyboardButton("2").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 2")), + tu.InlineKeyboardButton("3").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 3")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("4").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 4")), + tu.InlineKeyboardButton("5").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 5")), + tu.InlineKeyboardButton("6").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 6")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("7").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 7")), + tu.InlineKeyboardButton("8").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 8")), + tu.InlineKeyboardButton("9").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 9")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("🔄").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" -2")), + tu.InlineKeyboardButton("0").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" 0")), + tu.InlineKeyboardButton("⬅️").WithCallbackData(t.encodeQuery("add_client_ip_limit_in "+strconv.Itoa(inputNumber)+" -1")), + ), + ) + t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard) + return + } + } + t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation")) + t.searchClient(chatId, email, callbackQuery.Message.GetMessageID()) case "clear_ips": inlineKeyboard := tu.InlineKeyboard( tu.InlineKeyboardRow( @@ -1382,6 +1459,35 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool ), ) t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard) + case "add_client_ch_default_ip_limit": + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_ip_limit")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("add_client_ip_limit_c 0")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("add_client_ip_limit_in 0")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 1")), + tu.InlineKeyboardButton("2").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 2")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("3").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 3")), + tu.InlineKeyboardButton("4").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 4")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("5").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 5")), + tu.InlineKeyboardButton("6").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 6")), + tu.InlineKeyboardButton("7").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 7")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("8").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 8")), + tu.InlineKeyboardButton("9").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 9")), + tu.InlineKeyboardButton("10").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 10")), + ), + ) + t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard) case "add_client_default_info": t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) t.SendMsgToTgbotDeleteAfter(chatId, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove()) @@ -1403,6 +1509,16 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool message_text, err := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) t.addClient(chatId, message_text, messageId) t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+client_Email)) + case "add_client_default_ip_limit": + messageId := callbackQuery.Message.GetMessageID() + inbound, err := t.inboundService.GetInbound(receiver_inbound_ID) + if err != nil { + t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error()) + return + } + message_text, err := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(chatId, message_text, messageId) + t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+client_Email)) case "add_client_submit_disable": client_Enable = false _, err := t.SubmitAddClient() @@ -1423,6 +1539,71 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.successfulOperation"), tu.ReplyKeyboardRemove()) } + case "reset_all_traffics_cancel": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + t.SendMsgToTgbotDeleteAfter(chatId, t.I18nBot("tgbot.messages.cancel"), 1, tu.ReplyKeyboardRemove()) + case "reset_all_traffics": + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancelReset")).WithCallbackData(t.encodeQuery("reset_all_traffics_cancel")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmResetTraffic")).WithCallbackData(t.encodeQuery("reset_all_traffics_c")), + ), + ) + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.AreYouSure"), inlineKeyboard) + case "reset_all_traffics_c": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + emails, err := t.inboundService.getAllEmails() + if err != nil { + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove()) + return + } + + for _, email := range emails { + err := t.inboundService.ResetClientTrafficByEmail(email) + if err == nil { + msg := t.I18nBot("tgbot.messages.SuccessResetTraffic", "ClientEmail=="+email) + t.SendMsgToTgbot(chatId, msg, tu.ReplyKeyboardRemove()) + } else { + msg := t.I18nBot("tgbot.messages.FailedResetTraffic", "ClientEmail=="+email, "ErrorMessage=="+err.Error()) + t.SendMsgToTgbot(chatId, msg, tu.ReplyKeyboardRemove()) + } + } + + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.FinishProcess"), tu.ReplyKeyboardRemove()) + case "get_sorted_traffic_usage_report": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + emails, err := t.inboundService.getAllEmails() + + if err != nil { + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove()) + return + } + valid_emails, extra_emails, err := t.inboundService.FilterAndSortClientEmails(emails) + + for _, valid_emails := range valid_emails { + traffic, err := t.inboundService.GetClientTrafficByEmail(valid_emails) + if err != nil { + logger.Warning(err) + msg := t.I18nBot("tgbot.wentWrong") + t.SendMsgToTgbot(chatId, msg) + continue + } + if traffic == nil { + msg := t.I18nBot("tgbot.noResult") + t.SendMsgToTgbot(chatId, msg) + continue + } + + output := t.clientInfoMsg(traffic, false, false, false, false, true, false) + t.SendMsgToTgbot(chatId, output, tu.ReplyKeyboardRemove()) + } + for _, extra_emails := range extra_emails { + msg := fmt.Sprintf("📧 %s\n%s", extra_emails, t.I18nBot("tgbot.noResult")) + t.SendMsgToTgbot(chatId, msg, tu.ReplyKeyboardRemove()) + + } } } @@ -1451,15 +1632,22 @@ func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol mo traffic_value = common.FormatTraffic(client_TotalGB) } + ip_limit := "" + if client_LimitIP == 0 { + ip_limit = "♾️ Unlimited(Reset)" + } else { + ip_limit = fmt.Sprint(client_LimitIP) + } + switch protocol { case model.VMESS, model.VLESS: - message = t.I18nBot("tgbot.messages.inbound_client_data_id", "InboundRemark=="+inbound_remark, "ClientId=="+client_Id, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "ClientComment=="+client_Comment) + message = t.I18nBot("tgbot.messages.inbound_client_data_id", "InboundRemark=="+inbound_remark, "ClientId=="+client_Id, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "IpLimit=="+ip_limit, "ClientComment=="+client_Comment) case model.Trojan: - message = t.I18nBot("tgbot.messages.inbound_client_data_pass", "InboundRemark=="+inbound_remark, "ClientPass=="+client_TrPassword, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "ClientComment=="+client_Comment) + message = t.I18nBot("tgbot.messages.inbound_client_data_pass", "InboundRemark=="+inbound_remark, "ClientPass=="+client_TrPassword, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "IpLimit=="+ip_limit, "ClientComment=="+client_Comment) case model.Shadowsocks: - message = t.I18nBot("tgbot.messages.inbound_client_data_pass", "InboundRemark=="+inbound_remark, "ClientPass=="+client_ShPassword, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "ClientComment=="+client_Comment) + message = t.I18nBot("tgbot.messages.inbound_client_data_pass", "InboundRemark=="+inbound_remark, "ClientPass=="+client_ShPassword, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "IpLimit=="+ip_limit, "ClientComment=="+client_Comment) default: return "", errors.New("unknown protocol") @@ -1576,7 +1764,11 @@ func checkAdmin(tgId int64) bool { func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) { numericKeyboard := tu.InlineKeyboard( tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.SortedTrafficUsageReport")).WithCallbackData(t.encodeQuery("get_sorted_traffic_usage_report")), + ), + tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.serverUsage")).WithCallbackData(t.encodeQuery("get_usage")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.ResetAllTraffics")).WithCallbackData(t.encodeQuery("reset_all_traffics")), ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.dbBackup")).WithCallbackData(t.encodeQuery("get_backup")), @@ -2223,6 +2415,7 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) { ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_comment")).WithCallbackData("add_client_ch_default_comment"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.ipLimit")).WithCallbackData("add_client_ch_default_ip_limit"), ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"), @@ -2249,6 +2442,7 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) { ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_comment")).WithCallbackData("add_client_ch_default_comment"), + tu.InlineKeyboardButton("ip limit").WithCallbackData("add_client_ch_default_ip_limit"), ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"), @@ -2275,6 +2469,7 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) { ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_comment")).WithCallbackData("add_client_ch_default_comment"), + tu.InlineKeyboardButton("ip limit").WithCallbackData("add_client_ch_default_ip_limit"), ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"), |
