diff --git a/lib/nulform/buffer.ex b/lib/nulform/buffer.ex new file mode 100644 index 0000000..4c84fe6 --- /dev/null +++ b/lib/nulform/buffer.ex @@ -0,0 +1,79 @@ +defmodule Nulform.Buffer do + use GenServer.Behaviour + @moduledoc """ + A buffer takes outgoing messages from the bot and sends them to the + given connection process with a delay to prevent the bot from flooding + itself out of the network. + + The algorithm is as follows (reasonable values are t = 2, m = 10): + 1. Read messages from input + 2. Insert messages into queue + 3. Set timer to be current time, if lower + 4. For each message + 4.1. Raise timer value by t + 4.2. If timer value is higher than current time + m, next send is + at current time + t, otherwise send message immediately + """ + + defrecord Data, + connection: nil, + buffer: [] :: list, + waiting: :false, + timer: 0 :: integer + + def init(pid) do + data = Data.new(connection: pid) + {:ok, data} + end + + def handle_cast(msg, data) do + data = data.buffer data.buffer ++ [msg] + + data = handle_buffer data + {:noreply, data} + end + + def handle_info(msg, data) do + case msg do + {:send_message} -> + data = send_message data + data = handle_buffer data + end + {:noreply, data} + end + + + defp handle_buffer(data) do + {_, time, _} = :erlang.now() + if data.timer < time do + data = data.timer time + end + + if (data.timer - time) <= 10 do + if not Enum.empty? data.buffer do + data = send_message data + data = data.waiting :false + end + else + if (not Enum.empty? data.buffer) and not data.waiting do + {:ok, timer} = :timer.send_after 2000, {:send_message} + data = data.waiting :true + end + end + + data + end + + defp send_message(data) do + data = data.waiting :false + + if not Enum.empty? data.buffer do + [msg | tail] = data.buffer + data = data.buffer tail + data.connection <- {:nulform, :msg_out, msg} + data = data.timer data.timer + 2 + else + data + end + end +end