diff --git a/README.ar_EG.md b/README.ar_EG.md index 2a309545..f42df1e1 100644 --- a/README.ar_EG.md +++ b/README.ar_EG.md @@ -33,7 +33,7 @@ لتثبيت المشروع أو تحديثه، نفذ الأمر ده: ```bash -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## تثبيت النسخة القديمة (مش موصى بيها) diff --git a/README.es_ES.md b/README.es_ES.md index afa1b8e3..60dbd305 100644 --- a/README.es_ES.md +++ b/README.es_ES.md @@ -32,7 +32,7 @@ ## Instalar y Actualizar ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## Instalar versión antigua (no recomendamos) diff --git a/README.fa_IR.md b/README.fa_IR.md index 806f9e69..f69f54fa 100644 --- a/README.fa_IR.md +++ b/README.fa_IR.md @@ -32,7 +32,7 @@ ## نصب و ارتقا ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## نصب نسخه‌های قدیمی (توصیه نمی‌شود) diff --git a/README.md b/README.md index 0cace84a..cb8fef8c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ ## Install & Upgrade ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## Install legacy Version (we don't recommend) diff --git a/README.ru_RU.md b/README.ru_RU.md index bc0170bb..c75dba91 100644 --- a/README.ru_RU.md +++ b/README.ru_RU.md @@ -32,7 +32,7 @@ ## Установка и обновление ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## Установить старую версию (мы не рекомендуем) diff --git a/README.zh_CN.md b/README.zh_CN.md index 995a6ee1..703b6733 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -32,7 +32,7 @@ ## 安装 & 升级 ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## 安装旧版本 (我们不建议) diff --git a/install.sh b/install.sh index 92f62417..0398285d 100644 --- a/install.sh +++ b/install.sh @@ -82,14 +82,13 @@ gen_random_string() { } config_after_install() { - local existing_username=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'username: .+' | awk '{print $2}') - local existing_password=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'password: .+' | awk '{print $2}') + local existing_hasDefaultCredential=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}') local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') local server_ip=$(curl -s https://api.ipify.org) if [[ ${#existing_webBasePath} -lt 4 ]]; then - if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then + if [[ "$existing_hasDefaultCredential" == "true" ]]; then local config_webBasePath=$(gen_random_string 15) local config_username=$(gen_random_string 10) local config_password=$(gen_random_string 10) @@ -112,7 +111,6 @@ config_after_install() { echo -e "${green}WebBasePath: ${config_webBasePath}${plain}" echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}" echo -e "###############################################" - echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}" else local config_webBasePath=$(gen_random_string 15) echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}" @@ -121,7 +119,7 @@ config_after_install() { echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}" fi else - if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then + if [[ "$existing_hasDefaultCredential" == "true" ]]; then local config_username=$(gen_random_string 10) local config_password=$(gen_random_string 10) @@ -132,7 +130,6 @@ config_after_install() { echo -e "${green}Username: ${config_username}${plain}" echo -e "${green}Password: ${config_password}${plain}" echo -e "###############################################" - echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}" else echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}" fi diff --git a/main.go b/main.go index c9e26bd4..a94a899b 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "x-ui/web" "x-ui/web/global" "x-ui/web/service" + "x-ui/util/crypto" "github.com/op/go-logging" ) @@ -151,9 +152,7 @@ func showSetting(show bool) { fmt.Println("get current user info failed, error info:", err) } - username := userModel.Username - userpasswd := userModel.Password - if username == "" || userpasswd == "" { + if userModel.Username == "" || userModel.Password == "" { fmt.Println("current username or password is empty") } @@ -163,8 +162,12 @@ func showSetting(show bool) { } else { fmt.Println("Panel is secure with SSL") } - fmt.Println("username:", username) - fmt.Println("password:", userpasswd) + + hasDefaultCredential := func() bool { + return userModel.Username == "admin" && crypto.CheckPasswordHash(userModel.Password, "admin") + }() + + fmt.Println("hasDefaultCredential:", hasDefaultCredential) fmt.Println("port:", port) fmt.Println("webBasePath:", webBasePath) } diff --git a/web/controller/server.go b/web/controller/server.go index fc7756ba..5d89e356 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -44,6 +44,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) { g.POST("/stopXrayService", a.stopXrayService) g.POST("/restartXrayService", a.restartXrayService) g.POST("/installXray/:version", a.installXray) + g.POST("/updateGeofile/:fileName", a.updateGeofile) g.POST("/logs/:count", a.getLogs) g.POST("/getConfigJson", a.getConfigJson) g.GET("/getDb", a.getDb) @@ -95,7 +96,13 @@ func (a *ServerController) getXrayVersion(c *gin.Context) { func (a *ServerController) installXray(c *gin.Context) { version := c.Param("version") err := a.serverService.UpdateXray(version) - jsonMsg(c, I18nWeb(c, "install")+" xray", err) + jsonMsg(c, I18nWeb(c, "pages.index.xraySwitchVersionPopover"), err) +} + +func (a *ServerController) updateGeofile(c *gin.Context) { + fileName := c.Param("fileName") + err := a.serverService.UpdateGeofile(fileName) + jsonMsg(c, I18nWeb(c, "pages.index.geofileUpdatePopover"), err) } func (a *ServerController) stopXrayService(c *gin.Context) { diff --git a/web/html/index.html b/web/html/index.html index 574fc539..d17cd1e0 100644 --- a/web/html/index.html +++ b/web/html/index.html @@ -22,11 +22,14 @@ .ant-backup-list-item { gap: 10px; } - .ant-xray-version-list-item { + .ant-version-list-item { --padding: 12px; padding: var(--padding) !important; gap: var(--padding); } + .dark .ant-version-list-item svg{ + color: var(--dark-color-text-primary); + } .dark .ant-backup-list-item svg, .dark .ant-badge-status-text, .dark .ant-card-extra { @@ -43,7 +46,7 @@ border-color: var(--color-primary-100); } .dark .ant-backup-list, - .dark .ant-xray-version-list, + .dark .ant-version-list, .dark .ant-card-actions, .dark .ant-card-actions>li:not(:last-child) { border-color: var(--dark-color-stroke); @@ -353,14 +356,25 @@ - - - - [[ version ]] - - - + + + + + + [[ version ]] + + + + + + + + [[ file ]] + + + + + { + versionModal.hide(); + this.loading(true, '{{ i18n "pages.index.dontRefresh"}}'); + await HttpUtil.post(`/server/updateGeofile/${fileName}`); + this.loading(false); + }, + }); + }, async stopXrayService() { this.loading(true); const msg = await HttpUtil.post('server/stopXrayService'); diff --git a/web/html/xray.html b/web/html/xray.html index 0dfbdf50..fc94c31a 100644 --- a/web/html/xray.html +++ b/web/html/xray.html @@ -287,6 +287,7 @@ { label: 'Malware 🇮🇷', value: 'ext:geosite_IR.dat:malware' }, { label: 'Phishing 🇮🇷', value: 'ext:geosite_IR.dat:phishing' }, { label: 'Cryptominers 🇮🇷', value: 'ext:geosite_IR.dat:cryptominers' }, + { label: 'Adult +18', value: 'geosite:category-porn' }, { label: '🇮🇷 Iran', value: 'ext:geosite_IR.dat:ir' }, { label: '🇮🇷 .ir', value: 'regexp:.*\\.ir$' }, { label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' }, 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/server.go b/web/service/server.go index 99e2a97b..23167fb3 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -591,6 +591,66 @@ func (s *ServerService) ImportDB(file multipart.File) error { return nil } +func (s *ServerService) UpdateGeofile(fileName string) error { + files := []struct { + URL string + FileName string + }{ + {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip.dat"}, + {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite.dat"}, + {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat", "geoip_IR.dat"}, + {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat", "geosite_IR.dat"}, + {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip_RU.dat"}, + {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite_RU.dat"}, + } + + downloadFile := func(url, destPath string) error { + resp, err := http.Get(url) + if err != nil { + return common.NewErrorf("Failed to download Geofile from %s: %v", url, err) + } + defer resp.Body.Close() + + file, err := os.Create(destPath) + if err != nil { + return common.NewErrorf("Failed to create Geofile %s: %v", destPath, err) + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return common.NewErrorf("Failed to save Geofile %s: %v", destPath, err) + } + + return nil + } + + var fileURL string + for _, file := range files { + if file.FileName == fileName { + fileURL = file.URL + break + } + } + + if fileURL == "" { + return common.NewErrorf("File '%s' not found in the list of Geofiles", fileName) + } + + destPath := fmt.Sprintf("%s/%s", config.GetBinFolderPath(), fileName) + + if err := downloadFile(fileURL, destPath); err != nil { + return common.NewErrorf("Error downloading Geofile '%s': %v", fileName, err) + } + + err := s.RestartXrayService() + if err != nil { + return common.NewErrorf("Updated Geofile '%s' but Failed to start Xray: %v", fileName, err) + } + + return nil +} + func (s *ServerService) GetNewX25519Cert() (any, error) { // Run the command cmd := exec.Command(xray.GetBinaryPath(), "x25519") 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") @@ -1575,8 +1763,12 @@ 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"), diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml index 81547465..69815c5c 100644 --- a/web/translation/translate.ar_EG.toml +++ b/web/translation/translate.ar_EG.toml @@ -122,8 +122,12 @@ "totalData" = "إجمالي البيانات" "sent" = "مرسل" "received" = "مستقبل" -"xraySwitchVersionDialog" = "تغيير نسخة Xray" -"xraySwitchVersionDialogDesc" = "متأكد إنك عايز تغير نسخة Xray لـ" +"xraySwitchVersionDialog" = "هل تريد حقًا تغيير إصدار Xray؟" +"xraySwitchVersionDialogDesc" = "سيؤدي هذا إلى تغيير إصدار Xray إلى #version#." +"xraySwitchVersionPopover" = "تم تحديث Xray بنجاح" +"geofileUpdateDialog" = "هل تريد حقًا تحديث ملف الجغرافيا؟" +"geofileUpdateDialogDesc" = "سيؤدي هذا إلى تحديث ملف #filename#." +"geofileUpdatePopover" = "تم تحديث ملف الجغرافيا بنجاح" "dontRefresh" = "التثبيت شغال، متعملش Refresh للصفحة" "logs" = "السجلات" "config" = "الإعدادات" @@ -601,12 +605,17 @@ "pass_prompt" = "🔑 الباسورد الافتراضي: {{ .ClientPassword }}\n\nادخل الباسورد بتاعك." "email_prompt" = "📧 الإيميل الافتراضي: {{ .ClientEmail }}\n\nادخل الإيميل بتاعك." "comment_prompt" = "💬 التعليق الافتراضي: {{ .ClientComment }}\n\nادخل تعليقك." -"inbound_client_data_id" = "🔄 للإدخال: {{ .InboundRemark }}\n\n🔑 الـ ID: {{ .ClientId }}\n📧 الإيميل: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n💬 التعليق: {{ .ClientComment }}\n\nممكن تضيف العميل للإدخال دلوقتي!" -"inbound_client_data_pass" = "🔄 للإدخال: {{ .InboundRemark }}\n\n🔑 الباسورد: {{ .ClientPass }}\n📧 الإيميل: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n💬 التعليق: {{ .ClientComment }}\n\nممكن تضيف العميل للإدخال دلوقتي!" +"inbound_client_data_id" = "🔄 الدخول: {{ .InboundRemark }}\n\n🔑 المعرف: {{ .ClientId }}\n📧 البريد الإلكتروني: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n🌐 حدّ IP: {{ .IpLimit }}\n💬 تعليق: {{ .ClientComment }}\n\nدلوقتي تقدر تضيف العميل على الدخول!" +"inbound_client_data_pass" = "🔄 الدخول: {{ .InboundRemark }}\n\n🔑 كلمة المرور: {{ .ClientPass }}\n📧 البريد الإلكتروني: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n🌐 حدّ IP: {{ .IpLimit }}\n💬 تعليق: {{ .ClientComment }}\n\nدلوقتي تقدر تضيف العميل على الدخول!" "cancel" = "❌ العملية اتلغت! \n\nممكن تبدأ من /start في أي وقت. 🔄" "error_add_client" = "⚠️ حصل خطأ:\n\n {{ .error }}" "using_default_value" = "تمام، هشيل على القيمة الافتراضية. 😊" "incorrect_input" = "المدخلات مش صحيحة.\nالكلمات لازم تكون متصلة من غير فراغات.\nمثال صحيح: aaaaaa\nمثال غلط: aaa aaa 🚫" +"AreYouSure" = "إنت متأكد؟ 🤔" +"SuccessResetTraffic" = "📧 البريد الإلكتروني: {{ .ClientEmail }}\n🏁 النتيجة: ✅ تم بنجاح" +"FailedResetTraffic" = "📧 البريد الإلكتروني: {{ .ClientEmail }}\n🏁 النتيجة: ❌ فشل \n\n🛠️ الخطأ: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 عملية إعادة ضبط الترافيك خلصت لكل العملاء." + [tgbot.buttons] "closeKeyboard" = "❌ اقفل الكيبورد" @@ -650,6 +659,9 @@ "change_password" = "⚙️🔑 كلمة السر" "change_email" = "⚙️📧 البريد الإلكتروني" "change_comment" = "⚙️💬 تعليق" +"ResetAllTraffics" = "إعادة ضبط جميع الترافيك" +"SortedTrafficUsageReport" = "تقرير استخدام الترافيك المرتب" + [tgbot.answers] "successfulOperation" = "✅ العملية نجحت!" diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 2afb7c15..e1cf2bbc 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -122,8 +122,12 @@ "totalData" = "Total Data" "sent" = "Sent" "received" = "Received" -"xraySwitchVersionDialog" = "Change Xray Version" -"xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to" +"xraySwitchVersionDialog" = "Do you really want to change the Xray version?" +"xraySwitchVersionDialogDesc" = "This will change the Xray version to #version#." +"xraySwitchVersionPopover" = "Xray updated successfully" +"geofileUpdateDialog" = "Do you really want to update the geofile?" +"geofileUpdateDialogDesc" = "This will update the #filename# file." +"geofileUpdatePopover" = "Geofile updated successfully" "dontRefresh" = "Installation is in progress, please do not refresh this page" "logs" = "Logs" "config" = "Config" @@ -601,12 +605,17 @@ "pass_prompt" = "🔑 Default Password: {{ .ClientPassword }}\n\nEnter your password." "email_prompt" = "📧 Default Email: {{ .ClientEmail }}\n\nEnter your email." "comment_prompt" = "💬 Default Comment: {{ .ClientComment }}\n\nEnter your Comment." -"inbound_client_data_id" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!" -"inbound_client_data_pass" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!" +"inbound_client_data_id" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!" +"inbound_client_data_pass" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!" "cancel" = "❌ Process Canceled! \n\nYou can /start again anytime. 🔄" "error_add_client" = "⚠️ Error:\n\n {{ .error }}" "using_default_value" = "Okay, I'll stick with the default value. 😊" "incorrect_input" ="Your input is not valid.\nThe phrases should be continuous without spaces.\nCorrect example: aaaaaa\nIncorrect example: aaa aaa 🚫" +"AreYouSure" = "Are you sure? 🤔" +"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ✅ Success" +"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ❌ Failed \n\n🛠️ Error: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Traffic reset process finished for all clients." + [tgbot.buttons] "closeKeyboard" = "❌ Close Keyboard" @@ -650,6 +659,8 @@ "change_password" = "⚙️🔑 Password" "change_email" = "⚙️📧 Email" "change_comment" = "⚙️💬 Comment" +"ResetAllTraffics" = "Reset All Traffics" +"SortedTrafficUsageReport" = "Sorted Traffic Usage Report" [tgbot.answers] "successfulOperation" = "✅ Operation successful!" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 92a4d64e..c985e6bd 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -124,8 +124,12 @@ "totalData" = "Datos totales" "sent" = "Enviado" "received" = "Recibido" -"xraySwitchVersionDialog" = "Cambiar Versión de Xray" -"xraySwitchVersionDialogDesc" = "¿Estás seguro de que deseas cambiar la versión de Xray a" +"xraySwitchVersionDialog" = "¿Realmente deseas cambiar la versión de Xray?" +"xraySwitchVersionDialogDesc" = "Esto cambiará la versión de Xray a #version#." +"xraySwitchVersionPopover" = "Xray se actualizó correctamente" +"geofileUpdateDialog" = "¿Realmente deseas actualizar el geofichero?" +"geofileUpdateDialogDesc" = "Esto actualizará el archivo #filename#." +"geofileUpdatePopover" = "Geofichero actualizado correctamente" "dontRefresh" = "La instalación está en progreso, por favor no actualices esta página." "logs" = "Registros" "config" = "Configuración" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Contraseña predeterminada: {{ .ClientPassword }}\n\nIntroduce tu contraseña." "email_prompt" = "📧 Correo electrónico predeterminado: {{ .ClientEmail }}\n\nIntroduce tu correo electrónico." "comment_prompt" = "💬 Comentario predeterminado: {{ .ClientComment }}\n\nIntroduce tu comentario." -"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de vencimiento: {{ .ClientExp }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!" -"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Contraseña: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de vencimiento: {{ .ClientExp }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!" +"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!" +"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Contraseña: {{ .ClientPass }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!" "cancel" = "❌ ¡Proceso cancelado! \n\nPuedes /start de nuevo en cualquier momento. 🔄" "error_add_client" = "⚠️ Error:\n\n {{ .error }}" "using_default_value" = "Está bien, me quedaré con el valor predeterminado. 😊" "incorrect_input" ="Tu entrada no es válida.\nLas frases deben ser continuas sin espacios.\nEjemplo correcto: aaaaaa\nEjemplo incorrecto: aaa aaa 🚫" +"AreYouSure" = "¿Estás seguro? 🤔" +"SuccessResetTraffic" = "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ✅ Éxito" +"FailedResetTraffic" = "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ❌ Fallido \n\n🛠️ Error: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Proceso de reinicio de tráfico finalizado para todos los clientes." [tgbot.buttons] @@ -653,7 +661,8 @@ "change_password" = "⚙️🔑 Contraseña" "change_email" = "⚙️📧 Correo electrónico" "change_comment" = "⚙️💬 Comentario" - +"ResetAllTraffics" = "Reiniciar todo el tráfico" +"SortedTrafficUsageReport" = "Informe de uso de tráfico ordenado" [tgbot.answers] diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index dccb17e5..d104558d 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -124,8 +124,12 @@ "totalData" = "داده‌های کل" "sent" = "ارسال شده" "received" = "دریافت شده" -"xraySwitchVersionDialog" = "تغییر نسخه ایکس‌ری" -"xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه‌ مطمئن هستید؟" +"xraySwitchVersionDialog" = "آیا واقعاً می‌خواهید نسخه Xray را تغییر دهید؟" +"xraySwitchVersionDialogDesc" = "این کار نسخه Xray را به #version# تغییر می‌دهد." +"xraySwitchVersionPopover" = "Xray با موفقیت به‌روز شد" +"geofileUpdateDialog" = "آیا واقعاً می‌خواهید فایل جغرافیایی را به‌روز کنید؟" +"geofileUpdateDialogDesc" = "این عمل فایل #filename# را به‌روز می‌کند." +"geofileUpdatePopover" = "فایل جغرافیایی با موفقیت به‌روز شد" "dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید" "logs" = "گزارش‌ها" "config" = "پیکربندی" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 رمز عبور پیش‌فرض: {{ .ClientPassword }}\n\nرمز عبور خود را وارد کنید." "email_prompt" = "📧 ایمیل پیش‌فرض: {{ .ClientEmail }}\n\nایمیل خود را وارد کنید." "comment_prompt" = "💬 نظر پیش‌فرض: {{ .ClientComment }}\n\nنظر خود را وارد کنید." -"inbound_client_data_id" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 شناسه: {{ .ClientId }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n💬 نظر: {{ .ClientComment }}\n\nحالا می‌توانید مشتری را به ورودی اضافه کنید!" -"inbound_client_data_pass" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 رمز عبور: {{ .ClientPass }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n💬 نظر: {{ .ClientComment }}\n\nحالا می‌توانید مشتری را به ورودی اضافه کنید!" +"inbound_client_data_id" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 شناسه: {{ .ClientId }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n🌐 محدودیت IP: {{ .IpLimit }}\n💬 توضیح: {{ .ClientComment }}\n\nاکنون می‌تونی مشتری را به ورودی اضافه کنی!" +"inbound_client_data_pass" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 رمز عبور: {{ .ClientPass }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n🌐 محدودیت IP: {{ .IpLimit }}\n💬 توضیح: {{ .ClientComment }}\n\nاکنون می‌تونی مشتری را به ورودی اضافه کنی!" "cancel" = "❌ فرآیند لغو شد! \n\nمی‌توانید هر زمان که خواستید /start را دوباره اجرا کنید. 🔄" "error_add_client" = "⚠️ خطا:\n\n {{ .error }}" "using_default_value" = "باشه، از مقدار پیش‌فرض استفاده می‌کنم. 😊" "incorrect_input" ="ورودی شما معتبر نیست.\nعبارت‌ها باید بدون فاصله باشند.\nمثال صحیح: aaaaaa\nمثال نادرست: aaa aaa 🚫" +"AreYouSure" = "مطمئنی؟ 🤔" +"SuccessResetTraffic" = "📧 ایمیل: {{ .ClientEmail }}\n🏁 نتیجه: ✅ موفقیت‌آمیز" +"FailedResetTraffic" = "📧 ایمیل: {{ .ClientEmail }}\n🏁 نتیجه: ❌ ناموفق \n\n🛠️ خطا: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 فرآیند بازنشانی ترافیک برای همه مشتریان به پایان رسید." [tgbot.buttons] @@ -653,6 +661,9 @@ "change_password" = "⚙️🔑 گذرواژه" "change_email" = "⚙️📧 ایمیل" "change_comment" = "⚙️💬 نظر" +"ResetAllTraffics" = "بازنشانی همه ترافیک‌ها" +"SortedTrafficUsageReport" = "گزارش استفاده از ترافیک مرتب‌شده" + [tgbot.answers] "successfulOperation" = "✅ انجام شد!" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index 191f6311..c2a4dd7a 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -124,8 +124,12 @@ "totalData" = "Total data" "sent" = "Dikirim" "received" = "Diterima" -"xraySwitchVersionDialog" = "Ganti Versi Xray" -"xraySwitchVersionDialogDesc" = "Apakah Anda yakin ingin mengubah versi Xray menjadi" +"xraySwitchVersionDialog" = "Apakah Anda yakin ingin mengubah versi Xray?" +"xraySwitchVersionDialogDesc" = "Ini akan mengubah versi Xray ke #version#." +"xraySwitchVersionPopover" = "Xray berhasil diperbarui" +"geofileUpdateDialog" = "Apakah Anda yakin ingin memperbarui geofile?" +"geofileUpdateDialogDesc" = "Ini akan memperbarui file #filename#." +"geofileUpdatePopover" = "Geofile berhasil diperbarui" "dontRefresh" = "Instalasi sedang berlangsung, harap jangan menyegarkan halaman ini" "logs" = "Log" "config" = "Konfigurasi" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Kata Sandi Default: {{ .ClientPassword }}\n\nMasukkan kata sandi Anda." "email_prompt" = "📧 Email Default: {{ .ClientEmail }}\n\nMasukkan email Anda." "comment_prompt" = "💬 Komentar Default: {{ .ClientComment }}\n\nMasukkan komentar Anda." -"inbound_client_data_id" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu Lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang Anda bisa menambahkan klien ke masuk!" -"inbound_client_data_pass" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 Kata Sandi: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu Lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang Anda bisa menambahkan klien ke masuk!" +"inbound_client_data_id" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!" +"inbound_client_data_pass" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 Kata sandi: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!" "cancel" = "❌ Proses Dibatalkan! \n\nAnda dapat /start lagi kapan saja. 🔄" "error_add_client" = "⚠️ Kesalahan:\n\n {{ .error }}" "using_default_value" = "Oke, saya akan tetap menggunakan nilai default. 😊" "incorrect_input" ="Masukan Anda tidak valid.\nFrasa harus berlanjut tanpa spasi.\nContoh benar: aaaaaa\nContoh salah: aaa aaa 🚫" +"AreYouSure" = "Apakah kamu yakin? 🤔" +"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ✅ Berhasil" +"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ❌ Gagal \n\n🛠️ Kesalahan: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Proses reset traffic selesai untuk semua klien." [tgbot.buttons] @@ -653,7 +661,8 @@ "change_password" = "⚙️🔑 Kata Sandi" "change_email" = "⚙️📧 Email" "change_comment" = "⚙️💬 Komentar" - +"ResetAllTraffics" = "Reset Semua Lalu Lintas" +"SortedTrafficUsageReport" = "Laporan Penggunaan Lalu Lintas yang Terurut" [tgbot.answers] diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml index 3966ac4b..1332cbb2 100644 --- a/web/translation/translate.ja_JP.toml +++ b/web/translation/translate.ja_JP.toml @@ -124,8 +124,12 @@ "totalData" = "総データ量" "sent" = "送信" "received" = "受信" -"xraySwitchVersionDialog" = "Xrayバージョン切り替え" -"xraySwitchVersionDialogDesc" = "Xrayのバージョンを切り替えますか?" +"xraySwitchVersionDialog" = "Xrayのバージョンを本当に変更しますか?" +"xraySwitchVersionDialogDesc" = "Xrayのバージョンが#version#に変更されます。" +"xraySwitchVersionPopover" = "Xrayの更新が成功しました" +"geofileUpdateDialog" = "ジオファイルを本当に更新しますか?" +"geofileUpdateDialogDesc" = "これにより#filename#ファイルが更新されます。" +"geofileUpdatePopover" = "ジオファイルの更新が成功しました" "dontRefresh" = "インストール中、このページをリロードしないでください" "logs" = "ログ" "config" = "設定" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 デフォルトパスワード: {{ .ClientPassword }}\n\nパスワードを入力してください。" "email_prompt" = "📧 デフォルトメール: {{ .ClientEmail }}\n\nメールを入力してください。" "comment_prompt" = "💬 デフォルトコメント: {{ .ClientComment }}\n\nコメントを入力してください。" -"inbound_client_data_id" = "🔄 入力: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 期限日: {{ .ClientExp }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐクライアントをインバウンドに追加できます!" -"inbound_client_data_pass" = "🔄 入力: {{ .InboundRemark }}\n\n🔑 パスワード: {{ .ClientPass }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 期限日: {{ .ClientExp }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐクライアントをインバウンドに追加できます!" +"inbound_client_data_id" = "🔄 インバウンド: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 有効期限: {{ .ClientExp }}\n🌐 IP制限: {{ .IpLimit }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐこのクライアントをインバウンドに追加できます!" +"inbound_client_data_pass" = "🔄 インバウンド: {{ .InboundRemark }}\n\n🔑 パスワード: {{ .ClientPass }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 有効期限: {{ .ClientExp }}\n🌐 IP制限: {{ .IpLimit }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐこのクライアントをインバウンドに追加できます!" "cancel" = "❌ プロセスがキャンセルされました!\n\nいつでも /start で再開できます。 🔄" "error_add_client" = "⚠️ エラー:\n\n {{ .error }}" "using_default_value" = "わかりました、デフォルト値を使用します。 😊" "incorrect_input" ="入力が無効です。\nフレーズはスペースなしで続けて入力してください。\n正しい例: aaaaaa\n間違った例: aaa aaa 🚫" +"AreYouSure" = "本当にいいですか?🤔" +"SuccessResetTraffic" = "📧 メール: {{ .ClientEmail }}\n🏁 結果: ✅ 成功" +"FailedResetTraffic" = "📧 メール: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ エラー: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 すべてのクライアントのトラフィックリセットが完了しました。" [tgbot.buttons] @@ -653,6 +661,9 @@ "change_password" = "⚙️🔑 パスワード" "change_email" = "⚙️📧 メールアドレス" "change_comment" = "⚙️💬 コメント" +"ResetAllTraffics" = "すべてのトラフィックをリセット" +"SortedTrafficUsageReport" = "ソートされたトラフィック使用レポート" + [tgbot.answers] "successfulOperation" = "✅ 成功!" diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml index f6c2e88d..615392c4 100644 --- a/web/translation/translate.pt_BR.toml +++ b/web/translation/translate.pt_BR.toml @@ -124,8 +124,12 @@ "totalData" = "Dados totais" "sent" = "Enviado" "received" = "Recebido" -"xraySwitchVersionDialog" = "Alterar Versão do Xray" -"xraySwitchVersionDialogDesc" = "Tem certeza de que deseja alterar a versão do Xray para" +"xraySwitchVersionDialog" = "Você realmente deseja alterar a versão do Xray?" +"xraySwitchVersionDialogDesc" = "Isso mudará a versão do Xray para #version#." +"xraySwitchVersionPopover" = "Xray atualizado com sucesso" +"geofileUpdateDialog" = "Você realmente deseja atualizar o geofile?" +"geofileUpdateDialogDesc" = "Isso atualizará o arquivo #filename#." +"geofileUpdatePopover" = "Geofile atualizado com sucesso" "dontRefresh" = "Instalação em andamento, por favor não atualize a página" "logs" = "Logs" "config" = "Configuração" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Senha Padrão: {{ .ClientPassword }}\n\nDigite sua senha." "email_prompt" = "📧 E-mail Padrão: {{ .ClientEmail }}\n\nDigite seu e-mail." "comment_prompt" = "💬 Comentário Padrão: {{ .ClientComment }}\n\nDigite seu comentário." -"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!" -"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Senha: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!" +"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n🌐 Limite de IP: {{ .IpLimit }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!" +"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Senha: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n🌐 Limite de IP: {{ .IpLimit }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!" "cancel" = "❌ Processo Cancelado! \n\nVocê pode iniciar novamente a qualquer momento com /start. 🔄" "error_add_client" = "⚠️ Erro:\n\n {{ .error }}" "using_default_value" = "Tudo bem, vou manter o valor padrão. 😊" "incorrect_input" ="Sua entrada não é válida.\nAs frases devem ser contínuas, sem espaços.\nExemplo correto: aaaaaa\nExemplo incorreto: aaa aaa 🚫" +"AreYouSure" = "Você tem certeza? 🤔" +"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Resultado: ✅ Sucesso" +"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Resultado: ❌ Falhou \n\n🛠️ Erro: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Processo de redefinição de tráfego concluído para todos os clientes." [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 Senha" "change_email" = "⚙️📧 E-mail" "change_comment" = "⚙️💬 Comentário" +"ResetAllTraffics" = "Redefinir Todo o Tráfego" +"SortedTrafficUsageReport" = "Relatório de Uso de Tráfego Ordenado" [tgbot.answers] diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 634d393d..682ec273 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -124,8 +124,12 @@ "totalData" = "Общий объем данных" "sent" = "Отправлено" "received" = "Получено" -"xraySwitchVersionDialog" = "Переключить версию Xray" -"xraySwitchVersionDialogDesc" = "Вы точно хотите сменить версию Xray?" +"xraySwitchVersionDialog" = "Вы действительно хотите изменить версию Xray?" +"xraySwitchVersionDialogDesc" = "Это изменит версию Xray на #version#." +"xraySwitchVersionPopover" = "Xray успешно обновлён" +"geofileUpdateDialog" = "Вы действительно хотите обновить геофайл?" +"geofileUpdateDialogDesc" = "Это обновит файл #filename#." +"geofileUpdatePopover" = "Геофайл успешно обновлён" "dontRefresh" = "Установка в процессе. Не обновляйте страницу" "logs" = "Журнал" "config" = "Конфигурация" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Стандартный пароль: {{ .ClientPassword }}\n\nВведите ваш пароль." "email_prompt" = "📧 Стандартный email: {{ .ClientEmail }}\n\nВведите ваш email." "comment_prompt" = "💬 Стандартный комментарий: {{ .ClientComment }}\n\nВведите ваш комментарий." -"inbound_client_data_id" = "🔄 Входящие: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата истечения: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента во входящие!" -"inbound_client_data_pass" = "🔄 Входящие: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата истечения: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента во входящие!" +"inbound_client_data_id" = "🔄 Вход: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Почта: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата окончания: {{ .ClientExp }}\n🌐 Лимит IP: {{ .IpLimit }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента во входящий поток!" +"inbound_client_data_pass" = "🔄 Вход: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Почта: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата окончания: {{ .ClientExp }}\n🌐 Лимит IP: {{ .IpLimit }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента во входящий поток!" "cancel" = "❌ Процесс отменён! \n\nВы можете снова начать с /start в любое время. 🔄" "error_add_client" = "⚠️ Ошибка:\n\n {{ .error }}" "using_default_value" = "Хорошо, оставлю значение по умолчанию. 😊" "incorrect_input" ="Ваш ввод недействителен.\nФразы должны быть непрерывными без пробелов.\nПравильный пример: aaaaaa\nНеправильный пример: aaa aaa 🚫" +"AreYouSure" = "Вы уверены? 🤔" +"SuccessResetTraffic" = "📧 Почта: {{ .ClientEmail }}\n🏁 Результат: ✅ Успешно" +"FailedResetTraffic" = "📧 Почта: {{ .ClientEmail }}\n🏁 Результат: ❌ Неудача \n\n🛠️ Ошибка: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Сброс трафика завершён для всех клиентов." [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 Пароль" "change_email" = "⚙️📧 Электронная почта" "change_comment" = "⚙️💬 Комментарий" +"ResetAllTraffics" = "Сбросить весь трафик" +"SortedTrafficUsageReport" = "Отсортированный отчет об использовании трафика" [tgbot.answers] diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml index a43b68fb..80b76539 100644 --- a/web/translation/translate.tr_TR.toml +++ b/web/translation/translate.tr_TR.toml @@ -124,8 +124,12 @@ "totalData" = "Toplam veri" "sent" = "Gönderilen" "received" = "Alınan" -"xraySwitchVersionDialog" = "Xray Sürümünü Değiştir" -"xraySwitchVersionDialogDesc" = "Xray sürümünü değiştirmek istediğinizden emin misiniz" +"xraySwitchVersionDialog" = "Xray sürümünü gerçekten değiştirmek istiyor musunuz?" +"xraySwitchVersionDialogDesc" = "Bu işlem Xray sürümünü #version# olarak değiştirecektir." +"xraySwitchVersionPopover" = "Xray başarıyla güncellendi" +"geofileUpdateDialog" = "Geofile'ı gerçekten güncellemek istiyor musunuz?" +"geofileUpdateDialogDesc" = "Bu işlem #filename# dosyasını güncelleyecektir." +"geofileUpdatePopover" = "Geofile başarıyla güncellendi" "dontRefresh" = "Kurulum devam ediyor, lütfen bu sayfayı yenilemeyin" "logs" = "Günlükler" "config" = "Yapılandırma" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Varsayılan Şifre: {{ .ClientPassword }}\n\nŞifrenizi girin." "email_prompt" = "📧 Varsayılan E-posta: {{ .ClientEmail }}\n\nE-postanızı girin." "comment_prompt" = "💬 Varsayılan Yorum: {{ .ClientComment }}\n\nYorumunuzu girin." -"inbound_client_data_id" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Son Kullanım Tarihi: {{ .ClientExp }}\n💬 Yorum: {{ .ClientComment }}\n\nŞimdi müşteriyi girişe ekleyebilirsiniz!" -"inbound_client_data_pass" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Şifre: {{ .ClientPass }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Son Kullanım Tarihi: {{ .ClientExp }}\n💬 Yorum: {{ .ClientComment }}\n\nŞimdi müşteriyi girişe ekleyebilirsiniz!" +"inbound_client_data_id" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Kimlik: {{ .ClientId }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Bitiş Tarihi: {{ .ClientExp }}\n🌐 IP Sınırı: {{ .IpLimit }}\n💬 Yorum: {{ .ClientComment }}\n\nArtık bu müşteriyi girişe ekleyebilirsin!" +"inbound_client_data_pass" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Şifre: {{ .ClientPass }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Bitiş Tarihi: {{ .ClientExp }}\n🌐 IP Sınırı: {{ .IpLimit }}\n💬 Yorum: {{ .ClientComment }}\n\nArtık bu müşteriyi girişe ekleyebilirsin!" "cancel" = "❌ İşlem iptal edildi! \n\nİstediğiniz zaman /start ile yeniden başlayabilirsiniz. 🔄" "error_add_client" = "⚠️ Hata:\n\n {{ .error }}" "using_default_value" = "Tamam, varsayılan değeri kullanacağım. 😊" "incorrect_input" ="Girdiğiniz değer geçerli değil.\nKelime öbekleri boşluk olmadan devam etmelidir.\nDoğru örnek: aaaaaa\nYanlış örnek: aaa aaa 🚫" +"AreYouSure" = "Emin misin? 🤔" +"SuccessResetTraffic" = "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ✅ Başarılı" +"FailedResetTraffic" = "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ❌ Başarısız \n\n🛠️ Hata: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Tüm müşteriler için trafik sıfırlama işlemi tamamlandı." [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 Şifre" "change_email" = "⚙️📧 E-posta" "change_comment" = "⚙️💬 Yorum" +"ResetAllTraffics" = "Tüm Trafikleri Sıfırla" +"SortedTrafficUsageReport" = "Sıralı Trafik Kullanım Raporu" [tgbot.answers] diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index 4825ea37..b1313ee0 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -124,8 +124,12 @@ "totalData" = "Загальний обсяг даних" "sent" = "Відправлено" "received" = "Отримано" -"xraySwitchVersionDialog" = "Змінити версію Xray" -"xraySwitchVersionDialogDesc" = "Ви впевнені, що бажаєте змінити версію Xray на" +"xraySwitchVersionDialog" = "Ви дійсно хочете змінити версію Xray?" +"xraySwitchVersionDialogDesc" = "Це змінить версію Xray на #version#." +"xraySwitchVersionPopover" = "Xray успішно оновлено" +"geofileUpdateDialog" = "Ви дійсно хочете оновити геофайл?" +"geofileUpdateDialogDesc" = "Це оновить файл #filename#." +"geofileUpdatePopover" = "Геофайл успішно оновлено" "dontRefresh" = "Інсталяція триває, будь ласка, не оновлюйте цю сторінку" "logs" = "Журнали" "config" = "Конфігурація" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Стандартний пароль: {{ .ClientPassword }}\n\nВведіть ваш пароль." "email_prompt" = "📧 Стандартний email: {{ .ClientEmail }}\n\nВведіть ваш email." "comment_prompt" = "💬 Стандартний коментар: {{ .ClientComment }}\n\nВведіть ваш коментар." -"inbound_client_data_id" = "🔄 Вхідні: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Термін придатності: {{ .ClientExp }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідних!" -"inbound_client_data_pass" = "🔄 Вхідні: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Термін придатності: {{ .ClientExp }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідних!" +"inbound_client_data_id" = "🔄 Вхід: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Електронна пошта: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Дата завершення: {{ .ClientExp }}\n🌐 Обмеження IP: {{ .IpLimit }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідного з'єднання!" +"inbound_client_data_pass" = "🔄 Вхід: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Електронна пошта: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Дата завершення: {{ .ClientExp }}\n🌐 Обмеження IP: {{ .IpLimit }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідного з'єднання!" "cancel" = "❌ Процес скасовано! \n\nВи можете знову розпочати, використовуючи /start у будь-який час. 🔄" "error_add_client" = "⚠️ Помилка:\n\n {{ .error }}" "using_default_value" = "Гаразд, залишу значення за замовчуванням. 😊" "incorrect_input" ="Ваш ввід невірний.\nФрази повинні бути без пробілів.\nПравильний приклад: aaaaaa\nНеправильний приклад: aaa aaa 🚫" +"AreYouSure" = "Ви впевнені? 🤔" +"SuccessResetTraffic" = "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ✅ Успішно" +"FailedResetTraffic" = "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ❌ Невдача \n\n🛠️ Помилка: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Процес скидання трафіку завершено для всіх клієнтів." [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 Пароль" "change_email" = "⚙️📧 Електронна пошта" "change_comment" = "⚙️💬 Коментар" +"ResetAllTraffics" = "Скинути весь трафік" +"SortedTrafficUsageReport" = "Відсортований звіт про використання трафіку" [tgbot.answers] diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index ef10ddc7..592c6630 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -124,8 +124,12 @@ "totalData" = "Tổng dữ liệu" "sent" = "Đã gửi" "received" = "Đã nhận" -"xraySwitchVersionDialog" = "Chuyển đổi Phiên bản Xray" -"xraySwitchVersionDialogDesc" = "Bạn có chắc chắn muốn chuyển đổi phiên bản Xray sang" +"xraySwitchVersionDialog" = "Bạn có chắc chắn muốn thay đổi phiên bản Xray không?" +"xraySwitchVersionDialogDesc" = "Hành động này sẽ thay đổi phiên bản Xray thành #version#." +"xraySwitchVersionPopover" = "Xray đã được cập nhật thành công" +"geofileUpdateDialog" = "Bạn có chắc chắn muốn cập nhật geofile không?" +"geofileUpdateDialogDesc" = "Hành động này sẽ cập nhật tệp #filename#." +"geofileUpdatePopover" = "Geofile đã được cập nhật thành công" "dontRefresh" = "Đang tiến hành cài đặt, vui lòng không làm mới trang này." "logs" = "Nhật ký" "config" = "Cấu hình" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 Mật khẩu mặc định: {{ .ClientPassword }}\n\nVui lòng nhập mật khẩu của bạn." "email_prompt" = "📧 Email mặc định: {{ .ClientEmail }}\n\nVui lòng nhập email của bạn." "comment_prompt" = "💬 Bình luận mặc định: {{ .ClientComment }}\n\nVui lòng nhập bình luận của bạn." -"inbound_client_data_id" = "🔄 Vào: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lưu lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n💬 Bình luận: {{ .ClientComment }}\n\nBạn có thể thêm khách hàng vào vào ngay bây giờ!" -"inbound_client_data_pass" = "🔄 Vào: {{ .InboundRemark }}\n\n🔑 Mật khẩu: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lưu lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n💬 Bình luận: {{ .ClientComment }}\n\nBạn có thể thêm khách hàng vào vào ngay bây giờ!" +"inbound_client_data_id" = "🔄 Kết nối vào: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Dung lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n🌐 Giới hạn IP: {{ .IpLimit }}\n💬 Ghi chú: {{ .ClientComment }}\n\nBây giờ bạn có thể thêm khách hàng vào inbound!" +"inbound_client_data_pass" = "🔄 Kết nối vào: {{ .InboundRemark }}\n\n🔑 Mật khẩu: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Dung lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n🌐 Giới hạn IP: {{ .IpLimit }}\n💬 Ghi chú: {{ .ClientComment }}\n\nBây giờ bạn có thể thêm khách hàng vào inbound!" "cancel" = "❌ Quá trình đã bị hủy! \n\nBạn có thể bắt đầu lại bất cứ lúc nào bằng cách nhập /start. 🔄" "error_add_client" = "⚠️ Lỗi:\n\n {{ .error }}" "using_default_value" = "Được rồi, tôi sẽ sử dụng giá trị mặc định. 😊" "incorrect_input" ="Dữ liệu bạn nhập không hợp lệ.\nCác chuỗi phải liền mạch và không có dấu cách.\nVí dụ đúng: aaaaaa\nVí dụ sai: aaa aaa 🚫" +"AreYouSure" = "Bạn có chắc không? 🤔" +"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ✅ Thành công" +"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ❌ Thất bại \n\n🛠️ Lỗi: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 Quá trình đặt lại lưu lượng đã hoàn tất cho tất cả khách hàng." [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 Mật Khẩu" "change_email" = "⚙️📧 Email" "change_comment" = "⚙️💬 Bình Luận" +"ResetAllTraffics" = "Đặt lại tất cả lưu lượng" +"SortedTrafficUsageReport" = "Báo cáo sử dụng lưu lượng đã sắp xếp" [tgbot.answers] diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index 3769d89a..cca6f1e5 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -124,8 +124,12 @@ "totalData" = "总数据" "sent" = "已发送" "received" = "已接收" -"xraySwitchVersionDialog" = "切换 Xray 版本" -"xraySwitchVersionDialogDesc" = "是否切换 Xray 版本至" +"xraySwitchVersionDialog" = "您确定要更改Xray版本吗?" +"xraySwitchVersionDialogDesc" = "这将把Xray版本更改为#version#。" +"xraySwitchVersionPopover" = "Xray 更新成功" +"geofileUpdateDialog" = "您确定要更新地理文件吗?" +"geofileUpdateDialogDesc" = "这将更新 #filename# 文件。" +"geofileUpdatePopover" = "地理文件更新成功" "dontRefresh" = "安装中,请勿刷新此页面" "logs" = "日志" "config" = "配置" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 默认密码: {{ .ClientPassword }}\n\n请输入您的密码。" "email_prompt" = "📧 默认邮箱: {{ .ClientEmail }}\n\n请输入您的邮箱。" "comment_prompt" = "💬 默认评论: {{ .ClientComment }}\n\n请输入您的评论。" -"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 过期日期: {{ .ClientExp }}\n💬 评论: {{ .ClientComment }}\n\n您现在可以将客户添加到入站!" -"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密码: {{ .ClientPass }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 过期日期: {{ .ClientExp }}\n💬 评论: {{ .ClientComment }}\n\n您现在可以将客户添加到入站!" +"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日期: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 备注: {{ .ClientComment }}\n\n你现在可以将客户添加到入站了!" +"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密码: {{ .ClientPass }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日期: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 备注: {{ .ClientComment }}\n\n你现在可以将客户添加到入站了!" "cancel" = "❌ 进程已取消!\n\n您可以随时使用 /start 重新开始。 🔄" "error_add_client" = "⚠️ 错误:\n\n {{ .error }}" "using_default_value" = "好的,我会使用默认值。 😊" "incorrect_input" ="您的输入无效。\n短语应连续输入,不能有空格。\n正确示例: aaaaaa\n错误示例: aaa aaa 🚫" +"AreYouSure" = "你确定吗?🤔" +"SuccessResetTraffic" = "📧 邮箱: {{ .ClientEmail }}\n🏁 结果: ✅ 成功" +"FailedResetTraffic" = "📧 邮箱: {{ .ClientEmail }}\n🏁 结果: ❌ 失败 \n\n🛠️ 错误: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 所有客户的流量重置已完成。" [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 密码" "change_email" = "⚙️📧 邮箱" "change_comment" = "⚙️💬 评论" +"ResetAllTraffics" = "重置所有流量" +"SortedTrafficUsageReport" = "排序的流量使用报告" [tgbot.answers] diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml index 8dfc13a5..dd648677 100644 --- a/web/translation/translate.zh_TW.toml +++ b/web/translation/translate.zh_TW.toml @@ -124,8 +124,12 @@ "totalData" = "總數據" "sent" = "已發送" "received" = "已接收" -"xraySwitchVersionDialog" = "切換 Xray 版本" -"xraySwitchVersionDialogDesc" = "是否切換 Xray 版本至" +"xraySwitchVersionDialog" = "您確定要變更Xray版本嗎?" +"xraySwitchVersionDialogDesc" = "這將會把Xray版本變更為#version#。" +"xraySwitchVersionPopover" = "Xray 更新成功" +"geofileUpdateDialog" = "您確定要更新地理檔案嗎?" +"geofileUpdateDialogDesc" = "這將更新 #filename# 檔案。" +"geofileUpdatePopover" = "地理檔案更新成功" "dontRefresh" = "安裝中,請勿重新整理此頁面" "logs" = "日誌" "config" = "配置" @@ -603,12 +607,16 @@ "pass_prompt" = "🔑 預設密碼: {{ .ClientPassword }}\n\n請輸入您的密碼。" "email_prompt" = "📧 預設電子郵件: {{ .ClientEmail }}\n\n請輸入您的電子郵件。" "comment_prompt" = "💬 預設評論: {{ .ClientComment }}\n\n請輸入您的評論。" -"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n💬 評論: {{ .ClientComment }}\n\n您現在可以將客戶新增至入站!" -"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n💬 評論: {{ .ClientComment }}\n\n您現在可以將客戶新增至入站!" +"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!" +"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!" "cancel" = "❌ 程序已取消!\n\n您可以隨時使用 /start 重新開始。 🔄" "error_add_client" = "⚠️ 錯誤:\n\n {{ .error }}" "using_default_value" = "好的,我會使用預設值。 😊" "incorrect_input" ="您的輸入無效。\n短語應連續輸入,不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫" +"AreYouSure" = "你確定嗎?🤔" +"SuccessResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ✅ 成功" +"FailedResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ 錯誤: [ {{ .ErrorMessage }} ]" +"FinishProcess" = "🔚 所有客戶的流量重置已完成。" [tgbot.buttons] @@ -653,6 +661,8 @@ "change_password" = "⚙️🔑 密碼" "change_email" = "⚙️📧 電子郵件" "change_comment" = "⚙️💬 評論" +"ResetAllTraffics" = "重設所有流量" +"SortedTrafficUsageReport" = "排序過的流量使用報告" [tgbot.answers]