tgbot_router.go 58 KB

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