lib/ex_gram/updates/polling.ex

defmodule ExGram.Updates.Polling do
  @moduledoc """
  Updates implementation that uses polling method
  """

  use GenServer
  require Logger

  @polling_timeout 100

  def start_link({:bot, pid, :token, token}) do
    # Logger.debug "START WORKER"
    GenServer.start_link(__MODULE__, {:ok, pid, token})
  end

  def init({:ok, pid, token}) do
    # Clean webhook
    ExGram.delete_webhook(token: token)

    Process.send_after(self(), {:fetch, :update_id}, @polling_timeout)
    {:ok, {pid, token, -1}}
  end

  def handle_cast({:fetch, :update_id} = m, state), do: handle_info(m, state)

  def handle_info(:timeout, state), do: handle_info({:fetch, :update_id}, state)

  def handle_info({:fetch, :update_id}, {pid, token, uid}) do
    updates = get_updates(token, uid)
    send_updates(updates, pid)

    nid = next_pid(uid, updates)

    {:noreply, {pid, token, nid}, @polling_timeout}
  end

  def handle_info(unknown_message, state) do
    Logger.debug("Polling updates received an unknown message #{inspect(unknown_message)}")

    {:noreply, state, @polling_timeout}
  end

  @default_opts [limit: 100, timeout: 50]
  defp get_updates(token, uid, opts \\ []) do
    opts =
      @default_opts
      |> Keyword.merge(opts)
      |> Keyword.put(:offset, uid)
      |> Keyword.put(:token, token)

    try do
      ExGram.get_updates!(opts)
    rescue
      ExGram.Error -> []
    end
  end

  defp send_updates(updates, pid) do
    Enum.map(updates, &GenServer.call(pid, {:update, &1}))
  end

  defp next_pid(actual, []), do: actual

  defp next_pid(actual, updates) do
    updates
    |> Stream.map(&(&1.update_id + 1))
    |> Enum.reduce(actual, &max(&1, &2))
  end
end