add CAP LS 302 support and max-lines support for draft/multiline messages

This commit is contained in:
Luca Matei Pintilie
2026-01-13 19:20:46 +01:00
committed by mathieui
parent b250237e01
commit bf958d0e34
9 changed files with 318 additions and 21 deletions
+38 -15
View File
@@ -2,6 +2,7 @@
#include <utility>
#include <xmpp/biboumi_component.hpp>
#include <network/poller.hpp>
#include <utils/chunk.hpp>
#include <utils/empty_if_fixed_server.hpp>
#include <utils/encoding.hpp>
#include <utils/tolower.hpp>
@@ -12,6 +13,7 @@
#include <utils/split.hpp>
#include <xmpp/jid.hpp>
#include <database/database.hpp>
#include <irc/parse_caps.hpp>
#include "result_set_management.hpp"
#include <algorithm>
#include <cstring>
@@ -242,9 +244,27 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body, std::
if (id.empty())
id = utils::gen_uuid();
std::vector<IrcMessage> messages;
for (const std::string& line: lines)
{
// A message with a lot of lines may need to be split, even if the
// server supports draft/multiline, as we may not be allowed to send
// that many lines all at once. As such check max-lines
// https://ircv3.net/specs/extensions/multiline#capabilities
std::vector<std::vector<std::string>> lines_chunked = {lines};
auto cap_value = irc->get_capability_value("draft/multiline");
if (cap_value) {
auto cap_value_parsed = parse_cap_keyval(cap_value.value());
if (cap_value_parsed.contains("max-lines") &&
cap_value_parsed.at("max-lines").has_value()) {
std::stringstream max_lines_string(
cap_value_parsed.at("max-lines").value());
size_t size;
max_lines_string >> size;
if (size > 0)
lines_chunked = utils::chunk(lines, size);
}
}
for (const auto &lines : lines_chunked) {
std::vector<IrcMessage> messages;
for (const std::string &line : lines) {
constexpr auto max_username_size = 10;
constexpr auto max_hostname_size = 63;
const auto line_size = 512 - 1 - ::strlen("batch=") - uuid.length() - 1 - ::strlen("draft/multiline-concat") - 1 -
@@ -264,18 +284,21 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body, std::
}
}
MessageCallback mirror_to_all_resources = [this, iid, uuid, id, nodes_to_reflect, body](const IrcClient* irc, const IrcMessage&) {
for (const auto& resource: this->resources_in_chan[iid.to_tuple()])
{
auto stanza = this->xmpp.make_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(body),
this->user_jid + "/"
+ resource, uuid, id);
for (const auto& node: nodes_to_reflect)
stanza.add_child(node);
this->xmpp.send_stanza(stanza);
}
};
irc->send_batch(uuid, "draft/multiline", {iid.get_local()}, messages, std::move(mirror_to_all_resources));
MessageCallback mirror_to_all_resources = [this, iid, uuid, id, nodes_to_reflect, body](const IrcClient* irc, const IrcMessage&) {
for (const auto& resource: this->resources_in_chan[iid.to_tuple()])
{
auto stanza = this->xmpp.make_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(body),
this->user_jid + "/"
+ resource, uuid, id);
for (const auto& node: nodes_to_reflect)
stanza.add_child(node);
this->xmpp.send_stanza(stanza);
}
};
irc->send_batch(uuid, "draft/multiline", {iid.get_local()}, messages, std::move(mirror_to_all_resources));
id = utils::gen_uuid();
uuid = utils::gen_uuid();
}
return;
}