413 lines
12 KiB
TypeScript
413 lines
12 KiB
TypeScript
import type { OpenClawConfig } from "../../config/config.js";
|
|
import {
|
|
resolveChannelGroupRequireMention,
|
|
resolveChannelGroupToolsPolicy,
|
|
resolveToolsBySender,
|
|
} from "../../config/group-policy.js";
|
|
import type { DiscordConfig } from "../../config/types.js";
|
|
import type {
|
|
GroupToolPolicyBySenderConfig,
|
|
GroupToolPolicyConfig,
|
|
} from "../../config/types.tools.js";
|
|
import { resolveSlackAccount } from "../../slack/accounts.js";
|
|
|
|
type GroupMentionParams = {
|
|
cfg: OpenClawConfig;
|
|
groupId?: string | null;
|
|
groupChannel?: string | null;
|
|
groupSpace?: string | null;
|
|
accountId?: string | null;
|
|
senderId?: string | null;
|
|
senderName?: string | null;
|
|
senderUsername?: string | null;
|
|
senderE164?: string | null;
|
|
};
|
|
|
|
function normalizeDiscordSlug(value?: string | null) {
|
|
if (!value) {
|
|
return "";
|
|
}
|
|
let text = value.trim().toLowerCase();
|
|
if (!text) {
|
|
return "";
|
|
}
|
|
text = text.replace(/^[@#]+/, "");
|
|
text = text.replace(/[\s_]+/g, "-");
|
|
text = text.replace(/[^a-z0-9-]+/g, "-");
|
|
text = text.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
return text;
|
|
}
|
|
|
|
function normalizeSlackSlug(raw?: string | null) {
|
|
const trimmed = raw?.trim().toLowerCase() ?? "";
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
const dashed = trimmed.replace(/\s+/g, "-");
|
|
const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-");
|
|
return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
}
|
|
|
|
function parseTelegramGroupId(value?: string | null) {
|
|
const raw = value?.trim() ?? "";
|
|
if (!raw) {
|
|
return { chatId: undefined, topicId: undefined };
|
|
}
|
|
const parts = raw.split(":").filter(Boolean);
|
|
if (
|
|
parts.length >= 3 &&
|
|
parts[1] === "topic" &&
|
|
/^-?\d+$/.test(parts[0]) &&
|
|
/^\d+$/.test(parts[2])
|
|
) {
|
|
return { chatId: parts[0], topicId: parts[2] };
|
|
}
|
|
if (parts.length >= 2 && /^-?\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) {
|
|
return { chatId: parts[0], topicId: parts[1] };
|
|
}
|
|
return { chatId: raw, topicId: undefined };
|
|
}
|
|
|
|
function resolveTelegramRequireMention(params: {
|
|
cfg: OpenClawConfig;
|
|
chatId?: string;
|
|
topicId?: string;
|
|
}): boolean | undefined {
|
|
const { cfg, chatId, topicId } = params;
|
|
if (!chatId) {
|
|
return undefined;
|
|
}
|
|
const groupConfig = cfg.channels?.telegram?.groups?.[chatId];
|
|
const groupDefault = cfg.channels?.telegram?.groups?.["*"];
|
|
const topicConfig = topicId && groupConfig?.topics ? groupConfig.topics[topicId] : undefined;
|
|
const defaultTopicConfig =
|
|
topicId && groupDefault?.topics ? groupDefault.topics[topicId] : undefined;
|
|
if (typeof topicConfig?.requireMention === "boolean") {
|
|
return topicConfig.requireMention;
|
|
}
|
|
if (typeof defaultTopicConfig?.requireMention === "boolean") {
|
|
return defaultTopicConfig.requireMention;
|
|
}
|
|
if (typeof groupConfig?.requireMention === "boolean") {
|
|
return groupConfig.requireMention;
|
|
}
|
|
if (typeof groupDefault?.requireMention === "boolean") {
|
|
return groupDefault.requireMention;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function resolveDiscordGuildEntry(guilds: DiscordConfig["guilds"], groupSpace?: string | null) {
|
|
if (!guilds || Object.keys(guilds).length === 0) {
|
|
return null;
|
|
}
|
|
const space = groupSpace?.trim() ?? "";
|
|
if (space && guilds[space]) {
|
|
return guilds[space];
|
|
}
|
|
const normalized = normalizeDiscordSlug(space);
|
|
if (normalized && guilds[normalized]) {
|
|
return guilds[normalized];
|
|
}
|
|
if (normalized) {
|
|
const match = Object.values(guilds).find(
|
|
(entry) => normalizeDiscordSlug(entry?.slug ?? undefined) === normalized,
|
|
);
|
|
if (match) {
|
|
return match;
|
|
}
|
|
}
|
|
return guilds["*"] ?? null;
|
|
}
|
|
|
|
function resolveDiscordChannelEntry<TEntry>(
|
|
channelEntries: Record<string, TEntry> | undefined,
|
|
params: { groupId?: string | null; groupChannel?: string | null },
|
|
): TEntry | undefined {
|
|
if (!channelEntries || Object.keys(channelEntries).length === 0) {
|
|
return undefined;
|
|
}
|
|
const groupChannel = params.groupChannel;
|
|
const channelSlug = normalizeDiscordSlug(groupChannel);
|
|
return (
|
|
(params.groupId ? channelEntries[params.groupId] : undefined) ??
|
|
(channelSlug
|
|
? (channelEntries[channelSlug] ?? channelEntries[`#${channelSlug}`])
|
|
: undefined) ??
|
|
(groupChannel ? channelEntries[normalizeDiscordSlug(groupChannel)] : undefined)
|
|
);
|
|
}
|
|
|
|
export function resolveTelegramGroupRequireMention(
|
|
params: GroupMentionParams,
|
|
): boolean | undefined {
|
|
const { chatId, topicId } = parseTelegramGroupId(params.groupId);
|
|
const requireMention = resolveTelegramRequireMention({
|
|
cfg: params.cfg,
|
|
chatId,
|
|
topicId,
|
|
});
|
|
if (typeof requireMention === "boolean") {
|
|
return requireMention;
|
|
}
|
|
return resolveChannelGroupRequireMention({
|
|
cfg: params.cfg,
|
|
channel: "telegram",
|
|
groupId: chatId ?? params.groupId,
|
|
accountId: params.accountId,
|
|
});
|
|
}
|
|
|
|
export function resolveWhatsAppGroupRequireMention(params: GroupMentionParams): boolean {
|
|
return resolveChannelGroupRequireMention({
|
|
cfg: params.cfg,
|
|
channel: "whatsapp",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
});
|
|
}
|
|
|
|
export function resolveIMessageGroupRequireMention(params: GroupMentionParams): boolean {
|
|
return resolveChannelGroupRequireMention({
|
|
cfg: params.cfg,
|
|
channel: "imessage",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
});
|
|
}
|
|
|
|
export function resolveDiscordGroupRequireMention(params: GroupMentionParams): boolean {
|
|
const guildEntry = resolveDiscordGuildEntry(
|
|
params.cfg.channels?.discord?.guilds,
|
|
params.groupSpace,
|
|
);
|
|
const channelEntries = guildEntry?.channels;
|
|
if (channelEntries && Object.keys(channelEntries).length > 0) {
|
|
const entry = resolveDiscordChannelEntry(channelEntries, params);
|
|
if (entry && typeof entry.requireMention === "boolean") {
|
|
return entry.requireMention;
|
|
}
|
|
}
|
|
if (typeof guildEntry?.requireMention === "boolean") {
|
|
return guildEntry.requireMention;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function resolveGoogleChatGroupRequireMention(params: GroupMentionParams): boolean {
|
|
return resolveChannelGroupRequireMention({
|
|
cfg: params.cfg,
|
|
channel: "googlechat",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
});
|
|
}
|
|
|
|
export function resolveGoogleChatGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
return resolveChannelGroupToolsPolicy({
|
|
cfg: params.cfg,
|
|
channel: "googlechat",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
}
|
|
|
|
export function resolveSlackGroupRequireMention(params: GroupMentionParams): boolean {
|
|
const account = resolveSlackAccount({
|
|
cfg: params.cfg,
|
|
accountId: params.accountId,
|
|
});
|
|
const channels = account.channels ?? {};
|
|
const keys = Object.keys(channels);
|
|
if (keys.length === 0) {
|
|
return true;
|
|
}
|
|
const channelId = params.groupId?.trim();
|
|
const groupChannel = params.groupChannel;
|
|
const channelName = groupChannel?.replace(/^#/, "");
|
|
const normalizedName = normalizeSlackSlug(channelName);
|
|
const candidates = [
|
|
channelId ?? "",
|
|
channelName ? `#${channelName}` : "",
|
|
channelName ?? "",
|
|
normalizedName,
|
|
].filter(Boolean);
|
|
let matched: { requireMention?: boolean } | undefined;
|
|
for (const candidate of candidates) {
|
|
if (candidate && channels[candidate]) {
|
|
matched = channels[candidate];
|
|
break;
|
|
}
|
|
}
|
|
const fallback = channels["*"];
|
|
const resolved = matched ?? fallback;
|
|
if (typeof resolved?.requireMention === "boolean") {
|
|
return resolved.requireMention;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function resolveBlueBubblesGroupRequireMention(params: GroupMentionParams): boolean {
|
|
return resolveChannelGroupRequireMention({
|
|
cfg: params.cfg,
|
|
channel: "bluebubbles",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
});
|
|
}
|
|
|
|
export function resolveTelegramGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
const { chatId } = parseTelegramGroupId(params.groupId);
|
|
return resolveChannelGroupToolsPolicy({
|
|
cfg: params.cfg,
|
|
channel: "telegram",
|
|
groupId: chatId ?? params.groupId,
|
|
accountId: params.accountId,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
}
|
|
|
|
export function resolveWhatsAppGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
return resolveChannelGroupToolsPolicy({
|
|
cfg: params.cfg,
|
|
channel: "whatsapp",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
}
|
|
|
|
export function resolveIMessageGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
return resolveChannelGroupToolsPolicy({
|
|
cfg: params.cfg,
|
|
channel: "imessage",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
}
|
|
|
|
export function resolveDiscordGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
const guildEntry = resolveDiscordGuildEntry(
|
|
params.cfg.channels?.discord?.guilds,
|
|
params.groupSpace,
|
|
);
|
|
const channelEntries = guildEntry?.channels;
|
|
if (channelEntries && Object.keys(channelEntries).length > 0) {
|
|
const entry = resolveDiscordChannelEntry(channelEntries, params);
|
|
const senderPolicy = resolveToolsBySender({
|
|
toolsBySender: entry?.toolsBySender,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
if (senderPolicy) {
|
|
return senderPolicy;
|
|
}
|
|
if (entry?.tools) {
|
|
return entry.tools;
|
|
}
|
|
}
|
|
const guildSenderPolicy = resolveToolsBySender({
|
|
toolsBySender: guildEntry?.toolsBySender,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
if (guildSenderPolicy) {
|
|
return guildSenderPolicy;
|
|
}
|
|
if (guildEntry?.tools) {
|
|
return guildEntry.tools;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function resolveSlackGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
const account = resolveSlackAccount({
|
|
cfg: params.cfg,
|
|
accountId: params.accountId,
|
|
});
|
|
const channels = account.channels ?? {};
|
|
const keys = Object.keys(channels);
|
|
if (keys.length === 0) {
|
|
return undefined;
|
|
}
|
|
const channelId = params.groupId?.trim();
|
|
const groupChannel = params.groupChannel;
|
|
const channelName = groupChannel?.replace(/^#/, "");
|
|
const normalizedName = normalizeSlackSlug(channelName);
|
|
const candidates = [
|
|
channelId ?? "",
|
|
channelName ? `#${channelName}` : "",
|
|
channelName ?? "",
|
|
normalizedName,
|
|
].filter(Boolean);
|
|
let matched:
|
|
| { tools?: GroupToolPolicyConfig; toolsBySender?: GroupToolPolicyBySenderConfig }
|
|
| undefined;
|
|
for (const candidate of candidates) {
|
|
if (candidate && channels[candidate]) {
|
|
matched = channels[candidate];
|
|
break;
|
|
}
|
|
}
|
|
const resolved = matched ?? channels["*"];
|
|
const senderPolicy = resolveToolsBySender({
|
|
toolsBySender: resolved?.toolsBySender,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
if (senderPolicy) {
|
|
return senderPolicy;
|
|
}
|
|
if (resolved?.tools) {
|
|
return resolved.tools;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function resolveBlueBubblesGroupToolPolicy(
|
|
params: GroupMentionParams,
|
|
): GroupToolPolicyConfig | undefined {
|
|
return resolveChannelGroupToolsPolicy({
|
|
cfg: params.cfg,
|
|
channel: "bluebubbles",
|
|
groupId: params.groupId,
|
|
accountId: params.accountId,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
senderUsername: params.senderUsername,
|
|
senderE164: params.senderE164,
|
|
});
|
|
}
|