@openclaw/mattermost 2026.7.2-beta.1 → 2026.7.2-beta.2

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.
@@ -1,15 +1,10 @@
1
1
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
- import { resolveChannelPreviewStreamMode, resolveChannelStreamingBlockCoalesce, resolveChannelStreamingBlockEnabled, resolveChannelStreamingChunkMode } from "openclaw/plugin-sdk/channel-outbound";
3
- import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
4
2
  import { buildTimeoutAbortSignal } from "openclaw/plugin-sdk/extension-shared";
5
- import { fetchWithSsrFGuard, ssrfPolicyFromPrivateNetworkOptIn } from "openclaw/plugin-sdk/ssrf-runtime";
6
- import { createAccountListHelpers, hasConfiguredAccountValue } from "openclaw/plugin-sdk/account-helpers";
7
- import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
8
- import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
9
- import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
3
+ import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
10
4
  import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
11
5
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
12
6
  import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
7
+ import { fetchWithSsrFGuard, ssrfPolicyFromPrivateNetworkOptIn } from "openclaw/plugin-sdk/ssrf-runtime";
13
8
  import { z } from "zod";
14
9
  //#region extensions/mattermost/src/mattermost/client.ts
15
10
  const MATTERMOST_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
@@ -388,79 +383,4 @@ async function uploadMattermostFile(client, params) {
388
383
  return info;
389
384
  }
390
385
  //#endregion
