ProductPromotion
Logo

Elixir

made by https://0x3d.site

GitHub - danielberkompas/telephonist: Elixir state machines for Twilio calls
Elixir state machines for Twilio calls. Contribute to danielberkompas/telephonist development by creating an account on GitHub.
Visit Site

GitHub - danielberkompas/telephonist: Elixir state machines for Twilio calls

GitHub - danielberkompas/telephonist: Elixir state machines for Twilio calls

Telephonist

Hex Version Build Status Inline docs Deps Status

Telephonist makes it easy to design state machines for Twilio calls. These state machines bring TwiML and logic together in one place, making call flows easier to maintain.

Installation

Get it from Hex by adding it to your deps in mix.exs:

def deps do
  [{:telephonist, "~> 0.1.2"}]
end

Run mix deps.get to install the package. Then, add :telephonist to your applications list. For example:

def application do
  [mod: {YourApp, []},
   applications: [:logger, :telephonist]]
end

This will ensure that all Telephonist's processes are started and supervised properly.

Usage

Basic Concepts

Like most state machines, Telephonist state machines are based on two concepts: state and transitions.

State

A state is represented by the Telephonist.State struct.

%Telephonist.State{
  machine: MachineName,
  name: :state_name,
  options: [],
  twiml: "<?xml ..."
}

States are primarily used to define what TwiML should be displayed to Twilio for a given call at a particular time. Telephonist provides a simple macro to make generating and returning Telephonist.State structs easy:

defmodule CustomStateMachine do
  use Telephonist.StateMachine, initial_state: :introduction

  state :introduction, _twilio, _options do
    say "Welcome to my phone tree!"
  end
end

The state/3 macro is just sugar, and defines a function like this:

def state(:introduction, _twilio, options) do
  xml = twiml do
    say "Welcome to my phone tree!"
  end

  %Telephonist.State{
    machine: __MODULE__,
    name: :introduction,
    options: options,
    twiml: xml
  }
end

The three arguments are as follows:

  • state_name: the name of the state, obviously.
  • twilio: a map of parameters sent in from Twilio.
  • options: a map of custom options defined by you at various points during the call's lifecycle.

Whenever Telephonist wants to get a particular state out of your module, it will call the state/3 function generated by the state/3 macro, like so:

# twilio  -> a map of parameters that came from Twilio
# options -> any custom options that are appended to the call over time
CustomStateMachine.state(:introduction, twilio, options)

You can pattern match with the state/3 struct just like a function definition.

state :introduction, _twilio, %{error: msg} do
  say "An error occurred! #{msg}"
end

state :introduction, _twilio, _options do
  say "Welcome to my phone tree!"
end

Transitions

Transitions are handled through the transition/3 function. It takes the same three arguments as the state/3 function or macro.

  • state_name: the name of the state that is being transitioned from.
  • twilio: a map of parameters passed in from Twilio.
  • options: a map of custom parameters defined by you.

You can define it on your state machines like so:

defmodule CustomCallFlow do
  use Telephonist.StateMachine, initial_state: :choose_language

  state :choose_language, twilio, options do
    say "#{options[:error]}" # say any error, if present
    gather timeout: 10 do
      say "For English, press 1"
      say "Para español, presione 2"
    end
  end

  state :english, twilio, options do
    say "Proceeding in English..."
  end

  state :spanish, twilio, options do
    say "Procediendo en español..."
  end

  # If the user pressed "1" on their keypad, transition to English state
  def transition(:choose_language, %{Digits: "1"} = twilio, options) do
    state :english, twilio, options
  end

  # If the user pressed "2" on their keypad, transition to Spanish state
  def transition(:choose_language, %{Digits: "2"} = twilio, options) do
    state :spanish, twilio, options
  end

  # If neither of the above are true, append an error to the options and
  # remain on the current state
  def transition(:choose_language, twilio, options) do
    options = Map.put(options, :error, "You pressed an invalid digit. Please try again.")
    state :choose_language, twilio, options
  end
end

Note that transition/3 must return a Telephonist.State. This is easily done by simply calling the state/3 function. Also, note that you can easily switch to another state machine by simply calling state on it:

def transition(:choose_language, %{Digits: "1"} = twilio, options) do
  EnglishCallFlow.state(:introduction, twilio, options)
