Show WA nicknames and auto-fill group subjects in access UI (4.9.1-ops.9).

Fetch groupMetadata.subject on @mentions for group cards; render contact pushName under phone rows so admins are not staring at bare numbers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-05 17:29:43 +08:00
parent e8e58924ca
commit a169b164a8
10 changed files with 268 additions and 182 deletions

View file

@ -194,6 +194,7 @@ export async function gateWhatsappInbound({
message,
sendText,
accountJid,
resolveGroupTitle,
}) {
const { phone, lid } = resolveInboundPhone(message);
const scene = message.kind === 'group' ? 'group' : 'direct';
@ -205,7 +206,15 @@ export async function gateWhatsappInbound({
current = await rememberWhatsappContact(workspaces, botId, current, message, { phone, lid });
}
if (scene === 'group' && addressed && message.conversationId) {
const title = message.contextSource?.()?.conversationTitle;
let title = message.contextSource?.()?.conversationTitle;
const existingTitle = current.groups?.[message.conversationId]?.title;
if (!title && !existingTitle && typeof resolveGroupTitle === 'function') {
try {
title = await resolveGroupTitle(message.conversationId);
} catch {
title = '';
}
}
const withGroup = ensureGroupBucket(current, message.conversationId, {
...(title ? { title } : {}),
});

View file

@ -238,3 +238,21 @@ export async function enrichWhatsappInboundIdentities(message, raw, {
: {}),
};
}
/**
* Resolve a WhatsApp group subject for display / access-grant group cards.
* @param {{ groupMetadata?: (jid: string) => Promise<{ subject?: string }> }|null|undefined} socket
* @param {string} groupJid
* @returns {Promise<string>}
*/
export async function resolveWhatsappGroupSubject(socket, groupJid) {
if (!socket || typeof socket.groupMetadata !== 'function') return '';
if (typeof groupJid !== 'string' || !groupJid.endsWith('@g.us')) return '';
try {
const meta = await socket.groupMetadata(groupJid);
const subject = typeof meta?.subject === 'string' ? meta.subject.trim() : '';
return subject.slice(0, 128);
} catch {
return '';
}
}

View file

@ -12,7 +12,7 @@ import { t } from '../shared/i18n.mjs';
import { ImagePromptError } from '../shared/image-prompt.mjs';
import { trackOutboundArtifactProviderPromise } from '../shared/semantic/artifact.mjs';
import { createWhatsappBridgeStatus, WhatsappHarnessBridge } from './whatsapp-bridge.mjs';
import { enrichWhatsappInboundIdentities } from './whatsapp-identity.mjs';
import { enrichWhatsappInboundIdentities, resolveWhatsappGroupSubject } from './whatsapp-identity.mjs';
import {
gateWhatsappInbound,
loadWhatsappAccessGrant,
@ -754,6 +754,7 @@ export class WhatsappRuntime {
});
if (handled) return;
grant = this.#workspaces.accessGrantFor(this.#botId) ?? grant;
const socket = context?.socket ?? this.#session?.socket;
const gated = await gateWhatsappInbound({
workspaces: this.#workspaces,
botId: this.#botId,
@ -761,6 +762,7 @@ export class WhatsappRuntime {
message,
sendText,
accountJid: this.#config.accountJid,
resolveGroupTitle: (groupJid) => resolveWhatsappGroupSubject(socket, groupJid),
});
if (!gated.allowed) {
if (gated.accessDecision) {