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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//! Contains all code to dispatch incoming events onto framework commands
mod common;
mod permissions;
mod prefix;
mod slash;
pub use common::*;
pub use prefix::*;
pub use slash::*;
use crate::serenity_prelude as serenity;
/// A view into data stored by [`crate::Framework`]
pub struct FrameworkContext<'a, U, E> {
/// Serenity's context
pub serenity_context: &'a serenity::Context,
/// User ID of this bot, available through serenity_context if cache is enabled.
#[cfg(not(feature = "cache"))]
pub bot_id: serenity::UserId,
/// Framework configuration
pub options: &'a crate::FrameworkOptions<U, E>,
/// Your provided user data
pub user_data: &'a U,
/// Serenity shard manager. Can be used for example to shutdown the bot
pub shard_manager: &'a std::sync::Arc<serenity::ShardManager>,
// deliberately not non exhaustive because you need to create FrameworkContext from scratch
// to run your own event loop
}
impl<U, E> Copy for FrameworkContext<'_, U, E> {}
impl<U, E> Clone for FrameworkContext<'_, U, E> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, U, E> FrameworkContext<'a, U, E> {
/// Returns the user ID of the bot.
pub fn bot_id(&self) -> serenity::UserId {
#[cfg(feature = "cache")]
let bot_id = self.serenity_context.cache.current_user().id;
#[cfg(not(feature = "cache"))]
let bot_id = self.bot_id;
bot_id
}
/// Returns the stored framework options, including commands.
///
/// This function exists for API compatiblity with [`crate::Framework`]. On this type, you can
/// also just access the public `options` field.
pub fn options(&self) -> &'a crate::FrameworkOptions<U, E> {
self.options
}
/// Returns the serenity's client shard manager.
///
/// This function exists for API compatiblity with [`crate::Framework`]. On this type, you can
/// also just access the public `shard_manager` field.
pub fn shard_manager(&self) -> std::sync::Arc<serenity::ShardManager> {
self.shard_manager.clone()
}
/// Retrieves user data
///
/// This function exists for API compatiblity with [`crate::Framework`]. On this type, you can
/// also just access the public `user_data` field.
#[allow(clippy::unused_async)] // for API compatibility with Framework
pub async fn user_data(&self) -> &'a U {
self.user_data
}
}
/// Central event handling function of this library
pub async fn dispatch_event<U: Send + Sync, E>(
framework: crate::FrameworkContext<'_, U, E>,
event: serenity::FullEvent,
) {
match &event {
serenity::FullEvent::Message { new_message } => {
let invocation_data = tokio::sync::Mutex::new(Box::new(()) as _);
let mut command_tree = Vec::new();
let trigger = crate::MessageDispatchTrigger::MessageCreate;
if let Err(error) = prefix::dispatch_message(
framework,
new_message,
trigger,
&invocation_data,
&mut command_tree,
)
.await
{
error.handle(framework.options).await;
}
}
serenity::FullEvent::MessageUpdate {
event,
old_if_available,
..
} => {
if let Some(edit_tracker) = &framework.options.prefix_options.edit_tracker {
#[cfg(feature = "cache")]
if framework.options().prefix_options.check_edits_against_cache {
if let Some(old) = old_if_available {
if event
.content
.as_deref()
.is_some_and(|new_content| new_content == old.content)
{
return;
}
}
}
let msg = edit_tracker.write().unwrap().process_message_update(
event,
framework
.options()
.prefix_options
.ignore_edits_if_not_yet_responded,
framework
.options()
.prefix_options
.tracking_initiation_window
.as_ref(),
);
if let Some((msg, previously_tracked)) = msg {
let invocation_data = tokio::sync::Mutex::new(Box::new(()) as _);
let mut command_tree = Vec::new();
let trigger = match previously_tracked {
true => crate::MessageDispatchTrigger::MessageEdit,
false => crate::MessageDispatchTrigger::MessageEditFromInvalid,
};
if let Err(error) = prefix::dispatch_message(
framework,
&msg,
trigger,
&invocation_data,
&mut command_tree,
)
.await
{
error.handle(framework.options).await;
}
}
}
}
serenity::FullEvent::MessageDelete {
deleted_message_id, ..
} => {
if let Some(edit_tracker) = &framework.options.prefix_options.edit_tracker {
let bot_response = edit_tracker
.write()
.unwrap()
.process_message_delete(*deleted_message_id);
if let Some(bot_response) = bot_response {
if let Err(e) = bot_response.delete(framework.serenity_context).await {
tracing::warn!("failed to delete bot response: {}", e);
}
}
}
}
serenity::FullEvent::InteractionCreate {
interaction: serenity::Interaction::Command(interaction),
} => {
let invocation_data = tokio::sync::Mutex::new(Box::new(()) as _);
let mut command_tree = Vec::new();
if let Err(error) = slash::dispatch_interaction(
framework,
interaction,
&std::sync::atomic::AtomicBool::new(false),
&invocation_data,
&interaction.data.options(),
&mut command_tree,
)
.await
{
error.handle(framework.options).await;
}
}
serenity::FullEvent::InteractionCreate {
interaction: serenity::Interaction::Autocomplete(interaction),
} => {
let invocation_data = tokio::sync::Mutex::new(Box::new(()) as _);
let mut command_tree = Vec::new();
if let Err(error) = slash::dispatch_autocomplete(
framework,
interaction,
&std::sync::atomic::AtomicBool::new(false),
&invocation_data,
&interaction.data.options(),
&mut command_tree,
)
.await
{
error.handle(framework.options).await;
}
}
_ => {}
}
// Do this after the framework's Ready handling, so that get_user_data() doesnt
// potentially block infinitely
if let Err(error) = (framework.options.event_handler)(framework, &event).await {
let error = crate::FrameworkError::EventHandler {
error,
event: &event,
framework,
};
(framework.options.on_error)(error).await;
}
}