mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
refactor: Ruma upstraming, bake a little more
This commit is contained in:
@@ -20,6 +20,7 @@ unstable-msc3202 = []
|
||||
unstable-msc4203 = []
|
||||
|
||||
[dependencies]
|
||||
assign.workspace = true
|
||||
ruma.workspace = true
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -1 +1 @@
|
||||
pub mod rooms;
|
||||
pub mod rooms;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
pub mod v1 {
|
||||
use ruma::{OwnedRoomAliasId, OwnedRoomId, OwnedUserId, api::{auth_scheme::AccessToken, request, response}, metadata};
|
||||
use ruma::{
|
||||
OwnedRoomAliasId, OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: PUT,
|
||||
@@ -16,10 +20,10 @@ pub mod v1 {
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
/// Whether to ban (true) or unban (false) the room.
|
||||
/// If true, and the room is not banned, all local users will be evacuated
|
||||
/// and prevented from re-joining.
|
||||
/// If false, and the room is unbanned, local users will be allowed to re-join.
|
||||
/// No-ops are no-ops.
|
||||
/// If true, and the room is not banned, all local users will be
|
||||
/// evacuated and prevented from re-joining.
|
||||
/// If false, and the room is unbanned, local users will be allowed to
|
||||
/// re-join. No-ops are no-ops.
|
||||
pub banned: bool,
|
||||
}
|
||||
|
||||
@@ -27,24 +31,26 @@ pub mod v1 {
|
||||
pub struct Response {
|
||||
pub kicked_users: Vec<OwnedUserId>,
|
||||
pub failed_kicked_users: Vec<OwnedUserId>,
|
||||
pub local_aliases: Vec<OwnedRoomAliasId>
|
||||
pub local_aliases: Vec<OwnedRoomAliasId>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, banned: bool) -> Self {
|
||||
Self { room_id, banned }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, banned: bool) -> Self { Self { room_id, banned } }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
#[must_use]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
kicked_users: Vec<OwnedUserId>,
|
||||
failed_kicked_users: Vec<OwnedUserId>,
|
||||
local_aliases: Vec<OwnedRoomAliasId>,
|
||||
) -> Self {
|
||||
Self { kicked_users, failed_kicked_users, local_aliases }
|
||||
Self {
|
||||
kicked_users,
|
||||
failed_kicked_users,
|
||||
local_aliases,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, api::{auth_scheme::AccessToken, request, response}, metadata
|
||||
OwnedRoomId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
@@ -22,16 +24,12 @@ pub mod v1 {
|
||||
}
|
||||
|
||||
impl Request {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
#[must_use]
|
||||
pub fn new(rooms: Vec<OwnedRoomId>) -> Self {
|
||||
Self { rooms }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn new(rooms: Vec<OwnedRoomId>) -> Self { Self { rooms } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod list;
|
||||
pub mod ban;
|
||||
pub mod list;
|
||||
|
||||
@@ -3,51 +3,50 @@
|
||||
//! Check the suspension status of a target user
|
||||
|
||||
pub mod v1 {
|
||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}` ([msc])
|
||||
//!
|
||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}`
|
||||
//! ([msc])
|
||||
//!
|
||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||
|
||||
use ruma::{
|
||||
OwnedUserId, api::{auth_scheme::AccessToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedUserId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: GET,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: GET,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the get & set user suspension status endpoint.
|
||||
#[request(error = ruma::api::client::Error)]
|
||||
pub struct Request {
|
||||
/// The user to look up.
|
||||
#[ruma_api(path)]
|
||||
pub user_id: OwnedUserId,
|
||||
}
|
||||
/// Request type for the get & set user suspension status endpoint.
|
||||
#[request(error = ruma::api::error::Error)]
|
||||
pub struct Request {
|
||||
/// The user to look up.
|
||||
#[ruma_api(path)]
|
||||
pub user_id: OwnedUserId,
|
||||
}
|
||||
|
||||
/// Response type for the suspension endpoints
|
||||
#[response(error = ruma::api::client::Error)]
|
||||
pub struct Response {
|
||||
/// Whether the user is currently suspended.
|
||||
pub suspended: bool,
|
||||
}
|
||||
/// Response type for the suspension endpoints
|
||||
#[response(error = ruma::api::error::Error)]
|
||||
pub struct Response {
|
||||
/// Whether the user is currently suspended.
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given user id.
|
||||
#[must_use]
|
||||
pub fn new(user_id: OwnedUserId) -> Self {
|
||||
Self { user_id }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given user id.
|
||||
#[must_use]
|
||||
pub fn new(user_id: OwnedUserId) -> Self { Self { user_id } }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given suspension status.
|
||||
#[must_use]
|
||||
pub fn new(suspended: bool) -> Self {
|
||||
Self { suspended }
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given suspension status.
|
||||
#[must_use]
|
||||
pub fn new(suspended: bool) -> Self { Self { suspended } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub mod continuwuity;
|
||||
pub mod get_suspended;
|
||||
pub mod set_suspended;
|
||||
pub mod set_suspended;
|
||||
|
||||
@@ -3,53 +3,52 @@
|
||||
//! Set the suspension status of a target user
|
||||
|
||||
pub mod v1 {
|
||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}` ([msc])
|
||||
//!
|
||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}`
|
||||
//! ([msc])
|
||||
//!
|
||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||
|
||||
use ruma::{
|
||||
OwnedUserId, api::{auth_scheme::AccessToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedUserId,
|
||||
api::{auth_scheme::AccessToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: PUT,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: PUT,
|
||||
rate_limited: false,
|
||||
authentication: AccessToken,
|
||||
history: {
|
||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the set user suspension status endpoint.
|
||||
#[request(error = ruma::api::client::Error)]
|
||||
pub struct Request {
|
||||
/// The user to look up.
|
||||
#[ruma_api(path)]
|
||||
pub user_id: OwnedUserId,
|
||||
/// Request type for the set user suspension status endpoint.
|
||||
#[request(error = ruma::api::error::Error)]
|
||||
pub struct Request {
|
||||
/// The user to look up.
|
||||
#[ruma_api(path)]
|
||||
pub user_id: OwnedUserId,
|
||||
|
||||
pub suspended: bool,
|
||||
}
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
/// Response type for the suspension endpoints
|
||||
#[response(error = ruma::api::client::Error)]
|
||||
pub struct Response {
|
||||
/// Whether the user is currently suspended.
|
||||
pub suspended: bool,
|
||||
}
|
||||
/// Response type for the suspension endpoints
|
||||
#[response(error = ruma::api::error::Error)]
|
||||
pub struct Response {
|
||||
/// Whether the user is currently suspended.
|
||||
pub suspended: bool,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given user id.
|
||||
#[must_use]
|
||||
pub fn new(user_id: OwnedUserId, suspended: bool) -> Self {
|
||||
Self { user_id, suspended }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given user id.
|
||||
#[must_use]
|
||||
pub fn new(user_id: OwnedUserId, suspended: bool) -> Self { Self { user_id, suspended } }
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given suspension status.
|
||||
#[must_use]
|
||||
pub fn new(suspended: bool) -> Self {
|
||||
Self { suspended }
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given suspension status.
|
||||
#[must_use]
|
||||
pub fn new(suspended: bool) -> Self { Self { suspended } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod user_may_invite;
|
||||
pub mod user_may_join_room;
|
||||
pub mod user_may_join_room;
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
//! `POST /api/1/spam_check/user_may_invite`
|
||||
//!
|
||||
//! Checks that a user may invite the given user to the given room via Draupnir anti-spam
|
||||
//! Checks that a user may invite the given user to the given room via Draupnir
|
||||
//! anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/api/1/spam_check/user_may_invite",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/api/1/spam_check/user_may_invite",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_invite` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The room the invitee is being invited to
|
||||
pub room_id: OwnedRoomId,
|
||||
/// The user sending the invite
|
||||
pub inviter: OwnedUserId,
|
||||
/// The user being invited
|
||||
pub invitee: OwnedUserId,
|
||||
}
|
||||
/// Request type for the `user_may_invite` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The room the invitee is being invited to
|
||||
pub room_id: OwnedRoomId,
|
||||
/// The user sending the invite
|
||||
pub inviter: OwnedUserId,
|
||||
/// The user being invited
|
||||
pub invitee: OwnedUserId,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_invite` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
/// Response type for the `user_may_invite` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, inviter: OwnedUserId, invitee: OwnedUserId) -> Self {
|
||||
Self { room_id, inviter, invitee }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, inviter: OwnedUserId, invitee: OwnedUserId) -> Self {
|
||||
Self { room_id, inviter, invitee }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
//! `POST /api/1/spam_check/user_may_join_room`
|
||||
//!
|
||||
//! Endpoint that checks whether a user may join a given room via Draupnir anti-spam
|
||||
//! Endpoint that checks whether a user may join a given room via Draupnir
|
||||
//! anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/api/1/spam_check/user_may_join_room",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/api/1/spam_check/user_may_join_room",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_join_room` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
/// Whether the user was invited to this room
|
||||
pub is_invited: bool,
|
||||
}
|
||||
/// Request type for the `user_may_join_room` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
/// Whether the user was invited to this room
|
||||
pub is_invited: bool,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_join_room` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
/// Response type for the `user_may_join_room` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(user: OwnedUserId, room: OwnedRoomId, is_invited: bool) -> Self {
|
||||
Self { user, room, is_invited }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(user: OwnedUserId, room: OwnedRoomId, is_invited: bool) -> Self {
|
||||
Self { user, room, is_invited }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +1,271 @@
|
||||
//! Types for invite filtering ([MSC4155]).
|
||||
//!
|
||||
//!
|
||||
//! MSC4155: https://github.com/matrix-org/matrix-spec-proposals/pull/4155
|
||||
|
||||
use ruma::{ServerName, UserId};
|
||||
use ruma::exports::ruma_macros::EventContent;
|
||||
use ruma::{ServerName, UserId, exports::ruma_macros::EventContent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wildmatch::WildMatch;
|
||||
|
||||
/// Represents a user's level of filtering on actions from another user or server.
|
||||
/// "Ignore" and "block" are defined in [MSC4283].
|
||||
/// Represents a user's level of filtering on actions from another user or
|
||||
/// server. "Ignore" and "block" are defined in [MSC4283].
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FilterLevel {
|
||||
Allow,
|
||||
Ignore,
|
||||
Block,
|
||||
Allow,
|
||||
Ignore,
|
||||
Block,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.invite_permission_config", kind = GlobalAccountData)]
|
||||
pub struct InvitePermissionConfigEventContent {
|
||||
/// A global on/off toggle for all rules
|
||||
#[serde(default = "ruma::serde::default_true")]
|
||||
pub enabled: bool,
|
||||
|
||||
/// A list of globs matching users which are allowed to send an invite.
|
||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_users: Vec<String>,
|
||||
/// A list of globs matching users whose invites should be ignored (as defined in [MSC4283]).
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub ignored_users: Vec<String>,
|
||||
/// A list of globs matching users whose invites should be blocked (as defined in [MSC4283]).
|
||||
/// Invites from blocked users should be refused with the M_INVITE_BLOCKED status code.
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_users: Vec<String>,
|
||||
/// A global on/off toggle for all rules
|
||||
#[serde(default = "ruma::serde::default_true")]
|
||||
pub enabled: bool,
|
||||
|
||||
/// A list of globs matching users which are allowed to send an invite.
|
||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_users: Vec<String>,
|
||||
/// A list of globs matching users whose invites should be ignored (as
|
||||
/// defined in [MSC4283]).
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub ignored_users: Vec<String>,
|
||||
/// A list of globs matching users whose invites should be blocked (as
|
||||
/// defined in [MSC4283]). Invites from blocked users should be refused
|
||||
/// with the M_INVITE_BLOCKED status code.
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_users: Vec<String>,
|
||||
|
||||
/// A list of globs matching servers which are allowed to send an invite.
|
||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_servers: Vec<String>,
|
||||
/// A list of globs matching servers whose invites should be ignored (as defined in [MSC4283]).
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub ignored_servers: Vec<String>,
|
||||
/// A list of globs matching servers whose invites should be blocked (as defined in [MSC4283]).
|
||||
/// Invites from blocked servers should be refused with the M_INVITE_BLOCKED status code.
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_servers: Vec<String>,
|
||||
/// A list of globs matching servers which are allowed to send an invite.
|
||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub allowed_servers: Vec<String>,
|
||||
/// A list of globs matching servers whose invites should be ignored (as
|
||||
/// defined in [MSC4283]).
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub ignored_servers: Vec<String>,
|
||||
/// A list of globs matching servers whose invites should be blocked (as
|
||||
/// defined in [MSC4283]). Invites from blocked servers should be refused
|
||||
/// with the M_INVITE_BLOCKED status code.
|
||||
///
|
||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub blocked_servers: Vec<String>,
|
||||
}
|
||||
|
||||
impl InvitePermissionConfigEventContent {
|
||||
/// Creates a new `InvitePermissionConfigEventContent` from six lists of globs.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
enabled: bool,
|
||||
allowed_users: Vec<String>,
|
||||
ignored_users: Vec<String>,
|
||||
blocked_users: Vec<String>,
|
||||
allowed_servers: Vec<String>,
|
||||
ignored_servers: Vec<String>,
|
||||
blocked_servers: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
enabled,
|
||||
allowed_users,
|
||||
ignored_users,
|
||||
blocked_users,
|
||||
allowed_servers,
|
||||
ignored_servers,
|
||||
blocked_servers,
|
||||
}
|
||||
}
|
||||
/// Creates a new `InvitePermissionConfigEventContent` from six lists of
|
||||
/// globs.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
enabled: bool,
|
||||
allowed_users: Vec<String>,
|
||||
ignored_users: Vec<String>,
|
||||
blocked_users: Vec<String>,
|
||||
allowed_servers: Vec<String>,
|
||||
ignored_servers: Vec<String>,
|
||||
blocked_servers: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
enabled,
|
||||
allowed_users,
|
||||
ignored_users,
|
||||
blocked_users,
|
||||
allowed_servers,
|
||||
ignored_servers,
|
||||
blocked_servers,
|
||||
}
|
||||
}
|
||||
|
||||
/// Test the filters against a user id. This function will check both the
|
||||
/// user rules _and_ the server rules.
|
||||
#[must_use]
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
pub fn user_filter_level(&self, user: &UserId) -> FilterLevel {
|
||||
if !self.enabled {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.allowed_users, user.as_str()) {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.ignored_users, user.as_str()) {
|
||||
FilterLevel::Ignore
|
||||
} else if Self::matches(&self.blocked_users, user.as_str()) {
|
||||
FilterLevel::Block
|
||||
} else {
|
||||
self.server_filter_level(user.server_name())
|
||||
}
|
||||
}
|
||||
/// Test the filters against a user id. This function will check both the
|
||||
/// user rules _and_ the server rules.
|
||||
#[must_use]
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
pub fn user_filter_level(&self, user: &UserId) -> FilterLevel {
|
||||
if !self.enabled {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.allowed_users, user.as_str()) {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.ignored_users, user.as_str()) {
|
||||
FilterLevel::Ignore
|
||||
} else if Self::matches(&self.blocked_users, user.as_str()) {
|
||||
FilterLevel::Block
|
||||
} else {
|
||||
self.server_filter_level(user.server_name())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test the filters against a server name. Port numbers are ignored.
|
||||
#[must_use]
|
||||
pub fn server_filter_level(&self, server: &ServerName) -> FilterLevel {
|
||||
if !self.enabled {
|
||||
FilterLevel::Allow
|
||||
} else {
|
||||
let server = server.host();
|
||||
if Self::matches(&self.allowed_servers, server) {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.ignored_servers, server) {
|
||||
FilterLevel::Ignore
|
||||
} else if Self::matches(&self.blocked_servers, server) {
|
||||
FilterLevel::Block
|
||||
} else {
|
||||
FilterLevel::Allow
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Test the filters against a server name. Port numbers are ignored.
|
||||
#[must_use]
|
||||
pub fn server_filter_level(&self, server: &ServerName) -> FilterLevel {
|
||||
if !self.enabled {
|
||||
FilterLevel::Allow
|
||||
} else {
|
||||
let server = server.host();
|
||||
if Self::matches(&self.allowed_servers, server) {
|
||||
FilterLevel::Allow
|
||||
} else if Self::matches(&self.ignored_servers, server) {
|
||||
FilterLevel::Ignore
|
||||
} else if Self::matches(&self.blocked_servers, server) {
|
||||
FilterLevel::Block
|
||||
} else {
|
||||
FilterLevel::Allow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn matches(a: &[String], s: &str) -> bool {
|
||||
a.iter().map(String::as_str).any(|a| WildMatch::new(a).matches(s))
|
||||
}
|
||||
fn matches(a: &[String], s: &str) -> bool {
|
||||
a.iter()
|
||||
.map(String::as_str)
|
||||
.any(|a| WildMatch::new(a).matches(s))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ruma::{ServerName, UserId, events::GlobalAccountDataEvent};
|
||||
use serde_json::{from_value as from_json_value, json};
|
||||
use ruma::{ServerName, UserId, events::GlobalAccountDataEvent};
|
||||
use serde_json::{from_value as from_json_value, json};
|
||||
|
||||
use crate::invite_permission_config::{FilterLevel, InvitePermissionConfigEventContent};
|
||||
use crate::invite_permission_config::{FilterLevel, InvitePermissionConfigEventContent};
|
||||
|
||||
fn user_id(id: &str) -> &UserId {
|
||||
<&UserId>::try_from(id).unwrap()
|
||||
}
|
||||
fn user_id(id: &str) -> &UserId { <&UserId>::try_from(id).unwrap() }
|
||||
|
||||
fn server_name(name: &str) -> &ServerName {
|
||||
<&ServerName>::try_from(name).unwrap()
|
||||
}
|
||||
fn server_name(name: &str) -> &ServerName { <&ServerName>::try_from(name).unwrap() }
|
||||
|
||||
#[test]
|
||||
fn default_values() {
|
||||
let data = json!({
|
||||
"content": {},
|
||||
"type": "org.matrix.msc4155.invite_permission_config"
|
||||
});
|
||||
#[test]
|
||||
fn default_values() {
|
||||
let data = json!({
|
||||
"content": {},
|
||||
"type": "org.matrix.msc4155.invite_permission_config"
|
||||
});
|
||||
|
||||
let event: GlobalAccountDataEvent<InvitePermissionConfigEventContent> = from_json_value(data).unwrap();
|
||||
assert!(event.content.enabled);
|
||||
assert!(event.content.allowed_users.is_empty());
|
||||
assert!(event.content.ignored_users.is_empty());
|
||||
assert!(event.content.blocked_users.is_empty());
|
||||
assert!(event.content.allowed_servers.is_empty());
|
||||
assert!(event.content.ignored_servers.is_empty());
|
||||
assert!(event.content.blocked_servers.is_empty());
|
||||
assert_eq!(event.content.user_filter_level(user_id("@alice:example.com")), FilterLevel::Allow);
|
||||
assert_eq!(event.content.server_filter_level(server_name("example.com")), FilterLevel::Allow);
|
||||
}
|
||||
let event: GlobalAccountDataEvent<InvitePermissionConfigEventContent> =
|
||||
from_json_value(data).unwrap();
|
||||
assert!(event.content.enabled);
|
||||
assert!(event.content.allowed_users.is_empty());
|
||||
assert!(event.content.ignored_users.is_empty());
|
||||
assert!(event.content.blocked_users.is_empty());
|
||||
assert!(event.content.allowed_servers.is_empty());
|
||||
assert!(event.content.ignored_servers.is_empty());
|
||||
assert!(event.content.blocked_servers.is_empty());
|
||||
assert_eq!(
|
||||
event
|
||||
.content
|
||||
.user_filter_level(user_id("@alice:example.com")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
assert_eq!(
|
||||
event
|
||||
.content
|
||||
.server_filter_level(server_name("example.com")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_the_world() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
#[test]
|
||||
fn block_the_world() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:foo.com:8080")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
}
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:foo.com:8080")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_goodguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
#[test]
|
||||
fn only_goodguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org:8080")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
}
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@alice:goodguys.org:8080")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude_badguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_servers: vec!["badguys.org".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
#[test]
|
||||
fn exclude_badguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_servers: vec!["badguys.org".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org:8080")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@kevin:badguys.org:8080")),
|
||||
FilterLevel::Block
|
||||
);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_goodguys_except_for_kevin() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_users: vec!["@kevin:goodguys.org".to_owned()],
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
#[test]
|
||||
fn only_goodguys_except_for_kevin() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
blocked_users: vec!["@kevin:goodguys.org".to_owned()],
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:goodguys.org")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:goodguys.org")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_badguys_except_for_alice() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_users: vec!["@alice:badguys.org".to_owned()],
|
||||
blocked_servers: vec!["badguys.org".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
#[test]
|
||||
fn no_badguys_except_for_alice() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_users: vec!["@alice:badguys.org".to_owned()],
|
||||
blocked_servers: vec!["badguys.org".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:badguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:badguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_goodguys_and_ignore_reallybadguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
ignored_servers: vec!["reallybadguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
#[test]
|
||||
fn only_goodguys_and_ignore_reallybadguys() {
|
||||
let event = InvitePermissionConfigEventContent {
|
||||
enabled: true,
|
||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||
ignored_servers: vec!["reallybadguys.org".to_owned()],
|
||||
blocked_servers: vec!["*".to_owned()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org:8080")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
assert_eq!(event.user_filter_level(user_id("@kevin:reallybadguys.org")), FilterLevel::Ignore);
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@alice:goodguys.org:8080")),
|
||||
FilterLevel::Allow
|
||||
);
|
||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||
assert_eq!(
|
||||
event.user_filter_level(user_id("@kevin:reallybadguys.org")),
|
||||
FilterLevel::Ignore
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,53 +7,53 @@
|
||||
//! - https://mau.dev/maunium/synapse/-/blob/52741d3/synapse/handlers/event_auth.py#L280-292
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/accept_make_join",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/accept_make_join",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `accept_make_join` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
}
|
||||
/// Request type for the `accept_make_join` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
}
|
||||
|
||||
/// Response type for the `accept_make_join` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
/// Response type for the `accept_make_join` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
user: OwnedUserId,
|
||||
room: OwnedRoomId,
|
||||
) -> Self {
|
||||
Self { management_room_id, user, room }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
user: OwnedUserId,
|
||||
room: OwnedRoomId,
|
||||
) -> Self {
|
||||
Self { management_room_id, user, room }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod user_may_invite;
|
||||
pub mod user_may_join_room;
|
||||
|
||||
pub mod accept_make_join;
|
||||
pub mod accept_make_join;
|
||||
|
||||
@@ -1,58 +1,64 @@
|
||||
//! `POST /_meowlnir/antispam/*/user_may_invite`
|
||||
//!
|
||||
//! Checks that a user may invite the given user to the given room via Meowlnir anti-spam
|
||||
//! Checks that a user may invite the given user to the given room via Meowlnir
|
||||
//! anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_invite",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_invite",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_invite` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user sending the invite
|
||||
pub inviter: OwnedUserId,
|
||||
/// The user being invited
|
||||
pub invitee: OwnedUserId,
|
||||
/// The room the invitee is being invited to
|
||||
pub room_id: OwnedRoomId,
|
||||
}
|
||||
/// Request type for the `user_may_invite` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user sending the invite
|
||||
pub inviter: OwnedUserId,
|
||||
/// The user being invited
|
||||
pub invitee: OwnedUserId,
|
||||
/// The room the invitee is being invited to
|
||||
pub room_id: OwnedRoomId,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_invite` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
/// Response type for the `user_may_invite` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
inviter: OwnedUserId,
|
||||
invitee: OwnedUserId,
|
||||
room_id: OwnedRoomId,
|
||||
) -> Self {
|
||||
Self { management_room_id, inviter, invitee, room_id }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
inviter: OwnedUserId,
|
||||
invitee: OwnedUserId,
|
||||
room_id: OwnedRoomId,
|
||||
) -> Self {
|
||||
Self {
|
||||
management_room_id,
|
||||
inviter,
|
||||
invitee,
|
||||
room_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,56 +3,61 @@
|
||||
//! Endpoint to track invite joins via Meowlnir anti-spam
|
||||
|
||||
pub mod v1 {
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedUserId,
|
||||
api::{auth_scheme::AppserviceToken, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_join_room",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: AppserviceToken,
|
||||
history: {
|
||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_join_room",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `user_may_join_room` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
/// Whether the user was invited to this room
|
||||
pub is_invited: bool,
|
||||
}
|
||||
/// Request type for the `user_may_join_room` callback.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The relevant management room
|
||||
#[ruma_api(path)]
|
||||
pub management_room_id: OwnedRoomId,
|
||||
/// The user trying to join a room
|
||||
pub user: OwnedUserId,
|
||||
/// The room the user is trying to join
|
||||
pub room: OwnedRoomId,
|
||||
/// Whether the user was invited to this room
|
||||
pub is_invited: bool,
|
||||
}
|
||||
|
||||
/// Response type for the `user_may_join_room` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
/// Response type for the `user_may_join_room` callback.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
user: OwnedUserId,
|
||||
room: OwnedRoomId,
|
||||
is_invited: bool,
|
||||
) -> Self {
|
||||
Self { management_room_id, user, room, is_invited }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new empty `Request`.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
management_room_id: OwnedRoomId,
|
||||
user: OwnedUserId,
|
||||
room: OwnedRoomId,
|
||||
is_invited: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
management_room_id,
|
||||
user,
|
||||
room,
|
||||
is_invited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
pub mod admin;
|
||||
pub mod draupnir_antispam;
|
||||
pub mod invite_permission_config;
|
||||
pub mod meowlnir_antispam;
|
||||
pub mod policy;
|
||||
pub mod invite_permission_config;
|
||||
@@ -2,99 +2,90 @@
|
||||
//!
|
||||
//! [`org.matrix.msc4284.policy`]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
|
||||
use ruma::exports::ruma_macros::EventContent;
|
||||
use ruma::{events::EmptyStateKey, exports::ruma_macros::EventContent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruma::events::EmptyStateKey;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Default)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "org.matrix.msc4284.policy", kind = State, state_key_type = EmptyStateKey)]
|
||||
pub struct RoomPolicyEventContent {
|
||||
/// The server name of the room's policy server.
|
||||
///
|
||||
/// If the value is empty or unreachable, the policy server should be ignored.
|
||||
pub via: Option<String>,
|
||||
/// The public key this policy server will sign with.
|
||||
pub public_key: Option<String>
|
||||
/// The server name of the room's policy server.
|
||||
///
|
||||
/// If the value is empty or unreachable, the policy server should be
|
||||
/// ignored.
|
||||
pub via: Option<String>,
|
||||
/// The public key this policy server will sign with.
|
||||
pub public_key: Option<String>,
|
||||
}
|
||||
|
||||
impl RoomPolicyEventContent {
|
||||
/// Create an empty `RoomPolicyEventContent`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
/// Create an empty `RoomPolicyEventContent`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct PolicyServerResponseContent {
|
||||
/// The policy server's verdict. Either `ok` or `spam`.
|
||||
pub recommendation: String,
|
||||
/// The policy server's verdict. Either `ok` or `spam`.
|
||||
pub recommendation: String,
|
||||
}
|
||||
|
||||
impl PolicyServerResponseContent {
|
||||
/// Create a new `PolicyServerResponseContent` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(recommendation: String) -> Self {
|
||||
Self { recommendation }
|
||||
}
|
||||
/// Create a new `PolicyServerResponseContent` with the given
|
||||
/// recommendation.
|
||||
#[must_use]
|
||||
pub fn new(recommendation: String) -> Self { Self { recommendation } }
|
||||
}
|
||||
|
||||
impl From<String> for PolicyServerResponseContent {
|
||||
fn from(recommendation: String) -> Self {
|
||||
Self::new(recommendation)
|
||||
}
|
||||
fn from(recommendation: String) -> Self { Self::new(recommendation) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
use ruma::events::OriginalStateEvent;
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::RoomPolicyEventContent;
|
||||
use ruma::events::OriginalStateEvent;
|
||||
use super::RoomPolicyEventContent;
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let content = RoomPolicyEventContent {
|
||||
via: Some("example.com".to_owned()),
|
||||
public_key: Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
||||
};
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let content = RoomPolicyEventContent {
|
||||
via: Some("example.com".to_owned()),
|
||||
public_key: Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned()),
|
||||
};
|
||||
|
||||
let actual = to_json_value(content).unwrap();
|
||||
let expected = json!({
|
||||
"via": "example.com",
|
||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||
});
|
||||
let actual = to_json_value(content).unwrap();
|
||||
let expected = json!({
|
||||
"via": "example.com",
|
||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||
});
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"via": "example.com",
|
||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||
},
|
||||
"event_id": "123:example.com",
|
||||
"origin_server_ts": 1,
|
||||
"room_id": "!123456:example.com",
|
||||
"sender": "@carl:example.com",
|
||||
"state_key": "",
|
||||
"type": "org.matrix.msc4284.policy"
|
||||
});
|
||||
#[test]
|
||||
fn deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"via": "example.com",
|
||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||
},
|
||||
"event_id": "123:example.com",
|
||||
"origin_server_ts": 1,
|
||||
"room_id": "!123456:example.com",
|
||||
"sender": "@carl:example.com",
|
||||
"state_key": "",
|
||||
"type": "org.matrix.msc4284.policy"
|
||||
});
|
||||
|
||||
let content = from_json_value::<OriginalStateEvent<RoomPolicyEventContent>>(json_data)
|
||||
.unwrap()
|
||||
.content;
|
||||
assert_eq!(
|
||||
content.via,
|
||||
Some("example.com".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
content.public_key,
|
||||
Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
||||
);
|
||||
}
|
||||
let content = from_json_value::<OriginalStateEvent<RoomPolicyEventContent>>(json_data)
|
||||
.unwrap()
|
||||
.content;
|
||||
assert_eq!(content.via, Some("example.com".to_owned()));
|
||||
assert_eq!(
|
||||
content.public_key,
|
||||
Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod event;
|
||||
pub mod policy_check;
|
||||
pub mod policy_sign;
|
||||
pub mod report_content;
|
||||
pub mod event;
|
||||
@@ -4,57 +4,56 @@
|
||||
//! This is now a fallback behaviour that will be removed later.
|
||||
|
||||
pub mod unstable {
|
||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
|
||||
use ruma::{
|
||||
OwnedEventId, api::{federation::authentication::ServerSignatures, request, response}, metadata
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
use ruma::{
|
||||
OwnedEventId,
|
||||
api::{federation::authentication::ServerSignatures, request, response},
|
||||
metadata,
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
history: {
|
||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/event/{event_id}/check",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
history: {
|
||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/event/{event_id}/check",
|
||||
}
|
||||
}
|
||||
|
||||
/// Response type for the `check` endpoint.
|
||||
#[response]
|
||||
pub struct Response {
|
||||
/// Either `ok` or `spam`, indicating the policy server's recommendation.
|
||||
pub recommendation: String,
|
||||
}
|
||||
/// Response type for the `check` endpoint.
|
||||
#[response]
|
||||
pub struct Response {
|
||||
/// Either `ok` or `spam`, indicating the policy server's
|
||||
/// recommendation.
|
||||
pub recommendation: String,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(recommendation: String) -> Self {
|
||||
Self { recommendation }
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(recommendation: String) -> Self { Self { recommendation } }
|
||||
}
|
||||
|
||||
/// Request type for the `check` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The event ID to check.
|
||||
#[ruma_api(path)]
|
||||
pub event_id: OwnedEventId,
|
||||
/// Request type for the `check` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The event ID to check.
|
||||
#[ruma_api(path)]
|
||||
pub event_id: OwnedEventId,
|
||||
|
||||
/// The PDU body (optional)
|
||||
#[ruma_api(body)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pdu: Option<Box<RawJsonValue>>,
|
||||
}
|
||||
/// The PDU body (optional)
|
||||
#[ruma_api(body)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pdu: Option<Box<RawJsonValue>>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given event ID.
|
||||
#[must_use]
|
||||
pub fn new(event_id: OwnedEventId) -> Self {
|
||||
Self { event_id, pdu: None }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given event ID.
|
||||
#[must_use]
|
||||
pub fn new(event_id: OwnedEventId) -> Self { Self { event_id, pdu: None } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,53 +3,53 @@
|
||||
//! Asks a policy server to sign our event
|
||||
|
||||
pub mod unstable {
|
||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
use ruma::{
|
||||
ServerSignatures,
|
||||
api::{federation::authentication::ServerSignatures as ServerSignaturesAuth, request, response}, metadata
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||
use ruma::{
|
||||
ServerSignatures,
|
||||
api::{
|
||||
federation::authentication::ServerSignatures as ServerSignaturesAuth, request,
|
||||
response,
|
||||
},
|
||||
metadata,
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignaturesAuth,
|
||||
history: {
|
||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/sign",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignaturesAuth,
|
||||
history: {
|
||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/sign",
|
||||
}
|
||||
}
|
||||
|
||||
/// Response type for the `sign` endpoint.
|
||||
#[response]
|
||||
pub struct Response {
|
||||
/// The signatures returned from the policy server (if provided)
|
||||
#[ruma_api(body)]
|
||||
pub signatures: Option<ServerSignatures>
|
||||
}
|
||||
/// Response type for the `sign` endpoint.
|
||||
#[response]
|
||||
pub struct Response {
|
||||
/// The signatures returned from the policy server (if provided)
|
||||
#[ruma_api(body)]
|
||||
pub signatures: Option<ServerSignatures>,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(signatures: Option<ServerSignatures>) -> Self {
|
||||
Self { signatures }
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new `Response` with the given recommendation.
|
||||
#[must_use]
|
||||
pub fn new(signatures: Option<ServerSignatures>) -> Self { Self { signatures } }
|
||||
}
|
||||
|
||||
/// Request type for the `sign` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The PDU body (in canonical JSON)
|
||||
#[ruma_api(body)]
|
||||
pub pdu: Box<RawJsonValue>,
|
||||
}
|
||||
/// Request type for the `sign` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The PDU body (in canonical JSON)
|
||||
#[ruma_api(body)]
|
||||
pub pdu: Box<RawJsonValue>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given event JSON
|
||||
#[must_use]
|
||||
pub fn new(pdu: Box<RawJsonValue>) -> Self {
|
||||
Self { pdu }
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a new `Request` with the given event JSON
|
||||
#[must_use]
|
||||
pub fn new(pdu: Box<RawJsonValue>) -> Self { Self { pdu } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,56 +3,56 @@
|
||||
//! Send a request to report an event originating from another server.
|
||||
|
||||
pub mod msc3843 {
|
||||
//! `MSC3843` ([MSC])
|
||||
//!
|
||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
|
||||
//! `MSC3843` ([MSC])
|
||||
//!
|
||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
|
||||
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId, api::{federation::authentication::ServerSignatures, request, response}, metadata
|
||||
};
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId,
|
||||
api::{federation::authentication::ServerSignatures, request, response},
|
||||
metadata,
|
||||
};
|
||||
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
history: {
|
||||
unstable => "/_matrix/federation/unstable/org.matrix.msc3843/rooms/{room_id}/report/{event_id}",
|
||||
}
|
||||
}
|
||||
metadata! {
|
||||
method: POST,
|
||||
rate_limited: false,
|
||||
authentication: ServerSignatures,
|
||||
history: {
|
||||
unstable => "/_matrix/federation/unstable/org.matrix.msc3843/rooms/{room_id}/report/{event_id}",
|
||||
}
|
||||
}
|
||||
|
||||
/// Request type for the `report_content` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The room ID that the reported event was sent in.
|
||||
#[ruma_api(path)]
|
||||
pub room_id: OwnedRoomId,
|
||||
/// Request type for the `report_content` endpoint.
|
||||
#[request]
|
||||
pub struct Request {
|
||||
/// The room ID that the reported event was sent in.
|
||||
#[ruma_api(path)]
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
/// The event being reported.
|
||||
#[ruma_api(path)]
|
||||
pub event_id: OwnedEventId,
|
||||
/// The event being reported.
|
||||
#[ruma_api(path)]
|
||||
pub event_id: OwnedEventId,
|
||||
|
||||
/// The reason that the event is being reported.
|
||||
pub reason: String,
|
||||
}
|
||||
/// The reason that the event is being reported.
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
/// Response type for the `report_content` endpoint.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
/// Response type for the `report_content` endpoint.
|
||||
#[response]
|
||||
#[derive(Default)]
|
||||
pub struct Response;
|
||||
|
||||
impl Request {
|
||||
/// Creates a `Request` with the given room ID, event ID and reason.
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, event_id: OwnedEventId, reason: String) -> Self {
|
||||
Self { room_id, event_id, reason }
|
||||
}
|
||||
}
|
||||
impl Request {
|
||||
/// Creates a `Request` with the given room ID, event ID and reason.
|
||||
#[must_use]
|
||||
pub fn new(room_id: OwnedRoomId, event_id: OwnedEventId, reason: String) -> Self {
|
||||
Self { room_id, event_id, reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Response {
|
||||
/// Creates a new empty `Response`.
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user