Compare commits
No commits in common. "f8fe1c404bf08b2ef97ae04b16d8dd7cd522c438" and "de10b9374f5bb28987310df1dbdec04a81c1ef13" have entirely different histories.
f8fe1c404b
...
de10b9374f
|
@ -124,7 +124,7 @@ jobs:
|
||||||
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.92" --force
|
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.92" --force
|
||||||
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
||||||
wasm-chrome:
|
wasm-chrome:
|
||||||
runs-on: ubuntu-latest
|
runs-on: macos-latest
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
|
@ -231,7 +231,6 @@ dependencies = [
|
||||||
"crypto_secretbox",
|
"crypto_secretbox",
|
||||||
"custom_error",
|
"custom_error",
|
||||||
"discortp",
|
"discortp",
|
||||||
"flate2",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"hostname",
|
"hostname",
|
||||||
|
@ -251,7 +250,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"simple_logger",
|
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -266,7 +264,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.4.0"
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de4221700bc486c6e6bc261fdea478936d33067a06325895f5d2a8cde5917272"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -355,15 +355,6 @@ version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc32fast"
|
|
||||||
version = "1.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -564,16 +555,6 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
|
checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flate2"
|
|
||||||
version = "1.0.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
|
|
||||||
dependencies = [
|
|
||||||
"crc32fast",
|
|
||||||
"miniz_oxide",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -2145,16 +2126,6 @@ dependencies = [
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simple_logger"
|
|
||||||
version = "5.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
|
|
@ -16,7 +16,7 @@ default = ["client", "rt-multi-thread"]
|
||||||
backend = ["poem", "sqlx"]
|
backend = ["poem", "sqlx"]
|
||||||
rt-multi-thread = ["tokio/rt-multi-thread"]
|
rt-multi-thread = ["tokio/rt-multi-thread"]
|
||||||
rt = ["tokio/rt"]
|
rt = ["tokio/rt"]
|
||||||
client = ["flate2"]
|
client = []
|
||||||
voice = ["voice_udp", "voice_gateway"]
|
voice = ["voice_udp", "voice_gateway"]
|
||||||
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
|
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
|
||||||
voice_gateway = []
|
voice_gateway = []
|
||||||
|
@ -43,7 +43,7 @@ thiserror = "1.0.56"
|
||||||
jsonwebtoken = "8.3.0"
|
jsonwebtoken = "8.3.0"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
async-trait = "0.1.77"
|
async-trait = "0.1.77"
|
||||||
chorus-macros = { path = "./chorus-macros", version = "0" } # Note: version here is used when releasing. This will use the latest release. Make sure to republish the crate when code in macros is changed!
|
chorus-macros = "0.3.0"
|
||||||
sqlx = { version = "0.7.3", features = [
|
sqlx = { version = "0.7.3", features = [
|
||||||
"mysql",
|
"mysql",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
|
@ -56,7 +56,6 @@ sqlx = { version = "0.7.3", features = [
|
||||||
discortp = { version = "0.5.0", optional = true, features = ["rtp", "discord", "demux"] }
|
discortp = { version = "0.5.0", optional = true, features = ["rtp", "discord", "demux"] }
|
||||||
crypto_secretbox = { version = "0.1.1", optional = true }
|
crypto_secretbox = { version = "0.1.1", optional = true }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
flate2 = { version = "1.0.30", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
rustls = "0.21.10"
|
rustls = "0.21.10"
|
||||||
|
@ -79,4 +78,3 @@ wasmtimer = "0.2.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
wasm-bindgen-test = "0.3.42"
|
wasm-bindgen-test = "0.3.42"
|
||||||
wasm-bindgen = "0.2.92"
|
wasm-bindgen = "0.2.92"
|
||||||
simple_logger = { version = "5.0.0", default-features=false }
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.4.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.4.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
description = "Macros for the chorus crate."
|
description = "Macros for the chorus crate."
|
||||||
|
|
|
@ -155,68 +155,3 @@ pub fn composite_derive(input: TokenStream) -> TokenStream {
|
||||||
_ => panic!("Composite derive macro only supports structs"),
|
_ => panic!("Composite derive macro only supports structs"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[proc_macro_derive(SqlxBitFlags)]
|
|
||||||
pub fn sqlx_bitflag_derive(input: TokenStream) -> TokenStream {
|
|
||||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
|
||||||
|
|
||||||
let name = &ast.ident;
|
|
||||||
|
|
||||||
quote!{
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl sqlx::Type<sqlx::MySql> for #name {
|
|
||||||
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
|
|
||||||
u64::type_info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl<'q> sqlx::Encode<'q, sqlx::MySql> for #name {
|
|
||||||
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
|
|
||||||
u64::encode_by_ref(&self.bits(), buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl<'q> sqlx::Decode<'q, sqlx::MySql> for #name {
|
|
||||||
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'q>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
|
|
||||||
u64::decode(value).map(|d| #name::from_bits(d).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(SerdeBitFlags)]
|
|
||||||
pub fn serde_bitflag_derive(input: TokenStream) -> TokenStream {
|
|
||||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
|
||||||
|
|
||||||
let name = &ast.ident;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl std::str::FromStr for #name {
|
|
||||||
type Err = std::num::ParseIntError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<#name, Self::Err> {
|
|
||||||
s.parse::<u64>().map(#name::from_bits).map(|f| f.unwrap_or(#name::empty()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl serde::Serialize for #name {
|
|
||||||
fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str(&self.bits().to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> serde::Deserialize<'de> for #name {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<#name, D::Error> where D: serde::de::Deserializer<'de> + Sized {
|
|
||||||
// let s = String::deserialize(deserializer)?.parse::<u64>().map_err(serde::de::Error::custom)?;
|
|
||||||
let s = crate::types::serde::string_or_u64(deserializer)?;
|
|
||||||
|
|
||||||
Ok(Self::from_bits(s).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
|
@ -3,8 +3,6 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
// This example showcase how to properly use gateway observers.
|
// This example showcase how to properly use gateway observers.
|
||||||
// (This assumes you have a manually created gateway, if you created
|
|
||||||
// a ChorusUser by e.g. logging in, you can access the gateway with user.gateway)
|
|
||||||
//
|
//
|
||||||
// To properly run it, you will need to change the token below.
|
// To properly run it, you will need to change the token below.
|
||||||
|
|
||||||
|
@ -14,7 +12,7 @@ const TOKEN: &str = "";
|
||||||
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
|
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chorus::gateway::{Gateway, GatewayOptions};
|
use chorus::gateway::Gateway;
|
||||||
use chorus::{
|
use chorus::{
|
||||||
self,
|
self,
|
||||||
gateway::Observer,
|
gateway::Observer,
|
||||||
|
@ -49,14 +47,8 @@ impl Observer<GatewayReady> for ExampleObserver {
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
let gateway_websocket_url = GATEWAY_URL.to_string();
|
||||||
|
|
||||||
// These options specify the encoding format, compression, etc
|
|
||||||
//
|
|
||||||
// For most cases the defaults should work, though some implementations
|
|
||||||
// might only support some formats or not support compression
|
|
||||||
let options = GatewayOptions::default();
|
|
||||||
|
|
||||||
// Initiate the gateway connection
|
// Initiate the gateway connection
|
||||||
let gateway = Gateway::spawn(gateway_websocket_url, options).await.unwrap();
|
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
|
||||||
|
|
||||||
// Create an instance of our observer
|
// Create an instance of our observer
|
||||||
let observer = ExampleObserver {};
|
let observer = ExampleObserver {};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
// This example showcases how to initiate a gateway connection manually
|
// This example showcases how to initiate a gateway connection manually
|
||||||
// (e. g. not through ChorusUser or Instance)
|
// (e. g. not through ChorusUser)
|
||||||
//
|
//
|
||||||
// To properly run it, you will need to modify the token below.
|
// To properly run it, you will need to modify the token below.
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use chorus::gateway::{Gateway, GatewayOptions};
|
use chorus::gateway::Gateway;
|
||||||
use chorus::{self, types::GatewayIdentifyPayload};
|
use chorus::{self, types::GatewayIdentifyPayload};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -26,15 +26,9 @@ use wasmtimer::tokio::sleep;
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
let gateway_websocket_url = GATEWAY_URL.to_string();
|
||||||
|
|
||||||
// These options specify the encoding format, compression, etc
|
|
||||||
//
|
|
||||||
// For most cases the defaults should work, though some implementations
|
|
||||||
// might only support some formats or not support compression
|
|
||||||
let options = GatewayOptions::default();
|
|
||||||
|
|
||||||
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
|
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
|
||||||
let gateway = Gateway::spawn(gateway_websocket_url, options).await.unwrap();
|
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
|
||||||
|
|
||||||
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
|
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use tokio_tungstenite::{
|
||||||
connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream,
|
connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::gateway::{GatewayMessage, RawGatewayMessage};
|
use crate::gateway::GatewayMessage;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TungsteniteBackend;
|
pub struct TungsteniteBackend;
|
||||||
|
@ -80,22 +80,3 @@ impl From<tungstenite::Message> for GatewayMessage {
|
||||||
Self(value.to_string())
|
Self(value.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RawGatewayMessage> for tungstenite::Message {
|
|
||||||
fn from(message: RawGatewayMessage) -> Self {
|
|
||||||
match message {
|
|
||||||
RawGatewayMessage::Text(text) => tungstenite::Message::Text(text),
|
|
||||||
RawGatewayMessage::Bytes(bytes) => tungstenite::Message::Binary(bytes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<tungstenite::Message> for RawGatewayMessage {
|
|
||||||
fn from(value: tungstenite::Message) -> Self {
|
|
||||||
match value {
|
|
||||||
tungstenite::Message::Binary(bytes) => RawGatewayMessage::Bytes(bytes),
|
|
||||||
tungstenite::Message::Text(text) => RawGatewayMessage::Text(text),
|
|
||||||
_ => RawGatewayMessage::Text(value.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use futures_util::{
|
||||||
|
|
||||||
use ws_stream_wasm::*;
|
use ws_stream_wasm::*;
|
||||||
|
|
||||||
use crate::gateway::{GatewayMessage, RawGatewayMessage};
|
use crate::gateway::GatewayMessage;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WasmBackend;
|
pub struct WasmBackend;
|
||||||
|
@ -46,21 +46,3 @@ impl From<WsMessage> for GatewayMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RawGatewayMessage> for WsMessage {
|
|
||||||
fn from(message: RawGatewayMessage) -> Self {
|
|
||||||
match message {
|
|
||||||
RawGatewayMessage::Text(text) => WsMessage::Text(text),
|
|
||||||
RawGatewayMessage::Bytes(bytes) => WsMessage::Binary(bytes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WsMessage> for RawGatewayMessage {
|
|
||||||
fn from(value: WsMessage) -> Self {
|
|
||||||
match value {
|
|
||||||
WsMessage::Binary(bytes) => RawGatewayMessage::Bytes(bytes),
|
|
||||||
WsMessage::Text(text) => RawGatewayMessage::Text(text),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use flate2::Decompress;
|
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
use log::*;
|
use log::*;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -20,9 +19,6 @@ use crate::types::{
|
||||||
WebSocketEvent,
|
WebSocketEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Tells us we have received enough of the buffer to decompress it
|
|
||||||
const ZLIB_SUFFIX: [u8; 4] = [0, 0, 255, 255];
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Gateway {
|
pub struct Gateway {
|
||||||
events: Arc<Mutex<Events>>,
|
events: Arc<Mutex<Events>>,
|
||||||
|
@ -32,36 +28,21 @@ pub struct Gateway {
|
||||||
kill_send: tokio::sync::broadcast::Sender<()>,
|
kill_send: tokio::sync::broadcast::Sender<()>,
|
||||||
kill_receive: tokio::sync::broadcast::Receiver<()>,
|
kill_receive: tokio::sync::broadcast::Receiver<()>,
|
||||||
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||||
/// Url which was used to initialize the gateway
|
|
||||||
url: String,
|
url: String,
|
||||||
/// Options which were used to initialize the gateway
|
|
||||||
options: GatewayOptions,
|
|
||||||
zlib_inflate: Option<flate2::Decompress>,
|
|
||||||
zlib_buffer: Option<Vec<u8>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gateway {
|
impl Gateway {
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
/// Creates / opens a new gateway connection.
|
pub async fn spawn(websocket_url: String) -> Result<GatewayHandle, GatewayError> {
|
||||||
///
|
let (websocket_send, mut websocket_receive) =
|
||||||
/// # Note
|
match WebSocketBackend::connect(&websocket_url).await {
|
||||||
/// The websocket url should begin with the prefix wss:// or ws:// (for unsecure connections)
|
Ok(streams) => streams,
|
||||||
pub async fn spawn(
|
Err(e) => {
|
||||||
websocket_url: String,
|
return Err(GatewayError::CannotConnect {
|
||||||
options: GatewayOptions,
|
error: format!("{:?}", e),
|
||||||
) -> Result<GatewayHandle, GatewayError> {
|
})
|
||||||
let url = options.add_to_url(websocket_url);
|
}
|
||||||
|
};
|
||||||
debug!("GW: Connecting to {}", url);
|
|
||||||
|
|
||||||
let (websocket_send, mut websocket_receive) = match WebSocketBackend::connect(&url).await {
|
|
||||||
Ok(streams) => streams,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(GatewayError::CannotConnect {
|
|
||||||
error: format!("{:?}", e),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let shared_websocket_send = Arc::new(Mutex::new(websocket_send));
|
let shared_websocket_send = Arc::new(Mutex::new(websocket_send));
|
||||||
|
|
||||||
|
@ -71,32 +52,10 @@ impl Gateway {
|
||||||
// Wait for the first hello and then spawn both tasks so we avoid nested tasks
|
// 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
|
// This automatically spawns the heartbeat task, but from the main thread
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
let received: RawGatewayMessage = websocket_receive.next().await.unwrap().unwrap().into();
|
let msg: GatewayMessage = websocket_receive.next().await.unwrap().unwrap().into();
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
let received: RawGatewayMessage = websocket_receive.next().await.unwrap().into();
|
let msg: GatewayMessage = websocket_receive.next().await.unwrap().into();
|
||||||
|
let gateway_payload: types::GatewayReceivePayload = serde_json::from_str(&msg.0).unwrap();
|
||||||
let message: GatewayMessage;
|
|
||||||
|
|
||||||
let zlib_buffer;
|
|
||||||
let zlib_inflate;
|
|
||||||
|
|
||||||
match options.transport_compression {
|
|
||||||
GatewayTransportCompression::None => {
|
|
||||||
zlib_buffer = None;
|
|
||||||
zlib_inflate = None;
|
|
||||||
message = GatewayMessage::from_raw_json_message(received).unwrap();
|
|
||||||
}
|
|
||||||
GatewayTransportCompression::ZLibStream => {
|
|
||||||
zlib_buffer = Some(Vec::new());
|
|
||||||
let mut inflate = Decompress::new(true);
|
|
||||||
|
|
||||||
message = GatewayMessage::from_zlib_stream_json_message(received, &mut inflate).unwrap();
|
|
||||||
|
|
||||||
zlib_inflate = Some(inflate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let gateway_payload: types::GatewayReceivePayload = serde_json::from_str(&message.0).unwrap();
|
|
||||||
|
|
||||||
if gateway_payload.op_code != GATEWAY_HELLO {
|
if gateway_payload.op_code != GATEWAY_HELLO {
|
||||||
return Err(GatewayError::NonHelloOnInitiate {
|
return Err(GatewayError::NonHelloOnInitiate {
|
||||||
|
@ -126,10 +85,7 @@ impl Gateway {
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
kill_receive: kill_send.subscribe(),
|
kill_receive: kill_send.subscribe(),
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
url: url.clone(),
|
url: websocket_url.clone(),
|
||||||
options,
|
|
||||||
zlib_inflate,
|
|
||||||
zlib_buffer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Now we can continuously check for messages in a different task, since we aren't going to receive another hello
|
// Now we can continuously check for messages in a different task, since we aren't going to receive another hello
|
||||||
|
@ -143,7 +99,7 @@ impl Gateway {
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(GatewayHandle {
|
Ok(GatewayHandle {
|
||||||
url: url.clone(),
|
url: websocket_url.clone(),
|
||||||
events: shared_events,
|
events: shared_events,
|
||||||
websocket_send: shared_websocket_send.clone(),
|
websocket_send: shared_websocket_send.clone(),
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
|
@ -152,7 +108,7 @@ impl Gateway {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main gateway listener task;
|
/// The main gateway listener task;
|
||||||
async fn gateway_listen_task(&mut self) {
|
pub async fn gateway_listen_task(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
let msg;
|
let msg;
|
||||||
|
|
||||||
|
@ -169,12 +125,12 @@ impl Gateway {
|
||||||
// PRETTYFYME: Remove inline conditional compiling
|
// PRETTYFYME: Remove inline conditional compiling
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
if let Some(Ok(message)) = msg {
|
if let Some(Ok(message)) = msg {
|
||||||
self.handle_raw_message(message.into()).await;
|
self.handle_message(message.into()).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
if let Some(message) = msg {
|
if let Some(message) = msg {
|
||||||
self.handle_raw_message(message.into()).await;
|
self.handle_message(message.into()).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,41 +163,8 @@ impl Gateway {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a [RawGatewayMessage], converts it to [GatewayMessage] based
|
|
||||||
/// of connection options and calls handle_message
|
|
||||||
async fn handle_raw_message(&mut self, raw_message: RawGatewayMessage) {
|
|
||||||
let message;
|
|
||||||
|
|
||||||
match self.options.transport_compression {
|
|
||||||
GatewayTransportCompression::None => {
|
|
||||||
message = GatewayMessage::from_raw_json_message(raw_message).unwrap()
|
|
||||||
}
|
|
||||||
GatewayTransportCompression::ZLibStream => {
|
|
||||||
let message_bytes = raw_message.into_bytes();
|
|
||||||
|
|
||||||
let can_decompress = message_bytes.len() > 4
|
|
||||||
&& message_bytes[message_bytes.len() - 4..] == ZLIB_SUFFIX;
|
|
||||||
|
|
||||||
let zlib_buffer = self.zlib_buffer.as_mut().unwrap();
|
|
||||||
zlib_buffer.extend(message_bytes.clone());
|
|
||||||
|
|
||||||
if !can_decompress {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let zlib_buffer = self.zlib_buffer.as_ref().unwrap();
|
|
||||||
let inflate = self.zlib_inflate.as_mut().unwrap();
|
|
||||||
|
|
||||||
message = GatewayMessage::from_zlib_stream_json_bytes(zlib_buffer, inflate).unwrap();
|
|
||||||
self.zlib_buffer = Some(Vec::new());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.handle_message(message).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This handles a message as a websocket event and updates its events along with the events' observers
|
/// This handles a message as a websocket event and updates its events along with the events' observers
|
||||||
async fn handle_message(&mut self, msg: GatewayMessage) {
|
pub async fn handle_message(&mut self, msg: GatewayMessage) {
|
||||||
if msg.0.is_empty() {
|
if msg.0.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,41 +2,11 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::string::FromUtf8Error;
|
|
||||||
|
|
||||||
use crate::types;
|
use crate::types;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Defines a raw gateway message, being either string json or bytes
|
/// Represents a message received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
|
||||||
///
|
|
||||||
/// This is used as an intermediary type between types from different websocket implementations
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) enum RawGatewayMessage {
|
|
||||||
Text(String),
|
|
||||||
Bytes(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawGatewayMessage {
|
|
||||||
/// Attempt to consume the message into a String, will try to convert binary to utf8
|
|
||||||
pub fn into_text(self) -> Result<String, FromUtf8Error> {
|
|
||||||
match self {
|
|
||||||
RawGatewayMessage::Text(text) => Ok(text),
|
|
||||||
RawGatewayMessage::Bytes(bytes) => String::from_utf8(bytes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume the message into bytes, will convert text to binary
|
|
||||||
pub fn into_bytes(self) -> Vec<u8> {
|
|
||||||
match self {
|
|
||||||
RawGatewayMessage::Text(text) => text.as_bytes().to_vec(),
|
|
||||||
RawGatewayMessage::Bytes(bytes) => bytes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a json message received from the gateway.
|
|
||||||
/// This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
|
|
||||||
/// This struct is used internally when handling messages.
|
/// This struct is used internally when handling messages.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GatewayMessage(pub String);
|
pub struct GatewayMessage(pub String);
|
||||||
|
@ -74,41 +44,4 @@ impl GatewayMessage {
|
||||||
pub fn payload(&self) -> Result<types::GatewayReceivePayload, serde_json::Error> {
|
pub fn payload(&self) -> Result<types::GatewayReceivePayload, serde_json::Error> {
|
||||||
serde_json::from_str(&self.0)
|
serde_json::from_str(&self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create self from an uncompressed json [RawGatewayMessage]
|
|
||||||
pub(crate) fn from_raw_json_message(
|
|
||||||
message: RawGatewayMessage,
|
|
||||||
) -> Result<GatewayMessage, FromUtf8Error> {
|
|
||||||
let text = message.into_text()?;
|
|
||||||
Ok(GatewayMessage(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to create self by decompressing zlib-stream bytes
|
|
||||||
// Thanks to <https://github.com/ByteAlex/zlib-stream-rs>, their
|
|
||||||
// code helped a lot with the stream implementation
|
|
||||||
pub(crate) fn from_zlib_stream_json_bytes(
|
|
||||||
bytes: &[u8],
|
|
||||||
inflate: &mut flate2::Decompress,
|
|
||||||
) -> Result<GatewayMessage, std::io::Error> {
|
|
||||||
|
|
||||||
// Note: is there a better way to handle the size of this output buffer?
|
|
||||||
//
|
|
||||||
// This used to be 10, I measured it at 11.5, so a safe bet feels like 20
|
|
||||||
let mut output = Vec::with_capacity(bytes.len() * 20);
|
|
||||||
let _status = inflate.decompress_vec(bytes, &mut output, flate2::FlushDecompress::Sync)?;
|
|
||||||
|
|
||||||
output.shrink_to_fit();
|
|
||||||
|
|
||||||
let string = String::from_utf8(output).unwrap();
|
|
||||||
|
|
||||||
Ok(GatewayMessage(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to create self by decompressing a zlib-stream bytes raw message
|
|
||||||
pub(crate) fn from_zlib_stream_json_message(
|
|
||||||
message: RawGatewayMessage,
|
|
||||||
inflate: &mut flate2::Decompress,
|
|
||||||
) -> Result<GatewayMessage, std::io::Error> {
|
|
||||||
Self::from_zlib_stream_json_bytes(&message.into_bytes(), inflate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,12 @@ pub mod gateway;
|
||||||
pub mod handle;
|
pub mod handle;
|
||||||
pub mod heartbeat;
|
pub mod heartbeat;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod options;
|
|
||||||
|
|
||||||
pub use backends::*;
|
pub use backends::*;
|
||||||
pub use gateway::*;
|
pub use gateway::*;
|
||||||
pub use handle::*;
|
pub use handle::*;
|
||||||
use heartbeat::*;
|
use heartbeat::*;
|
||||||
pub use message::*;
|
pub use message::*;
|
||||||
pub use options::*;
|
|
||||||
|
|
||||||
use crate::errors::GatewayError;
|
use crate::errors::GatewayError;
|
||||||
use crate::types::{Snowflake, WebSocketEvent};
|
use crate::types::{Snowflake, WebSocketEvent};
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Default)]
|
|
||||||
/// Options passed when initializing the gateway connection.
|
|
||||||
///
|
|
||||||
/// E.g. compression
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// Discord allows specifying the api version (v10, v9, ...) as well, but chorus is built upon one
|
|
||||||
/// main version (v9).
|
|
||||||
///
|
|
||||||
/// Similarly, discord also supports etf encoding, while chorus does not (yet).
|
|
||||||
/// We are looking into supporting it as an option, since it is faster and more lightweight.
|
|
||||||
///
|
|
||||||
/// See <https://docs.discord.sex/topics/gateway#connections>
|
|
||||||
pub struct GatewayOptions {
|
|
||||||
pub encoding: GatewayEncoding,
|
|
||||||
pub transport_compression: GatewayTransportCompression,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GatewayOptions {
|
|
||||||
/// Adds the options to an existing gateway url
|
|
||||||
///
|
|
||||||
/// Returns the new url
|
|
||||||
pub(crate) fn add_to_url(&self, url: String) -> String {
|
|
||||||
|
|
||||||
let mut url = url;
|
|
||||||
|
|
||||||
let mut parameters = Vec::with_capacity(2);
|
|
||||||
|
|
||||||
let encoding = self.encoding.to_url_parameter();
|
|
||||||
parameters.push(encoding);
|
|
||||||
|
|
||||||
let compression = self.transport_compression.to_url_parameter();
|
|
||||||
if let Some(some_compression) = compression {
|
|
||||||
parameters.push(some_compression);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut has_parameters = url.contains('?') && url.contains('=');
|
|
||||||
|
|
||||||
if !has_parameters {
|
|
||||||
// Insure it ends in a /, so we don't get a 400 error
|
|
||||||
if !url.ends_with('/') {
|
|
||||||
url.push('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lets hope that if it already has parameters the person knew to add '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
for parameter in parameters {
|
|
||||||
if !has_parameters {
|
|
||||||
url = format!("{}?{}", url, parameter);
|
|
||||||
has_parameters = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
url = format!("{}&{}", url, parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug, Default)]
|
|
||||||
/// Possible transport compression options for the gateway.
|
|
||||||
///
|
|
||||||
/// See <https://docs.discord.sex/topics/gateway#transport-compression>
|
|
||||||
pub enum GatewayTransportCompression {
|
|
||||||
/// Do not transport compress packets
|
|
||||||
None,
|
|
||||||
/// Transport compress using zlib stream
|
|
||||||
#[default]
|
|
||||||
ZLibStream,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GatewayTransportCompression {
|
|
||||||
/// Returns the option as a url parameter.
|
|
||||||
///
|
|
||||||
/// If set to [GatewayTransportCompression::None] returns [None].
|
|
||||||
///
|
|
||||||
/// If set to anything else, returns a string like "compress=zlib-stream"
|
|
||||||
pub(crate) fn to_url_parameter(self) -> Option<String> {
|
|
||||||
match self {
|
|
||||||
Self::None => None,
|
|
||||||
Self::ZLibStream => Some(String::from("compress=zlib-stream"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug, Default)]
|
|
||||||
/// See <https://docs.discord.sex/topics/gateway#encoding-and-compression>
|
|
||||||
pub enum GatewayEncoding {
|
|
||||||
/// Javascript object notation, a standard for websocket connections,
|
|
||||||
/// but contains a lot of overhead
|
|
||||||
#[default]
|
|
||||||
Json,
|
|
||||||
/// A binary format originating from Erlang
|
|
||||||
///
|
|
||||||
/// Should be lighter and faster than json.
|
|
||||||
///
|
|
||||||
/// !! Chorus does not implement ETF yet !!
|
|
||||||
ETF
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GatewayEncoding {
|
|
||||||
/// Returns the option as a url parameter.
|
|
||||||
///
|
|
||||||
/// Returns a string like "encoding=json"
|
|
||||||
pub(crate) fn to_url_parameter(self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Json => String::from("encoding=json"),
|
|
||||||
Self::ETF => String::from("encoding=etf")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::gateway::{Gateway, GatewayHandle, GatewayOptions};
|
use crate::gateway::{Gateway, GatewayHandle};
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::types::subconfigs::limits::rates::RateLimits;
|
use crate::types::types::subconfigs::limits::rates::RateLimits;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
|
@ -31,8 +31,6 @@ pub struct Instance {
|
||||||
pub limits_information: Option<LimitsInformation>,
|
pub limits_information: Option<LimitsInformation>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
#[serde(skip)]
|
|
||||||
pub gateway_options: GatewayOptions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Instance {
|
impl PartialEq for Instance {
|
||||||
|
@ -106,7 +104,6 @@ impl Instance {
|
||||||
instance_info: GeneralConfiguration::default(),
|
instance_info: GeneralConfiguration::default(),
|
||||||
limits_information: limit_information,
|
limits_information: limit_information,
|
||||||
client: Client::new(),
|
client: Client::new(),
|
||||||
gateway_options: GatewayOptions::default(),
|
|
||||||
};
|
};
|
||||||
instance.instance_info = match instance.general_configuration_schema().await {
|
instance.instance_info = match instance.general_configuration_schema().await {
|
||||||
Ok(schema) => schema,
|
Ok(schema) => schema,
|
||||||
|
@ -142,13 +139,6 @@ impl Instance {
|
||||||
Err(_) => Ok(None),
|
Err(_) => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the [`GatewayOptions`] the instance will use when spawning new connections.
|
|
||||||
///
|
|
||||||
/// These options are used on the gateways created when logging in and registering.
|
|
||||||
pub fn set_gateway_options(&mut self, options: GatewayOptions) {
|
|
||||||
self.gateway_options = options;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
@ -225,9 +215,7 @@ impl ChorusUser {
|
||||||
let object = Arc::new(RwLock::new(User::default()));
|
let object = Arc::new(RwLock::new(User::default()));
|
||||||
let wss_url = instance.read().unwrap().urls.wss.clone();
|
let wss_url = instance.read().unwrap().urls.wss.clone();
|
||||||
// Dummy gateway object
|
// Dummy gateway object
|
||||||
let gateway = Gateway::spawn(wss_url, GatewayOptions::default())
|
let gateway = Gateway::spawn(wss_url).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
ChorusUser {
|
ChorusUser {
|
||||||
token,
|
token,
|
||||||
belongs_to: instance.clone(),
|
belongs_to: instance.clone(),
|
||||||
|
|
|
@ -88,7 +88,7 @@ impl ChorusRequest {
|
||||||
let client = user.belongs_to.read().unwrap().client.clone();
|
let client = user.belongs_to.read().unwrap().client.clone();
|
||||||
let result = match client.execute(self.request.build().unwrap()).await {
|
let result = match client.execute(self.request.build().unwrap()).await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
log::trace!("Request successful: {:?}", result);
|
debug!("Request successful: {:?}", result);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
@ -494,7 +494,7 @@ impl ChorusRequest {
|
||||||
user: &mut ChorusUser,
|
user: &mut ChorusUser,
|
||||||
) -> ChorusResult<T> {
|
) -> ChorusResult<T> {
|
||||||
let response = self.send_request(user).await?;
|
let response = self.send_request(user).await?;
|
||||||
log::trace!("Got response: {:?}", response);
|
debug!("Got response: {:?}", response);
|
||||||
let response_text = match response.text().await {
|
let response_text = match response.text().await {
|
||||||
Ok(string) => string,
|
Ok(string) => string,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_aux::prelude::deserialize_number_from_string;
|
|
||||||
|
|
||||||
use crate::types::{config::types::subconfigs::register::{
|
use crate::types::{config::types::subconfigs::register::{
|
||||||
DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration,
|
DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration,
|
||||||
|
@ -23,7 +22,6 @@ pub struct RegisterConfiguration {
|
||||||
pub allow_multiple_accounts: bool,
|
pub allow_multiple_accounts: bool,
|
||||||
pub block_proxies: bool,
|
pub block_proxies: bool,
|
||||||
pub incrementing_discriminators: bool,
|
pub incrementing_discriminators: bool,
|
||||||
#[serde(deserialize_with = "deserialize_number_from_string")]
|
|
||||||
pub default_rights: Rights,
|
pub default_rights: Rights,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct Application {
|
||||||
pub verify_key: String,
|
pub verify_key: String,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub owner: Shared<User>,
|
pub owner: Shared<User>,
|
||||||
pub flags: ApplicationFlags,
|
pub flags: u64,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
|
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
@ -73,7 +73,7 @@ impl Default for Application {
|
||||||
bot_require_code_grant: false,
|
bot_require_code_grant: false,
|
||||||
verify_key: "".to_string(),
|
verify_key: "".to_string(),
|
||||||
owner: Default::default(),
|
owner: Default::default(),
|
||||||
flags: ApplicationFlags::empty(),
|
flags: 0,
|
||||||
redirect_uris: None,
|
redirect_uris: None,
|
||||||
rpc_application_state: 0,
|
rpc_application_state: 0,
|
||||||
store_application_state: 1,
|
store_application_state: 1,
|
||||||
|
@ -93,6 +93,12 @@ impl Default for Application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Application {
|
||||||
|
pub fn flags(&self) -> ApplicationFlags {
|
||||||
|
ApplicationFlags::from_bits(self.flags.to_owned()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord.com/developers/docs/resources/application#install-params-object>
|
/// See <https://discord.com/developers/docs/resources/application#install-params-object>
|
||||||
|
@ -102,8 +108,7 @@ pub struct InstallParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord.com/developers/docs/resources/application#application-object-application-flags>
|
/// See <https://discord.com/developers/docs/resources/application#application-object-application-flags>
|
||||||
pub struct ApplicationFlags: u64 {
|
pub struct ApplicationFlags: u64 {
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_aux::prelude::deserialize_string_from_number;
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use crate::types::Shared;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
PermissionFlags, Shared,
|
|
||||||
entities::{GuildMember, User},
|
entities::{GuildMember, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
@ -63,9 +64,7 @@ pub struct Channel {
|
||||||
pub managed: Option<bool>,
|
pub managed: Option<bool>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub member: Option<ThreadMember>,
|
pub member: Option<ThreadMember>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub member_count: Option<i32>,
|
pub member_count: Option<i32>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub message_count: Option<i32>,
|
pub message_count: Option<i32>,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub nsfw: Option<bool>,
|
pub nsfw: Option<bool>,
|
||||||
|
@ -76,7 +75,6 @@ pub struct Channel {
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
#[cfg_attr(feature = "client", observe_option_vec)]
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>,
|
pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub permissions: Option<String>,
|
pub permissions: Option<String>,
|
||||||
pub position: Option<i32>,
|
pub position: Option<i32>,
|
||||||
pub rate_limit_per_user: Option<i32>,
|
pub rate_limit_per_user: Option<i32>,
|
||||||
|
@ -87,7 +85,6 @@ pub struct Channel {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub thread_metadata: Option<ThreadMetadata>,
|
pub thread_metadata: Option<ThreadMetadata>,
|
||||||
pub topic: Option<String>,
|
pub topic: Option<String>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub total_message_sent: Option<i32>,
|
pub total_message_sent: Option<i32>,
|
||||||
pub user_limit: Option<i32>,
|
pub user_limit: Option<i32>,
|
||||||
pub video_quality_mode: Option<i32>,
|
pub video_quality_mode: Option<i32>,
|
||||||
|
@ -147,20 +144,14 @@ pub struct Tag {
|
||||||
pub struct PermissionOverwrite {
|
pub struct PermissionOverwrite {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub overwrite_type: PermissionOverwriteType,
|
#[serde(deserialize_with = "deserialize_string_from_number")]
|
||||||
|
pub overwrite_type: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub allow: PermissionFlags,
|
#[serde(deserialize_with = "deserialize_string_from_number")]
|
||||||
|
pub allow: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub deny: PermissionFlags,
|
#[serde(deserialize_with = "deserialize_string_from_number")]
|
||||||
}
|
pub deny: String,
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize_repr, Deserialize_repr, Clone, PartialEq, Eq, PartialOrd)]
|
|
||||||
#[repr(u8)]
|
|
||||||
/// # Reference
|
|
||||||
pub enum PermissionOverwriteType {
|
|
||||||
Role = 0,
|
|
||||||
Member = 1,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
|
@ -265,12 +256,3 @@ pub enum ChannelType {
|
||||||
// TODO: Couldn't find reference
|
// TODO: Couldn't find reference
|
||||||
Unhandled = 255,
|
Unhandled = 255,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// # Reference
|
|
||||||
/// See <https://docs.discord.sex/resources/message#followed-channel-object>
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
|
||||||
pub struct FollowedChannel {
|
|
||||||
pub channel_id: Snowflake,
|
|
||||||
pub webhook_id: Snowflake
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{PartialEmoji, Shared};
|
use crate::types::Shared;
|
||||||
use crate::types::entities::User;
|
use crate::types::entities::User;
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
|
||||||
|
@ -66,18 +66,3 @@ impl PartialEq for Emoji {
|
||||||
|| self.available != other.available)
|
|| self.available != other.available)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PartialEmoji> for Emoji {
|
|
||||||
fn from(value: PartialEmoji) -> Self {
|
|
||||||
Self {
|
|
||||||
id: value.id.unwrap_or_default(), // TODO: this should be handled differently
|
|
||||||
name: Some(value.name),
|
|
||||||
roles: None,
|
|
||||||
user: None,
|
|
||||||
require_colons: Some(value.animated),
|
|
||||||
managed: None,
|
|
||||||
animated: Some(value.animated),
|
|
||||||
available: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -59,8 +59,7 @@ pub struct Guild {
|
||||||
pub emojis: Vec<Shared<Emoji>>,
|
pub emojis: Vec<Shared<Emoji>>,
|
||||||
pub explicit_content_filter: Option<ExplicitContentFilterLevel>,
|
pub explicit_content_filter: Option<ExplicitContentFilterLevel>,
|
||||||
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
|
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
|
||||||
#[serde(default)]
|
pub features: Option<GuildFeaturesList>,
|
||||||
pub features: GuildFeaturesList,
|
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub icon_hash: Option<String>,
|
pub icon_hash: Option<String>,
|
||||||
|
@ -100,7 +99,7 @@ pub struct Guild {
|
||||||
pub splash: Option<String>,
|
pub splash: Option<String>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub stickers: Option<Vec<Sticker>>,
|
pub stickers: Option<Vec<Sticker>>,
|
||||||
pub system_channel_flags: Option<SystemChannelFlags>,
|
pub system_channel_flags: Option<u64>,
|
||||||
pub system_channel_id: Option<Snowflake>,
|
pub system_channel_id: Option<Snowflake>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub vanity_url_code: Option<String>,
|
pub vanity_url_code: Option<String>,
|
||||||
|
@ -112,7 +111,7 @@ pub struct Guild {
|
||||||
#[cfg_attr(feature = "client", observe_option_vec)]
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
pub webhooks: Option<Vec<Shared<Webhook>>>,
|
pub webhooks: Option<Vec<Shared<Webhook>>>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub welcome_screen: sqlx::types::Json<Option<WelcomeScreenObject>>,
|
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub welcome_screen: Option<WelcomeScreenObject>,
|
pub welcome_screen: Option<WelcomeScreenObject>,
|
||||||
pub widget_channel_id: Option<Snowflake>,
|
pub widget_channel_id: Option<Snowflake>,
|
||||||
|
@ -423,8 +422,7 @@ pub enum PremiumTier {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#system-channel-flags>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#system-channel-flags>
|
||||||
pub struct SystemChannelFlags: u64 {
|
pub struct SystemChannelFlags: u64 {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{GuildMemberFlags, Shared};
|
use crate::types::Shared;
|
||||||
use crate::types::{entities::PublicUser, Snowflake};
|
use crate::types::{entities::PublicUser, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Default, Serialize, Clone)]
|
||||||
|
@ -25,7 +25,7 @@ pub struct GuildMember {
|
||||||
pub premium_since: Option<DateTime<Utc>>,
|
pub premium_since: Option<DateTime<Utc>>,
|
||||||
pub deaf: bool,
|
pub deaf: bool,
|
||||||
pub mute: bool,
|
pub mute: bool,
|
||||||
pub flags: Option<GuildMemberFlags>,
|
pub flags: Option<i32>,
|
||||||
pub pending: Option<bool>,
|
pub pending: Option<bool>,
|
||||||
pub permissions: Option<String>,
|
pub permissions: Option<String>,
|
||||||
pub communication_disabled_until: Option<DateTime<Utc>>,
|
pub communication_disabled_until: Option<DateTime<Utc>>,
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{Snowflake, WelcomeScreenObject, Shared, InviteFlags, InviteType, InviteTargetType, Guild, VerificationLevel};
|
use crate::types::{Snowflake, WelcomeScreenObject, Shared, InviteFlags, InviteType, InviteTargetType};
|
||||||
use crate::types::types::guild_configuration::GuildFeaturesList;
|
|
||||||
|
|
||||||
use super::guild::GuildScheduledEvent;
|
use super::guild::GuildScheduledEvent;
|
||||||
use super::{Application, Channel, GuildMember, NSFWLevel, User};
|
use super::{Application, Channel, GuildMember, NSFWLevel, User};
|
||||||
|
@ -16,9 +15,7 @@ use super::{Application, Channel, GuildMember, NSFWLevel, User};
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct Invite {
|
pub struct Invite {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub approximate_member_count: Option<i32>,
|
pub approximate_member_count: Option<i32>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub approximate_presence_count: Option<i32>,
|
pub approximate_presence_count: Option<i32>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub channel: Option<Channel>,
|
pub channel: Option<Channel>,
|
||||||
|
@ -47,7 +44,7 @@ pub struct Invite {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub target_user: Option<User>,
|
pub target_user: Option<User>,
|
||||||
pub temporary: Option<bool>,
|
pub temporary: Option<bool>,
|
||||||
pub uses: Option<u32>,
|
pub uses: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The guild an invite is for.
|
/// The guild an invite is for.
|
||||||
|
@ -58,8 +55,8 @@ pub struct InviteGuild {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub splash: Option<String>,
|
pub splash: Option<String>,
|
||||||
pub verification_level: VerificationLevel,
|
pub verification_level: i32,
|
||||||
pub features: GuildFeaturesList,
|
pub features: Vec<String>,
|
||||||
pub vanity_url_code: Option<String>,
|
pub vanity_url_code: Option<String>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
|
@ -71,29 +68,6 @@ pub struct InviteGuild {
|
||||||
pub welcome_screen: Option<WelcomeScreenObject>,
|
pub welcome_screen: Option<WelcomeScreenObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Guild> for InviteGuild {
|
|
||||||
fn from(value: Guild) -> Self {
|
|
||||||
Self {
|
|
||||||
id: value.id,
|
|
||||||
name: value.name.unwrap_or_default(),
|
|
||||||
icon: value.icon,
|
|
||||||
splash: value.splash,
|
|
||||||
verification_level: value.verification_level.unwrap_or_default(),
|
|
||||||
features: value.features,
|
|
||||||
vanity_url_code: value.vanity_url_code,
|
|
||||||
description: value.description,
|
|
||||||
banner: value.banner,
|
|
||||||
premium_subscription_count: value.premium_subscription_count,
|
|
||||||
nsfw_deprecated: None,
|
|
||||||
nsfw_level: value.nsfw_level.unwrap_or_default(),
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
welcome_screen: value.welcome_screen.0,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
welcome_screen: value.welcome_screen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object>
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct InviteStageInstance {
|
pub struct InviteStageInstance {
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
Shared,
|
Shared,
|
||||||
|
@ -41,7 +39,7 @@ pub struct Message {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub attachments: Option<Vec<Attachment>>,
|
pub attachments: Option<Vec<Attachment>>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub embeds: sqlx::types::Json<Vec<Embed>>,
|
pub embeds: Vec<sqlx::types::Json<Embed>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub embeds: Option<Vec<Embed>>,
|
pub embeds: Option<Vec<Embed>>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
|
@ -52,7 +50,7 @@ pub struct Message {
|
||||||
pub pinned: bool,
|
pub pinned: bool,
|
||||||
pub webhook_id: Option<Snowflake>,
|
pub webhook_id: Option<Snowflake>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub message_type: MessageType,
|
pub message_type: i32,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub activity: Option<sqlx::types::Json<MessageActivity>>,
|
pub activity: Option<sqlx::types::Json<MessageActivity>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
@ -64,22 +62,14 @@ pub struct Message {
|
||||||
pub message_reference: Option<sqlx::types::Json<MessageReference>>,
|
pub message_reference: Option<sqlx::types::Json<MessageReference>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub message_reference: Option<MessageReference>,
|
pub message_reference: Option<MessageReference>,
|
||||||
pub flags: Option<MessageFlags>,
|
pub flags: Option<u64>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub referenced_message: Option<Box<Message>>,
|
pub referenced_message: Option<Box<Message>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub interaction: Option<MessageInteraction>,
|
pub interaction: Option<MessageInteraction>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub thread: Option<Channel>,
|
pub thread: Option<Channel>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub components: Option<sqlx::types::Json<Vec<Component>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub components: Option<Vec<Component>>,
|
pub components: Option<Vec<Component>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub sticker_items: Option<Vec<StickerItem>>,
|
pub sticker_items: Option<Vec<StickerItem>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
|
||||||
pub stickers: Option<Vec<Sticker>>,
|
pub stickers: Option<Vec<Sticker>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
pub position: Option<i32>,
|
||||||
pub role_subscription_data: Option<RoleSubscriptionData>,
|
pub role_subscription_data: Option<RoleSubscriptionData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +103,7 @@ impl PartialEq for Message {
|
||||||
&& self.thread == other.thread
|
&& self.thread == other.thread
|
||||||
&& self.components == other.components
|
&& self.components == other.components
|
||||||
&& self.sticker_items == other.sticker_items
|
&& self.sticker_items == other.sticker_items
|
||||||
// && self.position == other.position
|
&& self.position == other.position
|
||||||
&& self.role_subscription_data == other.role_subscription_data
|
&& self.role_subscription_data == other.role_subscription_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,22 +112,12 @@ impl PartialEq for Message {
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object>
|
||||||
pub struct MessageReference {
|
pub struct MessageReference {
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub reference_type: MessageReferenceType,
|
|
||||||
pub message_id: Snowflake,
|
pub message_id: Snowflake,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
pub guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
pub fail_if_not_exists: Option<bool>,
|
pub fail_if_not_exists: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)]
|
|
||||||
pub enum MessageReferenceType {
|
|
||||||
/// A standard reference used by replies and system messages
|
|
||||||
Default = 0,
|
|
||||||
/// A reference used to point to a message at a point in time
|
|
||||||
Forward = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct MessageInteraction {
|
pub struct MessageInteraction {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
@ -247,15 +227,10 @@ pub struct EmbedField {
|
||||||
pub struct Reaction {
|
pub struct Reaction {
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
pub burst_count: u32,
|
pub burst_count: u32,
|
||||||
#[serde(default)]
|
|
||||||
pub me: bool,
|
pub me: bool,
|
||||||
#[serde(default)]
|
|
||||||
pub burst_me: bool,
|
pub burst_me: bool,
|
||||||
pub burst_colors: Vec<String>,
|
pub burst_colors: Vec<String>,
|
||||||
pub emoji: Emoji,
|
pub emoji: Emoji,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
#[serde(skip)]
|
|
||||||
pub user_ids: Vec<Snowflake>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
||||||
|
@ -278,155 +253,3 @@ pub struct MessageActivity {
|
||||||
pub activity_type: i64,
|
pub activity_type: i64,
|
||||||
pub party_id: Option<String>,
|
pub party_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize_repr, Deserialize_repr, Eq, PartialOrd, Ord)]
|
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
|
||||||
#[repr(u8)]
|
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
|
||||||
/// # Reference
|
|
||||||
/// See <https://docs.discord.sex/resources/message#message-type>
|
|
||||||
pub enum MessageType {
|
|
||||||
/// A default message
|
|
||||||
#[default]
|
|
||||||
Default = 0,
|
|
||||||
/// A message sent when a user is added to a group DM or thread
|
|
||||||
RecipientAdd = 1,
|
|
||||||
/// A message sent when a user is removed from a group DM or thread
|
|
||||||
RecipientRemove = 2,
|
|
||||||
/// A message sent when a user creates a call in a private channel
|
|
||||||
Call = 3,
|
|
||||||
/// A message sent when a group DM or thread's name is changed
|
|
||||||
ChannelNameChange = 4,
|
|
||||||
/// A message sent when a group DM's icon is changed
|
|
||||||
ChannelIconChange = 5,
|
|
||||||
/// A message sent when a message is pinned in a channel
|
|
||||||
ChannelPinnedMessage = 6,
|
|
||||||
/// A message sent when a user joins a guild
|
|
||||||
GuildMemberJoin = 7,
|
|
||||||
/// A message sent when a user subscribes to (boosts) a guild
|
|
||||||
UserPremiumGuildSubscription = 8,
|
|
||||||
/// A message sent when a user subscribes to (boosts) a guild to tier 1
|
|
||||||
UserPremiumGuildSubscriptionTier1 = 9,
|
|
||||||
/// A message sent when a user subscribes to (boosts) a guild to tier 2
|
|
||||||
UserPremiumGuildSubscriptionTier2 = 10,
|
|
||||||
/// A message sent when a user subscribes to (boosts) a guild to tier 3
|
|
||||||
UserPremiumGuildSubscriptionTier3 = 11,
|
|
||||||
/// A message sent when a news channel is followed
|
|
||||||
ChannelFollowAdd = 12,
|
|
||||||
/// A message sent when a user starts streaming in a guild (deprecated)
|
|
||||||
#[deprecated]
|
|
||||||
GuildStream = 13,
|
|
||||||
/// A message sent when a guild is disqualified from discovery
|
|
||||||
GuildDiscoveryDisqualified = 14,
|
|
||||||
/// A message sent when a guild requalifies for discovery
|
|
||||||
GuildDiscoveryRequalified = 15,
|
|
||||||
/// A message sent when a guild has failed discovery requirements for a week
|
|
||||||
GuildDiscoveryGracePeriodInitial = 16,
|
|
||||||
/// A message sent when a guild has failed discovery requirements for 3 weeks
|
|
||||||
GuildDiscoveryGracePeriodFinal = 17,
|
|
||||||
/// A message sent when a thread is created
|
|
||||||
ThreadCreated = 18,
|
|
||||||
/// A message sent when a user replies to a message
|
|
||||||
Reply = 19,
|
|
||||||
/// A message sent when a user uses a slash command
|
|
||||||
#[serde(rename = "CHAT_INPUT_COMMAND")]
|
|
||||||
ApplicationCommand = 20,
|
|
||||||
/// A message sent when a thread starter message is added to a thread
|
|
||||||
ThreadStarterMessage = 21,
|
|
||||||
/// A message sent to remind users to invite friends to a guild
|
|
||||||
GuildInviteReminder = 22,
|
|
||||||
/// A message sent when a user uses a context menu command
|
|
||||||
ContextMenuCommand = 23,
|
|
||||||
/// A message sent when auto moderation takes an action
|
|
||||||
AutoModerationAction = 24,
|
|
||||||
/// A message sent when a user purchases or renews a role subscription
|
|
||||||
RoleSubscriptionPurchase = 25,
|
|
||||||
/// A message sent when a user is upsold to a premium interaction
|
|
||||||
InteractionPremiumUpsell = 26,
|
|
||||||
/// A message sent when a stage channel starts
|
|
||||||
StageStart = 27,
|
|
||||||
/// A message sent when a stage channel ends
|
|
||||||
StageEnd = 28,
|
|
||||||
/// A message sent when a user starts speaking in a stage channel
|
|
||||||
StageSpeaker = 29,
|
|
||||||
/// A message sent when a user raises their hand in a stage channel
|
|
||||||
StageRaiseHand = 30,
|
|
||||||
/// A message sent when a stage channel's topic is changed
|
|
||||||
StageTopic = 31,
|
|
||||||
/// A message sent when a user purchases an application premium subscription
|
|
||||||
GuildApplicationPremiumSubscription = 32,
|
|
||||||
/// A message sent when a user adds an application to group DM
|
|
||||||
PrivateChannelIntegrationAdded = 33,
|
|
||||||
/// A message sent when a user removed an application from a group DM
|
|
||||||
PrivateChannelIntegrationRemoved = 34,
|
|
||||||
/// A message sent when a user gifts a premium (Nitro) referral
|
|
||||||
PremiumReferral = 35,
|
|
||||||
/// A message sent when a user enabled lockdown for the guild
|
|
||||||
GuildIncidentAlertModeEnabled = 36,
|
|
||||||
/// A message sent when a user disables lockdown for the guild
|
|
||||||
GuildIncidentAlertModeDisabled = 37,
|
|
||||||
/// A message sent when a user reports a raid for the guild
|
|
||||||
GuildIncidentReportRaid = 38,
|
|
||||||
/// A message sent when a user reports a false alarm for the guild
|
|
||||||
GuildIncidentReportFalseAlarm = 39,
|
|
||||||
/// A message sent when no one sends a message in the current channel for 1 hour
|
|
||||||
GuildDeadchatRevivePrompt = 40,
|
|
||||||
/// A message sent when a user buys another user a gift
|
|
||||||
CustomGift = 41,
|
|
||||||
GuildGamingStatsPrompt = 42,
|
|
||||||
/// A message sent when a user purchases a guild product
|
|
||||||
PurchaseNotification = 44
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, chorus_macros::SerdeBitFlags)]
|
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
/// # Reference
|
|
||||||
/// See <https://docs.discord.sex/resources/message#message-type>
|
|
||||||
pub struct MessageFlags: u64 {
|
|
||||||
/// This message has been published to subscribed channels (via Channel Following)
|
|
||||||
const CROSSPOSTED = 1 << 0;
|
|
||||||
/// This message originated from a message in another channel (via Channel Following)
|
|
||||||
const IS_CROSSPOST = 1 << 1;
|
|
||||||
/// Embeds will not be included when serializing this message
|
|
||||||
const SUPPRESS_EMBEDS = 1 << 2;
|
|
||||||
/// The source message for this crosspost has been deleted (via Channel Following)
|
|
||||||
const SOURCE_MESSAGE_DELETED = 1 << 3;
|
|
||||||
/// This message came from the urgent message system
|
|
||||||
const URGENT = 1 << 4;
|
|
||||||
/// This message has an associated thread, with the same ID as the message
|
|
||||||
const HAS_THREAD = 1 << 5;
|
|
||||||
/// This message is only visible to the user who invoked the interaction
|
|
||||||
const EPHEMERAL = 1 << 6;
|
|
||||||
/// This message is an interaction response and the bot is "thinking"
|
|
||||||
const LOADING = 1 << 7;
|
|
||||||
/// Some roles were not mentioned and added to the thread
|
|
||||||
const FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8;
|
|
||||||
/// This message contains a link that impersonates Discord
|
|
||||||
const SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10;
|
|
||||||
/// This message will not trigger push and desktop notifications
|
|
||||||
const SUPPRESS_NOTIFICATIONS = 1 << 12;
|
|
||||||
/// This message's audio attachments are rendered as voice messages
|
|
||||||
const VOICE_MESSAGE = 1 << 13;
|
|
||||||
/// This message has a forwarded message snapshot attached
|
|
||||||
const HAS_SNAPSHOT = 1 << 14;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct PartialEmoji {
|
|
||||||
#[serde(default)]
|
|
||||||
pub id: Option<Snowflake>,
|
|
||||||
pub name: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub animated: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, PartialOrd)]
|
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum ReactionType {
|
|
||||||
Normal = 0,
|
|
||||||
Burst = 1, // The dreaded super reactions
|
|
||||||
}
|
|
||||||
|
|
|
@ -71,8 +71,7 @@ pub struct RoleTags {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, PartialOrd, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
/// Permissions limit what users of certain roles can do on a Guild to Guild basis.
|
/// Permissions limit what users of certain roles can do on a Guild to Guild basis.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub struct User {
|
||||||
pub bot: Option<bool>,
|
pub bot: Option<bool>,
|
||||||
pub system: Option<bool>,
|
pub system: Option<bool>,
|
||||||
pub mfa_enabled: Option<bool>,
|
pub mfa_enabled: Option<bool>,
|
||||||
pub accent_color: Option<u32>,
|
pub accent_color: Option<u8>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(default))]
|
#[cfg_attr(feature = "sqlx", sqlx(default))]
|
||||||
pub locale: Option<String>,
|
pub locale: Option<String>,
|
||||||
pub verified: Option<bool>,
|
pub verified: Option<bool>,
|
||||||
|
@ -54,14 +54,14 @@ pub struct User {
|
||||||
/// So we need to account for that
|
/// So we need to account for that
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(deserialize_with = "deserialize_option_number_from_string")]
|
#[serde(deserialize_with = "deserialize_option_number_from_string")]
|
||||||
pub flags: Option<UserFlags>,
|
pub flags: Option<i32>,
|
||||||
pub premium_since: Option<DateTime<Utc>>,
|
pub premium_since: Option<DateTime<Utc>>,
|
||||||
pub premium_type: Option<u8>,
|
pub premium_type: Option<u8>,
|
||||||
pub pronouns: Option<String>,
|
pub pronouns: Option<String>,
|
||||||
pub public_flags: Option<u32>,
|
pub public_flags: Option<u32>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub bio: Option<String>,
|
pub bio: Option<String>,
|
||||||
pub theme_colors: Option<Vec<u32>>,
|
pub theme_colors: Option<Vec<u8>>,
|
||||||
pub phone: Option<String>,
|
pub phone: Option<String>,
|
||||||
pub nsfw_allowed: Option<bool>,
|
pub nsfw_allowed: Option<bool>,
|
||||||
pub premium: Option<bool>,
|
pub premium: Option<bool>,
|
||||||
|
@ -76,9 +76,9 @@ pub struct PublicUser {
|
||||||
pub username: Option<String>,
|
pub username: Option<String>,
|
||||||
pub discriminator: Option<String>,
|
pub discriminator: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub accent_color: Option<u32>,
|
pub accent_color: Option<u8>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub theme_colors: Option<Vec<u32>>,
|
pub theme_colors: Option<Vec<u8>>,
|
||||||
pub pronouns: Option<String>,
|
pub pronouns: Option<String>,
|
||||||
pub bot: Option<bool>,
|
pub bot: Option<bool>,
|
||||||
pub bio: Option<String>,
|
pub bio: Option<String>,
|
||||||
|
@ -111,8 +111,8 @@ impl From<User> for PublicUser {
|
||||||
const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32;
|
const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32;
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
pub struct UserFlags: u64 {
|
pub struct UserFlags: u64 {
|
||||||
const DISCORD_EMPLOYEE = 1 << 0;
|
const DISCORD_EMPLOYEE = 1 << 0;
|
||||||
const PARTNERED_SERVER_OWNER = 1 << 1;
|
const PARTNERED_SERVER_OWNER = 1 << 1;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use chrono::{serde::ts_milliseconds_option, Utc};
|
use chrono::{serde::ts_milliseconds_option, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{Shared, Snowflake};
|
use crate::types::Shared;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
@ -37,7 +37,7 @@ pub enum UserTheme {
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct UserSettings {
|
pub struct UserSettings {
|
||||||
pub afk_timeout: Option<u16>,
|
pub afk_timeout: u16,
|
||||||
pub allow_accessibility_detection: bool,
|
pub allow_accessibility_detection: bool,
|
||||||
pub animate_emoji: bool,
|
pub animate_emoji: bool,
|
||||||
pub animate_stickers: u8,
|
pub animate_stickers: u8,
|
||||||
|
@ -90,7 +90,7 @@ pub struct UserSettings {
|
||||||
impl Default for UserSettings {
|
impl Default for UserSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
afk_timeout: Some(3600),
|
afk_timeout: 3600,
|
||||||
allow_accessibility_detection: true,
|
allow_accessibility_detection: true,
|
||||||
animate_emoji: true,
|
animate_emoji: true,
|
||||||
animate_stickers: 0,
|
animate_stickers: 0,
|
||||||
|
@ -148,10 +148,10 @@ impl Default for FriendSourceFlags {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GuildFolder {
|
pub struct GuildFolder {
|
||||||
pub color: Option<u32>,
|
pub color: u32,
|
||||||
pub guild_ids: Vec<String>,
|
pub guild_ids: Vec<String>,
|
||||||
pub id: Option<Snowflake>,
|
pub id: u16,
|
||||||
pub name: Option<String>,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -32,13 +32,13 @@ use crate::types::{
|
||||||
pub struct Webhook {
|
pub struct Webhook {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub webhook_type: WebhookType,
|
pub webhook_type: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub avatar: String,
|
pub avatar: String,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
pub application_id: Option<Snowflake>,
|
pub application_id: Snowflake,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub user: Option<Shared<User>>,
|
pub user: Option<Shared<User>>,
|
||||||
|
@ -48,13 +48,3 @@ pub struct Webhook {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy)]
|
|
||||||
#[repr(u8)]
|
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
|
||||||
pub enum WebhookType {
|
|
||||||
#[default]
|
|
||||||
Incoming = 1,
|
|
||||||
ChannelFollower = 2,
|
|
||||||
Application = 3,
|
|
||||||
}
|
|
|
@ -20,9 +20,7 @@ pub struct UpdatePresence {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, WebSocketEvent)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, WebSocketEvent)]
|
||||||
/// Received to tell the client that a user updated their presence / status
|
/// Received to tell the client that a user updated their presence / status
|
||||||
///
|
|
||||||
/// See <https://discord.com/developers/docs/topics/gateway-events#presence-update-presence-update-event-fields>
|
/// See <https://discord.com/developers/docs/topics/gateway-events#presence-update-presence-update-event-fields>
|
||||||
/// (Same structure as <https://docs.discord.sex/resources/presence#presence-object>)
|
|
||||||
pub struct PresenceUpdate {
|
pub struct PresenceUpdate {
|
||||||
pub user: PublicUser,
|
pub user: PublicUser,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -6,14 +6,13 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::entities::{Guild, User};
|
use crate::types::entities::{Guild, User};
|
||||||
use crate::types::events::{Session, WebSocketEvent};
|
use crate::types::events::{Session, WebSocketEvent};
|
||||||
use crate::types::{Activity, Channel, ClientStatusObject, GuildMember, PresenceUpdate, Snowflake, VoiceState};
|
use crate::types::interfaces::ClientStatusObject;
|
||||||
|
use crate::types::{Activity, GuildMember, PresenceUpdate, VoiceState};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
|
||||||
/// 1/2 officially documented;
|
/// 1/2 half documented;
|
||||||
/// Received after identifying, provides initial user info;
|
/// Received after identifying, provides initial user info;
|
||||||
///
|
/// See <https://discord.com/developers/docs/topics/gateway-events#ready;>
|
||||||
/// See <https://docs.discord.sex/topics/gateway-events#ready> and <https://discord.com/developers/docs/topics/gateway-events#ready>
|
|
||||||
// TODO: There are a LOT of fields missing here
|
|
||||||
pub struct GatewayReady {
|
pub struct GatewayReady {
|
||||||
pub analytics_token: Option<String>,
|
pub analytics_token: Option<String>,
|
||||||
pub auth_session_id_hash: Option<String>,
|
pub auth_session_id_hash: Option<String>,
|
||||||
|
@ -33,47 +32,36 @@ pub struct GatewayReady {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
|
||||||
/// Officially Undocumented;
|
/// Officially Undocumented;
|
||||||
/// Sent after the READY event when a client is a user,
|
/// Sent after the READY event when a client is a user, seems to somehow add onto the ready event;
|
||||||
/// seems to somehow add onto the ready event;
|
|
||||||
///
|
|
||||||
/// See <https://docs.discord.sex/topics/gateway-events#ready-supplemental>
|
|
||||||
pub struct GatewayReadySupplemental {
|
pub struct GatewayReadySupplemental {
|
||||||
/// The presences of the user's relationships and guild presences sent at startup
|
|
||||||
pub merged_presences: MergedPresences,
|
pub merged_presences: MergedPresences,
|
||||||
pub merged_members: Vec<Vec<GuildMember>>,
|
pub merged_members: Vec<Vec<GuildMember>>,
|
||||||
pub lazy_private_channels: Vec<Channel>,
|
// ?
|
||||||
|
pub lazy_private_channels: Vec<serde_json::Value>,
|
||||||
pub guilds: Vec<SupplementalGuild>,
|
pub guilds: Vec<SupplementalGuild>,
|
||||||
// "Upcoming changes that the client should disclose to the user" (discord.sex)
|
// ? pomelo
|
||||||
pub disclose: Vec<String>,
|
pub disclose: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See <https://docs.discord.sex/topics/gateway-events#merged-presences-structure>
|
|
||||||
pub struct MergedPresences {
|
pub struct MergedPresences {
|
||||||
/// "Presences of the user's guilds in the same order as the guilds array in ready"
|
pub guilds: Vec<Vec<MergedPresenceGuild>>,
|
||||||
/// (discord.sex)
|
|
||||||
pub guilds: Vec<Vec<MergedPresenceGuild>>,
|
|
||||||
/// "Presences of the user's friends and implicit relationships" (discord.sex)
|
|
||||||
pub friends: Vec<MergedPresenceFriend>,
|
pub friends: Vec<MergedPresenceFriend>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// Not documented even unofficially
|
|
||||||
pub struct MergedPresenceFriend {
|
pub struct MergedPresenceFriend {
|
||||||
pub user_id: Snowflake,
|
pub user_id: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
// Looks like ms??
|
/// Looks like ms??
|
||||||
//
|
pub last_modified: u128,
|
||||||
// Not always sent
|
|
||||||
pub last_modified: Option<u128>,
|
|
||||||
pub client_status: ClientStatusObject,
|
pub client_status: ClientStatusObject,
|
||||||
pub activities: Vec<Activity>,
|
pub activities: Vec<Activity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// Not documented even unofficially
|
|
||||||
pub struct MergedPresenceGuild {
|
pub struct MergedPresenceGuild {
|
||||||
pub user_id: Snowflake,
|
pub user_id: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
// ?
|
// ?
|
||||||
pub game: Option<serde_json::Value>,
|
pub game: Option<serde_json::Value>,
|
||||||
|
@ -82,10 +70,8 @@ pub struct MergedPresenceGuild {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See <https://docs.discord.sex/topics/gateway-events#supplemental-guild-structure>
|
|
||||||
pub struct SupplementalGuild {
|
pub struct SupplementalGuild {
|
||||||
pub id: Snowflake,
|
|
||||||
pub voice_states: Option<Vec<VoiceState>>,
|
pub voice_states: Option<Vec<VoiceState>>,
|
||||||
/// Field not documented even unofficially
|
pub id: String,
|
||||||
pub embedded_activities: Vec<serde_json::Value>,
|
pub embedded_activities: Vec<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ bitflags! {
|
||||||
/// Bitflags of speaking types;
|
/// Bitflags of speaking types;
|
||||||
///
|
///
|
||||||
/// See <https://discord.com/developers/docs/topics/voice-connections#speaking>
|
/// See <https://discord.com/developers/docs/topics/voice-connections#speaking>
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub struct SpeakingBitflags: u64 {
|
pub struct SpeakingBitflags: u8 {
|
||||||
/// Whether we'll be transmitting normal voice audio
|
/// Whether we'll be transmitting normal voice audio
|
||||||
const MICROPHONE = 1 << 0;
|
const MICROPHONE = 1 << 0;
|
||||||
/// Whether we'll be transmitting context audio for video, no speaking indicator
|
/// Whether we'll be transmitting context audio for video, no speaking indicator
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use serde::de::Visitor;
|
||||||
|
|
||||||
use crate::types::{ChannelType, DefaultReaction, entities::PermissionOverwrite, Snowflake};
|
use crate::types::{ChannelType, DefaultReaction, Error, entities::PermissionOverwrite, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
|
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
@ -58,7 +59,7 @@ pub struct GetChannelMessagesSchema {
|
||||||
/// Between 1 and 100, defaults to 50.
|
/// Between 1 and 100, defaults to 50.
|
||||||
pub limit: Option<i32>,
|
pub limit: Option<i32>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub anchor: Option<ChannelMessagesAnchor>,
|
pub anchor: ChannelMessagesAnchor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
@ -73,21 +74,21 @@ impl GetChannelMessagesSchema {
|
||||||
pub fn before(anchor: Snowflake) -> Self {
|
pub fn before(anchor: Snowflake) -> Self {
|
||||||
Self {
|
Self {
|
||||||
limit: None,
|
limit: None,
|
||||||
anchor: Some(ChannelMessagesAnchor::Before(anchor)),
|
anchor: ChannelMessagesAnchor::Before(anchor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn around(anchor: Snowflake) -> Self {
|
pub fn around(anchor: Snowflake) -> Self {
|
||||||
Self {
|
Self {
|
||||||
limit: None,
|
limit: None,
|
||||||
anchor: Some(ChannelMessagesAnchor::Around(anchor)),
|
anchor: ChannelMessagesAnchor::Around(anchor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after(anchor: Snowflake) -> Self {
|
pub fn after(anchor: Snowflake) -> Self {
|
||||||
Self {
|
Self {
|
||||||
limit: None,
|
limit: None,
|
||||||
anchor: Some(ChannelMessagesAnchor::After(anchor)),
|
anchor: ChannelMessagesAnchor::After(anchor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,14 +131,63 @@ impl Default for CreateChannelInviteSchema {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
pub struct InviteFlags: u64 {
|
pub struct InviteFlags: u64 {
|
||||||
const GUEST = 1 << 0;
|
const GUEST = 1 << 0;
|
||||||
const VIEWED = 1 << 1;
|
const VIEWED = 1 << 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for InviteFlags {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
self.bits().to_string().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for InviteFlags {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
|
||||||
|
struct FlagsVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for FlagsVisitor
|
||||||
|
{
|
||||||
|
type Value = InviteFlags;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a raw u64 value of flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
|
||||||
|
InviteFlags::from_bits(v).ok_or(serde::de::Error::custom(Error::InvalidFlags(v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_u64(FlagsVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl sqlx::Type<sqlx::MySql> for InviteFlags {
|
||||||
|
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
|
||||||
|
u64::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'q> sqlx::Encode<'q, sqlx::MySql> for InviteFlags {
|
||||||
|
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
|
||||||
|
u64::encode_by_ref(&self.0.0, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'r> sqlx::Decode<'r, sqlx::MySql> for InviteFlags {
|
||||||
|
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'r>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
let raw = u64::decode(value)?;
|
||||||
|
|
||||||
|
Ok(Self::from_bits(raw).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
@ -176,15 +226,3 @@ pub struct ModifyChannelPositionsSchema {
|
||||||
pub lock_permissions: Option<bool>,
|
pub lock_permissions: Option<bool>,
|
||||||
pub parent_id: Option<Snowflake>,
|
pub parent_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://docs.discord.sex/resources/channel#follow-channel>
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
|
|
||||||
pub struct AddFollowingChannelSchema {
|
|
||||||
pub webhook_channel_id: Snowflake,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
|
|
||||||
pub struct CreateWebhookSchema {
|
|
||||||
pub name: String,
|
|
||||||
pub avatar: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
|
@ -157,7 +157,6 @@ pub struct ModifyGuildMemberSchema {
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
/// Represents the flags of a Guild Member.
|
/// Represents the flags of a Guild Member.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
|
||||||
/// Query parameters for the `Get Invite` route.
|
|
||||||
///
|
|
||||||
/// # Reference:
|
|
||||||
/// Read: <https://docs.discord.sex/resources/invite#query-string-params>
|
|
||||||
pub struct GetInvitesSchema {
|
|
||||||
pub with_counts: Option<bool>,
|
|
||||||
}
|
|
|
@ -7,13 +7,13 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::entities::{
|
use crate::types::entities::{
|
||||||
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
|
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
|
||||||
};
|
};
|
||||||
use crate::types::{Attachment, MessageFlags, MessageType, ReactionType, Snowflake};
|
use crate::types::{Attachment, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct MessageSendSchema {
|
pub struct MessageSendSchema {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub message_type: Option<MessageType>,
|
pub message_type: Option<i32>,
|
||||||
pub content: Option<String>,
|
pub content: Option<String>,
|
||||||
pub nonce: Option<String>,
|
pub nonce: Option<String>,
|
||||||
pub tts: Option<bool>,
|
pub tts: Option<bool>,
|
||||||
|
@ -118,21 +118,13 @@ pub struct MessageAck {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
||||||
pub struct MessageModifySchema {
|
pub struct MessageModifySchema {
|
||||||
pub content: Option<String>,
|
content: Option<String>,
|
||||||
pub embeds: Option<Vec<Embed>>,
|
embeds: Option<Vec<Embed>>,
|
||||||
pub embed: Option<Embed>,
|
embed: Option<Embed>,
|
||||||
pub allowed_mentions: Option<AllowedMention>,
|
allowed_mentions: Option<AllowedMention>,
|
||||||
pub components: Option<Vec<Component>>,
|
components: Option<Vec<Component>>,
|
||||||
pub flags: Option<MessageFlags>,
|
flags: Option<i32>,
|
||||||
pub files: Option<Vec<u8>>,
|
files: Option<Vec<u8>>,
|
||||||
pub payload_json: Option<String>,
|
payload_json: Option<String>,
|
||||||
pub attachments: Option<Vec<Attachment>>,
|
attachments: Option<Vec<Attachment>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
|
||||||
pub struct ReactionQuerySchema {
|
|
||||||
pub after: Option<Snowflake>,
|
|
||||||
pub limit: Option<u32>,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub reaction_type: Option<ReactionType>
|
|
||||||
}
|
|
|
@ -10,7 +10,6 @@ pub use message::*;
|
||||||
pub use relationship::*;
|
pub use relationship::*;
|
||||||
pub use role::*;
|
pub use role::*;
|
||||||
pub use user::*;
|
pub use user::*;
|
||||||
pub use invites::*;
|
|
||||||
|
|
||||||
mod apierror;
|
mod apierror;
|
||||||
mod auth;
|
mod auth;
|
||||||
|
@ -20,4 +19,3 @@ mod message;
|
||||||
mod relationship;
|
mod relationship;
|
||||||
mod role;
|
mod role;
|
||||||
mod user;
|
mod user;
|
||||||
mod invites;
|
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::types::UserFlags;
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
use sqlx::{{Decode, Encode, MySql}, database::{HasArguments, HasValueRef}, encode::IsNull, error::BoxDynError, mysql::MySqlValueRef};
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Rights are instance-wide, per-user permissions for everything you may perform on the instance,
|
/// Rights are instance-wide, per-user permissions for everything you may perform on the instance,
|
||||||
|
@ -18,8 +18,7 @@ bitflags! {
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://docs.spacebar.chat/setup/server/security/rights/>
|
/// See <https://docs.spacebar.chat/setup/server/security/rights/>
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, chorus_macros::SerdeBitFlags)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
|
|
||||||
pub struct Rights: u64 {
|
pub struct Rights: u64 {
|
||||||
/// All rights
|
/// All rights
|
||||||
const OPERATOR = 1 << 0;
|
const OPERATOR = 1 << 0;
|
||||||
|
@ -133,6 +132,33 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl sqlx::Type<MySql> for Rights {
|
||||||
|
fn type_info() -> <sqlx::MySql as sqlx::Database>::TypeInfo {
|
||||||
|
u64::type_info()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compatible(ty: &<sqlx::MySql as sqlx::Database>::TypeInfo) -> bool {
|
||||||
|
u64::compatible(ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'q> Encode<'q, MySql> for Rights {
|
||||||
|
fn encode_by_ref(&self, buf: &mut <MySql as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||||
|
<u64 as Encode<MySql>>::encode_by_ref(&self.0.0, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'r> Decode<'r, MySql> for Rights {
|
||||||
|
fn decode(value: <MySql as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
||||||
|
let raw = <u64 as Decode<MySql>>::decode(value)?;
|
||||||
|
Ok(Rights::from_bits(raw).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Rights {
|
impl Rights {
|
||||||
pub fn any(&self, permission: Rights, check_operator: bool) -> bool {
|
pub fn any(&self, permission: Rights, check_operator: bool) -> bool {
|
||||||
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)
|
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use chrono::{LocalResult, NaiveDateTime};
|
use chrono::{LocalResult, NaiveDateTime};
|
||||||
use serde::{de, Deserialize, Deserializer};
|
use serde::de;
|
||||||
use serde::de::Error;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -260,19 +259,4 @@ pub(crate) fn serde_from<T, E, V>(me: LocalResult<T>, _ts: &V) -> Result<T, E>
|
||||||
}
|
}
|
||||||
LocalResult::Single(val) => Ok(val),
|
LocalResult::Single(val) => Ok(val),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum StringOrU64 {
|
|
||||||
String(String),
|
|
||||||
U64(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string_or_u64<'de, D>(d: D) -> Result<u64, D::Error>
|
|
||||||
where D: Deserializer<'de> {
|
|
||||||
match StringOrU64::deserialize(d)? {
|
|
||||||
StringOrU64::String(s) => s.parse::<u64>().map_err(D::Error::custom),
|
|
||||||
StringOrU64::U64(u) => Ok(u)
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -8,6 +8,8 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
use sqlx::Type;
|
||||||
|
|
||||||
/// 2015-01-01
|
/// 2015-01-01
|
||||||
const EPOCH: i64 = 1420070400000;
|
const EPOCH: i64 = 1420070400000;
|
||||||
|
@ -17,6 +19,8 @@ const EPOCH: i64 = 1420070400000;
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord.com/developers/docs/reference#snowflakes>
|
/// See <https://discord.com/developers/docs/reference#snowflakes>
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
|
||||||
pub struct Snowflake(pub u64);
|
pub struct Snowflake(pub u64);
|
||||||
|
|
||||||
impl Snowflake {
|
impl Snowflake {
|
||||||
|
@ -98,27 +102,6 @@ impl<'de> serde::Deserialize<'de> for Snowflake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl sqlx::Type<sqlx::MySql> for Snowflake {
|
|
||||||
fn type_info() -> <sqlx::MySql as sqlx::Database>::TypeInfo {
|
|
||||||
<String as sqlx::Type<sqlx::MySql>>::type_info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl<'q> sqlx::Encode<'q, sqlx::MySql> for Snowflake {
|
|
||||||
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
|
|
||||||
<String as sqlx::Encode<'q, sqlx::MySql>>::encode_by_ref(&self.0.to_string(), buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl<'d> sqlx::Decode<'d, sqlx::MySql> for Snowflake {
|
|
||||||
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'d>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
|
|
||||||
<String as sqlx::Decode<'d, sqlx::MySql>>::decode(value).map(|s| s.parse::<u64>().map(Snowflake).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
|
@ -23,4 +23,3 @@ impl From<WsMessage> for VoiceGatewayMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl VoiceGateway {
|
||||||
pub async fn spawn(websocket_url: String) -> Result<VoiceGatewayHandle, VoiceGatewayError> {
|
pub async fn spawn(websocket_url: String) -> Result<VoiceGatewayHandle, VoiceGatewayError> {
|
||||||
// Append the needed things to the websocket url
|
// Append the needed things to the websocket url
|
||||||
let processed_url = format!("wss://{}/?v=7", websocket_url);
|
let processed_url = format!("wss://{}/?v=7", websocket_url);
|
||||||
trace!("VGW: Connecting to {}", processed_url.clone());
|
trace!("Created voice socket url: {}", processed_url.clone());
|
||||||
|
|
||||||
let (websocket_send, mut websocket_receive) =
|
let (websocket_send, mut websocket_receive) =
|
||||||
match WebSocketBackend::connect(&processed_url).await {
|
match WebSocketBackend::connect(&processed_url).await {
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use chorus::types::{self, Channel, GetChannelMessagesSchema, MessageSendSchema, PermissionFlags, PermissionOverwrite, PermissionOverwriteType, PrivateChannelCreateSchema, RelationshipType, Snowflake};
|
use chorus::types::{
|
||||||
|
self, Channel, GetChannelMessagesSchema, MessageSendSchema, PermissionFlags,
|
||||||
|
PermissionOverwrite, PrivateChannelCreateSchema, RelationshipType, Snowflake,
|
||||||
|
};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
@ -66,13 +69,16 @@ async fn modify_channel() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(modified_channel.name, Some(CHANNEL_NAME.to_string()));
|
assert_eq!(modified_channel.name, Some(CHANNEL_NAME.to_string()));
|
||||||
|
|
||||||
let permission_override = PermissionFlags::MANAGE_CHANNELS | PermissionFlags::MANAGE_MESSAGES;
|
let permission_override = PermissionFlags::from_vec(Vec::from([
|
||||||
|
PermissionFlags::MANAGE_CHANNELS,
|
||||||
|
PermissionFlags::MANAGE_MESSAGES,
|
||||||
|
]));
|
||||||
let user_id: types::Snowflake = bundle.user.object.read().unwrap().id;
|
let user_id: types::Snowflake = bundle.user.object.read().unwrap().id;
|
||||||
let permission_override = PermissionOverwrite {
|
let permission_override = PermissionOverwrite {
|
||||||
id: user_id,
|
id: user_id,
|
||||||
overwrite_type: PermissionOverwriteType::Member,
|
overwrite_type: "1".to_string(),
|
||||||
allow: permission_override,
|
allow: permission_override,
|
||||||
deny: PermissionFlags::empty(),
|
deny: "0".to_string(),
|
||||||
};
|
};
|
||||||
let channel_id: Snowflake = bundle.channel.read().unwrap().id;
|
let channel_id: Snowflake = bundle.channel.read().unwrap().id;
|
||||||
Channel::modify_permissions(
|
Channel::modify_permissions(
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chorus::gateway::{Gateway, GatewayOptions};
|
use chorus::gateway::Gateway;
|
||||||
use chorus::types::IntoShared;
|
use chorus::types::IntoShared;
|
||||||
use chorus::{
|
use chorus::{
|
||||||
instance::{ChorusUser, Instance},
|
instance::{ChorusUser, Instance},
|
||||||
|
@ -50,7 +50,7 @@ impl TestBundle {
|
||||||
limits: self.user.limits.clone(),
|
limits: self.user.limits.clone(),
|
||||||
settings: self.user.settings.clone(),
|
settings: self.user.settings.clone(),
|
||||||
object: self.user.object.clone(),
|
object: self.user.object.clone(),
|
||||||
gateway: Gateway::spawn(self.instance.urls.wss.clone(), GatewayOptions::default())
|
gateway: Gateway::spawn(self.instance.urls.wss.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}
|
}
|
||||||
|
@ -59,10 +59,6 @@ impl TestBundle {
|
||||||
|
|
||||||
// Set up a test by creating an Instance and a User. Reduces Test boilerplate.
|
// Set up a test by creating an Instance and a User. Reduces Test boilerplate.
|
||||||
pub(crate) async fn setup() -> TestBundle {
|
pub(crate) async fn setup() -> TestBundle {
|
||||||
|
|
||||||
// So we can get logs when tests fail
|
|
||||||
let _ = simple_logger::SimpleLogger::with_level(simple_logger::SimpleLogger::new(), log::LevelFilter::Debug).init();
|
|
||||||
|
|
||||||
let instance = Instance::new("http://localhost:3001/api").await.unwrap();
|
let instance = Instance::new("http://localhost:3001/api").await.unwrap();
|
||||||
// Requires the existence of the below user.
|
// Requires the existence of the below user.
|
||||||
let reg = RegisterSchema {
|
let reg = RegisterSchema {
|
||||||
|
@ -123,7 +119,7 @@ pub(crate) async fn setup() -> TestBundle {
|
||||||
let urls = UrlBundle::new(
|
let urls = UrlBundle::new(
|
||||||
"http://localhost:3001/api".to_string(),
|
"http://localhost:3001/api".to_string(),
|
||||||
"http://localhost:3001/api".to_string(),
|
"http://localhost:3001/api".to_string(),
|
||||||
"ws://localhost:3001/".to_string(),
|
"ws://localhost:3001".to_string(),
|
||||||
"http://localhost:3001".to_string(),
|
"http://localhost:3001".to_string(),
|
||||||
);
|
);
|
||||||
TestBundle {
|
TestBundle {
|
||||||
|
|
|
@ -30,7 +30,7 @@ use wasmtimer::tokio::sleep;
|
||||||
async fn test_gateway_establish() {
|
async fn test_gateway_establish() {
|
||||||
let bundle = common::setup().await;
|
let bundle = common::setup().await;
|
||||||
|
|
||||||
let _: GatewayHandle = Gateway::spawn(bundle.urls.wss.clone(), GatewayOptions::default()).await.unwrap();
|
let _: GatewayHandle = Gateway::spawn(bundle.urls.wss.clone()).await.unwrap();
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ impl Observer<GatewayReady> for GatewayReadyObserver {
|
||||||
async fn test_gateway_authenticate() {
|
async fn test_gateway_authenticate() {
|
||||||
let bundle = common::setup().await;
|
let bundle = common::setup().await;
|
||||||
|
|
||||||
let gateway: GatewayHandle = Gateway::spawn(bundle.urls.wss.clone(), GatewayOptions::default()).await.unwrap();
|
let gateway: GatewayHandle = Gateway::spawn(bundle.urls.wss.clone()).await.unwrap();
|
||||||
|
|
||||||
let (ready_send, mut ready_receive) = tokio::sync::mpsc::channel(1);
|
let (ready_send, mut ready_receive) = tokio::sync::mpsc::channel(1);
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ async fn test_gateway_authenticate() {
|
||||||
println!("Timed out waiting for event, failing..");
|
println!("Timed out waiting for event, failing..");
|
||||||
assert!(false);
|
assert!(false);
|
||||||
}
|
}
|
||||||
// Success, we have received it
|
// Sucess, we have received it
|
||||||
Some(_) = ready_receive.recv() => {}
|
Some(_) = ready_receive.recv() => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -920,7 +920,7 @@ mod entities {
|
||||||
.clone()
|
.clone()
|
||||||
);
|
);
|
||||||
let flags = ApplicationFlags::from_bits(0).unwrap();
|
let flags = ApplicationFlags::from_bits(0).unwrap();
|
||||||
assert!(application.flags == flags);
|
assert!(application.flags() == flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
|
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
|
||||||
|
|
Loading…
Reference in New Issue