forked from jmug/stoatchat
Compare commits
7
Commits
20240206-1
...
20240210-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf82972a80 | ||
|
|
ea00f0fec1 | ||
|
|
226dbca6e0 | ||
|
|
e4b5a23f71 | ||
|
|
9663caa1ff | ||
|
|
f9f0f409a8 | ||
|
|
8f4d6c864b |
No files matched your search
Generated
+9
-9
@@ -3580,7 +3580,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
@@ -3600,7 +3600,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"cached",
|
"cached",
|
||||||
@@ -3612,7 +3612,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -3657,7 +3657,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -3703,7 +3703,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"iso8601-timestamp 0.2.11",
|
"iso8601-timestamp 0.2.11",
|
||||||
@@ -3719,7 +3719,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3734,7 +3734,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"log",
|
"log",
|
||||||
@@ -3745,7 +3745,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -3798,7 +3798,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"revolt_okapi",
|
"revolt_okapi",
|
||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -23,11 +23,11 @@ default = ["mongodb", "async-std-runtime", "tasks"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.6.10", path = "../config" }
|
revolt-config = { version = "0.6.12", path = "../config" }
|
||||||
revolt-result = { version = "0.6.10", path = "../result" }
|
revolt-result = { version = "0.6.12", path = "../result" }
|
||||||
revolt-models = { version = "0.6.10", path = "../models" }
|
revolt-models = { version = "0.6.12", path = "../models" }
|
||||||
revolt-presence = { version = "0.6.10", path = "../presence" }
|
revolt-presence = { version = "0.6.12", path = "../presence" }
|
||||||
revolt-permissions = { version = "0.6.10", path = "../permissions", features = [
|
revolt-permissions = { version = "0.6.12", path = "../permissions", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"bson",
|
"bson",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ impl Message {
|
|||||||
author: MessageAuthor<'_>,
|
author: MessageAuthor<'_>,
|
||||||
mut idempotency: IdempotencyKey,
|
mut idempotency: IdempotencyKey,
|
||||||
generate_embeds: bool,
|
generate_embeds: bool,
|
||||||
|
allow_mentions: bool,
|
||||||
) -> Result<Message> {
|
) -> Result<Message> {
|
||||||
let config = config().await;
|
let config = config().await;
|
||||||
|
|
||||||
@@ -272,10 +273,12 @@ impl Message {
|
|||||||
|
|
||||||
// Parse mentions in message.
|
// Parse mentions in message.
|
||||||
let mut mentions = HashSet::new();
|
let mut mentions = HashSet::new();
|
||||||
if let Some(content) = &data.content {
|
if allow_mentions {
|
||||||
for capture in RE_MENTION.captures_iter(content) {
|
if let Some(content) = &data.content {
|
||||||
if let Some(mention) = capture.get(1) {
|
for capture in RE_MENTION.captures_iter(content) {
|
||||||
mentions.insert(mention.as_str().to_string());
|
if let Some(mention) = capture.get(1) {
|
||||||
|
mentions.insert(mention.as_str().to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +295,7 @@ impl Message {
|
|||||||
for ReplyIntent { id, mention } in entries {
|
for ReplyIntent { id, mention } in entries {
|
||||||
let message = db.fetch_message(&id).await?;
|
let message = db.fetch_message(&id).await?;
|
||||||
|
|
||||||
if mention {
|
if mention && allow_mentions {
|
||||||
mentions.insert(message.author.to_owned());
|
mentions.insert(message.author.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -341,11 +341,7 @@ impl From<crate::Message> for Message {
|
|||||||
embeds: value.embeds,
|
embeds: value.embeds,
|
||||||
mentions: value.mentions,
|
mentions: value.mentions,
|
||||||
replies: value.replies,
|
replies: value.replies,
|
||||||
reactions: value
|
reactions: value.reactions,
|
||||||
.reactions
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
|
||||||
.collect(),
|
|
||||||
interactions: value.interactions.into(),
|
interactions: value.interactions.into(),
|
||||||
masquerade: value.masquerade.map(|masq| masq.into()),
|
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||||
}
|
}
|
||||||
@@ -369,12 +365,7 @@ impl From<crate::PartialMessage> for PartialMessage {
|
|||||||
embeds: value.embeds,
|
embeds: value.embeds,
|
||||||
mentions: value.mentions,
|
mentions: value.mentions,
|
||||||
replies: value.replies,
|
replies: value.replies,
|
||||||
reactions: value.reactions.map(|reactions| {
|
reactions: value.reactions,
|
||||||
reactions
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, v.into_iter().collect()))
|
|
||||||
.collect()
|
|
||||||
}),
|
|
||||||
interactions: value.interactions.map(|interactions| interactions.into()),
|
interactions: value.interactions.map(|interactions| interactions.into()),
|
||||||
masquerade: value.masquerade.map(|masq| masq.into()),
|
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,6 +444,26 @@ impl<'a> DatabasePermissionQuery<'a> {
|
|||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the underlying user
|
||||||
|
pub fn user_ref(&self) -> &Option<Cow<User>> {
|
||||||
|
&self.user
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying server
|
||||||
|
pub fn channel_ref(&self) -> &Option<Cow<Channel>> {
|
||||||
|
&self.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying server
|
||||||
|
pub fn server_ref(&self) -> &Option<Cow<Server>> {
|
||||||
|
&self.server
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying member
|
||||||
|
pub fn member_ref(&self) -> &Option<Cow<Member>> {
|
||||||
|
&self.member
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Short-hand for creating a permission calculator
|
/// Short-hand for creating a permission calculator
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -18,8 +18,8 @@ default = ["serde", "partials"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.6.10", path = "../config" }
|
revolt-config = { version = "0.6.12", path = "../config" }
|
||||||
revolt-permissions = { version = "0.6.10", path = "../permissions" }
|
revolt-permissions = { version = "0.6.12", path = "../permissions" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
regex = "1"
|
regex = "1"
|
||||||
@@ -32,7 +32,7 @@ serde = { version = "1", features = ["derive"], optional = true }
|
|||||||
iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] }
|
iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] }
|
||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = { version = "0.8.8", optional = true }
|
schemars = { version = "0.8.8", optional = true, features = ["indexmap1"] }
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
validator = { version = "0.16.0", optional = true, features = ["derive"] }
|
validator = { version = "0.16.0", optional = true, features = ["derive"] }
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::{
|
use std::time::SystemTime;
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
time::SystemTime,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use indexmap::{IndexMap, IndexSet};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use revolt_config::config;
|
use revolt_config::config;
|
||||||
@@ -55,8 +53,8 @@ auto_derived_partial!(
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub replies: Option<Vec<String>>,
|
pub replies: Option<Vec<String>>,
|
||||||
/// Hashmap of emoji IDs to array of user IDs
|
/// Hashmap of emoji IDs to array of user IDs
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
#[serde(skip_serializing_if = "IndexMap::is_empty", default)]
|
||||||
pub reactions: HashMap<String, HashSet<String>>,
|
pub reactions: IndexMap<String, IndexSet<String>>,
|
||||||
/// Information about how this message should be interacted with
|
/// Information about how this message should be interacted with
|
||||||
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
||||||
pub interactions: Interactions,
|
pub interactions: Interactions,
|
||||||
@@ -119,7 +117,7 @@ auto_derived!(
|
|||||||
pub struct Interactions {
|
pub struct Interactions {
|
||||||
/// Reactions which should always appear and be distinct
|
/// Reactions which should always appear and be distinct
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub reactions: Option<HashSet<String>>,
|
pub reactions: Option<IndexSet<String>>,
|
||||||
/// Whether reactions should be restricted to the given list
|
/// Whether reactions should be restricted to the given list
|
||||||
///
|
///
|
||||||
/// Can only be set to true if reactions list is of at least length 1
|
/// Can only be set to true if reactions list is of at least length 1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use chrono::{Duration, Utc};
|
||||||
use revolt_database::util::permissions::DatabasePermissionQuery;
|
use revolt_database::util::permissions::DatabasePermissionQuery;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::idempotency::IdempotencyKey, util::reference::Reference, Database, User,
|
util::idempotency::IdempotencyKey, util::reference::Reference, Database, User,
|
||||||
@@ -61,6 +62,23 @@ pub async fn message_send(
|
|||||||
interactions.validate(db, &permissions).await?;
|
interactions.validate(db, &permissions).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disallow mentions for new users (TRUST-0: <12 hours age) in public servers
|
||||||
|
let allow_mentions = if let Some(server) = query.server_ref() {
|
||||||
|
if server.discoverable {
|
||||||
|
if (Utc::now() - ulid::Ulid::from_string(&user.id).unwrap().datetime())
|
||||||
|
< Duration::hours(12)
|
||||||
|
{
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
// Create the message
|
// Create the message
|
||||||
let author: v0::User = user.clone().into(db, Some(&user)).await;
|
let author: v0::User = user.clone().into(db, Some(&user)).await;
|
||||||
Ok(Json(
|
Ok(Json(
|
||||||
@@ -71,6 +89,7 @@ pub async fn message_send(
|
|||||||
v0::MessageAuthor::User(&author),
|
v0::MessageAuthor::User(&author),
|
||||||
idempotency,
|
idempotency,
|
||||||
permissions.has_channel_permission(ChannelPermission::SendEmbeds),
|
permissions.has_channel_permission(ChannelPermission::SendEmbeds),
|
||||||
|
allow_mentions,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ pub async fn webhook_execute(
|
|||||||
v0::MessageAuthor::Webhook(&webhook.into()),
|
v0::MessageAuthor::Webhook(&webhook.into()),
|
||||||
idempotency,
|
idempotency,
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ impl Ratelimiter {
|
|||||||
pub fn from(
|
pub fn from(
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
(bucket, resource): (&str, Option<&str>),
|
(bucket, resource): (&str, Option<&str>),
|
||||||
) -> Result<Ratelimiter, u128> {
|
) -> Result<Ratelimiter, Ratelimiter> {
|
||||||
let mut key = DefaultHasher::new();
|
let mut key = DefaultHasher::new();
|
||||||
key.write(identifier.as_bytes());
|
key.write(identifier.as_bytes());
|
||||||
key.write(bucket.as_bytes());
|
key.write(bucket.as_bytes());
|
||||||
@@ -194,27 +194,29 @@ impl Ratelimiter {
|
|||||||
let mut entry = Entry::from(key);
|
let mut entry = Entry::from(key);
|
||||||
|
|
||||||
let remaining = entry.get_remaining(limit);
|
let remaining = entry.get_remaining(limit);
|
||||||
if remaining > 0 {
|
let reset = entry.left_until_reset();
|
||||||
entry.deduct();
|
let mut ratelimiter = Ratelimiter {
|
||||||
|
key,
|
||||||
let reset = entry.left_until_reset();
|
limit,
|
||||||
entry.save(key);
|
remaining,
|
||||||
|
reset,
|
||||||
Ok(Ratelimiter {
|
};
|
||||||
key,
|
if remaining == 0 {
|
||||||
limit,
|
return Err(ratelimiter);
|
||||||
remaining: remaining - 1,
|
|
||||||
reset,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(entry.left_until_reset())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry.deduct();
|
||||||
|
entry.save(key);
|
||||||
|
ratelimiter.remaining -= 1;
|
||||||
|
ratelimiter.reset = entry.left_until_reset();
|
||||||
|
|
||||||
|
Ok(ratelimiter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<'r> FromRequest<'r> for Ratelimiter {
|
impl<'r> FromRequest<'r> for Ratelimiter {
|
||||||
type Error = u128;
|
type Error = Ratelimiter;
|
||||||
|
|
||||||
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
let ratelimiter = request
|
let ratelimiter = request
|
||||||
@@ -233,7 +235,7 @@ impl<'r> FromRequest<'r> for Ratelimiter {
|
|||||||
|
|
||||||
match ratelimiter {
|
match ratelimiter {
|
||||||
Ok(ratelimiter) => Outcome::Success(*ratelimiter),
|
Ok(ratelimiter) => Outcome::Success(*ratelimiter),
|
||||||
Err(retry_after) => Outcome::Failure((Status::TooManyRequests, *retry_after)),
|
Err(ratelimiter) => Outcome::Failure((Status::TooManyRequests, *ratelimiter)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,23 +277,24 @@ impl Fairing for RatelimitFairing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
|
async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
|
||||||
use rocket::outcome::Outcome;
|
let guard = request.guard::<Ratelimiter>().await;
|
||||||
match request.guard::<Ratelimiter>().await {
|
let (Outcome::Success(ratelimiter) | Outcome::Failure((_, ratelimiter))) = guard else {
|
||||||
Outcome::Success(ratelimiter) => {
|
unreachable!()
|
||||||
let Ratelimiter {
|
};
|
||||||
key,
|
let Ratelimiter {
|
||||||
limit,
|
key,
|
||||||
remaining,
|
limit,
|
||||||
reset,
|
remaining,
|
||||||
} = ratelimiter;
|
reset,
|
||||||
|
} = ratelimiter;
|
||||||
|
|
||||||
response.set_raw_header("X-RateLimit-Limit", limit.to_string());
|
response.set_raw_header("X-RateLimit-Limit", limit.to_string());
|
||||||
response.set_raw_header("X-RateLimit-Bucket", key.to_string());
|
response.set_raw_header("X-RateLimit-Bucket", key.to_string());
|
||||||
response.set_raw_header("X-RateLimit-Remaining", remaining.to_string());
|
response.set_raw_header("X-RateLimit-Remaining", remaining.to_string());
|
||||||
response.set_raw_header("X-RateLimit-Reset-After", reset.to_string());
|
response.set_raw_header("X-RateLimit-Reset-After", reset.to_string());
|
||||||
}
|
|
||||||
Outcome::Failure(_) => response.set_status(Status::TooManyRequests),
|
if guard.is_failure() {
|
||||||
Outcome::Forward(_) => unreachable!(),
|
response.set_status(Status::TooManyRequests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,11 +311,14 @@ impl<'r> FromRequest<'r> for RatelimitInformation {
|
|||||||
type Error = u128;
|
type Error = u128;
|
||||||
|
|
||||||
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r rocket::Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
Outcome::Success(match request.guard::<Ratelimiter>().await {
|
let info = match request.guard::<Ratelimiter>().await {
|
||||||
Outcome::Success(ratelimiter) => RatelimitInformation::Success(ratelimiter),
|
Outcome::Success(ratelimiter) => RatelimitInformation::Success(ratelimiter),
|
||||||
Outcome::Failure((_, retry_after)) => RatelimitInformation::Failure { retry_after },
|
Outcome::Failure((_, ratelimiter)) => RatelimitInformation::Failure {
|
||||||
|
retry_after: ratelimiter.reset,
|
||||||
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
};
|
||||||
|
Outcome::Success(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.6.10"
|
version = "0.6.12"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use iso8601_timestamp::Timestamp;
|
|||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1,
|
events::client::EventV1,
|
||||||
models::{
|
models::{
|
||||||
server::Role,
|
|
||||||
server_member::{FieldsMember, MemberCompositeKey, MemberWithRoles, PartialMember},
|
server_member::{FieldsMember, MemberCompositeKey, MemberWithRoles, PartialMember},
|
||||||
Member, Server,
|
Member, Server,
|
||||||
},
|
},
|
||||||
@@ -84,30 +83,13 @@ impl Member {
|
|||||||
FieldsMember::Timeout => self.timeout = None,
|
FieldsMember::Timeout => self.timeout = None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_roles(&self, roles: HashMap<String, Role>) -> MemberWithRoles {
|
|
||||||
MemberWithRoles {
|
|
||||||
id: self.id.clone(),
|
|
||||||
joined_at: self.joined_at,
|
|
||||||
nickname: self.nickname.clone(),
|
|
||||||
avatar: self.avatar.clone(),
|
|
||||||
roles,
|
|
||||||
timeout: self.timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemberWithRoles {
|
impl MemberWithRoles {
|
||||||
pub fn new(server_id: String, user_id: String) -> Self {
|
pub fn new(server_id: String, user_id: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: MemberCompositeKey {
|
member: Member::new(server_id, user_id),
|
||||||
server: server_id,
|
|
||||||
user: user_id,
|
|
||||||
},
|
|
||||||
joined_at: Timestamp::now_utc(),
|
|
||||||
nickname: None,
|
|
||||||
avatar: None,
|
|
||||||
roles: HashMap::new(),
|
roles: HashMap::new(),
|
||||||
timeout: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,10 +22,14 @@ impl AbstractServerMember for MongoDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles> {
|
async fn fetch_member_with_roles(&self, server: &str, user: &str) -> Result<MemberWithRoles> {
|
||||||
Ok(self
|
let member = self.fetch_member(server, user).await?;
|
||||||
.fetch_member(server, user)
|
let server_roles = self.fetch_server(server).await?.roles;
|
||||||
.await?
|
let roles = member
|
||||||
.with_roles(self.fetch_server(server).await?.roles))
|
.roles
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| server_roles.get(id).map(|r| (id.clone(), r.clone())))
|
||||||
|
.collect();
|
||||||
|
Ok(MemberWithRoles { member, roles })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_member(&self, member: &Member) -> Result<()> {
|
||||||
|
|||||||
@@ -47,47 +47,27 @@ pub struct Member {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of a member of a server on Revolt With Role Data
|
/// Representation of a member of a server on Revolt With Role Data
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, OptionalStruct)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)]
|
|
||||||
#[opt_skip_serializing_none]
|
|
||||||
#[opt_some_priority]
|
|
||||||
pub struct MemberWithRoles {
|
pub struct MemberWithRoles {
|
||||||
/// Unique member id
|
pub member: Member,
|
||||||
#[serde(rename = "_id")]
|
|
||||||
pub id: MemberCompositeKey,
|
|
||||||
|
|
||||||
/// Time at which this user joined the server
|
|
||||||
pub joined_at: Timestamp,
|
|
||||||
|
|
||||||
/// Member's nickname
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub nickname: Option<String>,
|
|
||||||
/// Avatar attachment
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub avatar: Option<File>,
|
|
||||||
|
|
||||||
/// Member's roles
|
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
|
||||||
pub roles: HashMap<String, Role>,
|
pub roles: HashMap<String, Role>,
|
||||||
/// Timestamp this member is timed out until
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub timeout: Option<Timestamp>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
pub enum MemberResponse {
|
pub enum MemberResponse {
|
||||||
Members(Member),
|
Member(Member),
|
||||||
MembersWithRoles(MemberWithRoles),
|
MemberWithRoles(MemberWithRoles),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Member> for MemberResponse {
|
impl From<Member> for MemberResponse {
|
||||||
fn from(value: Member) -> Self {
|
fn from(value: Member) -> Self {
|
||||||
Self::Members(value)
|
Self::Member(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<MemberWithRoles> for MemberResponse {
|
impl From<MemberWithRoles> for MemberResponse {
|
||||||
fn from(value: MemberWithRoles) -> Self {
|
fn from(value: MemberWithRoles) -> Self {
|
||||||
Self::MembersWithRoles(value)
|
Self::MemberWithRoles(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Optional fields on server member object
|
/// Optional fields on server member object
|
||||||
|
|||||||
Reference in new issue
Block a user