diff options
Diffstat (limited to 'lib/irc/puppet_connection.ex')
-rw-r--r-- | lib/irc/puppet_connection.ex | 238 |
1 files changed, 0 insertions, 238 deletions
diff --git a/lib/irc/puppet_connection.ex b/lib/irc/puppet_connection.ex deleted file mode 100644 index 91a26b3..0000000 --- a/lib/irc/puppet_connection.ex +++ /dev/null @@ -1,238 +0,0 @@ -defmodule IRC.PuppetConnection do - require Logger - @min_backoff :timer.seconds(5) - @max_backoff :timer.seconds(2*60) - @max_idle :timer.hours(12) - @env Mix.env - - 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 whereis(account = %IRC.Account{id: account_id}, connection = %IRC.Connection{id: connection_id}) do - {:global, name} = name(account_id, connection_id) - case :global.whereis_name(name) do - :undefined -> nil - pid -> pid - end - end - - def send_message(account = %IRC.Account{id: account_id}, connection = %IRC.Connection{id: connection_id}, channel, text) do - GenServer.cast(name(account_id, connection_id), {:send_message, self(), channel, text}) - end - - def start_and_send_message(account = %IRC.Account{id: account_id}, connection = %IRC.Connection{id: connection_id}, channel, text) do - {:global, name} = name(account_id, connection_id) - pid = whereis(account, connection) - pid = if !pid do - case IRC.PuppetConnection.Supervisor.start_child(account, connection) do - {:ok, pid} -> pid - {:error, {:already_started, pid}} -> pid - end - else - pid - end - GenServer.cast(pid, {:send_message, self(), channel, text}) - end - - def start(account = %IRC.Account{}, connection = %IRC.Connection{}) do - IRC.PuppetConnection.Supervisor.start_child(account, connection) - 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 - #ipv6 = if @env == :prod do - # subnet = Nola.Subnet.assign(state.account_id) - # IRC.Account.put_meta(IRC.Account.get(state.account_id), "subnet", subnet) - # ip = Pfx.host(subnet, 1) - # {:ok, ipv6} = :inet_parse.ipv6_address(to_charlist(ip)) - # System.cmd("add-ip6", [ip]) - # ipv6 - #end - - 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 - - base_opts = [ - {:nodelay, true} - ] - - #{ip, opts} = case {ipv6, :inet_res.resolve(to_charlist(conn.host), :in, :aaaa)} do - # {ipv6, {:ok, {:dns_rec, _dns_header, _query, rrs = [{:dns_rr, _, _, _, _, _, _, _, _, _} | _], _, _}}} -> - # ip = rrs - # |> Enum.map(fn({:dns_rr, _, :aaaa, :in, _, _, ipv6, _, _, _}) -> ipv6 end) - # |> Enum.shuffle() - # |> List.first() - - # opts = [ - # :inet6, - # {:ifaddr, ipv6} - # ] - # {ip, opts} - # _ -> - {ip, opts} = {to_charlist(conn.host), []} - #end - - conn_fun = if conn.tls, do: :connect_ssl!, else: :connect! - apply(ExIRC.Client, conn_fun, [client, ip, conn.port, base_opts ++ opts]) - - {:noreply, %{state | client: client}} - end - - def handle_continue(:connected, state) do - state = Enum.reduce(Enum.reverse(state.buffer), state, fn(b, state) -> - {:noreply, state} = handle_cast(b, state) - state - end) - {:noreply, %{state | buffer: []}} - end - - def handle_cast(cast = {:send_message, _pid, _channel, _text}, state = %{connected: false, buffer: buffer}) do - {:noreply, %{state | buffer: [cast | buffer]}} - end - - def handle_cast({:send_message, pid, 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) - - meta = %{puppet: true, from: pid} - account = IRC.Account.get(state.account_id) - nick = make_nick(state) - sender = %ExIRC.SenderInfo{network: state.network, nick: suffix_nick(nick), user: nick, host: "puppet."} - reply_fun = fn(text) -> - IRC.Connection.broadcast_message(state.network, channel, text) - end - message = %IRC.Message{id: FlakeId.get(), at: NaiveDateTime.utc_now(), text: text, network: state.network, account: account, sender: sender, channel: channel, replyfun: reply_fun, trigger: IRC.Connection.extract_trigger(text), meta: meta} - message = case IRC.UserTrack.messaged(message) do - :ok -> message - {:ok, message} -> message - end - IRC.Connection.publish(message, ["#{message.network}/#{channel}:messages"]) - - 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 was 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 #{inspect(server)}:#{port} #{inspect state}") - {_, backoff} = :backoff.succeed(state.backoff) - base_nick = make_nick(state) - ExIRC.Client.logon(state.client, "", suffix_nick(base_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) - # Create an UserTrack entry for the client so it's authenticated to the right account_id already. - IRC.UserTrack.connected(state.network, suffix_nick(make_nick(state)), make_nick(state), "puppet.", state.account_id, %{puppet: true}) - {: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 - - def make_nick(state) do - 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) - clean_nick = case String.split(base_nick, ":", parts: 2) do - ["@"<>nick, _] -> nick - [nick] -> nick - end - clean_nick - end - - if Mix.env == :dev do - def suffix_nick(nick), do: "#{nick}[d]" - else - def suffix_nick(nick), do: "#{nick}[p]" - end - -end |