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
//! Holds prefix-command definition structs.
use crate::{serenity_prelude as serenity, BoxFuture};
/// The event that triggered a prefix command execution
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MessageDispatchTrigger {
/// The invocation message was posted directly (common case)
MessageCreate,
/// The message was edited, and was already a valid invocation pre-edit
MessageEdit,
/// The message was edited, and was not a valid invocation pre-edit (i.e. user typoed the
/// command, then fixed it)
MessageEditFromInvalid,
#[doc(hidden)]
__NonExhaustive,
}
/// Prefix-specific context passed to command invocations.
///
/// Contains the trigger message, the Discord connection management stuff, and the user data.
#[derive(derivative::Derivative)]
#[derivative(Debug(bound = ""))]
pub struct PrefixContext<'a, U, E> {
/// Serenity's context, like HTTP or cache
#[derivative(Debug = "ignore")]
pub serenity_context: &'a serenity::Context,
/// The invoking user message
pub msg: &'a serenity::Message,
/// Prefix used by the user to invoke this command
pub prefix: &'a str,
/// Command name used by the user to invoke this command
pub invoked_command_name: &'a str,
/// Entire argument string
pub args: &'a str,
/// Read-only reference to the framework
///
/// Useful if you need the list of commands, for example for a custom help command
#[derivative(Debug = "ignore")]
pub framework: crate::FrameworkContext<'a, U, E>,
/// If the invoked command was a subcommand, these are the parent commands, ordered top down.
pub parent_commands: &'a [&'a crate::Command<U, E>],
/// The command object which is the current command
pub command: &'a crate::Command<U, E>,
/// Your custom user data
// TODO: redundant with framework
#[derivative(Debug = "ignore")]
pub data: &'a U,
/// Custom user data carried across a single command invocation
pub invocation_data: &'a tokio::sync::Mutex<Box<dyn std::any::Any + Send + Sync>>,
/// How this command invocation was triggered
pub trigger: MessageDispatchTrigger,
/// The function that is called to execute the actual command
#[derivative(Debug = "ignore")]
pub action: fn(
PrefixContext<'_, U, E>,
) -> crate::BoxFuture<'_, Result<(), crate::FrameworkError<'_, U, E>>>,
// #[non_exhaustive] forbids struct update syntax for ?? reason
#[doc(hidden)]
pub __non_exhaustive: (),
}
// manual Copy+Clone implementations because Rust is getting confused about the type parameter
impl<U, E> Clone for PrefixContext<'_, U, E> {
fn clone(&self) -> Self {
*self
}
}
impl<U, E> Copy for PrefixContext<'_, U, E> {}
impl<U, E> crate::_GetGenerics for PrefixContext<'_, U, E> {
type U = U;
type E = E;
}
/// Possible ways to define a command prefix
#[derive(Clone, Debug)]
pub enum Prefix {
/// A case-sensitive string literal prefix (passed to [`str::strip_prefix`])
Literal(&'static str),
/// Regular expression which matches the prefix
Regex(regex::Regex),
#[doc(hidden)]
__NonExhaustive,
}
/// Prefix-specific framework configuration
#[derive(derivative::Derivative)]
#[derivative(Debug(bound = ""))]
pub struct PrefixFrameworkOptions<U, E> {
/// The main bot prefix. Can be set to None if the bot supports only
/// [dynamic prefixes](Self::dynamic_prefix).
pub prefix: Option<String>,
/// List of additional bot prefixes
// TODO: maybe it would be nicer to have separate fields for literal and regex prefixes
// That way, you don't need to wrap every single literal prefix in a long path which looks ugly
pub additional_prefixes: Vec<Prefix>,
/// Callback invoked on every message to return a prefix.
///
/// Override this field for a simple dynamic prefix which changes depending on the guild or user.
///
/// For more advanced dynamic prefixes, see [`Self::stripped_dynamic_prefix`]
#[derivative(Debug = "ignore")]
pub dynamic_prefix:
Option<fn(crate::PartialContext<'_, U, E>) -> BoxFuture<'_, Result<Option<String>, E>>>,
/// Callback invoked on every message to strip the prefix off an incoming message.
///
/// Override this field for advanced dynamic prefixes which change depending on guild or user.
///
/// Return value is a tuple of the prefix and the rest of the message:
/// ```rust,no_run
/// # poise::PrefixFrameworkOptions::<(), ()> { stripped_dynamic_prefix: Some(|_, msg, _| Box::pin(async move {
/// let my_cool_prefix = "$";
/// if msg.content.starts_with(my_cool_prefix) {
/// return Ok(Some(msg.content.split_at(my_cool_prefix.len())));
/// }
/// Ok(None)
/// # })), ..Default::default() };
/// ```
#[derivative(Debug = "ignore")]
pub stripped_dynamic_prefix: Option<
for<'a> fn(
&'a serenity::Context,
&'a serenity::Message,
&'a U,
) -> BoxFuture<'a, Result<Option<(&'a str, &'a str)>, E>>,
>,
/// Treat a bot mention (a ping) like a prefix
pub mention_as_prefix: bool,
/// If Some, the framework will react to message edits by editing the corresponding bot response
/// with the new result.
pub edit_tracker: Option<std::sync::Arc<std::sync::RwLock<crate::EditTracker>>>,
/// If the user makes a typo in their message and a subsequent edit creates a valid invocation,
/// the bot will execute the command if this attribute is set. [`Self::edit_tracker`] does not
/// need to be set for this.
///
/// That does not mean that any subsequent edits will also trigger execution. For that,
/// see [`crate::Command::invoke_on_edit`].
///
/// Note: only has an effect if [`Self::edit_tracker`] is set.
pub execute_untracked_edits: bool,
/// Whether to ignore message edits on messages that have not yet been responded to.
///
/// This is the case if the message edit happens before a command has sent a response, or if the
/// command does not send a response at all.
pub ignore_edits_if_not_yet_responded: bool,
/// Whether commands in messages emitted by this bot itself should be executed as well.
pub execute_self_messages: bool,
/// Whether to ignore messages from bots for command invoking. Default `true`
pub ignore_bots: bool,
/// Whether to ignore commands contained within thread creation messages. Default `true`
pub ignore_thread_creation: bool,
/// Whether command names should be compared case-insensitively.
pub case_insensitive_commands: bool,
/// Callback for all non-command messages. Useful if you want to run code on any message that
/// is not a command
pub non_command_message: Option<
for<'a> fn(
&'a crate::FrameworkContext<'a, U, E>,
&'a serenity::Context,
&'a serenity::Message,
) -> crate::BoxFuture<'a, Result<(), E>>,
>,
/* // TODO: implement
/// Whether to invoke help command when someone sends a message with just a bot mention
pub help_when_mentioned: bool,
/// The bot's general help command. Currently used for [`Self::help_when_mentioned`].
pub help_commmand: Option<Command<U, E>>,
// /// The bot's help command for individial commands. Currently used when a command group without
// /// any specific subcommand is invoked. This command is expected to take the command name as a
// /// single parameter
// pub command_specific_help_commmand: Option<Command<U, E>>, */
// #[non_exhaustive] forbids struct update syntax for ?? reason
#[doc(hidden)]
pub __non_exhaustive: (),
}
impl<U, E> Default for PrefixFrameworkOptions<U, E> {
fn default() -> Self {
Self {
prefix: None,
additional_prefixes: Vec::new(),
dynamic_prefix: None,
stripped_dynamic_prefix: None,
mention_as_prefix: true,
edit_tracker: None,
execute_untracked_edits: true,
ignore_edits_if_not_yet_responded: false,
execute_self_messages: false,
ignore_bots: true,
ignore_thread_creation: true,
case_insensitive_commands: true,
non_command_message: None,
// help_when_mentioned: true,
// help_commmand: None,
// command_specific_help_commmand: None,
__non_exhaustive: (),
}
}
}