// src/services/voiceDiag/checks/hoteling.js // // Detects whether the store user's line is participating in the // Hoteling feature as a guest. Hoteling lets a "guest" line // temporarily associate with a shared desk phone — if it's turned on // for a store user that shouldn't have it, calls can end up at // whatever guest device most recently checked in, which usually // presents as "the phone at the store isn't the one that rings when // we call". // // Endpoint: GET/PUT /v1/people/{personId}/features/hoteling // Scope: spark-admin:people_read (GET) + spark-admin:people_write (PUT). // Response shape: { enabled: boolean } // // Standard: enabled=false. Store lines shouldn't be roaming to // shared endpoints. Remediation turns it off. import { logger } from '../../../utils/logger.js'; import { describeRequester } from '../../../utils/requester.js'; const ENDPOINT = (personId) => `people/${personId}/features/hoteling`; export const HOTELING_STANDARDS = Object.freeze({ enabled: false, }); export const hotelingCheck = { id: 'hoteling', label: 'Hoteling', requires: ['personId'], scope: 'spark-admin:people_read', standards: HOTELING_STANDARDS, async run(ctx) { const data = await ctx.webex.request('GET', ENDPOINT(ctx.personId)); const enabled = !!data?.enabled; if (enabled === HOTELING_STANDARDS.enabled) { return { status: 'ok', message: 'Hoteling is disabled (normal for a store line).', details: { enabled }, remediation: null, }; } return { status: 'warn', message: 'Hoteling is ENABLED — this line may be roaming to another desk phone.', details: { enabled }, remediation: { action: 'disable_hoteling', title: 'Disable Hoteling', summary: `Turn hoteling off for ${ctx.personLabel} so this line stops roaming.`, payload: { personId: ctx.personId, personLabel: ctx.personLabel, storeNum: ctx.storeNum, before: { enabled }, }, }, }; }, remediations: { async disable_hoteling(bot, data, requester) { const { personId, personLabel, storeNum, before } = data; logger( 'voicediag:audit', `CONFIRMED disable_hoteling for ${personLabel} (person=${personId}, store=${storeNum}) ` + `by ${describeRequester(requester)} — was enabled=${before?.enabled ?? 'unknown'}`, ); try { const { default: webex } = await import('../../../integrations/webex/WebexClient.js'); await webex.request('PUT', ENDPOINT(personId), { ...HOTELING_STANDARDS }); } catch (err) { logger( 'voicediag:audit', `FAILED disable_hoteling for ${personLabel}: ${err.message}`, 'error', ); await bot.say( 'markdown', `❌ Failed to disable hoteling for **${personLabel}**: ${err.message}`, ); return; } await bot.say( 'markdown', `✅ Hoteling disabled for **${personLabel}** (store ${storeNum}). ` + `Re-run \`/voicediag ${storeNum}\` to verify.`, ); logger( 'voicediag:audit', `COMPLETED disable_hoteling for ${personLabel} (store ${storeNum})`, ); }, }, };