Connection completely rewritten to use GenServer pattern
Buffer now calls Connection through :gen_server. Connection buffers all messages leading to a chicken and egg situation, but alas. Maybe I'll figure out a better solution some day.
This commit is contained in:
parent
21048ad9b9
commit
bbc2a6520b
3 changed files with 86 additions and 39 deletions
|
@ -1,13 +1,23 @@
|
|||
defmodule Nulform do
|
||||
|
||||
def run() do
|
||||
connection = spawn Nulform.Connection, :run, []
|
||||
connection <- {:nulform, :connect, 'irc.quakenet.org', 6667, "Nulform",
|
||||
nil, "nulform", "␀form"}
|
||||
{:ok, buffer} = :gen_server.start_link Nulform.Buffer, nil, []
|
||||
|
||||
IO.puts("Sent message")
|
||||
{:ok, connection} = :gen_server.start_link Nulform.Connection,
|
||||
[
|
||||
self(), buffer, "irc.quakenet.org", 6667,
|
||||
"Nulform", nil, "nulform", "␀form"
|
||||
], []
|
||||
buffer <- {:nulform, :buffer, :reset_connection, connection}
|
||||
:gen_server.cast connection, :connect
|
||||
|
||||
listen_loop
|
||||
end
|
||||
|
||||
def listen_loop() do
|
||||
receive do
|
||||
end
|
||||
listen_loop
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
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.
|
||||
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
|
||||
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
|
||||
"""
|
||||
|
||||
@t 2
|
||||
|
@ -41,6 +41,9 @@ defmodule Nulform.Buffer do
|
|||
{:send_message} ->
|
||||
data = send_message data
|
||||
data = handle_buffer data
|
||||
|
||||
{:nulform, :buffer, :reset_connection, pid} ->
|
||||
data = data.connection pid
|
||||
end
|
||||
{:noreply, data}
|
||||
end
|
||||
|
@ -73,7 +76,7 @@ defmodule Nulform.Buffer do
|
|||
if not Enum.empty? data.buffer do
|
||||
[msg | tail] = data.buffer
|
||||
data = data.buffer tail
|
||||
data.connection <- {:nulform, :msg_out, msg}
|
||||
:gen_server.cast data.connection, msg
|
||||
data = data.timer data.timer + @t
|
||||
else
|
||||
data
|
||||
|
|
|
@ -6,30 +6,55 @@ defmodule Nulform.Connection do
|
|||
Connection automatically handles using an altnick (but only one) and
|
||||
responding to pings.
|
||||
"""
|
||||
defrecord Data,
|
||||
handler: nil, # Pid
|
||||
buffer: nil, # Pid
|
||||
host: "" :: binary,
|
||||
port: 0 :: number,
|
||||
nick: "" :: binary,
|
||||
altnick: "" :: binary,
|
||||
username: "" :: binary,
|
||||
realname: "" :: binary,
|
||||
sock: nil
|
||||
|
||||
def run() do
|
||||
run nil, nil, nil, nil, nil, nil, nil, nil
|
||||
|
||||
def init([handler, buffer, host, port, nick, altnick, username, realname]) do
|
||||
data = Data.new handler: handler, buffer: buffer, host: host,
|
||||
port: port, nick: nick, altnick: altnick,
|
||||
username: username, realname: realname
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
|
||||
def run(sock, handler, host, port, nick, altnick, username, realname) do
|
||||
receive do
|
||||
{:nulform, :connect, host, port, nick, altnick, username, realname} ->
|
||||
{:ok, sock} = connect host, port
|
||||
def handle_cast(:connect, data) do
|
||||
{:ok, sock} = connect binary_to_list(data.host), data.port
|
||||
data = data.sock sock
|
||||
|
||||
# TODO: Do we need to delay here on some networks?
|
||||
send sock, "NICK " <> nick
|
||||
send sock, "USER " <> username <> " 8 * :" <> realname
|
||||
send_connect_info data
|
||||
{:noreply, data}
|
||||
end
|
||||
|
||||
{:nulform, :set_parent, handler} ->
|
||||
nil
|
||||
def handle_cast({:connect, sock}, data) do
|
||||
data = data.sock sock
|
||||
send_connect_info data
|
||||
|
||||
{:nulform, :msg_out, msg} ->
|
||||
send sock, msg
|
||||
{:noreply, data}
|
||||
end
|
||||
|
||||
{:tcp, sock, msg} ->
|
||||
def handle_cast(msg, data) when is_binary msg do
|
||||
send_raw data.sock, msg
|
||||
{:noreply, data}
|
||||
end
|
||||
|
||||
def handle_info(msg, data) do
|
||||
case msg do
|
||||
{:nulform, :connection, :reset_buffer, pid} ->
|
||||
data = data.buffer pid
|
||||
|
||||
{:tcp, _, msg} ->
|
||||
stripped = String.rstrip msg
|
||||
IO.puts stripped
|
||||
tell_handler handler, stripped
|
||||
tell_handler data.handler, stripped
|
||||
|
||||
# Handle PING responses
|
||||
case String.split stripped, ":" do
|
||||
|
@ -43,7 +68,7 @@ defmodule Nulform.Connection do
|
|||
last
|
||||
end
|
||||
|
||||
send sock, "PONG :" <> concat_list.(concat_list, rest, ":")
|
||||
send data.buffer, "PONG :" <> concat_list.(concat_list, rest, ":")
|
||||
|
||||
_ ->
|
||||
end
|
||||
|
@ -54,16 +79,14 @@ defmodule Nulform.Connection do
|
|||
# TODO: New altnick is predictable, no seeding
|
||||
case String.split stripped do
|
||||
[_, "433" | _] ->
|
||||
send sock, "NICK " <> altnick
|
||||
send data.buffer, "NICK " <> data.altnick
|
||||
uniqid = to_binary :random.uniform(9999)
|
||||
altnick = String.slice(altnick, 0, 10) <> "-" <> uniqid
|
||||
data = data.altnick String.slice(data.altnick, 0, 10) <> "-" <> uniqid
|
||||
_ ->
|
||||
end
|
||||
|
||||
tuple when is_tuple(tuple) and elem(tuple, 0) == :nulform -> Nulform.Utils.hn tuple
|
||||
end
|
||||
|
||||
run sock, handler, host, port, nick, altnick, username, realname
|
||||
{:noreply, data}
|
||||
end
|
||||
|
||||
defp connect(host, port) do
|
||||
|
@ -76,7 +99,18 @@ defmodule Nulform.Connection do
|
|||
:gen_tcp.close sock
|
||||
end
|
||||
|
||||
defp send(sock, msg) do
|
||||
defp send_connect_info(data) do
|
||||
# TODO: Do we need to delay here on some networks?
|
||||
send data.buffer, "NICK " <> data.nick
|
||||
send data.buffer, "USER " <> data.username <> " 8 * :" <> data.realname
|
||||
end
|
||||
|
||||
# Send message through buffer to avoid flooding
|
||||
defp send(buffer, msg) do
|
||||
:gen_server.cast buffer, msg
|
||||
end
|
||||
|
||||
defp send_raw(sock, msg) do
|
||||
:ok = :gen_tcp.send sock, msg <> "\r\n"
|
||||
IO.puts("<- " <> msg)
|
||||
end
|
||||
|
@ -85,7 +119,7 @@ defmodule Nulform.Connection do
|
|||
handler <- {:nulform, :msg_in, msg}
|
||||
end
|
||||
|
||||
defp tell_handler(_, msg) do
|
||||
defp tell_handler(_, _) do
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue