Replace /phonestatus follow-ups with /voicestatus, /wanstatus, /phonediag, and /dectdiag; extend /voicediag with relay probes and section-based MPP diagnostics. Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
4 KiB
JavaScript
103 lines
4 KiB
JavaScript
// src/commands/voiceStatus.js
|
||
//
|
||
// Chat + HTTP entry point for /voicestatus. The heavy rendering lives in
|
||
// services/renderers/phoneStatusRenderer.js so the Jira poller can emit
|
||
// the same markdown (see services/jiraPollerService.js). This handler
|
||
// stays thin: parse args, call the collector, hand data to the renderer,
|
||
// respond.
|
||
|
||
import { randomUUID } from 'node:crypto';
|
||
import { collectPhoneStatus } from '../services/phoneService.js';
|
||
import { renderPhoneStatusMarkdown } from '../services/renderers/phoneStatusRenderer.js';
|
||
import { buildIgmpFixCard } from './igmpFix.js';
|
||
import { pendingIgmpFixes } from '../utils/pendingIgmpFixes.js';
|
||
import { extractRequester } from '../utils/requester.js';
|
||
import { logger } from '../utils/logger.js';
|
||
|
||
export async function handleVoiceStatus(bot, trigger) {
|
||
logger('voice:status', 'Handler entered', 'debug');
|
||
|
||
const query = trigger.query || {};
|
||
const args = trigger.args || [];
|
||
|
||
let storeNum = args[0]?.trim() || query.storeNum || query.store || query.s;
|
||
|
||
const isDetailed = argIncludes(args, 'detailed')
|
||
|| (query.mode === 'detailed')
|
||
|| (query.detailed === 'true' || query.detailed === true);
|
||
|
||
if (!storeNum || !/^\d{2,4}$/.test(storeNum)) {
|
||
const errorMsg = 'Please provide a 2–4 digit store number.\n' +
|
||
'Example: `/voicestatus 782` (or `detailed` / `?detailed=true` for more; includes Meraki links) or `https://.../voicestatus?storeNum=782`';
|
||
await bot.say('markdown', errorMsg);
|
||
return;
|
||
}
|
||
|
||
logger('voice:status', `Collecting voice status for store ${storeNum}`, 'debug');
|
||
|
||
try {
|
||
const data = await collectPhoneStatus(storeNum);
|
||
if (!data) throw new Error('collectPhoneStatus returned undefined');
|
||
|
||
if (query.format === 'json' || (args[1] && args[1].toLowerCase() === 'json')) {
|
||
const jsonPayload = {
|
||
store: storeNum,
|
||
mainNumber: data.locationMainNumber,
|
||
timezone: (data.telephonyProfile && data.telephonyProfile.timeZone) || null,
|
||
person: data.person ? { displayName: data.person.displayName, phoneNumbers: data.person.phoneNumbers } : null,
|
||
timestamp: new Date().toISOString(),
|
||
};
|
||
await bot.say('markdown', '```json\n' + JSON.stringify(jsonPayload, null, 2) + '\n```');
|
||
return;
|
||
}
|
||
|
||
const reply = renderPhoneStatusMarkdown(data, {
|
||
storeNum,
|
||
detailed: isDetailed,
|
||
footer: true,
|
||
});
|
||
await bot.say('markdown', reply || 'No data available.');
|
||
|
||
if (data.multicast?.needsFix && data.multicast.networkId && trigger.person) {
|
||
const cardId = randomUUID();
|
||
const requester = extractRequester(trigger);
|
||
pendingIgmpFixes.set(cardId, {
|
||
networkId: data.multicast.networkId,
|
||
networkName: data.multicast.networkName,
|
||
storeNum,
|
||
requester,
|
||
summary: {
|
||
defaultSnoopOn: data.multicast.defaultSnoopOn,
|
||
defaultFloodOff: data.multicast.defaultFloodOff,
|
||
deviatingOverrides: data.multicast.deviatingOverrides || [],
|
||
},
|
||
});
|
||
const card = buildIgmpFixCard({
|
||
storeNum,
|
||
networkName: data.multicast.networkName,
|
||
summary: {
|
||
defaultSnoopOn: data.multicast.defaultSnoopOn,
|
||
defaultFloodOff: data.multicast.defaultFloodOff,
|
||
deviatingOverrides: data.multicast.deviatingOverrides || [],
|
||
},
|
||
cardId,
|
||
});
|
||
await bot.say({
|
||
markdown: `Multicast policy on store ${storeNum}'s network deviates from DECT-safe defaults. Review and confirm:`,
|
||
attachments: [{
|
||
contentType: 'application/vnd.microsoft.card.adaptive',
|
||
content: card,
|
||
}],
|
||
});
|
||
}
|
||
|
||
} catch (err) {
|
||
logger('voice:status', `Error collecting voice status for store ${storeNum}: ${err.message}`, 'error');
|
||
await bot.say('markdown', `Error collecting voice status: ${err.message}`);
|
||
}
|
||
}
|
||
|
||
function argIncludes(args, token) {
|
||
const needle = String(token).toLowerCase();
|
||
return (args || []).some((a) => String(a).toLowerCase() === needle);
|
||
}
|