Compare commits

..

46 commits

Author SHA1 Message Date
6353d77360 Merge pull request 'Remove RwLock for database' (#45) from cordlesscoder/discord-bot:#45_Remove_RwLock into main
All checks were successful
/ check_lfs (push) Successful in 3s
On_Push / lint_fmt (push) Successful in 7s
On_Push / lint_clippy (push) Successful in 7s
On_Push / build (push) Successful in 7s
On_Push / deploy (push) Successful in 11s
Reviewed-on: #45
2025-09-11 12:02:31 +00:00
Roman Moisieiev
062f826d28 Remove RwLock for database
All checks were successful
/ check_lfs (pull_request) Successful in 9s
/ lint_fmt (pull_request) Successful in 10s
/ lint_clippy (pull_request) Successful in 30s
/ build (pull_request) Successful in 1m19s
2025-09-11 12:54:54 +01:00
d8f785b0db Merge pull request 'Fix typos' (#44) from cordlesscoder/discord-bot:#44_Typos into main
All checks were successful
/ check_lfs (push) Successful in 2s
On_Push / lint_clippy (push) Successful in 7s
On_Push / lint_fmt (push) Successful in 13s
On_Push / build (push) Successful in 1m34s
On_Push / deploy (push) Successful in 10s
Reviewed-on: #44
2025-09-11 11:49:03 +00:00
Roman Moisieiev
d70a037057 Blame: ignore Fix Typos commit
All checks were successful
/ check_lfs (pull_request) Successful in 3s
/ lint_fmt (pull_request) Successful in 37s
/ lint_clippy (pull_request) Successful in 1m15s
/ build (pull_request) Successful in 3m1s
2025-09-11 12:36:48 +01:00
Roman Moisieiev
7e90f45196 Fix typos 2025-09-11 12:36:03 +01:00
7526a82bb7
feat: kill the service if it persists longer than 9 min
All checks were successful
/ check_lfs (push) Successful in 4s
On_Push / lint_fmt (push) Successful in 10s
On_Push / lint_clippy (push) Successful in 1m13s
On_Push / build (push) Successful in 1m36s
On_Push / deploy (push) Successful in 11s
2025-09-04 23:11:24 +01:00
3149a5f99f Bump 2
All checks were successful
/ check_lfs (push) Successful in 9s
On_Push / lint_fmt (push) Successful in 17s
On_Push / lint_clippy (push) Successful in 27s
On_Push / build (push) Successful in 1m38s
On_Push / deploy (push) Successful in 15s
2025-08-31 11:55:35 +00:00
061b73378a Bump the bot
Some checks failed
/ check_lfs (push) Successful in 14s
On_Push / lint_fmt (push) Failing after 1m13s
On_Push / lint_clippy (push) Successful in 2m53s
On_Push / build (push) Has been skipped
On_Push / deploy (push) Has been skipped
2025-08-31 11:51:57 +00:00
a225c14b4f
fix: was ddossing the poor database
All checks were successful
/ check_lfs (push) Successful in 24s
On_Push / lint_clippy (push) Successful in 15s
On_Push / lint_fmt (push) Successful in 21s
On_Push / build (push) Successful in 1m51s
On_Push / deploy (push) Successful in 23s
guild_members_chunk is triggered for each chunk for each server it is on, and the bot is currently in 10 servers so it was runnign teh same thigns 10 times, clogging up conenctions
2025-07-21 04:29:03 +01:00
095ff6f2ce
fix: better handling of returning teh committees
Some checks failed
On_Push / lint_fmt (push) Successful in 21s
On_Push / lint_clippy (push) Successful in 52s
On_Push / build (push) Successful in 1m57s
On_Push / deploy (push) Successful in 15s
/ check_lfs (push) Failing after 10m2s
2025-07-21 02:52:59 +01:00
18fd45d39b Merge pull request '#40_Improve_Preformance' (#41) from #40_Improve_Preformance into main
All checks were successful
On_Push / lint_fmt (push) Successful in 11s
/ check_lfs (push) Successful in 8s
On_Push / lint_clippy (push) Successful in 13s
On_Push / build (push) Successful in 8s
On_Push / deploy (push) Successful in 13s
Reviewed-on: #41

Closes #40
2025-07-21 01:06:27 +00:00
854e946a8f
ci: improve the pipeline to test for the full suite
All checks were successful
/ check_lfs (push) Successful in 10s
/ check_lfs (pull_request) Successful in 8s
/ lint_fmt (pull_request) Successful in 14s
/ lint_clippy (pull_request) Successful in 16s
/ build (pull_request) Successful in 3m39s
ci: dont pull in the lfs for teh PR pipeline build
ci: checkout without LFS
ci: see if using the ref directly will help
ci: test a slight modification
ci: see if new get ldfs script works
ci: test using teh new v8
2025-07-21 01:59:36 +01:00
d0726169ee
clippy: changes from nightly clippy
All checks were successful
/ check_lfs (push) Successful in 16s
/ check_lfs (pull_request) Successful in 12s
all of this is embeding teh var into teh format macro
2025-07-21 00:50:20 +01:00
9d409e3692
fmt: clippy and nightly fmt 2025-07-21 00:38:59 +01:00
57d4947edf
fix: no longer needint to wait until the cache in teh main program is filled 2025-07-20 23:48:05 +01:00
6d08312f48
fix: these were not using teh cache to access teh member/role data
*was executing before teh cache was built (``cache_ready`` is only when teh cache has been init, not when it is populated)
2025-07-20 23:46:30 +01:00
bd9d0cd43f
fix: these do not need to use teh cache 2025-07-20 23:44:13 +01:00
1af7f28a45
feat: restore teh original services, giving up the logging and control was too much 2025-07-20 23:40:34 +01:00
feff293043
feat: moved the update server icon to the main thread 2025-07-20 23:12:02 +01:00
227db8a741
feat: moved the update data to the main thread 2025-07-20 22:54:34 +01:00
13eb230754
fix: updating the wolves (user data) should not trigger a server update (directly).
That should always be triggered separately.
This was a holdover from a  time when updating teh users was expensive (timewise)
2025-07-20 22:48:04 +01:00
eb88216740
feat: removed the cronjob for updating the committee server 2025-07-20 22:33:56 +01:00
96eb81293b
feat: got the committee update running smoothly (using cache and far far faster due to fixing some logic issues) 2025-07-20 22:32:55 +01:00
5815cde38b
fmt: better wording for logs 2025-07-20 22:28:59 +01:00
1729ec0a54
feat: drastically speed up the committee server script, it no longer tries to remove the committee role from those who dont have it. 2025-07-20 22:26:51 +01:00
43ef787d59
fix: no need to pass in teh full object, a reference will do 2025-07-20 22:25:31 +01:00
a8bed0bacc
fix: was calling the wrong ctx 2025-07-20 20:43:46 +01:00
3dd81a5c54
feat: remove the update_users service 2025-07-20 20:40:15 +01:00
04aa0e63d4
feat: setup update users to be every 5 min while using the cache 2025-07-20 20:37:28 +01:00
2b2dfc2531
feat: cleaned up array that was used to count members/changes to a struct 2025-07-20 20:32:43 +01:00
e901f3ed74
feat: add caching to everything, should make all member interacts faster
All checks were successful
/ check_lfs (push) Successful in 13s
On_Push / lint_fmt (push) Successful in 24s
On_Push / lint_clippy (push) Successful in 25s
On_Push / build (push) Successful in 1m59s
On_Push / deploy (push) Successful in 17s
2025-07-07 22:22:25 +01:00
3abbb8d485
feat: added script to clean up the committee server if it got flooded with extra channels 2025-07-07 22:18:04 +01:00
b8ffd42184
feat: the bot wasnt using any caching, this should make many operations far faster now
All checks were successful
On_Push / lint_fmt (push) Successful in 23s
/ check_lfs (push) Successful in 22s
On_Push / lint_clippy (push) Successful in 1m45s
On_Push / build (push) Successful in 1m46s
On_Push / deploy (push) Successful in 14s
2025-07-07 21:29:37 +01:00
764e8cd620 Fix LFS on discord bot (#39)
All checks were successful
/ check_lfs (push) Successful in 10s
On_Push / lint_fmt (push) Successful in 29s
On_Push / lint_clippy (push) Successful in 1m11s
On_Push / build (push) Successful in 2m37s
On_Push / deploy (push) Successful in 13s
Reviewed-on: #39

Thanks @esy
Co-authored-by: Daragh <esy@skynet.ie>
Co-committed-by: Daragh <esy@skynet.ie>
2025-07-06 23:28:51 +00:00
esy
76f8aa2712 Update README.md
All checks were successful
/ check_lfs (push) Successful in 6s
2025-07-06 18:12:04 +00:00
c4da3e9109
fix: only allow image files to be chosen
All checks were successful
/ check_lfs (push) Successful in 20s
On_Push / lint_fmt (push) Successful in 1m5s
On_Push / lint_clippy (push) Successful in 2m18s
On_Push / build (push) Successful in 2m1s
On_Push / deploy (push) Successful in 21s
2025-07-05 15:31:53 +01:00
d27befdac6 Merge pull request 'Add command for link to documentation' (#38) from #37_add-documentation-command into main
All checks were successful
/ check_lfs (push) Successful in 17s
On_Push / lint_fmt (push) Successful in 1m5s
On_Push / lint_clippy (push) Successful in 2m10s
On_Push / build (push) Successful in 1m30s
On_Push / deploy (push) Successful in 24s
Reviewed-on: #38
2025-06-23 23:16:41 +00:00
7403f531eb
feat: the backend is pretty simple, just pull the rep link from teh config_toml and add on the path to the docs.
All checks were successful
/ check_lfs (push) Successful in 11s
/ check_lfs (pull_request) Successful in 12s
Then its just linking it all up.

Closes #37
2025-06-24 00:13:29 +01:00
1dc5c105df
fix: needed to add git and git lfs to teh path of the service
All checks were successful
/ check_lfs (push) Successful in 20s
On_Push / lint_fmt (push) Successful in 59s
On_Push / lint_clippy (push) Successful in 4m33s
On_Push / build (push) Successful in 2m2s
On_Push / deploy (push) Successful in 33s
2025-06-18 03:57:04 +01:00
3a56d7bba5
feat: lock the `nix build` to using the repo rust version
All checks were successful
/ check_lfs (push) Successful in 43s
On_Push / lint_fmt (push) Successful in 5m2s
On_Push / lint_clippy (push) Successful in 8m26s
On_Push / build (push) Successful in 7m58s
On_Push / deploy (push) Successful in 36s
2025-06-17 16:21:57 +01:00
327ff99b69
fix: the pipeline got caught on a lint
This isnt in the 1.87 rustfmt but its stilla  good catch
2025-06-17 16:21:15 +01:00
dedf8c3644 Merge pull request '#35_remove-hardcoded-servers' (#36) from #35_remove-hardcided-servers into main
Some checks failed
On_Push / lint_fmt (push) Successful in 24s
On_Push / lint_clippy (push) Failing after 4m4s
On_Push / build (push) Has been skipped
On_Push / deploy (push) Has been skipped
/ check_lfs (push) Failing after 12m3s
Reviewed-on: #36

Closes #35
2025-06-17 14:56:57 +00:00
a6eff75e39
feat: use values from teh env file to dictate the servers
All checks were successful
/ check_lfs (pull_request) Successful in 8s
/ check_lfs (push) Successful in 8s
2025-06-17 15:38:15 +01:00
72226cc59b
feat: add support for passing teh compsoc server id via env 2025-06-17 15:38:15 +01:00
f841039c53
fix: was pulling in the wrong env var 2025-06-17 15:38:15 +01:00
87dd04e12f Merge pull request 'Automatically change server icon daily' (#33) from #32_rotating-server-icon into main
Some checks failed
/ check_lfs (push) Successful in 27s
On_Push / lint_fmt (push) Successful in 3m13s
On_Push / build (push) Has been cancelled
On_Push / deploy (push) Has been cancelled
On_Push / lint_clippy (push) Has been cancelled
Reviewed-on: #33

Closes #32
2025-06-17 14:35:43 +00:00
29 changed files with 754 additions and 341 deletions

View file

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

View file

@ -0,0 +1,51 @@
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

2
.git-blame-ignore-revs Normal file
View file

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

View file

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

2
.taplo.toml Normal file
View file

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

View file

@ -4,13 +4,9 @@ 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"
@ -20,6 +16,9 @@ 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"] }
@ -35,7 +34,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

View file

@ -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 is hosted by the Computer Society on Skynet (computer cluster).
Skynet (bot) 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.

17
flake.lock generated
View file

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

View file

@ -4,6 +4,10 @@
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";
};
@ -17,12 +21,27 @@
nixpkgs,
utils,
naersk,
nixpkgs-mozilla,
}:
utils.lib.eachDefaultSystem (
system: let
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
pkgs = (import nixpkgs) {inherit system;};
naersk' = pkgs.callPackage naersk {};
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;
};
package_name = "skynet_discord_bot";
desc = "Skynet Discord Bot";
buildInputs = with pkgs; [
@ -103,12 +122,14 @@
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}"
@ -133,13 +154,14 @@
# modify these
scripts = {
# every 20 min
# every 10 min
"update_data" = "*:0,10,20,30,40,50";
# groups are updated every hour, offset from teh ldap
"update_users" = "*:05:00";
"update_users" = "*:5,15,25,35,45,55";
# Committee server has its own timer
"update_committee" = "*:15:00";
"update_committee" = "*:5,15,25,35,45,55";
# 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";
@ -202,6 +224,7 @@
after = ["network-online.target"];
wants = [];
environment = environment_config;
path = with pkgs; [ git git-lfs ];
serviceConfig = {
User = "${cfg.user}";

View file

@ -0,0 +1,137 @@
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,12 +1,17 @@
use serenity::{
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
async_trait,
client::{Context, EventHandler},
model::gateway::{GatewayIntents, Ready},
model::gateway::GatewayIntents,
Client,
};
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 skynet_discord_bot::{
common::{
database::{db_init, DataBase},
set_roles::committee,
},
get_config, Config,
};
use std::{process, sync::Arc};
use tokio::sync::RwLock;
@ -23,6 +28,7 @@ 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");
@ -30,25 +36,39 @@ async fn main() {
let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
data.insert::<DataBase>(Arc::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 ready(&self, ctx: Context, ready: Ready) {
let ctx = Arc::new(ctx);
println!("{} is connected!", ready.user.name);
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;
// u[date committee server
committee::check_committee(Arc::clone(&ctx)).await;
let server = config_global.committee_server;
// finish up
process::exit(0);
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);
}
}
}

View file

@ -4,10 +4,13 @@ use serenity::{
model::gateway::{GatewayIntents, Ready},
Client,
};
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 skynet_discord_bot::{
common::{
database::{db_init, DataBase},
wolves::{cns::get_wolves, committees::get_cns},
},
get_config, Config,
};
use std::{process, sync::Arc};
use tokio::sync::RwLock;
@ -27,6 +30,7 @@ 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");
@ -34,11 +38,11 @@ async fn main() {
let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
data.insert::<DataBase>(Arc::new(db));
}
if let Err(why) = client.start().await {
println!("Client error: {:?}", why);
println!("Client error: {why:?}");
}
}

View file

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

View file

@ -4,9 +4,11 @@ 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},
common::{
database::{db_init, DataBase},
server_icon::{get_config_icons, update_icon},
},
get_config, Config,
};
use std::{process, sync::Arc};
@ -25,6 +27,7 @@ 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");
@ -32,11 +35,11 @@ async fn main() {
let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
data.insert::<DataBase>(Arc::new(db));
}
if let Err(why) = client.start().await {
println!("Client error: {:?}", why);
println!("Client error: {why:?}");
}
}
@ -47,11 +50,10 @@ impl EventHandler for Handler {
let ctx = Arc::new(ctx);
println!("{} is connected!", ready.user.name);
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
};
let db = db_lock.read().await;
let config_lock = {
let data_read = ctx.data.read().await;

View file

@ -1,13 +1,24 @@
use serenity::{
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
async_trait,
client::{Context, EventHandler},
model::gateway::{GatewayIntents, Ready},
model::gateway::GatewayIntents,
Client,
};
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 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 tokio::sync::RwLock;
#[tokio::main]
@ -22,7 +33,11 @@ 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 {})
.event_handler(Handler {
server_count: Default::default(),
server_cached: Default::default(),
})
.cache_settings(serenity::cache::Settings::default())
.await
.expect("Error creating client");
@ -30,38 +45,51 @@ async fn main() {
let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
data.insert::<DataBase>(Arc::new(db));
}
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]
impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) {
let ctx = Arc::new(ctx);
println!("{} is connected!", ready.user.name);
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) {
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));
}
// this goes into each server and sets roles for each wolves member
check_bulk(Arc::clone(&ctx)).await;
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!");
// finish up
process::exit(0);
// this goes into each server and sets roles for each wolves member
check_bulk(&ctx).await;
// finish up
process::exit(0);
}
}
}
}
async fn check_bulk(ctx: Arc<Context>) {
let db_lock = {
async fn check_bulk(ctx: &Context) {
let db = {
let data_read = ctx.data.read().await;
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 {
normal::update_server(&ctx, &server_config, &[], &[]).await;
normal::update_server(ctx, &server_config, &[], &[]).await;
}
}

View file

@ -1,8 +1,12 @@
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 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 sqlx::{Error, Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
@ -52,11 +56,10 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
return "Please provide a valid channel for ``Bot Channel``".to_string();
};
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
let db = db_lock.read().await;
let server_data = Servers {
server: command.guild_id.unwrap_or_default(),
@ -72,8 +75,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:?}");
}
}
@ -98,7 +101,7 @@ async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Resul
.fetch_optional(db)
.await;
// if the entry does not exist already tehn do a user update
// if the entry does not exist already then do a user update
let (update, current_remove, current_role, past_remove, past_role) = match &existing {
None => (true, false, None, false, None),
Some(x) => {

View file

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

View file

@ -9,19 +9,24 @@ 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};
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 serenity::{
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction},
model::id::UserId,
};
use skynet_discord_bot::{
common::{
database::Wolves,
minecraft::{whitelist_update, Minecraft},
},
Config,
};
use sqlx::Error;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
let db = db_lock.read().await;
let config_lock = {
let data_read = ctx.data.read().await;
@ -69,14 +74,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 {
@ -185,14 +190,17 @@ pub(crate) mod server {
use super::*;
pub(crate) mod add {
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption};
use serenity::model::id::GuildId;
use serenity::{
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption},
model::id::GuildId,
};
use sqlx::Error;
// this is to managfe the server side of commands related to minecraft
// this is to manage the server side of commands related to minecraft
use super::*;
use skynet_discord_bot::common::minecraft::update_server;
use skynet_discord_bot::common::minecraft::Minecraft;
use skynet_discord_bot::Config;
use skynet_discord_bot::{
common::minecraft::{update_server, Minecraft},
Config,
};
pub fn register() -> CreateCommand {
CreateCommand::new("minecraft_add")
@ -220,16 +228,15 @@ pub(crate) mod server {
return String::from("Expected Server ID");
};
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
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 {
Ok(_) => {}
Err(e) => {
println!("{:?}", e);
println!("{e:?}");
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
}
}
@ -260,12 +267,14 @@ pub(crate) mod server {
}
pub(crate) mod list {
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;
use serenity::{all::CommandInteraction, builder::CreateCommand, client::Context};
use skynet_discord_bot::{
common::{
database::DataBase,
minecraft::{get_minecraft_config_server, server_information},
},
Config,
};
pub fn register() -> CreateCommand {
CreateCommand::new("minecraft_list")
@ -279,11 +288,10 @@ pub(crate) mod server {
Some(x) => x,
};
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
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;
@ -320,12 +328,13 @@ pub(crate) mod server {
}
pub(crate) mod delete {
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 serenity::{
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption},
builder::CreateCommand,
client::Context,
model::id::GuildId,
};
use skynet_discord_bot::common::{database::DataBase, minecraft::Minecraft};
use sqlx::{Error, Pool, Sqlite};
pub fn register() -> CreateCommand {
@ -354,16 +363,15 @@ pub(crate) mod server {
return String::from("Expected Server ID");
};
let db_lock = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
let db = {
let data = ctx.data.read().await;
data.get::<DataBase>().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);
}
}

View file

@ -62,11 +62,10 @@ pub mod edit {
false
};
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
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_data = RoleAdder {
@ -79,8 +78,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:?}");
}
}
@ -101,9 +100,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}")
}
}
@ -142,13 +141,12 @@ pub mod edit {
pub mod list {}
pub mod tools {
use serenity::client::Context;
use serenity::model::guild::Member;
use serenity::{client::Context, model::guild::Member};
use skynet_discord_bot::common::database::RoleAdder;
use sqlx::{Pool, Sqlite};
pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, new_data: Member) {
// check if the role changed is part of the oens for this server
// check if the role changed is part of the ones for this server
if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>(
r#"
SELECT *
@ -164,7 +162,7 @@ pub mod tools {
let mut roles_remove = vec![];
for role_adder in role_adders {
// if the user has both A dnd B give them C
// if the user has both A and 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);
@ -180,13 +178,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:?}");
}
}
}

View file

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

View file

@ -4,11 +4,16 @@ use lettre::{
Message, SmtpTransport, Transport,
};
use maud::html;
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 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 sqlx::{Pool, Sqlite};
pub mod link {
@ -16,11 +21,10 @@ pub mod link {
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
let db = db_lock.read().await;
let config_lock = {
let data_read = ctx.data.read().await;
@ -96,7 +100,7 @@ pub mod link {
return "Email already verified".to_string();
}
// generate a auth key
// generate an 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 {
@ -110,7 +114,7 @@ pub mod link {
}
}
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)
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.")
}
pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> {
@ -205,7 +209,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 url's
// also helps not trip spam settings (uneven number of urls)
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())),
@ -279,22 +283,40 @@ 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, GuildId, RoleId};
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 serenity::{
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction},
model::user::User,
};
use skynet_discord_bot::common::{
database::{get_server_config, ServerMembersWolves, Servers},
wolves::committees::Committees,
};
use sqlx::Error;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().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 {
@ -341,12 +363,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()
@ -403,7 +425,7 @@ pub mod verify {
}
if let Err(e) = member.add_roles(&ctx, &roles).await {
println!("{:?}", e);
println!("{e:?}");
}
}
}
@ -419,7 +441,7 @@ pub mod verify {
WHERE committee LIKE ?1
"#,
)
.bind(format!("%{}%", wolves_id))
.bind(format!("%{wolves_id}%"))
.fetch_all(db)
.await
.unwrap_or_else(|e| {
@ -429,12 +451,18 @@ pub mod verify {
}
async fn set_server_roles_committee(db: &Pool<Sqlite>, discord: &User, ctx: &Context) {
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;
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 the teh general committee role
// if they are a member of one or more committees, and in teh committee server then give them the 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 = GuildId::new(1220150752656363520);
let committee_member = RoleId::new(1226602779968274573);
let server = config.committee_server;
let committee_member = config.committee_role;
if let Ok(member) = server.member(ctx, &discord.id).await {
member.add_roles(&ctx, &[committee_member]).await.unwrap_or_default();
@ -464,13 +492,12 @@ pub mod unlink {
use sqlx::{Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
let db = db_lock.read().await;
// dosent matter if there is one or not, it will be removed regardless
// doesn't matter if there is one or not, it will be removed regardless
delete_link(&db, &command.user.id).await;
"Discord link removed".to_string()
@ -516,4 +543,5 @@ 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."))
}

View file

@ -1,17 +1,21 @@
use crate::Config;
use serde::{Deserialize, Serialize};
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;
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};
pub struct DataBase;
impl TypeMapKey for DataBase {
type Value = Arc<RwLock<Pool<Sqlite>>>;
type Value = Arc<Pool<Sqlite>>;
}
#[derive(Debug, Clone, Deserialize, Serialize)]
@ -220,7 +224,7 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, 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),
)

View file

@ -1,10 +1,7 @@
use crate::common::set_roles::normal::get_server_member_bulk;
use crate::Config;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate::{common::set_roles::normal::get_server_member_bulk, Config};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serenity::model::id::GuildId;
use sqlx::sqlite::SqliteRow;
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Minecraft {
@ -27,7 +24,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 arent members
and a list that aren't members
*/
pub async fn update_server(server_id: &str, db: &Pool<Sqlite>, g_id: &GuildId, config: &Config) {
let mut usernames = vec![];
@ -112,7 +109,7 @@ pub async fn whitelist_wipe(server: &str, token: &str) {
};
post(&format!("{url_base}/files/delete"), &bearer, &deletion).await;
// recreate teh file, passing in the type here so the compiler knows what type of vec it is
// recreate the 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;
// reload the whitelist
@ -155,7 +152,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) {
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}");

View file

@ -1,10 +1,11 @@
// 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 younked it from here.
// I was unable to figure out how to use usvg myself so yoinked it from here.
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::{
ffi::OsStr,
path::{Path, PathBuf},
};
// use clap::builder::OsStr;
use color_eyre::{eyre::bail, Result};
use usvg_text_layout::TreeTextToPath;
@ -15,7 +16,7 @@ pub struct Args {
/// Output folder where the PNG's will be placed
pub output: PathBuf,
/// Comma seperated colors that will be used in HEX Eg. 000000,ffffff
/// Comma separated colors that will be used in HEX Eg. 000000,ffffff
/// Can be like an object: black:000000,white:ffffff
pub colors: String,
@ -177,7 +178,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<String> {

View file

@ -114,7 +114,7 @@ pub mod update_icon {
/// Update the server icon, pulling from open governance.
pub async fn update_icon_main(ctx: &Context, db: &Pool<Sqlite>, config_global: &Config, config_toml_local: &ConfigTomlLocal) {
let server = GuildId::new(689189992417067052);
let server = config_global.compsoc_server;
// clone repo into local folder
clone_repo(config_global, config_toml_local);
@ -191,6 +191,28 @@ 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<LogoData> {
@ -259,7 +281,7 @@ pub mod update_icon {
// check if exists
if !path_new.exists() {
// convert if it hasnt been converted already
// convert if it hasn't been converted already
match r.render(&path_local, &args) {
Ok(_) => {}
Err(_e) => {
@ -286,9 +308,19 @@ pub mod update_icon {
fn logos_filter(festival_data: &FestivalData, existing: Vec<LogoData>) -> Vec<LogoData> {
let mut filtered: Vec<LogoData> = 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

View file

@ -1,18 +1,27 @@
pub mod normal {
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 crate::{
common::database::{DataBase, ServerMembersWolves, Servers, Wolves},
get_now_iso,
};
use serenity::{
client::Context,
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<RoleId>], members_changed: &[UserId]) {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
};
let db = db_lock.read().await;
let Servers {
server,
role_past,
@ -20,7 +29,12 @@ pub mod normal {
..
} = server;
let mut roles_set = [0, 0, 0];
let mut roles_set = RolesChange {
total: 0,
new: 0,
current_add: 0,
current_rem: 0,
};
let mut members = vec![];
for member in get_server_member_bulk(&db, server).await {
@ -32,28 +46,30 @@ 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 teh users in it
// members_changed acts as an override to only deal with the 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[0] += 1;
roles_set.new += 1;
roles.push(role.to_owned());
}
}
if !member.roles.contains(role_current) {
roles_set[1] += 1;
roles_set.current_add += 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
@ -65,16 +81,16 @@ pub mod normal {
}
if member.roles.contains(role_current) {
roles_set[2] += 1;
// if theya re not a current member and have the role then remove it
roles_set.current_rem += 1;
// if they'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:?}");
}
}
}
@ -83,7 +99,14 @@ 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!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]);
println!(
"{:?} 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> {
@ -123,7 +146,7 @@ pub mod normal {
Ok(_) => {}
Err(e) => {
println!("Failure to insert into {}", server.get());
println!("{:?}", e);
println!("{e:?}");
}
}
}
@ -131,58 +154,56 @@ pub mod normal {
// for updating committee members
pub mod committee {
use crate::common::database::{get_channel_from_row, get_role_from_row, DataBase, Wolves};
use crate::common::wolves::committees::Committees;
use crate::Config;
use crate::{
common::{
database::{get_channel_from_row, get_role_from_row, DataBase, Wolves},
wolves::committees::Committees,
},
Config,
};
use serde::{Deserialize, Serialize};
use serenity::all::{EditRole, GuildId};
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 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 std::collections::HashMap;
use std::sync::Arc;
pub async fn check_committee(ctx: Arc<Context>) {
let db_lock = {
pub async fn check_committee(ctx: &Context) {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().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::<Config>().expect("Expected Config in TypeMap.").clone()
};
let config_global = config_lock.read().await;
let server = GuildId::new(1220150752656363520);
let server = config_global.committee_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();
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 tehm the appropiate roles on teh committee server
This function can take a Vec of members (or just one) and gives them the appropriate roles on teh committee server
*/
pub async fn update_committees(db: &Pool<Sqlite>, ctx: &Context, _config: &Config, members: &mut Vec<Member>) {
let server = GuildId::new(1220150752656363520);
let committee_member = RoleId::new(1226602779968274573);
let committees = get_committees(db).await;
let categories = [
ChannelId::new(1226606560973815839),
// C&S Chats 2
ChannelId::new(1341457244973305927),
// C&S Chats 3
ChannelId::new(1341457509717639279),
];
pub async fn update_committees(db: &Pool<Sqlite>, ctx: &Context, config: &Config, members: &mut Vec<Member>) {
let server = config.committee_server;
let committee_member = config.committee_role;
let committees = match get_committees(db).await {
None => {
return;
}
Some(x) => x,
};
let categories = config.committee_category.clone();
// information about the server
let mut roles_db = HashMap::new();
@ -203,11 +224,11 @@ pub mod committee {
let mut channels = server.channels(&ctx).await.unwrap_or_default();
// a map of users and the roles they are goign to be getting
// a map of users and the roles they are going to be getting
let mut users_roles = HashMap::new();
let mut re_order = false;
// we need to create roles and channels if tehy dont already exist
// we need to create roles and channels if they don't already exist
let mut category_index = 0;
let mut i = 0;
loop {
@ -308,21 +329,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) => {
let mut tmp = x.to_owned();
if !tmp.is_empty() {
tmp.push(committee_member);
}
tmp
}
Some(x) => x.to_owned(),
};
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
@ -331,14 +352,25 @@ 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());
}
}
if !roles_required.is_empty() {
// if there are committee roles then give the general purporse role
roles_add.push(committee_member);
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 let Some(x) = roles_db.get_mut(&0) {
x.count += 1;
}
@ -357,8 +389,6 @@ 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();
}
}
@ -406,10 +436,10 @@ pub mod committee {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CommitteeRoles {
id_wolves: i64,
id_role: RoleId,
id_channel: ChannelId,
pub id_role: RoleId,
pub id_channel: ChannelId,
pub name_role: String,
name_channel: String,
pub name_channel: String,
pub count: i64,
}
@ -446,8 +476,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:?}");
}
}
}
@ -464,13 +494,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<Sqlite>) -> Vec<Committees> {
sqlx::query_as::<_, Committees>(
pub async fn get_committees(db: &Pool<Sqlite>) -> Option<Vec<Committees>> {
match sqlx::query_as::<_, Committees>(
r#"
SELECT *
FROM committees
@ -478,10 +508,13 @@ pub mod committee {
)
.fetch_all(db)
.await
.unwrap_or_else(|e| {
dbg!(e);
vec![]
})
{
Ok(x) => Some(x),
Err(e) => {
dbg!(e);
None
}
}
}
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(_) => {}
Err(e) => {
println!("Failure to insert into Wolves {:?}", user);
println!("{:?}", e);
println!("Failure to insert into Wolves {user:?}");
println!("{e:?}");
}
}
}
@ -48,12 +48,14 @@ async fn add_users_wolves(db: &Pool<Sqlite>, 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};
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 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 sqlx::{Pool, Sqlite};
use std::collections::BTreeMap;
@ -67,11 +69,10 @@ pub mod cns {
}
pub async fn get_wolves(ctx: &Context) {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
};
let db = db_lock.read().await;
let config_lock = {
let data_read = ctx.data.read().await;
@ -96,7 +97,6 @@ pub mod cns {
let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::<BTreeMap<_, _>>();
// 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,10 +115,6 @@ 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);
}
}
}
}
@ -129,9 +125,6 @@ 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;
}
}
}
@ -151,7 +144,7 @@ pub mod cns {
Ok(_) => {}
Err(e) => {
println!("Failure to set server name {}", server.get());
println!("{:?}", e);
println!("{e:?}");
}
}
}
@ -190,7 +183,7 @@ pub mod cns {
Ok(_) => {}
Err(e) => {
println!("Failure to insert into ServerMembers {} {:?}", server.get(), user);
println!("{:?}", e);
println!("{e:?}");
}
}
}
@ -200,8 +193,7 @@ pub mod cns {
Get and store the data on C&S committees
*/
pub mod committees {
use crate::common::database::DataBase;
use crate::Config;
use crate::{common::database::DataBase, Config};
use serenity::client::Context;
use sqlx::{Pool, Sqlite};
@ -231,11 +223,10 @@ pub mod committees {
}
pub async fn get_cns(ctx: &Context) {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
};
let db = db_lock.read().await;
let config_lock = {
let data_read = ctx.data.read().await;
@ -270,8 +261,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:?}");
}
}
}

View file

@ -3,8 +3,10 @@ pub mod common;
use chrono::{Datelike, SecondsFormat, Utc};
use dotenvy::dotenv;
use rand::{distr::Alphanumeric, rng, Rng};
use serenity::model::id::{ChannelId, GuildId, RoleId};
use serenity::prelude::TypeMapKey;
use serenity::{
model::id::{ChannelId, GuildId, RoleId},
prelude::TypeMapKey,
};
use std::{env, sync::Arc};
use tokio::sync::RwLock;
@ -32,7 +34,10 @@ pub struct Config {
// discord server for committee
pub committee_server: GuildId,
pub committee_role: RoleId,
pub committee_category: ChannelId,
pub committee_category: Vec<ChannelId>,
// items pertaining to CompSoc only
pub compsoc_server: GuildId,
}
impl TypeMapKey for Config {
type Value = Arc<RwLock<Config>>;
@ -57,7 +62,8 @@ pub fn get_config() -> Config {
wolves_api: "".to_string(),
committee_server: GuildId::new(1),
committee_role: RoleId::new(1),
committee_category: ChannelId::new(1),
committee_category: vec![],
compsoc_server: GuildId::new(1),
};
if let Ok(x) = env::var("DATABASE_HOME") {
@ -99,14 +105,22 @@ pub fn get_config() -> Config {
config.committee_server = GuildId::new(x);
}
}
if let Ok(x) = env::var("COMMITTEE_DISCORD") {
if let Ok(x) = env::var("COMMITTEE_ROLE") {
if let Ok(x) = x.trim().parse::<u64>() {
config.committee_role = RoleId::new(x);
}
}
if let Ok(x) = env::var("COMMITTEE_CATEGORY") {
for part in x.split(',') {
if let Ok(x) = part.trim().parse::<u64>() {
config.committee_category.push(ChannelId::new(x));
}
}
}
if let Ok(x) = env::var("COMPSOC_DISCORD") {
if let Ok(x) = x.trim().parse::<u64>() {
config.committee_category = ChannelId::new(x);
config.compsoc_server = GuildId::new(x);
}
}

View file

@ -1,23 +1,28 @@
pub mod commands;
use crate::commands::role_adder::tools::on_role_change;
use serenity::all::{
ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, 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};
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 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 sqlx::{Pool, Sqlite};
use std::sync::Arc;
use tokio::sync::RwLock;
@ -26,15 +31,21 @@ 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<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
async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().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::<Config>().expect("Expected Config in TypeMap.").clone()
@ -42,7 +53,7 @@ impl EventHandler for Handler {
let config_global = config_lock.read().await;
// committee server takes priority
let committee_server = GuildId::new(1220150752656363520);
let committee_server = config_global.committee_server;
if new_member.guild_id.get() == committee_server.get() {
let mut member = vec![new_member.clone()];
update_committees(&db, &ctx, &config_global, &mut member).await;
@ -68,7 +79,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;
@ -96,14 +107,12 @@ 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<Member>, new_data: Option<Member>, _: GuildMemberUpdateEvent) {
// get config/db
let db_lock = {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
};
let db = db_lock.read().await;
// check if the role changed is part of the oens for this server
// check if the role changed is part of the ones for this server
if let Some(x) = new_data {
on_role_change(&db, &ctx, x).await;
}
@ -113,6 +122,12 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
println!("[Main] {} is connected!", ready.user.name);
ctx.set_presence(Some(ActivityData::playing("with humanity's fate")), OnlineStatus::Online);
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;
match Command::set_global_commands(
&ctx.http,
vec![
@ -127,27 +142,25 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
{
Ok(_) => {}
Err(e) => {
println!("{:?}", e)
println!("{e:?}")
}
}
// Inter-Committee server
match GuildId::new(1220150752656363520)
.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(_) => {}
Err(e) => {
println!("{:?}", e)
println!("{e:?}")
}
}
// compsoc Server
match GuildId::new(689189992417067052)
// 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(),
],
@ -156,7 +169,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
{
Ok(_) => {}
Err(e) => {
println!("{:?}", e)
println!("{e:?}")
}
}
}
@ -164,7 +177,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
@ -175,6 +188,7 @@ 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()),
},
@ -242,7 +256,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}");
}
}
}
@ -277,7 +291,8 @@ 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 {})
.event_handler(Handler)
.cache_settings(serenity::cache::Settings::default())
.await
.expect("Error creating client");
@ -285,7 +300,7 @@ async fn main() {
let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
data.insert::<DataBase>(Arc::new(db));
}
// Finally, start a single shard, and start listening to events.
@ -293,6 +308,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:?}");
}
}