| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- package tgbot
- import (
- "errors"
- "fmt"
- "strconv"
- "strings"
- "time"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- "github.com/mhsanaei/3x-ui/v3/internal/logger"
- "github.com/mhsanaei/3x-ui/v3/internal/util/common"
- "github.com/mymmrac/telego"
- tu "github.com/mymmrac/telego/telegoutil"
- )
- // getInboundUsages retrieves and formats inbound usage information.
- func (t *Tgbot) getInboundUsages() string {
- var info strings.Builder
- inbounds, err := t.inboundService.GetAllInbounds()
- if err != nil {
- logger.Warning("GetAllInbounds run failed:", err)
- info.WriteString(t.I18nBot("tgbot.answers.getInboundsFailed"))
- return info.String()
- }
- for _, inbound := range inbounds {
- info.WriteString(t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark))
- info.WriteString(t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port)))
- info.WriteString(t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down)))
- clients, listErr := t.clientService.ListForInbound(nil, inbound.Id)
- if listErr == nil {
- info.WriteString(fmt.Sprintf("👥 Clients: %d\r\n", len(clients)))
- }
- if inbound.ExpiryTime == 0 {
- info.WriteString(t.I18nBot("tgbot.messages.expire", "Time=="+t.I18nBot("tgbot.unlimited")))
- } else {
- info.WriteString(t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05")))
- }
- info.WriteString("\r\n")
- }
- return info.String()
- }
- // getInbounds creates an inline keyboard with all inbounds.
- func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) {
- inbounds, err := t.inboundService.GetAllInbounds()
- if err != nil {
- logger.Warning("GetAllInbounds run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- if len(inbounds) == 0 {
- logger.Warning("No inbounds found")
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- var buttons []telego.InlineKeyboardButton
- for _, inbound := range inbounds {
- status := "❌"
- if inbound.Enable {
- status = "✅"
- }
- callbackData := t.encodeQuery(fmt.Sprintf("%s %d", "get_clients", inbound.Id))
- buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(callbackData))
- }
- cols := 1
- if len(buttons) >= 6 {
- cols = 2
- }
- keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
- return keyboard, nil
- }
- // getInboundsFor builds an inline keyboard of inbounds for a custom next action.
- func (t *Tgbot) getInboundsFor(nextAction string) (*telego.InlineKeyboardMarkup, error) {
- inbounds, err := t.inboundService.GetAllInbounds()
- if err != nil {
- logger.Warning("GetAllInbounds run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- if len(inbounds) == 0 {
- logger.Warning("No inbounds found")
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- var buttons []telego.InlineKeyboardButton
- for _, inbound := range inbounds {
- status := "❌"
- if inbound.Enable {
- status = "✅"
- }
- callbackData := t.encodeQuery(fmt.Sprintf("%s %d", nextAction, inbound.Id))
- buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(callbackData))
- }
- cols := 1
- if len(buttons) >= 6 {
- cols = 2
- }
- keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
- return keyboard, nil
- }
- // getInboundClientsFor lists clients of an inbound with a specific action prefix to be appended with email
- func (t *Tgbot) getInboundClientsFor(inboundID int, action string) (*telego.InlineKeyboardMarkup, error) {
- inbound, err := t.inboundService.GetInbound(inboundID)
- if err != nil {
- logger.Warning("getInboundClientsFor run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- clients, err := t.inboundService.GetClients(inbound)
- var buttons []telego.InlineKeyboardButton
- if err != nil {
- logger.Warning("GetInboundClients run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- } else {
- if len(clients) > 0 {
- for _, client := range clients {
- buttons = append(buttons, tu.InlineKeyboardButton(client.Email).WithCallbackData(t.encodeQuery(action+" "+client.Email)))
- }
- } else {
- return nil, errors.New(t.I18nBot("tgbot.answers.getClientsFailed"))
- }
- }
- cols := 0
- if len(buttons) < 6 {
- cols = 3
- } else {
- cols = 2
- }
- keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
- return keyboard, nil
- }
- // getInboundsAddClient creates an inline keyboard for adding clients to inbounds.
- func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) {
- inbounds, err := t.inboundService.GetAllInbounds()
- if err != nil {
- logger.Warning("GetAllInbounds run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- if len(inbounds) == 0 {
- logger.Warning("No inbounds found")
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- excludedProtocols := map[model.Protocol]bool{
- model.Tunnel: true,
- model.Mixed: true,
- model.WireGuard: true,
- model.HTTP: true,
- }
- var buttons []telego.InlineKeyboardButton
- for _, inbound := range inbounds {
- if excludedProtocols[inbound.Protocol] {
- continue
- }
- status := "❌"
- if inbound.Enable {
- status = "✅"
- }
- callbackData := t.encodeQuery(fmt.Sprintf("%s %d", "add_client_to", inbound.Id))
- buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(callbackData))
- }
- cols := 1
- if len(buttons) >= 6 {
- cols = 2
- }
- keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
- return keyboard, nil
- }
- // getInboundsAttachPicker builds a toggle picker over multi-client inbounds
- // for the "attach more inbounds to the new client" step. Each row shows the
- // current selection state for the inbound; tapping fires
- // add_client_toggle_attach <id> which flips it and re-renders. A final
- // "Done" button (add_client_attach_done) returns to the field-edit screen.
- func (t *Tgbot) getInboundsAttachPicker() (*telego.InlineKeyboardMarkup, error) {
- inbounds, err := t.inboundService.GetAllInbounds()
- if err != nil {
- logger.Warning("GetAllInbounds run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- if len(inbounds) == 0 {
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- excludedProtocols := map[model.Protocol]bool{
- model.Tunnel: true,
- model.Mixed: true,
- model.WireGuard: true,
- model.HTTP: true,
- }
- selected := make(map[int]bool, len(receiver_inbound_IDs))
- for _, id := range receiver_inbound_IDs {
- selected[id] = true
- }
- var buttons []telego.InlineKeyboardButton
- for _, ib := range inbounds {
- if excludedProtocols[ib.Protocol] {
- continue
- }
- mark := "☐"
- if selected[ib.Id] {
- mark = "✅"
- }
- label := fmt.Sprintf("%s %s (%s)", mark, ib.Remark, ib.Protocol)
- callback := t.encodeQuery(fmt.Sprintf("add_client_toggle_attach %d", ib.Id))
- buttons = append(buttons, tu.InlineKeyboardButton(label).WithCallbackData(callback))
- }
- cols := 1
- if len(buttons) >= 6 {
- cols = 2
- }
- rows := tu.InlineKeyboardCols(cols, buttons...)
- rows = append(rows, tu.InlineKeyboardRow(
- tu.InlineKeyboardButton("✅ Done").WithCallbackData(t.encodeQuery("add_client_attach_done")),
- ))
- return tu.InlineKeyboardGrid(rows), nil
- }
- // getInboundClients creates an inline keyboard with clients of a specific inbound.
- func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error) {
- inbound, err := t.inboundService.GetInbound(id)
- if err != nil {
- logger.Warning("getIboundClients run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- }
- clients, err := t.inboundService.GetClients(inbound)
- var buttons []telego.InlineKeyboardButton
- if err != nil {
- logger.Warning("GetInboundClients run failed:", err)
- return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
- } else {
- if len(clients) > 0 {
- for _, client := range clients {
- buttons = append(buttons, tu.InlineKeyboardButton(client.Email).WithCallbackData(t.encodeQuery("client_get_usage "+client.Email)))
- }
- } else {
- return nil, errors.New(t.I18nBot("tgbot.answers.getClientsFailed"))
- }
- }
- cols := 0
- if len(buttons) < 6 {
- cols = 3
- } else {
- cols = 2
- }
- keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
- return keyboard, nil
- }
- // searchInbound searches for inbounds by remark and sends the results.
- func (t *Tgbot) searchInbound(chatId int64, remark string) {
- inbounds, err := t.inboundService.SearchInbounds(remark)
- if err != nil {
- logger.Warning(err)
- msg := t.I18nBot("tgbot.wentWrong")
- t.SendMsgToTgbot(chatId, msg)
- return
- }
- if len(inbounds) == 0 {
- msg := t.I18nBot("tgbot.noInbounds")
- t.SendMsgToTgbot(chatId, msg)
- return
- }
- for _, inbound := range inbounds {
- info := ""
- info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
- info += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))
- info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down))
- if inbound.ExpiryTime == 0 {
- info += t.I18nBot("tgbot.messages.expire", "Time=="+t.I18nBot("tgbot.unlimited"))
- } else {
- info += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
- }
- t.SendMsgToTgbot(chatId, info)
- if len(inbound.ClientStats) > 0 {
- var output strings.Builder
- for _, traffic := range inbound.ClientStats {
- output.WriteString(t.clientInfoMsg(&traffic, true, true, true, true, true, true))
- }
- t.SendMsgToTgbot(chatId, output.String())
- }
- }
- }
|