Browse Source

Major changes to tgbot, also small changes for panel (#1463)

* Reduce outage time on Xray errors

* Improved logs clearing, added previous logs
File name change: 3xipl-access-persistent.log -> 3xipl-ap.log
All previous logs have .prev suffix

* Preparations for tgbot additions

* [tgbot] Improvements, Additions and Fixes
* Changed interaction with Expire Date for Clients
* Added more info and interactions with Online Clients
* Added a way to get Ban Logs (also added them to backup)
* Few fixes and optimizations in code
* Fixed RU translation

* [tgbot] More updates and fixes

* [tgbot] Quick Fix

* [tgbot] Quick Fix 2

* [tgbot] Big Updates
Added Notifications for Clients throught Tgbot (when Expire)
Added compability for Usernames both w/wo @
Added more buttons overall for admins

* [tgbot] Fixes

* [tbot] Fixes 2

* [tgbot] Removed usernames support for Notifications to work

* [tgbot] Fix

* [tgbot] Fix Notify

* [tgbot] small fixes

* [tgbot] replyMarkup only for last message on big messages

* [tgbot] Fixed last message is empty

* [tgbot] Fix messages split
somebodywashere 1 year ago
parent
commit
ceee1e4277

+ 3 - 0
web/job/check_client_ip_job.go

@@ -22,8 +22,11 @@ var job *CheckClientIpJob
 var disAllowedIps []string
 var disAllowedIps []string
 var ipFiles = []string{
 var ipFiles = []string{
 	xray.GetIPLimitLogPath(),
 	xray.GetIPLimitLogPath(),
+xray.GetIPLimitPrevLogPath(),
 	xray.GetIPLimitBannedLogPath(),
 	xray.GetIPLimitBannedLogPath(),
+xray.GetIPLimitBannedPrevLogPath(),
 	xray.GetAccessPersistentLogPath(),
 	xray.GetAccessPersistentLogPath(),
+xray.GetAccessPersistentPrevLogPath(),
 }
 }
 
 
 func NewCheckClientIpJob() *CheckClientIpJob {
 func NewCheckClientIpJob() *CheckClientIpJob {

+ 14 - 7
web/job/check_xray_running_job.go

@@ -1,6 +1,9 @@
 package job
 package job
 
 
-import "x-ui/web/service"
+import (
+	"x-ui/logger"
+	"x-ui/web/service"
+)
 
 
 type CheckXrayRunningJob struct {
 type CheckXrayRunningJob struct {
 	xrayService service.XrayService
 	xrayService service.XrayService
@@ -15,11 +18,15 @@ func NewCheckXrayRunningJob() *CheckXrayRunningJob {
 func (j *CheckXrayRunningJob) Run() {
 func (j *CheckXrayRunningJob) Run() {
 	if j.xrayService.IsXrayRunning() {
 	if j.xrayService.IsXrayRunning() {
 		j.checkTime = 0
 		j.checkTime = 0
-		return
+	} else {
+		j.checkTime++
+		//only restart if it's down 2 times in a row
+		if j.checkTime > 1 {
+			err := j.xrayService.RestartXray(false)
+			j.checkTime = 0
+			if err != nil {
+				logger.Error("Restart xray failed:", err)
+			}
+		}
 	}
 	}
-	j.checkTime++
-	if j.checkTime < 2 {
-		return
-	}
-	j.xrayService.SetToNeedRestart()
 }
 }

+ 29 - 2
web/job/clear_logs_job.go

@@ -15,10 +15,37 @@ func NewClearLogsJob() *ClearLogsJob {
 // Here Run is an interface method of the Job interface
 // Here Run is an interface method of the Job interface
 func (j *ClearLogsJob) Run() {
 func (j *ClearLogsJob) Run() {
 	logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()}
 	logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()}
+	logFilesPrev := []string{xray.GetIPLimitPrevLogPath(), xray.GetIPLimitBannedPrevLogPath(), xray.GetAccessPersistentPrevLogPath()}
 
 
-	// clear log files
+	// clear old previous logs
+	for i := 0; i < len(logFilesPrev); i++ {
+		if err := os.Truncate(logFilesPrev[i], 0); err != nil {
+			logger.Warning("clear logs job err:", err)
+		}
+	}
+
+	// clear log files and copy to previous logs
 	for i := 0; i < len(logFiles); i++ {
 	for i := 0; i < len(logFiles); i++ {
-		if err := os.Truncate(logFiles[i], 0); err != nil {
+		
+		// copy to previous logs
+		logFilePrev, err := os.OpenFile(logFilesPrev[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
+		if err != nil {
+			logger.Warning("clear logs job err:", err)
+		}
+
+		logFile, err := os.ReadFile(logFiles[i])
+		if err != nil {
+			logger.Warning("clear logs job err:", err)
+		}
+
+		_, err = logFilePrev.Write(logFile)
+		if err != nil {
+			logger.Warning("clear logs job err:", err)
+		}
+		defer logFilePrev.Close()
+
+		err = os.Truncate(logFiles[i], 0)
+		if err != nil {
 			logger.Warning("clear logs job err:", err)
 			logger.Warning("clear logs job err:", err)
 		}
 		}
 	}
 	}

+ 39 - 3
web/service/inbound.go

@@ -1146,6 +1146,8 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err
 		if oldClient.Email == clientEmail {
 		if oldClient.Email == clientEmail {
 			if inbound.Protocol == "trojan" {
 			if inbound.Protocol == "trojan" {
 				clientId = oldClient.Password
 				clientId = oldClient.Password
+			} else if inbound.Protocol == "shadowsocks" {
+				clientId = oldClient.Email
 			} else {
 			} else {
 				clientId = oldClient.ID
 				clientId = oldClient.ID
 			}
 			}
@@ -1184,6 +1186,32 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err
 	return nil
 	return nil
 }
 }
 
 
+func (s *InboundService) checkIsEnabledByEmail(clientEmail string) (bool, error) {
+	_, inbound, err := s.GetClientInboundByEmail(clientEmail)
+	if err != nil {
+		return false, err
+	}
+	if inbound == nil {
+		return false, common.NewError("Inbound Not Found For Email:", clientEmail)
+	}
+
+	clients, err := s.GetClients(inbound)
+	if err != nil {
+		return false, err
+	}
+
+	isEnable := false
+
+	for _, client := range clients {
+		if client.Email == clientEmail {
+			isEnable = client.Enable
+			break
+		}
+	}
+
+	return isEnable, err
+}
+
 func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, error) {
 func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, error) {
 	_, inbound, err := s.GetClientInboundByEmail(clientEmail)
 	_, inbound, err := s.GetClientInboundByEmail(clientEmail)
 	if err != nil {
 	if err != nil {
@@ -1205,6 +1233,8 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, er
 		if oldClient.Email == clientEmail {
 		if oldClient.Email == clientEmail {
 			if inbound.Protocol == "trojan" {
 			if inbound.Protocol == "trojan" {
 				clientId = oldClient.Password
 				clientId = oldClient.Password
+			} else if inbound.Protocol == "shadowsocks" {
+				clientId = oldClient.Email
 			} else {
 			} else {
 				clientId = oldClient.ID
 				clientId = oldClient.ID
 			}
 			}
@@ -1266,6 +1296,8 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
 		if oldClient.Email == clientEmail {
 		if oldClient.Email == clientEmail {
 			if inbound.Protocol == "trojan" {
 			if inbound.Protocol == "trojan" {
 				clientId = oldClient.Password
 				clientId = oldClient.Password
+			} else if inbound.Protocol == "shadowsocks" {
+				clientId = oldClient.Email
 			} else {
 			} else {
 				clientId = oldClient.ID
 				clientId = oldClient.ID
 			}
 			}
@@ -1324,6 +1356,8 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
 		if oldClient.Email == clientEmail {
 		if oldClient.Email == clientEmail {
 			if inbound.Protocol == "trojan" {
 			if inbound.Protocol == "trojan" {
 				clientId = oldClient.Password
 				clientId = oldClient.Password
+			} else if inbound.Protocol == "shadowsocks" {
+				clientId = oldClient.Email
 			} else {
 			} else {
 				clientId = oldClient.ID
 				clientId = oldClient.ID
 			}
 			}
@@ -1385,6 +1419,8 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota
 		if oldClient.Email == clientEmail {
 		if oldClient.Email == clientEmail {
 			if inbound.Protocol == "trojan" {
 			if inbound.Protocol == "trojan" {
 				clientId = oldClient.Password
 				clientId = oldClient.Password
+			} else if inbound.Protocol == "shadowsocks" {
+				clientId = oldClient.Email
 			} else {
 			} else {
 				clientId = oldClient.ID
 				clientId = oldClient.ID
 			}
 			}
@@ -1613,10 +1649,10 @@ func (s *InboundService) DelDepletedClients(id int) (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTraffic, error) {
+func (s *InboundService) GetClientTrafficTgBot(tgId string) ([]*xray.ClientTraffic, error) {
 	db := database.GetDB()
 	db := database.GetDB()
 	var inbounds []*model.Inbound
 	var inbounds []*model.Inbound
-	err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tguname)).Find(&inbounds).Error
+	err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tgId)).Find(&inbounds).Error
 	if err != nil && err != gorm.ErrRecordNotFound {
 	if err != nil && err != gorm.ErrRecordNotFound {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -1627,7 +1663,7 @@ func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTr
 			logger.Error("Unable to get clients from inbound")
 			logger.Error("Unable to get clients from inbound")
 		}
 		}
 		for _, client := range clients {
 		for _, client := range clients {
-			if client.TgID == tguname {
+			if client.TgID == tgId {
 				emails = append(emails, client.Email)
 				emails = append(emails, client.Email)
 			}
 			}
 		}
 		}

+ 407 - 291
web/service/tgbot.go

@@ -7,6 +7,7 @@ import (
 	"os"
 	"os"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+	"slices"
 	"time"
 	"time"
 	"x-ui/config"
 	"x-ui/config"
 	"x-ui/database"
 	"x-ui/database"
@@ -218,7 +219,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
 			if isAdmin {
 			if isAdmin {
 				t.searchClient(chatId, commandArgs[0])
 				t.searchClient(chatId, commandArgs[0])
 			} else {
 			} else {
-				t.searchForClient(chatId, commandArgs[0])
+				t.getClientUsage(chatId, strconv.FormatInt(message.From.ID, 10), commandArgs[0])
 			}
 			}
 		} else {
 		} else {
 			msg += t.I18nBot("tgbot.commands.usage")
 			msg += t.I18nBot("tgbot.commands.usage")
@@ -234,11 +235,14 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
 		msg += t.I18nBot("tgbot.commands.unknown")
 		msg += t.I18nBot("tgbot.commands.unknown")
 	}
 	}
 
 
-	if onlyMessage {
-		t.SendMsgToTgbot(chatId, msg)
-		return
+	if msg != ""{
+		if onlyMessage {
+			t.SendMsgToTgbot(chatId, msg)
+			return
+		} else {
+			t.SendAnswer(chatId, msg, isAdmin)
+		}
 	}
 	}
-	t.SendAnswer(chatId, msg, isAdmin)
 }
 }
 
 
 func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
 func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
@@ -257,6 +261,9 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 		if len(dataArray) >= 2 && len(dataArray[1]) > 0 {
 		if len(dataArray) >= 2 && len(dataArray[1]) > 0 {
 			email := dataArray[1]
 			email := dataArray[1]
 			switch dataArray[0] {
 			switch dataArray[0] {
+			case "client_get_usage":
+				t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.messages.email", "Email=="+email))
+				t.searchClient(chatId, email)
 			case "client_refresh":
 			case "client_refresh":
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.clientRefreshSuccess", "Email=="+email))
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.clientRefreshSuccess", "Email=="+email))
 				t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 				t.searchClient(chatId, email, callbackQuery.Message.MessageID)
@@ -352,7 +359,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 									inputNumber = 0
 									inputNumber = 0
 								} else if num == -1 {
 								} else if num == -1 {
 									if inputNumber > 0 {
 									if inputNumber > 0 {
-										inputNumber = (inputNumber / 10) ^ 0
+										inputNumber = (inputNumber / 10)
 									}
 									}
 								} else {
 								} else {
 									inputNumber = (inputNumber * 10) + num
 									inputNumber = (inputNumber * 10) + num
@@ -372,7 +379,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 								tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
 								tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
 							),
 							),
 							tu.InlineKeyboardRow(
 							tu.InlineKeyboardRow(
-								tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmNumber", "Num=="+strconv.Itoa(inputNumber))).WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" "+strconv.Itoa(inputNumber))),
+								tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmNumberAdd", "Num=="+strconv.Itoa(inputNumber))).WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" "+strconv.Itoa(inputNumber))),
 							),
 							),
 							tu.InlineKeyboardRow(
 							tu.InlineKeyboardRow(
 								tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("limit_traffic_in "+email+" "+strconv.Itoa(inputNumber)+" 1")),
 								tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("limit_traffic_in "+email+" "+strconv.Itoa(inputNumber)+" 1")),
@@ -411,20 +418,20 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 						tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("reset_exp_in "+email+" 0")),
 						tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("reset_exp_in "+email+" 0")),
 					),
 					),
 					tu.InlineKeyboardRow(
 					tu.InlineKeyboardRow(
-						tu.InlineKeyboardButton("1 "+t.I18nBot("tgbot.month")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 30")),
-						tu.InlineKeyboardButton("2 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 60")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 7 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 7")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 10 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 10")),
 					),
 					),
 					tu.InlineKeyboardRow(
 					tu.InlineKeyboardRow(
-						tu.InlineKeyboardButton("3 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 90")),
-						tu.InlineKeyboardButton("6 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 180")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 14 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 14")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 20 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 20")),
 					),
 					),
 					tu.InlineKeyboardRow(
 					tu.InlineKeyboardRow(
-						tu.InlineKeyboardButton("9 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 270")),
-						tu.InlineKeyboardButton("12 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 360")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 1 "+t.I18nBot("tgbot.month")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 30")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 3 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 90")),
 					),
 					),
 					tu.InlineKeyboardRow(
 					tu.InlineKeyboardRow(
-						tu.InlineKeyboardButton("10 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 10")),
-						tu.InlineKeyboardButton("20 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 20")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 6 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 180")),
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 12 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 365")),
 					),
 					),
 				)
 				)
 				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
 				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
@@ -434,7 +441,29 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 					if err == nil {
 					if err == nil {
 						var date int64 = 0
 						var date int64 = 0
 						if days > 0 {
 						if days > 0 {
-							date = int64(-(days * 24 * 60 * 60000))
+							traffic, err := t.inboundService.GetClientTrafficByEmail(email)
+							if err != nil {
+								logger.Warning(err)
+								msg := t.I18nBot("tgbot.wentWrong")
+								t.SendMsgToTgbot(chatId, msg)
+								return
+							}
+							if traffic == nil {
+								msg := t.I18nBot("tgbot.noResult")
+								t.SendMsgToTgbot(chatId, msg)
+								return
+							}
+
+							if traffic.ExpiryTime > 0 {
+								if traffic.ExpiryTime-time.Now().Unix()*1000 < 0 {
+									date = -int64(days * 24 * 60 * 60000)
+								} else {
+									date = traffic.ExpiryTime + int64(days*24*60*60000)
+								}
+							} else {
+								date = traffic.ExpiryTime - int64(days*24*60*60000)
+							}
+
 						}
 						}
 						err := t.inboundService.ResetClientExpiryTimeByEmail(email, date)
 						err := t.inboundService.ResetClientExpiryTimeByEmail(email, date)
 						if err == nil {
 						if err == nil {
@@ -459,7 +488,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 									inputNumber = 0
 									inputNumber = 0
 								} else if num == -1 {
 								} else if num == -1 {
 									if inputNumber > 0 {
 									if inputNumber > 0 {
-										inputNumber = (inputNumber / 10) ^ 0
+										inputNumber = (inputNumber / 10)
 									}
 									}
 								} else {
 								} else {
 									inputNumber = (inputNumber * 10) + num
 									inputNumber = (inputNumber * 10) + num
@@ -564,7 +593,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 									inputNumber = 0
 									inputNumber = 0
 								} else if num == -1 {
 								} else if num == -1 {
 									if inputNumber > 0 {
 									if inputNumber > 0 {
-										inputNumber = (inputNumber / 10) ^ 0
+										inputNumber = (inputNumber / 10)
 									}
 									}
 								} else {
 								} else {
 									inputNumber = (inputNumber * 10) + num
 									inputNumber = (inputNumber * 10) + num
@@ -661,6 +690,16 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
 				}
 				}
 			case "toggle_enable":
 			case "toggle_enable":
+				inlineKeyboard := tu.InlineKeyboard(
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
+					),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmToggle")).WithCallbackData(t.encodeQuery("toggle_enable_c "+email)),
+					),
+				)
+				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
+			case "toggle_enable_c":
 				enabled, err := t.inboundService.ToggleClientEnableByEmail(email)
 				enabled, err := t.inboundService.ToggleClientEnableByEmail(email)
 				if err == nil {
 				if err == nil {
 					t.xrayService.SetToNeedRestart()
 					t.xrayService.SetToNeedRestart()
@@ -678,24 +717,36 @@ func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
 		}
 		}
 	}
 	}
 
 
-	// Respond to the callback query, telling Telegram to show the user
-	// a message with the data received.
-	t.sendCallbackAnswerTgBot(callbackQuery.ID, callbackQuery.Data)
-
 	switch callbackQuery.Data {
 	switch callbackQuery.Data {
 	case "get_usage":
 	case "get_usage":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.serverUsage"))
 		t.SendMsgToTgbot(chatId, t.getServerUsage())
 		t.SendMsgToTgbot(chatId, t.getServerUsage())
 	case "inbounds":
 	case "inbounds":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.getInbounds"))
 		t.SendMsgToTgbot(chatId, t.getInboundUsages())
 		t.SendMsgToTgbot(chatId, t.getInboundUsages())
 	case "deplete_soon":
 	case "deplete_soon":
-		t.SendMsgToTgbot(chatId, t.getExhausted())
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.depleteSoon"))
+		t.getExhausted(chatId)
 	case "get_backup":
 	case "get_backup":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.dbBackup"))
 		t.sendBackup(chatId)
 		t.sendBackup(chatId)
+	case "get_banlogs":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.getBanLogs"))
+		t.sendBanLogs(chatId, true)
 	case "client_traffic":
 	case "client_traffic":
-		t.getClientUsage(chatId, callbackQuery.From.Username, strconv.FormatInt(callbackQuery.From.ID, 10))
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.clientUsage"))
+		t.getClientUsage(chatId, strconv.FormatInt(callbackQuery.From.ID, 10))
 	case "client_commands":
 	case "client_commands":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands"))
 		t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpClientCommands"))
 		t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpClientCommands"))
+	case "onlines":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.onlines"))
+		t.onlineClients(chatId)
+	case "onlines_refresh":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
+		t.onlineClients(chatId, callbackQuery.Message.MessageID)
 	case "commands":
 	case "commands":
+		t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands"))
 		t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpAdminCommands"))
 		t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpAdminCommands"))
 	}
 	}
 }
 }
