Packages

Telegram Bot API client and framework for Elixir

Current section

Files

Jump to
hotline lib hotline bot.ex
Raw

lib/hotline/bot.ex

defmodule Hotline.Bot do
@moduledoc """
Behaviour and GenServer for building Telegram bots.
Usage:
defmodule MyBot do
use Hotline.Bot
@impl Hotline.Bot
def handle_update(update, state) do
case update.message do
%{text: "/start"} ->
Hotline.send_message(%{chat_id: state.chat_id, text: "Hello!"})
_ ->
:ok
end
{:noreply, state}
end
end
Start the bot in your supervision tree:
children = [
{MyBot, token: "your-bot-token"}
]
## Restricting by user ID
Pass `allowed_ids` to only accept updates from specific users:
children = [
{MyBot, token: "your-bot-token", allowed_ids: [7644580464]}
]
Updates from other users are silently dropped. Omit `allowed_ids` to accept all.
"""
@callback handle_update(Hotline.Types.Update.t(), map()) :: {:noreply, map()}
@callback init_bot(map()) :: {:ok, map()}
@optional_callbacks init_bot: 1
defmacro __using__(opts) do
quote do
use GenServer
@behaviour Hotline.Bot
def start_link(init_opts \\ []) do
name = unquote(opts[:name]) || Keyword.get(init_opts, :name, __MODULE__)
GenServer.start_link(__MODULE__, init_opts, name: name)
end
@impl GenServer
def init(init_opts) do
Phoenix.PubSub.subscribe(Hotline.PubSub, "hotline:updates")
state = %{
chat_id: init_opts[:chat_id],
allowed_ids: init_opts[:allowed_ids],
opts: init_opts
}
state =
if function_exported?(__MODULE__, :init_bot, 1) do
case init_bot(state) do
{:ok, new_state} -> new_state
_ -> state
end
else
state
end
{:ok, state}
end
@impl GenServer
def handle_info({:hotline_update, update}, state) do
sender_id = Hotline.Bot.extract_sender_id(update)
if Hotline.Bot.allowed?(sender_id, state.allowed_ids) do
state =
case update do
%{message: %{chat: %{id: chat_id}}} when is_nil(state.chat_id) ->
%{state | chat_id: chat_id}
_ ->
state
end
handle_update(update, state)
else
{:noreply, state}
end
end
def handle_info(_msg, state), do: {:noreply, state}
defoverridable start_link: 1, init: 1
end
end
@doc false
def allowed?(_sender_id, nil), do: true
def allowed?(nil, _allowed_ids), do: false
def allowed?(sender_id, allowed_ids), do: sender_id in allowed_ids
@doc false
def extract_sender_id(%{message: %{from: %{id: id}}}), do: id
def extract_sender_id(%{callback_query: %{from: %{id: id}}}), do: id
def extract_sender_id(%{edited_message: %{from: %{id: id}}}), do: id
def extract_sender_id(%{channel_post: %{chat: %{id: id}}}), do: id
def extract_sender_id(_), do: nil
end