mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-11 10:36:05 +00:00
* fix(imessage): recognize bare hex group chat identifiers as chat targets A 32-char hex iMessage group chat identifier (as returned by `imsg` for group chats) with no explicit `chat_identifier:` prefix fell through to E.164 phone-number normalization. normalizeE164 strips non-digit characters and prepends `+`, turning `7d5297154d5f436d83dbbdf03fcc8fdd` into `+75297154543683038` -- a bogus phone number. Cron/announce delivery then reported `delivered: true` while silently sending to a nonexistent recipient. Detect bare 32-char hex strings in both the send-path target parser (extensions/imessage/src/targets.ts) and the outbound delivery-target normalizer (extensions/imessage/src/normalize.ts) and route them as chat_identifier targets, consistent with how an explicit `chat_identifier:<id>` prefix already resolves. Fixes #89235. * refactor(imessage): centralize bare chat identifiers * refactor(imessage): centralize bare chat identifiers --------- Co-authored-by: Peter Steinberger <peter@steipete.me> Co-authored-by: Peter Steinberger <steipete@gmail.com>
109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
// Imessage helper module supports normalize behavior.
|
|
import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
|
|
import {
|
|
normalizeLowercaseStringOrEmpty,
|
|
normalizeOptionalString,
|
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
import { normalizeBareIMessageChatIdentifier } from "./target-identifiers.js";
|
|
|
|
const SERVICE_PREFIXES = ["imessage:", "sms:", "auto:"] as const;
|
|
const CHAT_TARGET_PREFIX_RE =
|
|
/^(chat_id:|chatid:|chat:|chat_guid:|chatguid:|guid:|chat_identifier:|chatidentifier:|chatident:)/i;
|
|
|
|
function looksLikeHandleOrPhoneTarget(params: {
|
|
raw: string;
|
|
prefixPattern: RegExp;
|
|
phonePattern?: RegExp;
|
|
}): boolean {
|
|
const trimmed = params.raw.trim();
|
|
if (!trimmed) {
|
|
return false;
|
|
}
|
|
if (params.prefixPattern.test(trimmed)) {
|
|
return true;
|
|
}
|
|
if (trimmed.includes("@")) {
|
|
return true;
|
|
}
|
|
return (params.phonePattern ?? /^\+?\d{3,}$/).test(trimmed);
|
|
}
|
|
|
|
function normalizeIMessageHandle(raw: string): string {
|
|
const trimmed = raw.trim();
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
const lowered = normalizeLowercaseStringOrEmpty(trimmed);
|
|
if (lowered.startsWith("imessage:")) {
|
|
return normalizeIMessageHandle(trimmed.slice("imessage:".length));
|
|
}
|
|
if (lowered.startsWith("sms:")) {
|
|
return normalizeIMessageHandle(trimmed.slice("sms:".length));
|
|
}
|
|
if (lowered.startsWith("auto:")) {
|
|
return normalizeIMessageHandle(trimmed.slice("auto:".length));
|
|
}
|
|
if (CHAT_TARGET_PREFIX_RE.test(trimmed)) {
|
|
const prefix = trimmed.match(CHAT_TARGET_PREFIX_RE)?.[0];
|
|
if (!prefix) {
|
|
return "";
|
|
}
|
|
const value = trimmed.slice(prefix.length).trim();
|
|
return `${normalizeLowercaseStringOrEmpty(prefix)}${value}`;
|
|
}
|
|
if (trimmed.includes("@")) {
|
|
return normalizeLowercaseStringOrEmpty(trimmed);
|
|
}
|
|
const bareChatIdentifier = normalizeBareIMessageChatIdentifier(trimmed);
|
|
if (bareChatIdentifier) {
|
|
return `chat_identifier:${bareChatIdentifier}`;
|
|
}
|
|
const normalized = normalizeE164(trimmed);
|
|
if (normalized) {
|
|
return normalized;
|
|
}
|
|
return trimmed.replace(/\s+/g, "");
|
|
}
|
|
|
|
export function normalizeIMessageMessagingTarget(raw: string): string | undefined {
|
|
const trimmed = normalizeOptionalString(raw);
|
|
if (!trimmed) {
|
|
return undefined;
|
|
}
|
|
|
|
const lower = normalizeLowercaseStringOrEmpty(trimmed);
|
|
for (const prefix of SERVICE_PREFIXES) {
|
|
if (lower.startsWith(prefix)) {
|
|
const remainder = trimmed.slice(prefix.length).trim();
|
|
const normalizedHandle = normalizeIMessageHandle(remainder);
|
|
if (!normalizedHandle) {
|
|
return undefined;
|
|
}
|
|
if (CHAT_TARGET_PREFIX_RE.test(normalizedHandle)) {
|
|
return normalizedHandle;
|
|
}
|
|
return `${prefix}${normalizedHandle}`;
|
|
}
|
|
}
|
|
|
|
const normalized = normalizeIMessageHandle(trimmed);
|
|
return normalized || undefined;
|
|
}
|
|
|
|
export function looksLikeIMessageTargetId(raw: string): boolean {
|
|
const trimmed = normalizeOptionalString(raw);
|
|
if (!trimmed) {
|
|
return false;
|
|
}
|
|
if (CHAT_TARGET_PREFIX_RE.test(trimmed)) {
|
|
return true;
|
|
}
|
|
if (normalizeBareIMessageChatIdentifier(trimmed)) {
|
|
return true;
|
|
}
|
|
return looksLikeHandleOrPhoneTarget({
|
|
raw: trimmed,
|
|
prefixPattern: /^(imessage:|sms:|auto:)/i,
|
|
});
|
|
}
|