@@ -713,7 +764,10 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
 	numericKeyboard := tu.InlineKeyboard(
 	numericKeyboard := tu.InlineKeyboard(
 		tu.InlineKeyboardRow(
 		tu.InlineKeyboardRow(
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.serverUsage")).WithCallbackData(t.encodeQuery("get_usage")),
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.serverUsage")).WithCallbackData(t.encodeQuery("get_usage")),
+		),
+		tu.InlineKeyboardRow(
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.dbBackup")).WithCallbackData(t.encodeQuery("get_backup")),
 			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.InlineKeyboardRow(
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.getInbounds")).WithCallbackData(t.encodeQuery("inbounds")),
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.getInbounds")).WithCallbackData(t.encodeQuery("inbounds")),
@@ -721,6 +775,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
 		),
 		),
 		tu.InlineKeyboardRow(
 		tu.InlineKeyboardRow(
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.commands")).WithCallbackData(t.encodeQuery("commands")),
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.commands")).WithCallbackData(t.encodeQuery("commands")),
+			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.onlines")).WithCallbackData(t.encodeQuery("onlines")),
 		),
 		),
 	)
 	)
 	numericKeyboardClient := tu.InlineKeyboard(
 	numericKeyboardClient := tu.InlineKeyboard(
@@ -754,7 +809,7 @@ func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.R
 
 
 	// paging message if it is big
 	// paging message if it is big
 	if len(msg) > limit {
 	if len(msg) > limit {
-		messages := strings.Split(msg, "\r\n \r\n")
+		messages := strings.Split(msg, "\r\n\r\n")
 		lastIndex := -1
 		lastIndex := -1
 
 
 		for _, message := range messages {
 		for _, message := range messages {
@@ -762,19 +817,23 @@ func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.R
 				allMessages = append(allMessages, message)
 				allMessages = append(allMessages, message)
 				lastIndex++
 				lastIndex++
 			} else {
 			} else {
-				allMessages[lastIndex] += "\r\n \r\n" + message
+				allMessages[lastIndex] += "\r\n\r\n" + message
 			}
 			}
 		}
 		}
+		if strings.TrimSpace(allMessages[len(allMessages)-1]) == "" {
+			allMessages = allMessages[:len(allMessages)-1]
+		}
 	} else {
 	} else {
 		allMessages = append(allMessages, msg)
 		allMessages = append(allMessages, msg)
 	}
 	}
-	for _, message := range allMessages {
+	for n, message := range allMessages {
 		params := telego.SendMessageParams{
 		params := telego.SendMessageParams{
 			ChatID:    tu.ID(chatId),
 			ChatID:    tu.ID(chatId),
 			Text:      message,
 			Text:      message,
 			ParseMode: "HTML",
 			ParseMode: "HTML",
 		}
 		}
-		if len(replyMarkup) > 0 {
+		//only add replyMarkup to last message
+		if len(replyMarkup) > 0 && n == (len(allMessages)-1) {
 			params.ReplyMarkup = replyMarkup[0]
 			params.ReplyMarkup = replyMarkup[0]
 		}
 		}
 		_, err := bot.SendMessage(&params)
 		_, err := bot.SendMessage(&params)
@@ -785,9 +844,15 @@ func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.R
 	}
 	}
 }
 }
 
 
-func (t *Tgbot) SendMsgToTgbotAdmins(msg string) {
-	for _, adminId := range adminIds {
-		t.SendMsgToTgbot(adminId, msg)
+func (t *Tgbot) SendMsgToTgbotAdmins(msg string, replyMarkup ...telego.ReplyMarkup) {
+	if len(replyMarkup) > 0 {
+		for _, adminId := range adminIds {
+			t.SendMsgToTgbot(adminId, msg, replyMarkup[0])
+		}
+	} else {
+		for _, adminId := range adminIds {
+			t.SendMsgToTgbot(adminId, msg)
+		}
 	}
 	}
 }
 }
 
 
@@ -803,8 +868,8 @@ func (t *Tgbot) SendReport() {
 	info := t.getServerUsage()
 	info := t.getServerUsage()
 	t.SendMsgToTgbotAdmins(info)
 	t.SendMsgToTgbotAdmins(info)
 
 
-	exhausted := t.getExhausted()
-	t.SendMsgToTgbotAdmins(exhausted)
+	t.sendExhaustedToAdmins()
+	t.notifyExhausted()
 
 
 	backupEnable, err := t.settingService.GetTgBotBackup()
 	backupEnable, err := t.settingService.GetTgBotBackup()
 	if err == nil && backupEnable {
 	if err == nil && backupEnable {
@@ -821,6 +886,15 @@ func (t *Tgbot) SendBackupToAdmins() {
 	}
 	}
 }
 }
 
 
+func (t *Tgbot) sendExhaustedToAdmins() {
+	if !t.IsRunning() {
+		return
+	}
+	for _, adminId := range adminIds {
+		t.getExhausted(int64(adminId))
+	}
+}
+
 func (t *Tgbot) getServerUsage() string {
 func (t *Tgbot) getServerUsage() string {
 	info, ipv4, ipv6 := "", "", ""
 	info, ipv4, ipv6 := "", "", ""
 	info += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
 	info += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
@@ -831,7 +905,7 @@ func (t *Tgbot) getServerUsage() string {
 	if err != nil {
 	if err != nil {
 		logger.Error("net.Interfaces failed, err: ", err.Error())
 		logger.Error("net.Interfaces failed, err: ", err.Error())
 		info += t.I18nBot("tgbot.messages.ip", "IP=="+t.I18nBot("tgbot.unknown"))
 		info += t.I18nBot("tgbot.messages.ip", "IP=="+t.I18nBot("tgbot.unknown"))
-		info += " \r\n"
+		info += "\r\n"
 	} else {
 	} else {
 		for i := 0; i < len(netInterfaces); i++ {
 		for i := 0; i < len(netInterfaces); i++ {
 			if (netInterfaces[i].Flags & net.FlagUp) != 0 {
 			if (netInterfaces[i].Flags & net.FlagUp) != 0 {
@@ -855,9 +929,11 @@ func (t *Tgbot) getServerUsage() string {
 
 
 	// get latest status of server
 	// get latest status of server
 	t.lastStatus = t.serverService.GetStatus(t.lastStatus)
 	t.lastStatus = t.serverService.GetStatus(t.lastStatus)
+	onlines := p.GetOnlineClients()
 	info += t.I18nBot("tgbot.messages.serverUpTime", "UpTime=="+strconv.FormatUint(t.lastStatus.Uptime/86400, 10), "Unit=="+t.I18nBot("tgbot.days"))
 	info += t.I18nBot("tgbot.messages.serverUpTime", "UpTime=="+strconv.FormatUint(t.lastStatus.Uptime/86400, 10), "Unit=="+t.I18nBot("tgbot.days"))
 	info += t.I18nBot("tgbot.messages.serverLoad", "Load1=="+strconv.FormatFloat(t.lastStatus.Loads[0], 'f', 2, 64), "Load2=="+strconv.FormatFloat(t.lastStatus.Loads[1], 'f', 2, 64), "Load3=="+strconv.FormatFloat(t.lastStatus.Loads[2], 'f', 2, 64))
 	info += t.I18nBot("tgbot.messages.serverLoad", "Load1=="+strconv.FormatFloat(t.lastStatus.Loads[0], 'f', 2, 64), "Load2=="+strconv.FormatFloat(t.lastStatus.Loads[1], 'f', 2, 64), "Load3=="+strconv.FormatFloat(t.lastStatus.Loads[2], 'f', 2, 64))
 	info += t.I18nBot("tgbot.messages.serverMemory", "Current=="+common.FormatTraffic(int64(t.lastStatus.Mem.Current)), "Total=="+common.FormatTraffic(int64(t.lastStatus.Mem.Total)))
 	info += t.I18nBot("tgbot.messages.serverMemory", "Current=="+common.FormatTraffic(int64(t.lastStatus.Mem.Current)), "Total=="+common.FormatTraffic(int64(t.lastStatus.Mem.Total)))
+	info += t.I18nBot("tgbot.messages.onlinesCount", "Count=="+fmt.Sprint(len(onlines)))
 	info += t.I18nBot("tgbot.messages.tcpCount", "Count=="+strconv.Itoa(t.lastStatus.TcpCount))
 	info += t.I18nBot("tgbot.messages.tcpCount", "Count=="+strconv.Itoa(t.lastStatus.TcpCount))
 	info += t.I18nBot("tgbot.messages.udpCount", "Count=="+strconv.Itoa(t.lastStatus.UdpCount))
 	info += t.I18nBot("tgbot.messages.udpCount", "Count=="+strconv.Itoa(t.lastStatus.UdpCount))
 	info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Sent+t.lastStatus.NetTraffic.Recv)), "Upload=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Sent)), "Download=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Recv)))
 	info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Sent+t.lastStatus.NetTraffic.Recv)), "Upload=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Sent)), "Download=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Recv)))
@@ -914,85 +990,136 @@ func (t *Tgbot) getInboundUsages() string {
 			} else {
 			} else {
 				info += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
 				info += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
 			}
 			}
+			info += "\r\n"
 		}
 		}
 	}
 	}
 	return info
 	return info
 }
 }
 
 
