From 6b2aee7a4138b15d529de6e7d3673812bcbc24ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=83=E9=9A=A8=E7=B7=A3=E5=8B=95?= <88259403+xeefei@users.noreply.github.com> Date: Sat, 12 Jul 2025 04:40:46 +0800 Subject: [PATCH] v2.6.2 v2.6.2 --- web/service/inbound.go | 211 +++++--- web/service/setting.go | 182 ++++--- web/service/tgbot.go | 1151 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 1338 insertions(+), 206 deletions(-) diff --git a/web/service/inbound.go b/web/service/inbound.go index a76c5e2c..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" @@ -413,13 +414,13 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) { return false, err } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(data.Settings), &settings) if err != nil { return false, err } - interfaceClients := settings["clients"].([]interface{}) + interfaceClients := settings["clients"].([]any) existEmail, err := s.checkEmailsExistForClients(clients) if err != nil { return false, err @@ -450,13 +451,13 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) { } } - var oldSettings map[string]interface{} + var oldSettings map[string]any err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) if err != nil { return false, err } - oldClients := oldSettings["clients"].([]interface{}) + oldClients := oldSettings["clients"].([]any) oldClients = append(oldClients, interfaceClients...) oldSettings["clients"] = oldClients @@ -489,7 +490,7 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) { if oldInbound.Protocol == "shadowsocks" { cipher = oldSettings["method"].(string) } - err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ + err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]any{ "email": client.Email, "id": client.ID, "security": client.Security, @@ -519,7 +520,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, logger.Error("Load Old Data Error") return false, err } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(oldInbound.Settings), &settings) if err != nil { return false, err @@ -534,11 +535,11 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, client_key = "email" } - interfaceClients := settings["clients"].([]interface{}) - var newClients []interface{} + interfaceClients := settings["clients"].([]any) + var newClients []any needApiDel := false for _, client := range interfaceClients { - c := client.(map[string]interface{}) + c := client.(map[string]any) c_id := c[client_key].(string) if c_id == clientId { email, _ = c["email"].(string) @@ -588,8 +589,12 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, logger.Debug("Client deleted by api:", email) needRestart = false } else { - logger.Debug("Unable to del client by api:", err1) - needRestart = true + if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) { + logger.Debug("User is already deleted. Nothing to do more...") + } else { + logger.Debug("Error in deleting client by api:", err1) + needRestart = true + } } s.xrayApi.Close() } @@ -603,13 +608,13 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin return false, err } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(data.Settings), &settings) if err != nil { return false, err } - interfaceClients := settings["clients"].([]interface{}) + interfaceClients := settings["clients"].([]any) oldInbound, err := s.GetInbound(data.Id) if err != nil { @@ -623,7 +628,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin oldEmail := "" newClientId := "" - clientIndex := 0 + clientIndex := -1 for index, oldClient := range oldClients { oldClientId := "" if oldInbound.Protocol == "trojan" { @@ -644,7 +649,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin } // Validate new client ID - if newClientId == "" { + if newClientId == "" || clientIndex == -1 { return false, common.NewError("empty client ID") } @@ -658,12 +663,12 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin } } - var oldSettings map[string]interface{} + var oldSettings map[string]any err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) if err != nil { return false, err } - settingsClients := oldSettings["clients"].([]interface{}) + settingsClients := oldSettings["clients"].([]any) settingsClients[clientIndex] = interfaceClients[0] oldSettings["clients"] = settingsClients @@ -713,10 +718,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin if oldClients[clientIndex].Enable { err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) if err1 == nil { - logger.Debug("Old client deleted by api:", clients[0].Email) + logger.Debug("Old client deleted by api:", oldEmail) } else { - logger.Debug("Error in deleting client by api:", err1) - needRestart = true + if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", oldEmail)) { + logger.Debug("User is already deleted. Nothing to do more...") + } else { + logger.Debug("Error in deleting client by api:", err1) + needRestart = true + } } } if clients[0].Enable { @@ -724,7 +733,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin if oldInbound.Protocol == "shadowsocks" { cipher = oldSettings["method"].(string) } - err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ + err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]any{ "email": clients[0].Email, "id": clients[0].ID, "security": clients[0].Security, @@ -801,7 +810,7 @@ func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xray.Traffic for _, traffic := range traffics { if traffic.IsInbound { err = tx.Model(&model.Inbound{}).Where("tag = ?", traffic.Tag). - Updates(map[string]interface{}{ + Updates(map[string]any{ "up": gorm.Expr("up + ?", traffic.Up), "down": gorm.Expr("down + ?", traffic.Down), }).Error @@ -885,13 +894,13 @@ func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.Cl return nil, err } for inbound_index := range inbounds { - settings := map[string]interface{}{} + settings := map[string]any{} json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings) - clients, ok := settings["clients"].([]interface{}) + clients, ok := settings["clients"].([]any) if ok { - var newClients []interface{} + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) for traffic_index := range dbClientTraffics { if dbClientTraffics[traffic_index].ExpiryTime < 0 && c["email"] == dbClientTraffics[traffic_index].Email { oldExpiryTime := c["expiryTime"].(float64) @@ -901,7 +910,7 @@ func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.Cl break } } - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } settings["clients"] = newClients modifiedSettings, err := json.MarshalIndent(settings, "", " ") @@ -943,7 +952,7 @@ func (s *InboundService) autoRenewClients(tx *gorm.DB) (bool, int64, error) { var clientsToAdd []struct { protocol string tag string - client map[string]interface{} + client map[string]any } for _, traffic := range traffics { @@ -954,11 +963,11 @@ func (s *InboundService) autoRenewClients(tx *gorm.DB) (bool, int64, error) { return false, 0, err } for inbound_index := range inbounds { - settings := map[string]interface{}{} + settings := map[string]any{} json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings) - clients := settings["clients"].([]interface{}) + clients := settings["clients"].([]any) for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) for traffic_index, traffic := range traffics { if traffic.Email == c["email"].(string) { newExpiryTime := traffic.ExpiryTime @@ -975,14 +984,14 @@ func (s *InboundService) autoRenewClients(tx *gorm.DB) (bool, int64, error) { struct { protocol string tag string - client map[string]interface{} + client map[string]any }{ protocol: string(inbounds[inbound_index].Protocol), tag: inbounds[inbound_index].Tag, client: c, }) } - clients[client_index] = interface{}(c) + clients[client_index] = any(c) break } } @@ -1037,12 +1046,8 @@ func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error if err1 == nil { logger.Debug("Inbound disabled by api:", tag) } else { - if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", tag)) { - logger.Debug("User is already disabled. Nothing to do more...") - } else { - logger.Debug("Error in disabling client by api:", err1) - needRestart = true - } + logger.Debug("Error in disabling inbound by api:", err1) + needRestart = true } } s.xrayApi.Close() @@ -1143,7 +1148,7 @@ func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, client *model.Client) error { result := tx.Model(xray.ClientTraffic{}). Where("email = ?", email). - Updates(map[string]interface{}{ + Updates(map[string]any{ "enable": true, "email": client.Email, "total": client.TotalGB, @@ -1254,18 +1259,18 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId int64) (boo return false, common.NewError("Client Not Found For Email:", clientEmail) } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(inbound.Settings), &settings) if err != nil { return false, err } - clients := settings["clients"].([]interface{}) - var newClients []interface{} + clients := settings["clients"].([]any) + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["tgId"] = tgId - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } } settings["clients"] = newClients @@ -1339,18 +1344,18 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, bo return false, false, common.NewError("Client Not Found For Email:", clientEmail) } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(inbound.Settings), &settings) if err != nil { return false, false, err } - clients := settings["clients"].([]interface{}) - var newClients []interface{} + clients := settings["clients"].([]any) + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["enable"] = !clientOldEnabled - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } } settings["clients"] = newClients @@ -1401,18 +1406,18 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int return false, common.NewError("Client Not Found For Email:", clientEmail) } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(inbound.Settings), &settings) if err != nil { return false, err } - clients := settings["clients"].([]interface{}) - var newClients []interface{} + clients := settings["clients"].([]any) + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["limitIp"] = count - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } } settings["clients"] = newClients @@ -1458,18 +1463,18 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry return false, common.NewError("Client Not Found For Email:", clientEmail) } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(inbound.Settings), &settings) if err != nil { return false, err } - clients := settings["clients"].([]interface{}) - var newClients []interface{} + clients := settings["clients"].([]any) + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["expiryTime"] = expiry_time - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } } settings["clients"] = newClients @@ -1518,18 +1523,18 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota return false, common.NewError("Client Not Found For Email:", clientEmail) } - var settings map[string]interface{} + var settings map[string]any err = json.Unmarshal([]byte(inbound.Settings), &settings) if err != nil { return false, err } - clients := settings["clients"].([]interface{}) - var newClients []interface{} + clients := settings["clients"].([]any) + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) if c["email"] == clientEmail { c["totalGB"] = totalGB * 1024 * 1024 * 1024 - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } } settings["clients"] = newClients @@ -1547,7 +1552,7 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error { result := db.Model(xray.ClientTraffic{}). Where("email = ?", clientEmail). - Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0}) + Updates(map[string]any{"enable": true, "up": 0, "down": 0}) err := result.Error if err != nil { @@ -1578,14 +1583,14 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e s.xrayApi.Init(p.GetAPIPort()) cipher := "" if string(inbound.Protocol) == "shadowsocks" { - var oldSettings map[string]interface{} + var oldSettings map[string]any err = json.Unmarshal([]byte(inbound.Settings), &oldSettings) if err != nil { return false, err } cipher = oldSettings["method"].(string) } - err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{ + err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]any{ "email": client.Email, "id": client.ID, "security": client.Security, @@ -1630,7 +1635,7 @@ func (s *InboundService) ResetAllClientTraffics(id int) error { result := db.Model(xray.ClientTraffic{}). Where(whereText, id). - Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0}) + Updates(map[string]any{"enable": true, "up": 0, "down": 0}) err := result.Error return err @@ -1641,7 +1646,7 @@ func (s *InboundService) ResetAllTraffics() error { result := db.Model(model.Inbound{}). Where("user_id > ?", 0). - Updates(map[string]interface{}{"up": 0, "down": 0}) + Updates(map[string]any{"up": 0, "down": 0}) err := result.Error return err @@ -1677,17 +1682,17 @@ func (s *InboundService) DelDepletedClients(id int) (err error) { if err != nil { return err } - var oldSettings map[string]interface{} + var oldSettings map[string]any err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) if err != nil { return err } - oldClients := oldSettings["clients"].([]interface{}) - var newClients []interface{} + oldClients := oldSettings["clients"].([]any) + var newClients []any for _, client := range oldClients { deplete := false - c := client.(map[string]interface{}) + c := client.(map[string]any) for _, email := range emails { if email == c["email"].(string) { deplete = true @@ -1903,14 +1908,14 @@ func (s *InboundService) MigrationRequirements() { return } for inbound_index := range inbounds { - settings := map[string]interface{}{} + settings := map[string]any{} json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings) - clients, ok := settings["clients"].([]interface{}) + clients, ok := settings["clients"].([]any) if ok { // Fix Client configuration problems - var newClients []interface{} + var newClients []any for client_index := range clients { - c := clients[client_index].(map[string]interface{}) + c := clients[client_index].(map[string]any) // Add email='' if it is not exists if _, ok := c["email"]; !ok { @@ -1919,7 +1924,7 @@ func (s *InboundService) MigrationRequirements() { // Convert string tgId to int64 if _, ok := c["tgId"]; ok { - var tgId interface{} = c["tgId"] + var tgId any = c["tgId"] if tgIdStr, ok2 := tgId.(string); ok2 { tgIdInt64, err := strconv.ParseInt(strings.ReplaceAll(tgIdStr, " ", ""), 10, 64) if err == nil { @@ -1934,7 +1939,7 @@ func (s *InboundService) MigrationRequirements() { c["flow"] = "" } } - newClients = append(newClients, interface{}(c)) + newClients = append(newClients, any(c)) } settings["clients"] = newClients modifiedSettings, err := json.MarshalIndent(settings, "", " ") @@ -1981,14 +1986,14 @@ func (s *InboundService) MigrationRequirements() { } for _, ep := range externalProxy { - var reverses interface{} - var stream map[string]interface{} + var reverses any + var stream map[string]any json.Unmarshal(ep.StreamSettings, &stream) - if tlsSettings, ok := stream["tlsSettings"].(map[string]interface{}); ok { - if settings, ok := tlsSettings["settings"].(map[string]interface{}); ok { - if domains, ok := settings["domains"].([]interface{}); ok { + if tlsSettings, ok := stream["tlsSettings"].(map[string]any); ok { + if settings, ok := tlsSettings["settings"].(map[string]any); ok { + if domains, ok := settings["domains"].([]any); ok { for _, domain := range domains { - if domainMap, ok := domain.(map[string]interface{}); ok { + if domainMap, ok := domain.(map[string]any); ok { domainMap["forceTls"] = "same" domainMap["port"] = ep.Port domainMap["dest"] = domainMap["domain"].(string) @@ -2021,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/setting.go b/web/service/setting.go index f6b9087b..868d55bc 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -24,56 +24,60 @@ import ( var xrayTemplateConfig string var defaultValueMap = map[string]string{ - "xrayTemplateConfig": xrayTemplateConfig, - "webListen": "", - "webDomain": "", - "webPort": "2053", - "webCertFile": "", - "webKeyFile": "", - "secret": random.Seq(32), - "webBasePath": "/", - "sessionMaxAge": "60", - "pageSize": "50", - "expireDiff": "0", - "trafficDiff": "0", - "remarkModel": "-ieo", - "timeLocation": "Asia/Shanghai", - "tgBotEnable": "false", - "tgBotToken": "", - "tgBotProxy": "", - "tgBotAPIServer": "", - "tgBotChatId": "", - "tgRunTime": "@daily", - "tgBotBackup": "false", - "tgBotLoginNotify": "true", - "tgCpu": "30", - "tgLang": "zh-Hans", - "secretEnable": "false", - "subEnable": "false", - "subListen": "", - "subPort": "2096", - "subPath": "/sub/", - "subDomain": "", - "subCertFile": "", - "subKeyFile": "", - "subUpdates": "12", - "subEncrypt": "true", - "subShowInfo": "true", - "subURI": "", - "subJsonPath": "/json/", - "subJsonURI": "", - "subJsonFragment": "", - "subJsonNoises": "", - "subJsonMux": "", - "subJsonRules": "", - "datepicker": "gregorian", - "warp": "", + "xrayTemplateConfig": xrayTemplateConfig, + "webListen": "", + "webDomain": "", + "webPort": "2053", + "webCertFile": "", + "webKeyFile": "", + "secret": random.Seq(32), + "webBasePath": "/", + "sessionMaxAge": "60", + "pageSize": "50", + "expireDiff": "0", + "trafficDiff": "0", + "remarkModel": "-ieo", + "timeLocation": "Local", + "tgBotEnable": "false", + "tgBotToken": "", + "tgBotProxy": "", + "tgBotAPIServer": "", + "tgBotChatId": "", + "tgRunTime": "@daily", + "tgBotBackup": "false", + "tgBotLoginNotify": "true", + "tgCpu": "80", + "tgLang": "en-US", + "twoFactorEnable": "false", + "twoFactorToken": "", + "subEnable": "false", + "subTitle": "", + "subListen": "", + "subPort": "2096", + "subPath": "/sub/", + "subDomain": "", + "subCertFile": "", + "subKeyFile": "", + "subUpdates": "12", + "subEncrypt": "true", + "subShowInfo": "true", + "subURI": "", + "subJsonPath": "/json/", + "subJsonURI": "", + "subJsonFragment": "", + "subJsonNoises": "", + "subJsonMux": "", + "subJsonRules": "", + "datepicker": "gregorian", + "warp": "", + "externalTrafficInformEnable": "false", + "externalTrafficInformURI": "", } type SettingService struct{} -func (s *SettingService) GetDefaultJsonConfig() (interface{}, error) { - var jsonData interface{} +func (s *SettingService) GetDefaultJsonConfig() (any, error) { + var jsonData any err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData) if err != nil { return nil, err @@ -163,8 +167,7 @@ func (s *SettingService) ResetSettings() error { return err } return db.Model(model.User{}). - Where("1 = 1"). - Update("login_secret", "").Error + Where("1 = 1").Error } func (s *SettingService) getSetting(key string) (*model.Setting, error) { @@ -315,6 +318,22 @@ func (s *SettingService) GetTgLang() (string, error) { return s.getString("tgLang") } +func (s *SettingService) GetTwoFactorEnable() (bool, error) { + return s.getBool("twoFactorEnable") +} + +func (s *SettingService) SetTwoFactorEnable(value bool) error { + return s.setBool("twoFactorEnable", value) +} + +func (s *SettingService) GetTwoFactorToken() (string, error) { + return s.getString("twoFactorToken") +} + +func (s *SettingService) SetTwoFactorToken(value string) error { + return s.setString("twoFactorToken", value) +} + func (s *SettingService) GetPort() (int, error) { return s.getInt("webPort") } @@ -355,14 +374,6 @@ func (s *SettingService) GetRemarkModel() (string, error) { return s.getString("remarkModel") } -func (s *SettingService) GetSecretStatus() (bool, error) { - return s.getBool("secretEnable") -} - -func (s *SettingService) SetSecretStatus(value bool) error { - return s.setBool("secretEnable", value) -} - func (s *SettingService) GetSecret() ([]byte, error) { secret, err := s.getString("secret") if secret == defaultValueMap["secret"] { @@ -416,6 +427,10 @@ func (s *SettingService) GetSubEnable() (bool, error) { return s.getBool("subEnable") } +func (s *SettingService) GetSubTitle() (string, error) { + return s.getString("subTitle") +} + func (s *SettingService) GetSubListen() (string, error) { return s.getString("subListen") } @@ -496,6 +511,22 @@ func (s *SettingService) SetWarp(data string) error { return s.setString("warp", data) } +func (s *SettingService) GetExternalTrafficInformEnable() (bool, error) { + return s.getBool("externalTrafficInformEnable") +} + +func (s *SettingService) SetExternalTrafficInformEnable(value bool) error { + return s.setBool("externalTrafficInformEnable", value) +} + +func (s *SettingService) GetExternalTrafficInformURI() (string, error) { + return s.getString("externalTrafficInformURI") +} + +func (s *SettingService) SetExternalTrafficInformURI(InformURI string) error { + return s.setString("externalTrafficInformURI", InformURI) +} + func (s *SettingService) GetIpLimitEnable() (bool, error) { accessLogPath, err := xray.GetAccessLogPath() if err != nil { @@ -525,8 +556,8 @@ func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { return common.Combine(errs...) } -func (s *SettingService) GetDefaultXrayConfig() (interface{}, error) { - var jsonData interface{} +func (s *SettingService) GetDefaultXrayConfig() (any, error) { + var jsonData any err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData) if err != nil { return nil, err @@ -534,24 +565,25 @@ func (s *SettingService) GetDefaultXrayConfig() (interface{}, error) { return jsonData, nil } -func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) { - type settingFunc func() (interface{}, error) +func (s *SettingService) GetDefaultSettings(host string) (any, error) { + type settingFunc func() (any, error) settings := map[string]settingFunc{ - "expireDiff": func() (interface{}, error) { return s.GetExpireDiff() }, - "trafficDiff": func() (interface{}, error) { return s.GetTrafficDiff() }, - "pageSize": func() (interface{}, error) { return s.GetPageSize() }, - "defaultCert": func() (interface{}, error) { return s.GetCertFile() }, - "defaultKey": func() (interface{}, error) { return s.GetKeyFile() }, - "tgBotEnable": func() (interface{}, error) { return s.GetTgbotEnabled() }, - "subEnable": func() (interface{}, error) { return s.GetSubEnable() }, - "subURI": func() (interface{}, error) { return s.GetSubURI() }, - "subJsonURI": func() (interface{}, error) { return s.GetSubJsonURI() }, - "remarkModel": func() (interface{}, error) { return s.GetRemarkModel() }, - "datepicker": func() (interface{}, error) { return s.GetDatepicker() }, - "ipLimitEnable": func() (interface{}, error) { return s.GetIpLimitEnable() }, + "expireDiff": func() (any, error) { return s.GetExpireDiff() }, + "trafficDiff": func() (any, error) { return s.GetTrafficDiff() }, + "pageSize": func() (any, error) { return s.GetPageSize() }, + "defaultCert": func() (any, error) { return s.GetCertFile() }, + "defaultKey": func() (any, error) { return s.GetKeyFile() }, + "tgBotEnable": func() (any, error) { return s.GetTgbotEnabled() }, + "subEnable": func() (any, error) { return s.GetSubEnable() }, + "subTitle": func() (any, error) { return s.GetSubTitle() }, + "subURI": func() (any, error) { return s.GetSubURI() }, + "subJsonURI": func() (any, error) { return s.GetSubJsonURI() }, + "remarkModel": func() (any, error) { return s.GetRemarkModel() }, + "datepicker": func() (any, error) { return s.GetDatepicker() }, + "ipLimitEnable": func() (any, error) { return s.GetIpLimitEnable() }, } - result := make(map[string]interface{}) + result := make(map[string]any) for key, fn := range settings { value, err := fn() @@ -563,6 +595,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) { if result["subEnable"].(bool) && (result["subURI"].(string) == "" || result["subJsonURI"].(string) == "") { subURI := "" + subTitle, _ := s.GetSubTitle() subPort, _ := s.GetSubPort() subPath, _ := s.GetSubPath() subJsonPath, _ := s.GetSubJsonPath() @@ -589,6 +622,9 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) { if result["subURI"].(string) == "" { result["subURI"] = subURI + subPath } + if result["subTitle"].(string) == "" { + result["subTitle"] = subTitle + } if result["subJsonURI"].(string) == "" { result["subJsonURI"] = subURI + subJsonPath } diff --git a/web/service/tgbot.go b/web/service/tgbot.go index af51e253..ffdb63f9 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -1,12 +1,16 @@ package service import ( + "crypto/rand" "embed" + "encoding/base64" "errors" "fmt" + "math/big" "net" "net/url" "os" + "regexp" "strconv" "strings" "time" @@ -20,6 +24,7 @@ import ( "x-ui/web/locale" "x-ui/xray" + "github.com/google/uuid" "github.com/mymmrac/telego" th "github.com/mymmrac/telego/telegohandler" tu "github.com/mymmrac/telego/telegoutil" @@ -34,8 +39,29 @@ var ( isRunning bool hostname string hashStorage *global.HashStorage + handler *th.Handler + + // clients data to adding new client + receiver_inbound_ID int + client_Id string + client_Flow string + client_Email string + client_LimitIP int + client_TotalGB int64 + client_ExpiryTime int64 + client_Enable bool + client_TgID string + client_SubID string + client_Comment string + client_Reset int + client_Security string + client_ShPassword string + client_TrPassword string + client_Method string ) +var userStates = make(map[int64]string) + type LoginStatus byte const ( @@ -121,6 +147,19 @@ func (t *Tgbot) Start(i18nFS embed.FS) error { return err } + // After bot initialization, set up bot commands with localized descriptions + err = bot.SetMyCommands(&telego.SetMyCommandsParams{ + Commands: []telego.BotCommand{ + {Command: "start", Description: t.I18nBot("tgbot.commands.startDesc")}, + {Command: "help", Description: t.I18nBot("tgbot.commands.helpDesc")}, + {Command: "status", Description: t.I18nBot("tgbot.commands.statusDesc")}, + {Command: "id", Description: t.I18nBot("tgbot.commands.idDesc")}, + }, + }) + if err != nil { + logger.Warning("Failed to set bot commands:", err) + } + // Start receiving Telegram bot messages if !isRunning { logger.Info("Telegram bot receiver started") @@ -221,36 +260,161 @@ func (t *Tgbot) OnReceive() { botHandler, _ = th.NewBotHandler(bot, updates) botHandler.HandleMessage(func(_ *telego.Bot, message telego.Message) { + delete(userStates, message.Chat.ID) t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.keyboardClosed"), tu.ReplyKeyboardRemove()) }, th.TextEqual(t.I18nBot("tgbot.buttons.closeKeyboard"))) botHandler.HandleMessage(func(_ *telego.Bot, message telego.Message) { + delete(userStates, message.Chat.ID) t.answerCommand(&message, message.Chat.ID, checkAdmin(message.From.ID)) }, th.AnyCommand()) botHandler.HandleCallbackQuery(func(_ *telego.Bot, query telego.CallbackQuery) { + delete(userStates, query.Message.GetChat().ID) t.answerCallback(&query, checkAdmin(query.From.ID)) }, th.AnyCallbackQueryWithMessage()) botHandler.HandleMessage(func(_ *telego.Bot, message telego.Message) { - if message.UsersShared != nil { - if checkAdmin(message.From.ID) { - for _, sharedUser := range message.UsersShared.Users { - userID := sharedUser.UserID - needRestart, err := t.inboundService.SetClientTelegramUserID(message.UsersShared.RequestID, userID) - if needRestart { - t.xrayService.SetToNeedRestart() - } - output := "" - if err != nil { - output += t.I18nBot("tgbot.messages.selectUserFailed") - } else { - output += t.I18nBot("tgbot.messages.userSaved") - } - t.SendMsgToTgbot(message.Chat.ID, output, tu.ReplyKeyboardRemove()) + if userState, exists := userStates[message.Chat.ID]; exists { + switch userState { + case "awaiting_id": + if client_Id == strings.TrimSpace(message.Text) { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(message.Chat.ID, message_text) + return + } + + client_Id = strings.TrimSpace(message.Text) + if t.isSingleWord(client_Id) { + userStates[message.Chat.ID] = "awaiting_id" + + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + + t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.incorrect_input"), cancel_btn_markup) + } else { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_id"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(message.Chat.ID, message_text) + } + case "awaiting_password_tr": + if client_TrPassword == strings.TrimSpace(message.Text) { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + return + } + + client_TrPassword = strings.TrimSpace(message.Text) + if t.isSingleWord(client_TrPassword) { + userStates[message.Chat.ID] = "awaiting_password_tr" + + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + + t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.incorrect_input"), cancel_btn_markup) + } else { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_password"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(message.Chat.ID, message_text) + } + case "awaiting_password_sh": + if client_ShPassword == strings.TrimSpace(message.Text) { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + return + } + + client_ShPassword = strings.TrimSpace(message.Text) + if t.isSingleWord(client_ShPassword) { + userStates[message.Chat.ID] = "awaiting_password_sh" + + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + + t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.incorrect_input"), cancel_btn_markup) + } else { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_password"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(message.Chat.ID, message_text) + } + case "awaiting_email": + if client_Email == strings.TrimSpace(message.Text) { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + return + } + + client_Email = strings.TrimSpace(message.Text) + if t.isSingleWord(client_Email) { + userStates[message.Chat.ID] = "awaiting_email" + + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + + t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.incorrect_input"), cancel_btn_markup) + } else { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_email"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(message.Chat.ID, message_text) + } + case "awaiting_comment": + if client_Comment == strings.TrimSpace(message.Text) { + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + return + } + + client_Comment = strings.TrimSpace(message.Text) + t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_comment"), 3, tu.ReplyKeyboardRemove()) + delete(userStates, message.Chat.ID) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(message.Chat.ID, message_text) + } + + } else { + if message.UsersShared != nil { + if checkAdmin(message.From.ID) { + for _, sharedUser := range message.UsersShared.Users { + userID := sharedUser.UserID + needRestart, err := t.inboundService.SetClientTelegramUserID(message.UsersShared.RequestID, userID) + if needRestart { + t.xrayService.SetToNeedRestart() + } + output := "" + if err != nil { + output += t.I18nBot("tgbot.messages.selectUserFailed") + } else { + output += t.I18nBot("tgbot.messages.userSaved") + } + t.SendMsgToTgbot(message.Chat.ID, output, tu.ReplyKeyboardRemove()) + } + } else { + t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.noResult"), tu.ReplyKeyboardRemove()) } - } else { - t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.noResult"), tu.ReplyKeyboardRemove()) } } }, th.AnyMessage()) @@ -307,8 +471,6 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo onlyMessage = true if isAdmin { if len(commandArgs) == 0 { - msg += t.I18nBot("tgbot.commands.restartUsage") - } else if strings.ToLower(commandArgs[0]) == "force" { if t.xrayService.IsXrayRunning() { err := t.xrayService.RestartXray(true) if err != nil { @@ -344,6 +506,25 @@ func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool } } +func (t *Tgbot) randomLowerAndNum(length int) string { + charset := "abcdefghijklmnopqrstuvwxyz0123456789" + bytes := make([]byte, length) + for i := range bytes { + randomIndex, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) + bytes[i] = charset[randomIndex.Int64()] + } + return string(bytes) +} + +func (t *Tgbot) randomShadowSocksPassword() string { + array := make([]byte, 32) + _, err := rand.Read(array) + if err != nil { + return t.randomLowerAndNum(32) + } + return base64.StdEncoding.EncodeToString(array) +} + func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) { chatId := callbackQuery.Message.GetChat().ID @@ -507,6 +688,78 @@ 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_limit_traffic_c": + limitTraffic, _ := strconv.Atoi(dataArray[1]) + client_TotalGB = int64(limitTraffic) * 1024 * 1024 * 1024 + 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_limit_traffic_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_traffic_exp")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmNumberAdd", "Num=="+strconv.Itoa(inputNumber))).WithCallbackData(t.encodeQuery("add_client_limit_traffic_c "+strconv.Itoa(inputNumber))), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 1")), + tu.InlineKeyboardButton("2").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 2")), + tu.InlineKeyboardButton("3").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 3")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("4").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 4")), + tu.InlineKeyboardButton("5").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 5")), + tu.InlineKeyboardButton("6").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 6")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("7").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 7")), + tu.InlineKeyboardButton("8").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 8")), + tu.InlineKeyboardButton("9").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 9")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("πŸ”„").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" -2")), + tu.InlineKeyboardButton("0").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" 0")), + tu.InlineKeyboardButton("⬅️").WithCallbackData(t.encodeQuery("add_client_limit_traffic_in "+strconv.Itoa(inputNumber)+" -1")), + ), + ) + t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard) + return + } + } case "reset_exp": inlineKeyboard := tu.InlineKeyboard( tu.InlineKeyboardRow( @@ -638,6 +891,90 @@ 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_reset_exp_c": + client_ExpiryTime = 0 + days, _ := strconv.Atoi(dataArray[1]) + var date int64 = 0 + if client_ExpiryTime > 0 { + if client_ExpiryTime-time.Now().Unix()*1000 < 0 { + date = -int64(days * 24 * 60 * 60000) + } else { + date = client_ExpiryTime + int64(days*24*60*60000) + } + } else { + date = client_ExpiryTime - int64(days*24*60*60000) + } + client_ExpiryTime = date + + 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_reset_exp_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_traffic_exp")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmNumberAdd", "Num=="+strconv.Itoa(inputNumber))).WithCallbackData(t.encodeQuery("add_client_reset_exp_c "+strconv.Itoa(inputNumber))), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 1")), + tu.InlineKeyboardButton("2").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 2")), + tu.InlineKeyboardButton("3").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 3")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("4").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 4")), + tu.InlineKeyboardButton("5").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 5")), + tu.InlineKeyboardButton("6").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 6")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("7").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 7")), + tu.InlineKeyboardButton("8").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 8")), + tu.InlineKeyboardButton("9").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 9")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("πŸ”„").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" -2")), + tu.InlineKeyboardButton("0").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" 0")), + tu.InlineKeyboardButton("⬅️").WithCallbackData(t.encodeQuery("add_client_reset_exp_in "+strconv.Itoa(inputNumber)+" -1")), + ), + ) + t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard) + return + } + } case "ip_limit": inlineKeyboard := tu.InlineKeyboard( tu.InlineKeyboardRow( @@ -745,6 +1082,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( @@ -838,7 +1252,40 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool return } t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseClient", "Inbound=="+inbound.Remark), clients) + case "add_client_to": + // assign default values to clients variables + client_Id = uuid.New().String() + client_Flow = "" + client_Email = t.randomLowerAndNum(8) + client_LimitIP = 0 + client_TotalGB = 0 + client_ExpiryTime = 0 + client_Enable = true + client_TgID = "" + client_SubID = t.randomLowerAndNum(16) + client_Comment = "" + client_Reset = 0 + client_Security = "auto" + client_ShPassword = t.randomShadowSocksPassword() + client_TrPassword = t.randomLowerAndNum(10) + client_Method = "" + inboundId := dataArray[1] + inboundIdInt, err := strconv.Atoi(inboundId) + if err != nil { + t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error()) + return + } + receiver_inbound_ID = inboundIdInt + inbound, err := t.inboundService.GetInbound(inboundIdInt) + if err != nil { + t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error()) + return + } + + message_text, err := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + + t.addClient(chatId, message_text) } return } else { @@ -892,9 +1339,432 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool case "commands": t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands")) t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpAdminCommands")) + case "add_client": + // assign default values to clients variables + client_Id = uuid.New().String() + client_Flow = "" + client_Email = t.randomLowerAndNum(8) + client_LimitIP = 0 + client_TotalGB = 0 + client_ExpiryTime = 0 + client_Enable = true + client_TgID = "" + client_SubID = t.randomLowerAndNum(16) + client_Comment = "" + client_Reset = 0 + client_Security = "auto" + client_ShPassword = t.randomShadowSocksPassword() + client_TrPassword = t.randomLowerAndNum(10) + client_Method = "" + + inbounds, err := t.getInboundsAddClient() + if err != nil { + t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error()) + return + } + t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.addClient")) + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds) + case "add_client_ch_default_email": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + userStates[chatId] = "awaiting_email" + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + prompt_message := t.I18nBot("tgbot.messages.email_prompt", "ClientEmail=="+client_Email) + t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup) + case "add_client_ch_default_id": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + userStates[chatId] = "awaiting_id" + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + prompt_message := t.I18nBot("tgbot.messages.id_prompt", "ClientId=="+client_Id) + t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup) + case "add_client_ch_default_pass_tr": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + userStates[chatId] = "awaiting_password_tr" + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + prompt_message := t.I18nBot("tgbot.messages.pass_prompt", "ClientPassword=="+client_TrPassword) + t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup) + case "add_client_ch_default_pass_sh": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + userStates[chatId] = "awaiting_password_sh" + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + prompt_message := t.I18nBot("tgbot.messages.pass_prompt", "ClientPassword=="+client_ShPassword) + t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup) + case "add_client_ch_default_comment": + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + userStates[chatId] = "awaiting_comment" + cancel_btn_markup := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"), + ), + ) + prompt_message := t.I18nBot("tgbot.messages.comment_prompt", "ClientComment=="+client_Comment) + t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup) + case "add_client_ch_default_traffic": + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_traffic_exp")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 0")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("add_client_limit_traffic_in 0")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("1 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 1")), + tu.InlineKeyboardButton("5 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 5")), + tu.InlineKeyboardButton("10 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 10")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("20 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 20")), + tu.InlineKeyboardButton("30 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 30")), + tu.InlineKeyboardButton("40 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 40")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("50 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 50")), + tu.InlineKeyboardButton("60 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 60")), + tu.InlineKeyboardButton("80 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 80")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton("100 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 100")), + tu.InlineKeyboardButton("150 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 150")), + tu.InlineKeyboardButton("200 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 200")), + ), + ) + t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard) + case "add_client_ch_default_exp": + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_traffic_exp")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 0")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("add_client_reset_exp_in 0")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 7 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 7")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 10 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 10")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 14 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 14")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 20 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 20")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 1 "+t.I18nBot("tgbot.month")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 30")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 3 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 90")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 6 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 180")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 12 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 365")), + ), + ) + 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()) + delete(userStates, chatId) + inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID) + message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol) + t.addClient(chatId, message_text) + case "add_client_cancel": + delete(userStates, chatId) + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + t.SendMsgToTgbotDeleteAfter(chatId, t.I18nBot("tgbot.messages.cancel"), 3, tu.ReplyKeyboardRemove()) + case "add_client_default_traffic_exp": + 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_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() + if err != nil { + errorMessage := fmt.Sprintf("%v", err) + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.error_add_client", "error=="+errorMessage), tu.ReplyKeyboardRemove()) + } else { + t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID()) + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.successfulOperation"), tu.ReplyKeyboardRemove()) + } + case "add_client_submit_enable": + client_Enable = true + _, err := t.SubmitAddClient() + if err != nil { + errorMessage := fmt.Sprintf("%v", err) + t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.error_add_client", "error=="+errorMessage), tu.ReplyKeyboardRemove()) + } else { + 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()) + + } } } +func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol model.Protocol) (string, error) { + var message string + + currentTime := time.Now() + timestampMillis := currentTime.UnixNano() / int64(time.Millisecond) + + expiryTime := "" + diff := client_ExpiryTime/1000 - timestampMillis + if client_ExpiryTime == 0 { + expiryTime = t.I18nBot("tgbot.unlimited") + } else if diff > 172800 { + expiryTime = time.Unix((client_ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05") + } else if client_ExpiryTime < 0 { + expiryTime = fmt.Sprintf("%d %s", client_ExpiryTime/-86400000, t.I18nBot("tgbot.days")) + } else { + expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours")) + } + + traffic_value := "" + if client_TotalGB == 0 { + traffic_value = "♾️ Unlimited(Reset)" + } else { + 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, "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, "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, "IpLimit=="+ip_limit, "ClientComment=="+client_Comment) + + default: + return "", errors.New("unknown protocol") + } + + return message, nil +} + +func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string, error) { + var jsonString string + + switch protocol { + case model.VMESS: + jsonString = fmt.Sprintf(`{ + "clients": [{ + "id": "%s", + "security": "%s", + "email": "%s", + "limitIp": %d, + "totalGB": %d, + "expiryTime": %d, + "enable": %t, + "tgId": "%s", + "subId": "%s", + "comment": "%s", + "reset": %d + }] + }`, client_Id, client_Security, client_Email, client_LimitIP, client_TotalGB, client_ExpiryTime, client_Enable, client_TgID, client_SubID, client_Comment, client_Reset) + + case model.VLESS: + jsonString = fmt.Sprintf(`{ + "clients": [{ + "id": "%s", + "flow": "%s", + "email": "%s", + "limitIp": %d, + "totalGB": %d, + "expiryTime": %d, + "enable": %t, + "tgId": "%s", + "subId": "%s", + "comment": "%s", + "reset": %d + }] + }`, client_Id, client_Flow, client_Email, client_LimitIP, client_TotalGB, client_ExpiryTime, client_Enable, client_TgID, client_SubID, client_Comment, client_Reset) + + case model.Trojan: + jsonString = fmt.Sprintf(`{ + "clients": [{ + "password": "%s", + "email": "%s", + "limitIp": %d, + "totalGB": %d, + "expiryTime": %d, + "enable": %t, + "tgId": "%s", + "subId": "%s", + "comment": "%s", + "reset": %d + }] + }`, client_TrPassword, client_Email, client_LimitIP, client_TotalGB, client_ExpiryTime, client_Enable, client_TgID, client_SubID, client_Comment, client_Reset) + + case model.Shadowsocks: + jsonString = fmt.Sprintf(`{ + "clients": [{ + "method": "%s", + "password": "%s", + "email": "%s", + "limitIp": %d, + "totalGB": %d, + "expiryTime": %d, + "enable": %t, + "tgId": "%s", + "subId": "%s", + "comment": "%s", + "reset": %d + }] + }`, client_Method, client_ShPassword, client_Email, client_LimitIP, client_TotalGB, client_ExpiryTime, client_Enable, client_TgID, client_SubID, client_Comment, client_Reset) + + default: + return "", errors.New("unknown protocol") + } + + return jsonString, nil +} + +func (t *Tgbot) SubmitAddClient() (bool, error) { + + inbound, err := t.inboundService.GetInbound(receiver_inbound_ID) + if err != nil { + logger.Warning("getIboundClients run failed:", err) + return false, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed")) + } + + jsonString, err := t.BuildJSONForProtocol(inbound.Protocol) + + newInbound := &model.Inbound{ + Id: receiver_inbound_ID, + Settings: jsonString, + } + + return t.inboundService.AddInboundClient(newInbound) +} + func checkAdmin(tgId int64) bool { for _, adminId := range adminIds { if adminId == tgId { @@ -907,20 +1777,27 @@ 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.allClients")).WithCallbackData(t.encodeQuery("get_inbounds")), + 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")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.getBanLogs")).WithCallbackData(t.encodeQuery("get_banlogs")), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.getInbounds")).WithCallbackData(t.encodeQuery("inbounds")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.depleteSoon")).WithCallbackData(t.encodeQuery("deplete_soon")), ), tu.InlineKeyboardRow( tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.commands")).WithCallbackData(t.encodeQuery("commands")), tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.onlines")).WithCallbackData(t.encodeQuery("onlines")), ), tu.InlineKeyboardRow( - tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.depleteSoon")).WithCallbackData(t.encodeQuery("deplete_soon")), - tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.getInbounds")).WithCallbackData(t.encodeQuery("inbounds")), - ), - tu.InlineKeyboardRow( - tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.getBanLogs")).WithCallbackData(t.encodeQuery("get_banlogs")), - tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.dbBackup")).WithCallbackData(t.encodeQuery("get_backup")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.allClients")).WithCallbackData(t.encodeQuery("get_inbounds")), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.addClient")).WithCallbackData(t.encodeQuery("add_client")), ), // TODOOOOOOOOOOOOOO: Add restart button here. ) @@ -1166,35 +2043,75 @@ func (t *Tgbot) getInboundUsages() string { } return info } - func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) { inbounds, err := t.inboundService.GetAllInbounds() - var buttons []telego.InlineKeyboardButton - if err != nil { logger.Warning("GetAllInbounds run failed:", err) return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed")) - } else { - if len(inbounds) > 0 { - for _, inbound := range inbounds { - status := "❌" - if inbound.Enable { - status = "βœ…" - } - buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(t.encodeQuery("get_clients "+strconv.Itoa(inbound.Id)))) - } - } else { - logger.Warning("GetAllInbounds run failed:", err) - return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed")) - } - } - cols := 0 - if len(buttons) < 6 { - cols = 3 - } else { + + if len(inbounds) == 0 { + logger.Warning("No inbounds found") + return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed")) + } + + var buttons []telego.InlineKeyboardButton + for _, inbound := range inbounds { + status := "❌" + if inbound.Enable { + status = "βœ…" + } + callbackData := t.encodeQuery(fmt.Sprintf("%s %d", "get_clients", inbound.Id)) + buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(callbackData)) + } + + cols := 1 + if len(buttons) >= 6 { cols = 2 } + + keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...)) + return keyboard, nil +} + +func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) { + inbounds, err := t.inboundService.GetAllInbounds() + if err != nil { + logger.Warning("GetAllInbounds run failed:", err) + return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed")) + } + + if len(inbounds) == 0 { + logger.Warning("No inbounds found") + return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed")) + } + + excludedProtocols := map[model.Protocol]bool{ + model.DOKODEMO: true, + model.Socks: true, + model.WireGuard: true, + model.HTTP: true, + } + + var buttons []telego.InlineKeyboardButton + for _, inbound := range inbounds { + if excludedProtocols[inbound.Protocol] { + continue + } + + status := "❌" + if inbound.Enable { + status = "βœ…" + } + callbackData := t.encodeQuery(fmt.Sprintf("%s %d", "add_client_to", inbound.Id)) + buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(callbackData)) + } + + cols := 1 + if len(buttons) >= 6 { + cols = 2 + } + keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...)) return keyboard, nil } @@ -1489,6 +2406,102 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) { } } +func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) { + inbound, err := t.inboundService.GetInbound(receiver_inbound_ID) + if err != nil { + t.SendMsgToTgbot(chatId, err.Error()) + return + } + + protocol := inbound.Protocol + + switch protocol { + case model.VMESS, model.VLESS: + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_id")).WithCallbackData("add_client_ch_default_id"), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"), + ), + 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"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"), + ), + ) + if len(messageID) > 0 { + t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard) + } else { + t.SendMsgToTgbot(chatId, msg, inlineKeyboard) + } + case model.Trojan: + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_password")).WithCallbackData("add_client_ch_default_pass_tr"), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"), + ), + 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"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"), + ), + ) + if len(messageID) > 0 { + t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard) + } else { + t.SendMsgToTgbot(chatId, msg, inlineKeyboard) + } + case model.Shadowsocks: + inlineKeyboard := tu.InlineKeyboard( + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_password")).WithCallbackData("add_client_ch_default_pass_sh"), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"), + ), + 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"), + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"), + ), + tu.InlineKeyboardRow( + tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"), + ), + ) + + if len(messageID) > 0 { + t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard) + } else { + t.SendMsgToTgbot(chatId, msg, inlineKeyboard) + } + } + +} + func (t *Tgbot) searchInbound(chatId int64, remark string) { inbounds, err := t.inboundService.SearchInbounds(remark) if err != nil { @@ -1859,3 +2872,47 @@ func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlin logger.Warning(err) } } + +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 + if len(replyMarkup) > 0 { + replyMarkupParam = replyMarkup[0] // Use the first element + } + + // Send the message + sentMsg, err := bot.SendMessage(&telego.SendMessageParams{ + ChatID: tu.ID(chatId), + Text: msg, + ReplyMarkup: replyMarkupParam, // Use the correct replyMarkup value + }) + if err != nil { + logger.Warning("Failed to send message:", err) + return + } + + // Delete the sent message after the specified number of seconds + go func() { + time.Sleep(time.Duration(delayInSeconds) * time.Second) // Wait for the specified delay + t.deleteMessageTgBot(chatId, sentMsg.MessageID) // Delete the message + delete(userStates, chatId) + }() +} + +func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) { + params := telego.DeleteMessageParams{ + ChatID: tu.ID(chatId), + MessageID: messageID, + } + if err := bot.DeleteMessage(¶ms); err != nil { + logger.Warning("Failed to delete message:", err) + } else { + logger.Info("Message deleted successfully") + } +} + +func (t *Tgbot) isSingleWord(text string) bool { + text = strings.TrimSpace(text) + re := regexp.MustCompile(`\s+`) + return re.MatchString(text) +}