@mastra/discord 0.0.0 → 1.1.0-alpha.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/CHANGELOG.md +38 -0
- package/LICENSE.md +32 -0
- package/dist/index.cjs +116012 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +554 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1110 -0
- package/dist/index.js.map +1 -0
- package/package.json +12 -12
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { ChannelAdapterConfig, ChannelConfig, ChannelConnectResult, ChannelHandlers, ChannelInstallationInfo, ChannelPlatformInfo, ChannelProvider, StreamingConfig, WaitUntilFn } from "@mastra/core/channels";
|
|
2
|
+
import { ChannelsStorage } from "@mastra/core/storage";
|
|
3
|
+
import { DiscordAdapter, DiscordAdapter as DiscordAdapter$1, DiscordAdapterConfig, DiscordAdapterConfig as DiscordAdapterConfig$1, createDiscordAdapter } from "@chat-adapter/discord";
|
|
4
|
+
import { Mastra } from "@mastra/core/mastra";
|
|
5
|
+
import { ApiRoute } from "@mastra/core/server";
|
|
6
|
+
//#region src/types.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Default Discord REST base — **including** the `/api/v10` version segment. The
|
|
9
|
+
* client concatenates this with the request path verbatim, so an override must
|
|
10
|
+
* carry its own version segment (`https://example.test/api/v10`); passing a bare
|
|
11
|
+
* origin produces unversioned request URLs.
|
|
12
|
+
*/
|
|
13
|
+
declare const DISCORD_API_BASE_URL = "https://discord.com/api/v10";
|
|
14
|
+
/** Base for the OAuth2 authorize (bot-invite) URL. */
|
|
15
|
+
declare const DISCORD_OAUTH_AUTHORIZE_URL = "https://discord.com/oauth2/authorize";
|
|
16
|
+
/**
|
|
17
|
+
* Named Discord permission bits (a subset). Discord permissions are a 53+ bit
|
|
18
|
+
* field, so they are represented as `bigint` and serialized to a decimal string
|
|
19
|
+
* in the invite URL.
|
|
20
|
+
*
|
|
21
|
+
* @see https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags
|
|
22
|
+
*/
|
|
23
|
+
declare const DISCORD_PERMISSIONS: {
|
|
24
|
+
readonly ADD_REACTIONS: bigint;
|
|
25
|
+
readonly VIEW_CHANNEL: bigint;
|
|
26
|
+
readonly SEND_MESSAGES: bigint;
|
|
27
|
+
readonly EMBED_LINKS: bigint;
|
|
28
|
+
readonly ATTACH_FILES: bigint;
|
|
29
|
+
readonly READ_MESSAGE_HISTORY: bigint;
|
|
30
|
+
readonly USE_APPLICATION_COMMANDS: bigint;
|
|
31
|
+
readonly SEND_MESSAGES_IN_THREADS: bigint;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Default permissions requested in the bot-invite URL: enough for a chat agent
|
|
35
|
+
* to read and reply (in channels and threads), post embeds/files, add reactions,
|
|
36
|
+
* and see recent history. Kept intentionally minimal — no moderation or manage
|
|
37
|
+
* permissions. Callers can override via {@link DiscordConnectOptions.permissions}
|
|
38
|
+
* or {@link DiscordProviderConfig.permissions}.
|
|
39
|
+
*/
|
|
40
|
+
declare const DEFAULT_INVITE_PERMISSIONS: bigint;
|
|
41
|
+
/** OAuth2 scopes requested by the bot-invite URL. */
|
|
42
|
+
declare const DEFAULT_INVITE_SCOPES: readonly ['bot', 'applications.commands'];
|
|
43
|
+
/**
|
|
44
|
+
* A Discord application command as it goes over the wire (bulk-overwrite
|
|
45
|
+
* registration). Only the `CHAT_INPUT` (slash) shape is modeled here.
|
|
46
|
+
* @see https://discord.com/developers/docs/interactions/application-commands
|
|
47
|
+
*/
|
|
48
|
+
interface DiscordCommand {
|
|
49
|
+
/** 1-32 chars, lowercase `[a-z0-9_-]` (Discord also allows a leading `-`/`_`). */
|
|
50
|
+
name: string;
|
|
51
|
+
/** 1-100 chars. */
|
|
52
|
+
description: string;
|
|
53
|
+
}
|
|
54
|
+
/** Command input accepted by the provider — a bare name or `{ name, description }`. */
|
|
55
|
+
type DiscordCommandInput = string | {
|
|
56
|
+
name: string;
|
|
57
|
+
description?: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* App-level credentials for a single Discord application. Discord has **no
|
|
61
|
+
* programmatic app creation** — these come from the Developer Portal and are
|
|
62
|
+
* stored **once** (one app, many guilds). `botToken` is the only secret;
|
|
63
|
+
* `publicKey` and `applicationId` are public.
|
|
64
|
+
*/
|
|
65
|
+
interface DiscordAppConfig {
|
|
66
|
+
/** Bot token (`Authorization: Bot <token>`) — the control-plane credential. Secret. */
|
|
67
|
+
botToken: string;
|
|
68
|
+
/** Ed25519 public key used by the adapter to verify interaction signatures. */
|
|
69
|
+
publicKey: string;
|
|
70
|
+
/** The application (client) id — used as `client_id` in the invite URL. */
|
|
71
|
+
applicationId: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Configuration for {@link DiscordProvider}.
|
|
75
|
+
*
|
|
76
|
+
* Discord is **one app, many guilds**: a single application's credentials are
|
|
77
|
+
* reused across every guild the bot is invited to. There is no per-install
|
|
78
|
+
* token — "installing" is a bot invite (an OAuth2 authorize URL), and tenancy
|
|
79
|
+
* is keyed by `guildId` per interaction.
|
|
80
|
+
*/
|
|
81
|
+
interface DiscordProviderConfig {
|
|
82
|
+
/**
|
|
83
|
+
* App credentials (bot token / public key / application id). May be omitted
|
|
84
|
+
* here and provided via the `DISCORD_BOT_TOKEN` / `DISCORD_PUBLIC_KEY` /
|
|
85
|
+
* `DISCORD_APPLICATION_ID` env vars, or later via {@link DiscordProvider.configure}.
|
|
86
|
+
* Whichever source resolves first is persisted **once** to channels storage.
|
|
87
|
+
*/
|
|
88
|
+
app?: Partial<DiscordAppConfig>;
|
|
89
|
+
/**
|
|
90
|
+
* Public HTTPS base URL for the interactions endpoint (the URL set once in the
|
|
91
|
+
* Developer Portal). May be omitted and auto-detected from the Mastra server.
|
|
92
|
+
*/
|
|
93
|
+
baseUrl?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Persistence for the app config + per-agent installs. Defaults to Mastra's
|
|
96
|
+
* channels storage when attached to a Mastra instance with storage, and falls
|
|
97
|
+
* back to an in-memory store otherwise (dev/test — not persisted across restarts).
|
|
98
|
+
*/
|
|
99
|
+
storage?: ChannelsStorage;
|
|
100
|
+
/**
|
|
101
|
+
* Override the Discord REST origin (e.g. a test mock). Defaults to
|
|
102
|
+
* {@link DISCORD_API_BASE_URL}.
|
|
103
|
+
*/
|
|
104
|
+
apiBaseUrl?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Passphrase for encrypting the stored `botToken` at rest (AES-256-GCM).
|
|
107
|
+
* Defaults to the `MASTRA_ENCRYPTION_KEY` env var. When unset, the token is
|
|
108
|
+
* stored in plaintext (fine for the in-memory dev store; set a key for any
|
|
109
|
+
* persistent backend).
|
|
110
|
+
*/
|
|
111
|
+
encryptionKey?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Permissions bitfield requested in the invite URL. Overrides
|
|
114
|
+
* {@link DEFAULT_INVITE_PERMISSIONS}. Accepts a `bigint` or a decimal string.
|
|
115
|
+
*/
|
|
116
|
+
permissions?: bigint | string;
|
|
117
|
+
/**
|
|
118
|
+
* Default slash commands registered for every connected agent (a per-agent
|
|
119
|
+
* list can override via {@link DiscordConnectOptions.commands}). Defaults to
|
|
120
|
+
* the conventional `/help` seed.
|
|
121
|
+
*/
|
|
122
|
+
commands?: DiscordCommandInput[];
|
|
123
|
+
/**
|
|
124
|
+
* Where slash commands are registered:
|
|
125
|
+
* - `'guild'` (default) — per-guild (`PUT …/guilds/{id}/commands`), updates
|
|
126
|
+
* instantly; registered on first-seen guild, keyed by a per-guild hash.
|
|
127
|
+
* - `'global'` — app-wide (`PUT …/commands`), eventually consistent; registered
|
|
128
|
+
* once regardless of guild.
|
|
129
|
+
*
|
|
130
|
+
* @default 'guild'
|
|
131
|
+
*/
|
|
132
|
+
commandScope?: 'guild' | 'global';
|
|
133
|
+
/**
|
|
134
|
+
* Keep the serverless invocation alive while the agent stream runs after the
|
|
135
|
+
* interaction is acked (Vercel/AWS Lambda). See `ChannelConfig.waitUntil`.
|
|
136
|
+
*/
|
|
137
|
+
waitUntil?: WaitUntilFn;
|
|
138
|
+
/**
|
|
139
|
+
* Start the Gateway WebSocket (core-owned) so the bot receives DMs, @mentions,
|
|
140
|
+
* and reactions in addition to slash commands. Set `false` for interactions-
|
|
141
|
+
* only serverless deployments. Forwarded to the adapter entry as `gateway`.
|
|
142
|
+
*
|
|
143
|
+
* @default true
|
|
144
|
+
*/
|
|
145
|
+
gateway?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Role IDs (in addition to direct user mentions) that trigger mention
|
|
148
|
+
* handlers. Forwarded to the adapter (`DISCORD_MENTION_ROLE_IDS` env fallback).
|
|
149
|
+
*/
|
|
150
|
+
mentionRoleIds?: string[];
|
|
151
|
+
/**
|
|
152
|
+
* Return interaction response flags (e.g. ephemeral, flag 64) for the initial
|
|
153
|
+
* deferred slash-command response. Flags are locked at defer time, so this
|
|
154
|
+
* fires on the deferred ACK, not the followup. Forwarded to the adapter.
|
|
155
|
+
*/
|
|
156
|
+
interactionFlags?: DiscordAdapterConfig$1['interactionFlags'];
|
|
157
|
+
/** Logger forwarded to the underlying `DiscordAdapter` for internal error reporting. */
|
|
158
|
+
logger?: DiscordAdapterConfig$1['logger'];
|
|
159
|
+
/**
|
|
160
|
+
* Stream agent text to Discord as it generates, via the adapter's post-and-edit
|
|
161
|
+
* (`editMessage`) loop. Discord has no native token streaming, so this
|
|
162
|
+
* chunk-edits the interaction followup (throttle via `updateIntervalMs`).
|
|
163
|
+
*
|
|
164
|
+
* @default true
|
|
165
|
+
*/
|
|
166
|
+
streaming?: StreamingConfig;
|
|
167
|
+
/**
|
|
168
|
+
* Keep a typing indicator alive during generation. Set `false` to disable.
|
|
169
|
+
*
|
|
170
|
+
* @default true
|
|
171
|
+
*/
|
|
172
|
+
typingStatus?: boolean;
|
|
173
|
+
/** Override built-in event handlers. Forwarded to `AgentChannels`. */
|
|
174
|
+
handlers?: ChannelHandlers;
|
|
175
|
+
/** Which media types to send inline to the model. See `ChannelConfig.inlineMedia`. */
|
|
176
|
+
inlineMedia?: ChannelConfig['inlineMedia'];
|
|
177
|
+
/** Promote URLs in message text to file parts. See `ChannelConfig.inlineLinks`. */
|
|
178
|
+
inlineLinks?: ChannelConfig['inlineLinks'];
|
|
179
|
+
/** State adapter for deduplication, locking, and subscriptions. See `ChannelConfig.state`. */
|
|
180
|
+
state?: ChannelConfig['state'];
|
|
181
|
+
/** Fetch recent thread messages when the agent joins mid-conversation. See `ChannelConfig.threadContext`. */
|
|
182
|
+
threadContext?: ChannelConfig['threadContext'];
|
|
183
|
+
/** Additional options passed directly to the Chat SDK. See `ChannelConfig.chatOptions`. */
|
|
184
|
+
chatOptions?: ChannelConfig['chatOptions'];
|
|
185
|
+
/** Resolve the memory `resourceId` before a channel thread is created. See `ChannelConfig.resolveResourceId`. */
|
|
186
|
+
resolveResourceId?: ChannelConfig['resolveResourceId'];
|
|
187
|
+
/** Resolve `waitUntil` from the request's Hono `Context`. See `ChannelConfig.resolveWaitUntil`. */
|
|
188
|
+
resolveWaitUntil?: ChannelConfig['resolveWaitUntil'];
|
|
189
|
+
/** CORS configuration for the generated interactions route. */
|
|
190
|
+
cors?: ChannelAdapterConfig['cors'];
|
|
191
|
+
/** Override how errors are rendered in Discord messages. See `ChannelAdapterConfig.formatError`. */
|
|
192
|
+
formatError?: ChannelAdapterConfig['formatError'];
|
|
193
|
+
/**
|
|
194
|
+
* How tool calls are rendered in the reply. Discord has native embeds +
|
|
195
|
+
* action-row buttons, so this defaults to `'cards'` (unlike Telegram's
|
|
196
|
+
* `'text'`). See `ChannelAdapterConfig.toolDisplay`.
|
|
197
|
+
*
|
|
198
|
+
* @default 'cards'
|
|
199
|
+
*/
|
|
200
|
+
toolDisplay?: ChannelAdapterConfig['toolDisplay'];
|
|
201
|
+
/**
|
|
202
|
+
* Whether to expose channel reaction tools (`add_reaction`/`remove_reaction`)
|
|
203
|
+
* to the agent. Set `false` for models without function calling. See `ChannelConfig.tools`.
|
|
204
|
+
*
|
|
205
|
+
* @default true
|
|
206
|
+
*/
|
|
207
|
+
tools?: ChannelConfig['tools'];
|
|
208
|
+
/** Called after an agent successfully connects and the installation is persisted. */
|
|
209
|
+
onInstall?: (installation: DiscordInstallation) => void | Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/** Options accepted by {@link DiscordProvider.connect}. */
|
|
212
|
+
interface DiscordConnectOptions {
|
|
213
|
+
/**
|
|
214
|
+
* The guild to install into. When the bot is **already** a member of this
|
|
215
|
+
* guild, connect binds immediately (`{ type: 'immediate' }`) and registers its
|
|
216
|
+
* commands; otherwise the operator is sent the invite URL and the guild is
|
|
217
|
+
* confirmed lazily off the first inbound interaction's authoritative `guild_id`.
|
|
218
|
+
*/
|
|
219
|
+
guildId?: string;
|
|
220
|
+
/** Display name for this installation. Defaults to the application's name. */
|
|
221
|
+
name?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Slash commands to register for this agent. Overrides
|
|
224
|
+
* {@link DiscordProviderConfig.commands}. Defaults to the `/help` seed.
|
|
225
|
+
*/
|
|
226
|
+
commands?: DiscordCommandInput[];
|
|
227
|
+
/** Permissions bitfield override for this invite (bigint or decimal string). */
|
|
228
|
+
permissions?: bigint | string;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* A per-agent Discord installation. Unlike Telegram (one token = one row),
|
|
232
|
+
* Discord installs are keyed by agent and track the set of `guildIds` the bot is
|
|
233
|
+
* live in. **No secrets live here** — the bot token lives once in the app config
|
|
234
|
+
* ({@link DiscordAppConfig}); this row only holds routing + per-guild command
|
|
235
|
+
* versions. Persisted through {@link DiscordInstallStore}.
|
|
236
|
+
*/
|
|
237
|
+
interface DiscordInstallation {
|
|
238
|
+
/** Stable installation id. */
|
|
239
|
+
id: string;
|
|
240
|
+
/** The agent this installation is bound to. */
|
|
241
|
+
agentId: string;
|
|
242
|
+
/**
|
|
243
|
+
* Opaque id embedded in the interactions route path
|
|
244
|
+
* (`/discord/events/:webhookId`). Used to resolve the install on inbound POSTs.
|
|
245
|
+
*/
|
|
246
|
+
webhookId: string;
|
|
247
|
+
/**
|
|
248
|
+
* `pending` — invite issued, no guild confirmed yet.
|
|
249
|
+
* `active` — the bot is live in at least one guild (or bound to a known guild).
|
|
250
|
+
*/
|
|
251
|
+
status: 'active' | 'pending';
|
|
252
|
+
/** Guilds this agent's bot is confirmed live in (tenancy keys). */
|
|
253
|
+
guildIds: string[];
|
|
254
|
+
/** Display name (application name or an operator-supplied override). */
|
|
255
|
+
displayName?: string;
|
|
256
|
+
/** Normalized slash commands to register for this agent (resolved at connect). */
|
|
257
|
+
commands?: DiscordCommand[];
|
|
258
|
+
/**
|
|
259
|
+
* Registered-command version hash, keyed by `guildId` (guild scope) or the
|
|
260
|
+
* literal `'global'` (global scope). Used to skip re-`PUT`ting commands when
|
|
261
|
+
* unchanged (200/day/guild rate limit).
|
|
262
|
+
*/
|
|
263
|
+
commandVersions?: Record<string, string>;
|
|
264
|
+
/** When the installation was created. */
|
|
265
|
+
installedAt: Date;
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
//#region src/discord-provider.d.ts
|
|
269
|
+
/**
|
|
270
|
+
* Resolve the per-adapter config the provider applies to the Discord entry in
|
|
271
|
+
* `AgentChannels.adapters`. Discord has native embeds + action-row buttons, so
|
|
272
|
+
* `toolDisplay` defaults to `'cards'` (unlike Telegram's `'text'`); `streaming`
|
|
273
|
+
* post-and-edits the interaction followup; `gateway` (default `true`) makes core
|
|
274
|
+
* own the DM/mention Gateway reconnection loop.
|
|
275
|
+
*/
|
|
276
|
+
declare function resolveDiscordAdapterConfig(config: Pick<DiscordProviderConfig, 'streaming' | 'typingStatus' | 'toolDisplay' | 'gateway'>): {
|
|
277
|
+
streaming: StreamingConfig;
|
|
278
|
+
typingStatus: boolean;
|
|
279
|
+
toolDisplay: ChannelAdapterConfig['toolDisplay'];
|
|
280
|
+
gateway: boolean;
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Discord channel provider for Mastra — a {@link ChannelProvider} over
|
|
284
|
+
* `@chat-adapter/discord`. The adapter handles the protocol (Ed25519 request
|
|
285
|
+
* verification, PING/PONG, deferrals, embeds/buttons, the Gateway bridge,
|
|
286
|
+
* post-and-edit streaming); this provider adds the lifecycle layer.
|
|
287
|
+
*
|
|
288
|
+
* **One app, many guilds.** Discord has no programmatic app creation, so a
|
|
289
|
+
* single application's credentials (bot token / public key / application id) are
|
|
290
|
+
* stored **once** and reused across every guild. "Installing" is a bot invite —
|
|
291
|
+
* an OAuth2 authorize URL — not a token exchange, so `connect()` returns the
|
|
292
|
+
* `oauth` variant. A guild is confirmed either eagerly (the bot is already a
|
|
293
|
+
* member) or lazily off the first inbound interaction's authoritative `guild_id`
|
|
294
|
+
* (see {@link activateGuild}).
|
|
295
|
+
*
|
|
296
|
+
* **Gateway is core's job.** The mounted interactions route only ever receives
|
|
297
|
+
* HTTP Interactions (PING, slash commands, buttons). DMs / @mentions / reactions
|
|
298
|
+
* arrive over the Gateway WebSocket, whose reconnection loop core owns: setting
|
|
299
|
+
* `gateway: true` (default) on the adapter entry is enough — the wrapper never
|
|
300
|
+
* calls `startGatewayListener` or runs a reconnect loop.
|
|
301
|
+
*
|
|
302
|
+
* Implemented (issues `mastra-discord-13x.2` + `.3`): the app-config +
|
|
303
|
+
* guild-keyed install store, `connect()`/`disconnect()`, OAuth2 invite-URL
|
|
304
|
+
* generation, the interactions route (raw-body delegation), and adapter/Gateway
|
|
305
|
+
* wiring. Command registration lands in `mastra-discord-13x.4`.
|
|
306
|
+
*/
|
|
307
|
+
declare class DiscordProvider implements ChannelProvider {
|
|
308
|
+
#private;
|
|
309
|
+
readonly id = "discord";
|
|
310
|
+
constructor(config?: DiscordProviderConfig);
|
|
311
|
+
/**
|
|
312
|
+
* Called by Mastra when this channel is registered.
|
|
313
|
+
* @internal
|
|
314
|
+
*/
|
|
315
|
+
__attach(mastra: Mastra): void;
|
|
316
|
+
/**
|
|
317
|
+
* The interactions route: a single POST endpoint keyed by an opaque
|
|
318
|
+
* `webhookId`. The handler passes the **RAW** request bytes straight to
|
|
319
|
+
* `adapter.handleWebhook` — Ed25519 verification + PING/PONG live in the
|
|
320
|
+
* adapter, and any middleware that parsed/re-serialized the body would break
|
|
321
|
+
* every signature. `requiresAuth: false` (Discord authenticates via Ed25519,
|
|
322
|
+
* not a bearer token). Auto-initializes on first hit (mirrors `@mastra/slack`).
|
|
323
|
+
*/
|
|
324
|
+
getRoutes(): ApiRoute[];
|
|
325
|
+
/** Discovery metadata for the editor UI. */
|
|
326
|
+
getInfo(): ChannelPlatformInfo;
|
|
327
|
+
/**
|
|
328
|
+
* Restore state from storage: mark the provider configured when an app config
|
|
329
|
+
* or any active installation exists, and rebuild an adapter + `AgentChannels`
|
|
330
|
+
* per active install so its Gateway loop (core-owned) starts. Idempotent.
|
|
331
|
+
*/
|
|
332
|
+
initialize(): Promise<void>;
|
|
333
|
+
/**
|
|
334
|
+
* Provide or clear the app credentials at runtime. An object merges/overrides
|
|
335
|
+
* `botToken` / `publicKey` / `applicationId` (persisted on the next `connect`);
|
|
336
|
+
* `null` clears the stored app config.
|
|
337
|
+
*/
|
|
338
|
+
configure(credentials: Partial<DiscordAppConfig> | null): Promise<void>;
|
|
339
|
+
/**
|
|
340
|
+
* Connect an agent to Discord.
|
|
341
|
+
*
|
|
342
|
+
* - Ensures the app config exists (stored, or supplied via provider config /
|
|
343
|
+
* `DISCORD_*` env and persisted once, after validation). Throws if none —
|
|
344
|
+
* Discord apps are created in the Developer Portal, not programmatically.
|
|
345
|
+
* - Validates the bot token via `GET /applications/@me`.
|
|
346
|
+
* - If `options.guildId` is given **and the bot is already in that guild**,
|
|
347
|
+
* binds immediately (`{ type: 'immediate' }`).
|
|
348
|
+
* - Otherwise persists a pending install and returns the OAuth2 bot-invite URL
|
|
349
|
+
* (`{ type: 'oauth', authorizationUrl }`); the guild activates lazily on the
|
|
350
|
+
* first interaction.
|
|
351
|
+
*/
|
|
352
|
+
connect(agentId: string, options?: DiscordConnectOptions): Promise<ChannelConnectResult>;
|
|
353
|
+
/**
|
|
354
|
+
* Confirm a guild off an inbound interaction's authoritative `guild_id` and
|
|
355
|
+
* mark the installation active (lazy activation). Called by the interactions
|
|
356
|
+
* route on first use. Returns the updated installation, or `null` if the
|
|
357
|
+
* `webhookId` is unknown.
|
|
358
|
+
*/
|
|
359
|
+
activateGuild(webhookId: string, guildId: string): Promise<DiscordInstallation | null>;
|
|
360
|
+
/**
|
|
361
|
+
* Disconnect an agent from Discord: remove its installation row and drop the
|
|
362
|
+
* adapter entry.
|
|
363
|
+
*
|
|
364
|
+
* **Limitation:** the Gateway loop is owned by core (it calls
|
|
365
|
+
* `startGatewayListener` itself; there is no `stopGatewayListener`), so
|
|
366
|
+
* disconnect cannot kill an in-flight gateway window — it lapses at the next
|
|
367
|
+
* duration boundary. Contrast Telegram's clean `stopPolling()`.
|
|
368
|
+
*/
|
|
369
|
+
disconnect(agentId: string): Promise<void>;
|
|
370
|
+
/** List installations (public info only — no secrets). */
|
|
371
|
+
listInstallations(): Promise<ChannelInstallationInfo[]>;
|
|
372
|
+
/** The full installation for an agent (no secrets live on it), or `null`. */
|
|
373
|
+
getInstallation(agentId: string): Promise<DiscordInstallation | null>;
|
|
374
|
+
/** Whether the Discord app is configured (credentials resolvable). */
|
|
375
|
+
isConfigured(): boolean;
|
|
376
|
+
/** The live `DiscordAdapter` for an installation id, if one is active. */
|
|
377
|
+
getAdapter(installationId: string): DiscordAdapter$1 | undefined;
|
|
378
|
+
}
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/install-store.d.ts
|
|
381
|
+
/** Platform identifier used for every stored record, config, and route. */
|
|
382
|
+
declare const PLATFORM = "discord";
|
|
383
|
+
/**
|
|
384
|
+
* Two-tier persistence for Discord, layered over the platform-agnostic
|
|
385
|
+
* `ChannelsStorage` (the same store `@mastra/slack` / `@mastra/telegram` use).
|
|
386
|
+
*
|
|
387
|
+
* - **App tier** (`saveConfig`/`getConfig`) — the one application's
|
|
388
|
+
* `botToken` + `publicKey` + `applicationId`, stored **once** (one app, many
|
|
389
|
+
* guilds). `botToken` is AES-256-GCM encrypted at rest when an `encryptionKey`
|
|
390
|
+
* is supplied.
|
|
391
|
+
* - **Install tier** (`getInstallationByAgent`/`…ByWebhookId`/`saveInstallation`)
|
|
392
|
+
* — one row per agent, keyed by agent, tracking the `guildIds` the bot is live
|
|
393
|
+
* in. Secrets are **not** duplicated per row.
|
|
394
|
+
*/
|
|
395
|
+
declare class DiscordInstallStore {
|
|
396
|
+
#private;
|
|
397
|
+
private readonly storage;
|
|
398
|
+
private readonly encryptionKey?;
|
|
399
|
+
constructor(storage: ChannelsStorage, encryptionKey?: string | undefined);
|
|
400
|
+
/** The stored app config, if any (bot token decrypted). */
|
|
401
|
+
getAppConfig(): Promise<DiscordAppConfig | null>;
|
|
402
|
+
/** Persist the app config once (bot token encrypted at rest when keyed). */
|
|
403
|
+
saveAppConfig(app: DiscordAppConfig): Promise<void>;
|
|
404
|
+
/** Remove the stored app config. */
|
|
405
|
+
deleteAppConfig(): Promise<void>;
|
|
406
|
+
/** The installation for an agent, if any. */
|
|
407
|
+
getByAgent(agentId: string): Promise<DiscordInstallation | null>;
|
|
408
|
+
/** Look up an installation by the routing id in its interactions-route path. */
|
|
409
|
+
getByWebhookId(webhookId: string): Promise<DiscordInstallation | null>;
|
|
410
|
+
/**
|
|
411
|
+
* Find the installation that owns a guild (tenancy lookup by `guildId`). Used
|
|
412
|
+
* when the routing key is a guild rather than a webhook id.
|
|
413
|
+
*
|
|
414
|
+
* `guildIds` is per-agent and nothing enforces exclusivity, so two agents can
|
|
415
|
+
* legitimately be installed into the same guild. Returning the first match
|
|
416
|
+
* would make routing depend on storage row order — the same interaction could
|
|
417
|
+
* reach a different agent on the next lookup. Instead the **oldest** install
|
|
418
|
+
* wins (ties broken by id), which is stable across restarts and storage
|
|
419
|
+
* backends, and the ambiguity is logged once so an operator can see it.
|
|
420
|
+
*
|
|
421
|
+
* `ChannelsStorage` has no query-by-data-field, so this necessarily lists the
|
|
422
|
+
* platform's installations; it is not on the interactions hot path, which
|
|
423
|
+
* routes by `webhookId`.
|
|
424
|
+
*/
|
|
425
|
+
getByGuildId(guildId: string): Promise<DiscordInstallation | null>;
|
|
426
|
+
/** Insert or replace an installation. */
|
|
427
|
+
save(installation: DiscordInstallation): Promise<void>;
|
|
428
|
+
/** All Discord installations (active and pending). */
|
|
429
|
+
list(): Promise<DiscordInstallation[]>;
|
|
430
|
+
/** Remove an agent's installation, if present. */
|
|
431
|
+
deleteByAgent(agentId: string): Promise<void>;
|
|
432
|
+
}
|
|
433
|
+
/** Project an installation to its public, secret-free info for the editor UI. */
|
|
434
|
+
declare function toInstallationInfo(install: DiscordInstallation): ChannelInstallationInfo;
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/discord-client.d.ts
|
|
437
|
+
/**
|
|
438
|
+
* The subset of Discord's application object the control plane reads back from
|
|
439
|
+
* `GET /applications/@me`.
|
|
440
|
+
* @see https://discord.com/developers/docs/resources/application#application-object
|
|
441
|
+
*/
|
|
442
|
+
interface DiscordApplication {
|
|
443
|
+
/** The application (client) id. */
|
|
444
|
+
id: string;
|
|
445
|
+
/** The application's name. */
|
|
446
|
+
name: string;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Call the Discord REST API with Bot-token auth. `method` is required; supplying
|
|
450
|
+
* `body` adds the JSON `content-type` header and serializes the payload. Returns
|
|
451
|
+
* the raw {@link Response} so callers can classify the status themselves rather
|
|
452
|
+
* than having every non-2xx collapse into a thrown error.
|
|
453
|
+
*
|
|
454
|
+
* This is the **control plane only** — validating the app and reading guild
|
|
455
|
+
* membership. Sending replies uses the interaction token inside the adapter,
|
|
456
|
+
* never this bot-token path.
|
|
457
|
+
*/
|
|
458
|
+
declare function discordRequest(botToken: string, method: string, path: string, apiBaseUrl?: string, body?: unknown): Promise<Response>;
|
|
459
|
+
/**
|
|
460
|
+
* Validate the app's bot token and resolve the application identity via
|
|
461
|
+
* `GET /applications/@me` (Bot auth). Throws if the token is rejected.
|
|
462
|
+
*
|
|
463
|
+
* @see https://discord.com/developers/docs/resources/application#get-current-application
|
|
464
|
+
*/
|
|
465
|
+
declare function validateApp(botToken: string, apiBaseUrl?: string): Promise<DiscordApplication>;
|
|
466
|
+
/**
|
|
467
|
+
* Whether the bot is already a member of a guild. `GET /guilds/{id}` (Bot auth)
|
|
468
|
+
* returns `200` only when the bot is in the guild. Absence is `404` (`10004
|
|
469
|
+
* Unknown Guild`) — Discord standardized on 404 rather than 403 so the response
|
|
470
|
+
* can't confirm a guild exists to a caller that lacks access — with legacy
|
|
471
|
+
* deployments still answering `403` (`50001 Missing Access`).
|
|
472
|
+
*
|
|
473
|
+
* Any **other** failure (`401`, `429`, `5xx`) is a transient or auth problem,
|
|
474
|
+
* not evidence of absence, and throws. Collapsing those into `false` would send
|
|
475
|
+
* a caller whose bot *is* in the guild down the invite path on a rate limit.
|
|
476
|
+
*
|
|
477
|
+
* @see https://discord.com/developers/docs/resources/guild#get-guild
|
|
478
|
+
*/
|
|
479
|
+
declare function guildHealthCheck(botToken: string, guildId: string, apiBaseUrl?: string): Promise<boolean>;
|
|
480
|
+
/**
|
|
481
|
+
* Bulk-overwrite a **guild's** slash commands (`PUT …/guilds/{guildId}/commands`).
|
|
482
|
+
* Guild-scoped commands update **instantly**. Register on first-seen guild;
|
|
483
|
+
* callers skip the call when the command hash is unchanged (200 creates/day/guild).
|
|
484
|
+
*
|
|
485
|
+
* @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands
|
|
486
|
+
*/
|
|
487
|
+
declare function registerGuildCommands(botToken: string, applicationId: string, guildId: string, commands: readonly DiscordCommand[], apiBaseUrl?: string): Promise<void>;
|
|
488
|
+
/**
|
|
489
|
+
* Bulk-overwrite the app's **global** slash commands (`PUT …/commands`).
|
|
490
|
+
* Eventually consistent (propagation is not instant — don't quote a fixed
|
|
491
|
+
* number). Opt-in via `commandScope: 'global'`.
|
|
492
|
+
*
|
|
493
|
+
* @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands
|
|
494
|
+
*/
|
|
495
|
+
declare function registerGlobalCommands(botToken: string, applicationId: string, commands: readonly DiscordCommand[], apiBaseUrl?: string): Promise<void>;
|
|
496
|
+
/** Inputs for {@link buildInviteUrl}. */
|
|
497
|
+
interface BuildInviteUrlOptions {
|
|
498
|
+
/** The application (client) id — becomes `client_id`. */
|
|
499
|
+
applicationId: string;
|
|
500
|
+
/** Permissions bitfield (bigint or decimal string). Defaults to {@link DEFAULT_INVITE_PERMISSIONS}. */
|
|
501
|
+
permissions?: bigint | string;
|
|
502
|
+
/** OAuth2 scopes. Defaults to {@link DEFAULT_INVITE_SCOPES} (`bot applications.commands`). */
|
|
503
|
+
scopes?: readonly string[];
|
|
504
|
+
/**
|
|
505
|
+
* Preselect this guild in the authorize screen and lock the picker, so the
|
|
506
|
+
* operator can't authorize a different guild than the one `connect()` recorded.
|
|
507
|
+
*/
|
|
508
|
+
guildId?: string;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Build the OAuth2 bot-invite URL. This *is* the Discord "install" flow: the
|
|
512
|
+
* operator opens it, picks a guild, and authorizes — there is no token exchange.
|
|
513
|
+
* `scope=bot applications.commands` + a `permissions` bitfield.
|
|
514
|
+
*
|
|
515
|
+
* @see https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow
|
|
516
|
+
*/
|
|
517
|
+
declare function buildInviteUrl(options: BuildInviteUrlOptions): string;
|
|
518
|
+
//#endregion
|
|
519
|
+
//#region src/commands.d.ts
|
|
520
|
+
/**
|
|
521
|
+
* Conventional command seed registered when a connect provides none. Discord
|
|
522
|
+
* surfaces `/help` in the command picker; the built-in DM/mention handling
|
|
523
|
+
* covers everything else, so the seed is intentionally tiny.
|
|
524
|
+
* @see https://discord.com/developers/docs/interactions/application-commands
|
|
525
|
+
*/
|
|
526
|
+
declare const DEFAULT_COMMANDS: readonly DiscordCommandInput[];
|
|
527
|
+
/**
|
|
528
|
+
* Map user-supplied commands to Discord `CHAT_INPUT` command shapes, enforcing
|
|
529
|
+
* the API constraints: `name` is lowercased, stripped of a leading slash,
|
|
530
|
+
* reduced to `[a-z0-9_-]`, and clamped to 1-32 chars; `description` defaults to
|
|
531
|
+
* `Run /<name>` and is clamped to 1-100 chars. Empty or duplicate names are
|
|
532
|
+
* dropped.
|
|
533
|
+
*
|
|
534
|
+
* @see https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-naming
|
|
535
|
+
*/
|
|
536
|
+
declare function normalizeCommands(raw: readonly DiscordCommandInput[] | undefined): DiscordCommand[];
|
|
537
|
+
/**
|
|
538
|
+
* Stable content hash of a normalized command list, used to skip re-`PUT`ting a
|
|
539
|
+
* guild/global command set that hasn't changed (Discord allows only 200
|
|
540
|
+
* application-command creates per day, per guild). Order-independent: commands
|
|
541
|
+
* are sorted by name before hashing.
|
|
542
|
+
*/
|
|
543
|
+
declare function hashCommands(commands: readonly DiscordCommand[]): string;
|
|
544
|
+
//#endregion
|
|
545
|
+
//#region src/crypto.d.ts
|
|
546
|
+
/** Whether a stored value was produced by {@link encrypt}. */
|
|
547
|
+
declare function isEncrypted(value: string): boolean;
|
|
548
|
+
/** Encrypt a UTF-8 string with a per-value random salt + IV. */
|
|
549
|
+
declare function encrypt(plaintext: string, passphrase: string): string;
|
|
550
|
+
/** Decrypt a value from {@link encrypt}. Plaintext (unprefixed) is returned unchanged. */
|
|
551
|
+
declare function decrypt(value: string, passphrase: string): string;
|
|
552
|
+
//#endregion
|
|
553
|
+
export { type BuildInviteUrlOptions, DEFAULT_COMMANDS, DEFAULT_INVITE_PERMISSIONS, DEFAULT_INVITE_SCOPES, DISCORD_API_BASE_URL, DISCORD_OAUTH_AUTHORIZE_URL, DISCORD_PERMISSIONS, DiscordAdapter, type DiscordAdapterConfig, type DiscordAppConfig, type DiscordApplication, type DiscordCommand, type DiscordCommandInput, type DiscordConnectOptions, DiscordInstallStore, type DiscordInstallation, DiscordProvider, type DiscordProviderConfig, PLATFORM, buildInviteUrl, createDiscordAdapter, decrypt, discordRequest, encrypt, guildHealthCheck, hashCommands, isEncrypted, normalizeCommands, registerGlobalCommands, registerGuildCommands, resolveDiscordAdapterConfig, toInstallationInfo, validateApp };
|
|
554
|
+
//# sourceMappingURL=index.d.ts.map
|