Quellcode durchsuchen

[tgbot] Changed to telego

Masoud Hidden vor 1 Jahr
Ursprung
Commit
3d5f851fce
3 geänderte Dateien mit 228 neuen und 163 gelöschten Zeilen
  1. 7 1
      go.mod
  2. 12 2
      go.sum
  3. 209 160
      web/service/tgbot.go

+ 7 - 1
go.mod

@@ -7,8 +7,8 @@ require (
 	github.com/gin-contrib/sessions v0.0.4
 	github.com/gin-gonic/gin v1.9.0
 	github.com/go-cmd/cmd v1.4.1
-	github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
 	github.com/goccy/go-json v0.10.2
+	github.com/mymmrac/telego v0.24.0
 	github.com/nicksnyder/go-i18n/v2 v2.2.1
 	github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
 	github.com/pelletier/go-toml/v2 v2.0.7
@@ -24,8 +24,10 @@ require (
 
 require (
 	github.com/BurntSushi/toml v1.2.1 // indirect
+	github.com/andybalholm/brotli v1.0.5 // indirect
 	github.com/bytedance/sonic v1.8.8 // indirect
 	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+	github.com/fasthttp/router v1.4.19 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-ole/go-ole v1.2.6 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
@@ -38,6 +40,7 @@ require (
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.16.5 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
 	github.com/leodido/go-urn v1.2.4 // indirect
 	github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
@@ -47,11 +50,14 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pires/go-proxyproto v0.7.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
+	github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
 	github.com/shoenig/go-m1cpu v0.1.5 // indirect
 	github.com/tklauser/go-sysconf v0.3.11 // indirect
 	github.com/tklauser/numcpus v0.6.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.11 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	github.com/valyala/fasthttp v1.47.0 // indirect
 	github.com/yusufpapurcu/wmi v1.2.2 // indirect
 	golang.org/x/arch v0.3.0 // indirect
 	golang.org/x/crypto v0.8.0 // indirect

+ 12 - 2
go.sum

@@ -4,6 +4,7 @@ github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
 github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig=
 github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
 github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
+github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 github.com/antonlindstrom/pgstore v0.0.0-20200229204646-b08ebf1105e0/go.mod h1:2Ti6VUHVxpC0VSmTZzEvpzysnaGAfGBOoMIz5ykPyyw=
 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
 github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
@@ -18,6 +19,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
+github.com/fasthttp/router v1.4.19 h1:RLE539IU/S4kfb4MP56zgP0TIBU9kEg0ID9GpWO0vqk=
+github.com/fasthttp/router v1.4.19/go.mod h1:+Fh3YOd8x1+he6ZS+d2iUDBH9MGGZ1xQFUor0DE9rKE=
 github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
 github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk=
 github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
@@ -45,8 +48,6 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
 github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
 github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
-github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
 github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@@ -81,6 +82,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
 github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
+github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
 github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
@@ -105,6 +107,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mymmrac/telego v0.24.0 h1:0fd+v2/dToL6/DtsnWr+2saK7ZxIgLY+LI9kqJQbPEo=
+github.com/mymmrac/telego v0.24.0/go.mod h1:y557P/iMHSaOVDi5Nmy1gNelqrw+jaBMvP9guPaNJsQ=
 github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
 github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
 github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
@@ -133,6 +137,8 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
 github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk=
 github.com/sagernet/sing-shadowsocks v0.2.1 h1:FvdLQOqpvxHBJUcUe4fvgiYP2XLLwH5i1DtXQviVEPw=
 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
+github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
+github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
 github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
 github.com/shirou/gopsutil/v3 v3.23.4 h1:hZwmDxZs7Ewt75DV81r4pFMqbq+di2cbt9FsQBqLD2o=
 github.com/shirou/gopsutil/v3 v3.23.4/go.mod h1:ZcGxyfzAMRevhUR2+cfhXDH6gQdFYE/t8j1nsU4mPI8=
@@ -164,6 +170,10 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
 github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
 github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
+github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
 github.com/xtls/reality v0.0.0-20230331223127-176a94313eda h1:psRJD2RrZbnI0OWyHvXfgYCPqlRM5q5SPDcjDoDBWhE=
 github.com/xtls/xray-core v1.8.1 h1:iSTTqXj82ZdwC1ah+eV331X4JTcnrDz+WuKuB/EB3P4=
 github.com/xtls/xray-core v1.8.1/go.mod h1:AXxSso0MZwUE4NhRocCfHCg73BtJ+T2dSpQVo1Cg9VM=

+ 209 - 160
web/service/tgbot.go

@@ -13,10 +13,13 @@ import (
 	"x-ui/util/common"
 	"x-ui/xray"
 
-	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
+	"github.com/mymmrac/telego"
+	th "github.com/mymmrac/telego/telegohandler"
+	tu "github.com/mymmrac/telego/telegoutil"
 )
 
-var bot *tgbotapi.BotAPI
+var bot *telego.Bot
+var botHandler *th.BotHandler
 var adminIds []int64
 var isRunning bool
 
@@ -61,12 +64,11 @@ func (t *Tgbot) Start() error {
 		adminIds = append(adminIds, int64(id))
 	}
 
-	bot, err = tgbotapi.NewBotAPI(tgBottoken)
+	bot, err = telego.NewBot(tgBottoken)
 	if err != nil {
 		fmt.Println("Get tgbot's api error:", err)
 		return err
 	}
-	bot.Debug = false
 
 	// listen for TG bot income messages
 	if !isRunning {
@@ -83,38 +85,40 @@ func (t *Tgbot) IsRunnging() bool {
 }
 
 func (t *Tgbot) Stop() {
-	bot.StopReceivingUpdates()
+	botHandler.Stop()
+	bot.StopLongPolling()
 	logger.Info("Stop Telegram receiver ...")
 	isRunning = false
 	adminIds = nil
 }
 
 func (t *Tgbot) OnReceive() {
-	u := tgbotapi.NewUpdate(0)
-	u.Timeout = 10
-
-	updates := bot.GetUpdatesChan(u)
-
-	for update := range updates {
-		tgId := update.FromChat().ID
-		chatId := update.FromChat().ChatConfig().ChatID
-		isAdmin := checkAdmin(tgId)
-		if update.Message == nil {
-			if update.CallbackQuery != nil {
-				t.asnwerCallback(update.CallbackQuery, isAdmin)
-			}
-		} else {
-			if update.Message.IsCommand() {
-				t.answerCommand(update.Message, chatId, isAdmin)
-			}
-		}
+	params := telego.GetUpdatesParams{
+		Timeout: 10,
 	}
+
+	updates, _ := bot.UpdatesViaLongPolling(&params)
+
+	botHandler, _ = th.NewBotHandler(bot, updates)
+
+	botHandler.HandleMessage(func(bot *telego.Bot, message telego.Message) {
+		t.answerCommand(&message, message.Chat.ID, checkAdmin(message.From.ID))
+	}, th.AnyCommand())
+
+	botHandler.HandleCallbackQuery(func(bot *telego.Bot, query telego.CallbackQuery) {
+		t.asnwerCallback(&query, checkAdmin(query.From.ID))
+	}, th.AnyCallbackQueryWithMessage())
+
+	botHandler.Start()
 }
 
-func (t *Tgbot) answerCommand(message *tgbotapi.Message, chatId int64, isAdmin bool) {
+func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin bool) {
 	msg := ""
+
+	command, commandArgs := tu.ParseCommand(message.Text)
+
 	// Extract the command from the Message.
-	switch message.Command() {
+	switch command {
 	case "help":
 		msg = "This bot is providing you some specefic data from the server.\n\n Please choose:"
 	case "start":
@@ -127,18 +131,18 @@ func (t *Tgbot) answerCommand(message *tgbotapi.Message, chatId int64, isAdmin b
 	case "status":
 		msg = "bot is ok ✅"
 	case "usage":
-		if len(message.CommandArguments()) > 1 {
+		if len(commandArgs) > 0 {
 			if isAdmin {
-				t.searchClient(chatId, message.CommandArguments())
+				t.searchClient(chatId, commandArgs[0])
 			} else {
-				t.searchForClient(chatId, message.CommandArguments())
+				t.searchForClient(chatId, commandArgs[0])
 			}
 		} else {
 			msg = "❗Please provide a text for search!"
 		}
 	case "inbound":
-		if isAdmin {
-			t.searchInbound(chatId, message.CommandArguments())
+		if isAdmin && len(commandArgs) > 0 {
+			t.searchInbound(chatId, commandArgs[0])
 		} else {
 			msg = "❗ Unknown command"
 		}
@@ -148,7 +152,9 @@ func (t *Tgbot) answerCommand(message *tgbotapi.Message, chatId int64, isAdmin b
 	t.SendAnswer(chatId, msg, isAdmin)
 }
 
-func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bool) {
+func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
+
+	chatId := callbackQuery.Message.Chat.ID
 
 	if isAdmin {
 		dataArray := strings.Split(callbackQuery.Data, " ")
@@ -157,61 +163,61 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
 			switch dataArray[0] {
 			case "client_refresh":
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : Client Refreshed successfully.", email))
-				t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+				t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 			case "client_cancel":
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("❌ %s : Operation canceled.", email))
-				t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+				t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 			case "ips_refresh":
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : IPs Refreshed successfully.", email))
-				t.searchClientIps(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+				t.searchClientIps(chatId, email, callbackQuery.Message.MessageID)
 			case "ips_cancel":
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("❌ %s : Operation canceled.", email))
-				t.searchClientIps(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+				t.searchClientIps(chatId, email, callbackQuery.Message.MessageID)
 			case "reset_traffic":
-				var inlineKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("❌ Cancel Reset", "client_cancel "+email),
+				inlineKeyboard := tu.InlineKeyboard(
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("❌ Cancel Reset").WithCallbackData("client_cancel "+email),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("✅ Confirm Reset Traffic?", "reset_traffic_c "+email),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("✅ Confirm Reset Traffic?").WithCallbackData("reset_traffic_c "+email),
 					),
 				)
-				t.editMessageCallbackTgBot(callbackQuery.From.ID, callbackQuery.Message.MessageID, inlineKeyboard)
+				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
 			case "reset_traffic_c":
 				err := t.inboundService.ResetClientTrafficByEmail(email)
 				if err == nil {
 					t.xrayService.SetToNeedRestart()
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : Traffic reset successfully.", email))
-					t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+					t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 				} else {
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, "❗ Error in Operation.")
 				}
 			case "reset_exp":
-				var inlineKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("❌ Cancel Reset", "client_cancel "+email),
+				var inlineKeyboard = tu.InlineKeyboard(
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("❌ Cancel Reset").WithCallbackData("client_cancel "+email),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("♾ Unlimited", "reset_exp_c "+email+" 0"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("♾ Unlimited").WithCallbackData("reset_exp_c "+email+" 0"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("1 Month", "reset_exp_c "+email+" 30"),
-						tgbotapi.NewInlineKeyboardButtonData("2 Months", "reset_exp_c "+email+" 60"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("1 Month").WithCallbackData("reset_exp_c "+email+" 30"),
+						tu.InlineKeyboardButton("2 Months").WithCallbackData("reset_exp_c "+email+" 60"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("3 Months", "reset_exp_c "+email+" 90"),
-						tgbotapi.NewInlineKeyboardButtonData("6 Months", "reset_exp_c "+email+" 180"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("3 Months").WithCallbackData("reset_exp_c "+email+" 90"),
+						tu.InlineKeyboardButton("6 Months").WithCallbackData("reset_exp_c "+email+" 180"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("9 Months", "reset_exp_c "+email+" 270"),
-						tgbotapi.NewInlineKeyboardButtonData("12 Months", "reset_exp_c "+email+" 360"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("9 Months").WithCallbackData("reset_exp_c "+email+" 270"),
+						tu.InlineKeyboardButton("12 Months").WithCallbackData("reset_exp_c "+email+" 360"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("10 Days", "reset_exp_c "+email+" 10"),
-						tgbotapi.NewInlineKeyboardButtonData("20 Days", "reset_exp_c "+email+" 20"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("10 Days").WithCallbackData("reset_exp_c "+email+" 10"),
+						tu.InlineKeyboardButton("20 Days").WithCallbackData("reset_exp_c "+email+" 20"),
 					),
 				)
-				t.editMessageCallbackTgBot(callbackQuery.From.ID, callbackQuery.Message.MessageID, inlineKeyboard)
+				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
 			case "reset_exp_c":
 				if len(dataArray) == 3 {
 					days, err := strconv.Atoi(dataArray[2])
@@ -224,41 +230,41 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
 						if err == nil {
 							t.xrayService.SetToNeedRestart()
 							t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : Expire days reset successfully.", email))
-							t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+							t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 							return
 						}
 					}
 				}
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, "❗ Error in Operation.")
-				t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+				t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 			case "ip_limit":
-				var inlineKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("❌ Cancel IP Limit", "client_cancel "+email),
+				inlineKeyboard := tu.InlineKeyboard(
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("❌ Cancel IP Limit").WithCallbackData("client_cancel "+email),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("♾ Unlimited", "ip_limit_c "+email+" 0"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("♾ Unlimited").WithCallbackData("ip_limit_c "+email+" 0"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("1", "ip_limit_c "+email+" 1"),
-						tgbotapi.NewInlineKeyboardButtonData("2", "ip_limit_c "+email+" 2"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("1").WithCallbackData("ip_limit_c "+email+" 1"),
+						tu.InlineKeyboardButton("2").WithCallbackData("ip_limit_c "+email+" 2"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("3", "ip_limit_c "+email+" 3"),
-						tgbotapi.NewInlineKeyboardButtonData("4", "ip_limit_c "+email+" 4"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("3").WithCallbackData("ip_limit_c "+email+" 3"),
+						tu.InlineKeyboardButton("4").WithCallbackData("ip_limit_c "+email+" 4"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("5", "ip_limit_c "+email+" 5"),
-						tgbotapi.NewInlineKeyboardButtonData("6", "ip_limit_c "+email+" 6"),
-						tgbotapi.NewInlineKeyboardButtonData("7", "ip_limit_c "+email+" 7"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("5").WithCallbackData("ip_limit_c "+email+" 5"),
+						tu.InlineKeyboardButton("6").WithCallbackData("ip_limit_c "+email+" 6"),
+						tu.InlineKeyboardButton("7").WithCallbackData("ip_limit_c "+email+" 7"),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("8", "ip_limit_c "+email+" 8"),
-						tgbotapi.NewInlineKeyboardButtonData("9", "ip_limit_c "+email+" 9"),
-						tgbotapi.NewInlineKeyboardButtonData("10", "ip_limit_c "+email+" 10"),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("8").WithCallbackData("ip_limit_c "+email+" 8"),
+						tu.InlineKeyboardButton("9").WithCallbackData("ip_limit_c "+email+" 9"),
+						tu.InlineKeyboardButton("10").WithCallbackData("ip_limit_c "+email+" 10"),
 					),
 				)
-				t.editMessageCallbackTgBot(callbackQuery.From.ID, callbackQuery.Message.MessageID, inlineKeyboard)
+				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
 			case "ip_limit_c":
 				if len(dataArray) == 3 {
 					count, err := strconv.Atoi(dataArray[2])
@@ -267,34 +273,34 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
 						if err == nil {
 							t.xrayService.SetToNeedRestart()
 							t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : IP limit %d saved successfully.", email, count))
-							t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+							t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 							return
 						}
 					}
 				}
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, "❗ Error in Operation.")
-				t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+				t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 			case "clear_ips":
-				var inlineKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("❌ Cancel", "ips_cancel "+email),
+				inlineKeyboard := tu.InlineKeyboard(
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("❌ Cancel").WithCallbackData("ips_cancel "+email),
 					),
-					tgbotapi.NewInlineKeyboardRow(
-						tgbotapi.NewInlineKeyboardButtonData("✅ Confirm Clear IPs?", "clear_ips_c "+email),
+					tu.InlineKeyboardRow(
+						tu.InlineKeyboardButton("✅ Confirm Clear IPs?").WithCallbackData("clear_ips_c "+email),
 					),
 				)
-				t.editMessageCallbackTgBot(callbackQuery.From.ID, callbackQuery.Message.MessageID, inlineKeyboard)
+				t.editMessageCallbackTgBot(chatId, callbackQuery.Message.MessageID, inlineKeyboard)
 			case "clear_ips_c":
 				err := t.inboundService.ClearClientIps(email)
 				if err == nil {
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : IPs cleared successfully.", email))
-					t.searchClientIps(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+					t.searchClientIps(chatId, email, callbackQuery.Message.MessageID)
 				} else {
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, "❗ Error in Operation.")
 				}
 			case "ip_log":
 				t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : Get IP Log.", email))
-				t.searchClientIps(callbackQuery.From.ID, email)
+				t.searchClientIps(chatId, email)
 			case "toggle_enable":
 				enabled, err := t.inboundService.ToggleClientEnableByEmail(email)
 				if err == nil {
@@ -304,7 +310,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
 					} else {
 						t.sendCallbackAnswerTgBot(callbackQuery.ID, fmt.Sprintf("✅ %s : Disabled successfully.", email))
 					}
-					t.searchClient(callbackQuery.From.ID, email, callbackQuery.Message.MessageID)
+					t.searchClient(chatId, email, callbackQuery.Message.MessageID)
 				} else {
 					t.sendCallbackAnswerTgBot(callbackQuery.ID, "❗ Error in Operation.")
 				}
@@ -315,26 +321,23 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
 
 	// Respond to the callback query, telling Telegram to show the user
 	// a message with the data received.
-	callback := tgbotapi.NewCallback(callbackQuery.ID, callbackQuery.Data)
-	if _, err := bot.Request(callback); err != nil {
-		logger.Warning(err)
-	}
+	t.sendCallbackAnswerTgBot(callbackQuery.ID, callbackQuery.Data)
 
 	switch callbackQuery.Data {
 	case "get_usage":
-		t.SendMsgToTgbot(callbackQuery.From.ID, t.getServerUsage())
+		t.SendMsgToTgbot(chatId, t.getServerUsage())
 	case "inbounds":
-		t.SendMsgToTgbot(callbackQuery.From.ID, t.getInboundUsages())
+		t.SendMsgToTgbot(chatId, t.getInboundUsages())
 	case "deplete_soon":
-		t.SendMsgToTgbot(callbackQuery.From.ID, t.getExhausted())
+		t.SendMsgToTgbot(chatId, t.getExhausted())
 	case "get_backup":
-		t.sendBackup(callbackQuery.From.ID)
+		t.sendBackup(chatId)
 	case "client_traffic":
-		t.getClientUsage(callbackQuery.From.ID, callbackQuery.From.UserName, strconv.FormatInt(callbackQuery.From.ID, 10))
+		t.getClientUsage(chatId, callbackQuery.From.Username, strconv.FormatInt(callbackQuery.From.ID, 10))
 	case "client_commands":
-		t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Password]</code>\r\n \r\nUse UID for vmess/vless and Password for Trojan.")
+		t.SendMsgToTgbot(chatId, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Password]</code>\r\n \r\nUse UID for vmess/vless and Password for Trojan.")
 	case "commands":
-		t.SendMsgToTgbot(callbackQuery.From.ID, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
+		t.SendMsgToTgbot(chatId, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
 	}
 }
 
@@ -348,39 +351,42 @@ func checkAdmin(tgId int64) bool {
 }
 
 func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
-	var numericKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("Server Usage", "get_usage"),
-			tgbotapi.NewInlineKeyboardButtonData("Get DB Backup", "get_backup"),
+	numericKeyboard := tu.InlineKeyboard(
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("Server Usage").WithCallbackData("get_usage"),
+			tu.InlineKeyboardButton("Get DB Backup").WithCallbackData("get_backup"),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("Get Inbounds", "inbounds"),
-			tgbotapi.NewInlineKeyboardButtonData("Deplete soon", "deplete_soon"),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("Get Inbounds").WithCallbackData("inbounds"),
+			tu.InlineKeyboardButton("Deplete soon").WithCallbackData("deplete_soon"),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("Commands", "commands"),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("Commands").WithCallbackData("commands"),
 		),
 	)
-	var numericKeyboardClient = tgbotapi.NewInlineKeyboardMarkup(
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("Get Usage", "client_traffic"),
-			tgbotapi.NewInlineKeyboardButtonData("Commands", "client_commands"),
+	numericKeyboardClient := tu.InlineKeyboard(
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("Get Usage").WithCallbackData("client_traffic"),
+			tu.InlineKeyboardButton("Commands").WithCallbackData("client_commands"),
 		),
 	)
-	msgConfig := tgbotapi.NewMessage(chatId, msg)
-	msgConfig.ParseMode = "HTML"
+	params := telego.SendMessageParams{
+		ChatID:    tu.ID(chatId),
+		Text:      msg,
+		ParseMode: "HTML",
+	}
 	if isAdmin {
-		msgConfig.ReplyMarkup = numericKeyboard
+		params.ReplyMarkup = numericKeyboard
 	} else {
-		msgConfig.ReplyMarkup = numericKeyboardClient
+		params.ReplyMarkup = numericKeyboardClient
 	}
-	_, err := bot.Send(msgConfig)
+	_, err := bot.SendMessage(&params)
 	if err != nil {
 		logger.Warning("Error sending telegram message :", err)
 	}
 }
 
-func (t *Tgbot) SendMsgToTgbot(tgid int64, msg string, inlineKeyboard ...tgbotapi.InlineKeyboardMarkup) {
+func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, inlineKeyboard ...*telego.InlineKeyboardMarkup) {
 	if !isRunning {
 		return
 	}
@@ -402,12 +408,15 @@ func (t *Tgbot) SendMsgToTgbot(tgid int64, msg string, inlineKeyboard ...tgbotap
 		allMessages = append(allMessages, msg)
 	}
 	for _, message := range allMessages {
-		info := tgbotapi.NewMessage(tgid, message)
-		info.ParseMode = "HTML"
+		params := telego.SendMessageParams{
+			ChatID:    tu.ID(chatId),
+			Text:      message,
+			ParseMode: "HTML",
+		}
 		if len(inlineKeyboard) > 0 {
-			info.ReplyMarkup = inlineKeyboard[0]
+			params.ReplyMarkup = inlineKeyboard[0]
 		}
-		_, err := bot.Send(info)
+		_, err := bot.SendMessage(&params)
 		if err != nil {
 			logger.Warning("Error sending telegram message :", err)
 		}
@@ -589,12 +598,12 @@ func (t *Tgbot) searchClientIps(chatId int64, email string, messageID ...int) {
 		ips = "No IP Record"
 	}
 	output := fmt.Sprintf("📧 Email: %s\r\n🔢 IPs: \r\n%s\r\n", email, ips)
-	var inlineKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("🔄 Refresh", "ips_refresh "+email),
+	inlineKeyboard := tu.InlineKeyboard(
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("🔄 Refresh").WithCallbackData("ips_refresh "+email),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("❌ Clear IPs", "clear_ips "+email),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("❌ Clear IPs").WithCallbackData("clear_ips "+email),
 		),
 	)
 	if len(messageID) > 0 {
@@ -634,22 +643,22 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
 	output := fmt.Sprintf("💡 Active: %t\r\n📧 Email: %s\r\n🔼 Upload↑: %s\r\n🔽 Download↓: %s\r\n🔄 Total: %s / %s\r\n📅 Expire in: %s\r\n",
 		traffic.Enable, traffic.Email, common.FormatTraffic(traffic.Up), common.FormatTraffic(traffic.Down), common.FormatTraffic((traffic.Up + traffic.Down)),
 		total, expiryTime)
-	var inlineKeyboard = tgbotapi.NewInlineKeyboardMarkup(
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("🔄 Refresh", "client_refresh "+email),
+	inlineKeyboard := tu.InlineKeyboard(
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("🔄 Refresh").WithCallbackData("client_refresh "+email),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("📈 Reset Traffic", "reset_traffic "+email),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("📈 Reset Traffic").WithCallbackData("reset_traffic "+email),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("📅 Reset Expire Days", "reset_exp "+email),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("📅 Reset Expire Days").WithCallbackData("reset_exp "+email),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("🔢 IP Log", "ip_log "+email),
-			tgbotapi.NewInlineKeyboardButtonData("🔢 IP Limit", "ip_limit "+email),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("🔢 IP Log").WithCallbackData("ip_log "+email),
+			tu.InlineKeyboardButton("🔢 IP Limit").WithCallbackData("ip_limit "+email),
 		),
-		tgbotapi.NewInlineKeyboardRow(
-			tgbotapi.NewInlineKeyboardButtonData("🔘 Enable / Disable", "toggle_enable "+email),
+		tu.InlineKeyboardRow(
+			tu.InlineKeyboardButton("🔘 Enable / Disable").WithCallbackData("toggle_enable "+email),
 		),
 	)
 	if len(messageID) > 0 {
@@ -818,41 +827,81 @@ func (t *Tgbot) getExhausted() string {
 func (t *Tgbot) sendBackup(chatId int64) {
 	sendingTime := time.Now().Format("2006-01-02 15:04:05")
 	t.SendMsgToTgbot(chatId, "Backup time: "+sendingTime)
-	file := tgbotapi.FilePath(config.GetDBPath())
-	msg := tgbotapi.NewDocument(chatId, file)
-	_, err := bot.Send(msg)
+	file, err := os.Open(config.GetDBPath())
+	if err != nil {
+		logger.Warning("Error in opening db file for backup: ", err)
+	}
+	document := tu.Document(
+		tu.ID(chatId),
+		tu.File(file),
+	)
+	_, err = bot.SendDocument(document)
 	if err != nil {
 		logger.Warning("Error in uploading backup: ", err)
 	}
-	file = tgbotapi.FilePath(xray.GetConfigPath())
-	msg = tgbotapi.NewDocument(chatId, file)
-	_, err = bot.Send(msg)
+	file, err = os.Open(xray.GetConfigPath())
+	if err != nil {
+		logger.Warning("Error in opening config.json file for backup: ", err)
+	}
+	document = tu.Document(
+		tu.ID(chatId),
+		tu.File(file),
+	)
+	_, err = bot.SendDocument(document)
 	if err != nil {
 		logger.Warning("Error in uploading config.json: ", err)
 	}
 }
 
 func (t *Tgbot) sendCallbackAnswerTgBot(id string, message string) {
-	callback := tgbotapi.NewCallback(id, message)
-	if _, err := bot.Request(callback); err != nil {
+	params := telego.AnswerCallbackQueryParams{
+		CallbackQueryID: id,
+		Text:            message,
+	}
+	if err := bot.AnswerCallbackQuery(&params); err != nil {
 		logger.Warning(err)
 	}
 }
 
-func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, inlineKeyboard tgbotapi.InlineKeyboardMarkup) {
-	edit := tgbotapi.NewEditMessageReplyMarkup(chatId, messageID, inlineKeyboard)
-	if _, err := bot.Request(edit); err != nil {
+func (t *Tgbot) editMessageCallbackTgBot(chatId int64, messageID int, inlineKeyboard *telego.InlineKeyboardMarkup) {
+	params := telego.EditMessageReplyMarkupParams{
+		ChatID:      tu.ID(chatId),
+		MessageID:   messageID,
+		ReplyMarkup: inlineKeyboard,
+	}
+	if _, err := bot.EditMessageReplyMarkup(&params); err != nil {
 		logger.Warning(err)
 	}
 }
 
-func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlineKeyboard ...tgbotapi.InlineKeyboardMarkup) {
-	edit := tgbotapi.NewEditMessageText(chatId, messageID, text)
-	edit.ParseMode = "HTML"
+func (t *Tgbot) editMessageTgBot(chatId int64, messageID int, text string, inlineKeyboard ...*telego.InlineKeyboardMarkup) {
+	params := telego.EditMessageTextParams{
+		ChatID:    tu.ID(chatId),
+		MessageID: messageID,
+		Text:      text,
+		ParseMode: "HTML",
+	}
 	if len(inlineKeyboard) > 0 {
-		edit.ReplyMarkup = &inlineKeyboard[0]
+		params.ReplyMarkup = inlineKeyboard[0]
 	}
-	if _, err := bot.Request(edit); err != nil {
+	if _, err := bot.EditMessageText(&params); err != nil {
 		logger.Warning(err)
 	}
 }
+
+func fromChat(u *telego.Update) *telego.Chat {
+	switch {
+	case u.Message != nil:
+		return &u.Message.Chat
+	case u.EditedMessage != nil:
+		return &u.EditedMessage.Chat
+	case u.ChannelPost != nil:
+		return &u.ChannelPost.Chat
+	case u.EditedChannelPost != nil:
+		return &u.EditedChannelPost.Chat
+	case u.CallbackQuery != nil:
+		return &u.CallbackQuery.Message.Chat
+	default:
+		return nil
+	}
+}