use crate::streamer;
use log::info;
use megalodon::SNS;
use regex::Regex;
use serde::Deserialize;
use std::fs;
use std::path::Path;
use std::sync::OnceLock;
#[derive(Debug, Deserialize)]
pub struct Config {
pub instance: InstanceSetting,
#[serde(default)]
pub timelines: TimelineSetting,
#[serde(default)]
pub filter: FilterSetting,
#[serde(default)]
pub logger: LoggerSetting,
}
#[derive(Debug, Deserialize)]
pub struct InstanceSetting {
pub software: SNS,
pub url: String,
pub token: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct TimelineSetting {
pub home: bool,
pub local: bool,
pub public: bool,
}
impl Default for TimelineSetting {
fn default() -> Self {
Self {
home: true,
local: false,
public: false,
}
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct FilterSetting {
pub include: Vec<String>,
pub exclude: Vec<String>,
pub user_include: Vec<String>,
pub user_exclude: Vec<String>,
pub case_sensitive: bool,
pub use_regex: bool,
}
impl Default for FilterSetting {
fn default() -> Self {
Self {
include: vec![],
exclude: vec![],
user_include: vec![],
user_exclude: vec![],
case_sensitive: true,
use_regex: false,
}
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
pub struct LoggerSetting {
pub stdout: Stdout,
pub discord: Discord,
}
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Stdout {
pub enable: bool,
}
impl Default for Stdout {
fn default() -> Self {
Self { enable: true }
}
}
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Discord {
pub enable: bool,
pub webhook: String,
pub use_embed: bool,
}
impl Default for Discord {
fn default() -> Self {
Self {
enable: false,
webhook: String::new(),
use_embed: true,
}
}
}
pub static CONFIG: OnceLock<Config> = OnceLock::new();
pub async fn load() -> Result<(), String> {
let Ok(toml) = fs::read_to_string("config.toml") else {
return Err(if Path::new(".env").exists() {
"* Obsolete .env config file found. Please migrate to config.toml."
} else {
"* config.toml is not found."
}
.to_owned());
};
let config: Config = match toml::from_str(&toml) {
Ok(c) => c,
Err(e) => return Err(format!("* Failed to load config.toml: {:?}", e.message())),
};
if config.timelines.home && config.instance.token.is_none() {
eprintln!("* timelines.home is set, but instance.token is empty. Generating a token...");
streamer::oauth(config.instance.software, &config.instance.url).await;
return Err(String::new());
}
if config.filter.use_regex {
for exp in &config.filter.include {
if Regex::new(exp).is_err() {
return Err("* filter.include contains a invalid regex.".to_owned());
}
}
for exp in &config.filter.exclude {
if Regex::new(exp).is_err() {
return Err("* filter.exclude contains a invalid regex.".to_owned());
}
}
}
if config.logger.discord.enable && config.logger.discord.webhook.is_empty() {
return Err(
"* logger.discord.enable is set, but logger.discord.webhook is empty.".to_owned(),
);
}
let config = CONFIG.get_or_init(|| config);
info!("{:?}", config);
Ok(())
}