mirror of
https://github.com/matrix-construct/construct
synced 2024-12-25 23:14:13 +01:00
ircd:Ⓜ️:rooms: Refactor interface; split rooms::summary; split module.
This commit is contained in:
parent
f5b45f32d0
commit
84e79a64e6
13 changed files with 654 additions and 632 deletions
|
@ -16,67 +16,75 @@
|
|||
///
|
||||
namespace ircd::m::rooms
|
||||
{
|
||||
struct each_opts;
|
||||
struct opts extern const opts_default;
|
||||
|
||||
bool is_public(const room::id &);
|
||||
size_t count_public(const string_view &server = {});
|
||||
// Iterate the rooms
|
||||
bool for_each(const opts &, const room::id::closure_bool &);
|
||||
bool for_each(const room::id::closure_bool &);
|
||||
|
||||
bool for_each(const each_opts &);
|
||||
template<class... args> bool for_each(args&&...);
|
||||
|
||||
// Linkage to utils that build a publicrooms summary from room state.
|
||||
void summary_chunk(const m::room &, json::stack::object &chunk);
|
||||
json::object summary_chunk(const m::room &, const mutable_buffer &out);
|
||||
event::id::buf summary_set(const m::room::id &, const json::object &summary);
|
||||
event::id::buf summary_set(const m::room &);
|
||||
event::id::buf summary_del(const m::room &);
|
||||
std::pair<size_t, std::string> fetch_update(const net::hostport &, const string_view &since = {}, const size_t &limit = 64);
|
||||
// tools
|
||||
size_t count(const opts & = opts_default);
|
||||
bool has(const opts & = opts_default);
|
||||
}
|
||||
|
||||
/// Arguments structure to rooms::for_each(). This reduces the API surface to
|
||||
/// handle a rich set of ways to iterate over the rooms. Several convenience
|
||||
/// constructors based on common usage are provided; note that these are not
|
||||
/// the only options patterns.
|
||||
struct ircd::m::rooms::each_opts
|
||||
/// Interface to tools that build a publicrooms summary from room state.
|
||||
namespace ircd::m::rooms::summary
|
||||
{
|
||||
/// If set, seek to this room_id or lower-bound to the closest room_id.
|
||||
string_view key;
|
||||
struct fetch;
|
||||
|
||||
/// All public rooms only; key may be a server hostname or room_id.
|
||||
/// - for server hostname: iterates known rooms from that server.
|
||||
/// - for room_id: starts iteration from that (or closest) room_id
|
||||
/// which can be used as a since/pagination token.
|
||||
bool public_rooms {false};
|
||||
// observers
|
||||
bool has(const room::id &);
|
||||
void chunk(const room &, json::stack::object &chunk);
|
||||
json::object chunk(const room &, const mutable_buffer &out);
|
||||
|
||||
/// Principal closure for results; invoked synchronously during the call;
|
||||
/// for-each protocol: return true to continue, false to break.
|
||||
room::id::closure_bool closure;
|
||||
// mutators
|
||||
event::id::buf set(const room::id &, const json::object &summary);
|
||||
event::id::buf set(const room &);
|
||||
event::id::buf del(const room &);
|
||||
}
|
||||
|
||||
each_opts(room::id::closure_bool);
|
||||
each_opts(const string_view &key, room::id::closure_bool);
|
||||
each_opts() = default;
|
||||
struct ircd::m::rooms::summary::fetch
|
||||
{
|
||||
// conf
|
||||
static conf::item<size_t> limit;
|
||||
static conf::item<seconds> timeout;
|
||||
|
||||
// result
|
||||
size_t total_room_count_estimate {0};
|
||||
std::string next_batch;
|
||||
|
||||
// request
|
||||
fetch(const net::hostport &hp,
|
||||
const string_view &since = {},
|
||||
const size_t &limit = 64);
|
||||
|
||||
fetch() = default;
|
||||
};
|
||||
|
||||
inline
|
||||
ircd::m::rooms::each_opts::each_opts(room::id::closure_bool closure)
|
||||
:closure{std::move(closure)}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::m::rooms::each_opts::each_opts(const string_view &key,
|
||||
room::id::closure_bool closure)
|
||||
:key{key}
|
||||
,closure{std::move(closure)}
|
||||
{}
|
||||
|
||||
template<class... args>
|
||||
bool
|
||||
ircd::m::rooms::for_each(args&&... a)
|
||||
/// Arguments structure to rooms::for_each(). This reduces the API surface to
|
||||
/// handle a rich set of ways to iterate over the rooms.
|
||||
struct ircd::m::rooms::opts
|
||||
{
|
||||
const each_opts opts
|
||||
{
|
||||
std::forward<args>(a)...
|
||||
};
|
||||
/// A full or partial room_id can be defined; partial is only valid if
|
||||
/// lower_bound is true.
|
||||
string_view room_id;
|
||||
|
||||
return for_each(opts);
|
||||
}
|
||||
/// Set a string for the join_rule; undefined matches all. For example,
|
||||
/// if set to "join" then the iteration can list public rooms.
|
||||
string_view join_rule;
|
||||
|
||||
/// Set a string to localize query to a single server
|
||||
string_view server;
|
||||
|
||||
/// Spec search term
|
||||
string_view search_term;
|
||||
|
||||
/// Filters results to those that have a public rooms list summary
|
||||
bool summary {false};
|
||||
|
||||
/// Indicates if the interface treats the room_id specified as a lower
|
||||
/// bound rather than exact match. This means an iteration will start
|
||||
/// at the same or next key, and continue indefinitely. By false default,
|
||||
/// when a room_id is given a for_each() will have 0 or 1 iterations.
|
||||
bool lower_bound {false};
|
||||
};
|
||||
|
|
125
ircd/m.cc
125
ircd/m.cc
|
@ -204,6 +204,7 @@ ircd::m::module_names
|
|||
"m_feds",
|
||||
"m_events",
|
||||
"m_rooms",
|
||||
"m_rooms_summary",
|
||||
"m_users",
|
||||
"m_user",
|
||||
"m_user_rooms",
|
||||
|
@ -2407,130 +2408,6 @@ ircd::m::event_filter::event_filter(const mutable_buffer &buf,
|
|||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/rooms.h
|
||||
//
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::rooms::summary_del(const m::room &r)
|
||||
{
|
||||
using prototype = event::id::buf (const m::room &);
|
||||
|
||||
static mods::import<prototype> call
|
||||
{
|
||||
"m_rooms", "ircd::m::rooms::summary_del"
|
||||
};
|
||||
|
||||
return call(r);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::rooms::summary_set(const m::room &room)
|
||||
{
|
||||
if(!exists(room))
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"Cannot set a summary for room '%s' which I have no state for",
|
||||
string_view{room.room_id}
|
||||
};
|
||||
|
||||
const unique_buffer<mutable_buffer> buf
|
||||
{
|
||||
48_KiB
|
||||
};
|
||||
|
||||
const json::object summary
|
||||
{
|
||||
summary_chunk(room, buf)
|
||||
};
|
||||
|
||||
return summary_set(room.room_id, summary);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
ircd::m::rooms::summary_set(const m::room::id &room_id,
|
||||
const json::object &summary)
|
||||
{
|
||||
using prototype = event::id::buf (const m::room::id &, const json::object &);
|
||||
|
||||
static mods::import<prototype> function
|
||||
{
|
||||
"m_rooms", "ircd::m::rooms::summary_set"
|
||||
};
|
||||
|
||||
return function(room_id, summary);
|
||||
}
|
||||
|
||||
ircd::json::object
|
||||
ircd::m::rooms::summary_chunk(const m::room &room,
|
||||
const mutable_buffer &buf)
|
||||
{
|
||||
json::stack out{buf};
|
||||
{
|
||||
json::stack::object obj{out};
|
||||
summary_chunk(room, obj);
|
||||
}
|
||||
|
||||
return json::object
|
||||
{
|
||||
out.completed()
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::rooms::summary_chunk(const m::room &room,
|
||||
json::stack::object &chunk)
|
||||
{
|
||||
using prototype = void (const m::room &, json::stack::object &);
|
||||
|
||||
static mods::import<prototype> function
|
||||
{
|
||||
"m_rooms", "ircd::m::rooms::summary_chunk"
|
||||
};
|
||||
|
||||
return function(room, chunk);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::rooms::for_each(const each_opts &opts)
|
||||
{
|
||||
using prototype = bool (const each_opts &);
|
||||
|
||||
static mods::import<prototype> call
|
||||
{
|
||||
"m_rooms", "ircd::m::rooms::for_each"
|
||||
};
|
||||
|
||||
return call(opts);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::rooms::is_public(const room::id &room_id)
|
||||
{
|
||||
using prototype = bool (const room::id &);
|
||||
|
||||
static mods::import<prototype> call
|
||||
{
|
||||
"m_rooms", "ircd::m::rooms::is_public"
|
||||
};
|
||||
|
||||
return call(room_id);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::rooms::count_public(const string_view &server)
|
||||
{
|
||||
using prototype = size_t (const string_view &);
|
||||
|
||||
static mods::import<prototype> function
|
||||
{
|
||||
"m_rooms", "ircd::m::rooms::count_public"
|
||||
};
|
||||
|
||||
return function(server);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// m/user.h
|
||||
|
|
|
@ -123,6 +123,7 @@ m_ignored_user_list_la_SOURCES = m_ignored_user_list.cc
|
|||
m_breadcrumb_rooms_la_SOURCES = m_breadcrumb_rooms.cc
|
||||
m_events_la_SOURCES = m_events.cc
|
||||
m_rooms_la_SOURCES = m_rooms.cc
|
||||
m_rooms_summary_la_SOURCES = m_rooms_summary.cc
|
||||
m_room_timeline_la_SOURCES = m_room_timeline.cc
|
||||
m_room_create_la_SOURCES = m_room_create.cc
|
||||
m_room_member_la_SOURCES = m_room_member.cc
|
||||
|
@ -166,6 +167,7 @@ m_module_LTLIBRARIES = \
|
|||
m_breadcrumb_rooms.la \
|
||||
m_events.la \
|
||||
m_rooms.la \
|
||||
m_rooms_summary.la \
|
||||
m_room_timeline.la \
|
||||
m_room_create.la \
|
||||
m_room_member.la \
|
||||
|
|
|
@ -401,7 +401,7 @@ try
|
|||
// This call sends a message to the !public room to list this room in the
|
||||
// public rooms list. We set an empty summary for this room because we
|
||||
// already have its state on this server;
|
||||
m::rooms::summary_set(room.room_id, json::object{});
|
||||
m::rooms::summary::set(room.room_id, json::object{});
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
|
|
|
@ -100,11 +100,11 @@ put__list_appservice(client &client,
|
|||
case "public"_:
|
||||
// We set an empty summary for this room because
|
||||
// we already have its state on this server;
|
||||
m::rooms::summary_set(room.room_id, json::object{});
|
||||
m::rooms::summary::set(room.room_id, json::object{});
|
||||
break;
|
||||
|
||||
case "private"_:
|
||||
m::rooms::summary_del(room.room_id);
|
||||
m::rooms::summary::del(room.room_id);
|
||||
break;
|
||||
|
||||
default: throw m::UNSUPPORTED
|
||||
|
|
|
@ -98,11 +98,11 @@ put__list_room(client &client,
|
|||
case "public"_:
|
||||
// We set an empty summary for this room because
|
||||
// we already have its state on this server;
|
||||
m::rooms::summary_set(room.room_id, json::object{});
|
||||
m::rooms::summary::set(room.room_id, json::object{});
|
||||
break;
|
||||
|
||||
case "private"_:
|
||||
m::rooms::summary_del(room.room_id);
|
||||
m::rooms::summary::del(room.room_id);
|
||||
break;
|
||||
|
||||
default: throw m::UNSUPPORTED
|
||||
|
@ -153,7 +153,7 @@ get__list_room(client &client,
|
|||
|
||||
const string_view &visibility
|
||||
{
|
||||
m::rooms::is_public(room)? "public" : "private"
|
||||
m::rooms::summary::has(room_id)? "public" : "private"
|
||||
};
|
||||
|
||||
return resource::response
|
||||
|
|
|
@ -52,7 +52,7 @@ resource::response
|
|||
get__publicrooms(client &client,
|
||||
const resource::request &request)
|
||||
{
|
||||
char since_buf[256];
|
||||
char since_buf[m::room::id::buf::SIZE];
|
||||
const string_view &since
|
||||
{
|
||||
request.has("since")?
|
||||
|
@ -96,7 +96,10 @@ get__publicrooms(client &client,
|
|||
|
||||
if(server && !my_host(server)) try
|
||||
{
|
||||
m::rooms::fetch_update(server, since, limit);
|
||||
m::rooms::summary::fetch
|
||||
{
|
||||
server, since, limit
|
||||
};
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
|
@ -118,6 +121,14 @@ get__publicrooms(client &client,
|
|||
response.buf, response.flusher(), size_t(flush_hiwat)
|
||||
};
|
||||
|
||||
m::rooms::opts opts;
|
||||
opts.join_rule = "public";
|
||||
opts.summary = true;
|
||||
opts.search_term = search_term;
|
||||
opts.server = server?: my_host();
|
||||
opts.lower_bound = true;
|
||||
opts.room_id = since;
|
||||
|
||||
size_t count{0};
|
||||
m::room::id::buf prev_batch_buf;
|
||||
m::room::id::buf next_batch_buf;
|
||||
|
@ -125,42 +136,26 @@ get__publicrooms(client &client,
|
|||
{
|
||||
json::stack::member chunk_m{top, "chunk"};
|
||||
json::stack::array chunk{chunk_m};
|
||||
|
||||
//TODO: better keying for search terms
|
||||
const string_view &key
|
||||
{
|
||||
since?
|
||||
since:
|
||||
server?
|
||||
server:
|
||||
search_term?
|
||||
search_term:
|
||||
my_host()
|
||||
};
|
||||
|
||||
m::rooms::each_opts opts;
|
||||
opts.public_rooms = true;
|
||||
opts.key = key;
|
||||
opts.closure = [&](const m::room::id &room_id)
|
||||
m::rooms::for_each(opts, [&](const m::room::id &room_id)
|
||||
{
|
||||
json::stack::object obj{chunk};
|
||||
m::rooms::summary_chunk(room_id, obj);
|
||||
|
||||
m::rooms::summary::chunk(room_id, obj);
|
||||
if(!count && !empty(since))
|
||||
prev_batch_buf = room_id; //TODO: ???
|
||||
|
||||
next_batch_buf = room_id;
|
||||
return ++count < limit;
|
||||
};
|
||||
|
||||
m::rooms::for_each(opts);
|
||||
});
|
||||
}
|
||||
|
||||
// To count the total we clear the since token, otherwise the count
|
||||
// will be the remainder.
|
||||
opts.room_id = {};
|
||||
json::stack::member
|
||||
{
|
||||
top, "total_room_count_estimate", json::value
|
||||
{
|
||||
ssize_t(m::rooms::count_public(server))
|
||||
ssize_t(m::rooms::count(opts))
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ get__initialsync_local(client &client,
|
|||
|
||||
json::stack::member
|
||||
{
|
||||
out, "visibility", m::rooms::is_public(room)? "public"_sv: "private"_sv
|
||||
out, "visibility", m::rooms::summary::has(room)? "public"_sv: "private"_sv
|
||||
};
|
||||
|
||||
const m::user::room_account_data room_account_data
|
||||
|
|
|
@ -7566,11 +7566,45 @@ console_cmd__eval__file(opt &out, const string_view &line)
|
|||
bool
|
||||
console_cmd__rooms(opt &out, const string_view &line)
|
||||
{
|
||||
m::rooms::for_each([&out]
|
||||
(const m::room::id &room_id)
|
||||
const params param{line, " ",
|
||||
{
|
||||
"server", "search_term", "limit"
|
||||
}};
|
||||
|
||||
const string_view &server
|
||||
{
|
||||
param["server"] != "*"?
|
||||
param["server"]:
|
||||
string_view{}
|
||||
};
|
||||
|
||||
const string_view &search_term
|
||||
{
|
||||
param["server"] && startswith(param["server"], ':') && param["search_term"] != "*"?
|
||||
param["search_term"]:
|
||||
|
||||
param["server"] && startswith(param["server"], ':')?
|
||||
string_view{}:
|
||||
|
||||
param["server"] != "*"?
|
||||
param["server"]:
|
||||
|
||||
string_view{}
|
||||
};
|
||||
|
||||
auto limit
|
||||
{
|
||||
param.at("limit", 32L)
|
||||
};
|
||||
|
||||
m::rooms::opts opts;
|
||||
opts.server = server;
|
||||
opts.search_term = search_term;
|
||||
m::rooms::for_each(opts, [&limit, &out]
|
||||
(const m::room::id &room_id) -> bool
|
||||
{
|
||||
out << room_id << std::endl;
|
||||
return true;
|
||||
return --limit > 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -7581,12 +7615,26 @@ console_cmd__rooms__public(opt &out, const string_view &line)
|
|||
{
|
||||
const params param{line, " ",
|
||||
{
|
||||
"server|room_id_lb", "limit"
|
||||
"server", "search_term", "limit"
|
||||
}};
|
||||
|
||||
const string_view &key
|
||||
const string_view &server
|
||||
{
|
||||
param.at("server|room_id_lb", string_view{})
|
||||
param.at("server", string_view{})
|
||||
};
|
||||
|
||||
const string_view &search_term
|
||||
{
|
||||
param["server"] && startswith(param["server"], ':') && param["search_term"] != "*"?
|
||||
param["search_term"]:
|
||||
|
||||
param["server"] && startswith(param["server"], ':')?
|
||||
string_view{}:
|
||||
|
||||
param["server"] != "*"?
|
||||
param["server"]:
|
||||
|
||||
string_view{}
|
||||
};
|
||||
|
||||
auto limit
|
||||
|
@ -7594,17 +7642,18 @@ console_cmd__rooms__public(opt &out, const string_view &line)
|
|||
param.at("limit", 32L)
|
||||
};
|
||||
|
||||
m::rooms::each_opts opts;
|
||||
opts.public_rooms = true;
|
||||
opts.key = key;
|
||||
opts.closure = [&limit, &out]
|
||||
m::rooms::opts opts;
|
||||
opts.server = server;
|
||||
opts.search_term = search_term;
|
||||
opts.summary = true;
|
||||
opts.join_rule = "public";
|
||||
m::rooms::for_each(opts, [&limit, &out]
|
||||
(const m::room::id &room_id) -> bool
|
||||
{
|
||||
out << room_id << std::endl;
|
||||
return --limit > 0;
|
||||
};
|
||||
});
|
||||
|
||||
m::rooms::for_each(opts);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7626,14 +7675,14 @@ console_cmd__rooms__fetch(opt &out, const string_view &line)
|
|||
param.at("since", string_view{})
|
||||
};
|
||||
|
||||
const auto pair
|
||||
const m::rooms::summary::fetch fetch
|
||||
{
|
||||
m::rooms::fetch_update(server, since)
|
||||
server, since
|
||||
};
|
||||
|
||||
out << "done" << std::endl
|
||||
<< "total room count estimate: " << pair.first << std::endl
|
||||
<< "next batch: " << pair.second << std::endl
|
||||
<< "total room count estimate: " << fetch.total_room_count_estimate << std::endl
|
||||
<< "next batch: " << fetch.next_batch << std::endl
|
||||
;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -51,9 +51,10 @@ resource::response
|
|||
handle_get(client &client,
|
||||
const resource::request &request)
|
||||
{
|
||||
char sincebuf[m::room::id::buf::SIZE];
|
||||
const string_view &since
|
||||
{
|
||||
request.query["since"]
|
||||
url::decode(sincebuf, request.query["since"])
|
||||
};
|
||||
|
||||
if(since && !valid(m::id::ROOM, since))
|
||||
|
@ -90,6 +91,13 @@ handle_get(client &client,
|
|||
response.buf, response.flusher(), size_t(flush_hiwat)
|
||||
};
|
||||
|
||||
m::rooms::opts opts;
|
||||
opts.summary = true;
|
||||
opts.join_rule = "public";
|
||||
opts.server = my_host();
|
||||
opts.lower_bound = true;
|
||||
opts.room_id = since;
|
||||
|
||||
size_t count{0};
|
||||
m::room::id::buf prev_batch_buf;
|
||||
m::room::id::buf next_batch_buf;
|
||||
|
@ -100,30 +108,25 @@ handle_get(client &client,
|
|||
top, "chunk"
|
||||
};
|
||||
|
||||
const string_view &key
|
||||
m::rooms::for_each(opts, [&]
|
||||
(const m::room::id &room_id)
|
||||
{
|
||||
since?: my_host()
|
||||
};
|
||||
json::stack::object obj
|
||||
{
|
||||
chunk
|
||||
};
|
||||
|
||||
m::rooms::each_opts opts;
|
||||
opts.public_rooms = true;
|
||||
opts.key = key;
|
||||
opts.closure = [&](const m::room::id &room_id)
|
||||
{
|
||||
json::stack::object obj{chunk};
|
||||
m::rooms::summary_chunk(room_id, obj);
|
||||
m::rooms::summary::chunk(room_id, obj);
|
||||
next_batch_buf = room_id;
|
||||
return ++count < limit;
|
||||
};
|
||||
|
||||
m::rooms::for_each(opts);
|
||||
});
|
||||
}
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
top, "total_room_count_estimate", json::value
|
||||
{
|
||||
ssize_t(m::rooms::count_public(my_host()))
|
||||
ssize_t(m::rooms::count(opts))
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ _changed_rules(const m::event &event,
|
|||
// public rooms list. We set an empty summary for this room because we
|
||||
// already have its state on this server; saving a summary object in the
|
||||
// event sent to !public is only for rooms whose state is not synced.
|
||||
m::rooms::summary_set(room_id, json::object{});
|
||||
m::rooms::summary::set(room_id, json::object{});
|
||||
}
|
||||
|
||||
m::hookfn<m::vm::eval &>
|
||||
|
|
|
@ -8,443 +8,110 @@
|
|||
// copyright notice and this permission notice is present in all copies. The
|
||||
// full license for this software is available in the LICENSE file.
|
||||
|
||||
namespace ircd::m::rooms
|
||||
{
|
||||
static string_view make_state_key(const mutable_buffer &out, const m::room::id &);
|
||||
static m::room::id::buf unmake_state_key(const string_view &);
|
||||
|
||||
static void remote_summary_chunk(const m::room &room, json::stack::object &obj);
|
||||
static void local_summary_chunk(const m::room &room, json::stack::object &obj);
|
||||
static bool for_each_public(const string_view &room_id_lb, const room::id::closure_bool &);
|
||||
|
||||
extern conf::item<size_t> fetch_limit;
|
||||
extern conf::item<seconds> fetch_timeout;
|
||||
extern m::hookfn<vm::eval &> create_public_room;
|
||||
extern const room::id::buf public_room_id;
|
||||
}
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Matrix rooms interface; modular components"
|
||||
};
|
||||
|
||||
decltype(ircd::m::rooms::public_room_id)
|
||||
ircd::m::rooms::public_room_id
|
||||
{
|
||||
"public", ircd::my_host()
|
||||
};
|
||||
|
||||
/// Create the public rooms room during initial database bootstrap.
|
||||
/// This hooks the creation of the !ircd room which is a fundamental
|
||||
/// event indicating the database has just been created.
|
||||
decltype(ircd::m::rooms::create_public_room)
|
||||
ircd::m::rooms::create_public_room
|
||||
{
|
||||
{
|
||||
{ "_site", "vm.effect" },
|
||||
{ "room_id", "!ircd" },
|
||||
{ "type", "m.room.create" },
|
||||
},
|
||||
|
||||
[](const m::event &, m::vm::eval &)
|
||||
{
|
||||
m::create(public_room_id, m::me.user_id);
|
||||
}
|
||||
};
|
||||
decltype(ircd::m::rooms::opts_default)
|
||||
ircd::m::rooms::opts_default;
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::for_each(const each_opts &opts)
|
||||
ircd::m::rooms::has(const opts &opts)
|
||||
{
|
||||
if(opts.public_rooms)
|
||||
return for_each_public(opts.key, opts.closure);
|
||||
|
||||
const room::state state
|
||||
return !for_each(opts, []
|
||||
(const m::room::id &)
|
||||
{
|
||||
my_room
|
||||
};
|
||||
|
||||
const room::state::keys_bool keys{[&opts]
|
||||
(const string_view &room_id) -> bool
|
||||
{
|
||||
return opts.closure(room_id);
|
||||
}};
|
||||
|
||||
return state.for_each("ircd.room", opts.key, keys);
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::is_public(const room::id &room_id)
|
||||
{
|
||||
const m::room room{public_room_id};
|
||||
const m::room::state state{room};
|
||||
return state.has("ircd.room", room_id);
|
||||
// false to break; for_each() returns false
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
size_t
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::count_public(const string_view &server)
|
||||
ircd::m::rooms::count(const opts &opts)
|
||||
{
|
||||
size_t ret{0};
|
||||
const auto count{[&ret]
|
||||
(const m::room::id &room_id)
|
||||
for_each(opts, [&ret]
|
||||
(const m::room::id &)
|
||||
{
|
||||
++ret;
|
||||
return true;
|
||||
}};
|
||||
});
|
||||
|
||||
for_each_public(server, count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::rooms::for_each_public(const string_view &key,
|
||||
const room::id::closure_bool &closure)
|
||||
{
|
||||
const room::state state
|
||||
{
|
||||
public_room_id
|
||||
};
|
||||
|
||||
const bool is_room_id
|
||||
{
|
||||
m::valid(m::id::ROOM, key)
|
||||
};
|
||||
|
||||
char state_key_buf[256];
|
||||
const auto state_key
|
||||
{
|
||||
is_room_id?
|
||||
make_state_key(state_key_buf, key):
|
||||
key
|
||||
};
|
||||
|
||||
const string_view &server
|
||||
{
|
||||
is_room_id?
|
||||
room::id(key).host():
|
||||
key
|
||||
};
|
||||
|
||||
const room::state::keys_bool keys{[&closure, &server]
|
||||
(const string_view &state_key) -> bool
|
||||
{
|
||||
const auto room_id
|
||||
{
|
||||
unmake_state_key(state_key)
|
||||
};
|
||||
|
||||
if(server && room_id.host() != server)
|
||||
return false;
|
||||
|
||||
return closure(room_id);
|
||||
}};
|
||||
|
||||
return state.for_each("ircd.rooms", state_key, keys);
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary_chunk(const m::room &room,
|
||||
json::stack::object &obj)
|
||||
ircd::m::rooms::for_each(const room::id::closure_bool &closure)
|
||||
{
|
||||
return exists(room)?
|
||||
local_summary_chunk(room, obj):
|
||||
remote_summary_chunk(room, obj);
|
||||
return for_each(opts_default, closure);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::rooms::remote_summary_chunk(const m::room &room,
|
||||
json::stack::object &obj)
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::for_each(const opts &opts,
|
||||
const room::id::closure_bool &closure)
|
||||
{
|
||||
const m::room publix
|
||||
bool ret{true};
|
||||
const auto proffer{[&opts, &closure, &ret]
|
||||
(const m::room::id &room_id)
|
||||
{
|
||||
public_room_id
|
||||
};
|
||||
|
||||
char state_key_buf[256];
|
||||
const auto state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room.room_id)
|
||||
};
|
||||
|
||||
publix.get("ircd.rooms", state_key, [&obj]
|
||||
(const m::event &event)
|
||||
{
|
||||
const json::object &summary
|
||||
if(opts.room_id && !opts.lower_bound)
|
||||
{
|
||||
json::at<"content"_>(event)
|
||||
};
|
||||
ret = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for(const auto &member : summary)
|
||||
json::stack::member m{obj, member.first, member.second};
|
||||
});
|
||||
}
|
||||
if(opts.room_id)
|
||||
if(room_id < opts.room_id)
|
||||
return;
|
||||
|
||||
void
|
||||
ircd::m::rooms::local_summary_chunk(const m::room &room,
|
||||
json::stack::object &obj)
|
||||
{
|
||||
static const event::keys::include keys
|
||||
{
|
||||
"content",
|
||||
};
|
||||
if(opts.server && !opts.summary)
|
||||
if(opts.server != room_id.host())
|
||||
return;
|
||||
|
||||
const m::event::fetch::opts fopts
|
||||
{
|
||||
keys, room.fopts? room.fopts->gopts : db::gopts{}
|
||||
};
|
||||
if(opts.summary)
|
||||
if(!summary::has(room_id))
|
||||
return;
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
room, &fopts
|
||||
};
|
||||
if(opts.server && opts.summary)
|
||||
if(!room::aliases(room_id).count(opts.server))
|
||||
return;
|
||||
|
||||
// Closure over boilerplate primary room state queries; i.e matrix
|
||||
// m.room.$type events with state key "" where the content is directly
|
||||
// presented to the closure.
|
||||
const auto query{[&state]
|
||||
(const string_view &type, const string_view &content_key, const auto &closure)
|
||||
{
|
||||
state.get(std::nothrow, type, "", [&content_key, &closure]
|
||||
(const m::event &event)
|
||||
{
|
||||
const auto &content(json::get<"content"_>(event));
|
||||
const auto &value(unquote(content.get(content_key)));
|
||||
closure(value);
|
||||
});
|
||||
if(opts.join_rule)
|
||||
if(!room(room_id).join_rule(opts.join_rule))
|
||||
return;
|
||||
|
||||
ret = closure(room_id);
|
||||
}};
|
||||
|
||||
// Aliases array
|
||||
// branch for optimized public rooms searches.
|
||||
if(opts.summary)
|
||||
{
|
||||
json::stack::member aliases_m{obj, "aliases"};
|
||||
json::stack::array array{aliases_m};
|
||||
state.for_each("m.room.aliases", [&array]
|
||||
(const m::event &event)
|
||||
const room::id::buf public_room_id
|
||||
{
|
||||
const json::array aliases
|
||||
{
|
||||
json::get<"content"_>(event).get("aliases")
|
||||
};
|
||||
"!public", my_host()
|
||||
};
|
||||
|
||||
for(const string_view &alias : aliases)
|
||||
array.append(unquote(alias));
|
||||
const room::state state{public_room_id};
|
||||
return state.for_each("ircd.rooms", opts.server, [&proffer, &ret]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
room::id::buf buf;
|
||||
proffer(room::id::unswap(state_key, buf));
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
query("m.room.avatar_url", "url", [&obj]
|
||||
(const string_view &value)
|
||||
return events::for_each_in_type("m.room.create", [&proffer, &ret]
|
||||
(const string_view &type, const event::idx &event_idx)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "avatar_url", value
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.canonical_alias", "alias", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "canonical_alias", value
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.guest_access", "guest_access", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "guest_can_join", json::value
|
||||
{
|
||||
value == "can_join"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.name", "name", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "name", value
|
||||
};
|
||||
});
|
||||
|
||||
// num_join_members
|
||||
{
|
||||
const m::room::members members{room};
|
||||
json::stack::member
|
||||
{
|
||||
obj, "num_joined_members", json::value
|
||||
{
|
||||
long(members.count("join"))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// room_id
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "room_id", room.room_id
|
||||
};
|
||||
}
|
||||
|
||||
query("m.room.topic", "topic", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "topic", value
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.history_visibility", "history_visibility", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "world_readable", json::value
|
||||
{
|
||||
value == "world_readable"
|
||||
}
|
||||
};
|
||||
assert(type == "m.room.create");
|
||||
m::get(std::nothrow, event_idx, "room_id", proffer);
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
decltype(ircd::m::rooms::fetch_timeout)
|
||||
ircd::m::rooms::fetch_timeout
|
||||
{
|
||||
{ "name", "ircd.m.rooms.fetch.timeout" },
|
||||
{ "default", 45L /* matrix.org :-/ */ },
|
||||
};
|
||||
|
||||
decltype(ircd::m::rooms::fetch_limit)
|
||||
ircd::m::rooms::fetch_limit
|
||||
{
|
||||
{ "name", "ircd.m.rooms.fetch.limit" },
|
||||
{ "default", 64L },
|
||||
};
|
||||
|
||||
std::pair<size_t, std::string>
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::fetch_update(const net::hostport &hp,
|
||||
const string_view &since,
|
||||
const size_t &limit)
|
||||
{
|
||||
m::v1::public_rooms::opts opts;
|
||||
opts.limit = limit;
|
||||
opts.since = since;
|
||||
opts.include_all_networks = true;
|
||||
opts.dynamic = true;
|
||||
|
||||
// Buffer for headers and send content only; received content is dynamic
|
||||
const unique_buffer<mutable_buffer> buf
|
||||
{
|
||||
16_KiB
|
||||
};
|
||||
|
||||
m::v1::public_rooms request
|
||||
{
|
||||
hp, buf, std::move(opts)
|
||||
};
|
||||
|
||||
request.wait(seconds(fetch_timeout));
|
||||
|
||||
const auto code
|
||||
{
|
||||
request.get()
|
||||
};
|
||||
|
||||
const json::object response
|
||||
{
|
||||
request
|
||||
};
|
||||
|
||||
const json::array &chunk
|
||||
{
|
||||
response.get("chunk")
|
||||
};
|
||||
|
||||
for(const json::object &summary : chunk)
|
||||
{
|
||||
const m::room::id &room_id
|
||||
{
|
||||
unquote(summary.at("room_id"))
|
||||
};
|
||||
|
||||
summary_set(room_id, summary);
|
||||
}
|
||||
|
||||
return
|
||||
{
|
||||
response.get("total_room_count_estimate", 0UL),
|
||||
unquote(response.get("next_batch", string_view{}))
|
||||
};
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary_del(const m::room &room)
|
||||
{
|
||||
char state_key_buf[m::event::STATE_KEY_MAX_SIZE];
|
||||
const m::room::state state{public_room_id};
|
||||
const m::event::idx &event_idx
|
||||
{
|
||||
state.get(std::nothrow, "ircd.rooms", make_state_key(state_key_buf, room.room_id))
|
||||
};
|
||||
|
||||
if(!event_idx)
|
||||
return {};
|
||||
|
||||
const m::event::id::buf event_id
|
||||
{
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
return redact(public_room_id, m::me, event_id, "delisted");
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary_set(const m::room::id &room_id,
|
||||
const json::object &summary)
|
||||
{
|
||||
char state_key_buf[256];
|
||||
const auto state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room_id)
|
||||
};
|
||||
|
||||
return send(public_room_id, m::me, "ircd.rooms", state_key, summary);
|
||||
}
|
||||
|
||||
ircd::m::room::id::buf
|
||||
ircd::m::rooms::unmake_state_key(const string_view &key)
|
||||
{
|
||||
const auto s
|
||||
{
|
||||
split(key, '!')
|
||||
};
|
||||
|
||||
return m::room::id::buf
|
||||
{
|
||||
s.second, s.first
|
||||
};
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::rooms::make_state_key(const mutable_buffer &buf,
|
||||
const m::room::id &room_id)
|
||||
{
|
||||
mutable_buffer out{buf};
|
||||
consume(out, copy(out, room_id.host()));
|
||||
consume(out, copy(out, room_id.local()));
|
||||
return string_view
|
||||
{
|
||||
data(buf), data(out)
|
||||
};
|
||||
}
|
||||
|
|
421
modules/m_rooms_summary.cc
Normal file
421
modules/m_rooms_summary.cc
Normal file
|
@ -0,0 +1,421 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// 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.
|
||||
|
||||
namespace ircd::m::rooms::summary
|
||||
{
|
||||
static string_view make_state_key(const mutable_buffer &out, const m::room::id &);
|
||||
static room::id::buf unmake_state_key(const string_view &);
|
||||
static void chunk_remote(const room &, json::stack::object &o);
|
||||
static void chunk_local(const room &, json::stack::object &o);
|
||||
|
||||
extern const room::id::buf public_room_id;
|
||||
extern hookfn<vm::eval &> create_public_room;
|
||||
}
|
||||
|
||||
ircd::mapi::header
|
||||
IRCD_MODULE
|
||||
{
|
||||
"Matrix rooms summary"
|
||||
};
|
||||
|
||||
decltype(ircd::m::rooms::summary::public_room_id)
|
||||
ircd::m::rooms::summary::public_room_id
|
||||
{
|
||||
"public", ircd::my_host()
|
||||
};
|
||||
|
||||
/// Create the public rooms room during initial database bootstrap.
|
||||
/// This hooks the creation of the !ircd room which is a fundamental
|
||||
/// event indicating the database has just been created.
|
||||
decltype(ircd::m::rooms::summary::create_public_room)
|
||||
ircd::m::rooms::summary::create_public_room
|
||||
{
|
||||
{
|
||||
{ "_site", "vm.effect" },
|
||||
{ "room_id", "!ircd" },
|
||||
{ "type", "m.room.create" },
|
||||
},
|
||||
[](const m::event &, m::vm::eval &)
|
||||
{
|
||||
m::create(public_room_id, m::me.user_id);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// rooms::summary::fetch
|
||||
//
|
||||
|
||||
decltype(ircd::m::rooms::summary::fetch::timeout)
|
||||
ircd::m::rooms::summary::fetch::timeout
|
||||
{
|
||||
{ "name", "ircd.m.rooms.fetch.timeout" },
|
||||
{ "default", 45L /* matrix.org :-/ */ },
|
||||
};
|
||||
|
||||
decltype(ircd::m::rooms::summary::fetch::limit)
|
||||
ircd::m::rooms::summary::fetch::limit
|
||||
{
|
||||
{ "name", "ircd.m.rooms.fetch.limit" },
|
||||
{ "default", 64L },
|
||||
};
|
||||
|
||||
//
|
||||
// fetch::fetch
|
||||
//
|
||||
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::fetch::fetch(const net::hostport &hp,
|
||||
const string_view &since,
|
||||
const size_t &limit)
|
||||
{
|
||||
m::v1::public_rooms::opts opts;
|
||||
opts.limit = limit;
|
||||
opts.since = since;
|
||||
opts.include_all_networks = true;
|
||||
opts.dynamic = true;
|
||||
const unique_buffer<mutable_buffer> buf
|
||||
{
|
||||
// Buffer for headers and send content; received content is dynamic
|
||||
16_KiB
|
||||
};
|
||||
|
||||
m::v1::public_rooms request
|
||||
{
|
||||
hp, buf, std::move(opts)
|
||||
};
|
||||
|
||||
const auto code
|
||||
{
|
||||
request.get(seconds(timeout))
|
||||
};
|
||||
|
||||
const json::object response
|
||||
{
|
||||
request
|
||||
};
|
||||
|
||||
const json::array &chunk
|
||||
{
|
||||
response.get("chunk")
|
||||
};
|
||||
|
||||
for(const json::object &summary : chunk)
|
||||
{
|
||||
const json::string &room_id
|
||||
{
|
||||
summary.at("room_id")
|
||||
};
|
||||
|
||||
summary::set(room_id, summary);
|
||||
}
|
||||
|
||||
this->total_room_count_estimate =
|
||||
{
|
||||
response.get("total_room_count_estimate", 0UL)
|
||||
};
|
||||
|
||||
this->next_batch = json::string
|
||||
{
|
||||
response.get("next_batch", string_view{})
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// rooms::summary
|
||||
//
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::del(const m::room &room)
|
||||
{
|
||||
const m::room::state state
|
||||
{
|
||||
public_room_id
|
||||
};
|
||||
|
||||
char state_key_buf[m::event::STATE_KEY_MAX_SIZE];
|
||||
const auto state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room.room_id)
|
||||
};
|
||||
|
||||
const m::event::idx &event_idx
|
||||
{
|
||||
state.get(std::nothrow, "ircd.rooms", state_key)
|
||||
};
|
||||
|
||||
if(!event_idx)
|
||||
return {};
|
||||
|
||||
const m::event::id::buf event_id
|
||||
{
|
||||
m::event_id(event_idx)
|
||||
};
|
||||
|
||||
return redact(public_room_id, m::me, event_id, "delisted");
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::set(const m::room &room)
|
||||
{
|
||||
if(!exists(room))
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"Cannot set a summary for room '%s' which I have no state for",
|
||||
string_view{room.room_id}
|
||||
};
|
||||
|
||||
const unique_buffer<mutable_buffer> buf
|
||||
{
|
||||
48_KiB
|
||||
};
|
||||
|
||||
const json::object summary
|
||||
{
|
||||
chunk(room, buf)
|
||||
};
|
||||
|
||||
return set(room.room_id, summary);
|
||||
}
|
||||
|
||||
ircd::m::event::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::set(const m::room::id &room_id,
|
||||
const json::object &summary)
|
||||
{
|
||||
char state_key_buf[event::STATE_KEY_MAX_SIZE];
|
||||
const auto state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room_id)
|
||||
};
|
||||
|
||||
return send(public_room_id, m::me, "ircd.rooms", state_key, summary);
|
||||
}
|
||||
|
||||
ircd::json::object
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::chunk(const m::room &room,
|
||||
const mutable_buffer &buf)
|
||||
{
|
||||
json::stack out{buf};
|
||||
{
|
||||
json::stack::object obj{out};
|
||||
chunk(room, obj);
|
||||
}
|
||||
|
||||
return json::object
|
||||
{
|
||||
out.completed()
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::chunk(const m::room &room,
|
||||
json::stack::object &obj)
|
||||
{
|
||||
return exists(room)?
|
||||
chunk_local(room, obj):
|
||||
chunk_remote(room, obj);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::rooms::summary::chunk_remote(const m::room &room,
|
||||
json::stack::object &obj)
|
||||
{
|
||||
const m::room publix
|
||||
{
|
||||
public_room_id
|
||||
};
|
||||
|
||||
char state_key_buf[event::STATE_KEY_MAX_SIZE];
|
||||
const auto state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room.room_id)
|
||||
};
|
||||
|
||||
publix.get("ircd.rooms", state_key, [&obj]
|
||||
(const m::event &event)
|
||||
{
|
||||
const json::object &summary
|
||||
{
|
||||
json::at<"content"_>(event)
|
||||
};
|
||||
|
||||
for(const auto &member : summary)
|
||||
json::stack::member m
|
||||
{
|
||||
obj, member.first, member.second
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::rooms::summary::chunk_local(const m::room &room,
|
||||
json::stack::object &obj)
|
||||
{
|
||||
const m::room::state state
|
||||
{
|
||||
room
|
||||
};
|
||||
|
||||
// Closure over boilerplate primary room state queries; i.e matrix
|
||||
// m.room.$type events with state key "" where the content is directly
|
||||
// presented to the closure.
|
||||
const auto query{[&state]
|
||||
(const auto &type, const auto &content_key, const auto &closure)
|
||||
{
|
||||
const auto event_idx
|
||||
{
|
||||
state.get(std::nothrow, type, "")
|
||||
};
|
||||
|
||||
m::get(std::nothrow, event_idx, "content", [&content_key, &closure]
|
||||
(const json::object &content)
|
||||
{
|
||||
const json::string &value
|
||||
{
|
||||
content.get(content_key)
|
||||
};
|
||||
|
||||
closure(value);
|
||||
});
|
||||
}};
|
||||
|
||||
// Aliases array
|
||||
{
|
||||
json::stack::member aliases_m{obj, "aliases"};
|
||||
json::stack::array array{aliases_m};
|
||||
state.for_each("m.room.aliases", [&array]
|
||||
(const m::event &event)
|
||||
{
|
||||
const json::array aliases
|
||||
{
|
||||
json::get<"content"_>(event).get("aliases")
|
||||
};
|
||||
|
||||
for(const string_view &alias : aliases)
|
||||
array.append(unquote(alias));
|
||||
});
|
||||
}
|
||||
|
||||
query("m.room.avatar_url", "url", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "avatar_url", value
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.canonical_alias", "alias", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "canonical_alias", value
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.guest_access", "guest_access", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "guest_can_join", json::value
|
||||
{
|
||||
value == "can_join"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.name", "name", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "name", value
|
||||
};
|
||||
});
|
||||
|
||||
// num_join_members
|
||||
{
|
||||
const m::room::members members{room};
|
||||
json::stack::member
|
||||
{
|
||||
obj, "num_joined_members", json::value
|
||||
{
|
||||
long(members.count("join"))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// room_id
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "room_id", room.room_id
|
||||
};
|
||||
}
|
||||
|
||||
query("m.room.topic", "topic", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "topic", value
|
||||
};
|
||||
});
|
||||
|
||||
query("m.room.history_visibility", "history_visibility", [&obj]
|
||||
(const string_view &value)
|
||||
{
|
||||
json::stack::member
|
||||
{
|
||||
obj, "world_readable", json::value
|
||||
{
|
||||
value == "world_readable"
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::rooms::summary::has(const room::id &room_id)
|
||||
{
|
||||
const m::room::state state
|
||||
{
|
||||
public_room_id
|
||||
};
|
||||
|
||||
char state_key_buf[m::event::STATE_KEY_MAX_SIZE];
|
||||
const auto state_key
|
||||
{
|
||||
make_state_key(state_key_buf, room_id)
|
||||
};
|
||||
|
||||
return state.has("ircd.rooms", state_key);
|
||||
}
|
||||
|
||||
ircd::m::room::id::buf
|
||||
ircd::m::rooms::summary::unmake_state_key(const string_view &key)
|
||||
{
|
||||
m::room::id::buf ret;
|
||||
return m::room::id::unswap(key, ret);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::m::rooms::summary::make_state_key(const mutable_buffer &buf,
|
||||
const m::room::id &room_id)
|
||||
{
|
||||
return room_id.swap(buf);
|
||||
}
|
Loading…
Reference in a new issue