


























1+// Slack plugin module implements shortcut interaction behavior.
2+import type { GlobalShortcut, MessageShortcut, SlackShortcutMiddlewareArgs } from "@slack/bolt";
3+import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
4+import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
5+import { authorizeSlackSystemEventSender } from "../auth.js";
6+import type { SlackMonitorContext } from "../context.js";
7+8+type SlackShortcutBody = GlobalShortcut | MessageShortcut;
9+10+function resolveMessageThreadTs(body: MessageShortcut): string | undefined {
11+const threadTs = body.message.thread_ts;
12+return typeof threadTs === "string" && threadTs.trim() ? threadTs.trim() : undefined;
13+}
14+15+async function handleSlackShortcut(params: {
16+ctx: SlackMonitorContext;
17+trackEvent?: () => void;
18+args: SlackShortcutMiddlewareArgs;
19+formatSystemEvent: (payload: Record<string, unknown>) => string;
20+}): Promise<void> {
21+const { ack, body } = params.args;
22+await ack();
23+if (params.ctx.shouldDropMismatchedSlackEvent?.(body)) {
24+params.ctx.runtime.log?.("slack:interaction drop shortcut payload (mismatched app/team)");
25+return;
26+}
27+28+const callbackId = body.callback_id?.trim();
29+const userId = body.user?.id?.trim();
30+if (!callbackId || !userId) {
31+params.ctx.runtime.log?.("slack:interaction drop shortcut reason=invalid-payload");
32+return;
33+}
34+params.trackEvent?.();
35+36+const isMessageShortcut = body.type === "message_action";
37+const messageBody = isMessageShortcut ? body : undefined;
38+const channelId = messageBody?.channel.id?.trim() || undefined;
39+if (isMessageShortcut && !channelId) {
40+params.ctx.runtime.log?.(
41+`slack:interaction drop shortcut callback=${callbackId} user=${userId} reason=missing-channel`,
42+);
43+return;
44+}
45+const threadTs = messageBody ? resolveMessageThreadTs(messageBody) : undefined;
46+const auth = await authorizeSlackSystemEventSender({
47+ctx: params.ctx,
48+senderId: userId,
49+ channelId,
50+channelType: isMessageShortcut ? undefined : "im",
51+expectedSenderId: userId,
52+interactiveEvent: true,
53+});
54+if (!auth.allowed) {
55+params.ctx.runtime.log?.(
56+`slack:interaction drop shortcut callback=${callbackId} user=${userId} reason=${auth.reason ?? "unauthorized"}`,
57+);
58+return;
59+}
60+61+const interactionType = isMessageShortcut ? "message_shortcut" : "global_shortcut";
62+const messageTs = messageBody?.message.ts || messageBody?.message_ts;
63+const eventPayload = {
64+ interactionType,
65+actionId: `shortcut:${callbackId}`,
66+ callbackId,
67+ userId,
68+teamId: body.team?.id ?? body.user.team_id,
69+triggerId: body.trigger_id,
70+actionTs: body.action_ts,
71+ channelId,
72+channelName: messageBody?.channel.name,
73+ messageTs,
74+ threadTs,
75+messageUserId: messageBody?.message.user,
76+messageText: messageBody?.message.text,
77+responseUrl: messageBody?.response_url,
78+};
79+const sessionKey = params.ctx.resolveSlackSystemEventSessionKey({
80+ channelId,
81+channelType: auth.channelType,
82+senderId: userId,
83+ threadTs,
84+});
85+const contextKey = [
86+"slack:interaction:shortcut",
87+interactionType,
88+callbackId,
89+channelId,
90+messageTs,
91+body.action_ts,
92+]
93+.filter(Boolean)
94+.join(":");
95+96+params.ctx.runtime.log?.(
97+`slack:interaction ${interactionType} callback=${callbackId} user=${userId} channel=${channelId ?? "direct"}`,
98+);
99+const queued = enqueueSystemEvent(params.formatSystemEvent(eventPayload), {
100+ sessionKey,
101+ contextKey,
102+deliveryContext: {
103+channel: "slack",
104+to: auth.channelType === "im" ? `user:${userId}` : `channel:${channelId}`,
105+accountId: params.ctx.accountId,
106+threadId: threadTs,
107+},
108+});
109+if (queued) {
110+requestHeartbeat({
111+source: "hook",
112+intent: "immediate",
113+reason: "hook:slack-interaction",
114+ sessionKey,
115+heartbeat: { target: "last" },
116+});
117+}
118+}
119+120+export function registerSlackShortcutHandler(params: {
121+ctx: SlackMonitorContext;
122+trackEvent?: () => void;
123+formatSystemEvent: (payload: Record<string, unknown>) => string;
124+}): void {
125+if (typeof params.ctx.app.shortcut !== "function") {
126+return;
127+}
128+params.ctx.app.shortcut(/.+/, async (args: SlackShortcutMiddlewareArgs<SlackShortcutBody>) => {
129+await handleSlackShortcut({
130+ctx: params.ctx,
131+trackEvent: params.trackEvent,
132+ args,
133+formatSystemEvent: params.formatSystemEvent,
134+});
135+});
136+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。