mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-24 23:05:01 +03:00
7d45456feb
Every bot usage command and the hourly report resolved a Telegram ID by expanding every inbound's settings.clients, re-parsing the matched inbounds and looking up each client again by email. With thousands of clients this ran thousands of times per report and showed up as high PostgreSQL CPU, and an account whose lookup came back empty was retried once per client carrying it. The lookup now uses the normalized clients table and its Telegram-ID index in one query per account, and the hourly pass checks each account once. Bot output and thresholds are unchanged; link callbacks now authorize against the current client records instead of possibly stale settings JSON.
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
package tgbot
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/mymmrac/telego"
|
|
)
|
|
|
|
func withAdmins(t *testing.T, ids ...int64) {
|
|
t.Helper()
|
|
tgBotMutex.Lock()
|
|
orig := adminIds
|
|
adminIds = ids
|
|
tgBotMutex.Unlock()
|
|
t.Cleanup(func() {
|
|
tgBotMutex.Lock()
|
|
adminIds = orig
|
|
tgBotMutex.Unlock()
|
|
})
|
|
}
|
|
|
|
// newLevelTgbot binds ownerMail to ownerTgID in both the inbound settings and
|
|
// the clients table, and makes account 1 the only admin.
|
|
func newLevelTgbot(t *testing.T) (*Tgbot, func(string) int) {
|
|
t.Helper()
|
|
tb, calls := newLinksCallbackTgbot(t, ownerMail)
|
|
withAdmins(t, 1)
|
|
return tb, calls
|
|
}
|
|
|
|
func commandFrom(tgUserID int64, text string) *telego.Message {
|
|
return &telego.Message{
|
|
From: &telego.User{ID: tgUserID},
|
|
Chat: telego.Chat{ID: tgUserID, Type: telego.ChatTypePrivate},
|
|
Text: text,
|
|
}
|
|
}
|
|
|
|
func TestLevelOfFollowsTheClientBinding(t *testing.T) {
|
|
tb, _ := newLevelTgbot(t)
|
|
|
|
for id, want := range map[int64]userLevel{1: levelAdmin, ownerTgID: levelClient, 777: levelStranger, 0: levelStranger} {
|
|
if got := tb.levelOf(id); got != want {
|
|
t.Errorf("levelOf(%d) = %d, want %d", id, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A stranger's refused command must get no reply at all, while a bound client
|
|
// is still told the command is unknown, as before the gate existed.
|
|
func TestGateCommand(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
from int64
|
|
text string
|
|
wantOK bool
|
|
wantAdmin bool
|
|
wantSends int
|
|
}{
|
|
{"stranger start", 777, "/start", true, false, 0},
|
|
{"stranger help", 777, "/help", false, false, 0},
|
|
{"stranger admin command", 777, "/restart", false, false, 0},
|
|
{"client usage", ownerTgID, "/usage", true, false, 0},
|
|
{"client admin command", ownerTgID, "/clearall", false, false, 1},
|
|
{"admin", 1, "/clearall", true, true, 0},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
tb, calls := newLevelTgbot(t)
|
|
|
|
isAdmin, ok := tb.gateCommand(commandFrom(c.from, c.text))
|
|
if ok != c.wantOK || isAdmin != c.wantAdmin {
|
|
t.Errorf("gateCommand = (%v, %v), want (%v, %v)", isAdmin, ok, c.wantAdmin, c.wantOK)
|
|
}
|
|
if n := calls("sendMessage"); n != c.wantSends {
|
|
t.Errorf("sendMessage calls = %d, want %d", n, c.wantSends)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Regression test: a stranger's forged callback must be answered, so the button
|
|
// stops spinning, and must never reach answerCallback.
|
|
func TestGateCallbackStopsStrangers(t *testing.T) {
|
|
tb, calls := newLevelTgbot(t)
|
|
|
|
query := &telego.CallbackQuery{ID: "q1", From: telego.User{ID: 777}, Data: "client_sub_links " + ownerMail}
|
|
if _, ok := tb.gateCallback(query); ok {
|
|
t.Fatal("gateCallback admitted a stranger")
|
|
}
|
|
if n := calls("answerCallbackQuery"); n != 1 {
|
|
t.Errorf("answerCallbackQuery calls = %d, want 1", n)
|
|
}
|
|
|
|
query.From.ID = ownerTgID
|
|
if isAdmin, ok := tb.gateCallback(query); !ok || isAdmin {
|
|
t.Errorf("gateCallback(client) = (%v, %v), want (false, true)", isAdmin, ok)
|
|
}
|
|
}
|