kannix

kannix

Poll Telegram API until response

I am currently stuck with an implementation of a function that has to poll the Telegram API periodically and wait for a response from this. I guess there is some async / await magic that will help me to solve this problem in an elegant way. Maybe someone can jump in an give me a hint :wink:

Other suggestions for improvement are also very welcome :slight_smile:

defmodule TelegramHelper do
  @moduledoc """
  Documentation for TelegramHelper.
  """

  import Nadia

  @chat_id 123456

  @spec get_sms_verification_code() :: any()
  def get_sms_verification_code do
    # telegram api will return some old messages from time to time
    # so we have to check if there are old message and get the update_id to poll later only for newer messages
    update_id = 0
    {:ok, result} = get_updates
    if length(result) > 0 do
      update_id = List.last(result).update_id + 1
    end

    send_message(@chat_id, "Please send the Auth Code")
    get_reply(update_id)
  end

  defp get_reply(update_id) do
    Process.sleep(1000)
    {:ok, result} = get_updates(%{:update_id => update_id})
    if length(result) == 0 do
      # this should stop after a timeout of e.g. 5 Minutes
      # and is obviously the wrong way of doing it
      get_reply(update_id)
    end
    # this is only for reference on what i want to return if i get back a new message from telegram
    List.first(result).message.text
  end
end

Most Liked

david_ex

david_ex

I believe you should be returning a tuple from handle_info/2:

  def handle_info(:poll, state) do
    case get_updates(last_update_id) do
      [] ->
        Process.send_after(self(), :poll, 5000)
        {:noreply, state}
      new_messages ->
        state = handle_new_messages(state, new_messages)
        {:noreply, state}
    end
  end

Last Post!

idi527

idi527

I don’t think you need a looping async functions … I’d do something like

# in Auther

def wait_for_chat(server , chat_id, timeout \\ 5000) do
  GenServer.call(server, {:wait_for_chat, chat_id}, timeout)
end

@doc false
def handle_call({:wait_for_chat, chat_id}, from, state) do
  # note that `Map.put` would override any previous "subscription" (from)
  # if this is a problem, store `from`s in a list
  # auth_codes = %{chat_id => [from1, from2, from3, ...]}
  {:noreply, %{state | auth_codes: Map.put(state.auth_codes, chat_id, from)}}
end

defp handle_new_messages(state, new_messages) do
  # go over new messages and check if there are any auth codes from
  # the chat ids in state.auth_codes, if there are, reply to 
  # the client process in the corresponding to that chat_id's `from`
  auth_codes = Enum.reduce(new_messages, state.auth_codes, fn %{"chat" => %{"id" => chat_id}, "text" => message}, auth_codes ->
    with true <- auth_code?(message),
         {from, auth_codes} when not is_nil(from) <- Map.pop(auth_codes, chat_id) do
      GenServer.reply(from, message)
      auth_codes
    else
      _ -> auth_codes
    end
  end)
  %{state | auth_codes: auth_codes}
end

Then in your browser automation program you can do

fill_email_password("mail", "password")
click_auth_button
auth_code = Auther.wait_for_chat(auther_pid, chat_id, :infinity) # or just a large number for timeout
fill_auth_code(code_from_telegram)

state.auth_codes should probably be renamed to state.awaiting_auth_codes or awaiting_chats_auth_code something like that. It was named incorrectly the first time since I misunderstood your problem again, it seems.

Where Next?

Popular in Questions Top

vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New

Other popular topics Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New

We're in Beta

About us Mission Statement