tgbot_router.go 58 KB

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