forked from jmug/stoatchat
Compare commits
12 Commits
20240620-1
...
20240625-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f903f716e3 | ||
|
|
c5f4b94aa5 | ||
|
|
1d5dae4751 | ||
|
|
ac20b6bc99 | ||
|
|
6ec8007e4e | ||
|
|
e5eea267cf | ||
|
|
de5add09d0 | ||
|
|
1ec8f46c1d | ||
|
|
80666848cc | ||
|
|
e8e9613169 | ||
|
|
eda36436a8 | ||
|
|
93e05e9f18 |
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -3452,7 +3452,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-bonfire"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"async-channel 2.3.1",
|
||||
"async-std",
|
||||
@@ -3482,7 +3482,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-config"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"cached",
|
||||
@@ -3498,7 +3498,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-database"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-recursion",
|
||||
@@ -3544,7 +3544,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-delta"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"async-channel 1.6.1",
|
||||
"async-std",
|
||||
@@ -3572,6 +3572,7 @@ dependencies = [
|
||||
"revolt-database",
|
||||
"revolt-models",
|
||||
"revolt-permissions",
|
||||
"revolt-presence",
|
||||
"revolt-result",
|
||||
"revolt_rocket_okapi",
|
||||
"rocket",
|
||||
@@ -3590,7 +3591,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-models"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"iso8601-timestamp 0.2.11",
|
||||
@@ -3607,7 +3608,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-permissions"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -3622,7 +3623,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-presence"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"log",
|
||||
@@ -3633,7 +3634,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revolt-result"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
dependencies = [
|
||||
"revolt_okapi",
|
||||
"revolt_rocket_okapi",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-bonfire"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
license = "AGPL-3.0-or-later"
|
||||
edition = "2021"
|
||||
|
||||
@@ -41,7 +41,7 @@ revolt-result = { path = "../core/result" }
|
||||
revolt-models = { path = "../core/models" }
|
||||
revolt-config = { path = "../core/config" }
|
||||
revolt-database = { path = "../core/database" }
|
||||
revolt-permissions = { version = "0.7.7", path = "../core/permissions" }
|
||||
revolt-permissions = { version = "0.7.11", path = "../core/permissions" }
|
||||
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
||||
|
||||
# redis
|
||||
|
||||
@@ -180,7 +180,7 @@ impl State {
|
||||
.collect();
|
||||
|
||||
// Make sure we see our own user correctly.
|
||||
users.push(user.into_self().await);
|
||||
users.push(user.into_self(true).await);
|
||||
|
||||
// Set subscription state internally.
|
||||
self.reset_state().await;
|
||||
@@ -540,6 +540,20 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
EventV1::Message(message) => {
|
||||
// Since Message events are fanned out to many clients,
|
||||
// we must reconstruct the relationship value at this end.
|
||||
if let Some(user) = &mut message.user {
|
||||
user.relationship = self
|
||||
.cache
|
||||
.users
|
||||
.get(&self.cache.user_id)
|
||||
.expect("missing self?")
|
||||
.relationship_with(&message.author)
|
||||
.into();
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::{collections::HashSet, net::SocketAddr, sync::Arc};
|
||||
use async_tungstenite::WebSocketStream;
|
||||
use authifier::AuthifierEvent;
|
||||
use fred::{
|
||||
error::{RedisError, RedisErrorKind},
|
||||
interfaces::{ClientLike, EventInterface, PubsubInterface},
|
||||
types::RedisConfig,
|
||||
};
|
||||
@@ -229,11 +230,14 @@ async fn listener(
|
||||
// Handle Redis connection dropping
|
||||
let (clean_up_s, clean_up_r) = async_channel::bounded(1);
|
||||
let clean_up_s = Arc::new(Mutex::new(clean_up_s));
|
||||
subscriber.on_error(move |_| {
|
||||
let clean_up_s = clean_up_s.clone();
|
||||
spawn(async move {
|
||||
clean_up_s.lock().await.send(()).await.ok();
|
||||
});
|
||||
subscriber.on_error(move |err| {
|
||||
if let RedisErrorKind::Canceled = err.kind() {
|
||||
let clean_up_s = clean_up_s.clone();
|
||||
spawn(async move {
|
||||
clean_up_s.lock().await.send(()).await.ok();
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-config"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = ["Paul Makles <me@insrt.uk>"]
|
||||
|
||||
@@ -46,19 +46,40 @@ webhooks_enabled = false
|
||||
|
||||
[features.limits]
|
||||
|
||||
[features.limits.default]
|
||||
[features.limits.global]
|
||||
group_size = 100
|
||||
bots = 5
|
||||
message_length = 2000
|
||||
message_embeds = 5
|
||||
message_replies = 5
|
||||
message_attachments = 5
|
||||
message_reactions = 20
|
||||
servers = 100
|
||||
server_emoji = 100
|
||||
server_roles = 200
|
||||
server_channels = 200
|
||||
|
||||
new_user_days = 3
|
||||
|
||||
[features.limits.new_user]
|
||||
outgoing_friend_requests = 5
|
||||
|
||||
bots = 2
|
||||
message_length = 2000
|
||||
message_attachments = 5
|
||||
servers = 100
|
||||
|
||||
attachment_size = 20000000
|
||||
avatar_size = 4000000
|
||||
background_size = 6000000
|
||||
icon_size = 2500000
|
||||
banner_size = 6000000
|
||||
emoji_size = 500000
|
||||
|
||||
[features.limits.default]
|
||||
outgoing_friend_requests = 10
|
||||
|
||||
bots = 5
|
||||
message_length = 2000
|
||||
message_attachments = 5
|
||||
servers = 100
|
||||
|
||||
attachment_size = 20000000
|
||||
avatar_size = 4000000
|
||||
background_size = 6000000
|
||||
|
||||
@@ -105,19 +105,27 @@ pub struct Api {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct FeaturesLimits {
|
||||
pub struct GlobalLimits {
|
||||
pub group_size: usize,
|
||||
pub bots: usize,
|
||||
pub message_length: usize,
|
||||
pub message_replies: usize,
|
||||
pub message_attachments: usize,
|
||||
pub message_embeds: usize,
|
||||
pub message_replies: usize,
|
||||
pub message_reactions: usize,
|
||||
pub servers: usize,
|
||||
pub server_emoji: usize,
|
||||
pub server_roles: usize,
|
||||
pub server_channels: usize,
|
||||
|
||||
pub new_user_days: usize,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct FeaturesLimits {
|
||||
pub outgoing_friend_requests: usize,
|
||||
|
||||
pub bots: usize,
|
||||
pub message_length: usize,
|
||||
pub message_attachments: usize,
|
||||
pub servers: usize,
|
||||
|
||||
pub attachment_size: usize,
|
||||
pub avatar_size: usize,
|
||||
pub background_size: usize,
|
||||
@@ -128,6 +136,9 @@ pub struct FeaturesLimits {
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct FeaturesLimitsCollection {
|
||||
pub global: GlobalLimits,
|
||||
|
||||
pub new_user: FeaturesLimits,
|
||||
pub default: FeaturesLimits,
|
||||
|
||||
#[serde(flatten)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-database"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
authors = ["Paul Makles <me@insrt.uk>"]
|
||||
@@ -23,13 +23,13 @@ default = ["mongodb", "async-std-runtime", "tasks"]
|
||||
|
||||
[dependencies]
|
||||
# Core
|
||||
revolt-config = { version = "0.7.7", path = "../config" }
|
||||
revolt-result = { version = "0.7.7", path = "../result" }
|
||||
revolt-models = { version = "0.7.7", path = "../models", features = [
|
||||
revolt-config = { version = "0.7.11", path = "../config" }
|
||||
revolt-result = { version = "0.7.11", path = "../result" }
|
||||
revolt-models = { version = "0.7.11", path = "../models", features = [
|
||||
"validator",
|
||||
] }
|
||||
revolt-presence = { version = "0.7.7", path = "../presence" }
|
||||
revolt-permissions = { version = "0.7.7", path = "../permissions", features = [
|
||||
revolt-presence = { version = "0.7.11", path = "../presence" }
|
||||
revolt-permissions = { version = "0.7.11", path = "../permissions", features = [
|
||||
"serde",
|
||||
"bson",
|
||||
] }
|
||||
|
||||
@@ -80,8 +80,7 @@ impl Bot {
|
||||
return Err(create_error!(IsBot));
|
||||
}
|
||||
|
||||
let config = config().await;
|
||||
if db.get_number_of_bots_by_user(&owner.id).await? >= config.features.limits.default.bots {
|
||||
if db.get_number_of_bots_by_user(&owner.id).await? >= owner.limits().await.bots {
|
||||
return Err(create_error!(ReachedMaximumBots));
|
||||
}
|
||||
|
||||
|
||||
@@ -201,9 +201,9 @@ impl Channel {
|
||||
update_server: bool,
|
||||
) -> Result<Channel> {
|
||||
let config = config().await;
|
||||
if server.channels.len() > config.features.limits.default.server_channels {
|
||||
if server.channels.len() > config.features.limits.global.server_channels {
|
||||
return Err(create_error!(TooManyChannels {
|
||||
max: config.features.limits.default.server_channels,
|
||||
max: config.features.limits.global.server_channels,
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -263,9 +263,9 @@ impl Channel {
|
||||
data.users.insert(owner_id.to_string());
|
||||
|
||||
let config = config().await;
|
||||
if data.users.len() > config.features.limits.default.group_size {
|
||||
if data.users.len() > config.features.limits.global.group_size {
|
||||
return Err(create_error!(GroupTooLarge {
|
||||
max: config.features.limits.default.group_size,
|
||||
max: config.features.limits.global.group_size,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -346,9 +346,9 @@ impl Channel {
|
||||
}
|
||||
|
||||
let config = config().await;
|
||||
if recipients.len() >= config.features.limits.default.group_size {
|
||||
if recipients.len() >= config.features.limits.global.group_size {
|
||||
return Err(create_error!(GroupTooLarge {
|
||||
max: config.features.limits.default.group_size
|
||||
max: config.features.limits.global.group_size
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -377,6 +377,8 @@ impl Channel {
|
||||
username: &user.username,
|
||||
avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
self,
|
||||
false,
|
||||
)
|
||||
@@ -688,6 +690,8 @@ impl Channel {
|
||||
username: name,
|
||||
avatar: None,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
self,
|
||||
false,
|
||||
)
|
||||
@@ -723,6 +727,8 @@ impl Channel {
|
||||
username: &user.username,
|
||||
avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
self,
|
||||
false,
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashSet;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use revolt_config::config;
|
||||
use revolt_config::{config, FeaturesLimits};
|
||||
use revolt_models::v0::{
|
||||
self, BulkMessageResponse, DataMessageSend, Embed, MessageAuthor, MessageSort, MessageWebhook,
|
||||
PushNotification, ReplyIntent, SendableEmbed, Text, RE_MENTION,
|
||||
@@ -207,11 +207,15 @@ impl Default for Message {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
impl Message {
|
||||
/// Create message from API data
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_from_api(
|
||||
db: &Database,
|
||||
channel: Channel,
|
||||
data: DataMessageSend,
|
||||
author: MessageAuthor<'_>,
|
||||
user: Option<v0::User>,
|
||||
member: Option<v0::Member>,
|
||||
limits: FeaturesLimits,
|
||||
mut idempotency: IdempotencyKey,
|
||||
generate_embeds: bool,
|
||||
allow_mentions: bool,
|
||||
@@ -221,7 +225,7 @@ impl Message {
|
||||
Message::validate_sum(
|
||||
&data.content,
|
||||
data.embeds.as_deref().unwrap_or_default(),
|
||||
config.features.limits.default.message_length,
|
||||
limits.message_length,
|
||||
)?;
|
||||
|
||||
idempotency
|
||||
@@ -288,9 +292,9 @@ impl Message {
|
||||
// Verify replies are valid.
|
||||
let mut replies = HashSet::new();
|
||||
if let Some(entries) = data.replies {
|
||||
if entries.len() > config.features.limits.default.message_replies {
|
||||
if entries.len() > config.features.limits.global.message_replies {
|
||||
return Err(create_error!(TooManyReplies {
|
||||
max: config.features.limits.default.message_replies,
|
||||
max: config.features.limits.global.message_replies,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -320,20 +324,20 @@ impl Message {
|
||||
if data
|
||||
.attachments
|
||||
.as_ref()
|
||||
.is_some_and(|v| v.len() > config.features.limits.default.message_attachments)
|
||||
.is_some_and(|v| v.len() > limits.message_attachments)
|
||||
{
|
||||
return Err(create_error!(TooManyAttachments {
|
||||
max: config.features.limits.default.message_attachments,
|
||||
max: limits.message_attachments,
|
||||
}));
|
||||
}
|
||||
|
||||
if data
|
||||
.embeds
|
||||
.as_ref()
|
||||
.is_some_and(|v| v.len() > config.features.limits.default.message_embeds)
|
||||
.is_some_and(|v| v.len() > config.features.limits.global.message_embeds)
|
||||
{
|
||||
return Err(create_error!(TooManyEmbeds {
|
||||
max: config.features.limits.default.message_embeds,
|
||||
max: config.features.limits.global.message_embeds,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -360,7 +364,9 @@ impl Message {
|
||||
message.nonce = Some(idempotency.into_key());
|
||||
|
||||
// Send the message
|
||||
message.send(db, author, &channel, generate_embeds).await?;
|
||||
message
|
||||
.send(db, author, user, member, &channel, generate_embeds)
|
||||
.await?;
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
@@ -369,13 +375,15 @@ impl Message {
|
||||
pub async fn send_without_notifications(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
user: Option<v0::User>,
|
||||
member: Option<v0::Member>,
|
||||
is_dm: bool,
|
||||
generate_embeds: bool,
|
||||
) -> Result<()> {
|
||||
db.insert_message(self).await?;
|
||||
|
||||
// Fan out events
|
||||
EventV1::Message(self.clone().into())
|
||||
EventV1::Message(self.clone().into_model(user, member))
|
||||
.p(self.channel.to_string())
|
||||
.await;
|
||||
|
||||
@@ -416,11 +424,15 @@ impl Message {
|
||||
&mut self,
|
||||
db: &Database,
|
||||
author: MessageAuthor<'_>,
|
||||
user: Option<v0::User>,
|
||||
member: Option<v0::Member>,
|
||||
channel: &Channel,
|
||||
generate_embeds: bool,
|
||||
) -> Result<()> {
|
||||
self.send_without_notifications(
|
||||
db,
|
||||
user,
|
||||
member,
|
||||
matches!(channel, Channel::DirectMessage { .. }),
|
||||
generate_embeds,
|
||||
)
|
||||
@@ -436,7 +448,12 @@ impl Message {
|
||||
_ => vec![],
|
||||
}
|
||||
},
|
||||
PushNotification::from(self.clone().into(), Some(author), &channel.id()).await,
|
||||
PushNotification::from(
|
||||
self.clone().into_model(None, None),
|
||||
Some(author),
|
||||
&channel.id(),
|
||||
)
|
||||
.await,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -498,7 +515,7 @@ impl Message {
|
||||
.fetch_messages(query)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.map(|msg| msg.into_model(None, None))
|
||||
.collect();
|
||||
|
||||
if let Some(true) = include_users {
|
||||
@@ -616,7 +633,7 @@ impl Message {
|
||||
pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> {
|
||||
// Check how many reactions are already on the message
|
||||
let config = config().await;
|
||||
if self.reactions.len() >= config.features.limits.default.message_reactions
|
||||
if self.reactions.len() >= config.features.limits.global.message_reactions
|
||||
&& !self.reactions.contains_key(emoji)
|
||||
{
|
||||
return Err(create_error!(InvalidOperation));
|
||||
@@ -781,7 +798,7 @@ impl Interactions {
|
||||
if let Some(reactions) = &self.reactions {
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::React)?;
|
||||
|
||||
if reactions.len() > config.features.limits.default.message_reactions {
|
||||
if reactions.len() > config.features.limits.global.message_reactions {
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ impl Member {
|
||||
id: user.id.clone(),
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
.send_without_notifications(db, false, false)
|
||||
.send_without_notifications(db, None, None, false, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
@@ -250,7 +250,7 @@ impl Member {
|
||||
}
|
||||
.into_message(id.to_string())
|
||||
// TODO: support notifications here in the future?
|
||||
.send_without_notifications(db, false, false)
|
||||
.send_without_notifications(db, None, None, false, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
use std::{collections::HashSet, str::FromStr, time::Duration};
|
||||
|
||||
use crate::{events::client::EventV1, Database, File, RatelimitEvent};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::seq::SliceRandom;
|
||||
use revolt_config::config;
|
||||
use revolt_config::{config, FeaturesLimits};
|
||||
use revolt_models::v0;
|
||||
use revolt_presence::filter_online;
|
||||
use revolt_result::{create_error, Result};
|
||||
@@ -197,6 +197,22 @@ impl User {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
/// Get limits for this user
|
||||
pub async fn limits(&self) -> FeaturesLimits {
|
||||
let config = config().await;
|
||||
if ulid::Ulid::from_str(&self.id)
|
||||
.expect("`ulid`")
|
||||
.datetime()
|
||||
.elapsed()
|
||||
.expect("time went backwards")
|
||||
<= Duration::from_secs(86400u64 * config.features.limits.global.new_user_days as u64)
|
||||
{
|
||||
config.features.limits.new_user
|
||||
} else {
|
||||
config.features.limits.default
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the relationship with another user
|
||||
pub fn relationship_with(&self, user_b: &str) -> RelationshipStatus {
|
||||
if self.id == user_b {
|
||||
@@ -235,12 +251,11 @@ impl User {
|
||||
|
||||
/// Check if this user can acquire another server
|
||||
pub async fn can_acquire_server(&self, db: &Database) -> Result<()> {
|
||||
let config = config().await;
|
||||
if db.fetch_server_count(&self.id).await? <= config.features.limits.default.servers {
|
||||
if db.fetch_server_count(&self.id).await? <= self.limits().await.servers {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(TooManyServers {
|
||||
max: config.features.limits.default.servers
|
||||
max: self.limits().await.servers
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -477,6 +492,7 @@ impl User {
|
||||
RelationshipStatus::Blocked => Err(create_error!(Blocked)),
|
||||
RelationshipStatus::BlockedOther => Err(create_error!(BlockedByOther)),
|
||||
RelationshipStatus::Incoming => {
|
||||
// Accept incoming friend request
|
||||
self.apply_relationship(
|
||||
db,
|
||||
target,
|
||||
@@ -486,6 +502,26 @@ impl User {
|
||||
.await
|
||||
}
|
||||
RelationshipStatus::None => {
|
||||
// Get this user's current count of outgoing friend requests
|
||||
let count = self
|
||||
.relations
|
||||
.as_ref()
|
||||
.map(|relations| {
|
||||
relations
|
||||
.iter()
|
||||
.filter(|r| matches!(r.status, RelationshipStatus::Outgoing))
|
||||
.count()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
// If we're over the limit, don't allow creating more requests
|
||||
if count >= self.limits().await.outgoing_friend_requests {
|
||||
return Err(create_error!(TooManyPendingFriendRequests {
|
||||
max: self.limits().await.outgoing_friend_requests
|
||||
}));
|
||||
}
|
||||
|
||||
// Send the friend request
|
||||
self.apply_relationship(
|
||||
db,
|
||||
target,
|
||||
|
||||
@@ -56,7 +56,7 @@ pub async fn worker(db: Database) {
|
||||
let embeds = generate(
|
||||
task.content,
|
||||
&config.hosts.january,
|
||||
config.features.limits.default.message_embeds,
|
||||
config.features.limits.global.message_embeds,
|
||||
semaphore,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -459,26 +459,28 @@ impl From<Metadata> for crate::Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::Message> for Message {
|
||||
fn from(value: crate::Message) -> Self {
|
||||
impl crate::Message {
|
||||
pub fn into_model(self, user: Option<User>, member: Option<Member>) -> Message {
|
||||
Message {
|
||||
id: value.id,
|
||||
nonce: value.nonce,
|
||||
channel: value.channel,
|
||||
author: value.author,
|
||||
webhook: value.webhook,
|
||||
content: value.content,
|
||||
system: value.system.map(|system| system.into()),
|
||||
attachments: value
|
||||
id: self.id,
|
||||
nonce: self.nonce,
|
||||
channel: self.channel,
|
||||
author: self.author,
|
||||
user,
|
||||
member,
|
||||
webhook: self.webhook,
|
||||
content: self.content,
|
||||
system: self.system.map(|system| system.into()),
|
||||
attachments: self
|
||||
.attachments
|
||||
.map(|v| v.into_iter().map(|f| f.into()).collect()),
|
||||
edited: value.edited,
|
||||
embeds: value.embeds,
|
||||
mentions: value.mentions,
|
||||
replies: value.replies,
|
||||
reactions: value.reactions,
|
||||
interactions: value.interactions.into(),
|
||||
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||
edited: self.edited,
|
||||
embeds: self.embeds,
|
||||
mentions: self.mentions,
|
||||
replies: self.replies,
|
||||
reactions: self.reactions,
|
||||
interactions: self.interactions.into(),
|
||||
masquerade: self.masquerade.map(|masq| masq.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,6 +492,8 @@ impl From<crate::PartialMessage> for PartialMessage {
|
||||
nonce: value.nonce,
|
||||
channel: value.channel,
|
||||
author: value.author,
|
||||
user: None,
|
||||
member: None,
|
||||
webhook: value.webhook,
|
||||
content: value.content,
|
||||
system: value.system.map(|system| system.into()),
|
||||
@@ -1089,7 +1093,33 @@ impl crate::User {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn into_self(self) -> User {
|
||||
/// Convert user object into user model without presence information
|
||||
pub fn into_known_static<'a>(self, is_online: bool) -> User {
|
||||
User {
|
||||
username: self.username,
|
||||
discriminator: self.discriminator,
|
||||
display_name: self.display_name,
|
||||
avatar: self.avatar.map(|file| file.into()),
|
||||
relations: vec![],
|
||||
badges: self.badges.unwrap_or_default() as u32,
|
||||
online: is_online
|
||||
&& !matches!(
|
||||
self.status,
|
||||
Some(crate::UserStatus {
|
||||
presence: Some(crate::Presence::Invisible),
|
||||
..
|
||||
})
|
||||
),
|
||||
status: self.status.map(|status| status.into()),
|
||||
flags: self.flags.unwrap_or_default() as u32,
|
||||
privileged: self.privileged,
|
||||
bot: self.bot.map(|bot| bot.into()),
|
||||
relationship: RelationshipStatus::None, // events client will populate this from cache
|
||||
id: self.id,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn into_self(self, force_online: bool) -> User {
|
||||
User {
|
||||
username: self.username,
|
||||
discriminator: self.discriminator,
|
||||
@@ -1105,7 +1135,7 @@ impl crate::User {
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
badges: self.badges.unwrap_or_default() as u32,
|
||||
online: revolt_presence::is_online(&self.id).await
|
||||
online: (force_online || revolt_presence::is_online(&self.id).await)
|
||||
&& !matches!(
|
||||
self.status,
|
||||
Some(crate::UserStatus {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-models"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = ["Paul Makles <me@insrt.uk>"]
|
||||
@@ -19,8 +19,8 @@ default = ["serde", "partials", "rocket"]
|
||||
|
||||
[dependencies]
|
||||
# Core
|
||||
revolt-config = { version = "0.7.7", path = "../config" }
|
||||
revolt-permissions = { version = "0.7.7", path = "../permissions" }
|
||||
revolt-config = { version = "0.7.11", path = "../config" }
|
||||
revolt-permissions = { version = "0.7.11", path = "../permissions" }
|
||||
|
||||
# Utility
|
||||
regex = "1"
|
||||
|
||||
@@ -31,6 +31,12 @@ auto_derived_partial!(
|
||||
pub channel: String,
|
||||
/// Id of the user or webhook that sent this message
|
||||
pub author: String,
|
||||
/// The user that sent this message
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub user: Option<User>,
|
||||
/// The member that sent this message
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub member: Option<Member>,
|
||||
/// The webhook that sent this message
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub webhook: Option<MessageWebhook>,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-permissions"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = ["Paul Makles <me@insrt.uk>"]
|
||||
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
|
||||
|
||||
[dependencies]
|
||||
# Core
|
||||
revolt-result = { version = "0.7.7", path = "../result" }
|
||||
revolt-result = { version = "0.7.11", path = "../result" }
|
||||
|
||||
# Utility
|
||||
auto_ops = "0.3.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-presence"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
authors = ["Paul Makles <me@insrt.uk>"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-result"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = ["Paul Makles <me@insrt.uk>"]
|
||||
|
||||
@@ -60,6 +60,9 @@ pub enum ErrorType {
|
||||
Blocked,
|
||||
BlockedByOther,
|
||||
NotFriends,
|
||||
TooManyPendingFriendRequests {
|
||||
max: usize,
|
||||
},
|
||||
|
||||
// ? Channel related errors
|
||||
UnknownChannel,
|
||||
|
||||
@@ -25,6 +25,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
||||
ErrorType::Blocked => Status::Conflict,
|
||||
ErrorType::BlockedByOther => Status::Forbidden,
|
||||
ErrorType::NotFriends => Status::Forbidden,
|
||||
ErrorType::TooManyPendingFriendRequests { .. } => Status::BadRequest,
|
||||
|
||||
ErrorType::UnknownChannel => Status::NotFound,
|
||||
ErrorType::UnknownMessage => Status::NotFound,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revolt-delta"
|
||||
version = "0.7.7"
|
||||
version = "0.7.11"
|
||||
license = "AGPL-3.0-or-later"
|
||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||
edition = "2018"
|
||||
@@ -77,6 +77,7 @@ revolt-models = { path = "../core/models", features = [
|
||||
"validator",
|
||||
"rocket",
|
||||
] }
|
||||
revolt-presence = { path = "../core/presence" }
|
||||
revolt-result = { path = "../core/result", features = ["rocket", "okapi"] }
|
||||
revolt-permissions = { path = "../core/permissions", features = ["schemas"] }
|
||||
|
||||
|
||||
@@ -47,8 +47,7 @@ pub async fn web() -> Rocket<Build> {
|
||||
authifier::database::MongoDb(client.database("revolt")),
|
||||
),
|
||||
},
|
||||
config: Default::default(),
|
||||
// config: authifier_config().await,
|
||||
config: authifier_config().await,
|
||||
event_channel: Some(sender),
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ pub async fn fetch_owned_bots(db: &State<Database>, user: User) -> Result<Json<O
|
||||
users.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
|
||||
Ok(Json(OwnedBotsResponse {
|
||||
users: join_all(users.into_iter().map(|user| user.into_self())).await,
|
||||
users: join_all(users.into_iter().map(|user| user.into_self(false))).await,
|
||||
bots: bots.into_iter().map(|bot| bot.into()).collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ pub async fn edit(
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
.into_message(channel.id().to_string())
|
||||
.send(db, user.as_author_for_system(), &channel, false)
|
||||
.send(db, user.as_author_for_system(), None, None, &channel, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
@@ -151,7 +151,7 @@ pub async fn edit(
|
||||
by: user.id.clone(),
|
||||
}
|
||||
.into_message(channel.id().to_string())
|
||||
.send(db, user.as_author_for_system(), &channel, false)
|
||||
.send(db, user.as_author_for_system(), None, None, &channel, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
@@ -161,7 +161,7 @@ pub async fn edit(
|
||||
by: user.id.clone(),
|
||||
}
|
||||
.into_message(channel.id().to_string())
|
||||
.send(db, user.as_author_for_system(), &channel, false)
|
||||
.send(db, user.as_author_for_system(), None, None, &channel, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
@@ -171,7 +171,7 @@ pub async fn edit(
|
||||
by: user.id.clone(),
|
||||
}
|
||||
.into_message(channel.id().to_string())
|
||||
.send(db, user.as_author_for_system(), &channel, false)
|
||||
.send(db, user.as_author_for_system(), None, None, &channel, false)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -30,11 +30,10 @@ pub async fn edit(
|
||||
})
|
||||
})?;
|
||||
|
||||
let config = config().await;
|
||||
Message::validate_sum(
|
||||
&edit.content,
|
||||
edit.embeds.as_deref().unwrap_or_default(),
|
||||
config.features.limits.default.message_length,
|
||||
user.limits().await.message_length,
|
||||
)?;
|
||||
|
||||
// Ensure we have permissions to send a message
|
||||
@@ -98,5 +97,5 @@ pub async fn edit(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Json(message.into()))
|
||||
Ok(Json(message.into_model(None, None)))
|
||||
}
|
||||
|
||||
@@ -29,5 +29,5 @@ pub async fn fetch(
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
Ok(Json(message.into()))
|
||||
Ok(Json(message.into_model(None, None)))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use revolt_database::{
|
||||
};
|
||||
use revolt_database::{Interactions, Message};
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::PermissionQuery;
|
||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::serde::json::Json;
|
||||
@@ -75,17 +76,34 @@ pub async fn message_send(
|
||||
|
||||
// Create the message
|
||||
let author: v0::User = user.clone().into(db, Some(&user)).await;
|
||||
|
||||
// Make sure we have server member (edge case if server owner)
|
||||
query.are_we_a_member().await;
|
||||
|
||||
// Create model user / members
|
||||
let model_user = user
|
||||
.clone()
|
||||
.into_known_static(revolt_presence::is_online(&user.id).await);
|
||||
|
||||
let model_member: Option<v0::Member> = query
|
||||
.member_ref()
|
||||
.as_ref()
|
||||
.map(|member| member.clone().into_owned().into());
|
||||
|
||||
Ok(Json(
|
||||
Message::create_from_api(
|
||||
db,
|
||||
channel,
|
||||
data,
|
||||
v0::MessageAuthor::User(&author),
|
||||
Some(model_user.clone()),
|
||||
model_member.clone(),
|
||||
user.limits().await,
|
||||
idempotency,
|
||||
permissions.has_channel_permission(ChannelPermission::SendEmbeds),
|
||||
allow_mentions,
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
.into_model(Some(model_user), model_member),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ pub async fn create_emoji(
|
||||
|
||||
// Check that we haven't hit the emoji limit
|
||||
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
||||
if emojis.len() >= config.features.limits.default.server_emoji {
|
||||
if emojis.len() >= config.features.limits.global.server_emoji {
|
||||
return Err(create_error!(TooManyEmoji {
|
||||
max: config.features.limits.default.server_emoji,
|
||||
max: config.features.limits.global.server_emoji,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ pub async fn complete(
|
||||
Ok(Json(
|
||||
User::create(db, data.username, session.user_id, None)
|
||||
.await?
|
||||
.into_self()
|
||||
.into_self(false)
|
||||
.await,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ pub async fn list(
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|u| u.into_self()),
|
||||
.map(|u| u.into_self(false)),
|
||||
)
|
||||
.await;
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ pub async fn create(
|
||||
.throw_if_lacking_channel_permission(ChannelPermission::ManageRole)?;
|
||||
|
||||
let config = config().await;
|
||||
if server.roles.len() >= config.features.limits.default.server_roles {
|
||||
if server.roles.len() >= config.features.limits.global.server_roles {
|
||||
return Err(create_error!(TooManyRoles {
|
||||
max: config.features.limits.default.server_roles,
|
||||
max: config.features.limits.global.server_roles,
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ pub async fn edit(
|
||||
&& data.flags.is_none()
|
||||
&& data.remove.is_none()
|
||||
{
|
||||
return Ok(Json(user.into_self().await));
|
||||
return Ok(Json(user.into_self(false).await));
|
||||
}
|
||||
|
||||
// 1. Remove fields from object
|
||||
@@ -126,5 +126,5 @@ pub async fn edit(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(user.into_self().await))
|
||||
Ok(Json(user.into_self(false).await))
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@ use rocket::serde::json::Json;
|
||||
#[openapi(tag = "User Information")]
|
||||
#[get("/@me")]
|
||||
pub async fn fetch(user: User) -> Result<Json<v0::User>> {
|
||||
Ok(Json(user.into_self().await))
|
||||
Ok(Json(user.into_self(false).await))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use rocket::{serde::json::Json, State};
|
||||
#[get("/<target>")]
|
||||
pub async fn fetch(db: &State<Database>, user: User, target: Reference) -> Result<Json<v0::User>> {
|
||||
if user.id == target.id {
|
||||
return Ok(Json(user.into_self().await));
|
||||
return Ok(Json(user.into_self(false).await));
|
||||
}
|
||||
|
||||
let target = target.as_user(db).await?;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use revolt_config::config;
|
||||
use revolt_database::{
|
||||
util::{idempotency::IdempotencyKey, reference::Reference},
|
||||
Database, Message,
|
||||
@@ -58,11 +59,14 @@ pub async fn webhook_execute(
|
||||
channel,
|
||||
data,
|
||||
v0::MessageAuthor::Webhook(&webhook.into()),
|
||||
None,
|
||||
None,
|
||||
config().await.features.limits.default,
|
||||
idempotency,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.await?
|
||||
.into(),
|
||||
.into_model(None, None),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1072,6 +1072,13 @@ pub async fn webhook_execute_github(
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
message.attach_sendable_embed(db, sendable_embed).await?;
|
||||
message
|
||||
.send(db, MessageAuthor::Webhook(&webhook.into()), &channel, false)
|
||||
.send(
|
||||
db,
|
||||
MessageAuthor::Webhook(&webhook.into()),
|
||||
None,
|
||||
None,
|
||||
&channel,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user