@honeybbq/teamspeak-client 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/address-BR-5unVo.cjs.map +1 -1
  2. package/dist/address-BfmovVpp.js.map +1 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/client.d.ts +16 -1
  5. package/dist/client.d.ts.map +1 -1
  6. package/dist/command/index.cjs +1 -1
  7. package/dist/command/index.mjs +1 -1
  8. package/dist/command/parser.d.ts.map +1 -1
  9. package/dist/{crypt-init2-D70ha4LB.js → crypt-init2-BcGT--df.js} +2 -2
  10. package/dist/{crypt-init2-D70ha4LB.js.map → crypt-init2-BcGT--df.js.map} +1 -1
  11. package/dist/{crypt-init2-DparLekm.cjs → crypt-init2-Bkee1YxV.cjs} +2 -2
  12. package/dist/{crypt-init2-DparLekm.cjs.map → crypt-init2-Bkee1YxV.cjs.map} +1 -1
  13. package/dist/crypto/identity.d.ts +13 -1
  14. package/dist/crypto/identity.d.ts.map +1 -1
  15. package/dist/crypto/index.cjs +1 -1
  16. package/dist/crypto/index.d.ts +2 -1
  17. package/dist/crypto/index.d.ts.map +1 -1
  18. package/dist/crypto/index.mjs +3 -3
  19. package/dist/{crypto-CDlweUL3.cjs → crypto-BfDd9DP6.cjs} +2 -2
  20. package/dist/{crypto-CDlweUL3.cjs.map → crypto-BfDd9DP6.cjs.map} +1 -1
  21. package/dist/{crypto-Bg02m4ZE.js → crypto-DLV3oH5F.js} +12 -12
  22. package/dist/{crypto-Bg02m4ZE.js.map → crypto-DLV3oH5F.js.map} +1 -1
  23. package/dist/{handler-DI_abe9c.js → handler-BpdZFsKo.js} +104 -102
  24. package/dist/handler-BpdZFsKo.js.map +1 -0
  25. package/dist/handler-aywHvv7I.cjs +2 -0
  26. package/dist/handler-aywHvv7I.cjs.map +1 -0
  27. package/dist/handshake/index.cjs +1 -1
  28. package/dist/handshake/index.mjs +1 -1
  29. package/dist/handshake.d.ts +9 -0
  30. package/dist/handshake.d.ts.map +1 -1
  31. package/dist/index.cjs +2 -2
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.mjs +294 -290
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/{parser-DZMv3Jm-.js → parser-BX8NEiBe.js} +4 -4
  36. package/dist/parser-BX8NEiBe.js.map +1 -0
  37. package/dist/parser-BfwYH2fT.cjs +2 -0
  38. package/dist/parser-BfwYH2fT.cjs.map +1 -0
  39. package/dist/{primitives-xIr6Tjk4.js → primitives-CdX74Rgu.js} +194 -190
  40. package/dist/primitives-CdX74Rgu.js.map +1 -0
  41. package/dist/primitives-DkwW7wcP.cjs +2 -0
  42. package/dist/primitives-DkwW7wcP.cjs.map +1 -0
  43. package/dist/transfer.d.ts.map +1 -1
  44. package/dist/transport/handler.d.ts +9 -1
  45. package/dist/transport/handler.d.ts.map +1 -1
  46. package/dist/transport/index.cjs +1 -1
  47. package/dist/transport/index.mjs +1 -1
  48. package/dist/types.d.ts +11 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +5 -2
  51. package/dist/handler-DI_abe9c.js.map +0 -1
  52. package/dist/handler-DuwoO5RC.cjs +0 -2
  53. package/dist/handler-DuwoO5RC.cjs.map +0 -1
  54. package/dist/parser-CL33R9tr.cjs +0 -2
  55. package/dist/parser-CL33R9tr.cjs.map +0 -1
  56. package/dist/parser-DZMv3Jm-.js.map +0 -1
  57. package/dist/primitives-cNNAeFCo.cjs +0 -2
  58. package/dist/primitives-cNNAeFCo.cjs.map +0 -1
  59. package/dist/primitives-xIr6Tjk4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["consoleLogger","ClientStatus","noopLogger","ServerError","InvalidIdentityError","CryptoInitError","FileTransferError","getUidFromPublicKey","CommandTimeoutError","AlreadyConnectedError","Identity","sign","identityFromString","generateTemporaryKey","generateIdentity","EAXTagMismatchError","TeamspeakError","FileTransferTimeoutError","FakeSignatureMismatchError","Crypt","Resolver","PacketType","PacketHandler","unescape","buildCommandOrdered","buildCommand","processInit1","cryptoInit2","parseCommand","CommandTracker","#nextRC","rc","#pending","resolve","#welcomeComplete","#buffer","params","data","parseServerError","id","\"id\"","msg","\"msg\"","rcStr","\"return_code\"","err","parsed","appendReturnCode","cmd","FileTransferTracker","#nextID","cftid","value","dialFileTransfer","socket","createConnection","key","reject","uploadFileData","host","info","downloadFileData","dest","buildFtInitUpload","targetPath","path","channelID","password","size","overwrite","buildFtInitDownload","buildCommandChain","handler","base","i","middlewares","buildEventChain","handleHandshakeInitIV","alpha","\"alpha\"","beta","\"beta\"","omega","\"omega\"","client","sendClientInit","handleHandshakeExpand2","license","\"l\"","proof","\"proof\"","privateKey","sendClientEkPacket","handleInitServer","idStr","\"aclid\"","\"clid\"","clid","updateCmd","publicKey","ekBase64","clientProof","buildClientEkProof","clientEk","betaBytes","toSign","sig","prepareClientPassword","createHash","buildClientInitCommand","pubKeyBase64","initOptions","defaultChannelPassword","serverPassword","hwid","parseUint64","s","parseUint16","v","parseInt10","isAutoNicknameMatch","actual","expected","suffix","splitCommandRows","spaceIdx","line","name","rest","parts","rows","inheritedFields","part","tokens","currentFields","token","fieldName","inheritedTokens","field","resolvedTokens","equalIndex","handleNotification","handleClientEnterView","clients","nickname","handleClientLeftView","selfCLID","handleClientMoved","handleTextMessage","handleClientPoked","handleStartUpload","handleStartDownload","handleFileTransferStatus","ctid","\"ctid\"","clientType","\"client_type\"","groupsStr","\"client_servergroups\"","\"client_nickname\"","\"client_unique_identifier\"","reasonID","\"reasonid\"","isSelf","\"reasonmsg\"","\"targetid\"","existing","\"invokerid\"","\"invokername\"","\"invokeruid\"","invokerID","invokerInfo","\"targetmode\"","\"target\"","\"clientftfid\"","\"serverftfid\"","\"ftkey\"","\"port\"","\"seekpos\"","\"size\"","\"status\"","CommandThrottle","signal","now","elapsed","#lastUpdate","#tokens","waitMs","timer","normalizeClientInitOptions","options","Client","#identity","identity","#addr","addr","#resolver","#clientInitOptions","#handlePacket","p","#handleConnectionClosed","#cmdMiddlewares","#eventMiddlewares","#finalCmdHandler","#buildCmdHandler","#finalEvtHandler","#buildEvtHandler","#status","#resetForConnect","targetAddr","#resolveAddr","wasConnected","handlers","#disconnectedHandlers","h","#connectionError","waiter","onAbort","error","index","#connectedResolvers","reason","#throttle","timeoutMs","promise","#cmdTrack","withRc","result","event","#textMsgHandlers","#clientEnterHandlers","#clientLeaveHandlers","#clientMoveHandlers","#pokedHandlers","#voiceDataHandlers","#connectedHandlers","#kickedHandlers","mw","#clients","targetClientIds","codec","ftPromise","#ftTrack","st","addrWithPort","#handlePacketSync","pType","response","#handleVoicePacket","#handleCommandLines","clientId","payload","voiceData","lines","trimmed","row","#handleCommandStr","#processNotificationResult","#handleError","eqIdx","k","#failConnection","#dispatchEvent","waiters","evt","#dispatchEventDirect","sendTextMessage","targetMode","targetID","message","clientMove","hashChannelPassword","poke","getClientInfo","listChannels","item","\"cid\"","\"pid\"","\"cpid\"","\"channel_order\"","\"channel_name\"","listClients","fileTransferDeleteFile","paths","pathStr"],"sources":["../src/commands.ts","../src/transfer.ts","../src/events.ts","../src/handshake.ts","../src/helpers.ts","../src/notifications.ts","../src/throttle.ts","../src/client.ts","../src/api.ts"],"sourcesContent":["import { ServerError } from \"./errors.js\";\n\nexport interface CommandResult {\n err: Error | null;\n data: Record<string, string>[];\n}\n\n/**\n * Tracks in-flight commands by return_code.\n *\n * The TS3/TS5 server sends a \"welcome sequence\" of unsolicited data immediately\n * after the connection handshake (channellist, channelclientlist, etc.). This\n * data arrives on the event loop AFTER we may have registered our first pending\n * RC, which would contaminate our command responses.\n *\n * Solution: gate all row buffering on a `#welcomeComplete` flag. The flag is\n * set when `notifycliententerview` for our own clid arrives — the last event\n * the TS3/TS5 server sends in its welcome sequence. Any data arriving before\n * that is silently discarded.\n */\nexport class CommandTracker {\n readonly #pending = new Map<number, (result: CommandResult) => void>();\n #nextRC = 0;\n #buffer: Record<string, string>[] = [];\n\n /**\n * Set to true when we receive `notifycliententerview` for our own clid,\n * which marks the end of the server's welcome sequence.\n */\n #welcomeComplete = false;\n\n register(): [rc: number, promise: Promise<CommandResult>] {\n this.#nextRC++;\n const rc = this.#nextRC;\n const promise = new Promise<CommandResult>((resolve) => {\n this.#pending.set(rc, resolve);\n });\n return [rc, promise];\n }\n\n unregister(rc: number): void {\n this.#pending.delete(rc);\n }\n\n /**\n * Called when `notifycliententerview` for our own clid arrives.\n * Marks the welcome sequence as complete and discards any accumulated data.\n */\n signalWelcomeComplete(): void {\n this.#welcomeComplete = true;\n this.#buffer = [];\n }\n\n /**\n * Buffer a data row from the server. Rows arriving before the welcome\n * sequence is complete are silently discarded to prevent contamination.\n */\n buffer(params: Record<string, string>): void {\n if (!this.#welcomeComplete) return;\n if (this.#pending.size === 0) return;\n this.#buffer.push(params);\n }\n\n resolve(rc: number, err: Error | null): void {\n const resolve = this.#pending.get(rc);\n if (!resolve) {\n this.#buffer = [];\n return;\n }\n const data = this.#buffer;\n this.#buffer = [];\n this.#pending.delete(rc);\n resolve({ err, data });\n }\n\n discardBuffer(): void {\n this.#buffer = [];\n }\n\n reset(): void {\n this.#pending.clear();\n this.#buffer = [];\n this.#welcomeComplete = false;\n this.#nextRC = 0;\n }\n}\n\n/**\n * Parse and handle an `error` command from the server.\n * Returns the error (or null on success) and the resolved return_code.\n */\nexport function parseServerError(params: Record<string, string>): {\n err: Error | null;\n rc: number | null;\n} {\n const id = params[\"id\"] ?? \"0\";\n const msg = params[\"msg\"] ?? \"\";\n const rcStr = params[\"return_code\"];\n\n let err: Error | null = null;\n if (id !== \"0\") {\n err = new ServerError(id, msg);\n }\n\n let rc: number | null = null;\n if (rcStr !== undefined && rcStr !== \"\") {\n const parsed = parseInt(rcStr, 10);\n if (!isNaN(parsed)) rc = parsed;\n }\n\n return { err, rc };\n}\n\n/**\n * Append a return_code parameter to a command string if not already present.\n */\nexport function appendReturnCode(cmd: string, rc: number): string {\n if (cmd.includes(\"return_code=\")) return cmd;\n return `${cmd} return_code=${rc}`;\n}\n","import { createConnection } from \"node:net\";\nimport type { Readable, Writable } from \"node:stream\";\nimport type { FileUploadInfo, FileDownloadInfo } from \"./types.js\";\nimport { FileTransferError, FileTransferTimeoutError } from \"./errors.js\";\nimport { buildCommand } from \"./command/command.js\";\n\ntype FtNotification =\n | FileUploadInfo\n | FileDownloadInfo\n | import(\"./types.js\").FileTransferStatusInfo;\n\nexport class FileTransferTracker {\n readonly #pending = new Map<number, (v: FtNotification) => void>();\n #nextID = 0;\n\n register(): [cftid: number, promise: Promise<FtNotification>] {\n this.#nextID++;\n if (this.#nextID > 65535) this.#nextID = 1;\n const cftid = this.#nextID;\n const promise = new Promise<FtNotification>((resolve) => {\n this.#pending.set(cftid, resolve);\n });\n return [cftid, promise];\n }\n\n unregister(cftid: number): void {\n this.#pending.delete(cftid);\n }\n\n notify(cftid: number, value: FtNotification): void {\n const resolve = this.#pending.get(cftid);\n if (resolve) resolve(value);\n }\n\n reset(): void {\n this.#pending.clear();\n this.#nextID = 0;\n }\n}\n\n/**\n * Open a TCP connection to the TS3 file transfer port and perform the\n * ftkey handshake. The caller is responsible for closing the socket.\n */\nexport function dialFileTransfer(\n host: string,\n port: number,\n key: string,\n): Promise<import(\"node:net\").Socket> {\n return new Promise((resolve, reject) => {\n const socket = createConnection({ host, port }, () => {\n socket.write(key, (err) => {\n if (err) {\n socket.destroy();\n reject(new FileTransferError(`failed to send transfer key: ${err.message}`));\n } else {\n resolve(socket);\n }\n });\n });\n socket.setTimeout(10_000);\n socket.once(\"error\", reject);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new FileTransferError(\"connection timeout\"));\n });\n });\n}\n\n/** Upload data via a TS3 file transfer connection. */\nexport async function uploadFileData(\n host: string,\n info: FileUploadInfo,\n data: Readable,\n): Promise<void> {\n const socket = await dialFileTransfer(host, info.port, info.fileTransferKey);\n await new Promise<void>((resolve, reject) => {\n data.pipe(socket);\n socket.on(\"finish\", resolve);\n socket.on(\"error\", reject);\n });\n}\n\n/** Download data via a TS3 file transfer connection. */\nexport async function downloadFileData(\n host: string,\n info: FileDownloadInfo,\n dest: Writable,\n): Promise<void> {\n const socket = await dialFileTransfer(host, info.port, info.fileTransferKey);\n await new Promise<void>((resolve, reject) => {\n socket.pipe(dest);\n dest.on(\"finish\", resolve);\n socket.on(\"error\", reject);\n dest.on(\"error\", reject);\n });\n}\n\n/**\n * Build a ftinitupload command string.\n */\nexport function buildFtInitUpload(\n channelID: bigint,\n path: string,\n password: string,\n size: bigint,\n cftid: number,\n overwrite: boolean,\n): string {\n const targetPath = path.startsWith(\"/\") ? path : `/${path}`;\n return buildCommand(\"ftinitupload\", {\n cid: String(channelID),\n name: targetPath,\n cpw: password,\n size: String(size),\n clientftfid: String(cftid),\n overwrite: overwrite ? \"1\" : \"0\",\n resume: \"0\",\n });\n}\n\n/**\n * Build a ftinitdownload command string.\n */\nexport function buildFtInitDownload(\n channelID: bigint,\n path: string,\n password: string,\n cftid: number,\n): string {\n const targetPath = path.startsWith(\"/\") ? path : `/${path}`;\n return buildCommand(\"ftinitdownload\", {\n cid: String(channelID),\n name: targetPath,\n cpw: password,\n clientftfid: String(cftid),\n seekpos: \"0\",\n });\n}\n\nexport { FileTransferError, FileTransferTimeoutError };\n","import type { EventMap, CommandMiddleware, EventMiddleware } from \"./types.js\";\n\ntype EventHandler<K extends keyof EventMap> = EventMap[K] extends void\n ? () => void\n : (payload: EventMap[K]) => void;\n\nexport type { EventHandler };\n\n/**\n * Compose a chain of middlewares around a base handler.\n * Rightmost middleware wraps the base first.\n */\nexport function buildCommandChain(\n middlewares: CommandMiddleware[],\n base: (cmd: string) => Promise<void>,\n): (cmd: string) => Promise<void> {\n let handler = base;\n for (let i = middlewares.length - 1; i >= 0; i--) {\n handler = middlewares[i]!(handler);\n }\n return handler;\n}\n\nexport function buildEventChain(\n middlewares: EventMiddleware[],\n base: (evt: EventMap[keyof EventMap]) => void,\n): (evt: EventMap[keyof EventMap]) => void {\n let handler = base;\n for (let i = middlewares.length - 1; i >= 0; i--) {\n handler = middlewares[i]!(handler);\n }\n return handler;\n}\n","import { createHash } from \"node:crypto\";\nimport { buildCommandOrdered, buildCommand } from \"./command/command.js\";\nimport { sign, generateTemporaryKey } from \"./crypto/primitives.js\";\nimport { cryptoInit2 } from \"./handshake/crypt-init2.js\";\nimport { PacketType } from \"./transport/packet.js\";\nimport type { Client } from \"./client.js\";\n\n/** Handle the `clientinitiv` message (P-256 based crypto path). */\nexport function handleHandshakeInitIV(client: Client, params: Record<string, string>): void {\n const alpha = params[\"alpha\"] ?? \"\";\n const beta = params[\"beta\"] ?? \"\";\n const omega = params[\"omega\"] ?? \"\";\n\n client.crypt.initCrypto(alpha, beta, omega);\n client.logger.info(\"crypto initialized (P-256 path), sending clientinit\");\n sendClientInit(client);\n}\n\n/** Handle the `initivexpand2` message (Ed25519 / TS3 crypto path). */\nexport function handleHandshakeExpand2(client: Client, params: Record<string, string>): void {\n client.logger.info(\"received initivexpand2\");\n client.handler.receivedFinalInitAck();\n\n const license = params[\"l\"] ?? \"\";\n const omega = params[\"omega\"] ?? \"\";\n const proof = params[\"proof\"] ?? \"\";\n const beta = params[\"beta\"] ?? \"\";\n\n const privateKey = sendClientEkPacket(client, beta);\n cryptoInit2(client.crypt, license, omega, proof, beta, privateKey);\n sendClientInit(client);\n}\n\n/** Handle `initserver` — marks the client as connected. */\nexport function handleInitServer(client: Client, params: Record<string, string>): void {\n const idStr = params[\"aclid\"] ?? params[\"clid\"] ?? \"\";\n const clid = idStr ? parseInt(idStr, 10) : 0;\n\n if (clid > 0) {\n client.clid = clid;\n client.handler.setClientID(clid);\n }\n\n client.logger.info(\"connected to server\", { selfId: client.clid });\n client._markConnected();\n\n // Inform the server about mute state\n setImmediate(() => {\n const updateCmd = buildCommand(\"clientupdate\", {\n client_input_muted: \"0\",\n client_output_muted: \"0\",\n });\n client.sendCommandNoWait(updateCmd).catch(() => {});\n });\n}\n\nfunction sendClientEkPacket(client: Client, beta: string): Uint8Array {\n const [publicKey, privateKey] = generateTemporaryKey();\n const ekBase64 = Buffer.from(publicKey).toString(\"base64\");\n const clientProof = buildClientEkProof(client, publicKey, beta);\n\n const clientEk = buildCommandOrdered(\"clientek\", [\n [\"ek\", ekBase64],\n [\"proof\", clientProof],\n ]);\n client.handler.sendPacket(PacketType.Command, Buffer.from(clientEk), 0);\n return privateKey;\n}\n\nfunction buildClientEkProof(client: Client, publicKey: Uint8Array, beta: string): string {\n const betaBytes = Buffer.from(beta, \"base64\");\n const toSign = new Uint8Array(86);\n toSign.set(publicKey.slice(0, 32));\n toSign.set(betaBytes.slice(0, Math.min(54, betaBytes.length)), 32);\n const sig = sign(client.crypt.identity.privateKey, toSign);\n return Buffer.from(sig).toString(\"base64\");\n}\n\nfunction prepareClientPassword(password: string): string {\n if (password === \"\") return \"\";\n // TeamSpeak expects password fields as base64(sha1(password)).\n return createHash(\"sha1\").update(password).digest().toString(\"base64\");\n}\n\nexport function sendClientInit(client: Client): void {\n const cmd = buildClientInitCommand(client);\n client.handler.sendPacket(PacketType.Command, Buffer.from(cmd), 0);\n}\n\nfunction buildClientInitCommand(client: Client): string {\n const pubKeyBase64 = client.crypt.identity.publicKeyBase64();\n const initOptions = client.getClientInitOptions();\n const defaultChannelPassword = prepareClientPassword(initOptions.defaultChannelPassword);\n const serverPassword = prepareClientPassword(initOptions.serverPassword);\n // HWID should match TS3 client UID format: base64(SHA1(publicKeyBase64))\n const hwid = createHash(\"sha1\").update(pubKeyBase64).digest().toString(\"base64\");\n\n return buildCommandOrdered(\"clientinit\", [\n [\"client_nickname\", client.nickname],\n [\"client_version\", \"3.?.? [Build: 5680278000]\"],\n [\"client_platform\", \"Windows\"],\n [\"client_input_hardware\", \"1\"],\n [\"client_output_hardware\", \"1\"],\n [\"client_default_channel\", initOptions.defaultChannel],\n [\"client_default_channel_password\", defaultChannelPassword],\n [\"client_server_password\", serverPassword],\n [\"client_meta_data\", \"\"],\n [\n \"client_version_sign\",\n \"DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOoBzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg==\",\n ],\n [\"client_key_offset\", String(client.crypt.identity.offset)],\n [\"client_nickname_phonetic\", \"\"],\n [\"client_default_token\", \"\"],\n [\"hwid\", hwid],\n ]);\n}\n","export function parseUint64(s: string): bigint {\n if (s === \"\" || s === undefined) return 0n;\n try {\n return BigInt(s);\n } catch {\n return 0n;\n }\n}\n\nexport function parseUint16(s: string): number {\n const v = parseInt(s, 10);\n if (isNaN(v) || v < 0 || v > 65535) return 0;\n return v;\n}\n\nexport function parseInt10(s: string): number {\n const v = parseInt(s, 10);\n return isNaN(v) ? 0 : v;\n}\n\n/**\n * Reports whether `actual` equals `expected` or equals `expected` followed by\n * only digits — the pattern TeamSpeak uses when a nickname is already taken.\n */\nexport function isAutoNicknameMatch(expected: string, actual: string): boolean {\n if (actual === expected) return true;\n if (!actual.startsWith(expected)) return false;\n const suffix = actual.slice(expected.length);\n return /^\\d+$/.test(suffix);\n}\n\n/**\n * Expand a pipe-separated multi-row TS3 command line into individual rows,\n * each prefixed with the command name.\n */\nexport function splitCommandRows(line: string): string[] {\n const spaceIdx = line.indexOf(\" \");\n if (spaceIdx < 0) return [line];\n\n const name = line.slice(0, spaceIdx);\n const rest = line.slice(spaceIdx + 1);\n\n if (!rest.includes(\"|\")) return [line];\n\n const parts = rest.split(\"|\");\n const rows: string[] = [];\n let inheritedFields = new Map<string, string>();\n\n for (const part of parts) {\n if (part === \"\") continue;\n\n // TeamSpeak compresses pipe-separated rows by omitting fields whose\n // values are unchanged from the previous row. This is especially\n // important for notifycliententerview, where ctid is commonly present\n // only on the first row of a batch. Keep the raw escaped tokens so the\n // command parser can still perform the single unescape pass later.\n const tokens = part.split(\" \").filter(Boolean);\n const currentFields = new Set<string>();\n for (const token of tokens) currentFields.add(fieldName(token));\n\n const inheritedTokens: string[] = [];\n for (const [field, token] of inheritedFields) {\n if (!currentFields.has(field)) inheritedTokens.push(token);\n }\n\n const resolvedTokens = [...inheritedTokens, ...tokens];\n rows.push(`${name} ${resolvedTokens.join(\" \")}`);\n\n inheritedFields = new Map<string, string>();\n for (const token of resolvedTokens) {\n inheritedFields.set(fieldName(token), token);\n }\n }\n return rows.length === 0 ? [line] : rows;\n}\n\nfunction fieldName(token: string): string {\n const equalIndex = token.indexOf(\"=\");\n return equalIndex > 0 ? token.slice(0, equalIndex) : token;\n}\n","import type { Command } from \"./command/command.js\";\nimport { unescape } from \"./command/command.js\";\nimport type {\n ClientInfo,\n ClientLeftViewEvent,\n ClientMovedEvent,\n TextMessage,\n PokeEvent,\n FileUploadInfo,\n FileDownloadInfo,\n FileTransferStatusInfo,\n} from \"./types.js\";\nimport { parseUint64, parseUint16, parseInt10 } from \"./helpers.js\";\n\nexport type NotificationResult =\n | { kind: \"clientEnter\"; info: ClientInfo }\n | { kind: \"clientLeave\"; event: ClientLeftViewEvent; isSelf: boolean }\n | { kind: \"clientMoved\"; event: ClientMovedEvent }\n | { kind: \"textMessage\"; message: TextMessage }\n | { kind: \"poked\"; event: PokeEvent }\n | { kind: \"startUpload\"; info: FileUploadInfo }\n | { kind: \"startDownload\"; info: FileDownloadInfo }\n | { kind: \"fileTransferStatus\"; info: FileTransferStatusInfo }\n | { kind: \"unknown\" };\n\nexport function handleNotification(\n cmd: Command,\n selfCLID: number,\n clients: Map<number, ClientInfo>,\n nickname: string,\n): NotificationResult {\n switch (cmd.name) {\n case \"notifycliententerview\":\n return handleClientEnterView(cmd, clients, nickname);\n case \"notifyclientleftview\":\n return handleClientLeftView(cmd, selfCLID, clients);\n case \"notifyclientmoved\":\n return handleClientMoved(cmd, clients);\n case \"notifytextmessage\":\n return handleTextMessage(cmd, clients);\n case \"notifyclientpoke\":\n return handleClientPoked(cmd);\n case \"notifystartupload\":\n return { kind: \"startUpload\", info: handleStartUpload(cmd) };\n case \"notifystartdownload\":\n return { kind: \"startDownload\", info: handleStartDownload(cmd) };\n case \"notifystatusfiletransfer\":\n return { kind: \"fileTransferStatus\", info: handleFileTransferStatus(cmd) };\n default:\n return { kind: \"unknown\" };\n }\n}\n\nfunction handleClientEnterView(\n cmd: Command,\n clients: Map<number, ClientInfo>,\n _nickname: string,\n): NotificationResult {\n const clid = parseUint16(cmd.params[\"clid\"] ?? \"\");\n const ctid = parseUint64(cmd.params[\"ctid\"] ?? \"\");\n const clientType = parseInt10(cmd.params[\"client_type\"] ?? \"\");\n const groupsStr = cmd.params[\"client_servergroups\"] ?? \"\";\n\n const info: ClientInfo = {\n id: clid,\n nickname: cmd.params[\"client_nickname\"] ?? \"\",\n uid: cmd.params[\"client_unique_identifier\"] ?? \"\",\n channelID: ctid,\n type: clientType,\n serverGroups: groupsStr ? groupsStr.split(\",\") : [],\n };\n\n if (clid !== 0) {\n clients.set(clid, info);\n }\n\n return { kind: \"clientEnter\", info };\n}\n\nfunction handleClientLeftView(\n cmd: Command,\n selfCLID: number,\n clients: Map<number, ClientInfo>,\n): NotificationResult {\n const clid = parseUint16(cmd.params[\"clid\"] ?? \"\");\n const reasonID = parseInt10(cmd.params[\"reasonid\"] ?? \"\");\n\n const isSelf = clid === selfCLID;\n if (clid !== 0) clients.delete(clid);\n\n return {\n kind: \"clientLeave\",\n event: {\n id: clid,\n reasonID,\n reasonMsg: cmd.params[\"reasonmsg\"] ?? \"\",\n targetID: parseUint16(cmd.params[\"targetid\"] ?? \"\"),\n },\n isSelf,\n };\n}\n\nfunction handleClientMoved(cmd: Command, clients: Map<number, ClientInfo>): NotificationResult {\n const clid = parseUint16(cmd.params[\"clid\"] ?? \"\");\n const ctid = parseUint64(cmd.params[\"ctid\"] ?? \"\");\n\n if (clid !== 0) {\n const existing = clients.get(clid);\n if (existing) clients.set(clid, { ...existing, channelID: ctid });\n }\n\n return {\n kind: \"clientMoved\",\n event: {\n id: clid,\n targetChannelID: ctid,\n reasonID: parseInt10(cmd.params[\"reasonid\"] ?? \"\"),\n invokerID: parseUint16(cmd.params[\"invokerid\"] ?? \"\"),\n invokerName: cmd.params[\"invokername\"] ?? \"\",\n invokerUID: cmd.params[\"invokeruid\"] ?? \"\",\n },\n };\n}\n\nfunction handleTextMessage(cmd: Command, clients: Map<number, ClientInfo>): NotificationResult {\n const invokerID = parseUint16(cmd.params[\"invokerid\"] ?? \"\");\n const invokerInfo = clients.get(invokerID);\n\n const message: TextMessage = {\n targetMode: parseInt10(cmd.params[\"targetmode\"] ?? \"\"),\n targetID: parseUint64(cmd.params[\"target\"] ?? \"\"),\n invokerID,\n invokerName: cmd.params[\"invokername\"] ?? \"\",\n invokerUID: cmd.params[\"invokeruid\"] ?? invokerInfo?.uid ?? \"\",\n message: unescape(cmd.params[\"msg\"] ?? \"\"),\n invokerGroups: invokerInfo?.serverGroups ?? [],\n };\n\n return { kind: \"textMessage\", message };\n}\n\nfunction handleStartUpload(cmd: Command): FileUploadInfo {\n return {\n clientFileTransferID: parseUint16(cmd.params[\"clientftfid\"] ?? \"\"),\n serverFileTransferID: parseUint16(cmd.params[\"serverftfid\"] ?? \"\"),\n fileTransferKey: cmd.params[\"ftkey\"] ?? \"\",\n port: parseUint16(cmd.params[\"port\"] ?? \"\"),\n seekPosition: parseUint64(cmd.params[\"seekpos\"] ?? \"\"),\n };\n}\n\nfunction handleStartDownload(cmd: Command): FileDownloadInfo {\n return {\n clientFileTransferID: parseUint16(cmd.params[\"clientftfid\"] ?? \"\"),\n serverFileTransferID: parseUint16(cmd.params[\"serverftfid\"] ?? \"\"),\n fileTransferKey: cmd.params[\"ftkey\"] ?? \"\",\n port: parseUint16(cmd.params[\"port\"] ?? \"\"),\n size: parseUint64(cmd.params[\"size\"] ?? \"\"),\n };\n}\n\nfunction handleFileTransferStatus(cmd: Command): FileTransferStatusInfo {\n return {\n clientFileTransferID: parseUint16(cmd.params[\"clientftfid\"] ?? \"\"),\n status: parseInt10(cmd.params[\"status\"] ?? \"\"),\n message: cmd.params[\"msg\"] ?? \"\",\n };\n}\n\nfunction handleClientPoked(cmd: Command): NotificationResult {\n return {\n kind: \"poked\",\n event: {\n invokerID: parseUint16(cmd.params[\"invokerid\"] ?? \"\"),\n invokerName: unescape(cmd.params[\"invokername\"] ?? \"\"),\n invokerUID: cmd.params[\"invokeruid\"] ?? \"\",\n message: unescape(cmd.params[\"msg\"] ?? \"\"),\n },\n };\n}\n","/**\n * Token-bucket limiter for outbound TS3 commands.\n * Mirrors Go's commandThrottle.\n */\nexport class CommandThrottle {\n static readonly TOKEN_RATE = 4.0; // tokens per second\n static readonly TOKEN_MAX = 8.0; // bucket capacity\n\n #tokens = 5.0;\n #lastUpdate = Date.now();\n\n async wait(signal?: AbortSignal): Promise<void> {\n while (true) {\n if (signal?.aborted) throw signal.reason as Error;\n\n const now = Date.now();\n const elapsed = (now - this.#lastUpdate) / 1000;\n this.#tokens = Math.min(\n this.#tokens + elapsed * CommandThrottle.TOKEN_RATE,\n CommandThrottle.TOKEN_MAX,\n );\n this.#lastUpdate = now;\n\n if (this.#tokens >= 1.0) {\n this.#tokens -= 1.0;\n return;\n }\n\n const waitMs = Math.ceil(((1.0 - this.#tokens) / CommandThrottle.TOKEN_RATE) * 1000) + 10;\n\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, waitMs);\n if (signal) {\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer);\n reject(signal.reason as Error);\n },\n { once: true },\n );\n }\n });\n }\n }\n}\n","import {\n type EventMap,\n type ClientOptions,\n type CommandMiddleware,\n type EventMiddleware,\n type Logger,\n type AddrResolver,\n type ClientInfo,\n ClientStatus,\n consoleLogger,\n} from \"./types.js\";\nimport { AlreadyConnectedError } from \"./errors.js\";\nimport { Identity, Crypt } from \"./crypto/index.js\";\nimport { Resolver } from \"./discovery/resolver.js\";\nimport { PacketHandler } from \"./transport/handler.js\";\nimport { PacketType } from \"./transport/packet.js\";\nimport type { Packet } from \"./transport/packet.js\";\nimport { CommandTracker, appendReturnCode, parseServerError } from \"./commands.js\";\nimport {\n FileTransferTracker,\n buildFtInitUpload,\n buildFtInitDownload,\n uploadFileData,\n downloadFileData,\n} from \"./transfer.js\";\nimport { buildCommandChain, buildEventChain } from \"./events.js\";\nimport { processInit1 } from \"./handshake/crypt-handshake.js\";\nimport { handleHandshakeInitIV, handleHandshakeExpand2, handleInitServer } from \"./handshake.js\";\nimport { handleNotification } from \"./notifications.js\";\nimport { parseCommand } from \"./command/parser.js\";\nimport { CommandThrottle } from \"./throttle.js\";\nimport { splitCommandRows, isAutoNicknameMatch } from \"./helpers.js\";\nimport type { FileUploadInfo, FileDownloadInfo } from \"./types.js\";\nimport { CommandTimeoutError, FileTransferTimeoutError, FileTransferError } from \"./errors.js\";\nimport type { Readable, Writable } from \"node:stream\";\n\nexport { ClientStatus };\n\nexport interface ClientState {\n status: ClientStatus;\n clid: number;\n}\n\ninterface ClientInitOptions {\n readonly serverPassword: string;\n readonly defaultChannel: string;\n readonly defaultChannelPassword: string;\n}\n\nfunction normalizeClientInitOptions(options: ClientOptions): ClientInitOptions {\n return {\n serverPassword: options.serverPassword ?? \"\",\n defaultChannel: options.defaultChannel ?? \"\",\n defaultChannelPassword: options.defaultChannelPassword ?? \"\",\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyHandler = (arg: any) => void;\n\nexport class Client {\n // Internal — accessible from handshake.ts helpers\n /** @internal */ crypt: Crypt;\n /** @internal */ handler: PacketHandler;\n /** @internal */ logger: Logger;\n /** @internal */ nickname: string;\n /** @internal */ clid = 0;\n\n #identity: Identity;\n #addr: string;\n #resolver: AddrResolver;\n #clientInitOptions: ClientInitOptions;\n #status: ClientStatus = ClientStatus.Disconnected;\n #throttle = new CommandThrottle();\n #cmdTrack = new CommandTracker();\n #ftTrack = new FileTransferTracker();\n #clients = new Map<number, ClientInfo>();\n #connectedResolvers: Array<{ resolve: () => void; reject: (error: Error) => void }> = [];\n #connectionError: Error | null = null;\n\n // Event handler lists\n #textMsgHandlers: Array<(msg: import(\"./types.js\").TextMessage) => void> = [];\n #clientEnterHandlers: Array<(info: ClientInfo) => void> = [];\n #clientLeaveHandlers: Array<(evt: import(\"./types.js\").ClientLeftViewEvent) => void> = [];\n #clientMoveHandlers: Array<(evt: import(\"./types.js\").ClientMovedEvent) => void> = [];\n #pokedHandlers: Array<(evt: import(\"./types.js\").PokeEvent) => void> = [];\n #voiceDataHandlers: Array<(data: import(\"./types.js\").VoiceData) => void> = [];\n #connectedHandlers: Array<() => void> = [];\n #disconnectedHandlers: Array<(err: Error | undefined) => void> = [];\n #kickedHandlers: Array<(msg: string) => void> = [];\n\n // Middleware\n #cmdMiddlewares: CommandMiddleware[] = [];\n #eventMiddlewares: EventMiddleware[] = [];\n #finalCmdHandler: (cmd: string) => Promise<void>;\n #finalEvtHandler: (evt: EventMap[keyof EventMap]) => void;\n\n constructor(identity: Identity, addr: string, nickname: string, options: ClientOptions = {}) {\n this.#identity = identity;\n this.#addr = addr;\n this.nickname = nickname;\n this.logger = options.logger ?? consoleLogger;\n this.#resolver = options.resolver ?? new Resolver(this.logger);\n this.#clientInitOptions = normalizeClientInitOptions(options);\n\n this.crypt = new Crypt(identity);\n this.handler = new PacketHandler(this.crypt, this.logger);\n this.handler.onPacket = (p) => this.#handlePacket(p);\n this.handler.onClosed = (err) => this.#handleConnectionClosed(err);\n\n if (options.commandMiddleware) {\n this.#cmdMiddlewares.push(...options.commandMiddleware);\n }\n if (options.eventMiddleware) {\n this.#eventMiddlewares.push(...options.eventMiddleware);\n }\n\n this.#finalCmdHandler = this.#buildCmdHandler();\n this.#finalEvtHandler = this.#buildEvtHandler();\n }\n\n get status(): ClientStatus {\n return this.#status;\n }\n\n /** @internal */\n getClientInitOptions(): Readonly<{\n serverPassword: string;\n defaultChannel: string;\n defaultChannelPassword: string;\n }> {\n return this.#clientInitOptions;\n }\n\n // ---- Connection -----------------------------------------------------------\n\n async connect(): Promise<void> {\n if (this.#status !== ClientStatus.Disconnected) {\n throw new AlreadyConnectedError();\n }\n\n this.#resetForConnect();\n this.#status = ClientStatus.Connecting;\n\n const targetAddr = await this.#resolveAddr();\n this.logger.info(\"connecting to server\", { address: targetAddr });\n await this.handler.connect(targetAddr);\n }\n\n async disconnect(): Promise<void> {\n if (this.#status === ClientStatus.Disconnected) return;\n\n const wasConnected = this.#status === ClientStatus.Connected;\n this.#status = ClientStatus.Disconnected;\n\n this.logger.info(\"disconnecting from server\");\n\n if (wasConnected) {\n try {\n await this.execCommand(\"clientdisconnect reasonmsg=Shutdown\", 1000);\n } catch {\n // best-effort\n }\n }\n\n this.handler.close();\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(undefined));\n }\n\n waitConnected(signal?: AbortSignal): Promise<void> {\n if (this.#status === ClientStatus.Connected) return Promise.resolve();\n if (this.#connectionError) return Promise.reject(this.#connectionError);\n return new Promise<void>((resolve, reject) => {\n const waiter = {\n resolve: () => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n },\n reject: (error: Error) => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(error);\n },\n };\n const onAbort = (): void => {\n const index = this.#connectedResolvers.indexOf(waiter);\n if (index >= 0) this.#connectedResolvers.splice(index, 1);\n const reason: unknown = signal?.reason;\n waiter.reject(\n reason instanceof Error ? reason : new Error(String(reason ?? \"Connection wait aborted\")),\n );\n };\n this.#connectedResolvers.push(waiter);\n if (signal?.aborted) onAbort();\n else signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n\n // ---- Commands ------------------------------------------------------------\n\n async sendCommandNoWait(cmd: string): Promise<void> {\n await this.#throttle.wait();\n await this.#finalCmdHandler(cmd);\n }\n\n async execCommand(cmd: string, timeoutMs = 10_000): Promise<void> {\n await this.execCommandWithResponse(cmd, timeoutMs);\n }\n\n async execCommandWithResponse(\n cmd: string,\n timeoutMs = 10_000,\n ): Promise<Record<string, string>[]> {\n const [rc, promise] = this.#cmdTrack.register();\n const withRc = appendReturnCode(cmd, rc);\n\n try {\n await this.#throttle.wait();\n await this.#finalCmdHandler(withRc);\n } catch (err) {\n this.#cmdTrack.unregister(rc);\n throw err;\n }\n\n const result = await Promise.race([\n promise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new CommandTimeoutError(cmd)), timeoutMs),\n ),\n ]);\n\n this.#cmdTrack.unregister(rc);\n\n if (result.err) throw result.err;\n return result.data;\n }\n\n // ---- Events --------------------------------------------------------------\n\n on<K extends keyof EventMap>(\n event: K,\n handler: EventMap[K] extends void ? () => void : (payload: EventMap[K]) => void,\n ): this {\n switch (event) {\n case \"textMessage\":\n this.#textMsgHandlers.push(handler as AnyHandler);\n break;\n case \"clientEnter\":\n this.#clientEnterHandlers.push(handler as AnyHandler);\n break;\n case \"clientLeave\":\n this.#clientLeaveHandlers.push(handler as AnyHandler);\n break;\n case \"clientMoved\":\n this.#clientMoveHandlers.push(handler as AnyHandler);\n break;\n case \"poked\":\n this.#pokedHandlers.push(handler as AnyHandler);\n break;\n case \"voiceData\":\n this.#voiceDataHandlers.push(handler as AnyHandler);\n break;\n case \"connected\":\n this.#connectedHandlers.push(handler as () => void);\n break;\n case \"disconnected\":\n this.#disconnectedHandlers.push(handler as AnyHandler);\n break;\n case \"kicked\":\n this.#kickedHandlers.push(handler as AnyHandler);\n break;\n }\n return this;\n }\n\n useCommandMiddleware(...mw: CommandMiddleware[]): this {\n this.#cmdMiddlewares.push(...mw);\n this.#finalCmdHandler = this.#buildCmdHandler();\n return this;\n }\n\n useEventMiddleware(...mw: EventMiddleware[]): this {\n this.#eventMiddlewares.push(...mw);\n this.#finalEvtHandler = this.#buildEvtHandler();\n return this;\n }\n\n // ---- API shorthand -------------------------------------------------------\n\n clientID(): number {\n return this.clid;\n }\n\n channelID(): bigint {\n const info = this.#clients.get(this.clid);\n return info?.channelID ?? 0n;\n }\n\n /** Send Opus voice directly to specific clients, bypassing channel routing. */\n sendWhisper(data: Uint8Array, targetClientIds: number[], codec: number): void {\n this.handler.sendWhisperPacket(data, targetClientIds, codec);\n }\n\n sendVoice(data: Uint8Array, codec: number): void {\n this.handler.sendVoicePacket(data, codec);\n }\n\n // ---- File Transfer -------------------------------------------------------\n\n async fileTransferInitUpload(\n channelID: bigint,\n path: string,\n password: string,\n size: bigint,\n overwrite = false,\n ): Promise<FileUploadInfo> {\n const [cftid, ftPromise] = this.#ftTrack.register();\n const cmd = buildFtInitUpload(channelID, path, password, size, cftid, overwrite);\n\n try {\n await this.execCommand(cmd, 10_000);\n } catch (err) {\n this.#ftTrack.unregister(cftid);\n throw err;\n }\n\n const result = await Promise.race([\n ftPromise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new FileTransferTimeoutError()), 10_000),\n ),\n ]);\n this.#ftTrack.unregister(cftid);\n\n if (\"size\" in result) throw new FileTransferError(\"unexpected download response\");\n if (\"status\" in result) {\n const st = result as import(\"./types.js\").FileTransferStatusInfo;\n throw new FileTransferError(`${st.message} (status=${st.status})`);\n }\n return result as FileUploadInfo;\n }\n\n async fileTransferInitDownload(\n channelID: bigint,\n path: string,\n password: string,\n ): Promise<FileDownloadInfo> {\n const [cftid, ftPromise] = this.#ftTrack.register();\n const cmd = buildFtInitDownload(channelID, path, password, cftid);\n\n try {\n await this.execCommand(cmd, 10_000);\n } catch (err) {\n this.#ftTrack.unregister(cftid);\n throw err;\n }\n\n const result = await Promise.race([\n ftPromise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new FileTransferTimeoutError()), 10_000),\n ),\n ]);\n this.#ftTrack.unregister(cftid);\n\n if (\"seekPosition\" in result) throw new FileTransferError(\"unexpected upload response\");\n if (\"status\" in result) {\n const st = result as import(\"./types.js\").FileTransferStatusInfo;\n throw new FileTransferError(`${st.message} (status=${st.status})`);\n }\n return result as FileDownloadInfo;\n }\n\n uploadFileData(host: string, info: FileUploadInfo, data: Readable): Promise<void> {\n return uploadFileData(host, info, data);\n }\n\n downloadFileData(host: string, info: FileDownloadInfo, dest: Writable): Promise<void> {\n return downloadFileData(host, info, dest);\n }\n\n // ---- Internal (package-visible) ------------------------------------------\n\n /** @internal */\n _markConnected(): void {\n this.#status = ClientStatus.Connected;\n this.#connectionError = null;\n for (const waiter of this.#connectedResolvers) waiter.resolve();\n this.#connectedResolvers = [];\n const handlers = this.#connectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h());\n }\n\n // ---- Private -------------------------------------------------------------\n\n #resetForConnect(): void {\n // Waiters are deliberately left in place: connect() resets first, so a\n // caller that registered waitConnected() before calling connect() must\n // still be resolved by the handshake that follows. Clear only the error\n // carried over from a previous attempt.\n this.#connectionError = null;\n this.handler.close();\n this.crypt = new Crypt(this.#identity);\n this.handler = new PacketHandler(this.crypt, this.logger);\n this.handler.onPacket = (p) => this.#handlePacket(p);\n this.handler.onClosed = (err) => this.#handleConnectionClosed(err);\n this.#cmdTrack.reset();\n this.#ftTrack.reset();\n this.#clients.clear();\n this.clid = 0;\n this.#finalCmdHandler = this.#buildCmdHandler();\n }\n\n async #resolveAddr(): Promise<string> {\n const addrWithPort = this.#addr.includes(\":\") ? this.#addr : `${this.#addr}:9987`;\n try {\n const resolved = await this.#resolver.resolve(this.#addr);\n return resolved[0]?.addr ?? addrWithPort;\n } catch {\n return addrWithPort;\n }\n }\n\n #handlePacket(p: Packet): void {\n this.#handlePacketSync(p);\n }\n\n #handlePacketSync(p: Packet): void {\n const pType = p.typeFlagged & 0x0f;\n if (pType === 8 /* Init1 */) {\n const response = processInit1(this.crypt, p.data);\n if (response) {\n this.handler.sendPacket(PacketType.Init1, response, 0);\n }\n return;\n }\n\n if ((pType === 0 /* Voice */ || pType === 1) /* VoiceWhisper */ && p.data.length > 5) {\n this.#handleVoicePacket(p.data);\n return;\n }\n\n if ((pType === 2 /* Command */ || pType === 3) /* CommandLow */ && p.data.length > 0) {\n this.#handleCommandLines(Buffer.from(p.data).toString(\"utf8\"));\n }\n }\n\n /**\n * Parse an incoming S2C voice packet.\n * Format: [VId: u16] [CId: u16] [Codec: u8] [Data: var]\n */\n #handleVoicePacket(payload: Uint8Array): void {\n if (this.#voiceDataHandlers.length === 0) return;\n\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);\n const clientId = view.getUint16(2, false);\n if (clientId === this.clid) return;\n\n const codec = payload[4]!;\n const data = payload.subarray(5);\n\n const voiceData: import(\"./types.js\").VoiceData = { clientId, codec, data };\n for (const h of this.#voiceDataHandlers) setImmediate(() => h(voiceData));\n }\n\n #handleCommandLines(s: string): void {\n if (!s) return;\n const lines = s.split(\"\\0\").join(\"\\n\").split(\"\\n\");\n for (const line of lines) {\n const trimmed = line.replace(/\\r$/, \"\");\n if (!trimmed) continue;\n for (const row of splitCommandRows(trimmed)) {\n this.#handleCommandStr(row);\n }\n }\n }\n\n #handleCommandStr(s: string): void {\n const cmd = parseCommand(s);\n if (!cmd || !cmd.name) return;\n\n if (cmd.name.startsWith(\"notify\")) {\n const result = handleNotification(cmd, this.clid, this.#clients, this.nickname);\n this.#processNotificationResult(result, cmd.params);\n return;\n }\n\n switch (cmd.name) {\n case \"clientinitiv\":\n handleHandshakeInitIV(this, cmd.params);\n break;\n case \"initivexpand2\":\n handleHandshakeExpand2(this, cmd.params);\n break;\n case \"initserver\":\n handleInitServer(this, cmd.params);\n break;\n case \"error\":\n this.#handleError(cmd.params);\n break;\n default: {\n // TS5/TS6 servers send data rows without a command-name prefix.\n // In that case our parser treats the first key=value pair as the\n // \"command name\" (e.g. name=\"clid=1827\"). Reconstruct full params.\n let params = cmd.params;\n if (cmd.name.includes(\"=\")) {\n const eqIdx = cmd.name.indexOf(\"=\");\n const k = cmd.name.slice(0, eqIdx);\n const v = cmd.name.slice(eqIdx + 1);\n params = { [k]: v, ...cmd.params };\n }\n this.#cmdTrack.buffer(params);\n break;\n }\n }\n }\n\n #handleError(params: Record<string, string>): void {\n const { err, rc } = parseServerError(params);\n if (rc !== null) {\n this.#cmdTrack.resolve(rc, err);\n } else {\n // No return_code: server is acknowledging an unsolicited command\n // (welcome-sequence channellist, no-RC commands like clientupdate, etc.).\n // Discard any rows buffered since the last resolved command.\n this.#cmdTrack.discardBuffer();\n }\n\n // A server error before the welcome sequence finished is the connection\n // failure itself — a rejected password, or an invalid clientinit parameter.\n // Report it as the cause instead of leaving the caller with a bare timeout.\n if (err && this.#status !== ClientStatus.Connected) {\n this.#failConnection(err);\n return;\n }\n\n const id = params[\"id\"] ?? \"0\";\n if (id === \"3329\") {\n setImmediate(() => this.disconnect().catch(() => {}));\n }\n }\n\n #processNotificationResult(\n result: import(\"./notifications.js\").NotificationResult,\n _params: Record<string, string>,\n ): void {\n switch (result.kind) {\n case \"clientEnter\": {\n const info = result.info;\n // Our own notifycliententerview is the last event in the TS3/TS5\n // welcome sequence. Identify self by the clid initserver already\n // assigned rather than by nickname: any other client whose nickname\n // happens to be \"<our nick><digits>\" — which is how the server renames\n // a client whose nickname was taken — would otherwise be mistaken for\n // us, poisoning every outgoing packet header with a foreign clientID\n // until the server drops the connection for command-resend timeout.\n if (info.id !== 0 && info.id === this.clid) {\n // Carries the rename the server applied if our nickname was taken.\n this.nickname = info.nickname;\n this.#cmdTrack.signalWelcomeComplete();\n } else if (\n info.id !== 0 &&\n this.clid === 0 &&\n isAutoNicknameMatch(this.nickname, info.nickname)\n ) {\n // initserver carried no aclid, so the welcome enter is the only way\n // to learn our clid. Gated on clid still being unknown so a later\n // lookalike can never hijack an established session.\n this.clid = info.id;\n this.handler.setClientID(info.id);\n this.nickname = info.nickname;\n this.#cmdTrack.signalWelcomeComplete();\n }\n this.#dispatchEvent(\"clientEnter\", info);\n break;\n }\n case \"clientLeave\": {\n this.#dispatchEvent(\"clientLeave\", result.event);\n if (result.isSelf && (result.event.reasonID === 4 || result.event.reasonID === 5)) {\n const msg = result.event.reasonMsg;\n for (const h of this.#kickedHandlers) setImmediate(() => h(msg));\n }\n break;\n }\n case \"clientMoved\":\n this.#dispatchEvent(\"clientMoved\", result.event);\n break;\n case \"textMessage\":\n this.#dispatchEvent(\"textMessage\", result.message);\n break;\n case \"poked\":\n this.#dispatchEvent(\"poked\", result.event);\n break;\n case \"startUpload\":\n this.#ftTrack.notify(result.info.clientFileTransferID, result.info);\n break;\n case \"startDownload\":\n this.#ftTrack.notify(result.info.clientFileTransferID, result.info);\n break;\n case \"fileTransferStatus\":\n this.#ftTrack.notify(result.info.clientFileTransferID, result.info);\n break;\n }\n }\n\n #dispatchEvent<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n this.#finalEvtHandler(payload as EventMap[keyof EventMap]);\n }\n\n #dispatchEventDirect<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n switch (event) {\n case \"textMessage\":\n for (const h of this.#textMsgHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").TextMessage));\n break;\n case \"clientEnter\":\n for (const h of this.#clientEnterHandlers) setImmediate(() => h(payload as ClientInfo));\n break;\n case \"clientLeave\":\n for (const h of this.#clientLeaveHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").ClientLeftViewEvent));\n break;\n case \"clientMoved\":\n for (const h of this.#clientMoveHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").ClientMovedEvent));\n break;\n case \"poked\":\n for (const h of this.#pokedHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").PokeEvent));\n break;\n }\n }\n\n #handleConnectionClosed(err: Error | null): void {\n if (this.#status === ClientStatus.Disconnected) return;\n if (this.#status !== ClientStatus.Connected) {\n // The socket closed before the handshake finished, so this is a failed\n // connection attempt rather than a disconnect of a live session.\n this.#failConnection(\n err ?? new Error(\"Connection closed before the TeamSpeak handshake completed\"),\n );\n return;\n }\n this.#status = ClientStatus.Disconnected;\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(err ?? undefined));\n }\n\n /**\n * A connection attempt has failed for good. Record why, release anyone\n * waiting on `waitConnected`, and tear the connection down.\n *\n * Without this, a handshake the server rejects (a wrong password, say, which\n * the server answers with an error instead of closing the socket) surfaces\n * only as the caller's own timeout, and the client stays in `Connecting`\n * forever without ever emitting `disconnected`.\n */\n #failConnection(error: Error): void {\n this.#connectionError = error;\n const waiters = this.#connectedResolvers.splice(0);\n for (const waiter of waiters) waiter.reject(error);\n\n if (this.#status === ClientStatus.Disconnected) return;\n this.#status = ClientStatus.Disconnected;\n this.handler.close();\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(error));\n }\n\n #buildCmdHandler(): (cmd: string) => Promise<void> {\n const base = async (cmd: string): Promise<void> => {\n this.handler.sendPacket(PacketType.Command, Buffer.from(cmd), 0);\n };\n return buildCommandChain(this.#cmdMiddlewares, base);\n }\n\n #buildEvtHandler(): (evt: EventMap[keyof EventMap]) => void {\n const base = (evt: EventMap[keyof EventMap]): void => {\n // Determine which event type this is by checking the shape\n if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"invokerName\" in evt &&\n \"message\" in evt &&\n \"targetMode\" in evt\n ) {\n this.#dispatchEventDirect(\"textMessage\", evt as EventMap[\"textMessage\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"invokerName\" in evt &&\n \"message\" in evt &&\n !(\"targetMode\" in evt)\n ) {\n this.#dispatchEventDirect(\"poked\", evt as EventMap[\"poked\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"id\" in evt &&\n \"uid\" in evt\n ) {\n this.#dispatchEventDirect(\"clientEnter\", evt as EventMap[\"clientEnter\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"id\" in evt &&\n \"reasonID\" in evt &&\n \"targetChannelID\" in evt\n ) {\n this.#dispatchEventDirect(\"clientMoved\", evt as EventMap[\"clientMoved\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"id\" in evt &&\n \"reasonID\" in evt\n ) {\n this.#dispatchEventDirect(\"clientLeave\", evt as EventMap[\"clientLeave\"]);\n }\n };\n return buildEventChain(this.#eventMiddlewares, base);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { buildCommandOrdered, buildCommand, unescape } from \"./command/command.js\";\nimport type { ChannelInfo, ClientInfo } from \"./types.js\";\nimport type { Client } from \"./client.js\";\n\n/** Send a text message to a client (targetMode=1), channel (2), or server (3). */\nexport async function sendTextMessage(\n client: Client,\n targetMode: number,\n targetID: bigint,\n message: string,\n): Promise<void> {\n const cmd = buildCommandOrdered(\"sendtextmessage\", [\n [\"targetmode\", String(targetMode)],\n [\"target\", String(targetID)],\n [\"msg\", message],\n ]);\n await client.sendCommandNoWait(cmd);\n}\n\n/** Move a client to a different channel. */\nexport async function clientMove(\n client: Client,\n clid: number,\n channelID: bigint,\n password = \"\",\n): Promise<void> {\n const params: Array<readonly [string, string]> = [\n [\"clid\", String(clid)],\n [\"cid\", String(channelID)],\n ];\n // The native TeamSpeak client protocol expects channel passwords as the\n // SHA-1 Base64 digest, just like client_default_channel_password during\n // clientinit. Sending the plain password makes valid passwords fail with\n // error 781 (invalid channel password).\n if (password) params.push([\"cpw\", hashChannelPassword(password)]);\n const cmd = buildCommandOrdered(\"clientmove\", params);\n await client.execCommand(cmd, 10_000);\n}\n\nfunction hashChannelPassword(password: string): string {\n return createHash(\"sha1\").update(password).digest(\"base64\");\n}\n\n/** Send a poke message to a client. */\nexport async function poke(client: Client, clid: number, message: string): Promise<void> {\n const cmd = buildCommandOrdered(\"clientpoke\", [\n [\"clid\", String(clid)],\n [\"msg\", message],\n ]);\n await client.execCommand(cmd, 10_000);\n}\n\n/** Fetch raw clientinfo for a given clid. */\nexport async function getClientInfo(client: Client, clid: number): Promise<Record<string, string>> {\n const data = await client.execCommandWithResponse(`clientinfo clid=${clid}`, 5_000);\n const row = data[0];\n if (!row) throw new Error(`no data returned for client ${clid}`);\n return row;\n}\n\n/** List all channels on the server. */\nexport async function listChannels(client: Client): Promise<ChannelInfo[]> {\n const data = await client.execCommandWithResponse(\"channellist\", 5_000);\n return data.map((item) => ({\n id: BigInt(item[\"cid\"] ?? \"0\"),\n // The command response uses `pid`; unsolicited server pushes use `cpid`.\n parentID: BigInt(item[\"pid\"] ?? item[\"cpid\"] ?? \"0\"),\n order: BigInt(item[\"channel_order\"] ?? \"0\"),\n name: unescape(item[\"channel_name\"] ?? \"\"),\n description: \"\",\n }));\n}\n\n/** List all clients currently connected to the server. */\nexport async function listClients(client: Client): Promise<ClientInfo[]> {\n const data = await client.execCommandWithResponse(\"clientlist -uid -away -voice -groups\", 5_000);\n return data.map((item) => {\n const groupsStr = item[\"client_servergroups\"] ?? \"\";\n return {\n id: parseInt(item[\"clid\"] ?? \"0\", 10),\n nickname: unescape(item[\"client_nickname\"] ?? \"\"),\n uid: item[\"client_unique_identifier\"] ?? \"\",\n channelID: BigInt(item[\"cid\"] ?? \"0\"),\n type: parseInt(item[\"client_type\"] ?? \"0\", 10),\n serverGroups: groupsStr ? groupsStr.split(\",\") : [],\n };\n });\n}\n\n/** Delete a file on the server. */\nexport async function fileTransferDeleteFile(\n client: Client,\n channelID: bigint,\n paths: string[],\n): Promise<void> {\n if (paths.length === 0) return;\n const pathStr = paths.join(\"|\");\n const cmd = buildCommand(\"ftdeletefile\", {\n cid: String(channelID),\n cpw: \"\",\n name: pathStr,\n });\n await client.execCommand(cmd, 10_000);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["consoleLogger","ClientStatus","noopLogger","InvalidIdentityError","FileTransferTimeoutError","TeamspeakError","CommandTimeoutError","FakeSignatureMismatchError","getUidFromPublicKey","AlreadyConnectedError","uidAlgorithmForVersion","Identity","sign","generateTemporaryKey","generateIdentity","CryptoInitError","ServerError","FileTransferError","Crypt","Resolver","PacketType","PacketHandler","unescape","buildCommandOrdered","buildCommand","processInit1","cryptoInit2","parseCommand","CommandTracker","#nextRC","rc","#pending","resolve","#welcomeComplete","#buffer","params","data","parseServerError","id","\"id\"","msg","\"msg\"","rcStr","\"return_code\"","err","parsed","appendReturnCode","cmd","handleHandshakeInitIV","alpha","\"alpha\"","beta","\"beta\"","omega","\"omega\"","client","sendClientInit","handleHandshakeExpand2","license","\"l\"","proof","\"proof\"","privateKey","sendClientEkPacket","handleInitServer","idStr","\"aclid\"","\"clid\"","clid","version","\"virtualserver_version\"","updateCmd","publicKey","ekBase64","clientProof","buildClientEkProof","clientEk","betaBytes","toSign","sig","prepareClientPassword","password","createHash","buildClientInitCommand","pubKeyBase64","initOptions","defaultChannelPassword","serverPassword","hwid","FileTransferTracker","#nextID","cftid","value","dialFileTransfer","socket","createConnection","key","reject","uploadFileData","host","info","downloadFileData","dest","buildFtInitUpload","targetPath","path","channelID","size","overwrite","buildFtInitDownload","buildCommandChain","handler","base","i","middlewares","buildEventChain","parseUint64","s","parseUint16","v","parseInt10","isAutoNicknameMatch","actual","expected","suffix","splitCommandRows","spaceIdx","line","name","rest","parts","rows","inheritedFields","part","tokens","currentFields","token","fieldName","inheritedTokens","field","resolvedTokens","equalIndex","handleNotification","handleClientEnterView","clients","nickname","handleClientLeftView","selfCLID","handleClientMoved","handleTextMessage","handleClientPoked","handleStartUpload","handleStartDownload","handleFileTransferStatus","ctid","\"ctid\"","clientType","\"client_type\"","groupsStr","\"client_servergroups\"","\"client_nickname\"","\"client_unique_identifier\"","reasonID","\"reasonid\"","isSelf","\"reasonmsg\"","\"targetid\"","existing","\"invokerid\"","\"invokername\"","\"invokeruid\"","invokerID","invokerInfo","\"targetmode\"","\"target\"","\"clientftfid\"","\"serverftfid\"","\"ftkey\"","\"port\"","\"seekpos\"","\"size\"","\"status\"","CommandThrottle","signal","now","elapsed","#lastUpdate","#tokens","waitMs","timer","normalizeClientInitOptions","options","Client","#identity","identity","#addr","addr","#resolver","#clientInitOptions","#socketFactory","#handlePacket","p","#handleConnectionClosed","#cmdMiddlewares","#eventMiddlewares","#finalCmdHandler","#buildCmdHandler","#finalEvtHandler","#buildEvtHandler","#status","#resetForConnect","targetAddr","#resolveAddr","wasConnected","handlers","#disconnectedHandlers","h","#connectionError","waiter","onAbort","error","index","#connectedResolvers","reason","#throttle","timeoutMs","promise","#cmdTrack","withRc","result","event","#textMsgHandlers","#clientEnterHandlers","#clientLeaveHandlers","#clientMoveHandlers","#pokedHandlers","#voiceDataHandlers","#connectedHandlers","#kickedHandlers","mw","#clients","targetClientIds","codec","ftPromise","#ftTrack","st","addrWithPort","#handlePacketSync","pType","response","#handleVoicePacket","text","\"TEAMSPEAK_TRACE_RAW\"","#handleCommandLines","clientId","payload","voiceData","lines","trimmed","row","#handleCommandStr","#processNotificationResult","#handleError","#failConnection","#dispatchEvent","waiters","evt","#dispatchEventDirect","sendTextMessage","targetMode","targetID","message","clientMove","poke","getClientInfo","listChannels","item","\"cid\"","\"pid\"","\"cpid\"","\"channel_order\"","\"channel_name\"","listClients","fileTransferDeleteFile","paths","pathStr","EAXTagMismatchError","identityFromString"],"sources":["../src/commands.ts","../src/handshake.ts","../src/transfer.ts","../src/events.ts","../src/helpers.ts","../src/notifications.ts","../src/throttle.ts","../src/client.ts","../src/api.ts"],"sourcesContent":["import { ServerError } from \"./errors.js\";\n\nexport interface CommandResult {\n err: Error | null;\n data: Record<string, string>[];\n}\n\n/**\n * Tracks in-flight commands by return_code.\n *\n * The TS3/TS5 server sends a \"welcome sequence\" of unsolicited data immediately\n * after the connection handshake (channellist, channelclientlist, etc.). This\n * data arrives on the event loop AFTER we may have registered our first pending\n * RC, which would contaminate our command responses.\n *\n * Solution: gate all row buffering on a `#welcomeComplete` flag. The flag is\n * set when `notifycliententerview` for our own clid arrives — the last event\n * the TS3/TS5 server sends in its welcome sequence. Any data arriving before\n * that is silently discarded.\n */\nexport class CommandTracker {\n readonly #pending = new Map<number, (result: CommandResult) => void>();\n #nextRC = 0;\n #buffer: Record<string, string>[] = [];\n\n /**\n * Set to true when we receive `notifycliententerview` for our own clid,\n * which marks the end of the server's welcome sequence.\n */\n #welcomeComplete = false;\n\n register(): [rc: number, promise: Promise<CommandResult>] {\n this.#nextRC++;\n const rc = this.#nextRC;\n const promise = new Promise<CommandResult>((resolve) => {\n this.#pending.set(rc, resolve);\n });\n return [rc, promise];\n }\n\n unregister(rc: number): void {\n this.#pending.delete(rc);\n }\n\n /**\n * Called when `notifycliententerview` for our own clid arrives.\n * Marks the welcome sequence as complete and discards any accumulated data.\n */\n signalWelcomeComplete(): void {\n this.#welcomeComplete = true;\n this.#buffer = [];\n }\n\n /**\n * Buffer a data row from the server. Rows arriving before the welcome\n * sequence is complete are silently discarded to prevent contamination.\n */\n buffer(params: Record<string, string>): void {\n if (!this.#welcomeComplete) return;\n if (this.#pending.size === 0) return;\n this.#buffer.push(params);\n }\n\n resolve(rc: number, err: Error | null): void {\n const resolve = this.#pending.get(rc);\n if (!resolve) {\n this.#buffer = [];\n return;\n }\n const data = this.#buffer;\n this.#buffer = [];\n this.#pending.delete(rc);\n resolve({ err, data });\n }\n\n discardBuffer(): void {\n this.#buffer = [];\n }\n\n reset(): void {\n this.#pending.clear();\n this.#buffer = [];\n this.#welcomeComplete = false;\n this.#nextRC = 0;\n }\n}\n\n/**\n * Parse and handle an `error` command from the server.\n * Returns the error (or null on success) and the resolved return_code.\n */\nexport function parseServerError(params: Record<string, string>): {\n err: Error | null;\n rc: number | null;\n} {\n const id = params[\"id\"] ?? \"0\";\n const msg = params[\"msg\"] ?? \"\";\n const rcStr = params[\"return_code\"];\n\n let err: Error | null = null;\n if (id !== \"0\") {\n err = new ServerError(id, msg);\n }\n\n let rc: number | null = null;\n if (rcStr !== undefined && rcStr !== \"\") {\n const parsed = parseInt(rcStr, 10);\n if (!isNaN(parsed)) rc = parsed;\n }\n\n return { err, rc };\n}\n\n/**\n * Append a return_code parameter to a command string if not already present.\n */\nexport function appendReturnCode(cmd: string, rc: number): string {\n if (cmd.includes(\"return_code=\")) return cmd;\n return `${cmd} return_code=${rc}`;\n}\n","import { createHash } from \"node:crypto\";\nimport { buildCommandOrdered, buildCommand } from \"./command/command.js\";\nimport { sign, generateTemporaryKey } from \"./crypto/primitives.js\";\nimport { uidAlgorithmForVersion } from \"./crypto/identity.js\";\nimport { cryptoInit2 } from \"./handshake/crypt-init2.js\";\nimport { PacketType } from \"./transport/packet.js\";\nimport type { Client } from \"./client.js\";\n\n/** Handle the `clientinitiv` message (P-256 based crypto path). */\nexport function handleHandshakeInitIV(client: Client, params: Record<string, string>): void {\n const alpha = params[\"alpha\"] ?? \"\";\n const beta = params[\"beta\"] ?? \"\";\n const omega = params[\"omega\"] ?? \"\";\n\n client.crypt.initCrypto(alpha, beta, omega);\n client.logger.info(\"crypto initialized (P-256 path), sending clientinit\");\n sendClientInit(client);\n}\n\n/** Handle the `initivexpand2` message (Ed25519 / TS3 crypto path). */\nexport function handleHandshakeExpand2(client: Client, params: Record<string, string>): void {\n client.logger.info(\"received initivexpand2\");\n client.handler.receivedFinalInitAck();\n\n const license = params[\"l\"] ?? \"\";\n const omega = params[\"omega\"] ?? \"\";\n const proof = params[\"proof\"] ?? \"\";\n const beta = params[\"beta\"] ?? \"\";\n\n const privateKey = sendClientEkPacket(client, beta);\n cryptoInit2(client.crypt, license, omega, proof, beta, privateKey);\n sendClientInit(client);\n}\n\n/** Handle `initserver` — marks the client as connected. */\nexport function handleInitServer(client: Client, params: Record<string, string>): void {\n const idStr = params[\"aclid\"] ?? params[\"clid\"] ?? \"\";\n const clid = idStr ? parseInt(idStr, 10) : 0;\n\n if (clid > 0) {\n client.clid = clid;\n client.handler.setClientID(clid);\n }\n\n // initserver is the only place the server names itself, and it determines how\n // the server hashes our public key into client_unique_identifier: SHA-1 on\n // TS3, SHA-256 on TS6.\n const version = params[\"virtualserver_version\"];\n if (version) client.uidAlgorithm = uidAlgorithmForVersion(version);\n\n client.logger.info(\"connected to server\", { selfId: client.clid });\n client._markConnected();\n\n // Inform the server about mute state\n setImmediate(() => {\n const updateCmd = buildCommand(\"clientupdate\", {\n client_input_muted: \"0\",\n client_output_muted: \"0\",\n });\n client.sendCommandNoWait(updateCmd).catch(() => {});\n });\n}\n\nfunction sendClientEkPacket(client: Client, beta: string): Uint8Array {\n const [publicKey, privateKey] = generateTemporaryKey();\n const ekBase64 = Buffer.from(publicKey).toString(\"base64\");\n const clientProof = buildClientEkProof(client, publicKey, beta);\n\n const clientEk = buildCommandOrdered(\"clientek\", [\n [\"ek\", ekBase64],\n [\"proof\", clientProof],\n ]);\n client.handler.sendPacket(PacketType.Command, Buffer.from(clientEk), 0);\n return privateKey;\n}\n\nfunction buildClientEkProof(client: Client, publicKey: Uint8Array, beta: string): string {\n const betaBytes = Buffer.from(beta, \"base64\");\n const toSign = new Uint8Array(86);\n toSign.set(publicKey.slice(0, 32));\n toSign.set(betaBytes.slice(0, Math.min(54, betaBytes.length)), 32);\n const sig = sign(client.crypt.identity.privateKey, toSign);\n return Buffer.from(sig).toString(\"base64\");\n}\n\n/**\n * Encode a password field for the command protocol.\n *\n * TeamSpeak expects password fields as base64(sha1(password)) — server and\n * channel passwords during the handshake, the `clientmove` channel password,\n * and the `cpw` field of the file transfer commands. Sending the plain text\n * instead makes the server reject correct passwords with error 781.\n */\nexport function prepareClientPassword(password: string): string {\n if (password === \"\") return \"\";\n return createHash(\"sha1\").update(password).digest().toString(\"base64\");\n}\n\nexport function sendClientInit(client: Client): void {\n const cmd = buildClientInitCommand(client);\n client.handler.sendPacket(PacketType.Command, Buffer.from(cmd), 0);\n}\n\nfunction buildClientInitCommand(client: Client): string {\n const pubKeyBase64 = client.crypt.identity.publicKeyBase64();\n const initOptions = client.getClientInitOptions();\n const defaultChannelPassword = prepareClientPassword(initOptions.defaultChannelPassword);\n const serverPassword = prepareClientPassword(initOptions.serverPassword);\n // HWID should match TS3 client UID format: base64(SHA1(publicKeyBase64))\n const hwid = createHash(\"sha1\").update(pubKeyBase64).digest().toString(\"base64\");\n\n return buildCommandOrdered(\"clientinit\", [\n [\"client_nickname\", client.nickname],\n [\"client_version\", \"3.?.? [Build: 5680278000]\"],\n [\"client_platform\", \"Windows\"],\n [\"client_input_hardware\", \"1\"],\n [\"client_output_hardware\", \"1\"],\n [\"client_default_channel\", initOptions.defaultChannel],\n [\"client_default_channel_password\", defaultChannelPassword],\n [\"client_server_password\", serverPassword],\n [\"client_meta_data\", \"\"],\n [\n \"client_version_sign\",\n \"DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOoBzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg==\",\n ],\n [\"client_key_offset\", String(client.crypt.identity.offset)],\n [\"client_nickname_phonetic\", \"\"],\n [\"client_default_token\", \"\"],\n [\"hwid\", hwid],\n ]);\n}\n","import { createConnection } from \"node:net\";\nimport type { Readable, Writable } from \"node:stream\";\nimport type { FileUploadInfo, FileDownloadInfo } from \"./types.js\";\nimport { FileTransferError, FileTransferTimeoutError } from \"./errors.js\";\nimport { buildCommand } from \"./command/command.js\";\nimport { prepareClientPassword } from \"./handshake.js\";\n\ntype FtNotification =\n | FileUploadInfo\n | FileDownloadInfo\n | import(\"./types.js\").FileTransferStatusInfo;\n\nexport class FileTransferTracker {\n readonly #pending = new Map<number, (v: FtNotification) => void>();\n #nextID = 0;\n\n register(): [cftid: number, promise: Promise<FtNotification>] {\n this.#nextID++;\n if (this.#nextID > 65535) this.#nextID = 1;\n const cftid = this.#nextID;\n const promise = new Promise<FtNotification>((resolve) => {\n this.#pending.set(cftid, resolve);\n });\n return [cftid, promise];\n }\n\n unregister(cftid: number): void {\n this.#pending.delete(cftid);\n }\n\n notify(cftid: number, value: FtNotification): void {\n const resolve = this.#pending.get(cftid);\n if (resolve) resolve(value);\n }\n\n reset(): void {\n this.#pending.clear();\n this.#nextID = 0;\n }\n}\n\n/**\n * Open a TCP connection to the TS3 file transfer port and perform the\n * ftkey handshake. The caller is responsible for closing the socket.\n */\nexport function dialFileTransfer(\n host: string,\n port: number,\n key: string,\n): Promise<import(\"node:net\").Socket> {\n return new Promise((resolve, reject) => {\n const socket = createConnection({ host, port }, () => {\n socket.write(key, (err) => {\n if (err) {\n socket.destroy();\n reject(new FileTransferError(`failed to send transfer key: ${err.message}`));\n } else {\n resolve(socket);\n }\n });\n });\n socket.setTimeout(10_000);\n socket.once(\"error\", reject);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new FileTransferError(\"connection timeout\"));\n });\n });\n}\n\n/** Upload data via a TS3 file transfer connection. */\nexport async function uploadFileData(\n host: string,\n info: FileUploadInfo,\n data: Readable,\n): Promise<void> {\n const socket = await dialFileTransfer(host, info.port, info.fileTransferKey);\n await new Promise<void>((resolve, reject) => {\n data.pipe(socket);\n socket.on(\"finish\", resolve);\n socket.on(\"error\", reject);\n });\n}\n\n/** Download data via a TS3 file transfer connection. */\nexport async function downloadFileData(\n host: string,\n info: FileDownloadInfo,\n dest: Writable,\n): Promise<void> {\n const socket = await dialFileTransfer(host, info.port, info.fileTransferKey);\n await new Promise<void>((resolve, reject) => {\n socket.pipe(dest);\n dest.on(\"finish\", resolve);\n socket.on(\"error\", reject);\n dest.on(\"error\", reject);\n });\n}\n\n/**\n * Build a ftinitupload command string.\n */\nexport function buildFtInitUpload(\n channelID: bigint,\n path: string,\n password: string,\n size: bigint,\n cftid: number,\n overwrite: boolean,\n): string {\n const targetPath = path.startsWith(\"/\") ? path : `/${path}`;\n return buildCommand(\"ftinitupload\", {\n cid: String(channelID),\n name: targetPath,\n cpw: prepareClientPassword(password),\n size: String(size),\n clientftfid: String(cftid),\n overwrite: overwrite ? \"1\" : \"0\",\n resume: \"0\",\n });\n}\n\n/**\n * Build a ftinitdownload command string.\n */\nexport function buildFtInitDownload(\n channelID: bigint,\n path: string,\n password: string,\n cftid: number,\n): string {\n const targetPath = path.startsWith(\"/\") ? path : `/${path}`;\n return buildCommand(\"ftinitdownload\", {\n cid: String(channelID),\n name: targetPath,\n cpw: prepareClientPassword(password),\n clientftfid: String(cftid),\n seekpos: \"0\",\n });\n}\n\nexport { FileTransferError, FileTransferTimeoutError };\n","import type { EventMap, CommandMiddleware, EventMiddleware } from \"./types.js\";\n\ntype EventHandler<K extends keyof EventMap> = EventMap[K] extends void\n ? () => void\n : (payload: EventMap[K]) => void;\n\nexport type { EventHandler };\n\n/**\n * Compose a chain of middlewares around a base handler.\n * Rightmost middleware wraps the base first.\n */\nexport function buildCommandChain(\n middlewares: CommandMiddleware[],\n base: (cmd: string) => Promise<void>,\n): (cmd: string) => Promise<void> {\n let handler = base;\n for (let i = middlewares.length - 1; i >= 0; i--) {\n handler = middlewares[i]!(handler);\n }\n return handler;\n}\n\nexport function buildEventChain(\n middlewares: EventMiddleware[],\n base: (evt: EventMap[keyof EventMap]) => void,\n): (evt: EventMap[keyof EventMap]) => void {\n let handler = base;\n for (let i = middlewares.length - 1; i >= 0; i--) {\n handler = middlewares[i]!(handler);\n }\n return handler;\n}\n","export function parseUint64(s: string): bigint {\n if (s === \"\" || s === undefined) return 0n;\n try {\n return BigInt(s);\n } catch {\n return 0n;\n }\n}\n\nexport function parseUint16(s: string): number {\n const v = parseInt(s, 10);\n if (isNaN(v) || v < 0 || v > 65535) return 0;\n return v;\n}\n\nexport function parseInt10(s: string): number {\n const v = parseInt(s, 10);\n return isNaN(v) ? 0 : v;\n}\n\n/**\n * Reports whether `actual` equals `expected` or equals `expected` followed by\n * only digits — the pattern TeamSpeak uses when a nickname is already taken.\n */\nexport function isAutoNicknameMatch(expected: string, actual: string): boolean {\n if (actual === expected) return true;\n if (!actual.startsWith(expected)) return false;\n const suffix = actual.slice(expected.length);\n return /^\\d+$/.test(suffix);\n}\n\n/**\n * Expand a pipe-separated multi-row TS3 command line into individual rows,\n * each prefixed with the command name.\n */\nexport function splitCommandRows(line: string): string[] {\n const spaceIdx = line.indexOf(\" \");\n if (spaceIdx < 0) return [line];\n\n const name = line.slice(0, spaceIdx);\n const rest = line.slice(spaceIdx + 1);\n\n if (!rest.includes(\"|\")) return [line];\n\n const parts = rest.split(\"|\");\n const rows: string[] = [];\n let inheritedFields = new Map<string, string>();\n\n for (const part of parts) {\n if (part === \"\") continue;\n\n // TeamSpeak compresses pipe-separated rows by omitting fields whose\n // values are unchanged from the previous row. This is especially\n // important for notifycliententerview, where ctid is commonly present\n // only on the first row of a batch. Keep the raw escaped tokens so the\n // command parser can still perform the single unescape pass later.\n const tokens = part.split(\" \").filter(Boolean);\n const currentFields = new Set<string>();\n for (const token of tokens) currentFields.add(fieldName(token));\n\n const inheritedTokens: string[] = [];\n for (const [field, token] of inheritedFields) {\n if (!currentFields.has(field)) inheritedTokens.push(token);\n }\n\n const resolvedTokens = [...inheritedTokens, ...tokens];\n rows.push(`${name} ${resolvedTokens.join(\" \")}`);\n\n inheritedFields = new Map<string, string>();\n for (const token of resolvedTokens) {\n inheritedFields.set(fieldName(token), token);\n }\n }\n return rows.length === 0 ? [line] : rows;\n}\n\nfunction fieldName(token: string): string {\n const equalIndex = token.indexOf(\"=\");\n return equalIndex > 0 ? token.slice(0, equalIndex) : token;\n}\n","import type { Command } from \"./command/command.js\";\nimport { unescape } from \"./command/command.js\";\nimport type {\n ClientInfo,\n ClientLeftViewEvent,\n ClientMovedEvent,\n TextMessage,\n PokeEvent,\n FileUploadInfo,\n FileDownloadInfo,\n FileTransferStatusInfo,\n} from \"./types.js\";\nimport { parseUint64, parseUint16, parseInt10 } from \"./helpers.js\";\n\nexport type NotificationResult =\n | { kind: \"clientEnter\"; info: ClientInfo }\n | { kind: \"clientLeave\"; event: ClientLeftViewEvent; isSelf: boolean }\n | { kind: \"clientMoved\"; event: ClientMovedEvent }\n | { kind: \"textMessage\"; message: TextMessage }\n | { kind: \"poked\"; event: PokeEvent }\n | { kind: \"startUpload\"; info: FileUploadInfo }\n | { kind: \"startDownload\"; info: FileDownloadInfo }\n | { kind: \"fileTransferStatus\"; info: FileTransferStatusInfo }\n | { kind: \"unknown\" };\n\nexport function handleNotification(\n cmd: Command,\n selfCLID: number,\n clients: Map<number, ClientInfo>,\n nickname: string,\n): NotificationResult {\n switch (cmd.name) {\n case \"notifycliententerview\":\n return handleClientEnterView(cmd, clients, nickname);\n case \"notifyclientleftview\":\n return handleClientLeftView(cmd, selfCLID, clients);\n case \"notifyclientmoved\":\n return handleClientMoved(cmd, clients);\n case \"notifytextmessage\":\n return handleTextMessage(cmd, clients);\n case \"notifyclientpoke\":\n return handleClientPoked(cmd);\n case \"notifystartupload\":\n return { kind: \"startUpload\", info: handleStartUpload(cmd) };\n case \"notifystartdownload\":\n return { kind: \"startDownload\", info: handleStartDownload(cmd) };\n case \"notifystatusfiletransfer\":\n return { kind: \"fileTransferStatus\", info: handleFileTransferStatus(cmd) };\n default:\n return { kind: \"unknown\" };\n }\n}\n\nfunction handleClientEnterView(\n cmd: Command,\n clients: Map<number, ClientInfo>,\n _nickname: string,\n): NotificationResult {\n const clid = parseUint16(cmd.params[\"clid\"] ?? \"\");\n const ctid = parseUint64(cmd.params[\"ctid\"] ?? \"\");\n const clientType = parseInt10(cmd.params[\"client_type\"] ?? \"\");\n const groupsStr = cmd.params[\"client_servergroups\"] ?? \"\";\n\n const info: ClientInfo = {\n id: clid,\n nickname: cmd.params[\"client_nickname\"] ?? \"\",\n uid: cmd.params[\"client_unique_identifier\"] ?? \"\",\n channelID: ctid,\n type: clientType,\n serverGroups: groupsStr ? groupsStr.split(\",\") : [],\n };\n\n if (clid !== 0) {\n clients.set(clid, info);\n }\n\n return { kind: \"clientEnter\", info };\n}\n\nfunction handleClientLeftView(\n cmd: Command,\n selfCLID: number,\n clients: Map<number, ClientInfo>,\n): NotificationResult {\n const clid = parseUint16(cmd.params[\"clid\"] ?? \"\");\n const reasonID = parseInt10(cmd.params[\"reasonid\"] ?? \"\");\n\n const isSelf = clid === selfCLID;\n if (clid !== 0) clients.delete(clid);\n\n return {\n kind: \"clientLeave\",\n event: {\n id: clid,\n reasonID,\n reasonMsg: cmd.params[\"reasonmsg\"] ?? \"\",\n targetID: parseUint16(cmd.params[\"targetid\"] ?? \"\"),\n },\n isSelf,\n };\n}\n\nfunction handleClientMoved(cmd: Command, clients: Map<number, ClientInfo>): NotificationResult {\n const clid = parseUint16(cmd.params[\"clid\"] ?? \"\");\n const ctid = parseUint64(cmd.params[\"ctid\"] ?? \"\");\n\n if (clid !== 0) {\n const existing = clients.get(clid);\n if (existing) clients.set(clid, { ...existing, channelID: ctid });\n }\n\n return {\n kind: \"clientMoved\",\n event: {\n id: clid,\n targetChannelID: ctid,\n reasonID: parseInt10(cmd.params[\"reasonid\"] ?? \"\"),\n invokerID: parseUint16(cmd.params[\"invokerid\"] ?? \"\"),\n invokerName: cmd.params[\"invokername\"] ?? \"\",\n invokerUID: cmd.params[\"invokeruid\"] ?? \"\",\n },\n };\n}\n\nfunction handleTextMessage(cmd: Command, clients: Map<number, ClientInfo>): NotificationResult {\n const invokerID = parseUint16(cmd.params[\"invokerid\"] ?? \"\");\n const invokerInfo = clients.get(invokerID);\n\n const message: TextMessage = {\n targetMode: parseInt10(cmd.params[\"targetmode\"] ?? \"\"),\n targetID: parseUint64(cmd.params[\"target\"] ?? \"\"),\n invokerID,\n invokerName: cmd.params[\"invokername\"] ?? \"\",\n invokerUID: cmd.params[\"invokeruid\"] ?? invokerInfo?.uid ?? \"\",\n message: unescape(cmd.params[\"msg\"] ?? \"\"),\n invokerGroups: invokerInfo?.serverGroups ?? [],\n };\n\n return { kind: \"textMessage\", message };\n}\n\nfunction handleStartUpload(cmd: Command): FileUploadInfo {\n return {\n clientFileTransferID: parseUint16(cmd.params[\"clientftfid\"] ?? \"\"),\n serverFileTransferID: parseUint16(cmd.params[\"serverftfid\"] ?? \"\"),\n fileTransferKey: cmd.params[\"ftkey\"] ?? \"\",\n port: parseUint16(cmd.params[\"port\"] ?? \"\"),\n seekPosition: parseUint64(cmd.params[\"seekpos\"] ?? \"\"),\n };\n}\n\nfunction handleStartDownload(cmd: Command): FileDownloadInfo {\n return {\n clientFileTransferID: parseUint16(cmd.params[\"clientftfid\"] ?? \"\"),\n serverFileTransferID: parseUint16(cmd.params[\"serverftfid\"] ?? \"\"),\n fileTransferKey: cmd.params[\"ftkey\"] ?? \"\",\n port: parseUint16(cmd.params[\"port\"] ?? \"\"),\n size: parseUint64(cmd.params[\"size\"] ?? \"\"),\n };\n}\n\nfunction handleFileTransferStatus(cmd: Command): FileTransferStatusInfo {\n return {\n clientFileTransferID: parseUint16(cmd.params[\"clientftfid\"] ?? \"\"),\n status: parseInt10(cmd.params[\"status\"] ?? \"\"),\n message: cmd.params[\"msg\"] ?? \"\",\n };\n}\n\nfunction handleClientPoked(cmd: Command): NotificationResult {\n return {\n kind: \"poked\",\n event: {\n invokerID: parseUint16(cmd.params[\"invokerid\"] ?? \"\"),\n invokerName: unescape(cmd.params[\"invokername\"] ?? \"\"),\n invokerUID: cmd.params[\"invokeruid\"] ?? \"\",\n message: unescape(cmd.params[\"msg\"] ?? \"\"),\n },\n };\n}\n","/**\n * Token-bucket limiter for outbound TS3 commands.\n * Mirrors Go's commandThrottle.\n */\nexport class CommandThrottle {\n static readonly TOKEN_RATE = 4.0; // tokens per second\n static readonly TOKEN_MAX = 8.0; // bucket capacity\n\n #tokens = 5.0;\n #lastUpdate = Date.now();\n\n async wait(signal?: AbortSignal): Promise<void> {\n while (true) {\n if (signal?.aborted) throw signal.reason as Error;\n\n const now = Date.now();\n const elapsed = (now - this.#lastUpdate) / 1000;\n this.#tokens = Math.min(\n this.#tokens + elapsed * CommandThrottle.TOKEN_RATE,\n CommandThrottle.TOKEN_MAX,\n );\n this.#lastUpdate = now;\n\n if (this.#tokens >= 1.0) {\n this.#tokens -= 1.0;\n return;\n }\n\n const waitMs = Math.ceil(((1.0 - this.#tokens) / CommandThrottle.TOKEN_RATE) * 1000) + 10;\n\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, waitMs);\n if (signal) {\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer);\n reject(signal.reason as Error);\n },\n { once: true },\n );\n }\n });\n }\n }\n}\n","import {\n type EventMap,\n type ClientOptions,\n type CommandMiddleware,\n type EventMiddleware,\n type Logger,\n type AddrResolver,\n type ClientInfo,\n ClientStatus,\n consoleLogger,\n} from \"./types.js\";\nimport { AlreadyConnectedError } from \"./errors.js\";\nimport { Identity, Crypt, getUidFromPublicKey, type UidAlgorithm } from \"./crypto/index.js\";\nimport { Resolver } from \"./discovery/resolver.js\";\nimport { PacketHandler } from \"./transport/handler.js\";\nimport { PacketType } from \"./transport/packet.js\";\nimport type { Packet } from \"./transport/packet.js\";\nimport { CommandTracker, appendReturnCode, parseServerError } from \"./commands.js\";\nimport {\n FileTransferTracker,\n buildFtInitUpload,\n buildFtInitDownload,\n uploadFileData,\n downloadFileData,\n} from \"./transfer.js\";\nimport { buildCommandChain, buildEventChain } from \"./events.js\";\nimport { processInit1 } from \"./handshake/crypt-handshake.js\";\nimport { handleHandshakeInitIV, handleHandshakeExpand2, handleInitServer } from \"./handshake.js\";\nimport { handleNotification } from \"./notifications.js\";\nimport { parseCommand } from \"./command/parser.js\";\nimport { CommandThrottle } from \"./throttle.js\";\nimport { splitCommandRows, isAutoNicknameMatch } from \"./helpers.js\";\nimport type { FileUploadInfo, FileDownloadInfo } from \"./types.js\";\nimport { CommandTimeoutError, FileTransferTimeoutError, FileTransferError } from \"./errors.js\";\nimport type { Readable, Writable } from \"node:stream\";\nimport type { SocketFactory } from \"./types.js\";\n\nexport { ClientStatus };\n\nexport interface ClientState {\n status: ClientStatus;\n clid: number;\n}\n\ninterface ClientInitOptions {\n readonly serverPassword: string;\n readonly defaultChannel: string;\n readonly defaultChannelPassword: string;\n}\n\nfunction normalizeClientInitOptions(options: ClientOptions): ClientInitOptions {\n return {\n serverPassword: options.serverPassword ?? \"\",\n defaultChannel: options.defaultChannel ?? \"\",\n defaultChannelPassword: options.defaultChannelPassword ?? \"\",\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyHandler = (arg: any) => void;\n\nexport class Client {\n // Internal — accessible from handshake.ts helpers\n /** @internal */ crypt: Crypt;\n /** @internal */ handler: PacketHandler;\n /** @internal */ logger: Logger;\n /** @internal */ nickname: string;\n /** @internal */ clid = 0;\n /**\n * The UID hash the connected server uses, learned from `virtualserver_version`\n * during initserver. TS3 hashes the public key with SHA-1 and TS6 with\n * SHA-256, so anything comparing our UID to the server's needs this.\n */\n /** @internal */ uidAlgorithm: UidAlgorithm = \"sha1\";\n\n #identity: Identity;\n #addr: string;\n #resolver: AddrResolver;\n #clientInitOptions: ClientInitOptions;\n #socketFactory: SocketFactory | undefined;\n #status: ClientStatus = ClientStatus.Disconnected;\n #throttle = new CommandThrottle();\n #cmdTrack = new CommandTracker();\n #ftTrack = new FileTransferTracker();\n #clients = new Map<number, ClientInfo>();\n #connectedResolvers: Array<{ resolve: () => void; reject: (error: Error) => void }> = [];\n #connectionError: Error | null = null;\n\n // Event handler lists\n #textMsgHandlers: Array<(msg: import(\"./types.js\").TextMessage) => void> = [];\n #clientEnterHandlers: Array<(info: ClientInfo) => void> = [];\n #clientLeaveHandlers: Array<(evt: import(\"./types.js\").ClientLeftViewEvent) => void> = [];\n #clientMoveHandlers: Array<(evt: import(\"./types.js\").ClientMovedEvent) => void> = [];\n #pokedHandlers: Array<(evt: import(\"./types.js\").PokeEvent) => void> = [];\n #voiceDataHandlers: Array<(data: import(\"./types.js\").VoiceData) => void> = [];\n #connectedHandlers: Array<() => void> = [];\n #disconnectedHandlers: Array<(err: Error | undefined) => void> = [];\n #kickedHandlers: Array<(msg: string) => void> = [];\n\n // Middleware\n #cmdMiddlewares: CommandMiddleware[] = [];\n #eventMiddlewares: EventMiddleware[] = [];\n #finalCmdHandler: (cmd: string) => Promise<void>;\n #finalEvtHandler: (evt: EventMap[keyof EventMap]) => void;\n\n constructor(identity: Identity, addr: string, nickname: string, options: ClientOptions = {}) {\n this.#identity = identity;\n this.#addr = addr;\n this.nickname = nickname;\n this.logger = options.logger ?? consoleLogger;\n this.#resolver = options.resolver ?? new Resolver(this.logger);\n this.#clientInitOptions = normalizeClientInitOptions(options);\n this.#socketFactory = options.socketFactory;\n\n this.crypt = new Crypt(identity);\n this.handler = new PacketHandler(this.crypt, this.logger, this.#socketFactory);\n this.handler.onPacket = (p) => this.#handlePacket(p);\n this.handler.onClosed = (err) => this.#handleConnectionClosed(err);\n\n if (options.commandMiddleware) {\n this.#cmdMiddlewares.push(...options.commandMiddleware);\n }\n if (options.eventMiddleware) {\n this.#eventMiddlewares.push(...options.eventMiddleware);\n }\n\n this.#finalCmdHandler = this.#buildCmdHandler();\n this.#finalEvtHandler = this.#buildEvtHandler();\n }\n\n get status(): ClientStatus {\n return this.#status;\n }\n\n /** @internal */\n getClientInitOptions(): Readonly<{\n serverPassword: string;\n defaultChannel: string;\n defaultChannelPassword: string;\n }> {\n return this.#clientInitOptions;\n }\n\n // ---- Connection -----------------------------------------------------------\n\n async connect(): Promise<void> {\n if (this.#status !== ClientStatus.Disconnected) {\n throw new AlreadyConnectedError();\n }\n\n this.#resetForConnect();\n this.#status = ClientStatus.Connecting;\n\n const targetAddr = await this.#resolveAddr();\n this.logger.info(\"connecting to server\", { address: targetAddr });\n await this.handler.connect(targetAddr);\n }\n\n async disconnect(): Promise<void> {\n if (this.#status === ClientStatus.Disconnected) return;\n\n const wasConnected = this.#status === ClientStatus.Connected;\n this.#status = ClientStatus.Disconnected;\n\n this.logger.info(\"disconnecting from server\");\n\n if (wasConnected) {\n try {\n await this.execCommand(\"clientdisconnect reasonmsg=Shutdown\", 1000);\n } catch {\n // best-effort\n }\n }\n\n this.handler.close();\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(undefined));\n }\n\n waitConnected(signal?: AbortSignal): Promise<void> {\n if (this.#status === ClientStatus.Connected) return Promise.resolve();\n if (this.#connectionError) return Promise.reject(this.#connectionError);\n return new Promise<void>((resolve, reject) => {\n const waiter = {\n resolve: () => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n },\n reject: (error: Error) => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(error);\n },\n };\n const onAbort = (): void => {\n const index = this.#connectedResolvers.indexOf(waiter);\n if (index >= 0) this.#connectedResolvers.splice(index, 1);\n const reason: unknown = signal?.reason;\n waiter.reject(\n reason instanceof Error ? reason : new Error(String(reason ?? \"Connection wait aborted\")),\n );\n };\n this.#connectedResolvers.push(waiter);\n if (signal?.aborted) onAbort();\n else signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n\n // ---- Commands ------------------------------------------------------------\n\n async sendCommandNoWait(cmd: string): Promise<void> {\n await this.#throttle.wait();\n await this.#finalCmdHandler(cmd);\n }\n\n async execCommand(cmd: string, timeoutMs = 10_000): Promise<void> {\n await this.execCommandWithResponse(cmd, timeoutMs);\n }\n\n async execCommandWithResponse(\n cmd: string,\n timeoutMs = 10_000,\n ): Promise<Record<string, string>[]> {\n const [rc, promise] = this.#cmdTrack.register();\n const withRc = appendReturnCode(cmd, rc);\n\n try {\n await this.#throttle.wait();\n await this.#finalCmdHandler(withRc);\n } catch (err) {\n this.#cmdTrack.unregister(rc);\n throw err;\n }\n\n const result = await Promise.race([\n promise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new CommandTimeoutError(cmd)), timeoutMs),\n ),\n ]);\n\n this.#cmdTrack.unregister(rc);\n\n if (result.err) throw result.err;\n return result.data;\n }\n\n // ---- Events --------------------------------------------------------------\n\n on<K extends keyof EventMap>(\n event: K,\n handler: EventMap[K] extends void ? () => void : (payload: EventMap[K]) => void,\n ): this {\n switch (event) {\n case \"textMessage\":\n this.#textMsgHandlers.push(handler as AnyHandler);\n break;\n case \"clientEnter\":\n this.#clientEnterHandlers.push(handler as AnyHandler);\n break;\n case \"clientLeave\":\n this.#clientLeaveHandlers.push(handler as AnyHandler);\n break;\n case \"clientMoved\":\n this.#clientMoveHandlers.push(handler as AnyHandler);\n break;\n case \"poked\":\n this.#pokedHandlers.push(handler as AnyHandler);\n break;\n case \"voiceData\":\n this.#voiceDataHandlers.push(handler as AnyHandler);\n break;\n case \"connected\":\n this.#connectedHandlers.push(handler as () => void);\n break;\n case \"disconnected\":\n this.#disconnectedHandlers.push(handler as AnyHandler);\n break;\n case \"kicked\":\n this.#kickedHandlers.push(handler as AnyHandler);\n break;\n }\n return this;\n }\n\n useCommandMiddleware(...mw: CommandMiddleware[]): this {\n this.#cmdMiddlewares.push(...mw);\n this.#finalCmdHandler = this.#buildCmdHandler();\n return this;\n }\n\n useEventMiddleware(...mw: EventMiddleware[]): this {\n this.#eventMiddlewares.push(...mw);\n this.#finalEvtHandler = this.#buildEvtHandler();\n return this;\n }\n\n // ---- API shorthand -------------------------------------------------------\n\n clientID(): number {\n return this.clid;\n }\n\n /**\n * Our own `client_unique_identifier`, hashed with the algorithm the connected\n * server uses.\n *\n * TS3 uses SHA-1 and TS6 SHA-256, so this reads the algorithm learned during\n * initserver rather than assuming one. Comparing a hard-coded SHA-1 UID to a\n * TS6 server silently never matches.\n */\n uniqueIdentifier(): string {\n return getUidFromPublicKey(this.#identity.publicKeyBase64(), this.uidAlgorithm);\n }\n\n channelID(): bigint {\n const info = this.#clients.get(this.clid);\n return info?.channelID ?? 0n;\n }\n\n /** Send Opus voice directly to specific clients, bypassing channel routing. */\n sendWhisper(data: Uint8Array, targetClientIds: number[], codec: number): void {\n this.handler.sendWhisperPacket(data, targetClientIds, codec);\n }\n\n sendVoice(data: Uint8Array, codec: number): void {\n this.handler.sendVoicePacket(data, codec);\n }\n\n // ---- File Transfer -------------------------------------------------------\n\n async fileTransferInitUpload(\n channelID: bigint,\n path: string,\n password: string,\n size: bigint,\n overwrite = false,\n ): Promise<FileUploadInfo> {\n const [cftid, ftPromise] = this.#ftTrack.register();\n const cmd = buildFtInitUpload(channelID, path, password, size, cftid, overwrite);\n\n try {\n await this.execCommand(cmd, 10_000);\n } catch (err) {\n this.#ftTrack.unregister(cftid);\n throw err;\n }\n\n const result = await Promise.race([\n ftPromise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new FileTransferTimeoutError()), 10_000),\n ),\n ]);\n this.#ftTrack.unregister(cftid);\n\n if (\"size\" in result) throw new FileTransferError(\"unexpected download response\");\n if (\"status\" in result) {\n const st = result as import(\"./types.js\").FileTransferStatusInfo;\n throw new FileTransferError(`${st.message} (status=${st.status})`);\n }\n return result as FileUploadInfo;\n }\n\n async fileTransferInitDownload(\n channelID: bigint,\n path: string,\n password: string,\n ): Promise<FileDownloadInfo> {\n const [cftid, ftPromise] = this.#ftTrack.register();\n const cmd = buildFtInitDownload(channelID, path, password, cftid);\n\n try {\n await this.execCommand(cmd, 10_000);\n } catch (err) {\n this.#ftTrack.unregister(cftid);\n throw err;\n }\n\n const result = await Promise.race([\n ftPromise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new FileTransferTimeoutError()), 10_000),\n ),\n ]);\n this.#ftTrack.unregister(cftid);\n\n if (\"seekPosition\" in result) throw new FileTransferError(\"unexpected upload response\");\n if (\"status\" in result) {\n const st = result as import(\"./types.js\").FileTransferStatusInfo;\n throw new FileTransferError(`${st.message} (status=${st.status})`);\n }\n return result as FileDownloadInfo;\n }\n\n uploadFileData(host: string, info: FileUploadInfo, data: Readable): Promise<void> {\n return uploadFileData(host, info, data);\n }\n\n downloadFileData(host: string, info: FileDownloadInfo, dest: Writable): Promise<void> {\n return downloadFileData(host, info, dest);\n }\n\n // ---- Internal (package-visible) ------------------------------------------\n\n /** @internal */\n _markConnected(): void {\n this.#status = ClientStatus.Connected;\n this.#connectionError = null;\n for (const waiter of this.#connectedResolvers) waiter.resolve();\n this.#connectedResolvers = [];\n const handlers = this.#connectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h());\n }\n\n // ---- Private -------------------------------------------------------------\n\n #resetForConnect(): void {\n // Waiters are deliberately left in place: connect() resets first, so a\n // caller that registered waitConnected() before calling connect() must\n // still be resolved by the handshake that follows. Clear only the error\n // carried over from a previous attempt.\n this.#connectionError = null;\n this.handler.close();\n this.crypt = new Crypt(this.#identity);\n this.handler = new PacketHandler(this.crypt, this.logger, this.#socketFactory);\n this.handler.onPacket = (p) => this.#handlePacket(p);\n this.handler.onClosed = (err) => this.#handleConnectionClosed(err);\n this.#cmdTrack.reset();\n this.#ftTrack.reset();\n this.#clients.clear();\n this.clid = 0;\n this.#finalCmdHandler = this.#buildCmdHandler();\n }\n\n async #resolveAddr(): Promise<string> {\n const addrWithPort = this.#addr.includes(\":\") ? this.#addr : `${this.#addr}:9987`;\n try {\n const resolved = await this.#resolver.resolve(this.#addr);\n return resolved[0]?.addr ?? addrWithPort;\n } catch {\n return addrWithPort;\n }\n }\n\n #handlePacket(p: Packet): void {\n this.#handlePacketSync(p);\n }\n\n #handlePacketSync(p: Packet): void {\n const pType = p.typeFlagged & 0x0f;\n if (pType === 8 /* Init1 */) {\n const response = processInit1(this.crypt, p.data);\n if (response) {\n this.handler.sendPacket(PacketType.Init1, response, 0);\n }\n return;\n }\n\n if ((pType === 0 /* Voice */ || pType === 1) /* VoiceWhisper */ && p.data.length > 5) {\n this.#handleVoicePacket(p.data);\n return;\n }\n\n if ((pType === 2 /* Command */ || pType === 3) /* CommandLow */ && p.data.length > 0) {\n const text = Buffer.from(p.data).toString(\"utf8\");\n if (process.env[\"TEAMSPEAK_TRACE_RAW\"]) process.stderr.write(`S2C ${JSON.stringify(text)}\\n`);\n this.#handleCommandLines(text);\n }\n }\n\n /**\n * Parse an incoming S2C voice packet.\n * Format: [VId: u16] [CId: u16] [Codec: u8] [Data: var]\n */\n #handleVoicePacket(payload: Uint8Array): void {\n if (this.#voiceDataHandlers.length === 0) return;\n\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);\n const clientId = view.getUint16(2, false);\n if (clientId === this.clid) return;\n\n const codec = payload[4]!;\n const data = payload.subarray(5);\n\n const voiceData: import(\"./types.js\").VoiceData = { clientId, codec, data };\n for (const h of this.#voiceDataHandlers) setImmediate(() => h(voiceData));\n }\n\n #handleCommandLines(s: string): void {\n if (!s) return;\n const lines = s.split(\"\\0\").join(\"\\n\").split(\"\\n\");\n for (const line of lines) {\n const trimmed = line.replace(/\\r$/, \"\");\n if (!trimmed) continue;\n for (const row of splitCommandRows(trimmed)) {\n this.#handleCommandStr(row);\n }\n }\n }\n\n #handleCommandStr(s: string): void {\n const cmd = parseCommand(s);\n if (!cmd) return;\n\n // No name means a response row: the server sends data without echoing the\n // command, so the row belongs to whichever command is awaiting a result.\n if (cmd.name === \"\") {\n if (Object.keys(cmd.params).length > 0) this.#cmdTrack.buffer(cmd.params);\n return;\n }\n\n // `channellistfinished` ends the server's welcome sequence. It carries no\n // data of its own, so it is the only reliable marker: the guard used to\n // trip on our own notifycliententerview, which the server sends *in the\n // middle* of the sequence — the channellist rows after it were then\n // buffered as if they were the response to the caller's first command,\n // which is how a channellist could come back with duplicate and empty rows.\n if (cmd.name === \"channellistfinished\") {\n this.#cmdTrack.signalWelcomeComplete();\n return;\n }\n\n if (cmd.name.startsWith(\"notify\")) {\n const result = handleNotification(cmd, this.clid, this.#clients, this.nickname);\n this.#processNotificationResult(result, cmd.params);\n return;\n }\n\n switch (cmd.name) {\n case \"clientinitiv\":\n handleHandshakeInitIV(this, cmd.params);\n break;\n case \"initivexpand2\":\n handleHandshakeExpand2(this, cmd.params);\n break;\n case \"initserver\":\n handleInitServer(this, cmd.params);\n break;\n case \"error\":\n this.#handleError(cmd.params);\n break;\n default:\n // A command the SDK does not model, carrying its own data rows.\n this.#cmdTrack.buffer(cmd.params);\n break;\n }\n }\n\n #handleError(params: Record<string, string>): void {\n const { err, rc } = parseServerError(params);\n if (rc !== null) {\n this.#cmdTrack.resolve(rc, err);\n } else {\n // No return_code: server is acknowledging an unsolicited command\n // (welcome-sequence channellist, no-RC commands like clientupdate, etc.).\n // Discard any rows buffered since the last resolved command.\n this.#cmdTrack.discardBuffer();\n }\n\n // A server error before the welcome sequence finished is the connection\n // failure itself — a rejected password, or an invalid clientinit parameter.\n // Report it as the cause instead of leaving the caller with a bare timeout.\n if (err && this.#status !== ClientStatus.Connected) {\n this.#failConnection(err);\n return;\n }\n\n const id = params[\"id\"] ?? \"0\";\n if (id === \"3329\") {\n setImmediate(() => this.disconnect().catch(() => {}));\n }\n }\n\n #processNotificationResult(\n result: import(\"./notifications.js\").NotificationResult,\n _params: Record<string, string>,\n ): void {\n switch (result.kind) {\n case \"clientEnter\": {\n const info = result.info;\n // Identify self by the clid initserver already assigned rather than by\n // nickname: any other client whose nickname happens to be \"<our\n // nick><digits>\" — which is how the server renames a client whose\n // nickname was taken — would otherwise be mistaken for us, poisoning\n // every outgoing packet header with a foreign clientID until the server\n // drops the connection for command-resend timeout.\n //\n // This no longer marks the end of the welcome sequence: the server\n // sends more welcome data after it, and `channellistfinished` is the\n // real terminator.\n if (info.id !== 0 && info.id === this.clid) {\n // Carries the rename the server applied if our nickname was taken.\n this.nickname = info.nickname;\n } else if (\n info.id !== 0 &&\n this.clid === 0 &&\n isAutoNicknameMatch(this.nickname, info.nickname)\n ) {\n // initserver carried no aclid, so the welcome enter is the only way\n // to learn our clid. Gated on clid still being unknown so a later\n // lookalike can never hijack an established session.\n this.clid = info.id;\n this.handler.setClientID(info.id);\n this.nickname = info.nickname;\n }\n this.#dispatchEvent(\"clientEnter\", info);\n break;\n }\n case \"clientLeave\": {\n this.#dispatchEvent(\"clientLeave\", result.event);\n if (result.isSelf && (result.event.reasonID === 4 || result.event.reasonID === 5)) {\n const msg = result.event.reasonMsg;\n for (const h of this.#kickedHandlers) setImmediate(() => h(msg));\n }\n break;\n }\n case \"clientMoved\":\n this.#dispatchEvent(\"clientMoved\", result.event);\n break;\n case \"textMessage\":\n this.#dispatchEvent(\"textMessage\", result.message);\n break;\n case \"poked\":\n this.#dispatchEvent(\"poked\", result.event);\n break;\n case \"startUpload\":\n this.#ftTrack.notify(result.info.clientFileTransferID, result.info);\n break;\n case \"startDownload\":\n this.#ftTrack.notify(result.info.clientFileTransferID, result.info);\n break;\n case \"fileTransferStatus\":\n this.#ftTrack.notify(result.info.clientFileTransferID, result.info);\n break;\n }\n }\n\n #dispatchEvent<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n this.#finalEvtHandler(payload as EventMap[keyof EventMap]);\n }\n\n #dispatchEventDirect<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n switch (event) {\n case \"textMessage\":\n for (const h of this.#textMsgHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").TextMessage));\n break;\n case \"clientEnter\":\n for (const h of this.#clientEnterHandlers) setImmediate(() => h(payload as ClientInfo));\n break;\n case \"clientLeave\":\n for (const h of this.#clientLeaveHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").ClientLeftViewEvent));\n break;\n case \"clientMoved\":\n for (const h of this.#clientMoveHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").ClientMovedEvent));\n break;\n case \"poked\":\n for (const h of this.#pokedHandlers)\n setImmediate(() => h(payload as import(\"./types.js\").PokeEvent));\n break;\n }\n }\n\n #handleConnectionClosed(err: Error | null): void {\n if (this.#status === ClientStatus.Disconnected) return;\n if (this.#status !== ClientStatus.Connected) {\n // The socket closed before the handshake finished, so this is a failed\n // connection attempt rather than a disconnect of a live session.\n this.#failConnection(\n err ?? new Error(\"Connection closed before the TeamSpeak handshake completed\"),\n );\n return;\n }\n this.#status = ClientStatus.Disconnected;\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(err ?? undefined));\n }\n\n /**\n * A connection attempt has failed for good. Record why, release anyone\n * waiting on `waitConnected`, and tear the connection down.\n *\n * Without this, a handshake the server rejects (a wrong password, say, which\n * the server answers with an error instead of closing the socket) surfaces\n * only as the caller's own timeout, and the client stays in `Connecting`\n * forever without ever emitting `disconnected`.\n */\n #failConnection(error: Error): void {\n this.#connectionError = error;\n const waiters = this.#connectedResolvers.splice(0);\n for (const waiter of waiters) waiter.reject(error);\n\n if (this.#status === ClientStatus.Disconnected) return;\n this.#status = ClientStatus.Disconnected;\n this.handler.close();\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(error));\n }\n\n #buildCmdHandler(): (cmd: string) => Promise<void> {\n const base = async (cmd: string): Promise<void> => {\n this.handler.sendPacket(PacketType.Command, Buffer.from(cmd), 0);\n };\n return buildCommandChain(this.#cmdMiddlewares, base);\n }\n\n #buildEvtHandler(): (evt: EventMap[keyof EventMap]) => void {\n const base = (evt: EventMap[keyof EventMap]): void => {\n // Determine which event type this is by checking the shape\n if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"invokerName\" in evt &&\n \"message\" in evt &&\n \"targetMode\" in evt\n ) {\n this.#dispatchEventDirect(\"textMessage\", evt as EventMap[\"textMessage\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"invokerName\" in evt &&\n \"message\" in evt &&\n !(\"targetMode\" in evt)\n ) {\n this.#dispatchEventDirect(\"poked\", evt as EventMap[\"poked\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"id\" in evt &&\n \"uid\" in evt\n ) {\n this.#dispatchEventDirect(\"clientEnter\", evt as EventMap[\"clientEnter\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"id\" in evt &&\n \"reasonID\" in evt &&\n \"targetChannelID\" in evt\n ) {\n this.#dispatchEventDirect(\"clientMoved\", evt as EventMap[\"clientMoved\"]);\n } else if (\n evt !== null &&\n evt !== undefined &&\n typeof evt === \"object\" &&\n \"id\" in evt &&\n \"reasonID\" in evt\n ) {\n this.#dispatchEventDirect(\"clientLeave\", evt as EventMap[\"clientLeave\"]);\n }\n };\n return buildEventChain(this.#eventMiddlewares, base);\n }\n}\n","import { buildCommandOrdered, buildCommand, unescape } from \"./command/command.js\";\nimport { prepareClientPassword } from \"./handshake.js\";\nimport type { ChannelInfo, ClientInfo } from \"./types.js\";\nimport type { Client } from \"./client.js\";\n\n/** Send a text message to a client (targetMode=1), channel (2), or server (3). */\nexport async function sendTextMessage(\n client: Client,\n targetMode: number,\n targetID: bigint,\n message: string,\n): Promise<void> {\n const cmd = buildCommandOrdered(\"sendtextmessage\", [\n [\"targetmode\", String(targetMode)],\n [\"target\", String(targetID)],\n [\"msg\", message],\n ]);\n await client.sendCommandNoWait(cmd);\n}\n\n/** Move a client to a different channel. */\nexport async function clientMove(\n client: Client,\n clid: number,\n channelID: bigint,\n password = \"\",\n): Promise<void> {\n const params: Array<readonly [string, string]> = [\n [\"clid\", String(clid)],\n [\"cid\", String(channelID)],\n ];\n // The native TeamSpeak client protocol expects channel passwords as the\n // SHA-1 Base64 digest, just like client_default_channel_password during\n // clientinit. Sending the plain password makes valid passwords fail with\n // error 781 (invalid channel password).\n if (password) params.push([\"cpw\", prepareClientPassword(password)]);\n const cmd = buildCommandOrdered(\"clientmove\", params);\n await client.execCommand(cmd, 10_000);\n}\n\n/** Send a poke message to a client. */\nexport async function poke(client: Client, clid: number, message: string): Promise<void> {\n const cmd = buildCommandOrdered(\"clientpoke\", [\n [\"clid\", String(clid)],\n [\"msg\", message],\n ]);\n await client.execCommand(cmd, 10_000);\n}\n\n/** Fetch raw clientinfo for a given clid. */\nexport async function getClientInfo(client: Client, clid: number): Promise<Record<string, string>> {\n const data = await client.execCommandWithResponse(`clientinfo clid=${clid}`, 5_000);\n const row = data[0];\n if (!row) throw new Error(`no data returned for client ${clid}`);\n return row;\n}\n\n/** List all channels on the server. */\nexport async function listChannels(client: Client): Promise<ChannelInfo[]> {\n const data = await client.execCommandWithResponse(\"channellist\", 5_000);\n return data.map((item) => ({\n id: BigInt(item[\"cid\"] ?? \"0\"),\n // The command response uses `pid`; unsolicited server pushes use `cpid`.\n parentID: BigInt(item[\"pid\"] ?? item[\"cpid\"] ?? \"0\"),\n order: BigInt(item[\"channel_order\"] ?? \"0\"),\n name: unescape(item[\"channel_name\"] ?? \"\"),\n description: \"\",\n }));\n}\n\n/** List all clients currently connected to the server. */\nexport async function listClients(client: Client): Promise<ClientInfo[]> {\n const data = await client.execCommandWithResponse(\"clientlist -uid -away -voice -groups\", 5_000);\n return data.map((item) => {\n const groupsStr = item[\"client_servergroups\"] ?? \"\";\n return {\n id: parseInt(item[\"clid\"] ?? \"0\", 10),\n nickname: unescape(item[\"client_nickname\"] ?? \"\"),\n uid: item[\"client_unique_identifier\"] ?? \"\",\n channelID: BigInt(item[\"cid\"] ?? \"0\"),\n type: parseInt(item[\"client_type\"] ?? \"0\", 10),\n serverGroups: groupsStr ? groupsStr.split(\",\") : [],\n };\n });\n}\n\n/** Delete a file on the server. */\nexport async function fileTransferDeleteFile(\n client: Client,\n channelID: bigint,\n paths: string[],\n): Promise<void> {\n if (paths.length === 0) return;\n const pathStr = paths.join(\"|\");\n const cmd = buildCommand(\"ftdeletefile\", {\n cid: String(channelID),\n cpw: \"\",\n name: pathStr,\n });\n await client.execCommand(cmd, 10_000);\n}\n"],"mappings":"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"}
