Automatic channel messages

Hi,

I’m trying to send a message automatically to all users subscribed to a specific channel every couple of seconds(it’s a status update type message). I’ve gotten everything working with a handle_info which I can test by setting up a :timer.send_interval(5_000, :updates) in the join function of the channel module. That works fine but with each user that joins another timer starts so its not ideal for actual usage. Any idea how to start a timer like that outside of the channel?

I’ve tried creating a function in the channel called start_updates which just has that timer line in it then calling it from lib/myapp.ex in the start function with Myapp.UpdateChannel.start_updates() and though it does get called the timer inside doesn’t appear to work.

2 Likes

I’m not quite sure if it really helps you, but there is quantum available at hex.pm, perhaps it may help you.

A quick and dirty solution which comes to my mind using it, were to have a function which connects to the channel, sends the message and leaves the channel again.

A much saner way, were to start a GenServer (or similar) with your app, which does connect to the channel and gets a message from quantum and on this message it gets translated into the channel message and then send. But I’m not quite sure, how GenServer-messages and Channel-messages behave alltogether.

1 Like

Thanks for the suggestion NobbZ

Someone on Slack linked http://stackoverflow.com/questions/32085258/how-to-run-some-code-every-few-hours-in-phoenix-framework from which I got the following solution using a GenServer

I created a new file in /lib/myapp/update_users.ex

defmodule MyApp.UpdateUsers do
  use GenServer
  import Ecto
  import Ecto.Query, only: [from: 1, from: 2]

  def start_link do
    GenServer.start_link(__MODULE__, %{})
  end

  def init(state) do
    Process.send_after(self(), :work, 5_000) # In 5 seconds
    {:ok, state}
  end

  def handle_info(:work, state) do
    #get data here.  I also set the start_id from the state or set it to 0

    MyApp.Endpoint.broadcast("updates:activities", "activities", %{source: a.source, points: a.points, name: a.name})

    # Start the timer again
    Process.send_after(self(), :work, 5_000) # In 5 seconds

    #pass the max_id into the state so we can skip previously seen data in next iteration
    {:noreply, %{"last_id" => max_id}}
  end
end

Then added

worker(MyApp.UpdateUsers,[])

into lib/myapp.ex in the start/2 method and it seems to work a charm

2 Likes