diff options
-rw-r--r-- | config/config.exs | 5 | ||||
-rw-r--r-- | lib/irc/puppet_connection.ex | 157 | ||||
-rw-r--r-- | lib/lsg/telegram_room.ex | 6 | ||||
-rw-r--r-- | lib/lsg_irc.ex | 1 | ||||
-rw-r--r-- | lib/lsg_irc/say_plugin.ex | 10 |
5 files changed, 166 insertions, 13 deletions
diff --git a/config/config.exs b/config/config.exs index ba4426d..9a3633e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -36,11 +36,6 @@ config :lsg, LSGWeb.Endpoint, pubsub: [name: LSG.PubSub, adapter: Phoenix.PubSub.PG2] -# Configures Elixir's Logger -config :logger, :console, - format: "$time $metadata[$level] $message\n", - metadata: [:request_id] - config :mime, :types, %{"text/event-stream" => ["sse"]} # THIS IS NOT USED ANYMORE! diff --git a/lib/irc/puppet_connection.ex b/lib/irc/puppet_connection.ex new file mode 100644 index 0000000..88b4f8a --- /dev/null +++ b/lib/irc/puppet_connection.ex @@ -0,0 +1,157 @@ +defmodule IRC.PuppetConnection do + require Logger + @min_backoff :timer.seconds(5) + @max_backoff :timer.seconds(2*60) + @max_idle :timer.minutes(30) + + defmodule Supervisor do + use DynamicSupervisor + + def start_link() do + DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__) + end + + def start_child(%IRC.Account{id: account_id}, %IRC.Connection{id: connection_id}) do + spec = %{id: {account_id, connection_id}, start: {IRC.PuppetConnection, :start_link, [account_id, connection_id]}, restart: :transient} + DynamicSupervisor.start_child(__MODULE__, spec) + end + + @impl true + def init(_init_arg) do + DynamicSupervisor.init( + strategy: :one_for_one, + max_restarts: 10, + max_seconds: 1 + ) + end + end + + def send_message(account = %IRC.Account{id: account_id}, connection = %IRC.Connection{id: connection_id}, channel, text) do + pid = case IRC.PuppetConnection.Supervisor.start_child(account, connection) do + {:ok, pid} -> pid + {:error, {:already_started, pid}} -> pid + end + GenServer.cast(pid, {:send_message, channel, text}) + end + + def start_link(account_id, connection_id) do + GenServer.start_link(__MODULE__, [account_id, connection_id], name: name(account_id, connection_id)) + end + + def name(account_id, connection_id) do + {:global, {PuppetConnection, account_id, connection_id}} + end + + def init([account_id, connection_id]) do + account = %IRC.Account{} = IRC.Account.get(account_id) + connection = %IRC.Connection{} = IRC.Connection.lookup(connection_id) + Logger.metadata(puppet_conn: account.id <> "@" <> connection.id) + backoff = :backoff.init(@min_backoff, @max_backoff) + |> :backoff.type(:jitter) + idle = :erlang.send_after(@max_idle, self, :idle) + {:ok, %{client: nil, backoff: backoff, idle: idle, connected: false, buffer: [], channels: [], connection_id: connection_id, account_id: account_id, connected_server: nil, connected_port: nil, network: connection.network}, {:continue, :connect}} + end + + def handle_continue(:connect, state) do + conn = IRC.Connection.lookup(state.connection_id) + client_opts = [] + |> Keyword.put(:network, conn.network) + client = if state.client && Process.alive?(state.client) do + Logger.info("Reconnecting client") + state.client + else + Logger.info("Connecting") + {:ok, client} = ExIRC.Client.start_link(debug: false) + ExIRC.Client.add_handler(client, self()) + client + end + if conn.tls do + ExIRC.Client.connect_ssl!(client, conn.host, conn.port, [])#[{:ifaddr, {45,150,150,33}}]) + else + ExIRC.Client.connect!(client, conn.host, conn.port, [])#[{:ifaddr, {45,150,150,33}}]) + end + {:noreply, %{state | client: client}} + end + + def handle_continue(:connected, state) do + state = Enum.reduce(state.buffer, state, fn(b, state) -> + {:noreply, state} = handle_cast(b, state) + state + end) + {:noreply, %{state | buffer: []}} + end + + def handle_cast(cast = {:send_message, channel, text}, state = %{connected: false, buffer: buffer}) do + {:noreply, %{state | buffer: [cast | buffer]}} + end + + def handle_cast({:send_message, channel, text}, state = %{connected: true}) do + channels = if !Enum.member?(state.channels, channel) do + ExIRC.Client.join(state.client, channel) + [channel | state.channels] + else + state.channels + end + ExIRC.Client.msg(state.client, :privmsg, channel, text) + + idle = if length(state.buffer) == 0 do + :erlang.cancel_timer(state.idle) + :erlang.send_after(@max_idle, self(), :idle) + else + state.idle + end + + {:noreply, %{state | idle: idle, channels: channels}} + end + + def handle_info(:idle, state) do + ExIRC.Client.quit(state.client, "Puppet is idle for too long") + ExIRC.Client.stop!(state.client) + {:stop, :normal, state} + end + + def handle_info(:disconnected, state) do + {delay, backoff} = :backoff.fail(state.backoff) + Logger.info("#{inspect(self())} Disconnected -- reconnecting in #{inspect delay}ms") + Process.send_after(self(), :connect, delay) + {:noreply, %{state | connected: false, backoff: backoff}} + end + + def handle_info(:connect, state) do + {:noreply, state, {:continue, :connect}} + end + + # Connection successful + def handle_info({:connected, server, port}, state) do + Logger.info("#{inspect(self())} Connected to #{server}:#{port} #{inspect state}") + {_, backoff} = :backoff.succeed(state.backoff) + account = IRC.Account.get(state.account_id) + user = IRC.UserTrack.find_by_account(state.network, account) + base_nick = if(user, do: user.nick, else: account.name) + nick = "#{base_nick}[p]" + ExIRC.Client.logon(state.client, "", nick, base_nick, "#{base_nick}'s puppet") + {:noreply, %{state | backoff: backoff, connected_server: server, connected_port: port}} + end + + # Logon successful + def handle_info(:logged_in, state) do + Logger.info("#{inspect(self())} Logged in") + {_, backoff} = :backoff.succeed(state.backoff) + {:noreply, %{state | backoff: backoff}} + end + + # ISUP + def handle_info({:isup, network}, state) do + {:noreply, %{state | network: network, connected: true}, {:continue, :connected}} + end + + # Been kicked + def handle_info({:kicked, _sender, chan, _reason}, state) do + {:noreply, %{state | channels: state.channels -- [chan]}} + end + + def handle_info(_info, state) do + {:noreply, state} + end + +end diff --git a/lib/lsg/telegram_room.ex b/lib/lsg/telegram_room.ex index 8c228e1..f973c58 100644 --- a/lib/lsg/telegram_room.ex +++ b/lib/lsg/telegram_room.ex @@ -22,10 +22,8 @@ defmodule LSG.TelegramRoom do def handle_update(%{"message" => %{"from" => %{"id" => user_id}, "text" => text}}, _token, state) do account = IRC.Account.find_meta_account("telegram-id", user_id) - user = IRC.UserTrack.find_by_account(state.net, account) - nick = if(user, do: user.nick, else: account.name) - prefix = "<#{nick}> " - IRC.Connection.broadcast_message(state.net, state.chan, "#{prefix}#{text}") + connection = IRC.Connection.get_network(state.net) + IRC.PuppetConnection.send_message(account, connection, state.chan, text) {:ok, state} end diff --git a/lib/lsg_irc.ex b/lib/lsg_irc.ex index ba0828a..c2782ad 100644 --- a/lib/lsg_irc.ex +++ b/lib/lsg_irc.ex @@ -21,6 +21,7 @@ defmodule LSG.IRC do worker(IRC.Account.AccountPlugin, []), supervisor(IRC.Plugin.Supervisor, [], [name: IRC.Plugin.Supervisor]), supervisor(IRC.Connection.Supervisor, [], [name: IRC.Connection.Supervisor]), + supervisor(IRC.PuppetConnection.Supervisor, [], [name: IRC.PuppetConnection.Supervisor]), ] end diff --git a/lib/lsg_irc/say_plugin.ex b/lib/lsg_irc/say_plugin.ex index 690d0a6..c385e77 100644 --- a/lib/lsg_irc/say_plugin.ex +++ b/lib/lsg_irc/say_plugin.ex @@ -61,10 +61,12 @@ defmodule LSG.IRC.SayPlugin do for {net, chan} <- IRC.Membership.of_account(account) do chan2 = String.replace(chan, "#", "") if (target == "#{net}/#{chan}" || target == "#{net}/#{chan2}" || target == chan || target == chan2) do - user = IRC.UserTrack.find_by_account(net, account) - nick = if(user, do: user.nick, else: account.name) - prefix = if(with_nick?, do: "<#{nick}> ", else: "") - IRC.Connection.broadcast_message(net, chan, "#{prefix}#{text}") + if with_nick? do + connection = IRC.Connection.get_network(net) + IRC.PuppetConnection.send_message(account, connection, chan, text) + else + IRC.Connection.broadcast_message(net, chan, text) + end end end end |