Initial build: Asterisk voicemail transcription + portal
- mailcmd replacement (vm_mailcmd.py): faster-whisper transcription (CPU int8), extractive summary + intent tags + spoken-digit number extraction, multipart/alternative HTML email, fail-safe relay of original message - Telegram DM delivery (vm_telegram.py) with per-mailbox routing - Caller-ID -> name (vm_contacts.py): file / google / carddav backends - SQLite store (vm_store.py) with content-addressed audio - FastAPI portal (vm_web.py): PIN login, list/play/delete, per-user settings, zero JS, loopback-only behind Apache TLS - Backfill importer (vm_import.py) for existing spool recordings - systemd unit, Apache vhost + certbot TLS, install.sh - Docs: INSTALL, CONFIGURATION, ARCHITECTURE, OPERATIONS, SECURITY, TESTING Verified end-to-end on mail.txt3.net: 157 historical messages backfilled, live voicemail -> transcribed -> stored -> visible at https://vm.txt3.net.
This commit is contained in:
113
tests/test_contacts.py
Normal file
113
tests/test_contacts.py
Normal file
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Offline tests for vm_contacts: vCard/CSV parsing, digit matching, cache."""
|
||||
import os, sys, tempfile, json
|
||||
sys.path.insert(0, "/home/jp/asterisk-vm")
|
||||
|
||||
d = tempfile.mkdtemp()
|
||||
|
||||
VCF = """BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
FN:Dave Roberts
|
||||
TEL;TYPE=CELL:+44 7941 223856
|
||||
END:VCARD
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
FN:Alice Smith
|
||||
TEL;TYPE=WORK:020 7946 0018
|
||||
TEL;TYPE=CELL:07700 900123
|
||||
END:VCARD
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
FN:No Number Person
|
||||
END:VCARD
|
||||
"""
|
||||
vcf = os.path.join(d, "c.vcf"); open(vcf, "w").write(VCF)
|
||||
|
||||
CSV = """Name,Given Name,Family Name,Phone 1 - Type,Phone 1 - Value
|
||||
Bob Jones,Bob,Jones,Mobile,+1 (555) 010-9876
|
||||
Carol White,Carol,White,Mobile,07123 456789 ::: 02012345678
|
||||
"""
|
||||
csvp = os.path.join(d, "c.csv"); open(csvp, "w").write(CSV)
|
||||
|
||||
cache = os.path.join(d, "cache.json")
|
||||
|
||||
def write_conf(path_val, backends="file"):
|
||||
c = os.path.join(d, "contacts.conf")
|
||||
open(c, "w").write(f"""[contacts]
|
||||
enabled = yes
|
||||
backends = {backends}
|
||||
cache_path = {cache}
|
||||
cache_ttl = 86400
|
||||
match_digits = 9
|
||||
|
||||
[file]
|
||||
path = {path_val}
|
||||
""")
|
||||
os.environ["VM_CONTACTS_CONF"] = c
|
||||
return c
|
||||
|
||||
import importlib
|
||||
import vm_contacts as C
|
||||
|
||||
def fresh(path_val, backends="file"):
|
||||
write_conf(path_val, backends)
|
||||
if os.path.exists(cache): os.unlink(cache)
|
||||
importlib.reload(C)
|
||||
return C
|
||||
|
||||
print("== extract_number")
|
||||
for s in ['Dave Roberts <07941223856>', '"Alice" <+447941223856>', '07700900123', 'unknown', '']:
|
||||
print(" %-30r -> %r" % (s, C.extract_number(s)))
|
||||
|
||||
print("\n== vCard lookup, various formats of the SAME number")
|
||||
c = fresh(vcf)
|
||||
for s in ["<07941223856>", "<+447941223856>", "<447941223856>", "Dave <7941223856>"]:
|
||||
print(" %-24s -> %r" % (s, c.resolve(s, log=lambda m: None)))
|
||||
|
||||
print("\n== vCard: second number on a multi-TEL contact")
|
||||
c = fresh(vcf)
|
||||
print(" Alice work 02079460018 ->", c.resolve("<02079460018>", log=lambda m: None))
|
||||
c = fresh(vcf)
|
||||
print(" Alice cell 07700900123 ->", c.resolve("<07700900123>", log=lambda m: None))
|
||||
|
||||
print("\n== unknown number -> None (and cached as a miss)")
|
||||
c = fresh(vcf)
|
||||
print(" ->", c.resolve("<07999999999>", log=lambda m: None))
|
||||
print(" cache contents:", json.load(open(cache)))
|
||||
|
||||
print("\n== CSV backend (Google CSV export format, ::: multi-value)")
|
||||
c = fresh(csvp)
|
||||
print(" Bob 5550109876 ->", c.resolve("<+15550109876>", log=lambda m: None))
|
||||
c = fresh(csvp)
|
||||
print(" Carol 07123456789 ->", c.resolve("<07123456789>", log=lambda m: None))
|
||||
c = fresh(csvp)
|
||||
print(" Carol 2nd num 02012345678 ->", c.resolve("<02012345678>", log=lambda m: None))
|
||||
|
||||
print("\n== disabled / missing file / no number")
|
||||
c = fresh(os.path.join(d, "nope.vcf"))
|
||||
print(" missing file ->", c.resolve("<07941223856>", log=lambda m: None))
|
||||
open(os.environ["VM_CONTACTS_CONF"], "a").write("\n")
|
||||
w = os.path.join(d, "off.conf")
|
||||
open(w, "w").write("[contacts]\nenabled = no\nbackends = file\n")
|
||||
os.environ["VM_CONTACTS_CONF"] = w; importlib.reload(C)
|
||||
print(" disabled ->", C.resolve("<07941223856>", log=lambda m: None))
|
||||
c = fresh(vcf)
|
||||
print(" empty callerid ->", c.resolve("", log=lambda m: None))
|
||||
|
||||
print("\n== carddav pointed at google must refuse app-password auth")
|
||||
w2 = os.path.join(d, "cd.conf")
|
||||
open(w2, "w").write(f"""[contacts]
|
||||
enabled = yes
|
||||
backends = carddav
|
||||
cache_path = {os.path.join(d,'c2.json')}
|
||||
match_digits = 9
|
||||
|
||||
[carddav]
|
||||
url = https://www.google.com/carddav/v1/principals/me/lists/default/
|
||||
username = me@gmail.com
|
||||
app_password = abcdefghijklmnop
|
||||
""")
|
||||
os.environ["VM_CONTACTS_CONF"] = w2; importlib.reload(C)
|
||||
msgs = []
|
||||
print(" ->", C.resolve("<07941223856>", log=msgs.append))
|
||||
for m in msgs: print(" [log]", m)
|
||||
Reference in New Issue
Block a user