Compare commits

..

27 Commits

Author SHA1 Message Date
kozabrada123 a7fc00df29
Bump chorus macros to 0.4.0 (#506)
- Bumps chorus macros to 0.4.0, followup to #505
- Makes builds within the repo use the local path and published releases
use a set version of the macros crate
2024-06-18 17:59:41 +02:00
kozabrada123 b3e62b2c33 always update to latest release of macros 2024-06-18 17:48:57 +02:00
kozabrada123 8d56259eac chorus macros 0.4.0 2024-06-18 17:35:39 +02:00
kozabrada123 3aec5881e5
Merge Quat3rnion/backend/messages (#505)
- Adds a multitude of new Types, Flags, and a few Objects relating to
sending and managing messages.
- Adds new SqlxBitFlag macro, which handles BitFlag objects being read
from/written to the database.
- Modifies some entities to use (new) Distinct Types instead of
primitives
2024-06-18 17:30:50 +02:00
kozabrada123 4430a7c4e6
Fix deserialization error w/ guild features list 2024-06-18 17:18:20 +02:00
Quat3rnion a8b7d9dfb3 u8 -> u64 2024-06-18 10:07:56 -04:00
Quat3rnion e553d10f25 Add new SerdeBitFlags derive macro, to help reduce repetitive code 2024-06-18 10:04:00 -04:00
Quat3rnion c9a36ce725 Maybe fix tests, make UserFlags able to be deserialized from String or u64 2024-06-18 08:46:07 -04:00
Quat3rnion d7de1d2fb7 Fix compilation for real, no dirty hack 2024-06-18 05:46:27 -04:00
Quat3rnion c27bc8d575 Fix test
I feel silly.
2024-06-18 05:34:03 -04:00
Quat3rnion abc4608be5 Dirty hack 2024-06-18 05:32:27 -04:00
Quat3rnion 77004abcfa Feature lock the macro 2024-06-18 05:24:42 -04:00
Quat3rnion 06b4dc3abd forgot a file :( 2024-06-18 05:19:51 -04:00
Quat3rnion 3810170400 Remove unused imports, feature locks in macro 2024-06-18 05:14:28 -04:00
Quat3rnion dfffcf4313 Add forgotten feature locks 2024-06-18 05:09:00 -04:00
Quat3rnion 6261cebfdf Fix test 2024-06-18 05:03:55 -04:00
Quat3rnion d8560093f5 Use chorus_macros from path, since it's there anyway 2024-06-18 04:48:50 -04:00
Quat3rnion bec0269e70 Add partial emoji and custom reaction types, refine SQLx mapping 2024-06-17 15:22:58 -04:00
Quat3rnion 590a6d6828 Make UserFlags deserialize from string 2024-06-07 14:08:47 -04:00
Quat3rnion 9bd55b9b5f Fix error in macro 2024-06-07 14:08:24 -04:00
Quat3rnion d3e2ef947c Add distinct MessageType enum 2024-06-07 13:14:25 -04:00
Quat3rnion d89975819a Utilize new macros and use distinct Flag types 2024-06-07 13:13:27 -04:00
Quat3rnion 656e5e31c0 Add SqlxBitFlags derive macro 2024-06-07 13:03:44 -04:00
Quat3rnion c2035585ae Update Cargo.lock 2024-06-07 13:02:17 -04:00
Quat3rnion 96c3f53961 Merge branch 'dev' into backend/messages 2024-06-07 13:00:04 -04:00
Quat3rnion ed5365ade4 Fix inverted type wrapping 2024-06-07 02:08:41 -04:00
Quat3rnion 0468292ec6 Add type locks 2024-06-07 02:08:21 -04:00
23 changed files with 374 additions and 147 deletions

4
Cargo.lock generated
View File

@ -264,9 +264,7 @@ dependencies = [
[[package]]
name = "chorus-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de4221700bc486c6e6bc261fdea478936d33067a06325895f5d2a8cde5917272"
version = "0.4.0"
dependencies = [
"async-trait",
"quote",

View File

@ -43,7 +43,7 @@ thiserror = "1.0.56"
jsonwebtoken = "8.3.0"
log = "0.4.20"
async-trait = "0.1.77"
chorus-macros = "0.3.0"
chorus-macros = { path = "./chorus-macros", version = "0" } # Note: version here is used when releasing. This will use the latest release. Make sure to republish the crate when code in macros is changed!
sqlx = { version = "0.7.3", features = [
"mysql",
"sqlite",

View File

@ -15,7 +15,7 @@ dependencies = [
[[package]]
name = "chorus-macros"
version = "0.2.1"
version = "0.4.0"
dependencies = [
"async-trait",
"quote",

View File

@ -1,6 +1,6 @@
[package]
name = "chorus-macros"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
license = "MPL-2.0"
description = "Macros for the chorus crate."

View File

@ -155,3 +155,68 @@ pub fn composite_derive(input: TokenStream) -> TokenStream {
_ => panic!("Composite derive macro only supports structs"),
}
}
#[proc_macro_derive(SqlxBitFlags)]
pub fn sqlx_bitflag_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
quote!{
#[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::MySql> for #name {
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
u64::type_info()
}
}
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::MySql> for #name {
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
u64::encode_by_ref(&self.bits(), buf)
}
}
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Decode<'q, sqlx::MySql> for #name {
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'q>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
u64::decode(value).map(|d| #name::from_bits(d).unwrap())
}
}
}
.into()
}
#[proc_macro_derive(SerdeBitFlags)]
pub fn serde_bitflag_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
quote! {
impl std::str::FromStr for #name {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<#name, Self::Err> {
s.parse::<u64>().map(#name::from_bits).map(|f| f.unwrap_or(#name::empty()))
}
}
impl serde::Serialize for #name {
fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.bits().to_string())
}
}
impl<'de> serde::Deserialize<'de> for #name {
fn deserialize<D>(deserializer: D) -> Result<#name, D::Error> where D: serde::de::Deserializer<'de> + Sized {
// let s = String::deserialize(deserializer)?.parse::<u64>().map_err(serde::de::Error::custom)?;
let s = crate::types::serde::string_or_u64(deserializer)?;
Ok(Self::from_bits(s).unwrap())
}
}
}
.into()
}

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_number_from_string;
use crate::types::{config::types::subconfigs::register::{
DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration,
@ -22,6 +23,7 @@ pub struct RegisterConfiguration {
pub allow_multiple_accounts: bool,
pub block_proxies: bool,
pub incrementing_discriminators: bool,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub default_rights: Rights,
}

View File

@ -31,7 +31,7 @@ pub struct Application {
pub verify_key: String,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub owner: Shared<User>,
pub flags: u64,
pub flags: ApplicationFlags,
#[cfg(feature = "sqlx")]
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
#[cfg(not(feature = "sqlx"))]
@ -73,7 +73,7 @@ impl Default for Application {
bot_require_code_grant: false,
verify_key: "".to_string(),
owner: Default::default(),
flags: 0,
flags: ApplicationFlags::empty(),
redirect_uris: None,
rpc_application_state: 0,
store_application_state: 1,
@ -93,12 +93,6 @@ impl Default for Application {
}
}
impl Application {
pub fn flags(&self) -> ApplicationFlags {
ApplicationFlags::from_bits(self.flags.to_owned()).unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// # Reference
/// See <https://discord.com/developers/docs/resources/application#install-params-object>
@ -108,7 +102,8 @@ pub struct InstallParams {
}
bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// # Reference
/// See <https://discord.com/developers/docs/resources/application#application-object-application-flags>
pub struct ApplicationFlags: u64 {

View File

@ -64,7 +64,9 @@ pub struct Channel {
pub managed: Option<bool>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<ThreadMember>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub message_count: Option<i32>,
pub name: Option<String>,
pub nsfw: Option<bool>,
@ -75,6 +77,7 @@ pub struct Channel {
#[cfg(not(feature = "sqlx"))]
#[cfg_attr(feature = "client", observe_option_vec)]
pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub permissions: Option<String>,
pub position: Option<i32>,
pub rate_limit_per_user: Option<i32>,
@ -85,6 +88,7 @@ pub struct Channel {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread_metadata: Option<ThreadMetadata>,
pub topic: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub total_message_sent: Option<i32>,
pub user_limit: Option<i32>,
pub video_quality_mode: Option<i32>,

View File

@ -6,7 +6,7 @@ use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use crate::types::Shared;
use crate::types::{PartialEmoji, Shared};
use crate::types::entities::User;
use crate::types::Snowflake;
@ -66,3 +66,18 @@ impl PartialEq for Emoji {
|| self.available != other.available)
}
}
impl From<PartialEmoji> for Emoji {
fn from(value: PartialEmoji) -> Self {
Self {
id: value.id.unwrap_or_default(), // TODO: this should be handled differently
name: Some(value.name),
roles: None,
user: None,
require_colons: Some(value.animated),
managed: None,
animated: Some(value.animated),
available: None,
}
}
}

View File

@ -59,7 +59,8 @@ pub struct Guild {
pub emojis: Vec<Shared<Emoji>>,
pub explicit_content_filter: Option<ExplicitContentFilterLevel>,
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
pub features: Option<GuildFeaturesList>,
#[serde(default)]
pub features: GuildFeaturesList,
pub icon: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub icon_hash: Option<String>,
@ -99,7 +100,7 @@ pub struct Guild {
pub splash: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>,
pub system_channel_flags: Option<u64>,
pub system_channel_flags: Option<SystemChannelFlags>,
pub system_channel_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub vanity_url_code: Option<String>,
@ -111,7 +112,7 @@ pub struct Guild {
#[cfg_attr(feature = "client", observe_option_vec)]
pub webhooks: Option<Vec<Shared<Webhook>>>,
#[cfg(feature = "sqlx")]
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
pub welcome_screen: sqlx::types::Json<Option<WelcomeScreenObject>>,
#[cfg(not(feature = "sqlx"))]
pub welcome_screen: Option<WelcomeScreenObject>,
pub widget_channel_id: Option<Snowflake>,
@ -422,7 +423,8 @@ pub enum PremiumTier {
}
bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#system-channel-flags>
pub struct SystemChannelFlags: u64 {

View File

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

View File

@ -16,7 +16,9 @@ use super::{Application, Channel, GuildMember, NSFWLevel, User};
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Invite {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub approximate_member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub approximate_presence_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub channel: Option<Channel>,
@ -45,7 +47,7 @@ pub struct Invite {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub target_user: Option<User>,
pub temporary: Option<bool>,
pub uses: Option<i32>,
pub uses: Option<u32>,
}
/// The guild an invite is for.
@ -77,20 +79,17 @@ impl From<Guild> for InviteGuild {
icon: value.icon,
splash: value.splash,
verification_level: value.verification_level.unwrap_or_default(),
features: value.features.unwrap_or_default(),
features: value.features,
vanity_url_code: value.vanity_url_code,
description: value.description,
banner: value.banner,
premium_subscription_count: value.premium_subscription_count,
nsfw_deprecated: None,
nsfw_level: value.nsfw_level.unwrap_or_default(),
welcome_screen: value.welcome_screen.map(|obj| {
#[cfg(feature = "sqlx")]
let res = obj.0;
#[cfg(not(feature = "sqlx"))]
let res = obj;
res
}),
#[cfg(feature = "sqlx")]
welcome_screen: value.welcome_screen.0,
#[cfg(not(feature = "sqlx"))]
welcome_screen: value.welcome_screen,
}
}
}

View File

@ -2,8 +2,10 @@
// 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/.
use bitflags::bitflags;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::{
Shared,
@ -39,7 +41,7 @@ pub struct Message {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub attachments: Option<Vec<Attachment>>,
#[cfg(feature = "sqlx")]
pub embeds: Vec<sqlx::types::Json<Embed>>,
pub embeds: sqlx::types::Json<Vec<Embed>>,
#[cfg(not(feature = "sqlx"))]
pub embeds: Option<Vec<Embed>>,
#[cfg(feature = "sqlx")]
@ -50,7 +52,7 @@ pub struct Message {
pub pinned: bool,
pub webhook_id: Option<Snowflake>,
#[serde(rename = "type")]
pub message_type: i32,
pub message_type: MessageType,
#[cfg(feature = "sqlx")]
pub activity: Option<sqlx::types::Json<MessageActivity>>,
#[cfg(not(feature = "sqlx"))]
@ -62,14 +64,22 @@ pub struct Message {
pub message_reference: Option<sqlx::types::Json<MessageReference>>,
#[cfg(not(feature = "sqlx"))]
pub message_reference: Option<MessageReference>,
pub flags: Option<u64>,
pub flags: Option<MessageFlags>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub referenced_message: Option<Box<Message>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub interaction: Option<MessageInteraction>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread: Option<Channel>,
#[cfg(feature = "sqlx")]
pub components: Option<sqlx::types::Json<Vec<Component>>>,
#[cfg(not(feature = "sqlx"))]
pub components: Option<Vec<Component>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub sticker_items: Option<Vec<StickerItem>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>,
pub position: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub role_subscription_data: Option<RoleSubscriptionData>,
}
@ -103,7 +113,7 @@ impl PartialEq for Message {
&& self.thread == other.thread
&& self.components == other.components
&& self.sticker_items == other.sticker_items
&& self.position == other.position
// && self.position == other.position
&& self.role_subscription_data == other.role_subscription_data
}
}
@ -112,12 +122,22 @@ impl PartialEq for Message {
/// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object>
pub struct MessageReference {
#[serde(rename = "type")]
pub reference_type: MessageReferenceType,
pub message_id: Snowflake,
pub channel_id: Snowflake,
pub guild_id: Option<Snowflake>,
pub fail_if_not_exists: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)]
pub enum MessageReferenceType {
/// A standard reference used by replies and system messages
Default = 0,
/// A reference used to point to a message at a point in time
Forward = 1,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MessageInteraction {
pub id: Snowflake,
@ -227,10 +247,15 @@ pub struct EmbedField {
pub struct Reaction {
pub count: u32,
pub burst_count: u32,
#[serde(default)]
pub me: bool,
#[serde(default)]
pub burst_me: bool,
pub burst_colors: Vec<String>,
pub emoji: Emoji,
#[cfg(feature = "sqlx")]
#[serde(skip)]
pub user_ids: Vec<Snowflake>
}
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
@ -253,3 +278,155 @@ pub struct MessageActivity {
pub activity_type: i64,
pub party_id: Option<String>,
}
#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize_repr, Deserialize_repr, Eq, PartialOrd, Ord)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
/// # Reference
/// See <https://docs.discord.sex/resources/message#message-type>
pub enum MessageType {
/// A default message
#[default]
Default = 0,
/// A message sent when a user is added to a group DM or thread
RecipientAdd = 1,
/// A message sent when a user is removed from a group DM or thread
RecipientRemove = 2,
/// A message sent when a user creates a call in a private channel
Call = 3,
/// A message sent when a group DM or thread's name is changed
ChannelNameChange = 4,
/// A message sent when a group DM's icon is changed
ChannelIconChange = 5,
/// A message sent when a message is pinned in a channel
ChannelPinnedMessage = 6,
/// A message sent when a user joins a guild
GuildMemberJoin = 7,
/// A message sent when a user subscribes to (boosts) a guild
UserPremiumGuildSubscription = 8,
/// A message sent when a user subscribes to (boosts) a guild to tier 1
UserPremiumGuildSubscriptionTier1 = 9,
/// A message sent when a user subscribes to (boosts) a guild to tier 2
UserPremiumGuildSubscriptionTier2 = 10,
/// A message sent when a user subscribes to (boosts) a guild to tier 3
UserPremiumGuildSubscriptionTier3 = 11,
/// A message sent when a news channel is followed
ChannelFollowAdd = 12,
/// A message sent when a user starts streaming in a guild (deprecated)
#[deprecated]
GuildStream = 13,
/// A message sent when a guild is disqualified from discovery
GuildDiscoveryDisqualified = 14,
/// A message sent when a guild requalifies for discovery
GuildDiscoveryRequalified = 15,
/// A message sent when a guild has failed discovery requirements for a week
GuildDiscoveryGracePeriodInitial = 16,
/// A message sent when a guild has failed discovery requirements for 3 weeks
GuildDiscoveryGracePeriodFinal = 17,
/// A message sent when a thread is created
ThreadCreated = 18,
/// A message sent when a user replies to a message
Reply = 19,
/// A message sent when a user uses a slash command
#[serde(rename = "CHAT_INPUT_COMMAND")]
ApplicationCommand = 20,
/// A message sent when a thread starter message is added to a thread
ThreadStarterMessage = 21,
/// A message sent to remind users to invite friends to a guild
GuildInviteReminder = 22,
/// A message sent when a user uses a context menu command
ContextMenuCommand = 23,
/// A message sent when auto moderation takes an action
AutoModerationAction = 24,
/// A message sent when a user purchases or renews a role subscription
RoleSubscriptionPurchase = 25,
/// A message sent when a user is upsold to a premium interaction
InteractionPremiumUpsell = 26,
/// A message sent when a stage channel starts
StageStart = 27,
/// A message sent when a stage channel ends
StageEnd = 28,
/// A message sent when a user starts speaking in a stage channel
StageSpeaker = 29,
/// A message sent when a user raises their hand in a stage channel
StageRaiseHand = 30,
/// A message sent when a stage channel's topic is changed
StageTopic = 31,
/// A message sent when a user purchases an application premium subscription
GuildApplicationPremiumSubscription = 32,
/// A message sent when a user adds an application to group DM
PrivateChannelIntegrationAdded = 33,
/// A message sent when a user removed an application from a group DM
PrivateChannelIntegrationRemoved = 34,
/// A message sent when a user gifts a premium (Nitro) referral
PremiumReferral = 35,
/// A message sent when a user enabled lockdown for the guild
GuildIncidentAlertModeEnabled = 36,
/// A message sent when a user disables lockdown for the guild
GuildIncidentAlertModeDisabled = 37,
/// A message sent when a user reports a raid for the guild
GuildIncidentReportRaid = 38,
/// A message sent when a user reports a false alarm for the guild
GuildIncidentReportFalseAlarm = 39,
/// A message sent when no one sends a message in the current channel for 1 hour
GuildDeadchatRevivePrompt = 40,
/// A message sent when a user buys another user a gift
CustomGift = 41,
GuildGamingStatsPrompt = 42,
/// A message sent when a user purchases a guild product
PurchaseNotification = 44
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// # Reference
/// See <https://docs.discord.sex/resources/message#message-type>
pub struct MessageFlags: u64 {
/// This message has been published to subscribed channels (via Channel Following)
const CROSSPOSTED = 1 << 0;
/// This message originated from a message in another channel (via Channel Following)
const IS_CROSSPOST = 1 << 1;
/// Embeds will not be included when serializing this message
const SUPPRESS_EMBEDS = 1 << 2;
/// The source message for this crosspost has been deleted (via Channel Following)
const SOURCE_MESSAGE_DELETED = 1 << 3;
/// This message came from the urgent message system
const URGENT = 1 << 4;
/// This message has an associated thread, with the same ID as the message
const HAS_THREAD = 1 << 5;
/// This message is only visible to the user who invoked the interaction
const EPHEMERAL = 1 << 6;
/// This message is an interaction response and the bot is "thinking"
const LOADING = 1 << 7;
/// Some roles were not mentioned and added to the thread
const FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8;
/// This message contains a link that impersonates Discord
const SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10;
/// This message will not trigger push and desktop notifications
const SUPPRESS_NOTIFICATIONS = 1 << 12;
/// This message's audio attachments are rendered as voice messages
const VOICE_MESSAGE = 1 << 13;
/// This message has a forwarded message snapshot attached
const HAS_SNAPSHOT = 1 << 14;
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct PartialEmoji {
#[serde(default)]
pub id: Option<Snowflake>,
pub name: String,
#[serde(default)]
pub animated: bool
}
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, PartialOrd)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[repr(u8)]
pub enum ReactionType {
Normal = 0,
Burst = 1, // The dreaded super reactions
}

View File

@ -71,7 +71,8 @@ pub struct RoleTags {
}
bitflags! {
#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// Permissions limit what users of certain roles can do on a Guild to Guild basis.
///
/// # Reference:

View File

@ -4,9 +4,11 @@
use crate::types::utils::Snowflake;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_aux::prelude::deserialize_option_number_from_string;
use std::fmt::Debug;
use std::num::ParseIntError;
use std::str::FromStr;
#[cfg(feature = "client")]
use crate::gateway::Updateable;
@ -54,7 +56,7 @@ pub struct User {
/// So we need to account for that
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub flags: Option<i32>,
pub flags: Option<UserFlags>,
pub premium_since: Option<DateTime<Utc>>,
pub premium_type: Option<u8>,
pub pronouns: Option<String>,
@ -111,8 +113,8 @@ impl From<User> for PublicUser {
const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
pub struct UserFlags: u64 {
const DISCORD_EMPLOYEE = 1 << 0;
const PARTNERED_SERVER_OWNER = 1 << 1;

View File

@ -32,8 +32,8 @@ bitflags! {
/// Bitflags of speaking types;
///
/// See <https://discord.com/developers/docs/topics/voice-connections#speaking>
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize)]
pub struct SpeakingBitflags: u8 {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, chorus_macros::SerdeBitFlags)]
pub struct SpeakingBitflags: u64 {
/// Whether we'll be transmitting normal voice audio
const MICROPHONE = 1 << 0;
/// Whether we'll be transmitting context audio for video, no speaking indicator

View File

@ -59,7 +59,7 @@ pub struct GetChannelMessagesSchema {
/// Between 1 and 100, defaults to 50.
pub limit: Option<i32>,
#[serde(flatten)]
pub anchor: ChannelMessagesAnchor,
pub anchor: Option<ChannelMessagesAnchor>,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
@ -74,21 +74,21 @@ impl GetChannelMessagesSchema {
pub fn before(anchor: Snowflake) -> Self {
Self {
limit: None,
anchor: ChannelMessagesAnchor::Before(anchor),
anchor: Some(ChannelMessagesAnchor::Before(anchor)),
}
}
pub fn around(anchor: Snowflake) -> Self {
Self {
limit: None,
anchor: ChannelMessagesAnchor::Around(anchor),
anchor: Some(ChannelMessagesAnchor::Around(anchor)),
}
}
pub fn after(anchor: Snowflake) -> Self {
Self {
limit: None,
anchor: ChannelMessagesAnchor::After(anchor),
anchor: Some(ChannelMessagesAnchor::After(anchor)),
}
}
@ -131,63 +131,14 @@ impl Default for CreateChannelInviteSchema {
}
bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
pub struct InviteFlags: u64 {
const GUEST = 1 << 0;
const VIEWED = 1 << 1;
}
}
impl Serialize for InviteFlags {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.bits().to_string().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for InviteFlags {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
struct FlagsVisitor;
impl<'de> Visitor<'de> for FlagsVisitor
{
type Value = InviteFlags;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a raw u64 value of flags")
}
fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
InviteFlags::from_bits(v).ok_or(serde::de::Error::custom(Error::InvalidFlags(v)))
}
}
deserializer.deserialize_u64(FlagsVisitor)
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::MySql> for InviteFlags {
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
u64::type_info()
}
}
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::MySql> for InviteFlags {
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
u64::encode_by_ref(&self.0.0, buf)
}
}
#[cfg(feature = "sqlx")]
impl<'r> sqlx::Decode<'r, sqlx::MySql> for InviteFlags {
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'r>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
let raw = u64::decode(value)?;
Ok(Self::from_bits(raw).unwrap())
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]

View File

@ -132,6 +132,7 @@ pub struct ModifyGuildMemberSchema {
bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// Represents the flags of a Guild Member.
///
/// # Reference:

View File

@ -7,13 +7,13 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
};
use crate::types::{Attachment, Snowflake};
use crate::types::{Attachment, MessageFlags, MessageType, ReactionType, Snowflake};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct MessageSendSchema {
#[serde(rename = "type")]
pub message_type: Option<i32>,
pub message_type: Option<MessageType>,
pub content: Option<String>,
pub nonce: Option<String>,
pub tts: Option<bool>,
@ -118,13 +118,21 @@ pub struct MessageAck {
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct MessageModifySchema {
content: Option<String>,
embeds: Option<Vec<Embed>>,
embed: Option<Embed>,
allowed_mentions: Option<AllowedMention>,
components: Option<Vec<Component>>,
flags: Option<i32>,
files: Option<Vec<u8>>,
payload_json: Option<String>,
attachments: Option<Vec<Attachment>>,
pub content: Option<String>,
pub embeds: Option<Vec<Embed>>,
pub embed: Option<Embed>,
pub allowed_mentions: Option<AllowedMention>,
pub components: Option<Vec<Component>>,
pub flags: Option<MessageFlags>,
pub files: Option<Vec<u8>>,
pub payload_json: Option<String>,
pub attachments: Option<Vec<Attachment>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct ReactionQuerySchema {
pub after: Option<Snowflake>,
pub limit: Option<u32>,
#[serde(rename = "type")]
pub reaction_type: Option<ReactionType>
}

View File

@ -2,11 +2,11 @@
// 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/.
use std::num::ParseIntError;
use std::str::FromStr;
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::{{Decode, Encode, MySql}, database::{HasArguments, HasValueRef}, encode::IsNull, error::BoxDynError, mysql::MySqlValueRef};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::types::UserFlags;
bitflags! {
/// Rights are instance-wide, per-user permissions for everything you may perform on the instance,
@ -18,7 +18,8 @@ bitflags! {
///
/// # Reference
/// See <https://docs.spacebar.chat/setup/server/security/rights/>
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
pub struct Rights: u64 {
/// All rights
const OPERATOR = 1 << 0;
@ -132,33 +133,6 @@ bitflags! {
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<MySql> for Rights {
fn type_info() -> <sqlx::MySql as sqlx::Database>::TypeInfo {
u64::type_info()
}
fn compatible(ty: &<sqlx::MySql as sqlx::Database>::TypeInfo) -> bool {
u64::compatible(ty)
}
}
#[cfg(feature = "sqlx")]
impl<'q> Encode<'q, MySql> for Rights {
fn encode_by_ref(&self, buf: &mut <MySql as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
<u64 as Encode<MySql>>::encode_by_ref(&self.0.0, buf)
}
}
#[cfg(feature = "sqlx")]
impl<'r> Decode<'r, MySql> for Rights {
fn decode(value: <MySql as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
let raw = <u64 as Decode<MySql>>::decode(value)?;
Ok(Rights::from_bits(raw).unwrap())
}
}
impl Rights {
pub fn any(&self, permission: Rights, check_operator: bool) -> bool {
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)

View File

@ -1,6 +1,7 @@
use core::fmt;
use chrono::{LocalResult, NaiveDateTime};
use serde::de;
use serde::{de, Deserialize, Deserializer};
use serde::de::Error;
#[doc(hidden)]
#[derive(Debug)]
@ -259,4 +260,19 @@ pub(crate) fn serde_from<T, E, V>(me: LocalResult<T>, _ts: &V) -> Result<T, E>
}
LocalResult::Single(val) => Ok(val),
}
}
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(untagged)]
enum StringOrU64 {
String(String),
U64(u64),
}
pub fn string_or_u64<'de, D>(d: D) -> Result<u64, D::Error>
where D: Deserializer<'de> {
match StringOrU64::deserialize(d)? {
StringOrU64::String(s) => s.parse::<u64>().map_err(D::Error::custom),
StringOrU64::U64(u) => Ok(u)
}
}

View File

@ -8,8 +8,6 @@ use std::{
};
use chrono::{DateTime, TimeZone, Utc};
#[cfg(feature = "sqlx")]
use sqlx::Type;
/// 2015-01-01
const EPOCH: i64 = 1420070400000;
@ -19,8 +17,6 @@ const EPOCH: i64 = 1420070400000;
/// # Reference
/// See <https://discord.com/developers/docs/reference#snowflakes>
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "sqlx", derive(Type))]
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
pub struct Snowflake(pub u64);
impl Snowflake {
@ -102,6 +98,27 @@ impl<'de> serde::Deserialize<'de> for Snowflake {
}
}
#[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::MySql> for Snowflake {
fn type_info() -> <sqlx::MySql as sqlx::Database>::TypeInfo {
<String as sqlx::Type<sqlx::MySql>>::type_info()
}
}
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::MySql> for Snowflake {
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
<String as sqlx::Encode<'q, sqlx::MySql>>::encode_by_ref(&self.0.to_string(), buf)
}
}
#[cfg(feature = "sqlx")]
impl<'d> sqlx::Decode<'d, sqlx::MySql> for Snowflake {
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'d>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
<String as sqlx::Decode<'d, sqlx::MySql>>::decode(value).map(|s| s.parse::<u64>().map(Snowflake).unwrap())
}
}
#[cfg(test)]
mod test {
use chrono::{DateTime, Utc};

View File

@ -920,7 +920,7 @@ mod entities {
.clone()
);
let flags = ApplicationFlags::from_bits(0).unwrap();
assert!(application.flags() == flags);
assert!(application.flags == flags);
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]