UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

204 lines (203 loc) 8.81 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { D as resolveIntegerOption } from "./number-coercion-CJQ8TR--.js"; import "./number-runtime-DBLVDypr.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; import { r as isMatrixNotFoundError } from "./errors-C4iaVh6O.js"; import { _ as isPollStartType, a as sendMessageMatrix, g as isPollEventType, n as editMessageMatrix } from "./send-DOJVJulA.js"; import { n as MATRIX_REACTION_EVENT_TYPE } from "./reaction-common-DYnuzZbJ.js"; import { a as resolveMatrixMessageAttachment, l as resolveMatrixPollRootEventId, o as resolveMatrixMessageBody, s as fetchMatrixPollMessageSummary } from "./media-text-C7_8HQaZ.js"; import { n as withResolvedRoomAction } from "./client-BSvW01Nw.js"; //#region extensions/matrix/src/matrix/actions/limits.ts function resolveMatrixActionLimit(raw, fallback) { return resolveIntegerOption(raw, fallback, { min: 1 }); } //#endregion //#region extensions/matrix/src/matrix/actions/types.ts const EventType = { RoomMessage: "m.room.message", RoomPinnedEvents: "m.room.pinned_events", RoomTopic: "m.room.topic", Reaction: MATRIX_REACTION_EVENT_TYPE }; //#endregion //#region extensions/matrix/src/matrix/actions/summary.ts function summarizeMatrixRawEvent(event) { const content = event.content; const relates = content["m.relates_to"]; let relType; let eventId; if (relates) { if ("rel_type" in relates) { relType = relates.rel_type; eventId = relates.event_id; } else if ("m.in_reply_to" in relates) eventId = relates["m.in_reply_to"]?.event_id; } const relatesTo = relType || eventId ? { relType, eventId } : void 0; return { eventId: event.event_id, sender: event.sender, body: resolveMatrixMessageBody({ body: content.body, filename: content.filename, msgtype: content.msgtype }), msgtype: content.msgtype, attachment: resolveMatrixMessageAttachment({ body: content.body, filename: content.filename, msgtype: content.msgtype }), timestamp: event.origin_server_ts, relatesTo }; } async function readPinnedEvents(client, roomId) { try { return (await client.getRoomStateEvent(roomId, EventType.RoomPinnedEvents, "")).pinned.filter((id) => id.trim().length > 0); } catch (err) { if (isMatrixNotFoundError(err)) return []; throw err; } } async function fetchEventSummary(client, roomId, eventId) { try { const raw = await client.getEvent(roomId, eventId); if (raw.unsigned?.redacted_because) return null; const pollSummary = await fetchMatrixPollMessageSummary(client, roomId, raw); if (pollSummary) return pollSummary; return summarizeMatrixRawEvent(raw); } catch { return null; } } //#endregion //#region extensions/matrix/src/matrix/actions/messages.ts const MATRIX_THREAD_RELATIONS_START_CURSOR_PREFIX = "openclaw.matrix.thread-relations-start:"; async function sendMatrixMessage(to, content, opts = {}) { if (!opts.cfg) throw new Error("Matrix message actions require a resolved runtime config."); return await sendMessageMatrix(to, content, { cfg: opts.cfg, mediaUrl: opts.mediaUrl, mediaLocalRoots: opts.mediaLocalRoots, replyToId: opts.replyToId, threadId: opts.threadId, audioAsVoice: opts.audioAsVoice, accountId: opts.accountId ?? void 0, client: opts.client, timeoutMs: opts.timeoutMs }); } async function editMatrixMessage(roomId, messageId, content, opts = {}) { if (!opts.cfg) throw new Error("Matrix message actions require a resolved runtime config."); const trimmed = content.trim(); if (!trimmed) throw new Error("Matrix edit requires content"); return { eventId: await editMessageMatrix(roomId, messageId, trimmed, { cfg: opts.cfg, accountId: opts.accountId ?? void 0, client: opts.client, timeoutMs: opts.timeoutMs }) || null }; } async function deleteMatrixMessage(roomId, messageId, opts = {}) { await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => { await client.redactEvent(resolvedRoom, messageId, opts.reason); }); } async function readMatrixMessages(roomId, opts = {}) { return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => { const limit = resolveMatrixActionLimit(opts.limit, 20); const rawBefore = normalizeOptionalString(opts.before); const rawAfter = normalizeOptionalString(opts.after); const dir = opts.after ? "f" : "b"; const threadId = normalizeOptionalString(opts.threadId); const isThreadRelationsStartCursor = threadId ? isMatrixThreadRelationsStartCursor(rawBefore, threadId) : false; const token = isThreadRelationsStartCursor ? void 0 : rawBefore ?? rawAfter; const threadRootSummary = threadId !== void 0 && !token && !isThreadRelationsStartCursor && threadId ? await fetchDisplayableThreadRootSummary(client, resolvedRoom, threadId) : void 0; const rootCountsTowardLimit = threadRootSummary !== void 0; const rootFillsThreadPage = rootCountsTowardLimit && limit === 1; const relationLimit = rootCountsTowardLimit ? Math.max(limit - 1, 1) : limit; const seenPollRoots = /* @__PURE__ */ new Set(); const threadRootEventId = normalizeOptionalString(threadRootSummary?.eventId); if (threadRootEventId) seenPollRoots.add(threadRootEventId); const relationPage = threadId && relationLimit > 0 ? await client.getRelations(resolvedRoom, threadId, "m.thread", void 0, { dir, from: token, limit: relationLimit }) : null; const flatPage = threadId ? null : await client.doRequest("GET", `/_matrix/client/v3/rooms/${encodeURIComponent(resolvedRoom)}/messages`, { dir, limit, from: token }); const hydratedChunk = await client.hydrateEvents(resolvedRoom, relationPage ? rootFillsThreadPage ? [] : relationPage.events : flatPage?.chunk ?? []); const messages = []; if (threadRootSummary) messages.push(threadRootSummary); for (const event of hydratedChunk) { if (event.unsigned?.redacted_because) continue; if (!threadId && isMatrixThreadEvent(event)) continue; if (event.type === EventType.RoomMessage) { if (threadId && event.event_id === threadId) continue; messages.push(summarizeMatrixRawEvent(event)); continue; } if (!isPollEventType(event.type)) continue; const pollRootId = resolveMatrixPollRootEventId(event); if (!pollRootId || seenPollRoots.has(pollRootId)) continue; if (!threadId && await isMatrixPollRootThreaded({ client, event, pollRootId, resolvedRoom })) continue; seenPollRoots.add(pollRootId); const pollSummary = await fetchMatrixPollMessageSummary(client, resolvedRoom, event); if (pollSummary) messages.push(pollSummary); } return { messages, nextBatch: rootFillsThreadPage && threadId && relationPage?.events.length ? encodeMatrixThreadRelationsStartCursor(threadId) : relationPage?.nextBatch ?? flatPage?.end ?? null, prevBatch: relationPage?.prevBatch ?? flatPage?.start ?? null }; }); } function encodeMatrixThreadRelationsStartCursor(threadId) { return `${MATRIX_THREAD_RELATIONS_START_CURSOR_PREFIX}${Buffer.from(JSON.stringify({ v: 1, threadId }), "utf8").toString("base64url")}`; } function isMatrixThreadRelationsStartCursor(raw, threadId) { if (!raw?.startsWith(MATRIX_THREAD_RELATIONS_START_CURSOR_PREFIX)) return false; const encoded = raw.slice(39); try { const decoded = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8")); return decoded.v === 1 && decoded.threadId === threadId; } catch { return false; } } async function fetchDisplayableThreadRootSummary(client, resolvedRoom, threadId) { const rawRootEvent = await client.getEvent(resolvedRoom, threadId).catch(() => null); if (!rawRootEvent) return; const rootEvent = (await client.hydrateEvents(resolvedRoom, [rawRootEvent]))[0]; if (!rootEvent || rootEvent.unsigned?.redacted_because) return; if (rootEvent.type === EventType.RoomMessage) return summarizeMatrixRawEvent(rootEvent); if (isPollStartType(rootEvent.type)) return await fetchMatrixPollMessageSummary(client, resolvedRoom, rootEvent) ?? void 0; } function isMatrixThreadEvent(event) { const relates = event.content?.["m.relates_to"]; if (!relates || typeof relates !== "object") return false; return relates.rel_type === "m.thread"; } async function isMatrixPollRootThreaded(params) { if (isMatrixThreadEvent(params.event)) return true; const rootEvent = await params.client.getEvent(params.resolvedRoom, params.pollRootId).catch(() => null); if (!rootEvent) return false; const hydratedRoot = (await params.client.hydrateEvents(params.resolvedRoom, [rootEvent]))[0]; return hydratedRoot ? isMatrixThreadEvent(hydratedRoot) : false; } //#endregion export { fetchEventSummary as a, resolveMatrixActionLimit as c, sendMatrixMessage as i, editMatrixMessage as n, readPinnedEvents as o, readMatrixMessages as r, EventType as s, deleteMatrixMessage as t };