-func (t *Tgbot) getClientUsage(chatId int64, tgUserName string, tgUserID string) {
-	traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
+func (t *Tgbot) clientInfoMsg(traffic *xray.ClientTraffic, printEnabled bool, printOnline bool, printActive bool,
+								printDate bool, printTraffic bool, printRefreshed bool) string {
+
+	now := time.Now().Unix()
+	expiryTime := ""
+	flag := false
+	diff := traffic.ExpiryTime/1000 - now
+	if traffic.ExpiryTime == 0 {
+		expiryTime = t.I18nBot("tgbot.unlimited")
+	} else if diff > 172800 || !traffic.Enable {
+		expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
+	} else if traffic.ExpiryTime < 0 {
+		expiryTime = fmt.Sprintf("%d %s", traffic.ExpiryTime/-86400000, t.I18nBot("tgbot.days"))
+		flag = true
+	} else {
+		expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours"))
+		flag = true
+	}
+
+	total := ""
+	if traffic.Total == 0 {
+		total = t.I18nBot("tgbot.unlimited")
+	} else {
+		total = common.FormatTraffic((traffic.Total))
+	}
+
+	enabled := ""
+	isEnabled, err := t.inboundService.checkIsEnabledByEmail(traffic.Email)
 	if err != nil {
 	if err != nil {
 		logger.Warning(err)
 		logger.Warning(err)
-		msg := t.I18nBot("tgbot.wentWrong")
-		t.SendMsgToTgbot(chatId, msg)
-		return
+		enabled = t.I18nBot("tgbot.wentWrong")
+	} else if isEnabled {
+		enabled = t.I18nBot("tgbot.messages.yes")
+	} else {
+		enabled = t.I18nBot("tgbot.messages.no")
 	}
 	}
 
 
-	if len(traffics) == 0 {
-		if len(tgUserName) == 0 {
-			msg := t.I18nBot("tgbot.answers.askToAddUserId", "TgUserID=="+tgUserID)
-			t.SendMsgToTgbot(chatId, msg)
-			return
+	active := ""
+	if traffic.Enable {
+		active = t.I18nBot("tgbot.messages.yes")
+	} else {
+		active = t.I18nBot("tgbot.messages.no")
+	}
+
+	status := t.I18nBot("tgbot.offline")
+	if p.IsRunning() {
+		for _, online := range p.GetOnlineClients() {
+			if online == traffic.Email {
+				status = t.I18nBot("tgbot.online")
+				break
+			}
+		}
+	}
+
+	output := ""
+	output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email)
+	if printEnabled {
+		output += t.I18nBot("tgbot.messages.enabled", "Enable=="+enabled)
+	}
+	if printOnline {
+		output += t.I18nBot("tgbot.messages.online", "Status=="+status)
+	}
+	if printActive {
+		output += t.I18nBot("tgbot.messages.active", "Enable=="+active)
+	}
+	if printDate {
+		if flag {
+			output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime)
+		} else {
+			output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
 		}
 		}
-		traffics, err = t.inboundService.GetClientTrafficTgBot(tgUserName)
 	}
 	}
+	if printTraffic {
+		output += t.I18nBot("tgbot.messages.upload", "Upload=="+common.FormatTraffic(traffic.Up))
+		output += t.I18nBot("tgbot.messages.download", "Download=="+common.FormatTraffic(traffic.Down))
+		output += t.I18nBot("tgbot.messages.total", "UpDown=="+common.FormatTraffic((traffic.Up+traffic.Down)), "Total=="+total)
+	}
+	if printRefreshed {
+		output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
+	}
+
+	return output
+}
+
+func (t *Tgbot) getClientUsage(chatId int64, tgUserID string, email ...string) {
+	traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
 	if err != nil {
 	if err != nil {
 		logger.Warning(err)
 		logger.Warning(err)
 		msg := t.I18nBot("tgbot.wentWrong")
 		msg := t.I18nBot("tgbot.wentWrong")
 		t.SendMsgToTgbot(chatId, msg)
 		t.SendMsgToTgbot(chatId, msg)
 		return
 		return
 	}
 	}
+
 	if len(traffics) == 0 {
 	if len(traffics) == 0 {
-		msg := t.I18nBot("tgbot.answers.askToAddUserName", "TgUserName=="+tgUserName, "TgUserID=="+tgUserID)
-		t.SendMsgToTgbot(chatId, msg)
+		t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.askToAddUserId", "TgUserID=="+tgUserID))
 		return
 		return
 	}
 	}
 
 
-	now := time.Now().Unix()
-	for _, traffic := range traffics {
-		expiryTime := ""
-		flag := false
-		diff := traffic.ExpiryTime/1000 - now
-		if traffic.ExpiryTime == 0 {
-			expiryTime = t.I18nBot("tgbot.unlimited")
-		} else if diff > 172800 || !traffic.Enable {
-			expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
-		} else if traffic.ExpiryTime < 0 {
-			expiryTime = fmt.Sprintf("%d %s", traffic.ExpiryTime/-86400000, t.I18nBot("tgbot.days"))
-			flag = true
-		} else {
-			expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours"))
-			flag = true
-		}
-
-		total := ""
-		if traffic.Total == 0 {
-			total = t.I18nBot("tgbot.unlimited")
-		} else {
-			total = common.FormatTraffic((traffic.Total))
-		}
+	output := ""
 
 
-		output := ""
-		output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email)
-		if traffic.Enable {
-			output += t.I18nBot("tgbot.messages.active")
-			if flag {
-				output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime)
-			} else {
-				output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
+	if len(traffics) > 0 {
+		if len(email) > 0 {
+			for _, traffic := range traffics {
+				if traffic.Email == email[0] {
+					output := t.clientInfoMsg(traffic, true, true, true, true, true, true)
+					t.SendMsgToTgbot(chatId, output)
+					return
+				}
 			}
 			}
+			msg := t.I18nBot("tgbot.noResult")
+			t.SendMsgToTgbot(chatId, msg)
+			return
 		} else {
 		} else {
-			output += t.I18nBot("tgbot.messages.inactive")
-			output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
+			for _, traffic := range traffics {
+				output += t.clientInfoMsg(traffic, true, true, true, true, true, false)
+				output += "\r\n"
+			}
 		}
 		}
-		output += t.I18nBot("tgbot.messages.upload", "Upload=="+common.FormatTraffic(traffic.Up))
-		output += t.I18nBot("tgbot.messages.download", "Download=="+common.FormatTraffic(traffic.Down))
-		output += t.I18nBot("tgbot.messages.total", "UpDown=="+common.FormatTraffic((traffic.Up+traffic.Down)), "Total=="+total)
-		output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
-
-		t.SendMsgToTgbot(chatId, output)
 	}
 	}
-	t.SendAnswer(chatId, t.I18nBot("tgbot.commands.pleaseChoose"), false)
+
+	output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
+	t.SendMsgToTgbot(chatId, output)
+	output = t.I18nBot("tgbot.commands.pleaseChoose")
+	t.SendAnswer(chatId, output, false)
 }
 }
 
 
 func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) {
 func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) {
@@ -1088,46 +1215,7 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
 		return
 		return
 	}
 	}
 
 
-	now := time.Now().Unix()
-	expiryTime := ""
-	flag := false
-	diff := traffic.ExpiryTime/1000 - now
-	if traffic.ExpiryTime == 0 {
-		expiryTime = t.I18nBot("tgbot.unlimited")
-	} else if diff > 172800 || !traffic.Enable {
-		expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
-	} else if traffic.ExpiryTime < 0 {
-		expiryTime = fmt.Sprintf("%d %s", traffic.ExpiryTime/-86400000, t.I18nBot("tgbot.days"))
-		flag = true
-	} else {
-		expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours"))
-		flag = true
-	}
-
-	total := ""
-	if traffic.Total == 0 {
-		total = t.I18nBot("tgbot.unlimited")
-	} else {
-		total = common.FormatTraffic((traffic.Total))
-	}
-
-	output := ""
-	output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email)
-	if traffic.Enable {
-		output += t.I18nBot("tgbot.messages.active")
-		if flag {
-			output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime)
-		} else {
-			output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
-		}
-	} else {
-		output += t.I18nBot("tgbot.messages.inactive")
-		output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
-	}
-	output += t.I18nBot("tgbot.messages.upload", "Upload=="+common.FormatTraffic(traffic.Up))
-	output += t.I18nBot("tgbot.messages.download", "Download=="+common.FormatTraffic(traffic.Down))
-	output += t.I18nBot("tgbot.messages.total", "UpDown=="+common.FormatTraffic((traffic.Up+traffic.Down)), "Total=="+total)
-	output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
+	output := t.clientInfoMsg(traffic, true, true, true, true, true, true)
 
 
 	inlineKeyboard := tu.InlineKeyboard(
 	inlineKeyboard := tu.InlineKeyboard(
 		tu.InlineKeyboardRow(
 		tu.InlineKeyboardRow(
@@ -1151,7 +1239,6 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.toggle")).WithCallbackData(t.encodeQuery("toggle_enable "+email)),
 			tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.toggle")).WithCallbackData(t.encodeQuery("toggle_enable "+email)),
 		),
 		),
 	)
 	)
-
 	if len(messageID) > 0 {
 	if len(messageID) > 0 {
 		t.editMessageTgBot(chatId, messageID[0], output, inlineKeyboard)
 		t.editMessageTgBot(chatId, messageID[0], output, inlineKeyboard)
 	} else {
 	} else {
@@ -1173,7 +1260,6 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) {
 		return
 		return
 	}
 	}
 
 
-	now := time.Now().Unix()
 	for _, inbound := range inbouds {
 	for _, inbound := range inbouds {
 		info := ""
 		info := ""
 		info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
 		info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
@@ -1187,111 +1273,17 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) {
 		}
 		}
 		t.SendMsgToTgbot(chatId, info)
 		t.SendMsgToTgbot(chatId, info)
 
 
-		for _, traffic := range inbound.ClientStats {
-			expiryTime := ""
-			flag := false
-			diff := traffic.ExpiryTime/1000 - now
-			if traffic.ExpiryTime == 0 {
-				expiryTime = t.I18nBot("tgbot.unlimited")
-			} else if diff > 172800 || !traffic.Enable {
-				expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
-			} else if traffic.ExpiryTime < 0 {
-				expiryTime = fmt.Sprintf("%d %s", traffic.ExpiryTime/-86400000, t.I18nBot("tgbot.days"))
-				flag = true
-			} else {
-				expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours"))
-				flag = true
-			}
-
-			total := ""
-			if traffic.Total == 0 {
-				total = t.I18nBot("tgbot.unlimited")
-			} else {
-				total = common.FormatTraffic((traffic.Total))
-			}
-
+		if len(inbound.ClientStats) > 0 {
 			output := ""
 			output := ""
-			output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email)
-			if traffic.Enable {
-				output += t.I18nBot("tgbot.messages.active")
-				if flag {
-					output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime)
-				} else {
-					output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
-				}
-			} else {
-				output += t.I18nBot("tgbot.messages.inactive")
-				output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
+			for _, traffic := range inbound.ClientStats {
+				output += t.clientInfoMsg(&traffic, true, true, true, true, true, true)
 			}
 			}
-			output += t.I18nBot("tgbot.messages.upload", "Upload=="+common.FormatTraffic(traffic.Up))
-			output += t.I18nBot("tgbot.messages.download", "Download=="+common.FormatTraffic(traffic.Down))
-			output += t.I18nBot("tgbot.messages.total", "UpDown=="+common.FormatTraffic((traffic.Up+traffic.Down)), "Total=="+total)
-			output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
-
 			t.SendMsgToTgbot(chatId, output)
 			t.SendMsgToTgbot(chatId, output)
 		}
 		}
 	}
 	}
 }
 }
 
 
-func (t *Tgbot) searchForClient(chatId int64, query string) {
-	traffic, err := t.inboundService.SearchClientTraffic(query)
-	if err != nil {
-		logger.Warning(err)
-		msg := t.I18nBot("tgbot.wentWrong")
-		t.SendMsgToTgbot(chatId, msg)
-		return
-	}
-	if traffic == nil {
-		msg := t.I18nBot("tgbot.noResult")
-		t.SendMsgToTgbot(chatId, msg)
-		return
-	}
-
-	now := time.Now().Unix()
-	expiryTime := ""
-	flag := false
-	diff := traffic.ExpiryTime/1000 - now
-	if traffic.ExpiryTime == 0 {
-		expiryTime = t.I18nBot("tgbot.unlimited")
-	} else if diff > 172800 || !traffic.Enable {
-		expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
-	} else if traffic.ExpiryTime < 0 {
-		expiryTime = fmt.Sprintf("%d %s", traffic.ExpiryTime/-86400000, t.I18nBot("tgbot.days"))
-		flag = true
-	} else {
-		expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours"))
-		flag = true
-	}
-
-	total := ""
-	if traffic.Total == 0 {
-		total = t.I18nBot("tgbot.unlimited")
-	} else {
-		total = common.FormatTraffic((traffic.Total))
-	}
-
-	output := ""
-	output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email)
-	if traffic.Enable {
-		output += t.I18nBot("tgbot.messages.active")
-		if flag {
-			output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime)
-		} else {
-			output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
-		}
-	} else {
-		output += t.I18nBot("tgbot.messages.inactive")
-		output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
-	}
-	output += t.I18nBot("tgbot.messages.upload", "Upload=="+common.FormatTraffic(traffic.Up))
-	output += t.I18nBot("tgbot.messages.download", "Download=="+common.FormatTraffic(traffic.Down))
-	output += t.I18nBot("tgbot.messages.total", "UpDown=="+common.FormatTraffic((traffic.Up+traffic.Down)), "Total=="+total)
-	output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
-
-	t.SendMsgToTgbot(chatId, output)
-}
-
-func (t *Tgbot) getExhausted() string {
+func (t *Tgbot) getExhausted(chatId int64) {
 	trDiff := int64(0)
 	trDiff := int64(0)
 	exDiff := int64(0)
 	exDiff := int64(0)
 	now := time.Now().Unix() * 1000
 	now := time.Now().Unix() * 1000
@@ -1341,10 +1333,9 @@ func (t *Tgbot) getExhausted() string {
 	output += t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.inbounds"))
 	output += t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.inbounds"))
 	output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledInbounds)))
 	output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledInbounds)))
 	output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(len(exhaustedInbounds)))
 	output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(len(exhaustedInbounds)))
