cargo: gate telegram behind a default feature
This commit is contained in:
parent
de53e51c60
commit
9f0e3aae6d
6 changed files with 45 additions and 15 deletions
|
|
@ -6,10 +6,14 @@ description = "A configurable, self-hosted guestbook for the web, allowing visit
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.ily.rs/lew/guestbook"
|
repository = "https://git.ily.rs/lew/guestbook"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["telegram"]
|
||||||
|
telegram = ["dep:teloxide"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.8"
|
axum = "0.8"
|
||||||
tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "net"] }
|
tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "net"] }
|
||||||
teloxide = { version = "0.13", features = ["macros"] }
|
teloxide = { version = "0.13", features = ["macros"], optional = true }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
|
|
@ -19,6 +23,9 @@ base64 = "0.22"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tower = { version = "0.5", features = ["util"] }
|
tower = { version = "0.5", features = ["util"] }
|
||||||
http-body-util = "0.1"
|
http-body-util = "0.1"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ pub struct Config {
|
||||||
pub data_dir: PathBuf,
|
pub data_dir: PathBuf,
|
||||||
pub site_title: String,
|
pub site_title: String,
|
||||||
|
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
pub telegram_bot_token: Option<String>,
|
pub telegram_bot_token: Option<String>,
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
pub telegram_chat_id: Option<i64>,
|
pub telegram_chat_id: Option<i64>,
|
||||||
pub enable_honeypot: bool,
|
pub enable_honeypot: bool,
|
||||||
pub max_name_length: usize,
|
pub max_name_length: usize,
|
||||||
|
|
@ -67,7 +69,9 @@ impl Config {
|
||||||
.unwrap_or_else(|_| PathBuf::from("./data")),
|
.unwrap_or_else(|_| PathBuf::from("./data")),
|
||||||
site_title: env::var("BOOK_SITE_TITLE").unwrap_or_else(|_| "guestbook".into()),
|
site_title: env::var("BOOK_SITE_TITLE").unwrap_or_else(|_| "guestbook".into()),
|
||||||
|
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
telegram_bot_token: env::var("BOOK_TELEGRAM_BOT_TOKEN").ok(),
|
telegram_bot_token: env::var("BOOK_TELEGRAM_BOT_TOKEN").ok(),
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
telegram_chat_id: env::var("BOOK_TELEGRAM_CHAT_ID")
|
telegram_chat_id: env::var("BOOK_TELEGRAM_CHAT_ID")
|
||||||
.ok()
|
.ok()
|
||||||
.map(|v| v.parse().map_err(|_| "BOOK_TELEGRAM_CHAT_ID must be an integer"))
|
.map(|v| v.parse().map_err(|_| "BOOK_TELEGRAM_CHAT_ID must be an integer"))
|
||||||
|
|
@ -188,7 +192,9 @@ mod tests {
|
||||||
assert_eq!(config.listen_addr(), "127.0.0.1:9999");
|
assert_eq!(config.listen_addr(), "127.0.0.1:9999");
|
||||||
assert_eq!(config.data_dir, PathBuf::from("/tmp/gb"));
|
assert_eq!(config.data_dir, PathBuf::from("/tmp/gb"));
|
||||||
assert_eq!(config.site_title, "test.rs");
|
assert_eq!(config.site_title, "test.rs");
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
assert_eq!(config.telegram_bot_token.as_deref(), Some("123:ABC"));
|
assert_eq!(config.telegram_bot_token.as_deref(), Some("123:ABC"));
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
assert_eq!(config.telegram_chat_id, Some(12345));
|
assert_eq!(config.telegram_chat_id, Some(12345));
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
|
|
@ -221,7 +227,9 @@ mod tests {
|
||||||
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
||||||
|
|
||||||
let config = Config::from_env().unwrap();
|
let config = Config::from_env().unwrap();
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
assert!(config.telegram_bot_token.is_none());
|
assert!(config.telegram_bot_token.is_none());
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
assert!(config.telegram_chat_id.is_none());
|
assert!(config.telegram_chat_id.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ pub struct EntryMeta {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
|
#[cfg_attr(not(feature = "telegram"), allow(dead_code))]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub meta: EntryMeta,
|
pub meta: EntryMeta,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
|
|
@ -49,6 +50,7 @@ impl Entry {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "telegram", test))]
|
||||||
/// Return the short ID (UUID portion after the underscore).
|
/// Return the short ID (UUID portion after the underscore).
|
||||||
pub fn short_id(&self) -> &str {
|
pub fn short_id(&self) -> &str {
|
||||||
self.id.split('_').last().unwrap_or(&self.id)
|
self.id.split('_').last().unwrap_or(&self.id)
|
||||||
|
|
@ -103,6 +105,7 @@ pub fn read_approved(dir: &Path) -> Vec<Entry> {
|
||||||
read_by_status(dir, Status::Approved)
|
read_by_status(dir, Status::Approved)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "telegram", test))]
|
||||||
/// Find a single entry by short ID (the UUID portion after the underscore).
|
/// Find a single entry by short ID (the UUID portion after the underscore).
|
||||||
pub fn find_entry(dir: &Path, short_id: &str) -> Result<Entry, String> {
|
pub fn find_entry(dir: &Path, short_id: &str) -> Result<Entry, String> {
|
||||||
let read_dir = std::fs::read_dir(dir).map_err(|e| e.to_string())?;
|
let read_dir = std::fs::read_dir(dir).map_err(|e| e.to_string())?;
|
||||||
|
|
@ -119,6 +122,7 @@ pub fn find_entry(dir: &Path, short_id: &str) -> Result<Entry, String> {
|
||||||
Err("Not found.".into())
|
Err("Not found.".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "telegram", test))]
|
||||||
/// Delete an entry and its associated media files.
|
/// Delete an entry and its associated media files.
|
||||||
/// `data_dir` is the parent directory containing entries/, drawings/, and voice_notes/.
|
/// `data_dir` is the parent directory containing entries/, drawings/, and voice_notes/.
|
||||||
pub fn delete_entry(data_dir: &Path, short_id: &str) -> Result<String, String> {
|
pub fn delete_entry(data_dir: &Path, short_id: &str) -> Result<String, String> {
|
||||||
|
|
@ -148,6 +152,7 @@ pub fn delete_entry(data_dir: &Path, short_id: &str) -> Result<String, String> {
|
||||||
Ok(entry.meta.name.clone())
|
Ok(entry.meta.name.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "telegram", test))]
|
||||||
/// Find an entry file by short ID prefix and update its status.
|
/// Find an entry file by short ID prefix and update its status.
|
||||||
pub fn set_status(dir: &Path, short_id: &str, status: Status) -> Result<String, String> {
|
pub fn set_status(dir: &Path, short_id: &str, status: Status) -> Result<String, String> {
|
||||||
let mut entry = find_entry(dir, short_id)?;
|
let mut entry = find_entry(dir, short_id)?;
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -1,11 +1,11 @@
|
||||||
mod config;
|
mod config;
|
||||||
mod entries;
|
mod entries;
|
||||||
mod render;
|
mod render;
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
mod telegram;
|
mod telegram;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use teloxide::prelude::*;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
|
@ -18,16 +18,18 @@ async fn main() {
|
||||||
|
|
||||||
std::fs::create_dir_all(&entries_dir).expect("failed to create entries directory");
|
std::fs::create_dir_all(&entries_dir).expect("failed to create entries directory");
|
||||||
|
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel::<(entries::Entry, Option<Vec<u8>>, Option<Vec<u8>>)>(32);
|
let (tx, _rx) = tokio::sync::mpsc::channel::<(entries::Entry, Option<Vec<u8>>, Option<Vec<u8>>)>(32);
|
||||||
|
|
||||||
// Spawn telegram tasks if configured
|
#[cfg(feature = "telegram")]
|
||||||
|
{
|
||||||
|
use teloxide::prelude::*;
|
||||||
match (&config.telegram_bot_token, config.telegram_chat_id) {
|
match (&config.telegram_bot_token, config.telegram_chat_id) {
|
||||||
(Some(token), Some(chat_id)) => {
|
(Some(token), Some(chat_id)) => {
|
||||||
let chat_id = ChatId(chat_id);
|
let chat_id = ChatId(chat_id);
|
||||||
let bot = Bot::new(token);
|
let bot = Bot::new(token);
|
||||||
|
|
||||||
let notify_bot = bot.clone();
|
let notify_bot = bot.clone();
|
||||||
tokio::spawn(telegram::notification_task(notify_bot, chat_id, rx));
|
tokio::spawn(telegram::notification_task(notify_bot, chat_id, _rx));
|
||||||
|
|
||||||
let cmd_data_dir = config.data_dir.clone();
|
let cmd_data_dir = config.data_dir.clone();
|
||||||
tokio::spawn(telegram::bot_task(bot, chat_id, cmd_data_dir));
|
tokio::spawn(telegram::bot_task(bot, chat_id, cmd_data_dir));
|
||||||
|
|
@ -36,6 +38,10 @@ async fn main() {
|
||||||
tracing::info!("telegram not configured, moderation notifications disabled");
|
tracing::info!("telegram not configured, moderation notifications disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "telegram"))]
|
||||||
|
tracing::info!("compiled without telegram support");
|
||||||
|
|
||||||
let state = Arc::new(web::AppState { config, tx });
|
let state = Arc::new(web::AppState { config, tx });
|
||||||
let app = web::router(state);
|
let app = web::router(state);
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,9 @@ mod tests {
|
||||||
data_dir: PathBuf::from("./data"),
|
data_dir: PathBuf::from("./data"),
|
||||||
site_title: "test".into(),
|
site_title: "test".into(),
|
||||||
|
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
telegram_bot_token: None,
|
telegram_bot_token: None,
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
telegram_chat_id: None,
|
telegram_chat_id: None,
|
||||||
enable_honeypot: true,
|
enable_honeypot: true,
|
||||||
max_name_length: 0,
|
max_name_length: 0,
|
||||||
|
|
|
||||||
|
|
@ -345,7 +345,9 @@ mod tests {
|
||||||
data_dir: dir.to_path_buf(),
|
data_dir: dir.to_path_buf(),
|
||||||
site_title: "test".into(),
|
site_title: "test".into(),
|
||||||
|
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
telegram_bot_token: None,
|
telegram_bot_token: None,
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
telegram_chat_id: None,
|
telegram_chat_id: None,
|
||||||
enable_honeypot: true,
|
enable_honeypot: true,
|
||||||
max_name_length: 0,
|
max_name_length: 0,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue