Skip to content

Commit ebaee9e

Browse files
committed
Add basic support for CHATHISTORY LATEST
Just enough for Goguma to work
1 parent f515c97 commit ebaee9e

File tree

8 files changed

+200
-4
lines changed

8 files changed

+200
-4
lines changed

lib/irc/handler.ex

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -931,14 +931,28 @@ defmodule M51.IrcConn.Handler do
931931
]
932932
})
933933

934-
{"CHATHISTORY", ["LATEST", _target, _anchor | _]} ->
934+
{"CHATHISTORY", ["LATEST", target, "*", limit | _]} ->
935+
limit = String.to_integer(limit)
936+
937+
case M51.MatrixClient.ChatHistory.latest(sup_pid, target, limit) do
938+
{:ok, messages} ->
939+
send_batch.(messages, "chathistory")
940+
941+
{:error, message} ->
942+
send.(%M51.Irc.Command{
943+
command: "FAIL",
944+
params: ["CHATHISTORY", "MESSAGE_ERROR", "LATEST", message]
945+
})
946+
end
947+
948+
{"CHATHISTORY", ["LATEST", _target, _anchor, _limit | _]} ->
935949
send.(%M51.Irc.Command{
936950
command: "FAIL",
937951
params: [
938952
"CHATHISTORY",
939953
"INVALID_PARAMS",
940954
"LATEST",
941-
"CHATHISTORY LATEST is not supported yet."
955+
"CHATHISTORY LATEST with anchor is not supported yet."
942956
]
943957
})
944958

lib/irc_server.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ defmodule M51.IrcServer do
4040
end
4141

4242
defp accept(port, retries_left \\ 10) do
43-
case :gen_tcp.listen(port, [:binary, packet: :line, active: false, reuseaddr: true]) do
43+
case :gen_tcp.listen(port, [:binary, :inet6, packet: :line, active: false, reuseaddr: true]) do
4444
{:ok, server_sock} ->
4545
Logger.info("Listening on port #{port}")
4646
loop_accept(server_sock)

lib/matrix_client/chat_history.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ defmodule M51.MatrixClient.ChatHistory do
8989
end
9090
end
9191

92+
def latest(sup_pid, room_id, limit) do
93+
client = M51.IrcConn.Supervisor.matrix_client(sup_pid)
94+
95+
case M51.MatrixClient.Client.get_latest_events(
96+
client,
97+
room_id,
98+
limit
99+
) do
100+
{:ok, events} ->
101+
{:ok, process_events(sup_pid, room_id, Enum.reverse(events["chunk"]))}
102+
103+
{:error, message} ->
104+
{:error, Kernel.inspect(message)}
105+
end
106+
end
107+
92108
defp parse_anchor(anchor) do
93109
case String.split(anchor, "=", parts: 2) do
94110
["msgid", msgid] ->

lib/matrix_client/client.ex

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,35 @@ defmodule M51.MatrixClient.Client do
327327
{:reply, reply, state}
328328
end
329329

330+
@impl true
331+
def handle_call({:get_latest_events, channel, limit}, _from, state) do
332+
%M51.MatrixClient.Client{
333+
state: :connected,
334+
irc_pid: irc_pid,
335+
raw_client: raw_client
336+
} = state
337+
338+
matrix_state = M51.IrcConn.Supervisor.matrix_state(irc_pid)
339+
340+
reply =
341+
case M51.MatrixClient.State.room_from_irc_channel(matrix_state, channel) do
342+
nil ->
343+
{:error, {:room_not_found, channel}}
344+
345+
{room_id, _room} ->
346+
path =
347+
"/_matrix/client/v3/rooms/#{urlquote(room_id)}/messages?" <>
348+
URI.encode_query(%{"limit" => limit, "dir" => "b"})
349+
350+
case M51.Matrix.RawClient.get(raw_client, path) do
351+
{:ok, events} -> {:ok, events}
352+
{:error, error} -> {:error, error}
353+
end
354+
end
355+
356+
{:reply, reply, state}
357+
end
358+
330359
@impl true
331360
def handle_call({:is_valid_alias, room_id, room_alias}, _from, state) do
332361
%M51.MatrixClient.Client{
@@ -521,6 +550,15 @@ defmodule M51.MatrixClient.Client do
521550
GenServer.call(pid, {:get_event_context, channel, event_id, limit}, @timeout)
522551
end
523552

553+
@doc """
554+
Returns latest events of a room
555+
556+
https://matrix.org/docs/spec/client_server/r0.6.1#id131
557+
"""
558+
def get_latest_events(pid, channel, limit) do
559+
GenServer.call(pid, {:get_latest_events, channel, limit}, @timeout)
560+
end
561+
524562
defp urlquote(s) do
525563
M51.Matrix.Utils.urlquote(s)
526564
end

test/irc/handler_test.exs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,35 @@ defmodule M51.IrcConn.HandlerTest do
933933
assert_line("BATCH :-#{batch_id}\r\n")
934934
end
935935

936+
test "CHATHISTORY LATEST", %{handler: handler} do
937+
do_connection_registration(handler, ["message-tags"])
938+
939+
send(handler, cmd("@label=l2 CHATHISTORY LATEST #chan * 1"))
940+
{batch_id, line} = assert_open_batch()
941+
assert line == "@label=l2 BATCH +#{batch_id} :chathistory\r\n"
942+
943+
assert_line(
944+
"@batch=#{batch_id};msgid=$event5 :nick:[email protected] PRIVMSG #chan :fifth message\r\n"
945+
)
946+
947+
assert_line("BATCH :-#{batch_id}\r\n")
948+
949+
950+
send(handler, cmd("@label=l1 CHATHISTORY LATEST #chan * 2"))
951+
{batch_id, line} = assert_open_batch()
952+
assert line == "@label=l1 BATCH +#{batch_id} :chathistory\r\n"
953+
954+
assert_line(
955+
"@batch=#{batch_id};msgid=$event4 :nick:[email protected] PRIVMSG #chan :fourth message\r\n"
956+
)
957+
958+
assert_line(
959+
"@batch=#{batch_id};msgid=$event5 :nick:[email protected] PRIVMSG #chan :fifth message\r\n"
960+
)
961+
962+
assert_line("BATCH :-#{batch_id}\r\n")
963+
end
964+
936965
test "CHATHISTORY AFTER", %{handler: handler} do
937966
do_connection_registration(handler, ["message-tags"])
938967

test/matrix_client/client_test.exs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,50 @@ defmodule M51.MatrixClient.ClientTest do
500500
) == {:ok, %{"event" => "foo", "events_after" => [], "events_before" => []}}
501501
end
502502

