Files
Moltbot/src/auto-reply/command-auth.ts
2026-01-13 01:53:40 +00:00

128 lines
4.0 KiB
TypeScript

import type { ClawdbotConfig } from "../config/config.js";
import type { ProviderDock } from "../providers/dock.js";
import { getProviderDock, listProviderDocks } from "../providers/dock.js";
import type { ProviderId } from "../providers/plugins/types.js";
import { normalizeProviderId } from "../providers/registry.js";
import type { MsgContext } from "./templating.js";
export type CommandAuthorization = {
providerId?: ProviderId;
ownerList: string[];
senderId?: string;
isAuthorizedSender: boolean;
from?: string;
to?: string;
};
function resolveProviderFromContext(
ctx: MsgContext,
cfg: ClawdbotConfig,
): ProviderId | undefined {
const direct =
normalizeProviderId(ctx.Provider) ??
normalizeProviderId(ctx.Surface) ??
normalizeProviderId(ctx.OriginatingChannel);
if (direct) return direct;
const candidates = [ctx.From, ctx.To]
.filter((value): value is string => Boolean(value?.trim()))
.flatMap((value) => value.split(":").map((part) => part.trim()));
for (const candidate of candidates) {
const normalized = normalizeProviderId(candidate);
if (normalized) return normalized;
}
const configured = listProviderDocks()
.map((dock) => {
if (!dock.config?.resolveAllowFrom) return null;
const allowFrom = dock.config.resolveAllowFrom({
cfg,
accountId: ctx.AccountId,
});
if (!Array.isArray(allowFrom) || allowFrom.length === 0) return null;
return dock.id;
})
.filter((value): value is ProviderId => Boolean(value));
if (configured.length === 1) return configured[0];
return undefined;
}
function formatAllowFromList(params: {
dock?: ProviderDock;
cfg: ClawdbotConfig;
accountId?: string | null;
allowFrom: Array<string | number>;
}): string[] {
const { dock, cfg, accountId, allowFrom } = params;
if (!allowFrom || allowFrom.length === 0) return [];
if (dock?.config?.formatAllowFrom) {
return dock.config.formatAllowFrom({ cfg, accountId, allowFrom });
}
return allowFrom.map((entry) => String(entry).trim()).filter(Boolean);
}
export function resolveCommandAuthorization(params: {
ctx: MsgContext;
cfg: ClawdbotConfig;
commandAuthorized: boolean;
}): CommandAuthorization {
const { ctx, cfg, commandAuthorized } = params;
const providerId = resolveProviderFromContext(ctx, cfg);
const dock = providerId ? getProviderDock(providerId) : undefined;
const from = (ctx.From ?? "").trim();
const to = (ctx.To ?? "").trim();
const allowFromRaw = dock?.config?.resolveAllowFrom
? dock.config.resolveAllowFrom({ cfg, accountId: ctx.AccountId })
: [];
const allowFromList = formatAllowFromList({
dock,
cfg,
accountId: ctx.AccountId,
allowFrom: Array.isArray(allowFromRaw) ? allowFromRaw : [],
});
const allowAll =
allowFromList.length === 0 ||
allowFromList.some((entry) => entry.trim() === "*");
const ownerCandidates = allowAll
? []
: allowFromList.filter((entry) => entry !== "*");
if (!allowAll && ownerCandidates.length === 0 && to) {
const normalizedTo = formatAllowFromList({
dock,
cfg,
accountId: ctx.AccountId,
allowFrom: [to],
})[0];
if (normalizedTo) ownerCandidates.push(normalizedTo);
}
const ownerList = ownerCandidates;
const senderIdCandidate = ctx.SenderId?.trim() ?? "";
const senderE164Candidate = ctx.SenderE164?.trim() ?? "";
const senderRaw = senderIdCandidate || senderE164Candidate || from;
const senderId = senderRaw
? formatAllowFromList({
dock,
cfg,
accountId: ctx.AccountId,
allowFrom: [senderRaw],
})[0]
: undefined;
const enforceOwner = Boolean(dock?.commands?.enforceOwnerForCommands);
const isOwner =
!enforceOwner ||
allowAll ||
ownerList.length === 0 ||
(senderId ? ownerList.includes(senderId) : false);
const isAuthorizedSender = commandAuthorized && isOwner;
return {
providerId,
ownerList,
senderId: senderId || undefined,
isAuthorizedSender,
from: from || undefined,
to: to || undefined,
};
}