Expand description
§twilight-gateway
twilight-gateway is an implementation of Discord’s sharding gateway sessions.
This is responsible for receiving stateful events in real-time from Discord and
sending some stateful information.
The primary type is the Shard, a stateful interface to maintain a Websocket
connection to Discord’s gateway. Much of its functionality can be configured,
and it’s used to receive gateway events or raw Websocket messages, useful for
load balancing and microservices.
Multiple shards may easily be created at once, with a per shard config created
from a Fn(ShardId, ConfigBuilder) -> Config closure, with the help of the
create_ set of functions. These functions will reuse shards’ TLS context and
session queue, something otherwise achieved by cloning an existing
Config.
§Features
simd-json: usesimd-jsoninstead ofserde_jsonfor deserializing events- TLS (mutually exclusive)
native-tls: platform’s native TLS implementation vianative-tlsrustls-native-roots:rustlsusing native root certificatesrustls-platform-verifier(default):rustlsusing operating system’s certificate facilities viarustls-platform-verifierrustls-webpki-roots:rustlsusingwebpki-rootsfor root certificates, useful forscratchcontainers
twilight-http(default): enable thestream::create_recommendedfunction- Transport compression (mutually exclusive)
§Example
Create the recommended number of shards and loop over their guild messages:
mod context {
use std::{ops::Deref, sync::OnceLock};
use twilight_http::Client;
pub static CONTEXT: Handle = Handle(OnceLock::new());
#[derive(Debug)]
pub struct Context {
pub http: Client,
}
pub fn initialize(http: Client) {
let context = Context { http };
assert!(CONTEXT.0.set(context).is_ok());
}
pub struct Handle(OnceLock<Context>);
impl Deref for Handle {
type Target = Context;
fn deref(&self) -> &Self::Target {
self.0.get().unwrap()
}
}
}
use context::CONTEXT;
use std::{env, pin::pin};
use tokio::signal;
use tokio_util::task::TaskTracker;
use twilight_gateway::{
CloseFrame, Config, Event, EventTypeFlags, Intents, MessageSender, Shard, StreamExt as _,
};
use twilight_http::Client;
use twilight_model::gateway::payload::{incoming::MessageCreate, outgoing::UpdateVoiceState};
const EVENT_TYPES: EventTypeFlags = EventTypeFlags::MESSAGE_CREATE;
const INTENTS: Intents = Intents::GUILD_MESSAGES.union(Intents::MESSAGE_CONTENT);
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize the tracing subscriber.
tracing_subscriber::fmt::init();
// Select rustls backend
rustls::crypto::ring::default_provider().install_default().unwrap();
let token = env::var("DISCORD_TOKEN")?;
let config = Config::new(token.clone(), INTENTS);
let http = Client::new(token);
let shards =
twilight_gateway::create_recommended(&http, config, |_, builder| builder.build()).await?;
context::initialize(http);
let tracker = TaskTracker::new();
for shard in shards {
tracker.spawn(dispatcher(shard));
}
tracker.close();
tracker.wait().await;
Ok(())
}
#[tracing::instrument(fields(shard = %shard.id()), skip_all)]
async fn dispatcher(mut shard: Shard) {
let mut ctrl_c = pin!(signal::ctrl_c());
let mut shutdown = false;
let tracker = TaskTracker::new();
loop {
tokio::select! {
// Do not poll ctrl_c after it's completed.
_ = &mut ctrl_c, if !shutdown => {
// Cleanly shut down once we receive the echo close frame.
shard.close(CloseFrame::NORMAL);
shutdown = true;
},
Some(item) = shard.next_event(EVENT_TYPES) => {
let event = match item {
Ok(event) => event,
Err(source) => {
tracing::warn!(?source, "error receiving event");
continue;
}
};
let handler = match event {
// Clean shutdown exit condition.
Event::GatewayClose(_) if shutdown => break,
Event::MessageCreate(e) => message(e, shard.sender()),
_ => continue,
};
tracker.spawn(async move {
if let Err(source) = handler.await {
tracing::warn!(?source, "error handling event");
}
});
}
}
}
tracker.close();
tracker.wait().await;
}
#[tracing::instrument(fields(id = %event.id), skip_all)]
async fn message(event: Box<MessageCreate>, sender: MessageSender) -> anyhow::Result<()> {
match &*event.content {
"!join" if event.guild_id.is_some() => {
sender.command(&UpdateVoiceState::new(
event.guild_id.unwrap(),
Some(event.channel_id),
false,
false,
))?;
}
"!ping" => {
CONTEXT
.http
.create_message(event.channel_id)
.content("Pong!")
.await?;
}
_ => {}
}
Ok(())
}There are a few additional examples located in the repository.
Except for the s390x arch, where
zlib-ng-sysis used instead. ↩
Re-exports§
pub use twilight_gateway_queue as queue;pub use twilight_model::gateway::event::Event;pub use twilight_model::gateway::event::EventType;
Modules§
- error
- Errors returned by gateway operations.
Structs§
- Close
Frame - Information about a close message.
- Command
Ratelimiter - Ratelimiter for sending commands over the gateway to Discord.
- Config
- Configuration used by the shard to identify with the gateway and operate.
- Config
Builder - Builder to customize the operation of a shard.
- Event
Type Flags - Important optimization for narrowing requested event types.
- Intents
- Gateway intents.
- Latency
Shard’s gateway connection latency.- Message
Sender - Channel to send messages over a
Shardto the Discord gateway. - Session
- Gateway session information for a shard’s active connection.
- Shard
- Gateway API client responsible for up to 2500 guilds.
- ShardId
- Shard identifier to calculate if it receivies a given event.
Enums§
- Message
- Message to send over the connection to the remote.
- Shard
State - Current state of a Shard.
Constants§
- API_
VERSION - Discord Gateway API version used by this crate.
Traits§
- Command
- Trait marker denoting what can be provided to
Shard::command. - Stream
Ext - An extension trait for the [
Stream] trait.
Functions§
- create_
bucket - Create a single bucket’s worth of shards.
- create_
iterator - Create a iterator of shards.
- create_
recommended twilight-http - Create a range of shards from Discord’s recommendation.
- parse
- Parse a JSON encoded gateway event into a
GatewayEventifwanted_event_typescontains its type.