telegram icon indicating copy to clipboard operation
telegram copied to clipboard

Telegram library for the Elixir language

Telegram

CI Docs Coverage Status

Telegram library for the Elixir language.

It provides:

  • an inteface to the Telegram Bot HTTP-based APIs (Telegram.Api)
  • a couple of bot behaviours to define you bots (Telegram.Bot, Telegram.ChatBot)
  • two bot runners (Telegram.Poller, Telegram.Webhook)

Installation

The package can be installed by adding telegram to your list of dependencies in mix.exs:

def deps do
  [
    {:telegram, git: "https://github.com/visciang/telegram.git", tag: "xxx"}
  ]
end

Telegram Bot API

This module expose a light layer over the Telegram Bot API HTTP-based interface, it does not expose any "(data)binding" over the HTTP interface and tries to abstract away only the boilerplate for building / sending / serializing the API requests.

Compared to a full data-binded interface it could result less "typed frendly" but it will work with any version of the Bot API, hopefully without updates or incompatibily with new Bot API versions (as much as they remain backward compatible).

References:

Given the token of your Bot you can issue any request using:

  • method: Telegram API method name (ex. "getMe", "sendMessage")
  • options: Telegram API method specific parameters (you can use elixir native types)

Examples:

Given the bot token (something like):

token = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"

getMe

Telegram.Api.request(token, "getMe")

{:ok, %{"first_name" => "Abc", "id" => 1234567, "is_bot" => true, "username" => "ABC"}}

sendMessage

Telegram.Api.request(token, "sendMessage", chat_id: 876532, text: "Hello! .. silently", disable_notification: true)

{:ok,
  %{"chat" => %{"first_name" => "Firstname",
      "id" => 208255328,
      "last_name" => "Lastname",
      "type" => "private",
      "username" => "xxxx"},
    "date" => 1505118722,
    "from" => %{"first_name" => "Yyy",
      "id" => 234027650,
      "is_bot" => true,
      "username" => "yyy"},
    "message_id" => 1402,
    "text" => "Hello! .. silently"}}

getUpdates

Telegram.Api.request(token, "getUpdates", offset: -1, timeout: 30)

{:ok,
  [%{"message" => %{"chat" => %{"first_name" => "Firstname",
        "id" => 208255328,
        "last_name" => "Lastname",
        "type" => "private",
        "username" => "xxxx"},
      "date" => 1505118098,
      "from" => %{"first_name" => "Firstname",
        "id" => 208255328,
        "is_bot" => false,
        "language_code" => "en-IT",
        "last_name" => "Lastname",
        "username" => "xxxx"},
      "message_id" => 1401,
      "text" => "Hello!"},
    "update_id" => 129745295}]}

Sending files

If a API parameter has a InputFile type and you want to send a local file, for example a photo stored locally at "/tmp/photo.jpg", just wrap the parameter value in a tuple {:file, "/tmp/photo.jpg"}. If the file content is in memory wrap it in {:file_content, data, "photo.jpg"} tuple.

sendPhoto

Telegram.Api.request(token, "sendPhoto", chat_id: 876532, photo: {:file, "/tmp/photo.jpg"})
Telegram.Api.request(token, "sendPhoto", chat_id: 876532, photo: {:file_content, photo, "photo.jpg"})

Downloading files

To download a file from the telegram server you need a file_path pointer to the file. With that you can download the file via Telegram.Api.file.

{:ok, res} = Telegram.Api.request(token, "sendPhoto", chat_id: 12345, photo: {:file, "example/photo.jpg"})
# pick the 'file_obj' with the desired resolution
[file_obj | _] = res["photo"]
# get the 'file_id'
file_id = file_obj["file_id"]

getFile

{:ok, %{"file_path" => file_path}} = Telegram.Api.request(token, "getFile", file_id: file_id)
{:ok, file} = Telegram.Api.file(token, file_path)

Reply Markup

If a API parameter has a "A JSON-serialized object" type (InlineKeyboardMarkup, ReplyKeyboardMarkup, etc), just wrap the parameter value in a tuple {:json, value}.

Reference: Keyboards, Inline Keyboards

sendMessage with keyboard

keyboard = [
  ["A0", "A1"],
  ["B0", "B1", "B2"]
]
keyboard_markup = %{one_time_keyboard: true, keyboard: keyboard}
Telegram.Api.request(token, "sendMessage", chat_id: 876532, text: "Here a keyboard!", reply_markup: {:json, keyboard_markup})

Telegram Bot

Quick start

Check the examples under example/example_*.exs. You can run them as a Mix self-contained script.

BOT_TOKEN="..." example/example_chatbot.exs

Bot updates processing

The Telegram platform supports two ways of processing bot updates, getUpdates and setWebhook. getUpdates is a pull mechanism, setwebhook is push. (ref: bots webhook)

This library currently implements both models via two supervisors.

Poller

This mode can be used in a dev environment or if your bot doesn't need to "scale". Being in pull it works well behind a firewall (or behind an home internet router). Refer to the Telegram.Poller module docs fo more info.

Webhook

This mode interface with the telegram servers via a webhook, best for production use. The app is meant to be served over HTTP, a reverse proxy should be plance in front of it, facing the public network over HTTPS. Refer to the Telegram.Webhook module docs for more info.

Dispatch model

We can define stateless / statefull bot.

  • A stateless Bot has no memory of previous conversations, it just receives updates, process them and so on.

  • A statefull Bot instead can remember what happened in the past. The state here refer to a specific chat, a conversation (chat_id) between a user and a bot "instance".

Bot behaviours

  • Telegram.Bot: works with the stateless async dispatch model
  • Telegram.ChatBot: works with the statefull chat dispatch model

Logging

The library attach two metadata fields to the internal logs: [:bot, :chat_id]. If your app run more that one bot these fields can be included in your logs (ref. to the Logger config) to clearly identify and "trace" every BOT message flow.

Telegram Client Config

The Telegram.Client is based on Tesla + Gun adapter. It's possible to change the adapter options (at compile time) via the [:telegram, :gun_config] application environment key.

config :telegram,
  gun_config: [
    ...
  ]

Sample app

A chat_bot app: https://github.com/visciang/telegram_example