shardwire 1.0.0 → 1.2.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 +128 -51
- package/dist/index.d.mts +182 -9
- package/dist/index.d.ts +182 -9
- package/dist/index.js +707 -85
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +709 -85
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
# Shardwire
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/shardwire)
|
|
4
|
+
[](https://www.npmjs.com/package/shardwire)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](./LICENSE)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
Discord-first bridge for streaming bot events to external apps and executing bot actions over one WebSocket connection.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
- replying to interactions
|
|
9
|
-
- sending and editing messages
|
|
10
|
-
- moderation actions like ban, kick, and role changes
|
|
10
|
+
Shardwire is built for a common architecture: your Discord bot runs in one process, while your web app, backend API, worker, or dashboard runs in another.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## Why Shardwire
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
- **Discord-first API**: no generic command bus wiring required.
|
|
15
|
+
- **App-friendly payloads**: receive normalized JSON payloads instead of live `discord.js` objects.
|
|
16
|
+
- **Built-in actions**: send messages, reply to interactions, moderate members, and more from your app process.
|
|
17
|
+
- **Scoped permissions**: restrict each app secret to specific events and actions.
|
|
18
|
+
- **Capability-aware runtime**: apps can inspect what they are allowed to subscribe to and invoke.
|
|
15
19
|
|
|
16
|
-
##
|
|
20
|
+
## Requirements
|
|
17
21
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- **Scoped secrets**: optionally limit which apps can subscribe to which events or invoke which actions
|
|
22
|
+
- Node.js `>=18.18`
|
|
23
|
+
- A Discord bot token (`DISCORD_TOKEN`)
|
|
24
|
+
- At least one shared bridge secret (`SHARDWIRE_SECRET`)
|
|
25
|
+
- Discord gateway intents that match the events you want
|
|
23
26
|
|
|
24
27
|
## Install
|
|
25
28
|
|
|
@@ -29,14 +32,14 @@ npm install shardwire
|
|
|
29
32
|
|
|
30
33
|
## Quick Start
|
|
31
34
|
|
|
32
|
-
### 1
|
|
35
|
+
### 1) Start the bot bridge process
|
|
33
36
|
|
|
34
37
|
```ts
|
|
35
38
|
import { createBotBridge } from "shardwire";
|
|
36
39
|
|
|
37
40
|
const bridge = createBotBridge({
|
|
38
41
|
token: process.env.DISCORD_TOKEN!,
|
|
39
|
-
intents: ["Guilds", "GuildMessages", "MessageContent", "GuildMembers"],
|
|
42
|
+
intents: ["Guilds", "GuildMessages", "GuildMessageReactions", "MessageContent", "GuildMembers"],
|
|
40
43
|
server: {
|
|
41
44
|
port: 3001,
|
|
42
45
|
secrets: [process.env.SHARDWIRE_SECRET!],
|
|
@@ -44,9 +47,10 @@ const bridge = createBotBridge({
|
|
|
44
47
|
});
|
|
45
48
|
|
|
46
49
|
await bridge.ready();
|
|
50
|
+
console.log("Bot bridge listening on ws://127.0.0.1:3001/shardwire");
|
|
47
51
|
```
|
|
48
52
|
|
|
49
|
-
### 2
|
|
53
|
+
### 2) Connect from your app process
|
|
50
54
|
|
|
51
55
|
```ts
|
|
52
56
|
import { connectBotBridge } from "shardwire";
|
|
@@ -68,24 +72,12 @@ app.on("messageCreate", ({ message }) => {
|
|
|
68
72
|
await app.ready();
|
|
69
73
|
```
|
|
70
74
|
|
|
71
|
-
###
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
app.on(
|
|
75
|
-
"messageCreate",
|
|
76
|
-
({ message }) => {
|
|
77
|
-
console.log("Only channel 123:", message.content);
|
|
78
|
-
},
|
|
79
|
-
{ channelId: "123456789012345678" },
|
|
80
|
-
);
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### 3. Call built-in bot actions
|
|
75
|
+
### 3) Call bot actions from your app
|
|
84
76
|
|
|
85
77
|
```ts
|
|
86
78
|
const result = await app.actions.sendMessage({
|
|
87
79
|
channelId: "123456789012345678",
|
|
88
|
-
content: "Hello from
|
|
80
|
+
content: "Hello from app side",
|
|
89
81
|
});
|
|
90
82
|
|
|
91
83
|
if (!result.ok) {
|
|
@@ -93,43 +85,80 @@ if (!result.ok) {
|
|
|
93
85
|
}
|
|
94
86
|
```
|
|
95
87
|
|
|
88
|
+
### 4) Filter subscriptions when needed
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
app.on(
|
|
92
|
+
"messageCreate",
|
|
93
|
+
({ message }) => {
|
|
94
|
+
console.log("Only this channel:", message.content);
|
|
95
|
+
},
|
|
96
|
+
{ channelId: "123456789012345678" },
|
|
97
|
+
);
|
|
98
|
+
```
|
|
99
|
+
|
|
96
100
|
## Built-In Events
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
Apps subscribe to events with `app.on(...)`. The bridge forwards only what each app subscribes to.
|
|
99
103
|
|
|
100
104
|
- `ready`
|
|
101
105
|
- `interactionCreate`
|
|
102
106
|
- `messageCreate`
|
|
103
107
|
- `messageUpdate`
|
|
104
108
|
- `messageDelete`
|
|
109
|
+
- `messageReactionAdd`
|
|
110
|
+
- `messageReactionRemove`
|
|
111
|
+
- `guildCreate`
|
|
112
|
+
- `guildDelete`
|
|
105
113
|
- `guildMemberAdd`
|
|
106
114
|
- `guildMemberRemove`
|
|
115
|
+
- `guildMemberUpdate`
|
|
116
|
+
- `threadCreate`
|
|
117
|
+
- `threadUpdate`
|
|
118
|
+
- `threadDelete`
|
|
107
119
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Optional filters can narrow delivery by:
|
|
120
|
+
Supported filters:
|
|
111
121
|
|
|
112
122
|
- `guildId`
|
|
113
123
|
- `channelId`
|
|
114
124
|
- `userId`
|
|
115
|
-
- `commandName` for `interactionCreate`
|
|
125
|
+
- `commandName` (for `interactionCreate`)
|
|
126
|
+
- `customId` (for `interactionCreate`)
|
|
127
|
+
- `interactionKind` (for `interactionCreate`)
|
|
128
|
+
|
|
129
|
+
### Intent Notes
|
|
130
|
+
|
|
131
|
+
- `ready` and `interactionCreate`: no specific event intent requirement
|
|
132
|
+
- `messageCreate`, `messageUpdate`, `messageDelete`: `GuildMessages`
|
|
133
|
+
- `messageReactionAdd`, `messageReactionRemove`: `GuildMessageReactions`
|
|
134
|
+
- `guildCreate`, `guildDelete`, `threadCreate`, `threadUpdate`, `threadDelete`: `Guilds`
|
|
135
|
+
- `guildMemberAdd`, `guildMemberRemove`, `guildMemberUpdate`: `GuildMembers`
|
|
116
136
|
|
|
117
137
|
## Built-In Actions
|
|
118
138
|
|
|
119
|
-
`app.actions.*`
|
|
139
|
+
`app.actions.*` includes:
|
|
120
140
|
|
|
121
141
|
- `sendMessage`
|
|
122
142
|
- `editMessage`
|
|
123
143
|
- `deleteMessage`
|
|
124
144
|
- `replyToInteraction`
|
|
125
145
|
- `deferInteraction`
|
|
146
|
+
- `deferUpdateInteraction`
|
|
126
147
|
- `followUpInteraction`
|
|
148
|
+
- `editInteractionReply`
|
|
149
|
+
- `deleteInteractionReply`
|
|
150
|
+
- `updateInteraction`
|
|
151
|
+
- `showModal`
|
|
152
|
+
- `fetchMessage`
|
|
153
|
+
- `fetchMember`
|
|
127
154
|
- `banMember`
|
|
128
155
|
- `kickMember`
|
|
129
156
|
- `addMemberRole`
|
|
130
157
|
- `removeMemberRole`
|
|
158
|
+
- `addMessageReaction`
|
|
159
|
+
- `removeOwnMessageReaction`
|
|
131
160
|
|
|
132
|
-
|
|
161
|
+
All actions return:
|
|
133
162
|
|
|
134
163
|
```ts
|
|
135
164
|
type ActionResult<T> =
|
|
@@ -137,9 +166,34 @@ type ActionResult<T> =
|
|
|
137
166
|
| { ok: false; requestId: string; ts: number; error: { code: string; message: string; details?: unknown } };
|
|
138
167
|
```
|
|
139
168
|
|
|
169
|
+
### Idempotency for safe retries
|
|
170
|
+
|
|
171
|
+
You can provide an `idempotencyKey` in action options to dedupe repeated requests on the same connection:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
await app.actions.sendMessage(
|
|
175
|
+
{ channelId: "123456789012345678", content: "Hello once" },
|
|
176
|
+
{ idempotencyKey: "notify:order:123" },
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### App-side action metrics
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const app = connectBotBridge({
|
|
184
|
+
url: "ws://127.0.0.1:3001/shardwire",
|
|
185
|
+
secret: process.env.SHARDWIRE_SECRET!,
|
|
186
|
+
metrics: {
|
|
187
|
+
onActionComplete(meta) {
|
|
188
|
+
console.log(meta.name, meta.durationMs, meta.ok, meta.errorCode);
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
140
194
|
## Secret Scopes
|
|
141
195
|
|
|
142
|
-
Use a plain string secret for full access:
|
|
196
|
+
Use a plain string secret for full event/action access:
|
|
143
197
|
|
|
144
198
|
```ts
|
|
145
199
|
server: {
|
|
@@ -148,7 +202,7 @@ server: {
|
|
|
148
202
|
}
|
|
149
203
|
```
|
|
150
204
|
|
|
151
|
-
|
|
205
|
+
Use a scoped secret to limit what an app can do:
|
|
152
206
|
|
|
153
207
|
```ts
|
|
154
208
|
server: {
|
|
@@ -166,35 +220,58 @@ server: {
|
|
|
166
220
|
}
|
|
167
221
|
```
|
|
168
222
|
|
|
169
|
-
|
|
223
|
+
Inspect negotiated capabilities in the app:
|
|
170
224
|
|
|
171
225
|
```ts
|
|
172
226
|
const capabilities = app.capabilities();
|
|
173
227
|
console.log(capabilities.events, capabilities.actions);
|
|
174
228
|
```
|
|
175
229
|
|
|
230
|
+
## Run the Included Examples
|
|
231
|
+
|
|
232
|
+
In two terminals:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# terminal 1
|
|
236
|
+
DISCORD_TOKEN=your-token SHARDWIRE_SECRET=dev-secret npm run example:bot
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# terminal 2
|
|
241
|
+
SHARDWIRE_SECRET=dev-secret npm run example:app
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Examples:
|
|
245
|
+
|
|
246
|
+
- Bot bridge: `examples/bot-basic.ts`
|
|
247
|
+
- App client: `examples/app-basic.ts`
|
|
248
|
+
|
|
176
249
|
## Public API
|
|
177
250
|
|
|
178
251
|
```ts
|
|
179
252
|
import { createBotBridge, connectBotBridge } from "shardwire";
|
|
180
253
|
```
|
|
181
254
|
|
|
182
|
-
Main exports:
|
|
255
|
+
Main exports include:
|
|
183
256
|
|
|
184
257
|
- `createBotBridge(options)`
|
|
185
258
|
- `connectBotBridge(options)`
|
|
186
259
|
- `BridgeCapabilityError`
|
|
187
260
|
- bot/app option types
|
|
188
|
-
- normalized event payload types
|
|
189
|
-
- action payload
|
|
261
|
+
- normalized event payload types (for example `BridgeMessage`, `BridgeInteraction`, `BridgeGuildMember`)
|
|
262
|
+
- action payload and result types
|
|
263
|
+
|
|
264
|
+
## Security and Transport Notes
|
|
265
|
+
|
|
266
|
+
- Use `wss://` for non-loopback deployments.
|
|
267
|
+
- `ws://` is only accepted for loopback hosts (`127.0.0.1`, `localhost`, `::1`).
|
|
268
|
+
- Event availability depends on enabled intents and secret scope.
|
|
269
|
+
- For vulnerability reporting and security policy, see [`SECURITY.md`](./SECURITY.md).
|
|
190
270
|
|
|
191
|
-
##
|
|
271
|
+
## Contributing
|
|
192
272
|
|
|
193
|
-
|
|
194
|
-
- `discord.js` is used internally by the default runtime, but apps interact with Shardwire through Shardwire's own JSON payloads
|
|
195
|
-
- Event availability depends on the intents you enable for the bot bridge
|
|
273
|
+
Issues and pull requests are welcome: [github.com/unloopedmido/shardwire](https://github.com/unloopedmido/shardwire).
|
|
196
274
|
|
|
197
|
-
##
|
|
275
|
+
## License
|
|
198
276
|
|
|
199
|
-
-
|
|
200
|
-
- App: [examples/app-basic.ts](./examples/app-basic.ts)
|
|
277
|
+
MIT - see [`LICENSE`](./LICENSE).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Snowflake, APIEmbed, APIAllowedMentions } from 'discord-api-types/v10';
|
|
1
|
+
import { Snowflake, APIEmbed, APIAllowedMentions, APIActionRowComponent, APIComponentInMessageActionRow, APITextInputComponent } from 'discord-api-types/v10';
|
|
2
|
+
import { GatewayIntentBits } from 'discord.js';
|
|
2
3
|
|
|
3
4
|
type Unsubscribe = () => void;
|
|
4
5
|
interface ShardwireLogger {
|
|
@@ -7,7 +8,7 @@ interface ShardwireLogger {
|
|
|
7
8
|
warn?: (message: string, meta?: Record<string, unknown>) => void;
|
|
8
9
|
error?: (message: string, meta?: Record<string, unknown>) => void;
|
|
9
10
|
}
|
|
10
|
-
type BotIntentName =
|
|
11
|
+
type BotIntentName = keyof typeof GatewayIntentBits;
|
|
11
12
|
interface BridgeUser {
|
|
12
13
|
id: Snowflake;
|
|
13
14
|
username: string;
|
|
@@ -41,6 +42,23 @@ interface BridgeGuildMember {
|
|
|
41
42
|
pending?: boolean;
|
|
42
43
|
communicationDisabledUntil?: string | null;
|
|
43
44
|
}
|
|
45
|
+
/** Normalized guild snapshot for `guildCreate` / `guildDelete` events. */
|
|
46
|
+
interface BridgeGuild {
|
|
47
|
+
id: Snowflake;
|
|
48
|
+
name: string;
|
|
49
|
+
icon?: string | null;
|
|
50
|
+
ownerId?: Snowflake;
|
|
51
|
+
}
|
|
52
|
+
/** Normalized thread channel snapshot for thread lifecycle events. */
|
|
53
|
+
interface BridgeThread {
|
|
54
|
+
id: Snowflake;
|
|
55
|
+
guildId: Snowflake;
|
|
56
|
+
parentId: Snowflake | null;
|
|
57
|
+
name: string;
|
|
58
|
+
type: number;
|
|
59
|
+
archived?: boolean;
|
|
60
|
+
locked?: boolean;
|
|
61
|
+
}
|
|
44
62
|
interface BridgeMessage {
|
|
45
63
|
id: Snowflake;
|
|
46
64
|
channelId: Snowflake;
|
|
@@ -52,6 +70,8 @@ interface BridgeMessage {
|
|
|
52
70
|
editedAt?: string | null;
|
|
53
71
|
attachments: BridgeAttachment[];
|
|
54
72
|
embeds: APIEmbed[];
|
|
73
|
+
/** Message component rows (JSON-serializable API shape). */
|
|
74
|
+
components?: APIActionRowComponent<APIComponentInMessageActionRow>[];
|
|
55
75
|
reference?: BridgeMessageReference;
|
|
56
76
|
}
|
|
57
77
|
interface BridgeDeletedMessage {
|
|
@@ -60,6 +80,18 @@ interface BridgeDeletedMessage {
|
|
|
60
80
|
guildId?: Snowflake;
|
|
61
81
|
deletedAt: string;
|
|
62
82
|
}
|
|
83
|
+
interface BridgeReactionEmoji {
|
|
84
|
+
id?: Snowflake;
|
|
85
|
+
name?: string | null;
|
|
86
|
+
animated?: boolean;
|
|
87
|
+
}
|
|
88
|
+
interface BridgeMessageReaction {
|
|
89
|
+
messageId: Snowflake;
|
|
90
|
+
channelId: Snowflake;
|
|
91
|
+
guildId?: Snowflake;
|
|
92
|
+
user?: BridgeUser;
|
|
93
|
+
emoji: BridgeReactionEmoji;
|
|
94
|
+
}
|
|
63
95
|
type BridgeInteractionKind = "chatInput" | "contextMenu" | "button" | "stringSelect" | "userSelect" | "roleSelect" | "mentionableSelect" | "channelSelect" | "modalSubmit" | "unknown";
|
|
64
96
|
interface BridgeInteraction {
|
|
65
97
|
id: Snowflake;
|
|
@@ -78,6 +110,7 @@ interface BridgeInteraction {
|
|
|
78
110
|
}
|
|
79
111
|
interface EventEnvelopeBase {
|
|
80
112
|
receivedAt: number;
|
|
113
|
+
/** Populated when the bot runs under `ShardingManager` (multi-shard). */
|
|
81
114
|
shardId?: number;
|
|
82
115
|
}
|
|
83
116
|
interface ReadyEventPayload extends EventEnvelopeBase {
|
|
@@ -102,20 +135,58 @@ interface GuildMemberAddEventPayload extends EventEnvelopeBase {
|
|
|
102
135
|
interface GuildMemberRemoveEventPayload extends EventEnvelopeBase {
|
|
103
136
|
member: BridgeGuildMember;
|
|
104
137
|
}
|
|
138
|
+
interface GuildMemberUpdateEventPayload extends EventEnvelopeBase {
|
|
139
|
+
oldMember?: BridgeGuildMember;
|
|
140
|
+
member: BridgeGuildMember;
|
|
141
|
+
}
|
|
142
|
+
interface GuildCreateEventPayload extends EventEnvelopeBase {
|
|
143
|
+
guild: BridgeGuild;
|
|
144
|
+
}
|
|
145
|
+
interface GuildDeleteEventPayload extends EventEnvelopeBase {
|
|
146
|
+
guild: BridgeGuild;
|
|
147
|
+
}
|
|
148
|
+
interface ThreadCreateEventPayload extends EventEnvelopeBase {
|
|
149
|
+
thread: BridgeThread;
|
|
150
|
+
}
|
|
151
|
+
interface ThreadUpdateEventPayload extends EventEnvelopeBase {
|
|
152
|
+
oldThread?: BridgeThread;
|
|
153
|
+
thread: BridgeThread;
|
|
154
|
+
}
|
|
155
|
+
interface ThreadDeleteEventPayload extends EventEnvelopeBase {
|
|
156
|
+
thread: BridgeThread;
|
|
157
|
+
}
|
|
158
|
+
interface MessageReactionAddEventPayload extends EventEnvelopeBase {
|
|
159
|
+
reaction: BridgeMessageReaction;
|
|
160
|
+
}
|
|
161
|
+
interface MessageReactionRemoveEventPayload extends EventEnvelopeBase {
|
|
162
|
+
reaction: BridgeMessageReaction;
|
|
163
|
+
}
|
|
105
164
|
interface BotEventPayloadMap {
|
|
106
165
|
ready: ReadyEventPayload;
|
|
107
166
|
interactionCreate: InteractionCreateEventPayload;
|
|
108
167
|
messageCreate: MessageCreateEventPayload;
|
|
109
168
|
messageUpdate: MessageUpdateEventPayload;
|
|
110
169
|
messageDelete: MessageDeleteEventPayload;
|
|
170
|
+
messageReactionAdd: MessageReactionAddEventPayload;
|
|
171
|
+
messageReactionRemove: MessageReactionRemoveEventPayload;
|
|
172
|
+
guildCreate: GuildCreateEventPayload;
|
|
173
|
+
guildDelete: GuildDeleteEventPayload;
|
|
111
174
|
guildMemberAdd: GuildMemberAddEventPayload;
|
|
112
175
|
guildMemberRemove: GuildMemberRemoveEventPayload;
|
|
176
|
+
guildMemberUpdate: GuildMemberUpdateEventPayload;
|
|
177
|
+
threadCreate: ThreadCreateEventPayload;
|
|
178
|
+
threadUpdate: ThreadUpdateEventPayload;
|
|
179
|
+
threadDelete: ThreadDeleteEventPayload;
|
|
113
180
|
}
|
|
114
181
|
type BotEventName = keyof BotEventPayloadMap;
|
|
115
182
|
interface BridgeMessageInput {
|
|
116
183
|
content?: string;
|
|
117
184
|
embeds?: APIEmbed[];
|
|
118
185
|
allowedMentions?: APIAllowedMentions;
|
|
186
|
+
components?: APIActionRowComponent<APIComponentInMessageActionRow>[];
|
|
187
|
+
/** Bitfield compatible with `MessageFlags` from discord.js / Discord API. */
|
|
188
|
+
flags?: number;
|
|
189
|
+
stickerIds?: Snowflake[];
|
|
119
190
|
}
|
|
120
191
|
interface SendMessageActionPayload extends BridgeMessageInput {
|
|
121
192
|
channelId: Snowflake;
|
|
@@ -140,6 +211,32 @@ interface FollowUpInteractionActionPayload extends BridgeMessageInput {
|
|
|
140
211
|
interactionId: Snowflake;
|
|
141
212
|
ephemeral?: boolean;
|
|
142
213
|
}
|
|
214
|
+
interface DeferUpdateInteractionActionPayload {
|
|
215
|
+
interactionId: Snowflake;
|
|
216
|
+
}
|
|
217
|
+
interface EditInteractionReplyActionPayload extends BridgeMessageInput {
|
|
218
|
+
interactionId: Snowflake;
|
|
219
|
+
}
|
|
220
|
+
interface DeleteInteractionReplyActionPayload {
|
|
221
|
+
interactionId: Snowflake;
|
|
222
|
+
}
|
|
223
|
+
interface UpdateInteractionActionPayload extends BridgeMessageInput {
|
|
224
|
+
interactionId: Snowflake;
|
|
225
|
+
}
|
|
226
|
+
interface ShowModalActionPayload {
|
|
227
|
+
interactionId: Snowflake;
|
|
228
|
+
title: string;
|
|
229
|
+
customId: string;
|
|
230
|
+
components: APIActionRowComponent<APITextInputComponent>[];
|
|
231
|
+
}
|
|
232
|
+
interface FetchMessageActionPayload {
|
|
233
|
+
channelId: Snowflake;
|
|
234
|
+
messageId: Snowflake;
|
|
235
|
+
}
|
|
236
|
+
interface FetchMemberActionPayload {
|
|
237
|
+
guildId: Snowflake;
|
|
238
|
+
userId: Snowflake;
|
|
239
|
+
}
|
|
143
240
|
interface BanMemberActionPayload {
|
|
144
241
|
guildId: Snowflake;
|
|
145
242
|
userId: Snowflake;
|
|
@@ -163,17 +260,36 @@ interface RemoveMemberRoleActionPayload {
|
|
|
163
260
|
roleId: Snowflake;
|
|
164
261
|
reason?: string;
|
|
165
262
|
}
|
|
263
|
+
interface AddMessageReactionActionPayload {
|
|
264
|
+
channelId: Snowflake;
|
|
265
|
+
messageId: Snowflake;
|
|
266
|
+
emoji: string;
|
|
267
|
+
}
|
|
268
|
+
interface RemoveOwnMessageReactionActionPayload {
|
|
269
|
+
channelId: Snowflake;
|
|
270
|
+
messageId: Snowflake;
|
|
271
|
+
emoji: string;
|
|
272
|
+
}
|
|
166
273
|
interface BotActionPayloadMap {
|
|
167
274
|
sendMessage: SendMessageActionPayload;
|
|
168
275
|
editMessage: EditMessageActionPayload;
|
|
169
276
|
deleteMessage: DeleteMessageActionPayload;
|
|
170
277
|
replyToInteraction: ReplyToInteractionActionPayload;
|
|
171
278
|
deferInteraction: DeferInteractionActionPayload;
|
|
279
|
+
deferUpdateInteraction: DeferUpdateInteractionActionPayload;
|
|
172
280
|
followUpInteraction: FollowUpInteractionActionPayload;
|
|
281
|
+
editInteractionReply: EditInteractionReplyActionPayload;
|
|
282
|
+
deleteInteractionReply: DeleteInteractionReplyActionPayload;
|
|
283
|
+
updateInteraction: UpdateInteractionActionPayload;
|
|
284
|
+
showModal: ShowModalActionPayload;
|
|
285
|
+
fetchMessage: FetchMessageActionPayload;
|
|
286
|
+
fetchMember: FetchMemberActionPayload;
|
|
173
287
|
banMember: BanMemberActionPayload;
|
|
174
288
|
kickMember: KickMemberActionPayload;
|
|
175
289
|
addMemberRole: AddMemberRoleActionPayload;
|
|
176
290
|
removeMemberRole: RemoveMemberRoleActionPayload;
|
|
291
|
+
addMessageReaction: AddMessageReactionActionPayload;
|
|
292
|
+
removeOwnMessageReaction: RemoveOwnMessageReactionActionPayload;
|
|
177
293
|
}
|
|
178
294
|
interface DeleteMessageActionResult {
|
|
179
295
|
deleted: true;
|
|
@@ -184,21 +300,47 @@ interface DeferInteractionActionResult {
|
|
|
184
300
|
deferred: true;
|
|
185
301
|
interactionId: Snowflake;
|
|
186
302
|
}
|
|
303
|
+
interface DeferUpdateInteractionActionResult {
|
|
304
|
+
deferred: true;
|
|
305
|
+
interactionId: Snowflake;
|
|
306
|
+
}
|
|
307
|
+
interface DeleteInteractionReplyActionResult {
|
|
308
|
+
deleted: true;
|
|
309
|
+
interactionId: Snowflake;
|
|
310
|
+
}
|
|
311
|
+
interface ShowModalActionResult {
|
|
312
|
+
shown: true;
|
|
313
|
+
interactionId: Snowflake;
|
|
314
|
+
}
|
|
187
315
|
interface MemberModerationActionResult {
|
|
188
316
|
guildId: Snowflake;
|
|
189
317
|
userId: Snowflake;
|
|
190
318
|
}
|
|
319
|
+
interface MessageReactionActionResult {
|
|
320
|
+
messageId: Snowflake;
|
|
321
|
+
channelId: Snowflake;
|
|
322
|
+
emoji: string;
|
|
323
|
+
}
|
|
191
324
|
interface BotActionResultDataMap {
|
|
192
325
|
sendMessage: BridgeMessage;
|
|
193
326
|
editMessage: BridgeMessage;
|
|
194
327
|
deleteMessage: DeleteMessageActionResult;
|
|
195
328
|
replyToInteraction: BridgeMessage;
|
|
196
329
|
deferInteraction: DeferInteractionActionResult;
|
|
330
|
+
deferUpdateInteraction: DeferUpdateInteractionActionResult;
|
|
197
331
|
followUpInteraction: BridgeMessage;
|
|
332
|
+
editInteractionReply: BridgeMessage;
|
|
333
|
+
deleteInteractionReply: DeleteInteractionReplyActionResult;
|
|
334
|
+
updateInteraction: BridgeMessage;
|
|
335
|
+
showModal: ShowModalActionResult;
|
|
336
|
+
fetchMessage: BridgeMessage;
|
|
337
|
+
fetchMember: BridgeGuildMember;
|
|
198
338
|
banMember: MemberModerationActionResult;
|
|
199
339
|
kickMember: MemberModerationActionResult;
|
|
200
340
|
addMemberRole: BridgeGuildMember;
|
|
201
341
|
removeMemberRole: BridgeGuildMember;
|
|
342
|
+
addMessageReaction: MessageReactionActionResult;
|
|
343
|
+
removeOwnMessageReaction: MessageReactionActionResult;
|
|
202
344
|
}
|
|
203
345
|
type BotActionName = keyof BotActionPayloadMap;
|
|
204
346
|
interface BridgeCapabilities {
|
|
@@ -210,6 +352,10 @@ interface EventSubscriptionFilter {
|
|
|
210
352
|
channelId?: Snowflake | readonly Snowflake[];
|
|
211
353
|
userId?: Snowflake | readonly Snowflake[];
|
|
212
354
|
commandName?: string | readonly string[];
|
|
355
|
+
/** Matches `BridgeInteraction.customId` when present (components, modals). */
|
|
356
|
+
customId?: string | readonly string[];
|
|
357
|
+
/** Matches `BridgeInteraction.kind`. */
|
|
358
|
+
interactionKind?: BridgeInteractionKind | readonly BridgeInteractionKind[];
|
|
213
359
|
}
|
|
214
360
|
interface EventSubscription<K extends BotEventName = BotEventName> {
|
|
215
361
|
name: K;
|
|
@@ -225,6 +371,14 @@ interface ScopedSecretConfig {
|
|
|
225
371
|
allow?: SecretPermissions;
|
|
226
372
|
}
|
|
227
373
|
type BotBridgeSecret = string | ScopedSecretConfig;
|
|
374
|
+
/** Structured Discord / transport context for failed actions (machine-readable). */
|
|
375
|
+
interface ActionErrorDetails {
|
|
376
|
+
discordStatus?: number;
|
|
377
|
+
discordCode?: number;
|
|
378
|
+
/** When true, callers may retry with backoff (e.g. rate limits). */
|
|
379
|
+
retryable?: boolean;
|
|
380
|
+
[key: string]: unknown;
|
|
381
|
+
}
|
|
228
382
|
interface BotBridgeOptions {
|
|
229
383
|
token: string;
|
|
230
384
|
intents: readonly BotIntentName[];
|
|
@@ -235,9 +389,24 @@ interface BotBridgeOptions {
|
|
|
235
389
|
heartbeatMs?: number;
|
|
236
390
|
maxPayloadBytes?: number;
|
|
237
391
|
secrets: readonly BotBridgeSecret[];
|
|
392
|
+
/** Reject new TCP connections when authenticated client count reaches this cap (default: unlimited). */
|
|
393
|
+
maxConnections?: number;
|
|
394
|
+
/** Max concurrent action executions per bot process (default: 32). */
|
|
395
|
+
maxConcurrentActions?: number;
|
|
396
|
+
/** When the queue is full, fail fast with `SERVICE_UNAVAILABLE` (default: 5000). */
|
|
397
|
+
actionQueueTimeoutMs?: number;
|
|
238
398
|
};
|
|
239
399
|
logger?: ShardwireLogger;
|
|
240
400
|
}
|
|
401
|
+
interface AppBridgeMetricsHooks {
|
|
402
|
+
onActionComplete?: (meta: {
|
|
403
|
+
name: BotActionName;
|
|
404
|
+
requestId: string;
|
|
405
|
+
durationMs: number;
|
|
406
|
+
ok: boolean;
|
|
407
|
+
errorCode?: string;
|
|
408
|
+
}) => void;
|
|
409
|
+
}
|
|
241
410
|
interface AppBridgeOptions {
|
|
242
411
|
url: string;
|
|
243
412
|
secret: string;
|
|
@@ -251,11 +420,12 @@ interface AppBridgeOptions {
|
|
|
251
420
|
};
|
|
252
421
|
requestTimeoutMs?: number;
|
|
253
422
|
logger?: ShardwireLogger;
|
|
423
|
+
metrics?: AppBridgeMetricsHooks;
|
|
254
424
|
}
|
|
255
425
|
interface ActionError {
|
|
256
|
-
code: "UNAUTHORIZED" | "TIMEOUT" | "DISCONNECTED" | "FORBIDDEN" | "NOT_FOUND" | "INVALID_REQUEST" | "INTERNAL_ERROR";
|
|
426
|
+
code: "UNAUTHORIZED" | "TIMEOUT" | "DISCONNECTED" | "FORBIDDEN" | "NOT_FOUND" | "INVALID_REQUEST" | "INTERNAL_ERROR" | "SERVICE_UNAVAILABLE";
|
|
257
427
|
message: string;
|
|
258
|
-
details?: unknown;
|
|
428
|
+
details?: ActionErrorDetails | unknown;
|
|
259
429
|
}
|
|
260
430
|
interface ActionSuccess<T> {
|
|
261
431
|
ok: true;
|
|
@@ -276,11 +446,14 @@ declare class BridgeCapabilityError extends Error {
|
|
|
276
446
|
constructor(kind: "event" | "action", name: string, message?: string);
|
|
277
447
|
}
|
|
278
448
|
type EventHandler<K extends BotEventName> = (payload: BotEventPayloadMap[K]) => void;
|
|
449
|
+
type AppBridgeActionInvokeOptions = {
|
|
450
|
+
timeoutMs?: number;
|
|
451
|
+
requestId?: string;
|
|
452
|
+
/** When set, duplicate keys within TTL return the first result (best-effort idempotency). */
|
|
453
|
+
idempotencyKey?: string;
|
|
454
|
+
};
|
|
279
455
|
type AppBridgeActions = {
|
|
280
|
-
[K in BotActionName]: (payload: BotActionPayloadMap[K], options?:
|
|
281
|
-
timeoutMs?: number;
|
|
282
|
-
requestId?: string;
|
|
283
|
-
}) => Promise<ActionResult<BotActionResultDataMap[K]>>;
|
|
456
|
+
[K in BotActionName]: (payload: BotActionPayloadMap[K], options?: AppBridgeActionInvokeOptions) => Promise<ActionResult<BotActionResultDataMap[K]>>;
|
|
284
457
|
};
|
|
285
458
|
interface BotBridge {
|
|
286
459
|
ready(): Promise<void>;
|
|
@@ -305,4 +478,4 @@ declare function createBotBridge(options: BotBridgeOptions): BotBridge;
|
|
|
305
478
|
|
|
306
479
|
declare function connectBotBridge(options: AppBridgeOptions): AppBridge;
|
|
307
480
|
|
|
308
|
-
export { type ActionError, type ActionFailure, type ActionResult, type ActionSuccess, type AddMemberRoleActionPayload, type AppBridge, type AppBridgeActions, type AppBridgeOptions, type BanMemberActionPayload, type BotActionName, type BotActionPayloadMap, type BotActionResultDataMap, type BotBridge, type BotBridgeOptions, type BotBridgeSecret, type BotEventName, type BotEventPayloadMap, type BotIntentName, type BridgeAttachment, type BridgeCapabilities, BridgeCapabilityError, type BridgeDeletedMessage, type BridgeGuildMember, type BridgeInteraction, type BridgeInteractionKind, type BridgeMessage, type BridgeMessageInput, type BridgeMessageReference, type BridgeUser, type DeferInteractionActionPayload, type DeleteMessageActionPayload, type EditMessageActionPayload, type EventEnvelopeBase, type EventHandler, type EventSubscription, type EventSubscriptionFilter, type FollowUpInteractionActionPayload, type GuildMemberAddEventPayload, type GuildMemberRemoveEventPayload, type InteractionCreateEventPayload, type KickMemberActionPayload, type MessageCreateEventPayload, type MessageDeleteEventPayload, type MessageUpdateEventPayload, type ReadyEventPayload, type RemoveMemberRoleActionPayload, type ReplyToInteractionActionPayload, type ScopedSecretConfig, type SecretPermissions, type SendMessageActionPayload, type ShardwireLogger, type Unsubscribe, connectBotBridge, createBotBridge };
|
|
481
|
+
export { type ActionError, type ActionErrorDetails, type ActionFailure, type ActionResult, type ActionSuccess, type AddMemberRoleActionPayload, type AddMessageReactionActionPayload, type AppBridge, type AppBridgeActionInvokeOptions, type AppBridgeActions, type AppBridgeMetricsHooks, type AppBridgeOptions, type BanMemberActionPayload, type BotActionName, type BotActionPayloadMap, type BotActionResultDataMap, type BotBridge, type BotBridgeOptions, type BotBridgeSecret, type BotEventName, type BotEventPayloadMap, type BotIntentName, type BridgeAttachment, type BridgeCapabilities, BridgeCapabilityError, type BridgeDeletedMessage, type BridgeGuild, type BridgeGuildMember, type BridgeInteraction, type BridgeInteractionKind, type BridgeMessage, type BridgeMessageInput, type BridgeMessageReaction, type BridgeMessageReference, type BridgeReactionEmoji, type BridgeThread, type BridgeUser, type DeferInteractionActionPayload, type DeferInteractionActionResult, type DeferUpdateInteractionActionPayload, type DeferUpdateInteractionActionResult, type DeleteInteractionReplyActionPayload, type DeleteInteractionReplyActionResult, type DeleteMessageActionPayload, type DeleteMessageActionResult, type EditInteractionReplyActionPayload, type EditMessageActionPayload, type EventEnvelopeBase, type EventHandler, type EventSubscription, type EventSubscriptionFilter, type FetchMemberActionPayload, type FetchMessageActionPayload, type FollowUpInteractionActionPayload, type GuildCreateEventPayload, type GuildDeleteEventPayload, type GuildMemberAddEventPayload, type GuildMemberRemoveEventPayload, type GuildMemberUpdateEventPayload, type InteractionCreateEventPayload, type KickMemberActionPayload, type MemberModerationActionResult, type MessageCreateEventPayload, type MessageDeleteEventPayload, type MessageReactionActionResult, type MessageReactionAddEventPayload, type MessageReactionRemoveEventPayload, type MessageUpdateEventPayload, type ReadyEventPayload, type RemoveMemberRoleActionPayload, type RemoveOwnMessageReactionActionPayload, type ReplyToInteractionActionPayload, type ScopedSecretConfig, type SecretPermissions, type SendMessageActionPayload, type ShardwireLogger, type ShowModalActionPayload, type ShowModalActionResult, type ThreadCreateEventPayload, type ThreadDeleteEventPayload, type ThreadUpdateEventPayload, type Unsubscribe, type UpdateInteractionActionPayload, connectBotBridge, createBotBridge };
|