lib/tgapi.ex

defmodule Tgapi do
  use Tesla, docs: false

  @moduledoc """
  Documentation for `Tgapi`.
  """

  @doc """
  you should use this to call Telegram methods.
  """

  adapter(Tesla.Adapter.Hackney, recv_timeout: 63_000)
  plug(Tesla.Middleware.BaseUrl, "https://api.telegram.org")

  def client(token) do
    fn method ->
      fn parameters ->
        parameters =
          Enum.map_join(parameters, "&", fn param -> "#{elem(param, 0)}=#{elem(param, 1)}" end)

        case get("/bot#{token}/#{method}?#{parameters}") do
          {:ok, %{body: body}} ->
            {:ok, data} = JSON.decode(body)

            case data do
              %{"ok" => true} -> {:ok, data["result"]}
              %{"ok" => false} -> {:error, data}
            end

          {:error, error} ->
            {:error, error}
        end
      end
    end
  end

  def inline_keyboard(data) do
    JSON.encode!(%{inline_keyboard: data})
  end

  def keyboard(data) do
    JSON.encode!(%{keyboard: data})
  end

  def get_updates(token, offset \\ 0) do
    client(token).(:getUpdates).(offset: offset, timeout: 60)
  end

  def handle(token, handler, offset \\ 0) do
    {:ok, updates} = get_updates(token, offset)
    Enum.each(updates, &Task.Supervisor.start_child(Tgapi.TaskSupervisor, fn -> handler.(&1) end))
    last_update_id = Enum.at(updates, -1, %{"update_id" => -1})["update_id"]
    handle(token, handler, last_update_id + 1)
  end

  def spawn_handle(token, handler, offset \\ 0) do
    Task.start_link(fn -> handle(token, handler, offset) end)
  end

  def start(token, handler) do
    Supervisor.start_link([
      {Task.Supervisor, name: Tgapi.TaskSupervisor},
      {Tgapi.Session, Tgapi.BotSession},
      Supervisor.child_spec({Task, fn -> handle(token, handler) end}, restart: :permanent)
    ], [strategy: :one_for_one, name: Tgapi.Supervisor])
  end
end