From 5e848ffe7692751d4261b4809f49d848c4823ee3 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 27 Mar 2025 23:27:10 -0700 Subject: [PATCH] Rudimentary avatar updater --- tegakituesday/src/main.rs | 4 +- tegakituesday/src/models/database.rs | 64 ++++++++++++++++++++++------ tegakituesday/src/models/user/mod.rs | 2 +- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/tegakituesday/src/main.rs b/tegakituesday/src/main.rs index 3c23adb..44c9083 100644 --- a/tegakituesday/src/main.rs +++ b/tegakituesday/src/main.rs @@ -77,6 +77,8 @@ fn http() -> Http { async fn rocket() -> Result, rocket::Error> { let http = http(); + let database = load_database(); + database.refresh_users(&http).await.unwrap(); let config = rocket::Config::figment().merge(("port", 1313)).merge(( "secret_key", @@ -87,7 +89,7 @@ async fn rocket() -> Result, rocket::Error> { rocket::custom(config) .manage(Settings::new(&http).await.unwrap()) .manage(http) - .manage(load_database()) + .manage(database) .mount( "/", routes![ diff --git a/tegakituesday/src/models/database.rs b/tegakituesday/src/models/database.rs index 93bbfb4..3642619 100644 --- a/tegakituesday/src/models/database.rs +++ b/tegakituesday/src/models/database.rs @@ -1,11 +1,14 @@ use std::collections::HashSet; +use std::result; use std::{collections::HashMap, fs::File, io::Read, path::Path}; use derive_more::From; -use poise::serenity_prelude::{Http, SerenityError}; +use poise::serenity_prelude::http::error::ErrorResponse; +use poise::serenity_prelude::{Http, HttpError, SerenityError, StatusCode}; use r2d2::{Pool, PooledConnection}; use r2d2_sqlite::rusqlite::{self, params}; use r2d2_sqlite::SqliteConnectionManager; +use rocket::error; use crate::{models::User, utils::get_challenge_number}; @@ -26,7 +29,7 @@ pub enum DatabaseError { } #[derive(From, Debug)] -pub enum LoadLegacyError { +pub enum GenericError { Database(DatabaseError), Serenity(SerenityError), } @@ -82,7 +85,7 @@ impl Database { .is_ok()) } - pub async fn load_legacy(&self, http: &Http) -> std::result::Result<(), LoadLegacyError> { + pub async fn load_legacy(&self, http: &Http) -> std::result::Result<(), GenericError> { let latest_challenge = get_challenge_number(); // HashMap of archived users that are no longer sharing a server with 字ちゃん // Their historical usernames and discriminators will be used @@ -124,12 +127,7 @@ impl Database { }, ); } - Ok(user) => { - conn.execute( - "INSERT INTO User(id, name, discriminator, avatar, deleted) VALUES (?1, ?2, ?3, ?4, ?5)", - params![user.id, user.name, user.discriminator, user.avatar, user.deleted] - ).map_err(DatabaseError::Rusqlite)?; - } + Ok(user) => self.insert_user(&user)?, Err(error) if error.to_string().eq("Unknown User") => { // This will also be called in the case of an invalid user ID println!("Failed to fetch user {id}, adding to archive"); @@ -143,7 +141,7 @@ impl Database { }, ); } - Err(error) => return Err(LoadLegacyError::Serenity(error)), + Err(error) => return Err(GenericError::Serenity(error)), }, }; } @@ -265,11 +263,53 @@ impl Database { .collect() } + fn get_all_users(&self) -> Result> { + let conn = self.conn()?; + let mut stmt = conn.prepare("SELECT id, name, discriminator, avatar, deleted FROM User")?; + let result: Vec> = stmt.query_map([], |row| Ok(User { + id: row.get(0)?, + name: row.get(1)?, + discriminator: row.get(2)?, + avatar: row.get(3)?, + deleted: row.get(4)?, + }))?.map(|result| match result { + Ok(user) => Ok(user), + Err(err) => Err(DatabaseError::Rusqlite(err)), + }).collect(); + result.into_iter().collect() + } + + pub fn insert_user(&self, user: &User) -> Result<()> { + self.conn()?.execute( + "INSERT OR REPLACE INTO User(id, name, discriminator, avatar, deleted) VALUES (?1, ?2, ?3, ?4, ?5)", + params![user.id, user.name, user.discriminator, user.avatar, user.deleted] + )?; + Ok(()) + } + #[allow(dead_code)] - pub fn refresh_users(&self) -> rusqlite::Result<()> { + pub async fn refresh_users(&self, http: &Http) -> std::result::Result<(), GenericError> { // Periodically refresh all changable user data (name, discriminator, avatar) // Ideally this should run periodically. - todo!() + let all_users = self.get_all_users()?; + for (i, user) in all_users.iter().enumerate() { + print!("User {i}/{}, ", all_users.len()); + let updated = match User::fetch(http, user.id).await { + Ok(user) => user, + Err(SerenityError::Http(error)) if error.status_code().eq(&Some(StatusCode::NOT_FOUND)) => { + println!("not found"); + continue; + }, + Err(error) => panic!("{error}"), + }; + if user.eq(&updated) { + println!("no change"); + continue; + } + println!("updated"); + self.insert_user(&updated)?; + } + Ok(()) } #[allow(dead_code, unused_variables)] diff --git a/tegakituesday/src/models/user/mod.rs b/tegakituesday/src/models/user/mod.rs index 2447fa5..55f3de8 100644 --- a/tegakituesday/src/models/user/mod.rs +++ b/tegakituesday/src/models/user/mod.rs @@ -19,7 +19,7 @@ pub trait Username { fn username(&self) -> String; } -#[derive(Default, Deserialize, Debug)] +#[derive(Default, Deserialize, Debug, PartialEq, Eq)] pub struct User { #[serde(deserialize_with = "deserialize_id")] pub id: u64,