161 lines
5.4 KiB
TypeScript
161 lines
5.4 KiB
TypeScript
import type { AllowlistMatch, GroupPolicy } from "clawdbot/plugin-sdk";
|
|
import {
|
|
buildChannelKeyCandidates,
|
|
normalizeChannelSlug,
|
|
resolveChannelEntryMatchWithFallback,
|
|
resolveMentionGatingWithBypass,
|
|
resolveNestedAllowlistDecision,
|
|
} from "clawdbot/plugin-sdk";
|
|
|
|
import type { NextcloudTalkRoomConfig } from "./types.js";
|
|
|
|
function normalizeAllowEntry(raw: string): string {
|
|
return raw.trim().toLowerCase().replace(/^(nextcloud-talk|nc-talk|nc):/i, "");
|
|
}
|
|
|
|
export function normalizeNextcloudTalkAllowlist(
|
|
values: Array<string | number> | undefined,
|
|
): string[] {
|
|
return (values ?? []).map((value) => normalizeAllowEntry(String(value))).filter(Boolean);
|
|
}
|
|
|
|
export function resolveNextcloudTalkAllowlistMatch(params: {
|
|
allowFrom: Array<string | number> | undefined;
|
|
senderId: string;
|
|
senderName?: string | null;
|
|
}): AllowlistMatch<"wildcard" | "id" | "name"> {
|
|
const allowFrom = normalizeNextcloudTalkAllowlist(params.allowFrom);
|
|
if (allowFrom.length === 0) return { allowed: false };
|
|
if (allowFrom.includes("*")) {
|
|
return { allowed: true, matchKey: "*", matchSource: "wildcard" };
|
|
}
|
|
const senderId = normalizeAllowEntry(params.senderId);
|
|
if (allowFrom.includes(senderId)) {
|
|
return { allowed: true, matchKey: senderId, matchSource: "id" };
|
|
}
|
|
const senderName = params.senderName ? normalizeAllowEntry(params.senderName) : "";
|
|
if (senderName && allowFrom.includes(senderName)) {
|
|
return { allowed: true, matchKey: senderName, matchSource: "name" };
|
|
}
|
|
return { allowed: false };
|
|
}
|
|
|
|
export type NextcloudTalkRoomMatch = {
|
|
roomConfig?: NextcloudTalkRoomConfig;
|
|
wildcardConfig?: NextcloudTalkRoomConfig;
|
|
roomKey?: string;
|
|
matchSource?: "direct" | "parent" | "wildcard";
|
|
allowed: boolean;
|
|
allowlistConfigured: boolean;
|
|
};
|
|
|
|
export function resolveNextcloudTalkRoomMatch(params: {
|
|
rooms?: Record<string, NextcloudTalkRoomConfig>;
|
|
roomToken: string;
|
|
roomName?: string | null;
|
|
}): NextcloudTalkRoomMatch {
|
|
const rooms = params.rooms ?? {};
|
|
const allowlistConfigured = Object.keys(rooms).length > 0;
|
|
const roomName = params.roomName?.trim() || undefined;
|
|
const roomCandidates = buildChannelKeyCandidates(
|
|
params.roomToken,
|
|
roomName,
|
|
roomName ? normalizeChannelSlug(roomName) : undefined,
|
|
);
|
|
const match = resolveChannelEntryMatchWithFallback({
|
|
entries: rooms,
|
|
keys: roomCandidates,
|
|
wildcardKey: "*",
|
|
normalizeKey: normalizeChannelSlug,
|
|
});
|
|
const roomConfig = match.entry;
|
|
const allowed = resolveNestedAllowlistDecision({
|
|
outerConfigured: allowlistConfigured,
|
|
outerMatched: Boolean(roomConfig),
|
|
innerConfigured: false,
|
|
innerMatched: false,
|
|
});
|
|
|
|
return {
|
|
roomConfig,
|
|
wildcardConfig: match.wildcardEntry,
|
|
roomKey: match.matchKey ?? match.key,
|
|
matchSource: match.matchSource,
|
|
allowed,
|
|
allowlistConfigured,
|
|
};
|
|
}
|
|
|
|
export function resolveNextcloudTalkRequireMention(params: {
|
|
roomConfig?: NextcloudTalkRoomConfig;
|
|
wildcardConfig?: NextcloudTalkRoomConfig;
|
|
}): boolean {
|
|
if (typeof params.roomConfig?.requireMention === "boolean") {
|
|
return params.roomConfig.requireMention;
|
|
}
|
|
if (typeof params.wildcardConfig?.requireMention === "boolean") {
|
|
return params.wildcardConfig.requireMention;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export function resolveNextcloudTalkGroupAllow(params: {
|
|
groupPolicy: GroupPolicy;
|
|
outerAllowFrom: Array<string | number> | undefined;
|
|
innerAllowFrom: Array<string | number> | undefined;
|
|
senderId: string;
|
|
senderName?: string | null;
|
|
}): { allowed: boolean; outerMatch: AllowlistMatch; innerMatch: AllowlistMatch } {
|
|
if (params.groupPolicy === "disabled") {
|
|
return { allowed: false, outerMatch: { allowed: false }, innerMatch: { allowed: false } };
|
|
}
|
|
if (params.groupPolicy === "open") {
|
|
return { allowed: true, outerMatch: { allowed: true }, innerMatch: { allowed: true } };
|
|
}
|
|
|
|
const outerAllow = normalizeNextcloudTalkAllowlist(params.outerAllowFrom);
|
|
const innerAllow = normalizeNextcloudTalkAllowlist(params.innerAllowFrom);
|
|
if (outerAllow.length === 0 && innerAllow.length === 0) {
|
|
return { allowed: false, outerMatch: { allowed: false }, innerMatch: { allowed: false } };
|
|
}
|
|
|
|
const outerMatch = resolveNextcloudTalkAllowlistMatch({
|
|
allowFrom: params.outerAllowFrom,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
});
|
|
const innerMatch = resolveNextcloudTalkAllowlistMatch({
|
|
allowFrom: params.innerAllowFrom,
|
|
senderId: params.senderId,
|
|
senderName: params.senderName,
|
|
});
|
|
const allowed = resolveNestedAllowlistDecision({
|
|
outerConfigured: outerAllow.length > 0 || innerAllow.length > 0,
|
|
outerMatched: outerAllow.length > 0 ? outerMatch.allowed : true,
|
|
innerConfigured: innerAllow.length > 0,
|
|
innerMatched: innerMatch.allowed,
|
|
});
|
|
|
|
return { allowed, outerMatch, innerMatch };
|
|
}
|
|
|
|
export function resolveNextcloudTalkMentionGate(params: {
|
|
isGroup: boolean;
|
|
requireMention: boolean;
|
|
wasMentioned: boolean;
|
|
allowTextCommands: boolean;
|
|
hasControlCommand: boolean;
|
|
commandAuthorized: boolean;
|
|
}): { shouldSkip: boolean; shouldBypassMention: boolean } {
|
|
const result = resolveMentionGatingWithBypass({
|
|
isGroup: params.isGroup,
|
|
requireMention: params.requireMention,
|
|
canDetectMention: true,
|
|
wasMentioned: params.wasMentioned,
|
|
allowTextCommands: params.allowTextCommands,
|
|
hasControlCommand: params.hasControlCommand,
|
|
commandAuthorized: params.commandAuthorized,
|
|
});
|
|
return { shouldSkip: result.shouldSkip, shouldBypassMention: result.shouldBypassMention };
|
|
}
|