diff --git a/.forgejo/workflows/check_lfs.yaml b/.forgejo/workflows/check_lfs.yaml index aaa2117..dca575c 100644 --- a/.forgejo/workflows/check_lfs.yaml +++ b/.forgejo/workflows/check_lfs.yaml @@ -1,7 +1,9 @@ on: + - pull_request - push - workflow_dispatch + jobs: check_lfs: # nix/docker diff --git a/.forgejo/workflows/on_pr.yaml b/.forgejo/workflows/on_pr.yaml deleted file mode 100644 index d17dc47..0000000 --- a/.forgejo/workflows/on_pr.yaml +++ /dev/null @@ -1,51 +0,0 @@ -on: - - pull_request - -jobs: - check_lfs: - # nix/docker - runs-on: nix - steps: - - uses: https://github.com/MPLew-is/lfs-check-action@1 - - # rust code must be formatted for standardisation - lint_fmt: - # build it using teh base nixos system, helps with caching - runs-on: nix - steps: - # get the repo first - - uses: https://code.forgejo.org/actions/checkout@v4 - - uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8 - with: - server_url: ${{ gitea.server_url }} - repository: ${{ gitea.repository }} - - run: nix build .#fmt --verbose - - # clippy is incredibly useful for making yer code better - lint_clippy: - # build it using teh base nixos system, helps with caching - runs-on: nix - permissions: - checks: write - steps: - # get the repo first - - uses: https://code.forgejo.org/actions/checkout@v4 - - uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8 - with: - server_url: ${{ gitea.server_url }} - repository: ${{ gitea.repository }} - - run: nix build .#clippy --verbose - - build: - # build it using teh base nixos system, helps with caching - runs-on: nix - needs: [ lint_fmt, lint_clippy ] - steps: - # get the repo first - - uses: https://code.forgejo.org/actions/checkout@v4 - - uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8 - with: - server_url: ${{ gitea.server_url }} - repository: ${{ gitea.repository }} - - name: "Build it locally" - run: nix build --verbose diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs deleted file mode 100644 index a338a00..0000000 --- a/.git-blame-ignore-revs +++ /dev/null @@ -1,2 +0,0 @@ -# Fix typos -7e90f451965b0edbd331765ad295a02f31d2bf24 diff --git a/.rustfmt.toml b/.rustfmt.toml index 2b0831a..6aeb30c 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -7,4 +7,4 @@ fn_params_layout = "Compressed" struct_lit_width = 0 tab_spaces = 2 use_small_heuristics = "Max" -imports_granularity = "Crate" \ No newline at end of file +#imports_granularity="Crate" \ No newline at end of file diff --git a/.taplo.toml b/.taplo.toml deleted file mode 100644 index 3b409e9..0000000 --- a/.taplo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[formatting] -column_width = 120 diff --git a/Cargo.toml b/Cargo.toml index 832eb09..b179901 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,13 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + [[bin]] name = "update_data" +[[bin]] +name = "update_users" + [[bin]] name = "update_committee" @@ -16,9 +20,6 @@ name = "update_minecraft" [[bin]] name = "update_server-icon" -[[bin]] -name = "cleanup_committee" - [dependencies] # discord library serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } @@ -34,7 +35,7 @@ surf = "2.3" dotenvy = "0.15" # For sqlite -sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "migrate"] } +sqlx = { version = "0.8", features = [ "runtime-tokio", "sqlite", "migrate" ] } serde_json = { version = "1.0", features = ["raw_value"] } # create random strings diff --git a/README.md b/README.md index 1f49dc1..01543e6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Skynet Discord Bot The Skynet bot is designed to manage users on Discord. It allows users to link their UL Wolves account with Wolves in a GDPR compliant manner. -Skynet (bot) is hosted by the Computer Society on Skynet (computer cluster). +Skynet (bot) is hosted is hosted by the Computer Society on Skynet (computer cluster). ## Documentation We have split up the documentation into different segments depending on who the user is. diff --git a/flake.lock b/flake.lock index 2c4a9bc..9f7289a 100644 --- a/flake.lock +++ b/flake.lock @@ -32,22 +32,6 @@ "type": "indirect" } }, - "nixpkgs-mozilla": { - "flake": false, - "locked": { - "lastModified": 1744624473, - "narHash": "sha256-S6zT/w5SyAkJ//dYdjbrXgm+6Vkd/k7qqUl4WgZ6jjk=", - "owner": "mozilla", - "repo": "nixpkgs-mozilla", - "rev": "2292d4b35aa854e312ad2e95c4bb5c293656f21a", - "type": "github" - }, - "original": { - "owner": "mozilla", - "repo": "nixpkgs-mozilla", - "type": "github" - } - }, "nixpkgs_2": { "locked": { "lastModified": 1722995383, @@ -67,7 +51,6 @@ "inputs": { "naersk": "naersk", "nixpkgs": "nixpkgs_2", - "nixpkgs-mozilla": "nixpkgs-mozilla", "utils": "utils" } }, diff --git a/flake.nix b/flake.nix index 3a6032f..b707b60 100644 --- a/flake.nix +++ b/flake.nix @@ -4,10 +4,6 @@ inputs = { nixpkgs.url = "nixpkgs/nixos-unstable"; naersk.url = "github:nix-community/naersk"; - nixpkgs-mozilla = { - url = "github:mozilla/nixpkgs-mozilla"; - flake = false; - }; utils.url = "github:numtide/flake-utils"; }; @@ -21,27 +17,12 @@ nixpkgs, utils, naersk, - nixpkgs-mozilla, }: utils.lib.eachDefaultSystem ( system: let overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)); - pkgs = (import nixpkgs) { - inherit system; - - overlays = [ - (import nixpkgs-mozilla) - ]; - }; - toolchain = (pkgs.rustChannelOf { - rustToolchain = ./rust-toolchain.toml; - sha256 = "sha256-KUm16pHj+cRedf8vxs/Hd2YWxpOrWZ7UOrwhILdSJBU="; - }).rust; - - naersk' = pkgs.callPackage naersk { - cargo = toolchain; - rustc = toolchain; - }; + pkgs = (import nixpkgs) {inherit system;}; + naersk' = pkgs.callPackage naersk {}; package_name = "skynet_discord_bot"; desc = "Skynet Discord Bot"; buildInputs = with pkgs; [ @@ -122,14 +103,12 @@ wantedBy = []; after = ["network-online.target"]; environment = environment_config; - path = with pkgs; [ git git-lfs ]; + serviceConfig = { Type = "oneshot"; User = "${cfg.user}"; Group = "${cfg.user}"; ExecStart = "${self.defaultPackage."${system}"}/bin/${script}"; - # kill each service if its ran for 9 min - TimeoutStartSec=540; EnvironmentFile = [ "${cfg.env.discord}" "${cfg.env.mail}" @@ -154,14 +133,13 @@ # modify these scripts = { - # every 10 min + # every 20 min "update_data" = "*:0,10,20,30,40,50"; # groups are updated every hour, offset from teh ldap - "update_users" = "*:5,15,25,35,45,55"; + "update_users" = "*:05:00"; # Committee server has its own timer - "update_committee" = "*:5,15,25,35,45,55"; + "update_committee" = "*:15:00"; # minecraft stuff is updated at 5am - # this service does not depend on teh discord cache "update_minecraft" = "5:10:00"; # server icon gets updated daily at midnight "update_server-icon" = "0:01:00"; @@ -224,7 +202,6 @@ after = ["network-online.target"]; wants = []; environment = environment_config; - path = with pkgs; [ git git-lfs ]; serviceConfig = { User = "${cfg.user}"; diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs deleted file mode 100644 index 141af89..0000000 --- a/src/bin/cleanup_committee.rs +++ /dev/null @@ -1,137 +0,0 @@ -use serenity::{ - all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}, - async_trait, - client::{Context, EventHandler}, - model::gateway::GatewayIntents, - Client, -}; -use skynet_discord_bot::{ - common::{ - database::{db_init, DataBase}, - set_roles::committee::db_roles_get, - }, - get_config, Config, -}; -use sqlx::{Pool, Sqlite}; -use std::{process, sync::Arc}; -use tokio::sync::RwLock; - -/// Cleanup teh Committee server -/// -/// This removes any invalid roles/channels which have been set up accidentally -/// DO NOT run this locally unless you have a fresh copy of the live database handy. -#[tokio::main] -async fn main() { - let config = get_config(); - let db = match db_init(&config).await { - Ok(x) => x, - Err(_) => return, - }; - - // Intents are a bitflag, bitwise operations can be used to dictate which intents to use - let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; - // Build our client. - let mut client = Client::builder(&config.discord_token, intents) - .event_handler(Handler {}) - .cache_settings(serenity::cache::Settings::default()) - .await - .expect("Error creating client"); - - { - let mut data = client.data.write().await; - - data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(db)); - } - - if let Err(why) = client.start().await { - println!("Client error: {why:?}"); - } -} - -struct Handler; -#[async_trait] -impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, _guilds: Vec) { - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config_global = config_lock.read().await; - - let server = config_global.committee_server; - - ctx.shard.chunk_guild(server, Some(2000), false, ChunkGuildFilter::None, None); - - println!("Cache loaded"); - } - - async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { - if (chunk.chunk_index + 1) == chunk.chunk_count { - println!("Cache built successfully!"); - let db = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config = config_lock.read().await; - - cleanup(&db, &ctx, &config).await; - // finish up - process::exit(0); - } - } -} - -async fn cleanup(db: &Pool, ctx: &Context, config: &Config) { - let server = config.committee_server; - let committees = db_roles_get(db).await; - - if let Ok(channels) = server.channels(ctx).await { - for (id, channel) in &channels { - let name = &channel.name; - let committee_tmp = committees.iter().filter(|x| &x.name_channel == name).collect::>(); - let committee = match committee_tmp.first() { - // if there are no committees which match then this is not a channel we care about - None => { - continue; - } - Some(x) => x, - }; - - // if the id of the channel does not match then remove it - if id != &committee.id_channel { - println!("Deleting Channel - ID: {} Name: {}", id, &channel.name); - if let Err(e) = channel.delete(ctx).await { - dbg!(e); - } - } - } - } - - if let Ok(mut roles) = server.roles(ctx).await { - for (id, role) in &mut roles { - let name = &role.name; - let committee_tmp = committees.iter().filter(|x| &x.name_role == name).collect::>(); - let committee = match committee_tmp.first() { - // if there are no committees which match then this is not a channel we care about - None => { - continue; - } - Some(x) => x, - }; - - // if the id of the role does not match then remove it - if id != &committee.id_role { - println!("Deleting Role - ID: {} Name: {}", id, &role.name); - if let Err(e) = role.delete(ctx).await { - dbg!(e); - } - } - } - } -} diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index d90cbac..d2026c0 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -1,17 +1,12 @@ use serenity::{ - all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}, async_trait, client::{Context, EventHandler}, - model::gateway::GatewayIntents, + model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::{ - common::{ - database::{db_init, DataBase}, - set_roles::committee, - }, - get_config, Config, -}; +use skynet_discord_bot::common::database::{db_init, DataBase}; +use skynet_discord_bot::common::set_roles::committee; +use skynet_discord_bot::{get_config, Config}; use std::{process, sync::Arc}; use tokio::sync::RwLock; @@ -28,7 +23,6 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) - .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -36,39 +30,25 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(db)); + data.insert::(Arc::new(RwLock::new(db))); } if let Err(why) = client.start().await { - println!("Client error: {why:?}"); + println!("Client error: {:?}", why); } } struct Handler; #[async_trait] impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, _guilds: Vec) { - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let config_global = config_lock.read().await; + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); - let server = config_global.committee_server; + // u[date committee server + committee::check_committee(Arc::clone(&ctx)).await; - ctx.shard.chunk_guild(server, Some(2000), false, ChunkGuildFilter::None, None); - - println!("Cache loaded"); - } - - async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { - if (chunk.chunk_index + 1) == chunk.chunk_count { - println!("Cache built successfully!"); - // u[date committee server - committee::check_committee(&ctx).await; - - // finish up - process::exit(0); - } + // finish up + process::exit(0); } } diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index fe4138f..2d36892 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -4,13 +4,10 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::{ - common::{ - database::{db_init, DataBase}, - wolves::{cns::get_wolves, committees::get_cns}, - }, - get_config, Config, -}; +use skynet_discord_bot::common::database::{db_init, DataBase}; +use skynet_discord_bot::common::wolves::cns::get_wolves; +use skynet_discord_bot::common::wolves::committees::get_cns; +use skynet_discord_bot::{get_config, Config}; use std::{process, sync::Arc}; use tokio::sync::RwLock; @@ -30,7 +27,6 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) - .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -38,11 +34,11 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(db)); + data.insert::(Arc::new(RwLock::new(db))); } if let Err(why) = client.start().await { - println!("Client error: {why:?}"); + println!("Client error: {:?}", why); } } diff --git a/src/bin/update_minecraft.rs b/src/bin/update_minecraft.rs index f7c24e0..f5d3634 100644 --- a/src/bin/update_minecraft.rs +++ b/src/bin/update_minecraft.rs @@ -1,10 +1,6 @@ -use skynet_discord_bot::{ - common::{ - database::db_init, - minecraft::{get_minecraft_config, update_server, whitelist_wipe}, - }, - get_config, -}; +use skynet_discord_bot::common::database::db_init; +use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe}; +use skynet_discord_bot::get_config; use std::collections::HashSet; #[tokio::main] diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index c4f9eca..ff4cef9 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -4,11 +4,9 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; +use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon}; use skynet_discord_bot::{ - common::{ - database::{db_init, DataBase}, - server_icon::{get_config_icons, update_icon}, - }, + common::database::{db_init, DataBase}, get_config, Config, }; use std::{process, sync::Arc}; @@ -27,7 +25,6 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) - .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -35,11 +32,11 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(db)); + data.insert::(Arc::new(RwLock::new(db))); } if let Err(why) = client.start().await { - println!("Client error: {why:?}"); + println!("Client error: {:?}", why); } } @@ -50,10 +47,11 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; + let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 3150bcf..3617d14 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -1,24 +1,13 @@ use serenity::{ - all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}, async_trait, client::{Context, EventHandler}, - model::gateway::GatewayIntents, + model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::{ - common::{ - database::{db_init, get_server_config_bulk, DataBase}, - set_roles::normal, - }, - get_config, Config, -}; -use std::{ - process, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, - }, -}; +use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; +use skynet_discord_bot::common::set_roles::normal; +use skynet_discord_bot::{get_config, Config}; +use std::{process, sync::Arc}; use tokio::sync::RwLock; #[tokio::main] @@ -33,11 +22,7 @@ async fn main() { let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; // Build our client. let mut client = Client::builder(&config.discord_token, intents) - .event_handler(Handler { - server_count: Default::default(), - server_cached: Default::default(), - }) - .cache_settings(serenity::cache::Settings::default()) + .event_handler(Handler {}) .await .expect("Error creating client"); @@ -45,51 +30,38 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(db)); + data.insert::(Arc::new(RwLock::new(db))); } if let Err(why) = client.start().await { - println!("Client error: {why:?}"); + println!("Client error: {:?}", why); } } -struct Handler { - server_count: AtomicUsize, - server_cached: AtomicUsize, -} +struct Handler; #[async_trait] impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, guilds: Vec) { - self.server_count.swap(guilds.len(), Ordering::SeqCst); - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache loaded {}", &self.server_count.load(Ordering::SeqCst)); - } + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); - async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { - if (chunk.chunk_index + 1) == chunk.chunk_count { - self.server_cached.fetch_add(1, Ordering::SeqCst); - if (self.server_cached.load(Ordering::SeqCst) + 1) == self.server_count.load(Ordering::SeqCst) { - println!("Cache built successfully!"); + // this goes into each server and sets roles for each wolves member + check_bulk(Arc::clone(&ctx)).await; - // this goes into each server and sets roles for each wolves member - check_bulk(&ctx).await; - - // finish up - process::exit(0); - } - } + // finish up + process::exit(0); } } -async fn check_bulk(ctx: &Context) { - let db = { +async fn check_bulk(ctx: Arc) { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; + let db = db_lock.read().await; + for server_config in get_server_config_bulk(&db).await { - normal::update_server(ctx, &server_config, &[], &[]).await; + normal::update_server(&ctx, &server_config, &[], &[]).await; } } diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index eaf0971..387bd9e 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -1,12 +1,8 @@ -use serenity::{ - all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, - client::Context, -}; -use skynet_discord_bot::common::{ - database::{get_server_config, DataBase, Servers}, - set_roles::normal::update_server, - wolves::cns::get_wolves, -}; +use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; +use serenity::client::Context; +use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; +use skynet_discord_bot::common::set_roles::normal::update_server; +use skynet_discord_bot::common::wolves::cns::get_wolves; use sqlx::{Error, Pool, Sqlite}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { @@ -56,10 +52,11 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { return "Please provide a valid channel for ``Bot Channel``".to_string(); }; - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let server_data = Servers { server: command.guild_id.unwrap_or_default(), @@ -75,8 +72,8 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { match add_server(&db, ctx, &server_data).await { Ok(_) => {} Err(e) => { - println!("{e:?}"); - return format!("Failure to insert into Servers {server_data:?}"); + println!("{:?}", e); + return format!("Failure to insert into Servers {:?}", server_data); } } @@ -101,7 +98,7 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul .fetch_optional(db) .await; - // if the entry does not exist already then do a user update + // if the entry does not exist already tehn do a user update let (update, current_remove, current_role, past_remove, past_role) = match &existing { None => (true, false, None, false, None), Some(x) => { diff --git a/src/commands/count.rs b/src/commands/count.rs index 41b9d81..678c9e0 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -5,7 +5,8 @@ pub mod committee { use serenity::all::{ CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, }; - use skynet_discord_bot::common::{database::DataBase, set_roles::committee::db_roles_get}; + use skynet_discord_bot::common::database::DataBase; + use skynet_discord_bot::common::set_roles::committee::db_roles_get; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { let sub_options = if let Some(CommandDataOption { @@ -27,10 +28,11 @@ pub mod committee { false }; - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let mut cs = vec![]; // pull it from a DB @@ -51,7 +53,7 @@ pub mod committee { for (count, name) in cs { let leading = if count < 10 { " " } else { "" }; - let line = format!("{leading}{count} {name}"); + let line = format!("{}{} {}", leading, count, name); let length = line.len() + 1; @@ -83,27 +85,22 @@ pub mod servers { // get the list of all the current clubs/socs use serde::{Deserialize, Serialize}; use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; - use skynet_discord_bot::{ - common::{ - database::{get_server_config_bulk, DataBase}, - set_roles::committee::get_committees, - }, - get_now_iso, - }; + use skynet_discord_bot::common::database::{get_server_config_bulk, DataBase}; + use skynet_discord_bot::common::set_roles::committee::get_committees; + use skynet_discord_bot::get_now_iso; use sqlx::{Pool, Sqlite}; use std::collections::HashMap; pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let mut committees = HashMap::new(); - if let Some(x) = get_committees(&db).await { - for committee in x { - committees.insert(committee.id, committee.to_owned()); - } + for committee in get_committees(&db).await { + committees.insert(committee.id, committee.to_owned()); } let mut cs = vec![]; @@ -146,11 +143,11 @@ pub mod servers { "" }; - let line = format!("{current_leading}{current} {past_leading}{past} {name}"); + let line = format!("{}{} {}{} {}", current_leading, current, past_leading, past, name); let length = line.len() + 1; - // +3 is to account for the closing fence + // +3 is to account for the closing fense if length < (limit + 3) { response.push(line); limit -= length; diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index 5098f11..ca395c6 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -9,24 +9,19 @@ pub(crate) mod user { use super::*; use crate::commands::wolves::link::get_server_member_discord; use serde::{Deserialize, Serialize}; - use serenity::{ - all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, - model::id::UserId, - }; - use skynet_discord_bot::{ - common::{ - database::Wolves, - minecraft::{whitelist_update, Minecraft}, - }, - Config, - }; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; + use serenity::model::id::UserId; + use skynet_discord_bot::common::database::Wolves; + use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; + use skynet_discord_bot::Config; use sqlx::Error; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; @@ -74,14 +69,14 @@ pub(crate) mod user { Ok(_) => {} Err(e) => { dbg!("{:?}", e); - return format!("Failure to minecraft username {username:?}"); + return format!("Failure to minecraft username {:?}", username); } } username_mc = username.to_string(); } else { match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await { None => { - return format!("No UID found for {username:?}"); + return format!("No UID found for {:?}", username); } Some(x) => { match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await { @@ -190,17 +185,14 @@ pub(crate) mod server { use super::*; pub(crate) mod add { - use serenity::{ - all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}, - model::id::GuildId, - }; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}; + use serenity::model::id::GuildId; use sqlx::Error; - // this is to manage the server side of commands related to minecraft + // this is to managfe the server side of commands related to minecraft use super::*; - use skynet_discord_bot::{ - common::minecraft::{update_server, Minecraft}, - Config, - }; + use skynet_discord_bot::common::minecraft::update_server; + use skynet_discord_bot::common::minecraft::Minecraft; + use skynet_discord_bot::Config; pub fn register() -> CreateCommand { CreateCommand::new("minecraft_add") @@ -228,15 +220,16 @@ pub(crate) mod server { return String::from("Expected Server ID"); }; - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; match add_server(&db, &g_id, &server_minecraft).await { Ok(_) => {} Err(e) => { - println!("{e:?}"); + println!("{:?}", e); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); } } @@ -267,14 +260,12 @@ pub(crate) mod server { } pub(crate) mod list { - use serenity::{all::CommandInteraction, builder::CreateCommand, client::Context}; - use skynet_discord_bot::{ - common::{ - database::DataBase, - minecraft::{get_minecraft_config_server, server_information}, - }, - Config, - }; + use serenity::all::CommandInteraction; + use serenity::builder::CreateCommand; + use serenity::client::Context; + use skynet_discord_bot::common::database::DataBase; + use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information}; + use skynet_discord_bot::Config; pub fn register() -> CreateCommand { CreateCommand::new("minecraft_list") @@ -288,10 +279,11 @@ pub(crate) mod server { Some(x) => x, }; - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let servers = get_minecraft_config_server(&db, g_id).await; @@ -328,13 +320,12 @@ pub(crate) mod server { } pub(crate) mod delete { - use serenity::{ - all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}, - builder::CreateCommand, - client::Context, - model::id::GuildId, - }; - use skynet_discord_bot::common::{database::DataBase, minecraft::Minecraft}; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}; + use serenity::builder::CreateCommand; + use serenity::client::Context; + use serenity::model::id::GuildId; + use skynet_discord_bot::common::database::DataBase; + use skynet_discord_bot::common::minecraft::Minecraft; use sqlx::{Error, Pool, Sqlite}; pub fn register() -> CreateCommand { @@ -363,15 +354,16 @@ pub(crate) mod server { return String::from("Expected Server ID"); }; - let db = { - let data = ctx.data.read().await; - data.get::().expect("Expected Databse in TypeMap.").clone() + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; match server_remove(&db, &g_id, &server_minecraft).await { Ok(_) => {} Err(e) => { - println!("{e:?}"); + println!("{:?}", e); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); } } diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index 0573b37..f58803b 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -62,10 +62,11 @@ pub mod edit { false }; - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let server = command.guild_id.unwrap_or_default(); let server_data = RoleAdder { @@ -78,8 +79,8 @@ pub mod edit { match add_server(&db, &server_data, delete).await { Ok(_) => {} Err(e) => { - println!("{e:?}"); - return format!("Failure to insert into Servers {server_data:?}"); + println!("{:?}", e); + return format!("Failure to insert into Servers {:?}", server_data); } } @@ -100,9 +101,9 @@ pub mod edit { } if delete { - format!("Removed {role_a_name} + {role_b_name} = {role_c_name}") + format!("Removed {} + {} = {}", role_a_name, role_b_name, role_c_name) } else { - format!("Added {role_a_name} + {role_b_name} = {role_c_name}") + format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name) } } @@ -141,12 +142,13 @@ pub mod edit { pub mod list {} pub mod tools { - use serenity::{client::Context, model::guild::Member}; + use serenity::client::Context; + use serenity::model::guild::Member; use skynet_discord_bot::common::database::RoleAdder; use sqlx::{Pool, Sqlite}; pub async fn on_role_change(db: &Pool, ctx: &Context, new_data: Member) { - // check if the role changed is part of the ones for this server + // check if the role changed is part of the oens for this server if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>( r#" SELECT * @@ -162,7 +164,7 @@ pub mod tools { let mut roles_remove = vec![]; for role_adder in role_adders { - // if the user has both A and B give them C + // if the user has both A dnd B give them C if new_data.roles.contains(&role_adder.role_a) && new_data.roles.contains(&role_adder.role_b) && !new_data.roles.contains(&role_adder.role_c) { roles_add.push(role_adder.role_c); @@ -178,13 +180,13 @@ pub mod tools { if !roles_add.is_empty() { if let Err(e) = new_data.add_roles(&ctx, &roles_add).await { - println!("{e:?}"); + println!("{:?}", e); } } if !roles_remove.is_empty() { if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await { - println!("{e:?}"); + println!("{:?}", e); } } } diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index d4a78d5..9c970fc 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -18,10 +18,11 @@ pub(crate) mod admin { use super::*; pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; @@ -68,10 +69,11 @@ pub(crate) mod user { use sqlx::{Pool, Sqlite}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let config_toml = get_config_icons::minimal(); @@ -91,7 +93,7 @@ pub(crate) mod user { } } - pub async fn get_current_icon(db: &Pool) -> Option { + async fn get_current_icon(db: &Pool) -> Option { match sqlx::query_as::<_, ServerIcons>( " SELECT * from server_icons ORDER BY id DESC LIMIT 1 @@ -111,10 +113,8 @@ pub(crate) mod user { pub(crate) mod festival { use serenity::all::{CommandInteraction, Context}; - use skynet_discord_bot::{ - common::server_icon::{get_config_icons, update_icon::get_festival}, - Config, - }; + use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon::get_festival}; + use skynet_discord_bot::Config; // use this to return what current festivals are active? pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { @@ -143,10 +143,11 @@ pub(crate) mod user { use sqlx::{Pool, Sqlite}; pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let config_toml = get_config_icons::minimal(); @@ -204,11 +205,11 @@ pub(crate) mod user { // the `` is so that the numbers will be rendered in monospaced font // the <> is to suppress the URL embed - let line = format!("``{current_leading}{times}`` [{name}](<{url}>)"); + let line = format!("``{}{}`` [{}](<{}>)", current_leading, times, name, url); let length = line.len() + 1; - // +3 is to account for the closing fence + // +3 is to account for the closing fense if length < (limit + 3) { response.push(line); limit -= length; diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index 843ca70..d790f9f 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -4,16 +4,11 @@ use lettre::{ Message, SmtpTransport, Transport, }; use maud::html; -use serenity::{ - all::CommandOptionType, - builder::{CreateCommand, CreateCommandOption}, - client::Context, - model::id::UserId, -}; -use skynet_discord_bot::{ - common::database::{DataBase, Wolves, WolvesVerify}, - get_now_iso, random_string, Config, -}; +use serenity::all::CommandOptionType; +use serenity::builder::CreateCommandOption; +use serenity::{builder::CreateCommand, client::Context, model::id::UserId}; +use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify}; +use skynet_discord_bot::{get_now_iso, random_string, Config}; use sqlx::{Pool, Sqlite}; pub mod link { @@ -21,10 +16,11 @@ pub mod link { use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; @@ -100,7 +96,7 @@ pub mod link { return "Email already verified".to_string(); } - // generate an auth key + // generate a auth key let auth = random_string(20); match send_mail(&config, &details.email, &auth, &command.user.name) { Ok(_) => match save_to_db(&db, &details, &auth, &command.user.id).await { @@ -114,7 +110,7 @@ pub mod link { } } - format!("Verification email sent to {email}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.") + format!("Verification email sent to {}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.", email) } pub async fn get_server_member_discord(db: &Pool, user: &UserId) -> Option { @@ -209,7 +205,7 @@ pub mod link { .subject("Skynet: Link Discord to Wolves.") .multipart( // This is composed of two parts. - // also helps not trip spam settings (uneven number of urls) + // also helps not trip spam settings (uneven number of url's MultiPart::alternative() .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text)) .singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())), @@ -283,40 +279,22 @@ pub mod link { } } -pub mod link_docs { - use super::*; - pub mod users { - use super::*; - use serenity::all::CommandInteraction; - - pub async fn run(_command: &CommandInteraction, _ctx: &Context) -> String { - "https://forgejo.skynet.ie/Skynet/discord-bot/src/branch/main/doc/User.md".to_string() - } - } - - // pub mod committee { - // - // } -} - pub mod verify { use super::*; use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db}; - use serenity::{ - all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, - model::user::User, - }; - use skynet_discord_bot::common::{ - database::{get_server_config, ServerMembersWolves, Servers}, - wolves::committees::Committees, - }; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; + use serenity::model::user::User; + use skynet_discord_bot::common::database::get_server_config; + use skynet_discord_bot::common::database::{ServerMembersWolves, Servers}; + use skynet_discord_bot::common::wolves::committees::Committees; use sqlx::Error; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; + let db = db_lock.read().await; // check if user has used /link_wolves let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await { @@ -363,12 +341,12 @@ pub mod verify { "Discord username linked to Wolves".to_string() } Err(e) => { - println!("{e:?}"); + println!("{:?}", e); "Failed to save, please try /link_wolves again".to_string() } }; } - Err(e) => println!("{e:?}"), + Err(e) => println!("{:?}", e), } "Failed to verify".to_string() @@ -425,7 +403,7 @@ pub mod verify { } if let Err(e) = member.add_roles(&ctx, &roles).await { - println!("{e:?}"); + println!("{:?}", e); } } } @@ -441,7 +419,7 @@ pub mod verify { WHERE committee LIKE ?1 "#, ) - .bind(format!("%{wolves_id}%")) + .bind(format!("%{}%", wolves_id)) .fetch_all(db) .await .unwrap_or_else(|e| { @@ -458,7 +436,7 @@ pub mod verify { let config = config_lock.read().await; if let Some(x) = get_server_member_discord(db, &discord.id).await { - // if they are a member of one or more committees, and in teh committee server then give them the general committee role + // if they are a member of one or more committees, and in teh committee server then give the teh general committee role // they will get teh more specific vanity role later if !get_committees_id(db, x.id_wolves).await.is_empty() { let server = config.committee_server; @@ -492,12 +470,13 @@ pub mod unlink { use sqlx::{Pool, Sqlite}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; + let db = db_lock.read().await; - // doesn't matter if there is one or not, it will be removed regardless + // dosent matter if there is one or not, it will be removed regardless delete_link(&db, &command.user.id).await; "Discord link removed".to_string() @@ -543,5 +522,4 @@ pub fn register() -> CreateCommand { .add_sub_option(CreateCommandOption::new(CommandOptionType::String, "minecraft_username", "Your Minecraft username").required(true)) .add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "bedrock_account", "Is this a Bedrock account?").required(false)), ) - .add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "docs", "Link to where the documentation can be found.")) } diff --git a/src/common/database.rs b/src/common/database.rs index 2eaf5df..0663799 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -1,21 +1,17 @@ use crate::Config; use serde::{Deserialize, Serialize}; -use serenity::{ - model::{ - guild, - id::{ChannelId, GuildId, RoleId, UserId}, - }, - prelude::TypeMapKey, -}; -use sqlx::{ - sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}, - Error, FromRow, Pool, Row, Sqlite, -}; -use std::{str::FromStr, sync::Arc}; +use serenity::model::guild; +use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; +use serenity::prelude::TypeMapKey; +use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; +use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::RwLock; pub struct DataBase; impl TypeMapKey for DataBase { - type Value = Arc>; + type Value = Arc>>; } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -224,7 +220,7 @@ pub async fn db_init(config: &Config) -> Result, Error> { let pool = SqlitePoolOptions::new() .max_connections(5) .connect_with( - SqliteConnectOptions::from_str(&format!("sqlite://{database}"))? + SqliteConnectOptions::from_str(&format!("sqlite://{}", database))? .foreign_keys(true) .create_if_missing(true), ) diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 77cd754..4faa8ed 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -1,7 +1,10 @@ -use crate::{common::set_roles::normal::get_server_member_bulk, Config}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use crate::common::set_roles::normal::get_server_member_bulk; +use crate::Config; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; use serenity::model::id::GuildId; -use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; +use sqlx::sqlite::SqliteRow; +use sqlx::{Error, FromRow, Pool, Row, Sqlite}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Minecraft { @@ -24,7 +27,7 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft { /** loop through all members of server get a list of folks with mc accounts that are members -and a list that aren't members +and a list that arent members */ pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, config: &Config) { let mut usernames = vec![]; @@ -109,7 +112,7 @@ pub async fn whitelist_wipe(server: &str, token: &str) { }; post(&format!("{url_base}/files/delete"), &bearer, &deletion).await; - // recreate the file, passing in the type here so the compiler knows what type of Vec it is + // recreate teh file, passing in the type here so the compiler knows what type of vec it is post::>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; // reload the whitelist @@ -152,7 +155,7 @@ pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Ve } pub async fn whitelist_update(add: &Vec<(String, bool)>, server: &str, token: &str) { - println!("Update whitelist for {server}"); + println!("Update whitelist for {}", server); let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); let bearer = format!("Bearer {token}"); diff --git a/src/common/renderer.rs b/src/common/renderer.rs index f2c50cc..f99d891 100644 --- a/src/common/renderer.rs +++ b/src/common/renderer.rs @@ -1,11 +1,10 @@ // this code is taken from https://github.com/MCorange99/svg2colored-png/tree/main -// I was unable to figure out how to use usvg myself so yoinked it from here. +// I was unable to figure out how to use usvg myself so younked it from here. -use std::{ - ffi::OsStr, - path::{Path, PathBuf}, -}; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +// use clap::builder::OsStr; use color_eyre::{eyre::bail, Result}; use usvg_text_layout::TreeTextToPath; @@ -16,7 +15,7 @@ pub struct Args { /// Output folder where the PNG's will be placed pub output: PathBuf, - /// Comma separated colors that will be used in HEX Eg. 000000,ffffff + /// Comma seperated colors that will be used in HEX Eg. 000000,ffffff /// Can be like an object: black:000000,white:ffffff pub colors: String, @@ -178,7 +177,7 @@ impl Renderer { } fn set_color(&self, svg: &str, color: &String) -> String { - svg.replace("fill=\"currentColor\"", &format!("fill=\"#{color}\"")) + svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color)) } fn get_svg_data(&self, fi: &Path) -> Result { diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index 1a7c2ef..8fcbcee 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -191,28 +191,6 @@ pub mod update_icon { { dbg!(e); } - - if let Err(e) = Command::new("git") - // Install LFS for the repo - .arg("lfs") - .arg("install") - .current_dir(&folder) - .output() - { - dbg!(e); - } - - if let Err(e) = Command::new("git") - // clone the repo, gracefully "fails" - .arg("lfs") - .arg("pull") - .arg("origin") - .arg("main") - .current_dir(&folder) - .output() - { - dbg!(e); - } } fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { @@ -281,7 +259,7 @@ pub mod update_icon { // check if exists if !path_new.exists() { - // convert if it hasn't been converted already + // convert if it hasnt been converted already match r.render(&path_local, &args) { Ok(_) => {} Err(_e) => { @@ -308,19 +286,9 @@ pub mod update_icon { fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { let mut filtered: Vec = vec![]; - let allowed_files = vec![".png", ".jpeg", ".gif", ".svg"]; 'outer: for logo in existing { let name_lowercase0 = logo.name.to_ascii_lowercase(); let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); - let mut allowed = false; - for allowed_type in &allowed_files { - if name_lowercase.ends_with(allowed_type) { - allowed = true; - } - } - if !allowed { - continue; - } if !festival_data.current.is_empty() { // if its a current festival filter based on it diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index e11c637..4e24b1a 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -1,27 +1,18 @@ pub mod normal { - use crate::{ - common::database::{DataBase, ServerMembersWolves, Servers, Wolves}, - get_now_iso, - }; - use serenity::{ - client::Context, - model::id::{GuildId, RoleId, UserId}, - }; + use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves}; + use crate::get_now_iso; + use serenity::client::Context; + use serenity::model::id::{GuildId, RoleId, UserId}; use sqlx::{Pool, Sqlite}; - struct RolesChange { - total: i32, - new: i32, - current_add: i32, - current_rem: i32, - } - pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; + let db = db_lock.read().await; + let Servers { server, role_past, @@ -29,12 +20,7 @@ pub mod normal { .. } = server; - let mut roles_set = RolesChange { - total: 0, - new: 0, - current_add: 0, - current_rem: 0, - }; + let mut roles_set = [0, 0, 0]; let mut members = vec![]; for member in get_server_member_bulk(&db, server).await { @@ -46,30 +32,28 @@ pub mod normal { if let Ok(x) = server.members(ctx, None, None).await { for member in x { - // members_changed acts as an override to only deal with the users in it + // members_changed acts as an override to only deal with teh users in it if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { continue; } if members.contains(&member.user.id) { - roles_set.total += 1; - let mut roles = vec![]; if let Some(role) = &role_past { if !member.roles.contains(role) { - roles_set.new += 1; + roles_set[0] += 1; roles.push(role.to_owned()); } } if !member.roles.contains(role_current) { - roles_set.current_add += 1; + roles_set[1] += 1; roles.push(role_current.to_owned()); } if let Err(e) = member.add_roles(ctx, &roles).await { - println!("{e:?}"); + println!("{:?}", e); } } else { // old and never @@ -81,16 +65,16 @@ pub mod normal { } if member.roles.contains(role_current) { - roles_set.current_rem += 1; - // if they're not a current member and have the role then remove it + roles_set[2] += 1; + // if theya re not a current member and have the role then remove it if let Err(e) = member.remove_role(ctx, role_current).await { - println!("{e:?}"); + println!("{:?}", e); } } } for role in remove_roles.iter().flatten() { if let Err(e) = member.remove_role(ctx, role).await { - println!("{e:?}"); + println!("{:?}", e); } } } @@ -99,14 +83,7 @@ pub mod normal { set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; // small bit of logging to note changes over time - println!( - "{:?} Total: {} Changes: New: +{}, Current: +{}/-{}", - server.get(), - roles_set.total, - roles_set.new, - roles_set.current_add, - roles_set.current_rem - ); + println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]); } pub async fn get_server_member_bulk(db: &Pool, server: &GuildId) -> Vec { @@ -146,7 +123,7 @@ pub mod normal { Ok(_) => {} Err(e) => { println!("Failure to insert into {}", server.get()); - println!("{e:?}"); + println!("{:?}", e); } } } @@ -154,29 +131,30 @@ pub mod normal { // for updating committee members pub mod committee { - use crate::{ - common::{ - database::{get_channel_from_row, get_role_from_row, DataBase, Wolves}, - wolves::committees::Committees, - }, - Config, - }; + use crate::common::database::{get_channel_from_row, get_role_from_row, DataBase, Wolves}; + use crate::common::wolves::committees::Committees; + use crate::Config; use serde::{Deserialize, Serialize}; - use serenity::{ - all::EditRole, - builder::CreateChannel, - client::Context, - model::{channel::ChannelType, guild::Member, id::ChannelId, prelude::RoleId}, - }; - use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; + use serenity::all::EditRole; + use serenity::builder::CreateChannel; + use serenity::client::Context; + use serenity::model::channel::ChannelType; + use serenity::model::guild::Member; + use serenity::model::id::ChannelId; + use serenity::model::prelude::RoleId; + use sqlx::sqlite::SqliteRow; + use sqlx::{Error, FromRow, Pool, Row, Sqlite}; use std::collections::HashMap; + use std::sync::Arc; - pub async fn check_committee(ctx: &Context) { - let db = { + pub async fn check_committee(ctx: Arc) { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; + let db = db_lock.read().await; + let config_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() @@ -188,21 +166,16 @@ pub mod committee { // because to use it to update a single user we need to pre-get the members of teh server let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); - update_committees(&db, ctx, &config_global, &mut members).await; + update_committees(&db, &ctx, &config_global, &mut members).await; } /** - This function can take a Vec of members (or just one) and gives them the appropriate roles on teh committee server + This function can take a vec of members (or just one) and gives tehm the appropiate roles on teh committee server */ pub async fn update_committees(db: &Pool, ctx: &Context, config: &Config, members: &mut Vec) { let server = config.committee_server; let committee_member = config.committee_role; - let committees = match get_committees(db).await { - None => { - return; - } - Some(x) => x, - }; + let committees = get_committees(db).await; let categories = config.committee_category.clone(); // information about the server @@ -224,11 +197,11 @@ pub mod committee { let mut channels = server.channels(&ctx).await.unwrap_or_default(); - // a map of users and the roles they are going to be getting + // a map of users and the roles they are goign to be getting let mut users_roles = HashMap::new(); let mut re_order = false; - // we need to create roles and channels if they don't already exist + // we need to create roles and channels if tehy dont already exist let mut category_index = 0; let mut i = 0; loop { @@ -329,21 +302,21 @@ pub mod committee { // now we have a map of all users that should get roles time to go through all the folks on teh server for member in members { - // if member.user.id != 136522490632601600 { - // continue; - // } - // let roles_current = member.roles(ctx).unwrap_or_default(); let roles_required = match users_roles.get(&member.user.id) { None => { vec![] } - Some(x) => x.to_owned(), + Some(x) => { + let mut tmp = x.to_owned(); + if !tmp.is_empty() { + tmp.push(committee_member); + } + tmp + } }; - let on_committee = !roles_required.is_empty(); - let mut roles_rem = vec![]; let mut roles_add = vec![]; // get a list of all the roles to remove from someone @@ -352,25 +325,14 @@ pub mod committee { for role in &roles_current { roles_current_id.push(role.id.to_owned()); if !roles_required.contains(&role.id) { - if role.id == committee_member && on_committee { - continue; - } - roles_rem.push(role.id.to_owned()); } } - let has_committee_role = roles_current_id.contains(&committee_member); - - if on_committee && !has_committee_role { - // if there are committee roles then give the general purpose role - roles_add.push(committee_member); - } - if !on_committee && has_committee_role { - roles_rem.push(committee_member); - } - if !roles_required.is_empty() { + // if there are committee roles then give the general purporse role + roles_add.push(committee_member); + if let Some(x) = roles_db.get_mut(&0) { x.count += 1; } @@ -389,6 +351,8 @@ pub mod committee { if !roles_add.is_empty() { // these roles are flavor roles, only there to make folks mentionable member.add_roles(&ctx, &roles_add).await.unwrap_or_default(); + } else { + member.remove_roles(&ctx, &[committee_member]).await.unwrap_or_default(); } } @@ -436,10 +400,10 @@ pub mod committee { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct CommitteeRoles { id_wolves: i64, - pub id_role: RoleId, - pub id_channel: ChannelId, + id_role: RoleId, + id_channel: ChannelId, pub name_role: String, - pub name_channel: String, + name_channel: String, pub count: i64, } @@ -476,8 +440,8 @@ pub mod committee { { Ok(_) => {} Err(e) => { - println!("Failure to insert into Wolves {role:?}"); - println!("{e:?}"); + println!("Failure to insert into Wolves {:?}", role); + println!("{:?}", e); } } } @@ -494,13 +458,13 @@ pub mod committee { .await .unwrap_or_else(|e| { println!("Failure to get Roles from committee_roles"); - println!("{e:?}"); + println!("{:?}", e); vec![] }) } - pub async fn get_committees(db: &Pool) -> Option> { - match sqlx::query_as::<_, Committees>( + pub async fn get_committees(db: &Pool) -> Vec { + sqlx::query_as::<_, Committees>( r#" SELECT * FROM committees @@ -508,13 +472,10 @@ pub mod committee { ) .fetch_all(db) .await - { - Ok(x) => Some(x), - Err(e) => { - dbg!(e); - None - } - } + .unwrap_or_else(|e| { + dbg!(e); + vec![] + }) } async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 3744558..6f73842 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -38,8 +38,8 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { { Ok(_) => {} Err(e) => { - println!("Failure to insert into Wolves {user:?}"); - println!("{e:?}"); + println!("Failure to insert into Wolves {:?}", user); + println!("{:?}", e); } } } @@ -48,14 +48,12 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { This is getting data for Clubs and Socs */ pub mod cns { - use crate::{ - common::{ - database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}, - wolves::{add_users_wolves, WolvesResultUserMin}, - }, - Config, - }; - use serenity::{client::Context, model::id::GuildId}; + use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}; + use crate::common::set_roles::normal::update_server; + use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; + use crate::Config; + use serenity::client::Context; + use serenity::model::id::GuildId; use sqlx::{Pool, Sqlite}; use std::collections::BTreeMap; @@ -69,10 +67,11 @@ pub mod cns { } pub async fn get_wolves(ctx: &Context) { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; + let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; @@ -97,6 +96,7 @@ pub mod cns { let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::>(); // list of users that need to be updated for this server + let mut user_to_update = vec![]; let mut server_name_tmp = None; for user in wolves.get_members(wolves_api).await { // dbg!(&user.committee); @@ -115,6 +115,10 @@ pub mod cns { add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; if old.expiry != user.expiry { add_users_server_members(&db, server, &user).await; + + if let Some(discord_id) = old.discord { + user_to_update.push(discord_id); + } } } } @@ -125,6 +129,9 @@ pub mod cns { set_server_member(&db, server, cs_id).await; } } + if !user_to_update.is_empty() { + update_server(ctx, &server_config, &[], &user_to_update).await; + } } } @@ -144,7 +151,7 @@ pub mod cns { Ok(_) => {} Err(e) => { println!("Failure to set server name {}", server.get()); - println!("{e:?}"); + println!("{:?}", e); } } } @@ -183,7 +190,7 @@ pub mod cns { Ok(_) => {} Err(e) => { println!("Failure to insert into ServerMembers {} {:?}", server.get(), user); - println!("{e:?}"); + println!("{:?}", e); } } } @@ -193,7 +200,8 @@ pub mod cns { Get and store the data on C&S committees */ pub mod committees { - use crate::{common::database::DataBase, Config}; + use crate::common::database::DataBase; + use crate::Config; use serenity::client::Context; use sqlx::{Pool, Sqlite}; @@ -223,10 +231,11 @@ pub mod committees { } pub async fn get_cns(ctx: &Context) { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; + let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; @@ -261,8 +270,8 @@ pub mod committees { { Ok(_) => {} Err(e) => { - println!("Failure to insert into Committees {committee:?}"); - println!("{e:?}"); + println!("Failure to insert into Committees {:?}", committee); + println!("{:?}", e); } } } diff --git a/src/lib.rs b/src/lib.rs index 1a6afb1..abf8a2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,8 @@ pub mod common; use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distr::Alphanumeric, rng, Rng}; -use serenity::{ - model::id::{ChannelId, GuildId, RoleId}, - prelude::TypeMapKey, -}; +use serenity::model::id::{ChannelId, GuildId, RoleId}; +use serenity::prelude::TypeMapKey; use std::{env, sync::Arc}; use tokio::sync::RwLock; @@ -36,7 +34,7 @@ pub struct Config { pub committee_role: RoleId, pub committee_category: Vec, - // items pertaining to CompSoc only + // items pertaining to compsoc only pub compsoc_server: GuildId, } impl TypeMapKey for Config { @@ -111,7 +109,7 @@ pub fn get_config() -> Config { } } if let Ok(x) = env::var("COMMITTEE_CATEGORY") { - for part in x.split(',') { + for part in x.split(",") { if let Ok(x) = part.trim().parse::() { config.committee_category.push(ChannelId::new(x)); } diff --git a/src/main.rs b/src/main.rs index 8dff675..9de027e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,21 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; +use serenity::all::{ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildMemberUpdateEvent, Interaction}; +use serenity::model::guild::Member; use serenity::{ - all::{Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, Interaction}, async_trait, client::{Context, EventHandler}, - gateway::{ActivityData, ChunkGuildFilter}, model::{ - event::GuildMemberUpdateEvent, gateway::{GatewayIntents, Ready}, - guild::Member, - id::GuildId, user::OnlineStatus, }, Client, }; -use skynet_discord_bot::{ - common::{ - database::{db_init, get_server_config, get_server_member, DataBase}, - set_roles::committee::update_committees, - wolves::committees::Committees, - }, - get_config, Config, -}; +use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase}; +use skynet_discord_bot::common::set_roles::committee::update_committees; +use skynet_discord_bot::common::wolves::committees::Committees; +use skynet_discord_bot::{get_config, Config}; use sqlx::{Pool, Sqlite}; use std::sync::Arc; use tokio::sync::RwLock; @@ -31,21 +24,15 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - // this caches members of all servers teh bot is in - async fn cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - // handles previously linked accounts joining the server async fn guild_member_addition(&self, ctx: Context, new_member: Member) { - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; + let db = db_lock.read().await; + let config_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() @@ -79,7 +66,7 @@ impl EventHandler for Handler { } if let Err(e) = new_member.add_roles(&ctx, &roles).await { - println!("{e:?}"); + println!("{:?}", e); } } else { let tmp = get_committee(&db, config_server.wolves_id).await; @@ -107,12 +94,14 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use // handles role updates async fn guild_member_update(&self, ctx: Context, _old_data: Option, new_data: Option, _: GuildMemberUpdateEvent) { // get config/db - let db = { + let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; - // check if the role changed is part of the ones for this server + let db = db_lock.read().await; + + // check if the role changed is part of the oens for this server if let Some(x) = new_data { on_role_change(&db, &ctx, x).await; } @@ -142,7 +131,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use { Ok(_) => {} Err(e) => { - println!("{e:?}") + println!("{:?}", e) } } @@ -150,17 +139,17 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use match config.committee_server.set_commands(&ctx.http, vec![commands::count::committee::register()]).await { Ok(_) => {} Err(e) => { - println!("{e:?}") + println!("{:?}", e) } } - // CompSoc Server + // compsoc Server match config .compsoc_server .set_commands( &ctx.http, vec![ - // commands just for the CompSoc server + // commands just for the compsoc server commands::count::servers::register(), commands::server_icon::user::register(), ], @@ -169,7 +158,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use { Ok(_) => {} Err(e) => { - println!("{e:?}") + println!("{:?}", e) } } } @@ -177,7 +166,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use async fn interaction_create(&self, ctx: Context, interaction: Interaction) { if let Interaction::Command(command) = interaction { let _ = command.defer_ephemeral(&ctx.http).await; - // println!("Received command interaction: {:#?}", command); + //println!("Received command interaction: {:#?}", command); let content = match command.data.name.as_str() { // user commands @@ -188,7 +177,6 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use "verify" => commands::wolves::verify::run(&command, &ctx).await, "unlink" => commands::wolves::unlink::run(&command, &ctx).await, "link_minecraft" => commands::minecraft::user::add::run(&command, &ctx).await, - "docs" => commands::wolves::link_docs::users::run(&command, &ctx).await, // "link" => commands::count::servers::run(&command, &ctx).await, &_ => format!("not implemented :( wolves {}", x.name.as_str()), }, @@ -256,7 +244,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use }; if let Err(why) = command.edit_response(&ctx.http, EditInteractionResponse::new().content(content)).await { - println!("Cannot respond to slash command: {why}"); + println!("Cannot respond to slash command: {}", why); } } } @@ -291,8 +279,7 @@ async fn main() { let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; // Build our client. let mut client = Client::builder(&config.discord_token, intents) - .event_handler(Handler) - .cache_settings(serenity::cache::Settings::default()) + .event_handler(Handler {}) .await .expect("Error creating client"); @@ -300,7 +287,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(db)); + data.insert::(Arc::new(RwLock::new(db))); } // Finally, start a single shard, and start listening to events. @@ -308,6 +295,6 @@ async fn main() { // Shards will automatically attempt to reconnect, and will perform // exponential backoff until it reconnects. if let Err(why) = client.start().await { - println!("Client error: {why:?}"); + println!("Client error: {:?}", why); } }