diff --git a/Dockerfile b/Dockerfile index 29d38f8..9092b06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,9 @@ RUN cargo install cargo-chef ########### FROM chef AS planner -COPY src/ ./src COPY Cargo.toml . COPY Cargo.lock . +COPY src/ ./src RUN cargo chef prepare --recipe-path recipe.json @@ -37,9 +37,9 @@ FROM chef AS builder COPY --from=planner /insomnia_bot/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json -COPY src/ ./src COPY Cargo.toml . COPY Cargo.lock . +COPY src/ ./src RUN cargo build --release diff --git a/src/link_embed/media/dubz.rs b/src/link_embed/media/dubz.rs new file mode 100644 index 0000000..96ae5ec --- /dev/null +++ b/src/link_embed/media/dubz.rs @@ -0,0 +1,18 @@ +use once_cell::sync::Lazy; +use regex::Regex; + +use super::MediaLinkResolver; + +pub struct DubzMediaResolver; + +impl MediaLinkResolver for DubzMediaResolver { + async fn resolve(url: &reqwest::Url) -> Option> { + static PATH_RE: Lazy = Lazy::new(|| Regex::new(r"^/c/(?P\w+)").unwrap()); + PATH_RE + .captures(url.path()) + .map(|cap| cap.name("id").unwrap()) + .map(|id| { + format!("https://dubzalt.com/storage/videos/{}.mp4", id.as_str()).into_boxed_str() + }) + } +} diff --git a/src/link_embed/media/imgur.rs b/src/link_embed/media/imgur.rs new file mode 100644 index 0000000..fef244f --- /dev/null +++ b/src/link_embed/media/imgur.rs @@ -0,0 +1,18 @@ +use once_cell::sync::Lazy; +use regex::Regex; + +use super::MediaLinkResolver; + +pub struct ImgurMediaResolver; + +impl MediaLinkResolver for ImgurMediaResolver { + async fn resolve(url: &reqwest::Url) -> Option> { + static PATH_RE: Lazy = Lazy::new(|| Regex::new(r"^/(?P\w+)").unwrap()); + PATH_RE + .captures(url.path()) + .map(|cap| cap.name("id").unwrap()) + .map(|id| { + format!("https://i.imgur.com/{}.mp4", id.as_str()).into_boxed_str() + }) + } +} diff --git a/src/link_embed/media/mod.rs b/src/link_embed/media/mod.rs new file mode 100644 index 0000000..8c15686 --- /dev/null +++ b/src/link_embed/media/mod.rs @@ -0,0 +1,20 @@ +use self::dubz::DubzMediaResolver; +use self::imgur::ImgurMediaResolver; +use self::streamable::StreamableMediaResolver; + +mod dubz; +mod streamable; +mod imgur; + +pub trait MediaLinkResolver { + async fn resolve(url: &reqwest::Url) -> Option>; +} + +pub(crate) async fn resolve_media_link(url: &reqwest::Url) -> Option> { + match url.domain() { + Some("streamable.com") => StreamableMediaResolver::resolve(url).await, + Some("dubz.link") => DubzMediaResolver::resolve(url).await, + Some("imgur.com") | Some("i.imgur.com") => ImgurMediaResolver::resolve(url).await, + _ => None, + } +} diff --git a/src/link_embed/media/streamable.rs b/src/link_embed/media/streamable.rs new file mode 100644 index 0000000..148219d --- /dev/null +++ b/src/link_embed/media/streamable.rs @@ -0,0 +1,18 @@ +use once_cell::sync::Lazy; +use regex::Regex; + +use super::MediaLinkResolver; + +pub struct StreamableMediaResolver; + +impl MediaLinkResolver for StreamableMediaResolver { + async fn resolve(url: &reqwest::Url) -> Option> { + static PATH_RE: Lazy = Lazy::new(|| Regex::new(r"^/(?P\w+)").unwrap()); + PATH_RE + .captures(url.path()) + .map(|cap| cap.name("id").unwrap()) + .map(|id| { + format!("https://streamable.com/{}", id.as_str()).into_boxed_str() + }) + } +} diff --git a/src/link_embed/mod.rs b/src/link_embed/mod.rs index 14f5065..98e51d6 100644 --- a/src/link_embed/mod.rs +++ b/src/link_embed/mod.rs @@ -1,5 +1,6 @@ mod reddit; mod tweet; +mod media; use std::sync::Arc; diff --git a/src/link_embed/reddit.rs b/src/link_embed/reddit.rs index b26b18d..6acb54a 100644 --- a/src/link_embed/reddit.rs +++ b/src/link_embed/reddit.rs @@ -9,14 +9,16 @@ use time::{Duration, OffsetDateTime}; use tokio::sync::Mutex; use super::ReplacedLink; +use crate::link_embed::media::resolve_media_link; use crate::CLIENT; static REDDIT_RE: Lazy = Lazy::new(|| { - Regex::new(r"\bhttps?://(?:(?:www|old|new)\.)?reddit\.com/r/(?P\w+)\b(?:/comments/(?P\w+\b)(?:/[^/]+/(?P\w+\b))?)").unwrap() + Regex::new(r"\bhttps?://(?:(?:www|old|new)\.)?reddit\.com(/r/\w+)?\b(?:/comments/(?P\w+\b)(?:/[^/]+/(?P\w+\b))?)").unwrap() }); static REDDIT_SHARE_RE: Lazy = Lazy::new(|| { - Regex::new(r"\bhttps?://(?:(?:www|old|new)\.)?reddit\.com/r/(?P\w+)\b/s/(?P\w+)") - .unwrap() + let share_re = r"\bhttps?://(?:(?:www|old|new)\.)?reddit\.com/r/\w+/s/\w+"; + let vreddit_re = r"\bhttps?://v\.redd\.it/\w+"; + Regex::new(&format!("{}|{}", share_re, vreddit_re)).unwrap() }); static REDDIT_ACCESS_TOKEN: Lazy = Lazy::new(AccessToken::default); @@ -28,7 +30,6 @@ enum RedditLink { media: Option>, }, Comment { - subreddit: Box, submission_id: Box, comment_id: Box, }, @@ -41,13 +42,10 @@ impl RedditLink { format!("https://rxddit.com/{id}").into_boxed_str() } RedditLink::Comment { - subreddit, submission_id, comment_id, - } => { - format!("https://rxddit.com/r/{subreddit}/comments/{submission_id}/_/{comment_id}") - .into_boxed_str() - } + } => format!("https://rxddit.com/comments/{submission_id}/_/{comment_id}") + .into_boxed_str(), } } @@ -125,13 +123,12 @@ async fn reddit_share_links(text: &str) -> Vec { } async fn match_reddit_link(m: Captures<'_>) -> Option { - match (m.name("subreddit"), m.name("submission"), m.name("comment")) { - (Some(subreddit), Some(submission_id), Some(comment_id)) => Some(RedditLink::Comment { - subreddit: subreddit.as_str().into(), + match (m.name("submission"), m.name("comment")) { + (Some(submission_id), Some(comment_id)) => Some(RedditLink::Comment { submission_id: submission_id.as_str().into(), comment_id: comment_id.as_str().into(), }), - (_, Some(submission_id), _) => Some(RedditLink::Submission { + (Some(submission_id), _) => Some(RedditLink::Submission { id: submission_id.as_str().into(), media: reddit_post_media(submission_id.as_str()).await, }), @@ -188,16 +185,12 @@ async fn reddit_post_media(submission_id: &str) -> Option> { .first() .and_then(|r| r.data.children.first()) .and_then(|c| c.data.url.clone()) - .and_then(|url| reqwest::Url::parse(&url).ok()) - .and_then(|url| media_url(&url)); - - media_url -} + .and_then(|url| reqwest::Url::parse(&url).ok()); -fn media_url(url: &reqwest::Url) -> Option> { - match url.domain() { - Some("streamable.com") => Some(url.as_str().into()), - _ => None, + if let Some(media_url) = media_url { + resolve_media_link(&media_url).await + } else { + None } }