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)

      def lookup(name) do

      def kill(name) do

      def write_message(name, message = %{}) do
        GenServer.call(name, {:write_message, message})

      def write_response(name, message = %{}) do
        GenServer.call(name, {:write_response, message})

      def put_ticket(name, ticket_info= %{}) do
        GenServer.call(name, {:put_ticket, ticket_info})

      def clear_all(name) do
        GenServer.call(name, {:clear_all})

      def retrieve(name) do
        GenServer.call(name, {:retrieve})

      def init(conversation) do
        {:ok, conversation}

      def handle_call({:clear_all}, _from, %{message: _, response: _, tickets: _}) do
        conversation = %{message: [], response: [], tickets: []}
        {:reply, :ok, conversation}

      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}

      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}

      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}

      def handle_call({:retrieve}, _from, conversation = %{message: _, response: _, tickets: _}) do
        {:reply, {:ok, conversation}, conversation}



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 the ConversationTree, 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 next actor to act on an incoming message selected from list of options provided, this will give us the upportunity send the user response to the appropriate Module.Action using apply/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.


comments powered by Disqus