merge main

This commit is contained in:
Zertex 2023-05-27 19:54:43 -04:00
commit c300f1918c
64 changed files with 2194 additions and 542 deletions

5
.rusty-hook.toml Normal file
View File

@ -0,0 +1,5 @@
[hooks]
pre-push = "cargo fmt --all -- --check --color always"
[logging]
verbose = true

View File

@ -11,8 +11,10 @@ client = []
[dependencies]
tokio = {version = "1.28.1", features = ["rt", "macros", "rt-multi-thread", "full"]}
serde = {version = "1.0.162", features = ["derive"]}
serde_json = { version = "1.0.96", features = ["raw_value"] }
serde = {version = "1.0.163", features = ["derive"]}
serde_json = {version= "1.0.96", features = ["raw_value"]}
serde-aux = "4.2.0"
serde_with = "3.0.0"
serde_repr = "0.1.12"
reqwest = {version = "0.11.16", features = ["multipart"]}
url = "2.3.1"
@ -20,7 +22,7 @@ chrono = {version = "0.4.24", features = ["serde"]}
regex = "1.7.3"
custom_error = "1.9.2"
native-tls = "0.2.11"
tokio-tungstenite = {version = "0.18.0", features = ["native-tls"]}
tokio-tungstenite = {version = "0.19.0", features = ["native-tls"]}
futures-util = "0.3.28"
http = "0.2.9"
openssl = "0.10.52"
@ -38,3 +40,4 @@ jsonwebtoken = "8.3.0"
[dev-dependencies]
lazy_static = "1.4.0"
rusty-hook = "0.11.2"

View File

@ -70,54 +70,3 @@ pub mod login {
}
}
}
/*#[cfg(test)]
mod test {
use crate::api::schemas::schemas::{
AuthEmail, AuthPassword, AuthUsername, LoginSchema, RegisterSchema,
};
use crate::instance::Instance;
use crate::limit::LimitedRequester;
use crate::URLBundle;
#[tokio::test]
async fn test_login() {
let urls = URLBundle::new(
"http://localhost:3001/api".to_string(),
"http://localhost:3001".to_string(),
"http://localhost:3001".to_string(),
);
let limited_requester = LimitedRequester::new(urls.get_api().to_string()).await;
let mut test_instance = Instance::new(urls.clone(), limited_requester)
.await
.unwrap();
let reg = RegisterSchema::new(
AuthUsername::new("TestAccount".to_string()).unwrap(),
Some(AuthPassword::new("transrights".to_string()).unwrap()),
true,
Some(AuthEmail::new("apiauthlogin1@testlogin.xyz".to_string()).unwrap()),
None,
None,
Some("2000-01-01".to_string()),
None,
None,
None,
)
.unwrap();
test_instance.register_account(&reg).await.unwrap().token;
let login_schema = LoginSchema::new(
AuthUsername::new("apiauthlogin1@testlogin.xyz".to_string()).unwrap(),
"transrights".to_string(),
Some(false),
None,
None,
None,
);
let login_result = test_instance
.login_account(&login_schema.unwrap())
.await
.unwrap();
}
}*/

View File

@ -78,36 +78,3 @@ pub mod register {
}
}
}
#[cfg(test)]
mod test {
use crate::instance::Instance;
use crate::limit::LimitedRequester;
use crate::types::RegisterSchema;
use crate::URLBundle;
#[tokio::test]
async fn test_registration() {
let urls = URLBundle::new(
"http://localhost:3001/api".to_string(),
"http://localhost:3001".to_string(),
"http://localhost:3001".to_string(),
);
let _limited_requester = LimitedRequester::new().await;
let mut test_instance = Instance::new(urls.clone()).await.unwrap();
let reg = RegisterSchema::new(
"Hiiii".to_string(),
None,
true,
None,
None,
None,
Some("2000-01-01".to_string()),
None,
None,
None,
)
.unwrap();
let _ = test_instance.register_account(&reg).await.unwrap().token;
}
}

View File

@ -114,137 +114,3 @@ pub mod messages {
}
}
}
#[cfg(test)]
mod test {
use crate::instance::UserMeta;
use crate::types::{LoginSchema, MessageSendSchema, PartialDiscordFileAttachment};
use crate::{instance::Instance};
use std::io::Read;
use std::{cell::RefCell, fs::File};
use std::{io::BufReader, rc::Rc};
#[tokio::test]
async fn send_message() {
let channel_id = "1106954414356168802".to_string();
let mut message = MessageSendSchema::new(
None,
Some("A Message!".to_string()),
None,
None,
None,
None,
None,
None,
None,
None,
);
let mut instance = Instance::new(crate::URLBundle {
api: "http://localhost:3001/api".to_string(),
wss: "ws://localhost:3001/".to_string(),
cdn: "http://localhost:3001".to_string(),
})
.await
.unwrap();
let login_schema: LoginSchema = LoginSchema::new(
"user@test.xyz".to_string(),
"transrights".to_string(),
None,
None,
None,
None,
)
.unwrap();
let login_result = instance.login_account(&login_schema).await.unwrap();
let token = login_result.token;
println!("TOKEN: {}", token);
let settings = login_result.settings;
let limits = instance.limits.clone();
let mut user = UserMeta::new(
Rc::new(RefCell::new(instance)),
token,
limits,
settings,
None,
);
let _ = user
.send_message(&mut message, channel_id, None)
.await
.unwrap();
}
#[tokio::test]
async fn send_message_attachment() {
let channel_id = "1106954414356168802".to_string();
let f = File::open("./README.md").unwrap();
let mut reader = BufReader::new(f);
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer).unwrap();
let attachment = PartialDiscordFileAttachment {
id: None,
filename: "README.md".to_string(),
description: None,
content_type: None,
size: None,
url: None,
proxy_url: None,
width: None,
height: None,
ephemeral: None,
duration_secs: None,
waveform: None,
content: buffer,
};
let mut message = MessageSendSchema::new(
None,
Some("trans rights now".to_string()),
None,
None,
None,
None,
None,
None,
None,
Some(vec![attachment.clone()]),
);
let mut instance = Instance::new(crate::URLBundle {
api: "http://localhost:3001/api".to_string(),
wss: "ws://localhost:3001/".to_string(),
cdn: "http://localhost:3001".to_string(),
})
.await
.unwrap();
let login_schema: LoginSchema = LoginSchema::new(
"user@test.xyz".to_string(),
"transrights".to_string(),
None,
None,
None,
None,
)
.unwrap();
let login_result = instance.login_account(&login_schema).await.unwrap();
let token = login_result.token;
let settings = login_result.settings;
let limits = instance.limits.clone();
let mut user = UserMeta::new(
Rc::new(RefCell::new(instance)),
token,
limits,
settings,
None,
);
let vec_attach = vec![attachment.clone()];
let _arg = Some(&vec_attach);
let response = user
.send_message(&mut message, channel_id, Some(vec![attachment.clone()]))
.await
.unwrap();
println!("[Response:] {}", response.text().await.unwrap());
}
}

View File

@ -1,3 +1 @@
pub mod guilds;
use guilds::*;

View File

@ -37,21 +37,3 @@ impl Instance {
Ok(instance_policies_schema)
}
}
#[cfg(test)]
mod instance_policies_schema_test {
use crate::{instance::Instance, limit::LimitedRequester, URLBundle};
#[tokio::test]
async fn generate_instance_policies_schema() {
let urls = URLBundle::new(
"http://localhost:3001/api".to_string(),
"http://localhost:3001".to_string(),
"http://localhost:3001".to_string(),
);
let _limited_requester = LimitedRequester::new().await;
let test_instance = Instance::new(urls.clone()).await.unwrap();
let _schema = test_instance.general_configuration_schema().await.unwrap();
}
}

