mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
refactor(imessage): share target parsing helpers
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
import {
|
||||
parseChatAllowTargetPrefixes,
|
||||
parseChatTargetPrefixesOrThrow,
|
||||
resolveServicePrefixedAllowTarget,
|
||||
resolveServicePrefixedTarget,
|
||||
} from "openclaw/plugin-sdk";
|
||||
|
||||
export type BlueBubblesService = "imessage" | "sms" | "auto";
|
||||
|
||||
export type BlueBubblesTarget =
|
||||
@@ -205,54 +212,30 @@ export function parseBlueBubblesTarget(raw: string): BlueBubblesTarget {
|
||||
}
|
||||
const lower = trimmed.toLowerCase();
|
||||
|
||||
for (const { prefix, service } of SERVICE_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const remainder = stripPrefix(trimmed, prefix);
|
||||
if (!remainder) {
|
||||
throw new Error(`${prefix} target is required`);
|
||||
}
|
||||
const remainderLower = remainder.toLowerCase();
|
||||
const isChatTarget =
|
||||
CHAT_ID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_GUID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_IDENTIFIER_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
remainderLower.startsWith("group:");
|
||||
if (isChatTarget) {
|
||||
return parseBlueBubblesTarget(remainder);
|
||||
}
|
||||
return { kind: "handle", to: remainder, service };
|
||||
}
|
||||
const servicePrefixed = resolveServicePrefixedTarget({
|
||||
trimmed,
|
||||
lower,
|
||||
servicePrefixes: SERVICE_PREFIXES,
|
||||
isChatTarget: (remainderLower) =>
|
||||
CHAT_ID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_GUID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_IDENTIFIER_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
remainderLower.startsWith("group:"),
|
||||
parseTarget: parseBlueBubblesTarget,
|
||||
});
|
||||
if (servicePrefixed) {
|
||||
return servicePrefixed;
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_ID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
const chatId = Number.parseInt(value, 10);
|
||||
if (!Number.isFinite(chatId)) {
|
||||
throw new Error(`Invalid chat_id: ${value}`);
|
||||
}
|
||||
return { kind: "chat_id", chatId };
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_GUID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (!value) {
|
||||
throw new Error("chat_guid is required");
|
||||
}
|
||||
return { kind: "chat_guid", chatGuid: value };
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (!value) {
|
||||
throw new Error("chat_identifier is required");
|
||||
}
|
||||
return { kind: "chat_identifier", chatIdentifier: value };
|
||||
}
|
||||
const chatTarget = parseChatTargetPrefixesOrThrow({
|
||||
trimmed,
|
||||
lower,
|
||||
chatIdPrefixes: CHAT_ID_PREFIXES,
|
||||
chatGuidPrefixes: CHAT_GUID_PREFIXES,
|
||||
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
|
||||
});
|
||||
if (chatTarget) {
|
||||
return chatTarget;
|
||||
}
|
||||
|
||||
if (lower.startsWith("group:")) {
|
||||
@@ -293,42 +276,25 @@ export function parseBlueBubblesAllowTarget(raw: string): BlueBubblesAllowTarget
|
||||
}
|
||||
const lower = trimmed.toLowerCase();
|
||||
|
||||
for (const { prefix } of SERVICE_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const remainder = stripPrefix(trimmed, prefix);
|
||||
if (!remainder) {
|
||||
return { kind: "handle", handle: "" };
|
||||
}
|
||||
return parseBlueBubblesAllowTarget(remainder);
|
||||
}
|
||||
const servicePrefixed = resolveServicePrefixedAllowTarget({
|
||||
trimmed,
|
||||
lower,
|
||||
servicePrefixes: SERVICE_PREFIXES,
|
||||
parseAllowTarget: parseBlueBubblesAllowTarget,
|
||||
});
|
||||
if (servicePrefixed) {
|
||||
return servicePrefixed;
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_ID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
const chatId = Number.parseInt(value, 10);
|
||||
if (Number.isFinite(chatId)) {
|
||||
return { kind: "chat_id", chatId };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_GUID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (value) {
|
||||
return { kind: "chat_guid", chatGuid: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (value) {
|
||||
return { kind: "chat_identifier", chatIdentifier: value };
|
||||
}
|
||||
}
|
||||
const chatTarget = parseChatAllowTargetPrefixes({
|
||||
trimmed,
|
||||
lower,
|
||||
chatIdPrefixes: CHAT_ID_PREFIXES,
|
||||
chatGuidPrefixes: CHAT_GUID_PREFIXES,
|
||||
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
|
||||
});
|
||||
if (chatTarget) {
|
||||
return chatTarget;
|
||||
}
|
||||
|
||||
if (lower.startsWith("group:")) {
|
||||
|
||||
132
src/imessage/target-parsing-helpers.ts
Normal file
132
src/imessage/target-parsing-helpers.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
export type ServicePrefix<TService extends string> = { prefix: string; service: TService };
|
||||
|
||||
export type ChatTargetPrefixesParams = {
|
||||
trimmed: string;
|
||||
lower: string;
|
||||
chatIdPrefixes: string[];
|
||||
chatGuidPrefixes: string[];
|
||||
chatIdentifierPrefixes: string[];
|
||||
};
|
||||
|
||||
export type ParsedChatTarget =
|
||||
| { kind: "chat_id"; chatId: number }
|
||||
| { kind: "chat_guid"; chatGuid: string }
|
||||
| { kind: "chat_identifier"; chatIdentifier: string };
|
||||
|
||||
function stripPrefix(value: string, prefix: string): string {
|
||||
return value.slice(prefix.length).trim();
|
||||
}
|
||||
|
||||
export function resolveServicePrefixedTarget<TService extends string, TTarget>(params: {
|
||||
trimmed: string;
|
||||
lower: string;
|
||||
servicePrefixes: Array<ServicePrefix<TService>>;
|
||||
isChatTarget: (remainderLower: string) => boolean;
|
||||
parseTarget: (remainder: string) => TTarget;
|
||||
}): ({ kind: "handle"; to: string; service: TService } | TTarget) | null {
|
||||
for (const { prefix, service } of params.servicePrefixes) {
|
||||
if (!params.lower.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
const remainder = stripPrefix(params.trimmed, prefix);
|
||||
if (!remainder) {
|
||||
throw new Error(`${prefix} target is required`);
|
||||
}
|
||||
const remainderLower = remainder.toLowerCase();
|
||||
if (params.isChatTarget(remainderLower)) {
|
||||
return params.parseTarget(remainder);
|
||||
}
|
||||
return { kind: "handle", to: remainder, service };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function parseChatTargetPrefixesOrThrow(
|
||||
params: ChatTargetPrefixesParams,
|
||||
): ParsedChatTarget | null {
|
||||
for (const prefix of params.chatIdPrefixes) {
|
||||
if (params.lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(params.trimmed, prefix);
|
||||
const chatId = Number.parseInt(value, 10);
|
||||
if (!Number.isFinite(chatId)) {
|
||||
throw new Error(`Invalid chat_id: ${value}`);
|
||||
}
|
||||
return { kind: "chat_id", chatId };
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of params.chatGuidPrefixes) {
|
||||
if (params.lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(params.trimmed, prefix);
|
||||
if (!value) {
|
||||
throw new Error("chat_guid is required");
|
||||
}
|
||||
return { kind: "chat_guid", chatGuid: value };
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of params.chatIdentifierPrefixes) {
|
||||
if (params.lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(params.trimmed, prefix);
|
||||
if (!value) {
|
||||
throw new Error("chat_identifier is required");
|
||||
}
|
||||
return { kind: "chat_identifier", chatIdentifier: value };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function resolveServicePrefixedAllowTarget<TAllowTarget>(params: {
|
||||
trimmed: string;
|
||||
lower: string;
|
||||
servicePrefixes: Array<{ prefix: string }>;
|
||||
parseAllowTarget: (remainder: string) => TAllowTarget;
|
||||
}): (TAllowTarget | { kind: "handle"; handle: string }) | null {
|
||||
for (const { prefix } of params.servicePrefixes) {
|
||||
if (!params.lower.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
const remainder = stripPrefix(params.trimmed, prefix);
|
||||
if (!remainder) {
|
||||
return { kind: "handle", handle: "" };
|
||||
}
|
||||
return params.parseAllowTarget(remainder);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function parseChatAllowTargetPrefixes(
|
||||
params: ChatTargetPrefixesParams,
|
||||
): ParsedChatTarget | null {
|
||||
for (const prefix of params.chatIdPrefixes) {
|
||||
if (params.lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(params.trimmed, prefix);
|
||||
const chatId = Number.parseInt(value, 10);
|
||||
if (Number.isFinite(chatId)) {
|
||||
return { kind: "chat_id", chatId };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of params.chatGuidPrefixes) {
|
||||
if (params.lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(params.trimmed, prefix);
|
||||
if (value) {
|
||||
return { kind: "chat_guid", chatGuid: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of params.chatIdentifierPrefixes) {
|
||||
if (params.lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(params.trimmed, prefix);
|
||||
if (value) {
|
||||
return { kind: "chat_identifier", chatIdentifier: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
import { normalizeE164 } from "../utils.js";
|
||||
import {
|
||||
parseChatAllowTargetPrefixes,
|
||||
parseChatTargetPrefixesOrThrow,
|
||||
resolveServicePrefixedAllowTarget,
|
||||
resolveServicePrefixedTarget,
|
||||
} from "./target-parsing-helpers.js";
|
||||
|
||||
export type IMessageService = "imessage" | "sms" | "auto";
|
||||
|
||||
@@ -23,10 +29,6 @@ const SERVICE_PREFIXES: Array<{ prefix: string; service: IMessageService }> = [
|
||||
{ prefix: "auto:", service: "auto" },
|
||||
];
|
||||
|
||||
function stripPrefix(value: string, prefix: string): string {
|
||||
return value.slice(prefix.length).trim();
|
||||
}
|
||||
|
||||
export function normalizeIMessageHandle(raw: string): string {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
@@ -80,53 +82,29 @@ export function parseIMessageTarget(raw: string): IMessageTarget {
|
||||
}
|
||||
const lower = trimmed.toLowerCase();
|
||||
|
||||
for (const { prefix, service } of SERVICE_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const remainder = stripPrefix(trimmed, prefix);
|
||||
if (!remainder) {
|
||||
throw new Error(`${prefix} target is required`);
|
||||
}
|
||||
const remainderLower = remainder.toLowerCase();
|
||||
const isChatTarget =
|
||||
CHAT_ID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_GUID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_IDENTIFIER_PREFIXES.some((p) => remainderLower.startsWith(p));
|
||||
if (isChatTarget) {
|
||||
return parseIMessageTarget(remainder);
|
||||
}
|
||||
return { kind: "handle", to: remainder, service };
|
||||
}
|
||||
const servicePrefixed = resolveServicePrefixedTarget({
|
||||
trimmed,
|
||||
lower,
|
||||
servicePrefixes: SERVICE_PREFIXES,
|
||||
isChatTarget: (remainderLower) =>
|
||||
CHAT_ID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_GUID_PREFIXES.some((p) => remainderLower.startsWith(p)) ||
|
||||
CHAT_IDENTIFIER_PREFIXES.some((p) => remainderLower.startsWith(p)),
|
||||
parseTarget: parseIMessageTarget,
|
||||
});
|
||||
if (servicePrefixed) {
|
||||
return servicePrefixed;
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_ID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
const chatId = Number.parseInt(value, 10);
|
||||
if (!Number.isFinite(chatId)) {
|
||||
throw new Error(`Invalid chat_id: ${value}`);
|
||||
}
|
||||
return { kind: "chat_id", chatId };
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_GUID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (!value) {
|
||||
throw new Error("chat_guid is required");
|
||||
}
|
||||
return { kind: "chat_guid", chatGuid: value };
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (!value) {
|
||||
throw new Error("chat_identifier is required");
|
||||
}
|
||||
return { kind: "chat_identifier", chatIdentifier: value };
|
||||
}
|
||||
const chatTarget = parseChatTargetPrefixesOrThrow({
|
||||
trimmed,
|
||||
lower,
|
||||
chatIdPrefixes: CHAT_ID_PREFIXES,
|
||||
chatGuidPrefixes: CHAT_GUID_PREFIXES,
|
||||
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
|
||||
});
|
||||
if (chatTarget) {
|
||||
return chatTarget;
|
||||
}
|
||||
|
||||
return { kind: "handle", to: trimmed, service: "auto" };
|
||||
@@ -139,42 +117,25 @@ export function parseIMessageAllowTarget(raw: string): IMessageAllowTarget {
|
||||
}
|
||||
const lower = trimmed.toLowerCase();
|
||||
|
||||
for (const { prefix } of SERVICE_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const remainder = stripPrefix(trimmed, prefix);
|
||||
if (!remainder) {
|
||||
return { kind: "handle", handle: "" };
|
||||
}
|
||||
return parseIMessageAllowTarget(remainder);
|
||||
}
|
||||
const servicePrefixed = resolveServicePrefixedAllowTarget({
|
||||
trimmed,
|
||||
lower,
|
||||
servicePrefixes: SERVICE_PREFIXES,
|
||||
parseAllowTarget: parseIMessageAllowTarget,
|
||||
});
|
||||
if (servicePrefixed) {
|
||||
return servicePrefixed;
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_ID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
const chatId = Number.parseInt(value, 10);
|
||||
if (Number.isFinite(chatId)) {
|
||||
return { kind: "chat_id", chatId };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_GUID_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (value) {
|
||||
return { kind: "chat_guid", chatGuid: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
|
||||
if (lower.startsWith(prefix)) {
|
||||
const value = stripPrefix(trimmed, prefix);
|
||||
if (value) {
|
||||
return { kind: "chat_identifier", chatIdentifier: value };
|
||||
}
|
||||
}
|
||||
const chatTarget = parseChatAllowTargetPrefixes({
|
||||
trimmed,
|
||||
lower,
|
||||
chatIdPrefixes: CHAT_ID_PREFIXES,
|
||||
chatGuidPrefixes: CHAT_GUID_PREFIXES,
|
||||
chatIdentifierPrefixes: CHAT_IDENTIFIER_PREFIXES,
|
||||
});
|
||||
if (chatTarget) {
|
||||
return chatTarget;
|
||||
}
|
||||
|
||||
return { kind: "handle", handle: normalizeIMessageHandle(trimmed) };
|
||||
|
||||
@@ -313,6 +313,12 @@ export {
|
||||
looksLikeIMessageTargetId,
|
||||
normalizeIMessageMessagingTarget,
|
||||
} from "../channels/plugins/normalize/imessage.js";
|
||||
export {
|
||||
parseChatAllowTargetPrefixes,
|
||||
parseChatTargetPrefixesOrThrow,
|
||||
resolveServicePrefixedAllowTarget,
|
||||
resolveServicePrefixedTarget,
|
||||
} from "../imessage/target-parsing-helpers.js";
|
||||
|
||||
// Channel: Slack
|
||||
export {
|
||||
|
||||
Reference in New Issue
Block a user