Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 203 additions & 24 deletions web/service/tgbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,38 @@ func (t *Tgbot) OnReceive() {
message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
t.addClient(message.Chat.ID, message_text)
}
case "awaiting_subid":
newSubID := strings.TrimSpace(message.Text)

if client_SubID == newSubID {
t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove())
delete(userStates, message.Chat.ID)
inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID)
message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
t.addClient(message.Chat.ID, message_text)
return nil
}

isValidURI, _ := regexp.MatchString(`^[\p{L}\p{N}\-_]+$`, newSubID)

if !isValidURI {
userStates[message.Chat.ID] = "awaiting_subid"

cancel_btn_markup := tu.InlineKeyboard(
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
),
)

t.SendMsgToTgbot(message.Chat.ID, t.I18nBot("tgbot.messages.invalid_subid"), cancel_btn_markup)
} else {
client_SubID = newSubID
t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.received_subid"), 3, tu.ReplyKeyboardRemove())
delete(userStates, message.Chat.ID)
inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID)
message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
t.addClient(message.Chat.ID, message_text)
}
case "awaiting_password_tr":
if client_TrPassword == strings.TrimSpace(message.Text) {
t.SendMsgToTgbotDeleteAfter(message.Chat.ID, t.I18nBot("tgbot.messages.using_default_value"), 3, tu.ReplyKeyboardRemove())
Expand Down Expand Up @@ -1751,6 +1783,57 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
)
prompt_message := t.I18nBot("tgbot.messages.email_prompt", "ClientEmail=="+client_Email)
t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup)
case "add_client_ch_default_subid":
t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
userStates[chatId] = "awaiting_subid"
cancel_btn_markup := tu.InlineKeyboard(
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.use_default")).WithCallbackData("add_client_default_info"),
),
)
prompt_message := t.I18nBot("tgbot.messages.subid_prompt", "ClientSubId=="+client_SubID)
t.SendMsgToTgbot(chatId, prompt_message, cancel_btn_markup)

case "add_client_ch_default_flow":
inlineKeyboard := tu.InlineKeyboard(
tu.InlineKeyboardRow(
tu.InlineKeyboardButton("xtls-rprx-vision").WithCallbackData("set_flow_vision"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton("xtls-rprx-vision-udp443").WithCallbackData("set_flow_vision_udp443"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.flow_none")).WithCallbackData("set_flow_none"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_default_info"),
),
)
t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)

case "set_flow_vision":
client_Flow = "xtls-rprx-vision"
messageId := callbackQuery.Message.GetMessageID()
inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID)
message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
t.addClient(chatId, message_text, messageId)
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))

case "set_flow_vision_udp443":
client_Flow = "xtls-rprx-vision-udp443"
messageId := callbackQuery.Message.GetMessageID()
inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID)
message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
t.addClient(chatId, message_text, messageId)
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))

case "set_flow_none":
client_Flow = ""
messageId := callbackQuery.Message.GetMessageID()
inbound, _ := t.inboundService.GetInbound(receiver_inbound_ID)
message_text, _ := t.BuildInboundClientDataMessage(inbound.Remark, inbound.Protocol)
t.addClient(chatId, message_text, messageId)
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.successfulOperation"))
case "add_client_ch_default_id":
t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
userStates[chatId] = "awaiting_id"
Expand Down Expand Up @@ -2068,13 +2151,56 @@ func (t *Tgbot) BuildInboundClientDataMessage(inbound_remark string, protocol mo
case model.Shadowsocks:
message = t.I18nBot("tgbot.messages.inbound_client_data_pass", "InboundRemark=="+inbound_remark, "ClientPass=="+client_ShPassword, "ClientEmail=="+client_Email, "ClientTraffic=="+traffic_value, "ClientExp=="+expiryTime, "IpLimit=="+ip_limit, "ClientComment=="+client_Comment)

default:
default:
return "", errors.New("unknown protocol")
}

subidLabel := t.I18nBot("tgbot.messages.client_subid")
if subidLabel == "tgbot.messages.client_subid" || subidLabel == "" {
subidLabel = "Sub ID:"
}

