make nix module
This commit is contained in:
parent
a4b8bdd8c0
commit
3ff112b1cd
6 changed files with 96 additions and 35 deletions
31
CLAUDE.md
31
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 <token_file> <allowed_chats_file>
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
23
Cargo.lock
generated
23
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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<i64> {
|
||||
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::<i64>().ok())
|
||||
.collect()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Vec<i64>> = 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<Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue