openskynet / src /channels /plugins /group-mentions.ts
Darochin's picture
Mirror OpenSkyNet workspace snapshot from Git HEAD
fc93158 verified
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 { resolveExactLineGroupConfigKey } from "../../line/group-keys.js";
import { normalizeAtHashSlug, normalizeHyphenSlug } from "../../shared/string-normalization.js";
import { inspectSlackAccount } from "../../slack/account-inspect.js";
import type { ChannelGroupContext } from "./types.js";
type GroupMentionParams = ChannelGroupContext;
function normalizeDiscordSlug(value?: string | null) {
return normalizeAtHashSlug(value);
}
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)
);
}
type SlackChannelPolicyEntry = {
requireMention?: boolean;
tools?: GroupToolPolicyConfig;
toolsBySender?: GroupToolPolicyBySenderConfig;
};
type SenderScopedToolsEntry = {
tools?: GroupToolPolicyConfig;
toolsBySender?: GroupToolPolicyBySenderConfig;
};
type ChannelGroupPolicyChannel =
| "telegram"
| "whatsapp"
| "imessage"
| "googlechat"
| "bluebubbles"
| "line";
function resolveSlackChannelPolicyEntry(
params: GroupMentionParams,
): SlackChannelPolicyEntry | undefined {
const account = inspectSlackAccount({
cfg: params.cfg,
accountId: params.accountId,
});
const channels = (account.channels ?? {}) as Record<string, SlackChannelPolicyEntry>;
if (Object.keys(channels).length === 0) {
return undefined;
}
const channelId = params.groupId?.trim();
const groupChannel = params.groupChannel;
const channelName = groupChannel?.replace(/^#/, "");
const normalizedName = normalizeHyphenSlug(channelName);
const candidates = [
channelId ?? "",
channelName ? `#${channelName}` : "",
channelName ?? "",
normalizedName,
].filter(Boolean);
for (const candidate of candidates) {
if (candidate && channels[candidate]) {
return channels[candidate];
}
}
return channels["*"];
}
function resolveChannelRequireMention(
params: GroupMentionParams,
channel: ChannelGroupPolicyChannel,
groupId: string | null | undefined = params.groupId,
): boolean {
return resolveChannelGroupRequireMention({
cfg: params.cfg,
channel,
groupId,
accountId: params.accountId,
});
}
function resolveChannelToolPolicyForSender(
params: GroupMentionParams,
channel: ChannelGroupPolicyChannel,
groupId: string | null | undefined = params.groupId,
): GroupToolPolicyConfig | undefined {
return resolveChannelGroupToolsPolicy({
cfg: params.cfg,
channel,
groupId,
accountId: params.accountId,
senderId: params.senderId,
senderName: params.senderName,
senderUsername: params.senderUsername,
senderE164: params.senderE164,
});
}
function resolveSenderToolsEntry(
entry: SenderScopedToolsEntry | undefined | null,
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
if (!entry) {
return undefined;
}
const senderPolicy = resolveToolsBySender({
toolsBySender: entry.toolsBySender,
senderId: params.senderId,
senderName: params.senderName,
senderUsername: params.senderUsername,
senderE164: params.senderE164,
});
if (senderPolicy) {
return senderPolicy;
}
return entry.tools;
}
function resolveDiscordPolicyContext(params: GroupMentionParams) {
const guildEntry = resolveDiscordGuildEntry(
params.cfg.channels?.discord?.guilds,
params.groupSpace,
);
const channelEntries = guildEntry?.channels;
const channelEntry =
channelEntries && Object.keys(channelEntries).length > 0
? resolveDiscordChannelEntry(channelEntries, params)
: undefined;
return { guildEntry, channelEntry };
}
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 resolveChannelRequireMention(params, "whatsapp");
}
export function resolveIMessageGroupRequireMention(params: GroupMentionParams): boolean {
return resolveChannelRequireMention(params, "imessage");
}
export function resolveDiscordGroupRequireMention(params: GroupMentionParams): boolean {
const context = resolveDiscordPolicyContext(params);
if (typeof context.channelEntry?.requireMention === "boolean") {
return context.channelEntry.requireMention;
}
if (typeof context.guildEntry?.requireMention === "boolean") {
return context.guildEntry.requireMention;
}
return true;
}
export function resolveGoogleChatGroupRequireMention(params: GroupMentionParams): boolean {
return resolveChannelRequireMention(params, "googlechat");
}
export function resolveGoogleChatGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
return resolveChannelToolPolicyForSender(params, "googlechat");
}
export function resolveSlackGroupRequireMention(params: GroupMentionParams): boolean {
const resolved = resolveSlackChannelPolicyEntry(params);
if (typeof resolved?.requireMention === "boolean") {
return resolved.requireMention;
}
return true;
}
export function resolveBlueBubblesGroupRequireMention(params: GroupMentionParams): boolean {
return resolveChannelRequireMention(params, "bluebubbles");
}
export function resolveTelegramGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
const { chatId } = parseTelegramGroupId(params.groupId);
return resolveChannelToolPolicyForSender(params, "telegram", chatId ?? params.groupId);
}
export function resolveWhatsAppGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
return resolveChannelToolPolicyForSender(params, "whatsapp");
}
export function resolveIMessageGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
return resolveChannelToolPolicyForSender(params, "imessage");
}
export function resolveDiscordGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
const context = resolveDiscordPolicyContext(params);
const channelPolicy = resolveSenderToolsEntry(context.channelEntry, params);
if (channelPolicy) {
return channelPolicy;
}
return resolveSenderToolsEntry(context.guildEntry, params);
}
export function resolveSlackGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
const resolved = resolveSlackChannelPolicyEntry(params);
return resolveSenderToolsEntry(resolved, params);
}
export function resolveBlueBubblesGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
return resolveChannelToolPolicyForSender(params, "bluebubbles");
}
export function resolveLineGroupRequireMention(params: GroupMentionParams): boolean {
const exactGroupId = resolveExactLineGroupConfigKey({
cfg: params.cfg,
accountId: params.accountId,
groupId: params.groupId,
});
if (exactGroupId) {
return resolveChannelGroupRequireMention({
cfg: params.cfg,
channel: "line",
groupId: exactGroupId,
accountId: params.accountId,
});
}
return resolveChannelRequireMention(params, "line");
}
export function resolveLineGroupToolPolicy(
params: GroupMentionParams,
): GroupToolPolicyConfig | undefined {
const exactGroupId = resolveExactLineGroupConfigKey({
cfg: params.cfg,
accountId: params.accountId,
groupId: params.groupId,
});
if (exactGroupId) {
return resolveChannelToolPolicyForSender(params, "line", exactGroupId);
}
return resolveChannelToolPolicyForSender(params, "line");
}