#discord #discord-api #twilight #websocket #api-bindings #load-balancing

twilight-gateway

Twilight生态系统中的Discord网关实现

68个版本

0.16.0-rc.12024年2月21日
0.15.4 2023年9月10日
0.15.2 2023年4月27日
0.15.1 2023年2月26日
0.2.5 2020年11月29日

#98 in 网页编程

Download history 1065/week @ 2024-04-14 1014/week @ 2024-04-21 955/week @ 2024-04-28 901/week @ 2024-05-05 715/week @ 2024-05-12 768/week @ 2024-05-19 959/week @ 2024-05-26 753/week @ 2024-06-02 615/week @ 2024-06-09 909/week @ 2024-06-16 837/week @ 2024-06-23 724/week @ 2024-06-30 747/week @ 2024-07-07 982/week @ 2024-07-14 908/week @ 2024-07-21 1485/week @ 2024-07-28

4,264 每月下载量
10 个软件包中使用 (9 个直接使用)

ISC 许可证

2.5MB
55K SLoC

twilight-gateway

codecov badge discord badge github badge license badge rust badge

twilight-gateway 是Discord分片网关会话的实现。它负责实时接收Discord的状态事件,并发送一些状态信息。

主要类型是 Shard,这是一个维护与Discord网关的WebSocket连接的状态接口。其大部分功能都可以配置,并用于接收网关事件或原始WebSocket消息,这对于负载均衡和微服务很有用。

可以一次性轻松创建多个分片,使用 Fn(ShardId, ConfigBuilder) -> Config 闭包创建每个分片的自定义配置,借助 create_ 系列函数。这些函数将重用分片的TLS上下文和[会话队列],否则需要通过克隆现有的 Config 来实现。

特性

  • simd-json: 使用 simd-json 替代 serde_json 进行事件反序列化
  • TLS (互斥)
    • native-tls: 通过 native-tls 使用平台的本地TLS实现
    • rustls-native-roots (默认): 使用 rustls 和本地根证书
    • rustls-webpki-roots: 使用 rustlswebpki-roots 根证书,对 scratch 容器很有用
  • twilight-http (默认): 启用 stream::create_recommended 函数
  • zlib (互斥)
    • zlib-stock (默认): flate2 的内置 zlib 实现
    • zlib-simd: 使用 zlib-ng 进行 zlib,可能具有更好的性能

示例

创建推荐的分片数量,并并行遍历它们的公会事件

use std::{
    env,
    sync::atomic::{AtomicBool, Ordering},
};
use tokio::signal;
use twilight_gateway::{
    error::ReceiveMessageErrorType, CloseFrame, Config, Event, EventTypeFlags, Intents, Shard,
    StreamExt as _,
};
use twilight_http::Client;

static SHUTDOWN: AtomicBool = AtomicBool::new(false);

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    let token = env::var("DISCORD_TOKEN")?;
    let client = Client::new(token.clone());
    let config = Config::new(token, Intents::GUILDS);

    let shards =
        twilight_gateway::create_recommended(&client, config, |_, builder| builder.build()).await?;
    let mut senders = Vec::with_capacity(shards.len());
    let mut tasks = Vec::with_capacity(shards.len());

    for shard in shards {
        senders.push(shard.sender());
        tasks.push(tokio::spawn(runner(shard)));
    }

    signal::ctrl_c().await?;
    SHUTDOWN.store(true, Ordering::Relaxed);
    for sender in senders {
        // Ignore error if shard's already shutdown.
        _ = sender.close(CloseFrame::NORMAL);
    }

    for jh in tasks {
        _ = jh.await;
    }

    Ok(())
}

async fn runner(mut shard: Shard) {
    while let Some(item) = shard.next_event(EventTypeFlags::all()).await {
        let event = match item {
            Ok(Event::GatewayClose(_)) if SHUTDOWN.load(Ordering::Relaxed) => break,
            Ok(event) => event,
            Err(source)
                if SHUTDOWN.load(Ordering::Relaxed)
                    && matches!(source.kind(), ReceiveMessageErrorType::WebSocket) =>
            {
                break
            }
            Err(source) => {
                tracing::warn!(?source, "error receiving event");

                continue;
            }
        };

        // You'd normally want to spawn a new tokio task for each event and
        // handle the event there to not block the shard.
        tracing::debug!(?event, shard = ?shard.id(), "received event");
    }
}

更多示例位于 仓库 中。

依赖项

~9–23MB
~351K SLoC