ExUssd
Introduction
ExUssd lets you create simple, flexible, and customizable USSD interface. Under the hood ExUssd uses Elixir Registry to create and route individual USSD session.
Sections
Installation
If available in Hex, the package can be installed
by adding ex_ussd to your list of dependencies in mix.exs:
def deps do
[
{:ex_ussd, "~> 0.1.0"}
]
endDocumentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_ussd.
Providers
ExUssd currently supports
Configuration
To Use One of the above gateway providers for your project
Create a copy of config/dev.exs or config/prod.exs from config/dev.sample.exs
Use the gateway key to set the ussd vendor.
AfricasTalking
Add below config to dev.exs / prod.exs files
config :ex_ussd, :gateway, AfricasTalkingInfobip
Add below config to dev.exs / prod.exs files
config :ex_ussd, :gateway, InfobipMenu
ExUssd supports Ussd customizations through Menu struct via the render function
handler- This is a callback function that returns the menu struct, ussd api_parameters map and should_handle boolean.- menu - The menu struct is modified to produce ussd menu struct
- api_parameters - This a map of ussd response call
- should_handle - a check value, where ExUssd allows the developer to handle client input, more on
handle, default isfalse
name- This is the value display when Menu is rendered as menu_list. check more onmenu_list.title- Outputs the ussd's title,
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
end
)
{:ok, "CON Welcome"}menu_list- Takes a list of Ussd Menu
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 1
{:ok, "CON selected product a\n0:BACK"}should_close- This triggers ExUssd to end the current registry session and correct preffix the menu string
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
|> Map.put(:should_close, true)
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
|> Map.put(:should_close, true)
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 1
{:ok, "END selected product a"}default_error_message- This the default error message shown on invalid input. default"Invalid Choice\n"
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:default_error_message, "Invalid selection, try again\n")
|> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B"}
# simulate 11
{:ok, "CON Invalid selection, try again\nWelcome\n1:Product A\n2:Product B"}display_style- Used change the default's display style ":"
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:display_style, ")")
|> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end)]
)
end)
{:ok, "CON Welcome\n1)Product A\n2)Product B"}split- This is used to set the chunk size value when rendering menu_list. default value size7
ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:split, 2)
|> Map.put(:title, "Welcome")
|> Map.put(:menu_list,
[
Menu.render(
name: "Product A",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product a")
end),
Menu.render(
name: "Product B",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product b")
end),
Menu.render(
name: "Product C",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "selected product c")
end)]
)
end)
{:ok, "CON Welcome\n1:Product A\n2:Product B\n98:MORE"}
# simulate 98
{:ok, "CON Welcome\n3:Product C\n0:BACK"}next- Used render the next menu chunk, default"98"previous- Ussd to navigate to the previous menu, default "0"handle- To let ExUssd allow the developer to validate the client input, before navigating to the next menu.
iex> ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu
|> Map.put(:title, "Enter Pin Number")
|> Map.put(:handle, true)
|> Map.put(:validation_menu, Menu.render(
name: "",
handler: fn menu, api_parameters, should_handle ->
case should_handle do
true ->
case api_parameters.text == "5342" do
true ->
menu
|> Map.put(:title, "success, thank you.")
|> Map.put(:success, true)
|> Map.put(:should_close, true)
_->
menu |> Map.put(:error, "Wrong pin number\n")
end
false -> menu
end
end)
)
end
)
{:ok, "CON Enter Pin Number"}
## simulate 5342
{:ok, "END success, thank you."}
## simulate 5555
{:ok, "CON Wrong pin number\nEnter Pin Number"}error- custom error message on failed validation/handling,success- allows ExUssd to Render next menu on successful validation/handlingshow_options- hides menu list on falseshow_navigation- set to false to hide navigation menu
Render Menu
ExUssd to render Menu struct for different ussd providers. ExUssd provides goto function that starts and manages the ussd sessions.
The goto function receives the following parameters.
internal_routing- it takes a map with ussd text, session_id and serive_codemenu- Menu structapi_parameters- api_parameters
iex> menu = ExUssd.Menu.render(
name: "Home",
handler: fn menu, _api_parameters, _should_handle ->
menu |> Map.put(:title, "Welcome")
end
)
iex> ExUssd.goto(
internal_routing: %{text: "", session_id: "session_01", service_code: "*544#"},
menu: menu,
api_parameters: %{
"sessionId" => "session_01",
"phoneNumber" => "254722000000",
"networkCode" => "Safaricom",
"serviceCode" => "*544#",
"text" => ""
}
)
{:ok, "CON Welcome"}