feat(channels): add resolve command + defaults
This commit is contained in:
@@ -8,5 +8,7 @@ export type { ChannelsLogsOptions } from "./channels/logs.js";
|
||||
export { channelsLogsCommand } from "./channels/logs.js";
|
||||
export type { ChannelsRemoveOptions } from "./channels/remove.js";
|
||||
export { channelsRemoveCommand } from "./channels/remove.js";
|
||||
export type { ChannelsResolveOptions } from "./channels/resolve.js";
|
||||
export { channelsResolveCommand } from "./channels/resolve.js";
|
||||
export type { ChannelsStatusOptions } from "./channels/status.js";
|
||||
export { channelsStatusCommand, formatGatewayChannelsStatusLines } from "./channels/status.js";
|
||||
|
||||
131
src/commands/channels/resolve.ts
Normal file
131
src/commands/channels/resolve.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { danger } from "../../globals.js";
|
||||
import { getChannelPlugin } from "../../channels/plugins/index.js";
|
||||
import type { ChannelResolveKind, ChannelResolveResult } from "../../channels/plugins/types.js";
|
||||
import { resolveMessageChannelSelection } from "../../infra/outbound/channel-selection.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
|
||||
export type ChannelsResolveOptions = {
|
||||
channel?: string;
|
||||
account?: string;
|
||||
kind?: "auto" | "user" | "group" | "channel";
|
||||
json?: boolean;
|
||||
entries?: string[];
|
||||
};
|
||||
|
||||
type ResolveResult = {
|
||||
input: string;
|
||||
resolved: boolean;
|
||||
id?: string;
|
||||
name?: string;
|
||||
error?: string;
|
||||
note?: string;
|
||||
};
|
||||
|
||||
function resolvePreferredKind(kind?: ChannelsResolveOptions["kind"]): ChannelResolveKind | undefined {
|
||||
if (!kind || kind === "auto") return undefined;
|
||||
if (kind === "user") return "user";
|
||||
return "group";
|
||||
}
|
||||
|
||||
function detectAutoKind(input: string): ChannelResolveKind {
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return "group";
|
||||
if (trimmed.startsWith("@")) return "user";
|
||||
if (/^<@!?/.test(trimmed)) return "user";
|
||||
if (/^(user|discord|slack|matrix|msteams|teams|zalo|zalouser):/i.test(trimmed)) {
|
||||
return "user";
|
||||
}
|
||||
return "group";
|
||||
}
|
||||
|
||||
function formatResolveResult(result: ResolveResult): string {
|
||||
if (!result.resolved || !result.id) return `${result.input} -> unresolved`;
|
||||
const name = result.name ? ` (${result.name})` : "";
|
||||
const note = result.note ? ` [${result.note}]` : "";
|
||||
return `${result.input} -> ${result.id}${name}${note}`;
|
||||
}
|
||||
|
||||
export async function channelsResolveCommand(
|
||||
opts: ChannelsResolveOptions,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
const cfg = loadConfig();
|
||||
const entries = (opts.entries ?? []).map((entry) => entry.trim()).filter(Boolean);
|
||||
if (entries.length === 0) {
|
||||
throw new Error("At least one entry is required.");
|
||||
}
|
||||
|
||||
const selection = await resolveMessageChannelSelection({
|
||||
cfg,
|
||||
channel: opts.channel ?? null,
|
||||
});
|
||||
const plugin = getChannelPlugin(selection.channel);
|
||||
if (!plugin?.resolver?.resolveTargets) {
|
||||
throw new Error(`Channel ${selection.channel} does not support resolve.`);
|
||||
}
|
||||
const preferredKind = resolvePreferredKind(opts.kind);
|
||||
|
||||
let results: ResolveResult[] = [];
|
||||
if (preferredKind) {
|
||||
const resolved = await plugin.resolver.resolveTargets({
|
||||
cfg,
|
||||
accountId: opts.account ?? null,
|
||||
inputs: entries,
|
||||
kind: preferredKind,
|
||||
runtime,
|
||||
});
|
||||
results = resolved.map((entry) => ({
|
||||
input: entry.input,
|
||||
resolved: entry.resolved,
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
note: entry.note,
|
||||
}));
|
||||
} else {
|
||||
const byKind = new Map<ChannelResolveKind, string[]>();
|
||||
for (const entry of entries) {
|
||||
const kind = detectAutoKind(entry);
|
||||
byKind.set(kind, [...(byKind.get(kind) ?? []), entry]);
|
||||
}
|
||||
const resolved: ChannelResolveResult[] = [];
|
||||
for (const [kind, inputs] of byKind.entries()) {
|
||||
const batch = await plugin.resolver.resolveTargets({
|
||||
cfg,
|
||||
accountId: opts.account ?? null,
|
||||
inputs,
|
||||
kind,
|
||||
runtime,
|
||||
});
|
||||
resolved.push(...batch);
|
||||
}
|
||||
const byInput = new Map(resolved.map((entry) => [entry.input, entry]));
|
||||
results = entries.map((input) => {
|
||||
const entry = byInput.get(input);
|
||||
return {
|
||||
input,
|
||||
resolved: entry?.resolved ?? false,
|
||||
id: entry?.id,
|
||||
name: entry?.name,
|
||||
note: entry?.note,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
runtime.log(JSON.stringify(results, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const result of results) {
|
||||
if (result.resolved && result.id) {
|
||||
runtime.log(formatResolveResult(result));
|
||||
} else {
|
||||
runtime.error(
|
||||
danger(
|
||||
`${result.input} -> unresolved${result.error ? ` (${result.error})` : result.note ? ` (${result.note})` : ""}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user