503+
test "getting last events", %{sup_pid: sup_pid} do
504+
MockHTTPoison
505+
|> expect_login
506+
|> expect(:get, fn url, headers, _options ->
507+
assert headers == [Authorization: "Bearer t0ken"]
508+
509+
assert url ==
510+
"https://matrix.example.org/_matrix/client/v3/rooms/%21roomid%3Aexample.org/messages?dir=b&limit=5"
511+
512+
{:ok,
513+
%HTTPoison.Response{
514+
status_code: 200,
515+
body: ~s({"state": [], "chunk": []})
516+
}}
517+
end)
518+
519+
client = start_supervised!({M51.MatrixClient.Client, {sup_pid, [httpoison: MockHTTPoison]}})
520+
521+
state = M51.IrcConn.Supervisor.matrix_state(sup_pid)
522+
523+
M51.MatrixClient.State.set_room_canonical_alias(
524+
state,
525+
"!roomid:example.org",
526+
"#chan:example.org"
527+
)
528+
529+
assert M51.MatrixClient.Client.connect(
530+
client,
531+
"user",
532+
"matrix.example.org",
533+
"p4ssw0rd"
534+
) == {:ok}
535+
536+
receive do
537+
msg -> assert msg == :connected
538+
end
539+
540+
assert M51.MatrixClient.Client.get_latest_events(
541+
client,
542+
"#chan:example.org",
543+
5
544+
) == {:ok, %{"state" => [], "chunk" => []}}
545+
end
546+
503547
test "checking valid alias", %{sup_pid: sup_pid} do
504548
MockHTTPoison
505549
|> expect_login

test/matrix_client/poller_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,7 @@ defmodule M51.MatrixClient.PollerTest do
13991399
"rooms" => %{
14001400
"join" => %{
14011401
"!testid:example.org" => %{
1402-
"state" => %{"events" => state_events},
1402+
"state" => %{"events" => state_events}
14031403
}
14041404
}
14051405
}

test/test_helper.exs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,61 @@ defmodule MockMatrixClient do
224224
{:reply, {:ok, reply}, state}
225225
end
226226

227+
@impl true
228+
def handle_call({:get_latest_events, _channel, limit}, _from, state) do
229+
events =
230+
[
231+
%{
232+
"content" => %{"body" => "first message", "msgtype" => "m.text"},
233+
"event_id" => "$event1",
234+
"origin_server_ts" => 1_632_946_233_579,
235+
"sender" => "@nick:example.org",
236+
"type" => "m.room.message",
237+
"unsigned" => %{}
238+
},
239+
%{
240+
"content" => %{"body" => "second message", "msgtype" => "m.text"},
241+
"event_id" => "$event2",
242+
"origin_server_ts" => 1_632_946_233_579,
243+
"sender" => "@nick:example.org",
244+
"type" => "m.room.message",
245+
"unsigned" => %{}
246+
},
247+
%{
248+
"content" => %{"body" => "third message", "msgtype" => "m.text"},
249+
"event_id" => "$event3",
250+
"origin_server_ts" => 1_632_946_233_579,
251+
"sender" => "@nick:example.org",
252+
"type" => "m.room.message",
253+
"unsigned" => %{}
254+
},
255+
%{
256+
"content" => %{"body" => "fourth message", "msgtype" => "m.text"},
257+
"event_id" => "$event4",
258+
"origin_server_ts" => 1_632_946_233_579,
259+
"sender" => "@nick:example.org",
260+
"type" => "m.room.message",
261+
"unsigned" => %{}
262+
},
263+
%{
264+
"content" => %{"body" => "fifth message", "msgtype" => "m.text"},
265+
"event_id" => "$event5",
266+
"origin_server_ts" => 1_632_946_233_579,
267+
"sender" => "@nick:example.org",
268+
"type" => "m.room.message",
269+
"unsigned" => %{}
270+
}
271+
]
272+
# Keep the last ones
273+
|> Enum.slice(-limit..-1)
274+
# "For dir=b events will be in reverse-chronological order"
275+
|> Enum.reverse()
276+
277+
{:reply, {:ok, %{"state" => [], "chunk" => events}}, state}
278+
end
279+
280+
:w
281+
227282
@impl true
228283
def handle_call({:is_valid_alias, _room_id, "#invalidalias:example.org"}, _from, state) do
229284
{:reply, false, state}

0 commit comments

Comments
 (0)