Compare commits

..

No commits in common. "main" and "#35_remove-hardcided-servers" have entirely different histories.

29 changed files with 317 additions and 716 deletions

View file

@ -1,7 +1,9 @@
on: on:
- pull_request
- push - push
- workflow_dispatch - workflow_dispatch
jobs: jobs:
check_lfs: check_lfs:
# nix/docker # nix/docker

View file

@ -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

View file

@ -1,2 +0,0 @@
# Fix typos
7e90f451965b0edbd331765ad295a02f31d2bf24

View file

@ -7,4 +7,4 @@ fn_params_layout = "Compressed"
struct_lit_width = 0 struct_lit_width = 0
tab_spaces = 2 tab_spaces = 2
use_small_heuristics = "Max" use_small_heuristics = "Max"
imports_granularity = "Crate" #imports_granularity="Crate"

View file

@ -1,2 +0,0 @@
[formatting]
column_width = 120

View file

@ -4,9 +4,13 @@ version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]] [[bin]]
name = "update_data" name = "update_data"
[[bin]]
name = "update_users"
[[bin]] [[bin]]
name = "update_committee" name = "update_committee"
@ -16,9 +20,6 @@ name = "update_minecraft"
[[bin]] [[bin]]
name = "update_server-icon" name = "update_server-icon"
[[bin]]
name = "cleanup_committee"
[dependencies] [dependencies]
# discord library # discord library
serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] }
@ -34,7 +35,7 @@ surf = "2.3"
dotenvy = "0.15" dotenvy = "0.15"
# For sqlite # 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"] } serde_json = { version = "1.0", features = ["raw_value"] }
# create random strings # create random strings

View file

@ -1,7 +1,7 @@
# Skynet Discord Bot # Skynet Discord Bot
The Skynet bot is designed to manage users on Discord. 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. 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 ## Documentation
We have split up the documentation into different segments depending on who the user is. We have split up the documentation into different segments depending on who the user is.

17
flake.lock generated
View file

@ -32,22 +32,6 @@
"type": "indirect" "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": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1722995383, "lastModified": 1722995383,
@ -67,7 +51,6 @@
"inputs": { "inputs": {
"naersk": "naersk", "naersk": "naersk",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2",
"nixpkgs-mozilla": "nixpkgs-mozilla",
"utils": "utils" "utils": "utils"
} }
}, },

View file

@ -4,10 +4,6 @@
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable"; nixpkgs.url = "nixpkgs/nixos-unstable";
naersk.url = "github:nix-community/naersk"; naersk.url = "github:nix-community/naersk";
nixpkgs-mozilla = {
url = "github:mozilla/nixpkgs-mozilla";
flake = false;
};
utils.url = "github:numtide/flake-utils"; utils.url = "github:numtide/flake-utils";
}; };
@ -21,27 +17,12 @@
nixpkgs, nixpkgs,
utils, utils,
naersk, naersk,
nixpkgs-mozilla,
}: }:
utils.lib.eachDefaultSystem ( utils.lib.eachDefaultSystem (
system: let system: let
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)); overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
pkgs = (import nixpkgs) { pkgs = (import nixpkgs) {inherit system;};
inherit system; naersk' = pkgs.callPackage naersk {};
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;
};
package_name = "skynet_discord_bot"; package_name = "skynet_discord_bot";
desc = "Skynet Discord Bot"; desc = "Skynet Discord Bot";
buildInputs = with pkgs; [ buildInputs = with pkgs; [
@ -122,14 +103,12 @@
wantedBy = []; wantedBy = [];
after = ["network-online.target"]; after = ["network-online.target"];
environment = environment_config; environment = environment_config;
path = with pkgs; [ git git-lfs ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.user}"; Group = "${cfg.user}";
ExecStart = "${self.defaultPackage."${system}"}/bin/${script}"; ExecStart = "${self.defaultPackage."${system}"}/bin/${script}";
# kill each service if its ran for 9 min
TimeoutStartSec=540;
EnvironmentFile = [ EnvironmentFile = [
"${cfg.env.discord}" "${cfg.env.discord}"
"${cfg.env.mail}" "${cfg.env.mail}"
@ -154,14 +133,13 @@
# modify these # modify these
scripts = { scripts = {
# every 10 min # every 20 min
"update_data" = "*:0,10,20,30,40,50"; "update_data" = "*:0,10,20,30,40,50";
# groups are updated every hour, offset from teh ldap # 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 # Committee server has its own timer
"update_committee" = "*:5,15,25,35,45,55"; "update_committee" = "*:15:00";
# minecraft stuff is updated at 5am # minecraft stuff is updated at 5am
# this service does not depend on teh discord cache
"update_minecraft" = "5:10:00"; "update_minecraft" = "5:10:00";
# server icon gets updated daily at midnight # server icon gets updated daily at midnight
"update_server-icon" = "0:01:00"; "update_server-icon" = "0:01:00";
@ -224,7 +202,6 @@
after = ["network-online.target"]; after = ["network-online.target"];
wants = []; wants = [];
environment = environment_config; environment = environment_config;
path = with pkgs; [ git git-lfs ];
serviceConfig = { serviceConfig = {
User = "${cfg.user}"; User = "${cfg.user}";

View file

@ -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::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(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<GuildId>) {
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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::<DataBase>().expect("Expected Config in TypeMap.").clone()
};
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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<Sqlite>, 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::<Vec<_>>();
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::<Vec<_>>();
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);
}
}
}
}
}

View file

@ -1,17 +1,12 @@
use serenity::{ use serenity::{
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
async_trait, async_trait,
client::{Context, EventHandler}, client::{Context, EventHandler},
model::gateway::GatewayIntents, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, DataBase};
common::{ use skynet_discord_bot::common::set_roles::committee;
database::{db_init, DataBase}, use skynet_discord_bot::{get_config, Config};
set_roles::committee,
},
get_config, Config,
};
use std::{process, sync::Arc}; use std::{process, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -28,7 +23,6 @@ async fn main() {
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler {}) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -36,39 +30,25 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }
struct Handler; struct Handler;
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn cache_ready(&self, ctx: Context, _guilds: Vec<GuildId>) { async fn ready(&self, ctx: Context, ready: Ready) {
let config_lock = { let ctx = Arc::new(ctx);
let data_read = ctx.data.read().await; println!("{} is connected!", ready.user.name);
data_read.get::<Config>().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!");
// u[date committee server // u[date committee server
committee::check_committee(&ctx).await; committee::check_committee(Arc::clone(&ctx)).await;
// finish up // finish up
process::exit(0); process::exit(0);
} }
}
} }

