tgbot_router.go 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386
  1. package tgbot
  2. import (
  3. "context"
  4. "fmt"
  5. "html"
  6. "slices"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/mhsanaei/3x-ui/v3/internal/logger"
  11. "github.com/mymmrac/telego"
  12. th "github.com/mymmrac/telego/telegohandler"
  13. tu "github.com/mymmrac/telego/telegoutil"
  14. )
  15. // recoverBotPanic must be deferred by every bot handler entry point: telego's
  16. // dispatch has no recovery of its own, so one bad update would kill the panel.
  17. func recoverBotPanic() {
  18. if r := recover(); r != nil {
  19. logger.Error("Recovered panic in Telegram bot handler:", r)
  20. }
  21. }
  22. // runBotHandler runs a bot handler on a worker slot and recovers panics: a bad
  23. // callback must not take down the whole panel, the way a cron panic would not.
  24. func runBotHandler(fn func()) {
  25. messageWorkerPool <- struct{}{}
  26. defer func() { <-messageWorkerPool }()
  27. defer recoverBotPanic()
  28. fn()
  29. }
  30. // chooseInboundClient fetches the inbound once and reuses the row: the inline
  31. // keyboard outlives the inbound, so a stale tap must answer an error, not panic.
  32. func (t *Tgbot) chooseInboundClient(callbackQuery *telego.CallbackQuery, chatId int64, inboundID int, action string) {
  33. inbound, err := t.inboundService.GetInbound(inboundID)
  34. if err != nil {
  35. logger.Warning("chooseInboundClient GetInbound failed:", err)
  36. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.getInboundsFailed"))
  37. return
  38. }
  39. clientsKB, err := t.getInboundClientsFor(inbound, action)
  40. if err != nil {
  41. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  42. return
  43. }
  44. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseClient", "Inbound=="+inbound.Remark), clientsKB)
  45. }
  46. // OnReceive starts the message receiving loop for the Telegram bot.
  47. func (t *Tgbot) OnReceive() {
  48. params := telego.GetUpdatesParams{
  49. Timeout: 20, // Reduced timeout to detect connection issues faster
  50. }
  51. // Strict singleton: never start a second long-polling loop.
  52. tgBotMutex.Lock()
  53. if botCancel != nil || isRunning {
  54. tgBotMutex.Unlock()
  55. logger.Warning("TgBot OnReceive called while already running; ignoring.")
  56. return
  57. }
  58. ctx, cancel := context.WithCancel(context.Background())
  59. botCancel = cancel
  60. isRunning = true
  61. // Add to WaitGroup before releasing the lock so StopBot() can't return
  62. // before this receiver goroutine is accounted for.
  63. botWG.Add(1)
  64. tgBotMutex.Unlock()
  65. // Get updates channel using the context with shorter timeout for better error recovery
  66. updates, _ := bot.UpdatesViaLongPolling(ctx, &params)
  67. go func() {
  68. defer botWG.Done()
  69. h, _ := th.NewBotHandler(bot, updates)
  70. tgBotMutex.Lock()
  71. botHandler = h
  72. tgBotMutex.Unlock()
  73. h.HandleMessage(func(ctx *th.Context, message telego.Message) error {
  74. defer recoverBotPanic()
  75. userStateMgr.clear(message.Chat.ID)
  76. t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.keyboardClosed"), tu.ReplyKeyboardRemove())
  77. return nil
  78. }, th.TextEqual(t.I18nBot("tgbot.buttons.closeKeyboard")))
  79. h.HandleMessage(func(ctx *th.Context, message telego.Message) error {
  80. defer recoverBotPanic()
  81. if !t.isCommandForCurrentBot(&message) {
  82. return nil
  83. }
  84. // Use goroutine with worker pool for concurrent command processing
  85. go runBotHandler(func() {
  86. userStateMgr.clear(message.Chat.ID)
  87. t.answerCommand(&message, message.Chat.ID, checkAdmin(message.From.ID))
  88. })
  89. return nil
  90. }, th.AnyCommand())
  91. h.HandleCallbackQuery(func(ctx *th.Context, query telego.CallbackQuery) error {
  92. // Use goroutine with worker pool for concurrent callback processing
  93. go runBotHandler(func() {
  94. userStateMgr.clear(query.Message.GetChat().ID)
  95. t.answerCallback(&query, checkAdmin(query.From.ID))
  96. })
  97. return nil
  98. }, th.AnyCallbackQueryWithMessage())
  99. h.HandleMessage(func(ctx *th.Context, message telego.Message) error {
  100. defer recoverBotPanic()
  101. userStateMgr.maybePrune(time.Hour)
  102. if userState, exists := userStateMgr.get(message.Chat.ID); exists {
  103. // Only a wizard step touches the draft, so only it takes the lock.
  104. draft := addClientDrafts.forChat(message.Chat.ID)
  105. draft.Lock()
  106. defer draft.Unlock()
  107. switch userState {
  108. case "awaiting_email":
  109. if draft.email == strings.TrimSpace(message.Text) {
  110. t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove())
  111. userStateMgr.clear(message.Chat.ID)
  112. return nil
  113. }
  114. draft.email = strings.TrimSpace(message.Text)
  115. if t.isSingleWord(draft.email) {
  116. userStateMgr.set(message.Chat.ID, "awaiting_email")
  117. cancel_btn_markup := tu.InlineKeyboard(
  118. tu.InlineKeyboardRow(
  119. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
  120. ),
  121. )
  122. t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.incorrect_input"), cancel_btn_markup)
  123. } else {
  124. t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_email"), 3, tu.ReplyKeyboardRemove())
  125. userStateMgr.clear(message.Chat.ID)
  126. t.addClient(message.Chat.ID, draft, t.BuildClientDraftMessage(draft))
  127. }
  128. case "awaiting_comment":
  129. if draft.comment == strings.TrimSpace(message.Text) {
  130. t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove())
  131. userStateMgr.clear(message.Chat.ID)
  132. return nil
  133. }
  134. draft.comment = strings.TrimSpace(message.Text)
  135. t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_comment"), 3, tu.ReplyKeyboardRemove())
  136. userStateMgr.clear(message.Chat.ID)
  137. t.addClient(message.Chat.ID, draft, t.BuildClientDraftMessage(draft))
  138. case "awaiting_tg_id":
  139. input := strings.TrimSpace(message.Text)
  140. if input == "" || input == "-" || strings.EqualFold(input, "none") {
  141. draft.tgID = ""
  142. t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove())
  143. userStateMgr.clear(message.Chat.ID)
  144. t.addClient(message.Chat.ID, draft, t.BuildClientDraftMessage(draft))
  145. return nil
  146. }
  147. if _, err := strconv.ParseInt(input, 10, 64); err != nil {
  148. cancel_btn_markup := tu.InlineKeyboard(
  149. tu.InlineKeyboardRow(
  150. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
  151. ),
  152. )
  153. t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.incorrect_input"), cancel_btn_markup)
  154. return nil
  155. }
  156. draft.tgID = input
  157. t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.userSaved"), 3, tu.ReplyKeyboardRemove())
  158. userStateMgr.clear(message.Chat.ID)
  159. t.addClient(message.Chat.ID, draft, t.BuildClientDraftMessage(draft))
  160. }
  161. } else {
  162. if message.UsersShared != nil {
  163. if checkAdmin(message.From.ID) {
  164. for _, sharedUser := range message.UsersShared.Users {
  165. userID := sharedUser.UserID
  166. needRestart, err := t.clientService.SetClientTelegramUserID(&t.inboundService, message.UsersShared.RequestID, userID)
  167. if needRestart {
  168. t.xrayService.SetToNeedRestart()
  169. }
  170. output := ""
  171. if err != nil {
  172. output += t.I18nBot("tgbot.messages.selectUserFailed")
  173. } else {
  174. output += t.I18nBot("tgbot.messages.userSaved")
  175. }
  176. t.SendMsgToTgbot(message.Chat.ID, output, tu.ReplyKeyboardRemove())
  177. }
  178. } else {
  179. t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.noResult"), tu.ReplyKeyboardRemove())
  180. }
  181. }
  182. }
  183. return nil
  184. }, th.AnyMessage())
  185. _ = h.Start()
  186. }()
  187. }
  188. // answerCommand processes incoming command messages from Telegram users.
  189. func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin bool) {
  190. msg, onlyMessage := "", false
  191. command, _, commandArgs := tu.ParseCommand(message.Text)
  192. // Helper function to handle unknown commands.
  193. handleUnknownCommand := func() {
  194. msg += t.I18nBot("tgbot.commands.unknown")
  195. }
  196. // Handle the command.
  197. switch command {
  198. case "help":
  199. msg += t.I18nBot("tgbot.commands.help")
  200. msg += t.I18nBot("tgbot.commands.pleaseChoose")
  201. case "start":
  202. msg += t.I18nBot("tgbot.commands.start", "Firstname=="+html.EscapeString(message.From.FirstName))
  203. if isAdmin {
  204. msg += t.I18nBot("tgbot.commands.welcome", "Hostname=="+hostname)
  205. }
  206. msg += "\n\n" + t.I18nBot("tgbot.commands.pleaseChoose")
  207. case "status":
  208. onlyMessage = true
  209. msg += t.I18nBot("tgbot.commands.status")
  210. case "id":
  211. onlyMessage = true
  212. msg += t.I18nBot("tgbot.commands.getID", "ID=="+strconv.FormatInt(message.From.ID, 10))
  213. case "usage":
  214. onlyMessage = true
  215. if len(commandArgs) > 0 {
  216. if isAdmin {
  217. t.searchClient(chatId, commandArgs[0])
  218. } else {
  219. t.getClientUsage(chatId, message.From.ID, commandArgs[0])
  220. }
  221. } else {
  222. msg += t.I18nBot("tgbot.commands.usage")
  223. }
  224. case "inbound":
  225. onlyMessage = true
  226. if isAdmin && len(commandArgs) > 0 {
  227. t.searchInbound(chatId, commandArgs[0])
  228. } else {
  229. handleUnknownCommand()
  230. }
  231. case "restart":
  232. onlyMessage = true
  233. if isAdmin {
  234. if len(commandArgs) == 0 {
  235. if t.xrayService.IsXrayRunning() {
  236. err := t.xrayService.RestartXray(true)
  237. if err != nil {
  238. msg += t.I18nBot("tgbot.commands.restartFailed", "Error=="+err.Error())
  239. } else {
  240. msg += t.I18nBot("tgbot.commands.restartSuccess")
  241. }
  242. } else {
  243. msg += t.I18nBot("tgbot.commands.xrayNotRunning")
  244. }
  245. } else {
  246. handleUnknownCommand()
  247. msg += t.I18nBot("tgbot.commands.restartUsage")
  248. }
  249. } else {
  250. handleUnknownCommand()
  251. }
  252. case "clearall":
  253. onlyMessage = true
  254. if isAdmin {
  255. inlineKeyboard := tu.InlineKeyboard(
  256. tu.InlineKeyboardRow(
  257. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancelReset")).WithCallbackData(t.encodeQuery("reset_all_traffics_cancel")),
  258. ),
  259. tu.InlineKeyboardRow(
  260. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmResetTraffic")).WithCallbackData(t.encodeQuery("reset_all_traffics_c")),
  261. ),
  262. )
  263. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.AreYouSure"), inlineKeyboard)
  264. } else {
  265. handleUnknownCommand()
  266. }
  267. default:
  268. handleUnknownCommand()
  269. }
  270. if msg != "" {
  271. t.sendResponse(chatId, msg, onlyMessage, isAdmin)
  272. }
  273. }
  274. func (t *Tgbot) isCommandForCurrentBot(message *telego.Message) bool {
  275. return isCommandForBot(message.Text, botUsername())
  276. }
  277. func botUsername() string {
  278. if bot == nil {
  279. return ""
  280. }
  281. return bot.Username()
  282. }
  283. func isCommandForBot(text string, username string) bool {
  284. _, commandUsername, _ := tu.ParseCommand(text)
  285. return commandUsername == "" || username == "" || strings.EqualFold(commandUsername, username)
  286. }
  287. // answerCallback processes callback queries from inline keyboards.
  288. func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
  289. chatId := callbackQuery.Message.GetChat().ID
  290. // Only an admin's wizard callbacks touch a draft, so only they take its lock:
  291. // a report tap must not wait on a slot, a rejected chat must not be stored.
  292. var draft *clientDraft
  293. if isAdmin && isAddClientStep(callbackQuery.Data) {
  294. draft = addClientDrafts.forChat(chatId)
  295. draft.Lock()
  296. defer draft.Unlock()
  297. }
  298. if isAdmin {
  299. // get query from hash storage
  300. decodedQuery, err := t.decodeQuery(callbackQuery.Data)
  301. if err != nil {
  302. // A button older than the 20-minute hash window is the common case
  303. // here; the answer clears it, the message outlives a failed send.
  304. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.noQuery"))
  305. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.noQuery"))
  306. return
  307. }
  308. dataArray := strings.Split(decodedQuery, " ")
  309. if len(dataArray) >= 2 && len(dataArray[1]) > 0 {
  310. email := dataArray[1]
  311. switch dataArray[0] {
  312. case "get_clients_for_sub":
  313. inboundIdInt, err := strconv.Atoi(dataArray[1])
  314. if err != nil {
  315. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  316. return
  317. }
  318. t.chooseInboundClient(callbackQuery, chatId, inboundIdInt, "client_sub_links")
  319. case "get_clients_for_individual":
  320. inboundIdInt, err := strconv.Atoi(dataArray[1])
  321. if err != nil {
  322. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  323. return
  324. }
  325. t.chooseInboundClient(callbackQuery, chatId, inboundIdInt, "client_individual_links")
  326. case "get_clients_for_qr":
  327. inboundIdInt, err := strconv.Atoi(dataArray[1])
  328. if err != nil {
  329. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  330. return
  331. }
  332. t.chooseInboundClient(callbackQuery, chatId, inboundIdInt, "client_qr_links")
  333. case "client_sub_links":
  334. t.sendClientSubLinks(chatId, email)
  335. return
  336. case "client_individual_links":
  337. t.sendClientIndividualLinks(chatId, email)
  338. return
  339. case "client_qr_links":
  340. t.sendClientQRLinks(chatId, email)
  341. return
  342. case "client_get_usage":
  343. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.messages.email", "Email=="+email))
  344. t.searchClient(chatId, email)
  345. case "client_refresh":
  346. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.clientRefreshSuccess", "Email=="+email))
  347. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  348. case "client_cancel":
  349. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+email))
  350. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  351. case "ips_refresh":
  352. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.IpRefreshSuccess", "Email=="+email))
  353. t.searchClientIps(chatId, email, callbackQuery.Message.GetMessageID())
  354. case "ips_cancel":
  355. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+email))
  356. t.searchClientIps(chatId, email, callbackQuery.Message.GetMessageID())
  357. case "tgid_refresh":
  358. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.TGIdRefreshSuccess", "Email=="+email))
  359. t.clientTelegramUserInfo(chatId, email, callbackQuery.Message.GetMessageID())
  360. case "tgid_cancel":
  361. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+email))
  362. t.clientTelegramUserInfo(chatId, email, callbackQuery.Message.GetMessageID())
  363. case "reset_traffic":
  364. inlineKeyboard := tu.InlineKeyboard(
  365. tu.InlineKeyboardRow(
  366. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancelReset")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
  367. ),
  368. tu.InlineKeyboardRow(
  369. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmResetTraffic")).WithCallbackData(t.encodeQuery("reset_traffic_c "+email)),
  370. ),
  371. )
  372. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  373. case "reset_traffic_c":
  374. err := t.inboundService.ResetClientTrafficByEmail(email)
  375. if err == nil {
  376. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.resetTrafficSuccess", "Email=="+email))
  377. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  378. } else {
  379. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  380. }
  381. case "limit_traffic":
  382. inlineKeyboard := tu.InlineKeyboard(
  383. tu.InlineKeyboardRow(
  384. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
  385. ),
  386. tu.InlineKeyboardRow(
  387. tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 0")),
  388. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("limit_traffic_in "+email+" 0")),
  389. ),
  390. tu.InlineKeyboardRow(
  391. tu.InlineKeyboardButton("1 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 1")),
  392. tu.InlineKeyboardButton("5 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 5")),
  393. tu.InlineKeyboardButton("10 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 10")),
  394. ),
  395. tu.InlineKeyboardRow(
  396. tu.InlineKeyboardButton("20 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 20")),
  397. tu.InlineKeyboardButton("30 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 30")),
  398. tu.InlineKeyboardButton("40 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 40")),
  399. ),
  400. tu.InlineKeyboardRow(
  401. tu.InlineKeyboardButton("50 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 50")),
  402. tu.InlineKeyboardButton("60 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 60")),
  403. tu.InlineKeyboardButton("80 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 80")),
  404. ),
  405. tu.InlineKeyboardRow(
  406. tu.InlineKeyboardButton("100 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 100")),
  407. tu.InlineKeyboardButton("150 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 150")),
  408. tu.InlineKeyboardButton("200 GB").WithCallbackData(t.encodeQuery("limit_traffic_c "+email+" 200")),
  409. ),
  410. )
  411. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  412. case "limit_traffic_c":
  413. if len(dataArray) == 3 {
  414. limitTraffic, err := strconv.Atoi(dataArray[2])
  415. if err == nil {
  416. needRestart, err := t.clientService.ResetClientTrafficLimitByEmail(&t.inboundService, email, limitTraffic)
  417. if needRestart {
  418. t.xrayService.SetToNeedRestart()
  419. }
  420. if err == nil {
  421. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.setTrafficLimitSuccess", "Email=="+email))
  422. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  423. return
  424. }
  425. }
  426. }
  427. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  428. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  429. case "limit_traffic_in":
  430. if len(dataArray) >= 3 {
  431. oldInputNumber, err := strconv.Atoi(dataArray[2])
  432. inputNumber := oldInputNumber
  433. if err == nil {
  434. if len(dataArray) == 4 {
  435. num, err := strconv.Atoi(dataArray[3])
  436. if err == nil {
  437. inputNumber = updateNumericInput(inputNumber, num)
  438. }
  439. if inputNumber == oldInputNumber {
  440. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  441. return
  442. }
  443. if inputNumber >= 999999 {
  444. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  445. return
  446. }
  447. }
  448. inlineKeyboard := t.numericKeypad(numericKeypadSpec{dataBase: "limit_traffic", dataArgs: email + " ", cancelData: "client_cancel " + email, confirmLabelKey: "tgbot.buttons.confirmNumberAdd"}, inputNumber)
  449. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  450. return
  451. }
  452. }
  453. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  454. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  455. case "add_client_limit_traffic_c":
  456. limitTraffic, _ := strconv.ParseInt(dataArray[1], 10, 64)
  457. draft.totalGB = limitTraffic * 1024 * 1024 * 1024
  458. messageId := callbackQuery.Message.GetMessageID()
  459. message_text := t.BuildClientDraftMessage(draft)
  460. t.addClient(callbackQuery.Message.GetChat().ID, draft, message_text, messageId)
  461. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  462. case "add_client_limit_traffic_in":
  463. if len(dataArray) >= 2 {
  464. oldInputNumber, err := strconv.Atoi(dataArray[1])
  465. inputNumber := oldInputNumber
  466. if err == nil {
  467. if len(dataArray) == 3 {
  468. num, err := strconv.Atoi(dataArray[2])
  469. if err == nil {
  470. inputNumber = updateNumericInput(inputNumber, num)
  471. }
  472. if inputNumber == oldInputNumber {
  473. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  474. return
  475. }
  476. if inputNumber >= 999999 {
  477. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  478. return
  479. }
  480. }
  481. inlineKeyboard := t.numericKeypad(numericKeypadSpec{dataBase: "add_client_limit_traffic", cancelData: "add_client_default_traffic_exp", confirmLabelKey: "tgbot.buttons.confirmNumberAdd"}, inputNumber)
  482. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  483. return
  484. }
  485. }
  486. case "reset_exp":
  487. inlineKeyboard := tu.InlineKeyboard(
  488. tu.InlineKeyboardRow(
  489. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancelReset")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
  490. ),
  491. tu.InlineKeyboardRow(
  492. tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 0")),
  493. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("reset_exp_in "+email+" 0")),
  494. ),
  495. tu.InlineKeyboardRow(
  496. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 7 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 7")),
  497. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 10 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 10")),
  498. ),
  499. tu.InlineKeyboardRow(
  500. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 14 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 14")),
  501. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 20 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 20")),
  502. ),
  503. tu.InlineKeyboardRow(
  504. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 1 "+t.I18nBot("tgbot.month")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 30")),
  505. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 3 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 90")),
  506. ),
  507. tu.InlineKeyboardRow(
  508. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 6 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 180")),
  509. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 12 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("reset_exp_c "+email+" 365")),
  510. ),
  511. )
  512. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  513. case "reset_exp_c":
  514. if len(dataArray) == 3 {
  515. days, err := strconv.ParseInt(dataArray[2], 10, 64)
  516. if err == nil {
  517. var date int64
  518. if days > 0 {
  519. traffic, err := t.inboundService.GetClientTrafficByEmail(email)
  520. if err != nil {
  521. logger.Warning(err)
  522. msg := t.I18nBot("tgbot.wentWrong")
  523. t.SendMsgToTgbot(chatId, msg)
  524. return
  525. }
  526. if traffic == nil {
  527. msg := t.I18nBot("tgbot.noResult")
  528. t.SendMsgToTgbot(chatId, msg)
  529. return
  530. }
  531. if traffic.ExpiryTime > 0 {
  532. if traffic.ExpiryTime-time.Now().Unix()*1000 < 0 {
  533. date = -(days * 24 * 60 * 60000)
  534. } else {
  535. date = traffic.ExpiryTime + days*24*60*60000
  536. }
  537. } else {
  538. date = traffic.ExpiryTime - days*24*60*60000
  539. }
  540. }
  541. needRestart, err := t.clientService.ResetClientExpiryTimeByEmail(&t.inboundService, email, date)
  542. if needRestart {
  543. t.xrayService.SetToNeedRestart()
  544. }
  545. if err == nil {
  546. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.expireResetSuccess", "Email=="+email))
  547. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  548. return
  549. }
  550. }
  551. }
  552. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  553. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  554. case "reset_exp_in":
  555. if len(dataArray) >= 3 {
  556. oldInputNumber, err := strconv.Atoi(dataArray[2])
  557. inputNumber := oldInputNumber
  558. if err == nil {
  559. if len(dataArray) == 4 {
  560. num, err := strconv.Atoi(dataArray[3])
  561. if err == nil {
  562. inputNumber = updateNumericInput(inputNumber, num)
  563. }
  564. if inputNumber == oldInputNumber {
  565. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  566. return
  567. }
  568. if inputNumber >= 999999 {
  569. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  570. return
  571. }
  572. }
  573. inlineKeyboard := t.numericKeypad(numericKeypadSpec{dataBase: "reset_exp", dataArgs: email + " ", cancelData: "client_cancel " + email, confirmLabelKey: "tgbot.buttons.confirmNumber"}, inputNumber)
  574. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  575. return
  576. }
  577. }
  578. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  579. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  580. case "add_client_reset_exp_c":
  581. draft.expiryTime = 0
  582. days, _ := strconv.ParseInt(dataArray[1], 10, 64)
  583. var date int64
  584. if draft.expiryTime > 0 {
  585. if draft.expiryTime-time.Now().Unix()*1000 < 0 {
  586. date = -(days * 24 * 60 * 60000)
  587. } else {
  588. date = draft.expiryTime + days*24*60*60000
  589. }
  590. } else {
  591. date = draft.expiryTime - days*24*60*60000
  592. }
  593. draft.expiryTime = date
  594. messageId := callbackQuery.Message.GetMessageID()
  595. message_text := t.BuildClientDraftMessage(draft)
  596. t.addClient(callbackQuery.Message.GetChat().ID, draft, message_text, messageId)
  597. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  598. case "add_client_reset_exp_in":
  599. if len(dataArray) >= 2 {
  600. oldInputNumber, err := strconv.Atoi(dataArray[1])
  601. inputNumber := oldInputNumber
  602. if err == nil {
  603. if len(dataArray) == 3 {
  604. num, err := strconv.Atoi(dataArray[2])
  605. if err == nil {
  606. inputNumber = updateNumericInput(inputNumber, num)
  607. }
  608. if inputNumber == oldInputNumber {
  609. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  610. return
  611. }
  612. if inputNumber >= 999999 {
  613. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  614. return
  615. }
  616. }
  617. inlineKeyboard := t.numericKeypad(numericKeypadSpec{dataBase: "add_client_reset_exp", cancelData: "add_client_default_traffic_exp", confirmLabelKey: "tgbot.buttons.confirmNumberAdd"}, inputNumber)
  618. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  619. return
  620. }
  621. }
  622. case "ip_limit":
  623. inlineKeyboard := tu.InlineKeyboard(
  624. tu.InlineKeyboardRow(
  625. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancelIpLimit")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
  626. ),
  627. tu.InlineKeyboardRow(
  628. tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 0")),
  629. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("ip_limit_in "+email+" 0")),
  630. ),
  631. tu.InlineKeyboardRow(
  632. tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 1")),
  633. tu.InlineKeyboardButton("2").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 2")),
  634. ),
  635. tu.InlineKeyboardRow(
  636. tu.InlineKeyboardButton("3").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 3")),
  637. tu.InlineKeyboardButton("4").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 4")),
  638. ),
  639. tu.InlineKeyboardRow(
  640. tu.InlineKeyboardButton("5").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 5")),
  641. tu.InlineKeyboardButton("6").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 6")),
  642. tu.InlineKeyboardButton("7").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 7")),
  643. ),
  644. tu.InlineKeyboardRow(
  645. tu.InlineKeyboardButton("8").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 8")),
  646. tu.InlineKeyboardButton("9").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 9")),
  647. tu.InlineKeyboardButton("10").WithCallbackData(t.encodeQuery("ip_limit_c "+email+" 10")),
  648. ),
  649. )
  650. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  651. case "ip_limit_c":
  652. if len(dataArray) == 3 {
  653. count, err := strconv.Atoi(dataArray[2])
  654. if err == nil {
  655. needRestart, err := t.clientService.ResetClientIpLimitByEmail(&t.inboundService, email, count)
  656. if needRestart {
  657. t.xrayService.SetToNeedRestart()
  658. }
  659. if err == nil {
  660. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.resetIpSuccess", "Email=="+email, "Count=="+strconv.Itoa(count)))
  661. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  662. return
  663. }
  664. }
  665. }
  666. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  667. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  668. case "ip_limit_in":
  669. if len(dataArray) >= 3 {
  670. oldInputNumber, err := strconv.Atoi(dataArray[2])
  671. inputNumber := oldInputNumber
  672. if err == nil {
  673. if len(dataArray) == 4 {
  674. num, err := strconv.Atoi(dataArray[3])
  675. if err == nil {
  676. inputNumber = updateNumericInput(inputNumber, num)
  677. }
  678. if inputNumber == oldInputNumber {
  679. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  680. return
  681. }
  682. if inputNumber >= 999999 {
  683. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  684. return
  685. }
  686. }
  687. inlineKeyboard := t.numericKeypad(numericKeypadSpec{dataBase: "ip_limit", dataArgs: email + " ", cancelData: "client_cancel " + email, confirmLabelKey: "tgbot.buttons.confirmNumber"}, inputNumber)
  688. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  689. return
  690. }
  691. }
  692. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  693. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  694. case "add_client_ip_limit_c":
  695. if len(dataArray) == 2 {
  696. count, _ := strconv.Atoi(dataArray[1])
  697. draft.limitIP = count
  698. }
  699. messageId := callbackQuery.Message.GetMessageID()
  700. message_text := t.BuildClientDraftMessage(draft)
  701. t.addClient(callbackQuery.Message.GetChat().ID, draft, message_text, messageId)
  702. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  703. case "add_client_ip_limit_in":
  704. if len(dataArray) >= 2 {
  705. oldInputNumber, err := strconv.Atoi(dataArray[1])
  706. inputNumber := oldInputNumber
  707. if err == nil {
  708. if len(dataArray) == 3 {
  709. num, err := strconv.Atoi(dataArray[2])
  710. if err == nil {
  711. inputNumber = updateNumericInput(inputNumber, num)
  712. }
  713. if inputNumber == oldInputNumber {
  714. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  715. return
  716. }
  717. if inputNumber >= 999999 {
  718. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  719. return
  720. }
  721. }
  722. inlineKeyboard := t.numericKeypad(numericKeypadSpec{dataBase: "add_client_ip_limit", cancelData: "add_client_default_ip_limit", confirmLabelKey: "tgbot.buttons.confirmNumber"}, inputNumber)
  723. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  724. return
  725. }
  726. }
  727. case "clear_ips":
  728. inlineKeyboard := tu.InlineKeyboard(
  729. tu.InlineKeyboardRow(
  730. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("ips_cancel "+email)),
  731. ),
  732. tu.InlineKeyboardRow(
  733. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmClearIps")).WithCallbackData(t.encodeQuery("clear_ips_c "+email)),
  734. ),
  735. )
  736. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  737. case "clear_ips_c":
  738. err := t.inboundService.ClearClientIps(email)
  739. if err == nil {
  740. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.clearIpSuccess", "Email=="+email))
  741. t.searchClientIps(chatId, email, callbackQuery.Message.GetMessageID())
  742. } else {
  743. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  744. }
  745. case "ip_log":
  746. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.getIpLog", "Email=="+email))
  747. t.searchClientIps(chatId, email)
  748. case "tg_user":
  749. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.getUserInfo", "Email=="+email))
  750. t.clientTelegramUserInfo(chatId, email)
  751. case "tgid_remove":
  752. inlineKeyboard := tu.InlineKeyboard(
  753. tu.InlineKeyboardRow(
  754. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("tgid_cancel "+email)),
  755. ),
  756. tu.InlineKeyboardRow(
  757. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmRemoveTGUser")).WithCallbackData(t.encodeQuery("tgid_remove_c "+email)),
  758. ),
  759. )
  760. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  761. case "tgid_remove_c":
  762. traffic, err := t.inboundService.GetClientTrafficByEmail(email)
  763. if err != nil || traffic == nil {
  764. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  765. return
  766. }
  767. needRestart, err := t.clientService.SetClientTelegramUserID(&t.inboundService, traffic.Id, EmptyTelegramUserID)
  768. if needRestart {
  769. t.xrayService.SetToNeedRestart()
  770. }
  771. if err == nil {
  772. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.removedTGUserSuccess", "Email=="+email))
  773. t.clientTelegramUserInfo(chatId, email, callbackQuery.Message.GetMessageID())
  774. } else {
  775. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  776. }
  777. case "toggle_enable":
  778. inlineKeyboard := tu.InlineKeyboard(
  779. tu.InlineKeyboardRow(
  780. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("client_cancel "+email)),
  781. ),
  782. tu.InlineKeyboardRow(
  783. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmToggle")).WithCallbackData(t.encodeQuery("toggle_enable_c "+email)),
  784. ),
  785. )
  786. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  787. case "toggle_enable_c":
  788. enabled, needRestart, err := t.clientService.ToggleClientEnableByEmail(&t.inboundService, email)
  789. if needRestart {
  790. t.xrayService.SetToNeedRestart()
  791. }
  792. if err == nil {
  793. if enabled {
  794. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.enableSuccess", "Email=="+email))
  795. } else {
  796. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.disableSuccess", "Email=="+email))
  797. }
  798. t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
  799. } else {
  800. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  801. }
  802. case "get_clients":
  803. inboundId := dataArray[1]
  804. inboundIdInt, err := strconv.Atoi(inboundId)
  805. if err != nil {
  806. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  807. return
  808. }
  809. inbound, err := t.inboundService.GetInbound(inboundIdInt)
  810. if err != nil {
  811. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  812. return
  813. }
  814. clients, err := t.getInboundClients(inboundIdInt)
  815. if err != nil {
  816. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  817. return
  818. }
  819. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseClient", "Inbound=="+inbound.Remark), clients)
  820. case "add_client_to":
  821. draft.email = t.randomLowerAndNum(8)
  822. draft.limitIP = 0
  823. draft.totalGB = 0
  824. draft.expiryTime = 0
  825. draft.enable = true
  826. draft.tgID = ""
  827. draft.subID = t.randomLowerAndNum(16)
  828. draft.comment = ""
  829. draft.reset = 0
  830. inboundId := dataArray[1]
  831. inboundIdInt, err := strconv.Atoi(inboundId)
  832. if err != nil {
  833. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  834. return
  835. }
  836. draft.receiverInboundID = inboundIdInt
  837. draft.receiverInboundIDs = []int{inboundIdInt}
  838. t.addClient(callbackQuery.Message.GetChat().ID, draft, t.BuildClientDraftMessage(draft))
  839. case "add_client_toggle_attach":
  840. inboundIdStr := dataArray[1]
  841. inboundIdInt, err := strconv.Atoi(inboundIdStr)
  842. if err != nil {
  843. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  844. return
  845. }
  846. found := -1
  847. for i, id := range draft.receiverInboundIDs {
  848. if id == inboundIdInt {
  849. found = i
  850. break
  851. }
  852. }
  853. if found >= 0 {
  854. draft.receiverInboundIDs = append(draft.receiverInboundIDs[:found], draft.receiverInboundIDs[found+1:]...)
  855. } else {
  856. draft.receiverInboundIDs = append(draft.receiverInboundIDs, inboundIdInt)
  857. }
  858. picker, err := t.getInboundsAttachPicker(draft)
  859. if err != nil {
  860. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  861. return
  862. }
  863. t.editMessageCallbackTgBot(callbackQuery.Message.GetChat().ID, callbackQuery.Message.GetMessageID(), picker)
  864. default:
  865. // An unknown action with arguments is still a tap, and an
  866. // unanswered tap spins until Telegram times it out.
  867. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  868. }
  869. return
  870. } else {
  871. switch callbackQuery.Data {
  872. case "get_inbounds":
  873. inbounds, err := t.getInbounds()
  874. if err != nil {
  875. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  876. return
  877. }
  878. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.allClients"))
  879. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
  880. case "admin_client_sub_links":
  881. inbounds, err := t.getInboundsFor("get_clients_for_sub")
  882. if err != nil {
  883. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  884. return
  885. }
  886. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
  887. case "admin_client_individual_links":
  888. inbounds, err := t.getInboundsFor("get_clients_for_individual")
  889. if err != nil {
  890. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  891. return
  892. }
  893. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
  894. case "admin_client_qr_links":
  895. inbounds, err := t.getInboundsFor("get_clients_for_qr")
  896. if err != nil {
  897. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  898. return
  899. }
  900. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
  901. }
  902. }
  903. }
  904. if !isAdmin {
  905. // encodeQuery hashes any payload past 64 chars, so a long email's button
  906. // must be decoded before the gate can see which client it names.
  907. if decoded, err := t.decodeQuery(callbackQuery.Data); err == nil {
  908. callbackQuery.Data = decoded
  909. }
  910. if !isClientSelfCallback(callbackQuery.Data) {
  911. return
  912. }
  913. }
  914. switch callbackQuery.Data {
  915. case "get_usage":
  916. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.serverUsage"))
  917. t.getServerUsage(chatId)
  918. case "usage_refresh":
  919. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  920. t.getServerUsage(chatId, callbackQuery.Message.GetMessageID())
  921. case "inbounds":
  922. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.getInbounds"))
  923. t.SendMsgToTgbot(chatId, t.getInboundUsages())
  924. case "deplete_soon":
  925. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.depleteSoon"))
  926. t.getExhausted(chatId)
  927. case "get_backup":
  928. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.dbBackup"))
  929. t.sendBackup(chatId)
  930. case "get_banlogs":
  931. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.getBanLogs"))
  932. t.sendBanLogs(chatId, true)
  933. case "client_traffic":
  934. tgUserID := callbackQuery.From.ID
  935. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.clientUsage"))
  936. t.getClientUsage(chatId, tgUserID)
  937. case "client_commands":
  938. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands"))
  939. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpClientCommands"))
  940. case "client_sub_links":
  941. // show user's own clients to choose one for sub links
  942. tgUserID := callbackQuery.From.ID
  943. traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
  944. if err != nil {
  945. // fallback to message
  946. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation")+"\r\n"+err.Error())
  947. return
  948. }
  949. if len(traffics) == 0 {
  950. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.askToAddUserId", "TgUserID=="+strconv.FormatInt(tgUserID, 10)))
  951. return
  952. }
  953. var buttons []telego.InlineKeyboardButton
  954. for _, tr := range traffics {
  955. buttons = append(buttons, tu.InlineKeyboardButton(tr.Email).WithCallbackData(t.encodeQuery("client_sub_links "+tr.Email)))
  956. }
  957. cols := 1
  958. if len(buttons) >= 6 {
  959. cols = 2
  960. }
  961. keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
  962. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.pleaseChoose"), keyboard)
  963. case "client_individual_links":
  964. // show user's clients to choose for individual links
  965. tgUserID := callbackQuery.From.ID
  966. traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
  967. if err != nil {
  968. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation")+"\r\n"+err.Error())
  969. return
  970. }
  971. if len(traffics) == 0 {
  972. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.askToAddUserId", "TgUserID=="+strconv.FormatInt(tgUserID, 10)))
  973. return
  974. }
  975. var buttons2 []telego.InlineKeyboardButton
  976. for _, tr := range traffics {
  977. buttons2 = append(buttons2, tu.InlineKeyboardButton(tr.Email).WithCallbackData(t.encodeQuery("client_individual_links "+tr.Email)))
  978. }
  979. cols2 := 1
  980. if len(buttons2) >= 6 {
  981. cols2 = 2
  982. }
  983. keyboard2 := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols2, buttons2...))
  984. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.pleaseChoose"), keyboard2)
  985. case "client_qr_links":
  986. // show user's clients to choose for QR codes
  987. tgUserID := callbackQuery.From.ID
  988. traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
  989. if err != nil {
  990. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOccurred")+"\r\n"+err.Error())
  991. return
  992. }
  993. if len(traffics) == 0 {
  994. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.askToAddUserId", "TgUserID=="+strconv.FormatInt(tgUserID, 10)))
  995. return
  996. }
  997. var buttons3 []telego.InlineKeyboardButton
  998. for _, tr := range traffics {
  999. buttons3 = append(buttons3, tu.InlineKeyboardButton(tr.Email).WithCallbackData(t.encodeQuery("client_qr_links "+tr.Email)))
  1000. }
  1001. cols3 := 1
  1002. if len(buttons3) >= 6 {
  1003. cols3 = 2
  1004. }
  1005. keyboard3 := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols3, buttons3...))
  1006. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.pleaseChoose"), keyboard3)
  1007. case "onlines":
  1008. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.onlines"))
  1009. t.onlineClients(chatId)
  1010. case "onlines_refresh":
  1011. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
  1012. t.onlineClients(chatId, callbackQuery.Message.GetMessageID())
  1013. case "commands":
  1014. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands"))
  1015. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpAdminCommands"))
  1016. case "add_client":
  1017. draft.email = t.randomLowerAndNum(8)
  1018. draft.limitIP = 0
  1019. draft.totalGB = 0
  1020. draft.expiryTime = 0
  1021. draft.enable = true
  1022. draft.tgID = ""
  1023. draft.subID = t.randomLowerAndNum(16)
  1024. draft.comment = ""
  1025. draft.reset = 0
  1026. inbounds, err := t.getInboundsAddClient()
  1027. if err != nil {
  1028. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  1029. return
  1030. }
  1031. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.addClient"))
  1032. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
  1033. case "add_client_ch_default_email":
  1034. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1035. userStateMgr.set(chatId, "awaiting_email")
  1036. cancel_btn_markup := tu.InlineKeyboard(
  1037. tu.InlineKeyboardRow(
  1038. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
  1039. ),
  1040. )
  1041. prompt_message := t.I18nBot("tgbot.messages.email_prompt", "ClientEmail=="+html.EscapeString(draft.email))
  1042. t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup)
  1043. case "add_client_ch_default_comment":
  1044. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1045. userStateMgr.set(chatId, "awaiting_comment")
  1046. cancel_btn_markup := tu.InlineKeyboard(
  1047. tu.InlineKeyboardRow(
  1048. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
  1049. ),
  1050. )
  1051. prompt_message := t.I18nBot("tgbot.messages.comment_prompt", "ClientComment=="+html.EscapeString(draft.comment))
  1052. t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup)
  1053. case "add_client_ch_default_tg_id":
  1054. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1055. userStateMgr.set(chatId, "awaiting_tg_id")
  1056. cancel_btn_markup := tu.InlineKeyboard(
  1057. tu.InlineKeyboardRow(
  1058. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
  1059. ),
  1060. )
  1061. current := draft.tgID
  1062. if current == "" {
  1063. current = "—"
  1064. }
  1065. t.SendMsgToTgbot(chatId, fmt.Sprintf("Send the Telegram user id (numeric) to attach to this client, or send <code>-</code> / <code>none</code> to clear.\nCurrent: <code>%s</code>", html.EscapeString(current)), cancel_btn_markup)
  1066. case "add_client_ch_default_traffic":
  1067. inlineKeyboard := tu.InlineKeyboard(
  1068. tu.InlineKeyboardRow(
  1069. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_traffic_exp")),
  1070. ),
  1071. tu.InlineKeyboardRow(
  1072. tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 0")),
  1073. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("add_client_limit_traffic_in 0")),
  1074. ),
  1075. tu.InlineKeyboardRow(
  1076. tu.InlineKeyboardButton("1 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 1")),
  1077. tu.InlineKeyboardButton("5 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 5")),
  1078. tu.InlineKeyboardButton("10 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 10")),
  1079. ),
  1080. tu.InlineKeyboardRow(
  1081. tu.InlineKeyboardButton("20 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 20")),
  1082. tu.InlineKeyboardButton("30 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 30")),
  1083. tu.InlineKeyboardButton("40 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 40")),
  1084. ),
  1085. tu.InlineKeyboardRow(
  1086. tu.InlineKeyboardButton("50 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 50")),
  1087. tu.InlineKeyboardButton("60 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 60")),
  1088. tu.InlineKeyboardButton("80 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 80")),
  1089. ),
  1090. tu.InlineKeyboardRow(
  1091. tu.InlineKeyboardButton("100 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 100")),
  1092. tu.InlineKeyboardButton("150 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 150")),
  1093. tu.InlineKeyboardButton("200 GB").WithCallbackData(t.encodeQuery("add_client_limit_traffic_c 200")),
  1094. ),
  1095. )
  1096. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  1097. case "add_client_ch_default_exp":
  1098. inlineKeyboard := tu.InlineKeyboard(
  1099. tu.InlineKeyboardRow(
  1100. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_traffic_exp")),
  1101. ),
  1102. tu.InlineKeyboardRow(
  1103. tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 0")),
  1104. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("add_client_reset_exp_in 0")),
  1105. ),
  1106. tu.InlineKeyboardRow(
  1107. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 7 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 7")),
  1108. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 10 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 10")),
  1109. ),
  1110. tu.InlineKeyboardRow(
  1111. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 14 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 14")),
  1112. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 20 "+t.I18nBot("tgbot.days")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 20")),
  1113. ),
  1114. tu.InlineKeyboardRow(
  1115. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 1 "+t.I18nBot("tgbot.month")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 30")),
  1116. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 3 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 90")),
  1117. ),
  1118. tu.InlineKeyboardRow(
  1119. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 6 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 180")),
  1120. tu.InlineKeyboardButton(t.I18nBot("tgbot.add")+" 12 "+t.I18nBot("tgbot.months")).WithCallbackData(t.encodeQuery("add_client_reset_exp_c 365")),
  1121. ),
  1122. )
  1123. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  1124. case "add_client_ch_default_ip_limit":
  1125. inlineKeyboard := tu.InlineKeyboard(
  1126. tu.InlineKeyboardRow(
  1127. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_ip_limit")),
  1128. ),
  1129. tu.InlineKeyboardRow(
  1130. tu.InlineKeyboardButton(t.I18nBot("tgbot.unlimited")).WithCallbackData(t.encodeQuery("add_client_ip_limit_c 0")),
  1131. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.custom")).WithCallbackData(t.encodeQuery("add_client_ip_limit_in 0")),
  1132. ),
  1133. tu.InlineKeyboardRow(
  1134. tu.InlineKeyboardButton("1").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 1")),
  1135. tu.InlineKeyboardButton("2").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 2")),
  1136. ),
  1137. tu.InlineKeyboardRow(
  1138. tu.InlineKeyboardButton("3").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 3")),
  1139. tu.InlineKeyboardButton("4").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 4")),
  1140. ),
  1141. tu.InlineKeyboardRow(
  1142. tu.InlineKeyboardButton("5").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 5")),
  1143. tu.InlineKeyboardButton("6").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 6")),
  1144. tu.InlineKeyboardButton("7").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 7")),
  1145. ),
  1146. tu.InlineKeyboardRow(
  1147. tu.InlineKeyboardButton("8").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 8")),
  1148. tu.InlineKeyboardButton("9").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 9")),
  1149. tu.InlineKeyboardButton("10").WithCallbackData(t.encodeQuery("add_client_ip_limit_c 10")),
  1150. ),
  1151. )
  1152. t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
  1153. case "add_client_default_info":
  1154. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1155. t.SendMsgToTgbotDeleteAfter(chatId, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove())
  1156. userStateMgr.clear(chatId)
  1157. t.addClient(chatId, draft, t.BuildClientDraftMessage(draft))
  1158. case "add_client_cancel":
  1159. userStateMgr.clear(chatId)
  1160. addClientDrafts.reset(chatId)
  1161. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1162. t.SendMsgToTgbotDeleteAfter(chatId, t.I18nBot("tgbot.messages.cancel"), 3, tu.ReplyKeyboardRemove())
  1163. case "add_client_default_traffic_exp":
  1164. messageId := callbackQuery.Message.GetMessageID()
  1165. message_text := t.BuildClientDraftMessage(draft)
  1166. t.addClient(chatId, draft, message_text, messageId)
  1167. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+draft.email))
  1168. case "add_client_default_ip_limit":
  1169. messageId := callbackQuery.Message.GetMessageID()
  1170. message_text := t.BuildClientDraftMessage(draft)
  1171. t.addClient(chatId, draft, message_text, messageId)
  1172. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.canceled", "Email=="+draft.email))
  1173. case "add_client_attach_more":
  1174. picker, err := t.getInboundsAttachPicker(draft)
  1175. if err != nil {
  1176. t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
  1177. return
  1178. }
  1179. t.SendMsgToTgbot(chatId, "Pick inbound(s) to attach:", picker)
  1180. case "add_client_attach_done":
  1181. if draft.receiverInboundID == 0 && len(draft.receiverInboundIDs) > 0 {
  1182. draft.receiverInboundID = draft.receiverInboundIDs[0]
  1183. }
  1184. if draft.receiverInboundID == 0 {
  1185. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.getInboundsFailed"))
  1186. return
  1187. }
  1188. message_text := t.BuildClientDraftMessage(draft)
  1189. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1190. t.addClient(chatId, draft, message_text)
  1191. case "add_client_submit_disable":
  1192. draft.enable = false
  1193. _, err := t.SubmitAddClient(draft)
  1194. if err != nil {
  1195. errorMessage := fmt.Sprintf("%v", err)
  1196. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.error_add_client", "error=="+errorMessage), tu.ReplyKeyboardRemove())
  1197. } else {
  1198. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1199. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.successfulOperation"), tu.ReplyKeyboardRemove())
  1200. t.sendClientIndividualLinks(chatId, draft.email)
  1201. t.sendClientQRLinks(chatId, draft.email)
  1202. addClientDrafts.reset(chatId)
  1203. }
  1204. case "add_client_submit_enable":
  1205. draft.enable = true
  1206. _, err := t.SubmitAddClient(draft)
  1207. if err != nil {
  1208. errorMessage := fmt.Sprintf("%v", err)
  1209. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.error_add_client", "error=="+errorMessage), tu.ReplyKeyboardRemove())
  1210. } else {
  1211. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1212. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.successfulOperation"), tu.ReplyKeyboardRemove())
  1213. t.sendClientIndividualLinks(chatId, draft.email)
  1214. t.sendClientQRLinks(chatId, draft.email)
  1215. addClientDrafts.reset(chatId)
  1216. }
  1217. case "reset_all_traffics_cancel":
  1218. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1219. t.SendMsgToTgbotDeleteAfter(chatId, t.I18nBot("tgbot.messages.cancel"), 1, tu.ReplyKeyboardRemove())
  1220. case "reset_all_traffics":
  1221. inlineKeyboard := tu.InlineKeyboard(
  1222. tu.InlineKeyboardRow(
  1223. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancelReset")).WithCallbackData(t.encodeQuery("reset_all_traffics_cancel")),
  1224. ),
  1225. tu.InlineKeyboardRow(
  1226. tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.confirmResetTraffic")).WithCallbackData(t.encodeQuery("reset_all_traffics_c")),
  1227. ),
  1228. )
  1229. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.AreYouSure"), inlineKeyboard)
  1230. case "reset_all_traffics_c":
  1231. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1232. emails, err := t.inboundService.GetAllEmails()
  1233. if err != nil {
  1234. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove())
  1235. return
  1236. }
  1237. // One report per tap, not one message per client: a large panel would
  1238. // otherwise burst past Telegram's rate limit. SendMsgToTgbot pages it.
  1239. var report strings.Builder
  1240. for _, email := range emails {
  1241. if err := t.inboundService.ResetClientTrafficByEmail(email); err == nil {
  1242. report.WriteString(t.I18nBot("tgbot.messages.SuccessResetTraffic", "ClientEmail=="+email))
  1243. } else {
  1244. report.WriteString(t.I18nBot("tgbot.messages.FailedResetTraffic", "ClientEmail=="+email, "ErrorMessage=="+err.Error()))
  1245. }
  1246. report.WriteString("\r\n\r\n")
  1247. }
  1248. report.WriteString(t.I18nBot("tgbot.messages.FinishProcess"))
  1249. // Escaped whole: one stray "<" in a remark or email otherwise makes
  1250. // Telegram reject the page it landed on, losing ~15 clients at once.
  1251. t.SendMsgToTgbot(chatId, html.EscapeString(report.String()), tu.ReplyKeyboardRemove())
  1252. case "get_sorted_traffic_usage_report":
  1253. t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
  1254. emails, err := t.inboundService.GetAllEmails()
  1255. if err != nil {
  1256. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove())
  1257. return
  1258. }
  1259. validEmails, missingEmails, err := t.inboundService.FilterAndSortClientEmails(emails)
  1260. if err != nil {
  1261. t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove())
  1262. return
  1263. }
  1264. // Batched for the same reason as the reset report above: one message
  1265. // per client hits Telegram's rate limit on a large panel.
  1266. var report strings.Builder
  1267. for _, email := range validEmails {
  1268. traffic, err := t.inboundService.GetClientTrafficByEmail(email)
  1269. if err != nil {
  1270. logger.Warning(err)
  1271. report.WriteString(t.I18nBot("tgbot.wentWrong"))
  1272. report.WriteString("\r\n\r\n")
  1273. continue
  1274. }
  1275. if traffic == nil {
  1276. report.WriteString(t.I18nBot("tgbot.noResult"))
  1277. report.WriteString("\r\n\r\n")
  1278. continue
  1279. }
  1280. report.WriteString(t.clientInfoMsg(traffic, false, false, false, false, true, false))
  1281. report.WriteString("\r\n\r\n")
  1282. }
  1283. for _, email := range missingEmails {
  1284. fmt.Fprintf(&report, "📧 %s\r\n%s\r\n\r\n", email, t.I18nBot("tgbot.noResult"))
  1285. }
  1286. if report.Len() > 0 {
  1287. t.SendMsgToTgbot(chatId, html.EscapeString(report.String()), tu.ReplyKeyboardRemove())
  1288. }
  1289. default:
  1290. action, email, ok := splitClientLinkCallback(callbackQuery.Data)
  1291. if !ok {
  1292. // Nothing matched: an unknown button still has to be answered, or it
  1293. // keeps spinning until Telegram times the callback out.
  1294. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  1295. return
  1296. }
  1297. // The keyboard outlives the chat it was sent to, so the email in it
  1298. // cannot authorise itself: a non-admin only reaches their own clients.
  1299. if !isAdmin && !t.clientOwnedByTgUser(callbackQuery.From.ID, email) {
  1300. t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
  1301. return
  1302. }
  1303. switch action {
  1304. case "client_sub_links":
  1305. t.sendClientSubLinks(chatId, email)
  1306. case "client_individual_links":
  1307. t.sendClientIndividualLinks(chatId, email)
  1308. case "client_qr_links":
  1309. t.sendClientQRLinks(chatId, email)
  1310. }
  1311. }
  1312. }
  1313. // checkAdmin checks if the given Telegram ID is an admin.
  1314. func checkAdmin(tgId int64) bool {
  1315. return slices.Contains(adminSnapshot(), tgId)
  1316. }
  1317. // isClientSelfCallback reports whether a callback is per-user rather than
  1318. // admin-only; the caller still has to prove the client is its own.
  1319. func isClientSelfCallback(data string) bool {
  1320. switch data {
  1321. case "client_traffic", "client_commands", "client_sub_links",
  1322. "client_individual_links", "client_qr_links":
  1323. return true
  1324. }
  1325. _, _, ok := splitClientLinkCallback(data)
  1326. return ok
  1327. }
  1328. // splitClientLinkCallback splits "<action> <email>" for the per-client link
  1329. // callbacks; ok is false for every other data.
  1330. func splitClientLinkCallback(data string) (action, email string, ok bool) {
  1331. for _, candidate := range []string{"client_sub_links", "client_individual_links", "client_qr_links"} {
  1332. if rest, found := strings.CutPrefix(data, candidate+" "); found && rest != "" {
  1333. return candidate, rest, true
  1334. }
  1335. }
  1336. return "", "", false
  1337. }