@honeybbq/teamspeak-client 0.0.0 → 0.1.1

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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/dist/api.d.ts +17 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/client.d.ts +39 -0
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/command/command.d.ts +12 -0
  8. package/dist/command/command.d.ts.map +1 -0
  9. package/dist/command/command.test.d.ts +2 -0
  10. package/dist/command/command.test.d.ts.map +1 -0
  11. package/dist/command/index.cjs +1 -0
  12. package/dist/command/index.d.ts +4 -0
  13. package/dist/command/index.d.ts.map +1 -0
  14. package/dist/command/index.mjs +3 -0
  15. package/dist/command/parser.d.ts +3 -0
  16. package/dist/command/parser.d.ts.map +1 -0
  17. package/dist/command-Cu2v-5-K.cjs +4 -0
  18. package/dist/command-Cu2v-5-K.cjs.map +1 -0
  19. package/dist/command-caXc4h0n.js +76 -0
  20. package/dist/command-caXc4h0n.js.map +1 -0
  21. package/dist/commands.d.ts +48 -0
  22. package/dist/commands.d.ts.map +1 -0
  23. package/dist/crypt-handshake-CHmvZ2qs.js +55 -0
  24. package/dist/crypt-handshake-CHmvZ2qs.js.map +1 -0
  25. package/dist/crypt-handshake-Dbj2cSBZ.cjs +2 -0
  26. package/dist/crypt-handshake-Dbj2cSBZ.cjs.map +1 -0
  27. package/dist/crypt-init2-C2oihZ7e.cjs +2 -0
  28. package/dist/crypt-init2-C2oihZ7e.cjs.map +1 -0
  29. package/dist/crypt-init2-DWX7Tp-_.js +160 -0
  30. package/dist/crypt-init2-DWX7Tp-_.js.map +1 -0
  31. package/dist/crypto/crypt-ops.d.ts +7 -0
  32. package/dist/crypto/crypt-ops.d.ts.map +1 -0
  33. package/dist/crypto/crypt.d.ts +22 -0
  34. package/dist/crypto/crypt.d.ts.map +1 -0
  35. package/dist/crypto/eax.d.ts +12 -0
  36. package/dist/crypto/eax.d.ts.map +1 -0
  37. package/dist/crypto/eax.test.d.ts +2 -0
  38. package/dist/crypto/eax.test.d.ts.map +1 -0
  39. package/dist/crypto/identity.d.ts +18 -0
  40. package/dist/crypto/identity.d.ts.map +1 -0
  41. package/dist/crypto/identity.test.d.ts +2 -0
  42. package/dist/crypto/identity.test.d.ts.map +1 -0
  43. package/dist/crypto/index.cjs +1 -0
  44. package/dist/crypto/index.d.ts +6 -0
  45. package/dist/crypto/index.d.ts.map +1 -0
  46. package/dist/crypto/index.mjs +3 -0
  47. package/dist/crypto/primitives.d.ts +43 -0
  48. package/dist/crypto/primitives.d.ts.map +1 -0
  49. package/dist/crypto-2veVY1fC.cjs +2 -0
  50. package/dist/crypto-2veVY1fC.cjs.map +1 -0
  51. package/dist/crypto-VC7n6YJb.js +165 -0
  52. package/dist/crypto-VC7n6YJb.js.map +1 -0
  53. package/dist/discovery/index.cjs +1 -0
  54. package/dist/discovery/index.d.ts +2 -0
  55. package/dist/discovery/index.d.ts.map +1 -0
  56. package/dist/discovery/index.mjs +2 -0
  57. package/dist/discovery/resolver.d.ts +8 -0
  58. package/dist/discovery/resolver.d.ts.map +1 -0
  59. package/dist/errors.d.ts +34 -0
  60. package/dist/errors.d.ts.map +1 -0
  61. package/dist/events.d.ts +10 -0
  62. package/dist/events.d.ts.map +1 -0
  63. package/dist/handler-C_JhqGTd.js +455 -0
  64. package/dist/handler-C_JhqGTd.js.map +1 -0
  65. package/dist/handler-CqCD93f0.cjs +2 -0
  66. package/dist/handler-CqCD93f0.cjs.map +1 -0
  67. package/dist/handshake/crypt-handshake.d.ts +8 -0
  68. package/dist/handshake/crypt-handshake.d.ts.map +1 -0
  69. package/dist/handshake/crypt-init2.d.ts +7 -0
  70. package/dist/handshake/crypt-init2.d.ts.map +1 -0
  71. package/dist/handshake/index.cjs +1 -0
  72. package/dist/handshake/index.d.ts +4 -0
  73. package/dist/handshake/index.d.ts.map +1 -0
  74. package/dist/handshake/index.mjs +3 -0
  75. package/dist/handshake/license.d.ts +28 -0
  76. package/dist/handshake/license.d.ts.map +1 -0
  77. package/dist/handshake.d.ts +9 -0
  78. package/dist/handshake.d.ts.map +1 -0
  79. package/dist/helpers.d.ts +14 -0
  80. package/dist/helpers.d.ts.map +1 -0
  81. package/dist/helpers.test.d.ts +2 -0
  82. package/dist/helpers.test.d.ts.map +1 -0
  83. package/dist/index.cjs +2 -0
  84. package/dist/index.cjs.map +1 -0
  85. package/dist/index.d.ts +9 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.mjs +761 -0
  88. package/dist/index.mjs.map +1 -0
  89. package/dist/integration.test.d.ts +12 -0
  90. package/dist/integration.test.d.ts.map +1 -0
  91. package/dist/notifications.d.ts +32 -0
  92. package/dist/notifications.d.ts.map +1 -0
  93. package/dist/notifications.test.d.ts +2 -0
  94. package/dist/notifications.test.d.ts.map +1 -0
  95. package/dist/parser-CJjP3LlO.js +33 -0
  96. package/dist/parser-CJjP3LlO.js.map +1 -0
  97. package/dist/parser-DhAWj-TI.cjs +2 -0
  98. package/dist/parser-DhAWj-TI.cjs.map +1 -0
  99. package/dist/primitives-BIAYfJ2Y.js +1842 -0
  100. package/dist/primitives-BIAYfJ2Y.js.map +1 -0
  101. package/dist/primitives-bj-ml31v.cjs +2 -0
  102. package/dist/primitives-bj-ml31v.cjs.map +1 -0
  103. package/dist/resolver-DDZWomrF.js +165 -0
  104. package/dist/resolver-DDZWomrF.js.map +1 -0
  105. package/dist/resolver-Dey6omBe.cjs +4 -0
  106. package/dist/resolver-Dey6omBe.cjs.map +1 -0
  107. package/dist/throttle.d.ts +11 -0
  108. package/dist/throttle.d.ts.map +1 -0
  109. package/dist/throttle.test.d.ts +2 -0
  110. package/dist/throttle.test.d.ts.map +1 -0
  111. package/dist/transfer.d.ts +34 -0
  112. package/dist/transfer.d.ts.map +1 -0
  113. package/dist/transport/generation-window.d.ts +15 -0
  114. package/dist/transport/generation-window.d.ts.map +1 -0
  115. package/dist/transport/generation-window.test.d.ts +2 -0
  116. package/dist/transport/generation-window.test.d.ts.map +1 -0
  117. package/dist/transport/handler.d.ts +18 -0
  118. package/dist/transport/handler.d.ts.map +1 -0
  119. package/dist/transport/index.cjs +1 -0
  120. package/dist/transport/index.d.ts +6 -0
  121. package/dist/transport/index.d.ts.map +1 -0
  122. package/dist/transport/index.mjs +2 -0
  123. package/dist/transport/packet.d.ts +36 -0
  124. package/dist/transport/packet.d.ts.map +1 -0
  125. package/dist/transport/packet.test.d.ts +2 -0
  126. package/dist/transport/packet.test.d.ts.map +1 -0
  127. package/dist/transport/quicklz.d.ts +5 -0
  128. package/dist/transport/quicklz.d.ts.map +1 -0
  129. package/dist/transport/quicklz.test.d.ts +2 -0
  130. package/dist/transport/quicklz.test.d.ts.map +1 -0
  131. package/dist/types-CGKgXvpG.js +18 -0
  132. package/dist/types-CGKgXvpG.js.map +1 -0
  133. package/dist/types-DrnoCdSW.cjs +2 -0
  134. package/dist/types-DrnoCdSW.cjs.map +1 -0
  135. package/dist/types.d.ts +114 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/package.json +87 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["#pending","#nextRC","#welcomeComplete","#buffer","#pending","#nextID","#lastUpdate","#tokens","#identity","#addr","#resolver","#handlePacket","#handleConnectionClosed","#cmdMiddlewares","#eventMiddlewares","#finalCmdHandler","#buildCmdHandler","#finalEvtHandler","#buildEvtHandler","#status","#resetForConnect","#resolveAddr","#disconnectedHandlers","#connectedResolvers","#throttle","#cmdTrack","#textMsgHandlers","#clientEnterHandlers","#clientLeaveHandlers","#clientMoveHandlers","#pokedHandlers","#voiceDataHandlers","#connectedHandlers","#kickedHandlers","#clients","#ftTrack","#handlePacketSync","#handleVoicePacket","#handleCommandLines","#handleCommandStr","#processNotificationResult","#handleError","#dispatchEvent","#dispatchEventDirect"],"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\nexport interface FileTransferTracker {\n register(): [\n cftid: number,\n promise: Promise<\n FileUploadInfo | FileDownloadInfo | import(\"./types.js\").FileTransferStatusInfo\n >,\n ];\n unregister(cftid: number): void;\n notify(\n cftid: number,\n value: FileUploadInfo | FileDownloadInfo | import(\"./types.js\").FileTransferStatusInfo,\n ): void;\n reset(): void;\n}\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\nexport function sendClientInit(client: Client): void {\n const pubKeyBase64 = client.crypt.identity.publicKeyBase64();\n // HWID should match TS3 client UID format: base64(SHA1(publicKeyBase64))\n const hwid = createHash(\"sha1\").update(pubKeyBase64).digest().toString(\"base64\");\n\n const cmd = 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\", \"\"],\n [\"client_default_channel_password\", \"\"],\n [\"client_server_password\", \"\"],\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 client.handler.sendPacket(PacketType.Command, Buffer.from(cmd), 0);\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 for (const part of parts) {\n if (part !== \"\") rows.push(`${name} ${part}`);\n }\n return rows.length === 0 ? [line] : rows;\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 cid = parseUint64(cmd.params[\"cid\"] ?? \"\");\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: cid,\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 dialFileTransfer,\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\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 #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<() => void> = [];\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\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 // ---- 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 return new Promise<void>((resolve, reject) => {\n this.#connectedResolvers.push(resolve);\n if (signal) {\n signal.addEventListener(\"abort\", () => reject(signal.reason as Error), { once: true });\n }\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 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 for (const resolve of this.#connectedResolvers) 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 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(/[\\n\\0]/);\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 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 if (info.id !== 0 && isAutoNicknameMatch(this.nickname, info.nickname)) {\n this.clid = info.id;\n this.handler.setClientID(info.id);\n // Our own notifycliententerview is the last event in the TS3/TS5\n // welcome sequence. Signal that it's safe to buffer command responses.\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 this.#status = ClientStatus.Disconnected;\n const handlers = this.#disconnectedHandlers.slice();\n for (const h of handlers) setImmediate(() => h(err ?? undefined));\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 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 if (password) params.push([\"cpw\", 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 parentID: BigInt(item[\"pid\"] ?? \"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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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Integration tests against a live TeamSpeak 3 server.
