fix(tgbot): send the admin traffic reports as one message (#6490)

Reset all traffics and the sorted usage report replied with one Telegram
message per client. A panel with a few hundred clients therefore fired a
burst of sendMessage calls that trips Telegram's per-chat rate limit and
throttles the bot for every user, not just the admin who tapped.

Both reports are now assembled into one string and handed to
SendMsgToTgbot, which already pages long messages.

Two details the batching would otherwise lose: the reset report still
answers (with the reply keyboard removed) when the panel has no clients,
and both reports are HTML-escaped as a whole, because a single stray "<" in
a remark or an email now costs the ~15-client page it lands on instead of
one client's message.
This commit is contained in:
BlindMaster24
2026-09-13 13:52:55 +03:00
committed by GitHub
parent 6a5b4fab6a
commit c7518c4038
2 changed files with 217 additions and 21 deletions
+28 -21
View File
@@ -1268,18 +1268,22 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
return
}
// One report per tap, not one message per client: a large panel would
// otherwise burst past Telegram's rate limit. SendMsgToTgbot pages it.
var report strings.Builder
for _, email := range emails {
err := t.inboundService.ResetClientTrafficByEmail(email)
if err == nil {
msg := t.I18nBot("tgbot.messages.SuccessResetTraffic", "ClientEmail=="+email)
t.SendMsgToTgbot(chatId, msg, tu.ReplyKeyboardRemove())
if err := t.inboundService.ResetClientTrafficByEmail(email); err == nil {
report.WriteString(t.I18nBot("tgbot.messages.SuccessResetTraffic", "ClientEmail=="+email))
} else {
msg := t.I18nBot("tgbot.messages.FailedResetTraffic", "ClientEmail=="+email, "ErrorMessage=="+err.Error())
t.SendMsgToTgbot(chatId, msg, tu.ReplyKeyboardRemove())
report.WriteString(t.I18nBot("tgbot.messages.FailedResetTraffic", "ClientEmail=="+email, "ErrorMessage=="+err.Error()))
}
report.WriteString("\r\n\r\n")
}
report.WriteString(t.I18nBot("tgbot.messages.FinishProcess"))
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.FinishProcess"), tu.ReplyKeyboardRemove())
// Escaped whole: one stray "<" in a remark or email otherwise makes
// Telegram reject the page it landed on, losing ~15 clients at once.
t.SendMsgToTgbot(chatId, html.EscapeString(report.String()), tu.ReplyKeyboardRemove())
case "get_sorted_traffic_usage_report":
t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
emails, err := t.inboundService.GetAllEmails()
@@ -1287,33 +1291,36 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove())
return
}
valid_emails, extra_emails, err := t.inboundService.FilterAndSortClientEmails(emails)
validEmails, missingEmails, err := t.inboundService.FilterAndSortClientEmails(emails)
if err != nil {
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.errorOperation"), tu.ReplyKeyboardRemove())
return
}
for _, valid_emails := range valid_emails {
traffic, err := t.inboundService.GetClientTrafficByEmail(valid_emails)
// Batched for the same reason as the reset report above: one message
// per client hits Telegram's rate limit on a large panel.
var report strings.Builder
for _, email := range validEmails {
traffic, err := t.inboundService.GetClientTrafficByEmail(email)
if err != nil {
logger.Warning(err)
msg := t.I18nBot("tgbot.wentWrong")
t.SendMsgToTgbot(chatId, msg)
report.WriteString(t.I18nBot("tgbot.wentWrong"))
report.WriteString("\r\n\r\n")
continue
}
if traffic == nil {
msg := t.I18nBot("tgbot.noResult")
t.SendMsgToTgbot(chatId, msg)
report.WriteString(t.I18nBot("tgbot.noResult"))
report.WriteString("\r\n\r\n")
continue
}
output := t.clientInfoMsg(traffic, false, false, false, false, true, false)
t.SendMsgToTgbot(chatId, output, tu.ReplyKeyboardRemove())
report.WriteString(t.clientInfoMsg(traffic, false, false, false, false, true, false))
report.WriteString("\r\n\r\n")
}
for _, extra_emails := range extra_emails {
msg := fmt.Sprintf("📧 %s\n%s", extra_emails, t.I18nBot("tgbot.noResult"))
t.SendMsgToTgbot(chatId, msg, tu.ReplyKeyboardRemove())
for _, email := range missingEmails {
fmt.Fprintf(&report, "📧 %s\r\n%s\r\n\r\n", email, t.I18nBot("tgbot.noResult"))
}
if report.Len() > 0 {
t.SendMsgToTgbot(chatId, html.EscapeString(report.String()), tu.ReplyKeyboardRemove())
}
default:
action, email, ok := splitClientLinkCallback(callbackQuery.Data)