chapterjs 0.1.2 → 0.1.4
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/dist/assets/asset.d.ts +46 -0
- package/dist/assets/asset.js +86 -0
- package/dist/assets/public.d.ts +19 -0
- package/dist/assets/public.js +120 -0
- package/dist/cache/cache.d.ts +62 -0
- package/dist/cache/cache.js +80 -0
- package/dist/cache/store.d.ts +45 -0
- package/dist/cache/store.js +53 -0
- package/dist/cli/build.d.ts +41 -0
- package/dist/cli/build.js +206 -0
- package/dist/cli/cli.js +108 -25
- package/dist/cli/cluster.d.ts +70 -0
- package/dist/cli/cluster.js +213 -0
- package/dist/cli/dev.d.ts +18 -1
- package/dist/cli/dev.js +252 -198
- package/dist/cli/env.d.ts +31 -0
- package/dist/cli/env.js +93 -0
- package/dist/cli/log.d.ts +13 -0
- package/dist/cli/log.js +17 -0
- package/dist/cli/node-version.d.ts +8 -0
- package/dist/cli/node-version.js +13 -0
- package/dist/cli/preflight.d.ts +36 -0
- package/dist/cli/preflight.js +122 -0
- package/dist/cli/project.d.ts +122 -0
- package/dist/cli/project.js +452 -0
- package/dist/cli/start.d.ts +25 -0
- package/dist/cli/start.js +225 -0
- package/dist/commands/autocomplete.d.ts +13 -0
- package/dist/commands/autocomplete.js +159 -0
- package/dist/commands/command.d.ts +346 -0
- package/dist/commands/command.js +28 -0
- package/dist/commands/convention.d.ts +64 -0
- package/dist/commands/convention.js +464 -0
- package/dist/commands/register.d.ts +18 -0
- package/dist/commands/register.js +42 -0
- package/dist/commands/router.d.ts +20 -0
- package/dist/commands/router.js +228 -0
- package/dist/commands/tree.d.ts +43 -0
- package/dist/commands/tree.js +206 -0
- package/dist/commands/twins.d.ts +15 -0
- package/dist/commands/twins.js +78 -0
- package/dist/components/button.d.ts +79 -0
- package/dist/components/button.js +108 -0
- package/dist/components/component.d.ts +140 -0
- package/dist/components/component.js +39 -0
- package/dist/components/convention.d.ts +35 -0
- package/dist/components/convention.js +339 -0
- package/dist/components/custom-id.d.ts +72 -0
- package/dist/components/custom-id.js +128 -0
- package/dist/components/embed.d.ts +28 -0
- package/dist/components/embed.js +11 -0
- package/dist/components/file.d.ts +30 -0
- package/dist/components/file.js +52 -0
- package/dist/components/instance.d.ts +100 -0
- package/dist/components/instance.js +86 -0
- package/dist/components/layout.d.ts +103 -0
- package/dist/components/layout.js +279 -0
- package/dist/components/modal.d.ts +244 -0
- package/dist/components/modal.js +399 -0
- package/dist/components/options.d.ts +41 -0
- package/dist/components/options.js +101 -0
- package/dist/components/render.d.ts +12 -0
- package/dist/components/render.js +58 -0
- package/dist/components/router.d.ts +18 -0
- package/dist/components/router.js +256 -0
- package/dist/components/select.d.ts +144 -0
- package/dist/components/select.js +179 -0
- package/dist/core/bot.d.ts +99 -0
- package/dist/core/bot.js +174 -0
- package/dist/core/memory.d.ts +66 -0
- package/dist/core/memory.js +115 -0
- package/dist/discord/api.d.ts +112 -0
- package/dist/discord/api.js +115 -0
- package/dist/discord/cdn.d.ts +67 -0
- package/dist/discord/cdn.js +100 -0
- package/dist/discord/codes.d.ts +171 -0
- package/dist/discord/codes.js +421 -0
- package/dist/discord/endpoint.d.ts +56 -0
- package/dist/discord/endpoint.js +53 -0
- package/dist/discord/endpoints.d.ts +1529 -0
- package/dist/discord/endpoints.js +1139 -0
- package/dist/discord/formatting.d.ts +67 -0
- package/dist/discord/formatting.js +87 -0
- package/dist/discord/index.d.ts +10 -0
- package/dist/discord/index.js +13 -0
- package/dist/discord/intents.d.ts +48 -0
- package/dist/discord/intents.js +128 -0
- package/dist/discord/permissions.d.ts +94 -0
- package/dist/discord/permissions.js +185 -0
- package/dist/discord/snowflake.d.ts +21 -0
- package/dist/discord/snowflake.js +36 -0
- package/dist/discord/types/application-command.d.ts +332 -0
- package/dist/discord/types/application-command.js +48 -0
- package/dist/discord/types/application-role-connection-metadata.d.ts +42 -0
- package/dist/discord/types/application-role-connection-metadata.js +22 -0
- package/dist/discord/types/application.d.ts +208 -0
- package/dist/discord/types/application.js +48 -0
- package/dist/discord/types/audit-log.d.ts +255 -0
- package/dist/discord/types/audit-log.js +144 -0
- package/dist/discord/types/auto-moderation.d.ts +167 -0
- package/dist/discord/types/auto-moderation.js +52 -0
- package/dist/discord/types/channel.d.ts +557 -0
- package/dist/discord/types/channel.js +88 -0
- package/dist/discord/types/common.d.ts +54 -0
- package/dist/discord/types/common.js +38 -0
- package/dist/discord/types/component.d.ts +775 -0
- package/dist/discord/types/component.js +76 -0
- package/dist/discord/types/emoji.d.ts +74 -0
- package/dist/discord/types/emoji.js +1 -0
- package/dist/discord/types/entitlement.d.ts +84 -0
- package/dist/discord/types/entitlement.js +22 -0
- package/dist/discord/types/gateway-events.d.ts +1148 -0
- package/dist/discord/types/gateway-events.js +54 -0
- package/dist/discord/types/gateway.d.ts +38 -0
- package/dist/discord/types/gateway.js +1 -0
- package/dist/discord/types/guild-scheduled-event.d.ts +257 -0
- package/dist/discord/types/guild-scheduled-event.js +68 -0
- package/dist/discord/types/guild-template.d.ts +51 -0
- package/dist/discord/types/guild-template.js +1 -0
- package/dist/discord/types/guild.d.ts +910 -0
- package/dist/discord/types/guild.js +153 -0
- package/dist/discord/types/index.d.ts +31 -0
- package/dist/discord/types/index.js +31 -0
- package/dist/discord/types/interaction.d.ts +321 -0
- package/dist/discord/types/interaction.js +47 -0
- package/dist/discord/types/invite.d.ts +158 -0
- package/dist/discord/types/invite.js +39 -0
- package/dist/discord/types/message.d.ts +834 -0
- package/dist/discord/types/message.js +159 -0
- package/dist/discord/types/oauth2.d.ts +22 -0
- package/dist/discord/types/oauth2.js +1 -0
- package/dist/discord/types/permissions.d.ts +183 -0
- package/dist/discord/types/permissions.js +118 -0
- package/dist/discord/types/poll.d.ts +106 -0
- package/dist/discord/types/poll.js +8 -0
- package/dist/discord/types/sku.d.ts +47 -0
- package/dist/discord/types/sku.js +26 -0
- package/dist/discord/types/soundboard.d.ts +71 -0
- package/dist/discord/types/soundboard.js +1 -0
- package/dist/discord/types/stage-instance.d.ts +58 -0
- package/dist/discord/types/stage-instance.js +10 -0
- package/dist/discord/types/sticker.d.ts +115 -0
- package/dist/discord/types/sticker.js +20 -0
- package/dist/discord/types/subscription.d.ts +54 -0
- package/dist/discord/types/subscription.js +12 -0
- package/dist/discord/types/team.d.ts +55 -0
- package/dist/discord/types/team.js +21 -0
- package/dist/discord/types/user.d.ts +273 -0
- package/dist/discord/types/user.js +83 -0
- package/dist/discord/types/voice.d.ts +72 -0
- package/dist/discord/types/voice.js +1 -0
- package/dist/discord/types/webhook.d.ts +184 -0
- package/dist/discord/types/webhook.js +12 -0
- package/dist/events/convention.d.ts +25 -0
- package/dist/events/convention.js +97 -0
- package/dist/events/event.d.ts +8 -0
- package/dist/events/event.js +20 -0
- package/dist/events/registry.d.ts +706 -0
- package/dist/events/registry.js +839 -0
- package/dist/events/router.d.ts +45 -0
- package/dist/events/router.js +133 -0
- package/dist/events/types.d.ts +7 -0
- package/dist/events/types.js +48 -0
- package/dist/gateway/errors.d.ts +18 -0
- package/dist/gateway/errors.js +47 -0
- package/dist/gateway/gateway.d.ts +64 -0
- package/dist/gateway/gateway.js +117 -0
- package/dist/gateway/identify-queue.d.ts +25 -0
- package/dist/gateway/identify-queue.js +29 -0
- package/dist/gateway/shard.d.ts +94 -0
- package/dist/gateway/shard.js +414 -0
- package/dist/gateway/state.d.ts +11 -0
- package/dist/gateway/state.js +184 -0
- package/dist/index.d.ts +55 -18
- package/dist/index.js +38 -12
- package/dist/interactions/cooldown.d.ts +33 -0
- package/dist/interactions/cooldown.js +74 -0
- package/dist/interactions/dispatch.d.ts +91 -0
- package/dist/interactions/dispatch.js +110 -0
- package/dist/loader/generated.d.ts +37 -0
- package/dist/loader/generated.js +114 -0
- package/dist/loader/hot.d.ts +13 -0
- package/dist/loader/hot.js +85 -0
- package/dist/loader/loader.d.ts +72 -0
- package/dist/loader/loader.js +135 -0
- package/dist/loader/locate.d.ts +10 -0
- package/dist/loader/locate.js +36 -0
- package/dist/loader/watch.d.ts +17 -0
- package/dist/loader/watch.js +66 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +6 -0
- package/dist/messages/commands.d.ts +23 -0
- package/dist/messages/commands.js +123 -0
- package/dist/messages/convention.d.ts +89 -0
- package/dist/messages/convention.js +347 -0
- package/dist/messages/messages.d.ts +219 -0
- package/dist/messages/messages.js +30 -0
- package/dist/messages/phrases.d.ts +23 -0
- package/dist/messages/phrases.js +44 -0
- package/dist/messages/translate.d.ts +100 -0
- package/dist/messages/translate.js +249 -0
- package/dist/presence/convention.d.ts +26 -0
- package/dist/presence/convention.js +133 -0
- package/dist/presence/presence.d.ts +57 -0
- package/dist/presence/presence.js +27 -0
- package/dist/rest/errors.d.ts +55 -0
- package/dist/rest/errors.js +92 -0
- package/dist/rest/rate-limiter.d.ts +46 -0
- package/dist/rest/rate-limiter.js +153 -0
- package/dist/rest/rest.d.ts +63 -0
- package/dist/rest/rest.js +194 -0
- package/dist/structures/base.d.ts +48 -0
- package/dist/structures/base.js +84 -0
- package/dist/structures/channel.d.ts +380 -0
- package/dist/structures/channel.js +627 -0
- package/dist/structures/context.d.ts +30 -0
- package/dist/structures/context.js +7 -0
- package/dist/structures/emoji.d.ts +46 -0
- package/dist/structures/emoji.js +65 -0
- package/dist/structures/entities.d.ts +62 -0
- package/dist/structures/entities.js +226 -0
- package/dist/structures/guild.d.ts +348 -0
- package/dist/structures/guild.js +466 -0
- package/dist/structures/interaction.d.ts +197 -0
- package/dist/structures/interaction.js +396 -0
- package/dist/structures/invite.d.ts +38 -0
- package/dist/structures/invite.js +57 -0
- package/dist/structures/known.d.ts +28 -0
- package/dist/structures/known.js +58 -0
- package/dist/structures/member.d.ts +140 -0
- package/dist/structures/member.js +260 -0
- package/dist/structures/message.d.ts +197 -0
- package/dist/structures/message.js +268 -0
- package/dist/structures/payload.d.ts +91 -0
- package/dist/structures/payload.js +184 -0
- package/dist/structures/role.d.ts +69 -0
- package/dist/structures/role.js +112 -0
- package/dist/structures/user.d.ts +59 -0
- package/dist/structures/user.js +94 -0
- package/dist/structures/webhook.d.ts +46 -0
- package/dist/structures/webhook.js +79 -0
- package/dist/tasks/convention.d.ts +26 -0
- package/dist/tasks/convention.js +78 -0
- package/dist/tasks/schedule.d.ts +54 -0
- package/dist/tasks/schedule.js +236 -0
- package/dist/tasks/scheduler.d.ts +33 -0
- package/dist/tasks/scheduler.js +107 -0
- package/dist/tasks/task.d.ts +76 -0
- package/dist/tasks/task.js +28 -0
- package/dist/util/case.d.ts +13 -0
- package/dist/util/case.js +54 -0
- package/dist/util/duration.d.ts +8 -0
- package/dist/util/duration.js +29 -0
- package/dist/util/websocket.d.ts +9 -0
- package/dist/util/websocket.js +31 -0
- package/dist/voice/connection.d.ts +90 -0
- package/dist/voice/connection.js +494 -0
- package/dist/voice/dave.d.ts +109 -0
- package/dist/voice/dave.js +245 -0
- package/dist/voice/opus.d.ts +19 -0
- package/dist/voice/opus.js +273 -0
- package/dist/voice/session.d.ts +55 -0
- package/dist/voice/session.js +405 -0
- package/dist/voice/source.d.ts +22 -0
- package/dist/voice/source.js +195 -0
- package/dist/voice/transport.d.ts +43 -0
- package/dist/voice/transport.js +140 -0
- package/dist/voice/usage.d.ts +4 -0
- package/dist/voice/usage.js +33 -0
- package/package.json +26 -7
- package/vendor/dave/LICENSE +231 -0
- package/vendor/dave/libdave.d.mts +99 -0
- package/vendor/dave/libdave.mjs +2 -0
- package/vendor/dave/libdave.wasm +0 -0
- package/dist/components.d.ts +0 -97
- package/dist/components.js +0 -260
- package/dist/context.d.ts +0 -72
- package/dist/context.js +0 -273
- package/dist/discord/client.d.ts +0 -35
- package/dist/discord/client.js +0 -66
- package/dist/discord/constants.d.ts +0 -49
- package/dist/discord/constants.js +0 -61
- package/dist/discord/events.d.ts +0 -129
- package/dist/discord/events.js +0 -102
- package/dist/discord/gateway.d.ts +0 -61
- package/dist/discord/gateway.js +0 -320
- package/dist/discord/payloads.d.ts +0 -275
- package/dist/discord/payloads.js +0 -3
- package/dist/discord/presence.d.ts +0 -42
- package/dist/discord/presence.js +0 -51
- package/dist/discord/rest.d.ts +0 -49
- package/dist/discord/rest.js +0 -271
- package/dist/discord/types.d.ts +0 -334
- package/dist/discord/types.js +0 -4
- package/dist/events.d.ts +0 -94
- package/dist/events.js +0 -255
- package/dist/presence.d.ts +0 -92
- package/dist/presence.js +0 -250
- package/dist/structures/Channel.d.ts +0 -110
- package/dist/structures/Channel.js +0 -171
- package/dist/structures/Guild.d.ts +0 -70
- package/dist/structures/Guild.js +0 -105
- package/dist/structures/Interaction.d.ts +0 -113
- package/dist/structures/Interaction.js +0 -274
- package/dist/structures/Member.d.ts +0 -70
- package/dist/structures/Member.js +0 -124
- package/dist/structures/Message.d.ts +0 -87
- package/dist/structures/Message.js +0 -182
- package/dist/structures/Reaction.d.ts +0 -38
- package/dist/structures/Reaction.js +0 -64
- package/dist/structures/Role.d.ts +0 -43
- package/dist/structures/Role.js +0 -77
- package/dist/structures/User.d.ts +0 -41
- package/dist/structures/User.js +0 -79
- package/dist/structures/utils.d.ts +0 -72
- package/dist/structures/utils.js +0 -59
- package/dist/utils/errors.d.ts +0 -5
- package/dist/utils/errors.js +0 -28
- package/dist/utils/modules.d.ts +0 -39
- package/dist/utils/modules.js +0 -156
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
// Every event user code can listen to. An event is one entry here: its
|
|
2
|
+
// name, the gateway events it comes from, the intents that make Discord
|
|
3
|
+
// send them, and how to build what the handler receives. Adding an event is
|
|
4
|
+
// adding an entry; the router and the loader never change.
|
|
5
|
+
import { InviteType } from '../discord/types/invite.js';
|
|
6
|
+
import { dataOf } from '../structures/base.js';
|
|
7
|
+
import { storesOf } from '../structures/guild.js';
|
|
8
|
+
import { toCamelCase } from '../util/case.js';
|
|
9
|
+
import { GetChannel, GetUser } from '../discord/endpoints.js';
|
|
10
|
+
import { GatewayIntent } from '../discord/intents.js';
|
|
11
|
+
const I = GatewayIntent;
|
|
12
|
+
const findGuild = (ctx, id) => ctx.cache.guilds.get(id) ?? null;
|
|
13
|
+
/** The server of a context that has it. */
|
|
14
|
+
const ofGuild = ({ guild }) => guild;
|
|
15
|
+
const channel = (on) => ({
|
|
16
|
+
on,
|
|
17
|
+
build: (ctx, data) => {
|
|
18
|
+
const found = ctx.cache.channels.get(data.id);
|
|
19
|
+
return found ? { channel: found } : null;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const deletedChannel = (on) => ({
|
|
23
|
+
on,
|
|
24
|
+
before: (ctx, data) => ctx.cache.channels.get(data.id) ?? null,
|
|
25
|
+
build: (_ctx, _data, { before }) => before ? { channel: before } : null,
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Discord refused what an event needs before it can be given to its files:
|
|
29
|
+
* the channel it happened in, or the user who did it.
|
|
30
|
+
*/
|
|
31
|
+
export class MissingForEvent extends Error {
|
|
32
|
+
what;
|
|
33
|
+
cause;
|
|
34
|
+
constructor(what, cause) {
|
|
35
|
+
super(`Discord refused the ${what} of an event.`);
|
|
36
|
+
this.what = what;
|
|
37
|
+
this.cause = cause;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** What is being asked to Discord, for each bot, by `channel:id` or `user:id`. */
|
|
41
|
+
const asking = new WeakMap();
|
|
42
|
+
/** Asks Discord once for something several events may wait for. */
|
|
43
|
+
function askOnce(ctx, key, ask) {
|
|
44
|
+
let pending = asking.get(ctx);
|
|
45
|
+
if (!pending)
|
|
46
|
+
asking.set(ctx, (pending = new Map()));
|
|
47
|
+
const running = pending.get(key);
|
|
48
|
+
if (running)
|
|
49
|
+
return running;
|
|
50
|
+
const answer = ask().finally(() => pending.delete(key));
|
|
51
|
+
pending.set(key, answer);
|
|
52
|
+
return answer;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The channel of what happened in a server, asked to Discord when the bot
|
|
56
|
+
* does not know it (a thread it never saw, for example): one request, then
|
|
57
|
+
* it is remembered.
|
|
58
|
+
*
|
|
59
|
+
* Everything that happens in that channel meanwhile waits for the same
|
|
60
|
+
* answer, so it costs one request and is delivered in the order it
|
|
61
|
+
* happened: a message is never deleted before it was sent.
|
|
62
|
+
*/
|
|
63
|
+
const channelOf = (ctx, data) => {
|
|
64
|
+
const guildId = data.guild_id;
|
|
65
|
+
const id = data.channel_id;
|
|
66
|
+
if (!guildId || ctx.cache.channels.has(id))
|
|
67
|
+
return undefined;
|
|
68
|
+
return askOnce(ctx, `channel:${id}`, () => ctx.rest
|
|
69
|
+
.request(GetChannel, [id])
|
|
70
|
+
.then(raw => ctx.entities.channel(raw, guildId))
|
|
71
|
+
.catch((error) => {
|
|
72
|
+
throw new MissingForEvent('channel', error);
|
|
73
|
+
}));
|
|
74
|
+
};
|
|
75
|
+
/** A user the bot does not know, asked to Discord once, then remembered. */
|
|
76
|
+
const fetchUser = (ctx, id) => askOnce(ctx, `user:${id}`, () => ctx.rest
|
|
77
|
+
.request(GetUser, [id])
|
|
78
|
+
.then(raw => ctx.entities.user(raw))
|
|
79
|
+
.catch((error) => {
|
|
80
|
+
throw new MissingForEvent('user', error);
|
|
81
|
+
}));
|
|
82
|
+
/**
|
|
83
|
+
* The channel of what happened, and who did it when Discord only gives
|
|
84
|
+
* their id and the bot does not know them: one request, then remembered.
|
|
85
|
+
* Resolves to the user, which `build` receives as `prepared`, so it is
|
|
86
|
+
* there even when the bot can remember no user right now.
|
|
87
|
+
*/
|
|
88
|
+
const placeAndUser = (ctx, data, userId) => {
|
|
89
|
+
const channel = channelOf(ctx, data);
|
|
90
|
+
const known = userId ? ctx.cache.users.get(userId) : undefined;
|
|
91
|
+
const user = userId && !known ? fetchUser(ctx, userId) : undefined;
|
|
92
|
+
if (!channel && !user)
|
|
93
|
+
return undefined;
|
|
94
|
+
return Promise.all([channel, user]).then(([, fetched]) => fetched ?? known);
|
|
95
|
+
};
|
|
96
|
+
/** Who did it: what `prepare` got, or else what the bot knows. */
|
|
97
|
+
const userFrom = (ctx, prepared, id) => prepared ?? ctx.cache.users.get(id) ?? null;
|
|
98
|
+
/** Where something happened in a server or in private, as the bot knows it. */
|
|
99
|
+
const placeOf = (ctx, data) => {
|
|
100
|
+
const found = ctx.cache.channels.get(data.channel_id);
|
|
101
|
+
const guildId = data.guild_id ?? found?.guildId ?? null;
|
|
102
|
+
return {
|
|
103
|
+
channelId: data.channel_id,
|
|
104
|
+
channel: found?.isTextBased() ? found : null,
|
|
105
|
+
guildId,
|
|
106
|
+
guild: guildId ? (ctx.cache.guilds.get(guildId) ?? null) : null,
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
/** A message the bot remembers. */
|
|
110
|
+
const rememberedMessage = (channel, id) => channel?.messages.get(id) ?? null;
|
|
111
|
+
/** A member the bot remembers. */
|
|
112
|
+
const rememberedMember = (guild, userId) => guild?.members.get(userId) ?? null;
|
|
113
|
+
/**
|
|
114
|
+
* Whether a file wants something that happened in a server or in private:
|
|
115
|
+
* it listens there, and in a server what it is promised is there.
|
|
116
|
+
*/
|
|
117
|
+
const wantedPlace = (context, where, needsMember = false) => {
|
|
118
|
+
const inGuild = context.guildId !== null;
|
|
119
|
+
if (!listensThere(where, inGuild))
|
|
120
|
+
return false;
|
|
121
|
+
if (!inGuild)
|
|
122
|
+
return true;
|
|
123
|
+
return (context.guild !== null &&
|
|
124
|
+
context.channel !== null &&
|
|
125
|
+
(!needsMember || context.member != null));
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Private messages, bots and webhooks only reach the files that asked for
|
|
129
|
+
* them. This is also what makes the types true: in a server, the server
|
|
130
|
+
* and the channel are there, and without `bots` so is the member who wrote.
|
|
131
|
+
*/
|
|
132
|
+
/** Whether a file listens where something happened. */
|
|
133
|
+
const listensThere = (where, inGuild) => (inGuild ? where !== 'dm' : where === 'dm' || where === 'both');
|
|
134
|
+
/** The intents of messages, for where a file listens. */
|
|
135
|
+
const messagesIn = (where) => (where === 'dm' ? 0 : I.GuildMessages) |
|
|
136
|
+
(where === 'dm' || where === 'both' ? I.DirectMessages : 0);
|
|
137
|
+
const PLACES = ['guild', 'dm', 'both'];
|
|
138
|
+
const wantedMessage = ({ message }, options) => {
|
|
139
|
+
const inGuild = message.guildId !== null;
|
|
140
|
+
if (!listensThere(options.where, inGuild))
|
|
141
|
+
return false;
|
|
142
|
+
// In a server, the server and the channel are promised.
|
|
143
|
+
if (inGuild && (message.guild === null || message.channel === null)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (options.bots === true)
|
|
147
|
+
return true;
|
|
148
|
+
if (message.author.bot || message.webhookId !== null)
|
|
149
|
+
return false;
|
|
150
|
+
return !inGuild || message.member !== null;
|
|
151
|
+
};
|
|
152
|
+
const message = (on) => ({
|
|
153
|
+
on,
|
|
154
|
+
prepare: channelOf,
|
|
155
|
+
build: (ctx, data) => ({
|
|
156
|
+
message: ctx.entities.message(data, data.guild_id),
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
const member = (on) => ({
|
|
160
|
+
on,
|
|
161
|
+
build: (ctx, data) => {
|
|
162
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
163
|
+
if (!guild)
|
|
164
|
+
return null;
|
|
165
|
+
const { guild_id: guildId, ...raw } = data;
|
|
166
|
+
// The member comes with the event: it is there whatever the bot
|
|
167
|
+
// remembers of the members of the server.
|
|
168
|
+
const member = guild.members.get(data.user.id) ??
|
|
169
|
+
ctx.entities.member(guildId, raw);
|
|
170
|
+
return { member, guild };
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
const role = (on) => ({
|
|
174
|
+
on,
|
|
175
|
+
build: (ctx, data) => {
|
|
176
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
177
|
+
const found = guild?.roles.get(data.role.id);
|
|
178
|
+
return guild && found ? { role: found, guild } : null;
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
const ban = (on) => ({
|
|
182
|
+
on,
|
|
183
|
+
build: (ctx, data) => {
|
|
184
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
185
|
+
return guild ? { user: ctx.entities.user(data.user), guild } : null;
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
/**
|
|
189
|
+
* The emojis of a server that changed, from the list Discord sends: the
|
|
190
|
+
* emojis before (read by `before`) and after are compared one by one.
|
|
191
|
+
*/
|
|
192
|
+
const emojis = (pick) => ({
|
|
193
|
+
on: 'GUILD_EMOJIS_UPDATE',
|
|
194
|
+
before: (ctx, data) => new Map(findGuild(ctx, data.guild_id)?.emojis ?? []),
|
|
195
|
+
build: (ctx, data, { before }) => {
|
|
196
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
197
|
+
if (!guild)
|
|
198
|
+
return null;
|
|
199
|
+
const old = before;
|
|
200
|
+
const ids = new Set([...old.keys(), ...guild.emojis.keys()]);
|
|
201
|
+
const changed = [];
|
|
202
|
+
for (const id of ids) {
|
|
203
|
+
const emoji = pick(guild.emojis.get(id), old.get(id));
|
|
204
|
+
if (emoji)
|
|
205
|
+
changed.push({ emoji, guild });
|
|
206
|
+
}
|
|
207
|
+
return changed;
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
/** The server and the channel of an invite; `null` outside servers. */
|
|
211
|
+
const inviteChannel = (ctx, data) => {
|
|
212
|
+
const guild = data.guild_id ? findGuild(ctx, data.guild_id) : null;
|
|
213
|
+
const channel = guild?.channels.get(data.channel_id);
|
|
214
|
+
// An invite leads to a channel, never to a thread.
|
|
215
|
+
if (!guild || !channel || channel.isThread())
|
|
216
|
+
return null;
|
|
217
|
+
return { guild, channel };
|
|
218
|
+
};
|
|
219
|
+
/** The intents of reactions, for where a file listens. */
|
|
220
|
+
const reactionsIn = (where) => (where === 'dm' ? 0 : I.GuildMessageReactions) |
|
|
221
|
+
(where === 'dm' || where === 'both' ? I.DirectMessageReactions : 0);
|
|
222
|
+
/** The intents of polls, for where a file listens. */
|
|
223
|
+
const pollsIn = (where) => (where === 'dm' ? 0 : I.GuildMessagePolls) |
|
|
224
|
+
(where === 'dm' || where === 'both' ? I.DirectMessagePolls : 0);
|
|
225
|
+
const cleared = (on) => ({
|
|
226
|
+
on,
|
|
227
|
+
prepare: channelOf,
|
|
228
|
+
build: (ctx, data) => {
|
|
229
|
+
const place = placeOf(ctx, data);
|
|
230
|
+
return {
|
|
231
|
+
emoji: 'emoji' in data ? toCamelCase(data.emoji) : null,
|
|
232
|
+
messageId: data.message_id,
|
|
233
|
+
message: rememberedMessage(place.channel, data.message_id),
|
|
234
|
+
...place,
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
const vote = (on) => ({
|
|
239
|
+
on,
|
|
240
|
+
prepare: (ctx, data) => placeAndUser(ctx, data, data.user_id),
|
|
241
|
+
build: (ctx, data, { prepared }) => {
|
|
242
|
+
const place = placeOf(ctx, data);
|
|
243
|
+
const user = userFrom(ctx, prepared, data.user_id);
|
|
244
|
+
if (!user)
|
|
245
|
+
return null;
|
|
246
|
+
return {
|
|
247
|
+
answerId: data.answer_id,
|
|
248
|
+
user,
|
|
249
|
+
member: rememberedMember(place.guild, data.user_id),
|
|
250
|
+
messageId: data.message_id,
|
|
251
|
+
message: rememberedMessage(place.channel, data.message_id),
|
|
252
|
+
...place,
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
/** A thread and its server; `null` when the bot does not know it. */
|
|
257
|
+
const threadOf = (ctx, data) => {
|
|
258
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
259
|
+
const thread = ctx.cache.channels.get(data.id);
|
|
260
|
+
return guild && thread?.isThread() ? { thread, guild } : null;
|
|
261
|
+
};
|
|
262
|
+
/** A voice state as handlers read it. */
|
|
263
|
+
const stateOf = (entry) => toCamelCase(entry.state);
|
|
264
|
+
/**
|
|
265
|
+
* The voice events, from what someone's voice state was (read by `before`)
|
|
266
|
+
* and is now: each event picks the change it is about.
|
|
267
|
+
*/
|
|
268
|
+
const voice = (pick) => ({
|
|
269
|
+
on: 'VOICE_STATE_UPDATE',
|
|
270
|
+
before: (ctx, data) => {
|
|
271
|
+
const guild = data.guild_id ? findGuild(ctx, data.guild_id) : null;
|
|
272
|
+
return guild ? storesOf(guild).voiceStates.get(data.user_id) : undefined;
|
|
273
|
+
},
|
|
274
|
+
build: (ctx, data, { before }) => {
|
|
275
|
+
const guild = data.guild_id ? findGuild(ctx, data.guild_id) : null;
|
|
276
|
+
if (!guild)
|
|
277
|
+
return null;
|
|
278
|
+
const now = storesOf(guild).voiceStates.get(data.user_id);
|
|
279
|
+
const picked = pick(before, now);
|
|
280
|
+
// In a server, the member comes with the event.
|
|
281
|
+
const member = data.member
|
|
282
|
+
? ctx.entities.member(guild.id, data.member)
|
|
283
|
+
: guild.members.get(data.user_id);
|
|
284
|
+
if (!picked || !member)
|
|
285
|
+
return null;
|
|
286
|
+
return { ...picked, member, guild };
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
const scheduled = (on) => ({
|
|
290
|
+
on,
|
|
291
|
+
build: (ctx, data) => {
|
|
292
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
293
|
+
if (!guild)
|
|
294
|
+
return null;
|
|
295
|
+
if (data.creator)
|
|
296
|
+
ctx.entities.user(data.creator);
|
|
297
|
+
return { scheduledEvent: toCamelCase(data), guild };
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
const interested = (on) => ({
|
|
301
|
+
on,
|
|
302
|
+
prepare: (ctx, data) => ctx.cache.users.has(data.user_id)
|
|
303
|
+
? undefined
|
|
304
|
+
: fetchUser(ctx, data.user_id),
|
|
305
|
+
build: (ctx, data, { prepared }) => {
|
|
306
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
307
|
+
const user = userFrom(ctx, prepared, data.user_id);
|
|
308
|
+
if (!guild || !user)
|
|
309
|
+
return null;
|
|
310
|
+
return {
|
|
311
|
+
scheduledEventId: data.guild_scheduled_event_id,
|
|
312
|
+
user,
|
|
313
|
+
member: rememberedMember(guild, data.user_id),
|
|
314
|
+
guild,
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
/**
|
|
319
|
+
* The events, by name. `ready` has no source: it is not a gateway event but
|
|
320
|
+
* the moment the bot has finished connecting.
|
|
321
|
+
*/
|
|
322
|
+
export const EVENTS = {
|
|
323
|
+
ready: { intents: 0, sources: [] },
|
|
324
|
+
messageCreate: {
|
|
325
|
+
guildOf: ({ message }) => message.guild,
|
|
326
|
+
// Without Message Content, the messages of others come empty.
|
|
327
|
+
intents: ({ where }) => messagesIn(where) | I.MessageContent,
|
|
328
|
+
options: { bots: 'boolean', where: PLACES },
|
|
329
|
+
accepts: wantedMessage,
|
|
330
|
+
sources: [message('MESSAGE_CREATE')],
|
|
331
|
+
},
|
|
332
|
+
messageUpdate: {
|
|
333
|
+
guildOf: ({ message }) => message.guild,
|
|
334
|
+
intents: ({ where }) => messagesIn(where) | I.MessageContent,
|
|
335
|
+
options: { bots: 'boolean', where: PLACES },
|
|
336
|
+
accepts: wantedMessage,
|
|
337
|
+
sources: [message('MESSAGE_UPDATE')],
|
|
338
|
+
},
|
|
339
|
+
messageDelete: {
|
|
340
|
+
guildOf: ofGuild,
|
|
341
|
+
intents: ({ where }) => messagesIn(where),
|
|
342
|
+
options: { where: PLACES },
|
|
343
|
+
accepts: ({ guildId, guild, channel }, options) => listensThere(options.where, guildId !== null) &&
|
|
344
|
+
// In a server, the server and the channel are promised.
|
|
345
|
+
(guildId === null || (guild !== null && channel !== null)),
|
|
346
|
+
sources: [
|
|
347
|
+
{
|
|
348
|
+
on: 'MESSAGE_DELETE',
|
|
349
|
+
prepare: channelOf,
|
|
350
|
+
before: (ctx, data) => {
|
|
351
|
+
const found = ctx.cache.channels.get(data.channel_id);
|
|
352
|
+
return found?.isTextBased()
|
|
353
|
+
? (found.messages.get(data.id) ?? null)
|
|
354
|
+
: null;
|
|
355
|
+
},
|
|
356
|
+
build: (ctx, data, { before }) => ({
|
|
357
|
+
messageId: data.id,
|
|
358
|
+
...placeOf(ctx, data),
|
|
359
|
+
message: before,
|
|
360
|
+
}),
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
// Messages deleted together: one occurrence each.
|
|
364
|
+
on: 'MESSAGE_DELETE_BULK',
|
|
365
|
+
prepare: channelOf,
|
|
366
|
+
before: (ctx, data) => {
|
|
367
|
+
const found = ctx.cache.channels.get(data.channel_id);
|
|
368
|
+
return data.ids.map(id => found?.isTextBased() ? (found.messages.get(id) ?? null) : null);
|
|
369
|
+
},
|
|
370
|
+
build: (ctx, data, { before }) => {
|
|
371
|
+
const place = placeOf(ctx, data);
|
|
372
|
+
const messages = before;
|
|
373
|
+
return data.ids.map((messageId, index) => ({
|
|
374
|
+
messageId,
|
|
375
|
+
...place,
|
|
376
|
+
message: messages[index] ?? null,
|
|
377
|
+
}));
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
},
|
|
382
|
+
memberJoin: {
|
|
383
|
+
guildOf: ofGuild,
|
|
384
|
+
intents: I.GuildMembers,
|
|
385
|
+
sources: [member('GUILD_MEMBER_ADD')],
|
|
386
|
+
},
|
|
387
|
+
memberUpdate: {
|
|
388
|
+
guildOf: ofGuild,
|
|
389
|
+
intents: I.GuildMembers,
|
|
390
|
+
sources: [member('GUILD_MEMBER_UPDATE')],
|
|
391
|
+
},
|
|
392
|
+
memberLeave: {
|
|
393
|
+
guildOf: ofGuild,
|
|
394
|
+
intents: I.GuildMembers,
|
|
395
|
+
// "The member as it was" is a member the bot remembered.
|
|
396
|
+
remembers: { members: 1000 },
|
|
397
|
+
sources: [
|
|
398
|
+
{
|
|
399
|
+
on: 'GUILD_MEMBER_REMOVE',
|
|
400
|
+
before: (ctx, data) => findGuild(ctx, data.guild_id)?.members.get(data.user.id) ?? null,
|
|
401
|
+
build: (ctx, data, { before }) => {
|
|
402
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
403
|
+
if (!guild)
|
|
404
|
+
return null;
|
|
405
|
+
return {
|
|
406
|
+
user: ctx.entities.user(data.user),
|
|
407
|
+
guild,
|
|
408
|
+
member: before,
|
|
409
|
+
};
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
},
|
|
414
|
+
guildJoin: {
|
|
415
|
+
guildOf: ofGuild,
|
|
416
|
+
intents: I.Guilds,
|
|
417
|
+
sources: [
|
|
418
|
+
{
|
|
419
|
+
on: 'GUILD_CREATE',
|
|
420
|
+
build: (ctx, data, { joined }) => {
|
|
421
|
+
const guild = joined ? findGuild(ctx, data.id) : null;
|
|
422
|
+
return guild ? { guild } : null;
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
guildLeave: {
|
|
428
|
+
guildOf: ofGuild,
|
|
429
|
+
intents: I.Guilds,
|
|
430
|
+
sources: [
|
|
431
|
+
{
|
|
432
|
+
on: 'GUILD_DELETE',
|
|
433
|
+
before: (ctx, data) => findGuild(ctx, data.id),
|
|
434
|
+
// With `unavailable`, an outage: the bot is still in the server.
|
|
435
|
+
build: (_ctx, data, { before }) => before && !data.unavailable ? { guild: before } : null,
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
channelCreate: {
|
|
440
|
+
guildOf: ({ channel }) => channel.guild,
|
|
441
|
+
intents: I.Guilds,
|
|
442
|
+
sources: [channel('CHANNEL_CREATE'), channel('THREAD_CREATE')],
|
|
443
|
+
},
|
|
444
|
+
channelUpdate: {
|
|
445
|
+
guildOf: ({ channel }) => channel.guild,
|
|
446
|
+
intents: I.Guilds,
|
|
447
|
+
sources: [channel('CHANNEL_UPDATE'), channel('THREAD_UPDATE')],
|
|
448
|
+
},
|
|
449
|
+
channelDelete: {
|
|
450
|
+
guildOf: ({ channel }) => channel.guild,
|
|
451
|
+
intents: I.Guilds,
|
|
452
|
+
sources: [
|
|
453
|
+
deletedChannel('CHANNEL_DELETE'),
|
|
454
|
+
deletedChannel('THREAD_DELETE'),
|
|
455
|
+
],
|
|
456
|
+
},
|
|
457
|
+
roleCreate: {
|
|
458
|
+
guildOf: ofGuild,
|
|
459
|
+
intents: I.Guilds,
|
|
460
|
+
sources: [role('GUILD_ROLE_CREATE')],
|
|
461
|
+
},
|
|
462
|
+
roleUpdate: {
|
|
463
|
+
guildOf: ofGuild,
|
|
464
|
+
intents: I.Guilds,
|
|
465
|
+
sources: [role('GUILD_ROLE_UPDATE')],
|
|
466
|
+
},
|
|
467
|
+
roleDelete: {
|
|
468
|
+
guildOf: ofGuild,
|
|
469
|
+
intents: I.Guilds,
|
|
470
|
+
sources: [
|
|
471
|
+
{
|
|
472
|
+
on: 'GUILD_ROLE_DELETE',
|
|
473
|
+
before: (ctx, data) => findGuild(ctx, data.guild_id)?.roles.get(data.role_id) ?? null,
|
|
474
|
+
build: (ctx, data, { before }) => {
|
|
475
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
476
|
+
// Every role of a server is known: one that is not never was.
|
|
477
|
+
if (!guild || !before)
|
|
478
|
+
return null;
|
|
479
|
+
return { roleId: data.role_id, guild, role: before };
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
},
|
|
484
|
+
guildUpdate: {
|
|
485
|
+
guildOf: ofGuild,
|
|
486
|
+
intents: I.Guilds,
|
|
487
|
+
sources: [
|
|
488
|
+
{
|
|
489
|
+
on: 'GUILD_UPDATE',
|
|
490
|
+
build: (ctx, data) => {
|
|
491
|
+
const guild = findGuild(ctx, data.id);
|
|
492
|
+
return guild ? { guild } : null;
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
},
|
|
497
|
+
banAdd: {
|
|
498
|
+
guildOf: ofGuild,
|
|
499
|
+
intents: I.GuildModeration,
|
|
500
|
+
sources: [ban('GUILD_BAN_ADD')],
|
|
501
|
+
},
|
|
502
|
+
banRemove: {
|
|
503
|
+
guildOf: ofGuild,
|
|
504
|
+
intents: I.GuildModeration,
|
|
505
|
+
sources: [ban('GUILD_BAN_REMOVE')],
|
|
506
|
+
},
|
|
507
|
+
emojiCreate: {
|
|
508
|
+
guildOf: ofGuild,
|
|
509
|
+
intents: I.GuildExpressions,
|
|
510
|
+
sources: [emojis((now, old) => (old ? null : now))],
|
|
511
|
+
},
|
|
512
|
+
emojiUpdate: {
|
|
513
|
+
guildOf: ofGuild,
|
|
514
|
+
intents: I.GuildExpressions,
|
|
515
|
+
sources: [
|
|
516
|
+
emojis((now, old) => old &&
|
|
517
|
+
now &&
|
|
518
|
+
JSON.stringify(dataOf(old)) !== JSON.stringify(dataOf(now))
|
|
519
|
+
? now
|
|
520
|
+
: null),
|
|
521
|
+
],
|
|
522
|
+
},
|
|
523
|
+
emojiDelete: {
|
|
524
|
+
guildOf: ofGuild,
|
|
525
|
+
intents: I.GuildExpressions,
|
|
526
|
+
sources: [emojis((now, old) => (now ? null : old))],
|
|
527
|
+
},
|
|
528
|
+
inviteCreate: {
|
|
529
|
+
guildOf: ofGuild,
|
|
530
|
+
intents: I.GuildInvites,
|
|
531
|
+
sources: [
|
|
532
|
+
{
|
|
533
|
+
on: 'INVITE_CREATE',
|
|
534
|
+
prepare: channelOf,
|
|
535
|
+
build: (ctx, data) => {
|
|
536
|
+
const place = inviteChannel(ctx, data);
|
|
537
|
+
if (!place)
|
|
538
|
+
return null;
|
|
539
|
+
const { inviter, channel_id: channelId, guild_id: guildId, ...rest } = data;
|
|
540
|
+
const invite = ctx.entities.invite({
|
|
541
|
+
...rest,
|
|
542
|
+
type: InviteType.Guild,
|
|
543
|
+
guild: { id: guildId },
|
|
544
|
+
channel: { id: channelId },
|
|
545
|
+
...(inviter ? { inviter } : {}),
|
|
546
|
+
});
|
|
547
|
+
return { invite, ...place };
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
},
|
|
552
|
+
inviteDelete: {
|
|
553
|
+
guildOf: ofGuild,
|
|
554
|
+
intents: I.GuildInvites,
|
|
555
|
+
sources: [
|
|
556
|
+
{
|
|
557
|
+
on: 'INVITE_DELETE',
|
|
558
|
+
prepare: channelOf,
|
|
559
|
+
build: (ctx, data) => {
|
|
560
|
+
const place = inviteChannel(ctx, data);
|
|
561
|
+
return place ? { code: data.code, ...place } : null;
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
},
|
|
566
|
+
auditLogEntryCreate: {
|
|
567
|
+
guildOf: ofGuild,
|
|
568
|
+
intents: I.GuildModeration,
|
|
569
|
+
sources: [
|
|
570
|
+
{
|
|
571
|
+
on: 'GUILD_AUDIT_LOG_ENTRY_CREATE',
|
|
572
|
+
build: (ctx, data) => {
|
|
573
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
574
|
+
if (!guild)
|
|
575
|
+
return null;
|
|
576
|
+
const { guild_id: _guildId, ...entry } = data;
|
|
577
|
+
return { entry: toCamelCase(entry), guild };
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
reactionAdd: {
|
|
583
|
+
guildOf: ofGuild,
|
|
584
|
+
intents: ({ where }) => reactionsIn(where),
|
|
585
|
+
options: { bots: 'boolean', where: PLACES },
|
|
586
|
+
accepts: (context, options) => wantedPlace(context, options.where, true) &&
|
|
587
|
+
(options.bots === true || !context.user.bot),
|
|
588
|
+
sources: [
|
|
589
|
+
{
|
|
590
|
+
on: 'MESSAGE_REACTION_ADD',
|
|
591
|
+
// In a server, who reacted comes with the event.
|
|
592
|
+
prepare: (ctx, data) => placeAndUser(ctx, data, data.member?.user ? null : data.user_id),
|
|
593
|
+
build: (ctx, data, { prepared }) => {
|
|
594
|
+
const place = placeOf(ctx, data);
|
|
595
|
+
const member = data.member && data.guild_id
|
|
596
|
+
? ctx.entities.member(data.guild_id, data.member)
|
|
597
|
+
: null;
|
|
598
|
+
const user = member?.user ?? userFrom(ctx, prepared, data.user_id);
|
|
599
|
+
if (!user)
|
|
600
|
+
return null;
|
|
601
|
+
return {
|
|
602
|
+
emoji: toCamelCase(data.emoji),
|
|
603
|
+
user,
|
|
604
|
+
member,
|
|
605
|
+
burst: data.burst,
|
|
606
|
+
messageId: data.message_id,
|
|
607
|
+
message: rememberedMessage(place.channel, data.message_id),
|
|
608
|
+
messageAuthorId: data.message_author_id ?? null,
|
|
609
|
+
...place,
|
|
610
|
+
};
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
],
|
|
614
|
+
},
|
|
615
|
+
reactionRemove: {
|
|
616
|
+
guildOf: ofGuild,
|
|
617
|
+
intents: ({ where }) => reactionsIn(where),
|
|
618
|
+
options: { bots: 'boolean', where: PLACES },
|
|
619
|
+
accepts: (context, options) => wantedPlace(context, options.where) &&
|
|
620
|
+
(options.bots === true || !context.user.bot),
|
|
621
|
+
sources: [
|
|
622
|
+
{
|
|
623
|
+
on: 'MESSAGE_REACTION_REMOVE',
|
|
624
|
+
prepare: (ctx, data) => placeAndUser(ctx, data, data.user_id),
|
|
625
|
+
build: (ctx, data, { prepared }) => {
|
|
626
|
+
const place = placeOf(ctx, data);
|
|
627
|
+
const user = userFrom(ctx, prepared, data.user_id);
|
|
628
|
+
if (!user)
|
|
629
|
+
return null;
|
|
630
|
+
return {
|
|
631
|
+
emoji: toCamelCase(data.emoji),
|
|
632
|
+
user,
|
|
633
|
+
member: rememberedMember(place.guild, data.user_id),
|
|
634
|
+
burst: data.burst,
|
|
635
|
+
messageId: data.message_id,
|
|
636
|
+
message: rememberedMessage(place.channel, data.message_id),
|
|
637
|
+
...place,
|
|
638
|
+
};
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
},
|
|
643
|
+
reactionClear: {
|
|
644
|
+
guildOf: ofGuild,
|
|
645
|
+
intents: ({ where }) => reactionsIn(where),
|
|
646
|
+
options: { where: PLACES },
|
|
647
|
+
accepts: (context, options) => wantedPlace(context, options.where),
|
|
648
|
+
sources: [
|
|
649
|
+
cleared('MESSAGE_REACTION_REMOVE_ALL'),
|
|
650
|
+
cleared('MESSAGE_REACTION_REMOVE_EMOJI'),
|
|
651
|
+
],
|
|
652
|
+
},
|
|
653
|
+
pollVoteAdd: {
|
|
654
|
+
guildOf: ofGuild,
|
|
655
|
+
intents: ({ where }) => pollsIn(where),
|
|
656
|
+
options: { where: PLACES },
|
|
657
|
+
accepts: (context, options) => wantedPlace(context, options.where),
|
|
658
|
+
sources: [vote('MESSAGE_POLL_VOTE_ADD')],
|
|
659
|
+
},
|
|
660
|
+
pollVoteRemove: {
|
|
661
|
+
guildOf: ofGuild,
|
|
662
|
+
intents: ({ where }) => pollsIn(where),
|
|
663
|
+
options: { where: PLACES },
|
|
664
|
+
accepts: (context, options) => wantedPlace(context, options.where),
|
|
665
|
+
sources: [vote('MESSAGE_POLL_VOTE_REMOVE')],
|
|
666
|
+
},
|
|
667
|
+
typingStart: {
|
|
668
|
+
guildOf: ofGuild,
|
|
669
|
+
intents: ({ where }) => (where === 'dm' ? 0 : I.GuildMessageTyping) |
|
|
670
|
+
(where === 'dm' || where === 'both' ? I.DirectMessageTyping : 0),
|
|
671
|
+
options: { where: PLACES },
|
|
672
|
+
accepts: (context, options) => wantedPlace(context, options.where, true),
|
|
673
|
+
sources: [
|
|
674
|
+
{
|
|
675
|
+
on: 'TYPING_START',
|
|
676
|
+
prepare: (ctx, data) => placeAndUser(ctx, data, data.member?.user ? null : data.user_id),
|
|
677
|
+
build: (ctx, data, { prepared }) => {
|
|
678
|
+
const member = data.member && data.guild_id
|
|
679
|
+
? ctx.entities.member(data.guild_id, data.member)
|
|
680
|
+
: null;
|
|
681
|
+
const user = member?.user ?? userFrom(ctx, prepared, data.user_id);
|
|
682
|
+
if (!user)
|
|
683
|
+
return null;
|
|
684
|
+
return {
|
|
685
|
+
user,
|
|
686
|
+
member,
|
|
687
|
+
startedAt: new Date(data.timestamp * 1000),
|
|
688
|
+
...placeOf(ctx, data),
|
|
689
|
+
};
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
],
|
|
693
|
+
},
|
|
694
|
+
threadMemberJoin: {
|
|
695
|
+
guildOf: ofGuild,
|
|
696
|
+
// Without it, Discord only says when the bot itself joins a thread.
|
|
697
|
+
intents: I.GuildMembers,
|
|
698
|
+
sources: [
|
|
699
|
+
{
|
|
700
|
+
on: 'THREAD_MEMBERS_UPDATE',
|
|
701
|
+
prepare: (ctx, data) => data.added_members?.length
|
|
702
|
+
? channelOf(ctx, { channel_id: data.id, guild_id: data.guild_id })
|
|
703
|
+
: undefined,
|
|
704
|
+
build: (ctx, data) => {
|
|
705
|
+
const place = threadOf(ctx, data);
|
|
706
|
+
if (!place)
|
|
707
|
+
return null;
|
|
708
|
+
const joined = [];
|
|
709
|
+
for (const added of data.added_members ?? []) {
|
|
710
|
+
// Each one comes with its member in this event.
|
|
711
|
+
if (!added.member?.user)
|
|
712
|
+
continue;
|
|
713
|
+
const member = ctx.entities.member(data.guild_id, added.member);
|
|
714
|
+
joined.push({ member, user: member.user, ...place });
|
|
715
|
+
}
|
|
716
|
+
return joined;
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
threadMemberLeave: {
|
|
722
|
+
guildOf: ofGuild,
|
|
723
|
+
intents: I.GuildMembers,
|
|
724
|
+
sources: [
|
|
725
|
+
{
|
|
726
|
+
on: 'THREAD_MEMBERS_UPDATE',
|
|
727
|
+
prepare: (ctx, data) => data.removed_member_ids?.length
|
|
728
|
+
? channelOf(ctx, { channel_id: data.id, guild_id: data.guild_id })
|
|
729
|
+
: undefined,
|
|
730
|
+
build: (ctx, data) => {
|
|
731
|
+
const place = threadOf(ctx, data);
|
|
732
|
+
if (!place)
|
|
733
|
+
return null;
|
|
734
|
+
return (data.removed_member_ids ?? []).map(userId => ({
|
|
735
|
+
userId,
|
|
736
|
+
user: ctx.cache.users.get(userId) ?? null,
|
|
737
|
+
member: rememberedMember(place.guild, userId),
|
|
738
|
+
...place,
|
|
739
|
+
}));
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
],
|
|
743
|
+
},
|
|
744
|
+
voiceJoin: {
|
|
745
|
+
guildOf: ofGuild,
|
|
746
|
+
intents: I.GuildVoiceStates,
|
|
747
|
+
sources: [
|
|
748
|
+
voice((was, now) => !was && now ? { channel: now.channel, voice: stateOf(now) } : null),
|
|
749
|
+
],
|
|
750
|
+
},
|
|
751
|
+
voiceLeave: {
|
|
752
|
+
guildOf: ofGuild,
|
|
753
|
+
intents: I.GuildVoiceStates,
|
|
754
|
+
sources: [
|
|
755
|
+
voice((was, now) => was && !now ? { channel: was.channel, voice: stateOf(was) } : null),
|
|
756
|
+
],
|
|
757
|
+
},
|
|
758
|
+
voiceMove: {
|
|
759
|
+
guildOf: ofGuild,
|
|
760
|
+
intents: I.GuildVoiceStates,
|
|
761
|
+
sources: [
|
|
762
|
+
voice((was, now) => was && now && was.channel.id !== now.channel.id
|
|
763
|
+
? { from: was.channel, to: now.channel, voice: stateOf(now) }
|
|
764
|
+
: null),
|
|
765
|
+
],
|
|
766
|
+
},
|
|
767
|
+
voiceUpdate: {
|
|
768
|
+
guildOf: ofGuild,
|
|
769
|
+
intents: I.GuildVoiceStates,
|
|
770
|
+
sources: [
|
|
771
|
+
voice((was, now) => was &&
|
|
772
|
+
now &&
|
|
773
|
+
was.channel.id === now.channel.id &&
|
|
774
|
+
JSON.stringify(was.state) !== JSON.stringify(now.state)
|
|
775
|
+
? {
|
|
776
|
+
channel: now.channel,
|
|
777
|
+
voice: stateOf(now),
|
|
778
|
+
before: stateOf(was),
|
|
779
|
+
}
|
|
780
|
+
: null),
|
|
781
|
+
],
|
|
782
|
+
},
|
|
783
|
+
presenceUpdate: {
|
|
784
|
+
guildOf: ofGuild,
|
|
785
|
+
intents: I.GuildPresences,
|
|
786
|
+
sources: [
|
|
787
|
+
{
|
|
788
|
+
on: 'PRESENCE_UPDATE',
|
|
789
|
+
build: (ctx, data) => {
|
|
790
|
+
const guild = findGuild(ctx, data.guild_id);
|
|
791
|
+
if (!guild)
|
|
792
|
+
return null;
|
|
793
|
+
const { user: sent, guild_id: _guildId, ...presence } = data;
|
|
794
|
+
// Discord may only send the id of the user.
|
|
795
|
+
const raw = sent;
|
|
796
|
+
const user = raw.username !== undefined
|
|
797
|
+
? ctx.entities.user(raw)
|
|
798
|
+
: (ctx.cache.users.get(raw.id) ?? null);
|
|
799
|
+
return {
|
|
800
|
+
userId: raw.id,
|
|
801
|
+
user,
|
|
802
|
+
member: rememberedMember(guild, raw.id),
|
|
803
|
+
presence: toCamelCase(presence),
|
|
804
|
+
guild,
|
|
805
|
+
};
|
|
806
|
+
},
|
|
807
|
+
},
|
|
808
|
+
],
|
|
809
|
+
},
|
|
810
|
+
scheduledEventCreate: {
|
|
811
|
+
guildOf: ofGuild,
|
|
812
|
+
intents: I.GuildScheduledEvents,
|
|
813
|
+
sources: [scheduled('GUILD_SCHEDULED_EVENT_CREATE')],
|
|
814
|
+
},
|
|
815
|
+
scheduledEventUpdate: {
|
|
816
|
+
guildOf: ofGuild,
|
|
817
|
+
intents: I.GuildScheduledEvents,
|
|
818
|
+
sources: [scheduled('GUILD_SCHEDULED_EVENT_UPDATE')],
|
|
819
|
+
},
|
|
820
|
+
scheduledEventDelete: {
|
|
821
|
+
guildOf: ofGuild,
|
|
822
|
+
intents: I.GuildScheduledEvents,
|
|
823
|
+
sources: [scheduled('GUILD_SCHEDULED_EVENT_DELETE')],
|
|
824
|
+
},
|
|
825
|
+
scheduledEventUserAdd: {
|
|
826
|
+
guildOf: ofGuild,
|
|
827
|
+
intents: I.GuildScheduledEvents,
|
|
828
|
+
sources: [interested('GUILD_SCHEDULED_EVENT_USER_ADD')],
|
|
829
|
+
},
|
|
830
|
+
scheduledEventUserRemove: {
|
|
831
|
+
guildOf: ofGuild,
|
|
832
|
+
intents: I.GuildScheduledEvents,
|
|
833
|
+
sources: [interested('GUILD_SCHEDULED_EVENT_USER_REMOVE')],
|
|
834
|
+
},
|
|
835
|
+
};
|
|
836
|
+
export const EVENT_NAMES = Object.keys(EVENTS);
|
|
837
|
+
export function isEventName(name) {
|
|
838
|
+
return typeof name === 'string' && Object.hasOwn(EVENTS, name);
|
|
839
|
+
}
|