voicemail: index messages, live event, email with the recording

services/voicemail.ts rescans a mailbox on Asterisk's MessageWaiting event
(and every mailbox on AMI connect) instead of watching files, upserts
voicemail_messages by msg_id, marks vanished ones deleted, sends
voicemail.new to the owner only, and emails the wav. Delete-after-email
uses AMI VoicemailRemove, and only when the audio was attached. Asterisk
24 has VoicemailMove/VoicemailRemove, so 04 §5.6 drops the renumbering
plan. New VOICEMAIL_DIR setting; e2e m4-t2 checks the email arrives
within 10 s with a playable attachment (SMTP sink shared with m3-t4).

Task: M4-T2
This commit is contained in:
iamdoubz committed 2026-10-17 22:33:52 -05:00
1 parent 0d29131d18
commit 8d752ba705
16 files changed
+642 -58

No files matched your search

+14
View File
@@ -11,6 +11,7 @@ import { ensureInitialAdmin } from './services/bootstrap.ts';
import { LiveState } from './live/state.ts';
import { smtpMailer } from './services/mail.ts';
import { watchEmergencies } from './services/emergency.ts';
import { watchVoicemail } from './services/voicemail.ts';
let config;
try {
@@ -50,6 +51,18 @@ app.decorate('ami', ami);
const sendMail = smtpMailer(db);
app.decorate('sendMail', sendMail);
watchEmergencies({ db, ami, live, send: sendMail, log: app.log }, ami);
const voicemail = watchVoicemail(
{
db,
spoolDir: resolve(config.VOICEMAIL_DIR),
ami,
live,
send: sendMail,
publicHost: config.PUBLIC_HOSTNAME,
log: app.log,
},
ami,
);
app.decorate('live', live);
await runMigrations(db);
@@ -72,6 +85,7 @@ ami.on('connected', () => {
app.log.info('asterisk restarted: restart no longer required');
await generator.apply({ trigger: 'startup', force: true });
await live.loadFromAsterisk();
await voicemail.scanAll();
})().catch((err) => app.log.error({ err }, 'startup sync failed'));
});
ami.start();