// commands/resetVpPin.js // // /resetvppin — reset a location's voice portal PIN with confirmation card. // // Store number only (2–4 digits). Used to manage auto-attendant greetings. import { logger } from '../utils/logger.js'; import { pendingVpPinResets } from '../utils/pendingVpPinResets.js'; import { extractRequester, describeRequester } from '../utils/requester.js'; import { parseVpPinStore, resolveVpPinStore, } from '../services/voicePin/resolveVpPinStore.js'; import { resetVoicePortalPin } from '../services/voicePin/resetVoicePortalPin.js'; function buildConfirmationCard(cardId, resolved) { const portal = resolved.voicePortal || {}; const dialIn = portal.phoneNumber ? `${portal.phoneNumber}${portal.extension ? ` ext ${portal.extension}` : ''}` : '—'; return { type: 'AdaptiveCard', version: '1.3', body: [ { type: 'TextBlock', text: '🔐 RESET VOICE PORTAL PIN', weight: 'Bolder', size: 'Large', color: 'Attention', }, { type: 'TextBlock', text: 'Confirming will generate a new voice portal passcode for this location. ' + 'This PIN is used to dial in and manage **auto-attendant greetings**.', wrap: true, spacing: 'Small', }, { type: 'FactSet', spacing: 'Medium', facts: [ { title: 'Store', value: resolved.storeNum }, { title: 'Location', value: resolved.locationName || '—' }, { title: 'Location ID', value: resolved.locationId }, { title: 'Voice portal', value: portal.name || '—' }, { title: 'Dial-in', value: dialIn }, { title: 'Main number', value: resolved.mainNumber || '—' }, ], }, ], actions: [ { type: 'Action.Submit', title: '✅ Confirm Reset', data: { action: 'confirm_reset_vppin', cardId }, }, { type: 'Action.Submit', title: '❌ Cancel', data: { action: 'cancel_reset_vppin', cardId }, }, ], }; } export async function applyResetVpPinConfirmation(bot, data, _roomId, requester) { logger( 'resetvppin:audit', `CONFIRMED voice portal PIN reset for store ${data.storeNum} (location=${data.locationId}) ` + `by ${describeRequester(requester)}`, ); try { const newPin = await resetVoicePortalPin(data.locationId, { passcodeRules: data.passcodeRules, voicePortal: data.voicePortal, }); const portal = data.voicePortal || {}; const dialIn = portal.phoneNumber ? `${portal.phoneNumber}${portal.extension ? ` ext ${portal.extension}` : ''}` : null; await bot.say( 'markdown', `✅ Voice portal PIN reset for **Store ${data.storeNum}** ` + `(\`${data.locationName || data.locationId}\`).\n\n` + `**New voice portal PIN:** \`${newPin}\`\n` + (dialIn ? `\nDial in at **${dialIn}** to manage auto-attendant greetings.\n` : '\n') + '_Share this PIN only in trusted spaces._', ); logger( 'resetvppin:audit', `COMPLETED voice portal PIN reset for store ${data.storeNum} (location=${data.locationId}) ` + `by ${describeRequester(requester)}`, ); } catch (err) { const apiMsg = err?.response?.data?.message || err.message; logger( 'resetvppin:audit', `FAILED voice portal PIN reset for store ${data.storeNum}: ${apiMsg}`, 'error', ); await bot.say( 'markdown', `❌ Voice portal PIN reset failed for **Store ${data.storeNum}**: ${apiMsg}`, ); } } export async function cancelResetVpPinCard(bot, data, _roomId, requester) { await bot.say( 'markdown', `❌ Voice portal PIN reset cancelled for **Store ${data.storeNum}**. No changes were made.`, ); logger( 'resetvppin:audit', `CANCELLED voice portal PIN reset for store ${data.storeNum} by ${describeRequester(requester)}`, ); } export async function handleResetVpPin(bot, trigger) { const args = trigger.args || []; const query = trigger.query || {}; const raw = (args[0] || query.store || query.storeNum || query.s || '').trim(); if (!raw) { await bot.say( 'markdown', '**Usage:** `/resetvppin `\n\n' + 'Provide a 2–4 digit store number (e.g. `782`).', ); return; } const parsed = parseVpPinStore(raw); if (!parsed) { await bot.say( 'markdown', `❌ Invalid store \`${raw}\`. Use a 2–4 digit store number.`, ); return; } const requester = extractRequester(trigger); logger( 'resetvppin:audit', `REQUESTED voice portal PIN reset card for store ${parsed.storeNum} by ${describeRequester(requester)}`, ); try { const resolved = await resolveVpPinStore(parsed); if (!trigger.person) { await bot.say( 'markdown', `Resolved **Store ${resolved.storeNum}** → \`${resolved.locationName || resolved.locationId}\`. ` + 'Confirmation cards are only available in Webex chat.', ); return; } const cardId = `vppin-${Date.now()}`; pendingVpPinResets.set(cardId, { ...resolved, roomId: trigger.roomId || trigger.message?.roomId, requester, }); const adaptiveCard = buildConfirmationCard(cardId, resolved); await bot.say({ markdown: `Review voice portal PIN reset for **Store ${resolved.storeNum}** ` + `(\`${resolved.locationName || resolved.locationId}\`):`, attachments: [{ contentType: 'application/vnd.microsoft.card.adaptive', content: adaptiveCard, }], }); } catch (err) { logger('resetvppin', `Resolve failed for store ${raw}: ${err.message}`, 'warn'); await bot.say('markdown', `❌ ${err.message}`); } }