feat: add initial Hold Slayer AI telephony gateway implementation
Complete project scaffolding and core implementation of an AI-powered telephony system that calls companies, navigates IVR menus, waits on hold, and transfers to the user when a human answers. Key components: - FastAPI server with REST API, WebSocket, and MCP (SSE) interfaces - SIP/VoIP call management via PJSUA2 with RTP audio streaming - LLM-powered IVR navigation using OpenAI/Anthropic with tool calling - Hold detection service combining audio analysis and silence detection - Real-time STT (Whisper/Deepgram) and TTS (OpenAI/Piper) pipelines - Call recording with per-channel and mixed audio capture - Event bus (asyncio pub/sub) for real-time client updates - Web dashboard with live call monitoring - SQLite persistence via SQLAlchemy with call history and analytics - Notification support (email, SMS, webhook, desktop) - Docker Compose deployment with Opal VoIP and Opal Media containers - Comprehensive test suite with unit, integration, and E2E tests - Simplified .gitignore and full project documentation in README
This commit is contained in:
104
docs/dial-plan.md
Normal file
104
docs/dial-plan.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Hold Slayer Gateway — Dial Plan
|
||||
|
||||
## Overview
|
||||
|
||||
The gateway accepts calls from registered SIP endpoints and routes them
|
||||
based on the dialled digits. No trunk-access prefix (no "9") is needed.
|
||||
All routing is pattern-matched in order; the first match wins.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Emergency Services — 911
|
||||
|
||||
> **911 and 9911 are always routed directly to the PSTN trunk.**
|
||||
> No gateway logic intercepts, records, or delays these calls.
|
||||
> `9911` is accepted in addition to `911` to catch the common
|
||||
> mis-dial habit of dialling `9` for an outside line.
|
||||
>
|
||||
> **Your SIP trunk provider must support emergency calling on your DID.**
|
||||
> Verify this with your provider before putting this system in service.
|
||||
> VoIP emergency calling has location limitations — ensure your
|
||||
> registered location is correct with your provider.
|
||||
|
||||
---
|
||||
|
||||
## Extension Ranges
|
||||
|
||||
| Range | Purpose |
|
||||
|-------|--------------------------------|
|
||||
| 2XX | SIP endpoints (phones/softphones) |
|
||||
| 5XX | System services |
|
||||
|
||||
---
|
||||
|
||||
## 2XX — Endpoint Extensions
|
||||
|
||||
Extensions are auto-assigned from **221** upward when a SIP device
|
||||
registers (`SIP REGISTER`) with the gateway or via `POST /api/devices`.
|
||||
|
||||
| Extension | Format | Example |
|
||||
|-----------|---------------------------------|--------------------------------|
|
||||
| 221–299 | Auto-assigned to registered devices | `sip:221@gateway.helu.ca` |
|
||||
|
||||
### Assignment policy
|
||||
|
||||
- First device to register gets **221**, next **222**, and so on.
|
||||
- Extensions are persisted in the database and survive restarts.
|
||||
- If a device is removed its extension is freed and may be reassigned.
|
||||
- `GATEWAY_SIP_DOMAIN` in `.env` sets the domain part of the URI.
|
||||
|
||||
---
|
||||
|
||||
## 5XX — System Services
|
||||
|
||||
| Extension | Service | Notes |
|
||||
|-----------|----------------------|-----------------------------------------|
|
||||
| 500 | Auto-attendant | Reserved — not yet implemented |
|
||||
| 510 | Gateway status | Plays a status announcement |
|
||||
| 511 | Echo test | Returns audio back to caller |
|
||||
| 520 | Hold Slayer launch | Prompts for a number to hold-slay |
|
||||
| 599 | Operator fallback | Transfers to preferred device |
|
||||
|
||||
---
|
||||
|
||||
## Outbound PSTN
|
||||
|
||||
All outbound patterns are routed via the configured SIP trunk
|
||||
(`SIP_TRUNK_HOST`). No access code prefix is needed.
|
||||
|
||||
### Pattern table
|
||||
|
||||
| Pattern | Example input | Normalised to | Notes |
|
||||
|----------------------|--------------------|---------------------|------------------------------------|
|
||||
| `+1NPANXXXXXX` | `+16135550100` | `+16135550100` | E.164 — pass through as-is |
|
||||
| `1NPANXXXXXX` | `16135550100` | `+16135550100` | NANP with country code |
|
||||
| `NPANXXXXXX` | `6135550100` | `+16135550100` | 10-digit NANP — prepend `+1` |
|
||||
| `011CC…` | `01144201234567` | `+44201234567` | International — strip `011` |
|
||||
| `00CC…` | `004420…` | `+4420…` | International alt prefix |
|
||||
| `+CC…` | `+44201234567` | `+44201234567` | E.164 international — pass through |
|
||||
|
||||
### Rules
|
||||
|
||||
1. E.164 (`+` prefix) is always passed to the trunk unchanged.
|
||||
2. NANP 11-digit (`1` + 10 digits) is normalised to E.164 by prepending `+`.
|
||||
3. NANP 10-digit is normalised to E.164 by prepending `+1`.
|
||||
4. International via `011` or `00` strips the IDD prefix and prepends `+`.
|
||||
5. 7-digit local dialling is **not supported** — always dial the area code.
|
||||
|
||||
---
|
||||
|
||||
## Inbound PSTN
|
||||
|
||||
Calls arriving from the trunk on the DID (`SIP_TRUNK_DID`) are routed
|
||||
to the highest-priority online device. If no device is online the call
|
||||
is queued or dropped (configurable via `MAX_HOLD_TIME`).
|
||||
|
||||
---
|
||||
|
||||
## Future
|
||||
|
||||
- Named regions / area-code routing
|
||||
- Least-cost routing across multiple trunks
|
||||
- Time-of-day routing (business hours vs. after-hours)
|
||||
- Ring groups across multiple 2XX extensions
|
||||
- Voicemail (extension 500)
|
||||
Reference in New Issue
Block a user