summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/autobot.yaml3
-rw-r--r--.github/workflows/ci.yaml60
-rw-r--r--.github/workflows/clippy.yaml23
-rw-r--r--.github/workflows/docker.yaml37
-rw-r--r--.github/workflows/update-flake.yaml5
-rw-r--r--Cargo.lock38
-rw-r--r--Cargo.toml5
-rw-r--r--README.md4
-rw-r--r--flake.lock48
-rw-r--r--flake.nix207
-rw-r--r--nix/containerize.nix17
-rw-r--r--nix/derivation.nix67
-rw-r--r--nix/docker.nix43
-rw-r--r--nix/module.nix40
-rw-r--r--nix/static.nix20
-rw-r--r--src/api/guzzle.rs20
-rw-r--r--src/api/mod.rs29
-rw-r--r--src/api/shiggy.rs20
-rw-r--r--src/client.rs99
-rw-r--r--src/commands/general/ask.rs6
-rw-r--r--src/commands/general/bing.rs6
-rw-r--r--src/commands/general/config.rs8
-rw-r--r--src/commands/general/convert.rs12
-rw-r--r--src/commands/general/emoji.rs5
-rw-r--r--src/commands/general/pfp.rs7
-rw-r--r--src/commands/general/random.rs16
-rw-r--r--src/commands/general/version.rs5
-rw-r--r--src/commands/mod.rs31
-rw-r--r--src/commands/moderation/clear_messages.rs5
-rw-r--r--src/commands/optional/copypasta.rs73
-rw-r--r--src/commands/optional/mod.rs1
-rw-r--r--src/commands/optional/teawiespam.rs5
-rw-r--r--src/commands/optional/uwurandom.rs4
-rw-r--r--src/copypastas/astral.txt7
-rw-r--r--src/copypastas/dvd.txt1
-rw-r--r--src/copypastas/egrill.txt1
-rw-r--r--src/copypastas/happymeal.txt1
-rw-r--r--src/copypastas/sus.txt1
-rw-r--r--src/copypastas/ticktock.txt8
-rw-r--r--src/copypastas/twitter.txt35
-rw-r--r--src/events/error.rs (renamed from src/handlers/error.rs)5
-rw-r--r--src/events/guild.rs (renamed from src/handlers/event/guild.rs)6
-rw-r--r--src/events/message.rs (renamed from src/handlers/event/message.rs)2
-rw-r--r--src/events/mod.rs (renamed from src/handlers/event/mod.rs)5
-rw-r--r--src/events/pinboard.rs (renamed from src/handlers/event/pinboard.rs)2
-rw-r--r--src/events/reactboard.rs (renamed from src/handlers/event/reactboard.rs)2
-rw-r--r--src/handlers/mod.rs2
-rw-r--r--src/http/mod.rs43
-rw-r--r--src/http/shiggy.rs20
-rw-r--r--src/http/teawie.rs28
-rw-r--r--src/main.rs117
-rw-r--r--src/utils.rs2
-rw-r--r--treefmt.nix11
53 files changed, 577 insertions, 691 deletions
diff --git a/.github/workflows/autobot.yaml b/.github/workflows/autobot.yaml
index b57ea58..4e74d0a 100644
--- a/.github/workflows/autobot.yaml
+++ b/.github/workflows/autobot.yaml
@@ -14,7 +14,8 @@ jobs:
pull-requests: write
steps:
- - uses: dependabot/fetch-metadata@v2
+ - name: Fetch metadata
+ uses: dependabot/fetch-metadata@v2
id: metadata
with:
github-token: ${{ github.token }}
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 9394567..f084a2a 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -3,7 +3,17 @@ name: CI
on:
push:
branches: [main]
+ paths:
+ - "**.nix"
+ - "**.rs"
+ - "**.lock"
+ - "Cargo.toml"
pull_request:
+ paths:
+ - "**.nix"
+ - "**.rs"
+ - "**.lock"
+ - "Cargo.toml"
workflow_dispatch:
jobs:
@@ -13,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-latest, macos-latest, windows-latest]
+ os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
@@ -22,18 +32,14 @@ jobs:
uses: actions/checkout@v4
- name: Install Rust
- uses: dtolnay/rust-toolchain@stable
- with:
- toolchain: stable
-
- - name: Setup Rust cache
- uses: Swatinem/rust-cache@v2
+ uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Run build
- run: cargo build --locked --release
+ run: |
+ cargo build --locked --release
- format-and-lint:
- name: Format & lint
+ nix:
+ name: Nix
runs-on: ubuntu-latest
@@ -42,21 +48,41 @@ jobs:
uses: actions/checkout@v4
- name: Install Nix
- uses: DeterminateSystems/nix-installer-action@v12
+ uses: DeterminateSystems/nix-installer-action@v13
- name: Setup Nix cache
uses: DeterminateSystems/magic-nix-cache-action@v7
- - name: Run treefmt
+ - name: Run flake checks
run: |
- nix flake check --all-systems --print-build-logs --show-trace
+ nix build --print-build-logs --show-trace
+
+ rustfmt:
+ name: Rustfmt
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: actions-rust-lang/setup-rust-toolchain@v1
+ with:
+ components: rustfmt
+
+ - name: Run rustfmt
+ uses: actions-rust-lang/rustfmt@v1
release-gate:
- name: CI Release Gate
- needs: [build, format-and-lint]
+ name: CI Release gate
+ needs: [build, rustfmt, nix]
+
+ if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- - name: Exit with result
- run: echo "We're good to go!"
+ - name: Exit with error
+ if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
+ run: exit 1
diff --git a/.github/workflows/clippy.yaml b/.github/workflows/clippy.yaml
index 56b39df..cd99256 100644
--- a/.github/workflows/clippy.yaml
+++ b/.github/workflows/clippy.yaml
@@ -2,6 +2,7 @@ name: Clippy
on:
push:
+ branches: [main]
paths:
- 'Cargo.toml'
- 'Cargo.lock'
@@ -26,31 +27,23 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - name: Install Nix
- uses: DeterminateSystems/nix-installer-action@v12
-
- - name: Setup Nix cache
- uses: DeterminateSystems/magic-nix-cache-action@v7
-
- - name: Setup Rust cache
- uses: Swatinem/rust-cache@v2
+ - name: Install Rust
+ uses: actions-rust-lang/setup-rust-toolchain@v1
+ with:
+ components: clippy
- name: Install SARIF tools
run: |
- nix profile install \
- --inputs-from . \
- github:getchoo/nix-exprs#{clippy-sarif,sarif-fmt}
+ cargo install clippy-sarif sarif-fmt
- name: Fetch Cargo deps
run: |
- nix develop .#ci --command \
- cargo fetch --locked
+ cargo fetch --locked
- name: Run Clippy
continue-on-error: true
run: |
- nix develop .#ci --command \
- cargo clippy \
+ cargo clippy \
--all-features \
--all-targets \
--message-format=json \
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
index 30c84d9..0bb28ad 100644
--- a/.github/workflows/docker.yaml
+++ b/.github/workflows/docker.yaml
@@ -3,7 +3,17 @@ name: Docker
on:
push:
branches: [main]
+ paths:
+ - "**.nix"
+ - "**.rs"
+ - "**.lock"
+ - "Cargo.toml"
pull_request:
+ paths:
+ - "**.nix"
+ - "**.rs"
+ - "**.lock"
+ - "Cargo.toml"
workflow_dispatch:
jobs:
@@ -13,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- arch: [x86_64, aarch64]
+ arch: [amd64, arm64]
runs-on: ubuntu-latest
@@ -22,7 +32,7 @@ jobs:
uses: actions/checkout@v4
- name: Install Nix
- uses: DeterminateSystems/nix-installer-action@v12
+ uses: DeterminateSystems/nix-installer-action@v13
- name: Setup Nix cache
uses: DeterminateSystems/magic-nix-cache-action@v7
@@ -53,17 +63,21 @@ jobs:
name: Docker Release Gate
needs: build
+ if: always()
+
runs-on: ubuntu-latest
steps:
- - name: Exit with result
- run: echo "We're good to go!"
+ - name: Exit with error
+ if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
+ run: exit 1
push:
name: Push image
- if: github.event_name == 'push'
needs: release-gate
+ if: github.event_name == 'push'
+
runs-on: ubuntu-latest
permissions:
@@ -72,12 +86,9 @@ jobs:
env:
REGISTRY: ghcr.io
USERNAME: ${{ github.actor }}
+ IMAGE_NAME: teawie-bot
steps:
- - name: Set image name
- run: |
- echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
-
- name: Checkout repository
uses: actions/checkout@v4
@@ -97,15 +108,15 @@ jobs:
env:
TAG: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
run: |
- architectures=("x86_64" "aarch64")
+ architectures=("amd64" "arm64")
for arch in "${architectures[@]}"; do
docker load < images/container-"$arch"/*.tar.gz
- docker tag teawiebot:latest-"$arch" "$TAG"-"$arch"
+ docker tag teawie-bot:latest-"$arch" "$TAG"-"$arch"
docker push "$TAG"-"$arch"
done
docker manifest create "$TAG" \
- --amend "$TAG"-x86_64 \
- --amend "$TAG"-aarch64
+ --amend "$TAG"-amd64 \
+ --amend "$TAG"-arm64
docker manifest push "$TAG"
diff --git a/.github/workflows/update-flake.yaml b/.github/workflows/update-flake.yaml
index 968fc15..06eabfd 100644
--- a/.github/workflows/update-flake.yaml
+++ b/.github/workflows/update-flake.yaml
@@ -20,15 +20,14 @@ jobs:
uses: actions/checkout@v4
- name: Install Nix
- uses: DeterminateSystems/nix-installer-action@v12
+ uses: DeterminateSystems/nix-installer-action@v13
- name: Update flake.lock & make PR
- uses: DeterminateSystems/update-flake-lock@v23
id: update
+ uses: DeterminateSystems/update-flake-lock@v23
with:
commit-msg: "nix: update flake.lock"
pr-title: "nix: update flake.lock"
- token: ${{ secrets.MERGE_TOKEN }}
- name: Enable auto-merge
if: env.PR_ID != ''
diff --git a/Cargo.lock b/Cargo.lock
index 31a44c0..345ac6c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -983,25 +983,6 @@ dependencies = [
]
[[package]]
-name = "include_dir"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
-dependencies = [
- "include_dir_macros",
-]
-
-[[package]]
-name = "include_dir_macros"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
-dependencies = [
- "proc-macro2",
- "quote",
-]
-
-[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1130,6 +1111,12 @@ dependencies = [
]
[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2117,7 +2104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
-name = "teawiebot"
+name = "teawie-bot"
version = "1.0.0"
dependencies = [
"bottomify",
@@ -2125,7 +2112,6 @@ dependencies = [
"dotenvy",
"env_logger",
"eyre",
- "include_dir",
"log",
"poise",
"rand 0.8.5",
@@ -2193,12 +2179,13 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.30"
+version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
+ "num-conv",
"powerfmt",
"serde",
"time-core",
@@ -2213,10 +2200,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
-version = "0.2.15"
+version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [
+ "num-conv",
"time-core",
]
diff --git a/Cargo.toml b/Cargo.toml
index 51860c8..8406b6e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,10 +1,8 @@
[package]
-name = "teawiebot"
+name = "teawie-bot"
version = "1.0.0"
edition = "2021"
repository = "https://github.com/getchoo/teawieBot"
-license = "MIT"
-readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,7 +15,6 @@ eyre = { version = "0.6.12", default-features = false, features = [
"auto-install",
"track-caller",
] }
-include_dir = "0.7.4"
log = "0.4.22"
poise = "0.6.1"
rand = "0.8.5"
diff --git a/README.md b/README.md
index 026405e..9b8f719 100644
--- a/README.md
+++ b/README.md
@@ -7,14 +7,10 @@ and now in rust!!!)๐Ÿš€๐Ÿš€
## features / commands
-(some are slash, some are not)
-
**!ask** | ask the bot a question with predefined answers
(this is also a slash command, use with /ask)
**!teawiespam** | spams :teawiesmile:
-**/copypasta** | sends a random copypasta from src\teawie_bot\copypastas
-
**/random_teawie** | sends out a random teawie, which is a soft cute character made
by SympathyTea
diff --git a/flake.lock b/flake.lock
index 16741e3..651e65b 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,26 +1,5 @@
{
"nodes": {
- "fenix": {
- "inputs": {
- "nixpkgs": [
- "nixpkgs"
- ],
- "rust-analyzer-src": "rust-analyzer-src"
- },
- "locked": {
- "lastModified": 1721975407,
- "narHash": "sha256-XkNqglPxkfWOkysN5C1aZeoHCozwyRq3nC4jL0IFqlA=",
- "owner": "nix-community",
- "repo": "fenix",
- "rev": "27128b6e467ced6142264d02a884fde45931e708",
- "type": "github"
- },
- "original": {
- "owner": "nix-community",
- "repo": "fenix",
- "type": "github"
- }
- },
"nixpkgs": {
"locked": {
"lastModified": 1721970117,
@@ -39,24 +18,27 @@
},
"root": {
"inputs": {
- "fenix": "fenix",
- "nixpkgs": "nixpkgs"
+ "nixpkgs": "nixpkgs",
+ "treefmt-nix": "treefmt-nix"
}
},
- "rust-analyzer-src": {
- "flake": false,
+ "treefmt-nix": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
"locked": {
- "lastModified": 1721909430,
- "narHash": "sha256-u3e38jvjbxbbYH3caQFPE7gnNFNBjbkqkHRKBx1AOJs=",
- "owner": "rust-lang",
- "repo": "rust-analyzer",
- "rev": "c02a4a31eada45e591b5edb8c1a813ea7b9d408f",
+ "lastModified": 1721769617,
+ "narHash": "sha256-6Pqa0bi5nV74IZcENKYRToRNM5obo1EQ+3ihtunJ014=",
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "rev": "8db8970be1fb8be9c845af7ebec53b699fe7e009",
"type": "github"
},
"original": {
- "owner": "rust-lang",
- "ref": "nightly",
- "repo": "rust-analyzer",
+ "owner": "numtide",
+ "repo": "treefmt-nix",
"type": "github"
}
}
diff --git a/flake.nix b/flake.nix
index d2def00..e03e35d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -3,122 +3,105 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
- fenix = {
- url = "github:nix-community/fenix";
+
+ ## Everything below this is optional
+ ## `inputs.<name>.follows = ""`
+
+ treefmt-nix = {
+ url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
- outputs = {
- self,
- nixpkgs,
- ...
- } @ inputs: let
- systems = [
- "x86_64-linux"
- "aarch64-linux"
- "x86_64-darwin"
- "aarch64-darwin"
- ];
-
- forAllSystems = fn: nixpkgs.lib.genAttrs systems (system: fn nixpkgs.legacyPackages.${system});
- in {
- checks = forAllSystems ({
- lib,
- pkgs,
- ...
- }: {
- actionlint = pkgs.runCommand "check-actionlint" {} ''
- ${lib.getExe pkgs.actionlint} ${./.github/workflows}/*
- touch $out
- '';
-
- deadnix = pkgs.runCommand "check-deadnix" {} ''
- ${lib.getExe pkgs.deadnix} --fail ${./.}
- touch $out
- '';
-
- editorconfig = pkgs.runCommand "check-editorconfig" {} ''
- cd ${./.}
- ${lib.getExe pkgs.editorconfig-checker} \
- -exclude '.git' .
-
- touch $out
- '';
-
- rustfmt = pkgs.runCommand "check-rustfmt" {nativeBuildInputs = [pkgs.cargo pkgs.rustfmt];} ''
- cd ${./.}
- cargo fmt -- --check
- touch $out
- '';
-
- statix = pkgs.runCommand "check-statix" {} ''
- ${lib.getExe pkgs.statix} check ${./.}
- touch $out
- '';
- });
-
- devShells = forAllSystems ({
- pkgs,
- system,
- ...
- }: {
- default = pkgs.mkShell {
- packages = [
- # rust tools
- pkgs.clippy
- pkgs.rustfmt
- pkgs.rust-analyzer
-
- # nix tools
- pkgs.deadnix
- pkgs.nil
- pkgs.statix
-
- # misc formatter/linters
- pkgs.actionlint
- self.formatter.${system}
-
- pkgs.redis
- ];
-
- inputsFrom = [self.packages.${system}.teawiebot];
- RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}";
- };
-
- ci = pkgs.mkShell {
- packages = [
- pkgs.clippy
- pkgs.rustfmt
-
- self.formatter.${system}
- ];
-
- inputsFrom = [self.packages.${system}.teawiebot];
- };
- });
-
- formatter = forAllSystems (pkgs: pkgs.alejandra);
-
- nixosModules.default = import ./nix/module.nix self;
-
- packages = forAllSystems ({
- pkgs,
- system,
- ...
- }: let
- crossBuildsFor = arch: import ./nix/docker.nix {inherit pkgs arch inputs;};
+ outputs =
+ {
+ self,
+ nixpkgs,
+ treefmt-nix,
+ }:
+ let
+ inherit (nixpkgs) lib;
+ systems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
+
+ forAllSystems = lib.genAttrs systems;
+ nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});
+ treefmtFor = forAllSystems (system: treefmt-nix.lib.evalModule nixpkgsFor.${system} ./treefmt.nix);
in
- {
- teawiebot = pkgs.callPackage ./nix/derivation.nix {inherit self;};
-
- default = self.packages.${system}.teawiebot;
- }
- // crossBuildsFor "x86_64"
- // crossBuildsFor "aarch64");
-
- overlays.default = _: prev: {
- teawiebot = prev.callPackage ./nix/derivation.nix {inherit self;};
+ {
+ checks = forAllSystems (system: {
+ treefmt = treefmtFor.${system}.config.build.check self;
+ });
+
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgsFor.${system};
+ in
+ {
+ default = pkgs.mkShell {
+ packages = [
+ # rust tools
+ pkgs.clippy
+ pkgs.rustfmt
+ pkgs.rust-analyzer
+
+ # nix tools
+ pkgs.nil
+ pkgs.statix
+
+ # misc formatter/linters
+ pkgs.actionlint
+ self.formatter.${system}
+
+ pkgs.redis
+ ];
+
+ inputsFrom = [ self.packages.${system}.teawie-bot ];
+ RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}";
+ };
+
+ ci = pkgs.mkShell {
+ packages = [
+ pkgs.clippy
+ pkgs.rustfmt
+
+ self.formatter.${system}
+ ];
+
+ inputsFrom = [ self.packages.${system}.teawie-bot ];
+ };
+ }
+ );
+
+ formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);
+
+ nixosModules.default = import ./nix/module.nix self;
+
+ packages = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgsFor.${system};
+ packages' = self.packages.${system};
+
+ staticWith = pkgs.callPackage ./nix/static.nix { inherit (packages') teawie-bot; };
+ containerize = pkgs.callPackage ./nix/containerize.nix { };
+ in
+ {
+ container-amd64 = containerize packages'.static-x86_64;
+ container-arm64 = containerize packages'.static-aarch64;
+
+ static-x86_64 = staticWith { arch = "x86_64"; };
+ static-aarch64 = staticWith { arch = "aarch64"; };
+
+ teawie-bot = pkgs.callPackage ./nix/derivation.nix { inherit self; };
+
+ default = self.packages.${system}.teawie-bot;
+ }
+ );
};
- };
}
diff --git a/nix/containerize.nix b/nix/containerize.nix
new file mode 100644
index 0000000..8175e17
--- /dev/null
+++ b/nix/containerize.nix
@@ -0,0 +1,17 @@
+{ lib, dockerTools }:
+let
+ containerize =
+ teawie-bot:
+ let
+ inherit (teawie-bot.passthru) crossPkgs;
+ architecture = crossPkgs.go.GOARCH;
+ in
+ dockerTools.buildLayeredImage {
+ name = "teawie-bot";
+ tag = "latest-${architecture}";
+ contents = [ dockerTools.caCertificates ];
+ config.Cmd = [ (lib.getExe teawie-bot) ];
+ inherit architecture;
+ };
+in
+containerize
diff --git a/nix/derivation.nix b/nix/derivation.nix
index 6a25976..955f601 100644
--- a/nix/derivation.nix
+++ b/nix/derivation.nix
@@ -3,46 +3,51 @@
stdenv,
rustPlatform,
darwin,
- self ? {inherit ((lib.importTOML ../Cargo.toml).package) version;},
+ self ? { },
lto ? true,
optimizeSize ? false,
}:
+let
+ fs = lib.fileset;
+in
rustPlatform.buildRustPackage {
- pname = "teawiebot";
- version =
- (lib.importTOML ../Cargo.toml).package.version
- + "-"
- + self.shortRev or self.dirtyShortRev or self.version or "unknown";
+ pname = "teawie-bot";
+ version = (lib.importTOML ../Cargo.toml).package.version or "unknown";
- __structuredAttrs = true;
-
- src = lib.fileset.toSource {
+ src = fs.toSource {
root = ../.;
- fileset = lib.fileset.unions [
- ../src
- ../Cargo.toml
- ../Cargo.lock
- ];
+ fileset = fs.intersection (fs.gitTracked ../.) (
+ lib.fileset.unions [
+ ../src
+ ../Cargo.toml
+ ../Cargo.lock
+ ]
+ );
};
cargoLock = {
lockFile = ../Cargo.lock;
};
- buildInputs = lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [
- CoreFoundation
- Security
- SystemConfiguration
- darwin.libiconv
- ]);
+ buildInputs = lib.optionals stdenv.isDarwin (
+ with darwin.apple_sdk.frameworks;
+ [
+ CoreFoundation
+ Security
+ SystemConfiguration
+ darwin.libiconv
+ ]
+ );
- env = let
- toRustFlags = lib.mapAttrs' (
- name:
- lib.nameValuePair
- "CARGO_BUILD_RELEASE_${lib.toUpper (builtins.replaceStrings ["-"] ["_"] name)}"
- );
- in
+ env =
+ let
+ toRustFlags = lib.mapAttrs' (
+ name:
+ lib.nameValuePair "CARGO_BUILD_RELEASE_${
+ lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] name)
+ }"
+ );
+ in
{
GIT_SHA = self.shortRev or self.dirtyShortRev or "unknown";
}
@@ -56,11 +61,11 @@ rustPlatform.buildRustPackage {
strip = "symbols";
});
- meta = with lib; {
- mainProgram = "teawiebot";
+ meta = {
description = "funni bot";
homepage = "https://github.com/getchoo/teawiebot";
- license = licenses.mit;
- maintainers = with maintainers; [getchoo];
+ license = lib.licenses.mit;
+ maintainers = with lib.maintainers; [ getchoo ];
+ mainProgram = "teawie-bot";
};
}
diff --git a/nix/docker.nix b/nix/docker.nix
deleted file mode 100644
index 79f49cc..0000000
--- a/nix/docker.nix
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- pkgs,
- arch,
- inputs,
-}: let
- inherit (pkgs) lib;
- inputs' = lib.mapAttrs (_: lib.mapAttrs (_: v: v.${pkgs.system} or v)) inputs;
-
- crossTargets = with pkgs.pkgsCross; {
- x86_64 = musl64.pkgsStatic;
- aarch64 = aarch64-multiplatform.pkgsStatic;
- };
-
- rustStdFor = pkgs: inputs'.fenix.packages.targets.${pkgs.stdenv.hostPlatform.rust.rustcTarget}.stable.rust-std;
- toolchain = with inputs'.fenix.packages;
- combine (lib.flatten [
- stable.cargo
- stable.rustc
- (map rustStdFor (lib.attrValues crossTargets))
- ]);
-
- rustPlatformFor = pkgs:
- pkgs.makeRustPlatform (
- lib.genAttrs ["cargo" "rustc"] (lib.const toolchain)
- );
- crossPlatforms = lib.mapAttrs (lib.const rustPlatformFor) crossTargets;
-in {
- "teawiebot-static-${arch}" = inputs'.self.packages.teawiebot.override {
- rustPlatform = crossPlatforms.${arch};
- optimizeSize = true;
- };
-
- "container-${arch}" = pkgs.dockerTools.buildLayeredImage {
- name = "teawiebot";
- tag = "latest-${arch}";
- contents = [pkgs.dockerTools.caCertificates];
- config.Cmd = [
- (lib.getExe inputs'.self.packages."teawiebot-static-${arch}")
- ];
-
- architecture = inputs.nixpkgs.legacyPackages."${arch}-linux".pkgsStatic.go.GOARCH;
- };
-}
diff --git a/nix/module.nix b/nix/module.nix
index c129e68..4e3b683 100644
--- a/nix/module.nix
+++ b/nix/module.nix
@@ -1,14 +1,15 @@
-self: {
+self:
+{
config,
lib,
pkgs,
...
-}: let
+}:
+let
cfg = config.services.teawiebot;
defaultUser = "teawiebot";
- inherit
- (lib)
+ inherit (lib)
getExe
literalExpression
mdDoc
@@ -21,18 +22,18 @@ self: {
;
inherit (pkgs.stdenv.hostPlatform) system;
-in {
+in
+{
options.services.teawiebot = {
- enable = mkEnableOption "teawiebot";
- package = mkPackageOption (
- self.packages.${system} or (builtins.throw "${system} is not supported!")
- ) "teawiebot" {};
+ enable = mkEnableOption "teawieBot";
+ package = mkPackageOption (self.packages.${system} or (builtins.throw "${system} is not supported!")
+ ) "teawie-bot" { };
user = mkOption {
description = mdDoc ''
User under which the service should run. If this is the default value,
- the user will be created, with the specified group as the primary
- group.
+ the user will be created, with the specified group as the primary
+ group.
'';
type = types.str;
default = defaultUser;
@@ -88,10 +89,8 @@ in {
systemd.services."teawiebot" = {
enable = true;
- wantedBy = ["multi-user.target"];
- after =
- ["network.target"]
- ++ optionals (cfg.redisUrl == "local") ["redis-teawiebot.service"];
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ] ++ optionals (cfg.redisUrl == "local") [ "redis-teawiebot.service" ];
script = ''
${getExe cfg.package}
@@ -99,9 +98,10 @@ in {
environment = {
REDIS_URL =
- if cfg.redisUrl == "local"
- then "unix:${config.services.redis.servers.teawiebot.unixSocket}"
- else cfg.redisUrl;
+ if cfg.redisUrl == "local" then
+ "unix:${config.services.redis.servers.teawiebot.unixSocket}"
+ else
+ cfg.redisUrl;
};
serviceConfig = {
@@ -140,9 +140,7 @@ in {
};
};
- groups = mkIf (cfg.group == defaultUser) {
- ${defaultUser} = {};
- };
+ groups = mkIf (cfg.group == defaultUser) { ${defaultUser} = { }; };
};
};
}
diff --git a/nix/static.nix b/nix/static.nix
new file mode 100644
index 0000000..5a5606f
--- /dev/null
+++ b/nix/static.nix
@@ -0,0 +1,20 @@
+{
+ lib,
+ pkgsCross,
+ teawie-bot,
+}:
+let
+ crossPkgsFor = with pkgsCross; {
+ x86_64 = musl64.pkgsStatic;
+ aarch64 = aarch64-multiplatform.pkgsStatic;
+ };
+in
+{ arch }:
+let
+ crossPkgs = crossPkgsFor.${arch};
+in
+(crossPkgs.callPackage ./derivation.nix { optimizeSize = true; }).overrideAttrs (old: {
+ passthru = old.passthru or { } // {
+ inherit crossPkgs;
+ };
+})
diff --git a/src/api/guzzle.rs b/src/api/guzzle.rs
deleted file mode 100644
index 9ad6ad6..0000000
--- a/src/api/guzzle.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use eyre::Result;
-use log::debug;
-use serde::{Deserialize, Serialize};
-
-#[derive(Serialize, Deserialize)]
-struct RandomTeawieResponse {
- url: String,
-}
-
-// TODO: read this from an env var
-const GUZZLE: &str = "https://api.getchoo.com";
-const RANDOM_TEAWIE: &str = "/random_teawie";
-
-pub async fn random_teawie() -> Result<String> {
- let url = format!("{GUZZLE}{RANDOM_TEAWIE}");
- debug!("Making request to {url}");
- let json: RandomTeawieResponse = super::get_json(&url).await?;
-
- Ok(json.url)
-}
diff --git a/src/api/mod.rs b/src/api/mod.rs
deleted file mode 100644
index dac9209..0000000
--- a/src/api/mod.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use std::sync::OnceLock;
-
-use eyre::Result;
-use reqwest::Client;
-use serde::de::DeserializeOwned;
-
-pub mod guzzle;
-pub mod shiggy;
-
-pub fn client() -> &'static Client {
- static USER_AGENT: OnceLock<String> = OnceLock::new();
- static CLIENT: OnceLock<Client> = OnceLock::new();
-
- let user_agent = USER_AGENT.get_or_init(|| {
- let version = option_env!("CARGO_PKG_VERSION").unwrap_or("development");
-
- format!("teawieBot/{version}")
- });
-
- CLIENT.get_or_init(|| Client::builder().user_agent(user_agent).build().unwrap())
-}
-
-async fn get_json<T: DeserializeOwned>(url: &str) -> Result<T> {
- let resp = client().get(url).send().await?;
- resp.error_for_status_ref()?;
- let json = resp.json().await?;
-
- Ok(json)
-}
diff --git a/src/api/shiggy.rs b/src/api/shiggy.rs
deleted file mode 100644
index d6a6238..0000000
--- a/src/api/shiggy.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use eyre::Result;
-use log::debug;
-use serde::Deserialize;
-
-const SHIGGY: &str = "https://safebooru.donmai.us";
-const RANDOM_SHIGGY: &str = "/posts/random.json?tags=kemomimi-chan_(naga_u)+naga_u&only=file_url";
-
-#[derive(Deserialize)]
-struct SafebooruResponse {
- file_url: String,
-}
-
-#[allow(clippy::module_name_repetitions)]
-pub async fn random_shiggy() -> Result<String> {
- let url = format!("{SHIGGY}{RANDOM_SHIGGY}");
- debug!("Making request to {url}");
-
- let resp: SafebooruResponse = super::get_json(&url).await?;
- Ok(resp.file_url)
-}
diff --git a/src/client.rs b/src/client.rs
new file mode 100644
index 0000000..65c221b
--- /dev/null
+++ b/src/client.rs
@@ -0,0 +1,99 @@
+use crate::{commands, events, http, storage::Storage};
+
+use std::{sync::Arc, time::Duration};
+
+use eyre::{bail, Context as _, Result};
+use log::{info, trace, warn};
+use poise::{
+ serenity_prelude::{self as serenity},
+ EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
+};
+
+pub type Error = eyre::Report;
+pub type Context<'a> = poise::Context<'a, Data, Error>;
+
+#[derive(Clone, Debug, Default)]
+pub struct Data {
+ pub http_client: http::Client,
+ pub storage: Option<Storage>,
+}
+
+async fn setup(ctx: &serenity::Context) -> Result<Data> {
+ let storage = Storage::from_env().ok();
+
+ if let Some(storage) = storage.as_ref() {
+ if !storage.clone().is_connected() {
+ bail!("You specified a storage backend but there's no connection! Is it running?");
+ }
+ trace!("Storage backend connected!");
+
+ poise::builtins::register_globally(ctx, &commands::global()).await?;
+ info!("Registered global commands!");
+
+ // register "extra" commands in guilds that allow it
+ let guilds = storage.get_opted_guilds().await?;
+
+ for guild in guilds {
+ poise::builtins::register_in_guild(ctx, &commands::optional(), guild).await?;
+
+ info!("Registered guild commands to {}", guild);
+ }
+ } else {
+ warn!("No storage backend was specified. Features requiring storage cannot be used");
+ warn!("Registering optional commands globally since there's no storage backend");
+ poise::builtins::register_globally(ctx, &commands::all()).await?;
+ }
+
+ let http_client = <http::Client as http::Ext>::default();
+ let data = Data {
+ http_client,
+ storage,
+ };
+
+ Ok(data)
+}
+
+pub async fn handle_shutdown(shard_manager: Arc<serenity::ShardManager>, reason: &str) {
+ warn!("{reason}! Shutting down bot...");
+ shard_manager.shutdown_all().await;
+ println!("Everything is shutdown. Goodbye!");
+}
+
+pub async fn get() -> Result<serenity::Client> {
+ let token = std::env::var("TOKEN").wrap_err("Couldn't find bot token in environment!")?;
+
+ let intents =
+ serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT;
+
+ let options = FrameworkOptions {
+ commands: commands::all(),
+ on_error: |error| Box::pin(events::error::handle(error)),
+
+ command_check: Some(|ctx| {
+ Box::pin(async move { Ok(ctx.author().id != ctx.framework().bot_id) })
+ }),
+
+ event_handler: |ctx, event, _framework, data| Box::pin(events::handle(ctx, event, data)),
+
+ prefix_options: PrefixFrameworkOptions {
+ prefix: Some("!".into()),
+ edit_tracker: Some(Arc::new(EditTracker::for_timespan(Duration::from_secs(
+ 3600,
+ )))),
+ ..Default::default()
+ },
+
+ ..Default::default()
+ };
+
+ let framework = Framework::builder()
+ .options(options)
+ .setup(|ctx, _ready, _framework| Box::pin(setup(ctx)))
+ .build();
+
+ let client = serenity::ClientBuilder::new(token, intents)
+ .framework(framework)
+ .await?;
+
+ Ok(client)
+}
diff --git a/src/commands/general/ask.rs b/src/commands/general/ask.rs
index c715e3a..1300e97 100644
--- a/src/commands/general/ask.rs
+++ b/src/commands/general/ask.rs
@@ -1,6 +1,6 @@
-use crate::{consts, utils, Context, Error};
+use crate::{client::Context, consts, utils};
-use eyre::Context as _;
+use eyre::{Context as _, Result};
/// Ask teawie a question!
#[poise::command(prefix_command, slash_command)]
@@ -10,7 +10,7 @@ pub async fn ask(
#[rename = "question"]
#[description = "The question you want to ask teawie"]
_question: String,
-) -> Result<(), Error> {
+) -> Result<()> {
let resp = utils::random_choice(consts::RESPONSES)
.wrap_err("Couldn't choose from random responses!")?;
diff --git a/src/commands/general/bing.rs b/src/commands/general/bing.rs
index 54ee0dc..28fdf0d 100644
--- a/src/commands/general/bing.rs
+++ b/src/commands/general/bing.rs
@@ -1,8 +1,10 @@
-use crate::{Context, Error};
+use crate::client::Context;
+
+use eyre::Result;
/// Make sure the wie is alive
#[poise::command(prefix_command)]
-pub async fn bing(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn bing(ctx: Context<'_>) -> Result<()> {
ctx.say("bong!").await?;
Ok(())
}
diff --git a/src/commands/general/config.rs b/src/commands/general/config.rs
index 456e791..6adb78b 100644
--- a/src/commands/general/config.rs
+++ b/src/commands/general/config.rs
@@ -1,5 +1,5 @@
+use crate::client::Context;
use crate::storage::settings::{Properties, Settings};
-use crate::{Context, Error};
use std::str::FromStr;
@@ -41,7 +41,7 @@ fn prop_to_val(setting: &Properties, settings: &Settings) -> String {
required_permissions = "MANAGE_GUILD",
default_member_permissions = "MANAGE_GUILD"
)]
-pub async fn config(_: Context<'_>) -> Result<(), Error> {
+pub async fn config(_: Context<'_>) -> Result<()> {
Ok(())
}
@@ -72,7 +72,7 @@ pub async fn set(
#[description = "Toggle ReactBoard"] reactboard_enabled: Option<bool>,
#[description = "Enables 'extra' commands like teawiespam and copypasta. Defaults to false."]
optional_commands_enabled: Option<bool>,
-) -> Result<(), Error> {
+) -> Result<()> {
if let Some(storage) = &ctx.data().storage {
let gid = ctx.guild_id().unwrap_or_default();
let mut settings = storage.get_guild_settings(&gid).await?;
@@ -149,7 +149,7 @@ pub async fn set(
pub async fn get(
ctx: Context<'_>,
#[description = "The setting you want to get"] setting: Properties,
-) -> Result<(), Error> {
+) -> Result<()> {
let gid = &ctx
.guild_id()
.ok_or_eyre("Failed to get GuildId from context!")?;
diff --git a/src/commands/general/convert.rs b/src/commands/general/convert.rs
index 4d38eb2..b5e7018 100644
--- a/src/commands/general/convert.rs
+++ b/src/commands/general/convert.rs
@@ -1,4 +1,4 @@
-use crate::{Context, Error};
+use crate::client::Context;
use bottomify::bottom;
use eyre::Result;
@@ -9,7 +9,7 @@ use poise::serenity_prelude::constants::MESSAGE_CODE_LIMIT;
slash_command,
subcommands("to_fahrenheit", "to_celsius", "to_bottom", "from_bottom")
)]
-pub async fn convert(_: Context<'_>) -> Result<(), Error> {
+pub async fn convert(_: Context<'_>) -> Result<()> {
Ok(())
}
@@ -18,7 +18,7 @@ pub async fn convert(_: Context<'_>) -> Result<(), Error> {
pub async fn to_celsius(
ctx: Context<'_>,
#[description = "What teawie will convert"] degrees_fahrenheit: f32,
-) -> Result<(), Error> {
+) -> Result<()> {
let temp = (degrees_fahrenheit - 32.0) * (5.0 / 9.0);
ctx.say(temp.to_string()).await?;
Ok(())
@@ -29,7 +29,7 @@ pub async fn to_celsius(
pub async fn to_fahrenheit(
ctx: Context<'_>,
#[description = "What teawie will convert"] degrees_celsius: f32,
-) -> Result<(), Error> {
+) -> Result<()> {
let temp = (degrees_celsius * (9.0 / 5.0)) + 32.0;
ctx.say(temp.to_string()).await?;
Ok(())
@@ -40,7 +40,7 @@ pub async fn to_fahrenheit(
pub async fn to_bottom(
ctx: Context<'_>,
#[description = "What teawie will translate into bottom"] message: String,
-) -> Result<(), Error> {
+) -> Result<()> {
let encoded = bottom::encode_string(&message);
ctx.say(encoded).await?;
Ok(())
@@ -51,7 +51,7 @@ pub async fn to_bottom(
pub async fn from_bottom(
ctx: Context<'_>,
#[description = "What teawie will translate from bottom"] message: String,
-) -> Result<(), Error> {
+) -> Result<()> {
let resp: String;
if let Ok(decoded) = bottom::decode_string(&message.clone()) {
diff --git a/src/commands/general/emoji.rs b/src/commands/general/emoji.rs
index 81cd9a3..bbae0b5 100644
--- a/src/commands/general/emoji.rs
+++ b/src/commands/general/emoji.rs
@@ -1,5 +1,6 @@
-use crate::{consts::Colors, Context, Error};
+use crate::{client::Context, consts::Colors};
+use eyre::Result;
use poise::{
serenity_prelude::{CreateEmbed, Emoji},
CreateReply,
@@ -7,7 +8,7 @@ use poise::{
/// Get the URL for an emoji
#[poise::command(slash_command)]
-pub async fn emoji(ctx: Context<'_>, emoji: Emoji) -> Result<(), Error> {
+pub async fn emoji(ctx: Context<'_>, emoji: Emoji) -> Result<()> {
let url = emoji.url();
let embed = CreateEmbed::new()
.title(emoji.name)
diff --git a/src/commands/general/pfp.rs b/src/commands/general/pfp.rs
index 2ad062b..34ae795 100644
--- a/src/commands/general/pfp.rs
+++ b/src/commands/general/pfp.rs
@@ -1,13 +1,14 @@
+use crate::{client::Context, consts::Colors};
+
+use eyre::Result;
use poise::{
serenity_prelude::{CreateEmbed, User},
CreateReply,
};
-use crate::{consts::Colors, Context, Error};
-
/// Get someone's profile pic
#[poise::command(context_menu_command = "Get profile picture", slash_command)]
-pub async fn pfp(ctx: Context<'_>, user: User) -> Result<(), Error> {
+pub async fn pfp(ctx: Context<'_>, user: User) -> Result<()> {
let url = user
.avatar_url()
.unwrap_or_else(|| user.default_avatar_url());
diff --git a/src/commands/general/random.rs b/src/commands/general/random.rs
index 92e9188..094123b 100644
--- a/src/commands/general/random.rs
+++ b/src/commands/general/random.rs
@@ -1,14 +1,16 @@
-use crate::{api, consts, utils, Context, Error};
+use crate::{client::Context, consts, http, utils};
+
+use eyre::Result;
#[poise::command(slash_command, subcommands("lore", "teawie", "shiggy"))]
#[allow(clippy::unused_async)]
-pub async fn random(_: Context<'_>) -> Result<(), Error> {
+pub async fn random(_: Context<'_>) -> Result<()> {
Ok(())
}
/// Get a random piece of teawie lore!
#[poise::command(prefix_command, slash_command)]
-pub async fn lore(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn lore(ctx: Context<'_>) -> Result<()> {
let resp = utils::random_choice(consts::LORE)?;
ctx.say(resp).await?;
@@ -17,8 +19,8 @@ pub async fn lore(ctx: Context<'_>) -> Result<(), Error> {
/// Get a random teawie
#[poise::command(prefix_command, slash_command)]
-pub async fn teawie(ctx: Context<'_>) -> Result<(), Error> {
- let url = api::guzzle::random_teawie().await?;
+pub async fn teawie(ctx: Context<'_>) -> Result<()> {
+ let url = http::teawie::random(&ctx.data().http_client).await?;
utils::send_url_as_embed(ctx, url).await?;
Ok(())
@@ -26,8 +28,8 @@ pub async fn teawie(ctx: Context<'_>) -> Result<(), Error> {
/// Get a random shiggy
#[poise::command(prefix_command, slash_command)]
-pub async fn shiggy(ctx: Context<'_>) -> Result<(), Error> {
- let url = api::shiggy::random_shiggy().await?;
+pub async fn shiggy(ctx: Context<'_>) -> Result<()> {
+ let url = http::shiggy::random(&ctx.data().http_client).await?;
utils::send_url_as_embed(ctx, url).await?;
Ok(())
diff --git a/src/commands/general/version.rs b/src/commands/general/version.rs
index 5f8eac9..bdf6805 100644
--- a/src/commands/general/version.rs
+++ b/src/commands/general/version.rs
@@ -1,12 +1,13 @@
-use crate::{consts::Colors, Context, Error};
+use crate::{client::Context, consts::Colors};
use std::env::consts::{ARCH, OS};
+use eyre::Result;
use poise::{serenity_prelude::CreateEmbed, CreateReply};
/// Get version info
#[poise::command(slash_command)]
-pub async fn version(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn version(ctx: Context<'_>) -> Result<()> {
let sha = option_env!("GIT_SHA").unwrap_or("main");
let revision_url = format!(
"[{}]({}/tree/{})",
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index e8cac33..b8d0381 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,4 +1,4 @@
-use crate::{Data, Error};
+use crate::client::{Data, Error};
mod general;
mod moderation;
@@ -17,24 +17,13 @@ macro_rules! cmd {
};
}
-pub fn to_vec() -> Vec<Command> {
- vec![
- cmd!(general, ask),
- cmd!(general, bing),
- cmd!(general, config),
- cmd!(general, convert),
- cmd!(general, emoji),
- cmd!(general, pfp),
- cmd!(general, random),
- cmd!(general, version),
- cmd!(moderation, clear_messages),
- cmd!(optional, copypasta),
- cmd!(optional, teawiespam),
- cmd!(optional, uwurandom),
- ]
+pub fn all() -> Vec<Command> {
+ let mut all_commands = global();
+ all_commands.append(&mut optional());
+ all_commands
}
-pub fn to_vec_global() -> Vec<Command> {
+pub fn global() -> Vec<Command> {
vec![
cmd!(general, ask),
cmd!(general, bing),
@@ -48,10 +37,6 @@ pub fn to_vec_global() -> Vec<Command> {
]
}
-pub fn to_vec_optional() -> Vec<Command> {
- vec![
- cmd!(optional, copypasta),
- cmd!(optional, teawiespam),
- cmd!(optional, uwurandom),
- ]
+pub fn optional() -> Vec<Command> {
+ vec![cmd!(optional, teawiespam), cmd!(optional, uwurandom)]
}
diff --git a/src/commands/moderation/clear_messages.rs b/src/commands/moderation/clear_messages.rs
index 8761bcb..65a30be 100644
--- a/src/commands/moderation/clear_messages.rs
+++ b/src/commands/moderation/clear_messages.rs
@@ -1,5 +1,6 @@
-use crate::{Context, Error};
+use crate::client::Context;
+use eyre::Result;
use log::debug;
use poise::serenity_prelude::GetMessages;
@@ -13,7 +14,7 @@ use poise::serenity_prelude::GetMessages;
pub async fn clear_messages(
ctx: Context<'_>,
#[description = "How many messages to delete"] num_messages: u8,
-) -> Result<(), Error> {
+) -> Result<()> {
ctx.defer_ephemeral().await?;
let channel = ctx.channel_id();
diff --git a/src/commands/optional/copypasta.rs b/src/commands/optional/copypasta.rs
deleted file mode 100644
index 06440b1..0000000
--- a/src/commands/optional/copypasta.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use crate::{Context, Error};
-
-use include_dir::{include_dir, Dir};
-use log::debug;
-
-const COPYPASTAS: Dir = include_dir!("src/copypastas");
-
-#[derive(Debug, poise::ChoiceParameter)]
-pub enum Copypasta {
- Astral,
- Dvd,
- Egrill,
- HappyMeal,
- Sus,
- TickTock,
- Twitter,
-}
-
-impl ToString for Copypasta {
- fn to_string(&self) -> String {
- let str = match self {
- Self::Astral => "astral",
- Self::Dvd => "dvd",
- Self::Egrill => "egrill",
- Self::HappyMeal => "happymeal",
- Self::Sus => "sus",
- Self::TickTock => "ticktock",
- Self::Twitter => "twitter",
- };
- str.to_string()
- }
-}
-
-impl Copypasta {
- fn contents(&self) -> Option<&str> {
- let file_name = format!("{}.txt", self.to_string());
- COPYPASTAS
- .get_file(file_name)
- .and_then(|file| file.contents_utf8())
- }
-}
-
-/// ask teawie to send funni copypasta
-#[poise::command(slash_command)]
-pub async fn copypasta(
- ctx: Context<'_>,
- #[description = "the copypasta you want to send"] copypasta: Copypasta,
-) -> Result<(), Error> {
- if let Some(guild_id) = ctx.guild_id() {
- if let Some(storage) = &ctx.data().storage {
- let settings = storage.get_guild_settings(&guild_id).await?;
-
- if !settings.optional_commands_enabled {
- debug!("Not running command in {guild_id} since it's disabled");
- ctx.reply("I'm not allowed to do that here").await?;
-
- return Ok(());
- }
- } else {
- debug!("Ignoring restrictions on command; no storage backend is attached!");
- }
- } else {
- debug!("Ignoring restrictions on command; we're not in a guild");
- }
-
- if let Some(contents) = copypasta.contents() {
- ctx.say(contents).await?;
- } else {
- ctx.reply("I couldn't find that copypasta :(").await?;
- }
-
- Ok(())
-}
diff --git a/src/commands/optional/mod.rs b/src/commands/optional/mod.rs
index 95c39bd..a3d1bd2 100644
--- a/src/commands/optional/mod.rs
+++ b/src/commands/optional/mod.rs
@@ -1,3 +1,2 @@
-pub mod copypasta;
pub mod teawiespam;
pub mod uwurandom;
diff --git a/src/commands/optional/teawiespam.rs b/src/commands/optional/teawiespam.rs
index 3a9a387..bfac852 100644
--- a/src/commands/optional/teawiespam.rs
+++ b/src/commands/optional/teawiespam.rs
@@ -1,10 +1,11 @@
-use crate::{Context, Error};
+use crate::client::Context;
+use eyre::Result;
use log::debug;
/// teawie will spam you.
#[poise::command(slash_command)]
-pub async fn teawiespam(ctx: Context<'_>) -> Result<(), Error> {
+pub async fn teawiespam(ctx: Context<'_>) -> Result<()> {
if let Some(guild_id) = ctx.guild_id() {
if let Some(storage) = &ctx.data().storage {
let settings = storage.get_guild_settings(&guild_id).await?;
diff --git a/src/commands/optional/uwurandom.rs b/src/commands/optional/uwurandom.rs
index e717d5e..c952dee 100644
--- a/src/commands/optional/uwurandom.rs
+++ b/src/commands/optional/uwurandom.rs
@@ -1,4 +1,4 @@
-use crate::{Context, Error};
+use crate::client::Context;
use eyre::Result;
use log::debug;
@@ -12,7 +12,7 @@ pub async fn uwurandom(
#[min = 1]
#[max = 2000]
length: Option<u16>,
-) -> Result<(), Error> {
+) -> Result<()> {
if let Some(guild_id) = ctx.guild_id() {
if let Some(storage) = &ctx.data().storage {
let settings = storage.get_guild_settings(&guild_id).await?;
diff --git a/src/copypastas/astral.txt b/src/copypastas/astral.txt
deleted file mode 100644
index 9984f31..0000000
--- a/src/copypastas/astral.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Today while astral projecting I summoned allah to try and weaken him so our hexing spells would work better.
-
-He is so fucking powerful. Iโ€™m not at a power level to do this alone. I barely escaped with my life and Iโ€™m spiritually injured to a great amount, but I think Iโ€™ll make it.
-
-I canโ€™t imagine what he would do to a new, unsuspecting witch. Iโ€™m scared that I will have to face him again soon if I ever want to continue astral projecting. Iโ€™m currently burning healing incense and drawing spiritual energy from my crystals to try and heal as quickly as possible.
-
-Please be safe everyone. Allah is much stronger than I first imagined and we will have to do this together if we want to slay a god.
diff --git a/src/copypastas/dvd.txt b/src/copypastas/dvd.txt
deleted file mode 100644
index 16d396b..0000000
--- a/src/copypastas/dvd.txt
+++ /dev/null
@@ -1 +0,0 @@
-๐™๐™๐™ž๐™จ ๐˜ฟ๐™ž๐™จ๐™ฃ๐™š๐™ฎ ๐˜ฟ๐™‘๐˜ฟ ๐™ž๐™จ ๐™š๐™ฃ๐™๐™–๐™ฃ๐™˜๐™š๐™™ ๐™ฌ๐™ž๐™ฉ๐™ ๐˜ฟ๐™ž๐™จ๐™ฃ๐™š๐™ฎโ€™๐™จ ๐™๐™–๐™จ๐™ฉ๐™‹๐™ก๐™–๐™ฎ. ๐™”๐™ค๐™ช๐™ง ๐™ข๐™ค๐™ซ๐™ž๐™š ๐™–๐™ฃ๐™™ ๐™– ๐™จ๐™š๐™ก๐™š๐™˜๐™ฉ๐™ž๐™ค๐™ฃ ๐™ค๐™› ๐™—๐™ค๐™ฃ๐™ช๐™จ ๐™›๐™š๐™–๐™ฉ๐™ช๐™ง๐™š๐™จ ๐™ฌ๐™ž๐™ก๐™ก ๐™—๐™š๐™œ๐™ž๐™ฃ ๐™–๐™ช๐™ฉ๐™ค๐™ข๐™–๐™ฉ๐™ž๐™˜๐™–๐™ก๐™ก๐™ฎ. ๐™๐™ค ๐™—๐™ฎ๐™ฅ๐™–๐™จ๐™จ ๐™๐™–๐™จ๐™ฉ ๐™‹๐™ก๐™–๐™ฎ, ๐™จ๐™š๐™ก๐™š๐™˜๐™ฉ ๐™ฉ๐™๐™š ๐™ˆ๐™–๐™ž๐™ฃ ๐™ˆ๐™š๐™ฃ๐™ช ๐™—๐™ช๐™ฉ๐™ฉ๐™ค๐™ฃ ๐™–๐™ฉ ๐™–๐™ฃ๐™ฎ ๐™ฉ๐™ž๐™ข๐™š. ๐™๐™–๐™จ๐™ฉ ๐™‹๐™ก๐™–๐™ฎ ๐™ฌ๐™ž๐™ก๐™ก ๐™—๐™š๐™œ๐™ž๐™ฃ ๐™ž๐™ฃ ๐™– ๐™ข๐™ค๐™ข๐™š๐™ฃ๐™ฉโ€ฆ
diff --git a/src/copypastas/egrill.txt b/src/copypastas/egrill.txt
deleted file mode 100644
index 02919b8..0000000
--- a/src/copypastas/egrill.txt
+++ /dev/null
@@ -1 +0,0 @@
-Everyone jokes about grilling, but they all forget about people like me. I have existed in a constant state of grilling since a BBQ in 1997. I have been attending this charcoal grill for twenty-two years. Do you genuinely think I can make time to argue about politics? Don't make me laugh. I have been flipping this exact burger for a third of my life. It's been decades why is it still raw. It's literally not cooking and I'm not allowed to leave. It's been over a fucking a flame for 22 years and it's still fucking frozen. What fucking vengeful god cursed me with this. help
diff --git a/src/copypastas/happymeal.txt b/src/copypastas/happymeal.txt
deleted file mode 100644
index bf52100..0000000
--- a/src/copypastas/happymeal.txt
+++ /dev/null
@@ -1 +0,0 @@
-OH MY GOD ITS 3 IN THE MORNING AND IM IN MCDONALDS AND WE JUST FOUND OUT THAT WHEN U PULL UP IN MCDONALDS AT 3 AM YOU CAN BUY THE AMONG US HAPPY MEAL WITH A TOY IN IT WHICH IS EITHER THE IMPOSTOR OR THE CREWMATE AND IF YOU DONT KNOW WHAT AMONG US IS YOU MUST BE MUST REALLY BE LIVING UNDER A ROCK ITS AN AWESOME GAME WITH IMPOSTORS AND CREWMATES AND BASICALLY THE IMPOSTOR TRIES TO SABOTAGE THE WHOLE GAME AND THE CREWMATES NEED TO STOP HIM BUT APPARENTLY WHEN YOU PURCHASE THE AMONG US HAPPY MEAL SOMETHING SCARY HAPPENS
diff --git a/src/copypastas/sus.txt b/src/copypastas/sus.txt
deleted file mode 100644
index 83deb72..0000000
--- a/src/copypastas/sus.txt
+++ /dev/null
@@ -1 +0,0 @@
-HOLY SHIT DID YOU JUST SAY THE WORD SUS???๐Ÿ˜ณ1?/1๐Ÿ˜ฑ//1๐Ÿ˜ณ/1111!!!! Wait, you don't know what it is from?๐Ÿ˜ณ๐Ÿ˜ณ๐Ÿ˜ณLet ๐Ÿ‘†give you a brief r/history. ๐Ÿ“š๐Ÿ“š๐Ÿ“š๐Ÿ‘จโ€๐Ÿš€If you didn't r/knowyourshit, the r/term sus(suspicious) is a saying from the r/popular r/game r/AmongUs. Among us is so fun๐Ÿ˜” ๐Ÿ‘‰๐Ÿ‘ˆ, don't insult it, every youtuber and streamer says so!!!!!!!11 Corpses voice is so deep am i right or am i right๐Ÿ˜ณ๐Ÿ˜ณ????? I mean Mr beast and Dream play and pull big ๐Ÿง  1000000000000 iq moves in their videos..... YOU WERE THE IMPOSTER.... เถž เถž เถž Get it because you don't know what sus means? r/stupidquestions r/youranidot r/stupidcuck. I CAnT BELEeVE YOUU dont KNoW WHT SUS MeaNS?/??!??!?!!๐Ÿ–•๐Ÿ–•๐Ÿ–•๐Ÿ–•๐Ÿ–• Man why do i have to r/explain this to a r/idiot๐Ÿคช๐Ÿคช๐Ÿคช๐Ÿ“š๐Ÿ“š๐Ÿ“š... Sus is a GREAT WORD from a GREAT VIDEO GAME. in class, YOU CAN PLAY IT ON YOUR PHONE๐Ÿ˜œ๐Ÿ˜œ๐Ÿ˜œ๐Ÿ˜œ๐Ÿ˜œ๐Ÿ˜œ??!?!? such a masterpiece... FOR THE GREAT PRICE OF FREE!!!11!๐Ÿ’ฐ๐Ÿ’ฐ๐Ÿค‘๐Ÿค‘๐Ÿค‘๐Ÿค‘๐Ÿ˜œ๐Ÿ˜œ๐Ÿ˜œ๐Ÿ’ฐ๐Ÿ’ฐ It can also mean gay ๐Ÿ˜ณ๐Ÿ˜ณ๐Ÿ˜ณ๐Ÿ˜ณ
diff --git a/src/copypastas/ticktock.txt b/src/copypastas/ticktock.txt
deleted file mode 100644
index bd4f36e..0000000
--- a/src/copypastas/ticktock.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Tick-tock
-Heavy like a Brinks truck
-Looking like I'm tip-top
-Shining like a wristwatch
-Time will grab your wrist
-Lock it down 'til the thing pop
-Can you stick around for a minute 'til the ring stop?
-Please, God
diff --git a/src/copypastas/twitter.txt b/src/copypastas/twitter.txt
deleted file mode 100644
index 883cd1b..0000000
--- a/src/copypastas/twitter.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-Twitter's Recommendation Algorithm
-
diff --git a/src/handlers/error.rs b/src/events/error.rs
index e706fec..d98bb5c 100644
--- a/src/handlers/error.rs
+++ b/src/events/error.rs
@@ -1,4 +1,7 @@
-use crate::{consts::Colors, Data, Error};
+use crate::{
+ client::{Data, Error},
+ consts::Colors,
+};
use log::error;
use poise::serenity_prelude::{CreateEmbed, Timestamp};
diff --git a/src/handlers/event/guild.rs b/src/events/guild.rs
index 774179c..06af978 100644
--- a/src/handlers/event/guild.rs
+++ b/src/events/guild.rs
@@ -1,10 +1,10 @@
+use crate::{client::Data, storage};
+use storage::settings::Settings;
+
use eyre::Result;
use log::{debug, warn};
use poise::serenity_prelude::{Guild, UnavailableGuild};
-use crate::{storage, Data};
-use storage::settings::Settings;
-
pub async fn handle_create(guild: &Guild, data: &Data) -> Result<()> {
if let Some(storage) = &data.storage {
if storage.guild_settings_exist(&guild.id).await? {
diff --git a/src/handlers/event/message.rs b/src/events/message.rs
index 67dbb21..e115eb9 100644
--- a/src/handlers/event/message.rs
+++ b/src/events/message.rs
@@ -1,4 +1,4 @@
-use crate::{consts, Data};
+use crate::{client::Data, consts};
use eyre::{eyre, Result};
use log::{debug, warn};
diff --git a/src/handlers/event/mod.rs b/src/events/mod.rs
index cc7d727..390c3a8 100644
--- a/src/handlers/event/mod.rs
+++ b/src/events/mod.rs
@@ -1,16 +1,17 @@
-use crate::{consts, Data, Error};
+use crate::{client::Data, consts};
use eyre::Result;
use log::{debug, info};
use poise::serenity_prelude::{self as serenity, CreateBotAuthParameters};
use serenity::FullEvent;
+pub mod error;
mod guild;
mod message;
mod pinboard;
mod reactboard;
-pub async fn handle(ctx: &serenity::Context, event: &FullEvent, data: &Data) -> Result<(), Error> {
+pub async fn handle(ctx: &serenity::Context, event: &FullEvent, data: &Data) -> Result<()> {
match event {
FullEvent::Ready { data_about_bot } => {
info!("Logged in as {}!", data_about_bot.user.name);
diff --git a/src/handlers/event/pinboard.rs b/src/events/pinboard.rs
index 5b7d454..bb0dfe0 100644
--- a/src/handlers/event/pinboard.rs
+++ b/src/events/pinboard.rs
@@ -1,4 +1,4 @@
-use crate::{utils, Data};
+use crate::{client::Data, utils};
use eyre::{eyre, Context as _, OptionExt as _, Result};
use log::{debug, warn};
diff --git a/src/handlers/event/reactboard.rs b/src/events/reactboard.rs
index 75fc858..c27bd80 100644
--- a/src/handlers/event/reactboard.rs
+++ b/src/events/reactboard.rs
@@ -1,4 +1,4 @@
-use crate::{storage, utils, Data};
+use crate::{client::Data, storage, utils};
use storage::reactboard::ReactBoardEntry;
use eyre::{eyre, Context as _, Result};
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
deleted file mode 100644
index 1610d23..0000000
--- a/src/handlers/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod error;
-pub mod event;
diff --git a/src/http/mod.rs b/src/http/mod.rs
new file mode 100644
index 0000000..0f16852
--- /dev/null
+++ b/src/http/mod.rs
@@ -0,0 +1,43 @@
+use eyre::Result;
+use log::trace;
+use serde::de::DeserializeOwned;
+
+pub mod shiggy;
+pub mod teawie;
+
+pub type Client = reqwest::Client;
+pub type Response = reqwest::Response;
+
+/// Primary extensions for HTTP Client
+pub trait Ext {
+ async fn get_request(&self, url: &str) -> Result<Response>;
+ async fn get_json<T: DeserializeOwned>(&self, url: &str) -> Result<T>;
+ fn default() -> Self;
+}
+
+impl Ext for Client {
+ fn default() -> Self {
+ reqwest::ClientBuilder::new()
+ .user_agent(format!(
+ "teawie-bot/{}",
+ option_env!("CARGO_PKG_VERSION").unwrap_or("development")
+ ))
+ .build()
+ .unwrap()
+ }
+
+ async fn get_request(&self, url: &str) -> Result<Response> {
+ trace!("Making request to {url}");
+ let resp = self.get(url).send().await?;
+ resp.error_for_status_ref()?;
+
+ Ok(resp)
+ }
+
+ async fn get_json<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
+ let resp = self.get_request(url).await?;
+ let json = resp.json().await?;
+
+ Ok(json)
+ }
+}
diff --git a/src/http/shiggy.rs b/src/http/shiggy.rs
new file mode 100644
index 0000000..397d397
--- /dev/null
+++ b/src/http/shiggy.rs
@@ -0,0 +1,20 @@
+use eyre::Result;
+use serde::Deserialize;
+
+const SHIGGY: &str = "https://safebooru.donmai.us";
+const RANDOM: &str = "/posts/random.json?tags=kemomimi-chan_(naga_u)+naga_u&only=file_url";
+
+#[derive(Deserialize)]
+struct SafebooruResponse {
+ file_url: String,
+}
+
+pub async fn random<T>(http: &T) -> Result<String>
+where
+ T: super::Ext,
+{
+ let url = format!("{SHIGGY}{RANDOM}");
+ let resp: SafebooruResponse = http.get_json(&url).await?;
+
+ Ok(resp.file_url)
+}
diff --git a/src/http/teawie.rs b/src/http/teawie.rs
new file mode 100644
index 0000000..368fad5
--- /dev/null
+++ b/src/http/teawie.rs
@@ -0,0 +1,28 @@
+use eyre::{bail, OptionExt, Result};
+use serde::{Deserialize, Serialize};
+
+// https://github.com/getchoo/teawieAPI
+#[derive(Deserialize, Serialize)]
+struct RandomTeawieResponse {
+ url: Option<String>,
+ error: Option<String>,
+}
+
+// TODO: read this from an env var
+const TEAWIE: &str = "https://api.getchoo.com";
+const RANDOM: &str = "/random_teawie";
+
+pub async fn random<T>(http: &T) -> Result<String>
+where
+ T: super::Ext,
+{
+ let url = format!("{TEAWIE}{RANDOM}");
+ let json: RandomTeawieResponse = http.get_json(&url).await?;
+
+ if let Some(error) = json.error {
+ bail!("TeawieAPI reported error: {error}");
+ };
+
+ json.url
+ .ok_or_eyre("TeawieAPI didn't return an error or URL???")
+}
diff --git a/src/main.rs b/src/main.rs
index 7f19b9e..e91c8e6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,72 +1,18 @@
-use std::{sync::Arc, time::Duration};
-
-use eyre::{Context as _, Report, Result};
-use log::{info, trace, warn};
-use poise::{
- serenity_prelude::{self as serenity},
- EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
-};
-use tokio::signal::ctrl_c;
-#[cfg(target_family = "unix")]
-use tokio::signal::unix::{signal, SignalKind};
-#[cfg(target_family = "windows")]
-use tokio::signal::windows::ctrl_close;
-
-mod api;
+mod client;
mod commands;
mod consts;
-mod handlers;
+mod events;
+mod http;
mod storage;
mod utils;
-use storage::Storage;
-
-type Error = Box<dyn std::error::Error + Send + Sync>;
-type Context<'a> = poise::Context<'a, Data, Error>;
-
-#[derive(Clone, Debug, Default)]
-pub struct Data {
- storage: Option<Storage>,
-}
-
-async fn setup(ctx: &serenity::Context) -> Result<Data, Error> {
- let storage = Storage::from_env().ok();
-
- if let Some(storage) = storage.as_ref() {
- if !storage.clone().is_connected() {
- return Err(
- "You specified a storage backend but there's no connection! Is it running?".into(),
- );
- }
- trace!("Storage backend connected!");
-
- poise::builtins::register_globally(ctx, &commands::to_vec_global()).await?;
- info!("Registered global commands!");
-
- // register "extra" commands in guilds that allow it
- let guilds = storage.get_opted_guilds().await?;
-
- for guild in guilds {
- poise::builtins::register_in_guild(ctx, &commands::to_vec_optional(), guild).await?;
-
- info!("Registered guild commands to {}", guild);
- }
- } else {
- warn!("No storage backend was specified. Features requiring storage will be disabled");
- warn!("Registering optional commands globally since there's no storage backend");
- poise::builtins::register_globally(ctx, &commands::to_vec()).await?;
- }
-
- let data = Data { storage };
-
- Ok(data)
-}
+use eyre::{Report, Result};
-async fn handle_shutdown(shard_manager: Arc<serenity::ShardManager>, reason: &str) {
- warn!("{reason}! Shutting down bot...");
- shard_manager.shutdown_all().await;
- println!("Everything is shutdown. Goodbye!");
-}
+use tokio::signal::ctrl_c;
+#[cfg(target_family = "unix")]
+use tokio::signal::unix::{signal, SignalKind};
+#[cfg(target_family = "windows")]
+use tokio::signal::windows::ctrl_close;
#[tokio::main]
async fn main() -> Result<()> {
@@ -74,44 +20,9 @@ async fn main() -> Result<()> {
color_eyre::install()?;
env_logger::init();
- let token = std::env::var("TOKEN").wrap_err("Couldn't find bot token in environment!")?;
-
- let intents =
- serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT;
-
- let options = FrameworkOptions {
- commands: commands::to_vec(),
- on_error: |error| Box::pin(handlers::error::handle(error)),
-
- command_check: Some(|ctx| {
- Box::pin(async move { Ok(ctx.author().id != ctx.framework().bot_id) })
- }),
-
- event_handler: |ctx, event, _framework, data| {
- Box::pin(handlers::event::handle(ctx, event, data))
- },
-
- prefix_options: PrefixFrameworkOptions {
- prefix: Some("!".into()),
- edit_tracker: Some(Arc::new(EditTracker::for_timespan(Duration::from_secs(
- 3600,
- )))),
- ..Default::default()
- },
-
- ..Default::default()
- };
-
- let framework = Framework::builder()
- .options(options)
- .setup(|ctx, _ready, _framework| Box::pin(setup(ctx)))
- .build();
-
- let mut client = serenity::ClientBuilder::new(token, intents)
- .framework(framework)
- .await?;
+ let mut client = client::get().await?;
- let shard_manager = client.shard_manager.clone();
+ let shard_manager = client.shard_manager.clone(); // We need this to shut down the bot
#[cfg(target_family = "unix")]
let mut sigterm = signal(SignalKind::terminate())?;
#[cfg(target_family = "windows")]
@@ -120,11 +31,13 @@ async fn main() -> Result<()> {
tokio::select! {
result = client.start() => result.map_err(Report::from),
_ = sigterm.recv() => {
- handle_shutdown(shard_manager, "Received SIGTERM").await;
+ client::handle_shutdown(shard_manager, "Received SIGTERM").await;
+ println!("Everything is shutdown. Goodbye!");
std::process::exit(0);
},
_ = ctrl_c() => {
- handle_shutdown(shard_manager, "Interrupted").await;
+ client::handle_shutdown(shard_manager, "Interrupted").await;
+ println!("Everything is shutdown. Goodbye!");
std::process::exit(130);
}
}
diff --git a/src/utils.rs b/src/utils.rs
index 9b642a7..3cab8c3 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,4 +1,4 @@
-use crate::{consts::Colors, Context};
+use crate::{client::Context, consts::Colors};
use color_eyre::eyre::{eyre, Result};
use poise::serenity_prelude::{self as serenity, CreateEmbedAuthor, CreateEmbedFooter};
diff --git a/treefmt.nix b/treefmt.nix
new file mode 100644
index 0000000..81102bc
--- /dev/null
+++ b/treefmt.nix
@@ -0,0 +1,11 @@
+{
+ projectRootFile = ".git/config";
+
+ programs = {
+ actionlint.enable = true;
+ deadnix.enable = true;
+ nixfmt.enable = true;
+ rustfmt.enable = true;
+ statix.enable = true;
+ };
+}