diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 00cbfc1..d1f03f0 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -6,11 +6,7 @@ use crate::api::limits::Limits; use crate::errors::InstanceServerError; use crate::instance::UserMeta; use crate::limit::LimitedRequester; -use crate::types::Channel; -use crate::types::ChannelCreateSchema; -use crate::types::Guild; -use crate::types::GuildCreateResponse; -use crate::types::GuildCreateSchema; +use crate::types::{Channel, ChannelCreateSchema, Guild, GuildCreateResponse, GuildCreateSchema}; impl Guild { /// Creates a new guild with the given parameters. diff --git a/src/api/schemas.rs b/src/api/schemas.rs deleted file mode 100644 index fb1a14f..0000000 --- a/src/api/schemas.rs +++ /dev/null @@ -1,432 +0,0 @@ -use regex::Regex; -use serde::{Deserialize, Serialize}; - -use crate::errors::FieldFormatError; - -use super::{Channel, Embed}; - -/** -A struct that represents a well-formed email address. - */ -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct AuthEmail { - pub email: String, -} - -impl AuthEmail { - /** - Returns a new [`Result`]. - ## Arguments - The email address you want to validate. - ## Errors - You will receive a [`FieldFormatError`], if: - - The email address is not in a valid format. - - */ - pub fn new(email: String) -> Result { - let regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap(); - if !regex.is_match(email.as_str()) { - return Err(FieldFormatError::EmailError); - } - Ok(AuthEmail { email }) - } -} - -/** -A struct that represents a well-formed username. -## Arguments -Please use new() to create a new instance of this struct. -## Errors -You will receive a [`FieldFormatError`], if: -- The username is not between 2 and 32 characters. - */ -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct AuthUsername { - pub username: String, -} - -impl AuthUsername { - /** - Returns a new [`Result`]. - ## Arguments - The username you want to validate. - ## Errors - You will receive a [`FieldFormatError`], if: - - The username is not between 2 and 32 characters. - */ - pub fn new(username: String) -> Result { - if username.len() < 2 || username.len() > 32 { - Err(FieldFormatError::UsernameError) - } else { - Ok(AuthUsername { username }) - } - } -} - -/** -A struct that represents a well-formed password. -## Arguments -Please use new() to create a new instance of this struct. -## Errors -You will receive a [`FieldFormatError`], if: -- The password is not between 1 and 72 characters. - */ -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct AuthPassword { - pub password: String, -} - -impl AuthPassword { - /** - Returns a new [`Result`]. - ## Arguments - The password you want to validate. - ## Errors - You will receive a [`FieldFormatError`], if: - - The password is not between 1 and 72 characters. - */ - pub fn new(password: String) -> Result { - if password.is_empty() || password.len() > 72 { - Err(FieldFormatError::PasswordError) - } else { - Ok(AuthPassword { password }) - } - } -} - -/** -A struct that represents a well-formed register request. -## Arguments -Please use new() to create a new instance of this struct. -## Errors -You will receive a [`FieldFormatError`], if: -- The username is not between 2 and 32 characters. -- The password is not between 1 and 72 characters. - */ - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub struct RegisterSchema { - username: String, - password: Option, - consent: bool, - email: Option, - fingerprint: Option, - invite: Option, - date_of_birth: Option, - gift_code_sku_id: Option, - captcha_key: Option, - promotional_email_opt_in: Option, -} - -impl RegisterSchema { - /** - Returns a new [`Result`]. - ## Arguments - All but "String::username" and "bool::consent" are optional. - - ## Errors - You will receive a [`FieldFormatError`], if: - - The username is less than 2 or more than 32 characters in length - - You supply a `password` which is less than 1 or more than 72 characters in length. - - These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/) - */ - pub fn new( - username: AuthUsername, - password: Option, - consent: bool, - email: Option, - fingerprint: Option, - invite: Option, - date_of_birth: Option, - gift_code_sku_id: Option, - captcha_key: Option, - promotional_email_opt_in: Option, - ) -> Result { - let username = username.username; - - let email_addr; - if email.is_some() { - email_addr = Some(email.unwrap().email); - } else { - email_addr = None; - } - - let has_password; - if password.is_some() { - has_password = Some(password.unwrap().password); - } else { - has_password = None; - } - - if !consent { - return Err(FieldFormatError::ConsentError); - } - - Ok(RegisterSchema { - username, - password: has_password, - consent, - email: email_addr, - fingerprint, - invite, - date_of_birth, - gift_code_sku_id, - captcha_key, - promotional_email_opt_in, - }) - } -} - -/** -A struct that represents a well-formed login request. -## Arguments -Please use new() to create a new instance of this struct. -## Errors -You will receive a [`FieldFormatError`], if: -- The username is not between 2 and 32 characters. -- The password is not between 1 and 72 characters. - */ -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub struct LoginSchema { - login: String, - password: String, - undelete: Option, - captcha_key: Option, - login_source: Option, - gift_code_sku_id: Option, -} - -impl LoginSchema { - /** - Returns a new [`Result`]. - ## Arguments - login: The username you want to login with. - password: The password you want to login with. - undelete: Honestly no idea what this is for. - captcha_key: The captcha key you want to login with. - login_source: The login source. - gift_code_sku_id: The gift code sku id. - ## Errors - You will receive a [`FieldFormatError`], if: - - The username is less than 2 or more than 32 characters in length - */ - pub fn new( - login: AuthUsername, - password: String, - undelete: Option, - captcha_key: Option, - login_source: Option, - gift_code_sku_id: Option, - ) -> Result { - let login = login.username; - Ok(LoginSchema { - login, - password, - undelete, - captcha_key, - login_source, - gift_code_sku_id, - }) - } -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub struct TotpSchema { - code: String, - ticket: String, - gift_code_sku_id: Option, - login_source: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct MessageSendSchema { - #[serde(rename = "type")] - message_type: Option, - content: Option, - nonce: Option, - tts: Option, - embeds: Option>, - allowed_mentions: Option, - message_reference: Option, - components: Option>, - sticker_ids: Option>, - pub attachments: Option>, -} - -// make a new() method for MessageSendSchema -impl MessageSendSchema { - pub fn new( - message_type: Option, - content: Option, - nonce: Option, - tts: Option, - embeds: Option>, - allowed_mentions: Option, - message_reference: Option, - components: Option>, - sticker_ids: Option>, - attachments: Option>, - ) -> MessageSendSchema { - MessageSendSchema { - message_type, - content, - nonce, - tts, - embeds, - allowed_mentions, - message_reference, - components, - sticker_ids, - attachments, - } - } -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct GuildCreateSchema { - pub name: Option, - pub region: Option, - pub icon: Option, - pub channels: Option>, - pub guild_template_code: Option, - pub system_channel_id: Option, - pub rules_channel_id: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct UserModifySchema { - pub username: Option, - pub avatar: Option, - pub bio: Option, - pub accent_color: Option, - pub banner: Option, - pub current_password: Option, - pub new_password: Option, - pub code: Option, - pub email: Option, - pub discriminator: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -// TODO: Implement in polyphony/types -pub struct ChannelCreateSchema { - pub name: String, - #[serde(rename = "type")] - pub channel_type: Option, - pub topic: Option, - pub icon: Option, - pub bitrate: Option, - pub user_limit: Option, - pub rate_limit_per_user: Option, - pub position: Option, - pub permission_overwrites: Option>, - pub parent_id: Option, - pub id: Option, - pub nsfw: Option, - pub rtc_region: Option, - pub default_auto_archive_duration: Option, - pub default_reaction_emoji: Option, - pub flags: Option, - pub default_thread_rate_limit_per_user: Option, - pub video_quality_mode: Option, -} - -// I know that some of these tests are... really really basic and unneccessary, but sometimes, I -// just feel like writing tests, so there you go :) -@bitfl0wer -#[cfg(test)] -mod schemas_tests { - use super::*; - use crate::errors::FieldFormatError; - - #[test] - fn password_too_short() { - assert_eq!( - AuthPassword::new("".to_string()), - Err(FieldFormatError::PasswordError) - ); - } - - #[test] - fn password_too_long() { - let mut long_pw = String::new(); - for _ in 0..73 { - long_pw += "a"; - } - assert_eq!( - AuthPassword::new(long_pw), - Err(FieldFormatError::PasswordError) - ); - } - - #[test] - fn username_too_short() { - assert_eq!( - AuthUsername::new("T".to_string()), - Err(FieldFormatError::UsernameError) - ); - } - - #[test] - fn username_too_long() { - let mut long_un = String::new(); - for _ in 0..33 { - long_un += "a"; - } - assert_eq!( - AuthUsername::new(long_un), - Err(FieldFormatError::UsernameError) - ); - } - - #[test] - fn consent_false() { - assert_eq!( - RegisterSchema::new( - AuthUsername::new("Test".to_string()).unwrap(), - None, - false, - None, - None, - None, - None, - None, - None, - None, - ), - Err(FieldFormatError::ConsentError) - ); - } - - #[test] - fn invalid_email() { - assert_eq!( - AuthEmail::new("p@p.p".to_string()), - Err(FieldFormatError::EmailError) - ) - } - - #[test] - fn valid_email() { - let reg = RegisterSchema::new( - AuthUsername::new("Testy".to_string()).unwrap(), - None, - true, - Some(AuthEmail::new("me@mail.de".to_string()).unwrap()), - None, - None, - None, - None, - None, - None, - ); - assert_ne!(reg, Err(FieldFormatError::EmailError)); - } -} diff --git a/src/api/types.rs b/src/api/types.rs deleted file mode 100644 index f040dd2..0000000 --- a/src/api/types.rs +++ /dev/null @@ -1,1482 +0,0 @@ -/* -To learn more about the types implemented here, please visit -https://discord.com/developers/docs . -I do not feel like re-documenting all of this, as everything is already perfectly explained there. -*/ - -use std::{cell::RefCell, rc::Rc}; - -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use serde_json::from_value; - -use crate::{api::limits::Limits, instance::Instance}; - -pub trait WebSocketEvent {} - -#[derive(Debug, Serialize, Deserialize)] -pub struct LoginResult { - pub token: String, - pub settings: UserSettings, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UserSettings { - afk_timeout: i32, - allow_accessibility_detection: bool, - animate_emoji: bool, - animate_stickers: i32, - contact_sync_enabled: bool, - convert_emoticons: bool, - custom_status: Option, - default_guilds_restricted: bool, - detect_platform_accounts: bool, - developer_mode: bool, - disable_games_tab: bool, - enable_tts_command: bool, - explicit_content_filter: i32, - friend_source_flags: FriendSourceFlags, - friend_discovery_flags: Option, - gateway_connected: bool, - gif_auto_play: bool, - guild_folders: Vec, - guild_positions: Vec, - inline_attachment_media: bool, - inline_embed_media: bool, - locale: String, - message_display_compact: bool, - native_phone_integration_enabled: bool, - render_embeds: bool, - render_reactions: bool, - restricted_guilds: Vec, - show_current_game: bool, - status: String, - stream_notifications_enabled: bool, - theme: String, - timezone_offset: i32, - //view_nsfw_guilds: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct FriendSourceFlags { - all: Option, - mutual_friends: Option, - mutual_guilds: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct GuildFolder { - id: String, - guild_ids: Vec, - name: String, -} - -/** -Represents the result you get from GET: /api/instance/policies/. -*/ -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -pub struct InstancePolicies { - instance_name: String, - instance_description: Option, - front_page: Option, - tos_page: Option, - correspondence_email: Option, - correspondence_user_id: Option, - image: Option, - instance_id: Option, -} - -impl InstancePolicies { - pub fn new( - instance_name: String, - instance_description: Option, - front_page: Option, - tos_page: Option, - correspondence_email: Option, - correspondence_user_id: Option, - image: Option, - instance_id: Option, - ) -> Self { - InstancePolicies { - instance_name, - instance_description, - front_page, - tos_page, - correspondence_email, - correspondence_user_id, - image, - instance_id, - } - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ErrorResponse { - pub code: i32, - pub message: String, - pub errors: IntermittentError, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct IntermittentError { - #[serde(flatten)] - pub errors: std::collections::HashMap, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct ErrorField { - #[serde(default)] - pub _errors: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Error { - pub message: String, - pub code: String, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct UnavailableGuild { - id: String, - unavailable: bool, -} - -/// See https://discord.com/developers/docs/resources/guild -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct Guild { - pub id: String, - pub name: String, - pub icon: Option, - pub icon_hash: Option, - pub splash: Option, - pub discovery_splash: Option, - pub owner: Option, - pub owner_id: Option, - pub permissions: Option, - pub afk_channel_id: Option, - pub afk_timeout: Option, - pub widget_enabled: Option, - pub widget_channel_id: Option, - pub widget_channel: Option, - pub verification_level: Option, - pub default_message_notifications: Option, - pub explicit_content_filter: Option, - pub roles: Vec, - pub emojis: Vec, - pub features: Vec, - pub application_id: Option, - pub system_channel_id: Option, - pub system_channel_flags: Option, - pub rules_channel_id: Option, - pub rules_channel: Option, - pub max_presences: Option, - pub max_members: Option, - pub vanity_url_code: Option, - pub description: Option, - pub banner: Option, - pub premium_tier: Option, - pub premium_subscription_count: Option, - pub preferred_locale: Option, - pub public_updates_channel_id: Option, - pub public_updates_channel: Option, - pub max_video_channel_users: Option, - pub max_stage_video_channel_users: Option, - pub approximate_member_count: Option, - pub approximate_presence_count: Option, - pub member_count: Option, - pub presence_count: Option, - pub welcome_screen: Option, - pub nsfw_level: u8, - pub nsfw: bool, - pub stickers: Option>, - pub premium_progress_bar_enabled: Option, - pub joined_at: String, - pub afk_channel: Option, - pub bans: Option>, - pub primary_category_id: Option, - pub large: Option, - pub channels: Option>, - pub template_id: Option, - pub template: Option, - pub invites: Option>, - pub voice_states: Option>, - pub webhooks: Option>, - pub mfa_level: Option, - pub region: Option, - pub unavailable: bool, - pub parent: Option, -} - -/// See https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user- -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct GuildBan { - pub id: String, - pub user_id: String, - pub guild_id: String, - pub executor_id: String, - pub reason: Option, -} - -/// See https://discord.com/developers/docs/topics/gateway-events#guild-create-guild-create-extra-fields -/// This is like [Guild], expect it has extra fields -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct GuildCreateGuild { - pub id: String, - pub name: String, - pub icon: Option, - pub icon_hash: Option, - pub splash: Option, - pub discovery_splash: Option, - pub owner: Option, - pub owner_id: String, - pub permissions: Option, - pub afk_channel_id: Option, - pub afk_timeout: u8, - pub widget_enabled: Option, - pub widget_channel_id: Option, - pub verification_level: u8, - pub default_message_notifications: u8, - pub explicit_content_filter: u8, - pub roles: Vec, - pub emojis: Vec, - pub features: Vec, - pub mfa_level: u8, - pub application_id: Option, - pub system_channel_id: Option, - pub system_channel_flags: u8, - pub rules_channel_id: Option, - pub max_presences: Option, - pub max_members: Option, - pub vanity_url_code: Option, - pub description: Option, - pub banner: Option, - pub premium_tier: u8, - pub premium_subscription_count: Option, - pub preferred_locale: String, - pub public_updates_channel_id: Option, - pub max_video_channel_users: Option, - pub max_stage_video_channel_users: Option, - pub approximate_member_count: Option, - pub approximate_presence_count: Option, - pub welcome_screen: Option, - pub nsfw_level: u8, - pub stickers: Option>, - pub premium_progress_bar_enabled: bool, - // ------ Extra Fields ------ - pub joined_at: DateTime, - pub large: bool, - pub unavailable: Option, - pub member_count: u64, - // to:do implement voice states - //pub voice_states: Vec, - pub members: Vec, - pub channels: Vec, - pub threads: Vec, - pub presences: Vec, - // to:do add stage instances - //pub stage_instances: Vec, - // to:do add guild schedules events - //pub guild_scheduled_events: Vec -} - -impl GuildCreateGuild { - /// Converts self to a [Guild], discarding the extra fields - pub fn to_guild(&self) -> Guild { - let as_value = serde_json::to_value(&self).unwrap(); - return from_value(as_value).unwrap(); - } -} - -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct WelcomeScreenObject { - pub description: Option, - pub welcome_channels: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct WelcomeScreenChannel { - pub channel_id: String, - pub description: String, - pub emoji_id: Option, - pub emoji_name: Option, -} - -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -/// See https://discord.com/developers/docs/topics/permissions#role-object -pub struct RoleObject { - pub id: String, - pub name: String, - pub color: f64, - pub hoist: bool, - pub icon: Option, - pub unicode_emoji: Option, - pub position: u16, - pub permissions: String, - pub managed: bool, - pub mentionable: bool, - // to:do add role tags https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure - //pub tags: Option -} - -#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)] -pub struct UserObject { - pub id: String, - username: String, - discriminator: String, - avatar: Option, - bot: bool, - system: Option, - mfa_enabled: Option, - accent_color: Option, - locale: Option, - verified: Option, - email: Option, - flags: String, - premium_since: Option, - premium_type: i8, - pronouns: Option, - public_flags: Option, - banner: Option, - bio: String, - theme_colors: Option>, - phone: Option, - nsfw_allowed: bool, - premium: bool, - purchased_flags: i32, - premium_usage_flags: i32, - disabled: Option, -} - -#[derive(Debug)] -pub struct User { - pub belongs_to: Rc>, - pub token: String, - pub limits: Limits, - pub settings: UserSettings, - pub object: Option, -} - -impl User { - pub fn token(&self) -> String { - self.token.clone() - } - - pub fn set_token(&mut self, token: String) { - self.token = token; - } - - pub fn new( - belongs_to: Rc>, - token: String, - limits: Limits, - settings: UserSettings, - object: Option, - ) -> User { - User { - belongs_to, - token, - limits, - settings, - object, - } - } -} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct Message { - id: String, - pub channel_id: String, - author: UserObject, - content: String, - timestamp: String, - edited_timestamp: Option, - tts: bool, - mention_everyone: bool, - mentions: Vec, - mention_roles: Vec, - mention_channels: Option>, - pub attachments: Vec, - embeds: Vec, - reactions: Option>, - nonce: Option, - pinned: bool, - webhook_id: Option, - #[serde(rename = "type")] - message_type: i32, - activity: Option, - application: Option, - application_id: Option, - message_reference: Option, - flags: Option, - referenced_message: Option>, - interaction: Option, - thread: Option, - components: Option>, - sticker_items: Option>, - stickers: Option>, - position: Option, - role_subscription_data: Option, -} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageCreate { - #[serde(flatten)] - message: Message, - guild_id: Option, - member: Option, - mentions: Vec<(UserObject, GuildMember)>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create -} - -impl WebSocketEvent for MessageCreate {} - -#[derive(Debug, Serialize, Deserialize, Default)] -struct PartialMessage { - id: Option, - channel_id: Option, - author: Option, - content: Option, - timestamp: Option, - edited_timestamp: Option, - tts: Option, - mention_everyone: Option, - mentions: Option>, - mention_roles: Option>, - mention_channels: Option>, - attachments: Option>, - embeds: Option>, - reactions: Option>, - nonce: Option, - pinned: Option, - webhook_id: Option, - #[serde(rename = "type")] - message_type: Option, - activity: Option, - application: Option, - application_id: Option, - message_reference: Option, - flags: Option, - referenced_message: Option>, - interaction: Option, - thread: Option, - components: Option>, - sticker_items: Option>, - stickers: Option>, - position: Option, - role_subscription_data: Option, - guild_id: Option, - member: Option, -} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageUpdate { - #[serde(flatten)] - message: PartialMessage, - guild_id: Option, - member: Option, - mentions: Vec<(UserObject, GuildMember)>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create -} - -impl WebSocketEvent for MessageUpdate {} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageDelete { - id: String, - channel_id: String, - guild_id: Option, -} - -impl WebSocketEvent for MessageDelete {} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageDeleteBulk { - ids: Vec, - channel_id: String, - guild_id: Option, -} - -impl WebSocketEvent for MessageDeleteBulk {} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageReactionAdd { - user_id: String, - channel_id: String, - message_id: String, - guild_id: Option, - member: Option, - emoji: Emoji, -} - -impl WebSocketEvent for MessageReactionAdd {} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageReactionRemove { - user_id: String, - channel_id: String, - message_id: String, - guild_id: Option, - emoji: Emoji, -} - -impl WebSocketEvent for MessageReactionRemove {} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageReactionRemoveAll { - channel_id: String, - message_id: String, - guild_id: Option, -} - -impl WebSocketEvent for MessageReactionRemoveAll {} - -#[derive(Debug, Serialize, Deserialize, Default)] -pub struct MessageReactionRemoveEmoji { - channel_id: String, - message_id: String, - guild_id: Option, - emoji: Emoji, -} - -impl WebSocketEvent for MessageReactionRemoveEmoji {} - -#[derive(Debug, Serialize, Deserialize)] -struct ChannelMention { - id: String, - guild_id: String, - #[serde(rename = "type")] - channel_type: i32, - name: String, -} - -#[derive(Debug, Serialize, Deserialize)] -/** -Represents an Embed. [See the Discord Documentation](https://discord.com/developers/docs/resources/channel#embed-object). - */ -pub struct Embed { - title: Option, - #[serde(rename = "type")] - embed_type: Option, - description: Option, - url: Option, - timestamp: Option, - color: Option, - footer: Option, - image: Option, - thumbnail: Option, - video: Option, - provider: Option, - author: Option, - fields: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EmbedFooter { - text: String, - icon_url: Option, - proxy_icon_url: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EmbedImage { - url: String, - proxy_url: String, - height: Option, - width: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EmbedThumbnail { - url: String, - proxy_url: Option, - height: Option, - width: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EmbedVideo { - url: Option, - proxy_url: Option, - height: Option, - width: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EmbedProvider { - name: Option, - url: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -struct EmbedAuthor { - name: String, - url: Option, - icon_url: Option, - proxy_icon_url: Option, -} - -#[derive(Debug, Serialize, Deserialize)] - -struct EmbedField { - name: String, - value: String, - inline: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Reaction { - pub count: i32, - pub me: bool, - pub emoji: Emoji, -} - -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -pub struct Emoji { - pub id: Option, - pub name: Option, - pub roles: Option>, - pub user: Option, - pub require_colons: Option, - pub managed: Option, - pub animated: Option, - pub available: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct MessageActivity { - #[serde(rename = "type")] - pub activity_type: i64, - pub party_id: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct Application { - pub id: String, - pub name: String, - pub icon: Option, - pub description: String, - pub rpc_origins: Option>, - pub bot_public: bool, - pub bot_require_code_grant: bool, - pub terms_of_service_url: Option, - pub privacy_policy_url: Option, - pub owner: Option, - pub summary: String, - pub verify_key: String, - pub team: Option, - pub guild_id: Option, - pub primary_sku_id: Option, - pub slug: Option, - pub cover_image: Option, - pub flags: Option, - pub tags: Option>, - pub install_params: Option, - pub custom_install_url: Option, - pub role_connections_verification_url: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct Team { - pub icon: Option, - pub id: u64, - pub members: Vec, - pub name: String, - pub owner_user_id: u64, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct TeamMember { - pub membership_state: u8, - pub permissions: Vec, - pub team_id: u64, - pub user: UserObject, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum MembershipState { - Invited = 1, - Accepted = 2, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct InstallParams { - pub scopes: Vec, - pub permissions: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct MessageReference { - pub message_id: String, - pub channel_id: String, - pub guild_id: Option, - pub fail_if_not_exists: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct MessageInteraction { - pub id: u64, - #[serde(rename = "type")] - pub interaction_type: u8, - pub name: String, - pub user: UserObject, - pub member: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] -pub struct GuildMember { - pub user: Option, - pub nick: Option, - pub avatar: Option, - pub roles: Vec, - pub joined_at: String, - pub premium_since: Option, - pub deaf: bool, - pub mute: bool, - pub flags: i32, - pub pending: Option, - pub permissions: Option, - pub communication_disabled_until: Option, -} - -#[derive(Default, Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct Channel { - pub id: String, - #[serde(rename = "type")] - pub channel_type: i32, - pub guild_id: Option, - pub position: Option, - pub permission_overwrites: Option>, - pub name: Option, - pub topic: Option, - pub nsfw: Option, - pub last_message_id: Option, - pub bitrate: Option, - pub user_limit: Option, - pub rate_limit_per_user: Option, - pub recipients: Option>, - pub icon: Option, - pub owner_id: Option, - pub application_id: Option, - pub parent_id: Option, - pub last_pin_timestamp: Option, - pub rtc_region: Option, - pub video_quality_mode: Option, - pub message_count: Option, - pub member_count: Option, - pub thread_metadata: Option, - pub member: Option, - pub default_auto_archive_duration: Option, - pub permissions: Option, - pub flags: Option, - pub total_message_sent: Option, - pub available_tags: Option>, - pub applied_tags: Option>, - pub default_reaction_emoji: Option, - pub default_thread_rate_limit_per_user: Option, - pub default_sort_order: Option, - pub default_forum_layout: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] -pub struct Tag { - pub id: u64, - pub name: String, - pub moderated: bool, - pub emoji_id: Option, - pub emoji_name: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct PermissionOverwrite { - pub id: String, - #[serde(rename = "type")] - pub overwrite_type: u8, - pub allow: String, - pub deny: String, -} - -#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] -pub struct ThreadMetadata { - pub archived: bool, - pub auto_archive_duration: i32, - pub archive_timestamp: String, - pub locked: bool, - pub invitable: Option, - pub create_timestamp: Option, -} - -#[derive(Default, Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] -pub struct ThreadMember { - pub id: Option, - pub user_id: Option, - pub join_timestamp: Option, - pub flags: Option, - pub member: Option, -} - -#[derive(Default, Debug, Deserialize, Serialize, Clone)] -/// See https://discord.com/developers/docs/resources/guild#integration-object-integration-structure -pub struct Integration { - pub id: String, - pub name: String, - #[serde(rename = "type")] - pub integration_type: String, - pub enabled: bool, - pub syncing: Option, - pub role_id: Option, - pub enabled_emoticons: Option, - pub expire_behaviour: Option, - pub expire_grace_period: Option, - pub user: Option, - pub account: IntegrationAccount, - pub synced_at: Option>, - pub subscriber_count: Option, - pub revoked: Option, - pub application: Option, - pub scopes: Option>, -} - -#[derive(Default, Debug, Deserialize, Serialize, Clone)] -/// See https://discord.com/developers/docs/resources/guild#integration-account-object-integration-account-structure -pub struct IntegrationAccount { - pub id: String, - pub name: String, -} - -#[derive(Default, Debug, Deserialize, Serialize, Clone)] -/// See https://discord.com/developers/docs/resources/stage-instance#stage-instance-object -pub struct StageInstance { - pub id: String, - pub guild_id: String, - pub channel_id: String, - pub topic: String, - pub privacy_level: u8, - pub discoverable_disabled: bool, - pub guild_scheduled_event_id: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)] -pub struct DefaultReaction { - pub emoji_id: Option, - pub emoji_name: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum Component { - ActionRow = 1, - Button = 2, - StringSelect = 3, - TextInput = 4, - UserSelect = 5, - RoleSelect = 6, - MentionableSelect = 7, - ChannelSelect = 8, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct StickerItem { - pub id: u64, - pub name: String, - pub format_type: u8, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Sticker { - pub id: u64, - pub pack_id: Option, - pub name: String, - pub description: Option, - pub tags: String, - pub asset: Option, - #[serde(rename = "type")] - pub sticker_type: u8, - pub format_type: u8, - pub available: Option, - pub guild_id: Option, - pub user: Option, - pub sort_value: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct RoleSubscriptionData { - pub role_subscription_listing_id: u64, - pub tier_name: String, - pub total_months_subscribed: u32, - pub is_renewal: bool, -} - -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct TypingStartEvent { - pub channel_id: String, - pub guild_id: Option, - pub user_id: String, - pub timestamp: i64, - pub member: Option, -} - -impl WebSocketEvent for TypingStartEvent {} - -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct GatewayIdentifyPayload { - pub token: String, - pub properties: GatewayIdentifyConnectionProps, - pub compress: Option, - pub large_threshold: Option, //default: 50 - pub shard: Option>, - pub presence: Option, - pub intents: i32, -} - -impl WebSocketEvent for GatewayIdentifyPayload {} - -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct GatewayIdentifyConnectionProps { - pub os: String, - pub browser: String, - pub device: String, -} - -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -/// See https://discord.com/developers/docs/topics/gateway-events#presence-update-presence-update-event-fields -pub struct PresenceUpdate { - pub user: UserObject, - pub guild_id: String, - pub status: String, - pub activities: Vec, - pub client_status: ClientStatusObject, -} - -#[derive(Debug, Deserialize, Serialize, Default, Clone)] -/// See https://discord.com/developers/docs/topics/gateway-events#client-status-object -pub struct ClientStatusObject { - pub desktop: Option, - pub mobile: Option, - pub web: Option, -} - -impl WebSocketEvent for PresenceUpdate {} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct Activity { - name: String, - #[serde(rename = "type")] - activity_type: i32, - url: Option, - created_at: i64, - timestamps: Option, - application_id: Option, - details: Option, - state: Option, - emoji: Option, - party: Option, - assets: Option, - secrets: Option, - instance: Option, - flags: Option, - buttons: Option>, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -struct ActivityTimestamps { - start: Option, - end: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -struct ActivityParty { - id: Option, - size: Option>, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -struct ActivityAssets { - large_image: Option, - large_text: Option, - small_image: Option, - small_text: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -struct ActivitySecrets { - join: Option, - spectate: Option, - #[serde(rename = "match")] - match_string: Option, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -struct ActivityButton { - label: String, - url: String, -} - -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct GatewayResume { - pub token: String, - pub session_id: String, - pub seq: String, -} - -impl WebSocketEvent for GatewayResume {} - -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct GatewayReady { - pub v: u8, - pub user: UserObject, - pub guilds: Vec, - pub session_id: String, - pub resume_gateway_url: Option, - pub shard: Option<(u64, u64)>, -} - -impl WebSocketEvent for GatewayReady {} - -#[derive(Debug, Deserialize, Serialize, Default)] -/// See https://discord.com/developers/docs/topics/gateway-events#request-guild-members-request-guild-members-structure -pub struct GatewayRequestGuildMembers { - pub guild_id: String, - pub query: Option, - pub limit: u64, - pub presence: Option, - pub user_ids: Option, - pub nonce: Option, -} - -impl WebSocketEvent for GatewayRequestGuildMembers {} - -#[derive(Debug, Deserialize, Serialize, Default)] -/// See https://discord.com/developers/docs/topics/gateway-events#update-voice-state-gateway-voice-state-update-structure -pub struct GatewayVoiceStateUpdate { - pub guild_id: String, - pub channel_id: Option, - pub self_mute: bool, - pub self_deaf: bool, -} - -impl WebSocketEvent for GatewayVoiceStateUpdate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct GatewayHello { - pub op: i32, - pub d: HelloData, -} - -impl WebSocketEvent for GatewayHello {} - -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct HelloData { - pub heartbeat_interval: u128, -} - -impl WebSocketEvent for HelloData {} - -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct GatewayHeartbeat { - pub op: u8, - pub d: Option, -} - -impl WebSocketEvent for GatewayHeartbeat {} - -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct GatewayHeartbeatAck { - pub op: i32, -} - -impl WebSocketEvent for GatewayHeartbeatAck {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#channel-pins-update -pub struct ChannelPinsUpdate { - pub guild_id: Option, - pub channel_id: String, - pub last_pin_timestamp: Option>, -} - -impl WebSocketEvent for ChannelPinsUpdate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields -pub struct GuildBanAdd { - pub guild_id: String, - pub user: UserObject, -} - -impl WebSocketEvent for GuildBanAdd {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove -pub struct GuildBanRemove { - pub guild_id: String, - pub user: UserObject, -} - -impl WebSocketEvent for GuildBanRemove {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#user-update -/// Not directly serialized, as the inner payload is the user object -pub struct UserUpdate { - pub user: UserObject, -} - -impl WebSocketEvent for UserUpdate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#channel-create -/// Not directly serialized, as the inner payload is a channel object -pub struct ChannelCreate { - pub channel: Channel, -} - -impl WebSocketEvent for ChannelCreate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#channel-update -/// Not directly serialized, as the inner payload is a channel object -pub struct ChannelUpdate { - pub channel: Channel, -} - -impl WebSocketEvent for ChannelUpdate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#channel-delete -/// Not directly serialized, as the inner payload is a channel object -pub struct ChannelDelete { - pub channel: Channel, -} - -impl WebSocketEvent for ChannelDelete {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#thread-create -/// Not directly serialized, as the inner payload is a channel object -pub struct ThreadCreate { - pub thread: Channel, -} - -impl WebSocketEvent for ThreadCreate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#thread-update -/// Not directly serialized, as the inner payload is a channel object -pub struct ThreadUpdate { - pub thread: Channel, -} - -impl WebSocketEvent for ThreadUpdate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#thread-delete -/// Not directly serialized, as the inner payload is a channel object -pub struct ThreadDelete { - pub thread: Channel, -} - -impl WebSocketEvent for ThreadDelete {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#thread-list-sync -pub struct ThreadListSync { - pub guild_id: String, - pub channel_ids: Option>, - pub threads: Vec, - pub members: Vec, -} - -impl WebSocketEvent for ThreadListSync {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#thread-member-update -/// The inner payload is a thread member object with an extra field. -/// The extra field is a bit painful, because we can't just serialize a thread member object -pub struct ThreadMemberUpdate { - pub id: Option, - pub user_id: Option, - pub join_timestamp: Option, - pub flags: Option, - pub member: Option, - pub guild_id: String, -} - -impl ThreadMemberUpdate { - /// Convert self to a thread member, losing the added guild_id field - pub fn to_thread_member(&self) -> ThreadMember { - ThreadMember { - id: self.id, - user_id: self.user_id, - join_timestamp: self.join_timestamp.clone(), - flags: self.flags, - member: self.member.clone(), - } - } -} - -impl WebSocketEvent for ThreadMemberUpdate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -/// See https://discord.com/developers/docs/topics/gateway-events#thread-members-update -pub struct ThreadMembersUpdate { - pub id: String, - pub guild_id: String, - /// Capped at 50 - pub member_count: u8, - pub added_members: Option>, - pub removed_members: Option>, -} - -impl WebSocketEvent for ThreadMembersUpdate {} - -#[derive(Debug, Deserialize, Serialize, Default)] -/// See https://discord.com/developers/docs/topics/gateway-events#guild-create -/// This one is particularly painful, it can be a Guild object with extra field or an unavailbile guild object -pub struct GuildCreate { - pub d: GuildCreateDataOption, -} - -#[derive(Debug, Deserialize, Serialize)] -pub enum GuildCreateDataOption { - UnavailableGuild(UnavailableGuild), - Guild(Guild), -} - -impl Default for GuildCreateDataOption { - fn default() -> Self { - GuildCreateDataOption::UnavailableGuild(UnavailableGuild::default()) - } -} -impl WebSocketEvent for GuildCreate {} - -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct GatewayPayload { - pub op: u8, - pub d: Option, - pub s: Option, - pub t: Option, -} - -impl WebSocketEvent for GatewayPayload {} - -#[derive(Debug, Serialize, Deserialize)] -pub struct DiscordFileAttachment { - pub id: i16, - pub filename: String, - description: Option, - content_type: Option, - size: i64, - url: String, - proxy_url: String, - height: Option, - width: Option, - ephemeral: Option, - duration_secs: Option, - waveform: Option, - #[serde(skip_serializing)] - content: Vec, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] - -pub struct PartialDiscordFileAttachment { - pub id: Option, - pub filename: String, - pub description: Option, - pub content_type: Option, - pub size: Option, - pub url: Option, - pub proxy_url: Option, - pub height: Option, - pub width: Option, - pub ephemeral: Option, - pub duration_secs: Option, - pub waveform: Option, - #[serde(skip_serializing)] - pub content: Vec, -} - -impl PartialDiscordFileAttachment { - /** - Moves `self.content` out of `self` and returns it. - # Returns - Vec - */ - pub fn move_content(self) -> (Vec, PartialDiscordFileAttachment) { - let content = self.content; - let updated_struct = PartialDiscordFileAttachment { - id: self.id, - filename: self.filename, - description: self.description, - content_type: self.content_type, - size: self.size, - url: self.url, - proxy_url: self.proxy_url, - height: self.height, - width: self.width, - ephemeral: self.ephemeral, - duration_secs: self.duration_secs, - waveform: self.waveform, - content: Vec::new(), - }; - (content, updated_struct) - } - - pub fn move_filename(self) -> (String, PartialDiscordFileAttachment) { - let filename = self.filename; - let updated_struct = PartialDiscordFileAttachment { - id: self.id, - filename: String::new(), - description: self.description, - content_type: self.content_type, - size: self.size, - url: self.url, - proxy_url: self.proxy_url, - height: self.height, - width: self.width, - - ephemeral: self.ephemeral, - duration_secs: self.duration_secs, - waveform: self.waveform, - content: self.content, - }; - (filename, updated_struct) - } - - pub fn move_content_type(self) -> (Option, PartialDiscordFileAttachment) { - let content_type = self.content_type; - let updated_struct = PartialDiscordFileAttachment { - id: self.id, - filename: self.filename, - description: self.description, - content_type: None, - size: self.size, - url: self.url, - proxy_url: self.proxy_url, - height: self.height, - width: self.width, - ephemeral: self.ephemeral, - duration_secs: self.duration_secs, - waveform: self.waveform, - content: self.content, - }; - (content_type, updated_struct) - } - - pub fn set_id(&mut self, id: i16) { - self.id = Some(id); - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct AllowedMention { - parse: Vec, - roles: Vec, - users: Vec, - replied_user: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum AllowedMentionType { - Roles, - Users, - Everyone, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Token { - pub token: String, -} - -/// See https://docs.spacebar.chat/routes/#cmp--schemas-template -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct GuildTemplate { - pub code: String, - pub name: String, - pub description: Option, - pub usage_count: Option, - pub creator_id: String, - pub creator: UserObject, - pub created_at: DateTime, - pub updated_at: DateTime, - pub source_guild_id: String, - pub source_guild: Vec, // Unsure how a {recursive: Guild} looks like, might be a Vec? - pub serialized_source_guild: Vec, - id: String, -} - -/// See https://docs.spacebar.chat/routes/#cmp--schemas-invite -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct GuildInvite { - pub code: String, - pub temporary: Option, - pub uses: Option, - pub max_uses: Option, - pub max_age: Option, - pub created_at: DateTime, - pub expires_at: Option>, - pub guild_id: String, - pub guild: Option, - pub channel_id: String, - pub channel: Option, - pub inviter_id: Option, - pub inviter: Option, - pub target_user_id: Option, - pub target_user: Option, - pub target_user_type: Option, - pub vanity_url: Option, -} - -/// See https://docs.spacebar.chat/routes/#cmp--schemas-voicestate -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct VoiceState { - pub guild_id: String, - pub guild: Option, - pub channel_id: String, - pub channel: Option, - pub user_id: String, - pub user: Option, - pub member: Option, - pub session_id: String, - pub token: String, - pub deaf: bool, - pub mute: bool, - pub self_deaf: bool, - pub self_mute: bool, - pub self_stream: Option, - pub self_video: bool, - pub suppress: bool, - pub request_to_speak_timestamp: Option>, - pub id: String, -} - -/// See https://docs.spacebar.chat/routes/#cmp--schemas-webhook -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct Webhook { - #[serde(rename = "type")] - pub webhook_type: i32, - pub name: String, - pub avatar: String, - pub token: String, - pub guild_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub guild: Option, - pub channel_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub channel: Option, - pub application_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub application: Option, - pub user_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub user: Option, - pub source_guild_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub source_guild: Option, - pub id: String, -} - -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct GuildCreateResponse { - pub id: String, -} diff --git a/src/gateway.rs b/src/gateway.rs index 15b299e..a122d9b 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,41 +1,6 @@ use crate::errors::ObserverError; use crate::gateway::events::Events; -use crate::types::Channel; -use crate::types::ChannelCreate; -use crate::types::ChannelDelete; -use crate::types::ChannelPinsUpdate; -use crate::types::ChannelUpdate; -use crate::types::GatewayHeartbeat; -use crate::types::GatewayIdentifyPayload; -use crate::types::GatewayPayload; -use crate::types::GatewayReady; -use crate::types::GatewayRequestGuildMembers; -use crate::types::GatewayResume; -use crate::types::GatewayVoiceStateUpdate; -use crate::types::GuildBanAdd; -use crate::types::GuildBanRemove; -use crate::types::GuildCreate; -use crate::types::HelloData; -use crate::types::MessageCreate; -use crate::types::MessageDelete; -use crate::types::MessageDeleteBulk; -use crate::types::MessageReactionAdd; -use crate::types::MessageReactionRemove; -use crate::types::MessageReactionRemoveAll; -use crate::types::MessageReactionRemoveEmoji; -use crate::types::MessageUpdate; -use crate::types::PresenceUpdate; -use crate::types::ThreadCreate; -use crate::types::ThreadDelete; -use crate::types::ThreadListSync; -use crate::types::ThreadMemberUpdate; -use crate::types::ThreadMembersUpdate; -use crate::types::ThreadUpdate; -use crate::types::TypingStartEvent; -use crate::types::UnavailableGuild; -use crate::types::User; -use crate::types::UserUpdate; -use crate::types::WebSocketEvent; +use crate::types; use futures_util::stream::SplitSink; use futures_util::SinkExt; use futures_util::StreamExt; @@ -75,7 +40,7 @@ pub struct GatewayHandle { impl GatewayHandle { /// Sends json to the gateway with an opcode async fn send_json_event(&self, op: u8, to_send: serde_json::Value) { - let gateway_payload = GatewayPayload { + let gateway_payload = types::GatewayPayload { op, d: Some(to_send), s: None, @@ -90,7 +55,7 @@ impl GatewayHandle { } /// Sends an identify event to the gateway - pub async fn send_identify(&self, to_send: GatewayIdentifyPayload) { + pub async fn send_identify(&self, to_send: types::GatewayIdentifyPayload) { let to_send_value = serde_json::to_value(&to_send).unwrap(); println!("GW: Sending Identify.."); @@ -99,7 +64,7 @@ impl GatewayHandle { } /// Sends a resume event to the gateway - pub async fn send_resume(&self, to_send: GatewayResume) { + pub async fn send_resume(&self, to_send: types::GatewayResume) { let to_send_value = serde_json::to_value(&to_send).unwrap(); println!("GW: Sending Resume.."); @@ -108,7 +73,7 @@ impl GatewayHandle { } /// Sends an update presence event to the gateway - pub async fn send_update_presence(&self, to_send: PresenceUpdate) { + pub async fn send_update_presence(&self, to_send: types::PresenceUpdate) { let to_send_value = serde_json::to_value(&to_send).unwrap(); println!("GW: Sending Presence Update.."); @@ -117,7 +82,7 @@ impl GatewayHandle { } /// Sends a Request Guild Members to the server - pub async fn send_request_guild_members(&self, to_send: GatewayRequestGuildMembers) { + pub async fn send_request_guild_members(&self, to_send: types::GatewayRequestGuildMembers) { let to_send_value = serde_json::to_value(&to_send).unwrap(); println!("GW: Sending Request Guild Members.."); @@ -126,7 +91,7 @@ impl GatewayHandle { } /// Sends a Request Guild Members to the server - pub async fn send_update_voice_state(&self, to_send: GatewayVoiceStateUpdate) { + pub async fn send_update_voice_state(&self, to_send: types::GatewayVoiceStateUpdate) { let to_send_value = serde_json::to_value(&to_send).unwrap(); println!("GW: Sending Voice State Update.."); @@ -180,7 +145,8 @@ impl Gateway { // Wait for the first hello and then spawn both tasks so we avoid nested tasks // This automatically spawns the heartbeat task, but from the main thread let msg = ws_rx.next().await.unwrap().unwrap(); - let gateway_payload: GatewayPayload = serde_json::from_str(msg.to_text().unwrap()).unwrap(); + let gateway_payload: types::GatewayPayload = + serde_json::from_str(msg.to_text().unwrap()).unwrap(); if gateway_payload.op != 10 { println!("Recieved non hello on gateway init, what is happening?"); @@ -193,7 +159,8 @@ impl Gateway { println!("GW: Received Hello"); - let gateway_hello: HelloData = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); + let gateway_hello: types::HelloData = + serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); gateway.heartbeat_handler = Some(HeartbeatHandler::new( gateway_hello.heartbeat_interval, shared_tx.clone(), @@ -225,7 +192,7 @@ impl Gateway { let msg_string = msg.to_string(); - let gateway_payload: GatewayPayload = serde_json::from_str(&msg_string).unwrap(); + let gateway_payload: types::GatewayPayload = serde_json::from_str(&msg_string).unwrap(); // See https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes match gateway_payload.op { @@ -239,7 +206,7 @@ impl Gateway { // See https://discord.com/developers/docs/topics/gateway-events#receive-events match gateway_payload_t.as_str() { "READY" => { - let _data: GatewayReady = + let _data: types::GatewayReady = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); } "RESUMED" => {} @@ -249,9 +216,9 @@ impl Gateway { "AUTO_MODERATION_RULE_DELETE" => {} "AUTO_MODERATION_ACTION_EXECUTION" => {} "CHANNEL_CREATE" => { - let channel: Channel = + let channel: types::Channel = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = ChannelCreate { channel }; + let new_data = types::ChannelCreate { channel }; self.events .lock() .await @@ -261,9 +228,9 @@ impl Gateway { .await; } "CHANNEL_UPDATE" => { - let channel: Channel = + let channel: types::Channel = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = ChannelUpdate { channel }; + let new_data = types::ChannelUpdate { channel }; self.events .lock() .await @@ -273,9 +240,9 @@ impl Gateway { .await; } "CHANNEL_DELETE" => { - let channel: Channel = + let channel: types::Channel = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = ChannelDelete { channel }; + let new_data = types::ChannelDelete { channel }; self.events .lock() .await @@ -285,7 +252,7 @@ impl Gateway { .await; } "CHANNEL_PINS_UPDATE" => { - let new_data: ChannelPinsUpdate = + let new_data: types::ChannelPinsUpdate = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -296,9 +263,9 @@ impl Gateway { .await; } "THREAD_CREATE" => { - let thread: Channel = + let thread: types::Channel = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = ThreadCreate { thread }; + let new_data = types::ThreadCreate { thread }; self.events .lock() .await @@ -308,9 +275,9 @@ impl Gateway { .await; } "THREAD_UPDATE" => { - let thread: Channel = + let thread: types::Channel = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = ThreadUpdate { thread }; + let new_data = types::ThreadUpdate { thread }; self.events .lock() .await @@ -320,9 +287,9 @@ impl Gateway { .await; } "THREAD_DELETE" => { - let thread: Channel = + let thread: types::Channel = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = ThreadDelete { thread }; + let new_data = types::ThreadDelete { thread }; self.events .lock() .await @@ -332,7 +299,7 @@ impl Gateway { .await; } "THREAD_LIST_SYNC" => { - let new_data: ThreadListSync = + let new_data: types::ThreadListSync = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -343,7 +310,7 @@ impl Gateway { .await; } "THREAD_MEMBER_UPDATE" => { - let new_data: ThreadMemberUpdate = + let new_data: types::ThreadMemberUpdate = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -354,7 +321,7 @@ impl Gateway { .await; } "THREAD_MEMBERS_UPDATE" => { - let new_data: ThreadMembersUpdate = + let new_data: types::ThreadMembersUpdate = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -365,7 +332,8 @@ impl Gateway { .await; } "GUILD_CREATE" => { - let new_data: GuildCreate = serde_json::from_str(&msg_string).unwrap(); + let new_data: types::GuildCreate = + serde_json::from_str(&msg_string).unwrap(); self.events .lock() .await @@ -376,16 +344,16 @@ impl Gateway { } "GUILD_UPDATE" => {} "GUILD_DELETE" => { - let _new_data: UnavailableGuild = + let _new_data: types::UnavailableGuild = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); } "GUILD_AUDIT_LOG_ENTRY_CREATE" => {} "GUILD_BAN_ADD" => { - let _new_data: GuildBanAdd = + let _new_data: types::GuildBanAdd = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); } "GUILD_BAN_REMOVE" => { - let _new_data: GuildBanRemove = + let _new_data: types::GuildBanRemove = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); } "GUILD_EMOJIS_UPDATE" => {} @@ -410,7 +378,7 @@ impl Gateway { "INVITE_CREATE" => {} "INVITE_DELETE" => {} "MESSAGE_CREATE" => { - let new_data: MessageCreate = + let new_data: types::MessageCreate = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -421,7 +389,7 @@ impl Gateway { .await; } "MESSAGE_UPDATE" => { - let new_data: MessageUpdate = + let new_data: types::MessageUpdate = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -432,7 +400,7 @@ impl Gateway { .await; } "MESSAGE_DELETE" => { - let new_data: MessageDelete = + let new_data: types::MessageDelete = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -443,7 +411,7 @@ impl Gateway { .await; } "MESSAGE_DELETE_BULK" => { - let new_data: MessageDeleteBulk = + let new_data: types::MessageDeleteBulk = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -454,7 +422,7 @@ impl Gateway { .await; } "MESSAGE_REACTION_ADD" => { - let new_data: MessageReactionAdd = + let new_data: types::MessageReactionAdd = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -465,7 +433,7 @@ impl Gateway { .await; } "MESSAGE_REACTION_REMOVE" => { - let new_data: MessageReactionRemove = + let new_data: types::MessageReactionRemove = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -476,7 +444,7 @@ impl Gateway { .await; } "MESSAGE_REACTION_REMOVE_ALL" => { - let new_data: MessageReactionRemoveAll = + let new_data: types::MessageReactionRemoveAll = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -487,7 +455,7 @@ impl Gateway { .await; } "MESSAGE_REACTION_REMOVE_EMOJI" => { - let new_data: MessageReactionRemoveEmoji = + let new_data: types::MessageReactionRemoveEmoji = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -498,7 +466,7 @@ impl Gateway { .await; } "PRESENCE_UPDATE" => { - let new_data: PresenceUpdate = + let new_data: types::PresenceUpdate = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -514,7 +482,7 @@ impl Gateway { // Not documented in discord docs, I assume this isnt for bots / apps but is for users? "SESSIONS_REPLACE" => {} "TYPING_START" => { - let new_data: TypingStartEvent = + let new_data: types::TypingStartEvent = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); self.events .lock() @@ -525,9 +493,9 @@ impl Gateway { .await; } "USER_UPDATE" => { - let user: User = + let user: types::User = serde_json::from_value(gateway_payload.d.unwrap()).unwrap(); - let new_data = UserUpdate { user }; + let new_data = types::UserUpdate { user }; self.events .lock() .await @@ -634,7 +602,7 @@ impl HeartbeatHandler { if last_heartbeat.elapsed().as_millis() > heartbeat_interval { println!("GW: Sending Heartbeat.."); - let heartbeat = GatewayHeartbeat { + let heartbeat = types::GatewayHeartbeat { op: 1, d: last_seq_number, }; @@ -674,7 +642,7 @@ Trait which defines the behaviour of an Observer. An Observer is an object which an Observable. The Observer is notified when the Observable's data changes. In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent. */ -pub trait Observer: std::fmt::Debug { +pub trait Observer: std::fmt::Debug { fn update(&self, data: &T); } @@ -683,13 +651,13 @@ change in the WebSocketEvent. GatewayEvents are observable. */ #[derive(Default, Debug)] -pub struct GatewayEvent { +pub struct GatewayEvent { observers: Vec + Sync + Send>>>, pub event_data: T, pub is_observed: bool, } -impl GatewayEvent { +impl GatewayEvent { fn new(event_data: T) -> Self { Self { is_observed: false, @@ -764,50 +732,50 @@ mod events { pub channel: Channel, pub thread: Thread, pub guild: Guild, - pub gateway_identify_payload: GatewayEvent, - pub gateway_resume: GatewayEvent, + pub gateway_identify_payload: GatewayEvent, + pub gateway_resume: GatewayEvent, } #[derive(Default, Debug)] pub struct Message { - pub create: GatewayEvent, - pub update: GatewayEvent, - pub delete: GatewayEvent, - pub delete_bulk: GatewayEvent, - pub reaction_add: GatewayEvent, - pub reaction_remove: GatewayEvent, - pub reaction_remove_all: GatewayEvent, - pub reaction_remove_emoji: GatewayEvent, + pub create: GatewayEvent, + pub update: GatewayEvent, + pub delete: GatewayEvent, + pub delete_bulk: GatewayEvent, + pub reaction_add: GatewayEvent, + pub reaction_remove: GatewayEvent, + pub reaction_remove_all: GatewayEvent, + pub reaction_remove_emoji: GatewayEvent, } #[derive(Default, Debug)] pub struct User { - pub update: GatewayEvent, - pub presence_update: GatewayEvent, - pub typing_start_event: GatewayEvent, + pub update: GatewayEvent, + pub presence_update: GatewayEvent, + pub typing_start_event: GatewayEvent, } #[derive(Default, Debug)] pub struct Channel { - pub create: GatewayEvent, - pub update: GatewayEvent, - pub delete: GatewayEvent, - pub pins_update: GatewayEvent, + pub create: GatewayEvent, + pub update: GatewayEvent, + pub delete: GatewayEvent, + pub pins_update: GatewayEvent, } #[derive(Default, Debug)] pub struct Thread { - pub create: GatewayEvent, - pub update: GatewayEvent, - pub delete: GatewayEvent, - pub list_sync: GatewayEvent, - pub member_update: GatewayEvent, - pub members_update: GatewayEvent, + pub create: GatewayEvent, + pub update: GatewayEvent, + pub delete: GatewayEvent, + pub list_sync: GatewayEvent, + pub member_update: GatewayEvent, + pub members_update: GatewayEvent, } #[derive(Default, Debug)] pub struct Guild { - pub create: GatewayEvent, + pub create: GatewayEvent, /*pub update: GatewayEvent, pub delete: GatewayEvent, pub audit_log_entry_create: GatewayEvent, @@ -837,21 +805,21 @@ mod example { #[derive(Debug)] struct Consumer; - impl Observer for Consumer { - fn update(&self, data: &GatewayResume) { + impl Observer for Consumer { + fn update(&self, data: &types::GatewayResume) { println!("{}", data.token) } } #[tokio::test] async fn test_observer_behaviour() { - let mut event = GatewayEvent::new(GatewayResume { + let mut event = GatewayEvent::new(types::GatewayResume { token: "start".to_string(), session_id: "start".to_string(), seq: "start".to_string(), }); - let new_data = GatewayResume { + let new_data = types::GatewayResume { token: "token_3276ha37am3".to_string(), session_id: "89346671230".to_string(), seq: "3".to_string(),