feat(twitter): add handler

This commit is contained in:
Kristofers Solo 2025-10-27 11:23:28 +02:00
parent 0b6012e383
commit 40ae0b7371
Signed by: kristoferssolo
GPG Key ID: 74FF8144483D82C8
5 changed files with 82 additions and 3 deletions

View File

@ -30,10 +30,11 @@ tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
url = "2.5" url = "2.5"
[features] [features]
default = ["instagram", "youtube", "tiktok"] default = ["instagram", "tiktok", "twitter", "youtube"]
instagram = [] instagram = []
youtube = []
tiktok = [] tiktok = []
twitter = []
youtube = []
[lints.clippy] [lints.clippy]
pedantic = "warn" pedantic = "warn"

View File

@ -104,7 +104,7 @@ async fn run_command_in_tempdir(cmd: &str, args: &[&str]) -> Result<DownloadResu
}) })
} }
/// Download an Instagram shortcode using instaloader (wrapper). /// Download a Instagram URL with yt-dlp.
/// ///
/// # Errors /// # Errors
/// ///
@ -128,6 +128,11 @@ pub async fn download_instagram(url: &str) -> Result<DownloadResult> {
run_command_in_tempdir("yt-dlp", &args_ref).await run_command_in_tempdir("yt-dlp", &args_ref).await
} }
/// Download a Tiktok URL with yt-dlp.
///
/// # Errors
///
/// - Propagates `run_command_in_tempdir` errors.
#[cfg(feature = "tiktok")] #[cfg(feature = "tiktok")]
pub async fn download_tiktok(url: &str) -> Result<DownloadResult> { pub async fn download_tiktok(url: &str) -> Result<DownloadResult> {
let base_args = ["--extractor-args", "tiktok:"]; let base_args = ["--extractor-args", "tiktok:"];
@ -147,6 +152,17 @@ pub async fn download_tiktok(url: &str) -> Result<DownloadResult> {
run_command_in_tempdir("yt-dlp", &args_ref).await run_command_in_tempdir("yt-dlp", &args_ref).await
} }
/// Download a Twitter URL with yt-dlp.
///
/// # Errors
///
/// - Propagates `run_command_in_tempdir` errors.
#[cfg(feature = "twitter")]
pub async fn download_twitter(url: &str) -> Result<DownloadResult> {
let args = ["--extractor-args", "twitter:", url];
run_command_in_tempdir("yt-dlp", &args).await
}
/// Download a URL with yt-dlp. /// Download a URL with yt-dlp.
/// ///
/// # Errors /// # Errors

View File

@ -2,6 +2,8 @@
mod instagram; mod instagram;
#[cfg(feature = "tiktok")] #[cfg(feature = "tiktok")]
mod tiktok; mod tiktok;
#[cfg(feature = "twitter")]
mod twitter;
#[cfg(feature = "youtube")] #[cfg(feature = "youtube")]
mod youtube; mod youtube;
@ -12,6 +14,8 @@ use teloxide::{Bot, types::ChatId};
pub use instagram::InstagramHandler; pub use instagram::InstagramHandler;
#[cfg(feature = "tiktok")] #[cfg(feature = "tiktok")]
pub use tiktok::TiktokHandler; pub use tiktok::TiktokHandler;
#[cfg(feature = "twitter")]
pub use twitter::TwitterHandler;
#[cfg(feature = "youtube")] #[cfg(feature = "youtube")]
pub use youtube::YouTubeShortsHandler; pub use youtube::YouTubeShortsHandler;

56
src/handlers/twitter.rs Normal file
View File

@ -0,0 +1,56 @@
use crate::{
download::{download_twitter, process_download_result},
error::Result,
};
use regex::Regex;
use std::sync::OnceLock;
use teloxide::{Bot, types::ChatId};
use tracing::info;
use crate::handlers::SocialHandler;
static SHORTCODE_RE: OnceLock<Regex> = OnceLock::new();
fn shortcode_regex() -> &'static Regex {
SHORTCODE_RE.get_or_init(|| {
Regex::new(
r"https?://(?:www\.)?twitter\.com/([A-Za-z0-9_]+(?:/[A-Za-z0-9_]+)?)/status/(\d{1,20})",
)
.expect("filed to compile regex")
})
}
/// Handler for Tiktok
#[derive(Clone, Default)]
pub struct TwitterHandler;
impl TwitterHandler {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self
}
}
#[async_trait::async_trait]
impl SocialHandler for TwitterHandler {
fn name(&self) -> &'static str {
"twitter"
}
fn try_extract(&self, text: &str) -> Option<String> {
shortcode_regex()
.captures(text)
.and_then(|c| c.get(0).map(|m| m.as_str().to_owned()))
}
async fn handle(&self, bot: &Bot, chat_id: ChatId, url: String) -> Result<()> {
info!(handler = %self.name(), url = %url, "handling twitter url");
let dr = download_twitter(&url).await?;
process_download_result(bot, chat_id, dr).await
}
fn box_clone(&self) -> Box<dyn SocialHandler> {
Box::new(self.clone())
}
}

View File

@ -34,6 +34,8 @@ async fn main() -> color_eyre::Result<()> {
Arc::new(tg_relay_rs::handlers::YouTubeShortsHandler), Arc::new(tg_relay_rs::handlers::YouTubeShortsHandler),
#[cfg(feature = "tiktok")] #[cfg(feature = "tiktok")]
Arc::new(tg_relay_rs::handlers::TiktokHandler), Arc::new(tg_relay_rs::handlers::TiktokHandler),
#[cfg(feature = "twitter")]
Arc::new(tg_relay_rs::handlers::TwitterHandler),
]; ];
teloxide::repl(bot.clone(), move |bot: Bot, msg: Message| { teloxide::repl(bot.clone(), move |bot: Bot, msg: Message| {