-	output += "\r\n \r\n"
 
 
 	if len(exhaustedInbounds) > 0 {
 	if len(exhaustedInbounds) > 0 {
-		output += t.I18nBot("tgbot.messages.exhaustedMsg", "Type=="+t.I18nBot("tgbot.inbounds"))
+		output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+t.I18nBot("tgbot.inbounds"))
 
 
 		for _, inbound := range exhaustedInbounds {
 		for _, inbound := range exhaustedInbounds {
 			output += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
 			output += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
@@ -1355,63 +1346,148 @@ func (t *Tgbot) getExhausted() string {
 			} else {
 			} else {
 				output += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
 				output += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
 			}
 			}
-			output += "\r\n \r\n"
+			output += "\r\n"
 		}
 		}
 	}
 	}
 
 
 	// Clients
 	// Clients
+	exhaustedCC := len(exhaustedClients)
 	output += t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.clients"))
 	output += t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.clients"))
 	output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledClients)))
 	output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledClients)))
-	output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(len(exhaustedClients)))
-	output += "\r\n \r\n"
-
-	if len(exhaustedClients) > 0 {
-		output += t.I18nBot("tgbot.messages.exhaustedMsg", "Type=="+t.I18nBot("tgbot.clients"))
+	output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(exhaustedCC))
+	
 
 
+	if exhaustedCC > 0 {
+		output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+t.I18nBot("tgbot.clients"))
+		var buttons []telego.InlineKeyboardButton
 		for _, traffic := range exhaustedClients {
 		for _, traffic := range exhaustedClients {
-			expiryTime := ""
-			flag := false
-			diff := (traffic.ExpiryTime - now) / 1000
-			if traffic.ExpiryTime == 0 {
-				expiryTime = t.I18nBot("tgbot.unlimited")
-			} else if diff > 172800 || !traffic.Enable {
-				expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
-			} else if traffic.ExpiryTime < 0 {
-				expiryTime = fmt.Sprintf("%d %s", traffic.ExpiryTime/-86400000, t.I18nBot("tgbot.days"))
-				flag = true
-			} else {
-				expiryTime = fmt.Sprintf("%d %s", diff/3600, t.I18nBot("tgbot.hours"))
-				flag = true
-			}
+			output += t.clientInfoMsg(&traffic, true, false, false, true, true, false)
+			output += "\r\n"
+			buttons = append(buttons, tu.InlineKeyboardButton(traffic.Email).WithCallbackData(t.encodeQuery("client_get_usage "+traffic.Email)))
+		}
+		cols := 0
+		if exhaustedCC < 11 {
+			cols = 1
+		} else {
+			cols = 2
+		}
+		output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
+		keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
+		t.SendMsgToTgbot(chatId, output, keyboard)
+	} else {
+		output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
+		t.SendMsgToTgbot(chatId, output)
+	}
+}
 
 
-			total := ""
-			if traffic.Total == 0 {
-				total = t.I18nBot("tgbot.unlimited")
-			} else {
-				total = common.FormatTraffic((traffic.Total))
-			}
+func (t *Tgbot) notifyExhausted() {
+	trDiff := int64(0)
+	exDiff := int64(0)
+	now := time.Now().Unix() * 1000
 
 
-			output += t.I18nBot("tgbot.messages.email", "Email=="+traffic.Email)
-			if traffic.Enable {
-				output += t.I18nBot("tgbot.messages.active")
-				if flag {
-					output += t.I18nBot("tgbot.messages.expireIn", "Time=="+expiryTime)
-				} else {
-					output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
+	TrafficThreshold, err := t.settingService.GetTrafficDiff()
+	if err == nil && TrafficThreshold > 0 {
+		trDiff = int64(TrafficThreshold) * 1073741824
+	}
+	ExpireThreshold, err := t.settingService.GetExpireDiff()
+	if err == nil && ExpireThreshold > 0 {
+		exDiff = int64(ExpireThreshold) * 86400000
+	}
+	inbounds, err := t.inboundService.GetAllInbounds()
+	if err != nil {
+		logger.Warning("Unable to load Inbounds", err)
+	}
+
+	var chatIDsDone []string
+	for _, inbound := range inbounds {
+		if inbound.Enable {
+			if len(inbound.ClientStats) > 0 {
+				clients, err := t.inboundService.GetClients(inbound)
+				if err == nil {
+					for _, client := range clients {
+						if client.TgID != "" {
+							chatID, err := strconv.ParseInt(client.TgID, 10, 64)
+							if err != nil {
+								logger.Warning("TgID is not a number: ", client.TgID)
+								continue
+							}
+							if !slices.Contains(chatIDsDone, client.TgID) && !checkAdmin(chatID) {
+								var disabledClients []xray.ClientTraffic
+								var exhaustedClients []xray.ClientTraffic
+								traffics, err := t.inboundService.GetClientTrafficTgBot(client.TgID)
+								if err == nil {
+									output := t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.clients"))
+									for _, traffic := range traffics {
+										if traffic.Enable {
+											if (traffic.ExpiryTime > 0 && (traffic.ExpiryTime-now < exDiff)) ||
+												(traffic.Total > 0 && (traffic.Total-(traffic.Up+traffic.Down) < trDiff)) {
+												exhaustedClients = append(exhaustedClients, *traffic)
+											}
+										} else {
+											disabledClients = append(disabledClients, *traffic)
+										}
+									}
+									if len(exhaustedClients) > 0 {
+										output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledClients)))
+										if len(disabledClients) > 0 {
+											output += t.I18nBot("tgbot.clients") + ":\r\n"
+											for _, traffic := range disabledClients {
+												output += " " + traffic.Email
+											}
+											output += "\r\n"
+										}
+										output += "\r\n"
+										output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(len(exhaustedClients)))
+										for _, traffic := range exhaustedClients {
+											output += t.clientInfoMsg(&traffic, true, false, false, true, true, false)
+											output += "\r\n"
+										}
+										t.SendMsgToTgbot(chatID, output)
+									}
+									chatIDsDone = append(chatIDsDone, client.TgID)
+								}
+							}
+						}
+					}
 				}
 				}
-			} else {
-				output += t.I18nBot("tgbot.messages.inactive")
-				output += t.I18nBot("tgbot.messages.expire", "Time=="+expiryTime)
 			}
 			}
-			output += t.I18nBot("tgbot.messages.upload", "Upload=="+common.FormatTraffic(traffic.Up))
-			output += t.I18nBot("tgbot.messages.download", "Download=="+common.FormatTraffic(traffic.Down))
-			output += t.I18nBot("tgbot.messages.total", "UpDown=="+common.FormatTraffic((traffic.Up+traffic.Down)), "Total=="+total)
-			output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
-			output += "\r\n \r\n"
 		}
 		}
 	}
 	}
+}
 
 
-	return output
+func (t *Tgbot) onlineClients(chatId int64, messageID ...int) {
+	if !p.IsRunning() {
+		return
+	}
+
+	onlines := p.GetOnlineClients()
+	onlinesCount := len(onlines)
+	output := t.I18nBot("tgbot.messages.onlinesCount", "Count=="+fmt.Sprint(onlinesCount))
+	keyboard := tu.InlineKeyboard(tu.InlineKeyboardRow(
+		tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.refresh")).WithCallbackData(t.encodeQuery("onlines_refresh"))))
+	
+
+	if onlinesCount > 0 {
+		var buttons []telego.InlineKeyboardButton
+		for _, online := range onlines {
+			buttons = append(buttons, tu.InlineKeyboardButton(online).WithCallbackData(t.encodeQuery("client_get_usage "+online)))
+		}
+		cols := 0
+		if onlinesCount < 21 {
+			cols = 2
+		} else if onlinesCount < 61 {
+			cols = 3
+		} else {
+			cols = 4
+		}
+		keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, tu.InlineKeyboardCols(cols, buttons...)...)
+	}
+
+	if len(messageID) > 0 {
+		t.editMessageTgBot(chatId, messageID[0], output, keyboard)
+	} else {
+		t.SendMsgToTgbot(chatId, output, keyboard)
+	}
 }
 }
 
 
 func (t *Tgbot) sendBackup(chatId int64) {
 func (t *Tgbot) sendBackup(chatId int64) {
@@ -1421,33 +1497,73 @@ func (t *Tgbot) sendBackup(chatId int64) {
 	// Update by manually trigger a checkpoint operation
 	// Update by manually trigger a checkpoint operation
 	err := database.Checkpoint()
 	err := database.Checkpoint()
 	if err != nil {
 	if err != nil {
-		logger.Warning("Error in trigger a checkpoint operation: ", err)
+		logger.Error("Error in trigger a checkpoint operation: ", err)
 	}
 	}
 
 
 	file, err := os.Open(config.GetDBPath())
 	file, err := os.Open(config.GetDBPath())
-	if err != nil {
-		logger.Warning("Error in opening db file for backup: ", err)
-	}
-	document := tu.Document(
-		tu.ID(chatId),
-		tu.File(file),
-	)
-	_, err = bot.SendDocument(document)
-	if err != nil {
-		logger.Warning("Error in uploading backup: ", err)
+	if err == nil {
+		document := tu.Document(
+			tu.ID(chatId),
+			tu.File(file),
+		)
+		_, err = bot.SendDocument(document)
+		if err != nil {
+			logger.Error("Error in uploading backup: ", err)
+		}
+	} else {
+		logger.Error("Error in opening db file for backup: ", err)
+
 	}
 	}
 
 
 	file, err = os.Open(xray.GetConfigPath())
 	file, err = os.Open(xray.GetConfigPath())
-	if err != nil {
-		logger.Warning("Error in opening config.json file for backup: ", err)
+	if err == nil {
+		document := tu.Document(
+			tu.ID(chatId),
+			tu.File(file),
+		)
+		_, err = bot.SendDocument(document)
+		if err != nil {
+			logger.Error("Error in uploading config.json: ", err)
+		}
+	} else {
+		logger.Error("Error in opening config.json file for backup: ", err)
 	}
 	}
-	document = tu.Document(
-		tu.ID(chatId),
-		tu.File(file),
-	)
-	_, err = bot.SendDocument(document)
-	if err != nil {
-		logger.Warning("Error in uploading config.json: ", err)
+
+	t.sendBanLogs(chatId, false)
+}
+
+func (t *Tgbot) sendBanLogs(chatId int64, dt bool) {
+	if dt {
+		output := t.I18nBot("tgbot.messages.datetime", "DateTime=="+time.Now().Format("2006-01-02 15:04:05"))
+		t.SendMsgToTgbot(chatId, output)
+	}
+
+	file, err := os.Open(xray.GetIPLimitBannedPrevLogPath())
+	if err == nil {
+		document := tu.Document(
+			tu.ID(chatId),
+			tu.File(file),
+		)
+		_, err = bot.SendDocument(document)
+		if err != nil {
+			logger.Error("Error in uploading backup: ", err)
+		}
+	} else {
+		logger.Error("Error in opening db file for backup: ", err)
+	}
+
+	file, err = os.Open(xray.GetIPLimitBannedLogPath())
+	if err == nil {
+		document := tu.Document(
+			tu.ID(chatId),
+			tu.File(file),
+		)
+		_, err = bot.SendDocument(document)
+		if err != nil {
+			logger.Error("Error in uploading config.json: ", err)
+		}
+	} else {
+		logger.Error("Error in opening config.json file for backup: ", err)
 	}
 	}
 }
 }
 
 

+ 1 - 1
web/service/xray.go

@@ -185,7 +185,7 @@ func (s *XrayService) RestartXray(isForce bool) error {
 		return err
 		return err
 	}
 	}
 
 
