mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-04 02:21:36 +00:00
* refactor(channels): centralize inbound turn orchestration * refactor(runtime): remove stale compatibility paths * chore(guards): reject internal deprecated API use * refactor(channels): simplify core turn planning * chore(guards): keep deprecated checks boundary-focused * refactor(memory): keep modern config off compat barrel * fix(msteams): preserve feedback learning * test(channels): align modern inbound fixtures * refactor(channels): finish modern inbound migration * refactor(channels): tighten core inbound kernel * fix(channels): preserve turn assembly narrowing * test(sdk): keep runtime mock binding immutable * test(matrix): isolate read policy runtime * test(msteams): mock canonical reply factory * test(slack): mock core inbound turn dispatch * test(telegram): inject core session recorder * test(signal): inject core session recorder * test(googlechat): assert canonical inbound routing * test(synology-chat): align core turn fixture * fix(sdk): preserve direct DM runtime compat * refactor(channels): own inbound envelope compat in core * refactor(channels): trim inbound dispatch seams * refactor(channels): remove redundant async wrappers * test(synology-chat): type canonical dispatcher mock * refactor(channels): remove remaining dead compat seams * chore(sdk): refresh API baseline after rebase * fix(channels): preserve direct DM identity metadata
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
// Slack-private authored text placement after block compilation.
|
|
import type { LegacyInteractiveReply } from "openclaw/plugin-sdk/interactive-runtime";
|
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
|
|
export type SlackAuthoredTextPlacement = "none" | "blocks" | "outside-blocks";
|
|
|
|
function normalizeComparableSlackText(text: string): string {
|
|
return text.trim().replace(/\s+/g, " ");
|
|
}
|
|
|
|
function isSlackAuthoredTextRepresentedInInteractive(
|
|
text: string,
|
|
interactive?: LegacyInteractiveReply,
|
|
): boolean {
|
|
return isSlackAuthoredTextRepresentedInFragments(
|
|
text,
|
|
interactive?.blocks.flatMap((block) => (block.type === "text" ? [block.text] : [])) ?? [],
|
|
);
|
|
}
|
|
|
|
function isSlackAuthoredTextRepresentedInFragments(
|
|
text: string,
|
|
rawFragments: readonly string[],
|
|
): boolean {
|
|
const target = normalizeComparableSlackText(text);
|
|
const fragments = rawFragments.map(normalizeComparableSlackText).filter(Boolean);
|
|
// Legacy inline controls split surrounding text into multiple interactive text blocks.
|
|
for (let start = 0; start < fragments.length; start += 1) {
|
|
let combined = "";
|
|
for (let end = start; end < fragments.length; end += 1) {
|
|
combined = normalizeComparableSlackText(`${combined} ${fragments[end]}`);
|
|
if (combined === target) {
|
|
return true;
|
|
}
|
|
if (combined.length > target.length) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Resolve placement from producer facts, before accessibility text changes the payload text. */
|
|
export function resolveSlackAuthoredTextPlacement(params: {
|
|
text?: string;
|
|
interactive?: LegacyInteractiveReply;
|
|
renderedInBlocks?: boolean;
|
|
renderedTextFragments?: readonly string[];
|
|
}): SlackAuthoredTextPlacement {
|
|
const text = normalizeOptionalString(params.text);
|
|
if (!text) {
|
|
return "none";
|
|
}
|
|
const isRepresentedInBlocks =
|
|
params.renderedInBlocks ||
|
|
isSlackAuthoredTextRepresentedInFragments(text, params.renderedTextFragments ?? []) ||
|
|
isSlackAuthoredTextRepresentedInInteractive(text, params.interactive);
|
|
return isRepresentedInBlocks ? "blocks" : "outside-blocks";
|
|
}
|