View File

@ -178,10 +178,3 @@ impl Instance {
.await
}
}
#[cfg(test)]
mod test {
#[tokio::test]
async fn get_user() {}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,5 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LatLong {
pub latitude: f64,

View File

@ -3,6 +3,7 @@ use crate::types::{Team, User};
use bitflags::{bitflags, Flags};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
@ -50,6 +51,40 @@ pub struct Application {
pub team: Option<Team>,
}
impl Default for Application {
fn default() -> Self {
Self {
id: Default::default(),
name: "".to_string(),
icon: None,
description: None,
summary: None,
r#type: None,
hook: true,
bot_public: true,
bot_require_code_grant: false,
verify_key: "".to_string(),
owner: Default::default(),
flags: 0,
redirect_uris: None,
rpc_application_state: 0,
store_application_state: 1,
verification_state: 1,
interactions_endpoint_url: None,
integration_public: true,
integration_require_code_grant: false,
discoverability_state: 1,
discovery_eligibility_flags: 2240,
tags: None,
cover_image: None,
install_params: None,
terms_of_service_url: None,
privacy_policy_url: None,
team: None,
}
}
}
impl Application {
pub fn flags(&self) -> ApplicationFlags {
ApplicationFlags::from_bits(self.flags.to_owned()).unwrap()
@ -103,23 +138,17 @@ pub struct ApplicationCommandOptionChoice {
pub value: Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize_repr, Deserialize_repr)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[repr(i32)]
pub enum ApplicationCommandOptionType {
#[serde(rename = "SUB_COMMAND")]
SubCommand = 1,
#[serde(rename = "SUB_COMMAND_GROUP")]
SubCommandGroup = 2,
#[serde(rename = "STRING")]
String = 3,
#[serde(rename = "INTEGER")]
Integer = 4,
#[serde(rename = "BOOLEAN")]
Boolean = 5,
#[serde(rename = "USER")]
User = 6,
#[serde(rename = "CHANNEL")]
Channel = 7,
#[serde(rename = "ROLE")]
Role = 8,
}
@ -136,3 +165,33 @@ pub struct ApplicationCommandInteractionDataOption {
pub value: Value,
pub options: Vec<ApplicationCommandInteractionDataOption>,
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
/// See https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure
pub struct GuildApplicationCommandPermissions {
pub id: Snowflake,
pub application_id: Snowflake,
pub guild_id: Snowflake,
pub permissions: Vec<ApplicationCommandPermission>,
}
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
/// See https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure
pub struct ApplicationCommandPermission {
pub id: Snowflake,
#[serde(rename = "type")]
pub permission_type: ApplicationCommandPermissionType,
/// true to allow, false, to disallow
pub permission: bool,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[repr(u8)]
/// See https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type
pub enum ApplicationCommandPermissionType {
#[default]
Role = 1,
User = 2,
Channel = 3,
}

View File

@ -1,4 +1,3 @@
use crate::types::Message;
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;

View File

@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object
pub struct AuditLogEntry {
pub target_id: Option<String>,
pub changes: Option<Vec<AuditLogChange>>,
pub user_id: Option<String>,
pub id: Snowflake,
// to:do implement an enum for these types
pub action_type: u8,
// to:do add better options type
pub options: Option<serde_json::Value>,
pub reason: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/audit-log#audit-log-change-object
pub struct AuditLogChange {
pub new_value: Option<serde_json::Value>,
pub old_value: Option<serde_json::Value>,
pub key: String,
}

View File

@ -0,0 +1,135 @@
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object
pub struct AutoModerationRule {
pub id: Snowflake,
pub guild_id: Snowflake,
pub name: String,
pub creator_id: Snowflake,
pub event_type: AutoModerationRuleEventType,
pub trigger_type: AutoModerationRuleTriggerType,
pub trigger_metadata: AutoModerationRuleTriggerMetadata,
pub actions: Vec<AutoModerationAction>,
pub enabled: bool,
pub exempt_roles: Vec<Snowflake>,
pub exempt_channels: Vec<Snowflake>,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
#[repr(u8)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types
pub enum AutoModerationRuleEventType {
#[default]
MessageSend = 1,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
#[repr(u8)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types
pub enum AutoModerationRuleTriggerType {
#[default]
Keyword = 1,
Spam = 3,
KeywordPreset = 4,
MentionSpam = 5,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(untagged)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata
pub enum AutoModerationRuleTriggerMetadata {
ForKeyword(AutoModerationRuleTriggerMetadataForKeyword),
ForKeywordPreset(AutoModerationRuleTriggerMetadataForKeywordPreset),
ForMentionSpam(AutoModerationRuleTriggerMetadataForMentionSpam),
#[default]
None,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata
pub struct AutoModerationRuleTriggerMetadataForKeyword {
pub keyword_filter: Vec<String>,
pub regex_patterns: Vec<String>,
pub allow_list: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata
pub struct AutoModerationRuleTriggerMetadataForKeywordPreset {
pub presets: Vec<AutoModerationRuleKeywordPresetType>,
pub allow_list: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata
pub struct AutoModerationRuleTriggerMetadataForMentionSpam {
/// Max 50
pub mention_total_limit: u8,
pub mention_raid_protection_enabled: bool,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
#[repr(u8)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-preset-types
pub enum AutoModerationRuleKeywordPresetType {
#[default]
Profanity = 1,
SexualContent = 2,
Slurs = 3,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object
pub struct AutoModerationAction {
#[serde(rename = "type")]
pub action_type: AutoModerationActionType,
pub metadata: Option<AutoModerationActionMetadata>,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
#[repr(u8)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types
pub enum AutoModerationActionType {
#[default]
BlockMessage = 1,
SendAlertMessage = 2,
Timeout = 3,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(untagged)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata
pub enum AutoModerationActionMetadata {
ForBlockMessage(AutoModerationActionMetadataForBlockMessage),
ForSendAlertMessage(AutoModerationActionMetadataForSendAlertMessage),
ForTimeout(AutoModerationActionMetadataForTimeout),
#[default]
None,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata
pub struct AutoModerationActionMetadataForBlockMessage {
pub custom_message: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata
pub struct AutoModerationActionMetadataForSendAlertMessage {
pub channel_id: Snowflake,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata
pub struct AutoModerationActionMetadataForTimeout {
/// Max 2419200
pub duration_seconds: u32,
}

View File

@ -1,5 +1,9 @@
use chrono::Utc;
use serde::{Deserialize, Serialize};
use serde_aux::prelude::{
deserialize_number_from_string, deserialize_option_number_from_string,
deserialize_string_from_number,
};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::{
@ -66,9 +70,13 @@ pub struct Channel {
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct Tag {
#[serde(default)]
#[serde(deserialize_with = "deserialize_number_from_string")]
pub id: u64,
pub name: String,
pub moderated: bool,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub emoji_id: Option<u64>,
pub emoji_name: Option<String>,
}
@ -77,8 +85,13 @@ pub struct Tag {
pub struct PermissionOverwrite {
pub id: String,
#[serde(rename = "type")]
pub overwrite_type: u8,
#[serde(deserialize_with = "deserialize_string_from_number")]
pub overwrite_type: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub allow: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub deny: String,
}
@ -103,7 +116,9 @@ pub struct ThreadMember {
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct DefaultReaction {
pub emoji_id: Option<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub emoji_id: Option<u64>,
pub emoji_name: Option<String>,
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::types::entities::User;
use crate::types::{Guild, Snowflake};
use crate::types::Snowflake;
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]

View File

@ -1,8 +1,9 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::{
entities::{Channel, Emoji, GuildTemplate, RoleObject, Sticker, User, VoiceState, Webhook},
entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook},
interfaces::WelcomeScreenObject,
utils::Snowflake,
};
@ -12,7 +13,7 @@ use crate::types::{
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Guild {
pub id: Snowflake,
pub name: String,
pub name: Option<String>,
pub icon: Option<String>,
pub icon_hash: Option<String>,
pub splash: Option<String>,
@ -33,7 +34,7 @@ pub struct Guild {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub emojis: Vec<Emoji>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub features: String, // TODO: Make this a 'simple-array'
pub features: Option<Vec<String>>,
pub application_id: Option<String>,
pub system_channel_id: Option<Snowflake>,
pub system_channel_flags: Option<u8>,
@ -56,7 +57,7 @@ pub struct Guild {
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
#[cfg(not(feature = "sqlx"))]
pub welcome_screen: Option<WelcomeScreenObject>,
pub nsfw_level: u8,
pub nsfw_level: Option<u8>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>,
pub premium_progress_bar_enabled: Option<bool>,
@ -120,3 +121,59 @@ pub struct UnavailableGuild {
pub struct GuildCreateResponse {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object
pub struct GuildScheduledEvent {
pub id: String,
pub guild_id: String,
pub channel_id: Option<String>,
pub creator_id: Option<String>,
pub name: String,
pub description: String,
pub scheduled_start_time: DateTime<Utc>,
pub scheduled_end_time: Option<DateTime<Utc>>,
pub privacy_level: GuildScheduledEventPrivacyLevel,
pub status: GuildScheduledEventStatus,
pub entity_type: GuildScheduledEventEntityType,
pub entity_id: Option<String>,
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
pub creator: Option<User>,
pub user_count: Option<u64>,
pub image: Option<String>,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
#[repr(u8)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level
pub enum GuildScheduledEventPrivacyLevel {
#[default]
GuildOnly = 2,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
#[repr(u8)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status
pub enum GuildScheduledEventStatus {
#[default]
Scheduled = 1,
Active = 2,
Completed = 3,
Canceled = 4,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
#[repr(u8)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types
pub enum GuildScheduledEventEntityType {
#[default]
StageInstance = 1,
Voice = 2,
External = 3,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata
pub struct GuildScheduledEventEntityMetadata {
pub location: Option<String>,
}

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::User;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Default, Serialize, Clone, PartialEq, Eq)]
pub struct GuildMember {
pub user: Option<User>,
pub nick: Option<String>,
@ -12,7 +12,7 @@ pub struct GuildMember {
pub premium_since: Option<String>,
pub deaf: bool,
pub mute: bool,
pub flags: i32,
pub flags: Option<i32>,
pub pending: Option<bool>,
pub permissions: Option<String>,
pub communication_disabled_until: Option<String>,

View File

@ -21,7 +21,7 @@ pub struct Message {
pub tts: bool,
pub mention_everyone: bool,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub mentions: Vec<User>,
pub mentions: Option<Vec<User>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub mention_roles: Vec<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]

View File

@ -1,5 +1,7 @@
mod application;
mod attachment;
mod audit_log;
mod auto_moderation;
mod channel;
mod config;
mod emoji;
@ -7,8 +9,10 @@ mod guild;
mod guild_member;
mod integration;
mod message;
mod relationship;
mod role;
mod security_key;
mod stage_instance;
mod sticker;
mod team;
mod template;
@ -19,6 +23,8 @@ mod webhook;
pub use application::*;
pub use attachment::*;
pub use audit_log::*;
pub use auto_moderation::*;
pub use channel::*;
pub use config::*;
pub use emoji::*;
@ -26,8 +32,10 @@ pub use guild::*;
pub use guild_member::*;
pub use integration::*;
pub use message::*;
pub use relationship::*;
pub use role::*;
pub use security_key::*;
pub use stage_instance::*;
pub use sticker::*;
pub use team::*;
pub use template::*;

View File

@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::Snowflake;
use super::PublicUser;
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
/// See https://docs.spacebar.chat/routes/#get-/users/@me/relationships/
pub struct Relationship {
pub id: Snowflake,
#[serde(rename = "type")]
pub relationship_type: RelationshipType,
pub nickname: Option<String>,
pub user: PublicUser,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
#[repr(u8)]
/// See https://github.com/spacebarchat/server/blob/60394d8c43904ff17935d6edbbfb09ecd479570a/src/util/entities/Relationship.ts#L30
pub enum RelationshipType {
Outgoing = 4,
Incoming = 3,
Blocked = 2,
#[default]
Friends = 1,
}

View File

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_string_from_number};
use crate::types::utils::Snowflake;
@ -13,6 +14,8 @@ pub struct RoleObject {
pub icon: Option<String>,
pub unicode_emoji: Option<String>,
pub position: u16,
#[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub permissions: String,
pub managed: bool,
pub mentionable: bool,
@ -30,12 +33,20 @@ pub struct RoleSubscriptionData {
pub is_renewal: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
/// See https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure
pub struct RoleTags {
pub bot_id: Option<Snowflake>,
pub integration_id: Option<Snowflake>,
pub premium_subscriber: Option<bool>,
pub subscription_listing_id: Option<Snowflake>,
pub available_for_purchase: Option<bool>,
pub guild_connections: Option<bool>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub bot_id: Option<usize>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub integration_id: Option<usize>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub subscription_listing_id: Option<usize>,
// These use the bad bool format, "Tags with type null represent booleans. They will be present and set to null if they are "true", and will be not present if they are "false"."
// premium_subscriber: bool,
// available_for_purchase: bool,
// guild_connections: bool,
}

View File

@ -0,0 +1,29 @@
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/stage-instance
pub struct StageInstance {
pub id: Snowflake,
pub guild_id: Snowflake,
pub channel_id: Snowflake,
/// 1 - 120 chars
pub topic: String,
pub privacy_level: StageInstancePrivacyLevel,
/// deprecated, apparently
pub discoverable_disabled: Option<bool>,
pub guild_scheduled_event_id: Option<Snowflake>,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
#[repr(u8)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
/// See https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level
pub enum StageInstancePrivacyLevel {
/// deprecated, apparently
Public = 1,
#[default]
GuildOnly = 2,
}

View File

@ -5,6 +5,7 @@ use crate::types::{entities::User, utils::Snowflake};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Sticker {
#[serde(default)]
pub id: Snowflake,
pub pack_id: Option<Snowflake>,
pub name: String,

View File

@ -1,11 +1,8 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use serde_aux::prelude::deserialize_option_number_from_string;
use crate::types::{
errors::Error,
utils::Snowflake, //util::{email::adjust_email, entities::user_setting::UserSettings},
};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
@ -27,7 +24,7 @@ pub struct User {
pub username: String,
pub discriminator: String,
pub avatar: Option<String>,
pub bot: bool,
pub bot: Option<bool>,
pub system: Option<bool>,
pub mfa_enabled: Option<bool>,
pub accent_color: Option<u8>,
@ -35,7 +32,11 @@ pub struct User {
pub locale: Option<String>,
pub verified: Option<bool>,
pub email: Option<String>,
pub flags: String,
/// This field comes as either a string or a number as a string
/// So we need to account for that
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
flags: Option<i32>,
pub premium_since: Option<DateTime<Utc>>,
pub premium_type: u8,
pub pronouns: Option<String>,
@ -47,11 +48,11 @@ pub struct User {
pub nsfw_allowed: bool,
pub premium: bool,
pub purchased_flags: i32,
pub premium_usage_flags: i32,
pub premium_usage_flags: Option<i32>,
pub disabled: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct PublicUser {
pub id: Snowflake,
pub username: String,
@ -61,7 +62,7 @@ pub struct PublicUser {
pub banner: Option<String>,
pub theme_colors: Option<Vec<u8>>,
pub pronouns: Option<String>,
pub bot: bool,
pub bot: Option<bool>,
pub bio: String,
pub premium_type: u8,
pub premium_since: Option<DateTime<Utc>>,

View File

@ -1,8 +1,6 @@
use chrono::{serde::ts_milliseconds_option, Utc};
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename_all = "lowercase")]

View File

@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Channel, Guild, GuildMember, User},
entities::{Guild, GuildMember},
utils::Snowflake,
};
@ -10,11 +10,13 @@ use crate::types::{
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct VoiceState {
pub guild_id: Snowflake,
pub guild_id: Option<Snowflake>,
pub guild: Option<Guild>,
pub channel_id: Snowflake,
pub user_id: Snowflake,
pub member: Option<GuildMember>,
pub session_id: Snowflake,
pub token: Option<String>,
pub deaf: bool,
pub mute: bool,
pub self_deaf: bool,
@ -23,4 +25,5 @@ pub struct VoiceState {
pub self_video: bool,
pub suppress: bool,
pub request_to_speak_timestamp: Option<DateTime<Utc>>,
pub id: Option<Snowflake>,
}

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Application, Channel, Guild, User},
entities::{Guild, User},
utils::Snowflake,
};

View File

@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::types::{GuildApplicationCommandPermissions, WebSocketEvent};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#application-command-permissions-update
pub struct ApplicationCommandPermissionsUpdate {
#[serde(flatten)]
pub permissions: GuildApplicationCommandPermissions,
}
impl WebSocketEvent for ApplicationCommandPermissionsUpdate {}

View File

@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
use crate::types::{
AutoModerationAction, AutoModerationRule, AutoModerationRuleTriggerType, Snowflake,
WebSocketEvent,
};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-create
pub struct AutoModerationRuleCreate {
#[serde(flatten)]
pub rule: AutoModerationRule,
}
impl WebSocketEvent for AutoModerationRuleCreate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-update
pub struct AutoModerationRuleUpdate {
#[serde(flatten)]
pub rule: AutoModerationRule,
}
impl WebSocketEvent for AutoModerationRuleUpdate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-delete
pub struct AutoModerationRuleDelete {
#[serde(flatten)]
pub rule: AutoModerationRule,
}
impl WebSocketEvent for AutoModerationRuleDelete {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#auto-moderation-action-execution
pub struct AutoModerationActionExecution {
pub guild_id: Snowflake,
pub action: AutoModerationAction,
pub rule_id: Snowflake,
pub rule_trigger_type: AutoModerationRuleTriggerType,
pub user_id: Snowflake,
pub channel_id: Option<Snowflake>,
pub message_id: Option<Snowflake>,
pub alert_system_message_id: Option<Snowflake>,
pub content: Option<String>,
pub matched_keyword: Option<String>,
pub matched_content: Option<String>,
}
impl WebSocketEvent for AutoModerationActionExecution {}

51
src/types/events/call.rs Normal file
View File

@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
use crate::types::{VoiceState, WebSocketEvent};
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Is sent to a client by the server to signify a new being created
/// {"t":"CALL_CREATE","s":2,"op":0,"d":{"voice_states":[],"ringing":[],"region":"milan","message_id":"1107187514906775613","embedded_activities":[],"channel_id":"837609115475771392"}}
pub struct CallCreate {
pub voice_states: Vec<VoiceState>,
/// Seems like a vec of channel ids
pub ringing: Vec<String>,
pub region: String, // milan
pub message_id: String,
/// What is this?
pub embedded_activities: Vec<serde_json::Value>,
pub channel_id: String,
}
impl WebSocketEvent for CallCreate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Updates the status of calls
/// {"t":"CALL_UPDATE","s":5,"op":0,"d":{"ringing":["837606544539254834"],"region":"milan","message_id":"1107191540234846308","guild_id":null,"channel_id":"837609115475771392"}}
pub struct CallUpdate {
/// Seems like a vec of channel ids
pub ringing: Vec<String>,
pub region: String, // milan
pub message_id: String,
pub guild_id: Option<String>,
pub channel_id: String,
}
impl WebSocketEvent for CallUpdate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Deletes a ringing call
/// {"t":"CALL_DELETE","s":8,"op":0,"d":{"channel_id":"837609115475771392"}}
pub struct CallDelete {
pub channel_id: String,
}
impl WebSocketEvent for CallDelete {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// See https://unofficial-discord-docs.vercel.app/gateway/op13
/// {"op":13,"d":{"channel_id":"837609115475771392"}}
pub struct CallSync {
pub channel_id: String,
}
impl WebSocketEvent for CallSync {}

View File

@ -15,8 +15,8 @@ impl WebSocketEvent for ChannelPinsUpdate {}
#[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 {
#[serde(flatten)]
pub channel: Channel,
}
@ -24,17 +24,37 @@ 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 {
#[serde(flatten)]
pub channel: Channel,
}
impl WebSocketEvent for ChannelUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// Officially undocumented.
/// Sends updates to client about a new message with its id
/// {"channel_unread_updates": [{"id": "816412869766938648", "last_message_id": "1085892012085104680"}}
pub struct ChannelUnreadUpdate {
pub channel_unread_updates: Vec<ChannelUnreadUpdateObject>,
pub guild_id: String,
}
#[derive(Debug, Default, Deserialize, Serialize)]
/// Contains very few fields from [Channel]
/// See also [ChannelUnreadUpdates]
pub struct ChannelUnreadUpdateObject {
pub id: String,
pub last_message_id: String,
pub last_pin_timestamp: Option<String>,
}
impl WebSocketEvent for ChannelUnreadUpdate {}
#[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 {
#[serde(flatten)]
pub channel: Channel,
}

View File

@ -1,15 +1,21 @@
use crate::types::entities::{Guild, UnavailableGuild, User};
use crate::types::events::WebSocketEvent;
use crate::types::{AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, RoleObject, Sticker};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use super::PresenceUpdate;
#[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
/// This one is particularly painful, it can be a Guild object with an extra field or an unavailable guild object
pub struct GuildCreate {
#[serde(flatten)]
pub d: GuildCreateDataOption,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GuildCreateDataOption {
UnavailableGuild(UnavailableGuild),
Guild(Guild),
@ -39,3 +45,181 @@ pub struct GuildBanRemove {
}
impl WebSocketEvent for GuildBanRemove {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-update
pub struct GuildUpdate {
#[serde(flatten)]
pub guild: Guild,
}
impl WebSocketEvent for GuildUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-delete
pub struct GuildDelete {
#[serde(flatten)]
pub guild: UnavailableGuild,
}
impl WebSocketEvent for GuildDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create
pub struct GuildAuditLogEntryCreate {
#[serde(flatten)]
pub entry: AuditLogEntry,
}
impl WebSocketEvent for GuildAuditLogEntryCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update
pub struct GuildEmojisUpdate {
pub guild_id: String,
pub emojis: Vec<Emoji>,
}
impl WebSocketEvent for GuildEmojisUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update
pub struct GuildStickersUpdate {
pub guild_id: String,
pub stickers: Vec<Sticker>,
}
impl WebSocketEvent for GuildStickersUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update
pub struct GuildIntegrationsUpdate {
pub guild_id: String,
}
impl WebSocketEvent for GuildIntegrationsUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-add
pub struct GuildMemberAdd {
#[serde(flatten)]
pub member: GuildMember,
pub guild_id: String,
}
impl WebSocketEvent for GuildMemberAdd {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-remove
pub struct GuildMemberRemove {
pub guild_id: String,
pub user: User,
}
impl WebSocketEvent for GuildMemberRemove {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-update
pub struct GuildMemberUpdate {
pub guild_id: String,
pub roles: Vec<String>,
pub user: User,
pub nick: Option<String>,
pub avatar: Option<String>,
pub joined_at: Option<DateTime<Utc>>,
pub premium_since: Option<DateTime<Utc>>,
pub deaf: Option<bool>,
pub mute: Option<bool>,
pub pending: Option<bool>,
pub communication_disabled_until: Option<DateTime<Utc>>,
}
impl WebSocketEvent for GuildMemberUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk
pub struct GuildMembersChunk {
pub guild_id: String,
pub members: Vec<GuildMember>,
pub chunk_index: u16,
pub chunk_count: u16,
pub not_found: Option<Vec<String>>,
pub presences: Option<PresenceUpdate>,
pub nonce: Option<String>,
}
impl WebSocketEvent for GuildMembersChunk {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-create
pub struct GuildRoleCreate {
pub guild_id: String,
pub role: RoleObject,
}
impl WebSocketEvent for GuildRoleCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-update
pub struct GuildRoleUpdate {
pub guild_id: String,
pub role: RoleObject,
}
impl WebSocketEvent for GuildRoleUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-delete
pub struct GuildRoleDelete {
pub guild_id: String,
pub role_id: String,
}
impl WebSocketEvent for GuildRoleDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create
pub struct GuildScheduledEventCreate {
#[serde(flatten)]
pub event: GuildScheduledEvent,
}
impl WebSocketEvent for GuildScheduledEventCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update
pub struct GuildScheduledEventUpdate {
#[serde(flatten)]
pub event: GuildScheduledEvent,
}
impl WebSocketEvent for GuildScheduledEventUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete
pub struct GuildScheduledEventDelete {
#[serde(flatten)]
pub event: GuildScheduledEvent,
}
impl WebSocketEvent for GuildScheduledEventDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add
pub struct GuildScheduledEventUserAdd {
pub guild_scheduled_event_id: String,
pub user_id: String,
pub guild_id: String,
}
impl WebSocketEvent for GuildScheduledEventUserAdd {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove
pub struct GuildScheduledEventUserRemove {
pub guild_scheduled_event_id: String,
pub user_id: String,
pub guild_id: String,
}
impl WebSocketEvent for GuildScheduledEventUserRemove {}

View File

@ -1,22 +1,168 @@
use crate::types::events::{PresenceUpdate, WebSocketEvent};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
#[derive(Debug, Deserialize, Serialize, Default)]
#[derive(Debug, Deserialize, Serialize)]
pub struct GatewayIdentifyPayload {
pub token: String,
pub properties: GatewayIdentifyConnectionProps,
#[serde(skip_serializing_if = "Option::is_none")]
pub compress: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub large_threshold: Option<i16>, //default: 50
#[serde(skip_serializing_if = "Option::is_none")]
pub shard: Option<Vec<(i32, i32)>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presence: Option<PresenceUpdate>,
pub intents: i32,
// What is the difference between these two?
// Intents is documented, capabilities is used in users
// I wonder if these are interchangeable...
#[serde(skip_serializing_if = "Option::is_none")]
pub intents: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capabilities: Option<i32>,
}
impl Default for GatewayIdentifyPayload {
fn default() -> Self {
Self::common()
}
}
impl GatewayIdentifyPayload {
/// Uses the most common, 25% data along with client capabilities
///
/// Basically pretends to be an official client on Windows 10, with Chrome 113.0.0.0
pub fn common() -> Self {
Self {
token: "".to_string(),
properties: GatewayIdentifyConnectionProps::default(),
compress: Some(false),
large_threshold: None,
shard: None,
presence: None,
intents: None,
capabilities: Some(8189),
}
}
}
impl GatewayIdentifyPayload {
/// Creates an identify payload with the same default capabilities as the official client
pub fn default_w_client_capabilities() -> Self {
let mut def = Self::default();
def.capabilities = Some(8189); // Default capabilities for a client
def
}
/// Creates an identify payload with all possible capabilities
pub fn default_w_all_capabilities() -> Self {
let mut def = Self::default();
def.capabilities = Some(i32::MAX); // Since discord uses bitwise for capabilities, this has almost every bit as 1, so all capabilities
def
}
}
impl WebSocketEvent for GatewayIdentifyPayload {}
#[derive(Debug, Deserialize, Serialize, Default)]
#[derive(Debug, Deserialize, Serialize)]
#[serde_as]
pub struct GatewayIdentifyConnectionProps {
/// Almost always sent
///
/// ex: "Linux", "Windows", "Mac OS X"
///
/// ex (mobile): "Windows Mobile", "iOS", "Android", "BlackBerry"
pub os: String,
/// Almost always sent
///
/// ex: "Firefox", "Chrome", "Opera Mini", "Opera", "Blackberry", "Facebook Mobile", "Chrome iOS", "Mobile Safari", "Safari", "Android Chrome", "Android Mobile", "Edge", "Konqueror", "Internet Explorer", "Mozilla", "Discord Client"
pub browser: String,
pub device: String,
/// Sometimes not sent, acceptable to be ""
///
/// Speculation:
/// Only sent for mobile devices
///
/// ex: "BlackBerry", "Windows Phone", "Android", "iPhone", "iPad", ""
#[serde_as(as = "NoneAsEmptyString")]
pub device: Option<String>,
/// Almost always sent, most commonly en-US
///
/// ex: "en-US"
pub system_locale: String,
/// Almost always sent
///
/// ex: any user agent, most common is "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
pub browser_user_agent: String,
/// Almost always sent
///
/// ex: "113.0.0.0"
pub browser_version: String,
/// Sometimes not sent, acceptable to be ""
///
/// ex: "10" (For os = "Windows")
#[serde_as(as = "NoneAsEmptyString")]
pub os_version: Option<String>,
/// Sometimes not sent, acceptable to be ""
#[serde_as(as = "NoneAsEmptyString")]
pub referrer: Option<String>,
/// Sometimes not sent, acceptable to be ""
#[serde_as(as = "NoneAsEmptyString")]
pub referring_domain: Option<String>,
/// Sometimes not sent, acceptable to be ""
#[serde_as(as = "NoneAsEmptyString")]
pub referrer_current: Option<String>,
/// Almost always sent, most commonly "stable"
pub release_channel: String,
/// Almost always sent, identifiable if default is 0, should be around 199933
pub client_build_number: u64,
//pub client_event_source: Option<?>
}
impl Default for GatewayIdentifyConnectionProps {
/// Uses the most common, 25% data
fn default() -> Self {
Self::common()
}
}
impl GatewayIdentifyConnectionProps {
/// Returns a minimal, least data possible default
fn minimal() -> Self {
Self {
os: String::new(),
browser: String::new(),
device: None,
system_locale: String::from("en-US"),
browser_user_agent: String::new(),
browser_version: String::new(),
os_version: None,
referrer: None,
referring_domain: None,
referrer_current: None,
release_channel: String::from("stable"),
client_build_number: 199933,
}
}
/// Returns the most common connection props so we can't be tracked
pub fn common() -> Self {
let mut default = Self::minimal();
// See https://www.useragents.me/#most-common-desktop-useragents
// 25% of the web
//default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string();
default.browser = String::from("Chrome");
default.browser_version = String::from("113.0.0.0");
default.system_locale = String::from("en-US");
default.os = String::from("Windows");
default.os_version = Some(String::from("10"));
default.client_build_number = 199933;
default.release_channel = String::from("stable");
return default;
}
}

View File

@ -0,0 +1,33 @@
use serde::{Deserialize, Serialize};
use crate::types::{Integration, WebSocketEvent};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#integration-create
pub struct IntegrationCreate {
#[serde(flatten)]
pub integration: Integration,
pub guild_id: String,
}
impl WebSocketEvent for IntegrationCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#integration-update
pub struct IntegrationUpdate {
#[serde(flatten)]
pub integration: Integration,
pub guild_id: String,
}
impl WebSocketEvent for IntegrationUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#integration-delete
pub struct IntegrationDelete {
pub id: String,
pub guild_id: String,
pub application_id: Option<String>,
}
impl WebSocketEvent for IntegrationDelete {}

View File

@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::types::{Interaction, WebSocketEvent};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#interaction-create
pub struct InteractionCreate {
#[serde(flatten)]
pub interaction: Interaction,
}
impl WebSocketEvent for InteractionCreate {}

View File

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
use crate::types::{GuildInvite, WebSocketEvent};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#invite-create
pub struct InviteCreate {
#[serde(flatten)]
pub invite: GuildInvite,
}
impl WebSocketEvent for InviteCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#invite-delete
pub struct InviteDelete {
pub channel_id: String,
pub guild_id: Option<String>,
pub code: String,
}
impl WebSocketEvent for InviteDelete {}

View File

@ -0,0 +1,27 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
///
/// Sent to the server to signify lazy loading of a guild;
/// Sent by the official client when switching to a guild or channel;
/// After this, you should recieve message updates
///
/// See https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html#op-14-lazy-request
///
/// {"op":14,"d":{"guild_id":"848582562217590824","typing":true,"activities":true,"threads":true}}
pub struct LazyRequest {
pub guild_id: String,
pub typing: bool,
pub activities: bool,
pub threads: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub members: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channels: Option<HashMap<String, Vec<Vec<u64>>>>,
}
impl WebSocketEvent for LazyRequest {}

View File

@ -1,9 +1,7 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Emoji, GuildMember, Message, User},
utils::Snowflake,
};
use crate::types::entities::{Emoji, GuildMember, Message, User};
use super::WebSocketEvent;
@ -19,12 +17,43 @@ pub struct TypingStartEvent {
impl WebSocketEvent for TypingStartEvent {}
#[derive(Debug, Serialize, Deserialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#message-create
pub struct MessageCreate {
#[serde(flatten)]
message: Message,
guild_id: Option<Snowflake>,
guild_id: Option<String>,
member: Option<GuildMember>,
mentions: Vec<(User, GuildMember)>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create
mentions: Option<Vec<MessageCreateUser>>,
}
#[derive(Debug, Serialize, Deserialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields
pub struct MessageCreateUser {
pub id: String,
username: String,
discriminator: String,
avatar: Option<String>,
bot: Option<bool>,
system: Option<bool>,
mfa_enabled: Option<bool>,
accent_color: Option<String>,
locale: Option<String>,
verified: Option<bool>,
email: Option<String>,
premium_since: Option<String>,
premium_type: Option<i8>,
pronouns: Option<String>,
public_flags: Option<i32>,
banner: Option<String>,
bio: Option<String>,
theme_colors: Option<Vec<i32>>,
phone: Option<String>,
nsfw_allowed: Option<bool>,
premium: Option<bool>,
purchased_flags: Option<i32>,
premium_usage_flags: Option<i32>,
disabled: Option<bool>,
member: GuildMember,
}
impl WebSocketEvent for MessageCreate {}
@ -35,7 +64,7 @@ pub struct MessageUpdate {
message: Message,
guild_id: Option<String>,
member: Option<GuildMember>,
mentions: Vec<(User, GuildMember)>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create
mentions: Option<Vec<(User, GuildMember)>>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create
}
impl WebSocketEvent for MessageUpdate {}
@ -99,3 +128,23 @@ pub struct MessageReactionRemoveEmoji {
}
impl WebSocketEvent for MessageReactionRemoveEmoji {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
///
/// Not documented anywhere unofficially
///
/// Apparently "Message ACK refers to marking a message as read for Discord's API." (https://github.com/Rapptz/discord.py/issues/1851)
/// I suspect this is sent and recieved from the gateway to let clients on other devices know the user has read a message
///
/// {"t":"MESSAGE_ACK","s":3,"op":0,"d":{"version":52,"message_id":"1107236673638633472","last_viewed":null,"flags":null,"channel_id":"967363950217936897"}}
pub struct MessageACK {
/// ?
pub version: u16,
pub message_id: String,
pub last_viewed: Option<DateTime<Utc>>,
/// What flags?
pub flags: Option<serde_json::Value>,
pub channel_id: String,
}
impl WebSocketEvent for MessageACK {}

View File

@ -1,41 +1,86 @@
use serde::{Deserialize, Serialize};
mod application;
mod auto_moderation;
mod call;
mod channel;
mod guild;
mod heartbeat;
mod hello;
mod identify;
mod integration;
mod interaction;
mod invite;
mod lazy_request;
mod message;
mod passive_update;
mod presence;
mod ready;
mod relationship;
mod request_members;
mod resume;
mod session;
mod stage_instance;
mod thread;
mod user;
mod voice_status;
mod voice;
mod webhooks;
pub use application::*;
pub use auto_moderation::*;
pub use call::*;
pub use channel::*;
pub use guild::*;
pub use heartbeat::*;
pub use hello::*;
pub use identify::*;
pub use integration::*;
pub use interaction::*;
pub use invite::*;
pub use lazy_request::*;
pub use message::*;
pub use passive_update::*;
pub use presence::*;
pub use ready::*;
pub use relationship::*;
pub use request_members::*;
pub use resume::*;
pub use session::*;
pub use stage_instance::*;
pub use thread::*;
pub use user::*;
pub use voice_status::*;
pub use voice::*;
pub use webhooks::*;
pub trait WebSocketEvent {}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct GatewayPayload {
#[derive(Debug, Default, Serialize, Clone)]
/// The payload used for sending events to the gateway
///
/// Similar to [GatewayReceivePayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
/// Also, we never need to send the event name
pub struct GatewaySendPayload {
pub op: u8,
#[serde(skip_serializing_if = "Option::is_none")]
pub d: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s: Option<u64>,
}
impl WebSocketEvent for GatewaySendPayload {}
#[derive(Debug, Default, Deserialize, Clone)]
/// The payload used for receiving events from the gateway
///
/// Similar to [GatewaySendPayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
/// Also, we never need to sent the event name
pub struct GatewayReceivePayload<'a> {
pub op: u8,
#[serde(borrow)]
pub d: Option<&'a serde_json::value::RawValue>,
pub s: Option<u64>,
pub t: Option<String>,
}
impl WebSocketEvent for GatewayPayload {}
impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {}

View File

@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
use super::{ChannelUnreadUpdateObject, WebSocketEvent};
use crate::types::{GuildMember, VoiceState};
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
///
/// Seems to be passively set to update the client on guild details (though, why not just send the update events?)
pub struct PassiveUpdateV1 {
pub voice_states: Vec<VoiceState>,
pub members: Vec<GuildMember>,
pub guild_id: String,
pub channels: Vec<ChannelUnreadUpdateObject>,
}
impl WebSocketEvent for PassiveUpdateV1 {}

View File

@ -1,13 +1,13 @@
use crate::types::entities::User;
use crate::types::events::WebSocketEvent;
use crate::types::interfaces::Activity;
use crate::types::PublicUser;
use serde::{Deserialize, Serialize};
#[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: User,
pub guild_id: String,
pub user: PublicUser,
pub guild_id: Option<String>,
pub status: String,
pub activities: Vec<Activity>,
pub client_status: ClientStatusObject,

View File

@ -1,15 +1,77 @@
use crate::types::entities::{UnavailableGuild, User};
use crate::types::events::WebSocketEvent;
use crate::types::entities::{Guild, User};
use crate::types::events::{Session, WebSocketEvent};
use crate::types::interfaces::ClientStatusObject;
use crate::types::{Activity, GuildMember, PresenceUpdate, VoiceState};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
/// Sort of documented, though most fields are left out
/// For a full example see https://gist.github.com/kozabrada123/a347002b1fb8825a5727e40746d4e199
/// to:do add all undocumented fields
pub struct GatewayReady {
pub analytics_token: Option<String>,
pub auth_session_id_hash: Option<String>,
pub country_code: Option<String>,
pub v: u8,
pub user: User,
pub guilds: Vec<UnavailableGuild>,
/// For bots these are [UnavailableGuild]s, for users they are [Guild]
pub guilds: Vec<Guild>,
pub presences: Option<Vec<PresenceUpdate>>,
pub sessions: Option<Vec<Session>>,
pub session_id: String,
pub session_type: Option<String>,
pub resume_gateway_url: Option<String>,
pub shard: Option<(u64, u64)>,
}
impl WebSocketEvent for GatewayReady {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Sent after the READY event when a client is a user
/// {"t":"READY_SUPPLEMENTAL","s":2,"op":0,"d":{"merged_presences":{"guilds":[[{"user_id":"463640391196082177","status":"online","game":null,"client_status":{"web":"online"},"activities":[]}]],"friends":[{"user_id":"463640391196082177","status":"online","last_modified":1684053508443,"client_status":{"web":"online"},"activities":[]}]},"merged_members":[[{"user_id":"463640391196082177","roles":[],"premium_since":null,"pending":false,"nick":"pog","mute":false,"joined_at":"2021-05-30T15:24:08.763000+00:00","flags":0,"deaf":false,"communication_disabled_until":null,"avatar":null}]],"lazy_private_channels":[],"guilds":[{"voice_states":[],"id":"848582562217590824","embedded_activities":[]}],"disclose":["pomelo"]}}
pub struct GatewayReadySupplemental {
pub merged_presences: MergedPresences,
pub merged_members: Vec<Vec<GuildMember>>,
// ?
pub lazy_private_channels: Vec<serde_json::Value>,
pub guilds: Vec<SupplimentalGuild>,
// ? pomelo
pub disclose: Vec<String>,
}
impl WebSocketEvent for GatewayReadySupplemental {}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct MergedPresences {
pub guilds: Vec<Vec<MergedPresenceGuild>>,
pub friends: Vec<MergedPresenceFriend>,
}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct MergedPresenceFriend {
pub user_id: String,
pub status: String,
/// Looks like ms??
pub last_modified: u128,
pub client_status: ClientStatusObject,
pub activities: Vec<Activity>,
}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct MergedPresenceGuild {
pub user_id: String,
pub status: String,
// ?
pub game: Option<serde_json::Value>,
pub client_status: ClientStatusObject,
pub activities: Vec<Activity>,
}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct SupplimentalGuild {
pub voice_states: Option<Vec<VoiceState>>,
pub id: String,
pub embedded_activities: Vec<serde_json::Value>,
}

View File

@ -0,0 +1,22 @@
use crate::types::{events::WebSocketEvent, Relationship, RelationshipType, Snowflake};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://github.com/spacebarchat/server/issues/204
pub struct RelationshipAdd {
#[serde(flatten)]
pub relationship: Relationship,
pub should_notify: bool,
}
impl WebSocketEvent for RelationshipAdd {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://github.com/spacebarchat/server/issues/203
pub struct RelationshipRemove {
pub id: Snowflake,
#[serde(rename = "type")]
pub relationship_type: RelationshipType,
}
impl WebSocketEvent for RelationshipRemove {}

View File

@ -7,7 +7,7 @@ pub struct GatewayRequestGuildMembers {
pub guild_id: String,
pub query: Option<String>,
pub limit: u64,
pub presence: Option<bool>,
pub presences: Option<bool>,
pub user_ids: Option<String>,
pub nonce: Option<String>,
}

View File

@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};
use crate::types::{Activity, WebSocketEvent};
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Seems like it sends active session info to users on connect
/// [{"activities":[],"client_info":{"client":"web","os":"other","version":0},"session_id":"ab5941b50d818b1f8d93b4b1b581b192","status":"online"}]
pub struct SessionsReplace {
pub sessions: Vec<Session>,
}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Session info for the current user
pub struct Session {
pub activities: Vec<Activity>,
pub client_info: ClientInfo,
pub session_id: String,
pub status: String,
}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Another Client info object
/// {"client":"web","os":"other","version":0}
// Note: I don't think this one exists yet? Though I might've made a mistake and this might be a duplicate
pub struct ClientInfo {
pub client: String,
pub os: String,
pub version: u8,
}
impl WebSocketEvent for SessionsReplace {}

View File

@ -0,0 +1,30 @@
use serde::{Deserialize, Serialize};
use crate::types::{StageInstance, WebSocketEvent};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#stage-instance-create
pub struct StageInstanceCreate {
#[serde(flatten)]
pub stage_instance: StageInstance,
}
impl WebSocketEvent for StageInstanceCreate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#stage-instance-update
pub struct StageInstanceUpdate {
#[serde(flatten)]
pub stage_instance: StageInstance,
}
impl WebSocketEvent for StageInstanceUpdate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete
pub struct StageInstanceDelete {
#[serde(flatten)]
pub stage_instance: StageInstance,
}
impl WebSocketEvent for StageInstanceDelete {}

View File

@ -1,11 +1,11 @@
use crate::types::entities::{Channel, GuildMember, ThreadMember};
use crate::types::entities::{Channel, ThreadMember};
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[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 {
#[serde(flatten)]
pub thread: Channel,
}
@ -13,8 +13,8 @@ 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 {
#[serde(flatten)]
pub thread: Channel,
}
@ -22,8 +22,8 @@ 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 {
#[serde(flatten)]
pub thread: Channel,
}
@ -43,29 +43,12 @@ 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<u64>,
pub user_id: Option<u64>,
pub join_timestamp: Option<String>,
pub flags: Option<u64>,
pub member: Option<GuildMember>,
#[serde(flatten)]
pub member: ThreadMember,
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,
}
}
}
impl WebSocketEvent for ThreadMemberUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]

View File

@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
#[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 {
#[serde(flatten)]
pub user: User,
}

40
src/types/events/voice.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::types::{events::WebSocketEvent, VoiceState};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#update-voice-state
///
/// Sent to the server
///
/// Not to be confused with [VoiceStateUpdate]
pub struct UpdateVoiceState {
pub guild_id: Option<String>,
pub channel_id: Option<String>,
pub self_mute: bool,
pub self_deaf: bool,
}
impl WebSocketEvent for UpdateVoiceState {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#voice-state-update
///
/// Received from the server
///
/// Not to be confused with [UpdateVoiceState]
pub struct VoiceStateUpdate {
#[serde(flatten)]
pub state: VoiceState,
}
impl WebSocketEvent for VoiceStateUpdate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#voice-server-update
pub struct VoiceServerUpdate {
pub token: String,
pub guild_id: String,
pub endpoint: Option<String>,
}
impl WebSocketEvent for VoiceServerUpdate {}

View File

@ -1,13 +0,0 @@
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[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<String>,
pub self_mute: bool,
pub self_deaf: bool,
}
impl WebSocketEvent for GatewayVoiceStateUpdate {}

View File

@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};
use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#webhooks-update
pub struct WebhooksUpdate {
pub guild_id: String,
pub channel_id: String,
}
impl WebSocketEvent for WebhooksUpdate {}

View File

@ -0,0 +1 @@

View File

@ -3,7 +3,7 @@ use crate::types::utils::Snowflake;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct Interaction {
pub id: Snowflake,
pub r#type: InteractionType,
@ -15,8 +15,9 @@ pub struct Interaction {
pub version: i32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub enum InteractionType {
#[default]
SelfCommand = 0,
Ping = 1,
ApplicationCommand = 2,

View File

@ -187,7 +187,7 @@ You will receive a [`FieldFormatError`], if:
#[serde(rename_all = "snake_case")]
pub struct LoginSchema {
pub login: String,
pub password: String,
pub password: Option<String>,
pub undelete: Option<bool>,
pub captcha_key: Option<String>,
pub login_source: Option<String>,
@ -210,15 +210,12 @@ impl LoginSchema {
*/
pub fn new(
login: String,
password: String,
password: Option<String>,
undelete: Option<bool>,
captcha_key: Option<String>,
login_source: Option<String>,
gift_code_sku_id: Option<String>,
) -> Result<LoginSchema, FieldFormatError> {
let login = AuthUsername::new(login)?.username;
let password = AuthPassword::new(password)?.password;
Ok(LoginSchema {
login,
password,

22
tests/auth.rs Normal file
View File

@ -0,0 +1,22 @@
mod common;
use chorus::types;
#[tokio::test]
async fn test_registration() {
let mut bundle = common::setup().await;
let reg = types::RegisterSchema::new(
"Hiiii".to_string(),
None,
true,
None,
None,
None,
Some("2000-01-01".to_string()),
None,
None,
None,
)
.unwrap();
bundle.instance.register_account(&reg).await.unwrap();
common::teardown(bundle).await;
}

23
tests/channel.rs Normal file
View File

@ -0,0 +1,23 @@
mod common;
use chorus::types::Channel;
#[tokio::test]
async fn get_channel() {
let mut bundle = common::setup().await;
let bundle_channel = bundle.channel.clone();
let bundle_user = &mut bundle.user;
assert_eq!(
bundle_channel,
Channel::get(
bundle_user.token.as_str(),
bundle.instance.urls.get_api(),
&bundle_channel.id.to_string(),
&mut bundle_user.limits,
&mut bundle.instance.limits
)
.await
.unwrap()
);
common::teardown(bundle).await
}

View File

@ -5,16 +5,16 @@ use chorus::{
};
#[derive(Debug)]
struct TestBundle {
urls: URLBundle,
user: UserMeta,
instance: Instance,
guild_id: String,
channel: Channel,
pub struct TestBundle {
pub urls: URLBundle,
pub user: UserMeta,
pub instance: Instance,
pub guild_id: String,
pub channel: Channel,
}
// Set up a test by creating an Instance and a User. Reduces Test boilerplate.
async fn setup() -> TestBundle {
pub async fn setup() -> TestBundle {
let urls = URLBundle::new(
"http://localhost:3001/api".to_string(),
"ws://localhost:3001".to_string(),
@ -89,7 +89,7 @@ async fn setup() -> TestBundle {
}
// Teardown method to clean up after a test.
async fn teardown(mut bundle: TestBundle) {
pub async fn teardown(mut bundle: TestBundle) {
Guild::delete(
&mut bundle.user,
bundle.instance.urls.get_api(),
@ -98,55 +98,3 @@ async fn teardown(mut bundle: TestBundle) {
.await;
bundle.user.delete().await;
}
mod guild {
use chorus::types::{Channel, Guild, GuildCreateSchema};
#[tokio::test]
async fn guild_creation_deletion() {
let mut bundle = crate::setup().await;
let guild_create_schema = GuildCreateSchema {
name: Some("test".to_string()),
region: None,
icon: None,
channels: None,
guild_template_code: None,
system_channel_id: None,
rules_channel_id: None,
};
let guild = Guild::create(&mut bundle.user, bundle.urls.get_api(), guild_create_schema)
.await
.unwrap();
println!("{}", guild);
match Guild::delete(&mut bundle.user, bundle.urls.get_api(), guild).await {
None => assert!(true),
Some(_) => assert!(false),
}
crate::teardown(bundle).await
}
#[tokio::test]
async fn get_channel() {
let mut bundle = crate::setup().await;
let bundle_channel = bundle.channel.clone();
let bundle_user = &mut bundle.user;
assert_eq!(
bundle_channel,
Channel::get(
bundle_user.token.as_str(),
bundle.instance.urls.get_api(),
&bundle_channel.id.to_string(),
&mut bundle_user.limits,
&mut bundle.instance.limits
)
.await
.unwrap()
);
crate::teardown(bundle).await
}
}

29
tests/guild.rs Normal file
View File

@ -0,0 +1,29 @@
mod common;
use chorus::types::{Guild, GuildCreateSchema};
#[tokio::test]
async fn guild_creation_deletion() {
let mut bundle = common::setup().await;
let guild_create_schema = GuildCreateSchema {
name: Some("test".to_string()),
region: None,
icon: None,
channels: None,
guild_template_code: None,
system_channel_id: None,
rules_channel_id: None,
};
let guild = Guild::create(&mut bundle.user, bundle.urls.get_api(), guild_create_schema)
.await
.unwrap();
println!("{}", guild);
match Guild::delete(&mut bundle.user, bundle.urls.get_api(), guild).await {
None => assert!(true),
Some(_) => assert!(false),
}
common::teardown(bundle).await
}

12
tests/instance.rs Normal file
View File

@ -0,0 +1,12 @@
mod common;
#[tokio::test]
async fn generate_general_configuration_schema() {
let bundle = common::setup().await;
bundle
.instance
.general_configuration_schema()
.await
.unwrap();
common::teardown(bundle).await;
}

80
tests/message.rs Normal file
View File

@ -0,0 +1,80 @@
mod common;
use chorus::types;
use std::fs::File;
use std::io::{BufReader, Read};
#[tokio::test]
async fn send_message() {
let mut bundle = common::setup().await;
let mut message = types::MessageSendSchema::new(
None,
Some("A Message!".to_string()),
None,
None,
None,
None,
None,
None,
None,
None,
);
let _ = bundle
.user
.send_message(&mut message, bundle.channel.id.to_string(), None)
.await
.unwrap();
common::teardown(bundle).await
}
#[tokio::test]
async fn send_message_attachment() {
let f = File::open("./README.md").unwrap();
let mut reader = BufReader::new(f);
let mut buffer = Vec::new();
let mut bundle = common::setup().await;
reader.read_to_end(&mut buffer).unwrap();
let attachment = types::PartialDiscordFileAttachment {
id: None,
filename: "README.md".to_string(),
description: None,
content_type: None,
size: None,
url: None,
proxy_url: None,
width: None,
height: None,
ephemeral: None,
duration_secs: None,
waveform: None,
content: buffer,
};
let mut message = types::MessageSendSchema::new(
None,
Some("trans rights now".to_string()),
None,
None,
None,
None,
None,
None,
None,
Some(vec![attachment.clone()]),
);
let vec_attach = vec![attachment.clone()];
let _arg = Some(&vec_attach);
bundle
.user
.send_message(
&mut message,
bundle.channel.id.to_string(),
Some(vec![attachment.clone()]),
)
.await
.unwrap();
common::teardown(bundle).await
}