tgbot_router.go 59 KB

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