@seedcord/core 0.3.1 → 0.4.1
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/{Bus-BvF4VShN.mjs → Bus-yJGE6Fs2.mjs} +111 -92
- package/dist/Bus-yJGE6Fs2.mjs.map +1 -0
- package/dist/Pluggable-BI_LQuPm.mjs +280 -0
- package/dist/Pluggable-BI_LQuPm.mjs.map +1 -0
- package/dist/Pluggable-bFCmT4oB.d.mts +148 -0
- package/dist/{Plugin-D9ykXA5g.mjs → Plugin-B0SRHzMX.mjs} +7 -3
- package/dist/Plugin-B0SRHzMX.mjs.map +1 -0
- package/dist/{Plugin-CQ9IEj5X.d.mts → Plugin-BW2JH2_h.d.mts} +2 -2
- package/dist/{cursor-BANzac53.d.mts → cursor-CO_MTgdZ.d.mts} +8 -252
- package/dist/{cursor-CuoA9cRh.mjs → cursor-CaT051ii.mjs} +307 -425
- package/dist/cursor-CaT051ii.mjs.map +1 -0
- package/dist/{index-rt8rZg4d.d.mts → index-CvneCObW.d.mts} +2 -2
- package/dist/index.d.mts +78 -19
- package/dist/index.mjs +223 -21
- package/dist/index.mjs.map +1 -1
- package/dist/internal.index.d.mts +13 -6
- package/dist/internal.index.mjs +8 -455
- package/dist/internal.index.mjs.map +1 -1
- package/dist/node-internal.index.d.mts +6 -133
- package/dist/node-internal.index.mjs +16 -473
- package/dist/node-internal.index.mjs.map +1 -1
- package/dist/node.index.d.mts +2 -0
- package/dist/node.index.mjs +256 -0
- package/dist/node.index.mjs.map +1 -0
- package/dist/{phases-hBxMCD2V.d.mts → phases-C5Steaa2.d.mts} +151 -119
- package/dist/plugin.index.d.mts +1 -1
- package/dist/plugin.index.mjs +1 -1
- package/dist/publishDefault-D826XXDQ.mjs +14 -0
- package/dist/publishDefault-D826XXDQ.mjs.map +1 -0
- package/dist/{slashRouteLeaves-De1SxqnX.mjs → slashRouteLeaves-BQmbFm-S.mjs} +10 -2
- package/dist/slashRouteLeaves-BQmbFm-S.mjs.map +1 -0
- package/dist/{subscribers-wnMyNYZd.mjs → subscribers-DVa-Pr45.mjs} +3 -3
- package/dist/{subscribers-wnMyNYZd.mjs.map → subscribers-DVa-Pr45.mjs.map} +1 -1
- package/package.json +19 -12
- package/dist/Bus-BvF4VShN.mjs.map +0 -1
- package/dist/Plugin-D9ykXA5g.mjs.map +0 -1
- package/dist/cursor-CuoA9cRh.mjs.map +0 -1
- package/dist/slashRouteLeaves-De1SxqnX.mjs.map +0 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { b as
|
|
1
|
+
import { b as InteractionRouteKeys, k as BuilderComponent, x as InteractionRoutes, y as InteractionMetadataKey } from "./Bus-yJGE6Fs2.mjs";
|
|
2
|
+
import { n as asError, t as PublishDefault } from "./publishDefault-D826XXDQ.mjs";
|
|
2
3
|
import "reflect-metadata";
|
|
4
|
+
import { CustomId, decodeFor } from "@seedcord/custom-id";
|
|
3
5
|
import { TextDisplayBuilder } from "@discordjs/builders";
|
|
4
|
-
import { SeedcordErrorCode } from "@seedcord/errors";
|
|
5
6
|
import { ApplicationCommandType, PermissionFlagsBits } from "discord-api-types/v10";
|
|
6
|
-
import {
|
|
7
|
+
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
8
|
+
import { SeedcordError } from "@seedcord/errors/internal";
|
|
9
|
+
import { Logger } from "@seedcord/logger";
|
|
7
10
|
import { prettify, toEpochSeconds } from "@seedcord/utils";
|
|
8
11
|
|
|
9
12
|
//#region src/stops/Notice.ts
|
|
@@ -60,8 +63,9 @@ var Notice = class extends Error {
|
|
|
60
63
|
*/
|
|
61
64
|
ephemeral = true;
|
|
62
65
|
/**
|
|
63
|
-
* A short one-line reason. When every arm of an
|
|
64
|
-
*
|
|
66
|
+
* A short one-line reason. When every arm of an {@link or} gate refuses and each one sets this, `or`
|
|
67
|
+
* throws a refusal listing them under `You need to meet at least one of these:`. Write a verb phrase that
|
|
68
|
+
* finishes that line, for example, "be the bot owner".
|
|
65
69
|
*/
|
|
66
70
|
summary;
|
|
67
71
|
constructor(message, options) {
|
|
@@ -80,419 +84,6 @@ var NoticeCard = class extends BuilderComponent {
|
|
|
80
84
|
}
|
|
81
85
|
};
|
|
82
86
|
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/customId/Errors.ts
|
|
85
|
-
/**
|
|
86
|
-
* Thrown when a customId was minted by an older version of its shape.
|
|
87
|
-
*
|
|
88
|
-
* This is normal after the shape changes.
|
|
89
|
-
*/
|
|
90
|
-
var StaleCustomId = class extends Notice {
|
|
91
|
-
constructor(prefix) {
|
|
92
|
-
super(`Stale customId for "${prefix}".`);
|
|
93
|
-
}
|
|
94
|
-
render() {
|
|
95
|
-
return { components: [new NoticeCard("This button or menu is from an older version. Please run the command again.", "Outdated").component] };
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
/**
|
|
99
|
-
* Thrown when a customId wire is corrupt or tampered with and cannot be trusted.
|
|
100
|
-
*
|
|
101
|
-
* It reports because this is an odd case that should not happen.
|
|
102
|
-
*/
|
|
103
|
-
var InvalidCustomId = class extends Notice {
|
|
104
|
-
constructor(detail) {
|
|
105
|
-
super(`Invalid customId. ${detail}`);
|
|
106
|
-
this.report = true;
|
|
107
|
-
}
|
|
108
|
-
render() {
|
|
109
|
-
return { components: [new NoticeCard("Something went wrong. Please try again.").component] };
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
//#endregion
|
|
114
|
-
//#region src/customId/codec.ts
|
|
115
|
-
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
116
|
-
const BASE = 64n;
|
|
117
|
-
const CHAR_TO_VALUE = new Map([...ALPHABET].map((char, index) => [char, index]));
|
|
118
|
-
const DELIMITER = "";
|
|
119
|
-
const ESCAPE = "\x1B";
|
|
120
|
-
/** @internal */
|
|
121
|
-
const HASH_LENGTH = 3;
|
|
122
|
-
const SAFE_MAX = BigInt(Number.MAX_SAFE_INTEGER);
|
|
123
|
-
const SAFE_MIN = BigInt(Number.MIN_SAFE_INTEGER);
|
|
124
|
-
function bigintToBase64(value) {
|
|
125
|
-
if (value === 0n) return ALPHABET.charAt(0);
|
|
126
|
-
let text = "";
|
|
127
|
-
for (let remaining = value; remaining > 0n; remaining /= BASE) text = ALPHABET.charAt(Number(remaining % BASE)) + text;
|
|
128
|
-
return text;
|
|
129
|
-
}
|
|
130
|
-
function base64ToBigint(text) {
|
|
131
|
-
let value = 0n;
|
|
132
|
-
for (const char of text) {
|
|
133
|
-
const digit = CHAR_TO_VALUE.get(char);
|
|
134
|
-
if (digit === void 0) throw new InvalidCustomId(`bad character ${JSON.stringify(char)}`);
|
|
135
|
-
value = value * BASE + BigInt(digit);
|
|
136
|
-
}
|
|
137
|
-
return value;
|
|
138
|
-
}
|
|
139
|
-
function zigzagEncode(value) {
|
|
140
|
-
const big = BigInt(value);
|
|
141
|
-
return big >= 0n ? big << 1n : (-big << 1n) - 1n;
|
|
142
|
-
}
|
|
143
|
-
function zigzagDecode(encoded) {
|
|
144
|
-
return (encoded & 1n) === 1n ? -(encoded + 1n >> 1n) : encoded >> 1n;
|
|
145
|
-
}
|
|
146
|
-
function escapeToken(text) {
|
|
147
|
-
return text.replaceAll(/[\x1B\x1F]/g, (char) => ESCAPE + char);
|
|
148
|
-
}
|
|
149
|
-
function unescapeToken(text) {
|
|
150
|
-
let out = "";
|
|
151
|
-
for (let i = 0; i < text.length; i++) {
|
|
152
|
-
if (text.charAt(i) !== ESCAPE) {
|
|
153
|
-
out += text.charAt(i);
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
const next = text.charAt(i + 1);
|
|
157
|
-
if (next === "") throw new InvalidCustomId("dangling escape at end of token");
|
|
158
|
-
out += next;
|
|
159
|
-
i++;
|
|
160
|
-
}
|
|
161
|
-
return out;
|
|
162
|
-
}
|
|
163
|
-
function splitTokens(body) {
|
|
164
|
-
const pieces = [];
|
|
165
|
-
let current = "";
|
|
166
|
-
for (let i = 0; i < body.length; i++) {
|
|
167
|
-
const char = body.charAt(i);
|
|
168
|
-
if (char === ESCAPE) {
|
|
169
|
-
current += char + body.charAt(i + 1);
|
|
170
|
-
i++;
|
|
171
|
-
} else if (char === DELIMITER) {
|
|
172
|
-
pieces.push(current);
|
|
173
|
-
current = "";
|
|
174
|
-
} else current += char;
|
|
175
|
-
}
|
|
176
|
-
pieces.push(current);
|
|
177
|
-
return pieces;
|
|
178
|
-
}
|
|
179
|
-
function isBounded(field) {
|
|
180
|
-
if (field.kind === "int") return field.min !== void 0 && field.max !== void 0;
|
|
181
|
-
return field.kind === "snowflake" || field.kind === "uuid" || field.kind === "bool" || field.kind === "oneOf";
|
|
182
|
-
}
|
|
183
|
-
function radixOf(field) {
|
|
184
|
-
switch (field.kind) {
|
|
185
|
-
case "snowflake": return 1n << 64n;
|
|
186
|
-
case "uuid": return 1n << 128n;
|
|
187
|
-
case "bool": return 2n;
|
|
188
|
-
case "oneOf":
|
|
189
|
-
if (!field.choices?.length) throw new InvalidCustomId("oneOf field has no choices");
|
|
190
|
-
return BigInt(field.choices.length);
|
|
191
|
-
case "int":
|
|
192
|
-
if (field.min === void 0 || field.max === void 0) throw new InvalidCustomId("bounded int field is missing a bound");
|
|
193
|
-
return BigInt(field.max) - BigInt(field.min) + 1n;
|
|
194
|
-
default: throw new InvalidCustomId(`field kind ${field.kind} has no radix`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function boundedToBigint(field, name, value) {
|
|
198
|
-
const slot = boundedSlot(field, name, value);
|
|
199
|
-
if (slot < 0n || slot >= radixOf(field)) outOfRange(name, value);
|
|
200
|
-
return slot;
|
|
201
|
-
}
|
|
202
|
-
function boundedSlot(field, name, value) {
|
|
203
|
-
switch (field.kind) {
|
|
204
|
-
case "snowflake":
|
|
205
|
-
if (typeof value !== "string" || !/^\d+$/.test(value)) return outOfRange(name, value);
|
|
206
|
-
return BigInt(value);
|
|
207
|
-
case "uuid": {
|
|
208
|
-
if (typeof value !== "string") return outOfRange(name, value);
|
|
209
|
-
const hex = value.replaceAll("-", "");
|
|
210
|
-
if (!/^[0-9a-fA-F]{32}$/.test(hex)) return outOfRange(name, value);
|
|
211
|
-
return BigInt(`0x${hex}`);
|
|
212
|
-
}
|
|
213
|
-
case "bool": return value ? 1n : 0n;
|
|
214
|
-
case "oneOf": {
|
|
215
|
-
const index = (field.choices ?? []).indexOf(value);
|
|
216
|
-
return index === -1 ? outOfRange(name, value) : BigInt(index);
|
|
217
|
-
}
|
|
218
|
-
case "int":
|
|
219
|
-
if (!Number.isInteger(value)) return outOfRange(name, value);
|
|
220
|
-
return BigInt(value - (field.min ?? 0));
|
|
221
|
-
default: return outOfRange(name, value);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
function outOfRange(name, value) {
|
|
225
|
-
throw new SeedcordRangeError(SeedcordErrorCode.CustomIdValueOutOfRange, [name, String(value)]);
|
|
226
|
-
}
|
|
227
|
-
function bigintToBoundedValue(field, slot) {
|
|
228
|
-
switch (field.kind) {
|
|
229
|
-
case "snowflake": return slot.toString();
|
|
230
|
-
case "uuid": return bigintToUuid(slot);
|
|
231
|
-
case "bool": return slot === 1n;
|
|
232
|
-
case "oneOf": return (field.choices ?? [])[Number(slot)];
|
|
233
|
-
case "int": return Number(slot) + (field.min ?? 0);
|
|
234
|
-
default: throw new InvalidCustomId(`field kind ${field.kind} is not bounded`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
function bigintToUuid(value) {
|
|
238
|
-
const hex = value.toString(16).padStart(32, "0");
|
|
239
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
240
|
-
}
|
|
241
|
-
function encodeUnboundedToken(field, name, value) {
|
|
242
|
-
if (field.kind === "int") {
|
|
243
|
-
if (!Number.isSafeInteger(value)) outOfRange(name, value);
|
|
244
|
-
return bigintToBase64(zigzagEncode(value));
|
|
245
|
-
}
|
|
246
|
-
return escapeToken(value);
|
|
247
|
-
}
|
|
248
|
-
function decodeUnboundedToken(field, piece) {
|
|
249
|
-
if (field.kind !== "int") return unescapeToken(piece);
|
|
250
|
-
if (piece === "") throw new InvalidCustomId("empty integer token");
|
|
251
|
-
const decoded = zigzagDecode(base64ToBigint(piece));
|
|
252
|
-
if (decoded > SAFE_MAX || decoded < SAFE_MIN) throw new InvalidCustomId("integer out of safe range");
|
|
253
|
-
return Number(decoded);
|
|
254
|
-
}
|
|
255
|
-
/** @internal */
|
|
256
|
-
function computeLayoutHash(shape) {
|
|
257
|
-
const signature = JSON.stringify(Object.entries(shape).map(([name, field]) => [
|
|
258
|
-
name,
|
|
259
|
-
field.kind,
|
|
260
|
-
isBounded(field),
|
|
261
|
-
field.kind === "oneOf" ? field.choices ?? [] : null,
|
|
262
|
-
field.kind === "int" ? [field.min ?? null, field.max ?? null] : null
|
|
263
|
-
]));
|
|
264
|
-
const modulus = BASE ** BigInt(3);
|
|
265
|
-
let hash = 0n;
|
|
266
|
-
for (const char of signature) hash = (hash * 131n + BigInt(char.charCodeAt(0))) % modulus;
|
|
267
|
-
let text = "";
|
|
268
|
-
for (let i = 0; i < 3; i++) {
|
|
269
|
-
text = ALPHABET.charAt(Number(hash % BASE)) + text;
|
|
270
|
-
hash /= BASE;
|
|
271
|
-
}
|
|
272
|
-
return text;
|
|
273
|
-
}
|
|
274
|
-
/** @internal */
|
|
275
|
-
function encodeBody(shape, values) {
|
|
276
|
-
const fields = Object.entries(shape);
|
|
277
|
-
const pieces = [];
|
|
278
|
-
const bounded = fields.filter(([, field]) => isBounded(field));
|
|
279
|
-
if (bounded.length > 0) {
|
|
280
|
-
let packed = 0n;
|
|
281
|
-
for (const [name, field] of bounded) packed = packed * radixOf(field) + boundedToBigint(field, name, values[name]);
|
|
282
|
-
pieces.push(bigintToBase64(packed));
|
|
283
|
-
}
|
|
284
|
-
for (const [name, field] of fields) if (!isBounded(field)) pieces.push(encodeUnboundedToken(field, name, values[name]));
|
|
285
|
-
return pieces.join(DELIMITER);
|
|
286
|
-
}
|
|
287
|
-
function unpackBounded(bounded, blob, result) {
|
|
288
|
-
if (blob === void 0 || blob === "") throw new InvalidCustomId("empty packed block");
|
|
289
|
-
let packed = base64ToBigint(blob);
|
|
290
|
-
for (const [name, field] of [...bounded].reverse()) {
|
|
291
|
-
const radix = radixOf(field);
|
|
292
|
-
result[name] = bigintToBoundedValue(field, packed % radix);
|
|
293
|
-
packed /= radix;
|
|
294
|
-
}
|
|
295
|
-
if (packed !== 0n) throw new InvalidCustomId("leftover bits after unpacking");
|
|
296
|
-
}
|
|
297
|
-
/** @internal */
|
|
298
|
-
function decodeBody(shape, body) {
|
|
299
|
-
const fields = Object.entries(shape);
|
|
300
|
-
const bounded = fields.filter(([, field]) => isBounded(field));
|
|
301
|
-
const unbounded = fields.filter(([, field]) => !isBounded(field));
|
|
302
|
-
const expected = (bounded.length > 0 ? 1 : 0) + unbounded.length;
|
|
303
|
-
if (expected === 0) {
|
|
304
|
-
if (body !== "") throw new InvalidCustomId(`expected an empty body, got ${JSON.stringify(body)}`);
|
|
305
|
-
return {};
|
|
306
|
-
}
|
|
307
|
-
const pieces = splitTokens(body);
|
|
308
|
-
if (pieces.length !== expected) throw new InvalidCustomId(`expected ${expected} piece(s), got ${pieces.length}`);
|
|
309
|
-
const result = {};
|
|
310
|
-
let cursor = 0;
|
|
311
|
-
if (bounded.length > 0) {
|
|
312
|
-
unpackBounded(bounded, pieces[cursor], result);
|
|
313
|
-
cursor++;
|
|
314
|
-
}
|
|
315
|
-
for (const [name, field] of unbounded) {
|
|
316
|
-
const piece = pieces[cursor];
|
|
317
|
-
cursor++;
|
|
318
|
-
if (piece === void 0) throw new InvalidCustomId("missing trailing piece");
|
|
319
|
-
result[name] = decodeUnboundedToken(field, piece);
|
|
320
|
-
}
|
|
321
|
-
return result;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
//#endregion
|
|
325
|
-
//#region src/customId/CustomId.ts
|
|
326
|
-
const MAX_WIRE_LENGTH = 100;
|
|
327
|
-
function routeKeyOf(wire) {
|
|
328
|
-
const colon = wire.indexOf(":");
|
|
329
|
-
return colon === -1 ? "" : wire.slice(0, colon);
|
|
330
|
-
}
|
|
331
|
-
/** Strip the layout hash off the routeKey to recover the stable prefix the controller routes by. @internal */
|
|
332
|
-
function prefixOf(wire) {
|
|
333
|
-
const key = routeKeyOf(wire);
|
|
334
|
-
return key.length <= 3 ? "" : key.slice(0, key.length - 3);
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* A typed customId. The single source of truth shared by the component that mints it and the handler
|
|
338
|
-
* that reads it. This gives you typed reads on the `.customId` field in components. Values are packed
|
|
339
|
-
* into a compact wire string, which fits more of them inside Discord's 100-char limit.
|
|
340
|
-
*
|
|
341
|
-
* @typeParam Prefix - The stable route prefix, e.g. 'approve'.
|
|
342
|
-
* @typeParam Shape - The accumulated fields, filled in by the chain.
|
|
343
|
-
*
|
|
344
|
-
* @example
|
|
345
|
-
* ```ts
|
|
346
|
-
* const ApproveId = new CustomId('approve')
|
|
347
|
-
* .snowflake('userId')
|
|
348
|
-
* .oneOf('action', ['approve', 'deny']);
|
|
349
|
-
*
|
|
350
|
-
* // Set the custom id on a button when creating it.
|
|
351
|
-
* new ButtonBuilder().setCustomId(ApproveId.encode({ userId: '123', action: 'approve' }));
|
|
352
|
-
*
|
|
353
|
-
* // in the handler, userId comes back a string
|
|
354
|
-
* const { userId, action } = this.params; // userId: string, action: 'approve' | 'deny'
|
|
355
|
-
* await this.event.guild?.members.fetch(userId);
|
|
356
|
-
* ```
|
|
357
|
-
*/
|
|
358
|
-
var CustomId = class CustomId {
|
|
359
|
-
/** The stable route prefix, e.g. 'approve'. */
|
|
360
|
-
prefix;
|
|
361
|
-
/** The field definitions accumulated by the chain, keyed by name. */
|
|
362
|
-
shape;
|
|
363
|
-
/** The prefix plus a short hash of the shape, the part of the wire before the colon. */
|
|
364
|
-
routeKey;
|
|
365
|
-
constructor(prefix, shape = {}) {
|
|
366
|
-
if (!prefix || /[:\x1B\x1F]/.test(prefix)) throw new SeedcordError(SeedcordErrorCode.CustomIdInvalidPrefix, [prefix]);
|
|
367
|
-
this.prefix = prefix;
|
|
368
|
-
this.shape = shape;
|
|
369
|
-
this.routeKey = prefix + computeLayoutHash(shape);
|
|
370
|
-
}
|
|
371
|
-
add(name, field) {
|
|
372
|
-
if (/^(?:0|[1-9]\d*)$/.test(name)) throw new SeedcordError(SeedcordErrorCode.CustomIdReservedFieldName, [name]);
|
|
373
|
-
if (name in this.shape) throw new SeedcordError(SeedcordErrorCode.CustomIdDuplicateFieldName, [name]);
|
|
374
|
-
const shape = {
|
|
375
|
-
...this.shape,
|
|
376
|
-
[name]: field
|
|
377
|
-
};
|
|
378
|
-
return new CustomId(this.prefix, shape);
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Add a Discord ID field, decoded as a string (the `Snowflake` type from discord-api-types).
|
|
382
|
-
*
|
|
383
|
-
* @example
|
|
384
|
-
* ```ts
|
|
385
|
-
* new CustomId('ban').snowflake('userId');
|
|
386
|
-
* ```
|
|
387
|
-
*/
|
|
388
|
-
snowflake(name) {
|
|
389
|
-
return this.add(name, { kind: "snowflake" });
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* Add a UUID field, decoded as a lowercase uuid string.
|
|
393
|
-
*
|
|
394
|
-
* @example
|
|
395
|
-
* ```ts
|
|
396
|
-
* new CustomId('ticket').uuid('ticketId');
|
|
397
|
-
* ```
|
|
398
|
-
*/
|
|
399
|
-
uuid(name) {
|
|
400
|
-
return this.add(name, { kind: "uuid" });
|
|
401
|
-
}
|
|
402
|
-
int(name, min, max) {
|
|
403
|
-
if (min !== void 0 && max !== void 0 && min > max) throw new SeedcordError(SeedcordErrorCode.CustomIdInvalidBounds, [
|
|
404
|
-
name,
|
|
405
|
-
min,
|
|
406
|
-
max
|
|
407
|
-
]);
|
|
408
|
-
const field = min === void 0 || max === void 0 ? { kind: "int" } : {
|
|
409
|
-
kind: "int",
|
|
410
|
-
min,
|
|
411
|
-
max
|
|
412
|
-
};
|
|
413
|
-
return this.add(name, field);
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Add a boolean flag.
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* ```ts
|
|
420
|
-
* new CustomId('settings').bool('silent');
|
|
421
|
-
* ```
|
|
422
|
-
*/
|
|
423
|
-
bool(name) {
|
|
424
|
-
return this.add(name, { kind: "bool" });
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Add a field that is one value from a fixed list, decoded as the literal union. No `as const` needed.
|
|
428
|
-
*
|
|
429
|
-
* @example
|
|
430
|
-
* ```ts
|
|
431
|
-
* new CustomId('poll').oneOf('choice', ['yes', 'no', 'abstain']);
|
|
432
|
-
* ```
|
|
433
|
-
*/
|
|
434
|
-
oneOf(name, choices) {
|
|
435
|
-
if (choices.length === 0) throw new SeedcordError(SeedcordErrorCode.CustomIdEmptyChoices, [name]);
|
|
436
|
-
return this.add(name, {
|
|
437
|
-
kind: "oneOf",
|
|
438
|
-
choices
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* Add a free short text field. Avoid it where possible. It cannot be packed, which makes it the most
|
|
443
|
-
* expensive field on the wire.
|
|
444
|
-
*
|
|
445
|
-
* @example
|
|
446
|
-
* ```ts
|
|
447
|
-
* new CustomId('note').str('message');
|
|
448
|
-
* ```
|
|
449
|
-
*/
|
|
450
|
-
str(name) {
|
|
451
|
-
return this.add(name, { kind: "string" });
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Mint a wire string from values. Throws if a value is out of its field's range or the wire is over 100 chars.
|
|
455
|
-
*
|
|
456
|
-
* @param values - One value per field, typed by the chain.
|
|
457
|
-
* @returns The wire string to put on the component's customId.
|
|
458
|
-
*/
|
|
459
|
-
encode(values) {
|
|
460
|
-
const wire = `${this.routeKey}:${encodeBody(this.shape, values)}`;
|
|
461
|
-
if (wire.length > MAX_WIRE_LENGTH) throw new SeedcordRangeError(SeedcordErrorCode.CustomIdWireTooLong, [wire.length]);
|
|
462
|
-
return wire;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Read a wire string back into values.
|
|
466
|
-
*
|
|
467
|
-
* - This will refuse with a default Notice when the shape changed since the wire was minted.
|
|
468
|
-
* - Or refuse with a different default Notice on a corrupt or foreign wire.
|
|
469
|
-
*
|
|
470
|
-
* @param wire - The customId string from the interaction.
|
|
471
|
-
* @returns The decoded values, typed by the chain.
|
|
472
|
-
*/
|
|
473
|
-
decode(wire) {
|
|
474
|
-
const key = routeKeyOf(wire);
|
|
475
|
-
if (key !== this.routeKey) {
|
|
476
|
-
if (prefixOf(wire) === this.prefix) throw new StaleCustomId(this.prefix);
|
|
477
|
-
throw new InvalidCustomId(`routeKey ${JSON.stringify(key)} is not ${JSON.stringify(this.routeKey)}`);
|
|
478
|
-
}
|
|
479
|
-
return decodeBody(this.shape, wire.slice(key.length + 1));
|
|
480
|
-
}
|
|
481
|
-
/** True if this wire was minted from this customId's prefix, ignoring the shape hash. */
|
|
482
|
-
owns(wire) {
|
|
483
|
-
return prefixOf(wire) === this.prefix;
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
/** @internal */
|
|
487
|
-
function decodeFor(defs, wire) {
|
|
488
|
-
const match = defs.find((def) => def.owns(wire));
|
|
489
|
-
if (!match) throw new InvalidCustomId(`no customId owns ${JSON.stringify(routeKeyOf(wire))}`);
|
|
490
|
-
return {
|
|
491
|
-
prefix: match.prefix,
|
|
492
|
-
params: match.decode(wire)
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
|
|
496
87
|
//#endregion
|
|
497
88
|
//#region src/customId/routing.ts
|
|
498
89
|
/**
|
|
@@ -566,6 +157,285 @@ function contextMenuRouteOf(kind) {
|
|
|
566
157
|
return kind === ApplicationCommandType.User ? "userContextMenu" : "messageContextMenu";
|
|
567
158
|
}
|
|
568
159
|
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/reply/ackLegality.ts
|
|
162
|
+
const showModalReason = {
|
|
163
|
+
reason: "this interaction was already acknowledged",
|
|
164
|
+
alternative: "showModal() must be the initial response."
|
|
165
|
+
};
|
|
166
|
+
function deferReason(what) {
|
|
167
|
+
return {
|
|
168
|
+
reason: `this interaction was already acknowledged with ${what}`,
|
|
169
|
+
alternative: "A deferral can only be the initial response."
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const ILLEGAL = {
|
|
173
|
+
reply: {
|
|
174
|
+
"deferred-reply": {
|
|
175
|
+
reason: "this interaction was already deferred",
|
|
176
|
+
alternative: "Use edit() to fill the deferred reply or followUp() for a new message."
|
|
177
|
+
},
|
|
178
|
+
"deferred-update": {
|
|
179
|
+
reason: "this interaction was already acknowledged with a deferred update",
|
|
180
|
+
alternative: "Use followUp() for a new message."
|
|
181
|
+
},
|
|
182
|
+
replied: {
|
|
183
|
+
reason: "this interaction was already replied to",
|
|
184
|
+
alternative: "Use followUp() for a new message or edit() to rewrite the reply."
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
defer: {
|
|
188
|
+
"deferred-reply": deferReason("a deferred reply"),
|
|
189
|
+
"deferred-update": deferReason("a deferred update"),
|
|
190
|
+
replied: deferReason("a reply")
|
|
191
|
+
},
|
|
192
|
+
deferUpdate: {
|
|
193
|
+
"deferred-reply": deferReason("a deferred reply"),
|
|
194
|
+
"deferred-update": deferReason("a deferred update"),
|
|
195
|
+
replied: deferReason("a reply")
|
|
196
|
+
},
|
|
197
|
+
update: {
|
|
198
|
+
"deferred-reply": {
|
|
199
|
+
reason: "this interaction was deferred as a reply",
|
|
200
|
+
alternative: "The placeholder is yours via edit(). update() rewrites a source message."
|
|
201
|
+
},
|
|
202
|
+
replied: {
|
|
203
|
+
reason: "this interaction was already replied to",
|
|
204
|
+
alternative: "update() rewrites a source message. Use edit() or followUp() instead."
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
followUp: { unacked: {
|
|
208
|
+
reason: "this interaction has not been acknowledged yet",
|
|
209
|
+
alternative: "Ack first with reply() or defer()."
|
|
210
|
+
} },
|
|
211
|
+
edit: { unacked: {
|
|
212
|
+
reason: "nothing has been sent yet",
|
|
213
|
+
alternative: "Send with reply() or defer() first, then edit() rewrites it."
|
|
214
|
+
} },
|
|
215
|
+
delete: { unacked: {
|
|
216
|
+
reason: "nothing has been sent yet",
|
|
217
|
+
alternative: "Acknowledge first with reply() or defer(), then delete() removes it."
|
|
218
|
+
} },
|
|
219
|
+
showModal: {
|
|
220
|
+
"deferred-reply": showModalReason,
|
|
221
|
+
"deferred-update": showModalReason,
|
|
222
|
+
replied: showModalReason
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const SEND_TARGET = {
|
|
226
|
+
unacked: "reply",
|
|
227
|
+
"deferred-reply": "edit",
|
|
228
|
+
"deferred-update": "followUp",
|
|
229
|
+
replied: "followUp"
|
|
230
|
+
};
|
|
231
|
+
function checkAckLegality(method, state, routeId, ackedBy) {
|
|
232
|
+
if (method === "send") return;
|
|
233
|
+
const illegal = ILLEGAL[method][state];
|
|
234
|
+
if (!illegal) return;
|
|
235
|
+
throw new SeedcordError(SeedcordErrorCode.ReplyIllegalAckState, [
|
|
236
|
+
method,
|
|
237
|
+
illegal.reason,
|
|
238
|
+
illegal.alternative,
|
|
239
|
+
routeId
|
|
240
|
+
], { cause: ackedBy });
|
|
241
|
+
}
|
|
242
|
+
function sendTarget(state) {
|
|
243
|
+
return SEND_TARGET[state];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/reply/AckTrace.ts
|
|
248
|
+
var AckTrace = class extends Error {
|
|
249
|
+
constructor(method) {
|
|
250
|
+
super(`${method}() acknowledged this interaction`);
|
|
251
|
+
this.name = "AckTrace";
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/reply/responseReport.ts
|
|
257
|
+
let replyLogger;
|
|
258
|
+
function logger() {
|
|
259
|
+
replyLogger ??= new Logger("Reply", { channel: "interactions" });
|
|
260
|
+
return replyLogger;
|
|
261
|
+
}
|
|
262
|
+
/** @internal */
|
|
263
|
+
function publishResponse(telemetry, report) {
|
|
264
|
+
const durationMs = performance.now() - report.startedAt;
|
|
265
|
+
const write = {
|
|
266
|
+
routeId: report.routeId,
|
|
267
|
+
interactionId: telemetry.interactionId,
|
|
268
|
+
method: report.method,
|
|
269
|
+
durationMs
|
|
270
|
+
};
|
|
271
|
+
telemetry.bus[PublishDefault]("responseAttempted", report.outcome === "failed" ? {
|
|
272
|
+
...write,
|
|
273
|
+
outcome: "failed",
|
|
274
|
+
messageId: null,
|
|
275
|
+
error: report.error
|
|
276
|
+
} : {
|
|
277
|
+
...write,
|
|
278
|
+
outcome: "sent",
|
|
279
|
+
messageId: report.messageId
|
|
280
|
+
});
|
|
281
|
+
logger().trace(`${paint.sky.bold(report.routeId)} ${report.method} ${report.outcome} ${paint.mute("in")} ${Math.round(durationMs)}ms`);
|
|
282
|
+
}
|
|
283
|
+
async function reportedWrite(telemetry, routeId, method, write) {
|
|
284
|
+
const startedAt = performance.now();
|
|
285
|
+
const result = await attemptWrite(telemetry, routeId, method, startedAt, write);
|
|
286
|
+
publishResponse(telemetry, {
|
|
287
|
+
routeId,
|
|
288
|
+
method,
|
|
289
|
+
startedAt,
|
|
290
|
+
outcome: "sent",
|
|
291
|
+
messageId: null
|
|
292
|
+
});
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
async function attemptWrite(telemetry, routeId, method, startedAt, write) {
|
|
296
|
+
try {
|
|
297
|
+
return await write();
|
|
298
|
+
} catch (caught) {
|
|
299
|
+
publishResponse(telemetry, {
|
|
300
|
+
routeId,
|
|
301
|
+
method,
|
|
302
|
+
startedAt,
|
|
303
|
+
outcome: "failed",
|
|
304
|
+
error: asError(caught)
|
|
305
|
+
});
|
|
306
|
+
throw caught;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/reply/translateSerialization.ts
|
|
312
|
+
const MAX_DEPTH = 8;
|
|
313
|
+
const OPERATOR_WORD = {
|
|
314
|
+
"<=": "at most",
|
|
315
|
+
">=": "at least",
|
|
316
|
+
"<": "fewer than",
|
|
317
|
+
">": "more than"
|
|
318
|
+
};
|
|
319
|
+
const KIND_WORD = {
|
|
320
|
+
string: "a string",
|
|
321
|
+
number: "a number",
|
|
322
|
+
boolean: "a boolean",
|
|
323
|
+
bigint: "a bigint",
|
|
324
|
+
array: "a list",
|
|
325
|
+
tuple: "a list",
|
|
326
|
+
date: "a date",
|
|
327
|
+
object: "an object"
|
|
328
|
+
};
|
|
329
|
+
function asRecord(value) {
|
|
330
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
331
|
+
}
|
|
332
|
+
function firstLine(message) {
|
|
333
|
+
if (typeof message !== "string") return "unknown validation failure";
|
|
334
|
+
return message.split("\n")[0] ?? message;
|
|
335
|
+
}
|
|
336
|
+
const GIVEN_PREVIEW_LIMIT = 40;
|
|
337
|
+
function gotPhrase(given) {
|
|
338
|
+
if (given === void 0) return "nothing";
|
|
339
|
+
if (given === null) return "null";
|
|
340
|
+
if (typeof given === "string") {
|
|
341
|
+
if (given.length === 0) return "an empty string";
|
|
342
|
+
if (given.length > GIVEN_PREVIEW_LIMIT) return `${given.length} characters`;
|
|
343
|
+
return JSON.stringify(given);
|
|
344
|
+
}
|
|
345
|
+
if (Array.isArray(given)) return given.length === 0 ? "an empty list" : `${given.length} items`;
|
|
346
|
+
if (typeof given === "number" || typeof given === "bigint" || typeof given === "boolean") return String(given);
|
|
347
|
+
if (typeof given === "object") return given.constructor?.name ?? "an object";
|
|
348
|
+
return typeof given;
|
|
349
|
+
}
|
|
350
|
+
function needPhrase(expected, given) {
|
|
351
|
+
const bounds = [...expected.matchAll(/expected(\.length)?\s*(<=|>=|<|>)\s*(\d+)/g)];
|
|
352
|
+
if (bounds.length === 0) return null;
|
|
353
|
+
return bounds.map(([, lengthPart, operator, limit]) => {
|
|
354
|
+
const word = OPERATOR_WORD[operator ?? ""] ?? operator ?? "";
|
|
355
|
+
if (!lengthPart) return `${word} ${limit}`;
|
|
356
|
+
const unit = typeof given === "string" ? "character" : "item";
|
|
357
|
+
return `${word} ${limit} ${Number(limit) === 1 ? unit : `${unit}s`}`;
|
|
358
|
+
}).join(" and ");
|
|
359
|
+
}
|
|
360
|
+
function instanceName(expected) {
|
|
361
|
+
if (typeof expected === "function" && expected.name) return expected.name;
|
|
362
|
+
return "specific class instance";
|
|
363
|
+
}
|
|
364
|
+
function describeLeaf(record) {
|
|
365
|
+
if (typeof record.constraint === "string") {
|
|
366
|
+
const need = typeof record.expected === "string" ? needPhrase(record.expected, record.given) : null;
|
|
367
|
+
if (need) return `need ${need}, got ${gotPhrase(record.given)}`;
|
|
368
|
+
return `${firstLine(record.message)}${"given" in record ? ` (got ${gotPhrase(record.given)})` : ""}`;
|
|
369
|
+
}
|
|
370
|
+
if (typeof record.expected === "function") return `need a ${instanceName(record.expected)}, got ${gotPhrase(record.given)}`;
|
|
371
|
+
if (typeof record.validator === "string") {
|
|
372
|
+
const kind = KIND_WORD[record.validator.replace(/^s\./, "").replace(/\(.*$/, "")];
|
|
373
|
+
if (kind) return `need ${kind}, got ${gotPhrase(record.given)}`;
|
|
374
|
+
return `${firstLine(record.message)}, got ${gotPhrase(record.given)}`;
|
|
375
|
+
}
|
|
376
|
+
return firstLine(record.message);
|
|
377
|
+
}
|
|
378
|
+
function describeUnion(branches, depth) {
|
|
379
|
+
const records = [];
|
|
380
|
+
for (const branch of branches) {
|
|
381
|
+
const record = asRecord(branch);
|
|
382
|
+
if (record) records.push(record);
|
|
383
|
+
}
|
|
384
|
+
const bounded = records.find((record) => typeof record.constraint === "string");
|
|
385
|
+
if (bounded) return describeLeaf(bounded);
|
|
386
|
+
const names = [];
|
|
387
|
+
let allInstances = records.length > 0;
|
|
388
|
+
for (const record of records) if (typeof record.expected === "function") names.push(instanceName(record.expected));
|
|
389
|
+
else allInstances = false;
|
|
390
|
+
if (allInstances) return `need a ${[...new Set(names)].join(" or ")}, got ${gotPhrase(records[0]?.given)}`;
|
|
391
|
+
return detailOf(branches[0], depth + 1);
|
|
392
|
+
}
|
|
393
|
+
function detailOf(value, depth) {
|
|
394
|
+
const record = asRecord(value);
|
|
395
|
+
if (!record || depth >= MAX_DEPTH) return firstLine(record?.message);
|
|
396
|
+
if (Array.isArray(record.errors) && record.errors.length > 0) {
|
|
397
|
+
const errors = record.errors;
|
|
398
|
+
const [first] = errors;
|
|
399
|
+
if (Array.isArray(first)) {
|
|
400
|
+
const [key, inner] = first;
|
|
401
|
+
return `${String(key)}: ${detailOf(inner, depth + 1)}`;
|
|
402
|
+
}
|
|
403
|
+
return describeUnion(errors, depth);
|
|
404
|
+
}
|
|
405
|
+
return describeLeaf(record);
|
|
406
|
+
}
|
|
407
|
+
function translateSerializationError(error, componentClass, index, routeId) {
|
|
408
|
+
const detail = detailOf(error, 0);
|
|
409
|
+
return new SeedcordError(SeedcordErrorCode.ReplyComponentSerialization, [
|
|
410
|
+
componentClass,
|
|
411
|
+
index,
|
|
412
|
+
detail,
|
|
413
|
+
routeId
|
|
414
|
+
], { cause: error });
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/reply/serializeReply.ts
|
|
419
|
+
function serializeReply(response, routeId) {
|
|
420
|
+
if (typeof response === "string") try {
|
|
421
|
+
return { components: [new TextDisplayBuilder().setContent(response).toJSON()] };
|
|
422
|
+
} catch (error) {
|
|
423
|
+
throw translateSerializationError(error, "TextDisplayBuilder", 0, routeId);
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
components: response.components.map((component, index) => {
|
|
427
|
+
try {
|
|
428
|
+
return component.toJSON();
|
|
429
|
+
} catch (error) {
|
|
430
|
+
const name = component.constructor?.name;
|
|
431
|
+
throw translateSerializationError(error, !name || name === "Object" ? "component" : name, index, routeId);
|
|
432
|
+
}
|
|
433
|
+
}),
|
|
434
|
+
...response.allowedMentions && { allowedMentions: response.allowedMentions },
|
|
435
|
+
...response.files && { files: response.files }
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
569
439
|
//#endregion
|
|
570
440
|
//#region src/gates/Gate.ts
|
|
571
441
|
/**
|
|
@@ -660,23 +530,30 @@ var GateNotice = class extends Notice {
|
|
|
660
530
|
var NotOwner = class extends GateNotice {
|
|
661
531
|
constructor(message = "Only the bot owner can use this.") {
|
|
662
532
|
super(message);
|
|
533
|
+
this.summary = "be the bot owner";
|
|
663
534
|
}
|
|
664
535
|
};
|
|
665
536
|
var NotInGuild = class extends GateNotice {
|
|
666
537
|
constructor(message = "This can only be used in a server.") {
|
|
667
538
|
super(message);
|
|
539
|
+
this.summary = "be in a server";
|
|
668
540
|
}
|
|
669
541
|
};
|
|
670
542
|
var NotInDm = class extends GateNotice {
|
|
671
543
|
constructor(message = "This can only be used in a direct message.") {
|
|
672
544
|
super(message);
|
|
545
|
+
this.summary = "be in a direct message";
|
|
673
546
|
}
|
|
674
547
|
};
|
|
548
|
+
function retryAt(resetAt) {
|
|
549
|
+
return `<t:${toEpochSeconds(resetAt)}:R>`;
|
|
550
|
+
}
|
|
675
551
|
var OnCooldown = class extends GateNotice {
|
|
676
552
|
resetAt;
|
|
677
553
|
constructor(resetAt, message) {
|
|
678
|
-
super(message ?? `You are doing that too fast. Try again
|
|
554
|
+
super(message ?? `You are doing that too fast. Try again ${retryAt(resetAt)}.`);
|
|
679
555
|
this.resetAt = resetAt;
|
|
556
|
+
this.summary = `try again ${retryAt(resetAt)}`;
|
|
680
557
|
}
|
|
681
558
|
};
|
|
682
559
|
var NotAllowed = class extends Notice {
|
|
@@ -691,16 +568,17 @@ var NeedsAny = class extends Notice {
|
|
|
691
568
|
summaries;
|
|
692
569
|
constructor(summaries) {
|
|
693
570
|
super("not allowed");
|
|
694
|
-
this.summaries = summaries;
|
|
571
|
+
this.summaries = [...new Set(summaries)];
|
|
695
572
|
}
|
|
696
573
|
render() {
|
|
697
574
|
const bullets = this.summaries.map((summary) => `• ${summary}`).join("\n");
|
|
698
|
-
return { components: [new NoticeCard(`You need
|
|
575
|
+
return { components: [new NoticeCard(`You need to meet at least one of these:\n${bullets}`).component] };
|
|
699
576
|
}
|
|
700
577
|
};
|
|
701
578
|
var MissingRole = class extends GateNotice {
|
|
702
579
|
constructor(message, roleId) {
|
|
703
580
|
super(message ?? (roleId ? `You need the <@&${roleId}> role to use this.` : "You do not have the required role."));
|
|
581
|
+
this.summary = roleId ? `hold the <@&${roleId}> role` : "hold the required role";
|
|
704
582
|
}
|
|
705
583
|
};
|
|
706
584
|
var MissingPermissions = class extends Notice {
|
|
@@ -712,6 +590,8 @@ var MissingPermissions = class extends Notice {
|
|
|
712
590
|
this.subject = subject;
|
|
713
591
|
this.missingPerms = missingPerms;
|
|
714
592
|
this.customLead = message;
|
|
593
|
+
const names = missingPerms.join(", ");
|
|
594
|
+
this.summary = subject === null ? `hold ${names}` : `${subject} needs ${names}`;
|
|
715
595
|
}
|
|
716
596
|
render() {
|
|
717
597
|
const bullets = this.missingPerms.map((perm) => `• ${perm}`).join("\n");
|
|
@@ -728,6 +608,7 @@ var HasDangerousPermissions = class extends Notice {
|
|
|
728
608
|
this.subject = subject;
|
|
729
609
|
this.dangerousPerms = dangerousPerms;
|
|
730
610
|
this.customLead = message;
|
|
611
|
+
this.summary = `${subject} must not hold ${dangerousPerms.join(", ")}`;
|
|
731
612
|
}
|
|
732
613
|
render() {
|
|
733
614
|
const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
|
|
@@ -875,8 +756,9 @@ function RequireRole(roleId, options) {
|
|
|
875
756
|
* ```ts
|
|
876
757
|
* import { Silence } from '@seedcord/gateway';
|
|
877
758
|
*
|
|
878
|
-
* //
|
|
879
|
-
*
|
|
759
|
+
* // a helper under a guildMemberAdd handler, stopping when this guild configured nothing to send
|
|
760
|
+
* const template = await settings.welcome(member.guild.id);
|
|
761
|
+
* if (!template) throw new Silence('no welcome message set');
|
|
880
762
|
* ```
|
|
881
763
|
*/
|
|
882
764
|
var Silence = class extends Error {
|
|
@@ -901,5 +783,5 @@ function pageCursor(prefix) {
|
|
|
901
783
|
}
|
|
902
784
|
|
|
903
785
|
//#endregion
|
|
904
|
-
export {
|
|
905
|
-
//# sourceMappingURL=cursor-
|
|
786
|
+
export { translateSerializationError as A, interactionRoutesOf as B, NotInDm as C, defineEffectGate as D, OnCooldown as E, checkAckLegality as F, decodeComponentRoute as G, storeComponentRoute as H, sendTarget as I, NoticeCard as K, SelectMenuKind as L, publishResponse as M, reportedWrite as N, defineGate as O, AckTrace as P, areRoutes as R, NotAllowed as S, NotOwner as T, storeInteractionRoute as U, selectMenuRouteOf as V, ComponentDefsKey as W, GateNotice as _, RequirePermissions as a, MissingRole as b, hasAll as c, pickNotice as d, discardCommits as f, runCommits as g, runCheck as h, RequireBotPermissions as i, attemptWrite as j, serializeReply as k, missingNames as l, rollbackCommits as m, pageCursor as n, RequireRole as o, markCommits as p, Notice as q, Silence as r, PermissionNames as s, PAGE_MAX as t, presentNames as u, HasDangerousPermissions as v, NotInGuild as w, NeedsAny as x, MissingPermissions as y, contextMenuRouteOf as z };
|
|
787
|
+
//# sourceMappingURL=cursor-CaT051ii.mjs.map
|