Noesis / Integrations

Send an Asterisk channel's audio to a relay with AudioSocket, and forward it to Noesis.

Asterisk's AudioSocket (Asterisk 18 and later) sends a channel's audio to a TCP server as 16-bit PCM at 8 kHz. Run a relay that accepts the AudioSocket connection and forwards the audio to Noesis.

1. Send the channel's audio to your relay

In the dialplan, give AudioSocket a UUID for the call and your relay's address:

Config
exten => 100,1,Answer()
 same => n,AudioSocket(40325ec2-5efd-4bd3-805f-53576e581d13,relay.example.com:9092)

AudioSocket connects the channel to the relay until the connection closes. To listen to a call that continues with a person or bot at the same time, run AudioSocket on a spy channel of the person you called, for example with ChanSpy or an ARI snoop channel.

2. Forward the audio to Noesis

The relay uses noesis_relay.py from the relay helper. Each AudioSocket message is a 1-byte type, a 2-byte length (big-endian) and the payload:

Python
# asterisk_relay.py: Asterisk AudioSocket -> your server -> Noesis
import asyncio, uuid

from noesis_relay import NoesisCall

HANGUP, CALL_UUID, AUDIO = 0x00, 0x01, 0x10

async def on_answer(event):
    if event["type"] == "classification":
        print(event["call_id"], event["label"])     # your own logic

async def handle(reader, writer):
    call = None
    try:
        while True:
            header = await reader.readexactly(3)
            kind, size = header[0], int.from_bytes(header[1:3], "big")
            payload = await reader.readexactly(size) if size else b""
            if kind == CALL_UUID:
                call = NoesisCall(on_answer, encoding="pcm_s16le", sample_rate=8000,
                                  call_id=str(uuid.UUID(bytes=payload)))
                await call.open()
            elif kind == AUDIO and call is not None:
                await call.send(payload)            # 16-bit PCM, 8 kHz, mono
            elif kind == HANGUP:
                break
    except asyncio.IncompleteReadError:
        pass                                        # Asterisk closed the connection
    if call is not None:
        await call.close()
    writer.close()

async def main():
    server = await asyncio.start_server(handle, "0.0.0.0", 9092)
    async with server:
        await server.serve_forever()

asyncio.run(main())

To not record calls, create each call with record="false" as well.