cdr: store Cdr events, aggregate calls, fixtures from real calls
services/cdr.ts stores every AMI Cdr event in cdr and, 5 s after a call's last event, builds its calls row (direction, status, from, to, DID, answered by, talk time) and call_extensions (caller, rang, answered) per 04 §5.4; linkedids without a calls row are caught up on AMI connect. Aggregation is idempotent. test/asterisk/cdr-record.ts records 8 scenarios with our config, the fake voip.ms (now shared in provider.ts) and baresip phones; each has a fixture and a test with the expected calls row. Queue abandon and park come with M8. Found while recording: Asterisk 24's Cdr event has no LinkedID unless cdr_manager maps it, its times carry no zone (now usegmtime), and the mappings read CDR variables, so the dialplan sets CDR(SP_DID) and CDR(SP_DIRECTION) next to the channel variables. The Asterisk image needs a rebuild for the cdr_manager.conf change. Task: M7-T1
This commit is contained in:
1 parent
8a763ff130
commit
71440ac85d
31 files changed
+1332
-48
No files matched your search
@@ -16,6 +16,8 @@ import { AmiClient } from '../../src/ami/client.ts';
|
||||
import { GENERATED_FILES, render } from '../../src/generator/index.ts';
|
||||
import type { Snapshot } from '../../src/generator/load.ts';
|
||||
|
||||
import { providerDialplan, providerPjsip } from './provider.ts';
|
||||
|
||||
const IMAGE = process.env.ASTERISK_IMAGE ?? 'selfphoneng-asterisk';
|
||||
const RECORD = process.argv.includes('--record')
|
||||
? process.argv[process.argv.indexOf('--record') + 1]
|
||||
@@ -152,51 +154,6 @@ function phoneFiles(dir: string) {
|
||||
);
|
||||
}
|
||||
|
||||
/** The fake voip.ms: one sub-account that may register. */
|
||||
const PROVIDER_PJSIP = `[transport-udp]
|
||||
type = transport
|
||||
protocol = udp
|
||||
bind = 0.0.0.0:5060
|
||||
|
||||
[${SUB.user}]
|
||||
type = endpoint
|
||||
context = provider-in
|
||||
disallow = all
|
||||
allow = ulaw
|
||||
auth = ${SUB.user}-auth
|
||||
aors = ${SUB.user}
|
||||
|
||||
[${SUB.user}-auth]
|
||||
type = auth
|
||||
auth_type = userpass
|
||||
username = ${SUB.user}
|
||||
password = ${SUB.pass}
|
||||
|
||||
[${SUB.user}]
|
||||
type = aor
|
||||
max_contacts = 1
|
||||
remove_existing = yes
|
||||
`;
|
||||
|
||||
/**
|
||||
* provider-out: a mobile phone calls one of our DIDs. Like voip.ms, the Request-URI user is the
|
||||
* 10-digit DID (06 §3), sent to the sub-account's registered contact. Echo() returns the audio.
|
||||
*/
|
||||
const PROVIDER_DIALPLAN = `[provider-out]
|
||||
exten => _X.,1,Set(CALLERID(all)="Mobile" <${MOBILE}>)
|
||||
same => n,Dial(PJSIP/\${EXTEN}@${SUB.user},30)
|
||||
|
||||
[provider-echo]
|
||||
exten => s,1,Answer()
|
||||
same => n,Echo()
|
||||
|
||||
; Our outbound calls arrive here: remember what the provider saw, then echo the audio.
|
||||
[provider-in]
|
||||
exten => _X.,1,Set(GLOBAL(LAST_IN)=to=\${EXTEN} from=\${CALLERID(num)} rpid=\${PJSIP_HEADER(read,Remote-Party-ID)})
|
||||
same => n,Answer()
|
||||
same => n,Echo()
|
||||
`;
|
||||
|
||||
async function waitFor(what: string, fn: () => Promise<boolean>, seconds = 60) {
|
||||
for (let i = 0; i < seconds; i++) {
|
||||
if (await fn()) return;
|
||||
@@ -217,9 +174,9 @@ async function main() {
|
||||
writeFileSync(
|
||||
join(provConf, f),
|
||||
f === 'pjsip_selfphoneng.conf'
|
||||
? PROVIDER_PJSIP
|
||||
? providerPjsip(SUB)
|
||||
: f === 'extensions_selfphoneng.conf'
|
||||
? PROVIDER_DIALPLAN
|
||||
? providerDialplan(MOBILE, SUB)
|
||||
: '',
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user