@kodelyth/discord 2026.5.39 → 2026.6.1
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/account-inspect-Dqw-enky.js +81 -0
- package/dist/account-inspect-api.js +10 -0
- package/dist/accounts-B7OBFePq.js +224 -0
- package/dist/action-runtime-api.js +2 -0
- package/dist/agent-components.runtime-DVY_1VB4.js +4 -0
- package/dist/allow-list-B0s7evD7.js +354 -0
- package/dist/api-CXAcv9nZ.js +130 -0
- package/dist/api.js +23 -0
- package/dist/approval-handler.runtime-B9xUAF3n.js +426 -0
- package/dist/audit-DoiK49WO.js +24 -0
- package/dist/audit-core-BGrq3G7r.js +105 -0
- package/dist/channel-U_aeoFwW.js +795 -0
- package/dist/channel-actions-BxEBnEuv.js +173 -0
- package/dist/channel-actions.runtime-CPtpH-yl.js +263 -0
- package/dist/channel-api-BfjklLby.js +21 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.setup-BUSC0apv.js +337 -0
- package/dist/components-luonoe13.js +909 -0
- package/dist/config-api-DSYGqaLQ.js +2 -0
- package/dist/config-schema-DIqJBGwC.js +357 -0
- package/dist/configured-state.js +6 -0
- package/dist/contract-api.js +8 -0
- package/dist/conversation-identity-DXAm0_Mk.js +270 -0
- package/dist/directory-config-CYbuMmPS.js +49 -0
- package/dist/directory-contract-api.js +2 -0
- package/dist/directory-live-DX4dLRpJ.js +159 -0
- package/dist/doctor-bbKSvGVD.js +244 -0
- package/dist/doctor-contract-Btjt6NJD.js +383 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/gateway-registry-BKSpa4GB.js +74 -0
- package/dist/handle-action.guild-admin-B5BArS2n.js +286 -0
- package/dist/inbound-context-WAOqhGlT.js +48 -0
- package/dist/inbound-event-delivery-C-1Ji3WP.js +65 -0
- package/dist/index.js +26 -0
- package/dist/manager.runtime-DXHynKE4.js +2356 -0
- package/dist/message-handler-mXzc3tA_.js +381 -0
- package/dist/message-handler.preflight-BPD1a347.js +1113 -0
- package/dist/message-handler.process-GUa3aV8z.js +1438 -0
- package/dist/message-utils-dUbem16p.js +549 -0
- package/dist/outbound-adapter-C18OAc1y.js +536 -0
- package/dist/pluralkit-D1Q2x0w5.js +22 -0
- package/dist/preflight-audio-CZtpWcIm.js +72 -0
- package/dist/preflight-audio.runtime-Brx_0_xW.js +7 -0
- package/dist/preview-streaming-D_slNIiO.js +8 -0
- package/dist/probe-D--Ca4JF.js +139 -0
- package/dist/probe.runtime-DQBchZzv.js +2 -0
- package/dist/provider-B2-31CIT.js +9565 -0
- package/dist/provider-session.runtime-BwzzSsrH.js +6 -0
- package/dist/provider.runtime-CP3oHLls.js +2 -0
- package/dist/resolve-allowlist-common-CqxPLcJO.js +34 -0
- package/dist/resolve-channels-0LX4pUbB.js +265 -0
- package/dist/resolve-users-CztOv0Qs.js +120 -0
- package/dist/runtime-DUaw66V_.js +1073 -0
- package/dist/runtime-api.actions.js +3 -0
- package/dist/runtime-api.js +30 -0
- package/dist/runtime-api.lookup.js +7 -0
- package/dist/runtime-api.monitor-CvVKvEXW.js +5 -0
- package/dist/runtime-api.monitor.js +8 -0
- package/dist/runtime-api.send.js +6 -0
- package/dist/runtime-api.threads.js +6 -0
- package/dist/runtime-fC6f4UF2.js +8 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/secret-config-contract-B6WW5V88.js +115 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-CnyIQKz6.js +120 -0
- package/dist/security-audit-contract-api.js +2 -0
- package/dist/security-audit.runtime-CQSkjNLu.js +2 -0
- package/dist/security-contract-DLvYOgLM.js +26 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/security-doctor-DepqtNCI.js +18 -0
- package/dist/send-DCtPCHGk.js +881 -0
- package/dist/send.components-Bcgxvm52.js +474 -0
- package/dist/send.outbound-S9t0UuHc.js +330 -0
- package/dist/send.receipt-CDn3GBWC.js +3119 -0
- package/dist/send.shared-D4iBnAmn.js +669 -0
- package/dist/sender-identity-CxCe3_1a.js +43 -0
- package/dist/session-contract-Dwhw3RTY.js +6 -0
- package/dist/session-key-api.js +2 -0
- package/dist/session-key-normalization-CP8dPUid.js +23 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/shared-AIlvuZXt.js +171 -0
- package/dist/subagent-hooks-8bK-mgiU.js +120 -0
- package/dist/subagent-hooks-api.js +22 -0
- package/dist/system-events-Ba1TklaL.js +34 -0
- package/dist/target-resolver-BrtFQtoK.js +82 -0
- package/dist/targets-DWLLZE2l.js +3 -0
- package/dist/test-api.js +45 -0
- package/dist/thread-binding-api.js +4 -0
- package/dist/thread-bindings-9aKRmZv0.js +255 -0
- package/dist/thread-bindings.discord-api-ssGH5wc2.js +244 -0
- package/dist/thread-bindings.manager-0YBHGemk.js +534 -0
- package/dist/thread-bindings.session-updates-DJZGIwaU.js +54 -0
- package/dist/thread-bindings.state-eTFl-PqJ.js +318 -0
- package/dist/timeouts-CEwuGaWT.js +52 -0
- package/dist/timeouts.js +2 -0
- package/dist/typing-BmJKRpCS.js +14 -0
- package/package.json +19 -7
- package/account-inspect-api.js +0 -7
- package/action-runtime-api.js +0 -7
- package/api.js +0 -7
- package/channel-config-api.js +0 -7
- package/channel-plugin-api.js +0 -7
- package/configured-state.js +0 -7
- package/contract-api.js +0 -7
- package/directory-contract-api.js +0 -7
- package/doctor-contract-api.js +0 -7
- package/index.js +0 -7
- package/runtime-api.actions.js +0 -7
- package/runtime-api.js +0 -7
- package/runtime-api.lookup.js +0 -7
- package/runtime-api.monitor.js +0 -7
- package/runtime-api.send.js +0 -7
- package/runtime-api.threads.js +0 -7
- package/runtime-setter-api.js +0 -7
- package/secret-contract-api.js +0 -7
- package/security-audit-contract-api.js +0 -7
- package/security-contract-api.js +0 -7
- package/session-key-api.js +0 -7
- package/setup-entry.js +0 -7
- package/setup-plugin-api.js +0 -7
- package/subagent-hooks-api.js +0 -7
- package/test-api.js +0 -7
- package/thread-binding-api.js +0 -7
- package/timeouts.js +0 -7
|
@@ -0,0 +1,1113 @@
|
|
|
1
|
+
import { A as Message, c as discord_exports, mt as getChannelMessage } from "./send.receipt-CDn3GBWC.js";
|
|
2
|
+
import { o as resolveDefaultDiscordAccountId } from "./accounts-B7OBFePq.js";
|
|
3
|
+
import { S as resolveTimestampMs, _ as resolveGroupDmAllow, a as normalizeDiscordSlug, b as formatDiscordUserTag, c as resolveDiscordChannelConfigWithFallback, d as resolveDiscordGuildEntry, f as resolveDiscordMemberAccessState, g as resolveDiscordShouldRequireMention, i as normalizeDiscordDisplaySlug, n as isDiscordGroupAllowedByPolicy, x as resolveDiscordSystemLocation } from "./allow-list-B0s7evD7.js";
|
|
4
|
+
import { t as resolveDiscordConversationIdentity } from "./conversation-identity-DXAm0_Mk.js";
|
|
5
|
+
import { l as isRecentlyUnboundThreadWebhookMessage } from "./thread-bindings.state-eTFl-PqJ.js";
|
|
6
|
+
import { d as resolveDiscordChannelNameSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-ssGH5wc2.js";
|
|
7
|
+
import "./thread-bindings-9aKRmZv0.js";
|
|
8
|
+
import { C as resolveDiscordDmCommandAccess, f as buildDiscordRoutePeer, g as handleDiscordDmCommandDecision, h as shouldIgnoreStaleDiscordRouteBinding, m as resolveDiscordEffectiveRoute, p as resolveDiscordConversationRoute, w as resolveDiscordTextCommandAccess } from "./provider-B2-31CIT.js";
|
|
9
|
+
import { d as resolveDiscordMessageChannelId, l as resolveDiscordMessageStickers, o as resolveMediaList, r as resolveDiscordMessageText, u as resolveDiscordChannelInfo } from "./message-utils-dUbem16p.js";
|
|
10
|
+
import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-CxCe3_1a.js";
|
|
11
|
+
import { normalizeOptionalString } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
12
|
+
import { getChildLogger, logVerbose, shouldLogVerbose } from "klaw/plugin-sdk/runtime-env";
|
|
13
|
+
import { recordChannelActivity } from "klaw/plugin-sdk/channel-activity-runtime";
|
|
14
|
+
import { formatAllowlistMatchMeta } from "klaw/plugin-sdk/allow-from";
|
|
15
|
+
import { isDangerousNameMatchingEnabled } from "klaw/plugin-sdk/dangerous-name-runtime";
|
|
16
|
+
import { enqueueSystemEvent } from "klaw/plugin-sdk/system-event-runtime";
|
|
17
|
+
import { mimeTypeFromFilePath } from "klaw/plugin-sdk/media-mime";
|
|
18
|
+
import { buildMentionRegexes, classifyChannelInboundEvent, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, resolveInboundMentionDecision, resolveUnmentionedGroupInboundPolicy, toInboundMediaFacts } from "klaw/plugin-sdk/channel-inbound";
|
|
19
|
+
import { logDebug } from "klaw/plugin-sdk/logging-core";
|
|
20
|
+
import { recordDroppedChannelTurnHistory } from "klaw/plugin-sdk/inbound-reply-dispatch";
|
|
21
|
+
import { hasControlCommand } from "klaw/plugin-sdk/command-detection";
|
|
22
|
+
import { isAbortRequestText } from "klaw/plugin-sdk/command-primitives-runtime";
|
|
23
|
+
import { shouldHandleTextCommands } from "klaw/plugin-sdk/command-surface";
|
|
24
|
+
//#region extensions/discord/src/monitor/message-handler.dm-preflight.ts
|
|
25
|
+
let conversationRuntimePromise$1;
|
|
26
|
+
let discordSendRuntimePromise;
|
|
27
|
+
async function loadConversationRuntime$1() {
|
|
28
|
+
conversationRuntimePromise$1 ??= import("klaw/plugin-sdk/conversation-binding-runtime");
|
|
29
|
+
return await conversationRuntimePromise$1;
|
|
30
|
+
}
|
|
31
|
+
async function loadDiscordSendRuntime() {
|
|
32
|
+
discordSendRuntimePromise ??= import("./send-DCtPCHGk.js").then((n) => n.t);
|
|
33
|
+
return await discordSendRuntimePromise;
|
|
34
|
+
}
|
|
35
|
+
async function resolveDiscordDmPreflightAccess(params) {
|
|
36
|
+
if (params.dmPolicy === "disabled") {
|
|
37
|
+
logVerbose("discord: drop dm (dmPolicy: disabled)");
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const directBindingConversationId = resolveDiscordConversationIdentity({
|
|
41
|
+
isDirectMessage: true,
|
|
42
|
+
userId: params.author.id
|
|
43
|
+
}) ?? `user:${params.author.id}`;
|
|
44
|
+
const directBindingRecord = (await loadConversationRuntime$1()).getSessionBindingService().resolveByConversation({
|
|
45
|
+
channel: "discord",
|
|
46
|
+
accountId: params.preflight.accountId,
|
|
47
|
+
conversationId: directBindingConversationId
|
|
48
|
+
});
|
|
49
|
+
const dmAccess = await resolveDiscordDmCommandAccess({
|
|
50
|
+
accountId: params.resolvedAccountId,
|
|
51
|
+
dmPolicy: params.dmPolicy,
|
|
52
|
+
configuredAllowFrom: params.preflight.allowFrom ?? [],
|
|
53
|
+
sender: {
|
|
54
|
+
id: params.sender.id,
|
|
55
|
+
name: params.sender.name,
|
|
56
|
+
tag: params.sender.tag
|
|
57
|
+
},
|
|
58
|
+
allowNameMatching: params.allowNameMatching,
|
|
59
|
+
cfg: params.preflight.cfg,
|
|
60
|
+
token: params.preflight.token,
|
|
61
|
+
rest: params.preflight.client.rest
|
|
62
|
+
});
|
|
63
|
+
const commandAuthorized = dmAccess.senderAccess.allowed && dmAccess.commandAccess.authorized || directBindingRecord != null;
|
|
64
|
+
if (dmAccess.senderAccess.decision === "allow") return { commandAuthorized };
|
|
65
|
+
if (directBindingRecord) {
|
|
66
|
+
logVerbose(`discord: allow bound DM conversation ${directBindingConversationId} despite dmPolicy=${params.dmPolicy}`);
|
|
67
|
+
return { commandAuthorized };
|
|
68
|
+
}
|
|
69
|
+
await handleDiscordDmCommandDecision({
|
|
70
|
+
senderAccess: dmAccess.senderAccess,
|
|
71
|
+
accountId: params.resolvedAccountId,
|
|
72
|
+
sender: {
|
|
73
|
+
id: params.author.id,
|
|
74
|
+
tag: formatDiscordUserTag(params.author),
|
|
75
|
+
name: params.author.username ?? void 0
|
|
76
|
+
},
|
|
77
|
+
onPairingCreated: async (code) => {
|
|
78
|
+
logVerbose(`discord pairing request sender=${params.author.id} tag=${formatDiscordUserTag(params.author)} reason=${dmAccess.senderAccess.reasonCode}`);
|
|
79
|
+
try {
|
|
80
|
+
const conversationRuntime = await loadConversationRuntime$1();
|
|
81
|
+
const { sendMessageDiscord } = await loadDiscordSendRuntime();
|
|
82
|
+
await sendMessageDiscord(`user:${params.author.id}`, conversationRuntime.buildPairingReply({
|
|
83
|
+
channel: "discord",
|
|
84
|
+
idLine: `Your Discord user id: ${params.author.id}`,
|
|
85
|
+
code
|
|
86
|
+
}), {
|
|
87
|
+
cfg: params.preflight.cfg,
|
|
88
|
+
token: params.preflight.token,
|
|
89
|
+
rest: params.preflight.client.rest,
|
|
90
|
+
accountId: params.preflight.accountId
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
logVerbose(`discord pairing reply failed for ${params.author.id}: ${String(err)}`);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
onUnauthorized: async () => {
|
|
97
|
+
logVerbose(`Blocked unauthorized discord sender ${params.sender.id} (dmPolicy=${params.dmPolicy}, reason=${dmAccess.senderAccess.reasonCode})`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region extensions/discord/src/monitor/message-handler.hydration.ts
|
|
104
|
+
function mergeFetchedDiscordMessage(base, fetched) {
|
|
105
|
+
const baseRawData = readMessageRawData(base);
|
|
106
|
+
const baseFallback = readMessageFallback(base);
|
|
107
|
+
const rawData = {
|
|
108
|
+
...baseRawData,
|
|
109
|
+
...fetched,
|
|
110
|
+
id: fetched.id ?? baseRawData.id ?? baseFallback.id,
|
|
111
|
+
channel_id: fetched.channel_id ?? baseRawData.channel_id ?? baseFallback.channel_id,
|
|
112
|
+
content: fetched.content ?? baseRawData.content ?? baseFallback.content,
|
|
113
|
+
author: fetched.author ?? baseRawData.author ?? baseFallback.author,
|
|
114
|
+
attachments: fetched.attachments ?? baseRawData.attachments ?? baseFallback.attachments,
|
|
115
|
+
embeds: fetched.embeds ?? baseRawData.embeds ?? baseFallback.embeds,
|
|
116
|
+
mentions: fetched.mentions ?? baseRawData.mentions ?? baseFallback.mentions,
|
|
117
|
+
mention_roles: fetched.mention_roles ?? baseRawData.mention_roles ?? baseFallback.mention_roles,
|
|
118
|
+
mention_everyone: fetched.mention_everyone ?? baseRawData.mention_everyone ?? baseFallback.mention_everyone,
|
|
119
|
+
timestamp: fetched.timestamp ?? baseRawData.timestamp ?? baseFallback.timestamp,
|
|
120
|
+
tts: fetched.tts ?? baseRawData.tts ?? false,
|
|
121
|
+
pinned: fetched.pinned ?? baseRawData.pinned ?? false,
|
|
122
|
+
type: fetched.type ?? baseRawData.type ?? 0,
|
|
123
|
+
message_snapshots: fetched.message_snapshots ?? baseRawData.message_snapshots ?? baseFallback.message_snapshots,
|
|
124
|
+
sticker_items: fetched.sticker_items ?? baseRawData.sticker_items ?? baseFallback.sticker_items
|
|
125
|
+
};
|
|
126
|
+
const hydrated = new Message(readMessageClient(base), rawData);
|
|
127
|
+
copyRuntimeMessageFields(base, hydrated);
|
|
128
|
+
return hydrated;
|
|
129
|
+
}
|
|
130
|
+
function readMessageClient(message) {
|
|
131
|
+
return message.client;
|
|
132
|
+
}
|
|
133
|
+
function readMessageRawData(message) {
|
|
134
|
+
try {
|
|
135
|
+
const rawData = message.rawData;
|
|
136
|
+
return rawData && typeof rawData === "object" ? rawData : {};
|
|
137
|
+
} catch {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function readMessageFallback(message) {
|
|
142
|
+
const value = message;
|
|
143
|
+
return {
|
|
144
|
+
id: typeof value.id === "string" ? value.id : "",
|
|
145
|
+
channel_id: readString(value.channel_id) ?? readString(value.channelId) ?? "",
|
|
146
|
+
content: typeof value.content === "string" ? value.content : "",
|
|
147
|
+
author: normalizeApiUser(value.author),
|
|
148
|
+
attachments: Array.isArray(value.attachments) ? value.attachments : [],
|
|
149
|
+
embeds: Array.isArray(value.embeds) ? value.embeds : [],
|
|
150
|
+
mentions: normalizeApiUsers(value.mentionedUsers),
|
|
151
|
+
mention_roles: normalizeStringArray(value.mentionedRoles),
|
|
152
|
+
mention_everyone: value.mentionedEveryone === true,
|
|
153
|
+
timestamp: readString(value.timestamp) ?? "1970-01-01T00:00:00.000Z",
|
|
154
|
+
sticker_items: Array.isArray(value.sticker_items) ? value.sticker_items : Array.isArray(value.stickers) ? value.stickers : void 0,
|
|
155
|
+
message_snapshots: Array.isArray(value.message_snapshots) ? value.message_snapshots : void 0
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function readString(value) {
|
|
159
|
+
return typeof value === "string" ? value : void 0;
|
|
160
|
+
}
|
|
161
|
+
function normalizeStringArray(value) {
|
|
162
|
+
return Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" ? [entry] : []) : [];
|
|
163
|
+
}
|
|
164
|
+
function normalizeApiUsers(value) {
|
|
165
|
+
return Array.isArray(value) ? value.flatMap((entry) => {
|
|
166
|
+
const user = normalizeApiUser(entry);
|
|
167
|
+
return user.id ? [user] : [];
|
|
168
|
+
}) : [];
|
|
169
|
+
}
|
|
170
|
+
function normalizeApiUser(value) {
|
|
171
|
+
if (!value || typeof value !== "object") return {
|
|
172
|
+
id: "",
|
|
173
|
+
username: "",
|
|
174
|
+
discriminator: "0",
|
|
175
|
+
global_name: null,
|
|
176
|
+
avatar: null
|
|
177
|
+
};
|
|
178
|
+
const input = value;
|
|
179
|
+
return {
|
|
180
|
+
id: readString(input.id) ?? "",
|
|
181
|
+
username: readString(input.username) ?? "",
|
|
182
|
+
discriminator: readString(input.discriminator) ?? "0",
|
|
183
|
+
global_name: readString(input.global_name) ?? readString(input.globalName) ?? null,
|
|
184
|
+
avatar: input.avatar === null ? null : readString(input.avatar) ?? null,
|
|
185
|
+
...typeof input.bot === "boolean" ? { bot: input.bot } : {}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function copyRuntimeMessageFields(source, target) {
|
|
189
|
+
const channelDescriptor = Object.getOwnPropertyDescriptor(source, "channel");
|
|
190
|
+
if (channelDescriptor) Object.defineProperty(target, "channel", channelDescriptor);
|
|
191
|
+
}
|
|
192
|
+
function shouldHydrateDiscordMessage(params) {
|
|
193
|
+
let currentText = "";
|
|
194
|
+
try {
|
|
195
|
+
currentText = resolveDiscordMessageText(params.message, { includeForwarded: true });
|
|
196
|
+
} catch {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
if (!currentText) return true;
|
|
200
|
+
if ((params.message.mentionedUsers?.length ?? 0) > 0 || (params.message.mentionedRoles?.length ?? 0) > 0 || params.message.mentionedEveryone) return false;
|
|
201
|
+
return /<@!?\d+>|<@&\d+>|@everyone|@here/u.test(currentText);
|
|
202
|
+
}
|
|
203
|
+
async function hydrateDiscordMessageIfNeeded(params) {
|
|
204
|
+
params.channelInfo;
|
|
205
|
+
if (!shouldHydrateDiscordMessage({ message: params.message })) return params.message;
|
|
206
|
+
try {
|
|
207
|
+
const fetched = await getChannelMessage(params.client.rest, params.messageChannelId, params.message.id);
|
|
208
|
+
if (!fetched) return params.message;
|
|
209
|
+
logVerbose(`discord: hydrated inbound payload via REST for ${params.message.id}`);
|
|
210
|
+
return mergeFetchedDiscordMessage(params.message, fetched);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
logVerbose(`discord: failed to hydrate message ${params.message.id}: ${String(err)}`);
|
|
213
|
+
return params.message;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-channel-access.ts
|
|
218
|
+
function resolveDiscordPreflightChannelAccess(params) {
|
|
219
|
+
if (params.isGuildMessage && params.channelConfig?.enabled === false) {
|
|
220
|
+
logDebug(`[discord-preflight] drop: channel disabled`);
|
|
221
|
+
logVerbose(`Blocked discord channel ${params.messageChannelId} (channel disabled, ${params.channelMatchMeta})`);
|
|
222
|
+
return {
|
|
223
|
+
allowed: false,
|
|
224
|
+
channelAllowlistConfigured: false,
|
|
225
|
+
channelAllowed: false
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const groupDmAllowed = params.isGroupDm && resolveGroupDmAllow({
|
|
229
|
+
channels: params.groupDmChannels,
|
|
230
|
+
channelId: params.messageChannelId,
|
|
231
|
+
channelName: params.displayChannelName,
|
|
232
|
+
channelSlug: params.displayChannelSlug
|
|
233
|
+
});
|
|
234
|
+
if (params.isGroupDm && !groupDmAllowed) return {
|
|
235
|
+
allowed: false,
|
|
236
|
+
channelAllowlistConfigured: false,
|
|
237
|
+
channelAllowed: false
|
|
238
|
+
};
|
|
239
|
+
const channelAllowlistConfigured = Boolean(params.guildInfo?.channels) && Object.keys(params.guildInfo?.channels ?? {}).length > 0;
|
|
240
|
+
const channelAllowed = params.channelConfig?.allowed !== false;
|
|
241
|
+
if (params.isGuildMessage && !isDiscordGroupAllowedByPolicy({
|
|
242
|
+
groupPolicy: params.groupPolicy,
|
|
243
|
+
guildAllowlisted: Boolean(params.guildInfo),
|
|
244
|
+
channelAllowlistConfigured,
|
|
245
|
+
channelAllowed
|
|
246
|
+
})) {
|
|
247
|
+
if (params.groupPolicy === "disabled") {
|
|
248
|
+
logDebug(`[discord-preflight] drop: groupPolicy disabled`);
|
|
249
|
+
logVerbose(`discord: drop guild message (groupPolicy: disabled, ${params.channelMatchMeta})`);
|
|
250
|
+
} else if (!channelAllowlistConfigured) {
|
|
251
|
+
logDebug(`[discord-preflight] drop: groupPolicy allowlist, no channel allowlist configured`);
|
|
252
|
+
logVerbose(`discord: drop guild message (groupPolicy: allowlist, no channel allowlist, ${params.channelMatchMeta})`);
|
|
253
|
+
} else {
|
|
254
|
+
logDebug(`[discord] Ignored message from channel ${params.messageChannelId} (not in guild allowlist). Add to guilds.<guildId>.channels to enable.`);
|
|
255
|
+
logVerbose(`Blocked discord channel ${params.messageChannelId} not in guild channel allowlist (groupPolicy: allowlist, ${params.channelMatchMeta})`);
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
allowed: false,
|
|
259
|
+
channelAllowlistConfigured,
|
|
260
|
+
channelAllowed
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (params.isGuildMessage && params.channelConfig?.allowed === false) {
|
|
264
|
+
logDebug(`[discord-preflight] drop: channelConfig.allowed===false`);
|
|
265
|
+
logVerbose(`Blocked discord channel ${params.messageChannelId} not in guild channel allowlist (${params.channelMatchMeta})`);
|
|
266
|
+
return {
|
|
267
|
+
allowed: false,
|
|
268
|
+
channelAllowlistConfigured,
|
|
269
|
+
channelAllowed
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (params.isGuildMessage) {
|
|
273
|
+
logDebug(`[discord-preflight] pass: channel allowed`);
|
|
274
|
+
logVerbose(`discord: allow channel ${params.messageChannelId} (${params.channelMatchMeta})`);
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
allowed: true,
|
|
278
|
+
channelAllowlistConfigured,
|
|
279
|
+
channelAllowed
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-channel-context.ts
|
|
284
|
+
function resolveDiscordPreflightChannelContext(params) {
|
|
285
|
+
const threadName = params.threadChannel?.name;
|
|
286
|
+
const configChannelName = params.threadParentName ?? params.channelName;
|
|
287
|
+
const configChannelSlug = configChannelName ? normalizeDiscordSlug(configChannelName) : "";
|
|
288
|
+
const displayChannelName = threadName ?? params.channelName;
|
|
289
|
+
const displayChannelSlug = displayChannelName ? normalizeDiscordDisplaySlug(displayChannelName) : "";
|
|
290
|
+
const guildSlug = params.guildInfo?.slug || (params.guildName ? normalizeDiscordSlug(params.guildName) : "");
|
|
291
|
+
const threadChannelSlug = params.channelName ? normalizeDiscordSlug(params.channelName) : "";
|
|
292
|
+
const threadParentSlug = params.threadParentName ? normalizeDiscordSlug(params.threadParentName) : "";
|
|
293
|
+
return {
|
|
294
|
+
threadName,
|
|
295
|
+
configChannelName,
|
|
296
|
+
configChannelSlug,
|
|
297
|
+
displayChannelName,
|
|
298
|
+
displayChannelSlug,
|
|
299
|
+
guildSlug,
|
|
300
|
+
threadChannelSlug,
|
|
301
|
+
threadParentSlug,
|
|
302
|
+
channelConfig: params.isGuildMessage ? resolveDiscordChannelConfigWithFallback({
|
|
303
|
+
guildInfo: params.guildInfo,
|
|
304
|
+
channelId: params.messageChannelId,
|
|
305
|
+
channelName: params.channelName,
|
|
306
|
+
channelSlug: threadChannelSlug,
|
|
307
|
+
parentId: params.threadParentId,
|
|
308
|
+
parentName: params.threadParentName,
|
|
309
|
+
parentSlug: threadParentSlug,
|
|
310
|
+
scope: params.threadChannel ? "thread" : "channel"
|
|
311
|
+
}) : null
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-context.ts
|
|
316
|
+
function buildDiscordMessagePreflightContext({ preflightParams, ...fields }) {
|
|
317
|
+
return {
|
|
318
|
+
cfg: preflightParams.cfg,
|
|
319
|
+
discordConfig: preflightParams.discordConfig,
|
|
320
|
+
accountId: preflightParams.accountId,
|
|
321
|
+
token: preflightParams.token,
|
|
322
|
+
runtime: preflightParams.runtime,
|
|
323
|
+
botUserId: preflightParams.botUserId,
|
|
324
|
+
abortSignal: preflightParams.abortSignal,
|
|
325
|
+
guildHistories: preflightParams.guildHistories,
|
|
326
|
+
historyLimit: preflightParams.historyLimit,
|
|
327
|
+
mediaMaxBytes: preflightParams.mediaMaxBytes,
|
|
328
|
+
textLimit: preflightParams.textLimit,
|
|
329
|
+
replyToMode: preflightParams.replyToMode,
|
|
330
|
+
ackReactionScope: preflightParams.ackReactionScope,
|
|
331
|
+
groupPolicy: preflightParams.groupPolicy,
|
|
332
|
+
...fields,
|
|
333
|
+
threadBindings: preflightParams.threadBindings,
|
|
334
|
+
discordRestFetch: preflightParams.discordRestFetch
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-helpers.ts
|
|
339
|
+
const DISCORD_BOUND_THREAD_SYSTEM_PREFIXES = [
|
|
340
|
+
"⚙️",
|
|
341
|
+
"🤖",
|
|
342
|
+
"🧰"
|
|
343
|
+
];
|
|
344
|
+
function isBoundThreadBotSystemMessage(params) {
|
|
345
|
+
if (!params.isBoundThreadSession || !params.isBotAuthor) return false;
|
|
346
|
+
const text = params.text?.trim();
|
|
347
|
+
if (!text) return false;
|
|
348
|
+
return DISCORD_BOUND_THREAD_SYSTEM_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
349
|
+
}
|
|
350
|
+
function isDiscordThreadChannelType(type) {
|
|
351
|
+
return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
|
|
352
|
+
}
|
|
353
|
+
function isDiscordThreadChannelMessage(params) {
|
|
354
|
+
if (!params.isGuildMessage) return false;
|
|
355
|
+
const channel = "channel" in params.message ? params.message.channel : void 0;
|
|
356
|
+
return Boolean(channel && typeof channel === "object" && "isThread" in channel && typeof channel.isThread === "function" && channel.isThread() || isDiscordThreadChannelType(params.channelInfo?.type));
|
|
357
|
+
}
|
|
358
|
+
function resolveInjectedBoundThreadLookupRecord(params) {
|
|
359
|
+
const getByThreadId = params.threadBindings.getByThreadId;
|
|
360
|
+
if (typeof getByThreadId !== "function") return;
|
|
361
|
+
const binding = getByThreadId(params.threadId);
|
|
362
|
+
return binding && typeof binding === "object" ? binding : void 0;
|
|
363
|
+
}
|
|
364
|
+
function resolveDiscordMentionState(params) {
|
|
365
|
+
if (params.isDirectMessage) return {
|
|
366
|
+
implicitMentionKinds: [],
|
|
367
|
+
wasMentioned: false
|
|
368
|
+
};
|
|
369
|
+
const wasMentioned = params.mentionedEveryone && (!params.authorIsBot || params.senderIsPluralKit) || matchesMentionWithExplicit({
|
|
370
|
+
text: params.mentionText,
|
|
371
|
+
mentionRegexes: params.mentionRegexes,
|
|
372
|
+
explicit: {
|
|
373
|
+
hasAnyMention: params.hasAnyMention,
|
|
374
|
+
isExplicitlyMentioned: params.isExplicitlyMentioned,
|
|
375
|
+
canResolveExplicit: Boolean(params.botId)
|
|
376
|
+
},
|
|
377
|
+
transcript: params.transcript
|
|
378
|
+
});
|
|
379
|
+
return {
|
|
380
|
+
implicitMentionKinds: implicitMentionKindWhen("reply_to_bot", Boolean(params.botId) && Boolean(params.referencedAuthorId) && params.referencedAuthorId === params.botId),
|
|
381
|
+
wasMentioned
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function resolvePreflightMentionRequirement(params) {
|
|
385
|
+
if (!params.shouldRequireMention) return false;
|
|
386
|
+
return !params.bypassMentionRequirement;
|
|
387
|
+
}
|
|
388
|
+
function shouldIgnoreBoundThreadWebhookMessage(params) {
|
|
389
|
+
const webhookId = normalizeOptionalString(params.webhookId) ?? "";
|
|
390
|
+
if (!webhookId) return false;
|
|
391
|
+
const boundWebhookId = normalizeOptionalString(params.threadBinding?.webhookId) ?? normalizeOptionalString(params.threadBinding?.metadata?.webhookId) ?? "";
|
|
392
|
+
if (boundWebhookId && webhookId === boundWebhookId) return true;
|
|
393
|
+
const threadId = normalizeOptionalString(params.threadId) ?? "";
|
|
394
|
+
if (!threadId) return false;
|
|
395
|
+
if (params.threadBinding) return true;
|
|
396
|
+
return isRecentlyUnboundThreadWebhookMessage({
|
|
397
|
+
accountId: params.accountId,
|
|
398
|
+
threadId,
|
|
399
|
+
webhookId
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-history.ts
|
|
404
|
+
function buildDiscordPreflightHistoryEntry(params) {
|
|
405
|
+
const textForHistory = resolveDiscordMessageText(params.message, { includeForwarded: true });
|
|
406
|
+
return params.isGuildMessage && params.historyLimit > 0 && textForHistory ? {
|
|
407
|
+
sender: params.senderLabel,
|
|
408
|
+
body: textForHistory,
|
|
409
|
+
timestamp: resolveTimestampMs(params.message.timestamp),
|
|
410
|
+
messageId: params.message.id
|
|
411
|
+
} : void 0;
|
|
412
|
+
}
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-logging.ts
|
|
415
|
+
function logDiscordPreflightChannelConfig(params) {
|
|
416
|
+
if (!shouldLogVerbose()) return;
|
|
417
|
+
logDebug(`[discord-preflight] channelConfig=${params.channelConfig ? `allowed=${params.channelConfig.allowed} enabled=${params.channelConfig.enabled ?? "unset"} requireMention=${params.channelConfig.requireMention ?? "unset"} ignoreOtherMentions=${params.channelConfig.ignoreOtherMentions ?? "unset"} matchKey=${params.channelConfig.matchKey ?? "none"} matchSource=${params.channelConfig.matchSource ?? "none"} users=${params.channelConfig.users?.length ?? 0} roles=${params.channelConfig.roles?.length ?? 0} skills=${params.channelConfig.skills?.length ?? 0}` : "none"} channelMatchMeta=${params.channelMatchMeta} channelId=${params.channelId}`);
|
|
418
|
+
}
|
|
419
|
+
function logDiscordPreflightInboundSummary(params) {
|
|
420
|
+
if (!shouldLogVerbose()) return;
|
|
421
|
+
logVerbose(`discord: inbound id=${params.messageId} guild=${params.guildId ?? "dm"} channel=${params.channelId} mention=${params.wasMentioned ? "yes" : "no"} type=${params.isDirectMessage ? "dm" : params.isGroupDm ? "group-dm" : "guild"} content=${params.hasContent ? "yes" : "no"}`);
|
|
422
|
+
}
|
|
423
|
+
//#endregion
|
|
424
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-runtime.ts
|
|
425
|
+
let pluralkitRuntimePromise;
|
|
426
|
+
let preflightAudioRuntimePromise;
|
|
427
|
+
let systemEventsRuntimePromise;
|
|
428
|
+
let discordThreadingRuntimePromise;
|
|
429
|
+
async function loadPluralKitRuntime() {
|
|
430
|
+
pluralkitRuntimePromise ??= import("./pluralkit-D1Q2x0w5.js").then((n) => n.n);
|
|
431
|
+
return await pluralkitRuntimePromise;
|
|
432
|
+
}
|
|
433
|
+
async function loadPreflightAudioRuntime() {
|
|
434
|
+
preflightAudioRuntimePromise ??= import("./preflight-audio-CZtpWcIm.js");
|
|
435
|
+
return await preflightAudioRuntimePromise;
|
|
436
|
+
}
|
|
437
|
+
async function loadSystemEventsRuntime() {
|
|
438
|
+
systemEventsRuntimePromise ??= import("./system-events-Ba1TklaL.js");
|
|
439
|
+
return await systemEventsRuntimePromise;
|
|
440
|
+
}
|
|
441
|
+
async function loadDiscordThreadingRuntime() {
|
|
442
|
+
discordThreadingRuntimePromise ??= import("./provider-B2-31CIT.js").then((n) => n.v);
|
|
443
|
+
return await discordThreadingRuntimePromise;
|
|
444
|
+
}
|
|
445
|
+
function isPreflightAborted(abortSignal) {
|
|
446
|
+
return Boolean(abortSignal?.aborted);
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-pluralkit.ts
|
|
450
|
+
async function resolveDiscordPreflightPluralKitInfo(params) {
|
|
451
|
+
if (!params.config?.enabled) return null;
|
|
452
|
+
try {
|
|
453
|
+
const { fetchPluralKitMessageInfo } = await loadPluralKitRuntime();
|
|
454
|
+
const info = await fetchPluralKitMessageInfo({
|
|
455
|
+
messageId: params.message.id,
|
|
456
|
+
config: params.config
|
|
457
|
+
});
|
|
458
|
+
return isPreflightAborted(params.abortSignal) ? null : info;
|
|
459
|
+
} catch (err) {
|
|
460
|
+
logVerbose(`discord: pluralkit lookup failed for ${params.message.id}: ${String(err)}`);
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region extensions/discord/src/monitor/message-handler.preflight-thread.ts
|
|
466
|
+
async function resolveDiscordPreflightThreadContext(params) {
|
|
467
|
+
const { resolveDiscordThreadChannel, resolveDiscordThreadParentInfo } = await loadDiscordThreadingRuntime();
|
|
468
|
+
const earlyThreadChannel = resolveDiscordThreadChannel({
|
|
469
|
+
isGuildMessage: params.isGuildMessage,
|
|
470
|
+
message: params.message,
|
|
471
|
+
channelInfo: params.channelInfo,
|
|
472
|
+
messageChannelId: params.messageChannelId
|
|
473
|
+
});
|
|
474
|
+
if (!earlyThreadChannel) return { earlyThreadChannel: null };
|
|
475
|
+
const parentInfo = await resolveDiscordThreadParentInfo({
|
|
476
|
+
client: params.client,
|
|
477
|
+
threadChannel: earlyThreadChannel,
|
|
478
|
+
channelInfo: params.channelInfo
|
|
479
|
+
});
|
|
480
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
481
|
+
return {
|
|
482
|
+
earlyThreadChannel,
|
|
483
|
+
earlyThreadParentId: parentInfo.id,
|
|
484
|
+
earlyThreadParentName: parentInfo.name,
|
|
485
|
+
earlyThreadParentType: parentInfo.type
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region extensions/discord/src/monitor/message-handler.routing-preflight.ts
|
|
490
|
+
let conversationRuntimePromise;
|
|
491
|
+
async function loadConversationRuntime() {
|
|
492
|
+
conversationRuntimePromise ??= import("klaw/plugin-sdk/conversation-binding-runtime");
|
|
493
|
+
return await conversationRuntimePromise;
|
|
494
|
+
}
|
|
495
|
+
async function resolveDiscordPreflightRoute(params) {
|
|
496
|
+
const conversationRuntime = await loadConversationRuntime();
|
|
497
|
+
const route = resolveDiscordConversationRoute({
|
|
498
|
+
cfg: params.preflight.cfg,
|
|
499
|
+
accountId: params.preflight.accountId,
|
|
500
|
+
guildId: params.preflight.data.guild_id ?? void 0,
|
|
501
|
+
memberRoleIds: params.memberRoleIds,
|
|
502
|
+
peer: buildDiscordRoutePeer({
|
|
503
|
+
isDirectMessage: params.isDirectMessage,
|
|
504
|
+
isGroupDm: params.isGroupDm,
|
|
505
|
+
directUserId: params.author.id,
|
|
506
|
+
conversationId: params.messageChannelId
|
|
507
|
+
}),
|
|
508
|
+
parentConversationId: params.earlyThreadParentId
|
|
509
|
+
});
|
|
510
|
+
const bindingConversationId = params.isDirectMessage ? resolveDiscordConversationIdentity({
|
|
511
|
+
isDirectMessage: true,
|
|
512
|
+
userId: params.author.id
|
|
513
|
+
}) ?? `user:${params.author.id}` : params.messageChannelId;
|
|
514
|
+
let runtimeRoute = conversationRuntime.resolveRuntimeConversationBindingRoute({
|
|
515
|
+
route,
|
|
516
|
+
conversation: {
|
|
517
|
+
channel: "discord",
|
|
518
|
+
accountId: params.preflight.accountId,
|
|
519
|
+
conversationId: bindingConversationId,
|
|
520
|
+
parentConversationId: params.earlyThreadParentId
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
if (shouldIgnoreStaleDiscordRouteBinding({
|
|
524
|
+
bindingRecord: runtimeRoute.bindingRecord,
|
|
525
|
+
route
|
|
526
|
+
})) {
|
|
527
|
+
logVerbose(`discord: ignoring stale route binding for conversation ${bindingConversationId} (${runtimeRoute.bindingRecord?.targetSessionKey} -> ${route.sessionKey})`);
|
|
528
|
+
runtimeRoute = {
|
|
529
|
+
bindingRecord: null,
|
|
530
|
+
route
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
let threadBinding = runtimeRoute.bindingRecord ?? void 0;
|
|
534
|
+
const configuredRoute = threadBinding == null ? conversationRuntime.resolveConfiguredBindingRoute({
|
|
535
|
+
cfg: params.preflight.cfg,
|
|
536
|
+
route,
|
|
537
|
+
conversation: {
|
|
538
|
+
channel: "discord",
|
|
539
|
+
accountId: params.preflight.accountId,
|
|
540
|
+
conversationId: params.messageChannelId,
|
|
541
|
+
parentConversationId: params.earlyThreadParentId
|
|
542
|
+
}
|
|
543
|
+
}) : null;
|
|
544
|
+
const configuredBinding = configuredRoute?.bindingResolution ?? null;
|
|
545
|
+
if (!threadBinding && configuredBinding) threadBinding = configuredBinding.record;
|
|
546
|
+
const boundSessionKey = conversationRuntime.isPluginOwnedSessionBindingRecord(threadBinding) ? "" : runtimeRoute.boundSessionKey ?? threadBinding?.targetSessionKey?.trim();
|
|
547
|
+
const effectiveRoute = runtimeRoute.boundSessionKey ? runtimeRoute.route : resolveDiscordEffectiveRoute({
|
|
548
|
+
route,
|
|
549
|
+
boundSessionKey,
|
|
550
|
+
configuredRoute,
|
|
551
|
+
matchedBy: "binding.channel"
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
conversationRuntime,
|
|
555
|
+
threadBinding,
|
|
556
|
+
configuredBinding,
|
|
557
|
+
boundSessionKey,
|
|
558
|
+
effectiveRoute,
|
|
559
|
+
boundAgentId: boundSessionKey ? effectiveRoute.agentId : void 0,
|
|
560
|
+
baseSessionKey: effectiveRoute.sessionKey
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region extensions/discord/src/monitor/message-handler.preflight.ts
|
|
565
|
+
const DISCORD_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
|
|
566
|
+
const DISCORD_HISTORY_MEDIA_MAX_BYTES = 10 * 1024 * 1024;
|
|
567
|
+
const DISCORD_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
|
|
568
|
+
const DISCORD_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
|
|
569
|
+
function resolveDiscordPreflightConversationKind(params) {
|
|
570
|
+
const isGroupDm = params.channelType === discord_exports.ChannelType.GroupDM;
|
|
571
|
+
return {
|
|
572
|
+
isDirectMessage: params.channelType === discord_exports.ChannelType.DM || !params.isGuildMessage && !isGroupDm && params.channelType == null,
|
|
573
|
+
isGroupDm
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function isDiscordImageAttachmentCandidate(attachment) {
|
|
577
|
+
if ((attachment.content_type?.split(";")[0]?.trim().toLowerCase())?.startsWith("image/")) return true;
|
|
578
|
+
return Boolean(mimeTypeFromFilePath(attachment.filename)?.startsWith("image/") || mimeTypeFromFilePath(attachment.url)?.startsWith("image/"));
|
|
579
|
+
}
|
|
580
|
+
async function resolveDiscordHistoryMediaForPendingRecord(params) {
|
|
581
|
+
const imageAttachments = (params.message.attachments ?? []).filter(isDiscordImageAttachmentCandidate).slice(0, DISCORD_HISTORY_MEDIA_MAX_ATTACHMENTS);
|
|
582
|
+
const stickers = resolveDiscordMessageStickers(params.message).slice(0, Math.max(0, DISCORD_HISTORY_MEDIA_MAX_ATTACHMENTS - imageAttachments.length));
|
|
583
|
+
if (imageAttachments.length === 0 && stickers.length === 0) return [];
|
|
584
|
+
const rawData = (() => {
|
|
585
|
+
try {
|
|
586
|
+
return params.message.rawData;
|
|
587
|
+
} catch {
|
|
588
|
+
return {};
|
|
589
|
+
}
|
|
590
|
+
})();
|
|
591
|
+
const mediaMessage = Object.assign(Object.create(Object.getPrototypeOf(params.message)), params.message);
|
|
592
|
+
Object.defineProperties(mediaMessage, {
|
|
593
|
+
attachments: { value: imageAttachments },
|
|
594
|
+
rawData: { value: {
|
|
595
|
+
...rawData,
|
|
596
|
+
attachments: imageAttachments,
|
|
597
|
+
sticker_items: stickers,
|
|
598
|
+
stickers
|
|
599
|
+
} },
|
|
600
|
+
stickers: { value: stickers }
|
|
601
|
+
});
|
|
602
|
+
return toInboundMediaFacts(await resolveMediaList(mediaMessage, Math.min(params.preflight.mediaMaxBytes, DISCORD_HISTORY_MEDIA_MAX_BYTES), {
|
|
603
|
+
fetchImpl: params.preflight.discordRestFetch,
|
|
604
|
+
ssrfPolicy: params.preflight.cfg.browser?.ssrfPolicy,
|
|
605
|
+
readIdleTimeoutMs: DISCORD_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
|
|
606
|
+
totalTimeoutMs: DISCORD_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
|
|
607
|
+
abortSignal: params.preflight.abortSignal
|
|
608
|
+
}), {
|
|
609
|
+
kind: "image",
|
|
610
|
+
messageId: params.message.id
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
async function recordDiscordPendingHistoryEntry(params) {
|
|
614
|
+
if (params.preflight.historyLimit <= 0) return;
|
|
615
|
+
await recordDroppedChannelTurnHistory({
|
|
616
|
+
input: {
|
|
617
|
+
id: params.message.id,
|
|
618
|
+
timestamp: params.entry?.timestamp,
|
|
619
|
+
rawText: params.entry?.body ?? "",
|
|
620
|
+
textForAgent: params.entry?.body,
|
|
621
|
+
raw: params.message
|
|
622
|
+
},
|
|
623
|
+
admission: {
|
|
624
|
+
kind: "drop",
|
|
625
|
+
reason: "discord-preflight",
|
|
626
|
+
recordHistory: true
|
|
627
|
+
},
|
|
628
|
+
preflight: {
|
|
629
|
+
message: params.entry ? {
|
|
630
|
+
rawBody: params.entry.body,
|
|
631
|
+
body: params.entry.body,
|
|
632
|
+
bodyForAgent: params.entry.body,
|
|
633
|
+
senderLabel: params.entry.sender,
|
|
634
|
+
envelopeFrom: params.entry.sender
|
|
635
|
+
} : void 0,
|
|
636
|
+
history: {
|
|
637
|
+
key: params.historyKey,
|
|
638
|
+
historyMap: params.preflight.guildHistories,
|
|
639
|
+
limit: params.preflight.historyLimit,
|
|
640
|
+
recordOnDrop: true,
|
|
641
|
+
mediaLimit: DISCORD_HISTORY_MEDIA_MAX_ATTACHMENTS,
|
|
642
|
+
shouldRecord: () => !isPreflightAborted(params.preflight.abortSignal)
|
|
643
|
+
},
|
|
644
|
+
media: () => resolveDiscordHistoryMediaForPendingRecord({
|
|
645
|
+
preflight: params.preflight,
|
|
646
|
+
message: params.message
|
|
647
|
+
})
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
async function preflightDiscordMessage(params) {
|
|
652
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
653
|
+
const logger = getChildLogger({ module: "discord-auto-reply" });
|
|
654
|
+
let message = params.data.message;
|
|
655
|
+
const author = params.data.author;
|
|
656
|
+
if (!author) return null;
|
|
657
|
+
const messageChannelId = resolveDiscordMessageChannelId({
|
|
658
|
+
message,
|
|
659
|
+
eventChannelId: params.data.channel_id
|
|
660
|
+
});
|
|
661
|
+
if (!messageChannelId) {
|
|
662
|
+
logVerbose(`discord: drop message ${message.id} (missing channel id)`);
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
const allowBotsSetting = params.discordConfig?.allowBots;
|
|
666
|
+
const allowBotsMode = allowBotsSetting === "mentions" ? "mentions" : allowBotsSetting === true ? "all" : "off";
|
|
667
|
+
if (params.botUserId && author.id === params.botUserId) return null;
|
|
668
|
+
message = await hydrateDiscordMessageIfNeeded({
|
|
669
|
+
client: params.client,
|
|
670
|
+
message,
|
|
671
|
+
messageChannelId
|
|
672
|
+
});
|
|
673
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
674
|
+
const pluralkitConfig = params.discordConfig?.pluralkit;
|
|
675
|
+
const webhookId = resolveDiscordWebhookId(message);
|
|
676
|
+
const isGuildMessage = Boolean(params.data.guild_id);
|
|
677
|
+
const channelInfo = await resolveDiscordChannelInfo(params.client, messageChannelId);
|
|
678
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
679
|
+
const { isDirectMessage, isGroupDm } = resolveDiscordPreflightConversationKind({
|
|
680
|
+
isGuildMessage,
|
|
681
|
+
channelType: channelInfo?.type
|
|
682
|
+
});
|
|
683
|
+
const messageText = resolveDiscordMessageText(message, { includeForwarded: true });
|
|
684
|
+
const injectedBoundThreadBinding = !isDirectMessage && !isGroupDm ? resolveInjectedBoundThreadLookupRecord({
|
|
685
|
+
threadBindings: params.threadBindings,
|
|
686
|
+
threadId: messageChannelId
|
|
687
|
+
}) : void 0;
|
|
688
|
+
if (shouldIgnoreBoundThreadWebhookMessage({
|
|
689
|
+
accountId: params.accountId,
|
|
690
|
+
threadId: messageChannelId,
|
|
691
|
+
webhookId,
|
|
692
|
+
threadBinding: injectedBoundThreadBinding
|
|
693
|
+
})) {
|
|
694
|
+
logVerbose(`discord: drop bound-thread webhook echo message ${message.id}`);
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
if (isBoundThreadBotSystemMessage({
|
|
698
|
+
isBoundThreadSession: Boolean(injectedBoundThreadBinding) && isDiscordThreadChannelMessage({
|
|
699
|
+
isGuildMessage,
|
|
700
|
+
message,
|
|
701
|
+
channelInfo
|
|
702
|
+
}),
|
|
703
|
+
isBotAuthor: Boolean(author.bot),
|
|
704
|
+
text: messageText
|
|
705
|
+
})) {
|
|
706
|
+
logVerbose(`discord: drop bound-thread bot system message ${message.id}`);
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
const pluralkitInfo = await resolveDiscordPreflightPluralKitInfo({
|
|
710
|
+
message,
|
|
711
|
+
config: pluralkitConfig,
|
|
712
|
+
abortSignal: params.abortSignal
|
|
713
|
+
});
|
|
714
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
715
|
+
const sender = resolveDiscordSenderIdentity({
|
|
716
|
+
author,
|
|
717
|
+
member: params.data.member,
|
|
718
|
+
pluralkitInfo
|
|
719
|
+
});
|
|
720
|
+
if (author.bot) {
|
|
721
|
+
if (allowBotsMode === "off" && !sender.isPluralKit) {
|
|
722
|
+
logVerbose("discord: drop bot message (allowBots=false)");
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
const data = message === params.data.message ? params.data : {
|
|
727
|
+
...params.data,
|
|
728
|
+
message
|
|
729
|
+
};
|
|
730
|
+
logDebug(`[discord-preflight] channelId=${messageChannelId} guild_id=${params.data.guild_id} channelType=${channelInfo?.type} isGuild=${isGuildMessage} isDM=${isDirectMessage} isGroupDm=${isGroupDm}`);
|
|
731
|
+
if (isGroupDm && !params.groupDmEnabled) {
|
|
732
|
+
logVerbose("discord: drop group dm (group dms disabled)");
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
if (isDirectMessage && !params.dmEnabled) {
|
|
736
|
+
logVerbose("discord: drop dm (dms disabled)");
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
const dmPolicy = params.dmPolicy;
|
|
740
|
+
const resolvedAccountId = params.accountId ?? resolveDefaultDiscordAccountId(params.cfg);
|
|
741
|
+
const allowNameMatching = isDangerousNameMatchingEnabled(params.discordConfig);
|
|
742
|
+
let commandAuthorized = true;
|
|
743
|
+
if (isDirectMessage) {
|
|
744
|
+
const access = await resolveDiscordDmPreflightAccess({
|
|
745
|
+
preflight: params,
|
|
746
|
+
author,
|
|
747
|
+
sender,
|
|
748
|
+
dmPolicy,
|
|
749
|
+
resolvedAccountId,
|
|
750
|
+
allowNameMatching
|
|
751
|
+
});
|
|
752
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
753
|
+
if (!access) return null;
|
|
754
|
+
commandAuthorized = access.commandAuthorized;
|
|
755
|
+
}
|
|
756
|
+
const botId = params.botUserId;
|
|
757
|
+
const baseText = resolveDiscordMessageText(message, { includeForwarded: false });
|
|
758
|
+
recordChannelActivity({
|
|
759
|
+
channel: "discord",
|
|
760
|
+
accountId: params.accountId,
|
|
761
|
+
direction: "inbound"
|
|
762
|
+
});
|
|
763
|
+
const channelName = channelInfo?.name ?? (isGuildMessage || isGroupDm ? resolveDiscordChannelNameSafe("channel" in message ? message.channel : void 0) : void 0);
|
|
764
|
+
const threadContext = await resolveDiscordPreflightThreadContext({
|
|
765
|
+
client: params.client,
|
|
766
|
+
isGuildMessage,
|
|
767
|
+
message,
|
|
768
|
+
channelInfo,
|
|
769
|
+
messageChannelId,
|
|
770
|
+
abortSignal: params.abortSignal
|
|
771
|
+
});
|
|
772
|
+
if (!threadContext) return null;
|
|
773
|
+
const { earlyThreadChannel, earlyThreadParentId, earlyThreadParentName, earlyThreadParentType } = threadContext;
|
|
774
|
+
const memberRoleIds = Array.isArray(params.data.rawMember?.roles) ? params.data.rawMember.roles : [];
|
|
775
|
+
const { conversationRuntime, threadBinding, configuredBinding, boundSessionKey, effectiveRoute, boundAgentId, baseSessionKey } = await resolveDiscordPreflightRoute({
|
|
776
|
+
preflight: params,
|
|
777
|
+
author,
|
|
778
|
+
isDirectMessage,
|
|
779
|
+
isGroupDm,
|
|
780
|
+
messageChannelId,
|
|
781
|
+
memberRoleIds,
|
|
782
|
+
earlyThreadParentId
|
|
783
|
+
});
|
|
784
|
+
if (shouldIgnoreBoundThreadWebhookMessage({
|
|
785
|
+
accountId: params.accountId,
|
|
786
|
+
threadId: messageChannelId,
|
|
787
|
+
webhookId,
|
|
788
|
+
threadBinding
|
|
789
|
+
})) {
|
|
790
|
+
logVerbose(`discord: drop bound-thread webhook echo message ${message.id}`);
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
const isBoundThreadSession = Boolean(threadBinding && earlyThreadChannel);
|
|
794
|
+
const bypassMentionRequirement = isBoundThreadSession;
|
|
795
|
+
if (isBoundThreadBotSystemMessage({
|
|
796
|
+
isBoundThreadSession,
|
|
797
|
+
isBotAuthor: Boolean(author.bot),
|
|
798
|
+
text: messageText
|
|
799
|
+
})) {
|
|
800
|
+
logVerbose(`discord: drop bound-thread bot system message ${message.id}`);
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
const mentionRegexes = buildMentionRegexes(params.cfg, effectiveRoute.agentId);
|
|
804
|
+
const explicitlyMentioned = Boolean(botId && message.mentionedUsers?.some((user) => user.id === botId));
|
|
805
|
+
const hasAnyMention = !isDirectMessage && ((message.mentionedUsers?.length ?? 0) > 0 || (message.mentionedRoles?.length ?? 0) > 0 || message.mentionedEveryone && (!author.bot || sender.isPluralKit));
|
|
806
|
+
const hasUserOrRoleMention = !isDirectMessage && ((message.mentionedUsers?.length ?? 0) > 0 || (message.mentionedRoles?.length ?? 0) > 0);
|
|
807
|
+
if (isGuildMessage && (message.type === discord_exports.MessageType.ChatInputCommand || message.type === discord_exports.MessageType.ContextMenuCommand)) {
|
|
808
|
+
logVerbose("discord: drop channel command message");
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
const guildInfo = isGuildMessage ? resolveDiscordGuildEntry({
|
|
812
|
+
guild: params.data.guild ?? void 0,
|
|
813
|
+
guildId: params.data.guild_id ?? void 0,
|
|
814
|
+
guildEntries: params.guildEntries
|
|
815
|
+
}) : null;
|
|
816
|
+
logDebug(`[discord-preflight] guild_id=${params.data.guild_id} guild_obj=${!!params.data.guild} guild_obj_id=${params.data.guild?.id} guildInfo=${!!guildInfo} guildEntries=${params.guildEntries ? Object.keys(params.guildEntries).join(",") : "none"}`);
|
|
817
|
+
if (isGuildMessage && params.guildEntries && Object.keys(params.guildEntries).length > 0 && !guildInfo) {
|
|
818
|
+
logDebug(`[discord-preflight] guild blocked: guild_id=${params.data.guild_id} guildEntries keys=${Object.keys(params.guildEntries).join(",")}`);
|
|
819
|
+
logVerbose(`Blocked discord guild ${params.data.guild_id ?? "unknown"} (not in discord.guilds)`);
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
const threadChannel = earlyThreadChannel;
|
|
823
|
+
const threadParentId = earlyThreadParentId;
|
|
824
|
+
const threadParentName = earlyThreadParentName;
|
|
825
|
+
const threadParentType = earlyThreadParentType;
|
|
826
|
+
const { threadName, configChannelName, configChannelSlug, displayChannelName, displayChannelSlug, guildSlug, channelConfig } = resolveDiscordPreflightChannelContext({
|
|
827
|
+
isGuildMessage,
|
|
828
|
+
messageChannelId,
|
|
829
|
+
channelName,
|
|
830
|
+
guildName: params.data.guild?.name,
|
|
831
|
+
guildInfo,
|
|
832
|
+
threadChannel,
|
|
833
|
+
threadParentId,
|
|
834
|
+
threadParentName
|
|
835
|
+
});
|
|
836
|
+
const channelMatchMeta = formatAllowlistMatchMeta(channelConfig);
|
|
837
|
+
logDiscordPreflightChannelConfig({
|
|
838
|
+
channelConfig,
|
|
839
|
+
channelMatchMeta,
|
|
840
|
+
channelId: messageChannelId
|
|
841
|
+
});
|
|
842
|
+
const channelAccess = resolveDiscordPreflightChannelAccess({
|
|
843
|
+
isGuildMessage,
|
|
844
|
+
isGroupDm,
|
|
845
|
+
groupPolicy: params.groupPolicy,
|
|
846
|
+
groupDmChannels: params.groupDmChannels,
|
|
847
|
+
messageChannelId,
|
|
848
|
+
displayChannelName,
|
|
849
|
+
displayChannelSlug,
|
|
850
|
+
guildInfo,
|
|
851
|
+
channelConfig,
|
|
852
|
+
channelMatchMeta
|
|
853
|
+
});
|
|
854
|
+
if (!channelAccess.allowed) return null;
|
|
855
|
+
const { channelAllowlistConfigured, channelAllowed } = channelAccess;
|
|
856
|
+
const historyEntry = buildDiscordPreflightHistoryEntry({
|
|
857
|
+
isGuildMessage,
|
|
858
|
+
historyLimit: params.historyLimit,
|
|
859
|
+
message,
|
|
860
|
+
senderLabel: sender.label
|
|
861
|
+
});
|
|
862
|
+
const threadOwnerId = threadChannel ? resolveDiscordChannelInfoSafe(threadChannel).ownerId ?? channelInfo?.ownerId : void 0;
|
|
863
|
+
const shouldRequireMentionByConfig = resolveDiscordShouldRequireMention({
|
|
864
|
+
isGuildMessage,
|
|
865
|
+
isThread: Boolean(threadChannel),
|
|
866
|
+
botId,
|
|
867
|
+
threadOwnerId,
|
|
868
|
+
channelConfig,
|
|
869
|
+
guildInfo
|
|
870
|
+
});
|
|
871
|
+
const shouldRequireMention = resolvePreflightMentionRequirement({
|
|
872
|
+
shouldRequireMention: shouldRequireMentionByConfig,
|
|
873
|
+
bypassMentionRequirement
|
|
874
|
+
});
|
|
875
|
+
const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
|
|
876
|
+
channelConfig,
|
|
877
|
+
guildInfo,
|
|
878
|
+
memberRoleIds,
|
|
879
|
+
sender,
|
|
880
|
+
allowNameMatching
|
|
881
|
+
});
|
|
882
|
+
if (isGuildMessage && hasAccessRestrictions && !memberAllowed) {
|
|
883
|
+
logDebug(`[discord-preflight] drop: member not allowed`);
|
|
884
|
+
logVerbose("Blocked discord guild sender (not in users/roles allowlist)");
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
const { resolveDiscordPreflightAudioMentionContext } = await loadPreflightAudioRuntime();
|
|
888
|
+
const { hasTypedText, transcript: preflightTranscript } = await resolveDiscordPreflightAudioMentionContext({
|
|
889
|
+
message,
|
|
890
|
+
isDirectMessage,
|
|
891
|
+
shouldRequireMention,
|
|
892
|
+
mentionRegexes,
|
|
893
|
+
cfg: params.cfg,
|
|
894
|
+
abortSignal: params.abortSignal
|
|
895
|
+
});
|
|
896
|
+
if (isPreflightAborted(params.abortSignal)) return null;
|
|
897
|
+
const mentionText = hasTypedText ? baseText : "";
|
|
898
|
+
const { implicitMentionKinds, wasMentioned } = resolveDiscordMentionState({
|
|
899
|
+
authorIsBot: Boolean(author.bot),
|
|
900
|
+
botId,
|
|
901
|
+
hasAnyMention,
|
|
902
|
+
isDirectMessage,
|
|
903
|
+
isExplicitlyMentioned: explicitlyMentioned,
|
|
904
|
+
mentionRegexes,
|
|
905
|
+
mentionText,
|
|
906
|
+
mentionedEveryone: message.mentionedEveryone,
|
|
907
|
+
referencedAuthorId: message.referencedMessage?.author?.id,
|
|
908
|
+
senderIsPluralKit: sender.isPluralKit,
|
|
909
|
+
transcript: preflightTranscript
|
|
910
|
+
});
|
|
911
|
+
logDiscordPreflightInboundSummary({
|
|
912
|
+
messageId: message.id,
|
|
913
|
+
guildId: params.data.guild_id ?? void 0,
|
|
914
|
+
channelId: messageChannelId,
|
|
915
|
+
wasMentioned,
|
|
916
|
+
isDirectMessage,
|
|
917
|
+
isGroupDm,
|
|
918
|
+
hasContent: Boolean(messageText)
|
|
919
|
+
});
|
|
920
|
+
const allowTextCommands = shouldHandleTextCommands({
|
|
921
|
+
cfg: params.cfg,
|
|
922
|
+
surface: "discord"
|
|
923
|
+
});
|
|
924
|
+
const hasControlCommandInMessage = hasControlCommand(baseText, params.cfg);
|
|
925
|
+
const hasAbortRequest = isAbortRequestText(baseText);
|
|
926
|
+
if (!isDirectMessage) {
|
|
927
|
+
const commandAccess = await resolveDiscordTextCommandAccess({
|
|
928
|
+
accountId: params.accountId,
|
|
929
|
+
cfg: params.cfg,
|
|
930
|
+
ownerAllowFrom: params.allowFrom,
|
|
931
|
+
sender: {
|
|
932
|
+
id: sender.id,
|
|
933
|
+
name: sender.name,
|
|
934
|
+
tag: sender.tag
|
|
935
|
+
},
|
|
936
|
+
memberAccessConfigured: hasAccessRestrictions,
|
|
937
|
+
memberAllowed,
|
|
938
|
+
allowNameMatching,
|
|
939
|
+
allowTextCommands,
|
|
940
|
+
hasControlCommand: hasControlCommandInMessage
|
|
941
|
+
});
|
|
942
|
+
commandAuthorized = commandAccess.authorized;
|
|
943
|
+
if (commandAccess.shouldBlockControlCommand) {
|
|
944
|
+
logInboundDrop({
|
|
945
|
+
log: logVerbose,
|
|
946
|
+
channel: "discord",
|
|
947
|
+
reason: "control command (unauthorized)",
|
|
948
|
+
target: sender.id
|
|
949
|
+
});
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
const canDetectMention = Boolean(botId) || mentionRegexes.length > 0;
|
|
954
|
+
const mentionDecision = resolveInboundMentionDecision({
|
|
955
|
+
facts: {
|
|
956
|
+
canDetectMention,
|
|
957
|
+
wasMentioned,
|
|
958
|
+
hasAnyMention,
|
|
959
|
+
implicitMentionKinds
|
|
960
|
+
},
|
|
961
|
+
policy: {
|
|
962
|
+
isGroup: isGuildMessage,
|
|
963
|
+
requireMention: shouldRequireMention,
|
|
964
|
+
allowTextCommands,
|
|
965
|
+
hasControlCommand: hasControlCommandInMessage,
|
|
966
|
+
commandAuthorized
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
|
|
970
|
+
const inboundEventKind = classifyChannelInboundEvent({
|
|
971
|
+
conversation: { kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel" },
|
|
972
|
+
unmentionedGroupPolicy: resolveUnmentionedGroupInboundPolicy({
|
|
973
|
+
cfg: params.cfg,
|
|
974
|
+
agentId: effectiveRoute.agentId
|
|
975
|
+
}),
|
|
976
|
+
wasMentioned: effectiveWasMentioned,
|
|
977
|
+
hasControlCommand: hasControlCommandInMessage,
|
|
978
|
+
hasAbortRequest
|
|
979
|
+
});
|
|
980
|
+
logDebug(`[discord-preflight] shouldRequireMention=${shouldRequireMention} baseRequireMention=${shouldRequireMentionByConfig} boundThreadSession=${isBoundThreadSession} mentionDecision.shouldSkip=${mentionDecision.shouldSkip} wasMentioned=${wasMentioned}`);
|
|
981
|
+
if (isGuildMessage && shouldRequireMention) {
|
|
982
|
+
if (mentionDecision.shouldSkip) {
|
|
983
|
+
logDebug(`[discord-preflight] drop: no-mention`);
|
|
984
|
+
logVerbose(`discord: drop guild message (mention required, botId=${botId ?? "<missing>"})`);
|
|
985
|
+
logger.info({
|
|
986
|
+
channelId: messageChannelId,
|
|
987
|
+
reason: "no-mention"
|
|
988
|
+
}, "discord: skipping guild message");
|
|
989
|
+
await recordDiscordPendingHistoryEntry({
|
|
990
|
+
preflight: params,
|
|
991
|
+
historyKey: messageChannelId,
|
|
992
|
+
message,
|
|
993
|
+
entry: historyEntry
|
|
994
|
+
});
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (author.bot && !sender.isPluralKit && allowBotsMode === "mentions") {
|
|
999
|
+
if (!(isDirectMessage || wasMentioned || mentionDecision.implicitMention)) {
|
|
1000
|
+
logDebug(`[discord-preflight] drop: bot message missing mention (allowBots=mentions)`);
|
|
1001
|
+
logVerbose("discord: drop bot message (allowBots=mentions, missing mention)");
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const ignoreOtherMentions = channelConfig?.ignoreOtherMentions ?? guildInfo?.ignoreOtherMentions ?? false;
|
|
1006
|
+
if (isGuildMessage && ignoreOtherMentions && hasUserOrRoleMention && !wasMentioned && !mentionDecision.implicitMention) {
|
|
1007
|
+
logDebug(`[discord-preflight] drop: other-mention`);
|
|
1008
|
+
logVerbose(`discord: drop guild message (another user/role mentioned, ignoreOtherMentions=true, botId=${botId})`);
|
|
1009
|
+
await recordDiscordPendingHistoryEntry({
|
|
1010
|
+
preflight: params,
|
|
1011
|
+
historyKey: messageChannelId,
|
|
1012
|
+
message,
|
|
1013
|
+
entry: historyEntry
|
|
1014
|
+
});
|
|
1015
|
+
return null;
|
|
1016
|
+
}
|
|
1017
|
+
const systemLocation = resolveDiscordSystemLocation({
|
|
1018
|
+
isDirectMessage,
|
|
1019
|
+
isGroupDm,
|
|
1020
|
+
guild: params.data.guild ?? void 0,
|
|
1021
|
+
channelName: channelName ?? messageChannelId
|
|
1022
|
+
});
|
|
1023
|
+
const { resolveDiscordSystemEvent } = await loadSystemEventsRuntime();
|
|
1024
|
+
const systemText = resolveDiscordSystemEvent(message, systemLocation);
|
|
1025
|
+
if (systemText) {
|
|
1026
|
+
logDebug(`[discord-preflight] drop: system event`);
|
|
1027
|
+
enqueueSystemEvent(systemText, {
|
|
1028
|
+
sessionKey: effectiveRoute.sessionKey,
|
|
1029
|
+
contextKey: `discord:system:${messageChannelId}:${message.id}`,
|
|
1030
|
+
forceSenderIsOwnerFalse: true,
|
|
1031
|
+
trusted: false
|
|
1032
|
+
});
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
if (!messageText) {
|
|
1036
|
+
logDebug(`[discord-preflight] drop: empty content`);
|
|
1037
|
+
logVerbose(`discord: drop message ${message.id} (empty content)`);
|
|
1038
|
+
return null;
|
|
1039
|
+
}
|
|
1040
|
+
if (configuredBinding) {
|
|
1041
|
+
const ensured = await conversationRuntime.ensureConfiguredBindingRouteReady({
|
|
1042
|
+
cfg: params.cfg,
|
|
1043
|
+
bindingResolution: configuredBinding
|
|
1044
|
+
});
|
|
1045
|
+
if (!ensured.ok) {
|
|
1046
|
+
logVerbose(`discord: configured ACP binding unavailable for channel ${configuredBinding.record.conversation.conversationId}: ${ensured.error}`);
|
|
1047
|
+
return null;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
const botLoopProtection = author.bot && !sender.isPluralKit && allowBotsMode !== "off" && params.botUserId && author.id !== params.botUserId ? {
|
|
1051
|
+
scopeId: params.accountId,
|
|
1052
|
+
conversationId: messageChannelId,
|
|
1053
|
+
senderId: author.id,
|
|
1054
|
+
receiverId: params.botUserId,
|
|
1055
|
+
config: params.discordConfig?.botLoopProtection,
|
|
1056
|
+
defaultsConfig: params.cfg.channels?.defaults?.botLoopProtection,
|
|
1057
|
+
defaultEnabled: true,
|
|
1058
|
+
nowMs: resolveTimestampMs(message.timestamp)
|
|
1059
|
+
} : void 0;
|
|
1060
|
+
logDebug(`[discord-preflight] success: route=${effectiveRoute.agentId} sessionKey=${effectiveRoute.sessionKey}`);
|
|
1061
|
+
return buildDiscordMessagePreflightContext({
|
|
1062
|
+
preflightParams: params,
|
|
1063
|
+
data,
|
|
1064
|
+
client: params.client,
|
|
1065
|
+
message,
|
|
1066
|
+
messageChannelId,
|
|
1067
|
+
author,
|
|
1068
|
+
sender,
|
|
1069
|
+
canonicalMessageId: pluralkitInfo?.original?.trim() || void 0,
|
|
1070
|
+
memberRoleIds,
|
|
1071
|
+
channelInfo,
|
|
1072
|
+
channelName,
|
|
1073
|
+
isGuildMessage,
|
|
1074
|
+
isDirectMessage,
|
|
1075
|
+
isGroupDm,
|
|
1076
|
+
commandAuthorized,
|
|
1077
|
+
baseText,
|
|
1078
|
+
messageText,
|
|
1079
|
+
...preflightTranscript !== void 0 ? { preflightAudioTranscript: preflightTranscript } : {},
|
|
1080
|
+
wasMentioned,
|
|
1081
|
+
route: effectiveRoute,
|
|
1082
|
+
threadBinding,
|
|
1083
|
+
boundSessionKey: boundSessionKey || void 0,
|
|
1084
|
+
boundAgentId,
|
|
1085
|
+
guildInfo,
|
|
1086
|
+
guildSlug,
|
|
1087
|
+
threadChannel,
|
|
1088
|
+
threadParentId,
|
|
1089
|
+
threadParentName,
|
|
1090
|
+
threadParentType,
|
|
1091
|
+
threadName,
|
|
1092
|
+
configChannelName,
|
|
1093
|
+
configChannelSlug,
|
|
1094
|
+
displayChannelName,
|
|
1095
|
+
displayChannelSlug,
|
|
1096
|
+
baseSessionKey,
|
|
1097
|
+
channelConfig,
|
|
1098
|
+
channelAllowlistConfigured,
|
|
1099
|
+
channelAllowed,
|
|
1100
|
+
shouldRequireMention,
|
|
1101
|
+
hasAnyMention,
|
|
1102
|
+
hasControlCommand: hasControlCommandInMessage,
|
|
1103
|
+
allowTextCommands,
|
|
1104
|
+
shouldBypassMention: mentionDecision.shouldBypassMention,
|
|
1105
|
+
effectiveWasMentioned,
|
|
1106
|
+
inboundEventKind,
|
|
1107
|
+
canDetectMention,
|
|
1108
|
+
historyEntry,
|
|
1109
|
+
botLoopProtection
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
//#endregion
|
|
1113
|
+
export { preflightDiscordMessage, resolvePreflightMentionRequirement, shouldIgnoreBoundThreadWebhookMessage };
|