end

def transition(:choose_language, %{Digits: "2"} = twilio, options) do
  SpanishCallFlow.state(:introduction, twilio, options)
end

Control of the call will then be passed to the other state machine. This allows you to keep your state machines small, focused, and potentially reusable.

on_complete/3

When a call completes, Telephonist will call the on_complete/3 callback. It will receive the Telephonist.State of the call at the time it completed, Twilio's final request parameters, and the custom options the call accumulated during its life:

def on_complete({sid, twilio_call_status, state}, twilio, options) do
  :ok
end

This is a good place to put any cleanup logic that you need to perform after a call completes.

on_transition_error/4

This callback will be run if a transition fails due to an exception. This will most often occur when you fail to define a transition or state, or if your pattern matching left a case out. It provides you an opportunity to recover the call and prevent the user from hearing a Twilio error message.

def on_transition_error(exception, state_name, twilio, options) do
  # To prevent an error, return a new state:
  state :recover, twilio, options
end

The default implementation of on_transition_error/4 that comes with Telephonist.StateMachine will simply re-raise the error.

Processing Calls

Once you've defined a state machine, you can process calls through it using Telephonist.CallProcessor.

# The web framework shown here is pseudo-code
def index(conn, twilio) do
  options = %{} # Whatever I want to be able to use in my states and transitions
  state = Telephonist.CallProcessor.process(MyStateMachine, twilio, options)
  render conn, xml: state.twiml
end

That's it! New calls will start off in MyStateMachine.initial_state and progress from there.

Subscribing to Events

Telephonist publishes events via GenEvent. In fact, Telephonist.Logger is simply a subscriber to these events. Look there for an example of how to implement your own subscriber.

Other Twilio Libraries

See these other Elixir libraries I've written for Elixir:

  • ExTwilio. A Twilio API client.
  • ExTwiml. Render TwiML from Elixir. This is actually a dependency of Telephonist, and is used in the state/3 macro.

LICENSE

Telephonist is under the MIT license. See the LICENSE file for more details.

Articles
to learn more about the elixir concepts.

Resources
which are currently available to browse on.

mail [email protected] to add your project or resources here 🔥.

FAQ's
to know more about the topic.

mail [email protected] to add your project or resources here 🔥.

Queries
or most google FAQ's about Elixir.

mail [email protected] to add more queries here 🔍.

More Sites
to check out once you're finished browsing here.

0x3d
https://www.0x3d.site/
0x3d is designed for aggregating information.
NodeJS
https://nodejs.0x3d.site/
NodeJS Online Directory
Cross Platform
https://cross-platform.0x3d.site/
Cross Platform Online Directory
Open Source
https://open-source.0x3d.site/
Open Source Online Directory
Analytics
https://analytics.0x3d.site/
Analytics Online Directory
JavaScript
https://javascript.0x3d.site/
JavaScript Online Directory
GoLang
https://golang.0x3d.site/
GoLang Online Directory
Python
https://python.0x3d.site/
Python Online Directory
Swift
https://swift.0x3d.site/
Swift Online Directory
Rust
https://rust.0x3d.site/
Rust Online Directory
Scala
https://scala.0x3d.site/
Scala Online Directory
Ruby
https://ruby.0x3d.site/
Ruby Online Directory
Clojure
https://clojure.0x3d.site/
Clojure Online Directory
Elixir
https://elixir.0x3d.site/
Elixir Online Directory
Elm
https://elm.0x3d.site/
Elm Online Directory
Lua
https://lua.0x3d.site/
Lua Online Directory
C Programming
https://c-programming.0x3d.site/
C Programming Online Directory
C++ Programming
https://cpp-programming.0x3d.site/
C++ Programming Online Directory
R Programming
https://r-programming.0x3d.site/
R Programming Online Directory
Perl
https://perl.0x3d.site/
Perl Online Directory
Java
https://java.0x3d.site/
Java Online Directory
Kotlin
https://kotlin.0x3d.site/
Kotlin Online Directory
PHP
https://php.0x3d.site/
PHP Online Directory
React JS
https://react.0x3d.site/
React JS Online Directory
Angular
https://angular.0x3d.site/
Angular JS Online Directory