The Gateway connection is managed automatically via client.login(token). Direct interaction with WebSocketManager and WebSocketShard is not required in most cases.
Connection Lifecycle
GET /gateway/bot
↓
connect_async (TLS)
↓
← Hello { heartbeat_interval }
↓
→ Identify (or Resume if session_id + seq are available)
↓
← READY (or RESUMED)
↓
← Dispatch events...
Step Description GET /gateway/botFetches the WebSocket URL and recommended shard count. connect_asyncOpens a TLS WebSocket connection to the received URL. HelloThe server sends heartbeat_interval in milliseconds. The shard starts a heartbeat task. IdentifyThe shard sends the token, intents, shard info, and optional presence. ResumeIf session_id and seq are saved - Resume is sent instead of Identify. READY / RESUMEDSession established. The client starts receiving events.
WebSocketManagerOptions
Field Type Default Description tokenString""Bot token. Set automatically during client.login. intentsu640Gateway intents. Always 0 in Fluxer. presenceOption<GatewayPresenceUpdateSendData>NoneInitial presence on connection. shard_idsOption<Vec<u32>>NoneList of specific shard IDs to start. If None, all are started. shard_countOption<u32>NoneTotal number of shards. If None, taken from the /gateway/bot response. versionString"1"Gateway version.
WebSocketManager
Manages a pool of shards. Created and started inside client.login.
connect
pub async fn connect ( & mut self ) -> Result <(), RestError >
Fetches /gateway/bot, determines the shard count, and starts each shard in a separate tokio::spawn task.
broadcast
pub async fn broadcast ( & self , payload : Value )
Sends a JSON payload to all active shards. Called via client.send_to_gateway.
send
pub async fn send ( & self , shard_id : u32 , payload : Value ) -> Result <(), String >
Sends a JSON payload to a specific shard by its ID. Returns Err if the shard is not found or its channel is closed.
shard_count
pub fn shard_count ( & self ) -> u32
Returns the total number of running shards.
gateway_url
pub fn gateway_url ( & self ) -> Option < & str >
Returns the WebSocket URL received from /gateway/bot. None before connect is called.
WebSocketShard
One shard - one WebSocket connection. Manages its own heartbeat and automatically reconnects on disconnect.
ShardOptions
Field Type Description urlStringGateway WebSocket URL. tokenStringBot token. intentsu64Gateway intents. presenceOption<GatewayPresenceUpdateSendData>Initial presence. shard_idu32ID of this shard (from 0 to num_shards - 1). num_shardsu32Total number of shards. versionStringGateway version.
Heartbeat
After receiving Hello, the shard starts a separate tokio::spawn task:
The first heartbeat is sent with a random jitter: sleep(interval * rand()).
After that, a heartbeat is sent every heartbeat_interval milliseconds.
If HeartbeatAck is not received before the next heartbeat while a session is active - the shard disconnects and reconnects.
rand_f64() in the shard is implemented via SystemTime::subsec_nanos(). This is not cryptographically secure randomness, but it is sufficient for heartbeat jitter.
Reconnection
On any disconnect, the shard automatically reconnects with exponential backoff.
Parameter Value Initial delay 1,000 ms Maximum delay 45,000 ms Growth factor ×1.5 on each failure Jitter ×(0.75 … 1.25) of the current delay
After a successful connection, the delay resets to 1,000 ms.
Identify vs Resume
Condition Action session_id and seq are savedSends Resume - restores the session without losing missed events. session_id or seq are missingSends Identify - starts a new session. Opcode InvalidSession (9) Resets session_id and seq, waits 1–5 seconds, reconnects with Identify.
Opcodes
Opcode Value Direction Description Dispatch0 ← Server Gateway event (READY, MESSAGE_CREATE, etc.). Heartbeat1 → Client Server requests an immediate heartbeat. Identify2 → Client Authenticate a new session. PresenceUpdate3 → Client Update status and activity. VoiceStateUpdate4 → Client Connect/disconnect from a voice channel. Resume6 → Client Restore an interrupted session. Reconnect7 ← Server Server requests a reconnect. RequestGuildMembers8 → Client Request the guild member list. InvalidSession9 ← Server Session is invalid. Hello10 ← Server First message after connecting, contains heartbeat_interval. HeartbeatAck11 ← Server Heartbeat acknowledgement.
Close Codes
Determine whether to reconnect when a Close frame is received.
Code Description 1000Normal closure. 1001Client going away. 1005No code specified. 1006Abnormal closure (no Close frame). 1011Internal server error. 1012Service restart. 1013Try again later. 1014Bad Gateway. 1015TLS error. 4000Unknown error. 4007Invalid sequence number during Resume. 4009Session timed out. 4010Invalid shard. 4011Sharding required. 4012Invalid API version.
Code Description 4001Unknown opcode. 4002Decode error. 4003Not authenticated. 4004Token is invalid. Reconnecting is pointless. 4005Already authenticated. 4008Rate limit exceeded. 4013Invalid intents. 4014Disallowed intents.
When code 4004 is received, the shard shuts down without reconnecting. Verify that the token is correct.
GatewayPresenceUpdateSendData
Set in ClientOptions.presence or via send_to_gateway with opcode 3.
Field Type Description sinceOption<u64>Unix time (ms) since going AFK. None if not AFK. activitiesOption<Vec<GatewayActivity>>List of activities. custom_statusOption<GatewayCustomStatus>Custom status (Fluxer-specific). statusStringStatus: "online", "idle", "dnd", "invisible". afkOption<bool>Whether the session is AFK.
GatewayActivity
Field Type Description nameStringActivity name. kindu8Type: 0 - Playing, 1 - Streaming, 2 - Listening, 3 - Watching, 5 - Competing. urlOption<String>Stream URL. Used only when kind == 1.
GatewayCustomStatus
Field Type Description textOption<String>Custom status text. emoji_nameOption<String>Emoji name. emoji_idOption<String>Custom emoji ID.
WsEvent / ShardEvent
Internal events that shards pass to the manager.
ShardEvent (inside a shard)
Variant Description ShardEvent::Ready(Value)The shard received a READY payload. ShardEvent::ResumedThe shard restored its session via Resume. ShardEvent::Dispatch(GatewayReceivePayload)A Gateway event arrived. ShardEvent::Close(u16)Connection closed with the given code. ShardEvent::Error(String)WebSocket error. ShardEvent::Debug(String)Diagnostic message.
WsEvent (from manager to client)
Variant Fields Description WsEvent::ShardReadyshard_id: u32, data: ValueShard is ready. WsEvent::ShardResumedshard_id: u32Shard restored its session. WsEvent::Dispatchshard_id: u32, payload: GatewayReceivePayloadGateway event to process. WsEvent::ShardCloseshard_id: u32, code: u16Shard closed. WsEvent::Errorshard_id: u32, error: StringShard error. WsEvent::Debugmessage: StringDiagnostic message.
Examples
use fluxer_core :: client :: { Client , ClientOptions };
use fluxer_types :: gateway :: { GatewayActivity , GatewayPresenceUpdateSendData };
let options = ClientOptions {
intents : 0 ,
presence : Some ( GatewayPresenceUpdateSendData {
status : "online" . to_string (),
activities : Some ( vec! [ GatewayActivity {
name : "Fluxer.RUST" . to_string (),
kind : 0 ,
url : None ,
}]),
custom_status : None ,
since : None ,
afk : Some ( false ),
}),
.. Default :: default ()
};
let mut client = Client :: new ( options );
Update status via Gateway at runtime
use serde_json :: json;
let payload = json! ({
"op" : 3 ,
"d" : {
"status" : "dnd" ,
"afk" : false ,
"since" : null ,
"activities" : [{
"name" : "Maintenance" ,
"type" : 0
}]
}
});
client . send_to_gateway ( payload ) . await ;
Request guild members (RequestGuildMembers)
use serde_json :: json;
let payload = json! ({
"op" : 8 ,
"d" : {
"guild_id" : "GUILD_ID" ,
"query" : "" ,
"limit" : 0
}
});
client . send_to_gateway ( payload ) . await ;
Send VoiceStateUpdate to a specific shard
use serde_json :: json;
let payload = json! ({
"op" : 4 ,
"d" : {
"guild_id" : "GUILD_ID" ,
"channel_id" : "VOICE_CHANNEL_ID" ,
"self_mute" : false ,
"self_deaf" : false
}
});
if let Err ( e ) = client . send_to_shard ( 0 , payload ) . await {
tracing :: error! ( "send_to_shard: {e}" );
}
Track Debug events from the Gateway
client . on_typed ( | event | {
Box :: pin ( async move {
if let DispatchEvent :: Debug { message } = event {
tracing :: debug! ( "[Gateway] {message}" );
}
})
});