feat(tiktok): add tiktok handler

This commit is contained in:
Kristofers Solo 2025-10-27 10:59:49 +02:00
parent 881d730483
commit 24bfeb4efc
Signed by: kristoferssolo
GPG Key ID: 74FF8144483D82C8
5 changed files with 81 additions and 1 deletions

View File

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

View File

@ -128,6 +128,25 @@ pub async fn download_instagram(url: &str) -> Result<DownloadResult> {
run_command_in_tempdir("yt-dlp", &args_ref).await
}
#[cfg(feature = "tiktok")]
pub async fn download_tiktok(url: &str) -> Result<DownloadResult> {
let base_args = ["--extractor-args", "tiktok:"];
let mut args = base_args
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>();
if let Ok(cookies_path) = env::var("IG_SESSION_COOKIE_PATH") {
args.extend(["--cookies".into(), cookies_path]);
}
args.push(url.into());
let args_ref = args.iter().map(String::as_ref).collect::<Vec<_>>();
run_command_in_tempdir("yt-dlp", &args_ref).await
}
/// Download a URL with yt-dlp.
///
/// # Errors

View File

@ -1,5 +1,7 @@
#[cfg(feature = "instagram")]
mod instagram;
#[cfg(feature = "tiktok")]
mod tiktok;
#[cfg(feature = "youtube")]
mod youtube;
@ -8,6 +10,8 @@ use teloxide::{Bot, types::ChatId};
#[cfg(feature = "instagram")]
pub use instagram::InstagramHandler;
#[cfg(feature = "tiktok")]
pub use tiktok::TiktokHandler;
#[cfg(feature = "youtube")]
pub use youtube::YouTubeShortsHandler;

54
src/handlers/tiktok.rs Normal file
View File

@ -0,0 +1,54 @@
use crate::{
download::{download_tiktok, 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\.)?(?:vm|vt|tt|tik)\.tiktok\.com/([A-Za-z0-9_-]+)[/?#]?")
.expect("filed to compile regex")
})
}
/// Handler for Tiktok
#[derive(Clone, Default)]
pub struct TiktokHandler;
impl TiktokHandler {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self
}
}
#[async_trait::async_trait]
impl SocialHandler for TiktokHandler {
fn name(&self) -> &'static str {
"tiktok"
}
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 tiktok url");
let dr = download_tiktok(&url).await?;
process_download_result(bot, chat_id, dr).await
}
fn box_clone(&self) -> Box<dyn SocialHandler> {
Box::new(self.clone())
}
}

View File

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