如何将 discord-rs 事件与来自 Twitter 或计时器的其他事件结合起来?

Posted

技术标签:

【中文标题】如何将 discord-rs 事件与来自 Twitter 或计时器的其他事件结合起来?【英文标题】:How can I combine discord-rs events with other events from Twitter or timers? 【发布时间】:2017-04-26 11:39:40 【问题描述】:

我使用 discord-rs 库为 Discord 聊天服务编写了一个机器人。当它们出现在主循环中的单个线程中时,该库会为我提供事件:

fn start() 
    // ...
    loop 
        let event = match connection.recv_event() 
            Ok(event) => event,
            Err(err) =>  ... ,
        
    

我想添加一些计时器和其他在它们自己的线程中计算的东西,它们必须通知我在主循环的线程中做某事。我还想添加 Twitter 支持。所以它可能看起来像这样:

(Discord's network connection, Twitter network connection, some timer in another thread) -> main loop

这看起来像这样:

fn start() 
    // ...
    loop 
        let event = match recv_events() 
            // 1. if Discord - do something with discord
            // 2. if timer - handle timer's notification
            // 3. if Twitter network connection - handle twitter
        
    

在原始 C 和 C 套接字中,可以通过 (e) 轮询它们来完成,但在这里我不知道如何在 Rust 中做到这一点,或者是否可能。我想我想要像poll 这样的几个不同来源的东西,它们可以为我提供不同类型的对象。

我想如果我为 mio 的 Evented 特征提供一个包装器并使用 Deadline 示例中描述的 mio 的民意调查,这可以实现。

有没有办法把这些事件结合起来?

【问题讨论】:

【参考方案1】:

当它们出现在主循环的单个线程中时,这个库会为我提供事件

“单线程”仅适用于小型机器人。一旦您到达2500 guilds limit,Discord 将拒绝以正常方式连接您的机器人。你必须使用分片。而且我你不会为你的机器人分片配置新的虚拟服务器。机会是,您将生成新的线程,每个分片一个事件循环。

这是我的做法,顺便说一句:

fn event_loop(shard_id: u8, total_shards: u8)
    loop 
        let bot = Discord::from_bot_token("...").expect("!from_bot_token");
        let (mut dc, ready_ev) = bot.connect_sharded(shard_id, total_shards).expect("!connect");
        // ...
    


fn main() 
    let total_shards = 10;
    for shard_id in 0..total_shards 
        sleep(Duration::from_secs(6)); // There must be a five-second pause between connections from one IP.
        ThreadBuilder::new().name (fomat! ("shard " (shard_id)))
          .spawn (move || 
            loop 
              if let Err (err) = catch_unwind (move || event_loop (shard_id, total_shards)) 
                log! ("shard " (shard_id) " panic: " (gstuff::any_to_str (&*err) .unwrap_or ("")));
                sleep (Duration::from_secs (10));
                continue  // Panic restarts the shard.
              break
          ) .expect ("!spawn");
    

我想添加一些计时器和其他在它们自己的线程中计算的东西,它们必须通知我在主循环的线程中做某事

选项 1。不要。

机会是,您真的不需要返回到 Discord 事件循环!假设您要发布回复、更新嵌入等。您不需要 Discord 事件循环来执行此操作!

Discord API 分为两部分: 1) Websocket API,由Connection 表示,用于获取 Discord 中的事件。 2) REST API,由Discord 接口表示,用于发送事件。

您几乎可以从任何地方发送事件。从任何线程。甚至可能来自您的计时器。

DiscordSync。将其包装在 Arc 中并与您的计时器和线程共享。

选项 2. 抓住机会。

即使recv_event 没有超时,Discord 也会不断向您发送新事件。用户正在登录、退出、打字、发布消息、启动视频游戏、编辑内容等等。实际上,如果事件流停止,那么您的 Discord 连接就会出现问题(对于我的机器人,我已经根据该信号实现了高可用性故障转移)。

您可以与您的线程和计时器共享deque。一旦计时器完成,它将向双端队列发布一些内容,然后一旦 Discord 用新事件唤醒它,偶数循环将检查双端队列是否有新的事情要做。

选项 3. 鸟瞰图。

正如belst 所指出的,您可以启动一个通用事件循环,一个“统治所有事件”的循环,然后将 Discord 事件提升到该循环中。这特别有趣,因为通过分片,您将拥有多个事件循环。

所以,Discord 事件循环 -> 简单事件过滤器 -> 通道 -> 主事件循环。

选项 4。分片。

如果您希望您的机器人在代码升级和重新启动期间保持在线,那么您应该提供一种方法来分别重新启动每个分片(或者像我一样在分片级别实施高可用性故障转移)。因为您无法在进程重新启动后立即连接所有分片,所以 Discord 不会让您这样做。

如果您的所有分片共享同一个进程,则在该进程重新启动后,您必须等待 5 秒钟才能附加新分片。使用 10 个分片,机器人停机时间几乎是一分钟。

分离分片重启的一种方法是为每个分片分配一个进程。然后,当您需要升级机器人时,您将分别重新启动每个进程。这样仍然需要等待每个分片五到六秒,但你的用户的不需要。

更好的是,您现在只需为 discord-rs 升级和类似的维护相关任务重新启动 Discord 事件循环进程。另一方面,您的主事件循环可以立即重新启动,并且可以根据需要随时重新启动。这应该会大大加快编译-运行-测试循环。

所以,Discord 事件循环,在一个单独的分片进程中 -> 简单事件过滤器 -> RPC 或数据库 -> 主事件循环,在一个单独的进程中。

【讨论】:

【参考方案2】:

在您的情况下,我将为您需要的每个服务启动一个线程,然后使用 mpsc 通道将事件发送到主循环。

例子:

use std::thread;
use std::sync::mpsc::channel;

enum Event 
    Discord(()),
    Twitter(()),
    Timer(()),


fn main() 
    let (tx, rx) = channel();
    // discord
    let txprime = tx.clone();
    thread::spawn(move || 
        loop 
            // discord loop
            txprime.send(Event::Discord(())).unwrap()
        
    );

    // twitter
    let txprime = tx.clone();
    thread::spawn(move || 
        loop 
            // twitter loop
            txprime.send(Event::Twitter(())).unwrap()
        
    );

    // timer
    let txprime = tx.clone();
    thread::spawn(move || 
        loop 
            // timer loop
            txprime.send(Event::Timer(())).unwrap()
        
    );

    // Main loop
    loop 
        match rx.recv().unwrap() 
            Event::Discord(d) => unimplemented!(),
            Event::Twitter(t) => unimplemented!(),
            Event::Timer(t)   => unimplemented!(),
        
    

【讨论】:

以上是关于如何将 discord-rs 事件与来自 Twitter 或计时器的其他事件结合起来?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用“Twit”库从列表中选择一个随机单词

如何使用 Twit NPM 包获取 Twitter 用户名和头像

使用npm twit将图像上传到Twitter时“媒体类型无法识别”

Fabric,如何将崩溃与事件联系起来?

通过Twit w / Node.js发布Twitter线程

如何将python asyncio与线程结合起来?