* Extend the configuration format in order to allow access_token, user_id and device_id instead of username and password * Move the matrix login logic outside of login_and_sync for clarity * Add support for access token based session resuming instead of logging in every time (thus creating a new device each time the service starts up) * Delay the startup of feed reader loops until after the matrix module has had a chance to actually check authentication This change is quite involved and there are a few caveats, namely an intentional race condition between the feed reader loops and matrix authentication, as well as significantly different behaviors depending on which authentication scheme is being used: password based authentication requires an API call while resuming a session using an access token does not.
100 lines
2.5 KiB
Rust
100 lines
2.5 KiB
Rust
/**
|
|
* matrix-feedbot v0.1.0
|
|
*
|
|
* Copyright (C) 2024 The 1312 Media Collective
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
use serde::Deserialize;
|
|
use std::{fs, io, fmt};
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
pub struct FeedConfig {
|
|
pub url: String,
|
|
pub rooms: Vec<String>,
|
|
pub delay: u64
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(untagged)]
|
|
pub enum AuthConfig {
|
|
PasswordAuthConfig {
|
|
username: String,
|
|
password: String,
|
|
},
|
|
TokenAuthConfig {
|
|
user_id: String,
|
|
device_id: String,
|
|
access_token: String,
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct Config {
|
|
pub default_room: String,
|
|
pub feeds: Vec<FeedConfig>,
|
|
#[serde(flatten)]
|
|
pub auth: AuthConfig,
|
|
pub homeserver_uri: String
|
|
}
|
|
|
|
impl Config {
|
|
pub fn load(config_file: &str) -> Result<Self> {
|
|
|
|
let serialized_config = fs::read_to_string(config_file).map_err(|e| {
|
|
match e.kind() {
|
|
io::ErrorKind::NotFound => Error::FileNotFoundError(config_file.into()),
|
|
io::ErrorKind::PermissionDenied => Error::PermissionDeniedError(),
|
|
_ => e.into(),
|
|
}
|
|
})?;
|
|
|
|
let config: Config = serde_yaml::from_str(&serialized_config)?;
|
|
|
|
Ok(config)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[allow(dead_code)] // we use Debug, so inner fields are never read
|
|
pub enum Error {
|
|
FileNotFoundError(String),
|
|
PermissionDeniedError(),
|
|
InvalidFormatError(serde_yaml::Error),
|
|
IoError(io::Error),
|
|
}
|
|
|
|
impl From<serde_yaml::Error> for Error {
|
|
fn from(e: serde_yaml::Error) -> Self {
|
|
Self::InvalidFormatError(e)
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(e: io::Error) -> Self {
|
|
Self::IoError(e)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
|
|
write!(fmt, "{self:?}")
|
|
}
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, Error>;
|