-
Notifications
You must be signed in to change notification settings - Fork 597
/
Copy pathmain.rs
194 lines (165 loc) · 6.33 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
//! This example will showcase the beauty of collectors. They allow to await messages or reactions
//! from a user in the middle of a control flow, one being a command.
use std::collections::HashSet;
use std::env;
use std::time::Duration;
use serenity::async_trait;
use serenity::collector::MessageCollector;
use serenity::framework::standard::macros::{command, group, help};
use serenity::framework::standard::{
help_commands,
Args,
CommandGroup,
CommandResult,
HelpOptions,
StandardFramework,
};
// Collectors are streams, that means we can use `StreamExt` and `TryStreamExt`.
use serenity::futures::stream::StreamExt;
use serenity::http::Http;
use serenity::model::prelude::*;
use serenity::prelude::*;
#[group("collector")]
#[commands(challenge)]
struct Collector;
#[help]
async fn my_help(
context: &Context,
msg: &Message,
args: Args,
help_options: &'static HelpOptions,
groups: &[&'static CommandGroup],
owners: HashSet<UserId>,
) -> CommandResult {
let _ = help_commands::with_embeds(context, msg, args, help_options, groups, owners).await;
Ok(())
}
struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn ready(&self, _: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);
}
}
#[tokio::main]
async fn main() {
// Configure the client with your Discord bot token in the environment.
let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
let http = Http::new(&token);
// We will fetch your bot's id.
let bot_id = match http.get_current_user().await {
Ok(info) => info.id,
Err(why) => panic!("Could not access user info: {:?}", why),
};
let framework = StandardFramework::new().help(&MY_HELP).group(&COLLECTOR_GROUP);
framework.configure(|c| {
c.with_whitespace(true).on_mention(Some(bot_id)).prefix("~").delimiters(vec![", ", ","])
});
let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT
| GatewayIntents::GUILD_MESSAGE_REACTIONS;
let mut client = Client::builder(&token, intents)
.event_handler(Handler)
.framework(framework)
.await
.expect("Err creating client");
if let Err(why) = client.start().await {
println!("Client error: {why:?}");
}
}
#[command]
async fn challenge(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
let mut score = 0u32;
let _ = msg.reply(ctx, "How was that crusty crab called again? 10 seconds time!").await;
// There is a method implemented for some models to conveniently collect replies. They return a
// builder that can be turned into a Stream, or here, where we can await a single reply
let collector = msg.author.await_reply(&ctx.shard).timeout(Duration::from_secs(10));
if let Some(answer) = collector.await {
if answer.content.to_lowercase() == "ferris" {
let _ = answer.reply(ctx, "That's correct!").await;
score += 1;
} else {
let _ = answer.reply(ctx, "Wrong, it's Ferris!").await;
}
} else {
let _ = msg.reply(ctx, "No answer within 10 seconds.").await;
};
let react_msg = msg
.reply(ctx, "React with the reaction representing 1, you got 10 seconds!")
.await
.unwrap();
// The message model can also be turned into a Collector to collect reactions on it.
let collector = react_msg
.await_reaction(&ctx.shard)
.timeout(Duration::from_secs(10))
.author_id(msg.author.id);
if let Some(reaction) = collector.await {
let _ = if reaction.emoji.as_data() == "1️⃣" {
score += 1;
msg.reply(ctx, "That's correct!").await
} else {
msg.reply(ctx, "Wrong!").await
};
} else {
let _ = msg.reply(ctx, "No reaction within 10 seconds.").await;
};
let _ = msg.reply(ctx, "Write 5 messages in 10 seconds").await;
// We can create a collector from scratch too using this builder future.
let collector = MessageCollector::new(&ctx.shard)
// Only collect messages by this user.
.author_id(msg.author.id)
.channel_id(msg.channel_id)
.timeout(Duration::from_secs(10))
// Build the collector.
.stream()
.take(5);
// Let's acquire borrow HTTP to send a message inside the `async move`.
let http = &ctx.http;
// We want to process each message and get the length. There are a couple of ways to do this.
// Folding the stream with `fold` is one way.
//
// Using `then` to first reply and then create a new stream with all messages is another way to
// do it, which can be nice if you want to further process the messages.
//
// If you don't want to collect the stream, `for_each` may be sufficient.
let collected: Vec<_> = collector
.then(|msg| async move {
let _ = msg.reply(http, format!("I repeat: {}", msg.content)).await;
msg
})
.collect()
.await;
if collected.len() >= 5 {
score += 1;
}
// We can also collect arbitrary events using the collect() function. For example, here we
// collect updates to the messages that the user sent above and check for them updating all 5
// of them.
let mut collector = serenity::collector::collect(&ctx.shard, move |event| match event {
// Only collect MessageUpdate events for the 5 MessageIds we're interested in.
Event::MessageUpdate(event) if collected.iter().any(|msg| event.id == msg.id) => {
Some(event.id)
},
_ => None,
})
.take_until(Box::pin(tokio::time::sleep(Duration::from_secs(20))));
let _ = msg.reply(ctx, "Edit each of those 5 messages in 20 seconds").await;
let mut edited = HashSet::new();
while let Some(edited_message_id) = collector.next().await {
edited.insert(edited_message_id);
if edited.len() >= 5 {
break;
}
}
if edited.len() >= 5 {
score += 1;
let _ = msg.reply(ctx, "Great! You edited 5 out of 5").await;
} else {
let _ = msg.reply(ctx, &format!("You only edited {} out of 5", edited.len())).await;
}
let _ = msg
.reply(ctx, &format!("TIME'S UP! You completed {score} out of 4 tasks correctly!"))
.await;
Ok(())
}