Merge remote-tracking branch 'origin/main' into livekit

This commit is contained in:
Zomatree
2025-01-26 20:36:40 +00:00
227 changed files with 16549 additions and 2943 deletions

View File

@@ -6,6 +6,10 @@ extern crate serde;
#[macro_use]
extern crate schemars;
#[cfg(feature = "utoipa")]
#[macro_use]
extern crate utoipa;
#[cfg(feature = "partials")]
#[macro_use]
extern crate revolt_optional_struct;
@@ -18,6 +22,7 @@ macro_rules! auto_derived {
$(
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemas", derive(JsonSchema))]
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
#[derive(Debug, Clone, Eq, PartialEq)]
$item
)+
@@ -66,3 +71,8 @@ pub fn if_false(t: &bool) -> bool {
pub fn if_zero_u32(t: &u32) -> bool {
t == &0
}
/// Utility function to check if an option doesnt contain true
pub fn if_option_false(t: &Option<bool>) -> bool {
t != &Some(true)
}

View File

@@ -162,4 +162,11 @@ auto_derived!(
/// User objects
pub users: Vec<User>,
}
/// Bot with user response
pub struct BotWithUserResponse {
#[serde(flatten)]
pub bot: Bot,
pub user: User,
}
);

View File

@@ -2,6 +2,7 @@ use super::{Channel, File, Server, User};
auto_derived!(
/// Invite
#[serde(tag = "type")]
pub enum Invite {
/// Invite to a specific server channel
Server {

View File

@@ -16,6 +16,9 @@ auto_derived_partial!(
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub avatar: Option<File>,
/// User that created this webhook
pub creator_id: String,
/// The channel this webhook belongs to
pub channel_id: String,

View File

@@ -331,4 +331,18 @@ impl Channel {
| Channel::VoiceChannel { id, .. } => id,
}
}
/// This returns a Result because the recipient name can't be determined here without a db call,
/// which can't be done since this is models, which can't reference the database crate.
///
/// If it returns Err, you need to fetch the name from the db.
pub fn name(&self) -> Result<&str, ()> {
match self {
Channel::DirectMessage { .. } => Err(()),
Channel::SavedMessages { .. } => Ok("Saved Messages"),
Channel::TextChannel { name, .. }
| Channel::Group { name, .. }
| Channel::VoiceChannel { name, .. } => Ok(name),
}
}
}

View File

@@ -14,9 +14,9 @@ auto_derived!(
/// URL to the original image
pub url: String,
/// Width of the image
pub width: isize,
pub width: usize,
/// Height of the image
pub height: isize,
pub height: usize,
/// Positioning and size
pub size: ImageSize,
}
@@ -26,9 +26,9 @@ auto_derived!(
/// URL to the original video
pub url: String,
/// Width of the video
pub width: isize,
pub width: usize,
/// Height of the video
pub height: isize,
pub height: usize,
}
/// Type of remote Twitch content
@@ -84,6 +84,12 @@ auto_derived!(
content_type: BandcampType,
id: String,
},
AppleMusic {
album_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
track_id: Option<String>,
},
/// Streamable Video
Streamable { id: String },
}
@@ -92,38 +98,36 @@ auto_derived!(
pub struct WebsiteMetadata {
/// Direct URL to web page
#[serde(skip_serializing_if = "Option::is_none")]
url: Option<String>,
pub url: Option<String>,
/// Original direct URL
#[serde(skip_serializing_if = "Option::is_none")]
original_url: Option<String>,
pub original_url: Option<String>,
/// Remote content
#[serde(skip_serializing_if = "Option::is_none")]
special: Option<Special>,
pub special: Option<Special>,
/// Title of website
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
pub title: Option<String>,
/// Description of website
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
pub description: Option<String>,
/// Embedded image
#[serde(skip_serializing_if = "Option::is_none")]
image: Option<Image>,
pub image: Option<Image>,
/// Embedded video
#[serde(skip_serializing_if = "Option::is_none")]
video: Option<Video>,
pub video: Option<Video>,
// #[serde(skip_serializing_if = "Option::is_none")]
// opengraph_type: Option<String>,
/// Site name
#[serde(skip_serializing_if = "Option::is_none")]
site_name: Option<String>,
pub site_name: Option<String>,
/// URL to site icon
#[serde(skip_serializing_if = "Option::is_none")]
icon_url: Option<String>,
pub icon_url: Option<String>,
/// CSS Colour
#[serde(skip_serializing_if = "Option::is_none")]
colour: Option<String>,
pub colour: Option<String>,
}
/// Text Embed
@@ -150,11 +154,56 @@ auto_derived!(
/// Embed
#[serde(tag = "type")]
#[derive(Default)]
pub enum Embed {
Website(WebsiteMetadata),
Image(Image),
Video(Video),
Text(Text),
#[default]
None,
}
);
impl WebsiteMetadata {
/// Truncate strings in metadata
pub fn truncate(&mut self) {
if let Some(s) = self.url.as_mut() {
s.truncate(256);
}
if let Some(s) = self.original_url.as_mut() {
s.truncate(256);
}
if let Some(s) = self.title.as_mut() {
s.truncate(100);
}
if let Some(s) = self.description.as_mut() {
s.truncate(1000);
}
if let Some(s) = self.site_name.as_mut() {
s.truncate(32);
}
if let Some(s) = self.icon_url.as_mut() {
s.truncate(256);
}
if let Some(s) = self.colour.as_mut() {
s.truncate(32);
}
}
/// Check if this is considered "empty"
pub fn is_empty(&self) -> bool {
(self.title.is_none() || self.title.as_ref().is_some_and(|f| f.is_empty()))
&& (self.description.is_none()
|| self.description.as_ref().is_some_and(|f| f.is_empty()))
&& self.special.is_none()
&& self.video.is_none()
&& self.image.is_none()
}
}

View File

@@ -13,7 +13,7 @@ use rocket::{FromForm, FromFormField};
use iso8601_timestamp::Timestamp;
use super::{Embed, File, Member, MessageWebhook, User, Webhook, RE_COLOUR};
use super::{Channel, Embed, File, Member, MessageWebhook, User, Webhook, RE_COLOUR};
pub static RE_MENTION: Lazy<Regex> =
Lazy::new(|| Regex::new(r"<@([0-9A-HJKMNP-TV-Z]{26})>").unwrap());
@@ -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>,
@@ -64,6 +70,18 @@ auto_derived_partial!(
/// Name and / or avatar overrides for this message
#[serde(skip_serializing_if = "Option::is_none")]
pub masquerade: Option<Masquerade>,
/// Whether or not the message in pinned
#[serde(skip_serializing_if = "crate::if_option_false")]
pub pinned: Option<bool>,
/// Bitfield of message flags
///
/// https://docs.rs/revolt-models/latest/revolt_models/v0/enum.MessageFlags.html
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "crate::if_zero_u32", default)
)]
pub flags: u32,
},
"PartialMessage"
);
@@ -112,6 +130,10 @@ auto_derived!(
ChannelIconChanged { by: String },
#[serde(rename = "channel_ownership_changed")]
ChannelOwnershipChanged { from: String, to: String },
#[serde(rename = "message_pinned")]
MessagePinned { id: String, by: String },
#[serde(rename = "message_unpinned")]
MessageUnpinned { id: String, by: String },
}
/// Name and / or avatar override information
@@ -185,6 +207,10 @@ auto_derived!(
pub timestamp: u64,
/// URL to open when clicking notification
pub url: String,
/// The message object itself, to send to clients for processing
pub message: Message,
/// The channel object itself, for clients to process
pub channel: Channel,
}
/// Representation of a text embed before it is sent.
@@ -241,6 +267,11 @@ auto_derived!(
pub masquerade: Option<Masquerade>,
/// Information about how this message should be interacted with
pub interactions: Option<Interactions>,
/// Bitfield of message flags
///
/// https://docs.rs/revolt-models/latest/revolt_models/v0/enum.MessageFlags.html
pub flags: Option<u32>,
}
/// Options for querying messages
@@ -278,7 +309,9 @@ auto_derived!(
///
/// See [MongoDB documentation](https://docs.mongodb.com/manual/text-search/#-text-operator) for more information.
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 64)))]
pub query: String,
pub query: Option<String>,
/// Whether to only search for pinned messages, cannot be sent with `query`.
pub pinned: Option<bool>,
/// Maximum number of messages to fetch
#[cfg_attr(feature = "validator", validate(range(min = 1, max = 100)))]
@@ -328,6 +361,18 @@ auto_derived!(
/// Remove all reactions
pub remove_all: Option<bool>,
}
/// Message flag bitfield
#[repr(u32)]
pub enum MessageFlags {
/// Message will not send push / desktop notifications
SuppressNotifications = 1,
}
/// Optional fields on message
pub enum FieldsMessage {
Pinned,
}
);
/// Message Author Abstraction
@@ -391,13 +436,15 @@ impl From<SystemMessage> for String {
SystemMessage::ChannelOwnershipChanged { .. } => {
"Channel ownership changed.".to_string()
}
SystemMessage::MessagePinned { .. } => "Message pinned.".to_string(),
SystemMessage::MessageUnpinned { .. } => "Message unpinned.".to_string(),
}
}
}
impl PushNotification {
/// Create a new notification from a given message, author and channel ID
pub async fn from(msg: Message, author: Option<MessageAuthor<'_>>, channel_id: &str) -> Self {
pub async fn from(msg: Message, author: Option<MessageAuthor<'_>>, channel: Channel) -> Self {
let config = config().await;
let icon = if let Some(author) = &author {
@@ -410,15 +457,30 @@ impl PushNotification {
format!("{}/assets/logo.png", config.hosts.app)
};
let image = msg.attachments.and_then(|attachments| {
let image = msg.attachments.as_ref().and_then(|attachments| {
attachments
.first()
.map(|v| format!("{}/attachments/{}", config.hosts.autumn, v.id))
});
let body = if let Some(sys) = msg.system {
sys.into()
} else if let Some(text) = msg.content {
let body = if let Some(ref sys) = msg.system {
sys.clone().into()
} else if let Some(ref text) = msg.content {
text.clone()
} else if let Some(text) = msg.embeds.as_ref().and_then(|embeds| match embeds.first() {
Some(Embed::Image(_)) => Some("Sent an image".to_string()),
Some(Embed::Video(_)) => Some("Sent a video".to_string()),
Some(Embed::Text(e)) => e
.description
.clone()
.or(e.title.clone().or(Some("Empty Embed".to_string()))),
Some(Embed::Website(e)) => e.title.clone().or(e
.description
.clone()
.or(e.site_name.clone().or(Some("Empty Embed".to_string())))),
Some(Embed::None) => Some("Empty Message".to_string()), // ???
None => Some("Empty Message".to_string()), // ??
}) {
text
} else {
"Empty Message".to_string()
@@ -436,9 +498,11 @@ impl PushNotification {
icon,
image,
body,
tag: channel_id.to_string(),
tag: channel.id().to_string(),
timestamp,
url: format!("{}/channel/{}/{}", config.hosts.app, channel_id, msg.id),
url: format!("{}/channel/{}/{}", config.hosts.app, channel.id(), msg.id),
message: msg,
channel,
}
}
}

View File

@@ -42,6 +42,8 @@ auto_derived_partial!(
pub relations: Vec<Relationship>,
/// Bitfield of user badges
///
/// https://docs.rs/revolt-models/latest/revolt_models/v0/enum.UserBadges.html
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "crate::if_zero_u32", default)
@@ -50,11 +52,10 @@ auto_derived_partial!(
/// User's current status
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub status: Option<UserStatus>,
/// User's profile page
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub profile: Option<UserProfile>,
/// Enum of user flags
///
/// https://docs.rs/revolt-models/latest/revolt_models/v0/enum.UserFlags.html
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "crate::if_zero_u32", default)
@@ -86,6 +87,10 @@ auto_derived!(
StatusPresence,
ProfileContent,
ProfileBackground,
DisplayName,
/// Internal field, ignore this.
Internal,
}
/// User's relationship with another user (or themselves)
@@ -188,7 +193,7 @@ auto_derived!(
#[repr(u32)]
pub enum UserFlags {
/// User has been suspended from the platform
Suspended = 1,
SuspendedUntil = 1,
/// User has deleted their account
Deleted = 2,
/// User was banned off the platform
@@ -276,8 +281,8 @@ auto_derived_partial!(
/// Voice State information for a user
pub struct UserVoiceState {
pub id: String,
pub can_receive: bool,
pub can_publish: bool,
pub is_receiving: bool,
pub is_publishing: bool,
pub screensharing: bool,
pub camera: bool,
},