@honeybbq/teamspeak 1.0.0-beta.13 → 1.0.0-beta.15

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 (152) hide show
  1. package/README.md +321 -6
  2. package/dist/client/api/files.d.ts +89 -0
  3. package/dist/client/api/files.d.ts.map +1 -0
  4. package/dist/client/api/files.js +125 -0
  5. package/dist/client/api/files.js.map +1 -0
  6. package/dist/client/client.d.ts +2 -2
  7. package/dist/client/client.d.ts.map +1 -1
  8. package/dist/client/client.js.map +1 -1
  9. package/dist/client/commands.d.ts.map +1 -1
  10. package/dist/client/commands.js +5 -1
  11. package/dist/client/commands.js.map +1 -1
  12. package/dist/client/index.d.ts +2 -1
  13. package/dist/client/index.d.ts.map +1 -1
  14. package/dist/client/index.js +2 -1
  15. package/dist/client/index.js.map +1 -1
  16. package/dist/client/transport/generation-window.d.ts +6 -0
  17. package/dist/client/transport/generation-window.d.ts.map +1 -1
  18. package/dist/client/transport/generation-window.js +11 -0
  19. package/dist/client/transport/generation-window.js.map +1 -1
  20. package/dist/client/transport/handler.d.ts.map +1 -1
  21. package/dist/client/transport/handler.js +31 -1
  22. package/dist/client/transport/handler.js.map +1 -1
  23. package/dist/core/api/channels.d.ts +63 -8
  24. package/dist/core/api/channels.d.ts.map +1 -1
  25. package/dist/core/api/channels.js +74 -8
  26. package/dist/core/api/channels.js.map +1 -1
  27. package/dist/core/api/clients.d.ts +87 -1
  28. package/dist/core/api/clients.d.ts.map +1 -1
  29. package/dist/core/api/clients.js +95 -1
  30. package/dist/core/api/clients.js.map +1 -1
  31. package/dist/core/api/command.d.ts +51 -8
  32. package/dist/core/api/command.d.ts.map +1 -1
  33. package/dist/core/api/command.js +37 -8
  34. package/dist/core/api/command.js.map +1 -1
  35. package/dist/core/api/credentials.d.ts +3 -2
  36. package/dist/core/api/credentials.d.ts.map +1 -1
  37. package/dist/core/api/credentials.js +3 -2
  38. package/dist/core/api/credentials.js.map +1 -1
  39. package/dist/core/api/files.d.ts +47 -2
  40. package/dist/core/api/files.d.ts.map +1 -1
  41. package/dist/core/api/files.js +54 -2
  42. package/dist/core/api/files.js.map +1 -1
  43. package/dist/core/api/groups.d.ts +49 -11
  44. package/dist/core/api/groups.d.ts.map +1 -1
  45. package/dist/core/api/groups.js +53 -11
  46. package/dist/core/api/groups.js.map +1 -1
  47. package/dist/core/api/moderation.d.ts +36 -2
  48. package/dist/core/api/moderation.d.ts.map +1 -1
  49. package/dist/core/api/moderation.js +36 -3
  50. package/dist/core/api/moderation.js.map +1 -1
  51. package/dist/core/api/server.d.ts +112 -7
  52. package/dist/core/api/server.d.ts.map +1 -1
  53. package/dist/core/api/server.js +123 -7
  54. package/dist/core/api/server.js.map +1 -1
  55. package/dist/core/command/responses.d.ts +8 -3
  56. package/dist/core/command/responses.d.ts.map +1 -1
  57. package/dist/core/command/responses.js +11 -3
  58. package/dist/core/command/responses.js.map +1 -1
  59. package/dist/core/command-name.d.ts +12 -0
  60. package/dist/core/command-name.d.ts.map +1 -1
  61. package/dist/core/command-name.js +12 -0
  62. package/dist/core/command-name.js.map +1 -1
  63. package/dist/core/errorcode.d.ts +8 -0
  64. package/dist/core/errorcode.d.ts.map +1 -1
  65. package/dist/core/errorcode.js +8 -0
  66. package/dist/core/errorcode.js.map +1 -1
  67. package/dist/core/errors.d.ts +7 -6
  68. package/dist/core/errors.d.ts.map +1 -1
  69. package/dist/core/errors.js +7 -6
  70. package/dist/core/errors.js.map +1 -1
  71. package/dist/core/index.d.ts +1 -1
  72. package/dist/core/index.d.ts.map +1 -1
  73. package/dist/core/index.js.map +1 -1
  74. package/dist/core/notification-name.d.ts +16 -0
  75. package/dist/core/notification-name.d.ts.map +1 -1
  76. package/dist/core/notification-name.js +16 -0
  77. package/dist/core/notification-name.js.map +1 -1
  78. package/dist/query/client.d.ts.map +1 -1
  79. package/dist/query/client.js +85 -11
  80. package/dist/query/client.js.map +1 -1
  81. package/dist/query/late-rows.d.ts +33 -0
  82. package/dist/query/late-rows.d.ts.map +1 -0
  83. package/dist/query/late-rows.js +51 -0
  84. package/dist/query/late-rows.js.map +1 -0
  85. package/dist/stream/emitter.d.ts +18 -0
  86. package/dist/stream/emitter.d.ts.map +1 -0
  87. package/dist/stream/emitter.js +66 -0
  88. package/dist/stream/emitter.js.map +1 -0
  89. package/dist/stream/errors.d.ts +73 -0
  90. package/dist/stream/errors.d.ts.map +1 -0
  91. package/dist/stream/errors.js +128 -0
  92. package/dist/stream/errors.js.map +1 -0
  93. package/dist/stream/index.d.ts +27 -0
  94. package/dist/stream/index.d.ts.map +1 -0
  95. package/dist/stream/index.js +26 -0
  96. package/dist/stream/index.js.map +1 -0
  97. package/dist/stream/peer.d.ts +43 -0
  98. package/dist/stream/peer.d.ts.map +1 -0
  99. package/dist/stream/peer.js +100 -0
  100. package/dist/stream/peer.js.map +1 -0
  101. package/dist/stream/published.d.ts +54 -0
  102. package/dist/stream/published.d.ts.map +1 -0
  103. package/dist/stream/published.js +205 -0
  104. package/dist/stream/published.js.map +1 -0
  105. package/dist/stream/streams.d.ts +92 -0
  106. package/dist/stream/streams.d.ts.map +1 -0
  107. package/dist/stream/streams.js +671 -0
  108. package/dist/stream/streams.js.map +1 -0
  109. package/dist/stream/types.d.ts +428 -0
  110. package/dist/stream/types.d.ts.map +1 -0
  111. package/dist/stream/types.js +2 -0
  112. package/dist/stream/types.js.map +1 -0
  113. package/dist/stream/wait.d.ts +14 -0
  114. package/dist/stream/wait.d.ts.map +1 -0
  115. package/dist/stream/wait.js +46 -0
  116. package/dist/stream/wait.js.map +1 -0
  117. package/dist/stream/wire.d.ts +91 -0
  118. package/dist/stream/wire.d.ts.map +1 -0
  119. package/dist/stream/wire.js +248 -0
  120. package/dist/stream/wire.js.map +1 -0
  121. package/package.json +13 -6
  122. package/src/client/api/files.ts +249 -0
  123. package/src/client/client.ts +2 -2
  124. package/src/client/commands.ts +4 -1
  125. package/src/client/index.ts +2 -1
  126. package/src/client/transport/generation-window.ts +11 -0
  127. package/src/client/transport/handler.ts +28 -2
  128. package/src/core/api/channels.ts +132 -8
  129. package/src/core/api/clients.ts +167 -1
  130. package/src/core/api/command.ts +85 -8
  131. package/src/core/api/credentials.ts +3 -2
  132. package/src/core/api/files.ts +80 -2
  133. package/src/core/api/groups.ts +85 -11
  134. package/src/core/api/moderation.ts +52 -3
  135. package/src/core/api/server.ts +212 -7
  136. package/src/core/command/responses.ts +14 -3
  137. package/src/core/command-name.ts +12 -0
  138. package/src/core/errorcode.ts +8 -0
  139. package/src/core/errors.ts +7 -6
  140. package/src/core/index.ts +8 -1
  141. package/src/core/notification-name.ts +16 -0
  142. package/src/query/client.ts +91 -10
  143. package/src/query/late-rows.ts +66 -0
  144. package/src/stream/emitter.ts +73 -0
  145. package/src/stream/errors.ts +146 -0
  146. package/src/stream/index.ts +58 -0
  147. package/src/stream/peer.ts +141 -0
  148. package/src/stream/published.ts +254 -0
  149. package/src/stream/streams.ts +765 -0
  150. package/src/stream/types.ts +475 -0
  151. package/src/stream/wait.ts +62 -0
  152. package/src/stream/wire.ts +319 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @honeybbq/teamspeak
