feat: handle saving config better
This commit is contained in:
parent
ba8f175e8f
commit
ff0fd083c8
33
src/app.rs
33
src/app.rs
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue