privage.js 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/rest.d.ts +5 -5
- package/dist/rest.js +41 -7
- package/dist/structures/Interaction.d.ts +15 -2
- package/dist/structures/Interaction.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,9 @@ which have no source message) or `interaction.reply(...)` — optionally after
|
|
|
205
205
|
(`reply({ ..., ephemeral: true })`) are visible only to the interacting user
|
|
206
206
|
and never persisted — great for confirmations and errors; valid for every
|
|
207
207
|
kind. Selected values arrive on `interaction.values` (selects only).
|
|
208
|
+
Replies can carry uploads — `reply({ files: [...] })`, same limits and
|
|
209
|
+
`ATTACH_FILES` requirement as `client.send` (non-ephemeral only, no
|
|
210
|
+
embeds/components alongside in v1).
|
|
208
211
|
|
|
209
212
|
Limits: 5 rows/message; a row holds either ≤5 buttons or exactly one select
|
|
210
213
|
(≤25 interactive components/message); label ≤80, `custom_id` ≤100 and unique
|
package/dist/rest.d.ts
CHANGED
|
@@ -119,11 +119,11 @@ export declare class Rest {
|
|
|
119
119
|
/**
|
|
120
120
|
* Respond to an interaction. `type`: 'update' (terminal — edit the
|
|
121
121
|
* original message), 'reply' (terminal — post a reply; `data.ephemeral:
|
|
122
|
-
* true` for an only-you reply), 'defer' (extend
|
|
123
|
-
* callback must follow), or 'modal' (open a modal
|
|
124
|
-
* only, once). Response: `{ success, message }`
|
|
125
|
-
* ephemeral reply's `message` is a reduced,
|
|
126
|
-
* defer/modal are 204s and resolve `null`.
|
|
122
|
+
* true` for an only-you reply, `data.files` for uploads), 'defer' (extend
|
|
123
|
+
* the window; a terminal callback must follow), or 'modal' (open a modal
|
|
124
|
+
* — pending interactions only, once). Response: `{ success, message }`
|
|
125
|
+
* for update/reply (an ephemeral reply's `message` is a reduced,
|
|
126
|
+
* never-persisted object); defer/modal are 204s and resolve `null`.
|
|
127
127
|
*/
|
|
128
128
|
interactionCallback(interactionId: string, token: string, type: 'update' | 'reply' | 'defer' | 'modal', data?: object): Promise<any>;
|
|
129
129
|
/** PUT /api/bots/commands — bulk overwrite the bot's slash commands (whole-or-400). Response: `{ success, commands }`. */
|
package/dist/rest.js
CHANGED
|
@@ -144,14 +144,48 @@ class Rest {
|
|
|
144
144
|
/**
|
|
145
145
|
* Respond to an interaction. `type`: 'update' (terminal — edit the
|
|
146
146
|
* original message), 'reply' (terminal — post a reply; `data.ephemeral:
|
|
147
|
-
* true` for an only-you reply), 'defer' (extend
|
|
148
|
-
* callback must follow), or 'modal' (open a modal
|
|
149
|
-
* only, once). Response: `{ success, message }`
|
|
150
|
-
* ephemeral reply's `message` is a reduced,
|
|
151
|
-
* defer/modal are 204s and resolve `null`.
|
|
147
|
+
* true` for an only-you reply, `data.files` for uploads), 'defer' (extend
|
|
148
|
+
* the window; a terminal callback must follow), or 'modal' (open a modal
|
|
149
|
+
* — pending interactions only, once). Response: `{ success, message }`
|
|
150
|
+
* for update/reply (an ephemeral reply's `message` is a reduced,
|
|
151
|
+
* never-persisted object); defer/modal are 204s and resolve `null`.
|
|
152
152
|
*/
|
|
153
|
-
interactionCallback(interactionId, token, type, data) {
|
|
154
|
-
|
|
153
|
+
async interactionCallback(interactionId, token, type, data) {
|
|
154
|
+
const path = `/interactions/${interactionId}/${encodeURIComponent(token)}/callback`;
|
|
155
|
+
const d = data;
|
|
156
|
+
if (d?.files?.length) {
|
|
157
|
+
// File replies upload as multipart; the server accepts them for
|
|
158
|
+
// non-ephemeral 'reply' only — fail loud and local on everything else.
|
|
159
|
+
if (type !== 'reply') {
|
|
160
|
+
throw new RangeError(`File attachments are only valid on reply callbacks (got '${type}').`);
|
|
161
|
+
}
|
|
162
|
+
if (d.ephemeral) {
|
|
163
|
+
throw new RangeError('Ephemeral replies cannot carry file attachments — nothing is persisted, so there is nowhere for the files to live.');
|
|
164
|
+
}
|
|
165
|
+
if (d.embeds?.length || d.components?.length) {
|
|
166
|
+
throw new RangeError('Attachments cannot be combined with embeds or components in one reply (v1) — send them separately.');
|
|
167
|
+
}
|
|
168
|
+
if (d.files.length > 10) {
|
|
169
|
+
throw new RangeError(`At most 10 attachments per reply (got ${d.files.length}).`);
|
|
170
|
+
}
|
|
171
|
+
const { files, ephemeral: _e, embeds: _em, components: _c, nonce, ...rest } = d;
|
|
172
|
+
const form = new FormData();
|
|
173
|
+
form.append('type', 'reply');
|
|
174
|
+
// Always carry a nonce: the server commits the message before the
|
|
175
|
+
// terminal state change, so a retried callback dedupes against it
|
|
176
|
+
// instead of inserting a second message.
|
|
177
|
+
form.append('data', JSON.stringify({ ...rest, nonce: nonce ?? (0, crypto_1.randomUUID)() }));
|
|
178
|
+
const parts = await (0, attachments_1.resolveFiles)(files);
|
|
179
|
+
for (const { blob, name } of parts) {
|
|
180
|
+
form.append('files', blob, name);
|
|
181
|
+
}
|
|
182
|
+
if (parts.some((p) => p.spoiler)) {
|
|
183
|
+
form.append('attachment_spoilers', JSON.stringify(parts.map((p) => p.spoiler)));
|
|
184
|
+
}
|
|
185
|
+
// Uploads get a longer budget than ordinary REST calls.
|
|
186
|
+
return this.request(path, { method: 'POST', body: form, timeoutMs: 60000 });
|
|
187
|
+
}
|
|
188
|
+
return this.request(path, { method: 'POST', body: data === undefined ? { type } : { type, data } });
|
|
155
189
|
}
|
|
156
190
|
/** PUT /api/bots/commands — bulk overwrite the bot's slash commands (whole-or-400). Response: `{ success, commands }`. */
|
|
157
191
|
setCommands(commands) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Client } from '../Client';
|
|
2
2
|
import type { EmbedBuilder, Embed } from '../embeds';
|
|
3
3
|
import type { ActionRow, ActionRowBuilder } from '../components';
|
|
4
|
+
import type { AttachmentBuilder, AttachmentSource } from '../attachments';
|
|
4
5
|
import { ModalBuilder, type Modal } from '../modals';
|
|
5
6
|
/** Discriminates what the user did. Pre-v2 envelopes (no `kind`) read as `'button'`. */
|
|
6
7
|
export type InteractionKind = 'button' | 'select' | 'command' | 'modal_submit';
|
|
@@ -15,11 +16,22 @@ export interface InteractionReplyData {
|
|
|
15
16
|
/**
|
|
16
17
|
* Only-you reply: never persisted, delivered as an event to the
|
|
17
18
|
* interacting user alone ("Only you can see this"). Valid for every
|
|
18
|
-
* interaction kind. Still terminal.
|
|
19
|
+
* interaction kind. Still terminal. Cannot carry `files` (nothing is
|
|
20
|
+
* persisted, so there is nowhere for the bytes to live).
|
|
19
21
|
*/
|
|
20
22
|
ephemeral?: boolean;
|
|
21
23
|
/** Components on the reply — ephemeral replies only (they render, but aren't clickable v1). */
|
|
22
24
|
components?: (ActionRow | ActionRowBuilder | Record<string, unknown>)[];
|
|
25
|
+
/**
|
|
26
|
+
* File attachments on the reply — `AttachmentBuilder`s, paths, raw bytes,
|
|
27
|
+
* or Blobs (max 10, 25 MB each). Non-ephemeral replies only; cannot be
|
|
28
|
+
* combined with `embeds` or `components` (v1). Uploads as multipart with
|
|
29
|
+
* an auto-generated `nonce`, so a retried callback never duplicates the
|
|
30
|
+
* message.
|
|
31
|
+
*/
|
|
32
|
+
files?: (AttachmentBuilder | AttachmentSource)[];
|
|
33
|
+
/** Client-dedupe nonce — auto-generated for file replies when omitted. */
|
|
34
|
+
nonce?: string;
|
|
23
35
|
}
|
|
24
36
|
export type CommandOptionType = 'string' | 'integer' | 'number' | 'boolean' | 'user' | 'channel' | 'role';
|
|
25
37
|
/** One resolved option of a slash-command invocation (already validated + coerced by the server). */
|
|
@@ -112,7 +124,8 @@ export declare class Interaction {
|
|
|
112
124
|
/**
|
|
113
125
|
* Terminal: post a bot message replying to the original (a plain channel
|
|
114
126
|
* message for commands). Pass `{ ephemeral: true }` for an only-you reply
|
|
115
|
-
* that is never persisted
|
|
127
|
+
* that is never persisted, or `{ files: [...] }` to attach uploads
|
|
128
|
+
* (non-ephemeral only; needs `ATTACH_FILES` in the channel).
|
|
116
129
|
*/
|
|
117
130
|
reply(data: string | InteractionReplyData): Promise<any>;
|
|
118
131
|
/**
|
|
@@ -104,7 +104,8 @@ class Interaction {
|
|
|
104
104
|
/**
|
|
105
105
|
* Terminal: post a bot message replying to the original (a plain channel
|
|
106
106
|
* message for commands). Pass `{ ephemeral: true }` for an only-you reply
|
|
107
|
-
* that is never persisted
|
|
107
|
+
* that is never persisted, or `{ files: [...] }` to attach uploads
|
|
108
|
+
* (non-ephemeral only; needs `ATTACH_FILES` in the channel).
|
|
108
109
|
*/
|
|
109
110
|
reply(data) {
|
|
110
111
|
return this.transition('responded', () => this.client.rest.interactionCallback(this.id, this.token, 'reply', typeof data === 'string' ? { content: data } : data));
|