#discord #discord-api #twilight

twilight-standby

Utility to filter wait for filtered incoming events for the Twilight ecosystem

8 unstable releases (3 breaking)

0.3.0 Jan 8, 2021
0.2.2 Jan 5, 2021
0.2.1 Nov 29, 2020
0.2.0 Oct 30, 2020
0.0.0 Aug 30, 2020

#50 in Asynchronous

Download history 32/week @ 2020-11-07 33/week @ 2020-11-14 17/week @ 2020-11-21 35/week @ 2020-11-28 28/week @ 2020-12-05 10/week @ 2020-12-12 23/week @ 2020-12-19 10/week @ 2020-12-26 59/week @ 2021-01-02 20/week @ 2021-01-09 41/week @ 2021-01-16 27/week @ 2021-01-23 32/week @ 2021-01-30 27/week @ 2021-02-06 121/week @ 2021-02-13 43/week @ 2021-02-20

103 downloads per month
Used in twilight

ISC license

595KB
15K SLoC

twilight-standby

discord badge github badge license badge rust badge

Standby is a utility to wait for an event to happen based on a predicate check. For example, you may have a command that has a reaction menu of ✅ and ❌. If you want to handle a reaction to these, using something like an application-level state or event stream may not suit your use case. It may be cleaner to wait for a reaction inline to your function. This is where Twilight Standby comes in.

Standby allows you to wait for things like an event in a certain guild ([Standby::wait_for]), a new message in a channel ([Standby::wait_for_message]), a new reaction on a message ([Standby::wait_for_reaction]), and any event that might not take place in a guild, such as a new Ready event ([Standby::wait_for_event]). Each method also has a stream variant.

To use Standby, you must process events with it in your main event loop. Check out the [Standby::process] method.

When to use futures and streams

Standby has two variants of each method: a future variant and a stream variant. An example is [Standby::wait_for_message], which also has a [Standby::wait_for_message_stream] variant. The future variant is useful when you want to oneshot an event that you need to wait for. This means that if you only need to wait for one message in a channel to come in, you'd use the future variant. If you need to wait for multiple messages, such as maybe all of the messages within a minute's timespan, you'd use the [Standby::wait_for_message_stream] method.

The difference is that if you use the futures variant in a loop then you may miss some events while processing a received event. By using a stream, you won't miss any events.

Examples

At a glance

Wait for a message in channel 123 by user 456 with the content "test":

use twilight_model::{gateway::payload::MessageCreate, id::{ChannelId, UserId}};
use twilight_standby::Standby;

let standby = Standby::new();

let message = standby.wait_for_message(ChannelId(123), |event: &MessageCreate| {
    event.author.id == UserId(456) && event.content == "test"
}).await?;

A full example

A full sample bot connecting to the gateway, processing events, and including a handler to wait for reactions:

use futures_util::StreamExt;
use std::{env, error::Error};
use twilight_gateway::{Event, Intents, Shard};
use twilight_model::{
    channel::Message,
    gateway::payload::ReactionAdd,
    id::{ChannelId, UserId},
};
use twilight_standby::Standby;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Start a shard connected to the gateway to receive events.
    let intents = Intents::GUILD_MESSAGES | Intents::GUILD_MESSAGE_REACTIONS;
    let mut shard = Shard::new(env::var("DISCORD_TOKEN")?, intents);
    let mut events = shard.events();
    shard.start().await?;

    let standby = Standby::new();

    while let Some(event) = events.next().await {
        // Have standby process the event, which will fulfill any futures that
        // are waiting for an event.
        standby.process(&event);

        match event {
            Event::MessageCreate(msg) if msg.content == "!react" => {
                tokio::spawn(react(msg.0, standby.clone()));
            },
            _ => {},
        }
    }

    Ok(())
}

// Wait for a reaction from the user who sent the message, and then print it
// once they react.
async fn react(msg: Message, standby: Standby) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
    let author_id = msg.author.id;

    let reaction = standby.wait_for_reaction(msg.id, move |event: &ReactionAdd| {
        event.user_id == author_id
    }).await?;

    println!("user reacted with {:?}", reaction.emoji);

    Ok(())
}

For more examples, check out each of the methods on [Standby].

Dependencies

~1.9–2.7MB
~55K SLoC