wbxcallprov/src/commands/provisionDect.js
jmcqueen 5dce057ce7
Some checks are pending
CI / verify (push) Waiting to run
Add /provisionDect + create DECT network in /finalizeStore
New /provisionDect slash command manages DECT basestations and handsets
for a store via a single card: multi-select checkbox list doubles as
the display, MACs render as AA:BB:CC:DD:EE:FF, Add/Remove sit
side-by-side per section, and every removal goes through an explicit
confirm card. Handsets always auto-pair (no bind-to-basestation input)
so they roam.

/finalizeStore now idempotently creates the "Store XXXX" DECT network
(DBS-210) with the per-store default access code, so new stores are
DECT-ready the moment finalize completes. Location-scoped lookup
(findDectNetworkInLocation) handles both the finalize idempotency
check and the /provisionDect fallback for freshly-created empty
networks. Non-critical: a store can still go live if the DECT step
fails, and /provisionDect keeps a recovery "create network" prompt
for legacy stores.

Pure-logic helpers (generateDectAccessCode, MAC normalize/format/
display, dectNetworkName) are unit-tested via node:test.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 07:29:21 -04:00

63 lines
2.7 KiB
JavaScript

import { logger } from '../logger.js';
import { findWebexLocation } from '../webex/locations.js';
import {
dectNetworkName,
generateDectAccessCode,
getDectProvisioningStatus,
} from '../webex/dect.js';
import { dectCreateNetworkCard, dectManagementCard } from '../cards/dectCards.js';
import { parseStoreNumber } from './helpers.js';
// /provisionDect does NOT require the remote agent — every operation is
// against public Webex Calling APIs, which the bot reaches directly.
export function register(framework) {
framework.hears(
/\/provisiondect/i,
async (bot, trigger) => {
logger.info(`${trigger.person.displayName} ran the provisionDect command.`);
const storeNumber = parseStoreNumber(trigger);
if (!storeNumber) {
bot.say('Usage: `/provisionDect <storeNumber>` — e.g. `/provisionDect 499`');
return;
}
try {
const status = await getDectProvisioningStatus(storeNumber);
if (status.network) {
bot.sendCard(
dectManagementCard(storeNumber, status),
'Please use another client',
);
return;
}
// No network yet — need the store's Webex location to create in.
const matches = await findWebexLocation(dectNetworkName(storeNumber));
if (!matches.length) {
bot.say(
'markdown',
`No Webex location found for **${dectNetworkName(storeNumber)}**. ` +
`Run \`/stageStore ${storeNumber}\` first.`,
);
return;
}
const location = matches[0];
bot.sendCard(
dectCreateNetworkCard(storeNumber, {
locationId: location.id,
locationName: location.name,
suggestedAccessCode: generateDectAccessCode(storeNumber),
}),
'Please use another client',
);
} catch (error) {
logger.error('provisionDect command failed:', error);
bot.say(
'markdown',
`Error running /provisionDect:\n\`\`\`\n${error.message}\n\`\`\``,
);
}
},
'**/provisionDect** <storeNumber> - Add or remove DECT basestations and handsets for a store. New stores get their DECT network created by /finalizeStore; this command offers a recovery-create prompt if the network is missing.',
);
}