4
4
 
5
- **A clean-room TeamSpeak client protocol library written in pure TypeScript.**
5
+ **An independent TeamSpeak client protocol library written in pure TypeScript.**
6
6
 
7
7
  Works with TeamSpeak 3 and TeamSpeak 6 servers. No proprietary SDK, no copied code.
8
8
 
@@ -32,6 +32,7 @@ Upgrading from 0.x? The package was renamed; see the [migration guide](https://g
32
32
  - **Identity management** — Generate, import/export, and upgrade security level of identities
33
33
  - **Full command set** — 111 typed functions covering every command the voice protocol answers
34
34
  - **Reconnection** — Opt-in retry with backoff, tuned for TeamSpeak's antiflood ban
35
+ - **Streams** — TeamSpeak 6 screen share signalling: find, publish and watch streams, with SDP and ICE as strings for any WebRTC stack (experimental)
35
36
  - **Server capabilities** — Reports the server version and the uid hash it uses (SHA-1 on TS3, SHA-256 on TS6), and the server's name, icon, banner and welcome message
36
37
  - **Custom transports** — Supply your own socket, for proxies, tunnels or tests
37
38
  - **ESM with types** — Ships ES modules with full TypeScript declarations
@@ -296,7 +297,25 @@ sends a raw command line and resolves to the rows that answer it.
296
297
 
297
298
  ### File Transfers
298
299
 
299
- Negotiate with the `files` module, then move the bytes over TCP:
300
+ `uploadFile` and `downloadFile` (from `@honeybbq/teamspeak/client`) negotiate the
301
+ transfer and move the bytes in one call, and `listFilesRecursive` (from the
302
+ package) lists a directory and everything under it, each entry with its full path:
303
+
304
+ ```typescript
305
+ import { downloadFile, uploadFile } from "@honeybbq/teamspeak/client";
306
+ import { listFilesRecursive } from "@honeybbq/teamspeak";
307
+
308
+ await uploadFile(client, cid, "/docs/notes.txt", data);
309
+ const bytes = await downloadFile(client, cid, "/docs/notes.txt");
310
+ const tree = await listFilesRecursive(client, cid, "/", { maxDepth: 4 });
311
+ ```
312
+
313
+ They work over a voice `Client`, and over a `QueryClient` on raw TCP or SSH when
314
+ passed the file transfer host as `{ host }`; WebQuery refuses every file command.
315
+ `downloadFile` takes `{ dest }` to stream into a `Writable` instead of holding
316
+ the file, and refuses a file over `maxSize` (256 MiB) in memory.
317
+
318
+ To control the two halves, negotiate with the `files` module, then move the bytes over TCP:
300
319
 
301
320
  ```typescript
302
321
  import { Readable } from "node:stream";
@@ -530,6 +549,297 @@ The uid hash matters because TS3 hashes a client's public key with SHA-1 and TS6
530
549
  with SHA-256 — a client that assumes either one computes an identifier the other
531
550
  server never sends.
532
551
 
552
+ ### Streams (experimental)
553
+
554
+ TeamSpeak 6 clients can share a screen, a window or a camera. `@honeybbq/teamspeak/stream`
555
+ does the signalling of that: it finds the streams of the clients in view, publishes one,
556
+ and watches one. The video itself travels between the publisher and each viewer over
557
+ WebRTC, which is yours: `Streams` hands you the SDP and the ICE candidates as strings and
558
+ takes them back, so any WebRTC stack plugs in, in a browser, in Electron or in Node. The
559
+ package has no WebRTC dependency.
560
+
561
+ TeamSpeak does not document this protocol. It was measured on TeamSpeak 6 6.0.0-beta13.1,
562
+ and has not been tried against the native TeamSpeak 6 client yet, so every export is
563
+ `@beta` and may change in a minor release. A TeamSpeak 3 server does not answer the stream
564
+ commands at all, so `publish` and `watch` throw a `StreamsUnsupportedError` before sending
565
+ anything.
566
+
567
+ Watching: find a stream in `streams.list()` or wait for `started`, ask to watch it, and
568
+ give the publisher's offer to a peer connection. The viewer's connection is receive-only.
569
+
570
+ ```ts
571
+ import { Streams } from "@honeybbq/teamspeak/stream";
572
+
573
+ const streams = new Streams(client);
574
+ streams.on("started", (info) => console.log(`${info.name} is streaming`));
575
+
576
+ const view = await streams.watch(info, { message: "may I watch?" });
577
+
578
+ const pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] });
579
+ pc.ontrack = (event) => (video.srcObject = event.streams[0]);
580
+ pc.onicecandidate = (event) => {
581
+ if (event.candidate) void view.sendCandidate(event.candidate.toJSON());
582
+ };
583
+ // Candidates that arrived with the offer are delivered to the first listener.
584
+ view.on("candidate", (candidate) => void pc.addIceCandidate(candidate));
585
+ view.on("closed", () => pc.close());
586
+
587
+ await pc.setRemoteDescription({ type: "offer", sdp: view.offer });
588
+ const answer = await pc.createAnswer();
589
+ await pc.setLocalDescription(answer);
590
+ await view.answer(answer.sdp!);
591
+ ```
592
+
593
+ Publishing: the publisher makes the offer, one peer connection for each viewer. The server
594
+ relays every request to watch, from any channel, and enforces neither `viewerLimit` nor
595
+ `accessibility`: deciding who is let in is the publisher's job, and `request` is emitted
596
+ for each one, with nothing accepted for you.
597
+
598
+ ```ts
599
+ const stream = await streams.publish({ name: "My screen", source: "screen" });
600
+
601
+ stream.on("request", async (request) => {
602
+ if (!allowed(request.viewer)) return request.deny("Not today");
603
+
604
+ const pc = new RTCPeerConnection({ iceServers });
605
+ for (const track of capture.getTracks()) pc.addTrack(track, capture);
606
+ await pc.setLocalDescription(await pc.createOffer());
607
+
608
+ const viewer = await request.accept(pc.localDescription!.sdp);
609
+ pc.onicecandidate = (event) => {
610
+ if (event.candidate) void viewer.sendCandidate(event.candidate.toJSON());
611
+ };
612
+ viewer.on("answer", (sdp) => pc.setRemoteDescription({ type: "answer", sdp }));
613
+ viewer.on("candidate", (candidate) => void pc.addIceCandidate(candidate));
614
+ viewer.on("closed", () => pc.close());
615
+ });
616
+
617
+ await stream.stop();
618
+ ```
619
+
620
+ Things to know:
621
+
622
+ - A client may run several streams, so every stream is known by its id. `list()` is what
623
+ this client has heard of. A stream that ends while its publisher is out of the channel is
624
+ reported to nobody there, and stays listed until the connection ends.
625
+ - A stream survives its publisher changing channel, with no event. It ends when the
626
+ publisher stops it or disconnects.
627
+ - The server drops a command line longer than about 9.2 KB without an answer. Streams
628
+ commands are checked first and throw a `StreamPayloadTooLargeError`; an SDP offer or an
629
+ ICE candidate is a few kilobytes at most.
630
+ - The server lets anyone stop any stream. `Streams` sends `stopstream` only for streams it
631
+ published, and treats a `notifystreamstopped` for any stream as final.
632
+ - The server remembers no decision, so a client can ask again, and every ask is relayed.
633
+ To refuse for good, pass `rememberDenied: true` (the 1024 most recent uids, in memory
634
+ only; a requester out of view is looked up with `clientgetuidfromclid`), or leave the
635
+ request unanswered. The server has no way to remove a viewer: closing a viewer's peer
636
+ ends your side of it, and stopping the stream ends it for everyone.
637
+ - `dispose()` stops the streams published through that `Streams` and leaves the ones it
638
+ watched, while the client is connected. A stream whose `publish` gave up after the command
639
+ went out is stopped when its announcement arrives, so none is left running unseen.
640
+ - A client whose `client_is_streaming` clears, or that comes into view not streaming, has
641
+ its streams dropped, after a grace of two seconds: `stopped` with the cause `"gone"`. The
642
+ server clears the flag right before it reports the stop, so a stop that arrives in that time
643
+ ends the stream with its own cause and reason instead, and a flag set again cancels the drop.
644
+ - The bitrate the server reports for a stream is not the one requested.
645
+ - A client announces that it streams only with `client_is_streaming`, and a stream that was
646
+ running before you came is not announced at all. `Streams` asks for the streams of each
647
+ client that sets the flag, once, on a TeamSpeak 6 server.
648
+
649
+ ## Design
650
+
651
+ The conventions below hold across the whole package. Each is stated once here.
652
+
653
+ ### Errors
654
+
655
+ An error is one of two kinds:
656
+
657
+ - **A refusal the server reports.** It is a `ServerError` carrying the server's
658
+ numeric `code`, its text as `serverMessage`, and `extraMessage` and
659
+ `failedPermissionID` when the server names them.
660
+ - **A condition the SDK detects itself**: not connected, no data, a timeout, a
661
+ failed handshake, a failed file transfer. Each has its own class.
662
+
663
+ Every error the SDK throws on purpose extends `TeamspeakError`, so
664
+ `instanceof TeamspeakError` tells its failures from anything else. A failure
665
+ that wraps another keeps it in `cause`. Match a refusal by `error.code` against
666
+ `ErrorCode`, and everything else by class:
667
+
668
+ ```typescript
669
+ import {
670
+ ErrorCode,
671
+ NotConnectedError,
672
+ ServerError,
673
+ TeamspeakError,
674
+ asChannelID,
675
+ moveClient,
676
+ } from "@honeybbq/teamspeak";
677
+
678
+ try {
679
+ await moveClient(client, client.clientID, asChannelID(5), { password: "wrong" });
680
+ } catch (err) {
681
+ if (err instanceof ServerError && err.code === ErrorCode.ChannelInvalidPassword) {
682
+ // ask for the password again
683
+ } else if (err instanceof NotConnectedError) {
684
+ // the connection is gone
685
+ } else if (err instanceof ServerError) {
686
+ console.log(err.code, err.serverMessage, err.extraMessage, err.failedPermissionID);
687
+ } else if (err instanceof TeamspeakError) {
688
+ console.log(err.name, err.message, err.cause);
689
+ } else {
690
+ throw err;
691
+ }
692
+ }
693
+ ```
694
+
695
+ `ErrorCode` names the server's codes as constants. It is the table `errcode`
696
+ is in the Go SDK, with the same numbers. A server may send a code it does not
697
+ name, so `code` is a plain `number`. `instanceof` also works between two
698
+ installed copies of the package.
699
+
700
+ Errors live with the code that produces them, so each entry point exports only
701
+ the errors its own code throws:
702
+
703
+ | Entry point | Errors |
704
+ | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
705
+ | `@honeybbq/teamspeak` (shared by every connection) | `TeamspeakError`, `ServerError`, `CommandTimeoutError`, `NotConnectedError`, `AlreadyConnectedError`, `NoDataError` |
706
+ | `@honeybbq/teamspeak/client` (voice only) | `ConnectionTimeoutError`, `HandshakeError` and its `PuzzleTooHardError`, `InvalidAddressError`, `InvalidIdentityError`, `FileTransferError`, `RemovedError` (kick, ban or server stop) |
707
+ | `@honeybbq/teamspeak/stream` (streams only) | `StreamsUnsupportedError`, `StreamJoinDeniedError`, `StreamEndedError`, `StreamPayloadTooLargeError` |
708
+ | `@honeybbq/teamspeak/query` (query only) | `QueryAuthError`, `GuestAccessError`, `SshHostKeyError`, `MissingDependencyError`, `WebQueryHttpError` |
709
+
710
+ A call that is given a value it cannot use, such as an out-of-range id or an
711
+ option that contradicts another, throws the language's own `TypeError` or
712
+ `RangeError` before anything is sent. Those are not `TeamspeakError`s: they are
713
+ a bug in the caller, not a condition to handle. `WebQueryHttpError` carries the
714
+ HTTP `status`; a refusal the server itself reports is a `ServerError` over every
715
+ transport.
716
+
717
+ **Cancellation.** An aborted `signal` is the caller's own cancellation, not an
718
+ SDK error. `connect()`, the typed commands, `exec`, file transfers and `events()`
719
+ reject with the signal's `reason` itself, unwrapped: a `DOMException` named
720
+ `AbortError` after a bare `abort()`, a `TimeoutError` after
721
+ `AbortSignal.timeout()`, or whatever value was passed to `abort(reason)`. A
722
+ signal that is already aborted rejects before anything is sent. Aborting a
723
+ `connect()` abandons the attempt and leaves the client disconnected; a command
724
+ that was given up on may still have taken effect on the server. The signal and
725
+ the command's own `timeoutMs` are separate: when `timeoutMs` (ten seconds by
726
+ default) passes first, the command rejects with a `CommandTimeoutError`.
727
+
728
+ **No data.** `NoDataError` means the command succeeded but its answer lacked the
729
+ data it exists to return, such as the id of a channel just created. Treat the
730
+ command as having taken effect and do not retry it blindly, or it creates a
731
+ second object or is refused as a duplicate. `createChannel` and the group
732
+ commands look the new object up themselves and throw it only when nothing
733
+ matches. A list that comes back empty is `[]`, not this error.
734
+
735
+ ### Ids
736
+
737
+ Every kind of id is a branded type: `ChannelID`, `ClientID`, `ClientDBID`,
738
+ `ServerGroupID`, `ChannelGroupID`, `PermissionID`, `IconID`, `ServerID`,
739
+ `BanID` and the rest, with `UID` and `ChannelUID` as branded strings. The
740
+ brand costs nothing at runtime, and passing the wrong kind is a compile error.
741
+ Make one with its constructor, which throws a `RangeError` for a value outside
742
+ the id's wire range:
743
+
744
+ ```typescript
745
+ import { asChannelID, asClientID, moveClient } from "@honeybbq/teamspeak";
746
+
747
+ const cid = asChannelID(5); // or asChannelID(5n)
748
+ await moveClient(client, client.clientID, cid);
749
+ // moveClient(client, cid, client.clientID) does not compile.
750
+ const clid = asClientID(3);
751
+ ```
752
+
753
+ A `ClientID`, `PermissionID`, `IconID` and the file transfer ids are `number`s.
754
+ The ones the protocol makes 64 bits wide are `bigint`s: `ChannelID`,
755
+ `ClientDBID`, `ServerGroupID`, `ChannelGroupID`, `ServerID`, `BanID`,
756
+ `MessageID` and `APIKeyID`. Their constructors take a `number` or a `bigint`.
757
+
758
+ ### Options and absent values
759
+
760
+ **Options.** Required arguments are positional. Everything optional is one
761
+ options object, the last argument. A field that is not set is not sent:
762
+ `undefined` leaves the setting alone and `""` sends it empty.
763
+
764
+ ```typescript
765
+ import { editChannel } from "@honeybbq/teamspeak";
766
+
767
+ await editChannel(client, cid, {
768
+ name: "Lounge",
769
+ topic: "", // sent empty: clears the topic
770
+ // password is not set: not sent, so the password stays as it is.
771
+ });
772
+ ```
773
+
774
+ **Records.** In a record the server's answer fills in, an optional field is
775
+ `undefined` when the server did not send it. That is not the same as `0` or
776
+ `false`: `totalClients` is `undefined` in a `getChannelInfo` result, which does
777
+ not carry it, and `0` in a `listChannels` result of an empty channel.
778
+
779
+ ```typescript
780
+ import { getChannelInfo } from "@honeybbq/teamspeak";
781
+
782
+ const info = await getChannelInfo(client, cid);
783
+ if (info.totalClients !== undefined) console.log(info.totalClients, "clients");
784
+ ```
785
+
786
+ **Durations and times** are numbers of milliseconds with an `Ms` suffix
787
+ (`idleTimeMs`, `timeoutMs`) and `Date`. A `Date` field is `undefined` where the
788
+ server sends 0 for "never". The Go SDK uses `time.Duration` and `time.Time`.
789
+
790
+ ### Events
791
+
792
+ Each server event has a typed name (`"textMessage"`, `"clientEnter"`, ...;
793
+ `EventName` has a constant for each), and `"notification"` delivers every
794
+ notification as the server sent it, including those no typed event models. The
795
+ notification arrives after the typed event it produced. `on()` returns a
796
+ function that removes the listener; `events(name, { signal })` is the same as an
797
+ async iterator.
798
+
799
+ ```typescript
800
+ const off = client.on("textMessage", (message) => {
801
+ console.log(message.invokerName, message.message);
802
+ });
803
+ off();
804
+
805
+ client.on("notification", (n) => console.log(n.name, n.params));
806
+ ```
807
+
808
+ - **Order.** Listeners run one at a time, in the order the events arrived, so a
809
+ listener may call the client and await an answer.
810
+ - **State.** The client updates its state, such as `client.view`, before it
811
+ queues the event that reports the change, so a listener sees state at least as
812
+ new as its event.
813
+ - **A throw** in a listener does not stop the others. The exception is rethrown
814
+ from a microtask, as an uncaught exception, as an EventEmitter listener's
815
+ would be. The Go SDK recovers a panicking handler and logs it instead.
816
+ - **Middleware.** `eventMiddleware` sees each server event before the listeners
817
+ and may pass it on, replace it or drop it; `connected`, `disconnected`,
818
+ `kicked` and the view events bypass it. `commandMiddleware` wraps the typed commands. Both are
819
+ described above.
820
+
821
+ ### Stability
822
+
823
+ From 1.0 the package follows semver, as described under
824
+ [Compatibility](#compatibility): a breaking change to `@honeybbq/teamspeak`,
825
+ `/client` and `/versions` waits for 2.0. The enumerations, `ErrorCode` and the
826
+ event map may gain members in a minor release; give a `switch` over them a
827
+ default case.
828
+
829
+ An API that may still change in a minor release is marked `@experimental` (or, for
830
+ `@honeybbq/teamspeak/stream`, `@beta`) in its TSDoc, which editors and the API reference
831
+ show. Today that is the whole `@honeybbq/teamspeak/stream` entry point, and the whole
832
+ `@honeybbq/teamspeak/query` entry point, the server view (`client.view` and the
833
+ view events), the `previous` field of the client-left and channel-deleted
834
+ events, and `getServerVariables`.
835
+
836
+ ### Server behaviour the SDK works around
837
+
838
+ The servers answer some commands in ways that differ between TeamSpeak 3 and 6
839
+ and between transports, and the SDK evens those out. The measured tables are in
840
+ [voice-protocol-responses.md](https://github.com/HoneyBBQ/teamspeak-js/blob/main/docs/voice-protocol-responses.md)
841
+ and [serverquery-responses.md](https://github.com/HoneyBBQ/teamspeak-js/blob/main/docs/serverquery-responses.md).
842
+
533
843
  ## ServerQuery
534
844
 
535
845
  The `@experimental` subpath `@honeybbq/teamspeak/query` administers a server
@@ -733,6 +1043,7 @@ with the same semantics, apart from what the languages make differ:
733
1043
  | `@honeybbq/teamspeak` | What every TeamSpeak connection shares: the command language, ids, errors, the typed commands, the event types and the status and reconnection types |
734
1044
  | `@honeybbq/teamspeak/client` | The voice client, identities, file transfers, diagnostics and its retry policy |
735
1045
  | `@honeybbq/teamspeak/versions` | Client versions a server accepts, for the client's `version` option; data only, one export each |
1046
+ | `@honeybbq/teamspeak/stream` | TeamSpeak 6 streams, the screen share: finding, publishing and watching them; signalling only, no WebRTC code; `@beta` |
736
1047
  | `@honeybbq/teamspeak/query` | The ServerQuery client, running the same typed commands over raw TCP, SSH or WebQuery; `@experimental` |
737
1048
 
738
1049
  Each entry exports only its own symbols. The client entry does not re-export
@@ -751,7 +1062,8 @@ get no further releases. Coming from 0.x? The
751
1062
 
752
1063
  ```
753
1064
  src/
754
- ├── core/ # Shared: command language, ids, errors, typed commands
1065
+ ├── core/ # Shared (@honeybbq/teamspeak): command language, ids, errors, typed commands
1066
+ ├── versions/ # Client versions (@honeybbq/teamspeak/versions)
755
1067
  ├── client/ # The voice client (@honeybbq/teamspeak/client)
756
1068
  │ ├── client.ts # Client lifecycle, connection management
757
1069
  │ ├── commands.ts # Command sending and response tracking
@@ -767,7 +1079,8 @@ src/
767
1079
  │ ├── handshake/ # Crypto handshake and license verification
768
1080
  │ ├── transport/ # UDP packet framing, ACK, compression
769
1081
  │ └── discovery/ # SRV / TSDNS / direct address resolution
770
- └── query/ # ServerQuery client (not exported yet)
1082
+ ├── stream/ # TeamSpeak 6 streams (@honeybbq/teamspeak/stream)
1083
+ └── query/ # The ServerQuery client (@honeybbq/teamspeak/query)
771
1084
  examples/
772
1085
  └── connect.ts # Minimal connection example
773
1086
  ```
@@ -782,9 +1095,11 @@ Protocol knowledge was primarily informed by the [TSLib](https://github.com/Spla
782
1095
 
783
1096
  ## Disclaimer
784
1097
 
785
- TeamSpeak is a registered trademark of [TeamSpeak Systems GmbH](https://teamspeak.com/). This project is not affiliated with, endorsed by, or associated with TeamSpeak Systems GmbH in any way.
1098
+ TeamSpeak is a registered trademark of [TeamSpeak Systems, Inc.](https://teamspeak.com/). This project is not affiliated with, endorsed by, or associated with TeamSpeak Systems, Inc. in any way.
1099
+
1100
+ This library is an **independent implementation** built from public documentation, observation of network traffic, and independent research. It contains no TeamSpeak proprietary code, headers, libraries, or binaries.
786
1101
 
787
- This library is a **clean-room implementation** developed from publicly available documentation, protocol analysis of network traffic, and independent research. No proprietary TeamSpeak SDK code, headers, or libraries were used in its creation.
1102
+ The project implements the client side only and does not implement a TeamSpeak server.
788
1103
 
789
1104
  ## Contributing
790
1105
 
@@ -0,0 +1,89 @@
1
+ /**
2
+ * One-call file transfers: negotiate the transfer, move the bytes, and return.
3
+ *
4
+ * `initFileUpload` and `uploadFileData` (and the download twins) are the two
5
+ * halves of a transfer; these run both, over the connection that sends the
6
+ * commands. That is a voice `Client`, or a `QueryClient` over raw TCP or SSH:
7
+ * measured on TeamSpeak 3 3.13.8 (raw TCP, SSH, voice) and TeamSpeak 6
8
+ * beta13.1 (SSH, voice), the key a query connection negotiates opens the same
9
+ * file transfer port a voice client's does. WebQuery refuses every file command
10
+ * (error 5120), on both servers.
11
+ */
12
+ import { Readable, Writable } from "node:stream";
13
+ import type { ChannelID, CommandExecutor, FileDownloadOptions, FileUploadOptions } from "@honeybbq/teamspeak";
14
+ import type { FileTransferOptions } from "../transfer.js";
15
+ /**
16
+ * How {@link uploadFile} negotiates and moves the file: the options of
17
+ * `initFileUpload`, without `resume` (the whole file is sent), and those of
18
+ * `uploadFileData`. `signal` and `timeoutMs` apply to the negotiation, and the
19
+ * signal to the transfer too.
20
+ */
21
+ export interface UploadFileOptions extends Omit<FileUploadOptions, "resume">, FileTransferOptions {
22
+ /**
23
+ * The host of the file transfer port. A voice client sends to the host it
24
+ * connected to, so it needs none; any other connection must name it. A host
25
+ * given here wins over the client's own, and so does `socketFactory`.
26
+ */
27
+ readonly host?: string | undefined;
28
+ /**
29
+ * The size in bytes of a stream passed as the data, which the server needs
30
+ * before it takes the file. Required for a `Readable`; for bytes it is their
31
+ * length, and a different value here is refused.
32
+ */
33
+ readonly size?: bigint | number | undefined;
34
+ }
35
+ /** How {@link downloadFile} negotiates and moves the file. */
36
+ export interface DownloadFileOptions extends Omit<FileDownloadOptions, "seekPosition">, FileTransferOptions {
37
+ /** The host of the file transfer port, as for {@link UploadFileOptions}. */
38
+ readonly host?: string | undefined;
39
+ /**
40
+ * Write the file into this stream instead of returning its bytes. The stream
41
+ * is not ended, as for `downloadFileData`.
42
+ */
43
+ readonly dest?: Writable | undefined;
44
+ /**
45
+ * The largest file, in bytes, to hold in memory when no `dest` is given;
46
+ * 268435456 (256 MiB) by default. A larger file is refused with a
47
+ * {@link FileTransferError} before any byte is read; the transfer the server
48
+ * negotiated is left unused.
49
+ */
50
+ readonly maxSize?: number | undefined;
51
+ }
52
+ /**
53
+ * Upload a file into a channel's file area: negotiate the transfer
54
+ * (`initFileUpload`) and send the bytes (`uploadFileData`), in one call.
55
+ *
56
+ * `data` is the bytes, or a `Readable` with `options.size` the number of bytes
57
+ * it will give. An existing file is not replaced unless `overwrite` is set, and
58
+ * the directory the file goes in must exist (a missing one is refused with
59
+ * 2054, invalid file path): make it with `createDirectory` first. A refusal of
60
+ * the negotiation is thrown as the ServerError it is, before anything is sent;
61
+ * a failure of the connection to the file transfer port is a
62
+ * {@link FileTransferError}.
63
+ *
64
+ * Over a voice `Client` the bytes go through its own transfer sockets and to the
65
+ * host it connected to. Over any other connection, such as a `QueryClient`,
66
+ * name the host of the file transfer port in `options.host`.
67
+ */
68
+ export declare function uploadFile(client: CommandExecutor, channelID: ChannelID, path: string, data: Uint8Array | Readable, options?: UploadFileOptions): Promise<void>;
69
+ /**
70
+ * Download a file from a channel's file area into memory: negotiate the
71
+ * transfer (`initFileDownload`) and read the bytes (`downloadFileData`), in one
72
+ * call. With `options.dest` the bytes go into that stream instead and nothing
73
+ * is held, which is the way to fetch a large file.
74
+ *
75
+ * A file over `maxSize` (256 MiB unless set) is refused when no `dest` is given.
76
+ * A file that is not there is the ServerError of the negotiation: 2051 on TS3
77
+ * 3.13.8 and TS6 beta13.1. A path that names a directory is not refused by the
78
+ * server: it announces a size and sends nothing, so the call rejects with a
79
+ * {@link FileTransferError} for the bytes that never came.
80
+ *
81
+ * Over a voice `Client` the bytes come through its own transfer sockets from
82
+ * the host it connected to. Over any other connection, name the host of the
83
+ * file transfer port in `options.host`.
84
+ */
85
+ export declare function downloadFile(client: CommandExecutor, channelID: ChannelID, path: string, options: DownloadFileOptions & {
86
+ readonly dest: Writable;
87
+ }): Promise<void>;
88
+ export declare function downloadFile(client: CommandExecutor, channelID: ChannelID, path: string, options?: DownloadFileOptions): Promise<Uint8Array>;
89
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../src/client/api/files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EAEf,mBAAmB,EAEnB,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,EAAE,mBAAmB;IAC/F;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED,8DAA8D;AAC9D,MAAM,WAAW,mBACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,cAAc,CAAC,EAAE,mBAAmB;IACtE,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAqFD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,GAAG,QAAQ,EAC3B,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC;AACjB,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,UAAU,CAAC,CAAC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * One-call file transfers: negotiate the transfer, move the bytes, and return.
3
+ *
4
+ * `initFileUpload` and `uploadFileData` (and the download twins) are the two
5
+ * halves of a transfer; these run both, over the connection that sends the
6
+ * commands. That is a voice `Client`, or a `QueryClient` over raw TCP or SSH:
7
+ * measured on TeamSpeak 3 3.13.8 (raw TCP, SSH, voice) and TeamSpeak 6
8
+ * beta13.1 (SSH, voice), the key a query connection negotiates opens the same
9
+ * file transfer port a voice client's does. WebQuery refuses every file command
10
+ * (error 5120), on both servers.
11
+ */
12
+ import { Readable, Writable } from "node:stream";
13
+ import { initFileDownload, initFileUpload } from "@honeybbq/teamspeak";
14
+ import { FileTransferError } from "../errors.js";
15
+ import { downloadFileData, uploadFileData } from "../transfer.js";
16
+ function isTransferCapable(client) {
17
+ const candidate = client;
18
+ return (typeof candidate.host === "string" &&
19
+ typeof candidate.uploadFileData === "function" &&
20
+ typeof candidate.downloadFileData === "function");
21
+ }
22
+ /**
23
+ * How to move the bytes over this connection: through the client's own
24
+ * transfer sockets when it has them and the caller named neither a host nor a
25
+ * socket factory, else by dialling `options.host`.
26
+ */
27
+ function routeFor(client, options) {
28
+ if (isTransferCapable(client) && options.host === undefined && !options.socketFactory) {
29
+ return {
30
+ upload: (info, data, o) => client.uploadFileData(client.host, info, data, o),
31
+ download: (info, dest, o) => client.downloadFileData(client.host, info, dest, o),
32
+ };
33
+ }
34
+ const host = options.host ?? (isTransferCapable(client) ? client.host : undefined);
35
+ if (host === undefined || host === "") {
36
+ throw new TypeError("the file transfer port is on a host this connection does not name: pass it as options.host");
37
+ }
38
+ return {
39
+ upload: (info, data, o) => uploadFileData(host, info, data, o),
40
+ download: (info, dest, o) => downloadFileData(host, info, dest, o),
41
+ };
42
+ }
43
+ /** The part of the options a transfer's data phase reads. */
44
+ function transferOptions(options) {
45
+ return {
46
+ signal: options.signal,
47
+ socketFactory: options.socketFactory,
48
+ trace: options.trace,
49
+ idleTimeoutMs: options.idleTimeoutMs,
50
+ };
51
+ }
52
+ function sizeOf(data, declared) {
53
+ const size = declared === undefined ? undefined : BigInt(declared);
54
+ if (size !== undefined && (size < 0n || !Number.isInteger(Number(declared)))) {
55
+ throw new RangeError(`size is a non-negative whole number of bytes, not ${String(declared)}`);
56
+ }
57
+ if (data instanceof Uint8Array) {
58
+ if (size !== undefined && size !== BigInt(data.length)) {
59
+ throw new TypeError(`size says ${size} bytes and the data is ${data.length}`);
60
+ }
61
+ return BigInt(data.length);
62
+ }
63
+ if (size === undefined) {
64
+ throw new TypeError("a stream has no known length: pass options.size, in bytes");
65
+ }
66
+ return size;
67
+ }
68
+ /**
69
+ * Upload a file into a channel's file area: negotiate the transfer
70
+ * (`initFileUpload`) and send the bytes (`uploadFileData`), in one call.
71
+ *
72
+ * `data` is the bytes, or a `Readable` with `options.size` the number of bytes
73
+ * it will give. An existing file is not replaced unless `overwrite` is set, and
74
+ * the directory the file goes in must exist (a missing one is refused with
75
+ * 2054, invalid file path): make it with `createDirectory` first. A refusal of
76
+ * the negotiation is thrown as the ServerError it is, before anything is sent;
77
+ * a failure of the connection to the file transfer port is a
78
+ * {@link FileTransferError}.
79
+ *
80
+ * Over a voice `Client` the bytes go through its own transfer sockets and to the
81
+ * host it connected to. Over any other connection, such as a `QueryClient`,
82
+ * name the host of the file transfer port in `options.host`.
83
+ */
84
+ export async function uploadFile(client, channelID, path, data, options = {}) {
85
+ const size = sizeOf(data, options.size);
86
+ const route = routeFor(client, options);
87
+ const info = await initFileUpload(client, channelID, path, size, {
88
+ password: options.password,
89
+ overwrite: options.overwrite,
90
+ signal: options.signal,
91
+ timeoutMs: options.timeoutMs,
92
+ });
93
+ const source = data instanceof Uint8Array ? Readable.from([data]) : data;
94
+ await route.upload(info, source, transferOptions(options));
95
+ }
96
+ export async function downloadFile(client, channelID, path, options = {}) {
97
+ const maxSize = options.maxSize ?? DEFAULT_MAX_SIZE;
98
+ if (!Number.isFinite(maxSize) || maxSize < 0) {
99
+ throw new RangeError(`maxSize is a non-negative number of bytes, not ${String(maxSize)}`);
100
+ }
101
+ const route = routeFor(client, options);
102
+ const info = await initFileDownload(client, channelID, path, {
103
+ password: options.password,
104
+ signal: options.signal,
105
+ timeoutMs: options.timeoutMs,
106
+ });
107
+ if (options.dest !== undefined) {
108
+ await route.download(info, options.dest, transferOptions(options));
109
+ return;
110
+ }
111
+ if (info.size > BigInt(maxSize)) {
112
+ throw new FileTransferError(`the file is ${info.size} bytes, over the ${maxSize} bytes maxSize allows in memory`);
113
+ }
114
+ const chunks = [];
115
+ const sink = new Writable({
116
+ write(chunk, _encoding, done) {
117
+ chunks.push(chunk);
118
+ done();
119
+ },
120
+ });
121
+ await route.download(info, sink, transferOptions(options));
122
+ return Buffer.concat(chunks);
123
+ }
124
+ const DEFAULT_MAX_SIZE = 256 * 1024 * 1024;
125
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/client/api/files.ts"],"names":[],"mappings":"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"}
@@ -225,11 +225,11 @@ export declare class Client implements CommandExecutor, AsyncDisposable {
225
225
  * Upload `data` over a negotiated transfer, through the client's
226
226
  * `fileTransferSocketFactory` if it has one.
227
227
  */
228
- uploadFileData(host: string, info: FileUploadInfo, data: Readable, options?: Pick<FileTransferOptions, "signal" | "trace">): Promise<void>;
228
+ uploadFileData(host: string, info: FileUploadInfo, data: Readable, options?: Pick<FileTransferOptions, "signal" | "trace" | "idleTimeoutMs">): Promise<void>;
229
229
  /**
230
230
  * Download a negotiated transfer into `dest`, through the client's
231
231
  * `fileTransferSocketFactory` if it has one.
232
232
  */
233
- downloadFileData(host: string, info: FileDownloadInfo, dest: Writable, options?: Pick<FileTransferOptions, "signal" | "trace">): Promise<void>;
233
+ downloadFileData(host: string, info: FileDownloadInfo, dest: Writable, options?: Pick<FileTransferOptions, "signal" | "trace" | "idleTimeoutMs">): Promise<void>;
234
234
  }
235
235
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}