district.js 0.5.0 → 0.7.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 +28 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +4 -0
- package/dist/components.d.ts +64 -3
- package/dist/components.js +48 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/store.d.ts +41 -0
- package/dist/store.js +90 -0
- package/dist/structures.d.ts +20 -6
- package/dist/structures.js +18 -6
- package/dist/types.d.ts +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,34 @@ client.on("interactionCreate", (i) => {
|
|
|
110
110
|
});
|
|
111
111
|
```
|
|
112
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
|
+
|
|
113
141
|
## Slash commands
|
|
114
142
|
|
|
115
143
|
Register the command in the portal (name + handler URL), then:
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +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 { Store } from "./store.js";
|
|
4
5
|
import { ClientUser, Message, TextChannel, Interaction, User, Space, type ClientContext, type MessageContent } from "./structures.js";
|
|
5
6
|
import { type EventHeaders } from "./webhook.js";
|
|
6
7
|
import type { ClientOptions, ReactionEvent, DeletedMessage } from "./types.js";
|
|
@@ -17,6 +18,8 @@ export interface ClientEvents {
|
|
|
17
18
|
}
|
|
18
19
|
export declare class Client extends EventEmitter implements ClientContext {
|
|
19
20
|
readonly rest: REST;
|
|
21
|
+
/** The bot's persistent document store (`client.store.collection("users")…`). */
|
|
22
|
+
readonly store: Store;
|
|
20
23
|
/** The bot's own identity — populated after `login()`. */
|
|
21
24
|
user: ClientUser | null;
|
|
22
25
|
private signingSecret?;
|
package/dist/client.js
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
import { EventEmitter } from "node:events";
|
|
4
4
|
import http from "node:http";
|
|
5
5
|
import { REST } from "./rest.js";
|
|
6
|
+
import { Store } from "./store.js";
|
|
6
7
|
import { ClientUser, Message, TextChannel, Interaction, User, Space } from "./structures.js";
|
|
7
8
|
import { verifyAndParse } from "./webhook.js";
|
|
8
9
|
export class Client extends EventEmitter {
|
|
9
10
|
rest;
|
|
11
|
+
/** The bot's persistent document store (`client.store.collection("users")…`). */
|
|
12
|
+
store;
|
|
10
13
|
/** The bot's own identity — populated after `login()`. */
|
|
11
14
|
user = null;
|
|
12
15
|
signingSecret;
|
|
@@ -15,6 +18,7 @@ export class Client extends EventEmitter {
|
|
|
15
18
|
if (!options?.token)
|
|
16
19
|
throw new Error("district.js: `token` is required");
|
|
17
20
|
this.rest = new REST(options.token, options.apiBase);
|
|
21
|
+
this.store = new Store(this.rest);
|
|
18
22
|
this.signingSecret = options.signingSecret;
|
|
19
23
|
}
|
|
20
24
|
// ── typed emitter surface ──────────────────────────────────────────────────
|
package/dist/components.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
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
|
+
}
|
|
2
15
|
export interface ButtonData {
|
|
3
16
|
type: "button";
|
|
4
17
|
style: ButtonStyle;
|
|
@@ -7,10 +20,27 @@ export interface ButtonData {
|
|
|
7
20
|
url?: string;
|
|
8
21
|
emoji?: string;
|
|
9
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;
|
|
10
39
|
}
|
|
40
|
+
export type ComponentData = ButtonData | SelectData;
|
|
11
41
|
export interface ActionRowData {
|
|
12
42
|
type: "action_row";
|
|
13
|
-
components:
|
|
43
|
+
components: ComponentData[];
|
|
14
44
|
}
|
|
15
45
|
export declare class ButtonBuilder {
|
|
16
46
|
data: Partial<ButtonData>;
|
|
@@ -22,12 +52,43 @@ export declare class ButtonBuilder {
|
|
|
22
52
|
setUrl(url: string): this;
|
|
23
53
|
setEmoji(emoji: string): this;
|
|
24
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;
|
|
25
58
|
toJSON(): ButtonData;
|
|
26
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
|
+
}
|
|
27
88
|
export declare class ActionRowBuilder {
|
|
28
89
|
data: ActionRowData;
|
|
29
|
-
/** Add up to 5 buttons
|
|
30
|
-
addComponents(...
|
|
90
|
+
/** Add up to 5 buttons, or a single select menu. */
|
|
91
|
+
addComponents(...components: (ButtonBuilder | StringSelectMenuBuilder | ComponentData)[]): this;
|
|
31
92
|
toJSON(): ActionRowData;
|
|
32
93
|
}
|
|
33
94
|
export type ComponentResolvable = ActionRowBuilder | ActionRowData;
|
package/dist/components.js
CHANGED
|
@@ -12,14 +12,58 @@ export class ButtonBuilder {
|
|
|
12
12
|
setUrl(url) { this.data.url = url; this.data.style = "link"; return this; }
|
|
13
13
|
setEmoji(emoji) { this.data.emoji = emoji; return this; }
|
|
14
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
|
+
}
|
|
15
58
|
toJSON() { return this.data; }
|
|
16
59
|
}
|
|
17
60
|
export class ActionRowBuilder {
|
|
18
61
|
data = { type: "action_row", components: [] };
|
|
19
|
-
/** Add up to 5 buttons
|
|
20
|
-
addComponents(...
|
|
21
|
-
for (const
|
|
22
|
-
this.data.components.push(
|
|
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
|
+
}
|
|
23
67
|
return this;
|
|
24
68
|
}
|
|
25
69
|
toJSON() { return this.data; }
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { Client, type ClientEvents } from "./client.js";
|
|
|
2
2
|
export { REST, DistrictAPIError, DEFAULT_API_BASE } from "./rest.js";
|
|
3
3
|
export { ClientUser, Message, TextChannel, Interaction, User, Space, SpaceMember, Role, Attachment, Ban, Invite, Webhook, type ClientContext, type MessageContent, } from "./structures.js";
|
|
4
4
|
export { EmbedBuilder, resolveEmbed, type EmbedData, type EmbedField, type EmbedResolvable } from "./embed.js";
|
|
5
|
-
export { ButtonBuilder, ActionRowBuilder, resolveComponent, type ButtonStyle, type ButtonData, type ActionRowData, type ComponentResolvable, } from "./components.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";
|
|
6
|
+
export { Store, StoreCollection, type StoreDocument } from "./store.js";
|
|
6
7
|
export { Collection } from "./collection.js";
|
|
7
8
|
export { PermissionsBitField, type PermissionResolvable, type PermissionFlagName } from "./permissions.js";
|
|
8
9
|
export { Formatters } from "./formatters.js";
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,8 @@ 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
10
|
export { EmbedBuilder, resolveEmbed } from "./embed.js";
|
|
11
|
-
export { ButtonBuilder, ActionRowBuilder, resolveComponent, } from "./components.js";
|
|
11
|
+
export { ButtonBuilder, ActionRowBuilder, StringSelectMenuBuilder, ModalBuilder, TextInputBuilder, resolveComponent, } from "./components.js";
|
|
12
|
+
export { Store, StoreCollection } from "./store.js";
|
|
12
13
|
export { Collection } from "./collection.js";
|
|
13
14
|
export { PermissionsBitField } from "./permissions.js";
|
|
14
15
|
export { Formatters } from "./formatters.js";
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { REST } from "./rest.js";
|
|
2
|
+
/** One stored document: its `key` and JSON `value` (+ last-updated time on reads). */
|
|
3
|
+
export interface StoreDocument<T = any> {
|
|
4
|
+
key: string;
|
|
5
|
+
value: T;
|
|
6
|
+
updatedAt?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A handle to one collection of documents (e.g. "users", "guilds"). */
|
|
9
|
+
export declare class StoreCollection {
|
|
10
|
+
private readonly rest;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
constructor(rest: REST, name: string);
|
|
13
|
+
/** Read one document's value, or `null` if it doesn't exist. */
|
|
14
|
+
get<T = any>(key: string): Promise<T | null>;
|
|
15
|
+
/** Create or fully replace a document. Returns the stored value. */
|
|
16
|
+
set<T = any>(key: string, value: T): Promise<T>;
|
|
17
|
+
/** Shallow-merge `partial` into an existing document (creates it if absent).
|
|
18
|
+
* Returns the merged value. */
|
|
19
|
+
update<T = any>(key: string, partial: Partial<T>): Promise<T>;
|
|
20
|
+
/** Delete a document. No-op if it doesn't exist. */
|
|
21
|
+
delete(key: string): Promise<void>;
|
|
22
|
+
/** Atomically add `amount` to a numeric field (default field: `value`). Safe
|
|
23
|
+
* under concurrency — two simultaneous increments never lose a write. Pass an
|
|
24
|
+
* array for a nested path, e.g. `increment(id, ["stats", "wins"], 1)`. Returns
|
|
25
|
+
* the whole updated document value. */
|
|
26
|
+
increment<T = any>(key: string, field?: string | string[], amount?: number): Promise<T>;
|
|
27
|
+
/** List documents in this collection (newest-updated first by default). */
|
|
28
|
+
all<T = any>(opts?: {
|
|
29
|
+
limit?: number;
|
|
30
|
+
offset?: number;
|
|
31
|
+
order?: "asc" | "desc";
|
|
32
|
+
}): Promise<StoreDocument<T>[]>;
|
|
33
|
+
}
|
|
34
|
+
/** The bot's persistent store. Get a collection with `.collection(name)`. */
|
|
35
|
+
export declare class Store {
|
|
36
|
+
private readonly rest;
|
|
37
|
+
private readonly cache;
|
|
38
|
+
constructor(rest: REST);
|
|
39
|
+
/** A handle to a named collection (memoized). */
|
|
40
|
+
collection(name: string): StoreCollection;
|
|
41
|
+
}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Persistent bot storage — the district.js equivalent of "bring your own
|
|
2
|
+
// database". Each bot gets an isolated document store, addressed as
|
|
3
|
+
// `collection` → `key` → JSON `value`, so you can model users, balances,
|
|
4
|
+
// transactions, leaderboards, config… anything a data-heavy bot needs.
|
|
5
|
+
//
|
|
6
|
+
// const users = client.store.collection("users");
|
|
7
|
+
// await users.set("42", { balance: 100, streak: 0 });
|
|
8
|
+
// const me = await users.get<{ balance: number }>("42");
|
|
9
|
+
// await users.increment("42", "balance", 250); // atomic — safe under load
|
|
10
|
+
// const leaderboard = await users.all({ limit: 10 });
|
|
11
|
+
//
|
|
12
|
+
// Everything is scoped to your application automatically; a bot can only ever
|
|
13
|
+
// see its own data.
|
|
14
|
+
import { DistrictAPIError } from "./rest.js";
|
|
15
|
+
const enc = encodeURIComponent;
|
|
16
|
+
/** A handle to one collection of documents (e.g. "users", "guilds"). */
|
|
17
|
+
export class StoreCollection {
|
|
18
|
+
rest;
|
|
19
|
+
name;
|
|
20
|
+
constructor(rest, name) {
|
|
21
|
+
this.rest = rest;
|
|
22
|
+
this.name = name;
|
|
23
|
+
}
|
|
24
|
+
/** Read one document's value, or `null` if it doesn't exist. */
|
|
25
|
+
async get(key) {
|
|
26
|
+
try {
|
|
27
|
+
const r = await this.rest.get(`/storage/${enc(this.name)}/${enc(key)}`);
|
|
28
|
+
return r.value;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e instanceof DistrictAPIError && e.status === 404)
|
|
32
|
+
return null;
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Create or fully replace a document. Returns the stored value. */
|
|
37
|
+
async set(key, value) {
|
|
38
|
+
const r = await this.rest.put(`/storage/${enc(this.name)}/${enc(key)}`, value);
|
|
39
|
+
return r.value;
|
|
40
|
+
}
|
|
41
|
+
/** Shallow-merge `partial` into an existing document (creates it if absent).
|
|
42
|
+
* Returns the merged value. */
|
|
43
|
+
async update(key, partial) {
|
|
44
|
+
const r = await this.rest.patch(`/storage/${enc(this.name)}/${enc(key)}`, partial);
|
|
45
|
+
return r.value;
|
|
46
|
+
}
|
|
47
|
+
/** Delete a document. No-op if it doesn't exist. */
|
|
48
|
+
async delete(key) {
|
|
49
|
+
await this.rest.delete(`/storage/${enc(this.name)}/${enc(key)}`);
|
|
50
|
+
}
|
|
51
|
+
/** Atomically add `amount` to a numeric field (default field: `value`). Safe
|
|
52
|
+
* under concurrency — two simultaneous increments never lose a write. Pass an
|
|
53
|
+
* array for a nested path, e.g. `increment(id, ["stats", "wins"], 1)`. Returns
|
|
54
|
+
* the whole updated document value. */
|
|
55
|
+
async increment(key, field = "value", amount = 1) {
|
|
56
|
+
const path = Array.isArray(field) ? field : [field];
|
|
57
|
+
const r = await this.rest.post(`/storage/${enc(this.name)}/${enc(key)}/increment`, { path, amount });
|
|
58
|
+
return r.value;
|
|
59
|
+
}
|
|
60
|
+
/** List documents in this collection (newest-updated first by default). */
|
|
61
|
+
async all(opts = {}) {
|
|
62
|
+
const q = new URLSearchParams();
|
|
63
|
+
if (opts.limit != null)
|
|
64
|
+
q.set("limit", String(opts.limit));
|
|
65
|
+
if (opts.offset != null)
|
|
66
|
+
q.set("offset", String(opts.offset));
|
|
67
|
+
if (opts.order)
|
|
68
|
+
q.set("order", opts.order);
|
|
69
|
+
const qs = q.toString();
|
|
70
|
+
const r = await this.rest.get(`/storage/${enc(this.name)}${qs ? `?${qs}` : ""}`);
|
|
71
|
+
return r.items ?? [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** The bot's persistent store. Get a collection with `.collection(name)`. */
|
|
75
|
+
export class Store {
|
|
76
|
+
rest;
|
|
77
|
+
cache = new Map();
|
|
78
|
+
constructor(rest) {
|
|
79
|
+
this.rest = rest;
|
|
80
|
+
}
|
|
81
|
+
/** A handle to a named collection (memoized). */
|
|
82
|
+
collection(name) {
|
|
83
|
+
let c = this.cache.get(name);
|
|
84
|
+
if (!c) {
|
|
85
|
+
c = new StoreCollection(this.rest, name);
|
|
86
|
+
this.cache.set(name, c);
|
|
87
|
+
}
|
|
88
|
+
return c;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/dist/structures.d.ts
CHANGED
|
@@ -205,14 +205,18 @@ export declare class Role {
|
|
|
205
205
|
}
|
|
206
206
|
export declare class Interaction {
|
|
207
207
|
private client;
|
|
208
|
-
/** "command" (slash
|
|
209
|
-
type: "command" | "component";
|
|
208
|
+
/** "command" (slash), "component" (button/select), or "modal_submit". */
|
|
209
|
+
type: "command" | "component" | "modal_submit";
|
|
210
210
|
commandName: string;
|
|
211
211
|
commandId: string;
|
|
212
|
-
/** The
|
|
212
|
+
/** The component/modal customId that triggered this. */
|
|
213
213
|
customId: string;
|
|
214
|
-
/** The message the component is on
|
|
214
|
+
/** The message the component is on. */
|
|
215
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>;
|
|
216
220
|
spaceId: string;
|
|
217
221
|
channelId: string;
|
|
218
222
|
userId: string;
|
|
@@ -227,11 +231,21 @@ export declare class Interaction {
|
|
|
227
231
|
channel_id: string;
|
|
228
232
|
user_id: string;
|
|
229
233
|
args?: Record<string, unknown>;
|
|
234
|
+
data?: {
|
|
235
|
+
values?: string[];
|
|
236
|
+
fields?: Record<string, string>;
|
|
237
|
+
};
|
|
230
238
|
});
|
|
231
|
-
/** True if this is a button click. */
|
|
232
|
-
isButton(): boolean;
|
|
233
239
|
/** True if this is a slash command. */
|
|
234
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;
|
|
235
249
|
/** Respond to the command by posting a message in its channel. */
|
|
236
250
|
reply(content: MessageContent): Promise<Message>;
|
|
237
251
|
}
|
package/dist/structures.js
CHANGED
|
@@ -376,34 +376,46 @@ export class Role {
|
|
|
376
376
|
}
|
|
377
377
|
export class Interaction {
|
|
378
378
|
client;
|
|
379
|
-
/** "command" (slash
|
|
379
|
+
/** "command" (slash), "component" (button/select), or "modal_submit". */
|
|
380
380
|
type;
|
|
381
381
|
commandName;
|
|
382
382
|
commandId;
|
|
383
|
-
/** The
|
|
383
|
+
/** The component/modal customId that triggered this. */
|
|
384
384
|
customId;
|
|
385
|
-
/** The message the component is on
|
|
385
|
+
/** The message the component is on. */
|
|
386
386
|
messageId;
|
|
387
|
+
/** Selected values (select-menu interactions). */
|
|
388
|
+
values;
|
|
389
|
+
/** Submitted field values keyed by input customId (modal submits). */
|
|
390
|
+
fields;
|
|
387
391
|
spaceId;
|
|
388
392
|
channelId;
|
|
389
393
|
userId;
|
|
390
394
|
args;
|
|
391
395
|
constructor(client, raw) {
|
|
392
396
|
this.client = client;
|
|
393
|
-
this.type = raw.type === "component" ? "component" : "command";
|
|
397
|
+
this.type = raw.type === "component" ? "component" : raw.type === "modal_submit" ? "modal_submit" : "command";
|
|
394
398
|
this.commandName = raw.command ?? "";
|
|
395
399
|
this.commandId = raw.command_id ?? "";
|
|
396
400
|
this.customId = raw.custom_id ?? "";
|
|
397
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 : {};
|
|
398
404
|
this.spaceId = raw.space_id;
|
|
399
405
|
this.channelId = raw.channel_id;
|
|
400
406
|
this.userId = raw.user_id;
|
|
401
407
|
this.args = raw.args ?? {};
|
|
402
408
|
}
|
|
403
|
-
/** True if this is a button click. */
|
|
404
|
-
isButton() { return this.type === "component"; }
|
|
405
409
|
/** True if this is a slash command. */
|
|
406
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]; }
|
|
407
419
|
/** Respond to the command by posting a message in its channel. */
|
|
408
420
|
async reply(content) {
|
|
409
421
|
const p = toPayload(content);
|
package/dist/types.d.ts
CHANGED
|
@@ -146,7 +146,7 @@ export type DistrictEvent = {
|
|
|
146
146
|
emoji: string;
|
|
147
147
|
} | {
|
|
148
148
|
event: "interaction.create";
|
|
149
|
-
type?: "command" | "component";
|
|
149
|
+
type?: "command" | "component" | "modal_submit";
|
|
150
150
|
command?: string;
|
|
151
151
|
command_id?: string;
|
|
152
152
|
custom_id?: string;
|
|
@@ -155,6 +155,10 @@ export type DistrictEvent = {
|
|
|
155
155
|
channel_id: string;
|
|
156
156
|
user_id: string;
|
|
157
157
|
args?: Record<string, unknown>;
|
|
158
|
+
data?: {
|
|
159
|
+
values?: string[];
|
|
160
|
+
fields?: Record<string, string>;
|
|
161
|
+
};
|
|
158
162
|
};
|
|
159
163
|
export interface ReactionEvent {
|
|
160
164
|
spaceId: string;
|
package/package.json
CHANGED