Compare commits

..

No commits in common. "f8fe1c404bf08b2ef97ae04b16d8dd7cd522c438" and "de10b9374f5bb28987310df1dbdec04a81c1ef13" have entirely different histories.

45 changed files with 229 additions and 903 deletions

View File

@ -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

35
Cargo.lock generated
View File

@ -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"

View File

@ -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 }

View File

@ -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",

View File

@ -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."

View File

@ -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()
}

View File

@ -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 {};

View File

@ -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"))]
@ -27,14 +27,8 @@ use wasmtimer::tokio::sleep;
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

View File

@ -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()),
}
}
}

View File

@ -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),
}
}
}

View File

@ -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;
} }

View File

@ -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)
}
} }

View File

@ -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};

View File

@ -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")
}
}
}

View File

@ -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(),

View File

@ -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) => {

View File

@ -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,
} }

View File

@ -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 {

View File

@ -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
}

View File

@ -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,
}
}
}

View File

@ -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 {

View File

@ -5,7 +5,7 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{GuildMemberFlags, Shared}; use crate::types::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>>,

View File

@ -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 {

View File

@ -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
}

View File

@ -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:

View File

@ -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;

View File

@ -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)]

View File

@ -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,
}

View File

@ -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)]

View File

@ -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"
/// (discord.sex)
pub guilds: Vec<Vec<MergedPresenceGuild>>, 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>,
} }

View File

@ -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

View File

@ -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>,
}

View File

@ -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:

View File

@ -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>,
}

View File

@ -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>
} }

View File

@ -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;

View File

@ -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)

View File

@ -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)]
@ -261,18 +260,3 @@ 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)
}
}

View File

@ -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};

View File

@ -23,4 +23,3 @@ impl From<WsMessage> for VoiceGatewayMessage {
} }
} }
} }

View File

@ -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 {

View File

@ -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(

View File

@ -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 {

View File

@ -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() => {}
}; };

View File

@ -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)]