mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-10 08:41:13 +00:00
* refactor: move Discord channel implementation to extensions/discord/src/ Move all Discord source files from src/discord/ to extensions/discord/src/, following the extension migration pattern. Source files in src/discord/ are replaced with re-export shims. Channel-plugin files from src/channels/plugins/*/discord* are similarly moved and shimmed. - Copy all .ts source files preserving subdirectory structure (monitor/, voice/) - Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues) - Fix all relative imports to use correct paths from new location - Create re-export shims at original locations for backward compatibility - Delete test files from shim locations (tests live in extension now) - Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate extension files outside src/ - Update write-plugin-sdk-entry-dts.ts to match new declaration output paths * fix: add importOriginal to thread-bindings session-meta mock for extensions test * style: fix formatting in thread-bindings lifecycle test
84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
import { resolveDiscordDirectoryUserId } from "./directory-cache.js";
|
|
|
|
const MARKDOWN_CODE_SEGMENT_PATTERN = /```[\s\S]*?```|`[^`\n]*`/g;
|
|
const MENTION_CANDIDATE_PATTERN = /(^|[\s([{"'.,;:!?])@([a-z0-9_.-]{2,32}(?:#[0-9]{4})?)/gi;
|
|
const DISCORD_RESERVED_MENTIONS = new Set(["everyone", "here"]);
|
|
|
|
function normalizeSnowflake(value: string | number | bigint): string | null {
|
|
const text = String(value ?? "").trim();
|
|
if (!/^\d+$/.test(text)) {
|
|
return null;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
export function formatMention(params: {
|
|
userId?: string | number | bigint | null;
|
|
roleId?: string | number | bigint | null;
|
|
channelId?: string | number | bigint | null;
|
|
}): string {
|
|
const userId = params.userId == null ? null : normalizeSnowflake(params.userId);
|
|
const roleId = params.roleId == null ? null : normalizeSnowflake(params.roleId);
|
|
const channelId = params.channelId == null ? null : normalizeSnowflake(params.channelId);
|
|
const values = [
|
|
userId ? { kind: "user" as const, id: userId } : null,
|
|
roleId ? { kind: "role" as const, id: roleId } : null,
|
|
channelId ? { kind: "channel" as const, id: channelId } : null,
|
|
].filter((entry): entry is { kind: "user" | "role" | "channel"; id: string } => Boolean(entry));
|
|
if (values.length !== 1) {
|
|
throw new Error("formatMention requires exactly one of userId, roleId, or channelId");
|
|
}
|
|
const target = values[0];
|
|
if (target.kind === "user") {
|
|
return `<@${target.id}>`;
|
|
}
|
|
if (target.kind === "role") {
|
|
return `<@&${target.id}>`;
|
|
}
|
|
return `<#${target.id}>`;
|
|
}
|
|
|
|
function rewritePlainTextMentions(text: string, accountId?: string | null): string {
|
|
if (!text.includes("@")) {
|
|
return text;
|
|
}
|
|
return text.replace(MENTION_CANDIDATE_PATTERN, (match, prefix, rawHandle) => {
|
|
const handle = String(rawHandle ?? "").trim();
|
|
if (!handle) {
|
|
return match;
|
|
}
|
|
const lookup = handle.toLowerCase();
|
|
if (DISCORD_RESERVED_MENTIONS.has(lookup)) {
|
|
return match;
|
|
}
|
|
const userId = resolveDiscordDirectoryUserId({
|
|
accountId,
|
|
handle,
|
|
});
|
|
if (!userId) {
|
|
return match;
|
|
}
|
|
return `${String(prefix ?? "")}${formatMention({ userId })}`;
|
|
});
|
|
}
|
|
|
|
export function rewriteDiscordKnownMentions(
|
|
text: string,
|
|
params: { accountId?: string | null },
|
|
): string {
|
|
if (!text.includes("@")) {
|
|
return text;
|
|
}
|
|
let rewritten = "";
|
|
let offset = 0;
|
|
MARKDOWN_CODE_SEGMENT_PATTERN.lastIndex = 0;
|
|
for (const match of text.matchAll(MARKDOWN_CODE_SEGMENT_PATTERN)) {
|
|
const matchIndex = match.index ?? 0;
|
|
rewritten += rewritePlainTextMentions(text.slice(offset, matchIndex), params.accountId);
|
|
rewritten += match[0];
|
|
offset = matchIndex + match[0].length;
|
|
}
|
|
rewritten += rewritePlainTextMentions(text.slice(offset), params.accountId);
|
|
return rewritten;
|
|
}
|