View file

@ -4,13 +4,10 @@ use serenity::{
model::gateway::{GatewayIntents, Ready}, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, DataBase};
common::{ use skynet_discord_bot::common::wolves::cns::get_wolves;
database::{db_init, DataBase}, use skynet_discord_bot::common::wolves::committees::get_cns;
wolves::{cns::get_wolves, committees::get_cns}, use skynet_discord_bot::{get_config, Config};
},
get_config, Config,
};
use std::{process, sync::Arc}; use std::{process, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -30,7 +27,6 @@ async fn main() {
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler {}) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -38,11 +34,11 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }

View file

@ -1,10 +1,6 @@
use skynet_discord_bot::{ use skynet_discord_bot::common::database::db_init;
common::{ use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe};
database::db_init, use skynet_discord_bot::get_config;
minecraft::{get_minecraft_config, update_server, whitelist_wipe},
},
get_config,
};
use std::collections::HashSet; use std::collections::HashSet;
#[tokio::main] #[tokio::main]

View file

@ -4,11 +4,9 @@ use serenity::{
model::gateway::{GatewayIntents, Ready}, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon};
use skynet_discord_bot::{ use skynet_discord_bot::{
common::{ common::database::{db_init, DataBase},
database::{db_init, DataBase},
server_icon::{get_config_icons, update_icon},
},
get_config, Config, get_config, Config,
}; };
use std::{process, sync::Arc}; use std::{process, sync::Arc};
@ -27,7 +25,6 @@ async fn main() {
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler {}) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -35,11 +32,11 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { 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); let ctx = Arc::new(ctx);
println!("{} is connected!", ready.user.name); println!("{} is connected!", ready.user.name);
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;

View file

@ -1,24 +1,13 @@
use serenity::{ use serenity::{
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
async_trait, async_trait,
client::{Context, EventHandler}, client::{Context, EventHandler},
model::gateway::GatewayIntents, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase};
common::{ use skynet_discord_bot::common::set_roles::normal;
database::{db_init, get_server_config_bulk, DataBase}, use skynet_discord_bot::{get_config, Config};
set_roles::normal, use std::{process, sync::Arc};
},
get_config, Config,
};
use std::{
process,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use tokio::sync::RwLock; use tokio::sync::RwLock;
#[tokio::main] #[tokio::main]
@ -33,11 +22,7 @@ async fn main() {
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler { .event_handler(Handler {})
server_count: Default::default(),
server_cached: Default::default(),
})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -45,51 +30,38 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }
struct Handler { struct Handler;
server_count: AtomicUsize,
server_cached: AtomicUsize,
}
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) { async fn ready(&self, ctx: Context, ready: Ready) {
self.server_count.swap(guilds.len(), Ordering::SeqCst); let ctx = Arc::new(ctx);
for guild in guilds { println!("{} is connected!", ready.user.name);
ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None);
}
println!("Cache loaded {}", &self.server_count.load(Ordering::SeqCst));
}
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 // this goes into each server and sets roles for each wolves member
check_bulk(&ctx).await; check_bulk(Arc::clone(&ctx)).await;
// finish up // finish up
process::exit(0); process::exit(0);
} }
}
}
} }
async fn check_bulk(ctx: &Context) { async fn check_bulk(ctx: Arc<Context>) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
for server_config in get_server_config_bulk(&db).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;
} }
} }

View file

@ -1,12 +1,8 @@
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, use serenity::client::Context;
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::{ use skynet_discord_bot::common::wolves::cns::get_wolves;
database::{get_server_config, DataBase, Servers},
set_roles::normal::update_server,
wolves::cns::get_wolves,
};
use sqlx::{Error, Pool, Sqlite}; use sqlx::{Error, Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { 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(); return "Please provide a valid channel for ``Bot Channel``".to_string();
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let server_data = Servers { let server_data = Servers {
server: command.guild_id.unwrap_or_default(), 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 { match add_server(&db, ctx, &server_data).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Servers {server_data:?}"); return format!("Failure to insert into Servers {:?}", server_data);
} }
} }
@ -101,7 +98,7 @@ async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Resul
.fetch_optional(db) .fetch_optional(db)
.await; .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 { let (update, current_remove, current_role, past_remove, past_role) = match &existing {
None => (true, false, None, false, None), None => (true, false, None, false, None),
Some(x) => { Some(x) => {

View file

@ -5,7 +5,8 @@ pub mod committee {
use serenity::all::{ use serenity::all::{
CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, 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 { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let sub_options = if let Some(CommandDataOption { let sub_options = if let Some(CommandDataOption {
@ -27,10 +28,11 @@ pub mod committee {
false false
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let mut cs = vec![]; let mut cs = vec![];
// pull it from a DB // pull it from a DB
@ -51,7 +53,7 @@ pub mod committee {
for (count, name) in cs { for (count, name) in cs {
let leading = if count < 10 { " " } else { "" }; let leading = if count < 10 { " " } else { "" };
let line = format!("{leading}{count} {name}"); let line = format!("{}{} {}", leading, count, name);
let length = line.len() + 1; let length = line.len() + 1;
@ -83,28 +85,23 @@ pub mod servers {
// get the list of all the current clubs/socs // get the list of all the current clubs/socs
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption};
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{get_server_config_bulk, DataBase};
common::{ use skynet_discord_bot::common::set_roles::committee::get_committees;
database::{get_server_config_bulk, DataBase}, use skynet_discord_bot::get_now_iso;
set_roles::committee::get_committees,
},
get_now_iso,
};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::collections::HashMap; use std::collections::HashMap;
pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let mut committees = HashMap::new(); let mut committees = HashMap::new();
if let Some(x) = get_committees(&db).await { for committee in get_committees(&db).await {
for committee in x {
committees.insert(committee.id, committee.to_owned()); committees.insert(committee.id, committee.to_owned());
} }
}
let mut cs = vec![]; let mut cs = vec![];
// pull it from a DB // pull it from a DB
@ -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; 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) { if length < (limit + 3) {
response.push(line); response.push(line);
limit -= length; limit -= length;

View file

@ -9,24 +9,19 @@ pub(crate) mod user {
use super::*; use super::*;
use crate::commands::wolves::link::get_server_member_discord; use crate::commands::wolves::link::get_server_member_discord;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, use serenity::model::id::UserId;
model::id::UserId, use skynet_discord_bot::common::database::Wolves;
}; use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft};
use skynet_discord_bot::{ use skynet_discord_bot::Config;
common::{
database::Wolves,
minecraft::{whitelist_update, Minecraft},
},
Config,
};
use sqlx::Error; use sqlx::Error;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -74,14 +69,14 @@ pub(crate) mod user {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
dbg!("{:?}", e); dbg!("{:?}", e);
return format!("Failure to minecraft username {username:?}"); return format!("Failure to minecraft username {:?}", username);
} }
} }
username_mc = username.to_string(); username_mc = username.to_string();
} else { } else {
match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await { match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await {
None => { None => {
return format!("No UID found for {username:?}"); return format!("No UID found for {:?}", username);
} }
Some(x) => { Some(x) => {
match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await { match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await {
@ -190,17 +185,14 @@ pub(crate) mod server {
use super::*; use super::*;
pub(crate) mod add { pub(crate) mod add {
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}, use serenity::model::id::GuildId;
model::id::GuildId,
};
use sqlx::Error; 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 super::*;
use skynet_discord_bot::{ use skynet_discord_bot::common::minecraft::update_server;
common::minecraft::{update_server, Minecraft}, use skynet_discord_bot::common::minecraft::Minecraft;
Config, use skynet_discord_bot::Config;
};
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
CreateCommand::new("minecraft_add") CreateCommand::new("minecraft_add")
@ -228,15 +220,16 @@ pub(crate) mod server {
return String::from("Expected Server ID"); return String::from("Expected Server ID");
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
match add_server(&db, &g_id, &server_minecraft).await { match add_server(&db, &g_id, &server_minecraft).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
} }
} }
@ -267,14 +260,12 @@ pub(crate) mod server {
} }
pub(crate) mod list { pub(crate) mod list {
use serenity::{all::CommandInteraction, builder::CreateCommand, client::Context}; use serenity::all::CommandInteraction;
use skynet_discord_bot::{ use serenity::builder::CreateCommand;
common::{ use serenity::client::Context;
database::DataBase, use skynet_discord_bot::common::database::DataBase;
minecraft::{get_minecraft_config_server, server_information}, use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information};
}, use skynet_discord_bot::Config;
Config,
};
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
CreateCommand::new("minecraft_list") CreateCommand::new("minecraft_list")
@ -288,10 +279,11 @@ pub(crate) mod server {
Some(x) => x, Some(x) => x,
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let servers = get_minecraft_config_server(&db, g_id).await; let servers = get_minecraft_config_server(&db, g_id).await;
@ -328,13 +320,12 @@ pub(crate) mod server {
} }
pub(crate) mod delete { pub(crate) mod delete {
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}, use serenity::builder::CreateCommand;
builder::CreateCommand, use serenity::client::Context;
client::Context, use serenity::model::id::GuildId;
model::id::GuildId, use skynet_discord_bot::common::database::DataBase;
}; use skynet_discord_bot::common::minecraft::Minecraft;
use skynet_discord_bot::common::{database::DataBase, minecraft::Minecraft};
use sqlx::{Error, Pool, Sqlite}; use sqlx::{Error, Pool, Sqlite};
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
@ -363,15 +354,16 @@ pub(crate) mod server {
return String::from("Expected Server ID"); return String::from("Expected Server ID");
}; };
let db = { let db_lock = {
let data = ctx.data.read().await; let data_read = ctx.data.read().await;
data.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
match server_remove(&db, &g_id, &server_minecraft).await { match server_remove(&db, &g_id, &server_minecraft).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
} }
} }

View file

@ -62,10 +62,11 @@ pub mod edit {
false false
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let server = command.guild_id.unwrap_or_default(); let server = command.guild_id.unwrap_or_default();
let server_data = RoleAdder { let server_data = RoleAdder {
@ -78,8 +79,8 @@ pub mod edit {
match add_server(&db, &server_data, delete).await { match add_server(&db, &server_data, delete).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Servers {server_data:?}"); return format!("Failure to insert into Servers {:?}", server_data);
} }
} }
@ -100,9 +101,9 @@ pub mod edit {
} }
if delete { 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 { } 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 list {}
pub mod tools { 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 skynet_discord_bot::common::database::RoleAdder;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, new_data: Member) { pub async fn on_role_change(db: &Pool<Sqlite>, 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>( if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>(
r#" r#"
SELECT * SELECT *
@ -162,7 +164,7 @@ pub mod tools {
let mut roles_remove = vec![]; let mut roles_remove = vec![];
for role_adder in role_adders { 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) 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); roles_add.push(role_adder.role_c);
@ -178,13 +180,13 @@ pub mod tools {
if !roles_add.is_empty() { if !roles_add.is_empty() {
if let Err(e) = new_data.add_roles(&ctx, &roles_add).await { if let Err(e) = new_data.add_roles(&ctx, &roles_add).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
if !roles_remove.is_empty() { if !roles_remove.is_empty() {
if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await { if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }

View file

@ -18,10 +18,11 @@ pub(crate) mod admin {
use super::*; use super::*;
pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -68,10 +69,11 @@ pub(crate) mod user {
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_toml = get_config_icons::minimal(); let config_toml = get_config_icons::minimal();
@ -91,7 +93,7 @@ pub(crate) mod user {
} }
} }
pub async fn get_current_icon(db: &Pool<Sqlite>) -> Option<ServerIcons> { async fn get_current_icon(db: &Pool<Sqlite>) -> Option<ServerIcons> {
match sqlx::query_as::<_, ServerIcons>( match sqlx::query_as::<_, ServerIcons>(
" "
SELECT * from server_icons ORDER BY id DESC LIMIT 1 SELECT * from server_icons ORDER BY id DESC LIMIT 1
@ -111,10 +113,8 @@ pub(crate) mod user {
pub(crate) mod festival { pub(crate) mod festival {
use serenity::all::{CommandInteraction, Context}; use serenity::all::{CommandInteraction, Context};
use skynet_discord_bot::{ use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon::get_festival};
common::server_icon::{get_config_icons, update_icon::get_festival}, use skynet_discord_bot::Config;
Config,
};
// use this to return what current festivals are active? // use this to return what current festivals are active?
pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String {
@ -143,10 +143,11 @@ pub(crate) mod user {
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_toml = get_config_icons::minimal(); 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 so that the numbers will be rendered in monospaced font
// the <> is to suppress the URL embed // 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; 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) { if length < (limit + 3) {
response.push(line); response.push(line);
limit -= length; limit -= length;

View file

@ -4,16 +4,11 @@ use lettre::{
Message, SmtpTransport, Transport, Message, SmtpTransport, Transport,
}; };
use maud::html; use maud::html;
use serenity::{ use serenity::all::CommandOptionType;
all::CommandOptionType, use serenity::builder::CreateCommandOption;
builder::{CreateCommand, CreateCommandOption}, use serenity::{builder::CreateCommand, client::Context, model::id::UserId};
client::Context, use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify};
model::id::UserId, use skynet_discord_bot::{get_now_iso, random_string, Config};
};
use skynet_discord_bot::{
common::database::{DataBase, Wolves, WolvesVerify},
get_now_iso, random_string, Config,
};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub mod link { pub mod link {
@ -21,10 +16,11 @@ pub mod link {
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -100,7 +96,7 @@ pub mod link {
return "Email already verified".to_string(); return "Email already verified".to_string();
} }
// generate an auth key // generate a auth key
let auth = random_string(20); let auth = random_string(20);
match send_mail(&config, &details.email, &auth, &command.user.name) { match send_mail(&config, &details.email, &auth, &command.user.name) {
Ok(_) => match save_to_db(&db, &details, &auth, &command.user.id).await { 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<Sqlite>, user: &UserId) -> Option<Wolves> { pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> {
@ -209,7 +205,7 @@ pub mod link {
.subject("Skynet: Link Discord to Wolves.") .subject("Skynet: Link Discord to Wolves.")
.multipart( .multipart(
// This is composed of two parts. // 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() MultiPart::alternative()
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text)) .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text))
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())), .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 { pub mod verify {
use super::*; use super::*;
use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db}; use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db};
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, use serenity::model::user::User;
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::{ use skynet_discord_bot::common::wolves::committees::Committees;
database::{get_server_config, ServerMembersWolves, Servers},
wolves::committees::Committees,
};
use sqlx::Error; use sqlx::Error;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
// check if user has used /link_wolves // check if user has used /link_wolves
let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await { 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() "Discord username linked to Wolves".to_string()
} }
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
"Failed to save, please try /link_wolves again".to_string() "Failed to save, please try /link_wolves again".to_string()
} }
}; };
} }
Err(e) => println!("{e:?}"), Err(e) => println!("{:?}", e),
} }
"Failed to verify".to_string() "Failed to verify".to_string()
@ -425,7 +403,7 @@ pub mod verify {
} }
if let Err(e) = member.add_roles(&ctx, &roles).await { 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 WHERE committee LIKE ?1
"#, "#,
) )
.bind(format!("%{wolves_id}%")) .bind(format!("%{}%", wolves_id))
.fetch_all(db) .fetch_all(db)
.await .await
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
@ -458,7 +436,7 @@ pub mod verify {
let config = config_lock.read().await; let config = config_lock.read().await;
if let Some(x) = get_server_member_discord(db, &discord.id).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 // they will get teh more specific vanity role later
if !get_committees_id(db, x.id_wolves).await.is_empty() { if !get_committees_id(db, x.id_wolves).await.is_empty() {
let server = config.committee_server; let server = config.committee_server;
@ -492,12 +470,13 @@ pub mod unlink {
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().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; delete_link(&db, &command.user.id).await;
"Discord link removed".to_string() "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::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_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."))
} }

View file

@ -1,21 +1,17 @@
use crate::Config; use crate::Config;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::model::guild;
model::{ use serenity::model::id::{ChannelId, GuildId, RoleId, UserId};
guild, use serenity::prelude::TypeMapKey;
id::{ChannelId, GuildId, RoleId, UserId}, use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow};
}, use sqlx::{Error, FromRow, Pool, Row, Sqlite};
prelude::TypeMapKey, use std::str::FromStr;
}; use std::sync::Arc;
use sqlx::{ use tokio::sync::RwLock;
sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow},
Error, FromRow, Pool, Row, Sqlite,
};
use std::{str::FromStr, sync::Arc};
pub struct DataBase; pub struct DataBase;
impl TypeMapKey for DataBase { impl TypeMapKey for DataBase {
type Value = Arc<Pool<Sqlite>>; type Value = Arc<RwLock<Pool<Sqlite>>>;
} }
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
@ -224,7 +220,7 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
let pool = SqlitePoolOptions::new() let pool = SqlitePoolOptions::new()
.max_connections(5) .max_connections(5)
.connect_with( .connect_with(
SqliteConnectOptions::from_str(&format!("sqlite://{database}"))? SqliteConnectOptions::from_str(&format!("sqlite://{}", database))?
.foreign_keys(true) .foreign_keys(true)
.create_if_missing(true), .create_if_missing(true),
) )

View file

@ -1,7 +1,10 @@
use crate::{common::set_roles::normal::get_server_member_bulk, Config}; use crate::common::set_roles::normal::get_server_member_bulk;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::Config;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serenity::model::id::GuildId; 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)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Minecraft { pub struct Minecraft {
@ -24,7 +27,7 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft {
/** /**
loop through all members of server loop through all members of server
get a list of folks with mc accounts that are members 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<Sqlite>, g_id: &GuildId, config: &Config) { pub async fn update_server(server_id: &str, db: &Pool<Sqlite>, g_id: &GuildId, config: &Config) {
let mut usernames = vec![]; 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; 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::<Vec<&str>>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; post::<Vec<&str>>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await;
// reload the whitelist // reload the whitelist
@ -152,7 +155,7 @@ pub async fn get_minecraft_config_server(db: &Pool<Sqlite>, g_id: GuildId) -> Ve
} }
pub async fn whitelist_update(add: &Vec<(String, bool)>, server: &str, token: &str) { 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 url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}");
let bearer = format!("Bearer {token}"); let bearer = format!("Bearer {token}");

View file

@ -1,11 +1,10 @@
// this code is taken from https://github.com/MCorange99/svg2colored-png/tree/main // 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::{ use std::ffi::OsStr;
ffi::OsStr, use std::path::{Path, PathBuf};
path::{Path, PathBuf},
};
// use clap::builder::OsStr;
use color_eyre::{eyre::bail, Result}; use color_eyre::{eyre::bail, Result};
use usvg_text_layout::TreeTextToPath; use usvg_text_layout::TreeTextToPath;
@ -16,7 +15,7 @@ pub struct Args {
/// Output folder where the PNG's will be placed /// Output folder where the PNG's will be placed
pub output: PathBuf, 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 /// Can be like an object: black:000000,white:ffffff
pub colors: String, pub colors: String,
@ -178,7 +177,7 @@ impl Renderer {
} }
fn set_color(&self, svg: &str, color: &String) -> String { 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<String> { fn get_svg_data(&self, fi: &Path) -> Result<String> {

View file

@ -191,28 +191,6 @@ pub mod update_icon {
{ {
dbg!(e); 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<LogoData> { fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec<LogoData> {
@ -281,7 +259,7 @@ pub mod update_icon {
// check if exists // check if exists
if !path_new.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) { match r.render(&path_local, &args) {
Ok(_) => {} Ok(_) => {}
Err(_e) => { Err(_e) => {
@ -308,19 +286,9 @@ pub mod update_icon {
fn logos_filter(festival_data: &FestivalData, existing: Vec<LogoData>) -> Vec<LogoData> { fn logos_filter(festival_data: &FestivalData, existing: Vec<LogoData>) -> Vec<LogoData> {
let mut filtered: Vec<LogoData> = vec![]; let mut filtered: Vec<LogoData> = vec![];
let allowed_files = vec![".png", ".jpeg", ".gif", ".svg"];
'outer: for logo in existing { 'outer: for logo in existing {
let name_lowercase0 = logo.name.to_ascii_lowercase(); let name_lowercase0 = logo.name.to_ascii_lowercase();
let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); 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 !festival_data.current.is_empty() {
// if its a current festival filter based on it // if its a current festival filter based on it

View file

@ -1,27 +1,18 @@
pub mod normal { pub mod normal {
use crate::{ use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves};
common::database::{DataBase, ServerMembersWolves, Servers, Wolves}, use crate::get_now_iso;
get_now_iso, use serenity::client::Context;
}; use serenity::model::id::{GuildId, RoleId, UserId};
use serenity::{
client::Context,
model::id::{GuildId, RoleId, UserId},
};
use sqlx::{Pool, Sqlite}; 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<RoleId>], members_changed: &[UserId]) { pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option<RoleId>], members_changed: &[UserId]) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let Servers { let Servers {
server, server,
role_past, role_past,
@ -29,12 +20,7 @@ pub mod normal {
.. ..
} = server; } = server;
let mut roles_set = RolesChange { let mut roles_set = [0, 0, 0];
total: 0,
new: 0,
current_add: 0,
current_rem: 0,
};
let mut members = vec![]; let mut members = vec![];
for member in get_server_member_bulk(&db, server).await { 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 { if let Ok(x) = server.members(ctx, None, None).await {
for member in x { 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) { if !members_changed.is_empty() && !members_changed.contains(&member.user.id) {
continue; continue;
} }
if members.contains(&member.user.id) { if members.contains(&member.user.id) {
roles_set.total += 1;
let mut roles = vec![]; let mut roles = vec![];
if let Some(role) = &role_past { if let Some(role) = &role_past {
if !member.roles.contains(role) { if !member.roles.contains(role) {
roles_set.new += 1; roles_set[0] += 1;
roles.push(role.to_owned()); roles.push(role.to_owned());
} }
} }
if !member.roles.contains(role_current) { if !member.roles.contains(role_current) {
roles_set.current_add += 1; roles_set[1] += 1;
roles.push(role_current.to_owned()); roles.push(role_current.to_owned());
} }
if let Err(e) = member.add_roles(ctx, &roles).await { if let Err(e) = member.add_roles(ctx, &roles).await {
println!("{e:?}"); println!("{:?}", e);
} }
} else { } else {
// old and never // old and never
@ -81,16 +65,16 @@ pub mod normal {
} }
if member.roles.contains(role_current) { if member.roles.contains(role_current) {
roles_set.current_rem += 1; roles_set[2] += 1;
// if they're not a current member and have the role then remove it // 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 { if let Err(e) = member.remove_role(ctx, role_current).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
for role in remove_roles.iter().flatten() { for role in remove_roles.iter().flatten() {
if let Err(e) = member.remove_role(ctx, role).await { 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; set_server_numbers(&db, server, members_all as i64, members.len() as i64).await;
// small bit of logging to note changes over time // small bit of logging to note changes over time
println!( println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]);
"{:?} Total: {} Changes: New: +{}, Current: +{}/-{}",
server.get(),
roles_set.total,
roles_set.new,
roles_set.current_add,
roles_set.current_rem
);
} }
pub async fn get_server_member_bulk(db: &Pool<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> { pub async fn get_server_member_bulk(db: &Pool<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> {
@ -146,7 +123,7 @@ pub mod normal {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into {}", server.get()); println!("Failure to insert into {}", server.get());
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -154,29 +131,30 @@ pub mod normal {
// for updating committee members // for updating committee members
pub mod committee { pub mod committee {
use crate::{ use crate::common::database::{get_channel_from_row, get_role_from_row, DataBase, Wolves};
common::{ use crate::common::wolves::committees::Committees;
database::{get_channel_from_row, get_role_from_row, DataBase, Wolves}, use crate::Config;
wolves::committees::Committees,
},
Config,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::all::EditRole;
all::EditRole, use serenity::builder::CreateChannel;
builder::CreateChannel, use serenity::client::Context;
client::Context, use serenity::model::channel::ChannelType;
model::{channel::ChannelType, guild::Member, id::ChannelId, prelude::RoleId}, use serenity::model::guild::Member;
}; use serenity::model::id::ChannelId;
use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; use serenity::model::prelude::RoleId;
use sqlx::sqlite::SqliteRow;
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
pub async fn check_committee(ctx: &Context) { pub async fn check_committee(ctx: Arc<Context>) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone() data_read.get::<Config>().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 // 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(); 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<Sqlite>, ctx: &Context, config: &Config, members: &mut Vec<Member>) { pub async fn update_committees(db: &Pool<Sqlite>, ctx: &Context, config: &Config, members: &mut Vec<Member>) {
let server = config.committee_server; let server = config.committee_server;
let committee_member = config.committee_role; let committee_member = config.committee_role;
let committees = match get_committees(db).await { let committees = get_committees(db).await;
None => {
return;
}
Some(x) => x,
};
let categories = config.committee_category.clone(); let categories = config.committee_category.clone();
// information about the server // information about the server
@ -224,11 +197,11 @@ pub mod committee {
let mut channels = server.channels(&ctx).await.unwrap_or_default(); 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 users_roles = HashMap::new();
let mut re_order = false; 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 category_index = 0;
let mut i = 0; let mut i = 0;
loop { 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 // 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 { for member in members {
// if member.user.id != 136522490632601600 {
// continue;
// }
//
let roles_current = member.roles(ctx).unwrap_or_default(); let roles_current = member.roles(ctx).unwrap_or_default();
let roles_required = match users_roles.get(&member.user.id) { let roles_required = match users_roles.get(&member.user.id) {
None => { None => {
vec![] 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_rem = vec![];
let mut roles_add = vec![]; let mut roles_add = vec![];
// get a list of all the roles to remove from someone // get a list of all the roles to remove from someone
@ -352,25 +325,14 @@ pub mod committee {
for role in &roles_current { for role in &roles_current {
roles_current_id.push(role.id.to_owned()); roles_current_id.push(role.id.to_owned());
if !roles_required.contains(&role.id) { if !roles_required.contains(&role.id) {
if role.id == committee_member && on_committee {
continue;
}
roles_rem.push(role.id.to_owned()); 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 !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) { if let Some(x) = roles_db.get_mut(&0) {
x.count += 1; x.count += 1;
} }
@ -389,6 +351,8 @@ pub mod committee {
if !roles_add.is_empty() { if !roles_add.is_empty() {
// these roles are flavor roles, only there to make folks mentionable // these roles are flavor roles, only there to make folks mentionable
member.add_roles(&ctx, &roles_add).await.unwrap_or_default(); 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)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CommitteeRoles { pub struct CommitteeRoles {
id_wolves: i64, id_wolves: i64,
pub id_role: RoleId, id_role: RoleId,
pub id_channel: ChannelId, id_channel: ChannelId,
pub name_role: String, pub name_role: String,
pub name_channel: String, name_channel: String,
pub count: i64, pub count: i64,
} }
@ -476,8 +440,8 @@ pub mod committee {
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into Wolves {role:?}"); println!("Failure to insert into Wolves {:?}", role);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -494,13 +458,13 @@ pub mod committee {
.await .await
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
println!("Failure to get Roles from committee_roles"); println!("Failure to get Roles from committee_roles");
println!("{e:?}"); println!("{:?}", e);
vec![] vec![]
}) })
} }
pub async fn get_committees(db: &Pool<Sqlite>) -> Option<Vec<Committees>> { pub async fn get_committees(db: &Pool<Sqlite>) -> Vec<Committees> {
match sqlx::query_as::<_, Committees>( sqlx::query_as::<_, Committees>(
r#" r#"
SELECT * SELECT *
FROM committees FROM committees
@ -508,13 +472,10 @@ pub mod committee {
) )
.fetch_all(db) .fetch_all(db)
.await .await
{ .unwrap_or_else(|e| {
Ok(x) => Some(x),
Err(e) => {
dbg!(e); dbg!(e);
None vec![]
} })
}
} }
async fn get_server_member_discord(db: &Pool<Sqlite>, user: &i64) -> Option<Wolves> { async fn get_server_member_discord(db: &Pool<Sqlite>, user: &i64) -> Option<Wolves> {

View file

@ -38,8 +38,8 @@ async fn add_users_wolves(db: &Pool<Sqlite>, user: &WolvesResultUserMin) {
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into Wolves {user:?}"); println!("Failure to insert into Wolves {:?}", user);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -48,14 +48,12 @@ async fn add_users_wolves(db: &Pool<Sqlite>, user: &WolvesResultUserMin) {
This is getting data for Clubs and Socs This is getting data for Clubs and Socs
*/ */
pub mod cns { pub mod cns {
use crate::{ use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers};
common::{ use crate::common::set_roles::normal::update_server;
database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}, use crate::common::wolves::{add_users_wolves, WolvesResultUserMin};
wolves::{add_users_wolves, WolvesResultUserMin}, use crate::Config;
}, use serenity::client::Context;
Config, use serenity::model::id::GuildId;
};
use serenity::{client::Context, model::id::GuildId};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -69,10 +67,11 @@ pub mod cns {
} }
pub async fn get_wolves(ctx: &Context) { pub async fn get_wolves(ctx: &Context) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; 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::<BTreeMap<_, _>>(); let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::<BTreeMap<_, _>>();
// list of users that need to be updated for this server // list of users that need to be updated for this server
let mut user_to_update = vec![];
let mut server_name_tmp = None; let mut server_name_tmp = None;
for user in wolves.get_members(wolves_api).await { for user in wolves.get_members(wolves_api).await {
// dbg!(&user.committee); // dbg!(&user.committee);
@ -115,6 +115,10 @@ pub mod cns {
add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await;
if old.expiry != user.expiry { if old.expiry != user.expiry {
add_users_server_members(&db, server, &user).await; 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; 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(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to set server name {}", server.get()); println!("Failure to set server name {}", server.get());
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -183,7 +190,7 @@ pub mod cns {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into ServerMembers {} {:?}", server.get(), user); 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 Get and store the data on C&S committees
*/ */
pub mod committees { pub mod committees {
use crate::{common::database::DataBase, Config}; use crate::common::database::DataBase;
use crate::Config;
use serenity::client::Context; use serenity::client::Context;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
@ -223,10 +231,11 @@ pub mod committees {
} }
pub async fn get_cns(ctx: &Context) { pub async fn get_cns(ctx: &Context) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -261,8 +270,8 @@ pub mod committees {
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into Committees {committee:?}"); println!("Failure to insert into Committees {:?}", committee);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }

View file

@ -3,10 +3,8 @@ pub mod common;
use chrono::{Datelike, SecondsFormat, Utc}; use chrono::{Datelike, SecondsFormat, Utc};
use dotenvy::dotenv; use dotenvy::dotenv;
use rand::{distr::Alphanumeric, rng, Rng}; use rand::{distr::Alphanumeric, rng, Rng};
use serenity::{ use serenity::model::id::{ChannelId, GuildId, RoleId};
model::id::{ChannelId, GuildId, RoleId}, use serenity::prelude::TypeMapKey;
prelude::TypeMapKey,
};
use std::{env, sync::Arc}; use std::{env, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -36,7 +34,7 @@ pub struct Config {
pub committee_role: RoleId, pub committee_role: RoleId,
pub committee_category: Vec<ChannelId>, pub committee_category: Vec<ChannelId>,
// items pertaining to CompSoc only // items pertaining to compsoc only
pub compsoc_server: GuildId, pub compsoc_server: GuildId,
} }
impl TypeMapKey for Config { impl TypeMapKey for Config {
@ -111,7 +109,7 @@ pub fn get_config() -> Config {
} }
} }
if let Ok(x) = env::var("COMMITTEE_CATEGORY") { 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::<u64>() { if let Ok(x) = part.trim().parse::<u64>() {
config.committee_category.push(ChannelId::new(x)); config.committee_category.push(ChannelId::new(x));
} }

View file

@ -1,28 +1,21 @@
pub mod commands; pub mod commands;
use crate::commands::role_adder::tools::on_role_change; 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::{ use serenity::{
all::{Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, Interaction},
async_trait, async_trait,
client::{Context, EventHandler}, client::{Context, EventHandler},
gateway::{ActivityData, ChunkGuildFilter},
model::{ model::{
event::GuildMemberUpdateEvent,
gateway::{GatewayIntents, Ready}, gateway::{GatewayIntents, Ready},
guild::Member,
id::GuildId,
user::OnlineStatus, user::OnlineStatus,
}, },
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase};
common::{ use skynet_discord_bot::common::set_roles::committee::update_committees;
database::{db_init, get_server_config, get_server_member, DataBase}, use skynet_discord_bot::common::wolves::committees::Committees;
set_roles::committee::update_committees, use skynet_discord_bot::{get_config, Config};
wolves::committees::Committees,
},
get_config, Config,
};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -31,21 +24,15 @@ struct Handler;
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
// this caches members of all servers teh bot is in
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) {
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 // handles previously linked accounts joining the server
async fn guild_member_addition(&self, ctx: Context, new_member: Member) { async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone() data_read.get::<Config>().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 { if let Err(e) = new_member.add_roles(&ctx, &roles).await {
println!("{e:?}"); println!("{:?}", e);
} }
} else { } else {
let tmp = get_committee(&db, config_server.wolves_id).await; 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 // handles role updates
async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Option<Member>, _: GuildMemberUpdateEvent) { async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Option<Member>, _: GuildMemberUpdateEvent) {
// get config/db // get config/db
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().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 { if let Some(x) = new_data {
on_role_change(&db, &ctx, x).await; 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(_) => {} Ok(_) => {}
Err(e) => { 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 { match config.committee_server.set_commands(&ctx.http, vec![commands::count::committee::register()]).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}") println!("{:?}", e)
} }
} }
// CompSoc Server // compsoc Server
match config match config
.compsoc_server .compsoc_server
.set_commands( .set_commands(
&ctx.http, &ctx.http,
vec![ vec![
// commands just for the CompSoc server // commands just for the compsoc server
commands::count::servers::register(), commands::count::servers::register(),
commands::server_icon::user::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(_) => {} Ok(_) => {}
Err(e) => { 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) { async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::Command(command) = interaction { if let Interaction::Command(command) = interaction {
let _ = command.defer_ephemeral(&ctx.http).await; 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() { let content = match command.data.name.as_str() {
// user commands // 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, "verify" => commands::wolves::verify::run(&command, &ctx).await,
"unlink" => commands::wolves::unlink::run(&command, &ctx).await, "unlink" => commands::wolves::unlink::run(&command, &ctx).await,
"link_minecraft" => commands::minecraft::user::add::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, // "link" => commands::count::servers::run(&command, &ctx).await,
&_ => format!("not implemented :( wolves {}", x.name.as_str()), &_ => 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 { 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; let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -300,7 +287,7 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
// Finally, start a single shard, and start listening to events. // 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 // Shards will automatically attempt to reconnect, and will perform
// exponential backoff until it reconnects. // exponential backoff until it reconnects.
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }