Compare commits
1 commit
main
...
#6-incrime
Author | SHA1 | Date | |
---|---|---|---|
a8d3b50748 |
31 changed files with 1072 additions and 2668 deletions
|
@ -1,55 +0,0 @@
|
||||||
name: On_Push
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
paths:
|
|
||||||
- flake.*
|
|
||||||
- src/**/*
|
|
||||||
- Cargo.*
|
|
||||||
- .forgejo/**/*
|
|
||||||
- rust-toolchain.toml
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# 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
|
|
||||||
- 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
|
|
||||||
- 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
|
|
||||||
- name: "Build it locally"
|
|
||||||
run: nix build --verbose
|
|
||||||
|
|
||||||
# deploy it upstream
|
|
||||||
deploy:
|
|
||||||
# runs on teh default docker container
|
|
||||||
runs-on: docker
|
|
||||||
needs: [ build ]
|
|
||||||
steps:
|
|
||||||
- name: "Deploy to Skynet"
|
|
||||||
uses: https://forgejo.skynet.ie/Skynet/actions-deploy-to-skynet@v2
|
|
||||||
with:
|
|
||||||
input: 'skynet_discord_bot'
|
|
||||||
token: ${{ secrets.API_TOKEN_FORGEJO }}
|
|
37
.gitattributes
vendored
37
.gitattributes
vendored
|
@ -1,37 +0,0 @@
|
||||||
# Git config here
|
|
||||||
* text eol=lf
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Git lfs stuff
|
|
||||||
# Documents
|
|
||||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.doc filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.docx filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# Excel
|
|
||||||
*.xls filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.xlsx filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.xlsm filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# Powerpoints
|
|
||||||
*.ppt filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.pptx filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.ppsx filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# Images
|
|
||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# Video
|
|
||||||
*.mkv filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.wmv filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# Misc
|
|
||||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# ET4011
|
|
||||||
*.cbe filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.pbs filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# Open/Libre office
|
|
||||||
# from https://www.libreoffice.org/discover/what-is-opendocument/
|
|
||||||
*.odt filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.ods filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.odp filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.odg filter=lfs diff=lfs merge=lfs -text
|
|
||||||
# QT
|
|
||||||
*.ui filter=lfs diff=lfs merge=lfs -text
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,13 +2,11 @@
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
.env
|
.env
|
||||||
*.env
|
|
||||||
|
|
||||||
result
|
result
|
||||||
/result
|
/result
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.db.*
|
|
||||||
|
|
||||||
tmp/
|
tmp/
|
||||||
tmp.*
|
tmp.*
|
||||||
|
|
86
.gitlab-ci.yml
Normal file
86
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# copied a good chunk from my bfom config
|
||||||
|
image: rust:latest
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- lint
|
||||||
|
- build
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
cache:
|
||||||
|
key: "$CI_JOB_NAME"
|
||||||
|
paths:
|
||||||
|
- target/
|
||||||
|
|
||||||
|
# Set any required environment variables here
|
||||||
|
variables:
|
||||||
|
RUST_BACKTRACE: FULL
|
||||||
|
|
||||||
|
|
||||||
|
# clippy and fmt are magic
|
||||||
|
# runs on all commits/branches
|
||||||
|
lint-clippy:
|
||||||
|
stage: lint
|
||||||
|
script:
|
||||||
|
- rustup component add clippy
|
||||||
|
- rustc --version
|
||||||
|
- cargo version
|
||||||
|
- cargo clippy
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
when: never
|
||||||
|
- changes:
|
||||||
|
- src/**/*
|
||||||
|
- cargo.*
|
||||||
|
when: always
|
||||||
|
|
||||||
|
lint-fmt:
|
||||||
|
stage: lint
|
||||||
|
script:
|
||||||
|
- rustup component add rustfmt
|
||||||
|
- rustc --version
|
||||||
|
- cargo version
|
||||||
|
- cargo fmt -- --check
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
when: never
|
||||||
|
- changes:
|
||||||
|
- src/**/*
|
||||||
|
- cargo.*
|
||||||
|
when: always
|
||||||
|
|
||||||
|
|
||||||
|
# has to actually compile
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- rustc --version
|
||||||
|
- cargo version
|
||||||
|
- cargo build --verbose
|
||||||
|
- RUST_BACKTRACE=1 cargo test --verbose
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
when: never
|
||||||
|
- changes:
|
||||||
|
- src/**/*
|
||||||
|
- cargo.*
|
||||||
|
when: on_success
|
||||||
|
|
||||||
|
|
||||||
|
# from https://docs.gitlab.com/ee/ci/pipelines/multi_project_pipelines.html
|
||||||
|
# so simple to deploy now
|
||||||
|
nixos:
|
||||||
|
stage: deploy
|
||||||
|
variables:
|
||||||
|
PACKAGE_NAME: "skynet_discord_bot"
|
||||||
|
UPDATE_FLAKE: "yes"
|
||||||
|
trigger: compsoc1/skynet/nixos
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
when: never
|
||||||
|
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||||
|
when: on_success
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
1416
Cargo.lock
generated
1416
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -11,11 +11,7 @@ name = "update_data"
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "update_users"
|
name = "update_users"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "update_minecraft"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# discord library
|
|
||||||
serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] }
|
serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] }
|
||||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
|
@ -25,7 +21,7 @@ surf = "2.3.2"
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
|
|
||||||
# For sqlite
|
# For sqlite
|
||||||
sqlx = { version = "0.7.1", features = [ "runtime-tokio", "sqlite", "migrate" ] }
|
sqlx = { version = "0.7.1", features = [ "runtime-tokio", "sqlite" ] }
|
||||||
|
|
||||||
# create random strings
|
# create random strings
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
9
LICENSE
9
LICENSE
|
@ -1,9 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Skynet
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
97
README.md
97
README.md
|
@ -1,97 +0,0 @@
|
||||||
# Skynet Discord Bot
|
|
||||||
This bots core purpose is to give members roles based on their status on <https://ulwolves.ie>.
|
|
||||||
It uses an api key provided by wolves to get member lists.
|
|
||||||
|
|
||||||
Users are able to link their wolves account to the bot and that works across discord servers.
|
|
||||||
For example is a user links on the CompSoc Discord then they will also get their roles (automagically) on Games Dev if they are a member there.
|
|
||||||
|
|
||||||
## Setup - Committee
|
|
||||||
You need admin access to run any of the commands in this section.
|
|
||||||
Either the server owner or a user with the ``Administrator`` permission.
|
|
||||||
|
|
||||||
### Get the API Key
|
|
||||||
The ``api_key`` is used by the Bot in order to request information, it will be used later in the process.
|
|
||||||
|
|
||||||
1. Email ``keith@assurememberships.com`` from committee email and say you want an ``api_key`` for ``193.1.99.74``
|
|
||||||
* The committee email is the one here: <https://cp.ulwolves.ie/mailbox/>
|
|
||||||
* This may take up to a week to get the key.
|
|
||||||
|
|
||||||
### Setup Server
|
|
||||||
The Bot reason for existing is being able to give members Roles.
|
|
||||||
So we have to create those.
|
|
||||||
|
|
||||||
1. Create a role for Current Members.
|
|
||||||
* You can call it whatever you want.
|
|
||||||
* ``member-current`` is a good choice.
|
|
||||||
* This should be a new role
|
|
||||||
2. **Optional**: you can create a role that is given to folks who were but no longer a member.
|
|
||||||
* ``member`` would be a good choice for this
|
|
||||||
* If you have an existing member role this is also a good fit.
|
|
||||||
|
|
||||||
The reason for both roles is ye have one for active members while the second is for all current and past members.
|
|
||||||
|
|
||||||
### Invite Bot
|
|
||||||
1. Invite the bot https://discord.com/api/oauth2/authorize?client_id=1145761669256069270&permissions=139855185984&scope=bot
|
|
||||||
2. Make sure the bot role ``@skynet`` is above these two roles created in the previous step
|
|
||||||
* This is so it can manage the roles (give and remove them from users)
|
|
||||||
|
|
||||||
### Setup Bot
|
|
||||||
This is where the bot is configured.
|
|
||||||
You will need the ``api_key`` from the start of the process.
|
|
||||||
You (personally) will need a role with ``Administrator`` permission to be able to do this.
|
|
||||||
|
|
||||||
1. Use the command ``/add`` and a list of options will pop up.
|
|
||||||
2. ``api_key`` is the key you got from Keith earlier.
|
|
||||||
3. ``role_current`` is the ``member-current`` that you created earlier.
|
|
||||||
4. ``role_past`` (optional) is the role for all current and past members.
|
|
||||||
5. ``bot_channel`` is a channel that folks are recommended to use the bot.
|
|
||||||
* You can have it so folks cannot see message history
|
|
||||||
6. ``server_name`` For example ``UL Computer Society``
|
|
||||||
* Will be removed in the future
|
|
||||||
7. ``wolves_link`` for example <https://ulwolves.ie/society/computer>
|
|
||||||
* Will be removed in the future
|
|
||||||
|
|
||||||
At this point the bot is set up and no further action is required.
|
|
||||||
|
|
||||||
### Minecraft
|
|
||||||
The bot is able to manage the whitelist of a Minecraft server managed by the Computer Society.
|
|
||||||
Talk to us to get a server.
|
|
||||||
|
|
||||||
#### Add
|
|
||||||
This links a minecraft server with your club/society.
|
|
||||||
|
|
||||||
``/minecraft_add SERVER_ID``
|
|
||||||
|
|
||||||
|
|
||||||
#### List
|
|
||||||
List the servers linked to your club/society.
|
|
||||||
It is possible to have more than one minecraft server
|
|
||||||
|
|
||||||
``/minecraft_list``
|
|
||||||
|
|
||||||
#### Delete
|
|
||||||
This unlinks a minecraft server from your club/society.
|
|
||||||
|
|
||||||
``/minecraft_delete SERVER_ID``
|
|
||||||
|
|
||||||
## Setup - Users
|
|
||||||
This is to link your Discord account with your UL Wolves account.
|
|
||||||
**You will only need to do this once**.
|
|
||||||
|
|
||||||
### Setup
|
|
||||||
1. In a Discord server with the Skynet Bot enter ``/link_wolves YOUR_WOLVES_CONTACT_EMAIL``
|
|
||||||
<img src="media/setup_user_01.png" alt="link process start" width="50%" height="50%">
|
|
||||||
* Your ``YOUR_WOLVES_CONTACT_EMAIL`` is the email in the Contact Email here: <https://ulwolves.ie/memberships/profile>
|
|
||||||
* This is most likely your student mail
|
|
||||||
2. An email will be sent to you with a verification code.
|
|
||||||
<img src="media/setup_user_02.png" alt="signup email" width="50%" height="50%">
|
|
||||||
3. Verify the code using ``/verify CODE_FROM_EMAIL`` in Discord.
|
|
||||||
<img src="media/setup_user_03.png" alt="verify in discord" width="50%" height="50%">
|
|
||||||
4. Once complete your Wolves and Discord accounts will be linked.
|
|
||||||
|
|
||||||
You will get member roles on any Discord that is using the bot that you are a member of.
|
|
||||||
|
|
||||||
### Minecraft
|
|
||||||
You can link your Minecraft username to grant you access to any Minecraft server run by UL Computer Society.
|
|
||||||
|
|
||||||
``/link_minecraft MINECRAFT_USERNAME``
|
|
|
@ -1,38 +0,0 @@
|
||||||
-- setup initial tables
|
|
||||||
|
|
||||||
-- this handles the users "floating" account
|
|
||||||
CREATE TABLE IF NOT EXISTS wolves (
|
|
||||||
id_wolves integer PRIMARY KEY,
|
|
||||||
email text not null,
|
|
||||||
discord integer,
|
|
||||||
minecraft text
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS index_discord ON wolves (discord);
|
|
||||||
|
|
||||||
-- used to verify the users email address
|
|
||||||
CREATE TABLE IF NOT EXISTS wolves_verify (
|
|
||||||
discord integer PRIMARY KEY,
|
|
||||||
email text not null,
|
|
||||||
auth_code text not null,
|
|
||||||
date_expiry text not null
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS index_date_expiry ON wolves_verify (date_expiry);
|
|
||||||
|
|
||||||
-- information on teh server the bot is registered on
|
|
||||||
CREATE TABLE IF NOT EXISTS servers (
|
|
||||||
server integer PRIMARY KEY,
|
|
||||||
wolves_api text not null,
|
|
||||||
role_past integer,
|
|
||||||
role_current integer,
|
|
||||||
member_past integer DEFAULT 0,
|
|
||||||
member_current integer DEFAULT 0
|
|
||||||
);
|
|
||||||
|
|
||||||
-- keep track of the members on the server
|
|
||||||
CREATE TABLE IF NOT EXISTS server_members (
|
|
||||||
server integer not null,
|
|
||||||
id_wolves integer not null,
|
|
||||||
expiry text not null,
|
|
||||||
PRIMARY KEY(server,id_wolves),
|
|
||||||
FOREIGN KEY (id_wolves) REFERENCES wolves (id_wolves)
|
|
||||||
);
|
|
|
@ -1,3 +0,0 @@
|
||||||
-- add teh option to associate each discord server with a minecraft one managed by skynet
|
|
||||||
ALTER TABLE servers
|
|
||||||
ADD server_minecraft text;
|
|
|
@ -1,18 +0,0 @@
|
||||||
-- Create the new table
|
|
||||||
CREATE TABLE IF NOT EXISTS minecraft
|
|
||||||
(
|
|
||||||
server_discord integer not null,
|
|
||||||
server_minecraft text not null,
|
|
||||||
PRIMARY KEY (server_discord, server_minecraft),
|
|
||||||
FOREIGN KEY (server_discord) REFERENCES servers (server)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Import in the data
|
|
||||||
INSERT INTO minecraft (server_discord, server_minecraft)
|
|
||||||
SELECT server, server_minecraft
|
|
||||||
FROM servers
|
|
||||||
where server_minecraft is not null;
|
|
||||||
|
|
||||||
-- delete the col in teh server table
|
|
||||||
ALTER TABLE servers
|
|
||||||
DROP COLUMN server_minecraft;
|
|
|
@ -1,7 +0,0 @@
|
||||||
-- temp table to allow folks to verify by committee email.
|
|
||||||
CREATE TABLE IF NOT EXISTS committee (
|
|
||||||
discord integer PRIMARY KEY,
|
|
||||||
email text not null,
|
|
||||||
auth_code text not null,
|
|
||||||
committee integer DEFAULT 0
|
|
||||||
);
|
|
|
@ -1,5 +0,0 @@
|
||||||
-- temp table to allow folks to verify by committee email.
|
|
||||||
-- delete the col in teh server table
|
|
||||||
ALTER TABLE servers ADD COLUMN bot_channel_id integer DEFAULT 0;
|
|
||||||
ALTER TABLE servers ADD COLUMN server_name text NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE servers ADD COLUMN wolves_link text NOT NULL DEFAULT "";
|
|
|
@ -1,11 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS roles_adder (
|
|
||||||
server integer not null,
|
|
||||||
role_a integer not null,
|
|
||||||
role_b integer not null,
|
|
||||||
role_c integer not null,
|
|
||||||
PRIMARY KEY(server,role_a,role_b,role_c)
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS index_roles_adder_server ON roles_adder (server);
|
|
||||||
CREATE INDEX IF NOT EXISTS index_roles_adder_from ON roles_adder (role_a,role_b);
|
|
||||||
CREATE INDEX IF NOT EXISTS index_roles_adder_to ON roles_adder (role_c);
|
|
||||||
CREATE INDEX IF NOT EXISTS index_roles_adder_search ON roles_adder (server,role_a,role_b);
|
|
|
@ -1,8 +0,0 @@
|
||||||
HOME="."
|
|
||||||
DATABASE="database.db"
|
|
||||||
DISCORD_TOKEN=""
|
|
||||||
DISCORD_TOKEN_MINECRAFT=""
|
|
||||||
EMAIL_SMTP=""
|
|
||||||
EMAIL_USER=""
|
|
||||||
EMAIL_PASS=""
|
|
||||||
WOLVES_URL=""
|
|
26
flake.lock
26
flake.lock
|
@ -5,11 +5,11 @@
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721727458,
|
"lastModified": 1692351612,
|
||||||
"narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=",
|
"narHash": "sha256-KTGonidcdaLadRnv9KFgwSMh1ZbXoR/OBmPjeNMhFwU=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11",
|
"rev": "78789c30d64dea2396c9da516bbcc8db3a475207",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -20,11 +20,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1723151389,
|
"lastModified": 1693060755,
|
||||||
"narHash": "sha256-9AVY0ReCmSGXHrlx78+1RrqcDgVSRhHUKDVV1LLBy28=",
|
"narHash": "sha256-KNsbfqewEziFJEpPR0qvVz4rx0x6QXxw1CcunRhlFdk=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "13fe00cb6c75461901f072ae62b5805baef9f8b2",
|
"rev": "c66ccfa00c643751da2fd9290e096ceaa30493fc",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -34,16 +34,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1722995383,
|
"lastModified": 1693087214,
|
||||||
"narHash": "sha256-UzuXo7ZM8ZK0SkWFhHocKkLSGQPHS4JxaE1jvVR4fUo=",
|
"narHash": "sha256-Kn1SSqRfPpqcI1MDy82JXrPT1WI8c03TA2F0xu6kS+4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "957d95fc8b9bf1eb60d43f8d2eba352b71bbf2be",
|
"rev": "f155f0cf4ea43c4e3c8918d2d327d44777b6cad4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-23.05",
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -74,11 +74,11 @@
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710146030,
|
"lastModified": 1692799911,
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
114
flake.nix
114
flake.nix
|
@ -2,56 +2,31 @@
|
||||||
description = "Skynet Discord Bot";
|
description = "Skynet Discord Bot";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
nixpkgs.url = "nixpkgs/nixos-23.05";
|
||||||
naersk.url = "github:nix-community/naersk";
|
naersk.url = "github:nix-community/naersk";
|
||||||
utils.url = "github:numtide/flake-utils";
|
utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
|
|
||||||
nixConfig = {
|
outputs = { self, nixpkgs, utils, naersk }: utils.lib.eachDefaultSystem (system:
|
||||||
extra-substituters = "https://nix-cache.skynet.ie/skynet-cache";
|
let
|
||||||
extra-trusted-public-keys = "skynet-cache:zMFLzcRZPhUpjXUy8SF8Cf7KGAZwo98SKrzeXvdWABo=";
|
pkgs = nixpkgs.legacyPackages."${system}";
|
||||||
};
|
naersk-lib = naersk.lib."${system}";
|
||||||
|
|
||||||
outputs = {
|
|
||||||
self,
|
|
||||||
nixpkgs,
|
|
||||||
utils,
|
|
||||||
naersk,
|
|
||||||
}:
|
|
||||||
utils.lib.eachDefaultSystem (
|
|
||||||
system: let
|
|
||||||
pkgs = (import nixpkgs) {inherit system;};
|
|
||||||
naersk' = pkgs.callPackage naersk {};
|
|
||||||
package_name = "skynet_discord_bot";
|
package_name = "skynet_discord_bot";
|
||||||
desc = "Skynet Discord Bot";
|
desc = "Skynet Discord Bot";
|
||||||
buildInputs = with pkgs; [
|
|
||||||
openssl
|
|
||||||
pkg-config
|
|
||||||
rustfmt
|
|
||||||
];
|
|
||||||
in rec {
|
in rec {
|
||||||
packages = {
|
|
||||||
# For `nix build` & `nix run`:
|
# `nix build`
|
||||||
default = naersk'.buildPackage {
|
packages."${package_name}" = naersk-lib.buildPackage {
|
||||||
pname = "${package_name}";
|
pname = "${package_name}";
|
||||||
src = ./.;
|
root = ./.;
|
||||||
buildInputs = buildInputs;
|
|
||||||
};
|
buildInputs = [
|
||||||
# Run `nix build .#fmt` to run tests
|
pkgs.openssl
|
||||||
fmt = naersk'.buildPackage {
|
pkgs.pkg-config
|
||||||
src = ./.;
|
];
|
||||||
mode = "fmt";
|
|
||||||
buildInputs = buildInputs;
|
|
||||||
};
|
|
||||||
# Run `nix build .#clippy` to lint code
|
|
||||||
clippy = naersk'.buildPackage {
|
|
||||||
src = ./.;
|
|
||||||
mode = "clippy";
|
|
||||||
buildInputs = buildInputs;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
defaultPackage = packages.default;
|
defaultPackage = packages."${package_name}";
|
||||||
|
|
||||||
# `nix run`
|
# `nix run`
|
||||||
apps."${package_name}" = utils.lib.mkApp {
|
apps."${package_name}" = utils.lib.mkApp {
|
||||||
|
@ -62,28 +37,25 @@
|
||||||
|
|
||||||
# `nix develop`
|
# `nix develop`
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [rustc cargo pkg-config openssl rustfmt];
|
nativeBuildInputs = with pkgs; [ rustc cargo pkg-config openssl];
|
||||||
};
|
};
|
||||||
|
|
||||||
nixosModule = {
|
nixosModule = { lib, pkgs, config, ... }:
|
||||||
lib,
|
with lib;
|
||||||
pkgs,
|
let
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
with lib; let
|
|
||||||
cfg = config.services."${package_name}";
|
cfg = config.services."${package_name}";
|
||||||
# secret options are in the env file(s) loaded separately
|
# secret options are in the env file(s) loaded separately
|
||||||
environment_config = {
|
environment_config = {
|
||||||
DATABASE_HOME = cfg.home;
|
LDAP_API = cfg.ldap;
|
||||||
|
SKYNET_SERVER = cfg.discord.server;
|
||||||
|
HOME = cfg.home;
|
||||||
DATABASE = "database.db";
|
DATABASE = "database.db";
|
||||||
};
|
};
|
||||||
|
|
||||||
service_name = script: lib.strings.sanitizeDerivationName "${cfg.user}@${script}";
|
service_name = script: lib.strings.sanitizeDerivationName("${cfg.user}@${script}");
|
||||||
|
|
||||||
# oneshot scripts to run
|
# oneshot scripts to run
|
||||||
serviceGenerator = mapAttrs' (script: time:
|
serviceGenerator = mapAttrs' (script: time: nameValuePair (service_name script) {
|
||||||
nameValuePair (service_name script) {
|
|
||||||
description = "Service for ${desc} ${script}";
|
description = "Service for ${desc} ${script}";
|
||||||
wantedBy = [ ];
|
wantedBy = [ ];
|
||||||
after = [ "network-online.target" ];
|
after = [ "network-online.target" ];
|
||||||
|
@ -95,6 +67,7 @@
|
||||||
Group = "${cfg.user}";
|
Group = "${cfg.user}";
|
||||||
ExecStart = "${self.defaultPackage."${system}"}/bin/${script}";
|
ExecStart = "${self.defaultPackage."${system}"}/bin/${script}";
|
||||||
EnvironmentFile = [
|
EnvironmentFile = [
|
||||||
|
"${cfg.env.ldap}"
|
||||||
"${cfg.env.discord}"
|
"${cfg.env.discord}"
|
||||||
"${cfg.env.mail}"
|
"${cfg.env.mail}"
|
||||||
"${cfg.env.wolves}"
|
"${cfg.env.wolves}"
|
||||||
|
@ -103,8 +76,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
# each timer will run the above service
|
# each timer will run the above service
|
||||||
timerGenerator = mapAttrs' (script: time:
|
timerGenerator = mapAttrs' (script: time: nameValuePair (service_name script) {
|
||||||
nameValuePair (service_name script) {
|
|
||||||
description = "Timer for ${desc} ${script}";
|
description = "Timer for ${desc} ${script}";
|
||||||
|
|
||||||
wantedBy = [ "timers.target" ];
|
wantedBy = [ "timers.target" ];
|
||||||
|
@ -122,17 +94,20 @@
|
||||||
"update_data" = "*:0,20,40";
|
"update_data" = "*:0,20,40";
|
||||||
# groups are updated every hour, offset from teh ldap
|
# groups are updated every hour, offset from teh ldap
|
||||||
"update_users" = "*:05:00";
|
"update_users" = "*:05:00";
|
||||||
# minecraft stuff is updated at 5am
|
|
||||||
"update_minecraft" = "5:10:00";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
in {
|
in {
|
||||||
options.services."${package_name}" = {
|
options.services."${package_name}" = {
|
||||||
enable = mkEnableOption "enable ${package_name}";
|
enable = mkEnableOption "enable ${package_name}";
|
||||||
|
|
||||||
env = {
|
env = {
|
||||||
|
ldap = mkOption rec {
|
||||||
|
type = types.str;
|
||||||
|
description = "ENV file with LDAP_DISCORD_AUTH";
|
||||||
|
};
|
||||||
discord = mkOption rec {
|
discord = mkOption rec {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "ENV file with DISCORD_TOKEN, DISCORD_TOKEN_MINECRAFT";
|
description = "ENV file with DISCORD_TOKEN";
|
||||||
};
|
};
|
||||||
mail = mkOption rec {
|
mail = mkOption rec {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
@ -140,10 +115,23 @@
|
||||||
};
|
};
|
||||||
wolves = mkOption rec {
|
wolves = mkOption rec {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "Mail details, has WOLVES_URL";
|
description = "Mail details, has WOLVES_URL, WOLVES_KEY";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
discord = {
|
||||||
|
server = mkOption rec {
|
||||||
|
type = types.str;
|
||||||
|
description = "ID of the server the bot runs on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ldap = mkOption rec {
|
||||||
|
type = types.str;
|
||||||
|
default = "https://api.account.skynet.ie";
|
||||||
|
description = "Location of the ldap api";
|
||||||
|
};
|
||||||
|
|
||||||
user = mkOption rec {
|
user = mkOption rec {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "${package_name}";
|
default = "${package_name}";
|
||||||
|
@ -165,6 +153,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
users.groups."${cfg.user}" = { };
|
users.groups."${cfg.user}" = { };
|
||||||
|
|
||||||
users.users."${cfg.user}" = {
|
users.users."${cfg.user}" = {
|
||||||
|
@ -174,8 +163,7 @@
|
||||||
group = "${cfg.user}";
|
group = "${cfg.user}";
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services =
|
systemd.services = {
|
||||||
{
|
|
||||||
# main service
|
# main service
|
||||||
"${package_name}" = {
|
"${package_name}" = {
|
||||||
description = desc;
|
description = desc;
|
||||||
|
@ -191,22 +179,24 @@
|
||||||
ExecStart = "${self.defaultPackage."${system}"}/bin/${package_name}";
|
ExecStart = "${self.defaultPackage."${system}"}/bin/${package_name}";
|
||||||
# can have multiple env files
|
# can have multiple env files
|
||||||
EnvironmentFile = [
|
EnvironmentFile = [
|
||||||
|
"${cfg.env.ldap}"
|
||||||
"${cfg.env.discord}"
|
"${cfg.env.discord}"
|
||||||
"${cfg.env.mail}"
|
"${cfg.env.mail}"
|
||||||
"${cfg.env.wolves}"
|
"${cfg.env.wolves}"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
restartTriggers = [
|
restartTriggers = [
|
||||||
|
"${cfg.env.ldap}"
|
||||||
"${cfg.env.discord}"
|
"${cfg.env.discord}"
|
||||||
"${cfg.env.mail}"
|
"${cfg.env.mail}"
|
||||||
"${cfg.env.wolves}"
|
"${cfg.env.wolves}"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
} // serviceGenerator scripts;
|
||||||
// serviceGenerator scripts;
|
|
||||||
|
|
||||||
# timers to run the above services
|
# timers to run the above services
|
||||||
systemd.timers = timerGenerator scripts;
|
systemd.timers = timerGenerator scripts;
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
BIN
media/setup_user_01.png
(Stored with Git LFS)
BIN
media/setup_user_01.png
(Stored with Git LFS)
Binary file not shown.
BIN
media/setup_user_02.png
(Stored with Git LFS)
BIN
media/setup_user_02.png
(Stored with Git LFS)
Binary file not shown.
BIN
media/setup_user_03.png
(Stored with Git LFS)
BIN
media/setup_user_03.png
(Stored with Git LFS)
Binary file not shown.
|
@ -1,2 +0,0 @@
|
||||||
[toolchain]
|
|
||||||
channel = "1.80"
|
|
|
@ -1,25 +0,0 @@
|
||||||
use skynet_discord_bot::{db_init, get_config, get_minecraft_config, update_server, whitelist_wipe};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
let config = get_config();
|
|
||||||
let db = match db_init(&config).await {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(_) => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let servers = get_minecraft_config(&db).await;
|
|
||||||
let mut wiped = HashSet::new();
|
|
||||||
|
|
||||||
for server in &servers {
|
|
||||||
// wipe whitelist first
|
|
||||||
if !wiped.contains(&server.minecraft) {
|
|
||||||
whitelist_wipe(&server.minecraft, &config.discord_token_minecraft).await;
|
|
||||||
// add it to teh done list so its not done again
|
|
||||||
wiped.insert(&server.minecraft);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_server(&server.minecraft, &db, &server.discord, &config).await;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ use serenity::{
|
||||||
model::gateway::{GatewayIntents, Ready},
|
model::gateway::{GatewayIntents, Ready},
|
||||||
Client,
|
Client,
|
||||||
};
|
};
|
||||||
use skynet_discord_bot::{db_init, get_config, get_server_config_bulk, set_roles, Config, DataBase};
|
use skynet_discord_bot::{db_init, get_config, get_server_config_bulk, set_roles::update_server, Config, DataBase};
|
||||||
use std::{process, sync::Arc};
|
use std::{process, sync::Arc};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
@ -59,6 +59,6 @@ async fn bulk_check(ctx: Arc<Context>) {
|
||||||
let db = db_lock.read().await;
|
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 {
|
||||||
set_roles::update_server(&ctx, &server_config, &[], &[]).await;
|
update_server(&ctx, &server_config, &[], &vec![]).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,46 @@ use serenity::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use skynet_discord_bot::get_data::get_wolves;
|
use skynet_discord_bot::get_data::get_wolves;
|
||||||
use skynet_discord_bot::{get_server_config, is_admin, set_roles::update_server, DataBase, Servers};
|
use skynet_discord_bot::{get_server_config, set_roles::update_server, DataBase, Servers};
|
||||||
use sqlx::{Error, Pool, Sqlite};
|
use sqlx::{Error, Pool, Sqlite};
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
||||||
// check if user has high enough permisssions
|
// check if user has high enough permisssions
|
||||||
if let Some(msg) = is_admin(command, ctx).await {
|
let mut admin = false;
|
||||||
return msg;
|
|
||||||
|
let g_id = match command.guild_id {
|
||||||
|
None => return "Not in a server".to_string(),
|
||||||
|
Some(x) => x,
|
||||||
|
};
|
||||||
|
|
||||||
|
let roles_server = g_id.roles(&ctx.http).await.unwrap_or_default();
|
||||||
|
|
||||||
|
if let Ok(member) = g_id.member(&ctx.http, command.user.id).await {
|
||||||
|
if let Some(permissions) = member.permissions {
|
||||||
|
if permissions.administrator() {
|
||||||
|
admin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for role_id in member.roles {
|
||||||
|
if admin {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Some(role) = roles_server.get(&role_id) {
|
||||||
|
if role.permissions.administrator() {
|
||||||
|
admin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !admin {
|
||||||
|
return "Administrator permission required".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let api_key = if let CommandDataOptionValue::String(key) = command
|
let api_key = if let CommandDataOptionValue::String(key) = command
|
||||||
.data
|
.data
|
||||||
.options
|
.options
|
||||||
.first()
|
.get(0)
|
||||||
.expect("Expected user option")
|
.expect("Expected user option")
|
||||||
.resolved
|
.resolved
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -39,60 +66,18 @@ pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> Stri
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Expected role object")
|
.expect("Expected role object")
|
||||||
{
|
{
|
||||||
role.id.to_owned()
|
Some(role.id.to_owned())
|
||||||
} else {
|
} else {
|
||||||
return "Please provide a valid role for ``Role Current``".to_string();
|
return "Please provide a valid role for ``Role Current``".to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut role_past = None;
|
let mut role_past = None;
|
||||||
if let Some(x) = command.data.options.get(5) {
|
if let Some(x) = command.data.options.get(2) {
|
||||||
if let Some(CommandDataOptionValue::Role(role)) = &x.resolved {
|
if let Some(CommandDataOptionValue::Role(role)) = &x.resolved {
|
||||||
role_past = Some(role.id.to_owned());
|
role_past = Some(role.id.to_owned());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let bot_channel_id = if let CommandDataOptionValue::Channel(channel) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.get(2)
|
|
||||||
.expect("Expected channel option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected channel object")
|
|
||||||
{
|
|
||||||
channel.id.to_owned()
|
|
||||||
} else {
|
|
||||||
return "Please provide a valid channel for ``Bot Channel``".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
let server_name = if let CommandDataOptionValue::String(name) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.get(3)
|
|
||||||
.expect("Expected Server Name option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected Server Name object")
|
|
||||||
{
|
|
||||||
name
|
|
||||||
} else {
|
|
||||||
&"UL Computer Society".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let wolves_link = if let CommandDataOptionValue::String(wolves) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.get(4)
|
|
||||||
.expect("Expected Wolves Link option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected Server Name object")
|
|
||||||
{
|
|
||||||
wolves
|
|
||||||
} else {
|
|
||||||
&"https://ulwolves.ie/society/computer".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let db_lock = {
|
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()
|
||||||
|
@ -106,9 +91,6 @@ pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> Stri
|
||||||
role_current,
|
role_current,
|
||||||
member_past: 0,
|
member_past: 0,
|
||||||
member_current: 0,
|
member_current: 0,
|
||||||
bot_channel_id,
|
|
||||||
server_name: server_name.to_owned(),
|
|
||||||
wolves_link: wolves_link.to_string(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match add_server(&db, ctx, &server_data).await {
|
match add_server(&db, ctx, &server_data).await {
|
||||||
|
@ -140,27 +122,6 @@ pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicatio
|
||||||
.kind(CommandOptionType::Role)
|
.kind(CommandOptionType::Role)
|
||||||
.required(true)
|
.required(true)
|
||||||
})
|
})
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("bot_channel")
|
|
||||||
.description("Safe space for folks to use the bot commands.")
|
|
||||||
.kind(CommandOptionType::Channel)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("server_name")
|
|
||||||
.description("Name of the Discord Server.")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("wolves_link")
|
|
||||||
.description("Link to the Club/Society on UL Wolves.")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
.create_option(|option| {
|
.create_option(|option| {
|
||||||
option
|
option
|
||||||
.name("role_past")
|
.name("role_past")
|
||||||
|
@ -173,20 +134,18 @@ pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicatio
|
||||||
async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Result<Option<Servers>, Error> {
|
async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Result<Option<Servers>, Error> {
|
||||||
let existing = get_server_config(db, &server.server).await;
|
let existing = get_server_config(db, &server.server).await;
|
||||||
let role_past = server.role_past.map(|x| *x.as_u64() as i64);
|
let role_past = server.role_past.map(|x| *x.as_u64() as i64);
|
||||||
|
let role_current = server.role_current.map(|x| *x.as_u64() as i64);
|
||||||
|
|
||||||
let insert = sqlx::query_as::<_, Servers>(
|
let insert = sqlx::query_as::<_, Servers>(
|
||||||
"
|
"
|
||||||
INSERT OR REPLACE INTO servers (server, wolves_api, role_past, role_current, bot_channel_id, server_name, wolves_link)
|
INSERT OR REPLACE INTO servers (server, wolves_api, role_past, role_current)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
VALUES (?1, ?2, ?3, ?4)
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(*server.server.as_u64() as i64)
|
.bind(*server.server.as_u64() as i64)
|
||||||
.bind(&server.wolves_api)
|
.bind(&server.wolves_api)
|
||||||
.bind(role_past)
|
.bind(role_past)
|
||||||
.bind(*server.role_current.as_u64() as i64)
|
.bind(role_current)
|
||||||
.bind(*server.bot_channel_id.as_u64() as i64)
|
|
||||||
.bind(&server.server_name)
|
|
||||||
.bind(&server.wolves_link)
|
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -201,7 +160,7 @@ async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Resul
|
||||||
if x.role_current != server.role_current {
|
if x.role_current != server.role_current {
|
||||||
result.0 = true;
|
result.0 = true;
|
||||||
result.1 = true;
|
result.1 = true;
|
||||||
result.2 = Some(x.role_current);
|
result.2 = x.role_current;
|
||||||
}
|
}
|
||||||
if x.role_past != server.role_past {
|
if x.role_past != server.role_past {
|
||||||
result.0 = true;
|
result.0 = true;
|
||||||
|
@ -224,7 +183,7 @@ async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Resul
|
||||||
if past_remove {
|
if past_remove {
|
||||||
roles_remove.push(past_role)
|
roles_remove.push(past_role)
|
||||||
}
|
}
|
||||||
update_server(ctx, server, &roles_remove, &[]).await;
|
update_server(ctx, server, &roles_remove, &vec![]).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
insert
|
insert
|
||||||
|
|
|
@ -1,311 +0,0 @@
|
||||||
use lettre::{
|
|
||||||
message::{header, MultiPart, SinglePart},
|
|
||||||
transport::smtp::{self, authentication::Credentials},
|
|
||||||
Message, SmtpTransport, Transport,
|
|
||||||
};
|
|
||||||
use maud::html;
|
|
||||||
use serenity::{
|
|
||||||
builder::CreateApplicationCommand,
|
|
||||||
client::Context,
|
|
||||||
model::{
|
|
||||||
application::interaction::application_command::ApplicationCommandInteraction,
|
|
||||||
id::UserId,
|
|
||||||
prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use skynet_discord_bot::{random_string, Config, DataBase};
|
|
||||||
use sqlx::{Pool, Sqlite};
|
|
||||||
|
|
||||||
pub mod link {
|
|
||||||
use super::*;
|
|
||||||
use serenity::model::id::GuildId;
|
|
||||||
use skynet_discord_bot::Committee;
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
let committee_server = GuildId(1220150752656363520);
|
|
||||||
match command.guild_id {
|
|
||||||
None => {
|
|
||||||
return "Not in correct discord server.".to_string();
|
|
||||||
}
|
|
||||||
Some(x) => {
|
|
||||||
if x != committee_server {
|
|
||||||
return "Not in correct discord server.".to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let option = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.first()
|
|
||||||
.expect("Expected email option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected email object");
|
|
||||||
|
|
||||||
let email = if let CommandDataOptionValue::String(email) = option {
|
|
||||||
email.trim()
|
|
||||||
} else {
|
|
||||||
return "Please provide a valid committee email.".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
// fail early
|
|
||||||
if !email.ends_with("@ulwolves.ie") {
|
|
||||||
return "Please use a @ulwolves.ie address you have access to.".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
let db_lock = {
|
|
||||||
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;
|
|
||||||
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
|
|
||||||
};
|
|
||||||
let config = config_lock.read().await;
|
|
||||||
|
|
||||||
if get_server_member_discord(&db, &command.user.id).await.is_some() {
|
|
||||||
return "Already linked".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if get_verify_from_db(&db, &command.user.id).await.is_some() {
|
|
||||||
return "Linking already in process, please check email.".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a auth key
|
|
||||||
let auth = random_string(20);
|
|
||||||
match send_mail(&config, email, &auth, &command.user.name) {
|
|
||||||
Ok(_) => match save_to_db(&db, email, &auth, &command.user.id).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
return format!("Unable to save to db {} {e:?}", email);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
return format!("Unable to send mail to {} {e:?}", email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command
|
|
||||||
.name("link_committee")
|
|
||||||
.description("Verify you are a committee member")
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("email")
|
|
||||||
.description("UL Wolves Committee Email")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Committee> {
|
|
||||||
sqlx::query_as::<_, Committee>(
|
|
||||||
r#"
|
|
||||||
SELECT *
|
|
||||||
FROM committee
|
|
||||||
WHERE discord = ?
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(*user.as_u64() as i64)
|
|
||||||
.fetch_one(db)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_mail(config: &Config, mail: &str, auth: &str, user: &str) -> Result<smtp::response::Response, smtp::Error> {
|
|
||||||
let sender = format!("UL Computer Society <{}>", &config.mail_user);
|
|
||||||
|
|
||||||
// Create the html we want to send.
|
|
||||||
let html = html! {
|
|
||||||
head {
|
|
||||||
title { "Hello from Skynet!" }
|
|
||||||
style type="text/css" {
|
|
||||||
"h2, h4 { font-family: Arial, Helvetica, sans-serif; }"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div style="display: flex; flex-direction: column; align-items: center;" {
|
|
||||||
h2 { "Hello from Skynet!" }
|
|
||||||
// Substitute in the name of our recipient.
|
|
||||||
p { "Hi " (user) "," }
|
|
||||||
p {
|
|
||||||
"Please use " pre { "/verify_committee code: " (auth)} " to verify your discord account."
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
"Skynet Team"
|
|
||||||
br;
|
|
||||||
"UL Computer Society"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let body_text = format!(
|
|
||||||
r#"
|
|
||||||
Hi {user}
|
|
||||||
|
|
||||||
Please use "/verify_committee code: {auth}" to verify your discord account.
|
|
||||||
|
|
||||||
Skynet Team
|
|
||||||
UL Computer Society
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build the message.
|
|
||||||
let email = Message::builder()
|
|
||||||
.from(sender.parse().unwrap())
|
|
||||||
.to(mail.parse().unwrap())
|
|
||||||
.subject("Skynet-Discord: Link Committee.")
|
|
||||||
.multipart(
|
|
||||||
// This is composed of two parts.
|
|
||||||
// also helps not trip spam settings (uneven number of url's
|
|
||||||
MultiPart::alternative()
|
|
||||||
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text))
|
|
||||||
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())),
|
|
||||||
)
|
|
||||||
.expect("failed to build email");
|
|
||||||
|
|
||||||
let creds = Credentials::new(config.mail_user.clone(), config.mail_pass.clone());
|
|
||||||
|
|
||||||
// Open a remote connection to gmail using STARTTLS
|
|
||||||
let mailer = SmtpTransport::starttls_relay(&config.mail_smtp)?.credentials(creds).build();
|
|
||||||
|
|
||||||
// Send the email
|
|
||||||
mailer.send(&email)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_verify_from_db(db: &Pool<Sqlite>, user: &UserId) -> Option<Committee> {
|
|
||||||
sqlx::query_as::<_, Committee>(
|
|
||||||
r#"
|
|
||||||
SELECT *
|
|
||||||
FROM committee
|
|
||||||
WHERE discord = ?
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(*user.as_u64() as i64)
|
|
||||||
.fetch_one(db)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn save_to_db(db: &Pool<Sqlite>, email: &str, auth: &str, user: &UserId) -> Result<Option<Committee>, sqlx::Error> {
|
|
||||||
sqlx::query_as::<_, Committee>(
|
|
||||||
"
|
|
||||||
INSERT INTO committee (email, discord, auth_code)
|
|
||||||
VALUES (?1, ?2, ?3)
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(email.to_owned())
|
|
||||||
.bind(*user.as_u64() as i64)
|
|
||||||
.bind(auth.to_owned())
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod verify {
|
|
||||||
use super::*;
|
|
||||||
use crate::commands::committee::link::get_verify_from_db;
|
|
||||||
use serenity::model::id::{GuildId, RoleId};
|
|
||||||
use serenity::model::user::User;
|
|
||||||
use skynet_discord_bot::Committee;
|
|
||||||
use sqlx::Error;
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
let committee_server = GuildId(1220150752656363520);
|
|
||||||
match command.guild_id {
|
|
||||||
None => {
|
|
||||||
return "Not in correct discord server.".to_string();
|
|
||||||
}
|
|
||||||
Some(x) => {
|
|
||||||
if x != committee_server {
|
|
||||||
return "Not in correct discord server.".to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let db_lock = {
|
|
||||||
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_committee
|
|
||||||
let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await {
|
|
||||||
x
|
|
||||||
} else {
|
|
||||||
return "Please use /link_committee first".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
let option = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.first()
|
|
||||||
.expect("Expected code option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected code object");
|
|
||||||
|
|
||||||
let code = if let CommandDataOptionValue::String(code) = option {
|
|
||||||
code
|
|
||||||
} else {
|
|
||||||
return "Please provide a verification code".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
if &details.auth_code != code {
|
|
||||||
return "Invalid verification code".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
match set_discord(&db, &command.user.id).await {
|
|
||||||
Ok(_) => {
|
|
||||||
// get teh right roles for the user
|
|
||||||
set_server_roles(&command.user, ctx).await;
|
|
||||||
"Discord username linked to Wolves for committee".to_string()
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
"Failed to save, please try /link_committee again".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command
|
|
||||||
.name("verify_committee")
|
|
||||||
.description("Verify Wolves Committee Email")
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("code")
|
|
||||||
.description("Code from verification email")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_discord(db: &Pool<Sqlite>, discord: &UserId) -> Result<Option<Committee>, Error> {
|
|
||||||
sqlx::query_as::<_, Committee>(
|
|
||||||
"
|
|
||||||
UPDATE committee
|
|
||||||
SET committee = 1
|
|
||||||
WHERE discord = ?
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*discord.as_u64() as i64)
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_server_roles(discord: &User, ctx: &Context) {
|
|
||||||
let committee_server = GuildId(1220150752656363520);
|
|
||||||
if let Ok(mut member) = committee_server.member(&ctx.http, &discord.id).await {
|
|
||||||
let committee_member = RoleId(1226602779968274573);
|
|
||||||
if let Err(e) = member.add_role(&ctx, committee_member).await {
|
|
||||||
println!("{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ use serenity::{
|
||||||
};
|
};
|
||||||
use skynet_discord_bot::{get_now_iso, random_string, Config, DataBase, Wolves, WolvesVerify};
|
use skynet_discord_bot::{get_now_iso, random_string, Config, DataBase, Wolves, WolvesVerify};
|
||||||
use sqlx::{Pool, Sqlite};
|
use sqlx::{Pool, Sqlite};
|
||||||
pub mod link {
|
pub(crate) mod link {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
||||||
|
@ -44,7 +44,7 @@ pub mod link {
|
||||||
let option = command
|
let option = command
|
||||||
.data
|
.data
|
||||||
.options
|
.options
|
||||||
.first()
|
.get(0)
|
||||||
.expect("Expected email option")
|
.expect("Expected email option")
|
||||||
.resolved
|
.resolved
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -59,7 +59,7 @@ pub mod link {
|
||||||
// check if email exists
|
// check if email exists
|
||||||
let details = match get_server_member_email(&db, email).await {
|
let details = match get_server_member_email(&db, email).await {
|
||||||
None => {
|
None => {
|
||||||
return "Please check it matches (including case) your preferred contact on https://ulwolves.ie/memberships/profile and that you are fully paid up.".to_string()
|
return "Please check it is your preferred contact on https://ulwolves.ie/memberships/profile and that you are fully paid up.".to_string()
|
||||||
}
|
}
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
};
|
};
|
||||||
|
@ -87,12 +87,12 @@ pub mod link {
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
||||||
command
|
command
|
||||||
.name("link_wolves")
|
.name("link")
|
||||||
.description("Set Wolves Email")
|
.description("Set Wolves Email")
|
||||||
.create_option(|option| option.name("email").description("UL Wolves Email").kind(CommandOptionType::String).required(true))
|
.create_option(|option| option.name("email").description("UL Wolves Email").kind(CommandOptionType::String).required(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> {
|
async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> {
|
||||||
sqlx::query_as::<_, Wolves>(
|
sqlx::query_as::<_, Wolves>(
|
||||||
r#"
|
r#"
|
||||||
SELECT *
|
SELECT *
|
||||||
|
@ -133,7 +133,7 @@ pub mod link {
|
||||||
"h2, h4 { font-family: Arial, Helvetica, sans-serif; }"
|
"h2, h4 { font-family: Arial, Helvetica, sans-serif; }"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div style="display: flex; flex-direction: column; align-items: center;" {
|
||||||
h2 { "Hello from Skynet!" }
|
h2 { "Hello from Skynet!" }
|
||||||
// Substitute in the name of our recipient.
|
// Substitute in the name of our recipient.
|
||||||
p { "Hi " (user) "," }
|
p { "Hi " (user) "," }
|
||||||
|
@ -184,7 +184,7 @@ pub mod link {
|
||||||
let creds = Credentials::new(config.mail_user.clone(), config.mail_pass.clone());
|
let creds = Credentials::new(config.mail_user.clone(), config.mail_pass.clone());
|
||||||
|
|
||||||
// Open a remote connection to gmail using STARTTLS
|
// Open a remote connection to gmail using STARTTLS
|
||||||
let mailer = SmtpTransport::starttls_relay(&config.mail_smtp)?.credentials(creds).build();
|
let mailer = SmtpTransport::starttls_relay(&config.mail_smtp).unwrap().credentials(creds).build();
|
||||||
|
|
||||||
// Send the email
|
// Send the email
|
||||||
mailer.send(&email)
|
mailer.send(&email)
|
||||||
|
@ -234,7 +234,7 @@ pub mod link {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod verify {
|
pub(crate) mod verify {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::commands::link_email::link::{db_pending_clear_expired, get_verify_from_db};
|
use crate::commands::link_email::link::{db_pending_clear_expired, get_verify_from_db};
|
||||||
use serenity::model::user::User;
|
use serenity::model::user::User;
|
||||||
|
@ -248,17 +248,17 @@ pub mod verify {
|
||||||
};
|
};
|
||||||
let db = db_lock.read().await;
|
let db = db_lock.read().await;
|
||||||
|
|
||||||
// check if user has used /link_wolves
|
// check if user has used /link
|
||||||
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 {
|
||||||
x
|
x
|
||||||
} else {
|
} else {
|
||||||
return "Please use /link_wolves first".to_string();
|
return "Please use /link first".to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
let option = command
|
let option = command
|
||||||
.data
|
.data
|
||||||
.options
|
.options
|
||||||
.first()
|
.get(0)
|
||||||
.expect("Expected code option")
|
.expect("Expected code option")
|
||||||
.resolved
|
.resolved
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -286,7 +286,7 @@ pub mod verify {
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{:?}", e);
|
println!("{:?}", e);
|
||||||
"Failed to save, please try /link_wolves again".to_string()
|
"Failed to save, please try /link again".to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -352,8 +352,10 @@ pub mod verify {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !member.roles.contains(&role_current) {
|
if let Some(role) = &role_current {
|
||||||
roles.push(role_current.to_owned());
|
if !member.roles.contains(role) {
|
||||||
|
roles.push(role.to_owned());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = member.add_roles(&ctx, &roles).await {
|
if let Err(e) = member.add_roles(&ctx, &roles).await {
|
||||||
|
|
|
@ -1,334 +0,0 @@
|
||||||
use serenity::{
|
|
||||||
builder::CreateApplicationCommand,
|
|
||||||
client::Context,
|
|
||||||
model::{
|
|
||||||
application::interaction::application_command::ApplicationCommandInteraction,
|
|
||||||
prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use skynet_discord_bot::DataBase;
|
|
||||||
use sqlx::{Pool, Sqlite};
|
|
||||||
|
|
||||||
pub(crate) mod user {
|
|
||||||
use super::*;
|
|
||||||
pub(crate) mod add {
|
|
||||||
use super::*;
|
|
||||||
use crate::commands::link_email::link::get_server_member_discord;
|
|
||||||
use serenity::model::id::UserId;
|
|
||||||
use skynet_discord_bot::{whitelist_update, Config, Minecraft, Wolves};
|
|
||||||
use sqlx::Error;
|
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command.name("link_minecraft").description("Link your minecraft account").create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("minecraft-username")
|
|
||||||
.description("Your Minecraft username")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
let db_lock = {
|
|
||||||
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;
|
|
||||||
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
|
|
||||||
};
|
|
||||||
let config = config_lock.read().await;
|
|
||||||
|
|
||||||
// user has to have previously linked with wolves
|
|
||||||
if get_server_member_discord(&db, &command.user.id).await.is_none() {
|
|
||||||
return "Not linked with wolves, please use ``/link_wolves`` with your wolves email.".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
let username = if let CommandDataOptionValue::String(username) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.first()
|
|
||||||
.expect("Expected username option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected username object")
|
|
||||||
{
|
|
||||||
username.trim()
|
|
||||||
} else {
|
|
||||||
return "Please provide a valid username".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
// insert the username into the database
|
|
||||||
match add_minecraft(&db, &command.user.id, username).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
dbg!("{:?}", e);
|
|
||||||
return format!("Failure to minecraft username {:?}", username);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a list of servers that the user is a member of
|
|
||||||
if let Ok(servers) = get_servers(&db, &command.user.id).await {
|
|
||||||
for server in servers {
|
|
||||||
whitelist_update(&vec![username.to_string()], &server.minecraft, &config.discord_token_minecraft).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"Added/Updated minecraft_user info".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_minecraft(db: &Pool<Sqlite>, user: &UserId, minecraft: &str) -> Result<Option<Wolves>, Error> {
|
|
||||||
sqlx::query_as::<_, Wolves>(
|
|
||||||
"
|
|
||||||
UPDATE wolves
|
|
||||||
SET minecraft = ?2
|
|
||||||
WHERE discord = ?1;
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*user.as_u64() as i64)
|
|
||||||
.bind(minecraft)
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_servers(db: &Pool<Sqlite>, discord: &UserId) -> Result<Vec<Minecraft>, Error> {
|
|
||||||
sqlx::query_as::<_, Minecraft>(
|
|
||||||
"
|
|
||||||
SELECT minecraft.*
|
|
||||||
FROM minecraft
|
|
||||||
JOIN (
|
|
||||||
SELECT server
|
|
||||||
FROM server_members
|
|
||||||
JOIN wolves USING (id_wolves)
|
|
||||||
WHERE discord = ?1
|
|
||||||
) sub on minecraft.server_discord = sub.server
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*discord.as_u64() as i64)
|
|
||||||
.fetch_all(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod server {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub(crate) mod add {
|
|
||||||
use serenity::model::id::GuildId;
|
|
||||||
use sqlx::Error;
|
|
||||||
// this is to managfe the server side of commands related to minecraft
|
|
||||||
use super::*;
|
|
||||||
use skynet_discord_bot::{is_admin, update_server, Config, Minecraft};
|
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command.name("minecraft_add").description("Add a minecraft server").create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("server_id")
|
|
||||||
.description("ID of the Minecraft server hosted by the Computer Society")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
// check if user has high enough permisssions
|
|
||||||
if let Some(msg) = is_admin(command, ctx).await {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
let g_id = match command.guild_id {
|
|
||||||
None => return "Not in a server".to_string(),
|
|
||||||
Some(x) => x,
|
|
||||||
};
|
|
||||||
|
|
||||||
let server_minecraft = if let CommandDataOptionValue::String(id) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.first()
|
|
||||||
.expect("Expected server_id option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected server_id object")
|
|
||||||
{
|
|
||||||
id.to_owned()
|
|
||||||
} else {
|
|
||||||
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 = db_lock.read().await;
|
|
||||||
|
|
||||||
match add_server(&db, &g_id, &server_minecraft).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
update_server(&server_minecraft, &db, &g_id, &config).await;
|
|
||||||
|
|
||||||
"Added/Updated minecraft_server info".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_server(db: &Pool<Sqlite>, discord: &GuildId, minecraft: &str) -> Result<Option<Minecraft>, Error> {
|
|
||||||
sqlx::query_as::<_, Minecraft>(
|
|
||||||
"
|
|
||||||
INSERT OR REPLACE INTO minecraft (server_discord, server_minecraft)
|
|
||||||
VALUES (?1, ?2)
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*discord.as_u64() as i64)
|
|
||||||
.bind(minecraft)
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod list {
|
|
||||||
use serenity::builder::CreateApplicationCommand;
|
|
||||||
use serenity::client::Context;
|
|
||||||
use serenity::model::prelude::application_command::ApplicationCommandInteraction;
|
|
||||||
use skynet_discord_bot::{get_minecraft_config_server, is_admin, server_information, Config, DataBase};
|
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command.name("minecraft_list").description("List your minecraft servers")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
if let Some(msg) = is_admin(command, ctx).await {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
let g_id = match command.guild_id {
|
|
||||||
None => return "Not in a server".to_string(),
|
|
||||||
Some(x) => x,
|
|
||||||
};
|
|
||||||
|
|
||||||
let db_lock = {
|
|
||||||
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;
|
|
||||||
|
|
||||||
if servers.is_empty() {
|
|
||||||
return "No minecraft servers, use /minecraft_add to add one".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
let mut result = "Server Information:\n".to_string();
|
|
||||||
for server in get_minecraft_config_server(&db, g_id).await {
|
|
||||||
if let Some(x) = server_information(&server.minecraft, &config.discord_token_minecraft).await {
|
|
||||||
result.push_str(&format!(
|
|
||||||
r#"
|
|
||||||
Name: {name}
|
|
||||||
ID: {id}
|
|
||||||
Online: {online}
|
|
||||||
Info: {description}
|
|
||||||
Link: <http://panel.games.skynet.ie/server/{id}>
|
|
||||||
"#,
|
|
||||||
name = &x.attributes.name,
|
|
||||||
online = !x.attributes.is_suspended,
|
|
||||||
description = &x.attributes.description,
|
|
||||||
id = &x.attributes.identifier
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod delete {
|
|
||||||
use serenity::builder::CreateApplicationCommand;
|
|
||||||
use serenity::client::Context;
|
|
||||||
use serenity::model::application::command::CommandOptionType;
|
|
||||||
use serenity::model::id::GuildId;
|
|
||||||
use serenity::model::prelude::application_command::{ApplicationCommandInteraction, CommandDataOptionValue};
|
|
||||||
use skynet_discord_bot::{is_admin, DataBase, Minecraft};
|
|
||||||
use sqlx::{Error, Pool, Sqlite};
|
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command.name("minecraft_delete").description("Delete a minecraft server").create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("server_id")
|
|
||||||
.description("ID of the Minecraft server hosted by the Computer Society")
|
|
||||||
.kind(CommandOptionType::String)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
// check if user has high enough permisssions
|
|
||||||
if let Some(msg) = is_admin(command, ctx).await {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
let g_id = match command.guild_id {
|
|
||||||
None => return "Not in a server".to_string(),
|
|
||||||
Some(x) => x,
|
|
||||||
};
|
|
||||||
|
|
||||||
let server_minecraft = if let CommandDataOptionValue::String(id) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.first()
|
|
||||||
.expect("Expected server_id option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected server_id object")
|
|
||||||
{
|
|
||||||
id.to_owned()
|
|
||||||
} else {
|
|
||||||
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 = db_lock.read().await;
|
|
||||||
|
|
||||||
match server_remove(&db, &g_id, &server_minecraft).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no need to clear teh whitelist as it will be reset within 24hr anyways
|
|
||||||
|
|
||||||
"Removed minecraft_server info".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn server_remove(db: &Pool<Sqlite>, discord: &GuildId, minecraft: &str) -> Result<Option<Minecraft>, Error> {
|
|
||||||
sqlx::query_as::<_, Minecraft>(
|
|
||||||
"
|
|
||||||
DELETE FROM minecraft
|
|
||||||
WHERE server_discord = ?1 AND server_minecraft = ?2
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*discord.as_u64() as i64)
|
|
||||||
.bind(minecraft)
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,2 @@
|
||||||
pub mod add_server;
|
pub mod add_server;
|
||||||
pub mod committee;
|
|
||||||
pub mod link_email;
|
pub mod link_email;
|
||||||
pub mod minecraft;
|
|
||||||
pub mod role_adder;
|
|
||||||
|
|
|
@ -1,238 +0,0 @@
|
||||||
use serenity::{
|
|
||||||
builder::CreateApplicationCommand,
|
|
||||||
client::Context,
|
|
||||||
model::{
|
|
||||||
application::interaction::application_command::ApplicationCommandInteraction,
|
|
||||||
prelude::{command::CommandOptionType, interaction::application_command::CommandDataOptionValue},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use skynet_discord_bot::{is_admin, DataBase, RoleAdder};
|
|
||||||
use sqlx::{Error, Pool, Sqlite};
|
|
||||||
|
|
||||||
pub mod edit {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub async fn run(command: &ApplicationCommandInteraction, ctx: &Context) -> String {
|
|
||||||
// check if user has high enough permisssions
|
|
||||||
if let Some(msg) = is_admin(command, ctx).await {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
let role_a = if let CommandDataOptionValue::Role(role) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.first()
|
|
||||||
.expect("Expected role option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected role object")
|
|
||||||
{
|
|
||||||
role.id.to_owned()
|
|
||||||
} else {
|
|
||||||
return "Please provide a valid role for ``Role Current``".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
let role_b = if let CommandDataOptionValue::Role(role) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.get(1)
|
|
||||||
.expect("Expected role option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected role object")
|
|
||||||
{
|
|
||||||
role.id.to_owned()
|
|
||||||
} else {
|
|
||||||
return "Please provide a valid role for ``Role Current``".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
let role_c = if let CommandDataOptionValue::Role(role) = command
|
|
||||||
.data
|
|
||||||
.options
|
|
||||||
.get(2)
|
|
||||||
.expect("Expected role option")
|
|
||||||
.resolved
|
|
||||||
.as_ref()
|
|
||||||
.expect("Expected role object")
|
|
||||||
{
|
|
||||||
role.id.to_owned()
|
|
||||||
} else {
|
|
||||||
return "Please provide a valid role for ``Role Current``".to_string();
|
|
||||||
};
|
|
||||||
|
|
||||||
if role_a == role_b {
|
|
||||||
return "Roles A and B must be different".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role_c == role_a) || (role_c == role_b) {
|
|
||||||
return "Role C cannot be same as A or B".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut delete = false;
|
|
||||||
|
|
||||||
if let Some(x) = command.data.options.get(3) {
|
|
||||||
let tmp = x.to_owned();
|
|
||||||
if let Some(CommandDataOptionValue::Boolean(z)) = tmp.resolved {
|
|
||||||
delete = z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let db_lock = {
|
|
||||||
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 {
|
|
||||||
server,
|
|
||||||
role_a,
|
|
||||||
role_b,
|
|
||||||
role_c,
|
|
||||||
};
|
|
||||||
|
|
||||||
match add_server(&db, &server_data, delete).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
return format!("Failure to insert into Servers {:?}", server_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut role_a_name = String::new();
|
|
||||||
let mut role_b_name = String::new();
|
|
||||||
let mut role_c_name = String::new();
|
|
||||||
|
|
||||||
if let Ok(x) = server.roles(&ctx).await {
|
|
||||||
if let Some(y) = x.get(&role_a) {
|
|
||||||
role_a_name = y.to_owned().name;
|
|
||||||
}
|
|
||||||
if let Some(y) = x.get(&role_b) {
|
|
||||||
role_b_name = y.to_owned().name;
|
|
||||||
}
|
|
||||||
if let Some(y) = x.get(&role_b) {
|
|
||||||
role_c_name = y.to_owned().name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if delete {
|
|
||||||
format!("Removed {} + {} = {}", role_a_name, role_b_name, role_c_name)
|
|
||||||
} else {
|
|
||||||
format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
|
|
||||||
command
|
|
||||||
.name("roles_adder")
|
|
||||||
.description("Combine roles together to an new one")
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("role_a")
|
|
||||||
.description("A role you want to add to Role B")
|
|
||||||
.kind(CommandOptionType::Role)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("role_b")
|
|
||||||
.description("A role you want to add to Role A")
|
|
||||||
.kind(CommandOptionType::Role)
|
|
||||||
.required(true)
|
|
||||||
})
|
|
||||||
.create_option(|option| option.name("role_c").description("Sum of A and B").kind(CommandOptionType::Role).required(true))
|
|
||||||
.create_option(|option| {
|
|
||||||
option
|
|
||||||
.name("delete")
|
|
||||||
.description("Delete this entry.")
|
|
||||||
.kind(CommandOptionType::Boolean)
|
|
||||||
.required(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn add_server(db: &Pool<Sqlite>, server: &RoleAdder, delete: bool) -> Result<Option<RoleAdder>, Error> {
|
|
||||||
if delete {
|
|
||||||
sqlx::query_as::<_, RoleAdder>(
|
|
||||||
"
|
|
||||||
DELETE FROM roles_adder
|
|
||||||
WHERE server = ?1 AND role_a = ?2 AND role_b = ?3 AND role_c = ?4
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*server.server.as_u64() as i64)
|
|
||||||
.bind(*server.role_a.as_u64() as i64)
|
|
||||||
.bind(*server.role_b.as_u64() as i64)
|
|
||||||
.bind(*server.role_c.as_u64() as i64)
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
sqlx::query_as::<_, RoleAdder>(
|
|
||||||
"
|
|
||||||
INSERT OR REPLACE INTO roles_adder (server, role_a, role_b, role_c)
|
|
||||||
VALUES (?1, ?2, ?3, ?4)
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.bind(*server.server.as_u64() as i64)
|
|
||||||
.bind(*server.role_a.as_u64() as i64)
|
|
||||||
.bind(*server.role_b.as_u64() as i64)
|
|
||||||
.bind(*server.role_c.as_u64() as i64)
|
|
||||||
.fetch_optional(db)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
pub mod list {}
|
|
||||||
|
|
||||||
pub mod tools {
|
|
||||||
use serenity::client::Context;
|
|
||||||
use serenity::model::guild::Member;
|
|
||||||
use skynet_discord_bot::RoleAdder;
|
|
||||||
use sqlx::{Pool, Sqlite};
|
|
||||||
|
|
||||||
pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, mut new_data: Member) {
|
|
||||||
// check if the role changed is part of the oens for this server
|
|
||||||
if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>(
|
|
||||||
r#"
|
|
||||||
SELECT *
|
|
||||||
FROM roles_adder
|
|
||||||
WHERE server = ?
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(*new_data.guild_id.as_u64() as i64)
|
|
||||||
.fetch_all(db)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
let mut roles_add = vec![];
|
|
||||||
let mut roles_remove = vec![];
|
|
||||||
|
|
||||||
for role_adder in role_adders {
|
|
||||||
// if the user has both A dnd B give them C
|
|
||||||
if new_data.roles.contains(&role_adder.role_a) && new_data.roles.contains(&role_adder.role_b) && !new_data.roles.contains(&role_adder.role_c)
|
|
||||||
{
|
|
||||||
roles_add.push(role_adder.role_c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the suer has C but not A or B remove C
|
|
||||||
if new_data.roles.contains(&role_adder.role_c)
|
|
||||||
&& (!new_data.roles.contains(&role_adder.role_a) || !new_data.roles.contains(&role_adder.role_b))
|
|
||||||
{
|
|
||||||
roles_remove.push(role_adder.role_c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !roles_add.is_empty() {
|
|
||||||
if let Err(e) = new_data.add_roles(&ctx, &roles_add).await {
|
|
||||||
println!("{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !roles_remove.is_empty() {
|
|
||||||
if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await {
|
|
||||||
println!("{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
380
src/lib.rs
380
src/lib.rs
|
@ -3,18 +3,15 @@ use serde::{Deserialize, Serialize};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
model::{
|
model::{
|
||||||
guild,
|
guild,
|
||||||
id::{ChannelId, GuildId, RoleId},
|
id::{GuildId, RoleId},
|
||||||
},
|
},
|
||||||
prelude::TypeMapKey,
|
prelude::TypeMapKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::set_roles::get_server_member_bulk;
|
|
||||||
use chrono::{Datelike, SecondsFormat, Utc};
|
use chrono::{Datelike, SecondsFormat, Utc};
|
||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::model::id::UserId;
|
use serenity::model::id::UserId;
|
||||||
use serenity::model::prelude::application_command::ApplicationCommandInteraction;
|
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow},
|
sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow},
|
||||||
Error, FromRow, Pool, Row, Sqlite,
|
Error, FromRow, Pool, Row, Sqlite,
|
||||||
|
@ -23,20 +20,18 @@ use std::{env, str::FromStr, sync::Arc};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
// manages where teh database is stored
|
pub skynet_server: GuildId,
|
||||||
|
pub ldap_api: String,
|
||||||
pub home: String,
|
pub home: String,
|
||||||
pub database: String,
|
pub database: String,
|
||||||
|
|
||||||
// tokens for discord and other API's
|
pub auth: String,
|
||||||
pub discord_token: String,
|
pub discord_token: String,
|
||||||
pub discord_token_minecraft: String,
|
|
||||||
|
|
||||||
// email settings
|
|
||||||
pub mail_smtp: String,
|
pub mail_smtp: String,
|
||||||
pub mail_user: String,
|
pub mail_user: String,
|
||||||
pub mail_pass: String,
|
pub mail_pass: String,
|
||||||
|
|
||||||
// wolves API base for clubs/socs
|
|
||||||
pub wolves_url: String,
|
pub wolves_url: String,
|
||||||
}
|
}
|
||||||
impl TypeMapKey for Config {
|
impl TypeMapKey for Config {
|
||||||
|
@ -53,8 +48,10 @@ pub fn get_config() -> Config {
|
||||||
|
|
||||||
// reasonable defaults
|
// reasonable defaults
|
||||||
let mut config = Config {
|
let mut config = Config {
|
||||||
|
skynet_server: Default::default(),
|
||||||
|
ldap_api: "https://api.account.skynet.ie".to_string(),
|
||||||
|
auth: "".to_string(),
|
||||||
discord_token: "".to_string(),
|
discord_token: "".to_string(),
|
||||||
discord_token_minecraft: "".to_string(),
|
|
||||||
|
|
||||||
home: ".".to_string(),
|
home: ".".to_string(),
|
||||||
database: "database.db".to_string(),
|
database: "database.db".to_string(),
|
||||||
|
@ -65,19 +62,26 @@ pub fn get_config() -> Config {
|
||||||
wolves_url: "".to_string(),
|
wolves_url: "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(x) = env::var("DATABASE_HOME") {
|
if let Ok(x) = env::var("LDAP_API") {
|
||||||
|
config.ldap_api = x.trim().to_string();
|
||||||
|
}
|
||||||
|
if let Ok(x) = env::var("SKYNET_SERVER") {
|
||||||
|
config.skynet_server = GuildId::from(str_to_num::<u64>(&x));
|
||||||
|
}
|
||||||
|
if let Ok(x) = env::var("HOME") {
|
||||||
config.home = x.trim().to_string();
|
config.home = x.trim().to_string();
|
||||||
}
|
}
|
||||||
if let Ok(x) = env::var("DATABASE") {
|
if let Ok(x) = env::var("DATABASE") {
|
||||||
config.database = x.trim().to_string();
|
config.database = x.trim().to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Ok(x) = env::var("LDAP_DISCORD_AUTH") {
|
||||||
|
config.auth = x.trim().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(x) = env::var("DISCORD_TOKEN") {
|
if let Ok(x) = env::var("DISCORD_TOKEN") {
|
||||||
config.discord_token = x.trim().to_string();
|
config.discord_token = x.trim().to_string();
|
||||||
}
|
}
|
||||||
if let Ok(x) = env::var("DISCORD_TOKEN_MINECRAFT") {
|
|
||||||
config.discord_token_minecraft = x.trim().to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(x) = env::var("EMAIL_SMTP") {
|
if let Ok(x) = env::var("EMAIL_SMTP") {
|
||||||
config.mail_smtp = x.trim().to_string();
|
config.mail_smtp = x.trim().to_string();
|
||||||
|
@ -96,6 +100,10 @@ pub fn get_config() -> Config {
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn str_to_num<T: FromStr + Default>(x: &str) -> T {
|
||||||
|
x.trim().parse::<T>().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct ServerMembers {
|
pub struct ServerMembers {
|
||||||
pub server: GuildId,
|
pub server: GuildId,
|
||||||
|
@ -202,39 +210,14 @@ impl<'r> FromRow<'r, SqliteRow> for WolvesVerify {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Committee {
|
|
||||||
pub email: String,
|
|
||||||
pub discord: UserId,
|
|
||||||
pub auth_code: String,
|
|
||||||
pub committee: i64,
|
|
||||||
}
|
|
||||||
impl<'r> FromRow<'r, SqliteRow> for Committee {
|
|
||||||
fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
|
|
||||||
let user_tmp: i64 = row.try_get("discord")?;
|
|
||||||
let discord = UserId::from(user_tmp as u64);
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
email: row.try_get("email")?,
|
|
||||||
discord,
|
|
||||||
auth_code: row.try_get("auth_code")?,
|
|
||||||
committee: row.try_get("committee")?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Servers {
|
pub struct Servers {
|
||||||
pub server: GuildId,
|
pub server: GuildId,
|
||||||
pub wolves_api: String,
|
pub wolves_api: String,
|
||||||
pub role_past: Option<RoleId>,
|
pub role_past: Option<RoleId>,
|
||||||
pub role_current: RoleId,
|
pub role_current: Option<RoleId>,
|
||||||
pub member_past: i64,
|
pub member_past: i64,
|
||||||
pub member_current: i64,
|
pub member_current: i64,
|
||||||
pub bot_channel_id: ChannelId,
|
|
||||||
// these can be removed in teh future with an API update
|
|
||||||
pub server_name: String,
|
|
||||||
pub wolves_link: String,
|
|
||||||
}
|
}
|
||||||
impl<'r> FromRow<'r, SqliteRow> for Servers {
|
impl<'r> FromRow<'r, SqliteRow> for Servers {
|
||||||
fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
|
fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
|
||||||
|
@ -254,14 +237,15 @@ impl<'r> FromRow<'r, SqliteRow> for Servers {
|
||||||
let role_current = match row.try_get("role_current") {
|
let role_current = match row.try_get("role_current") {
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
let tmp: i64 = x;
|
let tmp: i64 = x;
|
||||||
RoleId::from(tmp as u64)
|
if tmp == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(RoleId::from(tmp as u64))
|
||||||
}
|
}
|
||||||
_ => RoleId::from(0u64),
|
}
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let bot_channel_tmp: i64 = row.try_get("bot_channel_id")?;
|
|
||||||
let bot_channel_id = ChannelId::from(bot_channel_tmp as u64);
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
server,
|
server,
|
||||||
wolves_api: row.try_get("wolves_api")?,
|
wolves_api: row.try_get("wolves_api")?,
|
||||||
|
@ -269,61 +253,10 @@ impl<'r> FromRow<'r, SqliteRow> for Servers {
|
||||||
role_current,
|
role_current,
|
||||||
member_past: row.try_get("member_past")?,
|
member_past: row.try_get("member_past")?,
|
||||||
member_current: row.try_get("member_current")?,
|
member_current: row.try_get("member_current")?,
|
||||||
bot_channel_id,
|
|
||||||
server_name: row.try_get("server_name")?,
|
|
||||||
wolves_link: row.try_get("wolves_link")?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Minecraft {
|
|
||||||
pub discord: GuildId,
|
|
||||||
pub minecraft: String,
|
|
||||||
}
|
|
||||||
impl<'r> FromRow<'r, SqliteRow> for Minecraft {
|
|
||||||
fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
|
|
||||||
let server_tmp: i64 = row.try_get("server_discord")?;
|
|
||||||
let discord = GuildId::from(server_tmp as u64);
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
discord,
|
|
||||||
minecraft: row.try_get("server_minecraft")?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct RoleAdder {
|
|
||||||
pub server: GuildId,
|
|
||||||
pub role_a: RoleId,
|
|
||||||
pub role_b: RoleId,
|
|
||||||
pub role_c: RoleId,
|
|
||||||
}
|
|
||||||
impl<'r> FromRow<'r, SqliteRow> for RoleAdder {
|
|
||||||
fn from_row(row: &'r SqliteRow) -> Result<Self, Error> {
|
|
||||||
let server_tmp: i64 = row.try_get("server")?;
|
|
||||||
let server = GuildId::from(server_tmp as u64);
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
server,
|
|
||||||
role_a: get_role_from_row(row, "role_a"),
|
|
||||||
role_b: get_role_from_row(row, "role_b"),
|
|
||||||
role_c: get_role_from_row(row, "role_c"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_role_from_row(row: &SqliteRow, col: &str) -> RoleId {
|
|
||||||
match row.try_get(col) {
|
|
||||||
Ok(x) => {
|
|
||||||
let tmp: i64 = x;
|
|
||||||
RoleId(tmp as u64)
|
|
||||||
}
|
|
||||||
_ => RoleId::from(0u64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
|
pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
|
||||||
let database = format!("{}/{}", &config.home, &config.database);
|
let database = format!("{}/{}", &config.home, &config.database);
|
||||||
|
|
||||||
|
@ -336,8 +269,58 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// migrations are amazing!
|
sqlx::query(
|
||||||
sqlx::migrate!("./db/migrations").run(&pool).await?;
|
"CREATE TABLE IF NOT EXISTS wolves (
|
||||||
|
id_wolves integer PRIMARY KEY,
|
||||||
|
email text not null,
|
||||||
|
discord integer,
|
||||||
|
minecraft text
|
||||||
|
)",
|
||||||
|
)
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
sqlx::query("CREATE INDEX IF NOT EXISTS index_discord ON wolves (discord)").execute(&pool).await?;
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
"CREATE TABLE IF NOT EXISTS wolves_verify (
|
||||||
|
discord integer PRIMARY KEY,
|
||||||
|
email text not null,
|
||||||
|
auth_code text not null,
|
||||||
|
date_expiry text not null
|
||||||
|
)",
|
||||||
|
)
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
sqlx::query("CREATE INDEX IF NOT EXISTS index_date_expiry ON wolves_verify (date_expiry)")
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
"CREATE TABLE IF NOT EXISTS server_members (
|
||||||
|
server integer not null,
|
||||||
|
id_wolves integer not null,
|
||||||
|
expiry text not null,
|
||||||
|
PRIMARY KEY(server,id_wolves),
|
||||||
|
FOREIGN KEY (id_wolves) REFERENCES wolves (id_wolves)
|
||||||
|
)",
|
||||||
|
)
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
"CREATE TABLE IF NOT EXISTS servers (
|
||||||
|
server integer PRIMARY KEY,
|
||||||
|
wolves_api text not null,
|
||||||
|
role_past integer,
|
||||||
|
role_current integer,
|
||||||
|
member_past integer DEFAULT 0,
|
||||||
|
member_current integer DEFAULT 0
|
||||||
|
)",
|
||||||
|
)
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(pool)
|
Ok(pool)
|
||||||
}
|
}
|
||||||
|
@ -398,7 +381,7 @@ pub fn random_string(len: usize) -> String {
|
||||||
|
|
||||||
pub mod set_roles {
|
pub mod set_roles {
|
||||||
use super::*;
|
use super::*;
|
||||||
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: &Vec<UserId>) {
|
||||||
let db_lock = {
|
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()
|
||||||
|
@ -440,9 +423,11 @@ pub mod set_roles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !member.roles.contains(role_current) {
|
if let Some(role) = &role_current {
|
||||||
|
if !member.roles.contains(role) {
|
||||||
roles_set[1] += 1;
|
roles_set[1] += 1;
|
||||||
roles.push(role_current.to_owned());
|
roles.push(role.to_owned());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = member.add_roles(ctx, &roles).await {
|
if let Err(e) = member.add_roles(ctx, &roles).await {
|
||||||
|
@ -457,14 +442,16 @@ pub mod set_roles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if member.roles.contains(role_current) {
|
if let Some(role) = &role_current {
|
||||||
|
if member.roles.contains(role) {
|
||||||
roles_set[2] += 1;
|
roles_set[2] += 1;
|
||||||
// if theya 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).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);
|
||||||
|
@ -479,7 +466,7 @@ pub mod set_roles {
|
||||||
println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.as_u64(), roles_set[0], roles_set[1], roles_set[2]);
|
println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.as_u64(), roles_set[0], roles_set[1], roles_set[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_server_member_bulk(db: &Pool<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> {
|
async fn get_server_member_bulk(db: &Pool<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> {
|
||||||
sqlx::query_as::<_, ServerMembersWolves>(
|
sqlx::query_as::<_, ServerMembersWolves>(
|
||||||
r#"
|
r#"
|
||||||
SELECT *
|
SELECT *
|
||||||
|
@ -530,11 +517,10 @@ pub mod get_data {
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
struct WolvesResultUser {
|
struct WolvesResultUser {
|
||||||
committee: String,
|
committee: String,
|
||||||
member_id: String,
|
wolves_id: String,
|
||||||
first_name: String,
|
first_name: String,
|
||||||
last_name: String,
|
last_name: String,
|
||||||
contact_email: String,
|
contact_email: String,
|
||||||
opt_in_email: String,
|
|
||||||
student_id: Option<String>,
|
student_id: Option<String>,
|
||||||
note: Option<String>,
|
note: Option<String>,
|
||||||
expiry: String,
|
expiry: String,
|
||||||
|
@ -582,7 +568,7 @@ pub mod get_data {
|
||||||
// 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 user_to_update = vec![];
|
||||||
for user in get_wolves_sub(&config, wolves_api).await {
|
for user in get_wolves_sub(&config, wolves_api).await {
|
||||||
let id = user.member_id.parse::<u64>().unwrap_or_default();
|
let id = user.wolves_id.parse::<u64>().unwrap_or_default();
|
||||||
match existing.get(&(id as i64)) {
|
match existing.get(&(id as i64)) {
|
||||||
None => {
|
None => {
|
||||||
// user does not exist already, add everything
|
// user does not exist already, add everything
|
||||||
|
@ -659,7 +645,7 @@ pub mod get_data {
|
||||||
ON CONFLICT(id_wolves) DO UPDATE SET email = $2
|
ON CONFLICT(id_wolves) DO UPDATE SET email = $2
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(&user.member_id)
|
.bind(&user.wolves_id)
|
||||||
.bind(&user.contact_email)
|
.bind(&user.contact_email)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
@ -679,7 +665,7 @@ pub mod get_data {
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(*server.as_u64() as i64)
|
.bind(*server.as_u64() as i64)
|
||||||
.bind(&user.member_id)
|
.bind(&user.wolves_id)
|
||||||
.bind(&user.expiry)
|
.bind(&user.expiry)
|
||||||
.fetch_optional(db)
|
.fetch_optional(db)
|
||||||
.await
|
.await
|
||||||
|
@ -692,179 +678,3 @@ pub mod get_data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
For any time ye need to check if a user who calls a command has admin privlages
|
|
||||||
*/
|
|
||||||
pub async fn is_admin(command: &ApplicationCommandInteraction, ctx: &Context) -> Option<String> {
|
|
||||||
let mut admin = false;
|
|
||||||
|
|
||||||
let g_id = match command.guild_id {
|
|
||||||
None => return Some("Not in a server".to_string()),
|
|
||||||
Some(x) => x,
|
|
||||||
};
|
|
||||||
|
|
||||||
let roles_server = g_id.roles(&ctx.http).await.unwrap_or_default();
|
|
||||||
|
|
||||||
if let Ok(member) = g_id.member(&ctx.http, command.user.id).await {
|
|
||||||
if let Some(permissions) = member.permissions {
|
|
||||||
if permissions.administrator() {
|
|
||||||
admin = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for role_id in member.roles {
|
|
||||||
if admin {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if let Some(role) = roles_server.get(&role_id) {
|
|
||||||
if role.permissions.administrator() {
|
|
||||||
admin = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !admin {
|
|
||||||
Some("Administrator permission required".to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
loop through all members of server
|
|
||||||
get a list of folks with mc accounts that are members
|
|
||||||
and a list that arent members
|
|
||||||
*/
|
|
||||||
pub async fn update_server(server_id: &str, db: &Pool<Sqlite>, g_id: &GuildId, config: &Config) {
|
|
||||||
let mut usernames = vec![];
|
|
||||||
for member in get_server_member_bulk(db, g_id).await {
|
|
||||||
if let Some(x) = member.minecraft {
|
|
||||||
usernames.push(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !usernames.is_empty() {
|
|
||||||
whitelist_update(&usernames, server_id, &config.discord_token_minecraft).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn post<T: Serialize>(url: &str, bearer: &str, data: &T) {
|
|
||||||
match surf::post(url)
|
|
||||||
.header("Authorization", bearer)
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.header("Accept", "Application/vnd.pterodactyl.v1+json")
|
|
||||||
.body_json(&data)
|
|
||||||
{
|
|
||||||
Ok(req) => {
|
|
||||||
req.await.ok();
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
dbg!(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
pub struct ServerDetailsResSub {
|
|
||||||
pub identifier: String,
|
|
||||||
pub name: String,
|
|
||||||
pub description: String,
|
|
||||||
pub is_suspended: bool,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
pub struct ServerDetailsRes {
|
|
||||||
pub attributes: ServerDetailsResSub,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get<T: Serialize + DeserializeOwned>(url: &str, bearer: &str) -> Option<T> {
|
|
||||||
match surf::get(url)
|
|
||||||
.header("Authorization", bearer)
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.header("Accept", "Application/vnd.pterodactyl.v1+json")
|
|
||||||
.recv_json()
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(res) => Some(res),
|
|
||||||
Err(e) => {
|
|
||||||
dbg!(e);
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
struct BodyCommand {
|
|
||||||
command: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
struct BodyDelete {
|
|
||||||
root: String,
|
|
||||||
files: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn whitelist_update(add: &Vec<String>, server: &str, token: &str) {
|
|
||||||
let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}");
|
|
||||||
let bearer = format!("Bearer {token}");
|
|
||||||
|
|
||||||
for name in add {
|
|
||||||
let data = BodyCommand {
|
|
||||||
command: format!("whitelist add {name}"),
|
|
||||||
};
|
|
||||||
post(&format!("{url_base}/command"), &bearer, &data).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn whitelist_wipe(server: &str, token: &str) {
|
|
||||||
let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}");
|
|
||||||
let bearer = format!("Bearer {token}");
|
|
||||||
|
|
||||||
// delete whitelist
|
|
||||||
let deletion = BodyDelete {
|
|
||||||
root: "/".to_string(),
|
|
||||||
files: vec!["whitelist.json".to_string()],
|
|
||||||
};
|
|
||||||
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
|
|
||||||
post::<Vec<&str>>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await;
|
|
||||||
|
|
||||||
// reload the whitelist
|
|
||||||
let data = BodyCommand {
|
|
||||||
command: "whitelist reload".to_string(),
|
|
||||||
};
|
|
||||||
post(&format!("{url_base}/command"), &bearer, &data).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn server_information(server: &str, token: &str) -> Option<ServerDetailsRes> {
|
|
||||||
let url_base = format!("http://panel.games.skynet.ie/api/client/servers/{server}");
|
|
||||||
let bearer = format!("Bearer {token}");
|
|
||||||
get::<ServerDetailsRes>(&format!("{url_base}/"), &bearer).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_minecraft_config(db: &Pool<Sqlite>) -> Vec<Minecraft> {
|
|
||||||
sqlx::query_as::<_, Minecraft>(
|
|
||||||
r#"
|
|
||||||
SELECT *
|
|
||||||
FROM minecraft
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.fetch_all(db)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_minecraft_config_server(db: &Pool<Sqlite>, g_id: GuildId) -> Vec<Minecraft> {
|
|
||||||
sqlx::query_as::<_, Minecraft>(
|
|
||||||
r#"
|
|
||||||
SELECT *
|
|
||||||
FROM minecraft
|
|
||||||
WHERE server_discord = ?1
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(*g_id.as_u64() as i64)
|
|
||||||
.fetch_all(db)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
77
src/main.rs
77
src/main.rs
|
@ -1,28 +1,25 @@
|
||||||
pub mod commands;
|
mod commands;
|
||||||
|
|
||||||
use crate::commands::role_adder::tools::on_role_change;
|
|
||||||
use serenity::model::guild::Member;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
async_trait,
|
async_trait,
|
||||||
client::{Context, EventHandler},
|
client::{Context, EventHandler},
|
||||||
model::{
|
model::{
|
||||||
application::{command::Command, interaction::Interaction},
|
application::{command::Command, interaction::Interaction},
|
||||||
gateway::{GatewayIntents, Ready},
|
gateway::{GatewayIntents, Ready},
|
||||||
prelude::Activity,
|
guild,
|
||||||
user::OnlineStatus,
|
|
||||||
},
|
},
|
||||||
Client,
|
Client,
|
||||||
};
|
};
|
||||||
use skynet_discord_bot::{db_init, get_config, get_server_config, get_server_member, Config, DataBase};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use skynet_discord_bot::{db_init, get_config, get_server_config, get_server_member, Config, DataBase};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
struct Handler;
|
struct Handler;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
// handles previously linked accounts joining the server
|
async fn guild_member_addition(&self, ctx: Context, mut new_member: guild::Member) {
|
||||||
async fn guild_member_addition(&self, ctx: Context, mut new_member: Member) {
|
|
||||||
let db_lock = {
|
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()
|
||||||
|
@ -43,49 +40,26 @@ impl EventHandler for Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !new_member.roles.contains(&config.role_current) {
|
if let Some(role) = &config.role_current {
|
||||||
roles.push(config.role_current.to_owned());
|
if !new_member.roles.contains(role) {
|
||||||
|
roles.push(role.to_owned());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|
||||||
let msg = format!(
|
|
||||||
r#"
|
|
||||||
Welcome {} to the {} server!
|
|
||||||
Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use ``/link_wolves`` to get full access.
|
|
||||||
"#,
|
|
||||||
new_member.display_name(),
|
|
||||||
&config.server_name,
|
|
||||||
&config.wolves_link,
|
|
||||||
&config.server,
|
|
||||||
&config.bot_channel_id
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(err) = new_member.user.direct_message(&ctx, |m| m.content(&msg)).await {
|
|
||||||
dbg!(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||||
println!("[Main] {} is connected!", ready.user.name);
|
println!("[Main] {} is connected!", ready.user.name);
|
||||||
ctx.set_presence(Some(Activity::playing("with humanity's fate")), OnlineStatus::Online).await;
|
|
||||||
|
|
||||||
match Command::set_global_application_commands(&ctx.http, |commands| {
|
match Command::set_global_application_commands(&ctx.http, |commands| {
|
||||||
commands
|
commands
|
||||||
.create_application_command(|command| commands::add_server::register(command))
|
.create_application_command(|command| commands::add_server::register(command))
|
||||||
.create_application_command(|command| commands::role_adder::edit::register(command))
|
|
||||||
.create_application_command(|command| commands::link_email::link::register(command))
|
.create_application_command(|command| commands::link_email::link::register(command))
|
||||||
.create_application_command(|command| commands::link_email::verify::register(command))
|
.create_application_command(|command| commands::link_email::verify::register(command))
|
||||||
.create_application_command(|command| commands::minecraft::server::add::register(command))
|
|
||||||
.create_application_command(|command| commands::minecraft::server::list::register(command))
|
|
||||||
.create_application_command(|command| commands::minecraft::server::delete::register(command))
|
|
||||||
.create_application_command(|command| commands::minecraft::user::add::register(command))
|
|
||||||
// for committee server, temp
|
|
||||||
.create_application_command(|command| commands::committee::link::register(command))
|
|
||||||
.create_application_command(|command| commands::committee::verify::register(command))
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -102,19 +76,9 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
||||||
//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
|
|
||||||
"link_wolves" => commands::link_email::link::run(&command, &ctx).await,
|
|
||||||
"verify" => commands::link_email::verify::run(&command, &ctx).await,
|
|
||||||
"link_minecraft" => commands::minecraft::user::add::run(&command, &ctx).await,
|
|
||||||
// admin commands
|
|
||||||
"add" => commands::add_server::run(&command, &ctx).await,
|
"add" => commands::add_server::run(&command, &ctx).await,
|
||||||
"roles_adder" => commands::role_adder::edit::run(&command, &ctx).await,
|
"link" => commands::link_email::link::run(&command, &ctx).await,
|
||||||
"minecraft_add" => commands::minecraft::server::add::run(&command, &ctx).await,
|
"verify" => commands::link_email::verify::run(&command, &ctx).await,
|
||||||
"minecraft_list" => commands::minecraft::server::list::run(&command, &ctx).await,
|
|
||||||
"minecraft_delete" => commands::minecraft::server::delete::run(&command, &ctx).await,
|
|
||||||
// for teh committee server, temporary
|
|
||||||
"link_committee" => commands::committee::link::run(&command, &ctx).await,
|
|
||||||
"verify_committee" => commands::committee::verify::run(&command, &ctx).await,
|
|
||||||
_ => "not implemented :(".to_string(),
|
_ => "not implemented :(".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,20 +87,6 @@ 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: Member) {
|
|
||||||
// get config/db
|
|
||||||
let db_lock = {
|
|
||||||
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
|
|
||||||
on_role_change(&db, &ctx, new_data).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -144,10 +94,7 @@ async fn main() {
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
let db = match db_init(&config).await {
|
let db = match db_init(&config).await {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(err) => {
|
Err(_) => return,
|
||||||
dbg!(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Intents are a bitflag, bitwise operations can be used to dictate which intents to use
|
// Intents are a bitflag, bitwise operations can be used to dictate which intents to use
|
||||||
|
|
Loading…
Reference in a new issue