collabSupport/services/voicePin/resolveVmPinTarget.js
jmcqueen 2ec2b7a486 Add /resetvmpin and /resetvppin with confirmation cards for Webex PIN resets.
Chat-only commands reset voicemail mailbox and voice portal passcodes via telephony_config_write, with extension lookup and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 16:34:09 -04:00

126 lines
3.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// services/voicePin/resolveVmPinTarget.js
// Parse and resolve /resetvmpin targets: email, store (24 digits), or extension (5 digits).
import { logger } from '../../utils/logger.js';
import { getPersonDetails } from '../phoneService.js';
import {
resolvePersonLocation,
resolveRecordsByExtension,
} from '../callTest/locationResolver.js';
export { parseVmPinTarget } from './parseVmPinTarget.js';
const LOG_SCOPE = 'resetvmpin:resolve';
/**
* @typedef {'email'|'store'|'extension'} VmPinMatchSource
*/
async function getWebex() {
const mod = await import('../../integrations/webex/WebexClient.js');
return mod.default;
}
async function fetchVoicemailSnapshot(personId) {
if (!personId) return { enabled: null };
try {
const webex = await getWebex();
const vm = await webex.request('GET', `people/${personId}/features/voicemail`);
return { enabled: vm?.enabled === true, raw: vm };
} catch (err) {
logger(LOG_SCOPE, `Voicemail feature fetch failed for ${personId}: ${err.message}`, 'warn');
return { enabled: null };
}
}
/**
* Resolve a parsed VM PIN target to a Webex person + context for the confirmation card.
*
* @param {ReturnType<typeof parseVmPinTarget>} parsed
* @returns {Promise<object>}
*/
export async function resolveVmPinTarget(parsed) {
if (!parsed) {
throw new Error('Invalid target. Use email, 24 digit store number, or 5-digit extension.');
}
const webex = await getWebex();
let personId = null;
let email = null;
let matchSource;
let storeNum = null;
let extension = null;
let numberRecord = null;
if (parsed.kind === 'email') {
matchSource = 'email';
email = parsed.email;
const person = await webex.findPersonByEmail(email);
if (!person) {
throw new Error(`No Webex person found for ${email}`);
}
personId = person.id;
} else if (parsed.kind === 'store') {
matchSource = 'store';
storeNum = parsed.storeNum;
const padded = storeNum.padStart(5, '0');
email = `ae${padded}@ae.com`;
const person = await webex.findPersonByEmail(email);
if (!person) {
throw new Error(`No Webex person found for store ${storeNum} (${email})`);
}
personId = person.id;
} else {
matchSource = 'extension';
extension = parsed.extension;
const records = await resolveRecordsByExtension(extension);
if (records.length === 0) {
throw new Error(`No telephony number found for extension ${extension}`);
}
const ownerIds = [...new Set(records.map((r) => r.owner?.id).filter(Boolean))];
if (ownerIds.length !== 1) {
throw new Error(
`Extension ${extension} matches ${records.length} number(s) across ${ownerIds.length} owner(s) — cannot resolve a unique user`,
);
}
personId = ownerIds[0];
numberRecord = records[0];
}
const [person, locationInfo, voicemail] = await Promise.all([
getPersonDetails(personId),
resolvePersonLocation(personId),
fetchVoicemailSnapshot(personId),
]);
if (!person) {
throw new Error(`Could not load Webex person ${personId}`);
}
if (!email) {
email = (person.emails || []).find((e) => typeof e === 'string' && e.includes('@'))
|| person.emails?.[0]?.value
|| person.emails?.[0]
|| null;
}
const ownedNumbers = locationInfo?.ownedNumbers || [];
const primaryNumber = numberRecord
|| ownedNumbers.find((n) => n.extension === extension)
|| ownedNumbers[0]
|| null;
return {
personId,
person,
email: email || person.displayName || personId,
displayName: person.displayName || email || personId,
matchSource,
storeNum,
extension: extension || primaryNumber?.extension || null,
locationId: locationInfo?.locationId || primaryNumber?.locationId || null,
locationName: locationInfo?.locationName || primaryNumber?.locationName || null,
mainNumber: primaryNumber?.phoneNumber || null,
voicemailEnabled: voicemail.enabled,
};
}