chapterjs 0.3.0 → 0.5.0
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 +5 -3
- package/dist/assets/public.js +4 -4
- package/dist/cli/build.d.ts +6 -2
- package/dist/cli/build.js +17 -4
- package/dist/cli/dev.js +37 -6
- package/dist/cli/env.d.ts +5 -0
- package/dist/cli/env.js +39 -3
- package/dist/cli/project.d.ts +61 -9
- package/dist/cli/project.js +250 -44
- package/dist/cli/redis.d.ts +23 -0
- package/dist/cli/redis.js +69 -0
- package/dist/cli/start.js +54 -18
- package/dist/cli/test.js +112 -18
- package/dist/commands/command.d.ts +140 -5
- package/dist/commands/command.js +17 -2
- package/dist/commands/declaration.d.ts +17 -4
- package/dist/commands/declaration.js +128 -2
- package/dist/commands/router.js +78 -18
- package/dist/commands/tree.d.ts +25 -4
- package/dist/commands/tree.js +95 -26
- package/dist/commands/twins.d.ts +8 -0
- package/dist/commands/twins.js +24 -6
- package/dist/components/button.d.ts +8 -3
- package/dist/components/declaration.d.ts +2 -2
- package/dist/components/declaration.js +16 -12
- package/dist/components/render.js +5 -0
- package/dist/components/router.js +41 -0
- package/dist/components/select.d.ts +12 -5
- package/dist/core/bot.d.ts +7 -0
- package/dist/core/bot.js +44 -0
- package/dist/discord/api.d.ts +2 -0
- package/dist/discord/api.js +2 -0
- package/dist/discord/permissions.d.ts +4 -4
- package/dist/discord/permissions.js +13 -0
- package/dist/events/registry.d.ts +265 -243
- package/dist/events/registry.js +131 -100
- package/dist/index.d.ts +7 -4
- package/dist/index.js +2 -1
- package/dist/interactions/dispatch.js +4 -2
- package/dist/loader/loader.d.ts +16 -0
- package/dist/loader/loader.js +52 -7
- package/dist/loader/usage.d.ts +15 -0
- package/dist/loader/usage.js +40 -0
- package/dist/loader/watch.d.ts +5 -3
- package/dist/loader/watch.js +88 -17
- package/dist/messages/commands.js +3 -0
- package/dist/messages/messages.d.ts +1 -0
- package/dist/messages/translate.js +5 -1
- package/dist/presence/declaration.d.ts +49 -2
- package/dist/presence/declaration.js +122 -39
- package/dist/presence/presence.d.ts +65 -6
- package/dist/presence/presence.js +11 -0
- package/dist/presence/rotation.d.ts +32 -0
- package/dist/presence/rotation.js +167 -0
- package/dist/ranking/declaration.d.ts +12 -0
- package/dist/ranking/declaration.js +48 -0
- package/dist/ranking/ranking.d.ts +102 -0
- package/dist/ranking/ranking.js +129 -0
- package/dist/redis/client.d.ts +36 -0
- package/dist/redis/client.js +252 -0
- package/dist/redis/protocol.d.ts +22 -0
- package/dist/redis/protocol.js +99 -0
- package/dist/redis/url.d.ts +24 -0
- package/dist/redis/url.js +50 -0
- package/dist/rest/errors.d.ts +0 -1
- package/dist/rest/errors.js +15 -1
- package/dist/rest/rest.js +10 -1
- package/dist/store/backend.d.ts +49 -4
- package/dist/store/backend.js +66 -11
- package/dist/store/declaration.d.ts +3 -0
- package/dist/store/declaration.js +8 -3
- package/dist/store/file-backend.d.ts +3 -2
- package/dist/store/file-backend.js +47 -1
- package/dist/store/ipc-backend.d.ts +24 -1
- package/dist/store/ipc-backend.js +49 -0
- package/dist/store/memory-backend.d.ts +4 -2
- package/dist/store/memory-backend.js +27 -1
- package/dist/store/ranking-backend.d.ts +45 -0
- package/dist/store/ranking-backend.js +82 -0
- package/dist/store/redis-backend.d.ts +38 -0
- package/dist/store/redis-backend.js +245 -0
- package/dist/store/redis-ranking.d.ts +15 -0
- package/dist/store/redis-ranking.js +69 -0
- package/dist/store/scope.d.ts +17 -1
- package/dist/store/scope.js +32 -3
- package/dist/store/store.d.ts +39 -5
- package/dist/store/store.js +79 -32
- package/dist/structures/channel.d.ts +13 -0
- package/dist/structures/channel.js +24 -0
- package/dist/structures/context.d.ts +3 -0
- package/dist/structures/entities.js +1 -0
- package/dist/structures/interaction.d.ts +6 -0
- package/dist/structures/interaction.js +20 -0
- package/dist/structures/invite.d.ts +6 -0
- package/dist/structures/invite.js +11 -0
- package/dist/structures/message.d.ts +17 -0
- package/dist/structures/message.js +33 -0
- package/dist/structures/wait.d.ts +105 -0
- package/dist/structures/wait.js +85 -0
- package/dist/testing/harness.d.ts +48 -8
- package/dist/testing/harness.js +156 -36
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/smoke.js +112 -1
- package/dist/testing/world.js +51 -2
- package/dist/util/call-site.d.ts +4 -0
- package/dist/util/call-site.js +22 -0
- package/dist/voice/connection.d.ts +2 -0
- package/dist/voice/connection.js +15 -1
- package/dist/voice/usage.d.ts +3 -2
- package/dist/voice/usage.js +7 -19
- package/dist/waiting/usage.d.ts +5 -0
- package/dist/waiting/usage.js +19 -0
- package/dist/waiting/waiters.d.ts +48 -0
- package/dist/waiting/waiters.js +107 -0
- package/package.json +1 -1
package/dist/voice/connection.js
CHANGED
|
@@ -218,6 +218,8 @@ export class VoiceManager {
|
|
|
218
218
|
#secure;
|
|
219
219
|
#joinTimeout;
|
|
220
220
|
#endedGrace;
|
|
221
|
+
/** Why voice can't be joined here (`chapterjs test`), or `null`. */
|
|
222
|
+
#unavailable;
|
|
221
223
|
constructor(ctx, gateway, options) {
|
|
222
224
|
this.#ctx = ctx;
|
|
223
225
|
this.#gateway = gateway;
|
|
@@ -225,6 +227,7 @@ export class VoiceManager {
|
|
|
225
227
|
this.#secure = options.secure ?? true;
|
|
226
228
|
this.#joinTimeout = options.joinTimeout ?? JOIN_TIMEOUT;
|
|
227
229
|
this.#endedGrace = options.endedGrace ?? 3000;
|
|
230
|
+
this.#unavailable = options.unavailable ?? null;
|
|
228
231
|
}
|
|
229
232
|
/** The connection of the bot in a server, if it is in voice there. */
|
|
230
233
|
connectionOf(guildId) {
|
|
@@ -249,6 +252,8 @@ export class VoiceManager {
|
|
|
249
252
|
* same server. Resolves once audio can be sent.
|
|
250
253
|
*/
|
|
251
254
|
async join(channel, options = {}) {
|
|
255
|
+
if (this.#unavailable !== null)
|
|
256
|
+
throw new Error(this.#unavailable);
|
|
252
257
|
const guild = channel.guild;
|
|
253
258
|
const me = guild.me;
|
|
254
259
|
const permissions = me.permissionsIn(channel);
|
|
@@ -273,7 +278,16 @@ export class VoiceManager {
|
|
|
273
278
|
? existing
|
|
274
279
|
: new VoiceConnection(this, guild.id, channel.id);
|
|
275
280
|
this.#connections.set(guild.id, connection);
|
|
276
|
-
|
|
281
|
+
try {
|
|
282
|
+
await this.#request(guild.id, channel, options);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
// Made again here: the reason comes from a timer or from leave(),
|
|
286
|
+
// whose lines are not the one of the file that asked to join.
|
|
287
|
+
throw error instanceof Error
|
|
288
|
+
? new Error(error.message, { cause: error })
|
|
289
|
+
: error;
|
|
290
|
+
}
|
|
277
291
|
if (channel.isStage)
|
|
278
292
|
await this.#speakOnStage(guild.id, channel);
|
|
279
293
|
return connection;
|
package/dist/voice/usage.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import type { CodeUse } from '../loader/usage.js';
|
|
2
|
+
/** Joining voice, as `loader/usage.ts` looks for it. */
|
|
3
|
+
export declare const VOICE_USE: CodeUse;
|
|
1
4
|
/** Whether some code joins a voice channel. */
|
|
2
5
|
export declare function joinsVoice(code: string): boolean;
|
|
3
|
-
/** Whether a file of `src/` joins a voice channel. */
|
|
4
|
-
export declare function sourcesJoinVoice(cwd: string): Promise<boolean>;
|
package/dist/voice/usage.js
CHANGED
|
@@ -2,32 +2,20 @@
|
|
|
2
2
|
// a bot its own voice state with the GUILD_VOICE_STATES intent, without
|
|
3
3
|
// which `join()` can't finish: like every intent, it is asked for when the
|
|
4
4
|
// project needs it, and only then.
|
|
5
|
-
import {
|
|
6
|
-
import { join } from 'node:path';
|
|
5
|
+
import { GatewayIntent } from '../discord/intents.js';
|
|
7
6
|
/**
|
|
8
7
|
* A call to `join()` without an argument or with options: what joining a
|
|
9
8
|
* voice channel looks like. The `join(', ')` of a list does not match; a
|
|
10
9
|
* `join()` of a list does, and only costs an intent.
|
|
11
10
|
*/
|
|
12
11
|
const JOINS_VOICE = /\.join\(\s*(?:\)|\{)/;
|
|
13
|
-
|
|
12
|
+
/** Joining voice, as `loader/usage.ts` looks for it. */
|
|
13
|
+
export const VOICE_USE = {
|
|
14
|
+
name: 'voice',
|
|
15
|
+
pattern: JOINS_VOICE,
|
|
16
|
+
intents: GatewayIntent.GuildVoiceStates,
|
|
17
|
+
};
|
|
14
18
|
/** Whether some code joins a voice channel. */
|
|
15
19
|
export function joinsVoice(code) {
|
|
16
20
|
return JOINS_VOICE.test(code);
|
|
17
21
|
}
|
|
18
|
-
/** Whether a file of `src/` joins a voice channel. */
|
|
19
|
-
export async function sourcesJoinVoice(cwd) {
|
|
20
|
-
const src = join(cwd, 'src');
|
|
21
|
-
const entries = await readdir(src, {
|
|
22
|
-
recursive: true,
|
|
23
|
-
withFileTypes: true,
|
|
24
|
-
}).catch(() => []);
|
|
25
|
-
for (const entry of entries) {
|
|
26
|
-
if (!entry.isFile() || !SOURCE.test(entry.name))
|
|
27
|
-
continue;
|
|
28
|
-
const code = await readFile(join(entry.parentPath, entry.name), 'utf8');
|
|
29
|
-
if (joinsVoice(code))
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CodeUse } from '../loader/usage.js';
|
|
2
|
+
/** `waitForMessage(` or `collectMessages(`: the messages of a channel. */
|
|
3
|
+
export declare const MESSAGES_USE: CodeUse;
|
|
4
|
+
/** `waitForReaction(`: the reactions to a message. */
|
|
5
|
+
export declare const REACTIONS_USE: CodeUse;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// What waiting needs from Discord, seen in the code: messages only reach
|
|
2
|
+
// the bot with the intents of messages (and their text with Message
|
|
3
|
+
// Content), reactions with the intents of reactions. Clicks need none.
|
|
4
|
+
// https://docs.discord.com/developers/events/gateway#gateway-intents
|
|
5
|
+
import { GatewayIntent } from '../discord/intents.js';
|
|
6
|
+
/** `waitForMessage(` or `collectMessages(`: the messages of a channel. */
|
|
7
|
+
export const MESSAGES_USE = {
|
|
8
|
+
name: 'messages',
|
|
9
|
+
pattern: /\.(?:waitForMessage|collectMessages)\s*\(/,
|
|
10
|
+
intents: GatewayIntent.GuildMessages |
|
|
11
|
+
GatewayIntent.DirectMessages |
|
|
12
|
+
GatewayIntent.MessageContent,
|
|
13
|
+
};
|
|
14
|
+
/** `waitForReaction(`: the reactions to a message. */
|
|
15
|
+
export const REACTIONS_USE = {
|
|
16
|
+
name: 'reactions',
|
|
17
|
+
pattern: /\.waitForReaction\s*\(/,
|
|
18
|
+
intents: GatewayIntent.GuildMessageReactions | GatewayIntent.DirectMessageReactions,
|
|
19
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** What can be waited for. */
|
|
2
|
+
export type WaitKind = 'message' | 'reaction' | 'click';
|
|
3
|
+
/** One wait, as a structure asks for it. */
|
|
4
|
+
export interface WaitRequest {
|
|
5
|
+
readonly kind: WaitKind;
|
|
6
|
+
/** Whether what is offered is what the wait is for. */
|
|
7
|
+
match(offered: unknown): boolean;
|
|
8
|
+
/** How many to take before the wait ends: 1 to wait for one. */
|
|
9
|
+
readonly max: number;
|
|
10
|
+
/** How long it waits, in milliseconds. */
|
|
11
|
+
readonly within: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Told when a run of the files starts and stops waiting, for `chapterjs
|
|
15
|
+
* test`: a run that waits is not one to wait for.
|
|
16
|
+
*/
|
|
17
|
+
export interface WaitWatcher {
|
|
18
|
+
parked(run: object): void;
|
|
19
|
+
resumed(run: object): void;
|
|
20
|
+
}
|
|
21
|
+
/** The waits of a bot. */
|
|
22
|
+
export interface Waiters {
|
|
23
|
+
/**
|
|
24
|
+
* Waits for what fits the request, until `max` were taken or the time is
|
|
25
|
+
* up: resolves with what was taken, in order (maybe nothing).
|
|
26
|
+
*/
|
|
27
|
+
wait<T>(request: WaitRequest): Promise<T[]>;
|
|
28
|
+
/** Whether a wait is waiting for this kind: nothing is built otherwise. */
|
|
29
|
+
wants(kind: WaitKind): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Gives what just happened to the waits that want it. A click goes to
|
|
32
|
+
* one wait only, and nothing else runs it: returns whether one took it.
|
|
33
|
+
*/
|
|
34
|
+
offer(kind: WaitKind, offered: unknown): boolean;
|
|
35
|
+
/** Ends every wait with what it has: the bot stops. */
|
|
36
|
+
close(): void;
|
|
37
|
+
}
|
|
38
|
+
/** The waits of a bot, in memory: the default. */
|
|
39
|
+
export declare class MemoryWaiters implements Waiters {
|
|
40
|
+
#private;
|
|
41
|
+
constructor(options?: {
|
|
42
|
+
watch?: WaitWatcher;
|
|
43
|
+
});
|
|
44
|
+
wait<T>(request: WaitRequest): Promise<T[]>;
|
|
45
|
+
wants(kind: WaitKind): boolean;
|
|
46
|
+
offer(kind: WaitKind, offered: unknown): boolean;
|
|
47
|
+
close(): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Waiting for what comes next: the next message of a channel, a click on
|
|
2
|
+
// the buttons of a message, a reaction. A function of the files asks
|
|
3
|
+
// (`channel.waitForMessage()`, `message.waitForClick()`...), the bot offers
|
|
4
|
+
// what Discord sends, and the first thing that fits ends the wait, or the
|
|
5
|
+
// time does. One mechanism for every kind: a new kind of wait is a new
|
|
6
|
+
// `WaitKind` and the place that offers it.
|
|
7
|
+
//
|
|
8
|
+
// Waits live in the process, behind the `Waiters` interface: everything of
|
|
9
|
+
// one server reaches one process (its shard), so a wait always hears what
|
|
10
|
+
// it waits for. A restart ends them: nothing is kept.
|
|
11
|
+
import { currentRun } from '../store/scope.js';
|
|
12
|
+
/** The kinds a single wait takes for itself: nothing else runs them. */
|
|
13
|
+
const EXCLUSIVE = new Set(['click']);
|
|
14
|
+
/** The waits of a bot, in memory: the default. */
|
|
15
|
+
export class MemoryWaiters {
|
|
16
|
+
#pending = new Map();
|
|
17
|
+
/** How many waits each run has going. */
|
|
18
|
+
#runs = new Map();
|
|
19
|
+
#watch;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.#watch = options.watch;
|
|
22
|
+
}
|
|
23
|
+
wait(request) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const run = currentRun();
|
|
26
|
+
const taken = [];
|
|
27
|
+
const list = this.#pending.get(request.kind) ?? new Set();
|
|
28
|
+
this.#pending.set(request.kind, list);
|
|
29
|
+
const pending = {
|
|
30
|
+
request,
|
|
31
|
+
taken,
|
|
32
|
+
run,
|
|
33
|
+
// Nothing of the bot waits for it: a wait never keeps it running.
|
|
34
|
+
timer: setTimeout(() => pending.end(), request.within).unref(),
|
|
35
|
+
end: error => {
|
|
36
|
+
if (!list.delete(pending))
|
|
37
|
+
return;
|
|
38
|
+
clearTimeout(pending.timer);
|
|
39
|
+
if (list.size === 0)
|
|
40
|
+
this.#pending.delete(request.kind);
|
|
41
|
+
this.#leave(run);
|
|
42
|
+
if (error === undefined)
|
|
43
|
+
resolve(taken);
|
|
44
|
+
else
|
|
45
|
+
reject(error);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
list.add(pending);
|
|
49
|
+
this.#enter(run);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
wants(kind) {
|
|
53
|
+
return (this.#pending.get(kind)?.size ?? 0) > 0;
|
|
54
|
+
}
|
|
55
|
+
offer(kind, offered) {
|
|
56
|
+
const list = this.#pending.get(kind);
|
|
57
|
+
if (!list)
|
|
58
|
+
return false;
|
|
59
|
+
let took = false;
|
|
60
|
+
for (const pending of [...list]) {
|
|
61
|
+
let fits;
|
|
62
|
+
try {
|
|
63
|
+
fits = pending.request.match(offered);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// A test of the files that throws ends its wait with the error,
|
|
67
|
+
// thrown where the files wait: reported with their line.
|
|
68
|
+
pending.end(error ?? new Error('The test of the wait threw.'));
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!fits)
|
|
72
|
+
continue;
|
|
73
|
+
took = true;
|
|
74
|
+
pending.taken.push(offered);
|
|
75
|
+
if (pending.taken.length >= pending.request.max)
|
|
76
|
+
pending.end();
|
|
77
|
+
if (EXCLUSIVE.has(kind))
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
return took;
|
|
81
|
+
}
|
|
82
|
+
close() {
|
|
83
|
+
for (const list of [...this.#pending.values()]) {
|
|
84
|
+
for (const pending of [...list])
|
|
85
|
+
pending.end();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
#enter(run) {
|
|
89
|
+
if (!run)
|
|
90
|
+
return;
|
|
91
|
+
const count = this.#runs.get(run) ?? 0;
|
|
92
|
+
this.#runs.set(run, count + 1);
|
|
93
|
+
if (count === 0)
|
|
94
|
+
this.#watch?.parked(run);
|
|
95
|
+
}
|
|
96
|
+
#leave(run) {
|
|
97
|
+
if (!run)
|
|
98
|
+
return;
|
|
99
|
+
const count = (this.#runs.get(run) ?? 1) - 1;
|
|
100
|
+
if (count > 0) {
|
|
101
|
+
this.#runs.set(run, count);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.#runs.delete(run);
|
|
105
|
+
this.#watch?.resumed(run);
|
|
106
|
+
}
|
|
107
|
+
}
|
package/package.json
CHANGED