use crate::client::bridge::gateway::{ChunkGuildFilter, GatewayIntents};
use crate::constants::{self, OpCode};
use crate::gateway::{CurrentPresence, WsStream};
use crate::internal::prelude::*;
use crate::internal::ws_impl::SenderExt;
use crate::model::id::GuildId;
use async_trait::async_trait;
use serde_json::json;
use std::env::consts;
use std::time::SystemTime;
use tracing::instrument;
use tracing::{debug, trace};
#[async_trait]
pub trait WebSocketGatewayClientExt {
async fn send_chunk_guild(
&mut self,
guild_id: GuildId,
shard_info: &[u64; 2],
limit: Option<u16>,
filter: ChunkGuildFilter,
nonce: Option<&str>,
) -> Result<()>;
async fn send_heartbeat(&mut self, shard_info: &[u64; 2], seq: Option<u64>)
-> Result<()>;
async fn send_identify(&mut self, shard_info: &[u64; 2], token: &str, guild_subscriptions: bool, intents: Option<GatewayIntents>)
-> Result<()>;
async fn send_presence_update(
&mut self,
shard_info: &[u64; 2],
current_presence: &CurrentPresence,
) -> Result<()>;
async fn send_resume(
&mut self,
shard_info: &[u64; 2],
session_id: &str,
seq: u64,
token: &str,
) -> Result<()>;
}
#[async_trait]
impl WebSocketGatewayClientExt for WsStream {
#[instrument(skip(self))]
async fn send_chunk_guild(
&mut self,
guild_id: GuildId,
shard_info: &[u64; 2],
limit: Option<u16>,
filter: ChunkGuildFilter,
nonce: Option<&str>,
) -> Result<()> {
debug!("[Shard {:?}] Requesting member chunks", shard_info);
let mut payload = json!({
"op": OpCode::GetGuildMembers.num(),
"d": {
"guild_id": [guild_id.as_ref().0],
"limit": limit.unwrap_or(0),
"nonce": nonce.unwrap_or(""),
},
});
match filter {
ChunkGuildFilter::None => {},
ChunkGuildFilter::Query(query) => payload["d"]["query"] = json!(query),
ChunkGuildFilter::UserIds(user_ids) => {
let ids = user_ids.iter().map(|x| x.as_ref().0).collect::<Vec<u64>>();
payload["d"]["user_ids"] = json!(ids)
},
};
self.send_json(&payload).await.map_err(From::from)
}
#[instrument(skip(self))]
async fn send_heartbeat(&mut self, shard_info: &[u64; 2], seq: Option<u64>)
-> Result<()> {
trace!("[Shard {:?}] Sending heartbeat d: {:?}", shard_info, seq);
self.send_json(&json!({
"d": seq,
"op": OpCode::Heartbeat.num(),
})).await.map_err(From::from)
}
#[instrument(skip(self, token))]
async fn send_identify(&mut self, shard_info: &[u64; 2], token: &str, guild_subscriptions: bool, intents: Option<GatewayIntents>)
-> Result<()> {
debug!("[Shard {:?}] Identifying", shard_info);
self.send_json(&json!({
"op": OpCode::Identify.num(),
"d": {
"compress": true,
"large_threshold": constants::LARGE_THRESHOLD,
"guild_subscriptions": guild_subscriptions,
"shard": shard_info,
"token": token,
"intents": intents,
"v": constants::GATEWAY_VERSION,
"properties": {
"$browser": "serenity",
"$device": "serenity",
"$os": consts::OS,
},
},
})).await
}
#[instrument(skip(self))]
async fn send_presence_update(
&mut self,
shard_info: &[u64; 2],
current_presence: &CurrentPresence,
) -> Result<()> {
let &(ref activity, ref status) = current_presence;
let now = SystemTime::now();
debug!("[Shard {:?}] Sending presence update", shard_info);
self.send_json(&json!({
"op": OpCode::StatusUpdate.num(),
"d": {
"afk": false,
"since": now,
"status": status.name(),
"game": activity.as_ref().map(|x| json!({
"name": x.name,
"type": x.kind,
"url": x.url,
})),
},
})).await
}
#[instrument(skip(self))]
async fn send_resume(
&mut self,
shard_info: &[u64; 2],
session_id: &str,
seq: u64,
token: &str,
) -> Result<()> {
debug!("[Shard {:?}] Sending resume; seq: {}", shard_info, seq);
self.send_json(&json!({
"op": OpCode::Resume.num(),
"d": {
"session_id": session_id,
"seq": seq,
"token": token,
},
})).await.map_err(From::from)
}
}