@honeybbq/teamspeak 1.0.0-beta.12 → 1.0.0-beta.13
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/README.md +40 -5
- package/dist/client/capabilities.d.ts +4 -4
- package/dist/client/capabilities.d.ts.map +1 -1
- package/dist/client/capabilities.js +3 -8
- package/dist/client/capabilities.js.map +1 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +50 -9
- package/dist/client/client.js.map +1 -1
- package/dist/client/crypto/identity.d.ts +2 -0
- package/dist/client/crypto/identity.d.ts.map +1 -1
- package/dist/client/crypto/identity.js +2 -2
- package/dist/client/crypto/identity.js.map +1 -1
- package/dist/client/crypto/index.d.ts +1 -1
- package/dist/client/crypto/index.d.ts.map +1 -1
- package/dist/client/crypto/index.js +1 -1
- package/dist/client/crypto/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/trace.d.ts +7 -0
- package/dist/client/trace.d.ts.map +1 -1
- package/dist/client/transport/handler.d.ts +2 -0
- package/dist/client/transport/handler.d.ts.map +1 -1
- package/dist/client/transport/handler.js +4 -0
- package/dist/client/transport/handler.js.map +1 -1
- package/dist/client/types.d.ts +9 -2
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -1
- package/dist/client/view.d.ts +3 -3
- package/dist/client/view.d.ts.map +1 -1
- package/dist/client/view.js.map +1 -1
- package/dist/core/api/channels.d.ts +7 -3
- package/dist/core/api/channels.d.ts.map +1 -1
- package/dist/core/api/channels.js +5 -3
- package/dist/core/api/channels.js.map +1 -1
- package/dist/core/api/clients.d.ts +38 -1
- package/dist/core/api/clients.d.ts.map +1 -1
- package/dist/core/api/clients.js +33 -2
- package/dist/core/api/clients.js.map +1 -1
- package/dist/core/api/command.d.ts +18 -1
- package/dist/core/api/command.d.ts.map +1 -1
- package/dist/core/api/command.js +7 -1
- package/dist/core/api/command.js.map +1 -1
- package/dist/core/api/instance.d.ts +10 -2
- package/dist/core/api/instance.d.ts.map +1 -1
- package/dist/core/api/instance.js +6 -2
- package/dist/core/api/instance.js.map +1 -1
- package/dist/core/api/moderation.d.ts +7 -0
- package/dist/core/api/moderation.d.ts.map +1 -1
- package/dist/core/api/moderation.js +7 -0
- package/dist/core/api/moderation.js.map +1 -1
- package/dist/core/api/parse.d.ts.map +1 -1
- package/dist/core/api/parse.js +1 -0
- package/dist/core/api/parse.js.map +1 -1
- package/dist/core/api/server.d.ts +9 -0
- package/dist/core/api/server.d.ts.map +1 -1
- package/dist/core/api/server.js +3 -0
- package/dist/core/api/server.js.map +1 -1
- package/dist/core/command/responses.d.ts.map +1 -1
- package/dist/core/command/responses.js +1 -0
- package/dist/core/command/responses.js.map +1 -1
- package/dist/core/events.d.ts +47 -5
- package/dist/core/events.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/notification-name.d.ts +4 -0
- package/dist/core/notification-name.d.ts.map +1 -1
- package/dist/core/notification-name.js +4 -0
- package/dist/core/notification-name.js.map +1 -1
- package/dist/core/notifications.d.ts +6 -2
- package/dist/core/notifications.d.ts.map +1 -1
- package/dist/core/notifications.js +32 -1
- package/dist/core/notifications.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +16 -0
- package/dist/core/types.js.map +1 -1
- package/dist/query/client.d.ts +12 -3
- package/dist/query/client.d.ts.map +1 -1
- package/dist/query/client.js +22 -15
- package/dist/query/client.js.map +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/options.d.ts +17 -5
- package/dist/query/options.d.ts.map +1 -1
- package/dist/query/options.js +1 -0
- package/dist/query/options.js.map +1 -1
- package/dist/query/ssh.d.ts.map +1 -1
- package/dist/query/ssh.js +4 -1
- package/dist/query/ssh.js.map +1 -1
- package/dist/query/stream.d.ts +8 -3
- package/dist/query/stream.d.ts.map +1 -1
- package/dist/query/stream.js +26 -3
- package/dist/query/stream.js.map +1 -1
- package/dist/query/tcp.js +1 -1
- package/dist/query/tcp.js.map +1 -1
- package/dist/query/transport.d.ts +35 -0
- package/dist/query/transport.d.ts.map +1 -1
- package/dist/query/web.d.ts +17 -5
- package/dist/query/web.d.ts.map +1 -1
- package/dist/query/web.js +38 -9
- package/dist/query/web.js.map +1 -1
- package/package.json +6 -5
- package/src/client/capabilities.ts +14 -11
- package/src/client/client.ts +47 -6
- package/src/client/crypto/identity.ts +2 -2
- package/src/client/crypto/index.ts +1 -0
- package/src/client/index.ts +2 -0
- package/src/client/trace.ts +7 -0
- package/src/client/transport/handler.ts +5 -0
- package/src/client/types.ts +9 -0
- package/src/client/view.ts +2 -3
- package/src/core/api/channels.ts +9 -3
- package/src/core/api/clients.ts +73 -2
- package/src/core/api/command.ts +22 -1
- package/src/core/api/instance.ts +20 -2
- package/src/core/api/moderation.ts +7 -0
- package/src/core/api/parse.ts +1 -0
- package/src/core/api/server.ts +12 -0
- package/src/core/command/responses.ts +1 -0
- package/src/core/events.ts +48 -5
- package/src/core/index.ts +2 -1
- package/src/core/notification-name.ts +4 -0
- package/src/core/notifications.ts +48 -17
- package/src/core/types.ts +14 -0
- package/src/query/client.ts +28 -15
- package/src/query/index.ts +1 -1
- package/src/query/options.ts +19 -5
- package/src/query/ssh.ts +6 -1
- package/src/query/stream.ts +31 -4
- package/src/query/tcp.ts +1 -1
- package/src/query/testing/live.ts +311 -0
- package/src/query/testing/ssh-keys.ts +67 -0
- package/src/query/transport.ts +36 -0
- package/src/query/web.ts +57 -14
|
@@ -435,6 +435,11 @@ export class PacketHandler {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
/** How many packets have been sent again for want of an acknowledgement. */
|
|
438
|
+
/** Whether clientek, sent under the dummy key, has not been acknowledged yet. */
|
|
439
|
+
get clientEkPending(): boolean {
|
|
440
|
+
return this.#dummyCommandIDs.size > 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
438
443
|
get resends(): number {
|
|
439
444
|
return this.#resends;
|
|
440
445
|
}
|
package/src/client/types.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ChannelSubscribedEvent,
|
|
5
5
|
ChannelUnsubscribedEvent,
|
|
6
6
|
ClientChannelGroupChangedEvent,
|
|
7
|
+
ClientChatClosedEvent,
|
|
7
8
|
ClientChatComposingEvent,
|
|
8
9
|
ClientID,
|
|
9
10
|
ServerGroupID,
|
|
@@ -122,6 +123,11 @@ export interface EventMap extends ServerEventMap, SessionEventMap {
|
|
|
122
123
|
* sends. Only the target receives it.
|
|
123
124
|
*/
|
|
124
125
|
clientChatComposing: ClientChatComposingEvent;
|
|
126
|
+
/**
|
|
127
|
+
* A client closing its private chat with this client, which `closePrivateChat`
|
|
128
|
+
* sends. Only a client the sender has messaged privately is told.
|
|
129
|
+
*/
|
|
130
|
+
clientChatClosed: ClientChatClosedEvent;
|
|
125
131
|
}
|
|
126
132
|
|
|
127
133
|
/**
|
|
@@ -251,6 +257,8 @@ const eventNameValues = {
|
|
|
251
257
|
PluginCommand: "pluginCommand",
|
|
252
258
|
/** A client typing a private message to this client. */
|
|
253
259
|
ClientChatComposing: "clientChatComposing",
|
|
260
|
+
/** A client closing its private chat with this client. */
|
|
261
|
+
ClientChatClosed: "clientChatClosed",
|
|
254
262
|
/** The view being complete; see `ServerView`. */
|
|
255
263
|
ViewReady: "viewReady",
|
|
256
264
|
/** The view changing; see `ServerView`. */
|
|
@@ -318,6 +326,7 @@ export type ServerEventName =
|
|
|
318
326
|
| "channelUnsubscribed"
|
|
319
327
|
| "pluginCommand"
|
|
320
328
|
| "clientChatComposing"
|
|
329
|
+
| "clientChatClosed"
|
|
321
330
|
| "poked"
|
|
322
331
|
| "voiceData"
|
|
323
332
|
| "notification";
|
package/src/client/view.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
type ChannelEditedEvent,
|
|
10
10
|
type ChannelID,
|
|
11
11
|
type ChannelMovedEvent,
|
|
12
|
-
type ClientEnterEvent,
|
|
13
12
|
type ClientEntry,
|
|
14
13
|
type ChannelGroup,
|
|
15
14
|
type ChannelGroupID,
|
|
@@ -186,7 +185,7 @@ export interface ServerView {
|
|
|
186
185
|
* The clients a client knows, by id. Every change to it, whoever makes it, is
|
|
187
186
|
* a change to the view, so it counts them.
|
|
188
187
|
*/
|
|
189
|
-
export class Roster extends Map<number,
|
|
188
|
+
export class Roster extends Map<number, ClientEntry> {
|
|
190
189
|
#changed: () => void = () => {};
|
|
191
190
|
|
|
192
191
|
/** Call `changed` after each change from now on. */
|
|
@@ -194,7 +193,7 @@ export class Roster extends Map<number, ClientEnterEvent> {
|
|
|
194
193
|
this.#changed = changed;
|
|
195
194
|
}
|
|
196
195
|
|
|
197
|
-
override set(id: number, entry:
|
|
196
|
+
override set(id: number, entry: ClientEntry): this {
|
|
198
197
|
super.set(id, entry);
|
|
199
198
|
this.#changed();
|
|
200
199
|
return this;
|
package/src/core/api/channels.ts
CHANGED
|
@@ -101,6 +101,10 @@ export interface ChannelSettings {
|
|
|
101
101
|
readonly deleteDelayMs?: number | undefined;
|
|
102
102
|
/** How the name is pronounced, for text-to-speech (`channel_name_phonetic`). */
|
|
103
103
|
readonly namePhonetic?: string | undefined;
|
|
104
|
+
/** The URL of the channel's banner image (`channel_banner_gfx_url`); needs `b_channel_modify_banner`. */
|
|
105
|
+
readonly bannerURL?: string | undefined;
|
|
106
|
+
/** How the banner is scaled (`channel_banner_mode`); needs `b_channel_modify_banner`. */
|
|
107
|
+
readonly bannerMode?: number | undefined;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
const settingNames: WireNames<ChannelSettings> = {
|
|
@@ -124,6 +128,8 @@ const settingNames: WireNames<ChannelSettings> = {
|
|
|
124
128
|
isDefault: "channel_flag_default",
|
|
125
129
|
deleteDelayMs: ["channel_delete_delay", inSeconds],
|
|
126
130
|
namePhonetic: "channel_name_phonetic",
|
|
131
|
+
bannerURL: "channel_banner_gfx_url",
|
|
132
|
+
bannerMode: "channel_banner_mode",
|
|
127
133
|
};
|
|
128
134
|
|
|
129
135
|
/** A new channel's settings. Only `name` is required; see {@link ChannelSettings}. */
|
|
@@ -251,9 +257,9 @@ const listFlags: ReadonlyArray<readonly [string, true]> = [
|
|
|
251
257
|
* All channels on the server, in the server's own sort order.
|
|
252
258
|
*
|
|
253
259
|
* Always asks for the topic, flags, voice, limits, icon, seconds empty and
|
|
254
|
-
* banners, which a bare `channellist` leaves out.
|
|
255
|
-
*
|
|
256
|
-
* `
|
|
260
|
+
* banners, which a bare `channellist` leaves out. `description`, `uid` and
|
|
261
|
+
* `filePath` are not part of `channellist` and stay empty or undefined;
|
|
262
|
+
* `getChannelInfo` has them.
|
|
257
263
|
*/
|
|
258
264
|
export async function listChannels(
|
|
259
265
|
client: CommandExecutor,
|
package/src/core/api/clients.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
str,
|
|
23
23
|
big,
|
|
24
24
|
num,
|
|
25
|
+
optNum,
|
|
25
26
|
date,
|
|
26
27
|
} from "./command.js";
|
|
27
28
|
import type { PagingOptions, Params, PermissionEntry, WireNames } from "./command.js";
|
|
@@ -108,6 +109,11 @@ export interface ClientEntry {
|
|
|
108
109
|
* the request (`client_talk_request`); any non-zero value is true. No flag.
|
|
109
110
|
*/
|
|
110
111
|
readonly isRequestingTalk?: boolean | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* When the client asked to talk (`client_talk_request`, a unix time; 0 on
|
|
114
|
+
* the wire when there is no request, which reads as undefined); no flag.
|
|
115
|
+
*/
|
|
116
|
+
readonly talkRequestedAt?: Date | undefined;
|
|
111
117
|
/** The message sent with a talk request (`client_talk_request_msg`); no flag. */
|
|
112
118
|
readonly talkRequestMessage?: string | undefined;
|
|
113
119
|
/** The client's description (`client_description`); no flag. */
|
|
@@ -116,7 +122,7 @@ export interface ClientEntry {
|
|
|
116
122
|
readonly metaData?: string | undefined;
|
|
117
123
|
/** The MD5 of the client's avatar (`client_flag_avatar`); "" for none; no flag. */
|
|
118
124
|
readonly avatar?: string | undefined;
|
|
119
|
-
/** The client's icon (`client_icon_id`)
|
|
125
|
+
/** The client's icon (`client_icon_id`), with `icon`; undefined for none as well as for absent. */
|
|
120
126
|
readonly iconID?: IconID | undefined;
|
|
121
127
|
/** How the nickname is pronounced (`client_nickname_phonetic`); no flag. */
|
|
122
128
|
readonly nicknamePhonetic?: string | undefined;
|
|
@@ -178,6 +184,11 @@ export function applyClientChanges(entry: ClientEntry, changes: ClientChanges):
|
|
|
178
184
|
for (const [key, value] of Object.entries(changes)) {
|
|
179
185
|
if (value !== undefined) next[key] = value;
|
|
180
186
|
}
|
|
187
|
+
// A withdrawn request is a 0 on the wire, which reads as no time and so is
|
|
188
|
+
// never in `changes`; the flag going false is what says so.
|
|
189
|
+
if (changes.isRequestingTalk === false && !("talkRequestedAt" in changes)) {
|
|
190
|
+
delete next["talkRequestedAt"];
|
|
191
|
+
}
|
|
181
192
|
return next as unknown as ClientEntry;
|
|
182
193
|
}
|
|
183
194
|
|
|
@@ -205,6 +216,8 @@ export interface ClientListOptions extends CommandOptions {
|
|
|
205
216
|
readonly ip?: boolean | undefined;
|
|
206
217
|
/** The client's badges (`-badges`). */
|
|
207
218
|
readonly badges?: boolean | undefined;
|
|
219
|
+
/** The client's icon (`-icon`), which fills `ClientEntry.iconID`. */
|
|
220
|
+
readonly icon?: boolean | undefined;
|
|
208
221
|
}
|
|
209
222
|
|
|
210
223
|
const listFlags: WireNames<ClientListOptions> = {
|
|
@@ -217,6 +230,7 @@ const listFlags: WireNames<ClientListOptions> = {
|
|
|
217
230
|
country: "-country",
|
|
218
231
|
ip: "-ip",
|
|
219
232
|
badges: "-badges",
|
|
233
|
+
icon: "-icon",
|
|
220
234
|
};
|
|
221
235
|
|
|
222
236
|
/** Every client currently connected. */
|
|
@@ -723,6 +737,37 @@ export async function getClientUIDByID(
|
|
|
723
737
|
return asUID(str(firstRow(rows, "clientgetuidfromclid", "cluid"), "cluid"));
|
|
724
738
|
}
|
|
725
739
|
|
|
740
|
+
/** What `clientgetuidfromclid` says about a connected client. */
|
|
741
|
+
export interface ClientUIDInfo {
|
|
742
|
+
/** The client's id (`clid`). */
|
|
743
|
+
readonly clientID: ClientID;
|
|
744
|
+
/** The client's unique identifier (`cluid`). */
|
|
745
|
+
readonly uid: UID;
|
|
746
|
+
/** The client's nickname (`nickname`). */
|
|
747
|
+
readonly nickname: string;
|
|
748
|
+
/** The client's myTeamSpeak id (`mytsid`); "" for none, as TS3 3.13.8 and TS6 6.0.0-beta13.1 sent it for a client without one. */
|
|
749
|
+
readonly mytsid: string;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* The uid, nickname and myTeamSpeak id of a connected client.
|
|
754
|
+
* {@link getClientUIDByID} is the same command, keeping only the uid.
|
|
755
|
+
*/
|
|
756
|
+
export async function getClientUIDInfoByID(
|
|
757
|
+
client: CommandExecutor,
|
|
758
|
+
clid: ClientID,
|
|
759
|
+
options?: CommandOptions,
|
|
760
|
+
): Promise<ClientUIDInfo> {
|
|
761
|
+
const rows = await query(client, "clientgetuidfromclid", [["clid", clid]], options);
|
|
762
|
+
const row = firstRow(rows, "clientgetuidfromclid", "cluid");
|
|
763
|
+
return {
|
|
764
|
+
clientID: asClientID(uint(row, "clid", U16)),
|
|
765
|
+
uid: asUID(str(row, "cluid")),
|
|
766
|
+
nickname: str(row, "nickname"),
|
|
767
|
+
mytsid: str(row, "mytsid"),
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
726
771
|
/** The persistent database id behind a uid. */
|
|
727
772
|
export async function getClientDBIDByUID(
|
|
728
773
|
client: CommandExecutor,
|
|
@@ -739,10 +784,16 @@ export interface ClientDatabaseName {
|
|
|
739
784
|
readonly databaseID: ClientDBID;
|
|
740
785
|
/** The nickname the client last used. */
|
|
741
786
|
readonly name: string;
|
|
787
|
+
/** The client's unique identifier (`cluid`); "" when the server sent none. */
|
|
788
|
+
readonly uid: UID;
|
|
742
789
|
}
|
|
743
790
|
|
|
744
791
|
function parseDatabaseName(row: Record<string, string>): ClientDatabaseName {
|
|
745
|
-
return {
|
|
792
|
+
return {
|
|
793
|
+
databaseID: asClientDBID(big(row, "cldbid")),
|
|
794
|
+
name: str(row, "name"),
|
|
795
|
+
uid: asUID(str(row, "cluid")),
|
|
796
|
+
};
|
|
746
797
|
}
|
|
747
798
|
|
|
748
799
|
/** The database id and nickname recorded for a database id. */
|
|
@@ -805,8 +856,22 @@ export interface ClientDatabaseEntry {
|
|
|
805
856
|
readonly description: string;
|
|
806
857
|
/** The address the client last connected from. */
|
|
807
858
|
readonly lastIP: string;
|
|
859
|
+
/** The client's query account name (`client_login_name`); "" for none; `clientdblist` rows only. */
|
|
860
|
+
readonly loginName?: string | undefined;
|
|
861
|
+
/** The MD5 of the client's avatar (`client_flag_avatar`), "" for none; `getClientDatabaseInfo` only. */
|
|
862
|
+
readonly avatarHash?: string | undefined;
|
|
863
|
+
/** Bytes the client has uploaded in all (`client_total_bytes_uploaded`); `getClientDatabaseInfo` only. */
|
|
864
|
+
readonly totalBytesUploaded?: number | undefined;
|
|
865
|
+
/** Bytes the client has downloaded in all (`client_total_bytes_downloaded`); `getClientDatabaseInfo` only. */
|
|
866
|
+
readonly totalBytesDownloaded?: number | undefined;
|
|
867
|
+
/** Bytes the client has uploaded this month (`client_month_bytes_uploaded`); `getClientDatabaseInfo` only. */
|
|
868
|
+
readonly monthBytesUploaded?: number | undefined;
|
|
869
|
+
/** Bytes the client has downloaded this month (`client_month_bytes_downloaded`); `getClientDatabaseInfo` only. */
|
|
870
|
+
readonly monthBytesDownloaded?: number | undefined;
|
|
808
871
|
}
|
|
809
872
|
|
|
873
|
+
const optStr = (row: Record<string, string>, key: string): string | undefined => row[key];
|
|
874
|
+
|
|
810
875
|
const parseDatabaseEntry = (row: Record<string, string>): ClientDatabaseEntry => ({
|
|
811
876
|
databaseID: asClientDBID(big(row, "cldbid")),
|
|
812
877
|
uid: asUID(str(row, "client_unique_identifier")),
|
|
@@ -816,6 +881,12 @@ const parseDatabaseEntry = (row: Record<string, string>): ClientDatabaseEntry =>
|
|
|
816
881
|
totalConnections: num(row, "client_totalconnections"),
|
|
817
882
|
description: str(row, "client_description"),
|
|
818
883
|
lastIP: str(row, "client_lastip"),
|
|
884
|
+
loginName: optStr(row, "client_login_name"),
|
|
885
|
+
avatarHash: optStr(row, "client_flag_avatar"),
|
|
886
|
+
totalBytesUploaded: optNum(row, "client_total_bytes_uploaded"),
|
|
887
|
+
totalBytesDownloaded: optNum(row, "client_total_bytes_downloaded"),
|
|
888
|
+
monthBytesUploaded: optNum(row, "client_month_bytes_uploaded"),
|
|
889
|
+
monthBytesDownloaded: optNum(row, "client_month_bytes_downloaded"),
|
|
819
890
|
});
|
|
820
891
|
|
|
821
892
|
/** Which slice of the client database {@link listClientDatabase} reads. */
|
package/src/core/api/command.ts
CHANGED
|
@@ -367,7 +367,7 @@ const date = (row: Record<string, string>, key: string): Date | undefined => {
|
|
|
367
367
|
const durationMs = (row: Record<string, string>, key: string, unitMs: number): number =>
|
|
368
368
|
num(row, key) * unitMs;
|
|
369
369
|
|
|
370
|
-
export { str, big, num, uint, U16, U32, bool, icon, date, durationMs };
|
|
370
|
+
export { str, big, num, optNum, uint, U16, U32, bool, icon, date, durationMs };
|
|
371
371
|
|
|
372
372
|
// ---- Read tables ------------------------------------------------------------
|
|
373
373
|
|
|
@@ -611,6 +611,21 @@ export interface Channel {
|
|
|
611
611
|
readonly bannerURL?: string | undefined;
|
|
612
612
|
/** How the banner is scaled (`channel_banner_mode`). */
|
|
613
613
|
readonly bannerMode?: number | undefined;
|
|
614
|
+
/**
|
|
615
|
+
* How long the channel has had no client, in milliseconds (`seconds_empty`,
|
|
616
|
+
* which the wire carries in seconds), as of the answer. Undefined for a
|
|
617
|
+
* channel with a client in it: both TS3 3.13.8 and TS6 6.0.0-beta13.1 send
|
|
618
|
+
* -1 for it, which is not a duration. `listChannels` and `getChannelInfo`
|
|
619
|
+
* report it; a notification never does.
|
|
620
|
+
*/
|
|
621
|
+
readonly emptyForMs?: number | undefined;
|
|
622
|
+
/**
|
|
623
|
+
* The channel's file storage quota (`channel_storage_quota`); TS6 only.
|
|
624
|
+
* TS6 6.0.0-beta13.1 sent 4294967295 for a channel with none set.
|
|
625
|
+
*/
|
|
626
|
+
readonly storageQuota?: number | undefined;
|
|
627
|
+
/** The channel's directory on the server (`channel_filepath`); `getChannelInfo` only. */
|
|
628
|
+
readonly filePath?: string | undefined;
|
|
614
629
|
}
|
|
615
630
|
|
|
616
631
|
/**
|
|
@@ -681,6 +696,12 @@ export const channelFields: {
|
|
|
681
696
|
isForcedSilence: { keys: ["channel_forced_silence"], read: bool },
|
|
682
697
|
bannerURL: { keys: ["channel_banner_gfx_url"], read: str },
|
|
683
698
|
bannerMode: { keys: ["channel_banner_mode"], read: num },
|
|
699
|
+
emptyForMs: {
|
|
700
|
+
keys: ["seconds_empty"],
|
|
701
|
+
read: (row, key) => (num(row, key) < 0 ? undefined : durationMs(row, key, SECOND)),
|
|
702
|
+
},
|
|
703
|
+
storageQuota: { keys: ["channel_storage_quota"], read: num },
|
|
704
|
+
filePath: { keys: ["channel_filepath"], read: str },
|
|
684
705
|
};
|
|
685
706
|
|
|
686
707
|
function parseChannel(row: Record<string, string>): Channel {
|
package/src/core/api/instance.ts
CHANGED
|
@@ -60,6 +60,14 @@ export interface VirtualServer {
|
|
|
60
60
|
readonly machineID: string | undefined;
|
|
61
61
|
/** Its unique identifier, with `uid`; "" otherwise (`virtualserver_unique_identifier`). */
|
|
62
62
|
readonly uid: string;
|
|
63
|
+
/** Its UUID, with `uid`; TS6 only (`virtualserver_uuid`). */
|
|
64
|
+
readonly uuid?: string | undefined;
|
|
65
|
+
/** Whether guests may create channels; TS6 only (`guest_can_create_channels`). */
|
|
66
|
+
readonly guestCanCreateChannels?: boolean | undefined;
|
|
67
|
+
/** Whether guests may create a homebase; TS6 only (`guest_can_create_homebase`). */
|
|
68
|
+
readonly guestCanCreateHomebase?: boolean | undefined;
|
|
69
|
+
/** Whether the server is on the public server list; TS6 only (`virtualserver_weblist_enabled`). */
|
|
70
|
+
readonly weblistEnabled?: boolean | undefined;
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
const optional = <T>(row: Record<string, string>, key: string, read: () => T): T | undefined =>
|
|
@@ -83,6 +91,16 @@ const parseVirtualServer = (row: Record<string, string>): VirtualServer => ({
|
|
|
83
91
|
autoStart: optional(row, "virtualserver_autostart", () => bool(row, "virtualserver_autostart")),
|
|
84
92
|
machineID: row["virtualserver_machine_id"],
|
|
85
93
|
uid: str(row, "virtualserver_unique_identifier"),
|
|
94
|
+
uuid: row["virtualserver_uuid"],
|
|
95
|
+
guestCanCreateChannels: optional(row, "guest_can_create_channels", () =>
|
|
96
|
+
bool(row, "guest_can_create_channels"),
|
|
97
|
+
),
|
|
98
|
+
guestCanCreateHomebase: optional(row, "guest_can_create_homebase", () =>
|
|
99
|
+
bool(row, "guest_can_create_homebase"),
|
|
100
|
+
),
|
|
101
|
+
weblistEnabled: optional(row, "virtualserver_weblist_enabled", () =>
|
|
102
|
+
bool(row, "virtualserver_weblist_enabled"),
|
|
103
|
+
),
|
|
86
104
|
});
|
|
87
105
|
|
|
88
106
|
/** Which servers {@link listServers} reports, and with what. */
|
|
@@ -109,8 +127,8 @@ const serverListFlags: WireNames<ServerListOptions> = {
|
|
|
109
127
|
*
|
|
110
128
|
* Needs `b_serverinstance_virtualserver_list`, which a voice client's Server
|
|
111
129
|
* Admin lacks (error 2568). TS6 also reports `guest_can_create_channels`,
|
|
112
|
-
* `guest_can_create_homebase` and `virtualserver_weblist_enabled`,
|
|
113
|
-
*
|
|
130
|
+
* `guest_can_create_homebase` and `virtualserver_weblist_enabled`, and its
|
|
131
|
+
* UUID with `uid`; TS3 sends none of them.
|
|
114
132
|
*/
|
|
115
133
|
export async function listServers(
|
|
116
134
|
client: CommandExecutor,
|
|
@@ -247,6 +247,13 @@ export async function addBan(
|
|
|
247
247
|
* uid, and on TS6 its myTeamSpeak id — and over ServerQuery answers one
|
|
248
248
|
* `banid` row for each; returning the first left the others to be found by
|
|
249
249
|
* hand. The voice protocol reports no ids, so there this resolves to `[]`.
|
|
250
|
+
*
|
|
251
|
+
* Measured on TS3 3.13.8 and TS6 beta13.1: two bans, one for the uid and one
|
|
252
|
+
* for the address. TS6 has also answered with no `banid` at all though both
|
|
253
|
+
* bans were made (about once in a hundred calls over SSH and WebQuery), so an
|
|
254
|
+
* empty result does not mean no ban. A caller that must undo the ban lists the
|
|
255
|
+
* bans before and after and removes what is new: a ban on the address refuses
|
|
256
|
+
* every later connection from it.
|
|
250
257
|
*/
|
|
251
258
|
export async function banClient(
|
|
252
259
|
client: CommandExecutor,
|
package/src/core/api/parse.ts
CHANGED
|
@@ -89,6 +89,7 @@ export const clientFields: {
|
|
|
89
89
|
talkPower: { keys: ["client_talk_power"], read: num },
|
|
90
90
|
// The wire value is when the request was made; any non-zero is a request.
|
|
91
91
|
isRequestingTalk: flag("client_talk_request"),
|
|
92
|
+
talkRequestedAt: { keys: ["client_talk_request"], read: date },
|
|
92
93
|
talkRequestMessage: text("client_talk_request_msg"),
|
|
93
94
|
description: text("client_description"),
|
|
94
95
|
metaData: text("client_meta_data"),
|
package/src/core/api/server.ts
CHANGED
|
@@ -250,6 +250,10 @@ export interface WhoAmI {
|
|
|
250
250
|
* it.
|
|
251
251
|
*/
|
|
252
252
|
readonly uid: UID;
|
|
253
|
+
/** The virtual server's UUID (`virtualserver_uuid`); TS6 only, undefined when not sent. */
|
|
254
|
+
readonly serverUUID?: string | undefined;
|
|
255
|
+
/** The server this client first connected to (`client_origin_server_id`); 0 when not sent. */
|
|
256
|
+
readonly originServerID: ServerID;
|
|
253
257
|
}
|
|
254
258
|
|
|
255
259
|
/** Which server and client this connection is, as the server sees it. */
|
|
@@ -266,6 +270,8 @@ export async function whoAmI(client: CommandExecutor, options?: CommandOptions):
|
|
|
266
270
|
databaseID: asClientDBID(big(row, "client_database_id")),
|
|
267
271
|
loginName: str(row, "client_login_name"),
|
|
268
272
|
uid: asUID(str(row, "client_unique_identifier")),
|
|
273
|
+
serverUUID: row["virtualserver_uuid"],
|
|
274
|
+
originServerID: asServerID(big(row, "client_origin_server_id")),
|
|
269
275
|
};
|
|
270
276
|
}
|
|
271
277
|
|
|
@@ -674,6 +680,11 @@ export interface TempPassword {
|
|
|
674
680
|
readonly endsAt: Date | undefined;
|
|
675
681
|
/** The channel a client joining with it lands in, or 0 for the default channel. */
|
|
676
682
|
readonly targetChannelID: ChannelID;
|
|
683
|
+
/**
|
|
684
|
+
* That channel's password, in cleartext (`tcpw`); "" for none. A secret like
|
|
685
|
+
* `password`: store it where it is needed, and do not log it.
|
|
686
|
+
*/
|
|
687
|
+
readonly targetChannelPassword: string;
|
|
677
688
|
}
|
|
678
689
|
|
|
679
690
|
/** The server's temporary passwords. */
|
|
@@ -690,6 +701,7 @@ export async function listTempPasswords(
|
|
|
690
701
|
startsAt: date(row, "start"),
|
|
691
702
|
endsAt: date(row, "end"),
|
|
692
703
|
targetChannelID: asChannelID(big(row, "tcid")),
|
|
704
|
+
targetChannelPassword: str(row, "tcpw"),
|
|
693
705
|
}));
|
|
694
706
|
}
|
|
695
707
|
|
|
@@ -88,6 +88,7 @@ const fileTransfer = (answer: string): ResponseSpec => ({
|
|
|
88
88
|
export const RESPONSES: Readonly<Record<string, ResponseSpec>> = {
|
|
89
89
|
apikeyadd: inline("notifyapikeyadd"),
|
|
90
90
|
apikeylist: inline("notifyapikeylist"),
|
|
91
|
+
banfind: inline("notifybanfind"),
|
|
91
92
|
banlist: inline("notifybanlist"),
|
|
92
93
|
channelclientpermlist: inline("notifychannelclientpermlist"),
|
|
93
94
|
channelcreate: { notifications: ["notifychannelcreated"], ownRowsOnly: true },
|
package/src/core/events.ts
CHANGED
|
@@ -44,6 +44,11 @@ export interface ClientMovedEvent {
|
|
|
44
44
|
readonly invokerUID: UID;
|
|
45
45
|
/** The channel the client is now in. */
|
|
46
46
|
readonly targetChannelID: ChannelID;
|
|
47
|
+
/**
|
|
48
|
+
* The channel the client came from (`cfid`). Absent when the server sends
|
|
49
|
+
* none: TS3 3.13.8 sends no `cfid` on a move, TS6 6.0.0-beta13.1 does.
|
|
50
|
+
*/
|
|
51
|
+
readonly fromChannelID?: ChannelID | undefined;
|
|
47
52
|
/** Why the client moved; compare it with `Reason`. */
|
|
48
53
|
readonly reason: number;
|
|
49
54
|
/** The message that came with the move, such as a channel kick's reason; "" when none. */
|
|
@@ -65,6 +70,16 @@ export interface ClientLeaveEvent {
|
|
|
65
70
|
readonly reason: number;
|
|
66
71
|
/** The client that left. */
|
|
67
72
|
readonly clientID: ClientID;
|
|
73
|
+
/**
|
|
74
|
+
* The channel the client was in (`cfid`); absent when the server sent none.
|
|
75
|
+
* Both servers sent it on every leave measured.
|
|
76
|
+
*/
|
|
77
|
+
readonly fromChannelID?: ChannelID | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* The channel the client went to (`ctid`), named as on the move event; 0
|
|
80
|
+
* when it left the server, and absent when the server sent none.
|
|
81
|
+
*/
|
|
82
|
+
readonly targetChannelID?: ChannelID | undefined;
|
|
68
83
|
/** Who kicked or banned the client; empty otherwise. */
|
|
69
84
|
readonly invokerName: string;
|
|
70
85
|
/** The kicker's unique identifier; empty unless kicked or banned. */
|
|
@@ -96,12 +111,28 @@ export interface KickedEvent {
|
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
/**
|
|
99
|
-
* A client coming into view: the
|
|
100
|
-
*
|
|
101
|
-
* in every record. The fields only
|
|
102
|
-
* here.
|
|
114
|
+
* A client coming into view: the record `listClients` returns, so one type
|
|
115
|
+
* describes a client however it was learned of, with what only the
|
|
116
|
+
* notification says. Its own id is `id`, as in every record. The fields only
|
|
117
|
+
* `clientlist -times` reports are undefined here.
|
|
118
|
+
*
|
|
119
|
+
* `fromChannelID` and `reason` belong to the event: the roster and
|
|
120
|
+
* `view.clients` keep the record without them.
|
|
103
121
|
*/
|
|
104
|
-
export
|
|
122
|
+
export interface ClientEnterEvent extends ClientEntry {
|
|
123
|
+
/**
|
|
124
|
+
* The channel the client came from (`cfid`); 0 for a client that joined the
|
|
125
|
+
* server, and absent when the server sent none. Measured on TS3 3.13.8 and
|
|
126
|
+
* TS6 6.0.0-beta13.1: 0 on a join and on a client listed by a subscription.
|
|
127
|
+
*/
|
|
128
|
+
readonly fromChannelID?: ChannelID | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* Why the client came into view (`reasonid`); compare it with `Reason`.
|
|
131
|
+
* Measured on both servers: 0 on a join, 2 (`Reason.Subscription`) for a
|
|
132
|
+
* client that was already there and came into view by a subscription.
|
|
133
|
+
*/
|
|
134
|
+
readonly reason?: number | undefined;
|
|
135
|
+
}
|
|
105
136
|
|
|
106
137
|
/** This client being poked. */
|
|
107
138
|
export interface PokeEvent {
|
|
@@ -311,6 +342,18 @@ export interface ClientChatComposingEvent {
|
|
|
311
342
|
readonly uid: UID;
|
|
312
343
|
}
|
|
313
344
|
|
|
345
|
+
/**
|
|
346
|
+
* A client closed its private chat with this client. It is delivered only to a
|
|
347
|
+
* client the sender has sent a private message to. Delivered by the voice
|
|
348
|
+
* client only.
|
|
349
|
+
*/
|
|
350
|
+
export interface ClientChatClosedEvent {
|
|
351
|
+
/** The client that closed the chat. */
|
|
352
|
+
readonly clientID: ClientID;
|
|
353
|
+
/** Its unique identifier. */
|
|
354
|
+
readonly uid: UID;
|
|
355
|
+
}
|
|
356
|
+
|
|
314
357
|
/** Audio from another client, in the channel or whispered. */
|
|
315
358
|
export interface VoiceDataEvent {
|
|
316
359
|
/** Who is speaking. */
|
package/src/core/index.ts
CHANGED
|
@@ -88,7 +88,7 @@ export { NotificationName } from "./notification-name.js";
|
|
|
88
88
|
export { PropertyName } from "./property-name.js";
|
|
89
89
|
export type { Permission } from "./permission.js";
|
|
90
90
|
|
|
91
|
-
export { noopLogger, consoleLogger, ClientStatus } from "./types.js";
|
|
91
|
+
export { noopLogger, consoleLogger, ClientStatus, serverFamilyForVersion } from "./types.js";
|
|
92
92
|
export type {
|
|
93
93
|
ClientVersion,
|
|
94
94
|
Listener,
|
|
@@ -118,6 +118,7 @@ export type {
|
|
|
118
118
|
ChannelUnsubscribedEvent,
|
|
119
119
|
PluginCommandEvent,
|
|
120
120
|
ClientChatComposingEvent,
|
|
121
|
+
ClientChatClosedEvent,
|
|
121
122
|
ClientUpdatedEvent,
|
|
122
123
|
ClientServerGroupEvent,
|
|
123
124
|
ClientServerGroupAddedEvent,
|
|
@@ -17,8 +17,12 @@ const notificationNameValues = {
|
|
|
17
17
|
APIKeyAdd: "notifyapikeyadd",
|
|
18
18
|
/** `notifyapikeylist`: Reply to apikeylist: one row per API key. */
|
|
19
19
|
APIKeyList: "notifyapikeylist",
|
|
20
|
+
/** `notifybanfind`: Reply to banfind: one row per ban rule that matches. */
|
|
21
|
+
BanFind: "notifybanfind",
|
|
20
22
|
/** `notifybanlist`: Reply to banlist: one row per ban rule on the virtual server. */
|
|
21
23
|
BanList: "notifybanlist",
|
|
24
|
+
/** `notifybanupdate`: A ban changed; sent to a ServerQuery connection registered for `bans`. An addition carries the ban row, a deletion its `banid`, a delete-all neither (TS6 6.0.0-beta13.1, `op` says which). */
|
|
25
|
+
BanUpdate: "notifybanupdate",
|
|
22
26
|
/** `notifychannelchanged`: A channel's properties changed and this client should fetch them again. */
|
|
23
27
|
ChannelChanged: "notifychannelchanged",
|
|
24
28
|
/** `notifychannelclientpermlist`: Reply to channelclientpermlist: one row per permission set for a client in a channel. */
|