Page MenuHomePhorge

No OneTemporary

Size
11 KB
Referenced Files
None
Subscribers
None
diff --git a/src/client/power-levels-desc.cpp b/src/client/power-levels-desc.cpp
index 486c478..42f137c 100644
--- a/src/client/power-levels-desc.cpp
+++ b/src/client/power-levels-desc.cpp
@@ -1,127 +1,137 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <event.hpp>
#include "validator.hpp"
#include "power-levels-desc.hpp"
namespace Kazv
{
static std::pair<bool, json> numericValidator(const json &j)
{
if (j.is_number()) {
return {true, json(j.template get<long long>())};
} else if (j.is_string()) {
auto str = j.template get<std::string>();
try {
std::size_t pos = 0;
auto num = std::stoll(str, &pos);
if (pos != str.size()) { // not entirely a number
return {false, json()};
}
return {true, json(num)};
} catch (const std::logic_error &) {
return {false, json()};
}
} else {
return {false, json()};
}
}
static std::pair<bool, json> eventsValidator(const json &j)
{
auto [ret, val] = numericValidator(j[1]);
return {ret, json::array({j[0], std::move(val)})};
}
static std::pair<bool, json> notificationsValidator(const json &j)
{
auto ret = json::object();
auto res = cast(ret, j, "room", numericValidator);
return {res, ret};
}
static std::pair<bool, json> usersValidator(const json &j)
{
auto [ret, val] = numericValidator(j[1]);
return {ret, json::array({j[0], std::move(val)})};
}
// https://spec.matrix.org/v1.8/client-server-api/#mroompower_levels
static const auto numericFields = immer::map<std::string, long long>{
{"ban", 50},
{"events_default", 0},
{"invite", 0},
{"kick", 50},
{"redact", 50},
{"state_default", 50},
{"users_default", 0},
};
struct PowerLevelsDesc::Private
{
Private(const Event &e)
: original(e)
, normalized(normalize(e))
{}
Event normalize(const Event &e)
{
auto content = e.content().get();
auto ret = json{
{"type", "m.power_levels"},
{"state_key", ""},
{"content", json::object()},
};
// cast numeric fields
for (const auto &[key, defaultValue] : numericFields) {
ret["content"][key] = defaultValue;
cast(ret["content"], content, key, numericValidator);
}
// cast events map
ret["content"]["events"] = json::object();
castObject(ret["content"], content, "events", eventsValidator, CastObjectStrategy::IgnoreInvalid);
// cast notifications map
cast(ret["content"], content, "notifications", notificationsValidator);
// cast users map
ret["content"]["users"] = json::object();
castObject(ret["content"], content, "users", usersValidator, CastObjectStrategy::IgnoreInvalid);
return Event(ret);
}
Event original;
Event normalized;
};
PowerLevelsDesc::PowerLevelsDesc(const Event &e)
: m_d(new Private(e))
{}
PowerLevelsDesc::~PowerLevelsDesc() = default;
KAZV_DEFINE_COPYABLE_UNIQUE_PTR(PowerLevelsDesc, m_d);
+ PowerLevels PowerLevelsDesc::powerLevelsOfUser(std::string userId) const
+ {
+ const auto content = m_d->normalized.content().get();
+ if (content["users"].contains(userId)) {
+ return content["users"][userId].template get<PowerLevels>();
+ } else {
+ return content["users_default"].template get<PowerLevels>();
+ }
+ }
+
bool PowerLevelsDesc::canSendMessage(std::string userId, std::string eventType) const
{
return true;
}
bool PowerLevelsDesc::canSendState(std::string userId, std::string eventType) const
{
return true;
}
Event PowerLevelsDesc::normalizedEvent() const
{
return m_d ? m_d->normalized : Event();
}
}
diff --git a/src/client/power-levels-desc.hpp b/src/client/power-levels-desc.hpp
index c00edeb..0003426 100644
--- a/src/client/power-levels-desc.hpp
+++ b/src/client/power-levels-desc.hpp
@@ -1,59 +1,72 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <memory>
+#include <cstdint>
#include <copy-helper.hpp>
namespace Kazv
{
class Event;
+ using PowerLevels = std::int_fast64_t;
+
/**
* Represent a m.power_levels event.
*/
class PowerLevelsDesc
{
public:
/**
* Construct a PowerLevelsDesc from an event.
*/
PowerLevelsDesc(const Event &e);
~PowerLevelsDesc();
KAZV_DECLARE_COPYABLE(PowerLevelsDesc);
+ /**
+ * Get the power levels of a user in the room.
+ *
+ * @param userId The id of the user.
+ * @return The power levels of
+ */
+ PowerLevels powerLevelsOfUser(std::string userId) const;
+
/**
* Determine whether a user can send a non-state event.
*
* @param userId The id of the user.
* @param eventType The type of the event to send.
+ * @return Whether the user can send such an event.
*/
bool canSendMessage(std::string userId, std::string eventType) const;
/**
* Determine whether a user can send a state event.
*
* @param userId The id of the user.
* @param eventType The type of the event to send.
+ * @return Whether the user can send such an event.
*/
bool canSendState(std::string userId, std::string eventType) const;
/**
* Get the normalized event of this.
*
* @return The normalized event for this power levels desc.
*/
Event normalizedEvent() const;
private:
struct Private;
std::unique_ptr<Private> m_d;
};
}
diff --git a/src/tests/client/power-levels-desc-test.cpp b/src/tests/client/power-levels-desc-test.cpp
index 614d42c..e72809d 100644
--- a/src/tests/client/power-levels-desc-test.cpp
+++ b/src/tests/client/power-levels-desc-test.cpp
@@ -1,135 +1,149 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch_test_macros.hpp>
#include <types.hpp>
#include <factory.hpp>
#include <power-levels-desc.hpp>
using namespace Kazv;
using namespace Kazv::Factory;
static auto examplePowerLevelsJson = R"({
"ban": 10,
"events": {
"m.room.message": 20
},
"events_default": 1,
"invite": 1,
"kick": 1,
"notifications": {"room": 99},
"redact": 49,
"state_default": 100,
"users": {
"@mew:example.com": 100
},
"users_default": 1
})"_json;
static auto examplePowerLevels = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(examplePowerLevelsJson)
);
TEST_CASE("Parse Power Levels", "[client][power-levels]")
{
auto powerLevels = PowerLevelsDesc(examplePowerLevels);
REQUIRE(powerLevels.normalizedEvent().content().get()
== examplePowerLevels.content().get());
SECTION("additional keys are discarded")
{
auto jsonWithAdditionalKeys = examplePowerLevelsJson;
jsonWithAdditionalKeys["moe.kazv.mxc.something-else"];
auto eventWithAdditionalKeys = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(jsonWithAdditionalKeys)
);
auto powerLevels = PowerLevelsDesc(eventWithAdditionalKeys);
REQUIRE(powerLevels.normalizedEvent().content().get()
== examplePowerLevels.content().get());
}
SECTION("additional keys in notifications are discarded")
{
auto jsonWithAdditionalKeys = examplePowerLevelsJson;
jsonWithAdditionalKeys["notifications"]["moe.kazv.mxc.something-else"];
auto eventWithAdditionalKeys = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(jsonWithAdditionalKeys)
);
auto powerLevels = PowerLevelsDesc(eventWithAdditionalKeys);
REQUIRE(powerLevels.normalizedEvent().content().get()
== examplePowerLevels.content().get());
}
SECTION("string values are converted to numbers")
{
auto jsonWithStringValues = examplePowerLevelsJson;
jsonWithStringValues["ban"] = "10";
auto eventWithStringValues = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(jsonWithStringValues)
);
auto powerLevels = PowerLevelsDesc(eventWithStringValues);
REQUIRE(powerLevels.normalizedEvent().content().get()
== examplePowerLevels.content().get());
}
SECTION("string values that cannot be converted are replaced by default values")
{
auto jsonWithStringValues = examplePowerLevelsJson;
jsonWithStringValues["ban"] = "10s";
auto expected = examplePowerLevelsJson;
expected["ban"] = 50;
auto eventWithStringValues = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(jsonWithStringValues)
);
auto powerLevels = PowerLevelsDesc(eventWithStringValues);
REQUIRE(powerLevels.normalizedEvent().content().get()
== expected);
}
SECTION("string values in events that cannot be converted are replaced by default values")
{
auto jsonWithStringValues = examplePowerLevelsJson;
jsonWithStringValues["events"]["ttt"] = "10s";
auto expected = examplePowerLevelsJson;
auto eventWithStringValues = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(jsonWithStringValues)
);
auto powerLevels = PowerLevelsDesc(eventWithStringValues);
REQUIRE(powerLevels.normalizedEvent().content().get()
== expected);
}
SECTION("string values in users that cannot be converted are replaced by default values")
{
auto jsonWithStringValues = examplePowerLevelsJson;
jsonWithStringValues["users"]["@ttt:example.com"] = "10s";
auto expected = examplePowerLevelsJson;
auto eventWithStringValues = makeEvent(
withEventType("m.power_levels")
| withStateKey("")
| withEventContent(jsonWithStringValues)
);
auto powerLevels = PowerLevelsDesc(eventWithStringValues);
REQUIRE(powerLevels.normalizedEvent().content().get()
== expected);
}
}
+
+TEST_CASE("powerLevelsOfUser()", "[client][power-levels]")
+{
+ auto powerLevels = PowerLevelsDesc(examplePowerLevels);
+ REQUIRE(powerLevels.powerLevelsOfUser("@mew:example.com") == 100);
+ REQUIRE(powerLevels.powerLevelsOfUser("@mewmew:example.com") == 1);
+
+ SECTION("default power levels object")
+ {
+ powerLevels = PowerLevelsDesc(json::object({}));
+ REQUIRE(powerLevels.powerLevelsOfUser("@mew:example.com") == 0);
+ REQUIRE(powerLevels.powerLevelsOfUser("@mewmew:example.com") == 0);
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 1:11 PM (20 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55192
Default Alt Text
(11 KB)

Event Timeline