feat: handle saving config better

This commit is contained in:
Anna 2021-09-01 13:39:16 -04:00
parent ba8f175e8f
commit ff0fd083c8
3 changed files with 43 additions and 14 deletions

View File

@ -9,6 +9,7 @@ pub mod web;
use anyhow::Result; use anyhow::Result;
use cached::{TimedCache, Cached}; use cached::{TimedCache, Cached};
use chrono::Duration;
use futures::{StreamExt, SinkExt}; use futures::{StreamExt, SinkExt};
use irc::client::prelude::Client as IrcClient; use irc::client::prelude::Client as IrcClient;
use rhai::Engine; use rhai::Engine;
@ -40,13 +41,13 @@ use crate::app::{
use std::{ use std::{
collections::HashMap, collections::HashMap,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
path::Path,
sync::{ sync::{
Arc, Arc,
atomic::AtomicBool, atomic::{AtomicBool, Ordering},
}, },
time::Instant, time::Instant,
}; };
use std::sync::atomic::Ordering;
pub struct State { pub struct State {
pub user_config: UserConfig, pub user_config: UserConfig,
@ -77,15 +78,15 @@ impl State {
pub async fn new(runtime: Handle, user_config: UserConfig, mut config: Config) -> Result<Arc<Self>> { pub async fn new(runtime: Handle, user_config: UserConfig, mut config: Config) -> Result<Arc<Self>> {
let http_client = reqwest::Client::new(); let http_client = reqwest::Client::new();
println!("Verifying bot token"); println!("verifying bot token");
verify_token(&mut config.bot_token, &user_config, &http_client, vec![ verify_token(&mut config.bot_token, &user_config, &http_client, vec![
// IRC // IRC
Scope::ChatRead, Scope::ChatRead,
Scope::ChatEdit, Scope::ChatEdit,
]).await?; ]).await?;
println!("Bot token ready"); println!("bot token ready");
println!("Verifying user token"); println!("verifying user token");
verify_token(&mut config.user_token, &user_config, &http_client, vec![ verify_token(&mut config.user_token, &user_config, &http_client, vec![
// Channel points redemptions // Channel points redemptions
Scope::ChannelReadRedemptions, Scope::ChannelReadRedemptions,
@ -96,7 +97,7 @@ impl State {
Scope::ModerationRead, Scope::ModerationRead,
Scope::ModeratorManageAutoMod, Scope::ModeratorManageAutoMod,
]).await?; ]).await?;
println!("User token ready"); println!("user token ready");
let config = Arc::new(RwLock::new(config)); let config = Arc::new(RwLock::new(config));
@ -104,13 +105,29 @@ impl State {
let twitch_config = Arc::clone(&config); let twitch_config = Arc::clone(&config);
let twitch = Arc::new(Twitch::new(twitch_client, twitch_config)); let twitch = Arc::new(Twitch::new(twitch_client, twitch_config));
println!("getting channel name");
let user_id = UserId::new(user_config.twitch.channel_id.to_string()); let user_id = UserId::new(user_config.twitch.channel_id.to_string());
let channel_name = twitch.client.helix.get_user_from_id(user_id, &twitch.bot_token().await?) let channel_name = twitch.client.helix.get_user_from_id(user_id, &twitch.bot_token().await?)
.await? .await?
.ok_or_else(|| anyhow::anyhow!("no channel for id {}", user_config.twitch.channel_id))? .ok_or_else(|| anyhow::anyhow!("no channel for id {}", user_config.twitch.channel_id))?
.login .login
.to_string(); .to_string();
println!("configured to run for channel: {}", channel_name);
println!("starting periodic config save task");
let task_config = Arc::clone(&config);
tokio::task::spawn(async move {
loop {
println!("saving config");
if let Err(e) = crate::save_config(Path::new("data.json"), &*task_config.read().await).await {
eprintln!("could not save config: {:?}", e);
}
tokio::time::sleep(Duration::minutes(5).to_std().unwrap()).await;
}
});
println!("connecting to irc");
let mut irc = self::twitch_irc::connect(&twitch).await?; let mut irc = self::twitch_irc::connect(&twitch).await?;
let mut irc_stream = irc.stream()?; let mut irc_stream = irc.stream()?;
@ -182,6 +199,7 @@ impl State {
)?; )?;
let task_state = Arc::clone(&state); let task_state = Arc::clone(&state);
tokio::task::spawn(async move { tokio::task::spawn(async move {
println!("starting pubsub listener");
let res: Result<()> = try { let res: Result<()> = try {
let (ws, _) = tokio_tungstenite::connect_async("wss://pubsub-edge.twitch.tv").await?; let (ws, _) = tokio_tungstenite::connect_async("wss://pubsub-edge.twitch.tv").await?;
let (mut write, mut read) = ws.split(); let (mut write, mut read) = ws.split();
@ -213,6 +231,7 @@ impl State {
// start irc task // start irc task
let task_state = Arc::clone(&state); let task_state = Arc::clone(&state);
tokio::task::spawn(async move { tokio::task::spawn(async move {
println!("listening for irc events");
loop { loop {
while let Some(event) = irc_stream.next().await.transpose().unwrap_or_default() { while let Some(event) = irc_stream.next().await.transpose().unwrap_or_default() {
let task_state = Arc::clone(&task_state); let task_state = Arc::clone(&task_state);
@ -259,6 +278,8 @@ impl State {
let task_state = Arc::clone(&state); let task_state = Arc::clone(&state);
self::task::stream_status::start_task(task_state).await; self::task::stream_status::start_task(task_state).await;
println!("initialised");
Ok(state) Ok(state)
} }

View File

@ -14,6 +14,7 @@ use std::{
}; };
pub async fn start_web(state: Arc<State>) { pub async fn start_web(state: Arc<State>) {
println!("starting web server");
let cookie_state = Arc::clone(&state); let cookie_state = Arc::clone(&state);
let authed = warp::cookie("access_token") let authed = warp::cookie("access_token")
.or(warp::header("x-api-key")) .or(warp::header("x-api-key"))

View File

@ -13,10 +13,7 @@ use crate::app::{
config::Config, config::Config,
user_config::UserConfig, user_config::UserConfig,
}; };
use std::{ use std::path::Path;
path::Path,
sync::Arc,
};
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let runtime = Builder::new_multi_thread() let runtime = Builder::new_multi_thread()
@ -28,10 +25,12 @@ fn main() -> anyhow::Result<()> {
} }
async fn inner(runtime: Handle) -> anyhow::Result<()> { async fn inner(runtime: Handle) -> anyhow::Result<()> {
println!("reading config");
let mut uc_toml = String::new(); let mut uc_toml = String::new();
tokio::fs::File::open("config.toml").await?.read_to_string(&mut uc_toml).await?; tokio::fs::File::open("config.toml").await?.read_to_string(&mut uc_toml).await?;
let user_config: UserConfig = toml::from_str(&uc_toml)?; let user_config: UserConfig = toml::from_str(&uc_toml)?;
println!("reading data");
let c_path = Path::new("data.json"); let c_path = Path::new("data.json");
let config: Config = if c_path.exists() { let config: Config = if c_path.exists() {
let mut c_json = String::new(); let mut c_json = String::new();
@ -41,23 +40,31 @@ async fn inner(runtime: Handle) -> anyhow::Result<()> {
Config::default() Config::default()
}; };
println!("starting bot");
let state = State::new(runtime, user_config, config).await?; let state = State::new(runtime, user_config, config).await?;
save_config(c_path, Arc::clone(&state)).await?; save_config(c_path, &*state.config.read().await).await?;
tokio::signal::ctrl_c() tokio::signal::ctrl_c()
.then(|_| save_config(c_path, Arc::clone(&state))) .then(|_| async {
println!("received ctrl-c");
println!("saving config");
save_config(c_path, &*state.config.read().await).await?;
Result::<(), anyhow::Error>::Ok(())
})
.await?; .await?;
println!("exiting");
Ok(()) Ok(())
} }
async fn save_config(path: &Path, state: Arc<State>) -> anyhow::Result<()> { pub async fn save_config(path: &Path, config: &Config) -> anyhow::Result<()> {
let mut config_file = OpenOptions::new() let mut config_file = OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
.truncate(true) .truncate(true)
.open(path) .open(path)
.await?; .await?;
config_file.write_all(&serde_json::to_vec(&*state.config.read().await)?).await?; config_file.write_all(&serde_json::to_vec(config)?).await?;
Ok(()) Ok(())
} }