refactor(channels): unify target parsing
This commit is contained in:
@@ -4,7 +4,7 @@ import { resolveSlackAccount } from "../../slack/accounts.js";
|
||||
import { resolveDiscordAccount } from "../../discord/accounts.js";
|
||||
import { resolveTelegramAccount } from "../../telegram/accounts.js";
|
||||
import { resolveWhatsAppAccount } from "../../web/accounts.js";
|
||||
import { normalizeSlackMessagingTarget } from "./normalize-target.js";
|
||||
import { normalizeSlackMessagingTarget } from "./normalize/slack.js";
|
||||
import { isWhatsAppGroupJid, normalizeWhatsAppTarget } from "../../whatsapp/normalize.js";
|
||||
|
||||
export type DirectoryConfigParams = {
|
||||
|
||||
@@ -26,7 +26,10 @@ import {
|
||||
} from "./config-helpers.js";
|
||||
import { resolveDiscordGroupRequireMention } from "./group-mentions.js";
|
||||
import { formatPairingApproveHint } from "./helpers.js";
|
||||
import { looksLikeDiscordTargetId, normalizeDiscordMessagingTarget } from "./normalize-target.js";
|
||||
import {
|
||||
looksLikeDiscordTargetId,
|
||||
normalizeDiscordMessagingTarget,
|
||||
} from "./normalize/discord.js";
|
||||
import { discordOnboardingAdapter } from "./onboarding/discord.js";
|
||||
import { PAIRING_APPROVED_MESSAGE } from "./pairing-message.js";
|
||||
import {
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import { parseDiscordTarget } from "../../discord/targets.js";
|
||||
import { parseSlackTarget } from "../../slack/targets.js";
|
||||
import { normalizeWhatsAppTarget } from "../../whatsapp/normalize.js";
|
||||
|
||||
export function normalizeSlackMessagingTarget(raw: string): string | undefined {
|
||||
const target = parseSlackTarget(raw, { defaultKind: "channel" });
|
||||
return target?.normalized;
|
||||
}
|
||||
|
||||
export function looksLikeSlackTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^<@([A-Z0-9]+)>$/i.test(trimmed)) return true;
|
||||
if (/^(user|channel):/i.test(trimmed)) return true;
|
||||
if (/^slack:/i.test(trimmed)) return true;
|
||||
if (/^[@#]/.test(trimmed)) return true;
|
||||
return /^[CUWGD][A-Z0-9]{8,}$/i.test(trimmed);
|
||||
}
|
||||
|
||||
export function normalizeDiscordMessagingTarget(raw: string): string | undefined {
|
||||
// Default bare IDs to channels so routing is stable across tool actions.
|
||||
const target = parseDiscordTarget(raw, { defaultKind: "channel" });
|
||||
return target?.normalized;
|
||||
}
|
||||
|
||||
export function looksLikeDiscordTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^<@!?\d+>$/.test(trimmed)) return true;
|
||||
if (/^(user|channel|discord):/i.test(trimmed)) return true;
|
||||
if (/^\d{6,}$/.test(trimmed)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function normalizeTelegramMessagingTarget(raw: string): string | undefined {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
let normalized = trimmed;
|
||||
if (normalized.startsWith("telegram:")) {
|
||||
normalized = normalized.slice("telegram:".length).trim();
|
||||
} else if (normalized.startsWith("tg:")) {
|
||||
normalized = normalized.slice("tg:".length).trim();
|
||||
}
|
||||
if (!normalized) return undefined;
|
||||
const tmeMatch =
|
||||
/^https?:\/\/t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized) ??
|
||||
/^t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized);
|
||||
if (tmeMatch?.[1]) normalized = `@${tmeMatch[1]}`;
|
||||
if (!normalized) return undefined;
|
||||
return `telegram:${normalized}`.toLowerCase();
|
||||
}
|
||||
|
||||
export function looksLikeTelegramTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^(telegram|tg):/i.test(trimmed)) return true;
|
||||
if (trimmed.startsWith("@")) return true;
|
||||
return /^-?\d{6,}$/.test(trimmed);
|
||||
}
|
||||
|
||||
export function normalizeSignalMessagingTarget(raw: string): string | undefined {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
let normalized = trimmed;
|
||||
if (normalized.toLowerCase().startsWith("signal:")) {
|
||||
normalized = normalized.slice("signal:".length).trim();
|
||||
}
|
||||
if (!normalized) return undefined;
|
||||
const lower = normalized.toLowerCase();
|
||||
if (lower.startsWith("group:")) {
|
||||
const id = normalized.slice("group:".length).trim();
|
||||
return id ? `group:${id}`.toLowerCase() : undefined;
|
||||
}
|
||||
if (lower.startsWith("username:")) {
|
||||
const id = normalized.slice("username:".length).trim();
|
||||
return id ? `username:${id}`.toLowerCase() : undefined;
|
||||
}
|
||||
if (lower.startsWith("u:")) {
|
||||
const id = normalized.slice("u:".length).trim();
|
||||
return id ? `username:${id}`.toLowerCase() : undefined;
|
||||
}
|
||||
return normalized.toLowerCase();
|
||||
}
|
||||
|
||||
export function looksLikeSignalTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^(signal:)?(group:|username:|u:)/i.test(trimmed)) return true;
|
||||
return /^\+?\d{3,}$/.test(trimmed);
|
||||
}
|
||||
|
||||
export function normalizeWhatsAppMessagingTarget(raw: string): string | undefined {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
return normalizeWhatsAppTarget(trimmed) ?? undefined;
|
||||
}
|
||||
|
||||
export function looksLikeWhatsAppTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^whatsapp:/i.test(trimmed)) return true;
|
||||
if (trimmed.includes("@")) return true;
|
||||
return /^\+?\d{3,}$/.test(trimmed);
|
||||
}
|
||||
16
src/channels/plugins/normalize/discord.ts
Normal file
16
src/channels/plugins/normalize/discord.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { parseDiscordTarget } from "../../../discord/targets.js";
|
||||
|
||||
export function normalizeDiscordMessagingTarget(raw: string): string | undefined {
|
||||
// Default bare IDs to channels so routing is stable across tool actions.
|
||||
const target = parseDiscordTarget(raw, { defaultKind: "channel" });
|
||||
return target?.normalized;
|
||||
}
|
||||
|
||||
export function looksLikeDiscordTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^<@!?\d+>$/.test(trimmed)) return true;
|
||||
if (/^(user|channel|discord):/i.test(trimmed)) return true;
|
||||
if (/^\d{6,}$/.test(trimmed)) return true;
|
||||
return false;
|
||||
}
|
||||
30
src/channels/plugins/normalize/signal.ts
Normal file
30
src/channels/plugins/normalize/signal.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export function normalizeSignalMessagingTarget(raw: string): string | undefined {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
let normalized = trimmed;
|
||||
if (normalized.toLowerCase().startsWith("signal:")) {
|
||||
normalized = normalized.slice("signal:".length).trim();
|
||||
}
|
||||
if (!normalized) return undefined;
|
||||
const lower = normalized.toLowerCase();
|
||||
if (lower.startsWith("group:")) {
|
||||
const id = normalized.slice("group:".length).trim();
|
||||
return id ? `group:${id}`.toLowerCase() : undefined;
|
||||
}
|
||||
if (lower.startsWith("username:")) {
|
||||
const id = normalized.slice("username:".length).trim();
|
||||
return id ? `username:${id}`.toLowerCase() : undefined;
|
||||
}
|
||||
if (lower.startsWith("u:")) {
|
||||
const id = normalized.slice("u:".length).trim();
|
||||
return id ? `username:${id}`.toLowerCase() : undefined;
|
||||
}
|
||||
return normalized.toLowerCase();
|
||||
}
|
||||
|
||||
export function looksLikeSignalTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^(signal:)?(group:|username:|u:)/i.test(trimmed)) return true;
|
||||
return /^\+?\d{3,}$/.test(trimmed);
|
||||
}
|
||||
16
src/channels/plugins/normalize/slack.ts
Normal file
16
src/channels/plugins/normalize/slack.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { parseSlackTarget } from "../../../slack/targets.js";
|
||||
|
||||
export function normalizeSlackMessagingTarget(raw: string): string | undefined {
|
||||
const target = parseSlackTarget(raw, { defaultKind: "channel" });
|
||||
return target?.normalized;
|
||||
}
|
||||
|
||||
export function looksLikeSlackTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^<@([A-Z0-9]+)>$/i.test(trimmed)) return true;
|
||||
if (/^(user|channel):/i.test(trimmed)) return true;
|
||||
if (/^slack:/i.test(trimmed)) return true;
|
||||
if (/^[@#]/.test(trimmed)) return true;
|
||||
return /^[CUWGD][A-Z0-9]{8,}$/i.test(trimmed);
|
||||
}
|
||||
25
src/channels/plugins/normalize/telegram.ts
Normal file
25
src/channels/plugins/normalize/telegram.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export function normalizeTelegramMessagingTarget(raw: string): string | undefined {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
let normalized = trimmed;
|
||||
if (normalized.startsWith("telegram:")) {
|
||||
normalized = normalized.slice("telegram:".length).trim();
|
||||
} else if (normalized.startsWith("tg:")) {
|
||||
normalized = normalized.slice("tg:".length).trim();
|
||||
}
|
||||
if (!normalized) return undefined;
|
||||
const tmeMatch =
|
||||
/^https?:\/\/t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized) ??
|
||||
/^t\.me\/([A-Za-z0-9_]+)$/i.exec(normalized);
|
||||
if (tmeMatch?.[1]) normalized = `@${tmeMatch[1]}`;
|
||||
if (!normalized) return undefined;
|
||||
return `telegram:${normalized}`.toLowerCase();
|
||||
}
|
||||
|
||||
export function looksLikeTelegramTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^(telegram|tg):/i.test(trimmed)) return true;
|
||||
if (trimmed.startsWith("@")) return true;
|
||||
return /^-?\d{6,}$/.test(trimmed);
|
||||
}
|
||||
15
src/channels/plugins/normalize/whatsapp.ts
Normal file
15
src/channels/plugins/normalize/whatsapp.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { normalizeWhatsAppTarget } from "../../../whatsapp/normalize.js";
|
||||
|
||||
export function normalizeWhatsAppMessagingTarget(raw: string): string | undefined {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
return normalizeWhatsAppTarget(trimmed) ?? undefined;
|
||||
}
|
||||
|
||||
export function looksLikeWhatsAppTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return false;
|
||||
if (/^whatsapp:/i.test(trimmed)) return true;
|
||||
if (trimmed.includes("@")) return true;
|
||||
return /^\+?\d{3,}$/.test(trimmed);
|
||||
}
|
||||
@@ -18,7 +18,10 @@ import {
|
||||
} from "./config-helpers.js";
|
||||
import { formatPairingApproveHint } from "./helpers.js";
|
||||
import { resolveChannelMediaMaxBytes } from "./media-limits.js";
|
||||
import { looksLikeSignalTargetId, normalizeSignalMessagingTarget } from "./normalize-target.js";
|
||||
import {
|
||||
looksLikeSignalTargetId,
|
||||
normalizeSignalMessagingTarget,
|
||||
} from "./normalize/signal.js";
|
||||
import { signalOnboardingAdapter } from "./onboarding/signal.js";
|
||||
import { PAIRING_APPROVED_MESSAGE } from "./pairing-message.js";
|
||||
import {
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from "./config-helpers.js";
|
||||
import { resolveSlackGroupRequireMention } from "./group-mentions.js";
|
||||
import { formatPairingApproveHint } from "./helpers.js";
|
||||
import { looksLikeSlackTargetId, normalizeSlackMessagingTarget } from "./normalize-target.js";
|
||||
import { looksLikeSlackTargetId, normalizeSlackMessagingTarget } from "./normalize/slack.js";
|
||||
import { slackOnboardingAdapter } from "./onboarding/slack.js";
|
||||
import { PAIRING_APPROVED_MESSAGE } from "./pairing-message.js";
|
||||
import {
|
||||
|
||||
@@ -26,7 +26,10 @@ import {
|
||||
} from "./config-helpers.js";
|
||||
import { resolveTelegramGroupRequireMention } from "./group-mentions.js";
|
||||
import { formatPairingApproveHint } from "./helpers.js";
|
||||
import { looksLikeTelegramTargetId, normalizeTelegramMessagingTarget } from "./normalize-target.js";
|
||||
import {
|
||||
looksLikeTelegramTargetId,
|
||||
normalizeTelegramMessagingTarget,
|
||||
} from "./normalize/telegram.js";
|
||||
import { telegramOnboardingAdapter } from "./onboarding/telegram.js";
|
||||
import { PAIRING_APPROVED_MESSAGE } from "./pairing-message.js";
|
||||
import {
|
||||
|
||||
@@ -26,7 +26,10 @@ import { buildChannelConfigSchema } from "./config-schema.js";
|
||||
import { createWhatsAppLoginTool } from "./agent-tools/whatsapp-login.js";
|
||||
import { resolveWhatsAppGroupRequireMention } from "./group-mentions.js";
|
||||
import { formatPairingApproveHint } from "./helpers.js";
|
||||
import { looksLikeWhatsAppTargetId, normalizeWhatsAppMessagingTarget } from "./normalize-target.js";
|
||||
import {
|
||||
looksLikeWhatsAppTargetId,
|
||||
normalizeWhatsAppMessagingTarget,
|
||||
} from "./normalize/whatsapp.js";
|
||||
import { whatsappOnboardingAdapter } from "./onboarding/whatsapp.js";
|
||||
import {
|
||||
applyAccountNameToChannelSection,
|
||||
|
||||
Reference in New Issue
Block a user