flowLabel := t.I18nBot("tgbot.messages.client_flow")
if flowLabel == "tgbot.messages.client_flow" || flowLabel == "" {
flowLabel = "Flow:"
}

extraInfo := fmt.Sprintf("\n📝 %s %s", subidLabel, client_SubID)

if protocol == model.VLESS {
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
if err == nil {
var streamSettings map[string]interface{}
if err := json.Unmarshal([]byte(inbound.StreamSettings), &streamSettings); err == nil {
network, _ := streamSettings["network"].(string)
security, _ := streamSettings["security"].(string)

if network == "tcp" && (security == "tls" || security == "reality") {
if client_Flow != "" {
extraInfo += fmt.Sprintf("\n🌊 %s %s", flowLabel, client_Flow)
} else {
extraInfo += fmt.Sprintf("\n🌊 %s %s", flowLabel, t.I18nBot("tgbot.messages.not_specified"))
}
}
}
}
}

separator := "\n\n"
if strings.Contains(message, "\r\n\r\n") {
separator = "\r\n\r\n"
}

lastIdx := strings.LastIndex(message, separator)

if lastIdx != -1 {
message = message[:lastIdx] + extraInfo + separator + message[lastIdx+len(separator):]
} else {
message += extraInfo + "\n"
}

return message, nil
}

// BuildJSONForProtocol builds a JSON string for the given protocol with client data.
func (t *Tgbot) BuildJSONForProtocol(protocol model.Protocol) (string, error) {
var jsonString string
Expand Down Expand Up @@ -3312,13 +3438,32 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {

protocol := inbound.Protocol

canUseFlow := false
if protocol == model.VLESS {
var streamSettings map[string]interface{}
if err := json.Unmarshal([]byte(inbound.StreamSettings), &streamSettings); err == nil {
network, _ := streamSettings["network"].(string)
security, _ := streamSettings["security"].(string)

// Strict: only TCP and only with TLS or REALITY
if network == "tcp" && (security == "tls" || security == "reality") {
canUseFlow = true
}
}
}

var inlineKeyboard *telego.InlineKeyboardMarkup

switch protocol {
case model.VMESS, model.VLESS:
inlineKeyboard := tu.InlineKeyboard(
case model.VMESS:
inlineKeyboard = tu.InlineKeyboard(
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"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton("📝 Sub ID").WithCallbackData("add_client_ch_default_subid"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"),
Expand All @@ -3335,24 +3480,62 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
),
)
if len(messageID) > 0 {
t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard)

case model.VLESS:
rows := [][]telego.InlineKeyboardButton{
{
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 canUseFlow {
rows = append(rows, []telego.InlineKeyboardButton{
tu.InlineKeyboardButton("📝 Sub ID").WithCallbackData("add_client_ch_default_subid"),
tu.InlineKeyboardButton("🌊 Flow").WithCallbackData("add_client_ch_default_flow"),
})
} else {
t.SendMsgToTgbot(chatId, msg, inlineKeyboard)
rows = append(rows, []telego.InlineKeyboardButton{
tu.InlineKeyboardButton("📝 Sub ID").WithCallbackData("add_client_ch_default_subid"),
})
client_Flow = "" //
}

rows = append(rows,
[]telego.InlineKeyboardButton{
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"),
},
[]telego.InlineKeyboardButton{
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_comment")).WithCallbackData("add_client_ch_default_comment"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.ipLimit")).WithCallbackData("add_client_ch_default_ip_limit"),
},
[]telego.InlineKeyboardButton{
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"),
},
[]telego.InlineKeyboardButton{
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
},
)
inlineKeyboard = tu.InlineKeyboard(rows...)

case model.Trojan:
inlineKeyboard := tu.InlineKeyboard(
inlineKeyboard = tu.InlineKeyboard(
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_password")).WithCallbackData("add_client_ch_default_pass_tr"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton("📝 Sub ID").WithCallbackData("add_client_ch_default_subid"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_comment")).WithCallbackData("add_client_ch_default_comment"),
tu.InlineKeyboardButton("ip limit").WithCallbackData("add_client_ch_default_ip_limit"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.ipLimit")).WithCallbackData("add_client_ch_default_ip_limit"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"),
Expand All @@ -3362,24 +3545,23 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
),
)
if len(messageID) > 0 {
t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard)
} else {
t.SendMsgToTgbot(chatId, msg, inlineKeyboard)
}