-	if p != nil && p.IsRunning() {
+	if s.IsXrayRunning() {
 		if !isForce && p.GetConfig().Equals(xrayConfig) {
 		if !isForce && p.GetConfig().Equals(xrayConfig) {
 			logger.Debug("It does not need to restart xray")
 			logger.Debug("It does not need to restart xray")
 			return nil
 			return nil

+ 22 - 12
web/translation/translate.en_US.toml

@@ -173,7 +173,7 @@
 "setDefaultCert" = "Set Cert from Panel"
 "setDefaultCert" = "Set Cert from Panel"
 "xtlsDesc" = "Xray core needs to be 1.7.5"
 "xtlsDesc" = "Xray core needs to be 1.7.5"
 "realityDesc" = "Xray core needs to be 1.8.0 or higher."
 "realityDesc" = "Xray core needs to be 1.8.0 or higher."
-"telegramDesc" = "use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
+"telegramDesc" = "Only use Chat ID (you can get it here @userinfobot or use '/id' command in bot)"
 "subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
 "subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
 "info" = "Info"
 "info" = "Info"
 "same" = "Same"
 "same" = "Same"
@@ -439,6 +439,7 @@
 "noIpRecord" = "❗ No IP Record!"
 "noIpRecord" = "❗ No IP Record!"
 "noInbounds" = "❗ No inbound found!"
 "noInbounds" = "❗ No inbound found!"
 "unlimited" = "♾ Unlimited"
 "unlimited" = "♾ Unlimited"
+"add" = "Add"
 "month" = "Month"
 "month" = "Month"
 "months" = "Months"
 "months" = "Months"
 "day" = "Day"
 "day" = "Day"
@@ -447,6 +448,8 @@
 "unknown" = "Unknown"
 "unknown" = "Unknown"
 "inbounds" = "Inbounds"
 "inbounds" = "Inbounds"
 "clients" = "Clients"
 "clients" = "Clients"
+"offline" = "🔴 Offline"
+"online" = "🟢 Online"
 
 
 [tgbot.commands]
 [tgbot.commands]
 "unknown" = "❗ Unknown command"
 "unknown" = "❗ Unknown command"
@@ -457,8 +460,8 @@
 "status" = "✅ Bot is OK!"
 "status" = "✅ Bot is OK!"
 "usage" = "❗ Please provide a text to search!"
 "usage" = "❗ Please provide a text to search!"
 "getID" = "🆔 Your ID: <code>{{ .ID }}</code>"
 "getID" = "🆔 Your ID: <code>{{ .ID }}</code>"
-"helpAdminCommands" = "Search for a client email:\r\n<code>/usage [Email]</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
-"helpClientCommands" = "To search for statistics, just use the following command:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\nUse UUID for vmess/vless and Password for Trojan."
+"helpAdminCommands" = "Search for a client email:\r\n<code>/usage [Email]</code>\r\n\r\nSearch for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
+"helpClientCommands" = "To search for statistics, just use the following command:\r\n\r\n<code>/usage [Email]</code>"
 
 
 [tgbot.messages]
 [tgbot.messages]
 "cpuThreshold" = "🔴 CPU Load {{ .Percent }}% is more than threshold {{ .Threshold }}%"
 "cpuThreshold" = "🔴 CPU Load {{ .Percent }}% is more than threshold {{ .Threshold }}%"
@@ -473,7 +476,7 @@
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
-"ips" = "🔢 IPs: \r\n{{ .IPs }}\r\n"
+"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n"
 "serverUpTime" = "⏳ Server Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverUpTime" = "⏳ Server Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverLoad" = "📈 Server Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverLoad" = "📈 Server Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverMemory" = "📋 Server RAM: {{ .Current }}/{{ .Total }}\r\n"
 "serverMemory" = "📋 Server RAM: {{ .Current }}/{{ .Total }}\r\n"
@@ -487,8 +490,9 @@
 "port" = "🔌 Port: {{ .Port }}\r\n"
 "port" = "🔌 Port: {{ .Port }}\r\n"
 "expire" = "📅 Expire Date: {{ .Time }}\r\n"
 "expire" = "📅 Expire Date: {{ .Time }}\r\n"
 "expireIn" = "📅 Expire In: {{ .Time }}\r\n"
 "expireIn" = "📅 Expire In: {{ .Time }}\r\n"
-"active" = "💡 Active: ✅ Yes\r\n"
-"inactive" = "💡 Active: ❌ No\r\n"
+"active" = "💡 Active: {{ .Enable }}\r\n"
+"enabled" = "🚨 Enabled: {{ .Enable }}\r\n"
+"online" = "🌐 Connection status: {{ .Status }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
 "upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
 "download" = "🔽 Download: ↓{{ .Download }}\r\n"
 "download" = "🔽 Download: ↓{{ .Download }}\r\n"
@@ -496,10 +500,13 @@
 "TGUser" = "👤 Telegram User: {{ .TelegramID }}\r\n"
 "TGUser" = "👤 Telegram User: {{ .TelegramID }}\r\n"
 "exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
 "exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
 "exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
 "exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
+"onlinesCount" = "🌐 Online clients: {{ .Count }}\r\n"
 "disabled" = "🛑 Disabled: {{ .Disabled }}\r\n"
 "disabled" = "🛑 Disabled: {{ .Disabled }}\r\n"
-"depleteSoon" = "🔜 Deplete Soon: {{ .Deplete }}\r\n \r\n"
+"depleteSoon" = "🔜 Deplete Soon: {{ .Deplete }}\r\n\r\n"
 "backupTime" = "🗄 Backup Time: {{ .Time }}\r\n"
 "backupTime" = "🗄 Backup Time: {{ .Time }}\r\n"
-"refreshedOn" = "\r\n📋🔄 Refreshed On: {{ .Time }}\r\n \r\n"
+"refreshedOn" = "\r\n📋🔄 Refreshed On: {{ .Time }}\r\n\r\n"
+"yes" = "✅ Yes"
+"no" = "❌ No"
 
 
 [tgbot.buttons]
 [tgbot.buttons]
 "closeKeyboard" = "❌ Close Keyboard"
 "closeKeyboard" = "❌ Close Keyboard"
@@ -509,11 +516,13 @@
 "confirmResetTraffic" = "✅ Confirm Reset Traffic?"
 "confirmResetTraffic" = "✅ Confirm Reset Traffic?"
 "confirmClearIps" = "✅ Confirm Clear IPs?"
 "confirmClearIps" = "✅ Confirm Clear IPs?"
 "confirmRemoveTGUser" = "✅ Confirm Remove Telegram User?"
 "confirmRemoveTGUser" = "✅ Confirm Remove Telegram User?"
+"confirmToggle" = "✅ Confirm Enable/Disable User?"
 "dbBackup" = "Get DB Backup"
 "dbBackup" = "Get DB Backup"
 "serverUsage" = "Server Usage"
 "serverUsage" = "Server Usage"
 "getInbounds" = "Get Inbounds"
 "getInbounds" = "Get Inbounds"
 "depleteSoon" = "Deplete soon"
 "depleteSoon" = "Deplete soon"
 "clientUsage" = "Get Usage"
 "clientUsage" = "Get Usage"
+"onlines" = "Online Clients"
 "commands" = "Commands"
 "commands" = "Commands"
 "refresh" = "🔄 Refresh"
 "refresh" = "🔄 Refresh"
 "clearIPs" = "❌ Clear IPs"
 "clearIPs" = "❌ Clear IPs"
@@ -521,14 +530,16 @@
 "selectTGUser" = "👤 Select Telegram User"
 "selectTGUser" = "👤 Select Telegram User"
 "selectOneTGUser" = "👤 Select a telegram user:"
 "selectOneTGUser" = "👤 Select a telegram user:"
 "resetTraffic" = "📈 Reset Traffic"
 "resetTraffic" = "📈 Reset Traffic"
-"resetExpire" = "📅 Reset Expire Days"
+"resetExpire" = "📅 Change Expiration Date"
 "ipLog" = "🔢 IP Log"
 "ipLog" = "🔢 IP Log"
 "ipLimit" = "🔢 IP Limit"
 "ipLimit" = "🔢 IP Limit"
 "setTGUser" = "👤 Set Telegram User"
 "setTGUser" = "👤 Set Telegram User"
 "toggle" = "🔘 Enable / Disable"
 "toggle" = "🔘 Enable / Disable"
 "custom" = "🔢 Custom"
 "custom" = "🔢 Custom"
-"confirmNumber" = "✅ Confirm : {{ .Num }}"
+"confirmNumber" = "✅ Confirm: {{ .Num }}"
+"confirmNumberAdd" = "✅ Confirm adding: {{ .Num }}"
 "limitTraffic" = "🚧 Traffic Limit"
 "limitTraffic" = "🚧 Traffic Limit"
+"getBanLogs" = "Get Ban Logs"
 
 
 [tgbot.answers]
 [tgbot.answers]
 "successfulOperation" = "✅ Successful!"
 "successfulOperation" = "✅ Successful!"
@@ -548,5 +559,4 @@
 "removedTGUserSuccess" = "✅ {{ .Email }} : Telegram User removed successfully."
 "removedTGUserSuccess" = "✅ {{ .Email }} : Telegram User removed successfully."
 "enableSuccess" = "✅ {{ .Email }} : Enabled successfully."
 "enableSuccess" = "✅ {{ .Email }} : Enabled successfully."
 "disableSuccess" = "✅ {{ .Email }} : Disabled successfully."
 "disableSuccess" = "✅ {{ .Email }} : Disabled successfully."
-"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram user id in your configuration(s).\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>"
-"askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram username or user id in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>"
+"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram user id in your configuration(s).\r\n\r\nYour user id: <code>{{ .TgUserID }}</code>"

+ 24 - 14
web/translation/translate.es_ES.toml

@@ -173,7 +173,7 @@
 "setDefaultCert" = "Establecer certificado desde el panel"
 "setDefaultCert" = "Establecer certificado desde el panel"
 "xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5"
 "xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5"
 "realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior."
 "realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior."
-"telegramDesc" = "Utiliza el ID de Telegram sin @ o los IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)."
+"telegramDesc" = "Utiliza únicamente IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)."
 "subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
 "subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
 "info" = "Info"
 "info" = "Info"
 "same" = "misma"
 "same" = "misma"
@@ -439,6 +439,7 @@
 "noIpRecord" = "❗ ¡Sin Registro de IP!"
 "noIpRecord" = "❗ ¡Sin Registro de IP!"
 "noInbounds" = "❗ ¡No se encontraron entradas!"
 "noInbounds" = "❗ ¡No se encontraron entradas!"
 "unlimited" = "♾ Ilimitado"
 "unlimited" = "♾ Ilimitado"
+"add" = "Agregar"
 "month" = "Mes"
 "month" = "Mes"
 "months" = "Meses"
 "months" = "Meses"
 "day" = "Día"
 "day" = "Día"
@@ -447,6 +448,8 @@
 "unknown" = "Desconocido"
 "unknown" = "Desconocido"
 "inbounds" = "Entradas"
 "inbounds" = "Entradas"
 "clients" = "Clientes"
 "clients" = "Clientes"
+"offline" = "🔴 Sin conexión"
+"online" = "🟢 En línea"
 
 
 [tgbot.commands]
 [tgbot.commands]
 "unknown" = "❗ Comando desconocido"
 "unknown" = "❗ Comando desconocido"
@@ -457,8 +460,8 @@
 "status" = "✅ ¡El bot está bien!"
 "status" = "✅ ¡El bot está bien!"
 "usage" = "❗ ¡Por favor proporciona un texto para buscar!"
 "usage" = "❗ ¡Por favor proporciona un texto para buscar!"
 "getID" = "🆔 Tu ID: <code>{{ .ID }}</code>"
 "getID" = "🆔 Tu ID: <code>{{ .ID }}</code>"
-"helpAdminCommands" = "Buscar un correo electrónico de cliente:\r\n<code>/usage [Email]</code>\r\n \r\nBuscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Nota]</code>"
-"helpClientCommands" = "Para buscar estadísticas, simplemente usa el siguiente comando:\r\n \r\n<code>/usage [UUID|Contraseña]</code>\r\n \r\nUsa UUID para vmess/vless y Contraseña para Trojan."
+"helpAdminCommands" = "Buscar un correo electrónico de cliente:\r\n<code>/usage [Email]</code>\r\n\r\nBuscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Nota]</code>"
+"helpClientCommands" = "Para buscar estadísticas, simplemente usa el siguiente comando:\r\n\r\n<code>/usage [UUID|Contraseña]</code>"
 
 
 [tgbot.messages]
 [tgbot.messages]
 "cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%"
 "cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%"
@@ -473,7 +476,7 @@
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
-"ips" = "🔢 IPs: \r\n{{ .IPs }}\r\n"
+"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n"
 "serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n"
 "serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n"
@@ -487,19 +490,23 @@
 "port" = "🔌 Puerto: {{ .Port }}\r\n"
 "port" = "🔌 Puerto: {{ .Port }}\r\n"
 "expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n"
 "expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n"
 "expireIn" = "📅 Vence en: {{ .Time }}\r\n"
 "expireIn" = "📅 Vence en: {{ .Time }}\r\n"
-"active" = "💡 Activo: ✅ Sí\r\n"
-"inactive" = "💡 Activo: ❌ No\r\n"
+"active" = "💡 Activo: {{ .Enable }}\r\n"
+"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n"
+"online" = "🌐 Estado de conexión: {{ .Status }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "upload" = "🔼 Subida: ↑{{ .Upload }}\r\n"
 "upload" = "🔼 Subida: ↑{{ .Upload }}\r\n"
 "download" = "🔽 Bajada: ↓{{ .Download }}\r\n"
 "download" = "🔽 Bajada: ↓{{ .Download }}\r\n"
 "total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
 "total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
 "TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n"
 "TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n"
-"exhaustedMsg" = "🚨 Agotado {{ .Type }}: \r\n"
-"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}: \r\n"
+"exhaustedMsg" = "🚨 Agotado {{ .Type }}:\r\n"
+"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}:\r\n"
+"onlinesCount" = "🌐 Clientes en línea: {{ .Count }}\r\n"
 "disabled" = "🛑 Desactivado: {{ .Disabled }}\r\n"
 "disabled" = "🛑 Desactivado: {{ .Disabled }}\r\n"
-"depleteSoon" = "🔜 Se agotará pronto: {{ .Deplete }}\r\n \r\n"
+"depleteSoon" = "🔜 Se agotará pronto: {{ .Deplete }}\r\n\r\n"
 "backupTime" = "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n"
 "backupTime" = "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n"
-"refreshedOn" = "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n \r\n"
+"refreshedOn" = "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n\r\n"
+"yes" = "✅ Sí"
+"no" = "❌ No"
 
 
 [tgbot.buttons]
 [tgbot.buttons]
 "closeKeyboard" = "❌ Cerrar Teclado"
 "closeKeyboard" = "❌ Cerrar Teclado"
@@ -509,11 +516,13 @@
 "confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?"
 "confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?"
 "confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?"
 "confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?"
 "confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?"
 "confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?"
+"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?"
 "dbBackup" = "Obtener Copia de Seguridad de BD"
 "dbBackup" = "Obtener Copia de Seguridad de BD"
 "serverUsage" = "Uso del Servidor"
 "serverUsage" = "Uso del Servidor"
 "getInbounds" = "Obtener Entradas"
 "getInbounds" = "Obtener Entradas"
 "depleteSoon" = "Pronto se Agotará"
 "depleteSoon" = "Pronto se Agotará"
 "clientUsage" = "Obtener Uso"
 "clientUsage" = "Obtener Uso"
+"onlines" = "Clientes en línea"
 "commands" = "Comandos"
 "commands" = "Comandos"
 "refresh" = "🔄 Actualizar"
 "refresh" = "🔄 Actualizar"
 "clearIPs" = "❌ Limpiar IPs"
 "clearIPs" = "❌ Limpiar IPs"
@@ -521,14 +530,16 @@
 "selectTGUser" = "👤 Seleccionar Usuario de Telegram"
 "selectTGUser" = "👤 Seleccionar Usuario de Telegram"
 "selectOneTGUser" = "👤 Selecciona un usuario de telegram:"
 "selectOneTGUser" = "👤 Selecciona un usuario de telegram:"
 "resetTraffic" = "📈 Reiniciar Tráfico"
 "resetTraffic" = "📈 Reiniciar Tráfico"
-"resetExpire" = "📅 Reiniciar Días de Vencimiento"
+"resetExpire" = "📅 Cambiar fecha de Vencimiento"
 "ipLog" = "🔢 Registro de IP"
 "ipLog" = "🔢 Registro de IP"
 "ipLimit" = "🔢 Límite de IP"
 "ipLimit" = "🔢 Límite de IP"
 "setTGUser" = "👤 Establecer Usuario de Telegram"
 "setTGUser" = "👤 Establecer Usuario de Telegram"
 "toggle" = "🔘 Habilitar / Deshabilitar"
 "toggle" = "🔘 Habilitar / Deshabilitar"
 "custom" = "🔢 Costumbre"
 "custom" = "🔢 Costumbre"
-"confirmNumber" = "✅ Confirmar : {{ .Num }}"
+"confirmNumber" = "✅ Confirmar: {{ .Num }}"
+"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}"
 "limitTraffic" = "🚧 Límite de tráfico"
 "limitTraffic" = "🚧 Límite de tráfico"
+"getBanLogs" = "Registros de prohibición"
 
 
 [tgbot.answers]
 [tgbot.answers]
 "successfulOperation" = "✅ ¡Exitosa!"
 "successfulOperation" = "✅ ¡Exitosa!"
@@ -548,5 +559,4 @@
 "removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente."
 "removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente."
 "enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
 "enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
 "disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente."
 "disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente."
-"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>"
-"askToAddUserName" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su nombre de usuario o ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu nombre de usuario: <b>@{{ .TgUserName }}</b>\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>"
+"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <code>{{ .TgUserID }}</code>"

+ 21 - 11
web/translation/translate.fa_IR.toml

@@ -173,7 +173,7 @@
 "setDefaultCert" = "استفاده از گواهی پنل"
 "setDefaultCert" = "استفاده از گواهی پنل"
 "xtlsDesc" = "هسته Xray باید 1.7.5 باشد"
 "xtlsDesc" = "هسته Xray باید 1.7.5 باشد"
 "realityDesc" = "هسته Xray باید 1.8.0 و بالاتر باشد"
 "realityDesc" = "هسته Xray باید 1.8.0 و بالاتر باشد"
-"telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
+"telegramDesc" = "فقط از شناسه چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
 "subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
 "subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
 "info" = "اطلاعات"
 "info" = "اطلاعات"
 "same" = "همسان"
 "same" = "همسان"
@@ -439,6 +439,7 @@
 "noIpRecord" = "❗ رکورد IP یافت نشد!"
 "noIpRecord" = "❗ رکورد IP یافت نشد!"
 "noInbounds" = "❗ هیچ ورودی یافت نشد!"
 "noInbounds" = "❗ هیچ ورودی یافت نشد!"
 "unlimited" = "♾ نامحدود"
 "unlimited" = "♾ نامحدود"
+"add" = "اضافه کردن"
 "month" = "ماه"
 "month" = "ماه"
 "months" = "ماه‌ها"
 "months" = "ماه‌ها"
 "day" = "روز"
 "day" = "روز"
@@ -447,6 +448,8 @@
 "unknown" = "نامشخص"
 "unknown" = "نامشخص"
 "inbounds" = "ورودی‌ها"
 "inbounds" = "ورودی‌ها"
 "clients" = "کلاینت‌ها"
 "clients" = "کلاینت‌ها"
+"offline" = "🔴 آفلاین"
+"online" = "🟢 برخط"
 
 
 [tgbot.commands]
 [tgbot.commands]
 "unknown" = "❗ دستور ناشناخته"
 "unknown" = "❗ دستور ناشناخته"
@@ -457,8 +460,8 @@
 "status" = "✅ ربات در حالت عادی است!"
 "status" = "✅ ربات در حالت عادی است!"
 "usage" = "❗ لطفاً یک متن برای جستجو وارد کنید!"
 "usage" = "❗ لطفاً یک متن برای جستجو وارد کنید!"
 "getID" = "🆔 شناسه شما: <code>{{ .ID }}</code>"
 "getID" = "🆔 شناسه شما: <code>{{ .ID }}</code>"
-"helpAdminCommands" = "برای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n \r\nبرای جستجوی ورودی‌ها (با آمار مشتری):\r\n<code>/inbound [توضیح]</code>"
-"helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفاده کنید:\r\n \r\n<code>/usage [UUID|رمز عبور]</code>\r\n \r\nاز UUID برای vmess/vless و از رمز عبور برای Trojan استفاده کنید."
+"helpAdminCommands" = "برای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n\r\nبرای جستجوی ورودی‌ها (با آمار مشتری):\r\n<code>/inbound [توضیح]</code>"
+"helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفاده کنید:\r\n\r\n<code>/usage [Email]</code>"
 
 
 [tgbot.messages]
 [tgbot.messages]
 "cpuThreshold" = "🔴 میزان استفاده از CPU {{ .Percent }}% بیشتر از آستانه {{ .Threshold }}% است."
 "cpuThreshold" = "🔴 میزان استفاده از CPU {{ .Percent }}% بیشتر از آستانه {{ .Threshold }}% است."
@@ -473,7 +476,7 @@
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ip" = "🌐 آدرس IP: {{ .IP }}\r\n"
 "ip" = "🌐 آدرس IP: {{ .IP }}\r\n"
-"ips" = "🔢 آدرس‌های IP: \r\n{{ .IPs }}\r\n"
+"ips" = "🔢 آدرس‌های IP:\r\n{{ .IPs }}\r\n"
 "serverUpTime" = "⏳ زمان کارکرد سرور: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverUpTime" = "⏳ زمان کارکرد سرور: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverLoad" = "📈 بار سرور: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverLoad" = "📈 بار سرور: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverMemory" = "📋 حافظه سرور: {{ .Current }}/{{ .Total }}\r\n"
 "serverMemory" = "📋 حافظه سرور: {{ .Current }}/{{ .Total }}\r\n"
@@ -487,8 +490,9 @@
 "port" = "🔌 پورت: {{ .Port }}\r\n"
 "port" = "🔌 پورت: {{ .Port }}\r\n"
 "expire" = "📅 تاریخ انقضا: {{ .Time }}\r\n"
 "expire" = "📅 تاریخ انقضا: {{ .Time }}\r\n"
 "expireIn" = "📅 باقیمانده از انقضا: {{ .Time }}\r\n"
 "expireIn" = "📅 باقیمانده از انقضا: {{ .Time }}\r\n"
-"active" = "💡 فعال: ✅\r\n"
-"inactive" = "💡 فعال: ❌\r\n"
+"active" = "💡 فعال: {{ .Enable }}\r\n"
+"enabled" = "🚨 مشمول: {{ .Enable }}\r\n"
+"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
 "email" = "📧 ایمیل: {{ .Email }}\r\n"
 "email" = "📧 ایمیل: {{ .Email }}\r\n"
 "upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
 "upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
 "download" = "🔽 دانلود↓: {{ .Download }}\r\n"
 "download" = "🔽 دانلود↓: {{ .Download }}\r\n"
@@ -496,10 +500,13 @@
 "TGUser" = "👤 کاربر تلگرام: {{ .TelegramID }}\r\n"
 "TGUser" = "👤 کاربر تلگرام: {{ .TelegramID }}\r\n"
 "exhaustedMsg" = "🚨 {{ .Type }} به اتمام رسیده است:\r\n"
 "exhaustedMsg" = "🚨 {{ .Type }} به اتمام رسیده است:\r\n"
 "exhaustedCount" = "🚨 تعداد {{ .Type }} به اتمام رسیده:\r\n"
 "exhaustedCount" = "🚨 تعداد {{ .Type }} به اتمام رسیده:\r\n"
+"onlinesCount" = "🌐 مشتریان آنلاین: {{ .Count }}\r\n"
 "disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
 "disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
-"depleteSoon" = "🔜 به زودی به پایان خواهد رسید: {{ .Deplete }}\r\n \r\n"
+"depleteSoon" = "🔜 به زودی به پایان خواهد رسید: {{ .Deplete }}\r\n\r\n"
 "backupTime" = "🗄 زمان پشتیبان‌گیری: {{ .Time }}\r\n"
 "backupTime" = "🗄 زمان پشتیبان‌گیری: {{ .Time }}\r\n"
-"refreshedOn" = "\r\n📋🔄 تازه‌سازی شده در: {{ .Time }}\r\n \r\n"
+"refreshedOn" = "\r\n📋🔄 تازه‌سازی شده در: {{ .Time }}\r\n\r\n"
+"yes" = "✅ بله"
+"no" = "❌ نه"
 
 
 [tgbot.buttons]
 [tgbot.buttons]
 "closeKeyboard" = "❌ بستن کیبورد"
 "closeKeyboard" = "❌ بستن کیبورد"
@@ -509,11 +516,13 @@
 "confirmResetTraffic" = "✅ تأیید تنظیم مجدد ترافیک؟"
 "confirmResetTraffic" = "✅ تأیید تنظیم مجدد ترافیک؟"
 "confirmClearIps" = "✅ تأیید پاک‌سازی آدرس‌های IP؟"
 "confirmClearIps" = "✅ تأیید پاک‌سازی آدرس‌های IP؟"
 "confirmRemoveTGUser" = "✅ تأیید حذف کاربر تلگرام؟"
 "confirmRemoveTGUser" = "✅ تأیید حذف کاربر تلگرام؟"
+"confirmToggle" = "✅ تایید فعال/غیرفعال کردن کاربر؟"
 "dbBackup" = "دریافت پشتیبان پایگاه داده"
 "dbBackup" = "دریافت پشتیبان پایگاه داده"
 "serverUsage" = "استفاده از سرور"
 "serverUsage" = "استفاده از سرور"
 "getInbounds" = "دریافت ورودی‌ها"
 "getInbounds" = "دریافت ورودی‌ها"
 "depleteSoon" = "به زودی به پایان خواهد رسید"
 "depleteSoon" = "به زودی به پایان خواهد رسید"
 "clientUsage" = "دریافت آمار کاربر"
 "clientUsage" = "دریافت آمار کاربر"
+"onlines" = "مشتریان آنلاین"
 "commands" = "دستورات"
 "commands" = "دستورات"
 "refresh" = "🔄 تازه‌سازی"
 "refresh" = "🔄 تازه‌سازی"
 "clearIPs" = "❌ پاک‌سازی آدرس‌ها"
 "clearIPs" = "❌ پاک‌سازی آدرس‌ها"
@@ -527,8 +536,10 @@
 "setTGUser" = "👤 تنظیم کاربر تلگرام"
 "setTGUser" = "👤 تنظیم کاربر تلگرام"
 "toggle" = "🔘 فعال / غیرفعال"
 "toggle" = "🔘 فعال / غیرفعال"
 "custom" = "🔢 سفارشی"
 "custom" = "🔢 سفارشی"
-"confirmNumber" = "✅ تایید : {{ .Num }}"
+"confirmNumber" = "✅ تایید: {{ .Num }}"
+"confirmNumberAdd" = "✅ تایید اضافه کردن: {{ .Num }}"
 "limitTraffic" = "🚧 محدودیت ترافیک"
 "limitTraffic" = "🚧 محدودیت ترافیک"
+"getBanLogs" = "گزارش های بلوک را دریافت کنید"
 
 
 [tgbot.answers]
 [tgbot.answers]
 "successfulOperation" = "✅ انجام شد!"
 "successfulOperation" = "✅ انجام شد!"
@@ -548,5 +559,4 @@
 "removedTGUserSuccess" = "✅ {{ .Email }} : کاربر تلگرام با موفقیت حذف شد."
 "removedTGUserSuccess" = "✅ {{ .Email }} : کاربر تلگرام با موفقیت حذف شد."
 "enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
 "enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
 "disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
 "disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
-"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>"
-"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که نام کاربری یا شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nنام کاربری شما: <b>@{{ .TgUserName }}</b>\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>"
+"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <code>{{ .TgUserID }}</code>"

+ 22 - 12
web/translation/translate.ru_RU.toml

@@ -173,7 +173,7 @@
 "setDefaultCert" = "Установить сертификат с панели"
 "setDefaultCert" = "Установить сертификат с панели"
 "xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5"
 "xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5"
 "realityDesc" = "Версия Xray должна быть не ниже 1.8.0"
 "realityDesc" = "Версия Xray должна быть не ниже 1.8.0"
-"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
+"telegramDesc" = "Используйте только ID чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
 "subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
 "subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
 "info" = "Информация"
 "info" = "Информация"
 "same" = "Тот же"
 "same" = "Тот же"
@@ -439,6 +439,7 @@
 "noIpRecord" = "❗ Нет записей об IP-адресе!"
 "noIpRecord" = "❗ Нет записей об IP-адресе!"
 "noInbounds" = "❗ Входящих соединений не найдено!"
 "noInbounds" = "❗ Входящих соединений не найдено!"
 "unlimited" = "♾ Неограниченно"
 "unlimited" = "♾ Неограниченно"
+"add" = "Добавить"
 "month" = "Месяц"
 "month" = "Месяц"
 "months" = "Месяцев"
 "months" = "Месяцев"
 "day" = "День"
 "day" = "День"
@@ -447,6 +448,8 @@
 "unknown" = "Неизвестно"
 "unknown" = "Неизвестно"
 "inbounds" = "Входящие"
 "inbounds" = "Входящие"
 "clients" = "Клиенты"
 "clients" = "Клиенты"
+"offline" = "🔴 Офлайн"
+"online" = "🟢 Онлайн"
 
 
 [tgbot.commands]
 [tgbot.commands]
 "unknown" = "❗ Неизвестная команда"
 "unknown" = "❗ Неизвестная команда"
@@ -457,8 +460,8 @@
 "status" = "✅ Бот работает нормально!"
 "status" = "✅ Бот работает нормально!"
 "usage" = "❗ Пожалуйста, укажите текст для поиска!"
 "usage" = "❗ Пожалуйста, укажите текст для поиска!"
 "getID" = "🆔 Ваш ID: <code>{{ .ID }}</code>"
 "getID" = "🆔 Ваш ID: <code>{{ .ID }}</code>"
-"helpAdminCommands" = "Поиск по электронной почте клиента:\r\n<code>/usage [Email]</code>\r\n \r\nПоиск входящих соединений (со статистикой клиента):\r\n<code>/inbound [Remark]</code>"
-"helpClientCommands" = "Для получения статистики используйте следующую команду:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\nИспользуйте UUID для vmess/vless и пароль для Trojan."
+"helpAdminCommands" = "Поиск по электронной почте клиента:\r\n<code>/usage [Email]</code>\r\n\r\nПоиск входящих соединений (со статистикой клиента):\r\n<code>/inbound [Remark]</code>"
+"helpClientCommands" = "Для получения статистики используйте следующую команду:\r\n\r\n<code>/usage [Email]</code>"
 
 
 [tgbot.messages]
 [tgbot.messages]
 "cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%"
 "cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%"
@@ -473,7 +476,7 @@
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
-"ips" = "🔢 IP-адреса: \r\n{{ .IPs }}\r\n"
+"ips" = "🔢 IP-адреса:\r\n{{ .IPs }}\r\n"
 "serverUpTime" = "⏳ Время работы сервера: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverUpTime" = "⏳ Время работы сервера: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverLoad" = "📈 Загрузка сервера: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverLoad" = "📈 Загрузка сервера: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverMemory" = "📋 Память сервера: {{ .Current }}/{{ .Total }}\r\n"
 "serverMemory" = "📋 Память сервера: {{ .Current }}/{{ .Total }}\r\n"
@@ -488,7 +491,8 @@
 "expire" = "📅 Дата окончания: {{ .Time }}\r\n"
 "expire" = "📅 Дата окончания: {{ .Time }}\r\n"
 "expireIn" = "📅 Окончание через: {{ .Time }}\r\n"
 "expireIn" = "📅 Окончание через: {{ .Time }}\r\n"
 "active" = "💡 Активен: ✅ Да\r\n"
 "active" = "💡 Активен: ✅ Да\r\n"
-"inactive" = "💡 Активен: ❌ Нет\r\n"
+"enabled" = "🚨 Включен: {{ .Enable }}\r\n"
+"online" = "🌐 Статус соединения: {{ .Status }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n"
 "upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n"
 "download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n"
 "download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n"
@@ -496,10 +500,13 @@
 "TGUser" = "👤 Пользователь Telegram: {{ .TelegramID }}\r\n"
 "TGUser" = "👤 Пользователь Telegram: {{ .TelegramID }}\r\n"
 "exhaustedMsg" = "🚨 Исчерпаны {{ .Type }}:\r\n"
 "exhaustedMsg" = "🚨 Исчерпаны {{ .Type }}:\r\n"
 "exhaustedCount" = "🚨 Количество исчерпанных {{ .Type }}:\r\n"
 "exhaustedCount" = "🚨 Количество исчерпанных {{ .Type }}:\r\n"
+"onlinesCount" = "🌐 Клиентов онлайн: {{ .Count }}\r\n"
 "disabled" = "🛑 Отключено: {{ .Disabled }}\r\n"
 "disabled" = "🛑 Отключено: {{ .Disabled }}\r\n"
-"depleteSoon" = "🔜 Скоро исчерпание: {{ .Deplete }}\r\n \r\n"
+"depleteSoon" = "🔜 Скоро исчерпание: {{ .Deplete }}\r\n\r\n"
 "backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n"
 "backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n"
-"refreshedOn" = "\r\n📋🔄 Обновлено: {{ .Time }}\r\n \r\n"
+"refreshedOn" = "\r\n📋🔄 Обновлено: {{ .Time }}\r\n\r\n"
+"yes" = "✅ Да"
+"no" = "❌ Нет"
 
 
 [tgbot.buttons]
 [tgbot.buttons]
 "closeKeyboard" = "❌ Закрыть клавиатуру"
 "closeKeyboard" = "❌ Закрыть клавиатуру"
@@ -509,11 +516,13 @@
 "confirmResetTraffic" = "✅ Подтвердить сброс трафика?"
 "confirmResetTraffic" = "✅ Подтвердить сброс трафика?"
 "confirmClearIps" = "✅ Подтвердить очистку IP?"
 "confirmClearIps" = "✅ Подтвердить очистку IP?"
 "confirmRemoveTGUser" = "✅ Подтвердить удаление пользователя Telegram?"
 "confirmRemoveTGUser" = "✅ Подтвердить удаление пользователя Telegram?"
+"confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?"
 "dbBackup" = "Получить резервную копию DB"
 "dbBackup" = "Получить резервную копию DB"
 "serverUsage" = "Использование сервера"
 "serverUsage" = "Использование сервера"
 "getInbounds" = "Получить входящие потоки"
 "getInbounds" = "Получить входящие потоки"
 "depleteSoon" = "Скоро исчерпание"
 "depleteSoon" = "Скоро исчерпание"
 "clientUsage" = "Получить использование"
 "clientUsage" = "Получить использование"
+"onlines" = "Онлайн-клиенты"
 "commands" = "Команды"
 "commands" = "Команды"
 "refresh" = "🔄 Обновить"
 "refresh" = "🔄 Обновить"
 "clearIPs" = "❌ Очистить IP"
 "clearIPs" = "❌ Очистить IP"
@@ -521,14 +530,16 @@
 "selectTGUser" = "👤 Выбрать пользователя Telegram"
 "selectTGUser" = "👤 Выбрать пользователя Telegram"
 "selectOneTGUser" = "👤 Выберите пользователя Telegram:"
 "selectOneTGUser" = "👤 Выберите пользователя Telegram:"
 "resetTraffic" = "📈 Сбросить трафик"
 "resetTraffic" = "📈 Сбросить трафик"
-"resetExpire" = "📅 Сбросить дату окончания"
+"resetExpire" = "📅 Изменить дату окончания"
 "ipLog" = "🔢 Лог IP"
 "ipLog" = "🔢 Лог IP"
 "ipLimit" = "🔢 Лимит IP"
 "ipLimit" = "🔢 Лимит IP"
 "setTGUser" = "👤 Установить пользователя Telegram"
 "setTGUser" = "👤 Установить пользователя Telegram"
 "toggle" = "🔘 Вкл./Выкл."
 "toggle" = "🔘 Вкл./Выкл."
-"custom" = "🔢 Обычай"
-"confirmNumber" = "✅ Подтвердить : {{ .Num }}"
+"custom" = "🔢 Свой"
+"confirmNumber" = "✅ Подтвердить: {{ .Num }}"
+"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
 "limitTraffic" = "🚧 Лимит трафика"
 "limitTraffic" = "🚧 Лимит трафика"
+"getBanLogs" = "Логи блокировок"
 
 
 [tgbot.answers]
 [tgbot.answers]
 "successfulOperation" = "✅ Успешный!"
 "successfulOperation" = "✅ Успешный!"
@@ -548,5 +559,4 @@
 "removedTGUserSuccess" = "✅ {{ .Email }}: Пользователь Telegram успешно удален."
 "removedTGUserSuccess" = "✅ {{ .Email }}: Пользователь Telegram успешно удален."
 "enableSuccess" = "✅ {{ .Email }}: Включено успешно."
 "enableSuccess" = "✅ {{ .Email }}: Включено успешно."
 "disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
 "disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
-"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>"
-"askToAddUserName" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваше имя пользователя или идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаше имя пользователя: <b>@{{ .TgUserName }}</b>\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>"
+"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>"

+ 22 - 12
web/translation/translate.vi_VN.toml

@@ -173,7 +173,7 @@
 "setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
 "setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
 "xtlsDesc" = "Xray core cần phiên bản 1.7.5"
 "xtlsDesc" = "Xray core cần phiên bản 1.7.5"
 "realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
 "realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
-"telegramDesc" = "Sử dụng Telegram ID mà không cần ký hiệu @ hoặc chat IDs (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
+"telegramDesc" = "Chỉ sử dụng ID trò chuyện (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
 "subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
 "subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
 "info" = "Thông tin"
 "info" = "Thông tin"
 "same" = "Giống nhau"
 "same" = "Giống nhau"
@@ -439,6 +439,7 @@
 "noIpRecord" = "❗ Không có bản ghi IP!"
 "noIpRecord" = "❗ Không có bản ghi IP!"
 "noInbounds" = "❗ Không tìm thấy inbound!"
 "noInbounds" = "❗ Không tìm thấy inbound!"
 "unlimited" = "♾ Không giới hạn"
 "unlimited" = "♾ Không giới hạn"
+"add" = "Thêm"
 "month" = "Tháng"
 "month" = "Tháng"
 "months" = "Tháng"
 "months" = "Tháng"
 "day" = "Ngày"
 "day" = "Ngày"
@@ -447,6 +448,8 @@
 "unknown" = "Không rõ"
 "unknown" = "Không rõ"
 "inbounds" = "Vào"
 "inbounds" = "Vào"
 "clients" = "Các khách hàng"
 "clients" = "Các khách hàng"
+"offline" = "🔴 Ngoại tuyến"
+"online" = "🟢 Trực tuyến"
 
 
 [tgbot.commands]
 [tgbot.commands]
 "unknown" = "❗ Lệnh không rõ"
 "unknown" = "❗ Lệnh không rõ"
@@ -457,8 +460,8 @@
 "status" = "✅ Bot hoạt động bình thường!"
 "status" = "✅ Bot hoạt động bình thường!"
 "usage" = "❗ Vui lòng cung cấp văn bản để tìm kiếm!"
 "usage" = "❗ Vui lòng cung cấp văn bản để tìm kiếm!"
 "getID" = "🆔 ID của bạn: <code>{{ .ID }}</code>"
 "getID" = "🆔 ID của bạn: <code>{{ .ID }}</code>"
-"helpAdminCommands" = "Tìm kiếm email của khách hàng:\r\n<code>/usage [Email]</code>\r\n \r\nTìm kiếm inbounds (với thống kê của khách hàng):\r\n<code>/inbound [Ghi chú]</code>"
-"helpClientCommands" = "Để tìm kiếm thống kê, hãy sử dụng lệnh sau:\r\n \r\n<code>/usage [UUID|Mật khẩu]</code>\r\n \r\nSử dụng UUID cho vmess/vless và Mật khẩu cho Trojan."
+"helpAdminCommands" = "Tìm kiếm email của khách hàng:\r\n<code>/usage [Email]</code>\r\n\r\nTìm kiếm inbounds (với thống kê của khách hàng):\r\n<code>/inbound [Ghi chú]</code>"
+"helpClientCommands" = "Để tìm kiếm thống kê, hãy sử dụng lệnh sau:\r\n\r\n<code>/usage [Email]</code>"
 
 
 [tgbot.messages]
 [tgbot.messages]
 "cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
 "cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
@@ -473,7 +476,7 @@
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
 "ip" = "🌐 IP: {{ .IP }}\r\n"
-"ips" = "🔢 Các IP: \r\n{{ .IPs }}\r\n"
+"ips" = "🔢 Các IP:\r\n{{ .IPs }}\r\n"
 "serverUpTime" = "⏳ Thời gian hoạt động của máy chủ: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverUpTime" = "⏳ Thời gian hoạt động của máy chủ: {{ .UpTime }} {{ .Unit }}\r\n"
 "serverLoad" = "📈 Tải máy chủ: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverLoad" = "📈 Tải máy chủ: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
 "serverMemory" = "📋 Bộ nhớ máy chủ: {{ .Current }}/{{ .Total }}\r\n"
 "serverMemory" = "📋 Bộ nhớ máy chủ: {{ .Current }}/{{ .Total }}\r\n"
@@ -487,8 +490,9 @@
 "port" = "🔌 Cổng: {{ .Port }}\r\n"
 "port" = "🔌 Cổng: {{ .Port }}\r\n"
 "expire" = "📅 Ngày hết hạn: {{ .Time }}\r\n"
 "expire" = "📅 Ngày hết hạn: {{ .Time }}\r\n"
 "expireIn" = "📅 Hết hạn sau: {{ .Time }}\r\n"
 "expireIn" = "📅 Hết hạn sau: {{ .Time }}\r\n"
-"active" = "💡 Hoạt động: ✅ Có\r\n"
-"inactive" = "💡 Hoạt động: ❌ Không\r\n"
+"active" = "💡 Đang hoạt động: {{ .Enable }}\r\n"
+"enabled" = "🚨 Đã bật: {{ .Enable }}\r\n"
+"online" = "🌐 Trạng thái kết nối: {{ .Status }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "email" = "📧 Email: {{ .Email }}\r\n"
 "upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n"
 "upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n"
 "download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n"
 "download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n"
@@ -496,10 +500,13 @@
 "TGUser" = "👤 Người dùng Telegram: {{ .TelegramID }}\r\n"
 "TGUser" = "👤 Người dùng Telegram: {{ .TelegramID }}\r\n"
 "exhaustedMsg" = "🚨 Sự cạn kiệt {{ .Type }}:\r\n"
 "exhaustedMsg" = "🚨 Sự cạn kiệt {{ .Type }}:\r\n"
 "exhaustedCount" = "🚨 Số lần cạn kiệt {{ .Type }}:\r\n"
 "exhaustedCount" = "🚨 Số lần cạn kiệt {{ .Type }}:\r\n"
+"onlinesCount" = "🌐 Khách hàng trực tuyến: {{ .Count }}\r\n"
 "disabled" = "🛑 Vô hiệu hóa: {{ .Disabled }}\r\n"
 "disabled" = "🛑 Vô hiệu hóa: {{ .Disabled }}\r\n"
-"depleteSoon" = "🔜 Sắp cạn kiệt: {{ .Deplete }}\r\n \r\n"
+"depleteSoon" = "🔜 Sắp cạn kiệt: {{ .Deplete }}\r\n\r\n"
 "backupTime" = "🗄 Thời gian sao lưu: {{ .Time }}\r\n"
 "backupTime" = "🗄 Thời gian sao lưu: {{ .Time }}\r\n"
-"refreshedOn" = "\r\n📋🔄 Đã cập nhật lần cuối vào: {{ .Time }}\r\n \r\n"
+"refreshedOn" = "\r\n📋🔄 Đã cập nhật lần cuối vào: {{ .Time }}\r\n\r\n"
+"yes" = "✅ Có"
+"no" = "❌ Không"
 
 
 [tgbot.buttons]
 [tgbot.buttons]
 "closeKeyboard" = "❌ Đóng Bàn Phím"
 "closeKeyboard" = "❌ Đóng Bàn Phím"
@@ -509,11 +516,13 @@
 "confirmResetTraffic" = "✅ Xác Nhận Đặt Lại Lưu Lượng?"
 "confirmResetTraffic" = "✅ Xác Nhận Đặt Lại Lưu Lượng?"
 "confirmClearIps" = "✅ Xác Nhận Xóa Các IP?"
 "confirmClearIps" = "✅ Xác Nhận Xóa Các IP?"
 "confirmRemoveTGUser" = "✅ Xác Nhận Xóa Người Dùng Telegram?"
 "confirmRemoveTGUser" = "✅ Xác Nhận Xóa Người Dùng Telegram?"
+"confirmToggle" = "✅ Xác nhận Bật/Tắt người dùng?"
 "dbBackup" = "Tải bản sao lưu cơ sở dữ liệu"
 "dbBackup" = "Tải bản sao lưu cơ sở dữ liệu"
 "serverUsage" = "Sử Dụng Máy Chủ"
 "serverUsage" = "Sử Dụng Máy Chủ"
 "getInbounds" = "Lấy cổng vào"
 "getInbounds" = "Lấy cổng vào"
 "depleteSoon" = "Depleted Soon"
 "depleteSoon" = "Depleted Soon"
 "clientUsage" = "Lấy Sử Dụng"
 "clientUsage" = "Lấy Sử Dụng"
+"onlines" = "Khách hàng trực tuyến"
 "commands" = "Lệnh"
 "commands" = "Lệnh"
 "refresh" = "🔄 Cập Nhật"
 "refresh" = "🔄 Cập Nhật"
 "clearIPs" = "❌ Xóa IP"
 "clearIPs" = "❌ Xóa IP"
@@ -521,14 +530,16 @@
 "selectTGUser" = "👤 Chọn Người Dùng Telegram"
 "selectTGUser" = "👤 Chọn Người Dùng Telegram"
 "selectOneTGUser" = "👤 Chọn một người dùng telegram:"
 "selectOneTGUser" = "👤 Chọn một người dùng telegram:"
 "resetTraffic" = "📈 Đặt Lại Lưu Lượng"
 "resetTraffic" = "📈 Đặt Lại Lưu Lượng"
-"resetExpire" = "📅 Đặt Lại Ngày Hết Hạn"
+"resetExpire" = "📅 Thay đổi ngày hết hạn"
 "ipLog" = "🔢 Nhật ký địa chỉ IP"
 "ipLog" = "🔢 Nhật ký địa chỉ IP"
 "ipLimit" = "🔢 Giới Hạn địa chỉ IP"
 "ipLimit" = "🔢 Giới Hạn địa chỉ IP"
 "setTGUser" = "👤 Đặt Người Dùng Telegram"
 "setTGUser" = "👤 Đặt Người Dùng Telegram"
 "toggle" = "🔘 Bật / Tắt"
 "toggle" = "🔘 Bật / Tắt"
 "custom" = "🔢 Tùy chỉnh"
 "custom" = "🔢 Tùy chỉnh"
-"confirmNumber" = "✅ Xác nhận : {{ .Num }}"
+"confirmNumber" = "✅ Xác nhận: {{ .Num }}"
+"confirmNumberAdd" = "✅ Xác nhận thêm: {{ .Num }}"
 "limitTraffic" = "🚧 Giới hạn lưu lượng"
 "limitTraffic" = "🚧 Giới hạn lưu lượng"
+"getBanLogs" = "Cấm nhật ký"
 
 
 [tgbot.answers]
 [tgbot.answers]
 "successfulOperation" = "✅ Thành công!"
 "successfulOperation" = "✅ Thành công!"
@@ -548,5 +559,4 @@
 "removedTGUserSuccess" = "✅ {{ .Email }} : Người Dùng Telegram Đã Được Xóa Thành Công."
 "removedTGUserSuccess" = "✅ {{ .Email }} : Người Dùng Telegram Đã Được Xóa Thành Công."
 "enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
 "enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
 "disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
 "disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
-"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"
-"askToAddUserName" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng tên người dùng hoặc ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nTên người dùng của bạn: <b>@{{ .TgUserName }}</b>\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"
+"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <code>{{ .TgUserID }}</code>"

+ 20 - 10
web/translation/translate.zh_Hans.toml

@@ -173,7 +173,7 @@
 "setDefaultCert" = "从面板设置证书"
 "setDefaultCert" = "从面板设置证书"
 "xtlsDesc" = "Xray核心需要1.7.5"
 "xtlsDesc" = "Xray核心需要1.7.5"
 "realityDesc" = "Xray核心需要1.8.0及以上版本"
 "realityDesc" = "Xray核心需要1.8.0及以上版本"
-"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
+"telegramDesc" = "使用聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
 "subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
 "subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
 "info" = "信息"
 "info" = "信息"
 "same" = "相同"
 "same" = "相同"
@@ -439,6 +439,7 @@
 "noIpRecord" = "❗ 没有IP记录!"
 "noIpRecord" = "❗ 没有IP记录!"
 "noInbounds" = "❗ 没有找到入站连接!"
 "noInbounds" = "❗ 没有找到入站连接!"
 "unlimited" = "♾ 无限制"
 "unlimited" = "♾ 无限制"
+"add" = "添加"
 "month" = "月"
 "month" = "月"
 "months" = "月"
 "months" = "月"
 "day" = "天"
 "day" = "天"
@@ -447,6 +448,8 @@
 "unknown" = "未知"
 "unknown" = "未知"
 "inbounds" = "入站连接"
 "inbounds" = "入站连接"
 "clients" = "客户端"
 "clients" = "客户端"
+"offline" = "🔴 离线"
+"online" = "🟢 在线的"
 
 
 [tgbot.commands]
 [tgbot.commands]
 "unknown" = "❗ 未知命令"
 "unknown" = "❗ 未知命令"
@@ -457,8 +460,8 @@
 "status" = "✅ 机器人正常运行!"
 "status" = "✅ 机器人正常运行!"
 "usage" = "❗ 请输入要搜索的文本!"
 "usage" = "❗ 请输入要搜索的文本!"
 "getID" = "🆔 您的ID为:<code>{{ .ID }}</code>"
 "getID" = "🆔 您的ID为:<code>{{ .ID }}</code>"
-"helpAdminCommands" = "搜索客户端邮箱:\r\n<code>/usage [Email]</code>\r\n \r\n搜索入站连接(包含客户端统计信息):\r\n<code>/inbound [Remark]</code>"
-"helpClientCommands" = "要搜索统计信息,请使用以下命令:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\n对于vmess/vless,请使用UUID;对于Trojan,请使用密码。"
+"helpAdminCommands" = "搜索客户端邮箱:\r\n<code>/usage [Email]</code>\r\n\r\n搜索入站连接(包含客户端统计信息):\r\n<code>/inbound [Remark]</code>"
+"helpClientCommands" = "要搜索统计信息,请使用以下命令:\r\n\r\n<code>/usage [Email]</code>"
 
 
 [tgbot.messages]
 [tgbot.messages]
 "cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%"
 "cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%"
@@ -488,7 +491,8 @@
 "expire" = "📅 过期日期:{{ .Time }}\r\n"
 "expire" = "📅 过期日期:{{ .Time }}\r\n"
 "expireIn" = "📅 剩余时间:{{ .Time }}\r\n"
 "expireIn" = "📅 剩余时间:{{ .Time }}\r\n"
 "active" = "💡 激活:✅\r\n"
 "active" = "💡 激活:✅\r\n"
-"inactive" = "💡 激活: ❌\r\n"
+"enabled" = "🚨 已启用:{{ .Enable }}\r\n"
+"online" = "🌐 连接状态:{{ .Status }}\r\n"
 "email" = "📧 邮箱:{{ .Email }}\r\n"
 "email" = "📧 邮箱:{{ .Email }}\r\n"
 "upload" = "🔼 上传↑:{{ .Upload }}\r\n"
 "upload" = "🔼 上传↑:{{ .Upload }}\r\n"
 "download" = "🔽 下载↓:{{ .Download }}\r\n"
 "download" = "🔽 下载↓:{{ .Download }}\r\n"
@@ -496,10 +500,13 @@
 "TGUser" = "👤 电报用户:{{ .TelegramID }}\r\n"
 "TGUser" = "👤 电报用户:{{ .TelegramID }}\r\n"
 "exhaustedMsg" = "🚨 耗尽的{{ .Type }}:\r\n"
 "exhaustedMsg" = "🚨 耗尽的{{ .Type }}:\r\n"
 "exhaustedCount" = "🚨 耗尽的{{ .Type }}数量:\r\n"
 "exhaustedCount" = "🚨 耗尽的{{ .Type }}数量:\r\n"
+"onlinesCount" = "🌐 在线客户:{{ .Count }}\r\n"
 "disabled" = "🛑 禁用:{{ .Disabled }}\r\n"
 "disabled" = "🛑 禁用:{{ .Disabled }}\r\n"
-"depleteSoon" = "🔜 即将耗尽:{{ .Deplete }}\r\n \r\n"
+"depleteSoon" = "🔜 即将耗尽:{{ .Deplete }}\r\n\r\n"
 "backupTime" = "🗄 备份时间:{{ .Time }}\r\n"
 "backupTime" = "🗄 备份时间:{{ .Time }}\r\n"
-"refreshedOn" = "\r\n📋🔄 刷新时间:{{ .Time }}\r\n \r\n"
+"refreshedOn" = "\r\n📋🔄 刷新时间:{{ .Time }}\r\n\r\n"
+"yes" = "✅ 是的"
+"no" = "❌ 没有"
 
 
 [tgbot.buttons]
 [tgbot.buttons]
 "closeKeyboard" = "❌ 关闭键盘"
 "closeKeyboard" = "❌ 关闭键盘"
@@ -509,11 +516,13 @@
 "confirmResetTraffic" = "✅ 确认重置流量?"
 "confirmResetTraffic" = "✅ 确认重置流量?"
 "confirmClearIps" = "✅ 确认清除 IP?"
 "confirmClearIps" = "✅ 确认清除 IP?"
 "confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?"
 "confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?"
+"confirmToggle" = "✅ 确认启用/禁用用户?"
 "dbBackup" = "获取数据库备份"
 "dbBackup" = "获取数据库备份"
 "serverUsage" = "服务器使用情况"
 "serverUsage" = "服务器使用情况"
 "getInbounds" = "获取入站信息"
 "getInbounds" = "获取入站信息"
 "depleteSoon" = "即将耗尽"
 "depleteSoon" = "即将耗尽"
 "clientUsage" = "获取使用情况"
 "clientUsage" = "获取使用情况"
+"onlines" = "在线客户"
 "commands" = "命令"
 "commands" = "命令"
 "refresh" = "🔄 刷新"
 "refresh" = "🔄 刷新"
 "clearIPs" = "❌ 清除 IP"
 "clearIPs" = "❌ 清除 IP"
@@ -521,14 +530,16 @@
 "selectTGUser" = "👤 选择 Telegram 用户"
 "selectTGUser" = "👤 选择 Telegram 用户"
 "selectOneTGUser" = "👤 选择一个 Telegram 用户:"
 "selectOneTGUser" = "👤 选择一个 Telegram 用户:"
 "resetTraffic" = "📈 重置流量"
 "resetTraffic" = "📈 重置流量"
-"resetExpire" = "📅 重置过期天数"
+"resetExpire" = "📅 更改到期日期"
 "ipLog" = "🔢 IP 日志"
 "ipLog" = "🔢 IP 日志"
 "ipLimit" = "🔢 IP 限制"
 "ipLimit" = "🔢 IP 限制"
 "setTGUser" = "👤 设置 Telegram 用户"
 "setTGUser" = "👤 设置 Telegram 用户"
 "toggle" = "🔘 启用/禁用"
 "toggle" = "🔘 启用/禁用"
 "custom" = "🔢 风俗"
 "custom" = "🔢 风俗"
-"confirmNumber" = "✅ 确认 : {{ .Num }}"
+"confirmNumber" = "✅ 确认: {{ .Num }}"
+"confirmNumberAdd" = "✅ 确认添加:{{ .Num }}"
 "limitTraffic" = "🚧 交通限制"
 "limitTraffic" = "🚧 交通限制"
+"getBanLogs" = "禁止日志"
 
 
 [tgbot.answers]
 [tgbot.answers]
 "successfulOperation" = "✅ 成功的!"
 "successfulOperation" = "✅ 成功的!"
@@ -548,5 +559,4 @@
 "removedTGUserSuccess" = "✅ {{ .Email }}:Telegram 用户已成功移除。"
 "removedTGUserSuccess" = "✅ {{ .Email }}:Telegram 用户已成功移除。"
 "enableSuccess" = "✅ {{ .Email }}:已成功启用。"
 "enableSuccess" = "✅ {{ .Email }}:已成功启用。"
 "disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
 "disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
-"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问,在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID:<b>{{ .TgUserID }}</b>"
-"askToAddUserName" = "未找到您的配置!\r\n请向管理员询问,在您的配置中使用您的 Telegram 用户名或用户ID。\r\n\r\n您的用户名:<b>@{{ .TgUserName }}</b>\r\n\r\n您的用户ID:<b>{{ .TgUserID }}</b>"
+"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问,在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID:<code>{{ .TgUserID }}</code>"

+ 4 - 4
web/web.go

@@ -240,11 +240,11 @@ func (s *Server) startTask() {
 	if err != nil {
 	if err != nil {
 		logger.Warning("start xray failed:", err)
 		logger.Warning("start xray failed:", err)
 	}
 	}
-	// Check whether xray is running every 30 seconds
-	s.cron.AddJob("@every 30s", job.NewCheckXrayRunningJob())
+	// Check whether xray is running every second
+	s.cron.AddJob("@every 1s", job.NewCheckXrayRunningJob())
 
 
-	// Check if xray needs to be restarted
-	s.cron.AddFunc("@every 10s", func() {
+	// Check if xray needs to be restarted every 30 seconds
+	s.cron.AddFunc("@every 30s", func() {
 		if s.xrayService.IsNeedRestartAndSetFalse() {
 		if s.xrayService.IsNeedRestartAndSetFalse() {
 			err := s.xrayService.RestartXray(false)
 			err := s.xrayService.RestartXray(false)
 			if err != nil {
 			if err != nil {

+ 13 - 1
xray/process.go

@@ -41,12 +41,24 @@ func GetIPLimitLogPath() string {
 	return config.GetLogFolder() + "/3xipl.log"
 	return config.GetLogFolder() + "/3xipl.log"
 }
 }
 
 
+func GetIPLimitPrevLogPath() string {
+	return config.GetLogFolder() + "/3xipl.prev.log"
+}
+
 func GetIPLimitBannedLogPath() string {
 func GetIPLimitBannedLogPath() string {
 	return config.GetLogFolder() + "/3xipl-banned.log"
 	return config.GetLogFolder() + "/3xipl-banned.log"
 }
 }
 
 
+func GetIPLimitBannedPrevLogPath() string {
+	return config.GetLogFolder() + "/3xipl-banned.prev.log"
+}
+
 func GetAccessPersistentLogPath() string {
 func GetAccessPersistentLogPath() string {
-	return config.GetLogFolder() + "/3xipl-access-persistent.log"
+	return config.GetLogFolder() + "/3xipl-ap.log"
+}
+
+func GetAccessPersistentPrevLogPath() string {
+	return config.GetLogFolder() + "/3xipl-ap.prev.log"
 }
 }
 
 
 func GetAccessLogPath() string {
 func GetAccessLogPath() string {