@developer.krd/discord-dashboard 0.1.3 → 0.1.7
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 +145 -136
- package/dist/index.cjs +2453 -2025
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +292 -125
- package/dist/index.d.ts +292 -125
- package/dist/index.js +2448 -2023
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -2,52 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
An advanced, plug-and-play Discord dashboard package for bot developers.
|
|
4
4
|
|
|
5
|
-
Build a
|
|
5
|
+
Build a full web dashboard for your bot without writing frontend code. The package provides built-in rendering, Discord OAuth2 flow, and adapters for Express, Elysia, and Fastify.
|
|
6
6
|
|
|
7
7
|
## ✨ Features
|
|
8
8
|
|
|
9
|
-
- **No
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
- **No frontend app required:** Server-rendered dashboard UI with built-in client script.
|
|
10
|
+
- **Multiple adapters:** `createExpressAdapter`, `createElysiaAdapter`, `createFastifyAdapter`.
|
|
11
|
+
- **Discord OAuth2 flow:** Login, callback exchange, and session persistence.
|
|
12
|
+
- **Theming and layouts:** Built-in layouts/themes (`default`, `compact`, `shadcn-magic`) plus custom renderers.
|
|
13
|
+
- **Home & plugin builders:** Dynamic sections/panels with typed action handlers.
|
|
14
|
+
- **Discord helper API:** Role/channel/member fetch & search helpers inside action context.
|
|
15
|
+
- **TypeScript-first:** Strict typed options, context, fields, and plugin contracts.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🎨 Templates
|
|
20
|
+
|
|
21
|
+
Browse built-in templates and screenshot placeholders in [src/templates/templates.md](src/templates/templates.md).
|
|
18
22
|
|
|
19
23
|
---
|
|
20
24
|
|
|
21
25
|
## 📦 Installation
|
|
22
26
|
|
|
23
27
|
```bash
|
|
24
|
-
npm install @developer.krd/discord-dashboard
|
|
28
|
+
npm install @developer.krd/discord-dashboard discord.js
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
Install only the server stack you use:
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
```bash
|
|
34
|
+
# Express
|
|
35
|
+
npm install express express-session
|
|
36
|
+
|
|
37
|
+
# Elysia
|
|
38
|
+
npm install elysia
|
|
39
|
+
|
|
40
|
+
# Fastify
|
|
41
|
+
npm install fastify @fastify/cookie @fastify/session
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Node.js `>=18` is required.
|
|
30
45
|
|
|
31
|
-
|
|
46
|
+
---
|
|
32
47
|
|
|
33
|
-
|
|
48
|
+
## 🚀 Quick Start (Express)
|
|
34
49
|
|
|
35
50
|
```ts
|
|
36
51
|
import express from "express";
|
|
37
|
-
import {
|
|
52
|
+
import { Client, GatewayIntentBits } from "discord.js";
|
|
53
|
+
import { createExpressAdapter } from "@developer.krd/discord-dashboard";
|
|
38
54
|
|
|
39
55
|
const app = express();
|
|
56
|
+
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
app,
|
|
58
|
+
createExpressAdapter({
|
|
59
|
+
app,
|
|
60
|
+
client,
|
|
43
61
|
basePath: "/dashboard",
|
|
44
62
|
dashboardName: "My Bot Control",
|
|
63
|
+
|
|
45
64
|
botToken: process.env.DISCORD_BOT_TOKEN!,
|
|
46
65
|
clientId: process.env.DISCORD_CLIENT_ID!,
|
|
47
66
|
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
|
|
48
67
|
redirectUri: "http://localhost:3000/dashboard/callback",
|
|
49
68
|
sessionSecret: process.env.DASHBOARD_SESSION_SECRET!,
|
|
50
|
-
|
|
69
|
+
|
|
70
|
+
uiTemplate: "shadcn-magic",
|
|
71
|
+
uiTheme: "shadcn-magic",
|
|
51
72
|
|
|
52
73
|
getOverviewCards: async (context) => [
|
|
53
74
|
{
|
|
@@ -55,7 +76,6 @@ const dashboard = new DiscordDashboard({
|
|
|
55
76
|
title: "Bot Uptime",
|
|
56
77
|
value: `${Math.floor(process.uptime() / 60)} min`,
|
|
57
78
|
subtitle: `Logged in as ${context.user.username}`,
|
|
58
|
-
intent: "success",
|
|
59
79
|
},
|
|
60
80
|
{
|
|
61
81
|
id: "guilds",
|
|
@@ -70,10 +90,10 @@ const dashboard = new DiscordDashboard({
|
|
|
70
90
|
{
|
|
71
91
|
id: "welcome",
|
|
72
92
|
title: "Welcome Settings",
|
|
93
|
+
categoryId: "general",
|
|
73
94
|
description: context.selectedGuildId ? "Guild-specific setup" : "User-level setup",
|
|
74
95
|
fields: [
|
|
75
96
|
{ id: "enabled", label: "Enable Welcome", type: "boolean", value: true },
|
|
76
|
-
{ id: "channel", label: "Channel", type: "channel-search" },
|
|
77
97
|
{ id: "message", label: "Message", type: "textarea", value: "Welcome to the server!" },
|
|
78
98
|
],
|
|
79
99
|
actions: [{ id: "saveWelcome", label: "Save", variant: "primary" }],
|
|
@@ -89,131 +109,116 @@ const dashboard = new DiscordDashboard({
|
|
|
89
109
|
},
|
|
90
110
|
});
|
|
91
111
|
|
|
92
|
-
|
|
112
|
+
await client.login(process.env.DISCORD_BOT_TOKEN!);
|
|
93
113
|
app.listen(3000, () => {
|
|
94
114
|
console.log("Dashboard live at: http://localhost:3000/dashboard");
|
|
95
115
|
});
|
|
96
116
|
```
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
## 🎨 Fluent Dashboard Designer (Recommended)
|
|
101
|
-
|
|
102
|
-
For larger bots, putting all your configuration in one object gets messy. Use the `DashboardDesigner` class to build your dashboard modularly.
|
|
103
|
-
|
|
104
|
-
This approach also allows you to easily inject **Custom CSS** and define dashboard colors.
|
|
118
|
+
## 🚀 Quick Start (Elysia)
|
|
105
119
|
|
|
106
120
|
```ts
|
|
107
|
-
import
|
|
108
|
-
import {
|
|
121
|
+
import { Elysia } from "elysia";
|
|
122
|
+
import { Client, GatewayIntentBits } from "discord.js";
|
|
123
|
+
import { createElysiaAdapter } from "@developer.krd/discord-dashboard";
|
|
109
124
|
|
|
110
|
-
const
|
|
125
|
+
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
126
|
+
const app = new Elysia();
|
|
111
127
|
|
|
112
|
-
|
|
128
|
+
createElysiaAdapter({
|
|
113
129
|
app,
|
|
130
|
+
client,
|
|
131
|
+
basePath: "/dashboard",
|
|
132
|
+
dashboardName: "My Bot Control",
|
|
133
|
+
|
|
114
134
|
botToken: process.env.DISCORD_BOT_TOKEN!,
|
|
115
135
|
clientId: process.env.DISCORD_CLIENT_ID!,
|
|
116
136
|
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
|
|
117
137
|
redirectUri: "http://localhost:3000/dashboard/callback",
|
|
118
138
|
sessionSecret: process.env.DASHBOARD_SESSION_SECRET!,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
rail: "#181a20",
|
|
128
|
-
panel: "#2f3136",
|
|
129
|
-
})
|
|
130
|
-
// Inject your own CSS rules
|
|
131
|
-
.customCss(
|
|
132
|
-
`
|
|
133
|
-
.brand { font-size: 1.2rem; text-transform: uppercase; }
|
|
134
|
-
button.primary { box-shadow: 0 4px 15px rgba(79, 70, 229, 0.4); }
|
|
135
|
-
`,
|
|
136
|
-
)
|
|
137
|
-
// Build a Guild-specific settings category
|
|
138
|
-
.guildCategory("pets", "Pets", (category) => {
|
|
139
|
-
category.section({
|
|
140
|
-
id: "pets-guild",
|
|
141
|
-
title: "Guild Pets",
|
|
142
|
-
fields: [{ id: "petsChannelId", label: "Pets Channel", type: "channel-search" }],
|
|
143
|
-
actions: [{ id: "saveGuildPets", label: "Save", variant: "primary" }],
|
|
144
|
-
});
|
|
145
|
-
})
|
|
146
|
-
// Handle the save action
|
|
147
|
-
.onHomeAction("saveGuildPets", async (context, payload) => {
|
|
148
|
-
const channelId = String(payload.values.petsChannelId ?? "");
|
|
149
|
-
const channel = await context.helpers.getChannel(channelId);
|
|
150
|
-
if (!channel) return { ok: false, message: "Channel not found" };
|
|
151
|
-
return { ok: true, message: "Saved successfully!", refresh: true };
|
|
152
|
-
})
|
|
153
|
-
// Automatically instantiates the DiscordDashboard class
|
|
154
|
-
.createDashboard();
|
|
155
|
-
|
|
156
|
-
app.listen(3000, () => console.log("Dashboard ready!"));
|
|
139
|
+
|
|
140
|
+
uiTemplate: "shadcn-magic",
|
|
141
|
+
uiTheme: "shadcn-magic",
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await client.login(process.env.DISCORD_BOT_TOKEN!);
|
|
145
|
+
app.listen({ port: 3000 });
|
|
146
|
+
console.log("Dashboard live at: http://localhost:3000/dashboard");
|
|
157
147
|
```
|
|
158
148
|
|
|
159
|
-
|
|
149
|
+
## 🚀 Quick Start (Fastify)
|
|
160
150
|
|
|
161
|
-
|
|
151
|
+
```ts
|
|
152
|
+
import Fastify from "fastify";
|
|
153
|
+
import fastifyCookie from "@fastify/cookie";
|
|
154
|
+
import fastifySession from "@fastify/session";
|
|
155
|
+
import { Client, GatewayIntentBits } from "discord.js";
|
|
156
|
+
import { createFastifyAdapter } from "@developer.krd/discord-dashboard";
|
|
157
|
+
|
|
158
|
+
const fastify = Fastify({ logger: true });
|
|
159
|
+
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
|
160
|
+
|
|
161
|
+
await fastify.register(fastifyCookie);
|
|
162
|
+
await fastify.register(fastifySession, {
|
|
163
|
+
secret: process.env.DASHBOARD_SESSION_SECRET!,
|
|
164
|
+
cookie: { secure: false },
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
createFastifyAdapter(fastify, {
|
|
168
|
+
client,
|
|
169
|
+
basePath: "/dashboard",
|
|
170
|
+
dashboardName: "My Bot Control",
|
|
162
171
|
|
|
163
|
-
|
|
172
|
+
botToken: process.env.DISCORD_BOT_TOKEN!,
|
|
173
|
+
clientId: process.env.DISCORD_CLIENT_ID!,
|
|
174
|
+
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
|
|
175
|
+
redirectUri: "http://localhost:3000/dashboard/callback",
|
|
176
|
+
sessionSecret: process.env.DASHBOARD_SESSION_SECRET!,
|
|
164
177
|
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
178
|
+
uiTemplate: "shadcn-magic",
|
|
179
|
+
uiTheme: "shadcn-magic",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await client.login(process.env.DISCORD_BOT_TOKEN!);
|
|
183
|
+
await fastify.listen({ port: 3000, host: "0.0.0.0" });
|
|
184
|
+
console.log("Dashboard live at: http://localhost:3000/dashboard");
|
|
185
|
+
```
|
|
172
186
|
|
|
173
187
|
---
|
|
174
188
|
|
|
175
|
-
##
|
|
189
|
+
## 🔌 Adapter Notes
|
|
176
190
|
|
|
177
|
-
|
|
191
|
+
- **Express:** `createExpressAdapter(options)`
|
|
192
|
+
- **Elysia:** `createElysiaAdapter(options)`
|
|
193
|
+
- **Fastify:** `createFastifyAdapter(fastify, options)`
|
|
178
194
|
|
|
179
|
-
|
|
180
|
-
- `scope: "guild"` → Shows only on the Guild Dashboard.
|
|
181
|
-
- `scope: "both"` → (Default) Shows everywhere.
|
|
195
|
+
For Fastify, you must register `@fastify/cookie` and `@fastify/session` before wiring the adapter, since dashboard routes read/write `request.session`.
|
|
182
196
|
|
|
183
|
-
|
|
197
|
+
---
|
|
184
198
|
|
|
185
|
-
|
|
186
|
-
import { DiscordDashboard } from "@developer.krd/discord-dashboard";
|
|
199
|
+
## 🛠️ Extensible Plugins
|
|
187
200
|
|
|
188
|
-
|
|
189
|
-
|
|
201
|
+
```ts
|
|
202
|
+
createExpressAdapter({
|
|
203
|
+
// ... core config ...
|
|
190
204
|
plugins: [
|
|
191
205
|
{
|
|
192
|
-
id: "
|
|
193
|
-
name: "
|
|
194
|
-
|
|
206
|
+
id: "runtime",
|
|
207
|
+
name: "System Runtime",
|
|
208
|
+
description: "Live bot diagnostics",
|
|
195
209
|
getPanels: async () => [
|
|
196
210
|
{
|
|
197
|
-
id: "
|
|
198
|
-
title: "
|
|
211
|
+
id: "runtime-status",
|
|
212
|
+
title: "Diagnostics",
|
|
199
213
|
fields: [
|
|
200
|
-
{
|
|
201
|
-
{
|
|
202
|
-
{ id: "description", label: "Embed Description", type: "textarea", editable: true },
|
|
203
|
-
{ id: "buttonLabel", label: "Button Label", type: "text", editable: true, value: "Open Ticket" },
|
|
214
|
+
{ label: "Logged in as", value: client.user?.tag ?? "Unknown" },
|
|
215
|
+
{ label: "Uptime", value: `${Math.floor(process.uptime())}s` },
|
|
204
216
|
],
|
|
205
|
-
actions: [{ id: "
|
|
217
|
+
actions: [{ id: "refreshRuntime", label: "Refresh", variant: "primary", collectFields: false }],
|
|
206
218
|
},
|
|
207
219
|
],
|
|
208
220
|
actions: {
|
|
209
|
-
|
|
210
|
-
// body.values contains the form data because collectFields was true
|
|
211
|
-
const data = body as { values?: Record<string, unknown> };
|
|
212
|
-
const values = data.values ?? {};
|
|
213
|
-
|
|
214
|
-
console.log(`Creating ticket panel in ${values.targetChannel}`);
|
|
215
|
-
return { ok: true, message: `Ticket panel published!`, data: values };
|
|
216
|
-
},
|
|
221
|
+
refreshRuntime: async () => ({ ok: true, message: "Data refreshed", refresh: true }),
|
|
217
222
|
},
|
|
218
223
|
},
|
|
219
224
|
],
|
|
@@ -222,15 +227,26 @@ const dashboard = new DiscordDashboard({
|
|
|
222
227
|
|
|
223
228
|
---
|
|
224
229
|
|
|
225
|
-
##
|
|
230
|
+
## 🧩 Built-in Helper Functions
|
|
226
231
|
|
|
227
|
-
|
|
232
|
+
Inside `home.actions` and `plugins.actions`, use `context.helpers`:
|
|
228
233
|
|
|
229
|
-
- `
|
|
230
|
-
- `
|
|
231
|
-
- `
|
|
234
|
+
- `getGuildIconUrl(guildId, iconHash)`
|
|
235
|
+
- `getUserAvatarUrl(userId, avatarHash)`
|
|
236
|
+
- `getChannel(channelId)`
|
|
237
|
+
- `getGuildChannels(guildId)`
|
|
238
|
+
- `searchGuildChannels(guildId, query, options?)`
|
|
239
|
+
- `getRole(guildId, roleId)`
|
|
240
|
+
- `getGuildRoles(guildId)`
|
|
241
|
+
- `searchGuildRoles(guildId, query, options?)`
|
|
242
|
+
- `searchGuildMembers(guildId, query, options?)`
|
|
243
|
+
- `getGuildMember(guildId, userId)`
|
|
244
|
+
|
|
245
|
+
---
|
|
232
246
|
|
|
233
|
-
|
|
247
|
+
## 🔍 Lookup Fields
|
|
248
|
+
|
|
249
|
+
Field types include `role-search`, `channel-search`, and `member-search`, and you can provide lookup options in field config (for limits and filters).
|
|
234
250
|
|
|
235
251
|
```ts
|
|
236
252
|
{
|
|
@@ -239,8 +255,8 @@ You can configure lookups with filters:
|
|
|
239
255
|
type: "channel-search",
|
|
240
256
|
lookup: {
|
|
241
257
|
limit: 5,
|
|
242
|
-
channelTypes: [0, 5]
|
|
243
|
-
}
|
|
258
|
+
channelTypes: [0, 5],
|
|
259
|
+
},
|
|
244
260
|
}
|
|
245
261
|
```
|
|
246
262
|
|
|
@@ -248,35 +264,28 @@ You can configure lookups with filters:
|
|
|
248
264
|
|
|
249
265
|
## 📚 API Reference
|
|
250
266
|
|
|
251
|
-
|
|
267
|
+
`DashboardOptions` includes:
|
|
252
268
|
|
|
253
|
-
-
|
|
254
|
-
-
|
|
255
|
-
-
|
|
256
|
-
-
|
|
257
|
-
- **`userCategory(id, label, build)`**: Add tabs to the user dashboard.
|
|
258
|
-
- **`guildCategory(id, label, build)`**: Add tabs to the server dashboard.
|
|
259
|
-
- **`onHomeAction(actionId, handler)`**: Define what happens when a button is clicked.
|
|
260
|
-
- **`createDashboard()`**: Terminal method. Builds the config and returns a `DiscordDashboard` instance.
|
|
269
|
+
- OAuth/session config (`clientId`, `clientSecret`, `redirectUri`, `sessionSecret`, ...)
|
|
270
|
+
- Dashboard presentation (`dashboardName`, `basePath`, `uiTemplate`, `uiTheme`, `setupDesign`)
|
|
271
|
+
- Dynamic content (`getOverviewCards`, `home`, `plugins`)
|
|
272
|
+
- Runtime dependencies (`client`, optional framework app where supported)
|
|
261
273
|
|
|
262
|
-
|
|
274
|
+
Also exported:
|
|
263
275
|
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
- **`stop()`**: Gracefully shuts down the internal server.
|
|
276
|
+
- `DashboardEngine`
|
|
277
|
+
- `DashboardDesigner`
|
|
267
278
|
|
|
268
279
|
---
|
|
269
280
|
|
|
270
281
|
## 🔒 Required Discord OAuth2 Setup
|
|
271
282
|
|
|
272
|
-
To make login work:
|
|
273
|
-
|
|
274
283
|
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications).
|
|
275
|
-
2. Open your Application
|
|
276
|
-
3. Add your
|
|
277
|
-
4.
|
|
284
|
+
2. Open your Application → **OAuth2**.
|
|
285
|
+
3. Add your redirect URI (example: `http://localhost:3000/dashboard/callback`).
|
|
286
|
+
4. Use your `CLIENT_ID` and `CLIENT_SECRET` in dashboard config.
|
|
278
287
|
|
|
279
|
-
|
|
288
|
+
Use a strong `sessionSecret` in production and serve behind HTTPS.
|
|
280
289
|
|
|
281
290
|
---
|
|
282
291
|
|