@@ -15,19 +15,19 @@ function t(t) {
15
15
  if (r.length === 0) return null;
16
16
  let i = r[0] ?? "";
17
17
  if (i === "") return null;
18
- let a = {};
19
- for (let t = 1; t < r.length; t++) {
18
+ let a = {}, o = /^[A-Za-z0-9]+$/.test(i) ? i : "", s = o === "" ? 0 : 1;
19
+ for (let t = s; t < r.length; t++) {
20
20
  let n = r[t];
21
21
  if (n === void 0 || n === "") continue;
22
22
  let i = n.indexOf("=");
23
23
  i >= 0 ? a[e(n.slice(0, i))] = e(n.slice(i + 1)) : a[e(n)] = "";
24
24
  }
25
25
  return {
26
- name: i,
26
+ name: o,
27
27
  params: a
28
28
  };
29
29
  }
30
30
  //#endregion
31
31
  export { t };
32
32
 
33
- //# sourceMappingURL=parser-DZMv3Jm-.js.map
33
+ //# sourceMappingURL=parser-BX8NEiBe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser-BX8NEiBe.js","names":["unescape","parseCommand","s","startIndex","i","code","parts","first","params","name","start","p","eqIdx"],"sources":["../src/command/parser.ts"],"sourcesContent":["import { unescape } from \"./command.js\";\nimport type { Command } from \"./command.js\";\n\nexport function parseCommand(s: string): Command | null {\n if (s === \"\") return null;\n\n // Skip leading non-printable bytes (< 0x20 or > 0x7E)\n let startIndex = 0;\n for (let i = 0; i < s.length; i++) {\n const code = s.charCodeAt(i);\n if (code >= 32 && code <= 126) {\n startIndex = i;\n break;\n }\n }\n if (startIndex > 0) {\n s = s.slice(startIndex);\n }\n\n const parts = s.split(\" \");\n if (parts.length === 0) return null;\n\n // A command name is a run of alphanumerics followed by a space — the rule\n // tsproto-packets uses (CommandParser::new). Anything else means the line is\n // not a command at all: response rows carry no name (\"clid=6 cid=2 ...\"),\n // and negative feedback is \"-1 id=2 ...\", naming the *rejected* command id\n // rather than a command of its own. Returning those as a nameless row keeps\n // the data intact instead of folding the first field into a name, which is\n // how a clientlist row used to arrive as name=\"clid=6\" and lose its clid.\n const first = parts[0] ?? \"\";\n if (first === \"\") return null;\n\n const params: Record<string, string> = {};\n const name = /^[A-Za-z0-9]+$/.test(first) ? first : \"\";\n const start = name === \"\" ? 0 : 1;\n\n for (let i = start; i < parts.length; i++) {\n const p = parts[i];\n if (p === undefined || p === \"\") continue;\n const eqIdx = p.indexOf(\"=\");\n if (eqIdx >= 0) {\n params[unescape(p.slice(0, eqIdx))] = unescape(p.slice(eqIdx + 1));\n } else {\n params[unescape(p)] = \"\";\n }\n }\n\n return { name, params };\n}\n"],"mappings":";;AAGA,SAAgBC,EAAa,GAA2B;CACtD,IAAIC,MAAM,IAAI,OAAO;CAGrB,IAAIC,IAAa;CACjB,KAAK,IAAIC,IAAI,GAAGA,IAAIF,EAAE,QAAQ,KAAK;EACjC,IAAMG,IAAOH,EAAE,WAAWE,CAAC;EAC3B,IAAIC,KAAQ,MAAMA,KAAQ,KAAK;GAC7B,IAAaD;GACb;EACF;CACF;CACA,AAAID,IAAa,MACf,IAAID,EAAE,MAAMC,CAAU;CAGxB,IAAMG,IAAQJ,EAAE,MAAM,GAAG;CACzB,IAAII,EAAM,WAAW,GAAG,OAAO;CAS/B,IAAMC,IAAQD,EAAM,MAAM;CAC1B,IAAIC,MAAU,IAAI,OAAO;CAEzB,IAAMC,IAAiC,CAAC,GAClCC,IAAO,iBAAiB,KAAKF,CAAK,IAAIA,IAAQ,IAC9CG,IAAQD,MAAS,KAAK,IAAI;CAEhC,KAAK,IAAIL,IAAIM,GAAON,IAAIE,EAAM,QAAQ,KAAK;EACzC,IAAMK,IAAIL,EAAMF;EAChB,IAAIO,MAAM,KAAA,KAAaA,MAAM,IAAI;EACjC,IAAMC,IAAQD,EAAE,QAAQ,GAAG;EAC3B,AAAIC,KAAS,IACX,EAAOZ,EAASW,EAAE,MAAM,GAAGC,CAAK,CAAC,KAAKZ,EAASW,EAAE,MAAMC,IAAQ,CAAC,CAAC,IAEjE,EAAOZ,EAASW,CAAC,KAAK;CAE1B;CAEA,OAAO;EAAE;EAAM;CAAO;AACxB"}
@@ -0,0 +1,2 @@
1
+ const e=require("./command-DAK4pUBP.cjs");function t(t){if(t===``)return null;let n=0;for(let e=0;e<t.length;e++){let r=t.charCodeAt(e);if(r>=32&&r<=126){n=e;break}}n>0&&(t=t.slice(n));let r=t.split(` `);if(r.length===0)return null;let i=r[0]??``;if(i===``)return null;let a={},o=/^[A-Za-z0-9]+$/.test(i)?i:``,s=o===``?0:1;for(let t=s;t<r.length;t++){let n=r[t];if(n===void 0||n===``)continue;let i=n.indexOf(`=`);i>=0?a[e.i(n.slice(0,i))]=e.i(n.slice(i+1)):a[e.i(n)]=``}return{name:o,params:a}}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return t}});
2
+ //# sourceMappingURL=parser-BfwYH2fT.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser-BfwYH2fT.cjs","names":["unescape","require_command","parseCommand","s","startIndex","i","code","parts","first","params","name","start","p","eqIdx"],"sources":["../src/command/parser.ts"],"sourcesContent":["import { unescape } from \"./command.js\";\nimport type { Command } from \"./command.js\";\n\nexport function parseCommand(s: string): Command | null {\n if (s === \"\") return null;\n\n // Skip leading non-printable bytes (< 0x20 or > 0x7E)\n let startIndex = 0;\n for (let i = 0; i < s.length; i++) {\n const code = s.charCodeAt(i);\n if (code >= 32 && code <= 126) {\n startIndex = i;\n break;\n }\n }\n if (startIndex > 0) {\n s = s.slice(startIndex);\n }\n\n const parts = s.split(\" \");\n if (parts.length === 0) return null;\n\n // A command name is a run of alphanumerics followed by a space — the rule\n // tsproto-packets uses (CommandParser::new). Anything else means the line is\n // not a command at all: response rows carry no name (\"clid=6 cid=2 ...\"),\n // and negative feedback is \"-1 id=2 ...\", naming the *rejected* command id\n // rather than a command of its own. Returning those as a nameless row keeps\n // the data intact instead of folding the first field into a name, which is\n // how a clientlist row used to arrive as name=\"clid=6\" and lose its clid.\n const first = parts[0] ?? \"\";\n if (first === \"\") return null;\n\n const params: Record<string, string> = {};\n const name = /^[A-Za-z0-9]+$/.test(first) ? first : \"\";\n const start = name === \"\" ? 0 : 1;\n\n for (let i = start; i < parts.length; i++) {\n const p = parts[i];\n if (p === undefined || p === \"\") continue;\n const eqIdx = p.indexOf(\"=\");\n if (eqIdx >= 0) {\n params[unescape(p.slice(0, eqIdx))] = unescape(p.slice(eqIdx + 1));\n } else {\n params[unescape(p)] = \"\";\n }\n }\n\n return { name, params };\n}\n"],"mappings":"0CAGA,SAAgBE,EAAa,EAA2B,CACtD,GAAIC,IAAM,GAAI,OAAO,KAGrB,IAAIC,EAAa,EACjB,IAAK,IAAIC,EAAI,EAAGA,EAAIF,EAAE,OAAQ,IAAK,CACjC,IAAMG,EAAOH,EAAE,WAAWE,CAAC,EAC3B,GAAIC,GAAQ,IAAMA,GAAQ,IAAK,CAC7B,EAAaD,EACb,KACF,CACF,CACID,EAAa,IACf,EAAID,EAAE,MAAMC,CAAU,GAGxB,IAAMG,EAAQJ,EAAE,MAAM,GAAG,EACzB,GAAII,EAAM,SAAW,EAAG,OAAO,KAS/B,IAAMC,EAAQD,EAAM,IAAM,GAC1B,GAAIC,IAAU,GAAI,OAAO,KAEzB,IAAMC,EAAiC,CAAC,EAClCC,EAAO,iBAAiB,KAAKF,CAAK,EAAIA,EAAQ,GAC9CG,EAAQD,IAAS,GAAK,EAAI,EAEhC,IAAK,IAAIL,EAAIM,EAAON,EAAIE,EAAM,OAAQ,IAAK,CACzC,IAAMK,EAAIL,EAAMF,GAChB,GAAIO,IAAM,IAAA,IAAaA,IAAM,GAAI,SACjC,IAAMC,EAAQD,EAAE,QAAQ,GAAG,EACvBC,GAAS,EACX,EAAOb,EAAAA,EAASY,EAAE,MAAM,EAAGC,CAAK,CAAC,GAAKb,EAAAA,EAASY,EAAE,MAAMC,EAAQ,CAAC,CAAC,EAEjE,EAAOb,EAAAA,EAASY,CAAC,GAAK,EAE1B,CAEA,MAAO,CAAE,OAAM,QAAO,CACxB"}