case model.Shadowsocks:
inlineKeyboard := tu.InlineKeyboard(
inlineKeyboard = tu.InlineKeyboard(
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_email")).WithCallbackData("add_client_ch_default_email"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_password")).WithCallbackData("add_client_ch_default_pass_sh"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton("📝 Sub ID").WithCallbackData("add_client_ch_default_subid"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.limitTraffic")).WithCallbackData("add_client_ch_default_traffic"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.resetExpire")).WithCallbackData("add_client_ch_default_exp"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.change_comment")).WithCallbackData("add_client_ch_default_comment"),
tu.InlineKeyboardButton("ip limit").WithCallbackData("add_client_ch_default_ip_limit"),
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.ipLimit")).WithCallbackData("add_client_ch_default_ip_limit"),
),
tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"),
Expand All @@ -3389,14 +3571,13 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
),
)

if len(messageID) > 0 {
t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard)
} else {
t.SendMsgToTgbot(chatId, msg, inlineKeyboard)
}
}

if len(messageID) > 0 {
t.editMessageTgBot(chatId, messageID[0], msg, inlineKeyboard)
} else {
t.SendMsgToTgbot(chatId, msg, inlineKeyboard)
}
}

// searchInbound searches for inbounds by remark and sends the results.
Expand Down Expand Up @@ -3814,10 +3995,8 @@ func (t *Tgbot) SendMsgToTgbotDeleteAfter(chatId int64, msg string, delayInSecon
go func() {
time.Sleep(time.Duration(delayInSeconds) * time.Second) // Wait for the specified delay
t.deleteMessageTgBot(chatId, sentMsg.MessageID) // Delete the message
delete(userStates, chatId)
}()
}

