From 8d42e6ecb38c5ed67dc4cb32c42b422aaaabf2e1 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Wed, 12 Aug 2020 15:33:04 -0700 Subject: [PATCH] Add ping watcher to Omnibot.Core Core module now pings the server and attempts to reconnect if a respective pong has not been received in a certain amount of time. Signed-off-by: Alek Ratzloff --- lib/core.ex | 66 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/lib/core.ex b/lib/core.ex index 261c970..229c431 100644 --- a/lib/core.ex +++ b/lib/core.ex @@ -1,8 +1,20 @@ defmodule Omnibot.Core do + require Logger use Omnibot.Plugin - alias Omnibot.{Config, Irc} + alias Omnibot.{Config, Irc, Util} - @default_config channels: :all + @default_config ping_every: 60, ping_after: 60, channels: :all + + @impl true + def children(_cfg) do + [{Task.Supervisor, name: Omnibot.Core.PingWatchers}] + end + + @impl true + def on_connect(irc) do + Logger.info("Starting ping watcher") + Task.Supervisor.async(Omnibot.Core.PingWatchers, fn -> ping_watcher(irc) end) + end @impl true def on_join(irc, channel, nick) do @@ -36,18 +48,27 @@ defmodule Omnibot.Core do end end + @impl true + def on_msg(irc, :connect) do + on_connect(irc) + end + @impl true def on_msg(irc, msg) do case String.upcase(msg.command) do "001" -> sync_channels(irc) - "PING" -> Irc.send_msg(irc, "PONG", msg.params) + "PING" -> + Irc.send_msg(irc, "PONG", msg.params) + update_last_ping(Util.now_unix()) + update_last_pong(Util.now_unix()) # also update pong because we ponged + "PONG" -> update_last_pong(Util.now_unix()) _ -> route_msg(irc, msg) end end @impl true def on_init(_cfg) do - %{channels: MapSet.new()} + %{channels: MapSet.new(), last_pong: Util.now_unix(), last_ping: Util.now_unix()} end defp sync_channels(irc) do @@ -71,5 +92,42 @@ defmodule Omnibot.Core do defp remove_channel(channel) do update_state(fn cfg = %{channels: channels} -> %{cfg | channels: MapSet.delete(channels, channel)} end) end + + defp last_pong() do + state().last_pong + end + + defp update_last_pong(last_pong) do + update_state(fn cfg -> %{cfg | last_pong: last_pong} end) + end + + defp last_ping() do + state().last_ping + end + + defp update_last_ping(last_ping) do + update_state(fn cfg -> %{cfg | last_ping: last_ping} end) + end + + defp ping_watcher(irc) do + since_pong = Util.now_unix() - last_pong() + since_ping = Util.now_unix() - last_ping() + ping_every = cfg(:ping_every) + ping_after = cfg(:ping_after) + cond do + # Kill IRC instance + since_pong >= (3 * ping_every) -> + Logger.error("IRC has not replied in #{3 * ping_every}") + Process.exit(irc, :ping_timeout) + + # Send ping message + since_pong >= ping_every and ping_after >= since_ping -> + Irc.send_msg(irc, "PING", "omnibot") + + true -> nil + end + Process.sleep(1000) + ping_watcher(irc) + end end