use chrono::{Duration, Utc}; use revolt_database::util::permissions::DatabasePermissionQuery; use revolt_database::{ util::idempotency::IdempotencyKey, util::reference::Reference, Database, User, }; use revolt_database::{Interactions, Message}; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use rocket::serde::json::Json; use rocket::State; use validator::Validate; /// # Send Message /// /// Sends a message to the given channel. #[openapi(tag = "Messaging")] #[post("//messages", data = "")] pub async fn message_send( db: &State, user: User, target: Reference, data: Json, idempotency: IdempotencyKey, ) -> Result> { let data = data.into_inner(); data.validate().map_err(|error| { create_error!(FailedValidation { error: error.to_string() }) })?; // Ensure we have permissions to send a message let channel = target.as_channel(db).await?; let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel); let permissions = calculate_channel_permissions(&mut query).await; permissions.throw_if_lacking_channel_permission(ChannelPermission::SendMessage)?; // Verify permissions for masquerade if let Some(masq) = &data.masquerade { permissions.throw_if_lacking_channel_permission(ChannelPermission::Masquerade)?; if masq.colour.is_some() { permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageRole)?; } } // Check permissions for embeds if data.embeds.as_ref().is_some_and(|v| !v.is_empty()) { permissions.throw_if_lacking_channel_permission(ChannelPermission::SendEmbeds)?; } // Check permissions for files if data.attachments.as_ref().is_some_and(|v| !v.is_empty()) { permissions.throw_if_lacking_channel_permission(ChannelPermission::UploadFiles)?; } // Ensure interactions information is correct if let Some(interactions) = &data.interactions { let interactions: Interactions = interactions.clone().into(); 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 { (Utc::now() - ulid::Ulid::from_string(&user.id).unwrap().datetime()) >= Duration::hours(12) } else { true } } else { true }; // Create the message let author: v0::User = user.clone().into(db, Some(&user)).await; Ok(Json( Message::create_from_api( db, channel, data, v0::MessageAuthor::User(&author), idempotency, permissions.has_channel_permission(ChannelPermission::SendEmbeds), allow_mentions, ) .await? .into(), )) }