Skip to content

Commit

Permalink
[Telegram] Allow specifying a global token and eliding tokens in notify
Browse files Browse the repository at this point in the history
  • Loading branch information
SpriteOvO committed Apr 4, 2024
1 parent 71ae6a1 commit 8e9e9c0
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 37 deletions.
124 changes: 90 additions & 34 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{borrow::Cow, collections::HashMap, env, fmt, time::Duration};

use anyhow::{anyhow, bail};
use anyhow::{anyhow, bail, ensure};
use once_cell::sync::OnceCell;
use serde::{de::DeserializeOwned, Deserialize};

Expand Down Expand Up @@ -59,6 +59,11 @@ impl Config {
}

fn validate(&self) -> anyhow::Result<()> {
// Validate platform_global
if let Some(platform) = &self.platform {
platform.validate()?;
}

// Validate notify ref
self.subscription
.values()
Expand All @@ -68,18 +73,36 @@ impl Config {
.collect::<Result<Vec<_>, _>>()?;

// Validate notify_map
self.notify_map.validate()?;
self.notify_map
.validate(self.platform.as_ref().unwrap_or(&PlatformGlobal::default()))?;

Ok(())
}
}

#[derive(Clone, Debug, PartialEq, Default, Deserialize)]
pub struct PlatformGlobal {
#[serde(rename = "Telegram")]
pub telegram: Option<PlatformGlobalTelegram>,
#[serde(rename = "Twitter")]
pub twitter: Option<PlatformGlobalTwitter>,
}

impl PlatformGlobal {
fn validate(&self) -> anyhow::Result<()> {
if let Some(telegram) = &self.telegram {
telegram.token.validate()?;
}
Ok(())
}
}

#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct PlatformGlobalTelegram {
#[serde(flatten)]
pub token: TelegramToken,
}

#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct PlatformGlobalTwitter {
pub nitter_host: String,
Expand Down Expand Up @@ -196,8 +219,10 @@ impl NotifyMap {
}
}

fn validate(&self) -> anyhow::Result<()> {
self.0.values().try_for_each(|notify| notify.validate())
fn validate(&self, global: &PlatformGlobal) -> anyhow::Result<()> {
self.0
.values()
.try_for_each(|notify| notify.validate(global))
}
}

Expand All @@ -216,9 +241,18 @@ pub enum Notify {
}

