kannix

kannix

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

First Post!

idi527

idi527

:waving_hand:

Have you considered using webhooks?

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? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews