mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 19:25:05 +03:00
15fee2a035
The bot answered every action with a new message, so a chat filled with stale menus whose buttons still acted, and long client or inbound lists were paged through dozens of buttons. Each chat now has one live screen that is edited in place: the menu is grouped into Server, Clients, Traffic and Maintenance, lists open through inline search, the add-client flow starts from a draft card, QR codes arrive as one labelled sheet, and repeated notices collapse into one with a counter. A client's /start and /usage show only that client's own links and usage, groups are refused, and the settings page warns when the bot's inline mode is off. Admin topics, the indexed Telegram lookup and every locale, Slovak included, carry over.
90 lines
3.1 KiB
Go
90 lines
3.1 KiB
Go
package tgbot
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/sub"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
|
)
|
|
|
|
// recordingBotServer answers sendMessage and records every text it was asked to send.
|
|
func recordingBotServer(t *testing.T) func() []string {
|
|
t.Helper()
|
|
var mu sync.Mutex
|
|
var texts []string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
method := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:]
|
|
switch method {
|
|
case "sendMessage", "sendPhoto":
|
|
// A screen is a photo message, so its caption travels as multipart
|
|
// form data; the raw body carries it either way.
|
|
raw, _ := io.ReadAll(r.Body)
|
|
mu.Lock()
|
|
texts = append(texts, string(raw))
|
|
mu.Unlock()
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "result": map[string]any{
|
|
"message_id": 1, "date": 0, "chat": map[string]any{"id": ownerTgID, "type": "private"},
|
|
}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
swapTestBot(t, srv.URL)
|
|
return func() []string {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
return append([]string(nil), texts...)
|
|
}
|
|
}
|
|
|
|
// With no sub or web domain set, the only host the bot knows is the machine
|
|
// name, which need not resolve; the links must not depend on reaching it.
|
|
func TestIndividualLinksDoNotNeedAResolvableHost(t *testing.T) {
|
|
sent := recordingBotServer(t)
|
|
dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
|
|
service.RegisterSubLinkProvider(sub.NewLinkProvider())
|
|
|
|
const uuid = "11111111-2222-4333-8444-555555555555"
|
|
db := database.GetDB()
|
|
ib := &model.Inbound{
|
|
UserId: 1, Tag: "in-443", Enable: true, Listen: "203.0.113.5", Port: 443,
|
|
Protocol: model.VLESS, Remark: "in",
|
|
Settings: `{"clients":[{"id":"` + uuid + `","email":"` + ownerMail + `","tgId":4242,"subId":"sub-owned","enable":true}],"decryption":"none"}`,
|
|
}
|
|
if err := db.Create(ib).Error; err != nil {
|
|
t.Fatalf("seed inbound: %v", err)
|
|
}
|
|
rec := &model.ClientRecord{Email: ownerMail, SubID: "sub-owned", UUID: uuid, TgID: ownerTgID, Enable: true}
|
|
if err := db.Create(rec).Error; err != nil {
|
|
t.Fatalf("seed client: %v", err)
|
|
}
|
|
if err := db.Create(&model.ClientInbound{ClientId: rec.Id, InboundId: ib.Id}).Error; err != nil {
|
|
t.Fatalf("seed client_inbound: %v", err)
|
|
}
|
|
if err := db.Create(&xray.ClientTraffic{InboundId: ib.Id, Email: ownerMail, Enable: true}).Error; err != nil {
|
|
t.Fatalf("seed traffic: %v", err)
|
|
}
|
|
|
|
origHost, origRunning := hostname, isRunning
|
|
t.Cleanup(func() { hostname, isRunning = origHost, origRunning })
|
|
hostname, isRunning = "unresolvable-panel-host.invalid", true
|
|
|
|
tapClientLinks(t, &Tgbot{}, ownerTgID, "client_individual_links "+ownerMail)
|
|
|
|
got := strings.Join(sent(), "\n")
|
|
if !strings.Contains(got, "vless://"+uuid+"@203.0.113.5:443") {
|
|
t.Fatalf("bot sent %q, want the client's vless link", got)
|
|
}
|
|
}
|