privage.js 0.21.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 +21 -0
- package/README.md +503 -0
- package/dist/Client.d.ts +192 -0
- package/dist/Client.js +904 -0
- package/dist/attachments.d.ts +41 -0
- package/dist/attachments.js +63 -0
- package/dist/collectors.d.ts +41 -0
- package/dist/collectors.js +52 -0
- package/dist/commands.d.ts +108 -0
- package/dist/commands.js +216 -0
- package/dist/compat.d.ts +72 -0
- package/dist/compat.js +75 -0
- package/dist/components.d.ts +137 -0
- package/dist/components.js +226 -0
- package/dist/embeds.d.ts +97 -0
- package/dist/embeds.js +125 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +48 -0
- package/dist/formatters.d.ts +16 -0
- package/dist/formatters.js +43 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +101 -0
- package/dist/intents.d.ts +28 -0
- package/dist/intents.js +33 -0
- package/dist/modals.d.ts +96 -0
- package/dist/modals.js +162 -0
- package/dist/rest.d.ts +208 -0
- package/dist/rest.js +356 -0
- package/dist/structures/Channel.d.ts +25 -0
- package/dist/structures/Channel.js +34 -0
- package/dist/structures/Interaction.d.ts +149 -0
- package/dist/structures/Interaction.js +170 -0
- package/dist/structures/Member.d.ts +35 -0
- package/dist/structures/Member.js +55 -0
- package/dist/structures/Message.d.ts +181 -0
- package/dist/structures/Message.js +191 -0
- package/dist/structures/Role.d.ts +17 -0
- package/dist/structures/Role.js +20 -0
- package/dist/structures/Server.d.ts +29 -0
- package/dist/structures/Server.js +36 -0
- package/dist/structures/ServerChannels.d.ts +24 -0
- package/dist/structures/ServerChannels.js +50 -0
- package/dist/types.d.ts +195 -0
- package/dist/types.js +2 -0
- package/package.json +49 -0
package/dist/rest.d.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { Embed, EmbedBuilder } from './embeds';
|
|
2
|
+
import type { ActionRow, ActionRowBuilder } from './components';
|
|
3
|
+
import { type AttachmentBuilder, type AttachmentSource } from './attachments';
|
|
4
|
+
export interface SendMessageOptions {
|
|
5
|
+
content?: string;
|
|
6
|
+
/** Bot-only. Accepts plain wire objects or `EmbedBuilder`s (max 10). */
|
|
7
|
+
embeds?: (Embed | EmbedBuilder | Record<string, unknown>)[];
|
|
8
|
+
/**
|
|
9
|
+
* File attachments — `AttachmentBuilder`s, paths, raw bytes, or Blobs
|
|
10
|
+
* (max 10, 25 MB each by default). Cannot be combined with `embeds` or
|
|
11
|
+
* `components` (v1).
|
|
12
|
+
*/
|
|
13
|
+
files?: (AttachmentBuilder | AttachmentSource)[];
|
|
14
|
+
/** Bot-only. Button rows — plain wire objects or `ActionRowBuilder`s (max 5 rows). */
|
|
15
|
+
components?: (ActionRow | ActionRowBuilder | Record<string, unknown>)[];
|
|
16
|
+
replyTo?: string;
|
|
17
|
+
nonce?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface EditMessageOptions {
|
|
20
|
+
content?: string;
|
|
21
|
+
/** Replace the message's button rows (bot's own messages). */
|
|
22
|
+
components?: (ActionRow | ActionRowBuilder | Record<string, unknown>)[];
|
|
23
|
+
}
|
|
24
|
+
/** Server limits: question ≤500 chars; 2–6 unique options ≤200 chars each; deadline within 7 days. */
|
|
25
|
+
export interface CreatePollOptions {
|
|
26
|
+
question: string;
|
|
27
|
+
options: string[];
|
|
28
|
+
/** When voting closes — Date, epoch ms, or ISO string. Required, ≤7 days out. */
|
|
29
|
+
deadline: Date | number | string;
|
|
30
|
+
nonce?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface CreateWebhookOptions {
|
|
33
|
+
/** Display name (defaults to 'Webhook'). */
|
|
34
|
+
name?: string;
|
|
35
|
+
avatarUrl?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface EditWebhookOptions {
|
|
38
|
+
name?: string;
|
|
39
|
+
/** `null` clears the avatar. */
|
|
40
|
+
avatarUrl?: string | null;
|
|
41
|
+
}
|
|
42
|
+
export type PresenceStatus = 'online' | 'idle' | 'dnd' | 'offline';
|
|
43
|
+
/** Rich-presence activity types (server-validated). */
|
|
44
|
+
export declare enum ActivityType {
|
|
45
|
+
Playing = 0,
|
|
46
|
+
Listening = 2,
|
|
47
|
+
Watching = 3,
|
|
48
|
+
Custom = 4,
|
|
49
|
+
Competing = 5
|
|
50
|
+
}
|
|
51
|
+
export interface Activity {
|
|
52
|
+
type: ActivityType | number;
|
|
53
|
+
/** Required, ≤128 chars. */
|
|
54
|
+
name: string;
|
|
55
|
+
/** Optional, ≤128 chars. */
|
|
56
|
+
details?: string;
|
|
57
|
+
/** Optional, ≤128 chars. */
|
|
58
|
+
state?: string;
|
|
59
|
+
timestamps?: {
|
|
60
|
+
start?: number;
|
|
61
|
+
end?: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export interface FetchMessagesOptions {
|
|
65
|
+
/** Max messages to return (≤100, default 30). */
|
|
66
|
+
limit?: number;
|
|
67
|
+
/** ISO timestamp — messages before this point. */
|
|
68
|
+
before?: string;
|
|
69
|
+
beforeId?: string;
|
|
70
|
+
/** ISO timestamp — messages after this point. */
|
|
71
|
+
after?: string;
|
|
72
|
+
afterId?: string;
|
|
73
|
+
around?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface FetchMembersOptions {
|
|
76
|
+
/** Max members to return (≤500, default 200). */
|
|
77
|
+
limit?: number;
|
|
78
|
+
offset?: number;
|
|
79
|
+
}
|
|
80
|
+
export interface KickBanOptions {
|
|
81
|
+
reason?: string;
|
|
82
|
+
/** Also delete the member's recent message history. */
|
|
83
|
+
deleteHistory?: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface TimeoutOptions {
|
|
86
|
+
/** How long to time out for, in seconds. */
|
|
87
|
+
durationSeconds?: number;
|
|
88
|
+
/** Or an absolute end time (mutually exclusive with `durationSeconds`). */
|
|
89
|
+
until?: Date;
|
|
90
|
+
reason?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Thin REST client. Every call authenticates with `Authorization: Bot <token>`
|
|
94
|
+
* and maps non-2xx responses to the typed error taxonomy.
|
|
95
|
+
*/
|
|
96
|
+
export declare class Rest {
|
|
97
|
+
private readonly api;
|
|
98
|
+
private token;
|
|
99
|
+
constructor(opts: {
|
|
100
|
+
api: string;
|
|
101
|
+
token: string;
|
|
102
|
+
});
|
|
103
|
+
setToken(token: string): void;
|
|
104
|
+
request<T = any>(path: string, init?: {
|
|
105
|
+
method?: string;
|
|
106
|
+
body?: unknown;
|
|
107
|
+
timeoutMs?: number;
|
|
108
|
+
}): Promise<T>;
|
|
109
|
+
/** Same as `request`, but retries a 429 (up to {@link REST_MAX_ATTEMPTS} total attempts). */
|
|
110
|
+
private requestAttempt;
|
|
111
|
+
get<T = any>(path: string): Promise<T>;
|
|
112
|
+
private buildQuery;
|
|
113
|
+
/**
|
|
114
|
+
* POST /api/messages/:channelId — `embeds` is bot-only; slowmode + rate
|
|
115
|
+
* limits apply. Response: `{ success, message }` (201) — `message` is the
|
|
116
|
+
* full message object, for both the JSON and multipart branches.
|
|
117
|
+
*/
|
|
118
|
+
sendMessage(channelId: string, data: SendMessageOptions): Promise<any>;
|
|
119
|
+
/**
|
|
120
|
+
* Respond to an interaction. `type`: 'update' (terminal — edit the
|
|
121
|
+
* original message), 'reply' (terminal — post a reply; `data.ephemeral:
|
|
122
|
+
* true` for an only-you reply), 'defer' (extend the window; a terminal
|
|
123
|
+
* callback must follow), or 'modal' (open a modal — pending interactions
|
|
124
|
+
* only, once). Response: `{ success, message }` for update/reply (an
|
|
125
|
+
* ephemeral reply's `message` is a reduced, never-persisted object);
|
|
126
|
+
* defer/modal are 204s and resolve `null`.
|
|
127
|
+
*/
|
|
128
|
+
interactionCallback(interactionId: string, token: string, type: 'update' | 'reply' | 'defer' | 'modal', data?: object): Promise<any>;
|
|
129
|
+
/** PUT /api/bots/commands — bulk overwrite the bot's slash commands (whole-or-400). Response: `{ success, commands }`. */
|
|
130
|
+
setCommands(commands: object[]): Promise<any>;
|
|
131
|
+
/** GET /api/bots/commands — list the bot's registered slash commands. Response: `{ success, commands }`. */
|
|
132
|
+
fetchCommands(): Promise<any>;
|
|
133
|
+
/** PUT — grant a role to a member. Needs MANAGE_ROLES + role below the bot's highest. Response: `{ success, message: string }`. */
|
|
134
|
+
addRole(serverId: string, userId: string, roleId: string): Promise<any>;
|
|
135
|
+
/** DELETE — remove a role from a member. Response: `{ success, message: string }`. */
|
|
136
|
+
removeRole(serverId: string, userId: string, roleId: string): Promise<any>;
|
|
137
|
+
/** Kick a member (needs KICK_MEMBERS + outrank the target). Response: `{ success, message: string }`. */
|
|
138
|
+
kickMember(serverId: string, userId: string, opts?: KickBanOptions): Promise<any>;
|
|
139
|
+
/** Ban a member (needs BAN_MEMBERS + outrank the target). Response: `{ success, message: string }`. */
|
|
140
|
+
banMember(serverId: string, userId: string, opts?: KickBanOptions): Promise<any>;
|
|
141
|
+
/** Lift a ban. Response: `{ success, message: string }`. */
|
|
142
|
+
unbanMember(serverId: string, userId: string): Promise<any>;
|
|
143
|
+
/** Time out (temporarily mute) a member (needs TIMEOUT_MEMBERS). Provide `durationSeconds` or `until`. Response: `{ success, timeout_until }`. */
|
|
144
|
+
timeoutMember(serverId: string, userId: string, opts: TimeoutOptions): Promise<any>;
|
|
145
|
+
/** Remove an active timeout. Response: `{ success }`. */
|
|
146
|
+
removeTimeout(serverId: string, userId: string): Promise<any>;
|
|
147
|
+
/** Delete a message (needs MANAGE_MESSAGES, or the bot's own message). Response: `{ success, message: string }`. */
|
|
148
|
+
deleteMessage(messageId: string): Promise<any>;
|
|
149
|
+
/** Add a reaction (the emoji is URL-path encoded). Response: `{ success, reactions, version }`. */
|
|
150
|
+
react(messageId: string, emoji: string): Promise<any>;
|
|
151
|
+
/** Remove the bot's own reaction. Response: `{ success, reactions, version }`. */
|
|
152
|
+
removeReaction(messageId: string, emoji: string): Promise<any>;
|
|
153
|
+
/**
|
|
154
|
+
* Create a poll in a channel (posts a poll message). Needs send
|
|
155
|
+
* permission there; poll creation is rate-limited (~10/min).
|
|
156
|
+
* Response: `{ success, message }` (201) — `message.poll` carries the
|
|
157
|
+
* poll state (question, options with counts, deadline, version).
|
|
158
|
+
*/
|
|
159
|
+
createPoll(channelId: string, opts: CreatePollOptions): Promise<any>;
|
|
160
|
+
/** Close a poll early. Creator or `MANAGE_MESSAGES`. Response: `{ success, version }` — deliberately lean, no poll/message included. */
|
|
161
|
+
closePoll(pollId: string): Promise<any>;
|
|
162
|
+
/** Create a webhook on a text/announcement channel (needs MANAGE_WEBHOOKS). Response: `{ success, webhook }` (201) — `webhook.token` is returned here and on the list routes. */
|
|
163
|
+
createWebhook(channelId: string, opts?: CreateWebhookOptions): Promise<any>;
|
|
164
|
+
/** List a channel's webhooks. Response: `{ success, webhooks }`. */
|
|
165
|
+
fetchChannelWebhooks(channelId: string): Promise<any>;
|
|
166
|
+
/** List a server's webhooks. Response: `{ success, webhooks }`. */
|
|
167
|
+
fetchServerWebhooks(serverId: string): Promise<any>;
|
|
168
|
+
/** Rename a webhook / change its avatar (`avatarUrl: null` clears it). Response: `{ success, webhook }`. */
|
|
169
|
+
editWebhook(webhookId: string, opts: EditWebhookOptions): Promise<any>;
|
|
170
|
+
/** Delete a webhook. Response: 204 — resolves `null`. */
|
|
171
|
+
deleteWebhook(webhookId: string): Promise<any>;
|
|
172
|
+
/** Rotate a webhook's token (the old one stops working). Response: `{ success, token }`. */
|
|
173
|
+
rotateWebhookToken(webhookId: string): Promise<any>;
|
|
174
|
+
/** Set the bot's presence status. Response: `{ success, status }` — the effective status. */
|
|
175
|
+
setStatus(status: PresenceStatus): Promise<any>;
|
|
176
|
+
/** Set the bot's rich-presence activity. Response: `{ success, activities }`. */
|
|
177
|
+
setActivity(activity: Activity): Promise<any>;
|
|
178
|
+
/** Clear the bot's rich-presence activity. Response: `{ success, activities }` (now empty). */
|
|
179
|
+
clearActivity(): Promise<any>;
|
|
180
|
+
/** Edit a message's content and/or components. Own messages only (403 otherwise). Response: `{ success, message }`. */
|
|
181
|
+
editMessage(messageId: string, contentOrOpts: string | EditMessageOptions): Promise<any>;
|
|
182
|
+
/** Pin a message. Response: `{ success, message: string }`. */
|
|
183
|
+
pinMessage(messageId: string): Promise<any>;
|
|
184
|
+
/** Unpin a message. Response: `{ success, message: string }`. */
|
|
185
|
+
unpinMessage(messageId: string): Promise<any>;
|
|
186
|
+
/**
|
|
187
|
+
* Fetch a channel's message history. Response: `{ success, messages,
|
|
188
|
+
* hasMore }` — plus `hasMoreBefore`/`hasMoreAfter` when `around` is used.
|
|
189
|
+
*/
|
|
190
|
+
fetchMessages(channelId: string, opts?: FetchMessagesOptions): Promise<{
|
|
191
|
+
messages: any[];
|
|
192
|
+
hasMore: boolean;
|
|
193
|
+
hasMoreBefore?: boolean;
|
|
194
|
+
hasMoreAfter?: boolean;
|
|
195
|
+
}>;
|
|
196
|
+
/** List a server's members. Response: `{ success, members }`. */
|
|
197
|
+
fetchMembers(serverId: string, opts?: FetchMembersOptions): Promise<{
|
|
198
|
+
members: any[];
|
|
199
|
+
}>;
|
|
200
|
+
/** List a server's roles. Response: `{ success, roles, PERMISSIONS }` — `PERMISSIONS` maps permission names to bits. */
|
|
201
|
+
fetchRoles(serverId: string): Promise<{
|
|
202
|
+
roles: any[];
|
|
203
|
+
PERMISSIONS: Record<string, number>;
|
|
204
|
+
}>;
|
|
205
|
+
/** Set (or clear, with `null`) a member's nickname. Others need MANAGE_NICKNAMES. Response: `{ success, nickname }`. */
|
|
206
|
+
setNickname(serverId: string, userId: string, nickname: string | null): Promise<any>;
|
|
207
|
+
private toError;
|
|
208
|
+
}
|
package/dist/rest.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Rest = exports.ActivityType = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const attachments_1 = require("./attachments");
|
|
7
|
+
const REST_TIMEOUT_MS = 15000;
|
|
8
|
+
// 429 auto-retry: at most 3 total attempts (1 original + 2 retries), waiting
|
|
9
|
+
// the server's `retryAfter` between them (floored so a 0/near-0 value from a
|
|
10
|
+
// buggy limiter still backs off a little).
|
|
11
|
+
const REST_MAX_ATTEMPTS = 3;
|
|
12
|
+
const REST_MIN_RETRY_MS = 250;
|
|
13
|
+
function sleep(ms) {
|
|
14
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
+
}
|
|
16
|
+
/** Rich-presence activity types (server-validated). */
|
|
17
|
+
var ActivityType;
|
|
18
|
+
(function (ActivityType) {
|
|
19
|
+
ActivityType[ActivityType["Playing"] = 0] = "Playing";
|
|
20
|
+
ActivityType[ActivityType["Listening"] = 2] = "Listening";
|
|
21
|
+
ActivityType[ActivityType["Watching"] = 3] = "Watching";
|
|
22
|
+
ActivityType[ActivityType["Custom"] = 4] = "Custom";
|
|
23
|
+
ActivityType[ActivityType["Competing"] = 5] = "Competing";
|
|
24
|
+
})(ActivityType || (exports.ActivityType = ActivityType = {}));
|
|
25
|
+
function kickBanBody(opts) {
|
|
26
|
+
if (opts.reason == null && opts.deleteHistory == null)
|
|
27
|
+
return undefined;
|
|
28
|
+
const body = {};
|
|
29
|
+
if (opts.reason != null)
|
|
30
|
+
body.reason = opts.reason;
|
|
31
|
+
if (opts.deleteHistory != null)
|
|
32
|
+
body.delete_history = opts.deleteHistory;
|
|
33
|
+
return body;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Thin REST client. Every call authenticates with `Authorization: Bot <token>`
|
|
37
|
+
* and maps non-2xx responses to the typed error taxonomy.
|
|
38
|
+
*/
|
|
39
|
+
class Rest {
|
|
40
|
+
constructor(opts) {
|
|
41
|
+
this.api = opts.api;
|
|
42
|
+
this.token = opts.token;
|
|
43
|
+
}
|
|
44
|
+
setToken(token) {
|
|
45
|
+
this.token = token;
|
|
46
|
+
}
|
|
47
|
+
request(path, init = {}) {
|
|
48
|
+
return this.requestAttempt(path, init, 1);
|
|
49
|
+
}
|
|
50
|
+
/** Same as `request`, but retries a 429 (up to {@link REST_MAX_ATTEMPTS} total attempts). */
|
|
51
|
+
async requestAttempt(path, init, attempt) {
|
|
52
|
+
// FormData bodies pass through untouched — fetch sets the multipart
|
|
53
|
+
// boundary itself, so Content-Type must NOT be set manually.
|
|
54
|
+
const isForm = typeof FormData !== 'undefined' && init.body instanceof FormData;
|
|
55
|
+
let res;
|
|
56
|
+
try {
|
|
57
|
+
res = await fetch(`${this.api}/api${path}`, {
|
|
58
|
+
method: init.method || 'GET',
|
|
59
|
+
headers: {
|
|
60
|
+
Authorization: `Bot ${this.token}`,
|
|
61
|
+
...(isForm ? {} : { 'Content-Type': 'application/json' }),
|
|
62
|
+
},
|
|
63
|
+
body: isForm
|
|
64
|
+
? init.body
|
|
65
|
+
: init.body != null
|
|
66
|
+
? JSON.stringify(init.body)
|
|
67
|
+
: undefined,
|
|
68
|
+
// A hung request must never stall the caller (e.g. hydration blocking `ready`).
|
|
69
|
+
signal: AbortSignal.timeout(init.timeoutMs ?? REST_TIMEOUT_MS),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
const why = e?.name === 'TimeoutError' ? `timed out after ${REST_TIMEOUT_MS}ms` : (e?.message || 'network error');
|
|
74
|
+
throw new errors_1.PrivageConnectionError(`${path} -> ${why}`);
|
|
75
|
+
}
|
|
76
|
+
const json = await res.json().catch(() => null);
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const err = this.toError(res.status, json, path, res.headers.get('retry-after'));
|
|
79
|
+
if (err instanceof errors_1.PrivageRateLimitError && attempt < REST_MAX_ATTEMPTS) {
|
|
80
|
+
await sleep(Math.max(err.retryAfter, REST_MIN_RETRY_MS));
|
|
81
|
+
return this.requestAttempt(path, init, attempt + 1);
|
|
82
|
+
}
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
return json;
|
|
86
|
+
}
|
|
87
|
+
get(path) {
|
|
88
|
+
return this.request(path);
|
|
89
|
+
}
|
|
90
|
+
buildQuery(params) {
|
|
91
|
+
const usp = new URLSearchParams();
|
|
92
|
+
for (const [key, value] of Object.entries(params)) {
|
|
93
|
+
if (value != null)
|
|
94
|
+
usp.set(key, String(value));
|
|
95
|
+
}
|
|
96
|
+
const qs = usp.toString();
|
|
97
|
+
return qs ? `?${qs}` : '';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* POST /api/messages/:channelId — `embeds` is bot-only; slowmode + rate
|
|
101
|
+
* limits apply. Response: `{ success, message }` (201) — `message` is the
|
|
102
|
+
* full message object, for both the JSON and multipart branches.
|
|
103
|
+
*/
|
|
104
|
+
async sendMessage(channelId, data) {
|
|
105
|
+
const nonce = data.nonce ?? (0, crypto_1.randomUUID)();
|
|
106
|
+
if (data.files?.length) {
|
|
107
|
+
// The multipart branch server-side does not parse embeds/components —
|
|
108
|
+
// fail loud instead of silently dropping them.
|
|
109
|
+
if (data.embeds?.length || data.components?.length) {
|
|
110
|
+
throw new RangeError('Attachments cannot be combined with embeds or components in one message (v1) — send them separately.');
|
|
111
|
+
}
|
|
112
|
+
if (data.files.length > 10) {
|
|
113
|
+
throw new RangeError(`At most 10 attachments per message (got ${data.files.length}).`);
|
|
114
|
+
}
|
|
115
|
+
const form = new FormData();
|
|
116
|
+
form.append('content', data.content ?? '');
|
|
117
|
+
form.append('nonce', nonce);
|
|
118
|
+
if (data.replyTo)
|
|
119
|
+
form.append('reply_to', data.replyTo);
|
|
120
|
+
const parts = await (0, attachments_1.resolveFiles)(data.files);
|
|
121
|
+
for (const { blob, name } of parts) {
|
|
122
|
+
form.append('files', blob, name);
|
|
123
|
+
}
|
|
124
|
+
// Per-file spoiler flags ride as a JSON boolean array aligned with the
|
|
125
|
+
// multipart file order; omitted entirely when nothing is a spoiler.
|
|
126
|
+
if (parts.some((p) => p.spoiler)) {
|
|
127
|
+
form.append('attachment_spoilers', JSON.stringify(parts.map((p) => p.spoiler)));
|
|
128
|
+
}
|
|
129
|
+
// Uploads get a longer budget than ordinary REST calls.
|
|
130
|
+
return this.request(`/messages/${channelId}`, { method: 'POST', body: form, timeoutMs: 60000 });
|
|
131
|
+
}
|
|
132
|
+
const body = {
|
|
133
|
+
content: data.content ?? '',
|
|
134
|
+
nonce,
|
|
135
|
+
};
|
|
136
|
+
if (data.embeds)
|
|
137
|
+
body.embeds = data.embeds;
|
|
138
|
+
if (data.components)
|
|
139
|
+
body.components = data.components;
|
|
140
|
+
if (data.replyTo)
|
|
141
|
+
body.reply_to = data.replyTo;
|
|
142
|
+
return this.request(`/messages/${channelId}`, { method: 'POST', body });
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Respond to an interaction. `type`: 'update' (terminal — edit the
|
|
146
|
+
* original message), 'reply' (terminal — post a reply; `data.ephemeral:
|
|
147
|
+
* true` for an only-you reply), 'defer' (extend the window; a terminal
|
|
148
|
+
* callback must follow), or 'modal' (open a modal — pending interactions
|
|
149
|
+
* only, once). Response: `{ success, message }` for update/reply (an
|
|
150
|
+
* ephemeral reply's `message` is a reduced, never-persisted object);
|
|
151
|
+
* defer/modal are 204s and resolve `null`.
|
|
152
|
+
*/
|
|
153
|
+
interactionCallback(interactionId, token, type, data) {
|
|
154
|
+
return this.request(`/interactions/${interactionId}/${encodeURIComponent(token)}/callback`, { method: 'POST', body: data === undefined ? { type } : { type, data } });
|
|
155
|
+
}
|
|
156
|
+
/** PUT /api/bots/commands — bulk overwrite the bot's slash commands (whole-or-400). Response: `{ success, commands }`. */
|
|
157
|
+
setCommands(commands) {
|
|
158
|
+
return this.request('/bots/commands', { method: 'PUT', body: { commands } });
|
|
159
|
+
}
|
|
160
|
+
/** GET /api/bots/commands — list the bot's registered slash commands. Response: `{ success, commands }`. */
|
|
161
|
+
fetchCommands() {
|
|
162
|
+
return this.request('/bots/commands');
|
|
163
|
+
}
|
|
164
|
+
/** PUT — grant a role to a member. Needs MANAGE_ROLES + role below the bot's highest. Response: `{ success, message: string }`. */
|
|
165
|
+
addRole(serverId, userId, roleId) {
|
|
166
|
+
return this.request(`/servers/${serverId}/members/${userId}/roles/${roleId}`, { method: 'PUT' });
|
|
167
|
+
}
|
|
168
|
+
/** DELETE — remove a role from a member. Response: `{ success, message: string }`. */
|
|
169
|
+
removeRole(serverId, userId, roleId) {
|
|
170
|
+
return this.request(`/servers/${serverId}/members/${userId}/roles/${roleId}`, { method: 'DELETE' });
|
|
171
|
+
}
|
|
172
|
+
/** Kick a member (needs KICK_MEMBERS + outrank the target). Response: `{ success, message: string }`. */
|
|
173
|
+
kickMember(serverId, userId, opts = {}) {
|
|
174
|
+
return this.request(`/servers/${serverId}/members/${userId}`, { method: 'DELETE', body: kickBanBody(opts) });
|
|
175
|
+
}
|
|
176
|
+
/** Ban a member (needs BAN_MEMBERS + outrank the target). Response: `{ success, message: string }`. */
|
|
177
|
+
banMember(serverId, userId, opts = {}) {
|
|
178
|
+
return this.request(`/servers/${serverId}/bans/${userId}`, { method: 'POST', body: kickBanBody(opts) ?? {} });
|
|
179
|
+
}
|
|
180
|
+
/** Lift a ban. Response: `{ success, message: string }`. */
|
|
181
|
+
unbanMember(serverId, userId) {
|
|
182
|
+
return this.request(`/servers/${serverId}/bans/${userId}`, { method: 'DELETE' });
|
|
183
|
+
}
|
|
184
|
+
/** Time out (temporarily mute) a member (needs TIMEOUT_MEMBERS). Provide `durationSeconds` or `until`. Response: `{ success, timeout_until }`. */
|
|
185
|
+
timeoutMember(serverId, userId, opts) {
|
|
186
|
+
const body = {};
|
|
187
|
+
if (opts.durationSeconds != null)
|
|
188
|
+
body.duration_seconds = opts.durationSeconds;
|
|
189
|
+
if (opts.until != null)
|
|
190
|
+
body.timeout_until = opts.until.toISOString();
|
|
191
|
+
if (opts.reason != null)
|
|
192
|
+
body.reason = opts.reason;
|
|
193
|
+
return this.request(`/servers/${serverId}/members/${userId}/timeout`, { method: 'POST', body });
|
|
194
|
+
}
|
|
195
|
+
/** Remove an active timeout. Response: `{ success }`. */
|
|
196
|
+
removeTimeout(serverId, userId) {
|
|
197
|
+
return this.request(`/servers/${serverId}/members/${userId}/timeout`, { method: 'DELETE' });
|
|
198
|
+
}
|
|
199
|
+
/** Delete a message (needs MANAGE_MESSAGES, or the bot's own message). Response: `{ success, message: string }`. */
|
|
200
|
+
deleteMessage(messageId) {
|
|
201
|
+
return this.request(`/messages/${messageId}`, { method: 'DELETE' });
|
|
202
|
+
}
|
|
203
|
+
/** Add a reaction (the emoji is URL-path encoded). Response: `{ success, reactions, version }`. */
|
|
204
|
+
react(messageId, emoji) {
|
|
205
|
+
return this.request(`/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`, { method: 'PUT' });
|
|
206
|
+
}
|
|
207
|
+
/** Remove the bot's own reaction. Response: `{ success, reactions, version }`. */
|
|
208
|
+
removeReaction(messageId, emoji) {
|
|
209
|
+
return this.request(`/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`, { method: 'DELETE' });
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create a poll in a channel (posts a poll message). Needs send
|
|
213
|
+
* permission there; poll creation is rate-limited (~10/min).
|
|
214
|
+
* Response: `{ success, message }` (201) — `message.poll` carries the
|
|
215
|
+
* poll state (question, options with counts, deadline, version).
|
|
216
|
+
*/
|
|
217
|
+
createPoll(channelId, opts) {
|
|
218
|
+
// Mirror the server's validation but fail loud and local.
|
|
219
|
+
const question = String(opts.question ?? '').trim();
|
|
220
|
+
if (!question)
|
|
221
|
+
throw new RangeError('Poll question is required.');
|
|
222
|
+
if (question.length > 500)
|
|
223
|
+
throw new RangeError(`Poll question exceeds 500 characters (got ${question.length}).`);
|
|
224
|
+
const options = (opts.options ?? []).map((o) => String(o).trim());
|
|
225
|
+
if (options.length < 2 || options.length > 6)
|
|
226
|
+
throw new RangeError(`Polls need 2–6 options (got ${options.length}).`);
|
|
227
|
+
if (options.some((o) => !o))
|
|
228
|
+
throw new RangeError('Poll options must be non-empty.');
|
|
229
|
+
if (options.some((o) => o.length > 200))
|
|
230
|
+
throw new RangeError('Poll options are limited to 200 characters each.');
|
|
231
|
+
if (new Set(options.map((o) => o.toLowerCase())).size !== options.length)
|
|
232
|
+
throw new RangeError('Poll options must be unique.');
|
|
233
|
+
const deadline = new Date(opts.deadline);
|
|
234
|
+
if (isNaN(deadline.getTime()))
|
|
235
|
+
throw new RangeError('Poll deadline is not a valid date.');
|
|
236
|
+
if (deadline.getTime() <= Date.now())
|
|
237
|
+
throw new RangeError('Poll deadline must be in the future.');
|
|
238
|
+
if (deadline.getTime() - Date.now() > 7 * 24 * 60 * 60 * 1000)
|
|
239
|
+
throw new RangeError('Poll deadline must be within 7 days.');
|
|
240
|
+
return this.request('/polls', {
|
|
241
|
+
method: 'POST',
|
|
242
|
+
body: {
|
|
243
|
+
channelId,
|
|
244
|
+
question,
|
|
245
|
+
options,
|
|
246
|
+
deadline: deadline.toISOString(),
|
|
247
|
+
nonce: opts.nonce ?? (0, crypto_1.randomUUID)(),
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/** Close a poll early. Creator or `MANAGE_MESSAGES`. Response: `{ success, version }` — deliberately lean, no poll/message included. */
|
|
252
|
+
closePoll(pollId) {
|
|
253
|
+
return this.request(`/polls/${pollId}/close`, { method: 'POST' });
|
|
254
|
+
}
|
|
255
|
+
/** Create a webhook on a text/announcement channel (needs MANAGE_WEBHOOKS). Response: `{ success, webhook }` (201) — `webhook.token` is returned here and on the list routes. */
|
|
256
|
+
createWebhook(channelId, opts = {}) {
|
|
257
|
+
return this.request('/webhooks', {
|
|
258
|
+
method: 'POST',
|
|
259
|
+
body: { channelId, name: opts.name, avatar_url: opts.avatarUrl },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/** List a channel's webhooks. Response: `{ success, webhooks }`. */
|
|
263
|
+
fetchChannelWebhooks(channelId) {
|
|
264
|
+
return this.request(`/webhooks/channel/${channelId}`);
|
|
265
|
+
}
|
|
266
|
+
/** List a server's webhooks. Response: `{ success, webhooks }`. */
|
|
267
|
+
fetchServerWebhooks(serverId) {
|
|
268
|
+
return this.request(`/webhooks/server/${serverId}`);
|
|
269
|
+
}
|
|
270
|
+
/** Rename a webhook / change its avatar (`avatarUrl: null` clears it). Response: `{ success, webhook }`. */
|
|
271
|
+
editWebhook(webhookId, opts) {
|
|
272
|
+
const body = {};
|
|
273
|
+
if (opts.name !== undefined)
|
|
274
|
+
body.name = opts.name;
|
|
275
|
+
if (opts.avatarUrl !== undefined)
|
|
276
|
+
body.avatar_url = opts.avatarUrl;
|
|
277
|
+
return this.request(`/webhooks/${webhookId}`, { method: 'PATCH', body });
|
|
278
|
+
}
|
|
279
|
+
/** Delete a webhook. Response: 204 — resolves `null`. */
|
|
280
|
+
deleteWebhook(webhookId) {
|
|
281
|
+
return this.request(`/webhooks/${webhookId}`, { method: 'DELETE' });
|
|
282
|
+
}
|
|
283
|
+
/** Rotate a webhook's token (the old one stops working). Response: `{ success, token }`. */
|
|
284
|
+
rotateWebhookToken(webhookId) {
|
|
285
|
+
return this.request(`/webhooks/${webhookId}/token`, { method: 'POST' });
|
|
286
|
+
}
|
|
287
|
+
/** Set the bot's presence status. Response: `{ success, status }` — the effective status. */
|
|
288
|
+
setStatus(status) {
|
|
289
|
+
return this.request('/users/me/status', { method: 'PATCH', body: { status } });
|
|
290
|
+
}
|
|
291
|
+
/** Set the bot's rich-presence activity. Response: `{ success, activities }`. */
|
|
292
|
+
setActivity(activity) {
|
|
293
|
+
return this.request('/users/me/activities', { method: 'PUT', body: activity });
|
|
294
|
+
}
|
|
295
|
+
/** Clear the bot's rich-presence activity. Response: `{ success, activities }` (now empty). */
|
|
296
|
+
clearActivity() {
|
|
297
|
+
return this.request('/users/me/activities', { method: 'DELETE' });
|
|
298
|
+
}
|
|
299
|
+
/** Edit a message's content and/or components. Own messages only (403 otherwise). Response: `{ success, message }`. */
|
|
300
|
+
editMessage(messageId, contentOrOpts) {
|
|
301
|
+
const opts = typeof contentOrOpts === 'string' ? { content: contentOrOpts } : contentOrOpts;
|
|
302
|
+
const body = {};
|
|
303
|
+
if (opts.content !== undefined)
|
|
304
|
+
body.content = opts.content;
|
|
305
|
+
if (opts.components !== undefined)
|
|
306
|
+
body.components = opts.components;
|
|
307
|
+
return this.request(`/messages/${messageId}`, { method: 'PATCH', body });
|
|
308
|
+
}
|
|
309
|
+
/** Pin a message. Response: `{ success, message: string }`. */
|
|
310
|
+
pinMessage(messageId) {
|
|
311
|
+
return this.request(`/messages/${messageId}/pin`, { method: 'POST' });
|
|
312
|
+
}
|
|
313
|
+
/** Unpin a message. Response: `{ success, message: string }`. */
|
|
314
|
+
unpinMessage(messageId) {
|
|
315
|
+
return this.request(`/messages/${messageId}/pin`, { method: 'DELETE' });
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Fetch a channel's message history. Response: `{ success, messages,
|
|
319
|
+
* hasMore }` — plus `hasMoreBefore`/`hasMoreAfter` when `around` is used.
|
|
320
|
+
*/
|
|
321
|
+
fetchMessages(channelId, opts = {}) {
|
|
322
|
+
const qs = this.buildQuery({
|
|
323
|
+
limit: opts.limit,
|
|
324
|
+
before: opts.before,
|
|
325
|
+
before_id: opts.beforeId,
|
|
326
|
+
after: opts.after,
|
|
327
|
+
after_id: opts.afterId,
|
|
328
|
+
around: opts.around,
|
|
329
|
+
});
|
|
330
|
+
return this.request(`/messages/${channelId}${qs}`);
|
|
331
|
+
}
|
|
332
|
+
/** List a server's members. Response: `{ success, members }`. */
|
|
333
|
+
fetchMembers(serverId, opts = {}) {
|
|
334
|
+
const qs = this.buildQuery({ limit: opts.limit, offset: opts.offset });
|
|
335
|
+
return this.request(`/servers/${serverId}/members${qs}`);
|
|
336
|
+
}
|
|
337
|
+
/** List a server's roles. Response: `{ success, roles, PERMISSIONS }` — `PERMISSIONS` maps permission names to bits. */
|
|
338
|
+
fetchRoles(serverId) {
|
|
339
|
+
return this.request(`/servers/${serverId}/roles`);
|
|
340
|
+
}
|
|
341
|
+
/** Set (or clear, with `null`) a member's nickname. Others need MANAGE_NICKNAMES. Response: `{ success, nickname }`. */
|
|
342
|
+
setNickname(serverId, userId, nickname) {
|
|
343
|
+
return this.request(`/servers/${serverId}/nicknames/${userId}`, { method: 'PUT', body: { nickname } });
|
|
344
|
+
}
|
|
345
|
+
toError(status, body, path, retryAfter) {
|
|
346
|
+
const msg = `${path} -> ${status} ${JSON.stringify(body)}`;
|
|
347
|
+
if (status === 401)
|
|
348
|
+
return new errors_1.PrivageAuthError(msg);
|
|
349
|
+
if (status === 403)
|
|
350
|
+
return new errors_1.PrivagePermissionError(msg);
|
|
351
|
+
if (status === 429)
|
|
352
|
+
return new errors_1.PrivageRateLimitError(msg, (Number(retryAfter) || 0) * 1000);
|
|
353
|
+
return new errors_1.PrivageHTTPError(msg, status, body);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
exports.Rest = Rest;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Client } from '../Client';
|
|
2
|
+
import type { SendMessageOptions } from '../rest';
|
|
3
|
+
type Sendable = string | Omit<SendMessageOptions, 'replyTo'>;
|
|
4
|
+
/**
|
|
5
|
+
* A channel the bot can see. `name`/`type`/`categoryId` come from the SDK's
|
|
6
|
+
* name cache — hydrated on connect, kept fresh via `channel-created`/
|
|
7
|
+
* `channel-updated`/`channel-deleted` events (`Intents.Servers`), and
|
|
8
|
+
* lazily filled on cache miss (see `Client#ensureChannel`). A channel handed
|
|
9
|
+
* out before it's resolved has `name: null`; it still works for `send`.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Channel {
|
|
12
|
+
private readonly client;
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly name: string | null;
|
|
15
|
+
readonly type: string | null;
|
|
16
|
+
readonly serverId: string | null;
|
|
17
|
+
readonly categoryId: string | null;
|
|
18
|
+
/** The raw wire/REST payload, for anything the typed surface doesn't expose yet. */
|
|
19
|
+
readonly raw: any;
|
|
20
|
+
constructor(client: Client, data: any);
|
|
21
|
+
/** Send a message to this channel. */
|
|
22
|
+
send(content: Sendable): Promise<any>;
|
|
23
|
+
}
|
|
24
|
+
/** @deprecated Alias of `Channel`, kept for backwards compatibility. */
|
|
25
|
+
export { Channel as TextChannel };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TextChannel = exports.Channel = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A channel the bot can see. `name`/`type`/`categoryId` come from the SDK's
|
|
6
|
+
* name cache — hydrated on connect, kept fresh via `channel-created`/
|
|
7
|
+
* `channel-updated`/`channel-deleted` events (`Intents.Servers`), and
|
|
8
|
+
* lazily filled on cache miss (see `Client#ensureChannel`). A channel handed
|
|
9
|
+
* out before it's resolved has `name: null`; it still works for `send`.
|
|
10
|
+
*/
|
|
11
|
+
class Channel {
|
|
12
|
+
constructor(client, data) {
|
|
13
|
+
this.client = client;
|
|
14
|
+
this.raw = data;
|
|
15
|
+
this.id = String(data.id ?? data._id ?? '');
|
|
16
|
+
this.name = data.name ?? null;
|
|
17
|
+
this.type = data.type ?? null;
|
|
18
|
+
this.serverId =
|
|
19
|
+
data.server_id != null ? String(data.server_id) : data.serverId != null ? String(data.serverId) : null;
|
|
20
|
+
this.categoryId =
|
|
21
|
+
data.category_id != null
|
|
22
|
+
? String(data.category_id)
|
|
23
|
+
: data.categoryId != null
|
|
24
|
+
? String(data.categoryId)
|
|
25
|
+
: null;
|
|
26
|
+
}
|
|
27
|
+
/** Send a message to this channel. */
|
|
28
|
+
send(content) {
|
|
29
|
+
const data = typeof content === 'string' ? { content } : content;
|
|
30
|
+
return this.client.rest.sendMessage(this.id, data);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.Channel = Channel;
|
|
34
|
+
exports.TextChannel = Channel;
|