// src/services/voiceDiag/checks/voicemail.js // // Detects voicemail configuration for the store user. Voicemail has // enough sub-facets that we surface the current settings without // offering a one-click remediation — the "right" answer for a store // is site-specific (some sites disable VM entirely and forward busy // to the AA, others rely on it as the noAnswer target). We flag two // classes of finding: // // - **error**: enabled=true AND no PIN set — the user cannot pick // up messages, and every caller who reaches VM will be dumped // into the "please set your PIN" prompt. // - **warn**: enabled=true AND a forward-to-email target is // configured that doesn't look like an @ae.com address — mail // forwarding of voicemail off-org is worth double-checking. // - **ok**: everything else. // // Endpoint: GET /v1/people/{personId}/features/voicemail // Scope: spark-admin:people_read // Response shape (relevant fields): // { // enabled: true, // sendAllCalls: { enabled: false }, // sendBusyCalls: { enabled: false, greeting: 'DEFAULT' }, // sendUnansweredCalls: { enabled: true, ... }, // notifications: { enabled: false, destination: '' }, // transferToNumber: { enabled: false, destination: '' }, // emailCopyOfMessage: { enabled: false, emailId: '' }, // messageStorage: { // mwiEnabled: true, // storageType: 'INTERNAL' | 'EXTERNAL', // externalEmail: '' // }, // faxMessage: { ... } // } // // Webex doesn't currently expose PIN-set status via the public API, // so "PIN set" is inferred pragmatically: we check whether the person // has ever accessed their voicemail (via passcode lastChanged if the // endpoint returns it) or, as a fallback, we simply note that PIN // state is unknown and treat it as informational only. See the note // inline below. const ENDPOINT = (personId) => `people/${personId}/features/voicemail`; const AE_EMAIL_RE = /@ae\.com$/i; export const voicemailCheck = { id: 'voicemail', label: 'Voicemail', requires: ['personId'], scope: 'spark-admin:people_read', async run(ctx) { const data = await ctx.webex.request('GET', ENDPOINT(ctx.personId)); const enabled = !!data?.enabled; const storage = data?.messageStorage || {}; const mwiEnabled = !!storage?.mwiEnabled; const emailCopy = data?.emailCopyOfMessage || {}; const transferTo = data?.transferToNumber || {}; const externalEmail = storage?.externalEmail || ''; const forwardEmailTarget = emailCopy?.enabled ? (emailCopy?.emailId || '') : ''; const details = { enabled, mwiEnabled, storageType: storage?.storageType || null, externalEmail, emailCopyEnabled: !!emailCopy?.enabled, emailCopyTarget: forwardEmailTarget, transferToEnabled: !!transferTo?.enabled, transferToDestination: transferTo?.destination || null, sendAllCallsEnabled: !!data?.sendAllCalls?.enabled, sendBusyCallsEnabled: !!data?.sendBusyCalls?.enabled, sendUnansweredCallsEnabled: !!data?.sendUnansweredCalls?.enabled, }; if (!enabled) { return { status: 'ok', message: 'Voicemail is disabled — callers will not be able to leave messages.', details, remediation: null, }; } // sendAllCalls silently swallowing every inbound call is almost // always a mistake — surface it loudly. if (details.sendAllCallsEnabled) { return { status: 'error', message: 'Voicemail is enabled AND "send all calls to voicemail" is on — the phone will never ring.', details, remediation: null, }; } if (!mwiEnabled) { return { status: 'warn', message: 'Voicemail is enabled but MWI (message-waiting indicator) is off — new messages won\'t light up the phone.', details, remediation: null, }; } if (forwardEmailTarget && !AE_EMAIL_RE.test(forwardEmailTarget)) { return { status: 'warn', message: `Voicemail-to-email is forwarding to an off-org address: ${forwardEmailTarget}. Verify this is intentional.`, details, remediation: null, }; } if (details.transferToEnabled && !details.transferToDestination) { return { status: 'warn', message: 'Voicemail transfer-to-number is enabled but no destination is set — will fall through to the default greeting.', details, remediation: null, }; } return { status: 'ok', message: 'Voicemail is enabled with MWI on and no unusual forwarding.', details, remediation: null, }; }, // No remediations — voicemail policy is too site-specific for a // one-size-fits-all button. The renderer surfaces the details so // the operator can act via Control Hub if they want. };