serenity/utils/argument_convert/
mod.rs

1mod member;
2pub use member::*;
3
4mod message;
5pub use message::*;
6
7mod user;
8pub use user::*;
9
10mod channel;
11pub use channel::*;
12
13// From HTTP you can only get PartialGuild; for Guild you need gateway and cache
14#[cfg(feature = "cache")]
15mod guild;
16#[cfg(feature = "cache")]
17pub use guild::*;
18
19mod role;
20pub use role::*;
21
22mod emoji;
23pub use emoji::*;
24
25use super::DOMAINS;
26use crate::model::prelude::*;
27use crate::prelude::*;
28
29/// Parse a value from a string in context of a received message.
30///
31/// This trait is a superset of [`std::str::FromStr`]. The difference is that this trait aims to
32/// support serenity-specific Discord types like [`Member`] or [`Message`].
33///
34/// Trait implementations may do network requests as part of their parsing procedure.
35///
36/// Useful for implementing argument parsing in command frameworks.
37#[async_trait::async_trait]
38pub trait ArgumentConvert: Sized {
39    /// The associated error which can be returned from parsing.
40    type Err;
41
42    /// Parses a string `s` as a command parameter of this type.
43    async fn convert(
44        ctx: impl CacheHttp,
45        guild_id: Option<GuildId>,
46        channel_id: Option<ChannelId>,
47        s: &str,
48    ) -> Result<Self, Self::Err>;
49}
50
51#[async_trait::async_trait]
52impl<T: std::str::FromStr> ArgumentConvert for T {
53    type Err = <T as std::str::FromStr>::Err;
54
55    async fn convert(
56        _: impl CacheHttp,
57        _: Option<GuildId>,
58        _: Option<ChannelId>,
59        s: &str,
60    ) -> Result<Self, Self::Err> {
61        T::from_str(s)
62    }
63}
64
65// The following few parse_XXX methods are in here (parse.rs) because they need to be gated behind
66// the model feature and it's just convenient to put them here for that
67
68/// Retrieves IDs from "{channel ID}-{message ID}" (retrieved by shift-clicking on "Copy ID").
69///
70/// If the string is invalid, None is returned.
71///
72/// # Examples
73/// ```rust
74/// use serenity::model::prelude::*;
75/// use serenity::utils::parse_message_id_pair;
76///
77/// assert_eq!(
78///     parse_message_id_pair("673965002805477386-842482646604972082"),
79///     Some((ChannelId::new(673965002805477386), MessageId::new(842482646604972082))),
80/// );
81/// assert_eq!(
82///     parse_message_id_pair("673965002805477386-842482646604972082-472029906943868929"),
83///     None,
84/// );
85/// ```
86#[must_use]
87pub fn parse_message_id_pair(s: &str) -> Option<(ChannelId, MessageId)> {
88    let mut parts = s.splitn(2, '-');
89    let channel_id = parts.next()?.parse().ok()?;
90    let message_id = parts.next()?.parse().ok()?;
91    Some((channel_id, message_id))
92}
93
94/// Retrieves guild, channel, and message ID from a message URL.
95///
96/// If the URL is malformed, None is returned.
97///
98/// # Examples
99/// ```rust
100/// use serenity::model::prelude::*;
101/// use serenity::utils::parse_message_url;
102///
103/// assert_eq!(
104///     parse_message_url(
105///         "https://discord.com/channels/381880193251409931/381880193700069377/806164913558781963"
106///     ),
107///     Some((
108///         GuildId::new(381880193251409931),
109///         ChannelId::new(381880193700069377),
110///         MessageId::new(806164913558781963),
111///     )),
112/// );
113/// assert_eq!(
114///     parse_message_url(
115///         "https://canary.discord.com/channels/381880193251409931/381880193700069377/806164913558781963"
116///     ),
117///     Some((
118///         GuildId::new(381880193251409931),
119///         ChannelId::new(381880193700069377),
120///         MessageId::new(806164913558781963),
121///     )),
122/// );
123/// assert_eq!(parse_message_url("https://google.com"), None);
124/// ```
125#[must_use]
126pub fn parse_message_url(s: &str) -> Option<(GuildId, ChannelId, MessageId)> {
127    for domain in DOMAINS {
128        if let Some(parts) = s.strip_prefix(&format!("https://{domain}/channels/")) {
129            let mut parts = parts.splitn(3, '/');
130
131            let guild_id = parts.next()?.parse().ok()?;
132            let channel_id = parts.next()?.parse().ok()?;
133            let message_id = parts.next()?.parse().ok()?;
134            return Some((guild_id, channel_id, message_id));
135        }
136    }
137    None
138}