You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Poise(Serenity) Discord机器人中获取Context及rx使用疑问

问题

我想获取Poise框架中的Context对象,并将其传递给不同函数,尤其是要在send_changes_via_bot函数里用ctx.say()向Discord API发送更新。另外我对rx的使用有疑问:如果递归调用函数,由于main里的tx一直在发送数据,每次传入的rx会不会不一样?还是说旧的rx会被复制,因为rx不是指针形式?

代码示例

pub async fn bot(rx: &mpsc::Receiver<Value>) {
    let discord_token = fs::read_to_string("discordtoken.txt")
                           .expect("Issue with token");
    let framework = poise::Framework::builder()
        .options(poise::FrameworkOptions {
            // Macro takes care of ctx and user
            commands: vec![account_age(), set_gdrive_channel(),
                           spawn_watcher()], 
            ..Default::default()
        })
        .token(discord_token)
        .intents(serenity::GatewayIntents::non_privileged())
        .setup(|ctx, _ready, framework| {
            Box::pin(async move {
                poise::builtins::register_globally(ctx, 
                     &framework.options().commands)
                .await?;
                Ok(Data {})
            })
        });

    // * ----------------------------------------------------------------
    // * ----------------------------------------------------------------
    // ! Don't know how to pass context here :(
    send_changes_via_bot(ctx, rx).await;
    // * ----------------------------------------------------------------
    // * ----------------------------------------------------------------

    framework.run().await.unwrap();
}
解答

一、获取并传递Poise的Context对象

你当前代码里的ctx只在setup闭包内部有效,没法直接在外部调用send_changes_via_bot时传入,这里给你两种可行的解决方式:

1. 把Context存入自定义Data结构体

  • 先修改你的Data结构体,添加字段存储serenity::Context(比Poise的Context更通用):
    use serenity::client::Context as SerenityContext;
    
    struct Data {
        discord_ctx: SerenityContext,
    }
    
  • 在setup闭包里克隆并保存ctx:
    .setup(|ctx, _ready, framework| {
        Box::pin(async move {
            poise::builtins::register_globally(ctx, &framework.options().commands).await?;
            Ok(Data { discord_ctx: ctx.clone() })
        })
    });
    
  • 后续要使用Context时,可通过ctx.data()(在命令逻辑里)或者框架实例的data方法获取。如果要启动send_changes_via_bot,可以在on_ready钩子中拿到Data里的Context,再异步启动任务。

2. 利用on_ready钩子启动任务

直接在框架配置里添加on_ready钩子,等机器人初始化完成后,再启动send_changes_via_bot并传入Context:

.options(poise::FrameworkOptions {
    commands: vec![account_age(), set_gdrive_channel(), spawn_watcher()],
    on_ready: Some(|ctx, _ready| {
        Box::pin(async move {
            // 注意:mpsc::Receiver不能共享,这里需要处理所有权,比如转移或改用其他通道类型
            let rx = rx.clone(); // 如果是broadcast类型的Receiver可克隆,mpsc则需要转移所有权
            tokio::spawn(async move {
                send_changes_via_bot(ctx, rx).await;
            });
        })
    }),
    ..Default::default()
})

注意:tokio::sync::mpsc::Receiver不支持克隆,同一时间只能有一个持有者接收消息。如果需要多消费者,建议改用broadcast或watch通道。

二、关于rx递归调用的疑问

  • Rust的mpsc::Receiver是所有权类型,不是指针。传递它时会转移所有权,旧的变量会立即失效,不会发生复制(因为mpsc::Receiver没有实现Clone,避免多消费者重复消费消息)。
  • 递归调用时,每次传入的rx都是同一个接收流的所有权持有者——本质是把所有权从当前递归层级转移到下一层,不会出现多个rx同时接收消息的情况。
  • 如果需要在多个地方接收消息,不要用mpsc,改用tokio::sync::broadcast::Receiver,它支持克隆多个接收端,每个都能收到相同的消息。

内容的提问来源于stack exchange,提问作者ScreX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:05:43