@spinabot/brigade 1.6.0 → 1.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/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +51 -1
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.d.ts +2 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.js +11 -0
- package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
- package/dist/agents/channels/discord/account-config.d.ts +177 -0
- package/dist/agents/channels/discord/account-config.d.ts.map +1 -0
- package/dist/agents/channels/discord/account-config.js +349 -0
- package/dist/agents/channels/discord/account-config.js.map +1 -0
- package/dist/agents/channels/discord/adapter.d.ts +79 -0
- package/dist/agents/channels/discord/adapter.d.ts.map +1 -0
- package/dist/agents/channels/discord/adapter.js +693 -0
- package/dist/agents/channels/discord/adapter.js.map +1 -0
- package/dist/agents/channels/discord/approval-authorize.d.ts +43 -0
- package/dist/agents/channels/discord/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/discord/approval-authorize.js +71 -0
- package/dist/agents/channels/discord/approval-authorize.js.map +1 -0
- package/dist/agents/channels/discord/approval-native.d.ts +68 -0
- package/dist/agents/channels/discord/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/discord/approval-native.js +81 -0
- package/dist/agents/channels/discord/approval-native.js.map +1 -0
- package/dist/agents/channels/discord/command-menu.d.ts +49 -0
- package/dist/agents/channels/discord/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/discord/command-menu.js +73 -0
- package/dist/agents/channels/discord/command-menu.js.map +1 -0
- package/dist/agents/channels/discord/component-blocks.d.ts +108 -0
- package/dist/agents/channels/discord/component-blocks.d.ts.map +1 -0
- package/dist/agents/channels/discord/component-blocks.js +113 -0
- package/dist/agents/channels/discord/component-blocks.js.map +1 -0
- package/dist/agents/channels/discord/components.d.ts +175 -0
- package/dist/agents/channels/discord/components.d.ts.map +1 -0
- package/dist/agents/channels/discord/components.js +220 -0
- package/dist/agents/channels/discord/components.js.map +1 -0
- package/dist/agents/channels/discord/connection.d.ts +570 -0
- package/dist/agents/channels/discord/connection.d.ts.map +1 -0
- package/dist/agents/channels/discord/connection.js +1600 -0
- package/dist/agents/channels/discord/connection.js.map +1 -0
- package/dist/agents/channels/discord/directory-cache.d.ts +47 -0
- package/dist/agents/channels/discord/directory-cache.d.ts.map +1 -0
- package/dist/agents/channels/discord/directory-cache.js +131 -0
- package/dist/agents/channels/discord/directory-cache.js.map +1 -0
- package/dist/agents/channels/discord/directory-live.d.ts +61 -0
- package/dist/agents/channels/discord/directory-live.d.ts.map +1 -0
- package/dist/agents/channels/discord/directory-live.js +140 -0
- package/dist/agents/channels/discord/directory-live.js.map +1 -0
- package/dist/agents/channels/discord/draft-stream.d.ts +92 -0
- package/dist/agents/channels/discord/draft-stream.d.ts.map +1 -0
- package/dist/agents/channels/discord/draft-stream.js +213 -0
- package/dist/agents/channels/discord/draft-stream.js.map +1 -0
- package/dist/agents/channels/discord/format.d.ts +70 -0
- package/dist/agents/channels/discord/format.d.ts.map +1 -0
- package/dist/agents/channels/discord/format.js +303 -0
- package/dist/agents/channels/discord/format.js.map +1 -0
- package/dist/agents/channels/discord/guilds.d.ts +25 -0
- package/dist/agents/channels/discord/guilds.d.ts.map +1 -0
- package/dist/agents/channels/discord/guilds.js +46 -0
- package/dist/agents/channels/discord/guilds.js.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts +377 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.js +589 -0
- package/dist/agents/channels/discord/inbound-extras.js.map +1 -0
- package/dist/agents/channels/discord/index.d.ts +21 -0
- package/dist/agents/channels/discord/index.d.ts.map +1 -0
- package/dist/agents/channels/discord/index.js +21 -0
- package/dist/agents/channels/discord/index.js.map +1 -0
- package/dist/agents/channels/discord/media.d.ts +85 -0
- package/dist/agents/channels/discord/media.d.ts.map +1 -0
- package/dist/agents/channels/discord/media.js +242 -0
- package/dist/agents/channels/discord/media.js.map +1 -0
- package/dist/agents/channels/discord/modal-registry.d.ts +89 -0
- package/dist/agents/channels/discord/modal-registry.d.ts.map +1 -0
- package/dist/agents/channels/discord/modal-registry.js +104 -0
- package/dist/agents/channels/discord/modal-registry.js.map +1 -0
- package/dist/agents/channels/discord/modals.d.ts +100 -0
- package/dist/agents/channels/discord/modals.d.ts.map +1 -0
- package/dist/agents/channels/discord/modals.js +124 -0
- package/dist/agents/channels/discord/modals.js.map +1 -0
- package/dist/agents/channels/discord/module.d.ts +15 -0
- package/dist/agents/channels/discord/module.d.ts.map +1 -0
- package/dist/agents/channels/discord/module.js +22 -0
- package/dist/agents/channels/discord/module.js.map +1 -0
- package/dist/agents/channels/discord/permission-audit.d.ts +43 -0
- package/dist/agents/channels/discord/permission-audit.d.ts.map +1 -0
- package/dist/agents/channels/discord/permission-audit.js +192 -0
- package/dist/agents/channels/discord/permission-audit.js.map +1 -0
- package/dist/agents/channels/discord/plugin.d.ts +89 -0
- package/dist/agents/channels/discord/plugin.d.ts.map +1 -0
- package/dist/agents/channels/discord/plugin.js +372 -0
- package/dist/agents/channels/discord/plugin.js.map +1 -0
- package/dist/agents/channels/discord/probe.d.ts +115 -0
- package/dist/agents/channels/discord/probe.d.ts.map +1 -0
- package/dist/agents/channels/discord/probe.js +193 -0
- package/dist/agents/channels/discord/probe.js.map +1 -0
- package/dist/agents/channels/discord/reasoning-lane.d.ts +42 -0
- package/dist/agents/channels/discord/reasoning-lane.d.ts.map +1 -0
- package/dist/agents/channels/discord/reasoning-lane.js +68 -0
- package/dist/agents/channels/discord/reasoning-lane.js.map +1 -0
- package/dist/agents/channels/discord/rest-actions.d.ts +346 -0
- package/dist/agents/channels/discord/rest-actions.d.ts.map +1 -0
- package/dist/agents/channels/discord/rest-actions.js +559 -0
- package/dist/agents/channels/discord/rest-actions.js.map +1 -0
- package/dist/agents/channels/discord/rest-components.d.ts +122 -0
- package/dist/agents/channels/discord/rest-components.d.ts.map +1 -0
- package/dist/agents/channels/discord/rest-components.js +243 -0
- package/dist/agents/channels/discord/rest-components.js.map +1 -0
- package/dist/agents/channels/discord/security-audit.d.ts +29 -0
- package/dist/agents/channels/discord/security-audit.d.ts.map +1 -0
- package/dist/agents/channels/discord/security-audit.js +94 -0
- package/dist/agents/channels/discord/security-audit.js.map +1 -0
- package/dist/agents/channels/discord/security-doctor.d.ts +43 -0
- package/dist/agents/channels/discord/security-doctor.d.ts.map +1 -0
- package/dist/agents/channels/discord/security-doctor.js +83 -0
- package/dist/agents/channels/discord/security-doctor.js.map +1 -0
- package/dist/agents/channels/discord/status-issues.d.ts +37 -0
- package/dist/agents/channels/discord/status-issues.d.ts.map +1 -0
- package/dist/agents/channels/discord/status-issues.js +66 -0
- package/dist/agents/channels/discord/status-issues.js.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts +57 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.js +98 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.js.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding.d.ts +95 -0
- package/dist/agents/channels/discord/subagent-thread-binding.d.ts.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding.js +208 -0
- package/dist/agents/channels/discord/subagent-thread-binding.js.map +1 -0
- package/dist/agents/channels/discord/system-events.d.ts +31 -0
- package/dist/agents/channels/discord/system-events.d.ts.map +1 -0
- package/dist/agents/channels/discord/system-events.js +74 -0
- package/dist/agents/channels/discord/system-events.js.map +1 -0
- package/dist/agents/channels/general-callback.d.ts +12 -0
- package/dist/agents/channels/general-callback.d.ts.map +1 -1
- package/dist/agents/channels/general-callback.js +18 -0
- package/dist/agents/channels/general-callback.js.map +1 -1
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +70 -10
- package/dist/agents/channels/inbound-pipeline.js.map +1 -1
- package/dist/agents/channels/sdk.d.ts +2 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -1
- package/dist/agents/channels/sdk.js +2 -0
- package/dist/agents/channels/sdk.js.map +1 -1
- package/dist/agents/extensions/modules/index.d.ts.map +1 -1
- package/dist/agents/extensions/modules/index.js +5 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/agents/extensions/types.d.ts +7 -0
- package/dist/agents/extensions/types.d.ts.map +1 -1
- package/dist/agents/extensions/types.js.map +1 -1
- package/dist/agents/subagent-announce-delivery.d.ts +10 -0
- package/dist/agents/subagent-announce-delivery.d.ts.map +1 -1
- package/dist/agents/subagent-announce-delivery.js +1 -0
- package/dist/agents/subagent-announce-delivery.js.map +1 -1
- package/dist/agents/subagent-completion-bridge.d.ts.map +1 -1
- package/dist/agents/subagent-completion-bridge.js +81 -0
- package/dist/agents/subagent-completion-bridge.js.map +1 -1
- package/dist/agents/subagent-spawn.d.ts.map +1 -1
- package/dist/agents/subagent-spawn.js +57 -4
- package/dist/agents/subagent-spawn.js.map +1 -1
- package/dist/agents/tools/cron-tool.d.ts.map +1 -1
- package/dist/agents/tools/cron-tool.js +4 -1
- package/dist/agents/tools/cron-tool.js.map +1 -1
- package/dist/agents/tools/discord-action-tool.d.ts +224 -0
- package/dist/agents/tools/discord-action-tool.d.ts.map +1 -0
- package/dist/agents/tools/discord-action-tool.js +848 -0
- package/dist/agents/tools/discord-action-tool.js.map +1 -0
- package/dist/agents/tools/registry.d.ts.map +1 -1
- package/dist/agents/tools/registry.js +21 -0
- package/dist/agents/tools/registry.js.map +1 -1
- package/dist/agents/tools/sessions/index.d.ts +8 -0
- package/dist/agents/tools/sessions/index.d.ts.map +1 -1
- package/dist/agents/tools/sessions/index.js +15 -3
- package/dist/agents/tools/sessions/index.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/channels.d.ts +2 -0
- package/dist/cli/commands/channels.d.ts.map +1 -1
- package/dist/cli/commands/channels.js +58 -1
- package/dist/cli/commands/channels.js.map +1 -1
- package/dist/core/auth-bridge.d.ts +1 -0
- package/dist/core/auth-bridge.d.ts.map +1 -1
- package/dist/core/auth-bridge.js +46 -1
- package/dist/core/auth-bridge.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +18 -2
- package/dist/core/server.js.map +1 -1
- package/dist/cron/isolated-agent/run-executor.d.ts +11 -0
- package/dist/cron/isolated-agent/run-executor.d.ts.map +1 -1
- package/dist/cron/isolated-agent/run-executor.js +20 -4
- package/dist/cron/isolated-agent/run-executor.js.map +1 -1
- package/dist/cron/types.d.ts +8 -0
- package/dist/cron/types.d.ts.map +1 -1
- package/dist/system-prompt/assembler.d.ts.map +1 -1
- package/dist/system-prompt/assembler.js +4 -2
- package/dist/system-prompt/assembler.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord status / doctor probe — a lightweight `GET /users/@me` reachability
|
|
3
|
+
* check.
|
|
4
|
+
*
|
|
5
|
+
* Discord is token-based and stateless on disk (the Gateway keeps no local creds
|
|
6
|
+
* the status command can stat), so "is this channel actually working?" can only
|
|
7
|
+
* be answered by asking Discord. This probe does the cheapest possible call — a
|
|
8
|
+
* single `GET /users/@me` over plain HTTPS (no `discord.js`, no gateway socket)
|
|
9
|
+
* with the `Authorization: Bot <token>` header — and reports the bot's identity
|
|
10
|
+
* so `brigade channels status` and `brigade doctor` can show real Discord health.
|
|
11
|
+
*
|
|
12
|
+
* It deliberately does NOT import `discord.js`: a status check must stay fast +
|
|
13
|
+
* dependency-light, and `/users/@me` is a trivial GET. The bot token is never
|
|
14
|
+
* logged; it rides in the `Authorization` header which is built locally and
|
|
15
|
+
* discarded.
|
|
16
|
+
*
|
|
17
|
+
* Returns a structured result the caller renders; never throws — a network
|
|
18
|
+
* failure / invalid token surfaces as `{ ok: false, error }` so the status
|
|
19
|
+
* command degrades gracefully instead of crashing. Discord mirror of
|
|
20
|
+
* `slack/probe.ts`.
|
|
21
|
+
*/
|
|
22
|
+
import { stripBotPrefix } from "./account-config.js";
|
|
23
|
+
const DISCORD_ME_URL = "https://discord.com/api/v10/users/@me";
|
|
24
|
+
/** Application-metadata endpoint — carries the `flags` bitfield we decode for the MESSAGE CONTENT intent. */
|
|
25
|
+
const DISCORD_APP_URL = "https://discord.com/api/v10/oauth2/applications/@me";
|
|
26
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 8_000;
|
|
27
|
+
/**
|
|
28
|
+
* Application-flag bits that indicate the bot HAS the privileged MESSAGE CONTENT
|
|
29
|
+
* intent. `GATEWAY_MESSAGE_CONTENT` (1 << 18) is set when the intent is enabled;
|
|
30
|
+
* `GATEWAY_MESSAGE_CONTENT_LIMITED` (1 << 19) when it's available only to a
|
|
31
|
+
* limited number of servers (still readable). Neither set, with the intent
|
|
32
|
+
* requested, means it's DISABLED — the bot connects but can't read message text.
|
|
33
|
+
*/
|
|
34
|
+
const FLAG_MESSAGE_CONTENT = 1 << 18;
|
|
35
|
+
const FLAG_MESSAGE_CONTENT_LIMITED = 1 << 19;
|
|
36
|
+
/**
|
|
37
|
+
* Application-flag bits for the other two privileged intents. GUILD MEMBERS
|
|
38
|
+
* (`1<<14` full / `1<<15` limited) is needed to receive member-join / role
|
|
39
|
+
* events + populate the member list; PRESENCE (`1<<12` full / `1<<13` limited)
|
|
40
|
+
* is needed to read member presence. Neither set means the intent is DISABLED.
|
|
41
|
+
*/
|
|
42
|
+
const FLAG_GUILD_MEMBERS = 1 << 14;
|
|
43
|
+
const FLAG_GUILD_MEMBERS_LIMITED = 1 << 15;
|
|
44
|
+
const FLAG_PRESENCE = 1 << 12;
|
|
45
|
+
const FLAG_PRESENCE_LIMITED = 1 << 13;
|
|
46
|
+
/**
|
|
47
|
+
* Decode the MESSAGE CONTENT intent state from an application `flags` bitfield.
|
|
48
|
+
* `enabled` (full), `limited` (limited rollout — still reads content), or
|
|
49
|
+
* `disabled` (neither bit set). Returns `undefined` when `flags` isn't a number
|
|
50
|
+
* (the flags fetch was skipped / failed) so callers don't warn on missing data.
|
|
51
|
+
*/
|
|
52
|
+
export function decodeMessageContentIntent(flags) {
|
|
53
|
+
if (typeof flags !== "number" || !Number.isFinite(flags))
|
|
54
|
+
return undefined;
|
|
55
|
+
if ((flags & FLAG_MESSAGE_CONTENT) !== 0)
|
|
56
|
+
return "enabled";
|
|
57
|
+
if ((flags & FLAG_MESSAGE_CONTENT_LIMITED) !== 0)
|
|
58
|
+
return "limited";
|
|
59
|
+
return "disabled";
|
|
60
|
+
}
|
|
61
|
+
/** Decode one intent's enabled/limited/disabled state from its two flag bits. */
|
|
62
|
+
function decodeIntentState(flags, enabledBit, limitedBit) {
|
|
63
|
+
if ((flags & enabledBit) !== 0)
|
|
64
|
+
return "enabled";
|
|
65
|
+
if ((flags & limitedBit) !== 0)
|
|
66
|
+
return "limited";
|
|
67
|
+
return "disabled";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Decode ALL three privileged gateway intents (message content, guild members,
|
|
71
|
+
* presence) from an application `flags` bitfield. Returns `undefined` when
|
|
72
|
+
* `flags` isn't a finite number (the flags fetch was skipped / failed) so
|
|
73
|
+
* callers don't warn on missing data.
|
|
74
|
+
*/
|
|
75
|
+
export function decodePrivilegedIntents(flags) {
|
|
76
|
+
if (typeof flags !== "number" || !Number.isFinite(flags))
|
|
77
|
+
return undefined;
|
|
78
|
+
return {
|
|
79
|
+
messageContent: decodeIntentState(flags, FLAG_MESSAGE_CONTENT, FLAG_MESSAGE_CONTENT_LIMITED),
|
|
80
|
+
guildMembers: decodeIntentState(flags, FLAG_GUILD_MEMBERS, FLAG_GUILD_MEMBERS_LIMITED),
|
|
81
|
+
presence: decodeIntentState(flags, FLAG_PRESENCE, FLAG_PRESENCE_LIMITED),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** Operator-facing warning surfaced when the MESSAGE CONTENT intent is disabled. */
|
|
85
|
+
export const MESSAGE_CONTENT_DISABLED_WARNING = "Enable the MESSAGE CONTENT intent in the Discord Developer Portal — the bot can't read channel messages without it.";
|
|
86
|
+
/**
|
|
87
|
+
* Best-effort fetch of the bot's application `flags`, decoded into the MESSAGE
|
|
88
|
+
* CONTENT intent state. Runs only AFTER `/users/@me` succeeds (the token is
|
|
89
|
+
* known good). A failed fetch / non-ok / unparseable body returns `undefined` so
|
|
90
|
+
* the probe NEVER fails on it — the intent state is observability, not health.
|
|
91
|
+
*/
|
|
92
|
+
async function probePrivilegedIntents(doFetch, token, signal) {
|
|
93
|
+
try {
|
|
94
|
+
const res = await doFetch(DISCORD_APP_URL, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers: { Authorization: `Bot ${token}`, "content-type": "application/json" },
|
|
97
|
+
signal,
|
|
98
|
+
});
|
|
99
|
+
if (!res.ok)
|
|
100
|
+
return undefined;
|
|
101
|
+
const body = (await res.json());
|
|
102
|
+
return decodePrivilegedIntents(body?.flags);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Run a `GET /users/@me` probe. Resolves to a structured result describing
|
|
110
|
+
* whether the token is valid + Discord is reachable, plus the bot identity. On
|
|
111
|
+
* success it ALSO best-effort decodes the privileged MESSAGE CONTENT intent
|
|
112
|
+
* (`/oauth2/applications/@me` → `flags`) and surfaces a warning when it's
|
|
113
|
+
* disabled — without that intent the bot connects but can't read channel
|
|
114
|
+
* messages. Never rejects.
|
|
115
|
+
*/
|
|
116
|
+
export async function probeDiscord(args) {
|
|
117
|
+
const started = Date.now();
|
|
118
|
+
const token = stripBotPrefix((args.token ?? "").trim());
|
|
119
|
+
if (!token) {
|
|
120
|
+
return { ok: false, error: "no Discord bot token configured", elapsedMs: 0 };
|
|
121
|
+
}
|
|
122
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
123
|
+
const timeoutMs = args.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
126
|
+
if (typeof timer.unref === "function")
|
|
127
|
+
timer.unref();
|
|
128
|
+
try {
|
|
129
|
+
const res = await doFetch(DISCORD_ME_URL, {
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers: {
|
|
132
|
+
Authorization: `Bot ${token}`,
|
|
133
|
+
"content-type": "application/json",
|
|
134
|
+
},
|
|
135
|
+
signal: controller.signal,
|
|
136
|
+
});
|
|
137
|
+
const elapsedMs = Date.now() - started;
|
|
138
|
+
let body = null;
|
|
139
|
+
try {
|
|
140
|
+
body = (await res.json());
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
body = null;
|
|
144
|
+
}
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
// 401 = bad/expired token; otherwise surface the status + any message.
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
status: res.status,
|
|
150
|
+
error: res.status === 401
|
|
151
|
+
? "Discord rejected the bot token — reset it in the Developer Portal and paste the fresh token."
|
|
152
|
+
: body?.message
|
|
153
|
+
? `Discord /users/@me failed (${body.message}).`
|
|
154
|
+
: `Discord /users/@me failed (HTTP ${res.status}).`,
|
|
155
|
+
elapsedMs,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// Token is good — best-effort decode ALL privileged intents (never fails the
|
|
159
|
+
// probe). Reuses the same abort signal/timeout window as the identity call.
|
|
160
|
+
const privilegedIntents = await probePrivilegedIntents(doFetch, token, controller.signal);
|
|
161
|
+
const messageContentIntent = privilegedIntents?.messageContent;
|
|
162
|
+
return {
|
|
163
|
+
ok: true,
|
|
164
|
+
status: res.status,
|
|
165
|
+
elapsedMs,
|
|
166
|
+
bot: {
|
|
167
|
+
...(typeof body?.id === "string" ? { id: body.id } : {}),
|
|
168
|
+
...(typeof body?.username === "string" ? { name: body.username } : {}),
|
|
169
|
+
...(typeof body?.discriminator === "string" && body.discriminator !== "0" ? { discriminator: body.discriminator } : {}),
|
|
170
|
+
},
|
|
171
|
+
...(privilegedIntents ? { privilegedIntents } : {}),
|
|
172
|
+
...(messageContentIntent ? { messageContentIntent } : {}),
|
|
173
|
+
...(messageContentIntent === "disabled" ? { messageContentWarning: MESSAGE_CONTENT_DISABLED_WARNING } : {}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const elapsedMs = Date.now() - started;
|
|
178
|
+
const aborted = controller.signal.aborted;
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
error: aborted
|
|
182
|
+
? `Discord /users/@me timed out after ${timeoutMs}ms`
|
|
183
|
+
: err instanceof Error
|
|
184
|
+
? err.message
|
|
185
|
+
: String(err),
|
|
186
|
+
elapsedMs,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord reasoning-lane split (OPTIONAL, default OFF).
|
|
3
|
+
*
|
|
4
|
+
* Brigade strips `<think>…</think>` reasoning from every channel reply via the
|
|
5
|
+
* shared `sanitizeReplyForChannel` — recipients see only the final answer. For
|
|
6
|
+
* Discord an operator can OPT IN (config `channels.discord.surfaceReasoning:
|
|
7
|
+
* true`) to ALSO receive the reasoning trace as a separate, prefixed message.
|
|
8
|
+
*
|
|
9
|
+
* This module is the pure splitter: given the raw agent reply, it returns
|
|
10
|
+
* `{ reasoningText?, answerText }`. When surfacing is OFF the pipeline never
|
|
11
|
+
* calls this and behavior is byte-identical to today (strip + send answer). When
|
|
12
|
+
* ON, the pipeline sends `reasoningText` first (a `🧠 Reasoning:` block) then the
|
|
13
|
+
* normal sanitized answer.
|
|
14
|
+
*
|
|
15
|
+
* The answer half is computed with the SAME sanitizer the default path uses, so
|
|
16
|
+
* enabling reasoning never changes what the answer message contains — it only
|
|
17
|
+
* ADDS the reasoning message in front.
|
|
18
|
+
*
|
|
19
|
+
* Pure / deterministic / dependency-light (re-uses `sanitizeReplyForChannel`).
|
|
20
|
+
* Discord mirror of `slack/reasoning-lane.ts`.
|
|
21
|
+
*/
|
|
22
|
+
/** Prefix on the reasoning message so the recipient knows it's the trace. */
|
|
23
|
+
export declare const REASONING_PREFIX = "\uD83E\uDDE0 Reasoning:\n";
|
|
24
|
+
export interface DiscordReasoningSplit {
|
|
25
|
+
/** The extracted reasoning trace (already prefixed), or undefined when none. */
|
|
26
|
+
reasoningText?: string;
|
|
27
|
+
/** The user-facing answer (sanitized exactly as the default path produces). */
|
|
28
|
+
answerText: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Extract the concatenated text INSIDE `<think>…</think>` blocks. Handles
|
|
32
|
+
* multiple blocks and an unclosed trailing block (model truncated mid-thought).
|
|
33
|
+
* Returns "" when there's no reasoning content.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractReasoning(text: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Split a raw agent reply into an optional reasoning message + the sanitized
|
|
38
|
+
* answer. The answer is identical to `sanitizeReplyForChannel(raw)`; the
|
|
39
|
+
* reasoning is only populated when a `<think>` block carried content.
|
|
40
|
+
*/
|
|
41
|
+
export declare function splitDiscordReasoning(raw: string): DiscordReasoningSplit;
|
|
42
|
+
//# sourceMappingURL=reasoning-lane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-lane.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/reasoning-lane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,8BAAoB,CAAC;AAKlD,MAAM,WAAW,qBAAqB;IACrC,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAKxE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord reasoning-lane split (OPTIONAL, default OFF).
|
|
3
|
+
*
|
|
4
|
+
* Brigade strips `<think>…</think>` reasoning from every channel reply via the
|
|
5
|
+
* shared `sanitizeReplyForChannel` — recipients see only the final answer. For
|
|
6
|
+
* Discord an operator can OPT IN (config `channels.discord.surfaceReasoning:
|
|
7
|
+
* true`) to ALSO receive the reasoning trace as a separate, prefixed message.
|
|
8
|
+
*
|
|
9
|
+
* This module is the pure splitter: given the raw agent reply, it returns
|
|
10
|
+
* `{ reasoningText?, answerText }`. When surfacing is OFF the pipeline never
|
|
11
|
+
* calls this and behavior is byte-identical to today (strip + send answer). When
|
|
12
|
+
* ON, the pipeline sends `reasoningText` first (a `🧠 Reasoning:` block) then the
|
|
13
|
+
* normal sanitized answer.
|
|
14
|
+
*
|
|
15
|
+
* The answer half is computed with the SAME sanitizer the default path uses, so
|
|
16
|
+
* enabling reasoning never changes what the answer message contains — it only
|
|
17
|
+
* ADDS the reasoning message in front.
|
|
18
|
+
*
|
|
19
|
+
* Pure / deterministic / dependency-light (re-uses `sanitizeReplyForChannel`).
|
|
20
|
+
* Discord mirror of `slack/reasoning-lane.ts`.
|
|
21
|
+
*/
|
|
22
|
+
import { sanitizeReplyForChannel } from "../reply-sanitizer.js";
|
|
23
|
+
/** Prefix on the reasoning message so the recipient knows it's the trace. */
|
|
24
|
+
export const REASONING_PREFIX = "🧠 Reasoning:\n";
|
|
25
|
+
/** Matches a `<think>`/`<thinking>`/`<thought>` open/close tag. */
|
|
26
|
+
const THINK_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought)\b[^<>]*>/gi;
|
|
27
|
+
/**
|
|
28
|
+
* Extract the concatenated text INSIDE `<think>…</think>` blocks. Handles
|
|
29
|
+
* multiple blocks and an unclosed trailing block (model truncated mid-thought).
|
|
30
|
+
* Returns "" when there's no reasoning content.
|
|
31
|
+
*/
|
|
32
|
+
export function extractReasoning(text) {
|
|
33
|
+
if (!text)
|
|
34
|
+
return "";
|
|
35
|
+
const parts = [];
|
|
36
|
+
let inThink = false;
|
|
37
|
+
let lastIndex = 0;
|
|
38
|
+
THINK_TAG_RE.lastIndex = 0;
|
|
39
|
+
for (const match of text.matchAll(THINK_TAG_RE)) {
|
|
40
|
+
const idx = match.index ?? 0;
|
|
41
|
+
const isClose = match[1] === "/";
|
|
42
|
+
if (inThink && isClose) {
|
|
43
|
+
parts.push(text.slice(lastIndex, idx));
|
|
44
|
+
inThink = false;
|
|
45
|
+
}
|
|
46
|
+
else if (!inThink && !isClose) {
|
|
47
|
+
inThink = true;
|
|
48
|
+
lastIndex = idx + match[0].length;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Unclosed trailing block — keep what the model emitted.
|
|
52
|
+
if (inThink)
|
|
53
|
+
parts.push(text.slice(lastIndex));
|
|
54
|
+
return parts.join("\n").trim();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Split a raw agent reply into an optional reasoning message + the sanitized
|
|
58
|
+
* answer. The answer is identical to `sanitizeReplyForChannel(raw)`; the
|
|
59
|
+
* reasoning is only populated when a `<think>` block carried content.
|
|
60
|
+
*/
|
|
61
|
+
export function splitDiscordReasoning(raw) {
|
|
62
|
+
const answerText = sanitizeReplyForChannel(raw ?? "");
|
|
63
|
+
const reasoning = extractReasoning(raw ?? "");
|
|
64
|
+
if (!reasoning)
|
|
65
|
+
return { answerText };
|
|
66
|
+
return { reasoningText: `${REASONING_PREFIX}${reasoning}`, answerText };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=reasoning-lane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-lane.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/reasoning-lane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,6EAA6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAElD,mEAAmE;AACnE,MAAM,YAAY,GAAG,kDAAkD,CAAC;AASxE;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC5C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,YAAY,CAAC,SAAS,GAAG,CAAC,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;QACjC,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;YACvC,OAAO,GAAG,KAAK,CAAC;QACjB,CAAC;aAAM,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,OAAO,GAAG,IAAI,CAAC;YACf,SAAS,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACnC,CAAC;IACF,CAAC;IACD,yDAAyD;IACzD,IAAI,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAChD,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACtC,OAAO,EAAE,aAAa,EAAE,GAAG,gBAAgB,GAAG,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord REST v10 action helpers — the self-contained REST surface behind the
|
|
3
|
+
* `discord_action` agent tool.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `probe.ts`: every helper talks to `https://discord.com/api/v10/...`
|
|
6
|
+
* over plain HTTPS with the `Authorization: Bot <token>` header — NO `discord.js`,
|
|
7
|
+
* NO Gateway socket, NO live adapter/connection. The bot token is resolved by the
|
|
8
|
+
* tool (via `resolveDiscordBotToken`) and threaded in; it rides only in the
|
|
9
|
+
* `Authorization` header, built locally and never logged.
|
|
10
|
+
*
|
|
11
|
+
* `fetch` is INJECTABLE (defaults to global fetch) so tests can stub the call and
|
|
12
|
+
* assert the exact METHOD + PATH + body without touching the network.
|
|
13
|
+
*
|
|
14
|
+
* The action surface here matches the guild capability set Brigade's live adapter
|
|
15
|
+
* lacks: messaging/content (send, embeds, polls, stickers, reads, reactions,
|
|
16
|
+
* threads, search), guild-admin (channels, categories, roles, members, emojis,
|
|
17
|
+
* stickers, scheduled events), and moderation (ban/unban/kick/timeout). Each
|
|
18
|
+
* helper returns the parsed Discord JSON (or `{ ok: true }` for empty 204s); REST
|
|
19
|
+
* failures throw a {@link DiscordRestError} carrying the decoded Discord JSON code
|
|
20
|
+
* so the tool can render an operator-readable message (permissions / 404 / rate
|
|
21
|
+
* limit).
|
|
22
|
+
*/
|
|
23
|
+
/** A REST call that came back non-2xx. Carries the HTTP status + decoded Discord code. */
|
|
24
|
+
export declare class DiscordRestError extends Error {
|
|
25
|
+
readonly status: number;
|
|
26
|
+
/** The Discord JSON error `code` (stable across endpoints), when present. */
|
|
27
|
+
readonly code?: number;
|
|
28
|
+
/** Retry-after seconds, populated on a 429 rate-limit. */
|
|
29
|
+
readonly retryAfter?: number;
|
|
30
|
+
constructor(message: string, opts: {
|
|
31
|
+
status: number;
|
|
32
|
+
code?: number;
|
|
33
|
+
retryAfter?: number;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Turn a non-2xx Discord REST response into an operator-readable {@link DiscordRestError}.
|
|
38
|
+
* Decodes the Discord JSON `code` into a remediation hint for the actionable cases
|
|
39
|
+
* (permissions 50013, missing access 50001, DM-blocked 50007, unknown-resource
|
|
40
|
+
* 100xx, rate-limit 429); everything else surfaces the raw Discord `message`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function decodeDiscordRestError(status: number, body: unknown): DiscordRestError;
|
|
43
|
+
/** Options every REST helper accepts: the resolved token + an injectable fetch. */
|
|
44
|
+
export interface DiscordRestOptions {
|
|
45
|
+
/** The resolved bot token (Bot-prefix tolerated; stripped here). NEVER logged. */
|
|
46
|
+
token: string;
|
|
47
|
+
/** Injectable fetch — defaults to global fetch; tests pass a stub. */
|
|
48
|
+
fetchImpl?: typeof fetch;
|
|
49
|
+
/** Request timeout in ms (default 12s). */
|
|
50
|
+
timeoutMs?: number;
|
|
51
|
+
}
|
|
52
|
+
interface RequestInput {
|
|
53
|
+
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
54
|
+
/** Path under `/api/v10` (leading slash required), e.g. `/channels/123/messages`. */
|
|
55
|
+
path: string;
|
|
56
|
+
/** JSON body (object) — serialized + sent with a JSON content-type. Omit for GET/DELETE. */
|
|
57
|
+
body?: unknown;
|
|
58
|
+
/** Audit-log reason → `X-Audit-Log-Reason` header (moderation actions use it). */
|
|
59
|
+
reason?: string;
|
|
60
|
+
/** Query params appended to the path. */
|
|
61
|
+
query?: Record<string, string | number | undefined>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Make one Discord REST call. Returns the parsed JSON (or `{ ok: true }` for an
|
|
65
|
+
* empty 204). Non-2xx throws a decoded {@link DiscordRestError}. Aborts after
|
|
66
|
+
* `timeoutMs`. The token rides only in the `Authorization` header.
|
|
67
|
+
*/
|
|
68
|
+
export declare function discordRestRequest(input: RequestInput, opts: DiscordRestOptions): Promise<unknown>;
|
|
69
|
+
/** A rich-embed spec the model can pass to `send` / `send-embed`. All fields optional. */
|
|
70
|
+
export interface DiscordEmbedSpec {
|
|
71
|
+
title?: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
/** Decimal color int (e.g. 0x5865f2 = 5793266). */
|
|
74
|
+
color?: number;
|
|
75
|
+
url?: string;
|
|
76
|
+
fields?: Array<{
|
|
77
|
+
name: string;
|
|
78
|
+
value: string;
|
|
79
|
+
inline?: boolean;
|
|
80
|
+
}>;
|
|
81
|
+
footer?: string;
|
|
82
|
+
image?: string;
|
|
83
|
+
thumbnail?: string;
|
|
84
|
+
}
|
|
85
|
+
/** Build a Discord embed object from a {@link DiscordEmbedSpec} (drops empty fields). */
|
|
86
|
+
export declare function buildEmbed(spec: DiscordEmbedSpec): Record<string, unknown>;
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a `to` target to a channel id for the messaging endpoints. A bare
|
|
89
|
+
* snowflake is used directly as a channel id. A `user:<id>` target opens (or
|
|
90
|
+
* reuses) a DM channel via `POST /users/@me/channels` and returns that id, so the
|
|
91
|
+
* agent can DM a user by id without knowing the DM channel id. A `channel:<id>`
|
|
92
|
+
* prefix is accepted and stripped.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveSendChannelId(to: string, opts: DiscordRestOptions): Promise<string>;
|
|
95
|
+
/**
|
|
96
|
+
* The wire-shape `allowed_mentions` Discord expects (snake_case). Mirrors the
|
|
97
|
+
* connection-path {@link safeDiscordAllowedMentions}: `parse` whitelists which
|
|
98
|
+
* mention CLASSES notify; omitting `"everyone"` is what kills the `@everyone` /
|
|
99
|
+
* `@here` mass-ping vector.
|
|
100
|
+
*/
|
|
101
|
+
export interface DiscordRestAllowedMentions {
|
|
102
|
+
parse?: Array<"users" | "roles" | "everyone">;
|
|
103
|
+
users?: string[];
|
|
104
|
+
roles?: string[];
|
|
105
|
+
replied_user?: boolean;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The SAFE default `allowed_mentions` applied to every REST send body. Matches the
|
|
109
|
+
* connection-path safety: `parse: ["users", "roles"]` lets explicit `<@id>` /
|
|
110
|
+
* `<@&roleid>` mentions ping, while the absence of `"everyone"` means an
|
|
111
|
+
* `@everyone` / `@here` that slipped into the content renders as inert text and
|
|
112
|
+
* pings no one. A fresh object per call so a send can't mutate the shared default.
|
|
113
|
+
*/
|
|
114
|
+
export declare function safeRestAllowedMentions(): DiscordRestAllowedMentions;
|
|
115
|
+
export declare function sendMessage(params: {
|
|
116
|
+
to: string;
|
|
117
|
+
content?: string;
|
|
118
|
+
embeds?: DiscordEmbedSpec[];
|
|
119
|
+
components?: unknown[];
|
|
120
|
+
replyTo?: string;
|
|
121
|
+
silent?: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Extra message flags to OR in (e.g. the Components-V2 flag 1<<15). The
|
|
124
|
+
* silent flag is added on top of these. A V2 message must NOT carry plain
|
|
125
|
+
* `content` — the caller moves text into TextDisplay blocks.
|
|
126
|
+
*/
|
|
127
|
+
flags?: number;
|
|
128
|
+
/**
|
|
129
|
+
* Opt-in `allowed_mentions` override. Omit for the SAFE default (users + roles,
|
|
130
|
+
* NO everyone) so `@everyone` in `content` can't mass-ping; pass an explicit
|
|
131
|
+
* shape only for a deliberate, owner-authorized broadcast.
|
|
132
|
+
*/
|
|
133
|
+
allowedMentions?: DiscordRestAllowedMentions;
|
|
134
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
135
|
+
export declare function sendEmbed(params: {
|
|
136
|
+
to: string;
|
|
137
|
+
embed: DiscordEmbedSpec;
|
|
138
|
+
content?: string;
|
|
139
|
+
allowedMentions?: DiscordRestAllowedMentions;
|
|
140
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
141
|
+
export declare function sendPoll(params: {
|
|
142
|
+
to: string;
|
|
143
|
+
question: string;
|
|
144
|
+
answers: string[];
|
|
145
|
+
durationHours?: number;
|
|
146
|
+
allowMultiselect?: boolean;
|
|
147
|
+
allowedMentions?: DiscordRestAllowedMentions;
|
|
148
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
149
|
+
export declare function sendSticker(params: {
|
|
150
|
+
to: string;
|
|
151
|
+
stickerIds: string[];
|
|
152
|
+
content?: string;
|
|
153
|
+
allowedMentions?: DiscordRestAllowedMentions;
|
|
154
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
155
|
+
/** Fetch the most recent messages from a channel (capped at 50). */
|
|
156
|
+
export declare function readMessages(params: {
|
|
157
|
+
channelId: string;
|
|
158
|
+
limit?: number;
|
|
159
|
+
before?: string;
|
|
160
|
+
after?: string;
|
|
161
|
+
around?: string;
|
|
162
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
163
|
+
/** Who reacted with a given emoji (capped at 50). `emoji` is the raw unicode or `name:id`. */
|
|
164
|
+
export declare function listReactions(params: {
|
|
165
|
+
channelId: string;
|
|
166
|
+
messageId: string;
|
|
167
|
+
emoji: string;
|
|
168
|
+
limit?: number;
|
|
169
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
170
|
+
/** Remove the bot's own reaction (or, with `userId`, another user's). */
|
|
171
|
+
export declare function removeReaction(params: {
|
|
172
|
+
channelId: string;
|
|
173
|
+
messageId: string;
|
|
174
|
+
emoji: string;
|
|
175
|
+
userId?: string;
|
|
176
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
177
|
+
/** Create a thread — from a message (when `messageId` given) or standalone/forum. */
|
|
178
|
+
export declare function threadCreate(params: {
|
|
179
|
+
channelId: string;
|
|
180
|
+
name: string;
|
|
181
|
+
messageId?: string;
|
|
182
|
+
autoArchiveMinutes?: number;
|
|
183
|
+
type?: number;
|
|
184
|
+
content?: string;
|
|
185
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
186
|
+
/** List active threads in a guild (active threads endpoint). */
|
|
187
|
+
export declare function listThreads(params: {
|
|
188
|
+
guildId: string;
|
|
189
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
190
|
+
/** Guild message search (capped at 25). */
|
|
191
|
+
export declare function searchMessages(params: {
|
|
192
|
+
guildId: string;
|
|
193
|
+
query: string;
|
|
194
|
+
authorId?: string;
|
|
195
|
+
channelId?: string;
|
|
196
|
+
limit?: number;
|
|
197
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
198
|
+
export declare function channelCreate(params: {
|
|
199
|
+
guildId: string;
|
|
200
|
+
name: string;
|
|
201
|
+
type?: number;
|
|
202
|
+
parentId?: string;
|
|
203
|
+
topic?: string;
|
|
204
|
+
position?: number;
|
|
205
|
+
nsfw?: boolean;
|
|
206
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
207
|
+
export declare function channelEdit(params: {
|
|
208
|
+
channelId: string;
|
|
209
|
+
name?: string;
|
|
210
|
+
topic?: string;
|
|
211
|
+
position?: number;
|
|
212
|
+
parentId?: string;
|
|
213
|
+
nsfw?: boolean;
|
|
214
|
+
rateLimitPerUser?: number;
|
|
215
|
+
archived?: boolean;
|
|
216
|
+
locked?: boolean;
|
|
217
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
218
|
+
export declare function channelDelete(params: {
|
|
219
|
+
channelId: string;
|
|
220
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
221
|
+
/** Move a channel (position + optional new parent) via the guild channel-positions endpoint. */
|
|
222
|
+
export declare function channelMove(params: {
|
|
223
|
+
guildId: string;
|
|
224
|
+
channelId: string;
|
|
225
|
+
position?: number;
|
|
226
|
+
parentId?: string;
|
|
227
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
228
|
+
/** Create a category (channel type 4). */
|
|
229
|
+
export declare function categoryCreate(params: {
|
|
230
|
+
guildId: string;
|
|
231
|
+
name: string;
|
|
232
|
+
position?: number;
|
|
233
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
234
|
+
export declare function categoryEdit(params: {
|
|
235
|
+
categoryId: string;
|
|
236
|
+
name?: string;
|
|
237
|
+
position?: number;
|
|
238
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
239
|
+
export declare function categoryDelete(params: {
|
|
240
|
+
categoryId: string;
|
|
241
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
242
|
+
export declare function roleList(params: {
|
|
243
|
+
guildId: string;
|
|
244
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
245
|
+
export declare function roleAdd(params: {
|
|
246
|
+
guildId: string;
|
|
247
|
+
userId: string;
|
|
248
|
+
roleId: string;
|
|
249
|
+
reason?: string;
|
|
250
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
251
|
+
export declare function roleRemove(params: {
|
|
252
|
+
guildId: string;
|
|
253
|
+
userId: string;
|
|
254
|
+
roleId: string;
|
|
255
|
+
reason?: string;
|
|
256
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
257
|
+
/** Alias of {@link roleList} — surfaced as the `role-info` action. */
|
|
258
|
+
export declare function roleInfo(params: {
|
|
259
|
+
guildId: string;
|
|
260
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
261
|
+
export declare function memberInfo(params: {
|
|
262
|
+
guildId: string;
|
|
263
|
+
userId: string;
|
|
264
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
265
|
+
export declare function emojiList(params: {
|
|
266
|
+
guildId: string;
|
|
267
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
268
|
+
/** Upload a custom emoji. `image` MUST be a data URI (`data:image/png;base64,...`). */
|
|
269
|
+
export declare function emojiUpload(params: {
|
|
270
|
+
guildId: string;
|
|
271
|
+
name: string;
|
|
272
|
+
image: string;
|
|
273
|
+
roleIds?: string[];
|
|
274
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
275
|
+
/**
|
|
276
|
+
* Upload a guild sticker. Discord requires `multipart/form-data` here (the sticker
|
|
277
|
+
* file is a binary part), so this is the one helper that does NOT go through the
|
|
278
|
+
* JSON `discordRestRequest`. `file` is the raw bytes; `contentType` is the
|
|
279
|
+
* MIME type (image/png, image/apng, application/json for Lottie).
|
|
280
|
+
*/
|
|
281
|
+
export declare function stickerUpload(params: {
|
|
282
|
+
guildId: string;
|
|
283
|
+
name: string;
|
|
284
|
+
description: string;
|
|
285
|
+
tags: string;
|
|
286
|
+
file: Uint8Array;
|
|
287
|
+
contentType: string;
|
|
288
|
+
filename?: string;
|
|
289
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
290
|
+
export declare function eventList(params: {
|
|
291
|
+
guildId: string;
|
|
292
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
293
|
+
/**
|
|
294
|
+
* Create a guild scheduled event. `entityType`: 1=stage, 2=voice, 3=external.
|
|
295
|
+
* A voice/stage event needs `channelId`; an external event needs `location` +
|
|
296
|
+
* `endTime`. Times are ISO-8601 strings.
|
|
297
|
+
*/
|
|
298
|
+
export declare function eventCreate(params: {
|
|
299
|
+
guildId: string;
|
|
300
|
+
name: string;
|
|
301
|
+
startTime: string;
|
|
302
|
+
endTime?: string;
|
|
303
|
+
description?: string;
|
|
304
|
+
channelId?: string;
|
|
305
|
+
location?: string;
|
|
306
|
+
entityType?: "stage" | "voice" | "external";
|
|
307
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
308
|
+
/**
|
|
309
|
+
* Ban a guild member. `deleteMessageDays` (0–7) purges their recent messages.
|
|
310
|
+
* Owner-only is the tool gate; Discord enforces the bot's own BAN_MEMBERS
|
|
311
|
+
* permission (a 50013 is decoded into a readable hint).
|
|
312
|
+
*/
|
|
313
|
+
export declare function ban(params: {
|
|
314
|
+
guildId: string;
|
|
315
|
+
userId: string;
|
|
316
|
+
reason?: string;
|
|
317
|
+
deleteMessageDays?: number;
|
|
318
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
319
|
+
export declare function unban(params: {
|
|
320
|
+
guildId: string;
|
|
321
|
+
userId: string;
|
|
322
|
+
reason?: string;
|
|
323
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
324
|
+
export declare function kick(params: {
|
|
325
|
+
guildId: string;
|
|
326
|
+
userId: string;
|
|
327
|
+
reason?: string;
|
|
328
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
329
|
+
/**
|
|
330
|
+
* Time a member out for `durationMinutes` from now (max 28 days), or clear it with
|
|
331
|
+
* {@link untimeout}. Sets `communication_disabled_until` to an ISO timestamp.
|
|
332
|
+
*/
|
|
333
|
+
export declare function timeout(params: {
|
|
334
|
+
guildId: string;
|
|
335
|
+
userId: string;
|
|
336
|
+
durationMinutes: number;
|
|
337
|
+
reason?: string;
|
|
338
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
339
|
+
/** Clear a member's timeout (sets `communication_disabled_until` to null). */
|
|
340
|
+
export declare function untimeout(params: {
|
|
341
|
+
guildId: string;
|
|
342
|
+
userId: string;
|
|
343
|
+
reason?: string;
|
|
344
|
+
}, opts: DiscordRestOptions): Promise<unknown>;
|
|
345
|
+
export {};
|
|
346
|
+
//# sourceMappingURL=rest-actions.d.ts.map
|