Elixir Telegram Bot
Telegram Bot backend implementation with elixir
December 27, 2017
elixir
telegram
bot
GenServer
The quest was to develop a telegram bot over 24hrs, during an office hackathon event.
I started out consulting documentation.
Bot interaction was implemented as a GenServer named ConversationTree
defmodule TelegramService.ConversationTree do
use GenServer
def start_link(name, conversation \\ %{message: [], response: [], tickets: []}) do
{:ok, _} = GenServer.start_link(__MODULE__, conversation, name: name)
name
end
def lookup(name) do
GenServer.whereis(name)
end
def kill(name) do
GenServer.stop(name)
end
def write_message(name, message = %{}) do
GenServer.call(name, {:write_message, message})
end
def write_response(name, message = %{}) do
GenServer.call(name, {:write_response, message})
end
def put_ticket(name, ticket_info= %{}) do
GenServer.call(name, {:put_ticket, ticket_info})
end
def clear_all(name) do
GenServer.call(name, {:clear_all})
end
def retrieve(name) do
GenServer.call(name, {:retrieve})
end
def init(conversation) do
{:ok, conversation}
end
def handle_call({:clear_all}, _from, %{message: _, response: _, tickets: _}) do
conversation = %{message: [], response: [], tickets: []}
{:reply, :ok, conversation}
end
def handle_call({:put_ticket, payload}, _from, %{message: messages, response: answers, tickets: tickets}) do
conversation = %{message: messages, response: answers, tickets: tickets ++ [payload]}
{:reply, :ok, conversation}
end
def handle_call({:write_message, payload}, _from, %{message: messages, response: answers, tickets: tickets}) do
conversation = %{message: messages ++ [payload], response: answers, tickets: tickets}
{:reply, :ok, conversation}
end
def handle_call({:write_response, payload}, _from, %{message: messages, response: answers, tickets: tickets}) do
conversation = %{message: messages, response: answers ++ [payload], tickets: tickets}
{:reply, :ok, conversation}
end
def handle_call({:retrieve}, _from, conversation = %{message: _, response: _, tickets: _}) do
{:reply, {:ok, conversation}, conversation}
end
end
Explanation
So, the main idea is to create a new instance of the ConversationTree
per chat received from the Telegram service.
The strategy adopted was to set a webhook endpoints which gets a POST request when ever a user engage the Bot.
message
list in the state maintained by theConversationTree
, is to persist all incoming messages, a user sends to the Bot.tickets
is peculiar to the business logic for which I was developing the bot for as at this time. Basically you can add aditional fields as required.response
is a list of responses sent back to the user via the Bot. A response structure is describe below%{text: text, options: options, next_actor: next_actor} = response %{module: module_name, action: action_in_module} = next_actor
For the every response sent to the user, a
list
of options are provided to the user to choose from using the telegram keyboad method from which the next message a user sends back is taken. Along side with the options sent back to the user, the nextactor
to act on an incoming message selected from list of options provided, this will give us the upportunity send the user response to the appropriateModule.Action
usingapply/3
.conversation = %{options: last_options, next_actor: %{module: actor_module, action: actor_action}, text: _} apply(Module.concat(TelegramService, actor_module), String.to_atom(actor_action), [incoming_message, chat_id, message_id])
- And this goes on and on inline with you business logic.
:)