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
37 lines
1.2 KiB
JavaScript
37 lines
1.2 KiB
JavaScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
// Just enough SMTP for nodemailer: no auth, no TLS, keeps every message (raw RFC 5322 text).
|
|
import { createServer } from 'node:net';
|
|
|
|
export async function smtpSink(port = 2525) {
|
|
const mails = [];
|
|
const server = createServer((sock) => {
|
|
let data = false;
|
|
let buf = '';
|
|
let msg = '';
|
|
sock.write('220 sink ESMTP\r\n');
|
|
sock.on('data', (d) => {
|
|
buf += d;
|
|
let i;
|
|
while ((i = buf.indexOf('\r\n')) >= 0) {
|
|
const line = buf.slice(0, i);
|
|
buf = buf.slice(i + 2);
|
|
if (data) {
|
|
if (line === '.') {
|
|
data = false;
|
|
mails.push(msg);
|
|
msg = '';
|
|
sock.write('250 ok\r\n');
|
|
} else msg += line.replace(/^\.\./, '.') + '\n';
|
|
} else if (/^EHLO/i.test(line)) sock.write('250-sink\r\n250 OK\r\n');
|
|
else if (/^DATA/i.test(line)) {
|
|
data = true;
|
|
sock.write('354 go\r\n');
|
|
} else if (/^QUIT/i.test(line)) sock.end('221 bye\r\n');
|
|
else sock.write('250 ok\r\n');
|
|
}
|
|
});
|
|
});
|
|
await new Promise((r) => server.listen(port, '127.0.0.1', r));
|
|
return { mails, server };
|
|
}
|