diff --git a/include/ircd/m/m.h b/include/ircd/m/m.h index 62575253e..7b8a34040 100644 --- a/include/ircd/m/m.h +++ b/include/ircd/m/m.h @@ -66,6 +66,7 @@ namespace ircd::m #include "txn.h" #include "relates.h" #include "room/room.h" +#include "trace.h" #include "user/user.h" #include "users.h" #include "rooms.h" diff --git a/include/ircd/m/trace.h b/include/ircd/m/trace.h new file mode 100644 index 000000000..5ccdd60cd --- /dev/null +++ b/include/ircd/m/trace.h @@ -0,0 +1,23 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2022 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_M_TRACE_H + +namespace ircd::m::trace +{ + using closure = util::closure_bool + < + std::function, + const event::idx &, const uint64_t &, const m::room::message & + >; + + bool for_each(const event::idx &, const closure &); +} diff --git a/matrix/Makefile.am b/matrix/Makefile.am index 87b13b285..561549a92 100644 --- a/matrix/Makefile.am +++ b/matrix/Makefile.am @@ -179,6 +179,7 @@ libircd_matrix_la_SOURCES += rooms.cc libircd_matrix_la_SOURCES += membership.cc libircd_matrix_la_SOURCES += rooms_summary.cc libircd_matrix_la_SOURCES += sync.cc +libircd_matrix_la_SOURCES += trace.cc libircd_matrix_la_SOURCES += typing.cc libircd_matrix_la_SOURCES += users.cc libircd_matrix_la_SOURCES += users_servers.cc diff --git a/matrix/trace.cc b/matrix/trace.cc new file mode 100644 index 000000000..902c8581e --- /dev/null +++ b/matrix/trace.cc @@ -0,0 +1,81 @@ +// Matrix Construct +// +// Copyright (C) The Construct Developers, Authors & Contributors +// Copyright (C) 2016-2022 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +bool +ircd::m::trace::for_each(const event::idx &event_idx, + const closure &closure) +{ + event::fetch event + { + std::nothrow, event_idx + }; + + if(!event.valid) + return true; + + if(redacted(event)) + return true; + + room::messages messages + { + room::id{json::get<"room_id"_>(event)}, + { + json::get<"depth"_>(event), -1L + } + }; + + bool ret(true), loop; do + { + loop = false; + messages.for_each([&closure, &event, &messages, &loop, &ret] + (const room::message &msg, const int64_t &depth, const event::idx &event_idx) + { + // Ignore messages at the same depth as the initial + if(event.event_idx != event_idx && depth == json::get<"depth"_>(event)) + return true; + + // Call user; check if they want to break iteration + if(!(ret = closure(event_idx, depth, msg))) + return false; + + const auto reply_to_id + { + msg.reply_to_event() + }; + + // If this is not a reply continue to the prior message. + if(!reply_to_id) + return true; + + // If we don't have the message being replied to, break here + if(!seek(std::nothrow, event, reply_to_id)) + return false; + + // The message replied to was redacted, break here. + if(redacted(event)) + return false; + + // Jump to the message being replied to + messages = + { + room::id{json::get<"room_id"_>(event)}, + { + json::get<"depth"_>(event), -1L + } + }; + + loop = true; + return false; + }); + } + while(ret && loop); + + return ret; +} diff --git a/modules/console.cc b/modules/console.cc index 2c374e673..f55869b3f 100644 --- a/modules/console.cc +++ b/modules/console.cc @@ -8876,6 +8876,56 @@ console_cmd__event__relates(opt &out, const string_view &line) return true; } +bool +console_cmd__event__trace(opt &out, const string_view &line) +{ + const params param{line, " ", + { + "event_id", "limit" + }}; + + const m::event::id &event_id + { + param.at("event_id") + }; + + ssize_t limit + { + param.at("limit", 48L) + }; + + const auto event_idx + { + index(event_id) + }; + + m::event::fetch event; + m::trace::for_each(event_idx, [&out, &limit, &event] + (const m::event::idx &event_idx, const uint64_t &depth, const m::room::message &msg) + { + if(!seek(std::nothrow, event, event_idx)) + return true; + + json::get<"content"_>(event) = msg.source; + const m::pretty_opts opts + { + .event_idx = event_idx, + .show_origin_server_ts = false, + .show_origin_server_ts_ago = true, + .show_event_id = false, + .show_state_key = false, + .show_msgtype = false, + }; + + out + << pretty_msgline(event, opts) + << std::endl; + return --limit > 0; + }); + + return true; +} + // // eval //