its-matrix-bot/src/main.rs

132 lines
3.3 KiB
Rust

#![forbid(unsafe_code)]
#![deny(unused_must_use)]
#![warn(clippy::pedantic, clippy::as_conversions)]
use std::path::{Path, PathBuf};
use bot::{Bot, Config};
use clap::Parser as _;
use color_eyre::{eyre::Context, Result};
use matrix_sdk::Session;
use serde::{Deserialize, Serialize};
use tracing::instrument;
use xdg::BaseDirectories;
mod bot;
mod its_api;
mod setup;
/// The session data required by the bot to log in. Stored as TOML at the path
/// given by [`session_path()`].
#[derive(Serialize, Deserialize, Debug)]
pub struct SessionData {
pub homeserver_url: Option<String>,
pub session: Session,
}
#[derive(clap::Parser)]
struct Cli {
/// Override path to the bot's configuration file
#[arg(long, short = 'c')]
config: Option<PathBuf>,
#[command(subcommand)]
sub: Subcommand,
}
#[derive(clap::Subcommand, Debug)]
enum Subcommand {
Setup(setup::Setup),
/// Run the bot
Run,
}
/// Returns the default path to the bot's configuration file.
///
/// # Errors
///
/// Returns an error if the directory containing the configuration file
/// could not be created.
fn default_config_path() -> Result<PathBuf> {
let base_dirs = BaseDirectories::with_prefix("its-matrix-bot")?;
let path = base_dirs.place_config_file("config.toml")?;
Ok(path)
}
/// Returns the path to the bot's session file.
///
/// # Errors
///
/// Returns an error if the directory containing the session file
/// could not be created.
fn session_path() -> Result<PathBuf> {
let base_dirs = BaseDirectories::with_prefix("its-matrix-bot")?;
let path = base_dirs.place_data_file("state.toml")?;
Ok(path)
}
/// Returns the path to the bot's internal database.
///
/// # Errors
///
/// Returns an error if no base directory could be determined (e.g. because
/// no home directory is set).
fn sled_store_path() -> Result<PathBuf> {
let base_dirs = BaseDirectories::with_prefix("gavel")?;
Ok(base_dirs.get_data_home())
}
#[instrument]
async fn load_config(path: &Path) -> Result<Config> {
let config = toml::from_str(&tokio::fs::read_to_string(path).await?)?;
Ok(config)
}
fn install_tracing() {
use tracing_subscriber::{
prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter,
};
let fmt_layer = tracing_subscriber::fmt::layer().with_target(false);
let filter_layer = tracing_subscriber::EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_from("info"))
.unwrap();
let error_layer = tracing_error::ErrorLayer::default();
tracing_subscriber::registry()
.with(fmt_layer)
.with(filter_layer)
.with(error_layer)
.init();
}
#[tokio::main]
async fn main() -> Result<()> {
install_tracing();
color_eyre::install()?;
let args = Cli::parse();
match args.sub {
Subcommand::Setup(setup) => {
setup::setup(setup).await?;
Ok(())
}
Subcommand::Run => {
let config_path = if let Some(config) = args.config {
config
} else {
default_config_path()?
};
let config = load_config(&config_path)
.await
.wrap_err("Failed to load bot configuration")?;
let bot = Bot::new(config).await?;
bot.run().await?
}
}
}