impl Notify {
fn validate(&self) -> anyhow::Result<()> {
fn validate(&self, global: &PlatformGlobal) -> anyhow::Result<()> {
match self {
Notify::Telegram(v) => v.validate().map_err(|err| anyhow!("[Telegram] {err}")),
Notify::Telegram(v) => match &v.token {
Some(token) => token.validate().map_err(|err| anyhow!("[Telegram] {err}")),
None => {
ensure!(
global.telegram.is_some(),
"[Telegram] both token in global and notify are missing"
);
Ok(())
}
},
}
}

Expand Down Expand Up @@ -249,7 +283,7 @@ pub struct NotifyTelegram {
pub chat: NotifyTelegramChat,
pub thread_id: Option<i64>,
#[serde(flatten)]
token: NotifyTelegramToken,
pub token: Option<TelegramToken>,
}

impl fmt::Display for NotifyTelegram {
Expand All @@ -262,33 +296,14 @@ impl fmt::Display for NotifyTelegram {
}
}

impl NotifyTelegram {
pub fn token(&self) -> anyhow::Result<Cow<str>> {
match &self.token {
NotifyTelegramToken::Token(token) => Ok(Cow::Borrowed(token)),
NotifyTelegramToken::TokenEnv(token_env) => Ok(Cow::Owned(env::var(token_env)?)),
}
}

fn validate(&self) -> anyhow::Result<()> {
match &self.token {
NotifyTelegramToken::Token(_) => Ok(()),
NotifyTelegramToken::TokenEnv(token_env) => match env::var(token_env) {
Ok(_) => Ok(()),
Err(err) => bail!("{err} ({token_env})"),
},
}
}
}

#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NotifyTelegramOverride {
#[serde(flatten)]
pub chat: Option<NotifyTelegramChat>,
pub thread_id: Option<i64>,
#[serde(flatten)]
token: Option<NotifyTelegramToken>,
token: Option<TelegramToken>,
}

impl Overridable for NotifyTelegram {
Expand All @@ -301,7 +316,7 @@ impl Overridable for NotifyTelegram {
Ok(Self {
chat: new.chat.unwrap_or(self.chat),
thread_id: new.thread_id.or(self.thread_id),
token: new.token.unwrap_or(self.token),
token: new.token.or(self.token),
})
}
}
Expand All @@ -324,11 +339,30 @@ impl fmt::Display for NotifyTelegramChat {

#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "snake_case")]
enum NotifyTelegramToken {
pub enum TelegramToken {
Token(String),
TokenEnv(String),
}

impl TelegramToken {
pub fn get(&self) -> anyhow::Result<Cow<str>> {
match &self {
Self::Token(token) => Ok(Cow::Borrowed(token)),
Self::TokenEnv(token_env) => Ok(Cow::Owned(env::var(token_env)?)),
}
}

fn validate(&self) -> anyhow::Result<()> {
match &self {
Self::Token(_) => Ok(()),
Self::TokenEnv(token_env) => match env::var(token_env) {
Ok(_) => Ok(()),
Err(err) => bail!("{err} ({token_env})"),
},
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -340,12 +374,15 @@ mod tests {
r#"
interval = '1min'
[platform."Telegram"]
token = "ttt"
[platform."Twitter"]
nitter_host = "https://nitter.example.com/"
[notify]
meow = { platform = "Telegram", id = 1234, thread_id = 123, token = "xxx" }
woof = { platform = "Telegram", id = 5678, thread_id = 900, token = "yyy" }
woof = { platform = "Telegram", id = 5678, thread_id = 900 }
[[subscription.meow]]
platform = { name = "bilibili.live", uid = 123456 }
Expand All @@ -365,6 +402,9 @@ notify = ["meow", "woof", { ref = "woof", id = 123 }]
Config {
interval: Duration::from_secs(60), // 1min
platform: Some(PlatformGlobal {
telegram: Some(PlatformGlobalTelegram {
token: TelegramToken::Token("ttt".into())
}),
twitter: Some(PlatformGlobalTwitter {
nitter_host: "https://nitter.example.com/".into()
})
Expand All @@ -375,15 +415,15 @@ notify = ["meow", "woof", { ref = "woof", id = 123 }]
Notify::Telegram(NotifyTelegram {
chat: NotifyTelegramChat::Id(1234),
thread_id: Some(123),
token: NotifyTelegramToken::Token("xxx".into()),
token: Some(TelegramToken::Token("xxx".into())),
})
),
(
"woof".into(),
Notify::Telegram(NotifyTelegram {
chat: NotifyTelegramChat::Id(5678),
thread_id: Some(900),
token: NotifyTelegramToken::Token("yyy".into()),
token: None,
})
)
])),
Expand Down Expand Up @@ -471,6 +511,22 @@ notify = ["meow", "woof"]
.unwrap_err()
.to_string()
.ends_with("reference of notify not found 'woof'"));

assert!(Config::from_str(
r#"
interval = '1min'
[notify]
meow = { platform = "Telegram", id = 1234, thread_id = 123 }
[[subscription.meow]]
platform = { name = "bilibili.live", uid = 123456 }
notify = ["meow"]
"#
)
.unwrap_err()
.to_string()
.ends_with("both token in global and notify are missing"));
}

#[test]
Expand Down Expand Up @@ -505,12 +561,12 @@ notify = ["meow", { ref = "woof", thread_id = 114 }]
Notify::Telegram(NotifyTelegram {
chat: NotifyTelegramChat::Id(1234),
thread_id: Some(123),
token: NotifyTelegramToken::Token("xxx".into()),
token: Some(TelegramToken::Token("xxx".into())),
}),
Notify::Telegram(NotifyTelegram {
chat: NotifyTelegramChat::Id(5678),
thread_id: Some(114),
token: NotifyTelegramToken::Token("yyy".into()),
token: Some(TelegramToken::Token("yyy".into())),
})
],
}
Expand Down
9 changes: 6 additions & 3 deletions src/notify/telegram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use tokio::sync::Mutex;

use super::Notifier;
use crate::{
config,
config::{self, Config},
source::{
LiveStatus, Notification, NotificationKind, Post, PostAttachment, PostsRef, RepostFrom,
StatusSource,
Expand Down Expand Up @@ -63,9 +63,12 @@ impl TelegramNotifier {
}
}

fn token(&self) -> anyhow::Result<Cow<'_, str>> {
fn token(&self) -> anyhow::Result<Cow<str>> {
self.params
.token()
.token
.as_ref()
.unwrap_or_else(|| &Config::platform_global().telegram.as_ref().unwrap().token)
.get()
.map_err(|err| anyhow!("failed to read token for telegram: {err}"))
}

Expand Down

0 comments on commit 8e9e9c0

Please sign in to comment.