3
+ *
4
+ * Opt-in: these tests only run when TEAMSPEAK_ADDR is set:
5
+ *
6
+ * TEAMSPEAK_ADDR=chenkr.cn pnpm test --reporter=verbose src/integration.test.ts
7
+ *
8
+ * A single shared client is reused across all tests to avoid the TS3
9
+ * anti-flood protection that bans IPs reconnecting too quickly.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,32 @@
1
+ import type { Command } from "./command/command.js";
2
+ import type { ClientInfo, ClientLeftViewEvent, ClientMovedEvent, TextMessage, PokeEvent, FileUploadInfo, FileDownloadInfo, FileTransferStatusInfo } from "./types.js";
3
+ export type NotificationResult = {
4
+ kind: "clientEnter";
5
+ info: ClientInfo;
6
+ } | {
7
+ kind: "clientLeave";
8
+ event: ClientLeftViewEvent;
9
+ isSelf: boolean;
10
+ } | {
11
+ kind: "clientMoved";
12
+ event: ClientMovedEvent;
13
+ } | {
14
+ kind: "textMessage";
15
+ message: TextMessage;
16
+ } | {
17
+ kind: "poked";
18
+ event: PokeEvent;
19
+ } | {
20
+ kind: "startUpload";
21
+ info: FileUploadInfo;
22
+ } | {
23
+ kind: "startDownload";
24
+ info: FileDownloadInfo;
25
+ } | {
26
+ kind: "fileTransferStatus";
27
+ info: FileTransferStatusInfo;
28
+ } | {
29
+ kind: "unknown";
30
+ };
31
+ export declare function handleNotification(cmd: Command, selfCLID: number, clients: Map<number, ClientInfo>, nickname: string): NotificationResult;
32
+ //# sourceMappingURL=notifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAGpB,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAChC,QAAQ,EAAE,MAAM,GACf,kBAAkB,CAqBpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=notifications.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.test.d.ts","sourceRoot":"","sources":["../src/notifications.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ import { i as e } from "./command-caXc4h0n.js";
2
+ //#region src/command/parser.ts
3
+ function t(t) {
4
+ if (t === "") return null;
5
+ let n = 0;
6
+ for (let e = 0; e < t.length; e++) {
7
+ let r = t.charCodeAt(e);
8
+ if (r >= 32 && r <= 126) {
9
+ n = e;
10
+ break;
11
+ }
12
+ }
13
+ n > 0 && (t = t.slice(n));
14
+ let r = t.split(" ");
15
+ if (r.length === 0) return null;
16
+ let i = r[0] ?? "";
17
+ if (i === "") return null;
18
+ let a = {};
19
+ for (let t = 1; t < r.length; t++) {
20
+ let n = r[t];
21
+ if (n === void 0 || n === "") continue;
22
+ let i = n.indexOf("=");
23
+ i >= 0 ? a[e(n.slice(0, i))] = e(n.slice(i + 1)) : a[e(n)] = "";
24
+ }
25
+ return {
26
+ name: i,
27
+ params: a
28
+ };
29
+ }
30
+ //#endregion
31
+ export { t };
32
+
33
+ //# sourceMappingURL=parser-CJjP3LlO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser-CJjP3LlO.js","names":[],"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 const name = parts[0] ?? \"\";\n if (name === \"\") return null;\n\n const params: Record<string, string> = {};\n\n for (let i = 1; 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,SAAgB,EAAa,GAA2B;AACtD,KAAI,MAAM,GAAI,QAAO;CAGrB,IAAI,IAAa;AACjB,MAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;EACjC,IAAM,IAAO,EAAE,WAAW,EAAE;AAC5B,MAAI,KAAQ,MAAM,KAAQ,KAAK;AAC7B,OAAa;AACb;;;AAGJ,CAAI,IAAa,MACf,IAAI,EAAE,MAAM,EAAW;CAGzB,IAAM,IAAQ,EAAE,MAAM,IAAI;AAC1B,KAAI,EAAM,WAAW,EAAG,QAAO;CAE/B,IAAM,IAAO,EAAM,MAAM;AACzB,KAAI,MAAS,GAAI,QAAO;CAExB,IAAM,IAAiC,EAAE;AAEzC,MAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAAK;EACrC,IAAM,IAAI,EAAM;AAChB,MAAI,MAAM,KAAA,KAAa,MAAM,GAAI;EACjC,IAAM,IAAQ,EAAE,QAAQ,IAAI;AAC5B,EAAI,KAAS,IACX,EAAO,EAAS,EAAE,MAAM,GAAG,EAAM,CAAC,IAAI,EAAS,EAAE,MAAM,IAAQ,EAAE,CAAC,GAElE,EAAO,EAAS,EAAE,IAAI;;AAI1B,QAAO;EAAE;EAAM;EAAQ"}
@@ -0,0 +1,2 @@
1
+ const e=require(`./command-Cu2v-5-K.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={};for(let t=1;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:i,params:a}}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return t}});
2
+ //# sourceMappingURL=parser-DhAWj-TI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser-DhAWj-TI.cjs","names":[],"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 const name = parts[0] ?? \"\";\n if (name === \"\") return null;\n\n const params: Record<string, string> = {};\n\n for (let i = 1; 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,SAAgB,EAAa,EAA2B,CACtD,GAAI,IAAM,GAAI,OAAO,KAGrB,IAAI,EAAa,EACjB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAE,OAAQ,IAAK,CACjC,IAAM,EAAO,EAAE,WAAW,EAAE,CAC5B,GAAI,GAAQ,IAAM,GAAQ,IAAK,CAC7B,EAAa,EACb,OAGA,EAAa,IACf,EAAI,EAAE,MAAM,EAAW,EAGzB,IAAM,EAAQ,EAAE,MAAM,IAAI,CAC1B,GAAI,EAAM,SAAW,EAAG,OAAO,KAE/B,IAAM,EAAO,EAAM,IAAM,GACzB,GAAI,IAAS,GAAI,OAAO,KAExB,IAAM,EAAiC,EAAE,CAEzC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAI,EAAM,GAChB,GAAI,IAAM,IAAA,IAAa,IAAM,GAAI,SACjC,IAAM,EAAQ,EAAE,QAAQ,IAAI,CACxB,GAAS,EACX,EAAO,EAAA,EAAS,EAAE,MAAM,EAAG,EAAM,CAAC,EAAI,EAAA,EAAS,EAAE,MAAM,EAAQ,EAAE,CAAC,CAElE,EAAO,EAAA,EAAS,EAAE,EAAI,GAI1B,MAAO,CAAE,OAAM,SAAQ"}