Skip to content

Commit d7c5ee3

Browse files
Add a function for parsing a channel ID from a channel URL (#3075)
Similar to message URLs, Discord also provides URLs for guild channels. Additionally, the function is added as an alternative for parsing a `Channel` from a string. Private channels are not affected by this change.
1 parent 6901986 commit d7c5ee3

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

src/utils/argument_convert/channel.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ async fn lookup_channel_global(
4545
guild_id: Option<GuildId>,
4646
s: &str,
4747
) -> Result<Channel, ChannelParseError> {
48-
if let Some(channel_id) = s.parse().ok().or_else(|| crate::utils::parse_channel_mention(s)) {
48+
if let Some(channel_id) = s
49+
.parse()
50+
.ok()
51+
.or_else(|| crate::utils::parse_channel_mention(s))
52+
.or_else(|| crate::utils::parse_channel_url(s).map(|(_, channel_id)| channel_id))
53+
{
4954
return channel_id.to_channel(ctx, guild_id).await.map_err(ChannelParseError::Http);
5055
}
5156

src/utils/argument_convert/mod.rs

+38
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,44 @@ pub fn parse_message_url(s: &str) -> Option<(GuildId, ChannelId, MessageId)> {
140140
None
141141
}
142142

143+
/// Retrieves guild, and channel ID from a channel URL.
144+
///
145+
/// If the URL is malformed, None is returned.
146+
///
147+
/// # Examples
148+
/// ```rust
149+
/// use serenity::model::prelude::*;
150+
/// use serenity::utils::parse_channel_url;
151+
///
152+
/// assert_eq!(
153+
/// parse_channel_url("https://discord.com/channels/381880193251409931/381880193700069377"),
154+
/// Some((GuildId::new(381880193251409931), ChannelId::new(381880193700069377),)),
155+
/// );
156+
/// assert_eq!(
157+
/// parse_channel_url(
158+
/// "https://canary.discord.com/channels/381880193251409931/381880193700069377"
159+
/// ),
160+
/// Some((GuildId::new(381880193251409931), ChannelId::new(381880193700069377),)),
161+
/// );
162+
/// assert_eq!(parse_channel_url("https://google.com"), None);
163+
/// ```
164+
#[must_use]
165+
pub fn parse_channel_url(s: &str) -> Option<(GuildId, ChannelId)> {
166+
use aformat::{aformat, CapStr};
167+
168+
for domain in DOMAINS {
169+
let prefix = aformat!("https://{}/channels/", CapStr::<MAX_DOMAIN_LEN>(domain));
170+
if let Some(parts) = s.strip_prefix(prefix.as_str()) {
171+
let mut parts = parts.splitn(2, '/');
172+
173+
let guild_id = parts.next()?.parse().ok()?;
174+
let channel_id = parts.next()?.parse().ok()?;
175+
return Some((guild_id, channel_id));
176+
}
177+
}
178+
None
179+
}
180+
143181
const MAX_DOMAIN_LEN: usize = {
144182
let mut max_len = 0;
145183
let mut i = 0;

0 commit comments

Comments
 (0)