megafarad / esl4s   0.1.0

MIT License GitHub

A Scala 3 library for communicating with FreeSWITCH via the Event Socket Layer (ESL) protocol. Built on Apache Pekko Streams for reactive, backpressure-aware communication.

Scala versions: 3.x

esl4s

A Scala 3 library for communicating with FreeSWITCH via the Event Socket Layer (ESL) protocol. Built on Apache Pekko Streams for reactive, backpressure-aware communication.

Features

  • Inbound mode — connect to FreeSWITCH's event socket (port 8021) to send commands and receive events
  • Outbound mode — accept connections from FreeSWITCH's socket dialplan application
  • Comprehensive ESL command setapi, bgapi, event, filter, execute, sendmsg, sendevent, and more
  • Pekko Streams native — protocol codec exposed as BidiFlow, events as Source[EslEvent, NotUsed]
  • Automatic protocol handling — auth handshake (inbound), connect/myevents handshake (outbound), nested text/event-plain parsing

Requirements

  • Scala 3.8+
  • sbt 1.x
  • JDK 21+

Usage

Inbound connection

Connect to a running FreeSWITCH instance:

import com.megafarad.esl4s.connection.InboundConnection
import com.megafarad.esl4s.model.*
import org.apache.pekko.actor.typed.ActorSystem
import org.apache.pekko.actor.typed.scaladsl.Behaviors

given system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "esl4s")
import system.executionContext

val settings = InboundConnection.Settings(
  host = "127.0.0.1",
  port = 8021,
  password = "ClueCon"
)

InboundConnection.connect(settings).foreach { session =>
  // Subscribe to events
  session.sendCommand(EventCommand("plain", Seq("ALL")))

  // Listen for events
  session.events.runForeach { event =>
    println(s"Event: ${event.eventName}")
  }

  // Send a command
  session.sendCommand(ApiCommand("status")).foreach { reply =>
    println(s"Reply: ${reply.asInstanceOf[EslEvent].body}")
  }
}

Outbound server

Accept connections from FreeSWITCH's socket dialplan app:

import com.megafarad.esl4s.connection.OutboundServer
import com.megafarad.esl4s.model.*
import org.apache.pekko.Done

import scala.concurrent.Future

OutboundServer.bind(OutboundServer.Settings(port = 8084)) { session =>
  println(s"Call from: ${session.channelData.header("Channel-Name")}")

  // Execute an application
  session.sendCommand(Execute(
    uuid = session.channelData.uniqueId.getOrElse(""),
    app = "playback",
    arg = "/usr/share/freeswitch/sounds/hello.wav"
  ))

  // Hang up when done
  session.hangup().map(_ => Done)
}

Low-level codec

For custom stream graphs, use the BidiFlow codec directly:

import com.megafarad.esl4s.codec.EslCodec
import org.apache.pekko.stream.scaladsl.Tcp

val codec = EslCodec()
val tcpFlow = Tcp()(using system.classicSystem).outgoingConnection("127.0.0.1", 8021)
val eslFlow = codec.join(tcpFlow) // Flow[EslCommand, EslFrame, _]

Building

sbt compile   # compile
sbt test      # run tests
sbt console   # Scala 3 REPL

License

MIT