Compare commits

...

3 Commits

Author SHA1 Message Date
kozabrada123 3946859bd0 Fix: deser error with guild member flags 2024-06-27 09:39:01 +02:00
kozabrada123 de9501b428 Merge branch 'dev' into some-routes 2024-06-27 09:20:38 +02:00
Quat3rnion 39e7f89c78
Backend/guilds (#509)
* Fix SQL encode/decode for GuildFeatures

* Use distinct PermissionFlags type

* Add Emoji schema types, modify GuildBan with feature lock

* Add Schemas for pruning guild members

* Add schemas for interfacing with stickers backend routes

* Add schemas for interfacing with vanity-url backend routes

* Add schema for interfacing with guilds/id/welcome-screen route

* Make all Option<Vec> types Vec types with #[serde(default)]

* Add various types to support guilds/* api routes

* Add missing enums and structs for searching messages

* Use proper distinct types

* Add EmbedType enum

* Use distinct PermissionFlags type

* Changes supporting backend for VoiceState

* Changes supporting backend for AuditLog's
2024-06-27 08:45:51 +02:00
25 changed files with 716 additions and 105 deletions

19
Cargo.lock generated
View File

@ -1272,6 +1272,24 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "multer"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http 1.1.0",
"httparse",
"memchr",
"mime",
"spin 0.9.8",
"tokio",
"version_check",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.11" version = "0.2.11"
@ -1609,6 +1627,7 @@ dependencies = [
"hyper 1.3.1", "hyper 1.3.1",
"hyper-util", "hyper-util",
"mime", "mime",
"multer",
"nix 0.28.0", "nix 0.28.0",
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",

View File

@ -38,7 +38,7 @@ http = "0.2.11"
base64 = "0.21.7" base64 = "0.21.7"
bitflags = { version = "2.4.1", features = ["serde"] } bitflags = { version = "2.4.1", features = ["serde"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
poem = { version = "3.0.1", optional = true } poem = { version = "3.0.1", features = ["multipart"], optional = true }
thiserror = "1.0.56" thiserror = "1.0.56"
jsonwebtoken = "8.3.0" jsonwebtoken = "8.3.0"
log = "0.4.20" log = "0.4.20"

View File

@ -3,19 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[cfg(feature = "sqlx")]
use std::io::Write;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::{
database::{HasArguments, HasValueRef},
encode::IsNull,
error::BoxDynError,
Decode, MySql,
};
use crate::types::config::types::subconfigs::guild::{ use crate::types::config::types::subconfigs::guild::{
autojoin::AutoJoinConfiguration, discovery::DiscoverConfiguration, autojoin::AutoJoinConfiguration, discovery::DiscoverConfiguration,
@ -172,8 +163,8 @@ impl Display for GuildFeaturesList {
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
impl<'r> sqlx::Decode<'r, sqlx::MySql> for GuildFeaturesList { impl<'r> sqlx::Decode<'r, sqlx::MySql> for GuildFeaturesList {
fn decode(value: <MySql as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> { fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'r>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
let v = <&str as Decode<sqlx::MySql>>::decode(value)?; let v = <String as sqlx::Decode<sqlx::MySql>>::decode(value)?;
Ok(Self( Ok(Self(
v.split(',') v.split(',')
.filter(|f| !f.is_empty()) .filter(|f| !f.is_empty())
@ -185,9 +176,9 @@ impl<'r> sqlx::Decode<'r, sqlx::MySql> for GuildFeaturesList {
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::MySql> for GuildFeaturesList { impl<'q> sqlx::Encode<'q, sqlx::MySql> for GuildFeaturesList {
fn encode_by_ref(&self, buf: &mut <MySql as HasArguments<'q>>::ArgumentBuffer) -> IsNull { fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
if self.is_empty() { if self.is_empty() {
return IsNull::Yes; return sqlx::encode::IsNull::Yes;
} }
let features = self let features = self
.iter() .iter()
@ -195,30 +186,18 @@ impl<'q> sqlx::Encode<'q, sqlx::MySql> for GuildFeaturesList {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(","); .join(",");
let _ = buf.write(features.as_bytes()); <String as sqlx::Encode<sqlx::MySql>>::encode_by_ref(&features, buf)
IsNull::No
} }
} }
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::MySql> for GuildFeaturesList { impl sqlx::Type<sqlx::MySql> for GuildFeaturesList {
fn type_info() -> sqlx::mysql::MySqlTypeInfo { fn type_info() -> sqlx::mysql::MySqlTypeInfo {
<&str as sqlx::Type<sqlx::MySql>>::type_info() <String as sqlx::Type<sqlx::MySql>>::type_info()
} }
fn compatible(ty: &sqlx::mysql::MySqlTypeInfo) -> bool { fn compatible(ty: &sqlx::mysql::MySqlTypeInfo) -> bool {
<&str as sqlx::Type<sqlx::MySql>>::compatible(ty) <String as sqlx::Type<sqlx::MySql>>::compatible(ty)
}
}
#[cfg(feature = "sqlx")]
impl sqlx::TypeInfo for GuildFeaturesList {
fn is_null(&self) -> bool {
false
}
fn name(&self) -> &str {
"TEXT"
} }
} }

View File

@ -3,21 +3,27 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::Shared; use crate::types::{AutoModerationRuleTriggerType, IntegrationType, PermissionOverwriteType, Shared};
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
/// See <https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object> /// See <https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object>
pub struct AuditLogEntry { pub struct AuditLogEntry {
pub target_id: Option<String>, pub target_id: Option<String>,
#[cfg(feature = "sqlx")]
pub changes: sqlx::types::Json<Option<Vec<Shared<AuditLogChange>>>>,
#[cfg(not(feature = "sqlx"))]
pub changes: Option<Vec<Shared<AuditLogChange>>>, pub changes: Option<Vec<Shared<AuditLogChange>>>,
pub user_id: Option<Snowflake>, pub user_id: Option<Snowflake>,
pub id: Snowflake, pub id: Snowflake,
// to:do implement an enum for these types pub action_type: AuditLogActionType,
pub action_type: u8, #[cfg(feature = "sqlx")]
// to:do add better options type pub options: Option<sqlx::types::Json<AuditEntryInfo>>,
pub options: Option<serde_json::Value>, #[cfg(not(feature = "sqlx"))]
pub options: Option<AuditEntryInfo>,
pub reason: Option<String>, pub reason: Option<String>,
} }
@ -28,3 +34,164 @@ pub struct AuditLogChange {
pub old_value: Option<serde_json::Value>, pub old_value: Option<serde_json::Value>,
pub key: String, pub key: String,
} }
#[derive(Default, Serialize_repr, Deserialize_repr, Debug, Clone, Copy)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
/// # Reference:
/// See <https://docs.discord.sex/resources/audit-log#audit-log-events>
pub enum AuditLogActionType {
#[default]
/// Guild settings were updated
GuildUpdate = 1,
/// Channel was created
ChannelCreate = 10,
/// Channel settings were updated
ChannelUpdate = 11,
/// Channel was deleted
ChannelDelete = 12,
/// Permission overwrite was added to a channel
ChannelOverwriteCreate = 13,
/// Permission overwrite was updated for a channel
ChannelOverwriteUpdate = 14,
/// Permission overwrite was deleted from a channel
ChannelOverwriteDelete = 15,
/// Member was removed from guild
MemberKick = 20,
/// Members were pruned from guild
MemberPrune = 21,
/// Member was banned from guild
MemberBanAdd = 22,
/// Member was unbanned from guild
MemberBanRemove = 23,
/// Member was updated in guild
MemberUpdate = 24,
/// Member was added or removed from a role
MemberRoleUpdate = 25,
/// Member was moved to a different voice channel
MemberMove = 26,
/// Member was disconnected from a voice channel
MemberDisconnect = 27,
/// Bot user was added to guild
BotAdd = 28,
/// Role was created
RoleCreate = 30,
/// Role was edited
RoleUpdate = 31,
/// Role was deleted
RoleDelete = 32,
/// Guild invite was created
InviteCreate = 40,
/// Guild invite was updated
InviteUpdate = 41,
/// Guild invite was deleted
InviteDelete = 42,
/// Webhook was created
WebhookCreate = 50,
/// Webhook properties or channel were updated
WebhookUpdate = 51,
/// Webhook was deleted
WebhookDelete = 52,
/// Emoji was created
EmojiCreate = 60,
/// Emoji name was updated
EmojiUpdate = 61,
/// Emoji was deleted
EmojiDelete = 62,
/// Single message was deleted
MessageDelete = 72,
/// Multiple messages were deleted
MessageBulkDelete = 73,
/// Message was pinned to a channel
MessagePin = 74,
/// Message was unpinned from a channel
MessageUnpin = 75,
/// Interaction was added to guild
IntegrationCreate = 80,
/// Integration was updated (e.g. its scopes were updated)
IntegrationUpdate = 81,
/// Integration was removed from guild
IntegrationDelete = 82,
/// Stage instance was created (stage channel becomes live)
StageInstanceCreate = 83,
/// Stage instance details were updated
StageInstanceUpdate = 84,
/// Stage instance was deleted (stage channel no longer live)
StageInstanceDelete = 85,
/// Sticker was created
StickerCreate = 90,
/// Sticker details were updated
StickerUpdate = 91,
/// Sticker was deleted
StickerDelete = 92,
/// Event was created
GuildScheduledEventCreate = 100,
/// Event was updated
GuildScheduledEventUpdate = 101,
/// Event was cancelled
GuildScheduledEventDelete = 102,
/// Thread was created in a channel
ThreadCreate = 110,
/// Thread was updated
ThreadUpdate = 111,
/// Thread was deleted
ThreadDelete = 112,
/// Permissions were updated for a command
ApplicationCommandPermissionUpdate = 121,
/// AutoMod rule created
AutoModerationRuleCreate = 140,
/// AutoMod rule was updated
AutoModerationRuleUpdate = 141,
/// AutoMod rule was deleted
AutoModerationRuleDelete = 142,
/// Message was blocked by AutoMod
AutoModerationBlockMessage = 143,
/// Message was flagged by AutoMod
AutoModerationFlagToChannel = 144,
/// Member was timed out by AutoMod
AutoModerationUserCommunicationDisabled = 145,
/// Member was quarantined by AutoMod
AutoModerationQuarantineUser = 146,
/// Creator monetization request was created
CreatorMonetizationRequestCreated = 150,
/// Creator monetization terms were accepted
CreatorMonetizationTermsAccepted = 151,
/// Onboarding prompt was created
OnboardingPromptCreate = 163,
/// Onboarding prompt was updated
OnboardingPromptUpdate = 164,
/// Onboarding prompt was deleted
OnboardingPromptDelete = 165,
/// Onboarding was created
OnboardingCreate = 166,
/// Onboarding was updated
OnboardingUpdate = 167,
/// Voice channel status was updated
VoiceChannelStatusUpdate = 192,
/// Voice channel status was deleted
VoiceChannelStatusDelete = 193
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AuditEntryInfo {
pub application_id: Option<Snowflake>,
pub auto_moderation_rule_name: Option<String>,
pub auto_moderation_rule_trigger_type: Option<AutoModerationRuleTriggerType>,
pub channel_id: Option<Snowflake>,
// #[serde(option_string)]
pub count: Option<u64>,
// #[serde(option_string)]
pub delete_member_days: Option<u64>,
/// The ID of the overwritten entity
pub id: Option<Snowflake>,
pub integration_type: Option<IntegrationType>,
// #[serde(option_string)]
pub members_removed: Option<u64>,
// #[serde(option_string)]
pub message_id: Option<u64>,
pub role_name: Option<String>,
#[serde(rename = "type")]
pub overwrite_type: Option<PermissionOverwriteType>,
pub status: Option<String>
}

View File

@ -157,7 +157,10 @@ pub struct PermissionOverwrite {
#[derive(Debug, Serialize_repr, Deserialize_repr, Clone, PartialEq, Eq, PartialOrd)] #[derive(Debug, Serialize_repr, Deserialize_repr, Clone, PartialEq, Eq, PartialOrd)]
#[repr(u8)] #[repr(u8)]
// Note: gateway sends this is as "role" or "member", causing deserialization errors
/// # Reference /// # Reference
///
/// See <https://docs.discord.sex/resources/channel#permission-overwrite-type>
pub enum PermissionOverwriteType { pub enum PermissionOverwriteType {
Role = 0, Role = 0,
Member = 1, Member = 1,

View File

@ -23,7 +23,7 @@ use super::PublicUser;
use crate::gateway::Updateable; use crate::gateway::Updateable;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use chorus_macros::{observe_option_vec, observe_vec, Composite, Updateable}; use chorus_macros::{observe_vec, Composite, Updateable};
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::types::Composite; use crate::types::Composite;
@ -46,10 +46,12 @@ pub struct Guild {
pub approximate_presence_count: Option<i32>, pub approximate_presence_count: Option<i32>,
pub banner: Option<String>, pub banner: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub bans: Option<Vec<GuildBan>>, #[serde(default)]
pub bans: Vec<GuildBan>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub channels: Option<Vec<Shared<Channel>>>, #[serde(default)]
pub channels: Vec<Shared<Channel>>,
pub default_message_notifications: Option<MessageNotificationLevel>, pub default_message_notifications: Option<MessageNotificationLevel>,
pub description: Option<String>, pub description: Option<String>,
pub discovery_splash: Option<String>, pub discovery_splash: Option<String>,
@ -66,7 +68,8 @@ pub struct Guild {
pub icon_hash: Option<String>, pub icon_hash: Option<String>,
pub id: Snowflake, pub id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub invites: Option<Vec<GuildInvite>>, #[serde(default)]
pub invites: Vec<GuildInvite>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub joined_at: Option<DateTime<Utc>>, pub joined_at: Option<DateTime<Utc>>,
pub large: Option<bool>, pub large: Option<bool>,
@ -92,25 +95,29 @@ pub struct Guild {
pub public_updates_channel_id: Option<Snowflake>, pub public_updates_channel_id: Option<Snowflake>,
pub region: Option<String>, pub region: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub roles: Option<Vec<Shared<RoleObject>>>, #[serde(default)]
pub roles: Vec<Shared<RoleObject>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub rules_channel: Option<String>, pub rules_channel: Option<String>,
pub rules_channel_id: Option<Snowflake>, pub rules_channel_id: Option<Snowflake>,
pub splash: Option<String>, pub splash: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>, #[serde(default)]
pub stickers: Vec<Sticker>,
pub system_channel_flags: Option<SystemChannelFlags>, pub system_channel_flags: Option<SystemChannelFlags>,
pub system_channel_id: Option<Snowflake>, pub system_channel_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub vanity_url_code: Option<String>, pub vanity_url_code: Option<String>,
pub verification_level: Option<VerificationLevel>, pub verification_level: Option<VerificationLevel>,
#[serde(default)]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub voice_states: Option<Vec<Shared<VoiceState>>>, pub voice_states: Vec<Shared<VoiceState>>,
#[serde(default)]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub webhooks: Option<Vec<Shared<Webhook>>>, pub webhooks: Vec<Shared<Webhook>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub welcome_screen: sqlx::types::Json<Option<WelcomeScreenObject>>, pub welcome_screen: sqlx::types::Json<Option<WelcomeScreenObject>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
@ -226,6 +233,7 @@ impl std::cmp::PartialEq for Guild {
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct GuildBan { pub struct GuildBan {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: PublicUser, pub user: PublicUser,
pub reason: Option<String>, pub reason: Option<String>,
} }

View File

@ -5,7 +5,7 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{GuildMemberFlags, Shared}; use crate::types::{GuildMemberFlags, PermissionFlags, Shared};
use crate::types::{entities::PublicUser, Snowflake}; use crate::types::{entities::PublicUser, Snowflake};
#[derive(Debug, Deserialize, Default, Serialize, Clone)] #[derive(Debug, Deserialize, Default, Serialize, Clone)]
@ -27,6 +27,7 @@ pub struct GuildMember {
pub mute: bool, pub mute: bool,
pub flags: Option<GuildMemberFlags>, pub flags: Option<GuildMemberFlags>,
pub pending: Option<bool>, pub pending: Option<bool>,
pub permissions: Option<String>, #[serde(default)]
pub permissions: PermissionFlags,
pub communication_disabled_until: Option<DateTime<Utc>>, pub communication_disabled_until: Option<DateTime<Utc>>,
} }

View File

@ -18,7 +18,7 @@ pub struct Integration {
pub id: Snowflake, pub id: Snowflake,
pub name: String, pub name: String,
#[serde(rename = "type")] #[serde(rename = "type")]
pub integration_type: String, pub integration_type: IntegrationType,
pub enabled: bool, pub enabled: bool,
pub syncing: Option<bool>, pub syncing: Option<bool>,
pub role_id: Option<String>, pub role_id: Option<String>,
@ -43,3 +43,15 @@ pub struct IntegrationAccount {
pub id: String, pub id: String,
pub name: String, pub name: String,
} }
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(feature = "sqlx", sqlx(rename_all = "snake_case"))]
pub enum IntegrationType {
#[default]
Twitch,
Youtube,
Discord,
GuildSubscription,
}

View File

@ -13,7 +13,7 @@ use super::{Application, Channel, GuildMember, NSFWLevel, User};
/// Represents a code that when used, adds a user to a guild or group DM channel, or creates a relationship between two users. /// Represents a code that when used, adds a user to a guild or group DM channel, or creates a relationship between two users.
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-object> /// See <https://discord-userdoccers.vercel.app/resources/invite#invite-object>
#[derive(Debug, Serialize, Deserialize)] #[derive(Default, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Invite { pub struct Invite {
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]

View File

@ -177,7 +177,7 @@ pub struct ChannelMention {
pub struct Embed { pub struct Embed {
title: Option<String>, title: Option<String>,
#[serde(rename = "type")] #[serde(rename = "type")]
embed_type: Option<String>, embed_type: Option<EmbedType>,
description: Option<String>, description: Option<String>,
url: Option<String>, url: Option<String>,
timestamp: Option<String>, timestamp: Option<String>,
@ -191,6 +191,24 @@ pub struct Embed {
fields: Option<Vec<EmbedField>>, fields: Option<Vec<EmbedField>>,
} }
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum EmbedType {
#[deprecated]
ApplicationNews,
Article,
AutoModerationMessage,
AutoModerationNotification,
Gift,
#[serde(rename = "gifv")]
GifVideo,
Image,
Link,
PostPreview,
Rich,
Video
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct EmbedFooter { pub struct EmbedFooter {
text: String, text: String,
@ -291,7 +309,7 @@ pub enum MessageType {
Default = 0, Default = 0,
/// A message sent when a user is added to a group DM or thread /// A message sent when a user is added to a group DM or thread
RecipientAdd = 1, RecipientAdd = 1,
/// A message sent when a user is removed from a group DM or thread /// A message sent when a user is removed from a group DM or thread
RecipientRemove = 2, RecipientRemove = 2,
/// A message sent when a user creates a call in a private channel /// A message sent when a user creates a call in a private channel
Call = 3, Call = 3,
@ -335,7 +353,7 @@ pub enum MessageType {
ThreadStarterMessage = 21, ThreadStarterMessage = 21,
/// A message sent to remind users to invite friends to a guild /// A message sent to remind users to invite friends to a guild
GuildInviteReminder = 22, GuildInviteReminder = 22,
/// A message sent when a user uses a context menu command /// A message sent when a user uses a context menu command
ContextMenuCommand = 23, ContextMenuCommand = 23,
/// A message sent when auto moderation takes an action /// A message sent when auto moderation takes an action
AutoModerationAction = 24, AutoModerationAction = 24,

View File

@ -4,7 +4,7 @@
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_string_from_number}; use serde_aux::prelude::deserialize_option_number_from_string;
use std::fmt::Debug; use std::fmt::Debug;
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
@ -34,8 +34,7 @@ pub struct RoleObject {
pub unicode_emoji: Option<String>, pub unicode_emoji: Option<String>,
pub position: u16, pub position: u16,
#[serde(default)] #[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")] pub permissions: PermissionFlags,
pub permissions: String,
pub managed: bool, pub managed: bool,
pub mentionable: bool, pub mentionable: bool,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::{entities::User, utils::Snowflake, Shared}; use crate::types::{entities::User, utils::Snowflake, Shared};
@ -18,15 +19,17 @@ pub struct Sticker {
pub pack_id: Option<Snowflake>, pub pack_id: Option<Snowflake>,
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub tags: String, pub tags: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub asset: Option<String>, pub asset: Option<String>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub sticker_type: u8, pub sticker_type: StickerType,
pub format_type: u8, pub format_type: StickerFormatType,
pub available: Option<bool>, pub available: Option<bool>,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Shared<User>>, pub user: Option<Shared<User>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub sort_value: Option<u8>, pub sort_value: Option<u8>,
} }
@ -108,6 +111,18 @@ impl PartialOrd for Sticker {
} }
} }
impl Sticker {
pub fn tags(&self) -> Vec<String> {
self.tags
.as_ref()
.map_or(vec![], |s|
s.split(',')
.map(|tag| tag.trim().to_string())
.collect()
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
/// A partial sticker object. /// A partial sticker object.
/// ///
@ -118,5 +133,61 @@ impl PartialOrd for Sticker {
pub struct StickerItem { pub struct StickerItem {
pub id: Snowflake, pub id: Snowflake,
pub name: String, pub name: String,
pub format_type: u8, pub format_type: StickerFormatType,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename = "SCREAMING_SNAKE_CASE")]
/// # Reference
/// See <https://docs.discord.sex/resources/sticker#sticker-types>
pub enum StickerType {
/// An official sticker in a current or legacy purchasable pack
Standard = 1,
#[default]
/// A sticker uploaded to a guild for the guild's members
Guild = 2,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
/// # Reference
/// See <https://docs.discord.sex/resources/sticker#sticker-format-types>
pub enum StickerFormatType {
#[default]
/// A PNG image
PNG = 1,
/// An animated PNG image, using the APNG format - uses CDN
APNG = 2,
/// A lottie animation; requires the VERIFIED and/or PARTNERED guild feature - uses CDN
LOTTIE = 3,
/// An animated GIF image - does not use CDN
GIF = 4,
}
impl StickerFormatType {
pub fn is_animated(&self) -> bool {
matches!(self, StickerFormatType::APNG | StickerFormatType::LOTTIE | StickerFormatType::GIF)
}
pub const fn to_mime(&self) -> &'static str {
match self {
StickerFormatType::PNG => "image/png",
StickerFormatType::APNG => "image/apng",
StickerFormatType::LOTTIE => "application/json",
StickerFormatType::GIF => "image/gif",
}
}
pub fn from_mime(mime: &str) -> Option<Self> {
match mime {
"image/png" => Some(StickerFormatType::PNG),
"image/apng" => Some(StickerFormatType::APNG),
"application/json" => Some(StickerFormatType::LOTTIE),
"image/gif" => Some(StickerFormatType::GIF),
_ => None,
}
}
} }

View File

@ -34,9 +34,11 @@ use crate::types::{
#[cfg_attr(feature = "client", derive(Composite))] #[cfg_attr(feature = "client", derive(Composite))]
pub struct VoiceState { pub struct VoiceState {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub guild: Option<Guild>, pub guild: Option<Guild>,
pub channel_id: Option<Snowflake>, pub channel_id: Option<Snowflake>,
pub user_id: Snowflake, pub user_id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<Shared<GuildMember>>, pub member: Option<Shared<GuildMember>>,
/// Includes alphanumeric characters, not a snowflake /// Includes alphanumeric characters, not a snowflake
pub session_id: String, pub session_id: String,

View File

@ -49,11 +49,7 @@ impl UpdateMessage<Guild> for ChannelCreate {
fn update(&mut self, object_to_update: Shared<Guild>) { fn update(&mut self, object_to_update: Shared<Guild>) {
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
let update = self.channel.clone().into_shared(); let update = self.channel.clone().into_shared();
if write.channels.is_some() { write.channels.push(update);
write.channels.as_mut().unwrap().push(update);
} else {
write.channels = Some(Vec::from([update]));
}
} }
} }
@ -122,12 +118,12 @@ impl UpdateMessage<Guild> for ChannelDelete {
return; return;
} }
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
if write.channels.is_none() { if write.channels.is_empty() {
return; return;
} }
for (iteration, item) in (0_u32..).zip(write.channels.as_mut().unwrap().iter()) { for (iteration, item) in (0_u32..).zip(write.channels.iter()) {
if item.read().unwrap().id == self.id().unwrap() { if item.read().unwrap().id == self.id().unwrap() {
write.channels.as_mut().unwrap().remove(iteration as usize); write.channels.remove(iteration as usize);
return; return;
} }
} }

View File

@ -214,15 +214,9 @@ impl UpdateMessage<Guild> for GuildRoleCreate {
fn update(&mut self, object_to_update: Shared<Guild>) { fn update(&mut self, object_to_update: Shared<Guild>) {
let mut object_to_update = object_to_update.write().unwrap(); let mut object_to_update = object_to_update.write().unwrap();
if object_to_update.roles.is_some() { object_to_update
object_to_update .roles
.roles .push(self.role.clone().into_shared());
.as_mut()
.unwrap()
.push(self.role.clone().into_shared());
} else {
object_to_update.roles = Some(Vec::from([self.role.clone().into_shared()]));
}
} }
} }

View File

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
use crate::types::{ApplicationCommand, AuditLogActionType, AuditLogEntry, AutoModerationRule, Channel, GuildScheduledEvent, Integration, Snowflake, User, Webhook};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AuditLogObject {
pub audit_log_entries: Vec<AuditLogEntry>,
pub application_commands: Vec<ApplicationCommand>,
pub auto_moderation_rules: Vec<AutoModerationRule>,
pub guild_scheduled_events: Vec<GuildScheduledEvent>,
pub integrations: Vec<Integration>,
pub threads: Vec<Channel>,
pub users: Vec<User>,
pub webhooks: Vec<Webhook>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetAuditLogsQuery {
pub before: Option<Snowflake>,
pub after: Option<Snowflake>,
pub limit: Option<u8>,
pub user_id: Option<Snowflake>,
pub action_type: Option<AuditLogActionType>
}

View File

@ -2,16 +2,14 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::collections::HashMap;
use bitflags::bitflags; use bitflags::bitflags;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::entities::Channel; use crate::types::entities::Channel;
use crate::types::types::guild_configuration::GuildFeatures; use crate::types::types::guild_configuration::GuildFeatures;
use crate::types::{ use crate::types::{Emoji, ExplicitContentFilterLevel, GenericSearchQueryWithLimit, MessageNotificationLevel, Snowflake, Sticker, StickerFormatType, SystemChannelFlags, VerificationLevel, WelcomeScreenChannel};
Emoji, ExplicitContentFilterLevel, MessageNotificationLevel, Snowflake, Sticker,
SystemChannelFlags, VerificationLevel,
};
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -32,10 +30,20 @@ pub struct GuildCreateSchema {
/// Represents the schema which needs to be sent to create a Guild Ban. /// Represents the schema which needs to be sent to create a Guild Ban.
/// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban> /// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban>
pub struct GuildBanCreateSchema { pub struct GuildBanCreateSchema {
/// Deprecated
pub delete_message_days: Option<u8>, pub delete_message_days: Option<u8>,
pub delete_message_seconds: Option<u32>, pub delete_message_seconds: Option<u32>,
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
/// Represents the schema which needs to be sent to create a Guild Ban.
/// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban>
pub struct GuildBanBulkCreateSchema {
pub user_ids: Vec<Snowflake>,
pub delete_message_seconds: Option<u32>,
}
#[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)] #[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
/// Represents the schema used to modify a guild. /// Represents the schema used to modify a guild.
@ -144,6 +152,12 @@ impl Default for GuildMemberSearchSchema {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct GuildGetMembersQuery {
pub limit: Option<u16>,
pub after: Option<Snowflake>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct ModifyGuildMemberSchema { pub struct ModifyGuildMemberSchema {
pub nick: Option<String>, pub nick: Option<String>,
@ -156,7 +170,7 @@ pub struct ModifyGuildMemberSchema {
} }
bitflags! { bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))] #[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// Represents the flags of a Guild Member. /// Represents the flags of a Guild Member.
/// ///
@ -199,3 +213,200 @@ pub struct GuildBansQuery {
pub after: Option<Snowflake>, pub after: Option<Snowflake>,
pub limit: Option<u16>, pub limit: Option<u16>,
} }
/// Max query length is 32 characters.
/// The limit argument is a number between 1 and 10, defaults to 10.
pub type GuildBansSearchQuery = GenericSearchQueryWithLimit;
/// Query is partial or full, username or nickname.
/// Limit argument is a number between 1 and 1000, defaults to 1.
pub type GuildMembersSearchQuery = GenericSearchQueryWithLimit;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// A guild's progress on meeting the requirements of joining discovery.
///
/// Certain guilds, such as those that are verified, are exempt from discovery requirements. These guilds will not have a fully populated discovery requirements object, and are guaranteed to receive only sufficient and sufficient_without_grace_period.
///
/// # Reference:
/// See <https://docs.discord.sex/resources/discovery#discovery-requirements-object>
pub struct GuildDiscoveryRequirements {
pub guild_id: Option<Snowflake>,
pub safe_environment: Option<bool>,
pub healthy: Option<bool>,
pub health_score_pending: Option<bool>,
pub size: Option<bool>,
pub nsfw_properties: Option<GuildDiscoveryNsfwProperties>,
pub protected: Option<bool>,
pub sufficient: Option<bool>,
pub sufficient_without_grace_period: Option<bool>,
pub valid_rules_channel: Option<bool>,
pub retention_healthy: Option<bool>,
pub engagement_healthy: Option<bool>,
pub age: Option<bool>,
pub minimum_age: Option<u16>,
pub health_score: Option<GuildDiscoveryHealthScore>,
pub minimum_size: Option<u64>,
pub grace_period_end_date: Option<DateTime<Utc>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/discovery#discovery-nsfw-properties-structure>
pub struct GuildDiscoveryNsfwProperties {
pub channels: Vec<Snowflake>,
pub channel_banned_keywords: HashMap<Snowflake, Vec<String>>,
pub name: Option<String>,
pub name_banned_keywords: Vec<String>,
pub description: Option<String>,
pub description_banned_keywords: Vec<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// Activity metrics are recalculated weekly, as an 8-week rolling average. If they are not yet eligible to be calculated, all fields will be null.
///
/// # Reference:
/// See <https://docs.discord.sex/resources/discovery#discovery-health-score-structure>
pub struct GuildDiscoveryHealthScore {
pub avg_nonnew_communicators: u64,
pub avg_nonnew_participators: u64,
pub num_intentful_joiners: u64,
pub perc_ret_w1_intentful: f64,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/emoji#create-guild-emoji>
pub struct EmojiCreateSchema {
pub name: Option<String>,
/// # Reference:
/// See <https://docs.discord.sex/reference#cdn-data>
pub image: String,
#[serde(default)]
pub roles: Vec<Snowflake>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/emoji#modify-guild-emoji>
pub struct EmojiModifySchema {
pub name: Option<String>,
pub roles: Option<Vec<Snowflake>>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#get-guild-prune>
pub struct GuildPruneQuerySchema {
pub days: u8,
/// Only used on POST
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compute_prune_count: Option<bool>,
#[serde(default)]
pub include_roles: Vec<Snowflake>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#get-guild-prune>
pub struct GuildPruneResult {
/// Null if compute_prune_count is false
pub pruned: Option<usize>,
}
#[derive(Default, Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/sticker#create-guild-sticker>
pub struct GuildCreateStickerSchema {
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub tags: Option<String>,
pub file_data: Vec<u8>,
#[serde(skip)]
pub sticker_format_type: StickerFormatType
}
impl GuildCreateStickerSchema {
#[cfg(feature = "poem")]
pub async fn from_multipart(mut multipart: poem::web::Multipart) -> Result<Self, poem::Error> {
let mut _self = GuildCreateStickerSchema::default();
while let Some(field) = multipart.next_field().await? {
let name = field.name().ok_or(poem::Error::from_string("All fields must be named", poem::http::StatusCode::BAD_REQUEST))?;
match name {
"name" => {
_self.name = field.text().await?;
}
"description" => {
_self.description = Some(field.text().await?);
}
"tags" => {
_self.tags = Some(field.text().await?);
}
"file_data" => {
if _self.name.is_empty() {
_self.name = field.file_name().map(String::from).ok_or(poem::Error::from_string("File name must be set", poem::http::StatusCode::BAD_REQUEST))?;
}
_self.sticker_format_type = StickerFormatType::from_mime(field.content_type().ok_or(poem::Error::from_string("Content type must be set", poem::http::StatusCode::BAD_REQUEST))?).ok_or(poem::Error::from_string("Unknown sticker format", poem::http::StatusCode::BAD_REQUEST))?;
_self.file_data = field.bytes().await?;
}
_ => {}
}
}
if _self.name.is_empty() || _self.file_data.is_empty() {
return Err(poem::Error::from_string("At least the name and file_data are required", poem::http::StatusCode::BAD_REQUEST));
}
Ok(_self)
}
// #[cfg(feature = "client")]
pub fn to_multipart(&self) -> reqwest::multipart::Form {
let mut form = reqwest::multipart::Form::new()
.text("name", self.name.clone())
.part("file_data", reqwest::multipart::Part::bytes(self.file_data.clone()).mime_str(self.sticker_format_type.to_mime()).unwrap());
if let Some(description) = &self.description {
form = form.text("description", description.to_owned());
}
if let Some(tags) = &self.tags {
form = form.text("tags", tags.to_owned())
}
form
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/sticker#modify-guild-sticker>
pub struct GuildModifyStickerSchema {
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub tags: Option<String>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#modify-guild-welcome-screen>
pub struct GuildModifyWelcomeScreenSchema {
pub enabled: Option<bool>,
pub description: Option<String>,
/// Max of 5
pub welcome_channels: Option<Vec<WelcomeScreenChannel>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild-template#create-guild-template>
pub struct GuildTemplateCreateSchema {
/// Name of the template (1-100 characters)
pub name: String,
/// Description of the template (max 120 characters)
pub description: Option<String>
}

View File

@ -7,4 +7,20 @@ use serde::{Deserialize, Serialize};
/// Read: <https://docs.discord.sex/resources/invite#query-string-params> /// Read: <https://docs.discord.sex/resources/invite#query-string-params>
pub struct GetInvitesSchema { pub struct GetInvitesSchema {
pub with_counts: Option<bool>, pub with_counts: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#get-guild-vanity-invite>
pub struct GuildVanityInviteResponse {
pub code: String,
#[serde(default)]
pub uses: Option<u32>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#modify-guild-vanity-invite>
pub struct GuildCreateVanitySchema {
pub code: String,
} }

View File

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{ use crate::types::entities::{
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment, AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
}; };
use crate::types::{Attachment, MessageFlags, MessageType, ReactionType, Snowflake}; use crate::types::{Attachment, EmbedType, Message, MessageFlags, MessageType, ReactionType, Snowflake};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -54,13 +54,13 @@ pub struct MessageSearchQuery {
pub attachment_extension: Option<Vec<String>>, pub attachment_extension: Option<Vec<String>>,
pub attachment_filename: Option<Vec<String>>, pub attachment_filename: Option<Vec<String>>,
pub author_id: Option<Vec<Snowflake>>, pub author_id: Option<Vec<Snowflake>>,
pub author_type: Option<Vec<String>>, pub author_type: Option<Vec<AuthorType>>,
pub channel_id: Option<Vec<Snowflake>>, pub channel_id: Option<Vec<Snowflake>>,
pub command_id: Option<Vec<Snowflake>>, pub command_id: Option<Vec<Snowflake>>,
pub content: Option<String>, pub content: Option<String>,
pub embed_provider: Option<Vec<String>>, pub embed_provider: Option<Vec<String>>,
pub embed_type: Option<Vec<String>>, pub embed_type: Option<Vec<EmbedType>>,
pub has: Option<Vec<String>>, pub has: Option<Vec<HasType>>,
pub include_nsfw: Option<bool>, pub include_nsfw: Option<bool>,
pub limit: Option<i32>, pub limit: Option<i32>,
pub link_hostname: Option<Vec<String>>, pub link_hostname: Option<Vec<String>>,
@ -70,8 +70,8 @@ pub struct MessageSearchQuery {
pub min_id: Option<String>, pub min_id: Option<String>,
pub offset: Option<i32>, pub offset: Option<i32>,
pub pinned: Option<bool>, pub pinned: Option<bool>,
pub sort_by: Option<String>, pub sort_by: Option<SortType>,
pub sort_order: Option<String>, pub sort_order: Option<SortOrder>,
} }
impl std::default::Default for MessageSearchQuery { impl std::default::Default for MessageSearchQuery {
@ -102,6 +102,75 @@ impl std::default::Default for MessageSearchQuery {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum AuthorType {
User,
#[serde(rename = "-user")]
NotUser,
Bot,
#[serde(rename = "-bot")]
NotBot,
Webhook,
#[serde(rename = "-webhook")]
NotWebhook,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum HasType {
Image,
#[serde(rename = "-image")]
NotImage,
Sound,
#[serde(rename = "-sound")]
NotSound,
Video,
#[serde(rename = "-video")]
NotVideo,
File,
#[serde(rename = "-file")]
NotFile,
Sticker,
#[serde(rename = "-sticker")]
NotSticker,
Embed,
#[serde(rename = "-embed")]
NotEmbed,
Link,
#[serde(rename = "-link")]
NotLink,
Poll,
#[serde(rename = "-poll")]
NotPoll,
Snapshot,
#[serde(rename = "-snapshot")]
NotSnapshot,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum SortType {
#[default]
Timestamp,
Relevance
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SortOrder {
#[default]
#[serde(rename = "desc")]
Descending,
#[serde(rename = "asc")]
Ascending,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
pub struct MessageSearchResponse {
pub messages: Vec<Message>,
pub total_results: u64,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct CreateGreetMessage { pub struct CreateGreetMessage {
pub sticker_ids: Vec<Snowflake>, pub sticker_ids: Vec<Snowflake>,

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub use apierror::*; pub use apierror::*;
pub use audit_log::*;
pub use auth::*; pub use auth::*;
pub use channel::*; pub use channel::*;
pub use guild::*; pub use guild::*;
@ -11,8 +12,10 @@ pub use relationship::*;
pub use role::*; pub use role::*;
pub use user::*; pub use user::*;
pub use invites::*; pub use invites::*;
pub use voice_state::*;
mod apierror; mod apierror;
mod audit_log;
mod auth; mod auth;
mod channel; mod channel;
mod guild; mod guild;
@ -21,3 +24,10 @@ mod relationship;
mod role; mod role;
mod user; mod user;
mod invites; mod invites;
mod voice_state;
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct GenericSearchQueryWithLimit {
pub query: String,
pub limit: Option<u16>,
}

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{PermissionFlags, Snowflake};
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -10,8 +11,8 @@ use serde::{Deserialize, Serialize};
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema) /// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema)
pub struct RoleCreateModifySchema { pub struct RoleCreateModifySchema {
pub name: Option<String>, pub name: Option<String>,
pub permissions: Option<String>, pub permissions: Option<PermissionFlags>,
pub color: Option<u32>, pub color: Option<f64>,
pub hoist: Option<bool>, pub hoist: Option<bool>,
pub icon: Option<Vec<u8>>, pub icon: Option<Vec<u8>>,
pub unicode_emoji: Option<String>, pub unicode_emoji: Option<String>,
@ -24,6 +25,6 @@ pub struct RoleCreateModifySchema {
/// Represents the schema which needs to be sent to update a roles' position. /// Represents the schema which needs to be sent to update a roles' position.
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema) /// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema)
pub struct RolePositionUpdateSchema { pub struct RolePositionUpdateSchema {
pub id: String, pub id: Snowflake,
pub position: u16, pub position: u16,
} }

View File

@ -0,0 +1,15 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::Snowflake;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
/// # Reference:
/// See <https://docs.discord.sex/resources/voice#json-params>
pub struct VoiceStateUpdateSchema {
/// The ID of the channel the user is currently in
pub channel_id: Option<Snowflake>,
/// Whether to suppress the user
pub suppress: Option<bool>,
/// The time at which the user requested to speak
pub request_to_speak_timestamp: Option<DateTime<Utc>>,
}

View File

@ -5,7 +5,7 @@
use std::str::FromStr; use std::str::FromStr;
use chorus::gateway::{Gateway, GatewayOptions}; use chorus::gateway::{Gateway, GatewayOptions};
use chorus::types::IntoShared; use chorus::types::{IntoShared, PermissionFlags};
use chorus::{ use chorus::{
instance::{ChorusUser, Instance}, instance::{ChorusUser, Instance},
types::{ types::{
@ -108,7 +108,7 @@ pub(crate) async fn setup() -> TestBundle {
let role_create_schema: chorus::types::RoleCreateModifySchema = RoleCreateModifySchema { let role_create_schema: chorus::types::RoleCreateModifySchema = RoleCreateModifySchema {
name: Some("Bundle role".to_string()), name: Some("Bundle role".to_string()),
permissions: Some("8".to_string()), // Administrator permissions permissions: PermissionFlags::from_bits(8), // Administrator permissions
hoist: Some(true), hoist: Some(true),
icon: None, icon: None,
unicode_emoji: Some("".to_string()), unicode_emoji: Some("".to_string()),

View File

@ -81,7 +81,7 @@ async fn test_gateway_authenticate() {
} }
// Success, we have received it // Success, we have received it
Some(_) = ready_receive.recv() => {} Some(_) = ready_receive.recv() => {}
}; }
common::teardown(bundle).await common::teardown(bundle).await
} }
@ -125,7 +125,7 @@ async fn test_self_updating_structs() {
.gateway .gateway
.observe_and_into_inner(bundle.guild.clone()) .observe_and_into_inner(bundle.guild.clone())
.await; .await;
assert!(guild.channels.is_none()); assert!(guild.channels.is_empty());
Channel::create( Channel::create(
&mut bundle.user, &mut bundle.user,
@ -145,8 +145,8 @@ async fn test_self_updating_structs() {
.gateway .gateway
.observe_and_into_inner(guild.into_shared()) .observe_and_into_inner(guild.into_shared())
.await; .await;
assert!(guild.channels.is_some()); assert!(!guild.channels.is_empty());
assert!(guild.channels.as_ref().unwrap().len() == 1); assert_eq!(guild.channels.len(), 1);
common::teardown(bundle).await common::teardown(bundle).await
} }
@ -160,13 +160,12 @@ async fn test_recursive_self_updating_structs() {
// Observe Guild, make sure it has no channels // Observe Guild, make sure it has no channels
let guild = bundle.user.gateway.observe(guild.clone()).await; let guild = bundle.user.gateway.observe(guild.clone()).await;
let inner_guild = guild.read().unwrap().clone(); let inner_guild = guild.read().unwrap().clone();
assert!(inner_guild.roles.is_none()); assert!(inner_guild.roles.is_empty());
// Create Role // Create Role
let permissions = types::PermissionFlags::CONNECT | types::PermissionFlags::MANAGE_EVENTS; let permissions = types::PermissionFlags::CONNECT | types::PermissionFlags::MANAGE_EVENTS;
let permissions = Some(permissions.to_string());
let mut role_create_schema: types::RoleCreateModifySchema = RoleCreateModifySchema { let mut role_create_schema: types::RoleCreateModifySchema = RoleCreateModifySchema {
name: Some("cool person".to_string()), name: Some("cool person".to_string()),
permissions, permissions: Some(permissions),
hoist: Some(true), hoist: Some(true),
icon: None, icon: None,
unicode_emoji: Some("".to_string()), unicode_emoji: Some("".to_string()),
@ -186,7 +185,7 @@ async fn test_recursive_self_updating_structs() {
.await; .await;
// Update Guild and check for Guild // Update Guild and check for Guild
let inner_guild = guild.read().unwrap().clone(); let inner_guild = guild.read().unwrap().clone();
assert!(inner_guild.roles.is_some()); assert!(!inner_guild.roles.is_empty());
// Update the Role // Update the Role
role_create_schema.name = Some("yippieee".to_string()); role_create_schema.name = Some("yippieee".to_string());
RoleObject::modify(&mut bundle.user, guild_id, role.id, role_create_schema) RoleObject::modify(&mut bundle.user, guild_id, role.id, role_create_schema)
@ -202,8 +201,7 @@ async fn test_recursive_self_updating_structs() {
let guild = bundle.user.gateway.observe(bundle.guild.clone()).await; let guild = bundle.user.gateway.observe(bundle.guild.clone()).await;
let inner_guild = guild.read().unwrap().clone(); let inner_guild = guild.read().unwrap().clone();
let guild_roles = inner_guild.roles; let guild_roles = inner_guild.roles;
let guild_role = guild_roles.unwrap(); let guild_role_inner = guild_roles.get(0).unwrap().read().unwrap().clone();
let guild_role_inner = guild_role.get(0).unwrap().read().unwrap().clone();
assert_eq!(guild_role_inner.name, "yippieee".to_string()); assert_eq!(guild_role_inner.name, "yippieee".to_string());
common::teardown(bundle).await; common::teardown(bundle).await;
} }

View File

@ -15,10 +15,9 @@ mod common;
async fn create_and_get_roles() { async fn create_and_get_roles() {
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let permissions = types::PermissionFlags::CONNECT | types::PermissionFlags::MANAGE_EVENTS; let permissions = types::PermissionFlags::CONNECT | types::PermissionFlags::MANAGE_EVENTS;
let permissions = Some(permissions.to_string());
let role_create_schema: types::RoleCreateModifySchema = RoleCreateModifySchema { let role_create_schema: types::RoleCreateModifySchema = RoleCreateModifySchema {
name: Some("cool person".to_string()), name: Some("cool person".to_string()),
permissions, permissions: Some(permissions),
hoist: Some(true), hoist: Some(true),
icon: None, icon: None,
unicode_emoji: Some("".to_string()), unicode_emoji: Some("".to_string()),