district.js 0.3.0 → 0.6.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 +69 -0
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/components.d.ts +95 -0
- package/dist/components.js +73 -0
- package/dist/embed.d.ts +55 -0
- package/dist/embed.js +37 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/structures.d.ts +43 -8
- package/dist/structures.js +49 -10
- package/dist/types.d.ts +10 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,6 +69,75 @@ import { Formatters } from "district.js";
|
|
|
69
69
|
channel.send(Formatters.bold("Heads up!") + " " + Formatters.code("v0.2.0"));
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## Embeds
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import { EmbedBuilder } from "district.js";
|
|
76
|
+
|
|
77
|
+
const embed = new EmbedBuilder()
|
|
78
|
+
.setTitle("Deploy complete")
|
|
79
|
+
.setUrl("https://example.com/builds/128")
|
|
80
|
+
.setColor("#c6a86b")
|
|
81
|
+
.setDescription("Build **#128** shipped to production.")
|
|
82
|
+
.addFields(
|
|
83
|
+
{ name: "Env", value: "prod", inline: true },
|
|
84
|
+
{ name: "By", value: "ci-bot", inline: true },
|
|
85
|
+
)
|
|
86
|
+
.setFooter({ text: "district.js" })
|
|
87
|
+
.setTimestamp();
|
|
88
|
+
|
|
89
|
+
await channel.send({ content: "New release 🎉", embeds: [embed] });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The District app renders it as an embed card (accent bar, title, fields, image).
|
|
93
|
+
|
|
94
|
+
## Buttons
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import { ActionRowBuilder, ButtonBuilder } from "district.js";
|
|
98
|
+
|
|
99
|
+
const row = new ActionRowBuilder().addComponents(
|
|
100
|
+
new ButtonBuilder().setLabel("Approve").setStyle("success").setCustomId("approve"),
|
|
101
|
+
new ButtonBuilder().setLabel("Deny").setStyle("danger").setCustomId("deny"),
|
|
102
|
+
new ButtonBuilder().setLabel("Docs").setUrl("https://usedistrict.org/developers/docs"),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await channel.send({ content: "Review this?", components: [row] });
|
|
106
|
+
|
|
107
|
+
// A click delivers an interactionCreate (needs an Event Subscription endpoint)
|
|
108
|
+
client.on("interactionCreate", (i) => {
|
|
109
|
+
if (i.isButton() && i.customId === "approve") i.reply("Approved ✅");
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Select menus & modals
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
import { ActionRowBuilder, StringSelectMenuBuilder, ButtonBuilder, ModalBuilder, TextInputBuilder } from "district.js";
|
|
117
|
+
|
|
118
|
+
// Dropdown
|
|
119
|
+
const menu = new ActionRowBuilder().addComponents(
|
|
120
|
+
new StringSelectMenuBuilder().setCustomId("role").setPlaceholder("Pick a role")
|
|
121
|
+
.addOptions({ label: "Dev", value: "dev" }, { label: "Design", value: "design" }),
|
|
122
|
+
);
|
|
123
|
+
await channel.send({ content: "Choose:", components: [menu] });
|
|
124
|
+
|
|
125
|
+
// Modal (opens instantly on click; only the submit is delivered to the bot)
|
|
126
|
+
const modal = new ModalBuilder().setCustomId("feedback").setTitle("Feedback")
|
|
127
|
+
.addComponents(new TextInputBuilder().setCustomId("msg").setLabel("Your message").setStyle("paragraph"));
|
|
128
|
+
const row = new ActionRowBuilder().addComponents(
|
|
129
|
+
new ButtonBuilder().setLabel("Give feedback").setStyle("primary").setModal(modal),
|
|
130
|
+
);
|
|
131
|
+
await channel.send({ components: [row] });
|
|
132
|
+
|
|
133
|
+
client.on("interactionCreate", (i) => {
|
|
134
|
+
if (i.isSelectMenu() && i.customId === "role") i.reply(`You picked ${i.values[0]}`);
|
|
135
|
+
if (i.isModalSubmit() && i.customId === "feedback") i.reply(`Thanks: ${i.field("msg")}`);
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> Because District delivers interactions asynchronously, modals are attached to a button and shown **instantly, client-side**; only the submitted values are sent back to your bot.
|
|
140
|
+
|
|
72
141
|
## Slash commands
|
|
73
142
|
|
|
74
143
|
Register the command in the portal (name + handler URL), then:
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import http from "node:http";
|
|
3
3
|
import { REST } from "./rest.js";
|
|
4
|
-
import { ClientUser, Message, TextChannel, Interaction, User, Space, type ClientContext } from "./structures.js";
|
|
4
|
+
import { ClientUser, Message, TextChannel, Interaction, User, Space, type ClientContext, type MessageContent } from "./structures.js";
|
|
5
5
|
import { type EventHeaders } from "./webhook.js";
|
|
6
6
|
import type { ClientOptions, ReactionEvent, DeletedMessage } from "./types.js";
|
|
7
7
|
/** The events a Client emits, and their argument tuples. */
|
|
@@ -30,8 +30,8 @@ export declare class Client extends EventEmitter implements ClientContext {
|
|
|
30
30
|
readonly channels: {
|
|
31
31
|
/** Fetch a channel's info (kind, name, space). */
|
|
32
32
|
fetch: (id: string) => Promise<TextChannel>;
|
|
33
|
-
/** Send a message to a channel. */
|
|
34
|
-
send: (channelId: string, content:
|
|
33
|
+
/** Send a message to a channel (text or { content, embeds }). */
|
|
34
|
+
send: (channelId: string, content: MessageContent) => Promise<Message>;
|
|
35
35
|
};
|
|
36
36
|
/** Fetch users by id. */
|
|
37
37
|
readonly users: {
|
package/dist/client.js
CHANGED
|
@@ -38,7 +38,7 @@ export class Client extends EventEmitter {
|
|
|
38
38
|
channels = {
|
|
39
39
|
/** Fetch a channel's info (kind, name, space). */
|
|
40
40
|
fetch: (id) => new TextChannel(this, id).fetch(),
|
|
41
|
-
/** Send a message to a channel. */
|
|
41
|
+
/** Send a message to a channel (text or { content, embeds }). */
|
|
42
42
|
send: (channelId, content) => new TextChannel(this, channelId).send(content),
|
|
43
43
|
};
|
|
44
44
|
/** Fetch users by id. */
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export type ButtonStyle = "primary" | "secondary" | "success" | "danger" | "link";
|
|
2
|
+
export interface TextInputData {
|
|
3
|
+
custom_id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
style: "short" | "paragraph";
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
max_length?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ModalData {
|
|
11
|
+
custom_id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
fields: TextInputData[];
|
|
14
|
+
}
|
|
15
|
+
export interface ButtonData {
|
|
16
|
+
type: "button";
|
|
17
|
+
style: ButtonStyle;
|
|
18
|
+
label: string;
|
|
19
|
+
custom_id?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
emoji?: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** A modal shown (instantly, client-side) when the button is clicked. */
|
|
24
|
+
modal?: ModalData;
|
|
25
|
+
}
|
|
26
|
+
export interface SelectOption {
|
|
27
|
+
label: string;
|
|
28
|
+
value: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface SelectData {
|
|
32
|
+
type: "select";
|
|
33
|
+
custom_id: string;
|
|
34
|
+
options: SelectOption[];
|
|
35
|
+
placeholder?: string;
|
|
36
|
+
min_values?: number;
|
|
37
|
+
max_values?: number;
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export type ComponentData = ButtonData | SelectData;
|
|
41
|
+
export interface ActionRowData {
|
|
42
|
+
type: "action_row";
|
|
43
|
+
components: ComponentData[];
|
|
44
|
+
}
|
|
45
|
+
export declare class ButtonBuilder {
|
|
46
|
+
data: Partial<ButtonData>;
|
|
47
|
+
setLabel(label: string): this;
|
|
48
|
+
setStyle(style: ButtonStyle): this;
|
|
49
|
+
/** A custom id you'll match in the interactionCreate handler (non-link buttons). */
|
|
50
|
+
setCustomId(id: string): this;
|
|
51
|
+
/** A link button — opens the URL, fires no interaction. */
|
|
52
|
+
setUrl(url: string): this;
|
|
53
|
+
setEmoji(emoji: string): this;
|
|
54
|
+
setDisabled(disabled?: boolean): this;
|
|
55
|
+
/** Attach a modal — clicking the button opens it instantly; submit fires a
|
|
56
|
+
* modal_submit interaction with the field values. */
|
|
57
|
+
setModal(modal: ModalBuilder | ModalData): this;
|
|
58
|
+
toJSON(): ButtonData;
|
|
59
|
+
}
|
|
60
|
+
export declare class StringSelectMenuBuilder {
|
|
61
|
+
data: SelectData;
|
|
62
|
+
setCustomId(id: string): this;
|
|
63
|
+
setPlaceholder(text: string): this;
|
|
64
|
+
setMinValues(n: number): this;
|
|
65
|
+
setMaxValues(n: number): this;
|
|
66
|
+
setDisabled(disabled?: boolean): this;
|
|
67
|
+
addOptions(...options: SelectOption[]): this;
|
|
68
|
+
toJSON(): SelectData;
|
|
69
|
+
}
|
|
70
|
+
export declare class TextInputBuilder {
|
|
71
|
+
data: Partial<TextInputData>;
|
|
72
|
+
setCustomId(id: string): this;
|
|
73
|
+
setLabel(label: string): this;
|
|
74
|
+
setStyle(style: "short" | "paragraph"): this;
|
|
75
|
+
setPlaceholder(text: string): this;
|
|
76
|
+
setRequired(required?: boolean): this;
|
|
77
|
+
setMaxLength(n: number): this;
|
|
78
|
+
toJSON(): TextInputData;
|
|
79
|
+
}
|
|
80
|
+
export declare class ModalBuilder {
|
|
81
|
+
data: ModalData;
|
|
82
|
+
setCustomId(id: string): this;
|
|
83
|
+
setTitle(title: string): this;
|
|
84
|
+
/** Add text inputs to the modal (up to 5). */
|
|
85
|
+
addComponents(...inputs: (TextInputBuilder | TextInputData)[]): this;
|
|
86
|
+
toJSON(): ModalData;
|
|
87
|
+
}
|
|
88
|
+
export declare class ActionRowBuilder {
|
|
89
|
+
data: ActionRowData;
|
|
90
|
+
/** Add up to 5 buttons, or a single select menu. */
|
|
91
|
+
addComponents(...components: (ButtonBuilder | StringSelectMenuBuilder | ComponentData)[]): this;
|
|
92
|
+
toJSON(): ActionRowData;
|
|
93
|
+
}
|
|
94
|
+
export type ComponentResolvable = ActionRowBuilder | ActionRowData;
|
|
95
|
+
export declare function resolveComponent(c: ComponentResolvable): ActionRowData;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Interactive message components — action rows of buttons. Built objects go in
|
|
2
|
+
// a message's payload.components; the District app renders them, and a click on
|
|
3
|
+
// a non-link button fires an `interactionCreate` (type "component") back to the
|
|
4
|
+
// bot with the button's customId.
|
|
5
|
+
export class ButtonBuilder {
|
|
6
|
+
data = { type: "button", style: "secondary" };
|
|
7
|
+
setLabel(label) { this.data.label = label; return this; }
|
|
8
|
+
setStyle(style) { this.data.style = style; return this; }
|
|
9
|
+
/** A custom id you'll match in the interactionCreate handler (non-link buttons). */
|
|
10
|
+
setCustomId(id) { this.data.custom_id = id; return this; }
|
|
11
|
+
/** A link button — opens the URL, fires no interaction. */
|
|
12
|
+
setUrl(url) { this.data.url = url; this.data.style = "link"; return this; }
|
|
13
|
+
setEmoji(emoji) { this.data.emoji = emoji; return this; }
|
|
14
|
+
setDisabled(disabled = true) { this.data.disabled = disabled; return this; }
|
|
15
|
+
/** Attach a modal — clicking the button opens it instantly; submit fires a
|
|
16
|
+
* modal_submit interaction with the field values. */
|
|
17
|
+
setModal(modal) {
|
|
18
|
+
this.data.modal = modal instanceof ModalBuilder ? modal.toJSON() : modal;
|
|
19
|
+
if (!this.data.custom_id)
|
|
20
|
+
this.data.custom_id = this.data.modal.custom_id;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
toJSON() { return this.data; }
|
|
24
|
+
}
|
|
25
|
+
export class StringSelectMenuBuilder {
|
|
26
|
+
data = { type: "select", custom_id: "", options: [] };
|
|
27
|
+
setCustomId(id) { this.data.custom_id = id; return this; }
|
|
28
|
+
setPlaceholder(text) { this.data.placeholder = text; return this; }
|
|
29
|
+
setMinValues(n) { this.data.min_values = n; return this; }
|
|
30
|
+
setMaxValues(n) { this.data.max_values = n; return this; }
|
|
31
|
+
setDisabled(disabled = true) { this.data.disabled = disabled; return this; }
|
|
32
|
+
addOptions(...options) {
|
|
33
|
+
this.data.options.push(...options);
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
toJSON() { return this.data; }
|
|
37
|
+
}
|
|
38
|
+
export class TextInputBuilder {
|
|
39
|
+
data = { style: "short", required: true };
|
|
40
|
+
setCustomId(id) { this.data.custom_id = id; return this; }
|
|
41
|
+
setLabel(label) { this.data.label = label; return this; }
|
|
42
|
+
setStyle(style) { this.data.style = style; return this; }
|
|
43
|
+
setPlaceholder(text) { this.data.placeholder = text; return this; }
|
|
44
|
+
setRequired(required = true) { this.data.required = required; return this; }
|
|
45
|
+
setMaxLength(n) { this.data.max_length = n; return this; }
|
|
46
|
+
toJSON() { return this.data; }
|
|
47
|
+
}
|
|
48
|
+
export class ModalBuilder {
|
|
49
|
+
data = { custom_id: "", title: "", fields: [] };
|
|
50
|
+
setCustomId(id) { this.data.custom_id = id; return this; }
|
|
51
|
+
setTitle(title) { this.data.title = title; return this; }
|
|
52
|
+
/** Add text inputs to the modal (up to 5). */
|
|
53
|
+
addComponents(...inputs) {
|
|
54
|
+
for (const i of inputs)
|
|
55
|
+
this.data.fields.push(i instanceof TextInputBuilder ? i.toJSON() : i);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
toJSON() { return this.data; }
|
|
59
|
+
}
|
|
60
|
+
export class ActionRowBuilder {
|
|
61
|
+
data = { type: "action_row", components: [] };
|
|
62
|
+
/** Add up to 5 buttons, or a single select menu. */
|
|
63
|
+
addComponents(...components) {
|
|
64
|
+
for (const c of components) {
|
|
65
|
+
this.data.components.push(c instanceof ButtonBuilder || c instanceof StringSelectMenuBuilder ? c.toJSON() : c);
|
|
66
|
+
}
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
toJSON() { return this.data; }
|
|
70
|
+
}
|
|
71
|
+
export function resolveComponent(c) {
|
|
72
|
+
return c instanceof ActionRowBuilder ? c.toJSON() : c;
|
|
73
|
+
}
|
package/dist/embed.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface EmbedField {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string;
|
|
4
|
+
inline?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface EmbedData {
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
color?: string;
|
|
11
|
+
author?: {
|
|
12
|
+
name: string;
|
|
13
|
+
iconUrl?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
};
|
|
16
|
+
fields?: EmbedField[];
|
|
17
|
+
image?: {
|
|
18
|
+
url: string;
|
|
19
|
+
};
|
|
20
|
+
thumbnail?: {
|
|
21
|
+
url: string;
|
|
22
|
+
};
|
|
23
|
+
footer?: {
|
|
24
|
+
text: string;
|
|
25
|
+
iconUrl?: string;
|
|
26
|
+
};
|
|
27
|
+
timestamp?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class EmbedBuilder {
|
|
30
|
+
data: EmbedData;
|
|
31
|
+
constructor(data?: EmbedData);
|
|
32
|
+
setTitle(title: string): this;
|
|
33
|
+
setDescription(description: string): this;
|
|
34
|
+
setUrl(url: string): this;
|
|
35
|
+
setColor(color: string | number): this;
|
|
36
|
+
setAuthor(author: {
|
|
37
|
+
name: string;
|
|
38
|
+
iconUrl?: string;
|
|
39
|
+
url?: string;
|
|
40
|
+
}): this;
|
|
41
|
+
setImage(url: string): this;
|
|
42
|
+
setThumbnail(url: string): this;
|
|
43
|
+
setFooter(footer: {
|
|
44
|
+
text: string;
|
|
45
|
+
iconUrl?: string;
|
|
46
|
+
}): this;
|
|
47
|
+
setTimestamp(ts?: Date | string | number): this;
|
|
48
|
+
/** Add one or more fields. */
|
|
49
|
+
addFields(...fields: EmbedField[]): this;
|
|
50
|
+
/** The plain object to send (also used implicitly by JSON.stringify). */
|
|
51
|
+
toJSON(): EmbedData;
|
|
52
|
+
}
|
|
53
|
+
/** Accept either a builder or a plain embed object wherever embeds are taken. */
|
|
54
|
+
export type EmbedResolvable = EmbedBuilder | EmbedData;
|
|
55
|
+
export declare function resolveEmbed(e: EmbedResolvable): EmbedData;
|
package/dist/embed.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// A rich embed builder (discord.js-style). The built object is sent in a
|
|
2
|
+
// message's payload.embeds and rendered by the District app as an embed card.
|
|
3
|
+
function toHex(color) {
|
|
4
|
+
if (typeof color === "number")
|
|
5
|
+
return "#" + color.toString(16).padStart(6, "0");
|
|
6
|
+
return color.startsWith("#") ? color : "#" + color;
|
|
7
|
+
}
|
|
8
|
+
export class EmbedBuilder {
|
|
9
|
+
data;
|
|
10
|
+
constructor(data = {}) {
|
|
11
|
+
this.data = { ...data };
|
|
12
|
+
}
|
|
13
|
+
setTitle(title) { this.data.title = title; return this; }
|
|
14
|
+
setDescription(description) { this.data.description = description; return this; }
|
|
15
|
+
setUrl(url) { this.data.url = url; return this; }
|
|
16
|
+
setColor(color) { this.data.color = toHex(color); return this; }
|
|
17
|
+
setAuthor(author) { this.data.author = author; return this; }
|
|
18
|
+
setImage(url) { this.data.image = { url }; return this; }
|
|
19
|
+
setThumbnail(url) { this.data.thumbnail = { url }; return this; }
|
|
20
|
+
setFooter(footer) { this.data.footer = footer; return this; }
|
|
21
|
+
setTimestamp(ts = new Date()) {
|
|
22
|
+
this.data.timestamp = ts instanceof Date ? ts.toISOString() : new Date(ts).toISOString();
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
/** Add one or more fields. */
|
|
26
|
+
addFields(...fields) {
|
|
27
|
+
this.data.fields = [...(this.data.fields ?? []), ...fields];
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/** The plain object to send (also used implicitly by JSON.stringify). */
|
|
31
|
+
toJSON() {
|
|
32
|
+
return this.data;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function resolveEmbed(e) {
|
|
36
|
+
return e instanceof EmbedBuilder ? e.toJSON() : e;
|
|
37
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { Client, type ClientEvents } from "./client.js";
|
|
2
2
|
export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
|
|
3
|
-
export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, type ClientContext, } from "./structures.js";
|
|
3
|
+
export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, type ClientContext, type MessageContent, } from "./structures.js";
|
|
4
|
+
export { EmbedBuilder, resolveEmbed, type EmbedData, type EmbedField, type EmbedResolvable } from "./embed.js";
|
|
5
|
+
export { ButtonBuilder, ActionRowBuilder, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, resolveComponent, type ButtonStyle, type ButtonData, type SelectData, type SelectOption, type ComponentData, type ModalData, type TextInputData, type ActionRowData, type ComponentResolvable, } from "./components.js";
|
|
4
6
|
export { Collection } from "./collection.js";
|
|
5
7
|
export { PermissionsBitField, type PermissionResolvable, type PermissionFlagName } from "./permissions.js";
|
|
6
8
|
export { Formatters } from "./formatters.js";
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
export { Client } from "./client.js";
|
|
8
8
|
export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
|
|
9
9
|
export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, } from "./structures.js";
|
|
10
|
+
export { EmbedBuilder, resolveEmbed } from "./embed.js";
|
|
11
|
+
export { ButtonBuilder, ActionRowBuilder, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, resolveComponent, } from "./components.js";
|
|
10
12
|
export { Collection } from "./collection.js";
|
|
11
13
|
export { PermissionsBitField } from "./permissions.js";
|
|
12
14
|
export { Formatters } from "./formatters.js";
|
package/dist/structures.d.ts
CHANGED
|
@@ -2,6 +2,14 @@ import type { REST } from "./rest.js";
|
|
|
2
2
|
import type { RawMessage, RawSelfUser, RawUser, RawSpace, RawChannel, RawRole, RawMember, RawAttachment, RawBan, RawInvite, RawWebhook } from "./types.js";
|
|
3
3
|
import { Collection } from "./collection.js";
|
|
4
4
|
import { PermissionsBitField } from "./permissions.js";
|
|
5
|
+
import { type EmbedResolvable } from "./embed.js";
|
|
6
|
+
import { type ComponentResolvable } from "./components.js";
|
|
7
|
+
/** What you can pass to send/reply: a string, or content + embeds + components. */
|
|
8
|
+
export type MessageContent = string | {
|
|
9
|
+
content?: string;
|
|
10
|
+
embeds?: EmbedResolvable[];
|
|
11
|
+
components?: ComponentResolvable[];
|
|
12
|
+
};
|
|
5
13
|
/** Minimal client surface the structures need (avoids a circular import). */
|
|
6
14
|
export interface ClientContext {
|
|
7
15
|
rest: REST;
|
|
@@ -30,8 +38,8 @@ export declare class Message {
|
|
|
30
38
|
constructor(client: ClientContext, raw: RawMessage);
|
|
31
39
|
/** True if this message was sent by the current bot. */
|
|
32
40
|
get authoredByMe(): boolean;
|
|
33
|
-
/** Reply to this message (threads it via replyTo). */
|
|
34
|
-
reply(content:
|
|
41
|
+
/** Reply to this message (threads it via replyTo). Accepts text or { content, embeds }. */
|
|
42
|
+
reply(content: MessageContent): Promise<Message>;
|
|
35
43
|
/** Edit this message (only the bot's own messages). */
|
|
36
44
|
edit(content: string): Promise<this>;
|
|
37
45
|
/** Delete this message. */
|
|
@@ -61,8 +69,8 @@ export declare class TextChannel {
|
|
|
61
69
|
fetch(): Promise<this>;
|
|
62
70
|
/** @internal populate info fields from a raw channel row. */
|
|
63
71
|
applyInfo(raw: RawChannel): this;
|
|
64
|
-
/** Send a message to this channel. */
|
|
65
|
-
send(content:
|
|
72
|
+
/** Send a message to this channel. Accepts text or { content, embeds }. */
|
|
73
|
+
send(content: MessageContent): Promise<Message>;
|
|
66
74
|
/** Fetch recent messages (oldest → newest), up to 100. */
|
|
67
75
|
fetchMessages(limit?: number): Promise<Collection<string, Message>>;
|
|
68
76
|
/** The channel's webhooks. */
|
|
@@ -197,20 +205,47 @@ export declare class Role {
|
|
|
197
205
|
}
|
|
198
206
|
export declare class Interaction {
|
|
199
207
|
private client;
|
|
208
|
+
/** "command" (slash), "component" (button/select), or "modal_submit". */
|
|
209
|
+
type: "command" | "component" | "modal_submit";
|
|
200
210
|
commandName: string;
|
|
201
211
|
commandId: string;
|
|
212
|
+
/** The component/modal customId that triggered this. */
|
|
213
|
+
customId: string;
|
|
214
|
+
/** The message the component is on. */
|
|
215
|
+
messageId: string | null;
|
|
216
|
+
/** Selected values (select-menu interactions). */
|
|
217
|
+
values: string[];
|
|
218
|
+
/** Submitted field values keyed by input customId (modal submits). */
|
|
219
|
+
fields: Record<string, string>;
|
|
202
220
|
spaceId: string;
|
|
203
221
|
channelId: string;
|
|
204
222
|
userId: string;
|
|
205
223
|
args: Record<string, unknown>;
|
|
206
224
|
constructor(client: ClientContext, raw: {
|
|
207
|
-
|
|
208
|
-
|
|
225
|
+
type?: string;
|
|
226
|
+
command?: string;
|
|
227
|
+
command_id?: string;
|
|
228
|
+
custom_id?: string;
|
|
229
|
+
message_id?: string;
|
|
209
230
|
space_id: string;
|
|
210
231
|
channel_id: string;
|
|
211
232
|
user_id: string;
|
|
212
|
-
args
|
|
233
|
+
args?: Record<string, unknown>;
|
|
234
|
+
data?: {
|
|
235
|
+
values?: string[];
|
|
236
|
+
fields?: Record<string, string>;
|
|
237
|
+
};
|
|
213
238
|
});
|
|
239
|
+
/** True if this is a slash command. */
|
|
240
|
+
isCommand(): boolean;
|
|
241
|
+
/** True if this is a button click (component with no selected values). */
|
|
242
|
+
isButton(): boolean;
|
|
243
|
+
/** True if this is a select-menu choice. */
|
|
244
|
+
isSelectMenu(): boolean;
|
|
245
|
+
/** True if this is a submitted modal. */
|
|
246
|
+
isModalSubmit(): boolean;
|
|
247
|
+
/** Get a submitted modal field value by its input customId. */
|
|
248
|
+
field(customId: string): string | undefined;
|
|
214
249
|
/** Respond to the command by posting a message in its channel. */
|
|
215
|
-
reply(content:
|
|
250
|
+
reply(content: MessageContent): Promise<Message>;
|
|
216
251
|
}
|
package/dist/structures.js
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
// act on it directly: message.reply(), channel.send(), interaction.reply().
|
|
3
3
|
import { Collection } from "./collection.js";
|
|
4
4
|
import { PermissionsBitField } from "./permissions.js";
|
|
5
|
+
import { resolveEmbed } from "./embed.js";
|
|
6
|
+
import { resolveComponent } from "./components.js";
|
|
7
|
+
function toPayload(input) {
|
|
8
|
+
if (typeof input === "string")
|
|
9
|
+
return { content: input };
|
|
10
|
+
return {
|
|
11
|
+
content: input.content,
|
|
12
|
+
embeds: input.embeds?.map(resolveEmbed),
|
|
13
|
+
components: input.components?.map(resolveComponent),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
5
16
|
export class ClientUser {
|
|
6
17
|
id;
|
|
7
18
|
handle;
|
|
@@ -40,10 +51,11 @@ export class Message {
|
|
|
40
51
|
}
|
|
41
52
|
/** True if this message was sent by the current bot. */
|
|
42
53
|
get authoredByMe() { return !!this.client.user && this.authorId === this.client.user.id; }
|
|
43
|
-
/** Reply to this message (threads it via replyTo). */
|
|
54
|
+
/** Reply to this message (threads it via replyTo). Accepts text or { content, embeds }. */
|
|
44
55
|
async reply(content) {
|
|
45
|
-
const
|
|
46
|
-
|
|
56
|
+
const p = toPayload(content);
|
|
57
|
+
const { message } = await this.client.rest.post(`/channels/${this.channelId}/messages`, { ...p, replyTo: this.id });
|
|
58
|
+
return new Message(this.client, { ...message, content: p.content ?? "", author_id: this.client.user?.id ?? "" });
|
|
47
59
|
}
|
|
48
60
|
/** Edit this message (only the bot's own messages). */
|
|
49
61
|
async edit(content) {
|
|
@@ -105,10 +117,11 @@ export class TextChannel {
|
|
|
105
117
|
this.position = raw.position ?? null;
|
|
106
118
|
return this;
|
|
107
119
|
}
|
|
108
|
-
/** Send a message to this channel. */
|
|
120
|
+
/** Send a message to this channel. Accepts text or { content, embeds }. */
|
|
109
121
|
async send(content) {
|
|
110
|
-
const
|
|
111
|
-
|
|
122
|
+
const p = toPayload(content);
|
|
123
|
+
const { message } = await this.client.rest.post(`/channels/${this.id}/messages`, p);
|
|
124
|
+
return new Message(this.client, { ...message, content: p.content ?? "", author_id: this.client.user?.id ?? "" });
|
|
112
125
|
}
|
|
113
126
|
/** Fetch recent messages (oldest → newest), up to 100. */
|
|
114
127
|
async fetchMessages(limit = 50) {
|
|
@@ -363,24 +376,50 @@ export class Role {
|
|
|
363
376
|
}
|
|
364
377
|
export class Interaction {
|
|
365
378
|
client;
|
|
379
|
+
/** "command" (slash), "component" (button/select), or "modal_submit". */
|
|
380
|
+
type;
|
|
366
381
|
commandName;
|
|
367
382
|
commandId;
|
|
383
|
+
/** The component/modal customId that triggered this. */
|
|
384
|
+
customId;
|
|
385
|
+
/** The message the component is on. */
|
|
386
|
+
messageId;
|
|
387
|
+
/** Selected values (select-menu interactions). */
|
|
388
|
+
values;
|
|
389
|
+
/** Submitted field values keyed by input customId (modal submits). */
|
|
390
|
+
fields;
|
|
368
391
|
spaceId;
|
|
369
392
|
channelId;
|
|
370
393
|
userId;
|
|
371
394
|
args;
|
|
372
395
|
constructor(client, raw) {
|
|
373
396
|
this.client = client;
|
|
374
|
-
this.
|
|
375
|
-
this.
|
|
397
|
+
this.type = raw.type === "component" ? "component" : raw.type === "modal_submit" ? "modal_submit" : "command";
|
|
398
|
+
this.commandName = raw.command ?? "";
|
|
399
|
+
this.commandId = raw.command_id ?? "";
|
|
400
|
+
this.customId = raw.custom_id ?? "";
|
|
401
|
+
this.messageId = raw.message_id ?? null;
|
|
402
|
+
this.values = Array.isArray(raw.data?.values) ? raw.data.values : [];
|
|
403
|
+
this.fields = raw.data?.fields && typeof raw.data.fields === "object" ? raw.data.fields : {};
|
|
376
404
|
this.spaceId = raw.space_id;
|
|
377
405
|
this.channelId = raw.channel_id;
|
|
378
406
|
this.userId = raw.user_id;
|
|
379
407
|
this.args = raw.args ?? {};
|
|
380
408
|
}
|
|
409
|
+
/** True if this is a slash command. */
|
|
410
|
+
isCommand() { return this.type === "command"; }
|
|
411
|
+
/** True if this is a button click (component with no selected values). */
|
|
412
|
+
isButton() { return this.type === "component" && this.values.length === 0; }
|
|
413
|
+
/** True if this is a select-menu choice. */
|
|
414
|
+
isSelectMenu() { return this.type === "component" && this.values.length > 0; }
|
|
415
|
+
/** True if this is a submitted modal. */
|
|
416
|
+
isModalSubmit() { return this.type === "modal_submit"; }
|
|
417
|
+
/** Get a submitted modal field value by its input customId. */
|
|
418
|
+
field(customId) { return this.fields[customId]; }
|
|
381
419
|
/** Respond to the command by posting a message in its channel. */
|
|
382
420
|
async reply(content) {
|
|
383
|
-
const
|
|
384
|
-
|
|
421
|
+
const p = toPayload(content);
|
|
422
|
+
const { message } = await this.client.rest.post(`/channels/${this.channelId}/messages`, p);
|
|
423
|
+
return new Message(this.client, { ...message, content: p.content ?? "", author_id: this.client.user?.id ?? "" });
|
|
385
424
|
}
|
|
386
425
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -146,12 +146,19 @@ export type DistrictEvent = {
|
|
|
146
146
|
emoji: string;
|
|
147
147
|
} | {
|
|
148
148
|
event: "interaction.create";
|
|
149
|
-
command
|
|
150
|
-
|
|
149
|
+
type?: "command" | "component" | "modal_submit";
|
|
150
|
+
command?: string;
|
|
151
|
+
command_id?: string;
|
|
152
|
+
custom_id?: string;
|
|
153
|
+
message_id?: string;
|
|
151
154
|
space_id: string;
|
|
152
155
|
channel_id: string;
|
|
153
156
|
user_id: string;
|
|
154
|
-
args
|
|
157
|
+
args?: Record<string, unknown>;
|
|
158
|
+
data?: {
|
|
159
|
+
values?: string[];
|
|
160
|
+
fields?: Record<string, string>;
|
|
161
|
+
};
|
|
155
162
|
};
|
|
156
163
|
export interface ReactionEvent {
|
|
157
164
|
spaceId: string;
|
package/package.json
CHANGED