Merge pull request #79 from polyphony-chat/perpetual/gateway-dev

Some gateway updates
This commit is contained in:
kozabrada123 2023-05-29 17:34:46 +02:00 committed by GitHub
commit b668d4f2db
16 changed files with 486 additions and 216 deletions

View File

@ -0,0 +1,58 @@
use chorus::{
self,
gateway::{Gateway, Observer},
types::{GatewayIdentifyPayload, GatewayReady},
};
use std::sync::Arc;
use tokio::{self, sync::Mutex};
// This example creates a simple gateway connection and a basic observer struct
// Due to certain limitations all observers must impl debug
#[derive(Debug)]
pub struct ExampleObserver {}
// This struct can observe GatewayReady events when subscribed, because it implements the trait Observer<GatewayReady>.
// The Observer trait can be implemented for a struct for a given websocketevent to handle observing it
// One struct can be an observer of multiple websocketevents, if needed
impl Observer<GatewayReady> for ExampleObserver {
// After we subscribe to an event this function is called every time we receive it
fn update(&self, data: &GatewayReady) {
println!("Observed Ready!");
}
}
#[tokio::main]
async fn main() {
// Find the gateway websocket url of the server we want to connect to
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
// Initiate the gateway connection
let gateway = Gateway::new(websocket_url_spacebar).await.unwrap();
// Create an instance of our observer
let observer = ExampleObserver {};
// Because observers have to reside in between the main and gateway thread, (they have to be shared between both) we need to put them in an Arc<Mutex>
let shared_observer = Arc::new(Mutex::new(observer));
// Subscribe our observer to the Ready event on this gateway
// From now on observer.update(data) will be called every time we receive the Ready event
gateway
.events
.lock()
.await
.session
.ready
.subscribe(shared_observer)
.unwrap();
// Authenticate so we will receive any events
let token = "SecretToken".to_string();
let mut identify = GatewayIdentifyPayload::common();
identify.token = token;
gateway.send_identify(identify).await;
// Do something on the main thread so we don't quit
loop {}
}

View File

