Hide WhatsApp LID ids from session-header peer labels (4.9.1-ops.21).

Resolve nickname/phone via contact LID mapping and show only group title, pushName, and phone.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-05 19:14:38 +08:00
parent 37742043a9
commit a92080e490
5 changed files with 331 additions and 215 deletions

View file

@ -25,10 +25,56 @@ export function parseConversationKey(key) {
return conversationId ? { kind: 'group', conversationId, senderId: null } : null;
}
function contactPushName(grant, phone) {
if (!phone || !grant || !Array.isArray(grant.contacts)) return '';
const hit = grant.contacts.find((contact) => contact?.phone === phone);
return typeof hit?.pushName === 'string' ? hit.pushName.trim() : '';
/**
* Normalize a WhatsApp LID identity for contact matching.
* Accepts `digits@lid`, `digits:device@lid`, or a bare lid token already stored on contacts.
* @param {unknown} value
* @returns {string|null}
*/
export function lidFromWhatsappIdentity(value) {
if (typeof value !== 'string') return null;
const raw = value.trim();
if (!raw) return null;
const at = raw.indexOf('@');
if (at > 0) {
const server = raw.slice(at + 1).toLowerCase();
if (server !== 'lid' && server !== 'hosted.lid') return null;
const user = raw.slice(0, at).split(':')[0].trim();
return user ? `${user}@lid` : null;
}
// Contacts may already store the canonical `digits@lid` form only.
return null;
}
function lidsMatch(stored, needle) {
if (typeof stored !== 'string' || !needle) return false;
const left = lidFromWhatsappIdentity(stored) ?? stored.trim();
return left === needle || left === needle.replace(/@lid$/, '');
}
/**
* Resolve contact phone/pushName from grant by PN or LID.
* @param {object|null} grant
* @param {string|null} identity
* @returns {{ phone: string|null, pushName: string|null }}
*/
export function resolveContactIdentity(grant, identity) {
const phone = phoneFromWhatsappJid(identity) ?? normalizeAccessPhone(identity);
const lid = lidFromWhatsappIdentity(identity);
const contacts = Array.isArray(grant?.contacts) ? grant.contacts : [];
const hit = contacts.find((contact) => {
if (phone && contact?.phone === phone) return true;
if (lid && Array.isArray(contact?.lids) && contact.lids.some((entry) => lidsMatch(entry, lid))) {
return true;
}
return false;
}) ?? null;
const resolvedPhone = phone
?? (typeof hit?.phone === 'string' && hit.phone ? hit.phone : null);
const pushName = typeof hit?.pushName === 'string' && hit.pushName.trim()
? hit.pushName.trim()
: null;
return { phone: resolvedPhone, pushName };
}
function groupTitle(grant, groupJid) {
@ -36,12 +82,21 @@ function groupTitle(grant, groupJid) {
return typeof title === 'string' && title.trim() ? title.trim() : '';
}
function phoneFromIdentity(value) {
return phoneFromWhatsappJid(value) ?? normalizeAccessPhone(value);
/**
* Join human-facing label parts. Never includes raw JIDs / LIDs.
* @param {...(string|null|undefined)} parts
* @returns {string}
*/
export function formatChannelPeerLabel(...parts) {
return parts
.map((part) => (typeof part === 'string' ? part.trim() : ''))
.filter(Boolean)
.join(' · ');
}
/**
* Build a public channel-peer summary for session-header display.
* Label shows only group title / nickname / phone — never opaque WhatsApp ids.
* @param {{
* channel: string,
* botId: string,
@ -61,12 +116,9 @@ export function resolveChannelPeerFromBinding(input) {
const grant = input.grant && typeof input.grant === 'object' ? input.grant : null;
if (parsed.kind === 'direct') {
const phone = phoneFromIdentity(parsed.conversationId);
const pushName = contactPushName(grant, phone);
const who = pushName || phone || parsed.conversationId;
const label = phone && pushName && pushName !== phone
? `${pushName} · ${phone}`
: who;
const { phone, pushName } = resolveContactIdentity(grant, parsed.conversationId);
const label = formatChannelPeerLabel(pushName, phone);
if (!label) return null;
return Object.freeze({
channel,
botId,
@ -74,8 +126,8 @@ export function resolveChannelPeerFromBinding(input) {
conversationKey,
conversationId: parsed.conversationId,
senderId: null,
phone: phone || null,
pushName: pushName || null,
phone,
pushName,
groupTitle: null,
label,
});
@ -93,13 +145,12 @@ export function resolveChannelPeerFromBinding(input) {
phone: null,
pushName: null,
groupTitle: title,
label: `群 · ${title}`,
label: title,
});
}
const phone = phoneFromIdentity(parsed.senderId);
const pushName = contactPushName(grant, phone);
const who = pushName || phone || parsed.senderId;
const { phone, pushName } = resolveContactIdentity(grant, parsed.senderId);
const label = formatChannelPeerLabel(title, pushName, phone) || title;
return Object.freeze({
channel,
botId,
@ -107,9 +158,9 @@ export function resolveChannelPeerFromBinding(input) {
conversationKey,
conversationId: parsed.conversationId,
senderId: parsed.senderId,
phone: phone || null,
pushName: pushName || null,
phone,
pushName,
groupTitle: title,
label: `${title} · ${who}`,
label,
});
}