summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/irc/account.ex2
-rw-r--r--lib/irc/connection.ex38
-rw-r--r--lib/irc/puppet_connection.ex4
-rw-r--r--lib/irc/user_track.ex21
-rw-r--r--lib/lsg/telegram.ex2
-rw-r--r--lib/lsg/telegram_room.ex6
-rw-r--r--lib/lsg_irc/correction_plugin.ex2
-rw-r--r--lib/lsg_irc/last_fm_plugin.ex5
-rw-r--r--lib/lsg_irc/link_plugin.ex4
-rw-r--r--lib/lsg_irc/link_plugin/youtube.ex4
-rw-r--r--lib/lsg_irc/outline_plugin.ex2
-rw-r--r--lib/lsg_irc/preums_plugin.ex2
-rw-r--r--lib/lsg_irc/say_plugin.ex2
-rw-r--r--lib/lsg_irc/seen_plugin.ex2
-rw-r--r--lib/lsg_irc/sms_plugin.ex2
-rw-r--r--lib/lsg_web.ex3
-rw-r--r--lib/lsg_web/components/component.ex40
-rw-r--r--lib/lsg_web/components/event_component.ex36
-rw-r--r--lib/lsg_web/components/message_component.ex10
-rw-r--r--lib/lsg_web/controllers/irc_auth_sse_controller.ex2
-rw-r--r--lib/lsg_web/endpoint.ex15
-rw-r--r--lib/lsg_web/live/chat_live.ex101
-rw-r--r--lib/lsg_web/live/chat_live.html.heex96
-rw-r--r--lib/lsg_web/router.ex9
-rw-r--r--lib/lsg_web/templates/irc/index.html.eex3
-rw-r--r--lib/lsg_web/templates/layout/app.html.eex24
-rw-r--r--lib/lsg_web/templates/layout/root.html.leex18
27 files changed, 394 insertions, 61 deletions
diff --git a/lib/irc/account.ex b/lib/irc/account.ex
index 0aa8638..c835d55 100644
--- a/lib/irc/account.ex
+++ b/lib/irc/account.ex
@@ -268,7 +268,7 @@ defmodule IRC.Account do
def irc_doc, do: @moduledoc
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "message:private", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "messages:private", [])
{:ok, nil}
end
diff --git a/lib/irc/connection.ex b/lib/irc/connection.ex
index e856114..eff5930 100644
--- a/lib/irc/connection.ex
+++ b/lib/irc/connection.ex
@@ -165,6 +165,10 @@ defmodule IRC.Connection do
end
end
+ def update_connection(connection) do
+ :dets.insert(dets(), to_tuple(connection))
+ end
+
def start_link(conn) do
GenServer.start_link(__MODULE__, [conn], name: {:global, conn.id})
end
@@ -275,12 +279,12 @@ defmodule IRC.Connection do
def handle_info({:received, text, sender, chan}, state) do
reply_fun = fn(text) -> irc_reply(state, {chan, sender}, text) end
account = IRC.Account.lookup(sender)
- message = %IRC.Message{text: text, network: network(state), account: account, sender: sender, channel: chan, replyfun: reply_fun, trigger: extract_trigger(text)}
+ message = %IRC.Message{at: NaiveDateTime.utc_now(), text: text, network: network(state), account: account, sender: sender, channel: chan, replyfun: reply_fun, trigger: extract_trigger(text)}
message = case IRC.UserTrack.messaged(message) do
:ok -> message
{:ok, message} -> message
end
- publish(message, ["message:#{chan}", "#{message.network}/#{chan}:message"])
+ publish(message, ["#{message.network}/#{chan}:messages"])
{:noreply, state}
end
@@ -293,7 +297,7 @@ defmodule IRC.Connection do
:ok -> message
{:ok, message} -> message
end
- publish(message, ["message:private"])
+ publish(message, ["messages:private", "#{message.network}/#{account.id}:messages"])
{:noreply, state}
end
@@ -336,8 +340,8 @@ defmodule IRC.Connection do
{:noreply, state}
end
- def handle_info({:quit, _reason, sender}, state) do
- IRC.UserTrack.quitted(sender)
+ def handle_info({:quit, reason, sender}, state) do
+ IRC.UserTrack.quitted(sender, reason)
{:noreply, state}
end
@@ -378,18 +382,32 @@ defmodule IRC.Connection do
def publish(pub), do: publish(pub, [])
def publish(m = %IRC.Message{trigger: nil}, keys) do
- dispatch(["message"] ++ keys, {:irc, :text, m})
+ dispatch(["messages"] ++ keys, {:irc, :text, m})
end
def publish(m = %IRC.Message{trigger: t = %IRC.Trigger{trigger: trigger}}, keys) do
dispatch(["triggers", "#{m.network}/#{m.channel}:triggers", "trigger:"<>trigger], {:irc, :trigger, trigger, m})
end
+ def publish_event(net, event = %{type: _}) when is_binary(net) do
+ event = event
+ |> Map.put(:at, NaiveDateTime.utc_now())
+ |> Map.put(:network, net)
+ dispatch("#{net}:events", {:irc, :event, event})
+ end
+ def publish_event({net, chan}, event = %{type: type}) do
+ event = event
+ |> Map.put(:at, NaiveDateTime.utc_now())
+ |> Map.put(:network, net)
+ |> Map.put(:channel, chan)
+ dispatch("#{net}/#{chan}:events", {:irc, :event, event})
+ end
+
def dispatch(keys, content, sub \\ IRC.PubSub)
def dispatch(key, content, sub) when is_binary(key), do: dispatch([key], content, sub)
def dispatch(keys, content, sub) when is_list(keys) do
- IO.puts "dispatching to #{inspect({sub,keys})} --> #{inspect content}"
+ Logger.debug("dispatch #{inspect keys} = #{inspect content}")
for key <- keys do
spawn(fn() -> Registry.dispatch(sub, key, fn h ->
for {pid, _} <- h, do: send(pid, content)
@@ -419,13 +437,15 @@ defmodule IRC.Connection do
# irc_reply(ExIRC.Client pid, {channel or nick, ExIRC.Sender}, binary | replies
# replies :: {:kick, reason} | {:kick, nick, reason} | {:mode, mode, nick}
- defp irc_reply(%{client: client, network: network}, {target, _}, text) when is_binary(text) or is_list(text) do
+ defp irc_reply(state = %{client: client, network: network}, {target, _}, text) when is_binary(text) or is_list(text) do
lines = IRC.splitlong(text)
|> Enum.map(fn(x) -> if(is_list(x), do: x, else: String.split(x, "\n")) end)
|> List.flatten()
- for line <- lines do
+ outputs = for line <- lines do
ExIRC.Client.msg(client, :privmsg, target, line)
+ {:irc, :out, %IRC.Message{network: network, channel: target, text: line, sender: %ExIRC.SenderInfo{nick: state.conn.nick}, at: NaiveDateTime.utc_now()}}
end
+ for f <- outputs, do: dispatch(["irc:outputs", "#{network}/#{target}:outputs"], f)
case :global.whereis_name({LSG.TelegramRoom, network, target}) do
pid when is_pid(pid) -> send(pid, {:raw, text})
_ -> :ok
diff --git a/lib/irc/puppet_connection.ex b/lib/irc/puppet_connection.ex
index 68f1425..8c9b218 100644
--- a/lib/irc/puppet_connection.ex
+++ b/lib/irc/puppet_connection.ex
@@ -52,6 +52,10 @@ defmodule IRC.PuppetConnection do
GenServer.cast(pid, {:send_message, 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
diff --git a/lib/irc/user_track.ex b/lib/irc/user_track.ex
index 5e1c3a3..4b1ee67 100644
--- a/lib/irc/user_track.ex
+++ b/lib/irc/user_track.ex
@@ -183,7 +183,7 @@ defmodule IRC.UserTrack do
user = if user = find_by_nick(sender.network, nick) do
%User{user | username: uname, host: host, privileges: Map.put(user.privileges || %{}, channel, privileges)}
else
- user = %User{network: sender.network, nick: nick, username: uname, host: host, privileges: %{channel => privileges}}
+ user = %User{id: IRC.UserTrack.Id.large_id, network: sender.network, nick: nick, username: uname, host: host, privileges: %{channel => privileges}}
account = IRC.Account.lookup(user).id
user = %User{user | account: account}
@@ -197,6 +197,8 @@ defmodule IRC.UserTrack do
Storage.op(fn(ets) ->
:ets.insert(ets, User.to_tuple(user))
end)
+
+ IRC.Connection.publish_event({sender.network, channel}, %{type: :join, user_id: user.id, account_id: user.account})
end
#def joined(network, channel, nick, privileges) do
@@ -235,6 +237,8 @@ defmodule IRC.UserTrack do
account = IRC.Account.lookup(user, false) || old_account
user = %User{user | nick: new_nick, account: account.id, nicks: [old_nick|user.nicks]}
Storage.insert(User.to_tuple(user))
+ channels = for {channel, _} <- user.privileges, do: channel
+ IRC.Connection.publish_event(network, %{type: :nick, user_id: user.id, account_id: account.id, nick: new_nick, old_nick: old_nick})
end
end
@@ -247,9 +251,11 @@ defmodule IRC.UserTrack do
user = %User{user | privileges: Map.put(user.privileges, channel, privs)}
Storage.insert(User.to_tuple(user))
+ IRC.Connection.publish_event({network, channel}, %{type: :privileges, user_id: user.id, account_id: user.account, added: add, removed: remove})
end
end
+ # XXX: Reason
def parted(channel, %{network: network, nick: nick}) do
parted(network, channel, nick)
end
@@ -265,16 +271,21 @@ defmodule IRC.UserTrack do
if Enum.count(privs) > 0 do
user = %User{user | privileges: privs}
Storage.insert(User.to_tuple(user))
+ IRC.Connection.publish_event({network, channel}, %{type: :part, user_id: user.id, account_id: user.account, reason: nil})
else
+ IRC.Connection.publish_event(network, %{type: :quit, user_id: user.id, account_id: user.account, reason: "Left all known channels"})
Storage.delete(user.id)
end
end
end
- def quitted(sender) do
+ def quitted(sender, reason) do
if user = find_by_nick(sender.network, sender.nick) do
if user.account do
- for({channel, _} <- user.privileges, do: IRC.Membership.touch(user.account, sender.network, channel))
+ for {channel, _} <- user.privileges do
+ IRC.Membership.touch(user.account, sender.network, channel)
+ end
+ IRC.Connection.publish_event(sender.network, %{type: :quit, user_id: user.id, account_id: user.account, reason: reason})
end
Storage.delete(user.id)
end
@@ -288,4 +299,8 @@ defmodule IRC.UserTrack do
%User{user | last_active: last_active}
end
+ defp userchans(%{privileges: privileges}) do
+ for({chan, _} <- privileges, do: chan)
+ end
+
end
diff --git a/lib/lsg/telegram.ex b/lib/lsg/telegram.ex
index e8758e3..63940dc 100644
--- a/lib/lsg/telegram.ex
+++ b/lib/lsg/telegram.ex
@@ -206,7 +206,7 @@ defmodule LSG.Telegram do
trigger: IRC.Connection.extract_trigger(trigger_text),
at: nil
}
- IRC.Connection.publish(message, ["message:private", "message:telegram"])
+ IRC.Connection.publish(message, ["messages:private", "messages:telegram", "telegram/#{account.id}:messages"])
message
end
diff --git a/lib/lsg/telegram_room.ex b/lib/lsg/telegram_room.ex
index 9504cd4..1eeec8f 100644
--- a/lib/lsg/telegram_room.ex
+++ b/lib/lsg/telegram_room.ex
@@ -12,7 +12,7 @@ defmodule LSG.TelegramRoom do
case IRC.Connection.get_network(net, chan) do
%IRC.Connection{} ->
:global.register_name({__MODULE__, net, chan}, self())
- {:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:message", plugin: __MODULE__)
+ {:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:messages", plugin: __MODULE__)
{:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:triggers", plugin: __MODULE__)
err ->
Logger.warn("Did not found telegram match for #{id} \"#{chat["title"]}\"")
@@ -44,6 +44,10 @@ defmodule LSG.TelegramRoom do
{:ok, state}
end
+ def handle_info({:irc, _, _, message}, state) do
+ handle_info({:irc, nil, message}, state)
+ end
+
def handle_info({:irc, _, %IRC.Message{sender: %{nick: nick}, text: text}}, state) do
LSG.Telegram.send_message(state.id, "<#{nick}> #{text}")
{:ok, state}
diff --git a/lib/lsg_irc/correction_plugin.ex b/lib/lsg_irc/correction_plugin.ex
index f370cf8..a77c4a2 100644
--- a/lib/lsg_irc/correction_plugin.ex
+++ b/lib/lsg_irc/correction_plugin.ex
@@ -11,7 +11,7 @@ defmodule LSG.IRC.CorrectionPlugin do
end
def init(_) do
- {:ok, _} = Registry.register(IRC.PubSub, "message", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(IRC.PubSub, "messages", [plugin: __MODULE__])
{:ok, _} = Registry.register(IRC.PubSub, "triggers", [plugin: __MODULE__])
{:ok, %{}}
end
diff --git a/lib/lsg_irc/last_fm_plugin.ex b/lib/lsg_irc/last_fm_plugin.ex
index 04f887c..0c6b8a6 100644
--- a/lib/lsg_irc/last_fm_plugin.ex
+++ b/lib/lsg_irc/last_fm_plugin.ex
@@ -98,8 +98,7 @@ defmodule LSG.IRC.LastFmPlugin do
username = case :dets.lookup(state.dets, id_or_user) do
[{_, username}] -> username
- _ ->
- id_or_user
+ _ -> id_or_user
end
case now_playing(username) do
@@ -108,7 +107,7 @@ defmodule LSG.IRC.LastFmPlugin do
{:ok, map} when is_map(map) ->
track = fetch_track(username, map)
text = format_now_playing(map, track)
- user = if account = IRC.Account.get(username) do
+ user = if account = IRC.Account.get(id_or_user) do
user = IRC.UserTrack.find_by_account(message.network, account)
if(user, do: user.nick, else: account.name)
else
diff --git a/lib/lsg_irc/link_plugin.ex b/lib/lsg_irc/link_plugin.ex
index 0c785a0..3d657ad 100644
--- a/lib/lsg_irc/link_plugin.ex
+++ b/lib/lsg_irc/link_plugin.ex
@@ -52,8 +52,8 @@ defmodule LSG.IRC.LinkPlugin do
defstruct [:client]
def init([]) do
- {:ok, _} = Registry.register(IRC.PubSub, "message", [plugin: __MODULE__])
- #{:ok, _} = Registry.register(IRC.PubSub, "message:telegram", [plugin: __MODULE__])
+ {:ok, _} = Registry.register(IRC.PubSub, "messages", [plugin: __MODULE__])
+ #{:ok, _} = Registry.register(IRC.PubSub, "messages:telegram", [plugin: __MODULE__])
Logger.info("Link handler started")
{:ok, %__MODULE__{}}
end
diff --git a/lib/lsg_irc/link_plugin/youtube.ex b/lib/lsg_irc/link_plugin/youtube.ex
index fd0f1b4..536cab6 100644
--- a/lib/lsg_irc/link_plugin/youtube.ex
+++ b/lib/lsg_irc/link_plugin/youtube.ex
@@ -54,8 +54,8 @@ defmodule LSG.IRC.LinkPlugin.YouTube do
|> Timex.format("{relative}", :relative)
|> elem(1)
- line = if Keyword.get(opts, :invidious, "yewtu.be") do
- ["-> https://#{}host}/watch?v=#{video_id}"]
+ line = if host = Keyword.get(opts, :invidious, "yewtu.be") do
+ ["-> https://#{host}/watch?v=#{video_id}"]
else
[]
end
diff --git a/lib/lsg_irc/outline_plugin.ex b/lib/lsg_irc/outline_plugin.ex
index d28d0d0..47fa6fa 100644
--- a/lib/lsg_irc/outline_plugin.ex
+++ b/lib/lsg_irc/outline_plugin.ex
@@ -21,7 +21,7 @@ defmodule LSG.IRC.OutlinePlugin do
def init([]) do
regopts = [plugin: __MODULE__]
{:ok, _} = Registry.register(IRC.PubSub, "trigger:outline", regopts)
- {:ok, _} = Registry.register(IRC.PubSub, "message", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "messages", regopts)
file = Path.join(LSG.data_path, "/outline.txt")
hosts = case File.read(file) do
{:error, :enoent} ->
diff --git a/lib/lsg_irc/preums_plugin.ex b/lib/lsg_irc/preums_plugin.ex
index 7bb2c78..68257f0 100644
--- a/lib/lsg_irc/preums_plugin.ex
+++ b/lib/lsg_irc/preums_plugin.ex
@@ -87,7 +87,7 @@ defmodule LSG.IRC.PreumsPlugin do
def init([]) do
regopts = [plugin: __MODULE__]
{:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
- {:ok, _} = Registry.register(IRC.PubSub, "message", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "messages", regopts)
{:ok, _} = Registry.register(IRC.PubSub, "triggers", regopts)
{:ok, dets} = :dets.open_file(dets(), [{:repair, :force}])
Util.ets_mutate_select_each(:dets, dets, [{:"$1", [], [:"$1"]}], fn(table, obj) ->
diff --git a/lib/lsg_irc/say_plugin.ex b/lib/lsg_irc/say_plugin.ex
index 085ca92..8e93ec2 100644
--- a/lib/lsg_irc/say_plugin.ex
+++ b/lib/lsg_irc/say_plugin.ex
@@ -21,7 +21,7 @@ defmodule LSG.IRC.SayPlugin do
regopts = [type: __MODULE__]
{:ok, _} = Registry.register(IRC.PubSub, "trigger:say", regopts)
{:ok, _} = Registry.register(IRC.PubSub, "trigger:asay", regopts)
- {:ok, _} = Registry.register(IRC.PubSub, "message:private", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "messages:private", regopts)
{:ok, nil}
end
diff --git a/lib/lsg_irc/seen_plugin.ex b/lib/lsg_irc/seen_plugin.ex
index f1a5473..405c372 100644
--- a/lib/lsg_irc/seen_plugin.ex
+++ b/lib/lsg_irc/seen_plugin.ex
@@ -13,7 +13,7 @@ defmodule LSG.IRC.SeenPlugin do
def init([]) do
regopts = [plugin: __MODULE__]
{:ok, _} = Registry.register(IRC.PubSub, "triggers", regopts)
- {:ok, _} = Registry.register(IRC.PubSub, "message", regopts)
+ {:ok, _} = Registry.register(IRC.PubSub, "messages", regopts)
dets_filename = (LSG.data_path() <> "/seen.dets") |> String.to_charlist()
{:ok, dets} = :dets.open_file(dets_filename, [])
{:ok, %{dets: dets}}
diff --git a/lib/lsg_irc/sms_plugin.ex b/lib/lsg_irc/sms_plugin.ex
index a37fb4e..b183f7d 100644
--- a/lib/lsg_irc/sms_plugin.ex
+++ b/lib/lsg_irc/sms_plugin.ex
@@ -44,7 +44,7 @@ defmodule LSG.IRC.SmsPlugin do
trigger: IRC.Connection.extract_trigger(trigger_text)
}
IO.puts("converted sms to message: #{inspect message}")
- IRC.Connection.publish(message, ["message:sms"])
+ IRC.Connection.publish(message, ["messages:sms"])
message
end
end
diff --git a/lib/lsg_web.ex b/lib/lsg_web.ex
index 113d00d..eb0cdc5 100644
--- a/lib/lsg_web.ex
+++ b/lib/lsg_web.ex
@@ -63,6 +63,8 @@ defmodule LSGWeb do
import LSGWeb.Router.Helpers
import LSGWeb.ErrorHelpers
import LSGWeb.Gettext
+
+ import Phoenix.LiveView.Helpers
end
end
@@ -71,6 +73,7 @@ defmodule LSGWeb do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
+ import Phoenix.LiveView.Router
end
end
diff --git a/lib/lsg_web/components/component.ex b/lib/lsg_web/components/component.ex
new file mode 100644
index 0000000..37d75e3
--- /dev/null
+++ b/lib/lsg_web/components/component.ex
@@ -0,0 +1,40 @@
+defmodule LSGWeb.Component do
+ use Phoenix.Component
+
+ @date_time_default_format "%F %H:%M"
+ @date_time_formats %{"time-24-with-seconds" => "%H:%M:%S"}
+ def naive_date_time_utc(assigns = %{format: format}) do
+ assigns = assign(assigns, :format, Map.get(@date_time_formats, format, format))
+ ~H"""
+ <time class="component"
+ id={"time-#{:erlang.phash2(@datetime)}"}
+ phx-hook="NaiveDateTimeUTC"
+ data-time-format={get_luxon_format(@format)}
+ datetime={NaiveDateTime.to_iso8601(@datetime)}>
+ <%= Timex.format!(@datetime, @format, :strftime) %>
+ </time>
+ """
+ end
+ def naive_date_time_utc(assigns) do
+ naive_date_time_utc(assign(assigns, :format, "%F %H:%M"))
+ end
+ def get_luxon_format("%H:%M:%S"), do: "TIME_24_WITH_SECONDS"
+
+ def nick(assigns = %{self: false}) do
+ ~H"""
+ <span class="nickname" data-account-id={@account_id} data-user-id={@user_id}>
+ <%= @nick %>
+ </span>
+ """
+ end
+
+ def nick(assigns = %{self: true}) do
+ ~H"""
+ <span class="nickname self" data-account-id={@account_id} data-user-id={@user_id}>
+ You
+ </span>
+ """
+ end
+
+
+end
diff --git a/lib/lsg_web/components/event_component.ex b/lib/lsg_web/components/event_component.ex
new file mode 100644
index 0000000..3b9cd3b
--- /dev/null
+++ b/lib/lsg_web/components/event_component.ex
@@ -0,0 +1,36 @@
+defmodule LSGWeb.EventComponent do
+ use Phoenix.Component
+
+ def content(assigns = %{event: %{type: :quit}}) do
+ ~H"""
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ has quit:
+ <span class="reason"><%= @reason %></span>
+ """
+ end
+
+ def content(assigns = %{event: %{type: :part}}) do
+ ~H"""
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ has left:
+ <span class="reason"><%= @reason %></span>
+ """
+ end
+
+ def content(assigns = %{event: %{type: :nick}}) do
+ ~H"""
+ <span class="old-nick"><%= @old_nick %></span>
+ is now known as
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ """
+ end
+
+ def content(assigns = %{event: %{type: :join}}) do
+ ~H"""
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ joined
+ """
+ end
+
+
+end
diff --git a/lib/lsg_web/components/message_component.ex b/lib/lsg_web/components/message_component.ex
new file mode 100644
index 0000000..2381411
--- /dev/null
+++ b/lib/lsg_web/components/message_component.ex
@@ -0,0 +1,10 @@
+defmodule LSGWeb.MessageComponent do
+ use Phoenix.Component
+
+ def content(assigns) do
+ ~H"""
+ <div class="inline-block flex-grow cursor-default"><%= @text %></div>
+ """
+ end
+
+end
diff --git a/lib/lsg_web/controllers/irc_auth_sse_controller.ex b/lib/lsg_web/controllers/irc_auth_sse_controller.ex
index c39a866..f370d97 100644
--- a/lib/lsg_web/controllers/irc_auth_sse_controller.ex
+++ b/lib/lsg_web/controllers/irc_auth_sse_controller.ex
@@ -26,7 +26,7 @@ defmodule LSGWeb.IrcAuthSseController do
def subscribe(conn) do
:timer.send_interval(@ping_interval, {:event, :ping})
:timer.send_after(@expire_delay, {:event, :expire})
- {:ok, _} = Registry.register(IRC.PubSub, "message:private", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "messages:private", [])
conn
end
diff --git a/lib/lsg_web/endpoint.ex b/lib/lsg_web/endpoint.ex
index 37f7e84..4dd8151 100644
--- a/lib/lsg_web/endpoint.ex
+++ b/lib/lsg_web/endpoint.ex
@@ -1,8 +1,6 @@
defmodule LSGWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :lsg
- socket "/socket", LSGWeb.UserSocket, websocket: true
-
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
@@ -30,13 +28,18 @@ defmodule LSGWeb.Endpoint do
plug Plug.MethodOverride
plug Plug.Head
+ @session_options [store: :cookie,
+ key: "_lsg_key",
+ signing_salt: "+p7K3wrj"]
+
+
+ socket "/live", Phoenix.LiveView.Socket,
+ websocket: [connect_info: [session: @session_options]]
+
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
- plug Plug.Session,
- store: :cookie,
- key: "_lsg_key",
- signing_salt: "+p7K3wrj"
+ plug Plug.Session, @session_options
plug LSGWeb.Router
diff --git a/lib/lsg_web/live/chat_live.ex b/lib/lsg_web/live/chat_live.ex
new file mode 100644
index 0000000..a2b4c13
--- /dev/null
+++ b/lib/lsg_web/live/chat_live.ex
@@ -0,0 +1,101 @@
+defmodule LSGWeb.ChatLive do
+ use Phoenix.LiveView
+ use Phoenix.HTML
+ require Logger
+
+ def mount(%{"network" => network, "chan" => chan}, %{"account" => account_id}, socket) do
+ chan = LSGWeb.reformat_chan(chan)
+ connection = IRC.Connection.get_network(network, chan)
+ account = IRC.Account.get(account_id)
+ membership = IRC.Membership.of_account(IRC.Account.get("DRgpD4fLf8PDJMLp8Dtb"))
+ if account && connection && Enum.member?(membership, {connection.network, chan}) do
+ {:ok, _} = Registry.register(IRC.PubSub, "#{connection.network}:events", plugin: __MODULE__)
+ for t <- ["messages", "triggers", "outputs", "events"] do
+ {:ok, _} = Registry.register(IRC.PubSub, "#{connection.network}/#{chan}:#{t}", plugin: __MODULE__)
+ end
+
+ IRC.PuppetConnection.start(account, connection)
+
+ users = IRC.UserTrack.channel(connection.network, chan)
+ |> Enum.map(fn(tuple) -> IRC.UserTrack.User.from_tuple(tuple) end)
+ |> Enum.reduce(Map.new, fn(user = %{id: id}, acc) ->
+ Map.put(acc, id, user)
+ end)
+
+ socket = socket
+ |> assign(:connection_id, connection.id)
+ |> assign(:network, connection.network)
+ |> assign(:chan, chan)
+ |> assign(:title, "live")
+ |> assign(:channel, chan)
+ |> assign(:account_id, account.id)
+ |> assign(:backlog, [])
+ |> assign(:users, users)
+ |> assign(:counter, 0)
+
+ {:ok, socket}
+ else
+ {:ok, redirect(socket, to: "/")}
+ end
+ end
+
+ def handle_event("send", %{"message" => %{"text" => text}}, socket) do
+ account = IRC.Account.get(socket.assigns.account_id)
+ IRC.send_message_as(account, socket.assigns.network, socket.assigns.channel, text, true)
+ {:noreply, assign(socket, :counter, socket.assigns.counter + 1)}
+ end
+
+ def handle_info({:irc, :event, event = %{type: :join, user_id: id}}, socket) do
+ if user = IRC.UserTrack.lookup(id) do
+ IO.puts("JOIN USER JOIN USER JOIN USER")
+
+ socket = socket
+ |> assign(:users, Map.put(socket.assigns.users, id, user))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+
+ IO.inspect(socket.assigns.users)
+
+ {:noreply, socket}
+ else
+ IO.puts("\n\n\n?!\n\n\n?!\n\n\n\n")
+ {:noreply, socket}
+ end
+ end
+
+ def handle_info({:irc, :event, event = %{type: :nick, user_id: id, nick: nick}}, socket) do
+ socket = socket
+ |> assign(:users, update_in(socket.assigns.users, [id, :nick], nick))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+ {:noreply, socket}
+ end
+
+ def handle_info({:irc, :event, event = %{type: :quit, user_id: id}}, socket) do
+ socket = socket
+ |> assign(:users, Map.delete(socket.assigns.users, id))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+ {:noreply, socket}
+ end
+
+ def handle_info({:irc, :event, event = %{type: :part, user_id: id}}, socket) do
+ socket = socket
+ |> assign(:users, Map.delete(socket.assigns.users, id))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+ {:noreply, socket}
+ end
+
+ def handle_info({:irc, :trigger, _, message}, socket) do
+ handle_info({:irc, nil, message}, socket)
+ end
+
+ def handle_info({:irc, :text, message}, socket) do
+ socket = socket
+ |> assign(:backlog, socket.assigns.backlog ++ [message])
+ {:noreply, socket}
+ end
+
+ def handle_info(info, socket) do
+ Logger.debug("Unhandled info: #{inspect info}")
+ {:noreply, socket}
+ end
+
+end
diff --git a/lib/lsg_web/live/chat_live.html.heex b/lib/lsg_web/live/chat_live.html.heex
new file mode 100644
index 0000000..01d8b3a
--- /dev/null
+++ b/lib/lsg_web/live/chat_live.html.heex
@@ -0,0 +1,96 @@
+<div class="chat" data-turbo="false">
+
+ <div class="py-4 px-4 bg-gradient-to-b from-black to-gray-900">
+ <div class="grid grid-cols-2">
+ <h1 class="text-gray-50 tracking-tight font-extrabold text-xl">
+ <%= @network %>
+ <span class="font-bold"><%= @chan %></span>
+ </h1>
+ <div class="text-right">
+ <a href="/" class="text-gray-400"><%= @account_id %></a>
+ </div>
+ </div>
+ </div>
+
+ <div class="body">
+
+ <div class="log">
+ <%= if Enum.empty?(@backlog) do %>
+ <p class="disconnected text-center text-6xl tracking-tight font-extrabold text-red-800 w-full my-24 mx-auto overflow-y-auto">
+ Disconnected
+ </p>
+ <p class="phx-errored text-center text-6xl tracking-tight font-extrabold text-red-800 w-full my-24 mx-auto overflow-y-auto">
+ Oh no error
+ </p>
+ <% end %>
+
+ <ul class="pt-4 pl-4">
+ <%= for message <- @backlog do %>
+ <%= if is_map(message) && Map.get(message, :__struct__) == IRC.Message do %>
+ <li class="flex gap-2 place-items-center message"
+ data-account-id={message.account.id}>
+ <LSGWeb.Component.naive_date_time_utc datetime={message.at} format="time-24-with-seconds" />
+ <span class="inline-block font-bold flex-none cursor-default"><%= message.sender.nick %></span>
+ <span class="inline-block flex-grow cursor-default">
+ <LSGWeb.MessageComponent.content
+ self={message.account.id == @account_id}
+ text={message.text}
+ />
+ </span>
+ </li>
+ <% end %>
+
+ <%= if is_binary(message) do %>
+ <li class="notice"><%= message %></li>
+ <% end %>
+
+ <%= if is_map(message) && Map.get(message, :type) do %>
+ <li class="flex gap-2 place-items-center event">
+ <LSGWeb.Component.naive_date_time_utc datetime={message.at} format="time-24-with-seconds" />
+ <span class="inline-block font-bold flex-none cursor-default text-gray-700">*&nbsp;*&nbsp;*</span>
+ <span class="inline-block flex-grow cursor-default text-gray-700">
+ <LSGWeb.EventComponent.content event={message}
+ self={@users[message.user_id] && @users[message.user_id].account == @account_id}
+ user={@users[message.user_id]}
+ />
+ </span>
+ </li>
+ <% end %>
+ <% end %>
+ </ul>
+ </div>
+
+ <aside>
+ <%= for {_, user} <- @users do %>
+ <details class="user dropdown">
+ <summary><%= user.nick %></summary>
+ <div class="content">
+ <h3 class="text-xl font-bold"><%= user.nick %></h3>
+
+ <ul class="mt-4 space-y-2">
+ <li class="">User: <span class="font-bold"><%= user.username %></span></li>
+ <li class="">Name: <%= user.realname || user.nick %></li>
+ <li class="">Host: <span class="font-mono"><%= user.host %></span></li>
+ </ul>
+
+ <div class="mt-4 font-xs text-gray-300 text-center">
+ UID: <%= user.id %>
+ <br />
+ AID: <%= user.account %>
+ </div>
+ </div>
+ </details>
+ <% end %>
+ </aside>
+
+ </div>
+
+ <.form let={f} id={"form-#{@counter}"} for={:message} phx-submit="send" class="w-full px-4 pt-4">
+ <div>
+ <div class="mt-1 flex rounded-md shadow-sm border border-gray-300">
+ <%= text_input f, :text, class: "focus:ring-indigo-500 focus:border-indigo-500 block w-full border rounded-md pl-4 sm:text-sm border-gray-300", autofocus: true, 'phx-hook': "AutoFocus", autocomplete: "off", placeholder: "Don't be shy, say something…" %>
+ <%= submit content_tag(:span, "Send"), class: "-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"%>
+ </div>
+ </div>
+ </.form>
+</div>
diff --git a/lib/lsg_web/router.ex b/lib/lsg_web/router.ex
index d681ed9..5fcf0a8 100644
--- a/lib/lsg_web/router.ex
+++ b/lib/lsg_web/router.ex
@@ -5,8 +5,10 @@ defmodule LSGWeb.Router do
plug :accepts, ["html", "txt"]
plug :fetch_session
plug :fetch_flash
- #plug :protect_from_forgery
- #plug :put_secure_browser_headers
+ plug :fetch_live_flash
+ plug :protect_from_forgery
+ plug :put_secure_browser_headers
+ plug :put_root_layout, {LSGWeb.LayoutView, :root}
end
pipeline :api do
@@ -22,8 +24,6 @@ defmodule LSGWeb.Router do
scope "/", LSGWeb do
pipe_through :browser
get "/", PageController, :index
- get "/embed/widget", PageController, :widget
- get "/api", PageController, :api
get "/login/irc/:token", PageController, :token, as: :login
get "/api/untappd/callback", UntappdController, :callback, as: :untappd_callback
@@ -43,6 +43,7 @@ defmodule LSGWeb.Router do
get "/:network/:chan/alcoolog/gls.json", AlcoologController, :index_gls_json
put "/api/alcoolog/minisync/:user_id/meta/:key", AlcoologController, :minisync_put_meta
get "/:network/:chan", IrcController, :index
+ live "/:network/:chan/live", ChatLive
get "/:network/:chan/txt", IrcController, :txt
get "/:network/:chan/txt/:name", IrcController, :txt
get "/:network/:channel/preums", IrcController, :preums
diff --git a/lib/lsg_web/templates/irc/index.html.eex b/lib/lsg_web/templates/irc/index.html.eex
index a8544b3..f20f444 100644
--- a/lib/lsg_web/templates/irc/index.html.eex
+++ b/lib/lsg_web/templates/irc/index.html.eex
@@ -1,5 +1,6 @@
<div class="hidden sm:block">
- <nav class="flex flex-wrap content-center">
+ <nav class="flex flex-wrap content-center">
+ <%= link("live", to: LSGWeb.Router.Helpers.live_path(LSGWeb.Endpoint, LSGWeb.ChatLive, @network, LSGWeb.format_chan(@chan)), class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %>
<% list = for {identifier, _} <- @commands do %>
<% name = String.replace(identifier, "_", " ") %>
<%= link(name, to: "##{identifier}", class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %>
diff --git a/lib/lsg_web/templates/layout/app.html.eex b/lib/lsg_web/templates/layout/app.html.eex
index 9ad05a6..bca1555 100644
--- a/lib/lsg_web/templates/layout/app.html.eex
+++ b/lib/lsg_web/templates/layout/app.html.eex
@@ -1,18 +1,3 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <%= page_title(@conn) %>
- <meta charset="utf-8">
- <meta name="robots" content="noindex, nofollow, nosnippet">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="robots" content="noindex, noarchive, nofollow, nosnippet" />
- <title><%= Map.get(assigns, :title, "") %></title>
- <link rel="stylesheet" href="<%= static_path(@conn, "/assets/site.css") %>">
- <script src="<%= static_path(@conn, "/assets/site.js") %>" defer></script>
- </head>
-
- <body>
<div>
<div class="bg-gray-800 pb-32">
<nav class="bg-gray-800">
@@ -129,16 +114,13 @@
</header>
</div>
- <main class="-mt-32">
- <div class="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
+ <main class="-mt-32 h-full">
+ <div class="max-w-7xl h-full mx-auto pb-12 px-4 sm:px-6 lg:px-8">
<!-- Replace with your content -->
- <div class="bg-white rounded-lg shadow px-5 py-6 sm:px-6">
+ <div class="bg-white h-full rounded-lg shadow px-5 py-6 sm:px-6">
<%= @inner_content %>
</div>
<!-- /End replace -->
</div>
</main>
</div>
-
- </body>
-</html>
diff --git a/lib/lsg_web/templates/layout/root.html.leex b/lib/lsg_web/templates/layout/root.html.leex
new file mode 100644
index 0000000..6a48506
--- /dev/null
+++ b/lib/lsg_web/templates/layout/root.html.leex
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <%= page_title(@conn) %>
+ <meta charset="utf-8">
+ <meta name="robots" content="noindex, nofollow, nosnippet">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="robots" content="noindex, noarchive, nofollow, nosnippet" />
+ <title><%= Map.get(assigns, :title, "") %></title>
+ <link rel="stylesheet" href="<%= static_path(@conn, "/assets/site.css") %>">
+ <%= csrf_meta_tag() %>
+ <script src="<%= static_path(@conn, "/assets/site.js") %>" defer></script>
+ </head>
+ <body>
+ <%= @inner_content %>
+ </body>
+</html>