@honeybbq/teamspeak 0.0.1 → 1.0.0-beta.8
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.
- package/LICENSE +21 -0
- package/README.md +726 -2
- package/dist/client/abort.d.ts +5 -0
- package/dist/client/abort.d.ts.map +1 -0
- package/dist/client/abort.js +23 -0
- package/dist/client/abort.js.map +1 -0
- package/dist/client/address.d.ts +17 -0
- package/dist/client/address.d.ts.map +1 -0
- package/dist/client/address.js +66 -0
- package/dist/client/address.js.map +1 -0
- package/dist/client/api/voice.d.ts +24 -0
- package/dist/client/api/voice.d.ts.map +1 -0
- package/dist/client/api/voice.js +33 -0
- package/dist/client/api/voice.js.map +1 -0
- package/dist/client/capabilities.d.ts +68 -0
- package/dist/client/capabilities.d.ts.map +1 -0
- package/dist/client/capabilities.js +59 -0
- package/dist/client/capabilities.js.map +1 -0
- package/dist/client/client.d.ts +196 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +1523 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/commands.d.ts +66 -0
- package/dist/client/commands.d.ts.map +1 -0
- package/dist/client/commands.js +162 -0
- package/dist/client/commands.js.map +1 -0
- package/dist/client/crypto/crypt-ops.d.ts +7 -0
- package/dist/client/crypto/crypt-ops.d.ts.map +1 -0
- package/dist/client/crypto/crypt-ops.js +2 -0
- package/dist/client/crypto/crypt-ops.js.map +1 -0
- package/dist/client/crypto/crypt.d.ts +37 -0
- package/dist/client/crypto/crypt.d.ts.map +1 -0
- package/dist/client/crypto/crypt.js +203 -0
- package/dist/client/crypto/crypt.js.map +1 -0
- package/dist/client/crypto/eax.d.ts +12 -0
- package/dist/client/crypto/eax.d.ts.map +1 -0
- package/dist/client/crypto/eax.js +118 -0
- package/dist/client/crypto/eax.js.map +1 -0
- package/dist/client/crypto/identity.d.ts +97 -0
- package/dist/client/crypto/identity.d.ts.map +1 -0
- package/dist/client/crypto/identity.js +412 -0
- package/dist/client/crypto/identity.js.map +1 -0
- package/dist/client/crypto/index.d.ts +7 -0
- package/dist/client/crypto/index.d.ts.map +1 -0
- package/dist/client/crypto/index.js +5 -0
- package/dist/client/crypto/index.js.map +1 -0
- package/dist/client/crypto/primitives.d.ts +43 -0
- package/dist/client/crypto/primitives.d.ts.map +1 -0
- package/dist/client/crypto/primitives.js +116 -0
- package/dist/client/crypto/primitives.js.map +1 -0
- package/dist/client/diagnostics.d.ts +45 -0
- package/dist/client/diagnostics.d.ts.map +1 -0
- package/dist/client/diagnostics.js +30 -0
- package/dist/client/diagnostics.js.map +1 -0
- package/dist/client/discovery/index.d.ts +2 -0
- package/dist/client/discovery/index.d.ts.map +1 -0
- package/dist/client/discovery/index.js +2 -0
- package/dist/client/discovery/index.js.map +1 -0
- package/dist/client/discovery/resolver.d.ts +26 -0
- package/dist/client/discovery/resolver.d.ts.map +1 -0
- package/dist/client/discovery/resolver.js +275 -0
- package/dist/client/discovery/resolver.js.map +1 -0
- package/dist/client/errors.d.ts +82 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +201 -0
- package/dist/client/errors.js.map +1 -0
- package/dist/client/events.d.ts +3 -0
- package/dist/client/events.d.ts.map +1 -0
- package/dist/client/events.js +8 -0
- package/dist/client/events.js.map +1 -0
- package/dist/client/handshake/crypt-handshake.d.ts +17 -0
- package/dist/client/handshake/crypt-handshake.d.ts.map +1 -0
- package/dist/client/handshake/crypt-handshake.js +128 -0
- package/dist/client/handshake/crypt-handshake.js.map +1 -0
- package/dist/client/handshake/crypt-init2.d.ts +7 -0
- package/dist/client/handshake/crypt-init2.d.ts.map +1 -0
- package/dist/client/handshake/crypt-init2.js +37 -0
- package/dist/client/handshake/crypt-init2.js.map +1 -0
- package/dist/client/handshake/index.d.ts +4 -0
- package/dist/client/handshake/index.d.ts.map +1 -0
- package/dist/client/handshake/index.js +4 -0
- package/dist/client/handshake/index.js.map +1 -0
- package/dist/client/handshake/license.d.ts +35 -0
- package/dist/client/handshake/license.d.ts.map +1 -0
- package/dist/client/handshake/license.js +169 -0
- package/dist/client/handshake/license.js.map +1 -0
- package/dist/client/handshake.d.ts +27 -0
- package/dist/client/handshake.d.ts.map +1 -0
- package/dist/client/handshake.js +126 -0
- package/dist/client/handshake.js.map +1 -0
- package/dist/client/helpers.d.ts +6 -0
- package/dist/client/helpers.d.ts.map +1 -0
- package/dist/client/helpers.js +13 -0
- package/dist/client/helpers.js.map +1 -0
- package/dist/client/hooks.d.ts +7 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +28 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/index.d.ts +26 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +28 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/reconnect.d.ts +12 -0
- package/dist/client/reconnect.d.ts.map +1 -0
- package/dist/client/reconnect.js +44 -0
- package/dist/client/reconnect.js.map +1 -0
- package/dist/client/throttle.d.ts +20 -0
- package/dist/client/throttle.d.ts.map +1 -0
- package/dist/client/throttle.js +83 -0
- package/dist/client/throttle.js.map +1 -0
- package/dist/client/trace.d.ts +223 -0
- package/dist/client/trace.d.ts.map +1 -0
- package/dist/client/trace.js +13 -0
- package/dist/client/trace.js.map +1 -0
- package/dist/client/transfer.d.ts +111 -0
- package/dist/client/transfer.d.ts.map +1 -0
- package/dist/client/transfer.js +243 -0
- package/dist/client/transfer.js.map +1 -0
- package/dist/client/transport/generation-window.d.ts +30 -0
- package/dist/client/transport/generation-window.d.ts.map +1 -0
- package/dist/client/transport/generation-window.js +97 -0
- package/dist/client/transport/generation-window.js.map +1 -0
- package/dist/client/transport/handler.d.ts +61 -0
- package/dist/client/transport/handler.d.ts.map +1 -0
- package/dist/client/transport/handler.js +793 -0
- package/dist/client/transport/handler.js.map +1 -0
- package/dist/client/transport/index.d.ts +6 -0
- package/dist/client/transport/index.d.ts.map +1 -0
- package/dist/client/transport/index.js +5 -0
- package/dist/client/transport/index.js.map +1 -0
- package/dist/client/transport/packet.d.ts +36 -0
- package/dist/client/transport/packet.d.ts.map +1 -0
- package/dist/client/transport/packet.js +55 -0
- package/dist/client/transport/packet.js.map +1 -0
- package/dist/client/transport/quicklz.d.ts +24 -0
- package/dist/client/transport/quicklz.d.ts.map +1 -0
- package/dist/client/transport/quicklz.js +179 -0
- package/dist/client/transport/quicklz.js.map +1 -0
- package/dist/client/types.d.ts +226 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +39 -0
- package/dist/client/types.js.map +1 -0
- package/dist/core/api/channels.d.ts +182 -0
- package/dist/core/api/channels.d.ts.map +1 -0
- package/dist/core/api/channels.js +155 -0
- package/dist/core/api/channels.js.map +1 -0
- package/dist/core/api/clients.d.ts +304 -0
- package/dist/core/api/clients.d.ts.map +1 -0
- package/dist/core/api/clients.js +260 -0
- package/dist/core/api/clients.js.map +1 -0
- package/dist/core/api/command.d.ts +305 -0
- package/dist/core/api/command.d.ts.map +1 -0
- package/dist/core/api/command.js +363 -0
- package/dist/core/api/command.js.map +1 -0
- package/dist/core/api/files.d.ts +153 -0
- package/dist/core/api/files.d.ts.map +1 -0
- package/dist/core/api/files.js +181 -0
- package/dist/core/api/files.js.map +1 -0
- package/dist/core/api/groups.d.ts +248 -0
- package/dist/core/api/groups.d.ts.map +1 -0
- package/dist/core/api/groups.js +271 -0
- package/dist/core/api/groups.js.map +1 -0
- package/dist/core/api/moderation.d.ts +205 -0
- package/dist/core/api/moderation.d.ts.map +1 -0
- package/dist/core/api/moderation.js +209 -0
- package/dist/core/api/moderation.js.map +1 -0
- package/dist/core/api/parse.d.ts +16 -0
- package/dist/core/api/parse.d.ts.map +1 -0
- package/dist/core/api/parse.js +40 -0
- package/dist/core/api/parse.js.map +1 -0
- package/dist/core/api/server.d.ts +310 -0
- package/dist/core/api/server.d.ts.map +1 -0
- package/dist/core/api/server.js +290 -0
- package/dist/core/api/server.js.map +1 -0
- package/dist/core/command/command.d.ts +20 -0
- package/dist/core/command/command.d.ts.map +1 -0
- package/dist/core/command/command.js +112 -0
- package/dist/core/command/command.js.map +1 -0
- package/dist/core/command/ids.d.ts +125 -0
- package/dist/core/command/ids.d.ts.map +1 -0
- package/dist/core/command/ids.js +85 -0
- package/dist/core/command/ids.js.map +1 -0
- package/dist/core/command/parser.d.ts +17 -0
- package/dist/core/command/parser.d.ts.map +1 -0
- package/dist/core/command/parser.js +65 -0
- package/dist/core/command/parser.js.map +1 -0
- package/dist/core/command/request.d.ts +61 -0
- package/dist/core/command/request.d.ts.map +1 -0
- package/dist/core/command/request.js +64 -0
- package/dist/core/command/request.js.map +1 -0
- package/dist/core/command/responses.d.ts +59 -0
- package/dist/core/command/responses.d.ts.map +1 -0
- package/dist/core/command/responses.js +74 -0
- package/dist/core/command/responses.js.map +1 -0
- package/dist/core/command-name.d.ts +333 -0
- package/dist/core/command-name.d.ts.map +1 -0
- package/dist/core/command-name.js +331 -0
- package/dist/core/command-name.js.map +1 -0
- package/dist/core/enums.d.ts +146 -0
- package/dist/core/enums.d.ts.map +1 -0
- package/dist/core/enums.js +132 -0
- package/dist/core/enums.js.map +1 -0
- package/dist/core/errorcode.d.ts +413 -0
- package/dist/core/errorcode.d.ts.map +1 -0
- package/dist/core/errorcode.js +410 -0
- package/dist/core/errorcode.js.map +1 -0
- package/dist/core/errors.d.ts +111 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +233 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/events.d.ts +207 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +6 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/helpers.d.ts +42 -0
- package/dist/core/helpers.d.ts.map +1 -0
- package/dist/core/helpers.js +156 -0
- package/dist/core/helpers.js.map +1 -0
- package/dist/core/index.d.ts +35 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +34 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/internal.d.ts +23 -0
- package/dist/core/internal.d.ts.map +1 -0
- package/dist/core/internal.js +20 -0
- package/dist/core/internal.js.map +1 -0
- package/dist/core/middleware.d.ts +14 -0
- package/dist/core/middleware.d.ts.map +1 -0
- package/dist/core/middleware.js +18 -0
- package/dist/core/middleware.js.map +1 -0
- package/dist/core/notification-name.d.ts +154 -0
- package/dist/core/notification-name.d.ts.map +1 -0
- package/dist/core/notification-name.js +152 -0
- package/dist/core/notification-name.js.map +1 -0
- package/dist/core/notifications.d.ts +43 -0
- package/dist/core/notifications.d.ts.map +1 -0
- package/dist/core/notifications.js +116 -0
- package/dist/core/notifications.js.map +1 -0
- package/dist/core/permission-name.d.ts +533 -0
- package/dist/core/permission-name.d.ts.map +1 -0
- package/dist/core/permission-name.js +531 -0
- package/dist/core/permission-name.js.map +1 -0
- package/dist/core/permission.d.ts +31 -0
- package/dist/core/permission.d.ts.map +1 -0
- package/dist/core/permission.js +20 -0
- package/dist/core/permission.js.map +1 -0
- package/dist/core/property-name.d.ts +403 -0
- package/dist/core/property-name.d.ts.map +1 -0
- package/dist/core/property-name.js +405 -0
- package/dist/core/property-name.js.map +1 -0
- package/dist/core/reason.d.ts +39 -0
- package/dist/core/reason.d.ts.map +1 -0
- package/dist/core/reason.js +36 -0
- package/dist/core/reason.js.map +1 -0
- package/dist/core/reconnect.d.ts +59 -0
- package/dist/core/reconnect.d.ts.map +1 -0
- package/dist/core/reconnect.js +55 -0
- package/dist/core/reconnect.js.map +1 -0
- package/dist/core/types.d.ts +110 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +27 -0
- package/dist/core/types.js.map +1 -0
- package/dist/query/client.d.ts +194 -0
- package/dist/query/client.d.ts.map +1 -0
- package/dist/query/client.js +969 -0
- package/dist/query/client.js.map +1 -0
- package/dist/query/errors.d.ts +61 -0
- package/dist/query/errors.d.ts.map +1 -0
- package/dist/query/errors.js +100 -0
- package/dist/query/errors.js.map +1 -0
- package/dist/query/index.d.ts +36 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +34 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/knownhosts.d.ts +61 -0
- package/dist/query/knownhosts.d.ts.map +1 -0
- package/dist/query/knownhosts.js +223 -0
- package/dist/query/knownhosts.js.map +1 -0
- package/dist/query/lines.d.ts +14 -0
- package/dist/query/lines.d.ts.map +1 -0
- package/dist/query/lines.js +29 -0
- package/dist/query/lines.js.map +1 -0
- package/dist/query/options.d.ts +187 -0
- package/dist/query/options.d.ts.map +1 -0
- package/dist/query/options.js +213 -0
- package/dist/query/options.js.map +1 -0
- package/dist/query/reconnect.d.ts +29 -0
- package/dist/query/reconnect.d.ts.map +1 -0
- package/dist/query/reconnect.js +87 -0
- package/dist/query/reconnect.js.map +1 -0
- package/dist/query/ssh.d.ts +345 -0
- package/dist/query/ssh.d.ts.map +1 -0
- package/dist/query/ssh.js +241 -0
- package/dist/query/ssh.js.map +1 -0
- package/dist/query/stream.d.ts +38 -0
- package/dist/query/stream.d.ts.map +1 -0
- package/dist/query/stream.js +70 -0
- package/dist/query/stream.js.map +1 -0
- package/dist/query/tcp.d.ts +38 -0
- package/dist/query/tcp.d.ts.map +1 -0
- package/dist/query/tcp.js +53 -0
- package/dist/query/tcp.js.map +1 -0
- package/dist/query/transport.d.ts +36 -0
- package/dist/query/transport.d.ts.map +1 -0
- package/dist/query/transport.js +2 -0
- package/dist/query/transport.js.map +1 -0
- package/dist/query/web.d.ts +54 -0
- package/dist/query/web.d.ts.map +1 -0
- package/dist/query/web.js +146 -0
- package/dist/query/web.js.map +1 -0
- package/dist/versions/index.d.ts +3094 -0
- package/dist/versions/index.d.ts.map +1 -0
- package/dist/versions/index.js +9237 -0
- package/dist/versions/index.js.map +1 -0
- package/package.json +118 -4
- package/src/client/abort.ts +24 -0
- package/src/client/address.ts +74 -0
- package/src/client/api/voice.ts +61 -0
- package/src/client/capabilities.ts +106 -0
- package/src/client/client.ts +1809 -0
- package/src/client/commands.ts +193 -0
- package/src/client/crypto/crypt-ops.ts +6 -0
- package/src/client/crypto/crypt.ts +275 -0
- package/src/client/crypto/eax.ts +152 -0
- package/src/client/crypto/identity.ts +484 -0
- package/src/client/crypto/index.ts +21 -0
- package/src/client/crypto/primitives.ts +141 -0
- package/src/client/diagnostics.ts +58 -0
- package/src/client/discovery/index.ts +1 -0
- package/src/client/discovery/resolver.ts +325 -0
- package/src/client/errors.ts +217 -0
- package/src/client/events.ts +12 -0
- package/src/client/handshake/crypt-handshake.ts +157 -0
- package/src/client/handshake/crypt-init2.ts +48 -0
- package/src/client/handshake/index.ts +3 -0
- package/src/client/handshake/license.ts +208 -0
- package/src/client/handshake.ts +150 -0
- package/src/client/helpers.ts +10 -0
- package/src/client/hooks.ts +30 -0
- package/src/client/index.ts +93 -0
- package/src/client/reconnect.ts +43 -0
- package/src/client/throttle.ts +92 -0
- package/src/client/trace.ts +237 -0
- package/src/client/transfer.ts +436 -0
- package/src/client/transport/generation-window.ts +108 -0
- package/src/client/transport/handler.ts +926 -0
- package/src/client/transport/index.ts +14 -0
- package/src/client/transport/packet.ts +69 -0
- package/src/client/transport/quicklz.ts +209 -0
- package/src/client/types.ts +268 -0
- package/src/core/api/channels.ts +396 -0
- package/src/core/api/clients.ts +654 -0
- package/src/core/api/command.ts +563 -0
- package/src/core/api/files.ts +405 -0
- package/src/core/api/groups.ts +622 -0
- package/src/core/api/moderation.ts +525 -0
- package/src/core/api/parse.ts +52 -0
- package/src/core/api/server.ts +709 -0
- package/src/core/command/command.ts +125 -0
- package/src/core/command/ids.ts +149 -0
- package/src/core/command/parser.ts +68 -0
- package/src/core/command/request.ts +119 -0
- package/src/core/command/responses.ts +114 -0
- package/src/core/command-name.ts +334 -0
- package/src/core/enums.ts +167 -0
- package/src/core/errorcode.ts +412 -0
- package/src/core/errors.ts +258 -0
- package/src/core/events.ts +219 -0
- package/src/core/helpers.ts +161 -0
- package/src/core/index.ts +126 -0
- package/src/core/internal.ts +23 -0
- package/src/core/middleware.ts +24 -0
- package/src/core/notification-name.ts +155 -0
- package/src/core/notifications.ts +157 -0
- package/src/core/permission-name.ts +538 -0
- package/src/core/permission.ts +34 -0
- package/src/core/property-name.ts +411 -0
- package/src/core/reason.ts +38 -0
- package/src/core/reconnect.ts +98 -0
- package/src/core/types.ts +138 -0
- package/src/query/client.ts +1129 -0
- package/src/query/errors.ts +108 -0
- package/src/query/index.ts +65 -0
- package/src/query/knownhosts.ts +257 -0
- package/src/query/lines.ts +28 -0
- package/src/query/options.ts +388 -0
- package/src/query/reconnect.ts +84 -0
- package/src/query/ssh.ts +614 -0
- package/src/query/stream.ts +91 -0
- package/src/query/tcp.ts +85 -0
- package/src/query/transport.ts +35 -0
- package/src/query/web.ts +195 -0
- package/src/versions/index.ts +10774 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branded identifiers.
|
|
3
|
+
*
|
|
4
|
+
* TeamSpeak uses one 64-bit integer space for several unrelated kinds of id, so
|
|
5
|
+
* a `bigint` alone cannot tell a channel from a client. Branding makes the
|
|
6
|
+
* mistake a compile error instead of a silent move-to-the-wrong-place, and the
|
|
7
|
+
* brands cost nothing at runtime.
|
|
8
|
+
*
|
|
9
|
+
* The ids the protocol defines as 32 bits or narrower — a clid, a permission,
|
|
10
|
+
* an icon — are `number` brands; the 64-bit ones are `bigint` brands.
|
|
11
|
+
*/
|
|
12
|
+
const MAX_UINT16 = 0xffff;
|
|
13
|
+
const MAX_UINT32 = 0xffff_ffff;
|
|
14
|
+
const MAX_UINT64 = (1n << 64n) - 1n;
|
|
15
|
+
/** `v` if it is an integer from 0 to `max`; a RangeError otherwise. */
|
|
16
|
+
function uintInRange(v, max, what) {
|
|
17
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 0 || v > max) {
|
|
18
|
+
throw new RangeError(`${what} is an integer from 0 to ${max}, not ${String(v)}`);
|
|
19
|
+
}
|
|
20
|
+
return v;
|
|
21
|
+
}
|
|
22
|
+
/** `v` as a bigint if it is an integer from 0 to 2^64-1; a RangeError otherwise. */
|
|
23
|
+
function uint64(v, what) {
|
|
24
|
+
const integer = typeof v === "bigint" || (typeof v === "number" && Number.isInteger(v));
|
|
25
|
+
const value = integer ? BigInt(v) : -1n;
|
|
26
|
+
if (value < 0n || value > MAX_UINT64) {
|
|
27
|
+
throw new RangeError(`${what} is an integer from 0 to 2^64-1, not ${String(v)}`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
// Every constructor checks its value against the id's wire width and throws a
|
|
32
|
+
// RangeError for anything else — a fraction, a negative, a value too wide —
|
|
33
|
+
// rather than brand a value no server could mean.
|
|
34
|
+
/** Brand a ban id, an integer from 0 to 2^64-1, from a number or a bigint. */
|
|
35
|
+
export const asBanID = (v) => uint64(v, "a ban id");
|
|
36
|
+
/** Brand an offline message id, an integer from 0 to 2^64-1. */
|
|
37
|
+
export const asMessageID = (v) => uint64(v, "a message id");
|
|
38
|
+
/** Brand a virtual server id, an integer from 0 to 2^64-1. */
|
|
39
|
+
export const asServerID = (v) => uint64(v, "a server id");
|
|
40
|
+
/** Brand the client's id for a file transfer, an integer from 0 to 65535. */
|
|
41
|
+
export const asClientFileTransferID = (v) => uintInRange(v, MAX_UINT16, "a client file transfer id");
|
|
42
|
+
/** Brand the server's id for a file transfer, an integer from 0 to 65535. */
|
|
43
|
+
export const asServerFileTransferID = (v) => uintInRange(v, MAX_UINT16, "a server file transfer id");
|
|
44
|
+
/** Brand a channel id, an integer from 0 to 2^64-1, from a number or a bigint. */
|
|
45
|
+
export const asChannelID = (v) => uint64(v, "a channel id");
|
|
46
|
+
/** Brand a client id, an integer from 0 to 65535: the voice protocol's width. */
|
|
47
|
+
export const asClientID = (v) => uintInRange(v, MAX_UINT16, "a client id");
|
|
48
|
+
/** Brand a client database id, an integer from 0 to 2^64-1. */
|
|
49
|
+
export const asClientDBID = (v) => uint64(v, "a client database id");
|
|
50
|
+
/** Brand a server group id, an integer from 0 to 2^64-1. */
|
|
51
|
+
export const asServerGroupID = (v) => uint64(v, "a server group id");
|
|
52
|
+
/** Brand a channel group id, an integer from 0 to 2^64-1. */
|
|
53
|
+
export const asChannelGroupID = (v) => uint64(v, "a channel group id");
|
|
54
|
+
/** Brand a permission id, an integer from 0 to 2^32-1. */
|
|
55
|
+
export const asPermissionID = (v) => uintInRange(v, MAX_UINT32, "a permission id");
|
|
56
|
+
/**
|
|
57
|
+
* Brand an icon id, an integer from -2^31 to 2^32-1. A negative value is taken
|
|
58
|
+
* as the signed form of the same CRC32, which is how the server sometimes
|
|
59
|
+
* sends one, and stored unsigned.
|
|
60
|
+
*/
|
|
61
|
+
export const asIconID = (v) => {
|
|
62
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < -(2 ** 31) || v > MAX_UINT32) {
|
|
63
|
+
throw new RangeError(`an icon id is an integer from -2^31 to 2^32-1, not ${String(v)}`);
|
|
64
|
+
}
|
|
65
|
+
return (v >>> 0);
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Read an icon id as the server sends it: the CRC32, sometimes as a negative
|
|
69
|
+
* 32-bit number, sometimes unsigned. Both name the same icon, so both come back
|
|
70
|
+
* as the unsigned value; 0, garbage and out-of-range values mean no icon.
|
|
71
|
+
*/
|
|
72
|
+
export function parseIconID(raw) {
|
|
73
|
+
const text = raw?.trim() ?? "";
|
|
74
|
+
if (!/^-?\d{1,10}$/.test(text))
|
|
75
|
+
return undefined;
|
|
76
|
+
const value = Number(text);
|
|
77
|
+
if (value < -(2 ** 31) || value >= 2 ** 32 || value === 0)
|
|
78
|
+
return undefined;
|
|
79
|
+
return asIconID(value);
|
|
80
|
+
}
|
|
81
|
+
/** Brand a unique identifier. */
|
|
82
|
+
export const asUID = (v) => v;
|
|
83
|
+
/** Brand a channel's unique identifier. */
|
|
84
|
+
export const asChannelUID = (v) => v;
|
|
85
|
+
//# sourceMappingURL=ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../../src/core/command/ids.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Command } from "./command.js";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a token is a command name.
|
|
4
|
+
*
|
|
5
|
+
* A command name is a run of ASCII alphanumerics — the rule tsproto-packets
|
|
6
|
+
* uses (CommandParser::new). Everything that splits or parses a line asks this
|
|
7
|
+
* one function, so the parser and the row splitter cannot disagree about where
|
|
8
|
+
* a command's data begins.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isCommandName(token: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Parse one command line, or one row of it. Returns null for an empty line.
|
|
13
|
+
* A line whose first token is not a command name is a data row, with `name`
|
|
14
|
+
* `""`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseCommand(s: string): Command | null;
|
|
17
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/core/command/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CA+CtD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { unescape } from "./command.js";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a token is a command name.
|
|
4
|
+
*
|
|
5
|
+
* A command name is a run of ASCII alphanumerics — the rule tsproto-packets
|
|
6
|
+
* uses (CommandParser::new). Everything that splits or parses a line asks this
|
|
7
|
+
* one function, so the parser and the row splitter cannot disagree about where
|
|
8
|
+
* a command's data begins.
|
|
9
|
+
*/
|
|
10
|
+
export function isCommandName(token) {
|
|
11
|
+
return /^[A-Za-z0-9]+$/.test(token);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse one command line, or one row of it. Returns null for an empty line.
|
|
15
|
+
* A line whose first token is not a command name is a data row, with `name`
|
|
16
|
+
* `""`.
|
|
17
|
+
*/
|
|
18
|
+
export function parseCommand(s) {
|
|
19
|
+
if (s === "")
|
|
20
|
+
return null;
|
|
21
|
+
// Skip leading non-printable bytes (< 0x20 or > 0x7E)
|
|
22
|
+
let startIndex = 0;
|
|
23
|
+
for (let i = 0; i < s.length; i++) {
|
|
24
|
+
const code = s.charCodeAt(i);
|
|
25
|
+
if (code >= 32 && code <= 126) {
|
|
26
|
+
startIndex = i;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (startIndex > 0) {
|
|
31
|
+
s = s.slice(startIndex);
|
|
32
|
+
}
|
|
33
|
+
const parts = s.split(" ");
|
|
34
|
+
if (parts.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
// A command name is a run of alphanumerics followed by a space — the rule
|
|
37
|
+
// tsproto-packets uses (CommandParser::new). Anything else means the line is
|
|
38
|
+
// not a command at all: response rows carry no name ("clid=6 cid=2 ..."),
|
|
39
|
+
// and negative feedback is "-1 id=2 ...", naming the *rejected* command id
|
|
40
|
+
// rather than a command of its own. Returning those as a nameless row keeps
|
|
41
|
+
// the data intact instead of folding the first field into a name, which is
|
|
42
|
+
// how a clientlist row used to arrive as name="clid=6" and lose its clid.
|
|
43
|
+
const first = parts[0] ?? "";
|
|
44
|
+
if (first === "")
|
|
45
|
+
return null;
|
|
46
|
+
// No prototype: a key is whatever the line says, and one named __proto__
|
|
47
|
+
// must be stored, not swallowed by Object.prototype's setter.
|
|
48
|
+
const params = Object.create(null);
|
|
49
|
+
const name = isCommandName(first) ? first : "";
|
|
50
|
+
const start = name === "" ? 0 : 1;
|
|
51
|
+
for (let i = start; i < parts.length; i++) {
|
|
52
|
+
const p = parts[i];
|
|
53
|
+
if (p === undefined || p === "")
|
|
54
|
+
continue;
|
|
55
|
+
const eqIdx = p.indexOf("=");
|
|
56
|
+
if (eqIdx >= 0) {
|
|
57
|
+
params[unescape(p.slice(0, eqIdx))] = unescape(p.slice(eqIdx + 1));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
params[unescape(p)] = "";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { name, params };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/core/command/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE1B,sDAAsD;IACtD,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;YAC9B,UAAU,GAAG,CAAC,CAAC;YACf,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,0EAA0E;IAC1E,6EAA6E;IAC7E,0EAA0E;IAC1E,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE9B,yEAAyE;IACzE,8DAA8D;IAC9D,MAAM,MAAM,GAA2B,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IACrF,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAElC,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE;YAAE,SAAS;QAC1C,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A command as the typed layer describes it, before a transport encodes it.
|
|
3
|
+
*
|
|
4
|
+
* The typed commands run on any connection that can execute a command — the
|
|
5
|
+
* voice client today, ServerQuery later — and the two disagree about how some
|
|
6
|
+
* values go on the wire. A channel password is the case that matters: the
|
|
7
|
+
* voice protocol expects `base64(sha1(password))`, while ServerQuery takes the
|
|
8
|
+
* password itself and hashes it server-side. So a request says *which*
|
|
9
|
+
* parameters are passwords and leaves their encoding to the transport.
|
|
10
|
+
*/
|
|
11
|
+
import type { CommandName } from "../command-name.js";
|
|
12
|
+
/** A parameter value as a caller may supply it. */
|
|
13
|
+
export type ParamValue = string | number | bigint | boolean | undefined;
|
|
14
|
+
/** A command and its parameters, in the order they are sent. */
|
|
15
|
+
export interface CommandRequest {
|
|
16
|
+
/** The command's name, such as `clientlist`; {@link CommandName} lists the known ones. */
|
|
17
|
+
readonly command: CommandName | (string & {});
|
|
18
|
+
/**
|
|
19
|
+
* Its parameters, as wire name and value pairs, in the order they are sent;
|
|
20
|
+
* an undefined value is omitted.
|
|
21
|
+
*/
|
|
22
|
+
readonly params?: ReadonlyArray<readonly [string, ParamValue]> | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Further rows, for a command that takes a list: each is a group of
|
|
25
|
+
* parameters sent after a `|`, as `ftdeletefile cid=1 cpw= name=/a|name=/b`
|
|
26
|
+
* sends two names. The first row's parameters follow `params`.
|
|
27
|
+
*/
|
|
28
|
+
readonly rows?: ReadonlyArray<ReadonlyArray<readonly [string, ParamValue]>> | undefined;
|
|
29
|
+
/** Names of the parameters that carry a channel password. */
|
|
30
|
+
readonly passwords?: readonly string[] | undefined;
|
|
31
|
+
}
|
|
32
|
+
/** How long a command may take, and how to abandon it. */
|
|
33
|
+
export interface CommandOptions {
|
|
34
|
+
/** Abandons the command; it then rejects with the signal's reason. */
|
|
35
|
+
readonly signal?: AbortSignal | undefined;
|
|
36
|
+
/** Milliseconds to wait for the answer before failing with a CommandTimeoutError. */
|
|
37
|
+
readonly timeoutMs?: number | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A connection the typed commands can run on.
|
|
41
|
+
*
|
|
42
|
+
* It is deliberately this narrow. A caller implements it to run the typed
|
|
43
|
+
* commands over a transport of their own, and `CommandMiddleware` wraps its
|
|
44
|
+
* `send`, so a method added here would break every implementation and every
|
|
45
|
+
* middleware along with it: this interface gains no methods in a minor
|
|
46
|
+
* release. A new capability arrives as an interface, a function or an option
|
|
47
|
+
* of its own instead, and an implementer or a middleware author may rely on
|
|
48
|
+
* that.
|
|
49
|
+
*/
|
|
50
|
+
export interface CommandExecutor {
|
|
51
|
+
/** Send a command and return the rows that answer it. */
|
|
52
|
+
send(request: CommandRequest, options?: CommandOptions): Promise<Record<string, string>[]>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Render a request as a command line.
|
|
56
|
+
*
|
|
57
|
+
* `encodePassword` is the transport's rule for password parameters. An empty
|
|
58
|
+
* password is sent as is: it means "no password", in every encoding.
|
|
59
|
+
*/
|
|
60
|
+
export declare function encodeRequest(request: CommandRequest, encodePassword: (password: string) => string): string;
|
|
61
|
+
//# sourceMappingURL=request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../../src/core/command/request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGtD,mDAAmD;AACnD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAExE,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC;IAC3E;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACxF,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACpD;AAED,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IAEzD,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;CAC5F;AAiBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAC3C,MAAM,CAWR"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A command as the typed layer describes it, before a transport encodes it.
|
|
3
|
+
*
|
|
4
|
+
* The typed commands run on any connection that can execute a command — the
|
|
5
|
+
* voice client today, ServerQuery later — and the two disagree about how some
|
|
6
|
+
* values go on the wire. A channel password is the case that matters: the
|
|
7
|
+
* voice protocol expects `base64(sha1(password))`, while ServerQuery takes the
|
|
8
|
+
* password itself and hashes it server-side. So a request says *which*
|
|
9
|
+
* parameters are passwords and leaves their encoding to the transport.
|
|
10
|
+
*/
|
|
11
|
+
import { buildCommandOrdered } from "./command.js";
|
|
12
|
+
/**
|
|
13
|
+
* Turn a value into its wire form.
|
|
14
|
+
*
|
|
15
|
+
* Returns undefined for values that should be omitted entirely — an unset
|
|
16
|
+
* optional field — because sending `foo=` is not the same as not sending it:
|
|
17
|
+
* for `channel_password` the first clears the password and the second leaves it
|
|
18
|
+
* alone. Callers distinguish "unset" from "empty string" by passing undefined
|
|
19
|
+
* versus "".
|
|
20
|
+
*/
|
|
21
|
+
function encodeValue(value) {
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (typeof value === "boolean")
|
|
25
|
+
return value ? "1" : "0";
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Render a request as a command line.
|
|
30
|
+
*
|
|
31
|
+
* `encodePassword` is the transport's rule for password parameters. An empty
|
|
32
|
+
* password is sent as is: it means "no password", in every encoding.
|
|
33
|
+
*/
|
|
34
|
+
export function encodeRequest(request, encodePassword) {
|
|
35
|
+
const passwords = request.passwords ?? [];
|
|
36
|
+
const encode = (params) => encodeParams(params, passwords, encodePassword);
|
|
37
|
+
const [first = [], ...rest] = request.rows ?? [];
|
|
38
|
+
const line = buildCommandOrdered(request.command, [
|
|
39
|
+
...encode(request.params ?? []),
|
|
40
|
+
...encode(first),
|
|
41
|
+
]);
|
|
42
|
+
// A row is the parameters alone, with no command name before them.
|
|
43
|
+
return [line, ...rest.map((row) => buildCommandOrdered("", encode(row)).slice(1))].join("|");
|
|
44
|
+
}
|
|
45
|
+
function encodeParams(params, passwords, encodePassword) {
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const [key, value] of params) {
|
|
48
|
+
// Flags (`-uid`) are switches: present when true, absent otherwise.
|
|
49
|
+
if (key.startsWith("-")) {
|
|
50
|
+
if (value === true)
|
|
51
|
+
out.push([key, ""]);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (passwords.includes(key) && typeof value === "string" && value !== "") {
|
|
55
|
+
out.push([key, encodePassword(value)]);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const encoded = encodeValue(value);
|
|
59
|
+
if (encoded !== undefined)
|
|
60
|
+
out.push([key, encoded]);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../../src/core/command/request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAiDnD;;;;;;;;GAQG;AACH,SAAS,WAAW,CAAC,KAAiB;IACpC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACzD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAuB,EACvB,cAA4C;IAE5C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,CAAC,MAAoD,EAAE,EAAE,CACtE,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,OAAO,EAAE;QAChD,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;QAC/B,GAAG,MAAM,CAAC,KAAK,CAAC;KACjB,CAAC,CAAC;IACH,mEAAmE;IACnE,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/F,CAAC;AAED,SAAS,YAAY,CACnB,MAAoD,EACpD,SAA4B,EAC5B,cAA4C;IAE5C,MAAM,GAAG,GAAqC,EAAE,CAAC;IACjD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,oEAAoE;QACpE,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,KAAK,KAAK,IAAI;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;YACxC,SAAS;QACX,CAAC;QACD,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACzE,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACvC,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,OAAO,KAAK,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where each command's answer actually arrives.
|
|
3
|
+
*
|
|
4
|
+
* ServerQuery answers a query with nameless rows followed by `error`. The voice
|
|
5
|
+
* protocol often does not: it answers `servergrouplist` with a named
|
|
6
|
+
* `notifyservergrouplist` command, `clientgetuidfromclid` with
|
|
7
|
+
* `notifyclientuidfromclid`, and so on. A client that only collects nameless
|
|
8
|
+
* rows reports an empty success for every one of those — which this SDK did,
|
|
9
|
+
* and documented as a property of the protocol.
|
|
10
|
+
*
|
|
11
|
+
* Notifications carry no return_code, so nothing on the wire ties one to the
|
|
12
|
+
* command that caused it. The pairing is a property of the command, and this
|
|
13
|
+
* table is where it lives. It was measured command by command
|
|
14
|
+
* against TS3 3.13.8 and TS6 6.0.0-beta13.1, which answer identically — see
|
|
15
|
+
* scripts/probe-response-shapes.mts to re-run that measurement.
|
|
16
|
+
*
|
|
17
|
+
* Commands not listed answer with nameless rows, or with nothing.
|
|
18
|
+
*/
|
|
19
|
+
/** How the voice protocol delivers one command's data. */
|
|
20
|
+
export interface ResponseSpec {
|
|
21
|
+
/**
|
|
22
|
+
* Notifications carrying the data, sent before the command's `error`.
|
|
23
|
+
*
|
|
24
|
+
* Rows of these are attributed to the command whose `error` follows them.
|
|
25
|
+
* The same notification can also arrive unprompted — `notifyservergrouplist`
|
|
26
|
+
* after someone else edits a group — so they are still dispatched as events.
|
|
27
|
+
*/
|
|
28
|
+
readonly notifications?: readonly string[];
|
|
29
|
+
/**
|
|
30
|
+
* Keep only rows whose `invokerid` is the client itself.
|
|
31
|
+
*
|
|
32
|
+
* `notifychannelcreated` is broadcast for every channel anyone creates; the
|
|
33
|
+
* one answering our `channelcreate` is the one we invoked.
|
|
34
|
+
*/
|
|
35
|
+
readonly ownRowsOnly?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Notifications that answer after the `error`, matched on a request field.
|
|
38
|
+
*
|
|
39
|
+
* File transfer negotiation reports its key once the command has already
|
|
40
|
+
* succeeded, echoing the `clientftfid` the request carried; that echo is the
|
|
41
|
+
* only link between the two.
|
|
42
|
+
*/
|
|
43
|
+
readonly correlated?: {
|
|
44
|
+
readonly notifications: readonly string[];
|
|
45
|
+
/** Request parameter the answer echoes back. */
|
|
46
|
+
readonly key: string;
|
|
47
|
+
/**
|
|
48
|
+
* A notification that reports failure rather than data. Its `status` is an
|
|
49
|
+
* ordinary server error id: an `ftinitupload` refused for permissions
|
|
50
|
+
* answers `error id=0` and then this with `status=2568`.
|
|
51
|
+
*/
|
|
52
|
+
readonly failure?: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** The table behind {@link responseSpec}, by command name; exported for the tests that check it against the name tables. */
|
|
56
|
+
export declare const RESPONSES: Readonly<Record<string, ResponseSpec>>;
|
|
57
|
+
/** The response shape of a command, or undefined for plain rows. */
|
|
58
|
+
export declare function responseSpec(command: string): ResponseSpec | undefined;
|
|
59
|
+
//# sourceMappingURL=responses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../../src/core/command/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE;QACpB,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1C,gDAAgD;QAChD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB;;;;WAIG;QACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAYD,4HAA4H;AAC5H,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAyC5D,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEtE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where each command's answer actually arrives.
|
|
3
|
+
*
|
|
4
|
+
* ServerQuery answers a query with nameless rows followed by `error`. The voice
|
|
5
|
+
* protocol often does not: it answers `servergrouplist` with a named
|
|
6
|
+
* `notifyservergrouplist` command, `clientgetuidfromclid` with
|
|
7
|
+
* `notifyclientuidfromclid`, and so on. A client that only collects nameless
|
|
8
|
+
* rows reports an empty success for every one of those — which this SDK did,
|
|
9
|
+
* and documented as a property of the protocol.
|
|
10
|
+
*
|
|
11
|
+
* Notifications carry no return_code, so nothing on the wire ties one to the
|
|
12
|
+
* command that caused it. The pairing is a property of the command, and this
|
|
13
|
+
* table is where it lives. It was measured command by command
|
|
14
|
+
* against TS3 3.13.8 and TS6 6.0.0-beta13.1, which answer identically — see
|
|
15
|
+
* scripts/probe-response-shapes.mts to re-run that measurement.
|
|
16
|
+
*
|
|
17
|
+
* Commands not listed answer with nameless rows, or with nothing.
|
|
18
|
+
*/
|
|
19
|
+
const inline = (...notifications) => ({ notifications });
|
|
20
|
+
const fileTransfer = (answer) => ({
|
|
21
|
+
correlated: {
|
|
22
|
+
notifications: [answer, "notifystatusfiletransfer"],
|
|
23
|
+
key: "clientftfid",
|
|
24
|
+
failure: "notifystatusfiletransfer",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
/** The table behind {@link responseSpec}, by command name; exported for the tests that check it against the name tables. */
|
|
28
|
+
export const RESPONSES = {
|
|
29
|
+
banlist: inline("notifybanlist"),
|
|
30
|
+
channelclientpermlist: inline("notifychannelclientpermlist"),
|
|
31
|
+
channelcreate: { notifications: ["notifychannelcreated"], ownRowsOnly: true },
|
|
32
|
+
channelgetdescription: inline("notifychanneledited"),
|
|
33
|
+
channelgroupadd: inline("notifychannelgrouplist"),
|
|
34
|
+
channelgroupclientlist: inline("notifychannelgroupclientlist"),
|
|
35
|
+
channelgroupcopy: inline("notifychannelgrouplist"),
|
|
36
|
+
channelgrouplist: inline("notifychannelgrouplist"),
|
|
37
|
+
channelgrouppermlist: inline("notifychannelgrouppermlist"),
|
|
38
|
+
channelpermlist: inline("notifychannelpermlist"),
|
|
39
|
+
clientdbfind: inline("notifyclientdbfind"),
|
|
40
|
+
clientdblist: inline("notifyclientdblist"),
|
|
41
|
+
clientgetdbidfromuid: inline("notifyclientdbidfromuid"),
|
|
42
|
+
clientgetids: inline("notifyclientids"),
|
|
43
|
+
clientgetnamefromdbid: inline("notifyclientnamefromdbid"),
|
|
44
|
+
clientgetnamefromuid: inline("notifyclientnamefromuid"),
|
|
45
|
+
clientgetuidfromclid: inline("notifyclientuidfromclid"),
|
|
46
|
+
clientpermlist: inline("notifyclientpermlist"),
|
|
47
|
+
complainlist: inline("notifycomplainlist"),
|
|
48
|
+
ftgetfileinfo: inline("notifyfileinfo"),
|
|
49
|
+
ftgetfilelist: inline("notifyfilelist"),
|
|
50
|
+
ftinitdownload: fileTransfer("notifystartdownload"),
|
|
51
|
+
ftinitupload: fileTransfer("notifystartupload"),
|
|
52
|
+
ftlist: inline("notifyfiletransferlist"),
|
|
53
|
+
logview: inline("notifyserverlog"),
|
|
54
|
+
messageget: inline("notifymessage"),
|
|
55
|
+
messagelist: inline("notifymessagelist"),
|
|
56
|
+
permfind: inline("notifypermfind"),
|
|
57
|
+
permissionlist: inline("notifypermissionlist"),
|
|
58
|
+
permoverview: inline("notifypermoverview"),
|
|
59
|
+
privilegekeyadd: inline("notifytokenadd"),
|
|
60
|
+
privilegekeylist: inline("notifytokenlist"),
|
|
61
|
+
servergroupadd: inline("notifyservergrouplist"),
|
|
62
|
+
servergroupclientlist: inline("notifyservergroupclientlist"),
|
|
63
|
+
servergroupcopy: inline("notifyservergrouplist"),
|
|
64
|
+
servergrouplist: inline("notifyservergrouplist"),
|
|
65
|
+
servergrouppermlist: inline("notifyservergrouppermlist"),
|
|
66
|
+
servergroupsbyclientid: inline("notifyservergroupsbyclientid"),
|
|
67
|
+
serverrequestconnectioninfo: inline("notifyserverconnectioninfo"),
|
|
68
|
+
servertemppasswordlist: inline("notifyservertemppasswordlist"),
|
|
69
|
+
};
|
|
70
|
+
/** The response shape of a command, or undefined for plain rows. */
|
|
71
|
+
export function responseSpec(command) {
|
|
72
|
+
return Object.hasOwn(RESPONSES, command) ? RESPONSES[command] : undefined;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=responses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responses.js","sourceRoot":"","sources":["../../../src/core/command/responses.ts"],"names":[],"mappings":"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"}
|