






















@@ -6,9 +6,145 @@ import type { SlackAppMentionEvent, SlackMessageEvent } from "../../types.js";
66import { normalizeSlackChannelType } from "../channel-type.js";
77import type { SlackMonitorContext } from "../context.js";
88import type { SlackMessageHandler } from "../message-handler.js";
9+import type { SlackMessageChangedEvent } from "../types.js";
910import { resolveSlackMessageSubtypeHandler } from "./message-subtype-handlers.js";
1011import { authorizeAndResolveSlackSystemEventContext } from "./system-event-context.js";
111213+type SlackAssistantMessageRecord = {
14+bot_id?: unknown;
15+user?: unknown;
16+text?: unknown;
17+ts?: unknown;
18+thread_ts?: unknown;
19+files?: unknown;
20+attachments?: unknown;
21+metadata?: unknown;
22+blocks?: unknown;
23+};
24+25+function asRecord(value: unknown): Record<string, unknown> | undefined {
26+return value && typeof value === "object" && !Array.isArray(value)
27+ ? (value as Record<string, unknown>)
28+ : undefined;
29+}
30+31+function asString(value: unknown): string | undefined {
32+return typeof value === "string" && value.trim() ? value.trim() : undefined;
33+}
34+35+function isSlackUserId(value: string): boolean {
36+return /^[UW][A-Z0-9]+$/.test(value);
37+}
38+39+function addUserCandidate(candidates: Set<string>, value: unknown, botUserId: string): void {
40+const id = asString(value);
41+if (!id || id === botUserId || !isSlackUserId(id)) {
42+return;
43+}
44+candidates.add(id);
45+}
46+47+function collectMetadataUserCandidates(
48+candidates: Set<string>,
49+value: unknown,
50+botUserId: string,
51+): void {
52+const metadata = asRecord(value);
53+const payload = asRecord(metadata?.event_payload);
54+if (!payload) {
55+return;
56+}
57+for (const key of ["user", "user_id", "actor_user_id", "author_user_id", "slack_user_id"]) {
58+addUserCandidate(candidates, payload[key], botUserId);
59+}
60+}
61+62+function collectBlockUserIds(candidates: Set<string>, value: unknown, botUserId: string): void {
63+if (Array.isArray(value)) {
64+for (const entry of value) {
65+collectBlockUserIds(candidates, entry, botUserId);
66+}
67+return;
68+}
69+const record = asRecord(value);
70+if (!record) {
71+return;
72+}
73+addUserCandidate(candidates, record.user_id, botUserId);
74+for (const key of ["elements", "accessory", "fields"]) {
75+collectBlockUserIds(candidates, record[key], botUserId);
76+}
77+}
78+79+function resolveAssistantMessageChangedSender(params: {
80+event: SlackMessageChangedEvent;
81+message?: SlackAssistantMessageRecord;
82+botUserId: string;
83+}): string | undefined {
84+const candidates = new Set<string>();
85+collectMetadataUserCandidates(candidates, params.message?.metadata, params.botUserId);
86+collectBlockUserIds(candidates, params.message?.blocks, params.botUserId);
87+return candidates.size === 1 ? [...candidates][0] : undefined;
88+}
89+90+function isSelfAttributedMessageChange(params: {
91+event: SlackMessageChangedEvent;
92+message?: SlackAssistantMessageRecord;
93+ctx: SlackMonitorContext;
94+}): boolean {
95+const topUser = asString((params.event as SlackMessageChangedEvent & { user?: unknown }).user);
96+const messageUser = asString(params.message?.user);
97+const messageBotId = asString(params.message?.bot_id);
98+return (
99+(Boolean(params.ctx.botUserId) &&
100+(topUser === params.ctx.botUserId || messageUser === params.ctx.botUserId)) ||
101+(Boolean(params.ctx.botId) && messageBotId === params.ctx.botId)
102+);
103+}
104+105+function resolveAssistantMessageChangedInbound(params: {
106+event: SlackMessageEvent;
107+ctx: SlackMonitorContext;
108+}): SlackMessageEvent | undefined {
109+if (params.event.subtype !== "message_changed") {
110+return undefined;
111+}
112+const changed = params.event as SlackMessageChangedEvent;
113+const message = asRecord(changed.message) as SlackAssistantMessageRecord | undefined;
114+if (!message || !isSelfAttributedMessageChange({ event: changed, message, ctx: params.ctx })) {
115+return undefined;
116+}
117+const channelType = normalizeSlackChannelType(
118+asString((changed as SlackMessageChangedEvent & { channel_type?: unknown }).channel_type),
119+changed.channel,
120+);
121+if (channelType !== "im") {
122+return undefined;
123+}
124+const senderId = resolveAssistantMessageChangedSender({
125+event: changed,
126+ message,
127+botUserId: params.ctx.botUserId,
128+});
129+if (!senderId) {
130+return undefined;
131+}
132+return {
133+type: "message",
134+channel: changed.channel ?? params.event.channel,
135+channel_type: "im",
136+user: senderId,
137+text: asString(message.text),
138+ts: asString(message.ts) ?? asString(changed.event_ts),
139+thread_ts: asString(message.thread_ts),
140+event_ts: changed.event_ts,
141+files: Array.isArray(message.files) ? (message.files as SlackMessageEvent["files"]) : undefined,
142+attachments: Array.isArray(message.attachments)
143+ ? (message.attachments as SlackMessageEvent["attachments"])
144+ : undefined,
145+};
146+}
147+12148export function registerSlackMessageEvents(params: {
13149ctx: SlackMonitorContext;
14150handleSlackMessage: SlackMessageHandler;
@@ -22,6 +158,28 @@ export function registerSlackMessageEvents(params: {
22158}
2315924160const message = event as SlackMessageEvent;
161+const assistantChangedInbound = resolveAssistantMessageChangedInbound({
162+event: message,
163+ ctx,
164+});
165+if (assistantChangedInbound) {
166+await handleSlackMessage(assistantChangedInbound, { source: "message" });
167+return;
168+}
169+170+if (
171+message.subtype === "message_changed" &&
172+isSelfAttributedMessageChange({
173+event: message as SlackMessageChangedEvent,
174+message: asRecord((message as SlackMessageChangedEvent).message) as
175+| SlackAssistantMessageRecord
176+| undefined,
177+ ctx,
178+})
179+) {
180+return;
181+}
182+25183const subtypeHandler = resolveSlackMessageSubtypeHandler(message);
26184if (subtypeHandler) {
27185const channelId = subtypeHandler.resolveChannelId(message);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。