391
- //#region extensions/mattermost/src/mattermost/accounts.ts
392
- const mattermostAccountHelpers = createAccountListHelpers("mattermost", { hasImplicitDefaultAccount: (cfg) => {
393
- const mattermost = cfg.channels?.mattermost;
394
- return Boolean(mattermost?.baseUrl?.trim() && (hasConfiguredAccountValue(mattermost.botToken) || process.env.MATTERMOST_BOT_TOKEN?.trim()));
395
- } });
396
- function listMattermostAccountIds(cfg) {
397
- return mattermostAccountHelpers.listAccountIds(cfg);
398
- }
399
- function resolveDefaultMattermostAccountId(cfg) {
400
- return mattermostAccountHelpers.resolveDefaultAccountId(cfg);
401
- }
402
- function mergeMattermostAccountConfig(cfg, accountId) {
403
- return resolveMergedAccountConfig({
404
- channelConfig: cfg.channels?.mattermost,
405
- accounts: cfg.channels?.mattermost?.accounts,
406
- accountId,
407
- omitKeys: ["defaultAccount"],
408
- nestedObjectKeys: ["commands"]
409
- });
410
- }
411
- function resolveMattermostRequireMention(config) {
412
- if (config.chatmode === "oncall") return true;
413
- if (config.chatmode === "onmessage") return false;
414
- if (config.chatmode === "onchar") return true;
415
- return config.requireMention;
416
- }
417
- function resolveMattermostAccount(params) {
418
- const accountId = normalizeAccountId(params.accountId ?? resolveDefaultMattermostAccountId(params.cfg));
419
- const baseEnabled = params.cfg.channels?.mattermost?.enabled !== false;
420
- const merged = mergeMattermostAccountConfig(params.cfg, accountId);
421
- const accountEnabled = merged.enabled !== false;
422
- const enabled = baseEnabled && accountEnabled;
423
- const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
424
- const envToken = allowEnv ? process.env.MATTERMOST_BOT_TOKEN?.trim() : void 0;
425
- const envUrl = allowEnv ? process.env.MATTERMOST_URL?.trim() : void 0;
426
- const configToken = params.allowUnresolvedSecretRef ? normalizeSecretInputString(merged.botToken) : normalizeResolvedSecretInputString({
427
- value: merged.botToken,
428
- path: `channels.mattermost.accounts.${accountId}.botToken`
429
- });
430
- const configUrl = merged.baseUrl?.trim();
431
- const botToken = configToken || envToken;
432
- const baseUrl = normalizeMattermostBaseUrl(configUrl || envUrl);
433
- const requireMention = resolveMattermostRequireMention(merged);
434
- const botTokenSource = configToken ? "config" : envToken ? "env" : "none";
435
- const baseUrlSource = configUrl ? "config" : envUrl ? "env" : "none";
436
- return {
437
- accountId,
438
- enabled,
439
- name: normalizeOptionalString(merged.name),
440
- botToken,
441
- baseUrl,
442
- botTokenSource,
443
- baseUrlSource,
444
- config: merged,
445
- chatmode: merged.chatmode,
446
- oncharPrefixes: merged.oncharPrefixes,
447
- requireMention,
448
- textChunkLimit: merged.textChunkLimit,
449
- chunkMode: resolveChannelStreamingChunkMode(merged),
450
- streamingMode: resolveChannelPreviewStreamMode(merged, "partial"),
451
- blockStreaming: resolveChannelStreamingBlockEnabled(merged),
452
- blockStreamingCoalesce: resolveChannelStreamingBlockCoalesce(merged)
453
- };
454
- }
455
- /**
456
- * Resolve the effective replyToMode for a given chat type.
457
- * Direct messages stay flat unless explicitly opted into a per-chat-type mode.
458
- */
459
- function resolveMattermostReplyToMode(account, kind) {
460
- const scopedMode = account.config.replyToModeByChatType?.[kind];
461
- if (scopedMode !== void 0) return scopedMode;
462
- if (kind === "direct") return "off";
463
- return account.config.replyToMode ?? "off";
464
- }
465
- //#endregion
466
- export { hasConfiguredSecretInput as C, buildSecretInputSchema as S, readMattermostError as _, MattermostPostSchema as a, updateMattermostPost as b, createMattermostPost as c, fetchMattermostChannelByName as d, fetchMattermostMe as f, normalizeMattermostBaseUrl as g, fetchMattermostUserTeams as h, resolveMattermostReplyToMode as i, deleteMattermostPost as l, fetchMattermostUserByUsername as m, resolveDefaultMattermostAccountId as n, createMattermostClient as o, fetchMattermostUser as p, resolveMattermostAccount as r, createMattermostDirectChannelWithRetry as s, listMattermostAccountIds as t, fetchMattermostChannel as u, resolveMattermostReplyDeliveryBarrierTimeoutMs as v, uploadMattermostFile as x, sendMattermostTyping as y };
386
+ export { uploadMattermostFile as _, deleteMattermostPost as a, fetchMattermostMe as c, fetchMattermostUserTeams as d, normalizeMattermostBaseUrl as f, updateMattermostPost as g, sendMattermostTyping as h, createMattermostPost as i, fetchMattermostUser as l, resolveMattermostReplyDeliveryBarrierTimeoutMs as m, createMattermostClient as n, fetchMattermostChannel as o, readMattermostError as p, createMattermostDirectChannelWithRetry as r, fetchMattermostChannelByName as s, MattermostPostSchema as t, fetchMattermostUserByUsername as u };
@@ -0,0 +1,375 @@
1
+ import { t as getMattermostRuntime } from "./runtime-CNB4YGqJ.js";
2
+ import { g as updateMattermostPost } from "./client-BzLj4dyf.js";
3
+ import { b as readRequestBodyWithLimit, m as isTrustedProxyAddress, w as resolveClientIp } from "./monitor-auth-dEHa1_On.js";
4
+ import { createHmac } from "node:crypto";
5
+ import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
6
+ import { normalizeOptionalString, normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ //#region extensions/mattermost/src/mattermost/interactions.ts
8
+ const INTERACTION_MAX_BODY_BYTES = 64 * 1024;
9
+ const INTERACTION_BODY_TIMEOUT_MS = 1e4;
10
+ const SIGNED_CHANNEL_ID_CONTEXT_KEY = "__openclaw_channel_id";
11
+ const callbackUrls = /* @__PURE__ */ new Map();
12
+ function setInteractionCallbackUrl(accountId, url) {
13
+ callbackUrls.set(accountId, url);
14
+ }
15
+ function resolveInteractionCallbackPath(accountId) {
16
+ return `/mattermost/interactions/${accountId}`;
17
+ }
18
+ function isWildcardBindHost(rawHost) {
19
+ const trimmed = rawHost.trim();
20
+ if (!trimmed) return false;
21
+ const host = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
22
+ return host === "0.0.0.0" || host === "::" || host === "0:0:0:0:0:0:0:0" || host === "::0";
23
+ }
24
+ function normalizeCallbackBaseUrl(baseUrl) {
25
+ return baseUrl.trim().replace(/\/+$/, "");
26
+ }
27
+ function headerValue(value) {
28
+ if (Array.isArray(value)) return normalizeOptionalString(value[0]);
29
+ return normalizeOptionalString(value);
30
+ }
31
+ function isAllowedInteractionSource(params) {
32
+ const { allowedSourceIps } = params;
33
+ if (!allowedSourceIps?.length) return true;
34
+ return isTrustedProxyAddress(resolveClientIp({
35
+ remoteAddr: params.req.socket?.remoteAddress,
36
+ forwardedFor: headerValue(params.req.headers["x-forwarded-for"]),
37
+ realIp: headerValue(params.req.headers["x-real-ip"]),
38
+ trustedProxies: params.trustedProxies,
39
+ allowRealIpFallback: params.allowRealIpFallback
40
+ }), allowedSourceIps);
41
+ }
42
+ /**
43
+ * Resolve the interaction callback URL for an account.
44
+ * Falls back to computing it from interactions.callbackBaseUrl or gateway host config.
45
+ */
46
+ function computeInteractionCallbackUrl(accountId, cfg) {
47
+ const path = resolveInteractionCallbackPath(accountId);
48
+ const callbackBaseUrl = normalizeOptionalString(cfg?.interactions?.callbackBaseUrl) ?? normalizeOptionalString(cfg?.channels?.mattermost?.interactions?.callbackBaseUrl);
49
+ if (callbackBaseUrl) return `${normalizeCallbackBaseUrl(callbackBaseUrl)}${path}`;
50
+ const port = typeof cfg?.gateway?.port === "number" ? cfg.gateway.port : 18789;
51
+ let host = cfg?.gateway?.customBindHost && !isWildcardBindHost(cfg.gateway.customBindHost) ? cfg.gateway.customBindHost.trim() : "localhost";
52
+ if (host.includes(":") && !(host.startsWith("[") && host.endsWith("]"))) host = `[${host}]`;
53
+ return `http://${host}:${port}${path}`;
54
+ }
55
+ /**
56
+ * Resolve the interaction callback URL for an account.
57
+ * Prefers the in-memory registered URL (set by the gateway monitor) so callers outside the
58
+ * monitor lifecycle can reuse the runtime-validated callback destination.
59
+ */
60
+ function resolveInteractionCallbackUrl(accountId, cfg) {
61
+ const cached = callbackUrls.get(accountId);
62
+ if (cached) return cached;
63
+ return computeInteractionCallbackUrl(accountId, cfg);
64
+ }
65
+ const interactionSecrets = /* @__PURE__ */ new Map();
66
+ let defaultInteractionSecret;
67
+ function deriveInteractionSecret(botToken) {
68
+ return createHmac("sha256", "openclaw-mattermost-interactions").update(botToken).digest("hex");
69
+ }
70
+ function setInteractionSecret(accountIdOrBotToken, botToken) {
71
+ if (typeof botToken === "string") {
72
+ interactionSecrets.set(accountIdOrBotToken, deriveInteractionSecret(botToken));
73
+ return;
74
+ }
75
+ defaultInteractionSecret = deriveInteractionSecret(accountIdOrBotToken);
76
+ }
77
+ function getInteractionSecret(accountId) {
78
+ const scoped = accountId ? interactionSecrets.get(accountId) : void 0;
79
+ if (scoped) return scoped;
80
+ if (defaultInteractionSecret) return defaultInteractionSecret;
81
+ if (interactionSecrets.size === 1) {
82
+ const first = interactionSecrets.values().next().value;
83
+ if (typeof first === "string") return first;
84
+ }
85
+ throw new Error("Interaction secret not initialized — call setInteractionSecret(accountId, botToken) first");
86
+ }
87
+ function canonicalizeInteractionContext(value) {
88
+ if (Array.isArray(value)) return value.map((item) => canonicalizeInteractionContext(item));
89
+ if (value && typeof value === "object") {
90
+ const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, canonicalizeInteractionContext(entryValue)]);
91
+ return Object.fromEntries(entries);
92
+ }
93
+ return value;
94
+ }
95
+ function generateInteractionToken(context, accountId) {
96
+ const secret = getInteractionSecret(accountId);
97
+ const payload = JSON.stringify(canonicalizeInteractionContext(context));
98
+ return createHmac("sha256", secret).update(payload).digest("hex");
99
+ }
100
+ function verifyInteractionToken(context, token, accountId) {
101
+ return safeEqualSecret(generateInteractionToken(context, accountId), token);
102
+ }
103
+ /**
104
+ * Build Mattermost `props.attachments` with interactive buttons.
105
+ *
106
+ * Each button includes an HMAC token in its integration context so the
107
+ * callback handler can verify the request originated from a legitimate
108
+ * button click (Mattermost's recommended security pattern).
109
+ */
110
+ /**
111
+ * Sanitize a button ID so Mattermost's action router can match it.
112
+ * Mattermost uses the action ID in the URL path `/api/v4/posts/{id}/actions/{actionId}`
113
+ * and IDs containing hyphens or underscores break the server-side routing.
114
+ * See: https://github.com/mattermost/mattermost/issues/25747
115
+ */
116
+ function sanitizeActionId(id) {
117
+ return id.replace(/[-_]/g, "");
118
+ }
119
+ function buildButtonAttachments(params) {
120
+ const actions = params.buttons.map((btn) => {
121
+ const safeId = sanitizeActionId(btn.id);
122
+ const context = {
123
+ action_id: safeId,
124
+ ...btn.context
125
+ };
126
+ const token = generateInteractionToken(context, params.accountId);
127
+ return {
128
+ id: safeId,
129
+ type: "button",
130
+ name: btn.name,
131
+ style: btn.style,
132
+ integration: {
133
+ url: params.callbackUrl,
134
+ context: {
135
+ ...context,
136
+ _token: token
137
+ }
138
+ }
139
+ };
140
+ });
141
+ return [{
142
+ text: params.text ?? "",
143
+ actions
144
+ }];
145
+ }
146
+ function buildButtonProps(params) {
147
+ const buttons = params.buttons.flatMap((item) => Array.isArray(item) ? item : [item]).map((btn) => ({
148
+ id: normalizeStringifiedOptionalString(btn.id ?? btn.callback_data) ?? "",
149
+ name: normalizeStringifiedOptionalString(btn.text ?? btn.name ?? btn.label) ?? "",
150
+ style: btn.style ?? "default",
151
+ context: typeof btn.context === "object" && btn.context !== null ? {
152
+ ...btn.context,
153
+ [SIGNED_CHANNEL_ID_CONTEXT_KEY]: params.channelId
154
+ } : { [SIGNED_CHANNEL_ID_CONTEXT_KEY]: params.channelId }
155
+ })).filter((btn) => btn.id && btn.name);
156
+ if (buttons.length === 0) return;
157
+ return { attachments: buildButtonAttachments({
158
+ callbackUrl: params.callbackUrl,
159
+ accountId: params.accountId,
160
+ buttons,
161
+ text: params.text
162
+ }) };
163
+ }
164
+ function readInteractionBody(req) {
165
+ return readRequestBodyWithLimit(req, {
166
+ maxBytes: INTERACTION_MAX_BODY_BYTES,
167
+ timeoutMs: INTERACTION_BODY_TIMEOUT_MS
168
+ });
169
+ }
170
+ function createMattermostInteractionHandler(params) {
171
+ const { client, accountId, log } = params;
172
+ const core = getMattermostRuntime();
173
+ function parseInteractionPayload(raw) {
174
+ try {
175
+ return JSON.parse(raw);
176
+ } catch {
177
+ throw new Error("Mattermost interaction body was malformed JSON");
178
+ }
179
+ }
180
+ return async (req, res) => {
181
+ if (req.method !== "POST") {
182
+ res.statusCode = 405;
183
+ res.setHeader("Allow", "POST");
184
+ res.setHeader("Content-Type", "application/json");
185
+ res.end(JSON.stringify({ error: "Method Not Allowed" }));
186
+ return;
187
+ }
188
+ if (!isAllowedInteractionSource({
189
+ req,
190
+ allowedSourceIps: params.allowedSourceIps,
191
+ trustedProxies: params.trustedProxies,
192
+ allowRealIpFallback: params.allowRealIpFallback
193
+ })) {
194
+ log?.(`mattermost interaction: rejected callback source remote=${req.socket?.remoteAddress ?? "?"}`);
195
+ res.statusCode = 403;
196
+ res.setHeader("Content-Type", "application/json");
197
+ res.end(JSON.stringify({ error: "Forbidden origin" }));
198
+ return;
199
+ }
200
+ let payload;
201
+ try {
202
+ payload = parseInteractionPayload(await readInteractionBody(req));
203
+ } catch (err) {
204
+ log?.(`mattermost interaction: failed to parse body: ${String(err)}`);
205
+ res.statusCode = 400;
206
+ res.setHeader("Content-Type", "application/json");
207
+ res.end(JSON.stringify({ error: "Invalid request body" }));
208
+ return;
209
+ }
210
+ const context = payload.context;
211
+ if (!context) {
212
+ res.statusCode = 400;
213
+ res.setHeader("Content-Type", "application/json");
214
+ res.end(JSON.stringify({ error: "Missing context" }));
215
+ return;
216
+ }
217
+ const token = context["_token"];
218
+ if (typeof token !== "string") {
219
+ log?.("mattermost interaction: missing _token in context");
220
+ res.statusCode = 403;
221
+ res.setHeader("Content-Type", "application/json");
222
+ res.end(JSON.stringify({ error: "Missing token" }));
223
+ return;
224
+ }
225
+ const { _token, ...contextWithoutToken } = context;
226
+ if (!verifyInteractionToken(contextWithoutToken, token, accountId)) {
227
+ log?.("mattermost interaction: invalid _token");
228
+ res.statusCode = 403;
229
+ res.setHeader("Content-Type", "application/json");
230
+ res.end(JSON.stringify({ error: "Invalid token" }));
231
+ return;
232
+ }
233
+ const actionId = context.action_id;
234
+ if (typeof actionId !== "string") {
235
+ res.statusCode = 400;
236
+ res.setHeader("Content-Type", "application/json");
237
+ res.end(JSON.stringify({ error: "Missing action_id in context" }));
238
+ return;
239
+ }
240
+ const signedChannelId = typeof contextWithoutToken[SIGNED_CHANNEL_ID_CONTEXT_KEY] === "string" ? contextWithoutToken[SIGNED_CHANNEL_ID_CONTEXT_KEY].trim() : "";
241
+ if (signedChannelId && signedChannelId !== payload.channel_id) {
242
+ log?.(`mattermost interaction: signed channel mismatch payload=${payload.channel_id} signed=${signedChannelId}`);
243
+ res.statusCode = 403;
244
+ res.setHeader("Content-Type", "application/json");
245
+ res.end(JSON.stringify({ error: "Channel mismatch" }));
246
+ return;
247
+ }
248
+ const userName = payload.user_name ?? payload.user_id;
249
+ let originalMessage;
250
+ let originalPost;
251
+ let clickedButtonName = null;
252
+ try {
253
+ originalPost = await client.request(`/posts/${payload.post_id}`);
254
+ const postChannelId = originalPost.channel_id?.trim();
255
+ if (!postChannelId || postChannelId !== payload.channel_id) {
256
+ log?.(`mattermost interaction: post channel mismatch payload=${payload.channel_id} post=${postChannelId ?? "<missing>"}`);
257
+ res.statusCode = 403;
258
+ res.setHeader("Content-Type", "application/json");
259
+ res.end(JSON.stringify({ error: "Post/channel mismatch" }));
260
+ return;
261
+ }
262
+ originalMessage = originalPost.message ?? "";
263
+ const postAttachments = Array.isArray(originalPost?.props?.attachments) ? originalPost.props.attachments : [];
264
+ for (const att of postAttachments) {
265
+ const match = att.actions?.find((a) => a.id === actionId);
266
+ if (match?.name) {
267
+ clickedButtonName = match.name;
268
+ break;
269
+ }
270
+ }
271
+ if (clickedButtonName === null) {
272
+ log?.(`mattermost interaction: action ${actionId} not found in post ${payload.post_id}`);
273
+ res.statusCode = 403;
274
+ res.setHeader("Content-Type", "application/json");
275
+ res.end(JSON.stringify({ error: "Unknown action" }));
276
+ return;
277
+ }
278
+ } catch (err) {
279
+ log?.(`mattermost interaction: failed to validate post ${payload.post_id}: ${String(err)}`);
280
+ res.statusCode = 500;
281
+ res.setHeader("Content-Type", "application/json");
282
+ res.end(JSON.stringify({ error: "Failed to validate interaction" }));
283
+ return;
284
+ }
285
+ if (!originalPost) {
286
+ log?.(`mattermost interaction: missing fetched post ${payload.post_id}`);
287
+ res.statusCode = 500;
288
+ res.setHeader("Content-Type", "application/json");
289
+ res.end(JSON.stringify({ error: "Failed to load interaction post" }));
290
+ return;
291
+ }
292
+ log?.(`mattermost interaction: action=${actionId} user=${payload.user_name ?? payload.user_id} post=${payload.post_id} channel=${payload.channel_id}`);
293
+ if (params.authorizeButtonClick) try {
294
+ const authorization = await params.authorizeButtonClick({
295
+ payload,
296
+ post: originalPost
297
+ });
298
+ if (!authorization.ok) {
299
+ res.statusCode = authorization.statusCode ?? 200;
300
+ res.setHeader("Content-Type", "application/json");
301
+ res.end(JSON.stringify(authorization.response ?? { ephemeral_text: "You are not allowed to use this action here." }));
302
+ return;
303
+ }
304
+ } catch (err) {
305
+ log?.(`mattermost interaction: authorization failed: ${String(err)}`);
306
+ res.statusCode = 500;
307
+ res.setHeader("Content-Type", "application/json");
308
+ res.end(JSON.stringify({ error: "Interaction authorization failed" }));
309
+ return;
310
+ }
311
+ if (params.handleInteraction) try {
312
+ const response = await params.handleInteraction({
313
+ payload,
314
+ userName,
315
+ actionId,
316
+ actionName: clickedButtonName,
317
+ originalMessage,
318
+ context: contextWithoutToken,
319
+ post: originalPost
320
+ });
321
+ if (response !== null) {
322
+ res.statusCode = 200;
323
+ res.setHeader("Content-Type", "application/json");
324
+ res.end(JSON.stringify(response));
325
+ return;
326
+ }
327
+ } catch (err) {
328
+ log?.(`mattermost interaction: custom handler failed: ${String(err)}`);
329
+ res.statusCode = 500;
330
+ res.setHeader("Content-Type", "application/json");
331
+ res.end(JSON.stringify({ error: "Interaction handler failed" }));
332
+ return;
333
+ }
334
+ try {
335
+ const eventLabel = `Mattermost button click: action="${actionId}" by ${payload.user_name ?? payload.user_id} in channel ${payload.channel_id}`;
336
+ const sessionKey = params.resolveSessionKey ? await params.resolveSessionKey({
337
+ channelId: payload.channel_id,
338
+ userId: payload.user_id,
339
+ post: originalPost
340
+ }) : `agent:main:mattermost:${accountId}:${payload.channel_id}`;
341
+ core.system.enqueueSystemEvent(eventLabel, {
342
+ sessionKey,
343
+ contextKey: `mattermost:interaction:${payload.post_id}:${actionId}`
344
+ });
345
+ } catch (err) {
346
+ log?.(`mattermost interaction: system event dispatch failed: ${String(err)}`);
347
+ }
348
+ try {
349
+ await updateMattermostPost(client, payload.post_id, {
350
+ message: originalMessage,
351
+ props: { attachments: [{ text: `✓ **${clickedButtonName}** selected by @${userName}` }] }
352
+ });
353
+ } catch (err) {
354
+ log?.(`mattermost interaction: failed to update post ${payload.post_id}: ${String(err)}`);
355
+ }
356
+ res.statusCode = 200;
357
+ res.setHeader("Content-Type", "application/json");
358
+ res.end("{}");
359
+ if (params.dispatchButtonClick) try {
360
+ await params.dispatchButtonClick({
361
+ channelId: payload.channel_id,
362
+ userId: payload.user_id,
363
+ userName,
364
+ actionId,
365
+ actionName: clickedButtonName,
366
+ postId: payload.post_id,
367
+ post: originalPost
368
+ });
369
+ } catch (err) {
370
+ log?.(`mattermost interaction: dispatchButtonClick failed: ${String(err)}`);
371
+ }
372
+ };
373
+ }
374
+ //#endregion
375
+ export { resolveInteractionCallbackPath as a, setInteractionSecret as c, createMattermostInteractionHandler as i, buildButtonProps as n, resolveInteractionCallbackUrl as o, computeInteractionCallbackUrl as r, setInteractionCallbackUrl as s, buildButtonAttachments as t };
@@ -1,2 +1,2 @@
1
- import { r as isMattermostSenderAllowed } from "./monitor-auth-BiDuyvOc.js";
1
+ import { r as isMattermostSenderAllowed } from "./monitor-auth-dEHa1_On.js";
2
2
  export { isMattermostSenderAllowed };
@@ -1,2 +1,2 @@
1
- import { i as registerSlashCommandRoute } from "./slash-state-Cr58Xd12.js";
1
+ import { i as registerSlashCommandRoute } from "./slash-state-C9bpBN40.js";
2
2
  export { registerSlashCommandRoute };