- Create docs/ with 7 focused files: architecture, api, scoring, agent, deployment, freeswitch, development - Rewrite README.md as concise overview with links to docs/ - Remove AGENTS.md (content merged into docs/ and README)
105 lines
3.0 KiB
Markdown
105 lines
3.0 KiB
Markdown
# FreeSWITCH Integration
|
|
|
|
pulse-lets-go routes calls through FreeSWITCH via `mod_curl` → Lua dialplan → `/api/route`.
|
|
|
|
## Dialplan (route.lua)
|
|
|
|
FreeSWITCH uses a Lua script in its dialplan. The script calls `/api/route` via `mod_curl` and bridges to the returned gateway.
|
|
|
|
```lua
|
|
-- contrib/route.lua (simplified)
|
|
api = freeswitch.API()
|
|
caller_id = session:getVariable("caller_id_number") or ""
|
|
dest = session:getVariable("destination_number") or ""
|
|
url = "http://localhost:8080/api/route?caller_id=" .. caller_id .. "&dest=" .. dest
|
|
|
|
raw = api:execute("curl", url)
|
|
body = raw:match("\r?\n\r?\n(.+)") or raw
|
|
|
|
local ok, route = pcall(cjson.decode, body)
|
|
if not ok then session:hangup("NORMAL_TEMPORARY_FAILURE") return end
|
|
|
|
if route.fallback then
|
|
session:bridge(route.sip_gateway) -- operator
|
|
elseif route.sip_gateway then
|
|
session:bridge(route.sip_gateway) -- best node
|
|
else
|
|
session:hangup("NORMAL_TEMPORARY_FAILURE")
|
|
end
|
|
```
|
|
|
|
## Dialplan Configuration
|
|
|
|
```xml
|
|
<extension name="route_call">
|
|
<condition field="destination_number" expression="^.*$">
|
|
<action application="set" data="balancer_url=http://balancer.lan:8080"/>
|
|
<action application="set" data="ingress_trunk=trk-001"/>
|
|
<action application="lua" data="route.lua"/>
|
|
</condition>
|
|
</extension>
|
|
```
|
|
|
|
Reload dialplan:
|
|
```bash
|
|
sudo fs_cli -x "reloadxml"
|
|
```
|
|
|
|
## ESL (Gateway Management)
|
|
|
|
When `esl.host` is configured in `config.json`, the balancer:
|
|
- Connects to FreeSWITCH ESL (`:8021`)
|
|
- Synchronizes ingress trunks as Sofia gateways
|
|
- Listens for gateway registration events
|
|
- Streams gateway status to WebSocket frontend
|
|
|
|
### Configuration
|
|
|
|
```json
|
|
{
|
|
"esl": {
|
|
"host": "127.0.0.1",
|
|
"port": 8021,
|
|
"password": "ClueCon",
|
|
"password_env": "",
|
|
"sofia_profile": "external",
|
|
"gateway_prefix": "pulse"
|
|
}
|
|
}
|
|
```
|
|
|
|
- `password_env` — alternative: name of environment variable containing password
|
|
- `sofia_profile` — Sofia profile to manage (default: `external`)
|
|
- `gateway_prefix` — prefix for auto-created gateways (default: `pulse`)
|
|
|
|
## Prerequisites (before deployment)
|
|
|
|
| # | Check | Command | Expected |
|
|
|:-:|-------|---------|----------|
|
|
| 1 | **ESL running** | `nc -z 127.0.0.1 8021 && echo OK` | `OK` |
|
|
| 2 | **ESL password** | `echo "auth ClueCon" \| nc 127.0.0.1 8021` | `+OK accepted` |
|
|
| 3 | **mod_curl loaded** | `fs_cli -x "show modules" \| grep mod_curl` | `<load module="mod_curl"/>` |
|
|
| 4 | **mod_lua loaded** | `fs_cli -x "show modules" \| grep mod_lua` | `<load module="mod_lua"/>` |
|
|
| 5 | **ESL ACL** | ACL in `event_socket.conf.xml` allows 127.0.0.1 | `localhost` or `127.0.0.1` in ACL |
|
|
|
|
## Loading mod_curl Without Restart
|
|
|
|
```bash
|
|
fs_cli -x "load mod_curl"
|
|
# Add to modules.conf.xml for persistence:
|
|
# <load module="mod_curl"/>
|
|
```
|
|
|
|
## Rollback (if route.lua breaks calls)
|
|
|
|
```bash
|
|
# 1. Remove pulse_route from dialplan
|
|
sudo sed -i '/pulse_route/,/<\/extension>/d' /etc/freeswitch/dialplan/default.xml
|
|
sudo fs_cli -x "reloadxml"
|
|
|
|
# 2. Stop pulse-lets-go
|
|
sudo systemctl stop pulse-lets-go
|
|
|
|
# Calls continue through original dialplan without balancer.
|
|
```
|