refactor: plugin catalog + nextcloud policy
This commit is contained in:
160
extensions/nextcloud-talk/src/policy.ts
Normal file
160
extensions/nextcloud-talk/src/policy.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user