1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
//! The gateway module contains the pieces - primarily the `Shard` -
//! responsible for maintaing a WebSocket connection with Discord.
//!
//! A shard is an interface for the lower-level receiver and sender. It provides
//! what can otherwise be thought of as "sugar methods". A shard represents a
//! single connection to Discord. You can make use of a method named "sharding"
//! to have multiple shards, potentially offloading some server load to another
//! server(s).
//!
//! # Sharding
//!
//! Sharding is a method to split portions of bots into separate processes. This
//! is an enforced strategy by Discord once a bot reaches a certain number of
//! guilds (2500). Once this number is reached, a bot must be sharded in a way
//! that only 2500 guilds maximum may be allocated per shard.
//!
//! The "recommended" number of guilds per shard is _around_ 1000. Sharding can
//! be useful for splitting processes across separate servers. Often you may
//! want some or all shards to be in the same process, allowing for a shared
//! State. This is possible through this library.
//!
//! See [Discord's documentation][docs] for more information.
//!
//! If you are not using a bot account or do not require sharding - such as for
//! a small bot - then use [`Client::start`].
//!
//! There are a few methods of sharding available:
//!
//! - [`Client::start_autosharded`]: retrieves the number of shards Discord
//! recommends using from the API, and then automatically starts that number of
//! shards.
//! - [`Client::start_shard`]: starts a single shard for use in the instance,
//! handled by the instance of the Client. Use this if you only want 1 shard
//! handled by this instance.
//! - [`Client::start_shards`]: starts all shards in this instance. This is best
//! for when you want a completely shared State.
//! - [`Client::start_shard_range`]: start a range of shards within this
//! instance. This should be used when you, for example, want to split 10 shards
//! across 3 instances.
//!
//! [`Client`]: crate::Client
//! [`Client::start`]: crate::Client::start
//! [`Client::start_autosharded`]: crate::Client::start_autosharded
//! [`Client::start_shard`]: crate::Client::start_shard
//! [`Client::start_shard_range`]: crate::Client::start_shard_range
//! [`Client::start_shards`]: crate::Client::start_shards
//! [docs]: https://discordapp.com/developers/docs/topics/gateway#sharding
mod error;
mod shard;
mod ws_client_ext;
use std::fmt::{Display, Formatter, Result as FmtResult};
use serde_json::Value;
pub use self::{
error::Error as GatewayError,
shard::Shard,
ws_client_ext::WebSocketGatewayClientExt,
};
#[cfg(feature = "client")]
use crate::client::bridge::gateway::ShardClientMessage;
use crate::model::{gateway::Activity, user::OnlineStatus};
pub type CurrentPresence = (Option<Activity>, OnlineStatus);
use async_tungstenite::{tokio::ConnectStream, WebSocketStream};
pub type WsStream = WebSocketStream<ConnectStream>;
/// Indicates the current connection stage of a [`Shard`].
///
/// This can be useful for knowing which shards are currently "down"/"up".
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum ConnectionStage {
/// Indicator that the [`Shard`] is normally connected and is not in, e.g.,
/// a resume phase.
Connected,
/// Indicator that the [`Shard`] is connecting and is in, e.g., a resume
/// phase.
Connecting,
/// Indicator that the [`Shard`] is fully disconnected and is not in a
/// reconnecting phase.
Disconnected,
/// Indicator that the [`Shard`] is currently initiating a handshake.
Handshake,
/// Indicator that the [`Shard`] has sent an IDENTIFY packet and is awaiting
/// a READY packet.
Identifying,
/// Indicator that the [`Shard`] has sent a RESUME packet and is awaiting a
/// RESUMED packet.
Resuming,
}
impl ConnectionStage {
/// Whether the stage is a form of connecting.
///
/// This will return `true` on:
///
/// - [`Connecting`][`ConnectionStage::Connecting`]
/// - [`Handshake`][`ConnectionStage::Handshake`]
/// - [`Identifying`][`ConnectionStage::Identifying`]
/// - [`Resuming`][`ConnectionStage::Resuming`]
///
/// All other variants will return `false`.
///
/// # Examples
///
/// Assert that [`ConnectionStage::Identifying`] is a connecting stage:
///
/// ```rust
/// use serenity::gateway::ConnectionStage;
///
/// assert!(ConnectionStage::Identifying.is_connecting());
/// ```
///
/// Assert that [`ConnectionStage::Connected`] is _not_ a connecting stage:
///
/// ```rust
/// use serenity::gateway::ConnectionStage;
///
/// assert!(!ConnectionStage::Connected.is_connecting());
/// ```
pub fn is_connecting(self) -> bool {
use self::ConnectionStage::*;
match self {
Connecting | Handshake | Identifying | Resuming => true,
Connected | Disconnected => false,
}
}
}
impl Display for ConnectionStage {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
use self::ConnectionStage::*;
f.write_str(match *self {
Connected => "connected",
Connecting => "connecting",
Disconnected => "disconnected",
Handshake => "handshaking",
Identifying => "identifying",
Resuming => "resuming",
})
}
}
/// A message to be passed around within the library.
///
/// As a user you usually don't need to worry about this, but when working with
/// the lower-level internals of the [`client`], [`gateway`], and [`voice`] modules it
/// may be necessary.
///
/// [`client`]: crate::client
/// [`gateway`]: crate::gateway
/// [`voice`]: crate::model::voice
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum InterMessage {
#[cfg(feature = "client")]
Client(Box<ShardClientMessage>),
Json(Value),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ShardAction {
Heartbeat,
Identify,
Reconnect(ReconnectType),
}
/// The type of reconnection that should be performed.
#[derive(Debug)]
#[non_exhaustive]
pub enum ReconnectType {
/// Indicator that a new connection should be made by sending an IDENTIFY.
Reidentify,
/// Indicator that a new connection should be made by sending a RESUME.
Resume,
}