mirror of https://github.com/openclaw/openclaw.git
138 lines
4.7 KiB
TypeScript
138 lines
4.7 KiB
TypeScript
import { formatInboundEnvelope } from "../../../auto-reply/envelope.js";
|
|
import { readSessionUpdatedAt } from "../../../config/sessions.js";
|
|
import { logVerbose } from "../../../globals.js";
|
|
import type { ResolvedSlackAccount } from "../../accounts.js";
|
|
import type { SlackMessageEvent } from "../../types.js";
|
|
import type { SlackMonitorContext } from "../context.js";
|
|
import {
|
|
resolveSlackMedia,
|
|
resolveSlackThreadHistory,
|
|
type SlackMediaResult,
|
|
type SlackThreadStarter,
|
|
} from "../media.js";
|
|
|
|
export type SlackThreadContextData = {
|
|
threadStarterBody: string | undefined;
|
|
threadHistoryBody: string | undefined;
|
|
threadSessionPreviousTimestamp: number | undefined;
|
|
threadLabel: string | undefined;
|
|
threadStarterMedia: SlackMediaResult[] | null;
|
|
};
|
|
|
|
export async function resolveSlackThreadContextData(params: {
|
|
ctx: SlackMonitorContext;
|
|
account: ResolvedSlackAccount;
|
|
message: SlackMessageEvent;
|
|
isThreadReply: boolean;
|
|
threadTs: string | undefined;
|
|
threadStarter: SlackThreadStarter | null;
|
|
roomLabel: string;
|
|
storePath: string;
|
|
sessionKey: string;
|
|
envelopeOptions: ReturnType<
|
|
typeof import("../../../auto-reply/envelope.js").resolveEnvelopeFormatOptions
|
|
>;
|
|
effectiveDirectMedia: SlackMediaResult[] | null;
|
|
}): Promise<SlackThreadContextData> {
|
|
let threadStarterBody: string | undefined;
|
|
let threadHistoryBody: string | undefined;
|
|
let threadSessionPreviousTimestamp: number | undefined;
|
|
let threadLabel: string | undefined;
|
|
let threadStarterMedia: SlackMediaResult[] | null = null;
|
|
|
|
if (!params.isThreadReply || !params.threadTs) {
|
|
return {
|
|
threadStarterBody,
|
|
threadHistoryBody,
|
|
threadSessionPreviousTimestamp,
|
|
threadLabel,
|
|
threadStarterMedia,
|
|
};
|
|
}
|
|
|
|
const starter = params.threadStarter;
|
|
if (starter?.text) {
|
|
threadStarterBody = starter.text;
|
|
const snippet = starter.text.replace(/\s+/g, " ").slice(0, 80);
|
|
threadLabel = `Slack thread ${params.roomLabel}${snippet ? `: ${snippet}` : ""}`;
|
|
if (!params.effectiveDirectMedia && starter.files && starter.files.length > 0) {
|
|
threadStarterMedia = await resolveSlackMedia({
|
|
files: starter.files,
|
|
token: params.ctx.botToken,
|
|
maxBytes: params.ctx.mediaMaxBytes,
|
|
});
|
|
if (threadStarterMedia) {
|
|
const starterPlaceholders = threadStarterMedia.map((item) => item.placeholder).join(", ");
|
|
logVerbose(`slack: hydrated thread starter file ${starterPlaceholders} from root message`);
|
|
}
|
|
}
|
|
} else {
|
|
threadLabel = `Slack thread ${params.roomLabel}`;
|
|
}
|
|
|
|
const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
|
|
threadSessionPreviousTimestamp = readSessionUpdatedAt({
|
|
storePath: params.storePath,
|
|
sessionKey: params.sessionKey,
|
|
});
|
|
|
|
if (threadInitialHistoryLimit > 0 && !threadSessionPreviousTimestamp) {
|
|
const threadHistory = await resolveSlackThreadHistory({
|
|
channelId: params.message.channel,
|
|
threadTs: params.threadTs,
|
|
client: params.ctx.app.client,
|
|
currentMessageTs: params.message.ts,
|
|
limit: threadInitialHistoryLimit,
|
|
});
|
|
|
|
if (threadHistory.length > 0) {
|
|
const uniqueUserIds = [
|
|
...new Set(
|
|
threadHistory.map((item) => item.userId).filter((id): id is string => Boolean(id)),
|
|
),
|
|
];
|
|
const userMap = new Map<string, { name?: string }>();
|
|
await Promise.all(
|
|
uniqueUserIds.map(async (id) => {
|
|
const user = await params.ctx.resolveUserName(id);
|
|
if (user) {
|
|
userMap.set(id, user);
|
|
}
|
|
}),
|
|
);
|
|
|
|
const historyParts: string[] = [];
|
|
for (const historyMsg of threadHistory) {
|
|
const msgUser = historyMsg.userId ? userMap.get(historyMsg.userId) : null;
|
|
const msgSenderName =
|
|
msgUser?.name ?? (historyMsg.botId ? `Bot (${historyMsg.botId})` : "Unknown");
|
|
const isBot = Boolean(historyMsg.botId);
|
|
const role = isBot ? "assistant" : "user";
|
|
const msgWithId = `${historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
|
|
historyParts.push(
|
|
formatInboundEnvelope({
|
|
channel: "Slack",
|
|
from: `${msgSenderName} (${role})`,
|
|
timestamp: historyMsg.ts ? Math.round(Number(historyMsg.ts) * 1000) : undefined,
|
|
body: msgWithId,
|
|
chatType: "channel",
|
|
envelope: params.envelopeOptions,
|
|
}),
|
|
);
|
|
}
|
|
threadHistoryBody = historyParts.join("\n\n");
|
|
logVerbose(
|
|
`slack: populated thread history with ${threadHistory.length} messages for new session`,
|
|
);
|
|
}
|
|
}
|
|
|
|
return {
|
|
threadStarterBody,
|
|
threadHistoryBody,
|
|
threadSessionPreviousTimestamp,
|
|
threadLabel,
|
|
threadStarterMedia,
|
|
};
|
|
}
|