@wolfstar/plugin-cache 0.0.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/LICENSE +202 -0
- package/README.md +150 -0
- package/dist/esm/index.d.ts +469 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +1436 -0
- package/dist/esm/index.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1436 @@
|
|
|
1
|
+
import { GatewayDispatchEvents } from "discord-api-types/v10";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { brotliCompress, brotliDecompress, gunzip, gzip } from "node:zlib";
|
|
4
|
+
|
|
5
|
+
//#region src/lib/keys.ts
|
|
6
|
+
/**
|
|
7
|
+
* Creates a stable key for a guild-scoped entity.
|
|
8
|
+
*/
|
|
9
|
+
function guildScopedKey(guildId, id) {
|
|
10
|
+
return `${guildId}:${id}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a stable key for a channel message.
|
|
14
|
+
*/
|
|
15
|
+
function messageKey(channelId, messageId) {
|
|
16
|
+
return `${channelId}:${messageId}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a stable key for a guild member.
|
|
20
|
+
*/
|
|
21
|
+
function memberKey(guildId, userId) {
|
|
22
|
+
return guildScopedKey(guildId, userId);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a stable key for a guild presence.
|
|
26
|
+
*/
|
|
27
|
+
function presenceKey(guildId, userId) {
|
|
28
|
+
return guildScopedKey(guildId, userId);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a stable key for a guild voice state.
|
|
32
|
+
*/
|
|
33
|
+
function voiceStateKey(guildId, userId) {
|
|
34
|
+
return guildScopedKey(guildId, userId);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Creates a stable key for a guild role.
|
|
38
|
+
*/
|
|
39
|
+
function roleKey(guildId, roleId) {
|
|
40
|
+
return guildScopedKey(guildId, roleId);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a stable key for a guild emoji.
|
|
44
|
+
*/
|
|
45
|
+
function emojiKey(guildId, emojiId) {
|
|
46
|
+
return guildScopedKey(guildId, emojiId);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Creates a stable key for a guild sticker.
|
|
50
|
+
*/
|
|
51
|
+
function stickerKey(guildId, stickerId) {
|
|
52
|
+
return guildScopedKey(guildId, stickerId);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a stable key for a guild scheduled event.
|
|
56
|
+
*/
|
|
57
|
+
function scheduledEventKey(guildId, scheduledEventId) {
|
|
58
|
+
return guildScopedKey(guildId, scheduledEventId);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a stable key for a stage instance.
|
|
62
|
+
*/
|
|
63
|
+
function stageInstanceKey(guildId, channelId) {
|
|
64
|
+
return guildScopedKey(guildId, channelId);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates a stable key for a guild soundboard sound.
|
|
68
|
+
*/
|
|
69
|
+
function soundboardSoundKey(guildId, soundId) {
|
|
70
|
+
return guildScopedKey(guildId, soundId);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Creates a stable key for a guild auto moderation rule.
|
|
74
|
+
*/
|
|
75
|
+
function autoModerationRuleKey(guildId, ruleId) {
|
|
76
|
+
return guildScopedKey(guildId, ruleId);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a stable key for a guild ban.
|
|
80
|
+
*/
|
|
81
|
+
function banKey(guildId, userId) {
|
|
82
|
+
return guildScopedKey(guildId, userId);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a stable key for a guild integration.
|
|
86
|
+
*/
|
|
87
|
+
function integrationKey(guildId, integrationId) {
|
|
88
|
+
return guildScopedKey(guildId, integrationId);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates a stable key for an invite, using `@global` for invites that do not belong to a guild.
|
|
92
|
+
*/
|
|
93
|
+
function inviteKey(guildId, code) {
|
|
94
|
+
return `${guildId ?? "@global"}:${code}`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a stable key for a thread member.
|
|
98
|
+
*/
|
|
99
|
+
function threadMemberKey(threadId, userId) {
|
|
100
|
+
return `${threadId}:${userId}`;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a stable key for application command permissions.
|
|
104
|
+
*/
|
|
105
|
+
function applicationCommandPermissionsKey(applicationId, guildId, commandId) {
|
|
106
|
+
return `${applicationId}:${guildId}:${commandId}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/lib/reactions.ts
|
|
111
|
+
/**
|
|
112
|
+
* Whether two reaction emojis are the same: custom emojis match by ID, Unicode emojis by name.
|
|
113
|
+
*/
|
|
114
|
+
function isSameEmoji(a, b) {
|
|
115
|
+
return a.id ? a.id === b.id : !b.id && a.name === b.name;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Adds a `MESSAGE_REACTION_ADD` to a cached message's reactions.
|
|
119
|
+
*
|
|
120
|
+
* @param message The cached message.
|
|
121
|
+
* @param data The dispatch data.
|
|
122
|
+
* @param clientUserId The bot's user ID, to set `me` when the bot reacted.
|
|
123
|
+
*/
|
|
124
|
+
function addReaction(message, data, clientUserId) {
|
|
125
|
+
const me = data.user_id === clientUserId;
|
|
126
|
+
const kind = data.burst ? "burst" : "normal";
|
|
127
|
+
const reactions = message.reactions ?? [];
|
|
128
|
+
const existing = reactions.find((reaction) => isSameEmoji(reaction.emoji, data.emoji));
|
|
129
|
+
if (!existing) {
|
|
130
|
+
const reaction = {
|
|
131
|
+
emoji: data.emoji,
|
|
132
|
+
count: 1,
|
|
133
|
+
count_details: {
|
|
134
|
+
normal: data.burst ? 0 : 1,
|
|
135
|
+
burst: data.burst ? 1 : 0
|
|
136
|
+
},
|
|
137
|
+
me: me && !data.burst,
|
|
138
|
+
me_burst: me && data.burst,
|
|
139
|
+
burst_colors: data.burst_colors ?? []
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
...message,
|
|
143
|
+
reactions: [...reactions, reaction]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
...message,
|
|
148
|
+
reactions: reactions.map((reaction) => reaction === existing ? {
|
|
149
|
+
...reaction,
|
|
150
|
+
count: reaction.count + 1,
|
|
151
|
+
count_details: {
|
|
152
|
+
...reaction.count_details,
|
|
153
|
+
[kind]: reaction.count_details[kind] + 1
|
|
154
|
+
},
|
|
155
|
+
me: reaction.me || me && !data.burst,
|
|
156
|
+
me_burst: reaction.me_burst || me && data.burst,
|
|
157
|
+
burst_colors: data.burst ? data.burst_colors ?? reaction.burst_colors : reaction.burst_colors
|
|
158
|
+
} : reaction)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Removes a `MESSAGE_REACTION_REMOVE` from a cached message's reactions, dropping the reaction when its count reaches
|
|
163
|
+
* zero.
|
|
164
|
+
*
|
|
165
|
+
* @param message The cached message.
|
|
166
|
+
* @param data The dispatch data.
|
|
167
|
+
* @param clientUserId The bot's user ID, to clear `me` when the bot's reaction was removed.
|
|
168
|
+
*/
|
|
169
|
+
function removeReaction(message, data, clientUserId) {
|
|
170
|
+
const me = data.user_id === clientUserId;
|
|
171
|
+
const kind = data.burst ? "burst" : "normal";
|
|
172
|
+
const reactions = (message.reactions ?? []).flatMap((reaction) => {
|
|
173
|
+
if (!isSameEmoji(reaction.emoji, data.emoji)) return [reaction];
|
|
174
|
+
if (reaction.count <= 1) return [];
|
|
175
|
+
return [{
|
|
176
|
+
...reaction,
|
|
177
|
+
count: reaction.count - 1,
|
|
178
|
+
count_details: {
|
|
179
|
+
...reaction.count_details,
|
|
180
|
+
[kind]: Math.max(0, reaction.count_details[kind] - 1)
|
|
181
|
+
},
|
|
182
|
+
me: reaction.me && !(me && !data.burst),
|
|
183
|
+
me_burst: reaction.me_burst && !(me && data.burst)
|
|
184
|
+
}];
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
...message,
|
|
188
|
+
reactions
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Removes every reaction with one emoji from a cached message.
|
|
193
|
+
*
|
|
194
|
+
* @param message The cached message.
|
|
195
|
+
* @param emoji The emoji.
|
|
196
|
+
*/
|
|
197
|
+
function removeReactionEmoji(message, emoji) {
|
|
198
|
+
return {
|
|
199
|
+
...message,
|
|
200
|
+
reactions: (message.reactions ?? []).filter((reaction) => !isSameEmoji(reaction.emoji, emoji))
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Counts a `MESSAGE_POLL_VOTE_ADD` or `MESSAGE_POLL_VOTE_REMOVE` in a cached message's poll results.
|
|
205
|
+
*
|
|
206
|
+
* @param message The cached message.
|
|
207
|
+
* @param data The dispatch data.
|
|
208
|
+
* @param delta `1` for a vote, `-1` for a removed vote.
|
|
209
|
+
* @param clientUserId The bot's user ID, to set `me_voted` when the bot voted.
|
|
210
|
+
*/
|
|
211
|
+
function countPollVote(message, data, delta, clientUserId) {
|
|
212
|
+
const { poll } = message;
|
|
213
|
+
if (!poll) return message;
|
|
214
|
+
const me = data.user_id === clientUserId;
|
|
215
|
+
const results = poll.results ?? {
|
|
216
|
+
is_finalized: false,
|
|
217
|
+
answer_counts: []
|
|
218
|
+
};
|
|
219
|
+
const counts = results.answer_counts.some((count) => count.id === data.answer_id) ? results.answer_counts : [...results.answer_counts, {
|
|
220
|
+
id: data.answer_id,
|
|
221
|
+
count: 0,
|
|
222
|
+
me_voted: false
|
|
223
|
+
}];
|
|
224
|
+
return {
|
|
225
|
+
...message,
|
|
226
|
+
poll: {
|
|
227
|
+
...poll,
|
|
228
|
+
results: {
|
|
229
|
+
...results,
|
|
230
|
+
answer_counts: counts.map((count) => count.id === data.answer_id ? {
|
|
231
|
+
...count,
|
|
232
|
+
count: Math.max(0, count.count + delta),
|
|
233
|
+
me_voted: me ? delta === 1 : count.me_voted
|
|
234
|
+
} : count)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/lib/operations.ts
|
|
242
|
+
const cacheEntityNameRecord = {
|
|
243
|
+
applicationCommandPermissions: true,
|
|
244
|
+
auditLogEntries: true,
|
|
245
|
+
autoModerationRules: true,
|
|
246
|
+
bans: true,
|
|
247
|
+
channels: true,
|
|
248
|
+
emojis: true,
|
|
249
|
+
entitlements: true,
|
|
250
|
+
guilds: true,
|
|
251
|
+
integrations: true,
|
|
252
|
+
invites: true,
|
|
253
|
+
members: true,
|
|
254
|
+
messages: true,
|
|
255
|
+
presences: true,
|
|
256
|
+
roles: true,
|
|
257
|
+
scheduledEvents: true,
|
|
258
|
+
soundboardSounds: true,
|
|
259
|
+
stageInstances: true,
|
|
260
|
+
stickers: true,
|
|
261
|
+
subscriptions: true,
|
|
262
|
+
threadMembers: true,
|
|
263
|
+
threads: true,
|
|
264
|
+
users: true,
|
|
265
|
+
voiceStates: true
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* The name of every entity cache held by a {@link Cache}.
|
|
269
|
+
*/
|
|
270
|
+
const CacheEntityNames = Object.keys(cacheEntityNameRecord);
|
|
271
|
+
/**
|
|
272
|
+
* Translates a gateway dispatch into the list of {@link CacheOperation}s it implies, including the cascades (e.g.
|
|
273
|
+
* `CHANNEL_DELETE` also drops that channel's messages).
|
|
274
|
+
*
|
|
275
|
+
* @remarks
|
|
276
|
+
* This is a pure function, it does not touch any cache. Use {@link applyGatewayDispatch} to apply them.
|
|
277
|
+
* `INTERACTION_CREATE` is intentionally ignored: interactions are short-lived and never cached.
|
|
278
|
+
*
|
|
279
|
+
* @param payload The gateway dispatch payload.
|
|
280
|
+
* @param context The bot's user ID, for the `me` flags of reactions and poll votes.
|
|
281
|
+
*/
|
|
282
|
+
function createCacheOperations(payload, context = {}) {
|
|
283
|
+
const { clientUserId } = context;
|
|
284
|
+
const operations = [];
|
|
285
|
+
switch (payload.t) {
|
|
286
|
+
case GatewayDispatchEvents.ApplicationCommandPermissionsUpdate: {
|
|
287
|
+
const data = payload.d;
|
|
288
|
+
operations.push({
|
|
289
|
+
type: "upsert",
|
|
290
|
+
store: "applicationCommandPermissions",
|
|
291
|
+
key: applicationCommandPermissionsKey(data.application_id, data.guild_id, data.id),
|
|
292
|
+
raw: data
|
|
293
|
+
});
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case GatewayDispatchEvents.AutoModerationRuleCreate:
|
|
297
|
+
case GatewayDispatchEvents.AutoModerationRuleUpdate: {
|
|
298
|
+
const data = payload.d;
|
|
299
|
+
operations.push({
|
|
300
|
+
type: "upsert",
|
|
301
|
+
store: "autoModerationRules",
|
|
302
|
+
key: autoModerationRuleKey(data.guild_id, data.id),
|
|
303
|
+
raw: data
|
|
304
|
+
});
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case GatewayDispatchEvents.AutoModerationRuleDelete: {
|
|
308
|
+
const data = payload.d;
|
|
309
|
+
operations.push({
|
|
310
|
+
type: "delete",
|
|
311
|
+
store: "autoModerationRules",
|
|
312
|
+
key: autoModerationRuleKey(data.guild_id, data.id)
|
|
313
|
+
});
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case GatewayDispatchEvents.ChannelCreate:
|
|
317
|
+
case GatewayDispatchEvents.ChannelUpdate: {
|
|
318
|
+
const data = payload.d;
|
|
319
|
+
operations.push({
|
|
320
|
+
type: "upsert",
|
|
321
|
+
store: "channels",
|
|
322
|
+
key: data.id,
|
|
323
|
+
raw: data,
|
|
324
|
+
merge: payload.t === GatewayDispatchEvents.ChannelUpdate
|
|
325
|
+
});
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case GatewayDispatchEvents.ChannelDelete: {
|
|
329
|
+
const data = payload.d;
|
|
330
|
+
operations.push({
|
|
331
|
+
type: "delete",
|
|
332
|
+
store: "channels",
|
|
333
|
+
key: data.id
|
|
334
|
+
});
|
|
335
|
+
operations.push({
|
|
336
|
+
type: "deletePrefix",
|
|
337
|
+
store: "messages",
|
|
338
|
+
prefix: `${data.id}:`
|
|
339
|
+
});
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case GatewayDispatchEvents.EntitlementCreate:
|
|
343
|
+
case GatewayDispatchEvents.EntitlementUpdate: {
|
|
344
|
+
const data = payload.d;
|
|
345
|
+
operations.push({
|
|
346
|
+
type: "upsert",
|
|
347
|
+
store: "entitlements",
|
|
348
|
+
key: data.id,
|
|
349
|
+
raw: data
|
|
350
|
+
});
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case GatewayDispatchEvents.EntitlementDelete:
|
|
354
|
+
operations.push({
|
|
355
|
+
type: "delete",
|
|
356
|
+
store: "entitlements",
|
|
357
|
+
key: payload.d.id
|
|
358
|
+
});
|
|
359
|
+
break;
|
|
360
|
+
case GatewayDispatchEvents.GuildAuditLogEntryCreate: {
|
|
361
|
+
const data = payload.d;
|
|
362
|
+
operations.push({
|
|
363
|
+
type: "upsert",
|
|
364
|
+
store: "auditLogEntries",
|
|
365
|
+
key: guildScopedKey(data.guild_id, data.id),
|
|
366
|
+
raw: data
|
|
367
|
+
});
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
case GatewayDispatchEvents.GuildBanAdd: {
|
|
371
|
+
const data = payload.d;
|
|
372
|
+
operations.push({
|
|
373
|
+
type: "upsert",
|
|
374
|
+
store: "bans",
|
|
375
|
+
key: banKey(data.guild_id, data.user.id),
|
|
376
|
+
raw: data
|
|
377
|
+
});
|
|
378
|
+
operations.push({
|
|
379
|
+
type: "upsert",
|
|
380
|
+
store: "users",
|
|
381
|
+
key: data.user.id,
|
|
382
|
+
raw: data.user
|
|
383
|
+
});
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case GatewayDispatchEvents.GuildBanRemove: {
|
|
387
|
+
const data = payload.d;
|
|
388
|
+
operations.push({
|
|
389
|
+
type: "delete",
|
|
390
|
+
store: "bans",
|
|
391
|
+
key: banKey(data.guild_id, data.user.id)
|
|
392
|
+
});
|
|
393
|
+
operations.push({
|
|
394
|
+
type: "upsert",
|
|
395
|
+
store: "users",
|
|
396
|
+
key: data.user.id,
|
|
397
|
+
raw: data.user
|
|
398
|
+
});
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
case GatewayDispatchEvents.GuildCreate:
|
|
402
|
+
hydrateGuildCreate(operations, payload.d);
|
|
403
|
+
break;
|
|
404
|
+
case GatewayDispatchEvents.GuildUpdate: {
|
|
405
|
+
const { roles, emojis, stickers, ...data } = payload.d;
|
|
406
|
+
operations.push({
|
|
407
|
+
type: "upsert",
|
|
408
|
+
store: "guilds",
|
|
409
|
+
key: data.id,
|
|
410
|
+
raw: data,
|
|
411
|
+
merge: true
|
|
412
|
+
});
|
|
413
|
+
for (const role of roles ?? []) hydrateRole(operations, data.id, role);
|
|
414
|
+
for (const emoji of emojis ?? []) hydrateEmoji(operations, data.id, emoji);
|
|
415
|
+
for (const sticker of stickers ?? []) hydrateSticker(operations, data.id, sticker);
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
case GatewayDispatchEvents.GuildDelete: {
|
|
419
|
+
const data = payload.d;
|
|
420
|
+
if (data.unavailable) {
|
|
421
|
+
operations.push({
|
|
422
|
+
type: "upsert",
|
|
423
|
+
store: "guilds",
|
|
424
|
+
key: data.id,
|
|
425
|
+
raw: data,
|
|
426
|
+
merge: true
|
|
427
|
+
});
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
operations.push({
|
|
431
|
+
type: "delete",
|
|
432
|
+
store: "guilds",
|
|
433
|
+
key: data.id
|
|
434
|
+
});
|
|
435
|
+
deleteGuildScopedResources(operations, data.id);
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case GatewayDispatchEvents.GuildEmojisUpdate: {
|
|
439
|
+
const data = payload.d;
|
|
440
|
+
operations.push({
|
|
441
|
+
type: "deletePrefix",
|
|
442
|
+
store: "emojis",
|
|
443
|
+
prefix: `${data.guild_id}:`
|
|
444
|
+
});
|
|
445
|
+
for (const emoji of data.emojis) {
|
|
446
|
+
if (!emoji.id) continue;
|
|
447
|
+
operations.push({
|
|
448
|
+
type: "upsert",
|
|
449
|
+
store: "emojis",
|
|
450
|
+
key: emojiKey(data.guild_id, emoji.id),
|
|
451
|
+
raw: withGuildId(emoji, data.guild_id)
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
case GatewayDispatchEvents.GuildStickersUpdate: {
|
|
457
|
+
const data = payload.d;
|
|
458
|
+
operations.push({
|
|
459
|
+
type: "deletePrefix",
|
|
460
|
+
store: "stickers",
|
|
461
|
+
prefix: `${data.guild_id}:`
|
|
462
|
+
});
|
|
463
|
+
for (const sticker of data.stickers) operations.push({
|
|
464
|
+
type: "upsert",
|
|
465
|
+
store: "stickers",
|
|
466
|
+
key: stickerKey(data.guild_id, sticker.id),
|
|
467
|
+
raw: withGuildId(sticker, data.guild_id)
|
|
468
|
+
});
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
case GatewayDispatchEvents.GuildMemberAdd:
|
|
472
|
+
case GatewayDispatchEvents.GuildMemberUpdate: {
|
|
473
|
+
const data = payload.d;
|
|
474
|
+
operations.push({
|
|
475
|
+
type: "upsert",
|
|
476
|
+
store: "members",
|
|
477
|
+
key: memberKey(data.guild_id, data.user.id),
|
|
478
|
+
raw: data,
|
|
479
|
+
merge: true
|
|
480
|
+
});
|
|
481
|
+
operations.push({
|
|
482
|
+
type: "upsert",
|
|
483
|
+
store: "users",
|
|
484
|
+
key: data.user.id,
|
|
485
|
+
raw: data.user
|
|
486
|
+
});
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case GatewayDispatchEvents.GuildMemberRemove: {
|
|
490
|
+
const data = payload.d;
|
|
491
|
+
operations.push({
|
|
492
|
+
type: "delete",
|
|
493
|
+
store: "members",
|
|
494
|
+
key: memberKey(data.guild_id, data.user.id)
|
|
495
|
+
});
|
|
496
|
+
operations.push({
|
|
497
|
+
type: "upsert",
|
|
498
|
+
store: "users",
|
|
499
|
+
key: data.user.id,
|
|
500
|
+
raw: data.user
|
|
501
|
+
});
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case GatewayDispatchEvents.GuildMembersChunk:
|
|
505
|
+
hydrateGuildMembersChunk(operations, payload.d);
|
|
506
|
+
break;
|
|
507
|
+
case GatewayDispatchEvents.GuildRoleCreate:
|
|
508
|
+
case GatewayDispatchEvents.GuildRoleUpdate: {
|
|
509
|
+
const data = payload.d;
|
|
510
|
+
operations.push({
|
|
511
|
+
type: "upsert",
|
|
512
|
+
store: "roles",
|
|
513
|
+
key: roleKey(data.guild_id, data.role.id),
|
|
514
|
+
raw: withGuildId(data.role, data.guild_id),
|
|
515
|
+
merge: payload.t === GatewayDispatchEvents.GuildRoleUpdate
|
|
516
|
+
});
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case GatewayDispatchEvents.GuildRoleDelete: {
|
|
520
|
+
const data = payload.d;
|
|
521
|
+
operations.push({
|
|
522
|
+
type: "delete",
|
|
523
|
+
store: "roles",
|
|
524
|
+
key: roleKey(data.guild_id, data.role_id)
|
|
525
|
+
});
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case GatewayDispatchEvents.GuildScheduledEventCreate:
|
|
529
|
+
case GatewayDispatchEvents.GuildScheduledEventUpdate: {
|
|
530
|
+
const data = payload.d;
|
|
531
|
+
operations.push({
|
|
532
|
+
type: "upsert",
|
|
533
|
+
store: "scheduledEvents",
|
|
534
|
+
key: scheduledEventKey(data.guild_id, data.id),
|
|
535
|
+
raw: data,
|
|
536
|
+
merge: true
|
|
537
|
+
});
|
|
538
|
+
if (data.creator) operations.push({
|
|
539
|
+
type: "upsert",
|
|
540
|
+
store: "users",
|
|
541
|
+
key: data.creator.id,
|
|
542
|
+
raw: data.creator
|
|
543
|
+
});
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case GatewayDispatchEvents.GuildScheduledEventDelete: {
|
|
547
|
+
const data = payload.d;
|
|
548
|
+
operations.push({
|
|
549
|
+
type: "delete",
|
|
550
|
+
store: "scheduledEvents",
|
|
551
|
+
key: scheduledEventKey(data.guild_id, data.id)
|
|
552
|
+
});
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
case GatewayDispatchEvents.GuildSoundboardSoundCreate:
|
|
556
|
+
case GatewayDispatchEvents.GuildSoundboardSoundUpdate: {
|
|
557
|
+
const data = payload.d;
|
|
558
|
+
if (!data.guild_id) break;
|
|
559
|
+
operations.push({
|
|
560
|
+
type: "upsert",
|
|
561
|
+
store: "soundboardSounds",
|
|
562
|
+
key: soundboardSoundKey(data.guild_id, data.sound_id),
|
|
563
|
+
raw: data,
|
|
564
|
+
merge: payload.t === GatewayDispatchEvents.GuildSoundboardSoundUpdate
|
|
565
|
+
});
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
case GatewayDispatchEvents.GuildSoundboardSoundDelete: {
|
|
569
|
+
const data = payload.d;
|
|
570
|
+
operations.push({
|
|
571
|
+
type: "delete",
|
|
572
|
+
store: "soundboardSounds",
|
|
573
|
+
key: soundboardSoundKey(data.guild_id, data.sound_id)
|
|
574
|
+
});
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
case GatewayDispatchEvents.GuildSoundboardSoundsUpdate:
|
|
578
|
+
hydrateGuildSoundboardSoundsUpdate(operations, payload.d);
|
|
579
|
+
break;
|
|
580
|
+
case GatewayDispatchEvents.SoundboardSounds:
|
|
581
|
+
hydrateSoundboardSounds(operations, payload.d);
|
|
582
|
+
break;
|
|
583
|
+
case GatewayDispatchEvents.IntegrationCreate:
|
|
584
|
+
case GatewayDispatchEvents.IntegrationUpdate: {
|
|
585
|
+
const data = payload.d;
|
|
586
|
+
operations.push({
|
|
587
|
+
type: "upsert",
|
|
588
|
+
store: "integrations",
|
|
589
|
+
key: integrationKey(data.guild_id, data.id),
|
|
590
|
+
raw: data,
|
|
591
|
+
merge: true
|
|
592
|
+
});
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
case GatewayDispatchEvents.IntegrationDelete: {
|
|
596
|
+
const data = payload.d;
|
|
597
|
+
operations.push({
|
|
598
|
+
type: "delete",
|
|
599
|
+
store: "integrations",
|
|
600
|
+
key: integrationKey(data.guild_id, data.id)
|
|
601
|
+
});
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
case GatewayDispatchEvents.InviteCreate: {
|
|
605
|
+
const data = payload.d;
|
|
606
|
+
operations.push({
|
|
607
|
+
type: "upsert",
|
|
608
|
+
store: "invites",
|
|
609
|
+
key: inviteKey(data.guild_id, data.code),
|
|
610
|
+
raw: data
|
|
611
|
+
});
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
case GatewayDispatchEvents.InviteDelete: {
|
|
615
|
+
const data = payload.d;
|
|
616
|
+
operations.push({
|
|
617
|
+
type: "delete",
|
|
618
|
+
store: "invites",
|
|
619
|
+
key: inviteKey(data.guild_id, data.code)
|
|
620
|
+
});
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case GatewayDispatchEvents.MessageCreate:
|
|
624
|
+
case GatewayDispatchEvents.MessageUpdate: {
|
|
625
|
+
const data = payload.d;
|
|
626
|
+
operations.push({
|
|
627
|
+
type: "upsert",
|
|
628
|
+
store: "messages",
|
|
629
|
+
key: messageKey(data.channel_id, data.id),
|
|
630
|
+
raw: data,
|
|
631
|
+
merge: payload.t === GatewayDispatchEvents.MessageUpdate
|
|
632
|
+
});
|
|
633
|
+
const author = "author" in data ? data.author : void 0;
|
|
634
|
+
if (author) {
|
|
635
|
+
operations.push({
|
|
636
|
+
type: "upsert",
|
|
637
|
+
store: "users",
|
|
638
|
+
key: author.id,
|
|
639
|
+
raw: author
|
|
640
|
+
});
|
|
641
|
+
if (data.member && data.guild_id) operations.push({
|
|
642
|
+
type: "upsert",
|
|
643
|
+
store: "members",
|
|
644
|
+
key: memberKey(data.guild_id, author.id),
|
|
645
|
+
raw: withGuildId(data.member, data.guild_id),
|
|
646
|
+
merge: true
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
case GatewayDispatchEvents.MessageDelete: {
|
|
652
|
+
const data = payload.d;
|
|
653
|
+
operations.push({
|
|
654
|
+
type: "delete",
|
|
655
|
+
store: "messages",
|
|
656
|
+
key: messageKey(data.channel_id, data.id)
|
|
657
|
+
});
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
case GatewayDispatchEvents.MessageDeleteBulk: {
|
|
661
|
+
const data = payload.d;
|
|
662
|
+
for (const id of data.ids) operations.push({
|
|
663
|
+
type: "delete",
|
|
664
|
+
store: "messages",
|
|
665
|
+
key: messageKey(data.channel_id, id)
|
|
666
|
+
});
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
669
|
+
case GatewayDispatchEvents.MessageReactionAdd: {
|
|
670
|
+
const data = payload.d;
|
|
671
|
+
operations.push(updateMessage(data.channel_id, data.message_id, (message) => addReaction(message, data, clientUserId)));
|
|
672
|
+
const user = data.member?.user;
|
|
673
|
+
if (user && data.guild_id) {
|
|
674
|
+
operations.push({
|
|
675
|
+
type: "upsert",
|
|
676
|
+
store: "users",
|
|
677
|
+
key: user.id,
|
|
678
|
+
raw: user
|
|
679
|
+
});
|
|
680
|
+
operations.push({
|
|
681
|
+
type: "upsert",
|
|
682
|
+
store: "members",
|
|
683
|
+
key: memberKey(data.guild_id, user.id),
|
|
684
|
+
raw: withGuildId(data.member, data.guild_id),
|
|
685
|
+
merge: true
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
case GatewayDispatchEvents.MessageReactionRemove: {
|
|
691
|
+
const data = payload.d;
|
|
692
|
+
operations.push(updateMessage(data.channel_id, data.message_id, (message) => removeReaction(message, data, clientUserId)));
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case GatewayDispatchEvents.MessageReactionRemoveAll: {
|
|
696
|
+
const data = payload.d;
|
|
697
|
+
operations.push(updateMessage(data.channel_id, data.message_id, (message) => ({
|
|
698
|
+
...message,
|
|
699
|
+
reactions: []
|
|
700
|
+
})));
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
case GatewayDispatchEvents.MessageReactionRemoveEmoji: {
|
|
704
|
+
const data = payload.d;
|
|
705
|
+
operations.push(updateMessage(data.channel_id, data.message_id, (message) => removeReactionEmoji(message, data.emoji)));
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
case GatewayDispatchEvents.MessagePollVoteAdd:
|
|
709
|
+
case GatewayDispatchEvents.MessagePollVoteRemove: {
|
|
710
|
+
const data = payload.d;
|
|
711
|
+
const delta = payload.t === GatewayDispatchEvents.MessagePollVoteAdd ? 1 : -1;
|
|
712
|
+
operations.push(updateMessage(data.channel_id, data.message_id, (message) => countPollVote(message, data, delta, clientUserId)));
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
case GatewayDispatchEvents.PresenceUpdate: {
|
|
716
|
+
const data = payload.d;
|
|
717
|
+
operations.push({
|
|
718
|
+
type: "upsert",
|
|
719
|
+
store: "presences",
|
|
720
|
+
key: presenceKey(data.guild_id, data.user.id),
|
|
721
|
+
raw: data,
|
|
722
|
+
merge: true
|
|
723
|
+
});
|
|
724
|
+
operations.push({
|
|
725
|
+
type: "upsert",
|
|
726
|
+
store: "users",
|
|
727
|
+
key: data.user.id,
|
|
728
|
+
raw: data.user,
|
|
729
|
+
merge: true
|
|
730
|
+
});
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
case GatewayDispatchEvents.Ready: {
|
|
734
|
+
const data = payload.d;
|
|
735
|
+
operations.push({
|
|
736
|
+
type: "upsert",
|
|
737
|
+
store: "users",
|
|
738
|
+
key: data.user.id,
|
|
739
|
+
raw: data.user
|
|
740
|
+
});
|
|
741
|
+
for (const guild of data.guilds) operations.push({
|
|
742
|
+
type: "upsert",
|
|
743
|
+
store: "guilds",
|
|
744
|
+
key: guild.id,
|
|
745
|
+
raw: guild,
|
|
746
|
+
merge: true
|
|
747
|
+
});
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
case GatewayDispatchEvents.StageInstanceCreate:
|
|
751
|
+
case GatewayDispatchEvents.StageInstanceUpdate: {
|
|
752
|
+
const data = payload.d;
|
|
753
|
+
operations.push({
|
|
754
|
+
type: "upsert",
|
|
755
|
+
store: "stageInstances",
|
|
756
|
+
key: stageInstanceKey(data.guild_id, data.channel_id),
|
|
757
|
+
raw: data,
|
|
758
|
+
merge: true
|
|
759
|
+
});
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
case GatewayDispatchEvents.StageInstanceDelete: {
|
|
763
|
+
const data = payload.d;
|
|
764
|
+
operations.push({
|
|
765
|
+
type: "delete",
|
|
766
|
+
store: "stageInstances",
|
|
767
|
+
key: stageInstanceKey(data.guild_id, data.channel_id)
|
|
768
|
+
});
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
case GatewayDispatchEvents.SubscriptionCreate:
|
|
772
|
+
case GatewayDispatchEvents.SubscriptionUpdate: {
|
|
773
|
+
const data = payload.d;
|
|
774
|
+
operations.push({
|
|
775
|
+
type: "upsert",
|
|
776
|
+
store: "subscriptions",
|
|
777
|
+
key: data.id,
|
|
778
|
+
raw: data
|
|
779
|
+
});
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
case GatewayDispatchEvents.SubscriptionDelete:
|
|
783
|
+
operations.push({
|
|
784
|
+
type: "delete",
|
|
785
|
+
store: "subscriptions",
|
|
786
|
+
key: payload.d.id
|
|
787
|
+
});
|
|
788
|
+
break;
|
|
789
|
+
case GatewayDispatchEvents.ThreadCreate:
|
|
790
|
+
case GatewayDispatchEvents.ThreadUpdate: {
|
|
791
|
+
const data = payload.d;
|
|
792
|
+
operations.push({
|
|
793
|
+
type: "upsert",
|
|
794
|
+
store: "threads",
|
|
795
|
+
key: data.id,
|
|
796
|
+
raw: data,
|
|
797
|
+
merge: payload.t === GatewayDispatchEvents.ThreadUpdate
|
|
798
|
+
});
|
|
799
|
+
const member = "member" in data ? data.member : void 0;
|
|
800
|
+
if (member?.user_id) operations.push({
|
|
801
|
+
type: "upsert",
|
|
802
|
+
store: "threadMembers",
|
|
803
|
+
key: threadMemberKey(data.id, member.user_id),
|
|
804
|
+
raw: data.guild_id ? withGuildId(member, data.guild_id) : member,
|
|
805
|
+
merge: true
|
|
806
|
+
});
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
case GatewayDispatchEvents.ThreadDelete: {
|
|
810
|
+
const data = payload.d;
|
|
811
|
+
operations.push({
|
|
812
|
+
type: "delete",
|
|
813
|
+
store: "threads",
|
|
814
|
+
key: data.id
|
|
815
|
+
});
|
|
816
|
+
operations.push({
|
|
817
|
+
type: "deletePrefix",
|
|
818
|
+
store: "threadMembers",
|
|
819
|
+
prefix: `${data.id}:`
|
|
820
|
+
});
|
|
821
|
+
operations.push({
|
|
822
|
+
type: "deletePrefix",
|
|
823
|
+
store: "messages",
|
|
824
|
+
prefix: `${data.id}:`
|
|
825
|
+
});
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case GatewayDispatchEvents.ThreadListSync:
|
|
829
|
+
hydrateThreadListSync(operations, payload.d);
|
|
830
|
+
break;
|
|
831
|
+
case GatewayDispatchEvents.ThreadMemberUpdate: {
|
|
832
|
+
const data = payload.d;
|
|
833
|
+
if (!data.id || !data.user_id) break;
|
|
834
|
+
operations.push({
|
|
835
|
+
type: "upsert",
|
|
836
|
+
store: "threadMembers",
|
|
837
|
+
key: threadMemberKey(data.id, data.user_id),
|
|
838
|
+
raw: data,
|
|
839
|
+
merge: true
|
|
840
|
+
});
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
case GatewayDispatchEvents.ThreadMembersUpdate:
|
|
844
|
+
hydrateThreadMembersUpdate(operations, payload.d);
|
|
845
|
+
break;
|
|
846
|
+
case GatewayDispatchEvents.UserUpdate: {
|
|
847
|
+
const data = payload.d;
|
|
848
|
+
operations.push({
|
|
849
|
+
type: "upsert",
|
|
850
|
+
store: "users",
|
|
851
|
+
key: data.id,
|
|
852
|
+
raw: data,
|
|
853
|
+
merge: true
|
|
854
|
+
});
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
case GatewayDispatchEvents.VoiceStateUpdate: {
|
|
858
|
+
const data = payload.d;
|
|
859
|
+
if (!data.guild_id) break;
|
|
860
|
+
const key = voiceStateKey(data.guild_id, data.user_id);
|
|
861
|
+
if (data.channel_id) operations.push({
|
|
862
|
+
type: "upsert",
|
|
863
|
+
store: "voiceStates",
|
|
864
|
+
key,
|
|
865
|
+
raw: data,
|
|
866
|
+
merge: true
|
|
867
|
+
});
|
|
868
|
+
else operations.push({
|
|
869
|
+
type: "delete",
|
|
870
|
+
store: "voiceStates",
|
|
871
|
+
key
|
|
872
|
+
});
|
|
873
|
+
const user = data.member?.user;
|
|
874
|
+
if (data.member && user) {
|
|
875
|
+
operations.push({
|
|
876
|
+
type: "upsert",
|
|
877
|
+
store: "users",
|
|
878
|
+
key: user.id,
|
|
879
|
+
raw: user
|
|
880
|
+
});
|
|
881
|
+
operations.push({
|
|
882
|
+
type: "upsert",
|
|
883
|
+
store: "members",
|
|
884
|
+
key: memberKey(data.guild_id, user.id),
|
|
885
|
+
raw: withGuildId(data.member, data.guild_id),
|
|
886
|
+
merge: true
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return operations;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Applies a list of {@link CacheOperation}s to a {@link Cache}, sequentially and in order.
|
|
896
|
+
*
|
|
897
|
+
* @param cache The cache to mutate.
|
|
898
|
+
* @param operations The operations to apply, usually created by {@link createCacheOperations}.
|
|
899
|
+
*/
|
|
900
|
+
async function applyCacheOperations(cache, operations) {
|
|
901
|
+
for (const operation of operations) {
|
|
902
|
+
const store = cache[operation.store];
|
|
903
|
+
switch (operation.type) {
|
|
904
|
+
case "upsert": {
|
|
905
|
+
const value = operation.merge ? mergeValues(await store.get(operation.key), operation.raw) : operation.raw;
|
|
906
|
+
await store.set(operation.key, value);
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
case "update": {
|
|
910
|
+
const existing = await store.get(operation.key);
|
|
911
|
+
if (existing !== void 0) await store.set(operation.key, operation.update(existing));
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
914
|
+
case "delete":
|
|
915
|
+
await store.delete(operation.key);
|
|
916
|
+
break;
|
|
917
|
+
case "deletePrefix":
|
|
918
|
+
for (const key of await store.keys()) if (key.startsWith(operation.prefix)) await store.delete(key);
|
|
919
|
+
break;
|
|
920
|
+
case "deleteWhere": for (const [key, value] of await store.entries()) if (operation.predicate(value)) await store.delete(key);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Writes a gateway dispatch into every relevant entity cache of a {@link Cache}.
|
|
926
|
+
*
|
|
927
|
+
* @param cache The cache to mutate.
|
|
928
|
+
* @param payload The gateway dispatch payload.
|
|
929
|
+
* @param context The bot's user ID, for the `me` flags of reactions and poll votes.
|
|
930
|
+
*/
|
|
931
|
+
function applyGatewayDispatch(cache, payload, context) {
|
|
932
|
+
return applyCacheOperations(cache, createCacheOperations(payload, context));
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Shallow-merges `value` onto `existing` when both are plain objects, returning `value` otherwise.
|
|
936
|
+
*/
|
|
937
|
+
function mergeValues(existing, value) {
|
|
938
|
+
if (isObject(existing) && isObject(value)) return {
|
|
939
|
+
...existing,
|
|
940
|
+
...value
|
|
941
|
+
};
|
|
942
|
+
return value;
|
|
943
|
+
}
|
|
944
|
+
function hydrateGuildCreate(operations, guild) {
|
|
945
|
+
if ("unavailable" in guild && guild.unavailable) {
|
|
946
|
+
operations.push({
|
|
947
|
+
type: "upsert",
|
|
948
|
+
store: "guilds",
|
|
949
|
+
key: guild.id,
|
|
950
|
+
raw: guild,
|
|
951
|
+
merge: true
|
|
952
|
+
});
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
const { channels, threads, members, presences, voice_states: voiceStates, stage_instances: stageInstances, guild_scheduled_events: scheduledEvents, soundboard_sounds: soundboardSounds, roles, emojis, stickers, ...rest } = guild;
|
|
956
|
+
operations.push({
|
|
957
|
+
type: "upsert",
|
|
958
|
+
store: "guilds",
|
|
959
|
+
key: guild.id,
|
|
960
|
+
raw: rest
|
|
961
|
+
});
|
|
962
|
+
for (const channel of channels ?? []) operations.push({
|
|
963
|
+
type: "upsert",
|
|
964
|
+
store: "channels",
|
|
965
|
+
key: channel.id,
|
|
966
|
+
raw: withGuildId(channel, guild.id)
|
|
967
|
+
});
|
|
968
|
+
for (const thread of threads ?? []) {
|
|
969
|
+
operations.push({
|
|
970
|
+
type: "upsert",
|
|
971
|
+
store: "threads",
|
|
972
|
+
key: thread.id,
|
|
973
|
+
raw: withGuildId(thread, guild.id)
|
|
974
|
+
});
|
|
975
|
+
if (thread.member?.user_id) operations.push({
|
|
976
|
+
type: "upsert",
|
|
977
|
+
store: "threadMembers",
|
|
978
|
+
key: threadMemberKey(thread.id, thread.member.user_id),
|
|
979
|
+
raw: withGuildId(thread.member, guild.id)
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
for (const member of members ?? []) hydrateMember(operations, guild.id, member);
|
|
983
|
+
for (const presence of presences ?? []) operations.push({
|
|
984
|
+
type: "upsert",
|
|
985
|
+
store: "presences",
|
|
986
|
+
key: presenceKey(guild.id, presence.user.id),
|
|
987
|
+
raw: withGuildId(presence, guild.id)
|
|
988
|
+
});
|
|
989
|
+
for (const role of roles ?? []) hydrateRole(operations, guild.id, role);
|
|
990
|
+
for (const emoji of emojis ?? []) hydrateEmoji(operations, guild.id, emoji);
|
|
991
|
+
for (const sticker of stickers ?? []) hydrateSticker(operations, guild.id, sticker);
|
|
992
|
+
for (const voiceState of voiceStates ?? []) {
|
|
993
|
+
if (!voiceState.channel_id) continue;
|
|
994
|
+
operations.push({
|
|
995
|
+
type: "upsert",
|
|
996
|
+
store: "voiceStates",
|
|
997
|
+
key: voiceStateKey(guild.id, voiceState.user_id),
|
|
998
|
+
raw: withGuildId(voiceState, guild.id)
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
for (const stageInstance of stageInstances ?? []) operations.push({
|
|
1002
|
+
type: "upsert",
|
|
1003
|
+
store: "stageInstances",
|
|
1004
|
+
key: stageInstanceKey(guild.id, stageInstance.channel_id),
|
|
1005
|
+
raw: stageInstance
|
|
1006
|
+
});
|
|
1007
|
+
for (const scheduledEvent of scheduledEvents ?? []) operations.push({
|
|
1008
|
+
type: "upsert",
|
|
1009
|
+
store: "scheduledEvents",
|
|
1010
|
+
key: scheduledEventKey(guild.id, scheduledEvent.id),
|
|
1011
|
+
raw: scheduledEvent
|
|
1012
|
+
});
|
|
1013
|
+
for (const sound of soundboardSounds ?? []) hydrateSoundboardSound(operations, guild.id, sound);
|
|
1014
|
+
}
|
|
1015
|
+
function hydrateGuildMembersChunk(operations, data) {
|
|
1016
|
+
for (const member of data.members) hydrateMember(operations, data.guild_id, member);
|
|
1017
|
+
for (const presence of data.presences ?? []) operations.push({
|
|
1018
|
+
type: "upsert",
|
|
1019
|
+
store: "presences",
|
|
1020
|
+
key: presenceKey(data.guild_id, presence.user.id),
|
|
1021
|
+
raw: withGuildId(presence, data.guild_id)
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
function hydrateGuildSoundboardSoundsUpdate(operations, data) {
|
|
1025
|
+
operations.push({
|
|
1026
|
+
type: "deletePrefix",
|
|
1027
|
+
store: "soundboardSounds",
|
|
1028
|
+
prefix: `${data.guild_id}:`
|
|
1029
|
+
});
|
|
1030
|
+
for (const sound of data.soundboard_sounds) hydrateSoundboardSound(operations, data.guild_id, sound);
|
|
1031
|
+
}
|
|
1032
|
+
function hydrateSoundboardSounds(operations, data) {
|
|
1033
|
+
for (const sound of data.soundboard_sounds) hydrateSoundboardSound(operations, data.guild_id, sound);
|
|
1034
|
+
}
|
|
1035
|
+
function hydrateThreadListSync(operations, data) {
|
|
1036
|
+
for (const thread of data.threads) operations.push({
|
|
1037
|
+
type: "upsert",
|
|
1038
|
+
store: "threads",
|
|
1039
|
+
key: thread.id,
|
|
1040
|
+
raw: withGuildId(thread, data.guild_id),
|
|
1041
|
+
merge: true
|
|
1042
|
+
});
|
|
1043
|
+
for (const member of data.members) {
|
|
1044
|
+
if (!member.id || !member.user_id) continue;
|
|
1045
|
+
operations.push({
|
|
1046
|
+
type: "upsert",
|
|
1047
|
+
store: "threadMembers",
|
|
1048
|
+
key: threadMemberKey(member.id, member.user_id),
|
|
1049
|
+
raw: withGuildId(member, data.guild_id),
|
|
1050
|
+
merge: true
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function hydrateThreadMembersUpdate(operations, data) {
|
|
1055
|
+
for (const member of data.added_members ?? []) {
|
|
1056
|
+
if (!member.user_id) continue;
|
|
1057
|
+
operations.push({
|
|
1058
|
+
type: "upsert",
|
|
1059
|
+
store: "threadMembers",
|
|
1060
|
+
key: threadMemberKey(data.id, member.user_id),
|
|
1061
|
+
raw: withGuildId(member, data.guild_id),
|
|
1062
|
+
merge: true
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
for (const userId of data.removed_member_ids ?? []) operations.push({
|
|
1066
|
+
type: "delete",
|
|
1067
|
+
store: "threadMembers",
|
|
1068
|
+
key: threadMemberKey(data.id, userId)
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
function hydrateMember(operations, guildId, member) {
|
|
1072
|
+
if (!member.user) return;
|
|
1073
|
+
operations.push({
|
|
1074
|
+
type: "upsert",
|
|
1075
|
+
store: "members",
|
|
1076
|
+
key: memberKey(guildId, member.user.id),
|
|
1077
|
+
raw: withGuildId(member, guildId),
|
|
1078
|
+
merge: true
|
|
1079
|
+
});
|
|
1080
|
+
operations.push({
|
|
1081
|
+
type: "upsert",
|
|
1082
|
+
store: "users",
|
|
1083
|
+
key: member.user.id,
|
|
1084
|
+
raw: member.user
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
function hydrateRole(operations, guildId, role) {
|
|
1088
|
+
operations.push({
|
|
1089
|
+
type: "upsert",
|
|
1090
|
+
store: "roles",
|
|
1091
|
+
key: roleKey(guildId, role.id),
|
|
1092
|
+
raw: withGuildId(role, guildId)
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
function hydrateEmoji(operations, guildId, emoji) {
|
|
1096
|
+
if (!emoji.id) return;
|
|
1097
|
+
operations.push({
|
|
1098
|
+
type: "upsert",
|
|
1099
|
+
store: "emojis",
|
|
1100
|
+
key: emojiKey(guildId, emoji.id),
|
|
1101
|
+
raw: withGuildId(emoji, guildId)
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
function hydrateSticker(operations, guildId, sticker) {
|
|
1105
|
+
operations.push({
|
|
1106
|
+
type: "upsert",
|
|
1107
|
+
store: "stickers",
|
|
1108
|
+
key: stickerKey(guildId, sticker.id),
|
|
1109
|
+
raw: withGuildId(sticker, guildId)
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
function hydrateSoundboardSound(operations, guildId, sound) {
|
|
1113
|
+
operations.push({
|
|
1114
|
+
type: "upsert",
|
|
1115
|
+
store: "soundboardSounds",
|
|
1116
|
+
key: soundboardSoundKey(guildId, sound.sound_id),
|
|
1117
|
+
raw: withGuildId(sound, guildId)
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
function deleteGuildScopedResources(operations, guildId) {
|
|
1121
|
+
for (const store of [
|
|
1122
|
+
"auditLogEntries",
|
|
1123
|
+
"autoModerationRules",
|
|
1124
|
+
"bans",
|
|
1125
|
+
"emojis",
|
|
1126
|
+
"integrations",
|
|
1127
|
+
"members",
|
|
1128
|
+
"presences",
|
|
1129
|
+
"roles",
|
|
1130
|
+
"scheduledEvents",
|
|
1131
|
+
"soundboardSounds",
|
|
1132
|
+
"stageInstances",
|
|
1133
|
+
"stickers",
|
|
1134
|
+
"voiceStates"
|
|
1135
|
+
]) operations.push({
|
|
1136
|
+
type: "deletePrefix",
|
|
1137
|
+
store,
|
|
1138
|
+
prefix: `${guildId}:`
|
|
1139
|
+
});
|
|
1140
|
+
for (const store of [
|
|
1141
|
+
"applicationCommandPermissions",
|
|
1142
|
+
"channels",
|
|
1143
|
+
"threads",
|
|
1144
|
+
"threadMembers",
|
|
1145
|
+
"messages",
|
|
1146
|
+
"invites"
|
|
1147
|
+
]) operations.push({
|
|
1148
|
+
type: "deleteWhere",
|
|
1149
|
+
store,
|
|
1150
|
+
predicate: (value) => isObject(value) && value.guild_id === guildId
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function updateMessage(channelId, messageId, update) {
|
|
1154
|
+
return {
|
|
1155
|
+
type: "update",
|
|
1156
|
+
store: "messages",
|
|
1157
|
+
key: messageKey(channelId, messageId),
|
|
1158
|
+
update: (value) => update(value)
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
function withGuildId(value, guildId) {
|
|
1162
|
+
return {
|
|
1163
|
+
...value,
|
|
1164
|
+
guild_id: guildId
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
function isObject(value) {
|
|
1168
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
//#endregion
|
|
1172
|
+
//#region src/lib/gateway.ts
|
|
1173
|
+
/**
|
|
1174
|
+
* Writes gateway dispatches into a cache. Call the returned function to stop listening.
|
|
1175
|
+
* Use this when a gateway has no GatewayClient managing its dispatch queue.
|
|
1176
|
+
*/
|
|
1177
|
+
function attachCacheToGateway(gateway, cache, options = {}) {
|
|
1178
|
+
const listener = (payload, shardId) => {
|
|
1179
|
+
applyGatewayDispatch(cache, payload, { clientUserId: options.clientUserId }).catch((error) => {
|
|
1180
|
+
if (options.onError) options.onError(error, payload, shardId);
|
|
1181
|
+
else console.error("Failed to cache gateway dispatch", error);
|
|
1182
|
+
});
|
|
1183
|
+
};
|
|
1184
|
+
gateway.on("dispatch", listener);
|
|
1185
|
+
return () => gateway.off?.("dispatch", listener);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
//#endregion
|
|
1189
|
+
//#region src/lib/memory.ts
|
|
1190
|
+
/**
|
|
1191
|
+
* An {@link EntityCache} backed by a `Map`, optionally bounded as a least-recently-used cache.
|
|
1192
|
+
*/
|
|
1193
|
+
var MemoryEntityCache = class {
|
|
1194
|
+
/**
|
|
1195
|
+
* The maximum amount of entries, `Infinity` for an unbounded cache.
|
|
1196
|
+
*/
|
|
1197
|
+
maxSize;
|
|
1198
|
+
#items = /* @__PURE__ */ new Map();
|
|
1199
|
+
constructor(maxSize = Infinity) {
|
|
1200
|
+
if (maxSize !== Infinity && (!Number.isInteger(maxSize) || maxSize < 0)) throw new RangeError(`maxSize must be a non-negative integer or Infinity, received ${maxSize}`);
|
|
1201
|
+
this.maxSize = maxSize;
|
|
1202
|
+
}
|
|
1203
|
+
get(key) {
|
|
1204
|
+
const value = this.#items.get(key);
|
|
1205
|
+
if (value !== void 0 && this.maxSize !== Infinity) {
|
|
1206
|
+
this.#items.delete(key);
|
|
1207
|
+
this.#items.set(key, value);
|
|
1208
|
+
}
|
|
1209
|
+
return value;
|
|
1210
|
+
}
|
|
1211
|
+
set(key, value) {
|
|
1212
|
+
if (this.maxSize === 0) return;
|
|
1213
|
+
this.#items.delete(key);
|
|
1214
|
+
this.#items.set(key, value);
|
|
1215
|
+
if (this.#items.size > this.maxSize) this.#items.delete(this.#items.keys().next().value);
|
|
1216
|
+
}
|
|
1217
|
+
has(key) {
|
|
1218
|
+
return this.#items.has(key);
|
|
1219
|
+
}
|
|
1220
|
+
delete(key) {
|
|
1221
|
+
return this.#items.delete(key);
|
|
1222
|
+
}
|
|
1223
|
+
clear() {
|
|
1224
|
+
this.#items.clear();
|
|
1225
|
+
}
|
|
1226
|
+
getSize() {
|
|
1227
|
+
return this.#items.size;
|
|
1228
|
+
}
|
|
1229
|
+
keys() {
|
|
1230
|
+
return [...this.#items.keys()];
|
|
1231
|
+
}
|
|
1232
|
+
values() {
|
|
1233
|
+
return [...this.#items.values()];
|
|
1234
|
+
}
|
|
1235
|
+
entries() {
|
|
1236
|
+
return [...this.#items.entries()];
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
/**
|
|
1240
|
+
* Creates a {@link Cache} that keeps everything in the process' memory.
|
|
1241
|
+
*
|
|
1242
|
+
* @example
|
|
1243
|
+
* ```typescript
|
|
1244
|
+
* import { createInMemoryCache } from '@wolfstar/plugin-cache';
|
|
1245
|
+
*
|
|
1246
|
+
* // Keep at most 1000 messages around, everything else is unbounded.
|
|
1247
|
+
* const cache = createInMemoryCache({ maxSize: { messages: 1_000 } });
|
|
1248
|
+
* ```
|
|
1249
|
+
*
|
|
1250
|
+
* @param options The options for the cache.
|
|
1251
|
+
*/
|
|
1252
|
+
function createInMemoryCache(options = {}) {
|
|
1253
|
+
const { maxSize } = options;
|
|
1254
|
+
const resolve = (name) => typeof maxSize === "number" ? maxSize : maxSize?.[name] ?? Infinity;
|
|
1255
|
+
return Object.freeze(Object.fromEntries(CacheEntityNames.map((name) => [name, new MemoryEntityCache(resolve(name))])));
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
//#endregion
|
|
1259
|
+
//#region src/lib/redis.ts
|
|
1260
|
+
const gzipAsync = promisify(gzip);
|
|
1261
|
+
const gunzipAsync = promisify(gunzip);
|
|
1262
|
+
const brotliCompressAsync = promisify(brotliCompress);
|
|
1263
|
+
const brotliDecompressAsync = promisify(brotliDecompress);
|
|
1264
|
+
/**
|
|
1265
|
+
* Thrown when a value stored in Redis cannot be read back: invalid JSON, or compressed bytes that fail to decompress.
|
|
1266
|
+
*
|
|
1267
|
+
* @remarks
|
|
1268
|
+
* A missing value is not an error, `get` resolves to `undefined` for it. Redis connection errors are not wrapped
|
|
1269
|
+
* either, they propagate as the client throws them.
|
|
1270
|
+
*/
|
|
1271
|
+
var CacheValueError = class extends Error {
|
|
1272
|
+
/**
|
|
1273
|
+
* The Redis key holding the unreadable value.
|
|
1274
|
+
*/
|
|
1275
|
+
key;
|
|
1276
|
+
constructor(key, cause) {
|
|
1277
|
+
super(`Cannot read the cached value at "${key}"`, { cause });
|
|
1278
|
+
this.name = "CacheValueError";
|
|
1279
|
+
this.key = key;
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
const CompressionMarkers = {
|
|
1283
|
+
gzip: "gz:",
|
|
1284
|
+
brotli: "br:"
|
|
1285
|
+
};
|
|
1286
|
+
/**
|
|
1287
|
+
* An {@link EntityCache} backed by Redis.
|
|
1288
|
+
*
|
|
1289
|
+
* @remarks
|
|
1290
|
+
* Every entry is stored as its own string key (`<prefix>:<key>`), and a sorted set (`<prefix>:@index`) tracks the
|
|
1291
|
+
* stored keys with their expiration time as score, which is what `keys`, `entries`, `getSize`, and `clear` read from.
|
|
1292
|
+
*/
|
|
1293
|
+
var RedisEntityCache = class {
|
|
1294
|
+
prefix;
|
|
1295
|
+
ttl;
|
|
1296
|
+
compression;
|
|
1297
|
+
compressionThreshold;
|
|
1298
|
+
#redis;
|
|
1299
|
+
constructor(redis, options) {
|
|
1300
|
+
if (options.ttl !== void 0 && !(options.ttl > 0)) throw new RangeError(`ttl must be a positive amount of seconds, received ${options.ttl}`);
|
|
1301
|
+
this.#redis = redis;
|
|
1302
|
+
this.prefix = options.prefix;
|
|
1303
|
+
this.ttl = options.ttl;
|
|
1304
|
+
this.compression = options.compression ?? "none";
|
|
1305
|
+
this.compressionThreshold = options.compressionThreshold ?? 1024;
|
|
1306
|
+
}
|
|
1307
|
+
async get(key) {
|
|
1308
|
+
const valueKey = this.valueKey(key);
|
|
1309
|
+
const value = await this.#redis.get(valueKey);
|
|
1310
|
+
return value === null ? void 0 : this.deserialize(valueKey, value);
|
|
1311
|
+
}
|
|
1312
|
+
async set(key, value) {
|
|
1313
|
+
const serialized = await this.serialize(value);
|
|
1314
|
+
const transaction = this.#redis.multi();
|
|
1315
|
+
if (this.ttl === void 0) transaction.set(this.valueKey(key), serialized).zadd(this.indexKey, "+inf", key);
|
|
1316
|
+
else {
|
|
1317
|
+
const now = Date.now();
|
|
1318
|
+
const milliseconds = Math.round(this.ttl * 1e3);
|
|
1319
|
+
transaction.set(this.valueKey(key), serialized, "PX", milliseconds).zadd(this.indexKey, now + milliseconds, key).zremrangebyscore(this.indexKey, "-inf", now);
|
|
1320
|
+
}
|
|
1321
|
+
await execute(transaction);
|
|
1322
|
+
}
|
|
1323
|
+
async has(key) {
|
|
1324
|
+
return await this.#redis.exists(this.valueKey(key)) > 0;
|
|
1325
|
+
}
|
|
1326
|
+
async delete(key) {
|
|
1327
|
+
const [deleted] = await execute(this.#redis.multi().del(this.valueKey(key)).zrem(this.indexKey, key));
|
|
1328
|
+
return deleted > 0;
|
|
1329
|
+
}
|
|
1330
|
+
async clear() {
|
|
1331
|
+
const keys = await this.#redis.zrange(this.indexKey, "0", "-1");
|
|
1332
|
+
if (keys.length > 0) await this.#redis.del(...keys.map((key) => this.valueKey(key)));
|
|
1333
|
+
await this.#redis.del(this.indexKey);
|
|
1334
|
+
}
|
|
1335
|
+
async getSize() {
|
|
1336
|
+
await this.prune();
|
|
1337
|
+
return this.#redis.zcard(this.indexKey);
|
|
1338
|
+
}
|
|
1339
|
+
async keys() {
|
|
1340
|
+
await this.prune();
|
|
1341
|
+
return this.#redis.zrange(this.indexKey, "0", "-1");
|
|
1342
|
+
}
|
|
1343
|
+
async values() {
|
|
1344
|
+
return (await this.entries()).map(([, value]) => value);
|
|
1345
|
+
}
|
|
1346
|
+
async entries() {
|
|
1347
|
+
const keys = await this.keys();
|
|
1348
|
+
if (keys.length === 0) return [];
|
|
1349
|
+
const values = await this.#redis.mget(...keys.map((key) => this.valueKey(key)));
|
|
1350
|
+
const entries = [];
|
|
1351
|
+
for (const [index, value] of values.entries()) if (value !== null) {
|
|
1352
|
+
const key = keys[index];
|
|
1353
|
+
entries.push([key, await this.deserialize(this.valueKey(key), value)]);
|
|
1354
|
+
}
|
|
1355
|
+
return entries;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Gets the Redis key a value is stored at.
|
|
1359
|
+
* @param key The entity cache key.
|
|
1360
|
+
*/
|
|
1361
|
+
valueKey(key) {
|
|
1362
|
+
return `${this.prefix}:${key}`;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* The Redis key of the sorted set indexing the stored keys.
|
|
1366
|
+
*/
|
|
1367
|
+
get indexKey() {
|
|
1368
|
+
return `${this.prefix}:@index`;
|
|
1369
|
+
}
|
|
1370
|
+
async prune() {
|
|
1371
|
+
if (this.ttl !== void 0) await this.#redis.zremrangebyscore(this.indexKey, "-inf", Date.now());
|
|
1372
|
+
}
|
|
1373
|
+
async serialize(value) {
|
|
1374
|
+
const json = JSON.stringify(value);
|
|
1375
|
+
if (this.compression === "none" || Buffer.byteLength(json) < this.compressionThreshold) return json;
|
|
1376
|
+
const compressed = this.compression === "gzip" ? await gzipAsync(json) : await brotliCompressAsync(json);
|
|
1377
|
+
return `${CompressionMarkers[this.compression]}${compressed.toString("base64")}`;
|
|
1378
|
+
}
|
|
1379
|
+
async deserialize(valueKey, value) {
|
|
1380
|
+
try {
|
|
1381
|
+
if (value.startsWith(CompressionMarkers.gzip)) return JSON.parse((await gunzipAsync(decode(value))).toString("utf8"));
|
|
1382
|
+
if (value.startsWith(CompressionMarkers.brotli)) return JSON.parse((await brotliDecompressAsync(decode(value))).toString("utf8"));
|
|
1383
|
+
return JSON.parse(value);
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
throw new CacheValueError(valueKey, error);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
/**
|
|
1390
|
+
* Runs a transaction, throwing the first command error, and resolves to the command results.
|
|
1391
|
+
*/
|
|
1392
|
+
async function execute(transaction) {
|
|
1393
|
+
const results = await transaction.exec();
|
|
1394
|
+
if (results === null) throw new Error("The Redis transaction was aborted");
|
|
1395
|
+
return results.map(([error, result]) => {
|
|
1396
|
+
if (error) throw error;
|
|
1397
|
+
return result;
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
function decode(value) {
|
|
1401
|
+
return Buffer.from(value.slice(3), "base64");
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* The default prefix of every Redis key owned by a cache created with {@link createRedisCache}.
|
|
1405
|
+
*/
|
|
1406
|
+
const DefaultRedisCachePrefix = "wolfstar:cache";
|
|
1407
|
+
/**
|
|
1408
|
+
* Creates a {@link Cache} stored in Redis, optionally compressing its values.
|
|
1409
|
+
*
|
|
1410
|
+
* @example
|
|
1411
|
+
* ```typescript
|
|
1412
|
+
* import { createRedisCache } from '@wolfstar/plugin-cache';
|
|
1413
|
+
* import { Redis } from 'ioredis';
|
|
1414
|
+
*
|
|
1415
|
+
* const cache = createRedisCache({
|
|
1416
|
+
* redis: new Redis(process.env.REDIS_URL!),
|
|
1417
|
+
* compression: 'gzip',
|
|
1418
|
+
* ttl: { guilds: 60 * 60, users: 30 * 60 },
|
|
1419
|
+
* });
|
|
1420
|
+
* ```
|
|
1421
|
+
*
|
|
1422
|
+
* @param options The options for the cache.
|
|
1423
|
+
*/
|
|
1424
|
+
function createRedisCache(options) {
|
|
1425
|
+
const { redis, prefix = DefaultRedisCachePrefix, compression, compressionThreshold, ttl } = options;
|
|
1426
|
+
return Object.freeze(Object.fromEntries(CacheEntityNames.map((name) => [name, new RedisEntityCache(redis, {
|
|
1427
|
+
prefix: `${prefix}:${name}`,
|
|
1428
|
+
ttl: ttl?.[name],
|
|
1429
|
+
compression,
|
|
1430
|
+
compressionThreshold
|
|
1431
|
+
})])));
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
//#endregion
|
|
1435
|
+
export { CacheEntityNames, CacheValueError, DefaultRedisCachePrefix, MemoryEntityCache, RedisEntityCache, applicationCommandPermissionsKey, applyCacheOperations, applyGatewayDispatch, attachCacheToGateway, autoModerationRuleKey, banKey, createCacheOperations, createInMemoryCache, createRedisCache, emojiKey, guildScopedKey, integrationKey, inviteKey, memberKey, mergeValues, messageKey, presenceKey, roleKey, scheduledEventKey, soundboardSoundKey, stageInstanceKey, stickerKey, threadMemberKey, voiceStateKey };
|
|
1436
|
+
//# sourceMappingURL=index.js.map
|