// deleteMessageTgBot deletes a message from the chat.
func (t *Tgbot) deleteMessageTgBot(chatId int64, messageID int) {
params := telego.DeleteMessageParams{
Expand Down
9 changes: 9 additions & 0 deletions web/translation/translate.ar_EG.toml
Original file line number Diff line number Diff line change
Expand Up @@ -709,17 +709,23 @@
"received_password" = "🔑📥 الباسورد اتحدث."
"received_email" = "📧📥 الإيميل اتحدث."
"received_comment" = "💬📥 التعليق اتحدث."
"received_subid" = "تم تغيير الـ Sub ID بنجاح!"
"id_prompt" = "🔑 الـ ID الافتراضي: {{ .ClientId }}\n\nادخل الـ ID بتاعك."
"pass_prompt" = "🔑 الباسورد الافتراضي: {{ .ClientPassword }}\n\nادخل الباسورد بتاعك."
"email_prompt" = "📧 الإيميل الافتراضي: {{ .ClientEmail }}\n\nادخل الإيميل بتاعك."
"comment_prompt" = "💬 التعليق الافتراضي: {{ .ClientComment }}\n\nادخل تعليقك."
"subid_prompt" = "من فضلك أدخل Sub ID جديد:\nالحالي: {{.ClientSubId}}"
"inbound_client_data_id" = "🔄 الدخول: {{ .InboundRemark }}\n\n🔑 المعرف: {{ .ClientId }}\n📧 البريد الإلكتروني: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n🌐 حدّ IP: {{ .IpLimit }}\n💬 تعليق: {{ .ClientComment }}\n\nدلوقتي تقدر تضيف العميل على الدخول!"
"inbound_client_data_pass" = "🔄 الدخول: {{ .InboundRemark }}\n\n🔑 كلمة المرور: {{ .ClientPass }}\n📧 البريد الإلكتروني: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n🌐 حدّ IP: {{ .IpLimit }}\n💬 تعليق: {{ .ClientComment }}\n\nدلوقتي تقدر تضيف العميل على الدخول!"
"cancel" = "❌ العملية اتلغت! \n\nممكن تبدأ من /start في أي وقت. 🔄"
"error_add_client" = "⚠️ حصل خطأ:\n\n {{ .error }}"
"using_default_value" = "تمام، هشيل على القيمة الافتراضية. 😊"
"incorrect_input" = "المدخلات مش صحيحة.\nالكلمات لازم تكون متصلة من غير فراغات.\nمثال صحيح: aaaaaa\nمثال غلط: aaa aaa 🚫"
"invalid_subid" = "تنسيق غير صالح.\nيمكن أن يحتوي Sub ID فقط على أحرف وأرقام وشرطات (-) وشرطات سفلية (_).\nالمسافات والأحرف الخاصة غير مسموح بها. 🚫"
"client_subid" = "معرف الاشتراك (Sub ID):"
"client_flow" = "التوجيه (Flow):"
"AreYouSure" = "إنت متأكد؟ 🤔"
"not_specified" = "غير محدد"
"SuccessResetTraffic" = "📧 البريد الإلكتروني: {{ .ClientEmail }}\n🏁 النتيجة: ✅ تم بنجاح"
"FailedResetTraffic" = "📧 البريد الإلكتروني: {{ .ClientEmail }}\n🏁 النتيجة: ❌ فشل \n\n🛠️ الخطأ: [ {{ .ErrorMessage }} ]"
"FinishProcess" = "🔚 عملية إعادة ضبط الترافيك خلصت لكل العملاء."
Expand Down Expand Up @@ -765,6 +771,9 @@
"change_password" = "⚙️🔑 كلمة السر"
"change_email" = "⚙️📧 البريد الإلكتروني"
"change_comment" = "⚙️💬 تعليق"
"change_subid" = "📝 Sub ID"
"change_flow" = "🌊 Flow"
"flow_none" = "مفيش"
"ResetAllTraffics" = "إعادة ضبط جميع الترافيك"
"SortedTrafficUsageReport" = "تقرير استخدام الترافيك المرتب"

Expand Down
9 changes: 9 additions & 0 deletions web/translation/translate.en_US.toml
Original file line number Diff line number Diff line change
Expand Up @@ -709,17 +709,23 @@
"received_password" = "🔑📥 Password updated."
"received_email" = "📧📥 Email updated."
"received_comment" = "💬📥 Comment updated."
"received_subid" = "Sub ID updated successfully!"
"client_subid" = "Sub ID:"
"client_flow" = "Flow:"
"id_prompt" = "🔑 Default ID: {{ .ClientId }}\n\nEnter your id."
"pass_prompt" = "🔑 Default Password: {{ .ClientPassword }}\n\nEnter your password."
"email_prompt" = "📧 Default Email: {{ .ClientEmail }}\n\nEnter your email."
"subid_prompt" = "Please enter new Sub ID:\nCurrent: {{.ClientSubId}}"
"comment_prompt" = "💬 Default Comment: {{ .ClientComment }}\n\nEnter your Comment."
"inbound_client_data_id" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
"inbound_client_data_pass" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
"cancel" = "❌ Process Canceled! \n\nYou can /start again anytime. 🔄"
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
"using_default_value" = "Okay, I'll stick with the default value. 😊"
"incorrect_input" = "Your input is not valid.\nThe phrases should be continuous without spaces.\nCorrect example: aaaaaa\nIncorrect example: aaa aaa 🚫"
"invalid_subid" = "Invalid format.\nSub ID can only contain letters, numbers, hyphens (-), and underscores (_).\nSpaces and special characters are not allowed. 🚫"
"AreYouSure" = "Are you sure? 🤔"
"not_specified" = "Not specified"
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ✅ Success"
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ❌ Failed \n\n🛠️ Error: [ {{ .ErrorMessage }} ]"
"FinishProcess" = "🔚 Traffic reset process finished for all clients."
Expand Down Expand Up @@ -765,6 +771,9 @@
"change_password" = "⚙️🔑 Password"
"change_email" = "⚙️📧 Email"
"change_comment" = "⚙️💬 Comment"
"change_subid" = "📝 Sub ID"
"change_flow" = "🌊 Flow"
"flow_none" = "None"
"ResetAllTraffics" = "Reset All Traffics"
"SortedTrafficUsageReport" = "Sorted Traffic Usage Report"

Expand Down
Loading
Loading