@went.tf/discord-bot-framework 2.3.0 → 2.5.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 +116 -2
- package/build/.tsbuildinfo +1 -1
- package/build/client/create-shard-manager.d.ts +13 -0
- package/build/client/create-shard-manager.d.ts.map +1 -1
- package/build/client/create-shard-manager.js +26 -1
- package/build/client/create-shard-manager.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/webhook/create-webhook-interaction-responder.d.ts +33 -0
- package/build/webhook/create-webhook-interaction-responder.d.ts.map +1 -0
- package/build/webhook/create-webhook-interaction-responder.js +25 -0
- package/build/webhook/create-webhook-interaction-responder.js.map +1 -0
- package/build/webhook/handle-webhook-interaction-request.d.ts +41 -0
- package/build/webhook/handle-webhook-interaction-request.d.ts.map +1 -0
- package/build/webhook/handle-webhook-interaction-request.js +45 -0
- package/build/webhook/handle-webhook-interaction-request.js.map +1 -0
- package/build/webhook/index.d.ts +4 -0
- package/build/webhook/index.d.ts.map +1 -0
- package/build/webhook/index.js +8 -0
- package/build/webhook/index.js.map +1 -0
- package/build/webhook/verify-interaction-request.d.ts +22 -0
- package/build/webhook/verify-interaction-request.d.ts.map +1 -0
- package/build/webhook/verify-interaction-request.js +37 -0
- package/build/webhook/verify-interaction-request.js.map +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -152,15 +152,34 @@ If other code (e.g. a locale-file type, or a helper building a command
|
|
|
152
152
|
mention) needs the literal name/id union as a *type*, derive it from the
|
|
153
153
|
registry you already built instead of hand-writing a parallel
|
|
154
154
|
`const enum CommandName { ... }` — that enum is exactly the duplication the
|
|
155
|
-
registry's `const` type inference exists to avoid
|
|
155
|
+
registry's `const` type inference exists to avoid. **This only works if each
|
|
156
|
+
command's own `name` stays a literal type up to the point it's passed into
|
|
157
|
+
`createChatInputCommandRegistry`** — a real gotcha, not a hypothetical one:
|
|
158
|
+
a plain, unannotated `const pingCommand = { name: 'ping', handle: ... };`
|
|
159
|
+
widens `name` to `string` right there (standard TS object-literal-property
|
|
160
|
+
widening, the same reason `const x = { n: 1 }; x.n` is `number` not `1`),
|
|
161
|
+
*before* it ever reaches the registry call, so `RegistryName` would silently
|
|
162
|
+
resolve to `string` too. `satisfies BotChatInputCommand` does **not** fix
|
|
163
|
+
this either — it only checks compatibility, it doesn't request a literal.
|
|
164
|
+
Pin each command's own name as an explicit generic argument instead (the
|
|
165
|
+
same pattern this package's own registry tests use):
|
|
156
166
|
|
|
157
167
|
```ts
|
|
158
|
-
import { RegistryName } from '@went.tf/discord-bot-framework/interactions';
|
|
168
|
+
import { NamedChatInputCommand, RegistryName } from '@went.tf/discord-bot-framework/interactions';
|
|
169
|
+
|
|
170
|
+
const pingCommand: NamedChatInputCommand<Ctx, 'ping'> = { name: 'ping', handle: (i) => i.reply('pong') };
|
|
171
|
+
const searchCommand: NamedChatInputCommand<Ctx, 'search'> = { name: 'search', handle: (i) => i.reply('...') };
|
|
159
172
|
|
|
160
173
|
const chatInputCommandRegistry = createChatInputCommandRegistry([pingCommand, searchCommand]);
|
|
161
174
|
type ChatInputCommandName = RegistryName<typeof chatInputCommandRegistry>; // 'ping' | 'search'
|
|
162
175
|
```
|
|
163
176
|
|
|
177
|
+
(Object literals passed directly inline into the array — not through an
|
|
178
|
+
intermediate `const` — infer literally on their own, since TS 5's `const`
|
|
179
|
+
type parameter modifier on `createChatInputCommandRegistry` requests literal
|
|
180
|
+
inference for its argument; the explicit-generic form above is what you need
|
|
181
|
+
once each command lives in its own file, which is the normal case.)
|
|
182
|
+
|
|
164
183
|
A command's name should only ever be written down in two places: its
|
|
165
184
|
`commands.json` entry and its own registry object's `name` field — nothing
|
|
166
185
|
else should define it again, only reference the same string (or the derived
|
|
@@ -433,6 +452,101 @@ const manager = await createShardManager({
|
|
|
433
452
|
});
|
|
434
453
|
```
|
|
435
454
|
|
|
455
|
+
**Graceful deploys with `gracefulRespawnSignal`:** a plain process restart
|
|
456
|
+
kills every shard at once, then respawns them sequentially — Discord's
|
|
457
|
+
identify rate limit forces roughly one shard every 5s, so a bot with 20+
|
|
458
|
+
shards can be fully down for minutes on every deploy. Passing
|
|
459
|
+
`gracefulRespawnSignal: 'SIGUSR2'` registers a handler that calls the
|
|
460
|
+
manager's `respawnAll()` instead, which kills/respawns shards **one at a
|
|
461
|
+
time** on the same long-lived manager process — at most one shard is briefly
|
|
462
|
+
offline (a few seconds) at any point, not the whole fleet:
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
const manager = await createShardManager({
|
|
466
|
+
token, botScriptPath, logger,
|
|
467
|
+
beforeSpawn: () => startupCommandsUpdate(logger),
|
|
468
|
+
gracefulRespawnSignal: 'SIGUSR2',
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Your deploy script then sends that signal to the running manager process
|
|
473
|
+
(e.g. `kill -s USR2 "$(pm2 pid my-bot)"` — note dash's `kill` builtin
|
|
474
|
+
rejects bash's `-SIGUSR2` form, use `-s USR2`) instead of restarting it,
|
|
475
|
+
whenever only shard-side code changed. Since a forked shard process re-reads
|
|
476
|
+
its script fresh off disk, this picks up ordinary code changes fine — but
|
|
477
|
+
NOT changes to the top-level process that calls `createShardManager` itself
|
|
478
|
+
(this file, its `beforeSpawn`, or anything it holds in memory), since that
|
|
479
|
+
process is never restarted. Fall back to a real restart for those, and for
|
|
480
|
+
anything that needs `beforeSpawn`'s one-time setup (e.g. slash command
|
|
481
|
+
registration) to re-run.
|
|
482
|
+
|
|
483
|
+
### `@went.tf/discord-bot-framework/webhook` (experimental)
|
|
484
|
+
|
|
485
|
+
> **Experimental, unvalidated against a real bot.** Unlike every other
|
|
486
|
+
> subpath in this package, `./webhook` hasn't yet been proven against a real
|
|
487
|
+
> migration — see the scope note at the end of this section and CLAUDE.md's
|
|
488
|
+
> `./webhook` design-decision entry. The API may change in a minor/patch
|
|
489
|
+
> release until that validation happens, despite semver-major otherwise being
|
|
490
|
+
> reserved for breaking changes in this package.
|
|
491
|
+
|
|
492
|
+
An alternate, independent transport alongside `./client`'s gateway-based
|
|
493
|
+
`createBotClient`/`createShardManager`, for bots that want to receive
|
|
494
|
+
interactions over an HTTP Interactions Endpoint instead of holding a
|
|
495
|
+
WebSocket open. As with `createBotClient`/`createShardManager`, this doesn't
|
|
496
|
+
unify with the gateway path behind one entry point — pick one transport per
|
|
497
|
+
bot and use its functions directly.
|
|
498
|
+
|
|
499
|
+
`verifyInteractionRequest` checks a request's `X-Signature-Ed25519`/
|
|
500
|
+
`X-Signature-Timestamp` headers against your application's public key, using
|
|
501
|
+
Node's native `crypto` module (no `tweetnacl`/`discord-interactions`
|
|
502
|
+
dependency needed). `handleWebhookInteractionRequest` wraps that plus
|
|
503
|
+
Discord's PING→PONG endpoint-validation handshake around your own handler,
|
|
504
|
+
taking/returning plain data so it can be wired into any HTTP framework (or
|
|
505
|
+
none):
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
import { handleWebhookInteractionRequest } from '@went.tf/discord-bot-framework/webhook';
|
|
509
|
+
import { createServer } from 'node:http';
|
|
510
|
+
|
|
511
|
+
createServer(async (req, res) => {
|
|
512
|
+
const chunks: Buffer[] = [];
|
|
513
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
514
|
+
const rawBody = Buffer.concat(chunks);
|
|
515
|
+
|
|
516
|
+
const { status, body } = await handleWebhookInteractionRequest(
|
|
517
|
+
{
|
|
518
|
+
signature: req.headers['x-signature-ed25519'] as string,
|
|
519
|
+
timestamp: req.headers['x-signature-timestamp'] as string,
|
|
520
|
+
rawBody,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
publicKey: env.DISCORD_PUBLIC_KEY,
|
|
524
|
+
logger,
|
|
525
|
+
onInteraction: (interaction) => handleInteraction(interaction), // bot-side: build an APIInteractionResponse
|
|
526
|
+
},
|
|
527
|
+
);
|
|
528
|
+
res.writeHead(status, { 'Content-Type': 'application/json' }).end(JSON.stringify(body));
|
|
529
|
+
}).listen(3000);
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
`createWebhookInteractionResponder` gives you the REST calls needed *after*
|
|
533
|
+
that initial response — editing a deferred reply, sending a follow-up, or
|
|
534
|
+
deleting the reply — built on `@discordjs/rest` (already a peer dependency):
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
import { createWebhookInteractionResponder } from '@went.tf/discord-bot-framework/webhook';
|
|
538
|
+
|
|
539
|
+
const responder = createWebhookInteractionResponder({ rest, applicationId, interactionToken: interaction.token });
|
|
540
|
+
await responder.editReply({ content: 'Done!' });
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**This is not a drop-in replacement for discord.js's `Interaction#reply()`/
|
|
544
|
+
`editReply()`/`options.get*()` surface.** It's intentionally the thinner,
|
|
545
|
+
already-validated half of that problem (signature verification, the PING
|
|
546
|
+
handshake, and the plain REST calls) — see CLAUDE.md's `./webhook`
|
|
547
|
+
design-decision entry for why a discord.js-`Interaction`-shaped compatibility
|
|
548
|
+
adapter isn't built here yet.
|
|
549
|
+
|
|
436
550
|
### `@went.tf/discord-bot-framework/dev`
|
|
437
551
|
|
|
438
552
|
Live-reloads compiled command/interaction handler *implementations* during
|