@ -0,0 +1,31 @@
use chorus::{self, gateway::Gateway, types::GatewayIdentifyPayload};
use tokio;
/// This example creates a simple gateway connection and a session with an Identify event
#[tokio::main]
async fn main() {
// Find the gateway websocket url of the server we want to connect to
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
let gateway = Gateway::new(websocket_url_spacebar).await.unwrap();
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
// Get a token for an account on the server
let token = "SecretToken".to_string();
// Create an identify event
// An Identify event is how the server authenticates us and gets info about our os and browser, along with our intents / capabilities
// (Chorus never sends real telemetry data about your system to servers, always just using the most common option or a custom set one)
// By default the capabilities requests all the data of a regular client
let mut identify = GatewayIdentifyPayload::common();
identify.token = token;
// Send off the event
gateway.send_identify(identify).await;
// Do something on the main thread so we don't quit
loop {}
}

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ use crate::types::{
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Channel { pub struct Channel {
pub id: Snowflake, pub id: Snowflake,
pub created_at: chrono::DateTime<Utc>, pub created_at: Option<chrono::DateTime<Utc>>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub channel_type: ChannelType, pub channel_type: ChannelType,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,

View File

@ -19,7 +19,7 @@ pub struct Guild {
pub splash: Option<String>, pub splash: Option<String>,
pub discovery_splash: Option<String>, pub discovery_splash: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub owner: bool, // True if requesting user is owner pub owner: Option<bool>, // True if requesting user is owner
pub owner_id: Option<Snowflake>, pub owner_id: Option<Snowflake>,
pub permissions: Option<String>, pub permissions: Option<String>,
pub afk_channel_id: Option<Snowflake>, pub afk_channel_id: Option<Snowflake>,

View File

@ -1,10 +1,10 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::entities::User; use crate::types::entities::PublicUser;
#[derive(Debug, Deserialize, Default, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Default, Serialize, Clone, PartialEq, Eq)]
pub struct GuildMember { pub struct GuildMember {
pub user: Option<User>, pub user: Option<PublicUser>,
pub nick: Option<String>, pub nick: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
pub roles: Vec<String>, pub roles: Vec<String>,

View File

@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize};
use crate::types::{ use crate::types::{
entities::{ entities::{
Application, Attachment, Channel, Emoji, GuildMember, RoleSubscriptionData, Sticker, Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData,
StickerItem, User, Sticker, StickerItem, User,
}, },
utils::Snowflake, utils::Snowflake,
}; };
@ -14,7 +14,7 @@ pub struct Message {
pub id: Snowflake, pub id: Snowflake,
pub channel_id: Snowflake, pub channel_id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub author: User, pub author: PublicUser,
pub content: String, pub content: String,
pub timestamp: String, pub timestamp: String,
pub edited_timestamp: Option<String>, pub edited_timestamp: Option<String>,

View File

@ -38,11 +38,11 @@ pub struct User {
#[serde(deserialize_with = "deserialize_option_number_from_string")] #[serde(deserialize_with = "deserialize_option_number_from_string")]
flags: Option<i32>, flags: Option<i32>,
pub premium_since: Option<DateTime<Utc>>, pub premium_since: Option<DateTime<Utc>>,
pub premium_type: u8, pub premium_type: Option<u8>,
pub pronouns: Option<String>, pub pronouns: Option<String>,
pub public_flags: Option<u16>, pub public_flags: Option<u32>,
pub banner: Option<String>, pub banner: Option<String>,
pub bio: String, pub bio: Option<String>,
pub theme_colors: Option<Vec<u8>>, pub theme_colors: Option<Vec<u8>>,
pub phone: Option<String>, pub phone: Option<String>,
pub nsfw_allowed: bool, pub nsfw_allowed: bool,
@ -52,29 +52,29 @@ pub struct User {
pub disabled: Option<bool>, pub disabled: Option<bool>,
} }
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PublicUser { pub struct PublicUser {
pub id: Snowflake, pub id: Snowflake,
pub username: String, pub username: Option<String>,
pub discriminator: String, pub discriminator: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
pub accent_color: Option<u8>, pub accent_color: Option<u8>,
pub banner: Option<String>, pub banner: Option<String>,
pub theme_colors: Option<Vec<u8>>, pub theme_colors: Option<Vec<u8>>,
pub pronouns: Option<String>, pub pronouns: Option<String>,
pub bot: Option<bool>, pub bot: Option<bool>,
pub bio: String, pub bio: Option<String>,
pub premium_type: u8, pub premium_type: Option<u8>,
pub premium_since: Option<DateTime<Utc>>, pub premium_since: Option<DateTime<Utc>>,
pub public_flags: Option<u16>, pub public_flags: Option<u32>,
} }
impl From<User> for PublicUser { impl From<User> for PublicUser {
fn from(value: User) -> Self { fn from(value: User) -> Self {
Self { Self {
id: value.id, id: value.id,
username: value.username, username: Some(value.username),
discriminator: value.discriminator, discriminator: Some(value.discriminator),
avatar: value.avatar, avatar: value.avatar,
accent_color: value.accent_color, accent_color: value.accent_color,
banner: value.banner, banner: value.banner,

View File

@ -12,7 +12,7 @@ use crate::types::{
pub struct VoiceState { pub struct VoiceState {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
pub guild: Option<Guild>, pub guild: Option<Guild>,
pub channel_id: Snowflake, pub channel_id: Option<Snowflake>,
pub user_id: Snowflake, pub user_id: Snowflake,
pub member: Option<GuildMember>, pub member: Option<GuildMember>,
pub session_id: Snowflake, pub session_id: Snowflake,

View File

@ -1,4 +1,4 @@
use crate::types::entities::{Guild, UnavailableGuild, User}; use crate::types::entities::{Guild, PublicUser, UnavailableGuild};
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::{AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, RoleObject, Sticker}; use crate::types::{AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, RoleObject, Sticker};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -32,7 +32,7 @@ impl WebSocketEvent for GuildCreate {}
/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields /// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields
pub struct GuildBanAdd { pub struct GuildBanAdd {
pub guild_id: String, pub guild_id: String,
pub user: User, pub user: PublicUser,
} }
impl WebSocketEvent for GuildBanAdd {} impl WebSocketEvent for GuildBanAdd {}
@ -41,7 +41,7 @@ impl WebSocketEvent for GuildBanAdd {}
/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove /// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove
pub struct GuildBanRemove { pub struct GuildBanRemove {
pub guild_id: String, pub guild_id: String,
pub user: User, pub user: PublicUser,
} }
impl WebSocketEvent for GuildBanRemove {} impl WebSocketEvent for GuildBanRemove {}
@ -113,7 +113,7 @@ impl WebSocketEvent for GuildMemberAdd {}
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-remove /// See https://discord.com/developers/docs/topics/gateway-events#guild-member-remove
pub struct GuildMemberRemove { pub struct GuildMemberRemove {
pub guild_id: String, pub guild_id: String,
pub user: User, pub user: PublicUser,
} }
impl WebSocketEvent for GuildMemberRemove {} impl WebSocketEvent for GuildMemberRemove {}
@ -123,7 +123,7 @@ impl WebSocketEvent for GuildMemberRemove {}
pub struct GuildMemberUpdate { pub struct GuildMemberUpdate {
pub guild_id: String, pub guild_id: String,
pub roles: Vec<String>, pub roles: Vec<String>,
pub user: User, pub user: PublicUser,
pub nick: Option<String>, pub nick: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
pub joined_at: Option<DateTime<Utc>>, pub joined_at: Option<DateTime<Utc>>,

View File

@ -1,7 +1,7 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::entities::{Emoji, GuildMember, Message, User}; use crate::types::entities::{Emoji, GuildMember, Message, PublicUser};
use super::WebSocketEvent; use super::WebSocketEvent;
@ -29,31 +29,9 @@ pub struct MessageCreate {
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields /// See https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields
pub struct MessageCreateUser { pub struct MessageCreateUser {
pub id: String, #[serde(flatten)]
username: String, user: PublicUser,
discriminator: String, member: Option<GuildMember>,
avatar: Option<String>,
bot: Option<bool>,
system: Option<bool>,
mfa_enabled: Option<bool>,
accent_color: Option<String>,
locale: Option<String>,
verified: Option<bool>,
email: Option<String>,
premium_since: Option<String>,
premium_type: Option<i8>,
pronouns: Option<String>,
public_flags: Option<i32>,
banner: Option<String>,
bio: Option<String>,
theme_colors: Option<Vec<i32>>,
phone: Option<String>,
nsfw_allowed: Option<bool>,
premium: Option<bool>,
purchased_flags: Option<i32>,
premium_usage_flags: Option<i32>,
disabled: Option<bool>,
member: GuildMember,
} }
impl WebSocketEvent for MessageCreate {} impl WebSocketEvent for MessageCreate {}
@ -64,7 +42,7 @@ pub struct MessageUpdate {
message: Message, message: Message,
guild_id: Option<String>, guild_id: Option<String>,
member: Option<GuildMember>, member: Option<GuildMember>,
mentions: Option<Vec<(User, GuildMember)>>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create mentions: Option<Vec<MessageCreateUser>>,
} }
impl WebSocketEvent for MessageUpdate {} impl WebSocketEvent for MessageUpdate {}
@ -142,7 +120,9 @@ pub struct MessageACK {
/// ? /// ?
pub version: u16, pub version: u16,
pub message_id: String, pub message_id: String,
pub last_viewed: Option<DateTime<Utc>>, /// This is an integer???
/// Not even unix, see '3070'???
pub last_viewed: Option<u64>,
/// What flags? /// What flags?
pub flags: Option<serde_json::Value>, pub flags: Option<serde_json::Value>,
pub channel_id: String, pub channel_id: String,

View File

@ -60,11 +60,16 @@ pub trait WebSocketEvent {}
/// Similar to [GatewayReceivePayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue] /// Similar to [GatewayReceivePayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
/// Also, we never need to send the event name /// Also, we never need to send the event name
pub struct GatewaySendPayload { pub struct GatewaySendPayload {
pub op: u8, #[serde(rename = "op")]
pub op_code: u8,
#[serde(rename = "d")]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub d: Option<serde_json::Value>, pub event_data: Option<serde_json::Value>,
#[serde(rename = "s")]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub s: Option<u64>, pub sequence_number: Option<u64>,
} }
impl WebSocketEvent for GatewaySendPayload {} impl WebSocketEvent for GatewaySendPayload {}
@ -76,11 +81,18 @@ impl WebSocketEvent for GatewaySendPayload {}
/// Also, we never need to sent the event name /// Also, we never need to sent the event name
pub struct GatewayReceivePayload<'a> { pub struct GatewayReceivePayload<'a> {
pub op: u8, #[serde(rename = "op")]
pub op_code: u8,
#[serde(borrow)] #[serde(borrow)]
pub d: Option<&'a serde_json::value::RawValue>, #[serde(rename = "d")]
pub s: Option<u64>, pub event_data: Option<&'a serde_json::value::RawValue>,
pub t: Option<String>,
#[serde(rename = "s")]
pub sequence_number: Option<u64>,
#[serde(rename = "t")]
pub event_name: Option<String>,
} }
impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {} impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {}

View File

@ -9,7 +9,7 @@ use crate::types::{GuildMember, VoiceState};
/// Seems to be passively set to update the client on guild details (though, why not just send the update events?) /// Seems to be passively set to update the client on guild details (though, why not just send the update events?)
pub struct PassiveUpdateV1 { pub struct PassiveUpdateV1 {
pub voice_states: Vec<VoiceState>, pub voice_states: Vec<VoiceState>,
pub members: Vec<GuildMember>, pub members: Option<Vec<GuildMember>>,
pub guild_id: String, pub guild_id: String,
pub channels: Vec<ChannelUnreadUpdateObject>, pub channels: Vec<ChannelUnreadUpdateObject>,
} }

View File

@ -36,7 +36,7 @@ pub struct GatewayReadySupplemental {
pub merged_members: Vec<Vec<GuildMember>>, pub merged_members: Vec<Vec<GuildMember>>,
// ? // ?
pub lazy_private_channels: Vec<serde_json::Value>, pub lazy_private_channels: Vec<serde_json::Value>,
pub guilds: Vec<SupplimentalGuild>, pub guilds: Vec<SupplementalGuild>,
// ? pomelo // ? pomelo
pub disclose: Vec<String>, pub disclose: Vec<String>,
} }
@ -70,7 +70,7 @@ pub struct MergedPresenceGuild {
} }
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default)]
pub struct SupplimentalGuild { pub struct SupplementalGuild {
pub voice_states: Option<Vec<VoiceState>>, pub voice_states: Option<Vec<VoiceState>>,
pub id: String, pub id: String,
pub embedded_activities: Vec<serde_json::Value>, pub embedded_activities: Vec<serde_json::Value>,

View File

@ -35,7 +35,7 @@ pub struct ThreadListSync {
pub guild_id: String, pub guild_id: String,
pub channel_ids: Option<Vec<String>>, pub channel_ids: Option<Vec<String>>,
pub threads: Vec<Channel>, pub threads: Vec<Channel>,
pub members: Vec<ThreadMember>, pub members: Option<Vec<ThreadMember>>,
} }
impl WebSocketEvent for ThreadListSync {} impl WebSocketEvent for ThreadListSync {}

View File

@ -1,12 +1,54 @@
use crate::types::entities::User; use crate::types::entities::PublicUser;
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::utils::Snowflake;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#user-update /// See https://discord.com/developers/docs/topics/gateway-events#user-update
pub struct UserUpdate { pub struct UserUpdate {
#[serde(flatten)] #[serde(flatten)]
pub user: User, pub user: PublicUser,
} }
impl WebSocketEvent for UserUpdate {} impl WebSocketEvent for UserUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// Undocumented
///
/// Possibly an update for muted guild / channel settings for the current user
///
/// {"version":2,"suppress_roles":false,"suppress_everyone":false,"notify_highlights":0,"muted":false,"mute_scheduled_events":false,"mute_config":null,"mobile_push":true,"message_notifications":1,"hide_muted_channels":false,"guild_id":"848582562217590824","flags":0,"channel_overrides":[{"muted":false,"mute_config":null,"message_notifications":3,"flags":4096,"collapsed":false,"channel_id":"1042689182893604885"}]}
pub struct UserGuildSettingsUpdate {
pub version: u8,
pub suppress_roles: bool,
pub suppress_everyone: bool,
pub notify_highlights: u8,
pub muted: bool,
pub mute_scheduled_events: bool,
/// ??
pub mute_config: Option<serde_json::Value>,
pub mobile_push: bool,
pub message_notifications: u8,
pub hide_muted_channels: bool,
pub guild_id: Snowflake,
pub flags: i32,
pub channel_overrides: Vec<UserGuildSettingsChannelOverride>,
}
impl WebSocketEvent for UserGuildSettingsUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// Undocumented
///
/// Received in [UserGuildSettingsUpdate]
///
/// {"muted":false,"mute_config":null,"message_notifications":3,"flags":4096,"collapsed":false,"channel_id":"1042689182893604885"}
pub struct UserGuildSettingsChannelOverride {
pub muted: bool,
/// ??
pub mute_config: Option<serde_json::Value>,
pub message_notifications: u8,
pub flags: i32,
pub collapsed: bool,
pub channel_id: Snowflake,
}