mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 21:12:49 +08:00
Add dual TS3/TS6 protocol support with auto-detection
The @honeybbq/teamspeak-client library already handles TS6 license block type 8 (Ts5Server) in its handshake, so voice connections work with both TS3 and TS6 servers. This commit adds the surrounding infrastructure: - protocol-detect.ts: Auto-detect server type by probing TS3 ServerQuery (port 10011) and TS6 HTTP Query (port 10080) in parallel - http-query.ts: TS6 HTTP Query client replacing the raw-TCP ServerQuery that TS6 servers no longer support (ports 10080/10443) - client.ts: Protocol-aware connection with auto-detection, TS6 HTTP Query setup, and forced protocol override option - manager.ts: Pass through serverProtocol and ts6ApiKey config options - connection.ts: Mark legacy TS3 ServerQuery as deprecated for TS6 https://claude.ai/code/session_016WhH58avUD9xy2dgADJgTh
This commit is contained in:
6 files changed
+386
-5
No files matched your search
@@ -0,0 +1,114 @@
|
||||
import net from "node:net";
|
||||
import http from "node:http";
|
||||
|
||||
export type ServerProtocol = "ts3" | "ts6" | "unknown";
|
||||
|
||||
export interface ProtocolDetectResult {
|
||||
protocol: ServerProtocol;
|
||||
/** The query port that responded (10011 for TS3, 10080 for TS6 HTTP) */
|
||||
queryPort: number | null;
|
||||
/** Whether the voice port (UDP 9987) is the same for both */
|
||||
voicePort: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe a TeamSpeak server to determine if it's running TS3 or TS6.
|
||||
*
|
||||
* Detection strategy:
|
||||
* 1. Try TCP connect to port 10011 (TS3 ServerQuery) — if banner starts with "TS3", it's TS3.
|
||||
* 2. Try HTTP GET to port 10080 (TS6 HTTP Query) — if we get a valid HTTP response, it's TS6.
|
||||
* 3. If neither responds, return "unknown" (voice-only connection may still work).
|
||||
*/
|
||||
export async function detectServerProtocol(
|
||||
host: string,
|
||||
voicePort = 9987,
|
||||
timeoutMs = 3000,
|
||||
): Promise<ProtocolDetectResult> {
|
||||
const [ts3, ts6] = await Promise.allSettled([
|
||||
probeTS3Query(host, 10011, timeoutMs),
|
||||
probeTS6HttpQuery(host, 10080, timeoutMs),
|
||||
]);
|
||||
|
||||
if (ts3.status === "fulfilled" && ts3.value) {
|
||||
return { protocol: "ts3", queryPort: 10011, voicePort };
|
||||
}
|
||||
if (ts6.status === "fulfilled" && ts6.value) {
|
||||
return { protocol: "ts6", queryPort: 10080, voicePort };
|
||||
}
|
||||
|
||||
return { protocol: "unknown", queryPort: null, voicePort };
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe TS3 ServerQuery by connecting to raw TCP and checking for "TS3" banner.
|
||||
*/
|
||||
function probeTS3Query(host: string, port: number, timeoutMs: number): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const socket = net.createConnection({ host, port, timeout: timeoutMs });
|
||||
let banner = "";
|
||||
|
||||
const cleanup = () => {
|
||||
socket.removeAllListeners();
|
||||
socket.destroy();
|
||||
};
|
||||
|
||||
socket.setTimeout(timeoutMs);
|
||||
|
||||
socket.on("data", (data: Buffer) => {
|
||||
banner += data.toString("utf-8");
|
||||
if (banner.includes("TS3")) {
|
||||
cleanup();
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("connect", () => {
|
||||
// Wait briefly for banner
|
||||
setTimeout(() => {
|
||||
cleanup();
|
||||
resolve(banner.includes("TS3"));
|
||||
}, 500);
|
||||
});
|
||||
|
||||
socket.on("error", () => {
|
||||
cleanup();
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
socket.on("timeout", () => {
|
||||
cleanup();
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe TS6 HTTP Query by sending GET / and checking for a valid response.
|
||||
*/
|
||||
function probeTS6HttpQuery(host: string, port: number, timeoutMs: number): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const req = http.request(
|
||||
{
|
||||
hostname: host,
|
||||
port,
|
||||
path: "/",
|
||||
method: "GET",
|
||||
timeout: timeoutMs,
|
||||
headers: { Accept: "application/json" },
|
||||
},
|
||||
(res) => {
|
||||
// TS6 HTTP Query returns some response (even 401/403 is valid — it means the service exists)
|
||||
res.resume();
|
||||
resolve(res.statusCode !== undefined);
|
||||
},
|
||||
);
|
||||
|
||||
req.on("error", () => resolve(false));
|
||||
req.on("timeout", () => {
|
||||
req.destroy();
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user