mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-18 12:05:53 +00:00
feat(tgbot): add Flow picker when creating a VLESS client
Some checks failed
CI / go-test (push) Has been cancelled
CI / govulncheck (push) Has been cancelled
CI / frontend (push) Has been cancelled
CodeQL Advanced / Analyze (go) (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Release 3X-UI / build (386) (push) Has been cancelled
Release 3X-UI / build (amd64) (push) Has been cancelled
Release 3X-UI / build (arm64) (push) Has been cancelled
Release 3X-UI / build (armv5) (push) Has been cancelled
Release 3X-UI / build (armv6) (push) Has been cancelled
Release 3X-UI / build (armv7) (push) Has been cancelled
Release 3X-UI / build (s390x) (push) Has been cancelled
Release 3X-UI / Build for Windows (push) Has been cancelled
Some checks failed
CI / go-test (push) Has been cancelled
CI / govulncheck (push) Has been cancelled
CI / frontend (push) Has been cancelled
CodeQL Advanced / Analyze (go) (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Release 3X-UI / build (386) (push) Has been cancelled
Release 3X-UI / build (amd64) (push) Has been cancelled
Release 3X-UI / build (arm64) (push) Has been cancelled
Release 3X-UI / build (armv5) (push) Has been cancelled
Release 3X-UI / build (armv6) (push) Has been cancelled
Release 3X-UI / build (armv7) (push) Has been cancelled
Release 3X-UI / build (s390x) (push) Has been cancelled
Release 3X-UI / Build for Windows (push) Has been cancelled
The bot's add-client flow already serialised client_Flow into the VLESS JSON template but never exposed a way to set it from Telegram, so every client ended up with an empty flow regardless of the inbound's transport. Added an inline "Flow" row to the VLESS protocol keyboard with three choices — None, xtls-rprx-vision, and xtls-rprx-vision-udp443 — and a matching i18n key in all 13 locale files. The row is only shown when the inbound can actually use Vision flow (mirrors the frontend's canEnableTlsFlow check: VLESS over TCP with TLS or Reality); on other transports it's hidden and any stale client_Flow value is reset, so the generated JSON stays consistent with the inbound's stream settings.
This commit is contained in:
parent
07cdb82027
commit
2928b52b04
14 changed files with 86 additions and 1 deletions
|
|
@ -1398,6 +1398,25 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.addClient(callbackQuery.Message.GetChat().ID, message_text, messageId)
|
||||||
|
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
|
||||||
|
case "add_client_set_flow":
|
||||||
|
if dataArray[1] == "none" {
|
||||||
|
client_Flow = ""
|
||||||
|
} else {
|
||||||
|
client_Flow = dataArray[1]
|
||||||
|
}
|
||||||
|
messageId := callbackQuery.Message.GetMessageID()
|
||||||
|
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
||||||
|
if err != nil {
|
||||||
|
t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message_text, err := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
|
||||||
|
if err != nil {
|
||||||
|
t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
t.addClient(callbackQuery.Message.GetChat().ID, message_text, messageId)
|
t.addClient(callbackQuery.Message.GetChat().ID, message_text, messageId)
|
||||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
|
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
|
||||||
case "add_client_ip_limit_in":
|
case "add_client_ip_limit_in":
|
||||||
|
|
@ -1865,6 +1884,22 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
|
t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
|
||||||
|
case "add_client_ch_default_flow":
|
||||||
|
inlineKeyboard := tu.InlineKeyboard(
|
||||||
|
tu.InlineKeyboardRow(
|
||||||
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData(t.encodeQuery("add_client_default_traffic_exp")),
|
||||||
|
),
|
||||||
|
tu.InlineKeyboardRow(
|
||||||
|
tu.InlineKeyboardButton("None").WithCallbackData(t.encodeQuery("add_client_set_flow none")),
|
||||||
|
),
|
||||||
|
tu.InlineKeyboardRow(
|
||||||
|
tu.InlineKeyboardButton("xtls-rprx-vision").WithCallbackData(t.encodeQuery("add_client_set_flow xtls-rprx-vision")),
|
||||||
|
),
|
||||||
|
tu.InlineKeyboardRow(
|
||||||
|
tu.InlineKeyboardButton("xtls-rprx-vision-udp443").WithCallbackData(t.encodeQuery("add_client_set_flow xtls-rprx-vision-udp443")),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
|
||||||
case "add_client_ch_default_ip_limit":
|
case "add_client_ch_default_ip_limit":
|
||||||
inlineKeyboard := tu.InlineKeyboard(
|
inlineKeyboard := tu.InlineKeyboard(
|
||||||
tu.InlineKeyboardRow(
|
tu.InlineKeyboardRow(
|
||||||
|
|
@ -3345,6 +3380,25 @@ func (t *Tgbot) getCommonClientButtons() [][]telego.InlineKeyboardButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inboundCanEnableTlsFlow mirrors Inbound.canEnableTlsFlow() from the frontend
|
||||||
|
// model: xtls-rprx-vision is only valid on VLESS-over-TCP with TLS or Reality.
|
||||||
|
func inboundCanEnableTlsFlow(ib *model.Inbound) bool {
|
||||||
|
if ib == nil || ib.Protocol != model.VLESS {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var stream struct {
|
||||||
|
Network string `json:"network"`
|
||||||
|
Security string `json:"security"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(ib.StreamSettings), &stream); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if stream.Network != "tcp" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return stream.Security == "tls" || stream.Security == "reality"
|
||||||
|
}
|
||||||
|
|
||||||
// addClient handles the process of adding a new client to an inbound.
|
// addClient handles the process of adding a new client to an inbound.
|
||||||
func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||||
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
||||||
|
|
@ -3357,13 +3411,31 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||||
|
|
||||||
var protocolRows [][]telego.InlineKeyboardButton
|
var protocolRows [][]telego.InlineKeyboardButton
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case model.VMESS, model.VLESS:
|
case model.VMESS:
|
||||||
protocolRows = [][]telego.InlineKeyboardButton{
|
protocolRows = [][]telego.InlineKeyboardButton{
|
||||||
tu.InlineKeyboardRow(
|
tu.InlineKeyboardRow(
|
||||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"),
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"),
|
||||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_id")).WithCallbackData("add_client_ch_default_id"),
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_id")).WithCallbackData("add_client_ch_default_id"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
case model.VLESS:
|
||||||
|
protocolRows = [][]telego.InlineKeyboardButton{
|
||||||
|
tu.InlineKeyboardRow(
|
||||||
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"),
|
||||||
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_id")).WithCallbackData("add_client_ch_default_id"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
if inboundCanEnableTlsFlow(inbound) {
|
||||||
|
flowLabel := t.I18nBot("tgbot.buttons.change_flow")
|
||||||
|
if client_Flow != "" {
|
||||||
|
flowLabel = flowLabel + ": " + client_Flow
|
||||||
|
}
|
||||||
|
protocolRows = append(protocolRows, tu.InlineKeyboardRow(
|
||||||
|
tu.InlineKeyboardButton(flowLabel).WithCallbackData("add_client_ch_default_flow"),
|
||||||
|
))
|
||||||
|
} else if client_Flow != "" {
|
||||||
|
client_Flow = ""
|
||||||
|
}
|
||||||
case model.Trojan:
|
case model.Trojan:
|
||||||
protocolRows = [][]telego.InlineKeyboardButton{
|
protocolRows = [][]telego.InlineKeyboardButton{
|
||||||
tu.InlineKeyboardRow(
|
tu.InlineKeyboardRow(
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 كلمة السر",
|
"change_password": "⚙️🔑 كلمة السر",
|
||||||
"change_email": "⚙️📧 البريد الإلكتروني",
|
"change_email": "⚙️📧 البريد الإلكتروني",
|
||||||
"change_comment": "⚙️💬 تعليق",
|
"change_comment": "⚙️💬 تعليق",
|
||||||
|
"change_flow": "⚙️🚦 التدفق",
|
||||||
"ResetAllTraffics": "إعادة ضبط جميع الترافيك",
|
"ResetAllTraffics": "إعادة ضبط جميع الترافيك",
|
||||||
"SortedTrafficUsageReport": "تقرير استخدام الترافيك المرتب"
|
"SortedTrafficUsageReport": "تقرير استخدام الترافيك المرتب"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Password",
|
"change_password": "⚙️🔑 Password",
|
||||||
"change_email": "⚙️📧 Email",
|
"change_email": "⚙️📧 Email",
|
||||||
"change_comment": "⚙️💬 Comment",
|
"change_comment": "⚙️💬 Comment",
|
||||||
|
"change_flow": "⚙️🚦 Flow",
|
||||||
"ResetAllTraffics": "Reset All Traffics",
|
"ResetAllTraffics": "Reset All Traffics",
|
||||||
"SortedTrafficUsageReport": "Sorted Traffic Usage Report"
|
"SortedTrafficUsageReport": "Sorted Traffic Usage Report"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Contraseña",
|
"change_password": "⚙️🔑 Contraseña",
|
||||||
"change_email": "⚙️📧 Correo electrónico",
|
"change_email": "⚙️📧 Correo electrónico",
|
||||||
"change_comment": "⚙️💬 Comentario",
|
"change_comment": "⚙️💬 Comentario",
|
||||||
|
"change_flow": "⚙️🚦 Flujo",
|
||||||
"ResetAllTraffics": "Reiniciar todo el tráfico",
|
"ResetAllTraffics": "Reiniciar todo el tráfico",
|
||||||
"SortedTrafficUsageReport": "Informe de uso de tráfico ordenado"
|
"SortedTrafficUsageReport": "Informe de uso de tráfico ordenado"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 گذرواژه",
|
"change_password": "⚙️🔑 گذرواژه",
|
||||||
"change_email": "⚙️📧 ایمیل",
|
"change_email": "⚙️📧 ایمیل",
|
||||||
"change_comment": "⚙️💬 نظر",
|
"change_comment": "⚙️💬 نظر",
|
||||||
|
"change_flow": "⚙️🚦 جریان",
|
||||||
"ResetAllTraffics": "بازنشانی همه ترافیکها",
|
"ResetAllTraffics": "بازنشانی همه ترافیکها",
|
||||||
"SortedTrafficUsageReport": "گزارش استفاده از ترافیک مرتبشده"
|
"SortedTrafficUsageReport": "گزارش استفاده از ترافیک مرتبشده"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Kata Sandi",
|
"change_password": "⚙️🔑 Kata Sandi",
|
||||||
"change_email": "⚙️📧 Email",
|
"change_email": "⚙️📧 Email",
|
||||||
"change_comment": "⚙️💬 Komentar",
|
"change_comment": "⚙️💬 Komentar",
|
||||||
|
"change_flow": "⚙️🚦 Flow",
|
||||||
"ResetAllTraffics": "Reset Semua Lalu Lintas",
|
"ResetAllTraffics": "Reset Semua Lalu Lintas",
|
||||||
"SortedTrafficUsageReport": "Laporan Penggunaan Lalu Lintas yang Terurut"
|
"SortedTrafficUsageReport": "Laporan Penggunaan Lalu Lintas yang Terurut"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 パスワード",
|
"change_password": "⚙️🔑 パスワード",
|
||||||
"change_email": "⚙️📧 メールアドレス",
|
"change_email": "⚙️📧 メールアドレス",
|
||||||
"change_comment": "⚙️💬 コメント",
|
"change_comment": "⚙️💬 コメント",
|
||||||
|
"change_flow": "⚙️🚦 フロー",
|
||||||
"ResetAllTraffics": "すべてのトラフィックをリセット",
|
"ResetAllTraffics": "すべてのトラフィックをリセット",
|
||||||
"SortedTrafficUsageReport": "ソートされたトラフィック使用レポート"
|
"SortedTrafficUsageReport": "ソートされたトラフィック使用レポート"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Senha",
|
"change_password": "⚙️🔑 Senha",
|
||||||
"change_email": "⚙️📧 E-mail",
|
"change_email": "⚙️📧 E-mail",
|
||||||
"change_comment": "⚙️💬 Comentário",
|
"change_comment": "⚙️💬 Comentário",
|
||||||
|
"change_flow": "⚙️🚦 Fluxo",
|
||||||
"ResetAllTraffics": "Redefinir Todo o Tráfego",
|
"ResetAllTraffics": "Redefinir Todo o Tráfego",
|
||||||
"SortedTrafficUsageReport": "Relatório de Uso de Tráfego Ordenado"
|
"SortedTrafficUsageReport": "Relatório de Uso de Tráfego Ordenado"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Пароль",
|
"change_password": "⚙️🔑 Пароль",
|
||||||
"change_email": "⚙️📧 Email",
|
"change_email": "⚙️📧 Email",
|
||||||
"change_comment": "⚙️💬 Комментарий",
|
"change_comment": "⚙️💬 Комментарий",
|
||||||
|
"change_flow": "⚙️🚦 Поток",
|
||||||
"ResetAllTraffics": "Сбросить весь трафик",
|
"ResetAllTraffics": "Сбросить весь трафик",
|
||||||
"SortedTrafficUsageReport": "Отсортированный отчет об использовании трафика"
|
"SortedTrafficUsageReport": "Отсортированный отчет об использовании трафика"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Şifre",
|
"change_password": "⚙️🔑 Şifre",
|
||||||
"change_email": "⚙️📧 E-posta",
|
"change_email": "⚙️📧 E-posta",
|
||||||
"change_comment": "⚙️💬 Yorum",
|
"change_comment": "⚙️💬 Yorum",
|
||||||
|
"change_flow": "⚙️🚦 Akış",
|
||||||
"ResetAllTraffics": "Tüm Trafikleri Sıfırla",
|
"ResetAllTraffics": "Tüm Trafikleri Sıfırla",
|
||||||
"SortedTrafficUsageReport": "Sıralı Trafik Kullanım Raporu"
|
"SortedTrafficUsageReport": "Sıralı Trafik Kullanım Raporu"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Пароль",
|
"change_password": "⚙️🔑 Пароль",
|
||||||
"change_email": "⚙️📧 Електронна пошта",
|
"change_email": "⚙️📧 Електронна пошта",
|
||||||
"change_comment": "⚙️💬 Коментар",
|
"change_comment": "⚙️💬 Коментар",
|
||||||
|
"change_flow": "⚙️🚦 Потік",
|
||||||
"ResetAllTraffics": "Скинути весь трафік",
|
"ResetAllTraffics": "Скинути весь трафік",
|
||||||
"SortedTrafficUsageReport": "Відсортований звіт про використання трафіку"
|
"SortedTrafficUsageReport": "Відсортований звіт про використання трафіку"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 Mật Khẩu",
|
"change_password": "⚙️🔑 Mật Khẩu",
|
||||||
"change_email": "⚙️📧 Email",
|
"change_email": "⚙️📧 Email",
|
||||||
"change_comment": "⚙️💬 Bình Luận",
|
"change_comment": "⚙️💬 Bình Luận",
|
||||||
|
"change_flow": "⚙️🚦 Flow",
|
||||||
"ResetAllTraffics": "Đặt lại tất cả lưu lượng",
|
"ResetAllTraffics": "Đặt lại tất cả lưu lượng",
|
||||||
"SortedTrafficUsageReport": "Báo cáo sử dụng lưu lượng đã sắp xếp"
|
"SortedTrafficUsageReport": "Báo cáo sử dụng lưu lượng đã sắp xếp"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 密码",
|
"change_password": "⚙️🔑 密码",
|
||||||
"change_email": "⚙️📧 邮箱",
|
"change_email": "⚙️📧 邮箱",
|
||||||
"change_comment": "⚙️💬 评论",
|
"change_comment": "⚙️💬 评论",
|
||||||
|
"change_flow": "⚙️🚦 流控",
|
||||||
"ResetAllTraffics": "重置所有流量",
|
"ResetAllTraffics": "重置所有流量",
|
||||||
"SortedTrafficUsageReport": "排序的流量使用报告"
|
"SortedTrafficUsageReport": "排序的流量使用报告"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -952,6 +952,7 @@
|
||||||
"change_password": "⚙️🔑 密碼",
|
"change_password": "⚙️🔑 密碼",
|
||||||
"change_email": "⚙️📧 電子郵件",
|
"change_email": "⚙️📧 電子郵件",
|
||||||
"change_comment": "⚙️💬 評論",
|
"change_comment": "⚙️💬 評論",
|
||||||
|
"change_flow": "⚙️🚦 流控",
|
||||||
"ResetAllTraffics": "重設所有流量",
|
"ResetAllTraffics": "重設所有流量",
|
||||||
"SortedTrafficUsageReport": "排序過的流量使用報告"
|
"SortedTrafficUsageReport": "排序過的流量使用報告"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue