From 3ff112b1cd123d89023a9ba08ffee450ce2a88d9 Mon Sep 17 00:00:00 2001 From: Fabian Montero Date: Sat, 16 May 2026 14:25:20 -0600 Subject: [PATCH] make nix module --- CLAUDE.md | 31 +++++++++++++++++++++++----- Cargo.lock | 23 +++++++++++---------- Cargo.toml | 3 ++- src/config.rs | 29 ++++++++++++++++---------- src/handlers/instagram.rs | 43 ++++++++++++++++++++++++++++++++------- src/main.rs | 2 ++ 6 files changed, 96 insertions(+), 35 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 015d75e..51bce92 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# Telegram Bot +# Task Force Beta Bot General-purpose Telegram bot with modular handler architecture. @@ -6,17 +6,38 @@ General-purpose Telegram bot with modular handler architecture. ```bash cargo build --release -cargo run ``` -## Configuration +## Usage -Bot token must be placed at: `/var/trust/instagram_link_stripper/telegram_token` +``` +task_force_beta_bot +``` + +Both arguments are required: +- `token_file` - Path to file containing the Telegram bot token +- `allowed_chats_file` - Path to file containing allowed chat IDs + +Example: +```bash +cargo run -- /path/to/token /path/to/allowed_chats +``` + +### Allowed Chats File + +One chat ID per line. Lines starting with `#` are comments. + +``` +# My group +-100123456789 +``` + +The bot ignores messages from chats not in this list. ## Architecture - `src/main.rs` - Entry point, dispatcher setup -- `src/config.rs` - Token loading from /var/trust/ +- `src/config.rs` - Token and allowed chats loading from CLI arguments - `src/handlers/` - Message handlers (modular, each feature in own file) - `src/utils/` - Shared utilities diff --git a/Cargo.lock b/Cargo.lock index d953e5d..352a3df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -757,17 +757,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "instagram_link_stripper" -version = "0.1.0" -dependencies = [ - "log", - "pretty_env_logger", - "teloxide", - "tokio", - "url", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -1536,6 +1525,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e" +[[package]] +name = "task_force_beta_bot" +version = "0.1.0" +dependencies = [ + "log", + "once_cell", + "pretty_env_logger", + "teloxide", + "tokio", + "url", +] + [[package]] name = "teloxide" version = "0.17.0" diff --git a/Cargo.toml b/Cargo.toml index 52c275e..f5e3fe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "instagram_link_stripper" +name = "task_force_beta_bot" version = "0.1.0" edition = "2024" @@ -9,3 +9,4 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros"] } url = "2" log = "0.4" pretty_env_logger = "0.5" +once_cell = "1" diff --git a/src/config.rs b/src/config.rs index 881a316..3439515 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,18 +1,25 @@ use std::fs; -use std::path::Path; -const TOKEN_PATH: &str = "/var/trust/instagram_link_stripper/telegram_token"; +fn get_required_arg(n: usize, name: &str) -> String { + std::env::args() + .nth(n) + .unwrap_or_else(|| panic!("Missing required argument {}: {}", n, name)) +} pub fn load_token() -> String { - let path = Path::new(TOKEN_PATH); - - fs::read_to_string(path) - .unwrap_or_else(|e| { - panic!( - "Failed to read Telegram bot token from {}: {}", - TOKEN_PATH, e - ) - }) + let path = get_required_arg(1, "token_file"); + fs::read_to_string(&path) + .unwrap_or_else(|_| panic!("Failed to read token")) .trim() .to_string() } + +pub fn load_allowed_chats() -> Vec { + let path = get_required_arg(2, "allowed_chats_file"); + fs::read_to_string(&path) + .unwrap_or_else(|_| panic!("Failed to read allowed chats")) + .lines() + .filter(|line| !line.trim().is_empty() && !line.trim().starts_with('#')) + .filter_map(|line| line.trim().parse::().ok()) + .collect() +} diff --git a/src/handlers/instagram.rs b/src/handlers/instagram.rs index 9e1b5c3..aed04ad 100644 --- a/src/handlers/instagram.rs +++ b/src/handlers/instagram.rs @@ -1,22 +1,48 @@ +use once_cell::sync::Lazy; use teloxide::dispatching::UpdateHandler; use teloxide::prelude::*; use teloxide::types::ReplyParameters; +use url::Url; +use crate::config; use crate::utils::url::strip_tracking_params; -const INSTAGRAM_PATTERN: &str = "instagram.com/"; +static ALLOWED_CHATS: Lazy> = Lazy::new(config::load_allowed_chats); + +/// Force initialization and log the allowed chat IDs. Call at startup. +pub fn init() { + let chats = &*ALLOWED_CHATS; + log::info!("Loaded {} allowed chat IDs", chats.len()); + for chat_id in chats { + log::debug!(" Allowed chat: {}", chat_id); + } +} + +fn is_chat_allowed(chat_id: i64) -> bool { + let allowed = ALLOWED_CHATS.contains(&chat_id); + if !allowed { + log::debug!("Message from unauthorized chat {}, ignoring", chat_id); + } + allowed +} fn get_message_text(msg: &Message) -> Option<&str> { msg.text().or_else(|| msg.caption()) } -fn contains_instagram_link(text: &str) -> bool { - text.contains(INSTAGRAM_PATTERN) +fn is_instagram_host(url: &Url) -> bool { + url.host_str() + .map(|h| h == "instagram.com" || h == "www.instagram.com") + .unwrap_or(false) } fn extract_instagram_urls(text: &str) -> Vec<&str> { text.split_whitespace() - .filter(|word| word.contains(INSTAGRAM_PATTERN)) + .filter(|word| { + Url::parse(word) + .map(|u| is_instagram_host(&u)) + .unwrap_or(false) + }) .collect() } @@ -47,10 +73,13 @@ async fn handle_instagram_links( Ok(()) } +fn contains_instagram_url(msg: &Message) -> bool { + get_message_text(msg).map_or(false, |text| !extract_instagram_urls(text).is_empty()) +} + pub fn handler() -> UpdateHandler> { Update::filter_message() - .filter(|msg: Message| { - get_message_text(&msg).map_or(false, contains_instagram_link) - }) + .filter(|msg: Message| is_chat_allowed(msg.chat.id.0)) + .filter(|msg: Message| contains_instagram_url(&msg)) .endpoint(handle_instagram_links) } diff --git a/src/main.rs b/src/main.rs index 875e93f..436b982 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,8 @@ async fn main() { let token = config::load_token(); let bot = Bot::new(token); + handlers::instagram::init(); + Dispatcher::builder(bot, handlers::schema()) .enable_ctrlc_handler() .build()