emergency: 911 dialplan, notifications, test button, settings, warnings
Dialplan: sp-emergency-dial (911, 9911) first in sp-from-ext; sp-emergency has no class, channel-limit or DND check, dials every enabled trunk in name order with the E911 caller ID, and raises UserEvent SpEmergency. Static sp-emergency-alert (_X! so "0" matches) for the ring-out. api: SpEmergency -> emergency_events row, live emergency.call (admins and notify users only), email via nodemailer (SMTP from settings) and an Originate to every notify extension, each outcome recorded. Test button runs the same with TEST in front, records and dials nothing. GET/PATCH /api/settings (smtp password write-only, references checked, read-only system keys), SMTP test, /api/system/warnings. Fix: clearing a nullable setting stored SQL NULL (now JSON null). UI: Settings page (Email, Emergency with the 06 §6 notice), user Emergency tab, dashboard warnings banner, red 911 banner. Verified: generator/emergency.test.ts; trunk-test.ts dials 911 from a caller with no class to the fake provider (E911 caller ID in RPID, SpEmergency on AMI, alert plays on baresip 101); scripts/e2e/m3-t4.mjs (SMTP sink receives the test email, 101 rings, banner shows). nodemailer pinned to 10.0.11 (newest release over two weeks old). Task: M3-T4
This commit is contained in:
1 parent
4496f6b89a
commit
409724bc55
45 files changed
+1781
-139
No files matched your search
@@ -418,6 +418,56 @@ async function main() {
|
||||
log('class check: ss-noservice');
|
||||
await ami.command('channel request hangup all');
|
||||
|
||||
// M3-T4: 911 from a caller with no class at all still goes out, with the E911 caller ID, and
|
||||
// the dialplan tells the api. (The fake provider answers it; nothing real is dialed.)
|
||||
const userEvents: Record<string, string | string[]>[] = [];
|
||||
ami.on('event:UserEvent', (m) => userEvents.push(m));
|
||||
await ami.command('channel originate Local/911@sp-from-ext application Wait 5');
|
||||
await waitFor(
|
||||
'the provider to receive 911',
|
||||
async () =>
|
||||
/LAST_IN=to=911 /.test(
|
||||
docker('exec', PROVIDER, 'asterisk', '-rx', 'dialplan show globals').out,
|
||||
),
|
||||
15,
|
||||
);
|
||||
const e911 = /LAST_IN=(.*)/.exec(
|
||||
docker('exec', PROVIDER, 'asterisk', '-rx', 'dialplan show globals').out,
|
||||
)?.[1];
|
||||
log(`provider received: ${e911}`);
|
||||
if (!new RegExp(`rpid=.*sip:${DID}@`).test(e911 ?? ''))
|
||||
problems.push(`911 without the E911 caller ID ${DID}: ${e911}`);
|
||||
// AMI delivers the UserEvent asynchronously: it can arrive after the trunk call has started.
|
||||
await waitFor(
|
||||
'SpEmergency on AMI',
|
||||
async () => userEvents.some((m) => m.UserEvent === 'SpEmergency'),
|
||||
10,
|
||||
).catch(() => {});
|
||||
const ev = userEvents.find((m) => m.UserEvent === 'SpEmergency');
|
||||
if (!ev || ev.CallerId !== DID || ev.Dialed !== '911')
|
||||
problems.push(`no SpEmergency UserEvent with CallerId ${DID}: ${JSON.stringify(userEvents)}`);
|
||||
await ami.command('channel request hangup all');
|
||||
// The alert ring-out the api originates: 101 answers and hears beeps and the extension.
|
||||
await ami.action({
|
||||
Action: 'Originate',
|
||||
Channel: 'PJSIP/101',
|
||||
Context: 'sp-emergency-alert',
|
||||
Exten: '102',
|
||||
Priority: '1',
|
||||
CallerID: '"EMERGENCY 911" <102>',
|
||||
Async: 'true',
|
||||
});
|
||||
await waitFor(
|
||||
'the alert to play on 101',
|
||||
async () =>
|
||||
/sp-emergency-alert!102!.*!Up!(Playback|SayDigits|Wait)/.test(
|
||||
await ami.command('core show channels concise'),
|
||||
),
|
||||
15,
|
||||
);
|
||||
log('911: out with the E911 caller ID, SpEmergency raised, alert plays on 101');
|
||||
await ami.command('channel request hangup all');
|
||||
|
||||
// eslint-disable-next-line no-control-regex -- strips Asterisk's ANSI colors
|
||||
const plainLog = docker('logs', PBX).out.replace(/\x1b\[[0-9;]*m/g, '');
|
||||
const errors = plainLog.split('\n').filter((l) => /ERROR\[/.test(l));
|
||||
@@ -441,7 +491,7 @@ async function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(
|
||||
'ok trunks (registration, rejection, qualify, inbound DID → 101, outbound with DID caller ID, class check)',
|
||||
'ok trunks (registration, rejection, qualify, inbound DID → 101, outbound with DID caller ID, class check, 911)',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user