Compare commits

..

34 Commits
5.0.0 ... 5.0.6

Author SHA1 Message Date
Kwoth
d51dfa88f1 fix: Fixed .ttt and gifted strings
docs: Updated changelog
dev: Updated some packages
2024-05-14 18:36:33 +00:00
Kwoth
869b9d3b9d change: .hearthstone command will no longer show text
dev: removed html2markdown as it was only used for a fluff text of the hearthstone command
2024-05-13 19:09:35 +00:00
Kwoth
f4b26c5b40 dev: removed docker_compose.yml as you don't need redis.
dev: Removed aws package as we don't need gencmdlist to upload commands anymore, have to find a new way though.
2024-05-13 17:34:06 +00:00
Kwoth
15f629ec53 dev: Using built in rng.Shuffle, using some new .net types, removed some transactions 2024-05-13 17:14:35 +00:00
Kwoth
9406a9cc34 dev: Using collection expressions, no functional change 2024-05-13 14:54:24 +00:00
Kwoth
52438f45e1 dev: Added argumentoutofrange static methods, no functional change 2024-05-13 14:50:55 +00:00
Kwoth
7b2ce072ee dev: removed some unused code 2024-05-13 14:26:45 +00:00
Kwoth
89d93dcffb docs: updated command strings for .repeat command to include targetting channels 2024-05-13 14:18:56 +00:00
Kwoth
0fb34b1c61 docs: Updated changelog 2024-05-13 10:53:47 +00:00
Kwoth
980a6b0af8 dev: removed crowdin.yml 2024-05-13 10:48:55 +00:00
Kwoth
5c7a467caa Merge branch 'v5' of https://gitlab.com/kwoth/nadekobot into v5 2024-05-13 10:48:18 +00:00
Kwoth
35fd5c415d fix: Blackjack and .pick command responses will no longer reply as the original message will always be deleted 2024-05-13 10:48:05 +00:00
Kwoth
2762108986 Update Crowdin configuration file 2024-05-13 06:49:15 +00:00
Kwoth
876d63fd8b fix: Fixed a blackjack string usage 2024-05-13 06:32:46 +00:00
Kwoth
263ef4b47f fix: Fixed blackjack strings 2024-05-13 06:22:48 +00:00
Kwoth
1c540476d3 dev: Fixed a build warning and small cleanup 2024-05-12 19:00:16 +00:00
Kwoth
1d0f3d3fd6 docs: Removed ubuntu 16.04 and 18.04 from supoported version as there are issues installing .net8 there due to glibc version 2024-05-11 10:49:53 +00:00
Kwoth
c7cec25a29 change: .greet / .bye will get properly disabled if the bot can't write to the specified channel 2024-05-11 08:08:48 +00:00
Kwoth
23c8dc00e8 dev: Upped version to 5.0.5 2024-05-11 08:02:59 +00:00
Kwoth
0da8190637 fix: repeat, greet, bye, boost messages can now once again mention anyone
fix: .say #channel fixed
2024-05-11 08:00:48 +00:00
Kwoth
d766295286 docs: Updated supported distros (thx cata) 2024-05-11 05:57:31 +00:00
Kwoth
21e3c64e01 Merge branch 'v5' of https://gitlab.com/kwoth/nadekobot into v5 2024-05-11 02:30:40 +00:00
Kwoth
9ddcd6d89e docs: Updated changelog 2024-05-11 02:05:56 +00:00
Kwoth
a154d5881c docs: Updated changelog 2024-05-10 15:36:12 +00:00
Kwoth
fb594e50fd fix: .h fixed, .xp fixed, pagination in .lb fixed 2024-05-10 15:29:04 +00:00
Kwoth
75c5a003bf fix: cleverbot:response should now be a valid module name 2024-05-10 09:53:20 +00:00
Kwoth
6f4cbddcad dev: Changed some namespaces. Pushing tags, Releasing 5.0.3 2024-05-10 05:56:02 +00:00
Kwoth
40528150f7 dev: Revert links back in the code, check for updates in the original nadekobot project 2024-05-10 04:58:25 +00:00
Kwoth
2b80823a1b docs: revert links back to kwoth/nadekobot as gitlab's organization thing is a ripoff 2024-05-10 04:56:26 +00:00
Kwoth
37ee769119 docs: Changed changelog version to 5.0.2 to avoid confusion 2024-05-10 02:56:14 +00:00
Kwoth
cfd39ebbd5 fix: windows exe shouldn't ask for .net to be installed anymore
fix: Version number is now correct
docs: Updated .net version name in the docs
2024-05-10 02:05:35 +00:00
Kwoth
0532b30f7f docs: updated docs to point to the new installer location 2024-05-09 14:17:25 +00:00
Kwoth
b92a38f49d docs: Changelog updated 2024-05-09 13:37:38 +00:00
Kwoth
81711c557a ci: give up on single file, try to fix docker, medusa builds 2024-05-09 13:30:02 +00:00
124 changed files with 639 additions and 912 deletions

View File

@@ -30,12 +30,12 @@ variables:
build: build:
stage: build stage: build
script: script:
- "dotnet publish -c Release -r linux-x64 --self-contained -p:PublishSingleFile=true -o $LINUX_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj" - "dotnet publish -c Release -r linux-x64 --self-contained -o $LINUX_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r linux-arm64 --self-contained -p:PublishSingleFile=true -o $LINUX_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj" - "dotnet publish -c Release -r linux-arm64 --self-contained -o $LINUX_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o $WIN_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj" - "dotnet publish -c Release -r win-x64 --self-contained -o $WIN_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-arm64 --self-contained -p:PublishSingleFile=true -o $WIN_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj" - "dotnet publish -c Release -r win-arm64 --self-contained -o $WIN_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-x64 --self-contained -p:PublishSingleFile=true -o $MACOS_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj" - "dotnet publish -c Release -r osx-x64 --self-contained -o $MACOS_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-arm64 --self-contained -p:PublishSingleFile=true -o $MACOS_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj" - "dotnet publish -c Release -r osx-arm64 --self-contained -o $MACOS_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
artifacts: artifacts:
paths: paths:
- "$LINUX_X64_OUTPUT_DIR/" - "$LINUX_X64_OUTPUT_DIR/"
@@ -81,7 +81,7 @@ release:
- if: $CI_COMMIT_TAG - if: $CI_COMMIT_TAG
script: script:
- | - |
release-cli create --name "NadekoBot v$CI_COMMIT_TAG" --description "## [Changelog](https://gitlab.com/nadeko/nadekobot/-/blob/v5/CHANGELOG.md#$(echo "$CI_COMMIT_TAG" | sed "s/\.//g")-$(date +%d%m%Y))" --tag-name $CI_COMMIT_TAG \ release-cli create --name "NadekoBot v$CI_COMMIT_TAG" --description "## [Changelog](https://gitlab.com/kwoth/nadekobot/-/blob/v5/CHANGELOG.md#$(echo "$CI_COMMIT_TAG" | sed "s/\.//g")-$(date +%d%m%Y))" --tag-name $CI_COMMIT_TAG \
--assets-link "{\"name\":\"${LINUX_X64_RELEASE}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${LINUX_X64_RELEASE}\"}" \ --assets-link "{\"name\":\"${LINUX_X64_RELEASE}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${LINUX_X64_RELEASE}\"}" \
--assets-link "{\"name\":\"${LINUX_ARM64_RELEASE}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${LINUX_ARM64_RELEASE}\"}" \ --assets-link "{\"name\":\"${LINUX_ARM64_RELEASE}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${LINUX_ARM64_RELEASE}\"}" \
--assets-link "{\"name\":\"${WIN_X64_RELEASE}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${WIN_X64_RELEASE}\"}" \ --assets-link "{\"name\":\"${WIN_X64_RELEASE}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${WIN_X64_RELEASE}\"}" \
@@ -113,7 +113,7 @@ build-installer:
script: script:
- dotnet clean - dotnet clean
- dotnet restore -f --no-cache -v n - dotnet restore -f --no-cache -v n
- dotnet publish -c Release -p:PublishSingleFile=true --runtime win-x64 /p:Version=$CI_COMMIT_TAG src/NadekoBot - dotnet publish -c Release --self-contained --runtime win-x64 /p:Version=$CI_COMMIT_TAG src/NadekoBot
- $env:NADEKOBOT_INSTALL_VERSION = $CI_COMMIT_TAG - $env:NADEKOBOT_INSTALL_VERSION = $CI_COMMIT_TAG
- iscc.exe "/O+" ".\exe_builder.iss" - iscc.exe "/O+" ".\exe_builder.iss"
tags: tags:
@@ -121,11 +121,12 @@ build-installer:
publish-medusa-package: publish-medusa-package:
stage: publish-medusa-package stage: publish-medusa-package
allow_failure: true
rules: rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG
script: script:
- LAST_TAG=$(git describe --tags --abbrev=0) - LAST_TAG=$(git describe --tags --abbrev=0)
- if [ $CI_COMMIT_TAG ];then MEDUSA_VERSION="$CI_COMMIT_TAG"; else MEDUSA_VERSION="$LAST_TAG+$CI_COMMIT_SHORT_SHA"; fi - if [ $CI_COMMIT_TAG ];then MEDUSA_VERSION="$CI_COMMIT_TAG"; else MEDUSA_VERSION="$LAST_TAG-alpha$CI_COMMIT_SHORT_SHA"; fi
- cd src/Nadeko.Medusa/ - cd src/Nadeko.Medusa/
- dotnet pack -c Release /p:Version=$MEDUSA_VERSION -o bin/Release/packed - dotnet pack -c Release /p:Version=$MEDUSA_VERSION -o bin/Release/packed
- dotnet nuget push bin/Release/packed/ --source https://www.myget.org/F/nadeko/api/v2/package --api-key "$MYGET_API_KEY" - dotnet nuget push bin/Release/packed/ --source https://www.myget.org/F/nadeko/api/v2/package --api-key "$MYGET_API_KEY"

View File

@@ -2,7 +2,27 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [5.0.0] ## [5.0.6] - 14.05.2024
### Changed
- `.greet` and `.bye` will now be automatically disabled if the bot losses permissions to post in the specified channel
- Removed response replies from `.blackjack` and `.pick` as the original message will always be deleted
### Fixed
- Fixed `.blackjack` response string as it contained no user name
- Fixed `.ttt` and `.gift` strings not mentioning the user
## [5.0.5] - 11.05.2024
### Fixed
- `%server.members%` placeholder fixed
- `.say #channel <message>` should now be working properly again
- `.repeat`, `.greet`, `.bye` and `.boost` command can now once again mention anyone
## [5.0.4] - 10.05.2024
### Added ### Added
@@ -71,7 +91,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except da
- Removed log voice presence TTS - Removed log voice presence TTS
- Cleanup: Removed a lot of obsolete aliases from aliases.yml - Cleanup: Removed a lot of obsolete aliases from aliases.yml
## [4.3.22] - 23.04.2023 ## [4.3.22] - 23.04.2024
### Added ### Added
- Added `.setbanner` command (thx cata) - Added `.setbanner` command (thx cata)
@@ -80,7 +100,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except da
- Fixed pagination error due to a missing emoji - Fixed pagination error due to a missing emoji
## [4.3.21] - 19.04.2023 ## [4.3.21] - 19.04.2024
### Fixed ### Fixed
- Possible fix for a duplicate in `.h bank` - Possible fix for a duplicate in `.h bank`

View File

@@ -27,7 +27,7 @@ RUN set -xe; \
useradd -m nadeko; \ useradd -m nadeko; \
apt-get update; \ apt-get update; \
apt-get install -y --no-install-recommends libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 sudo; \ apt-get install -y --no-install-recommends libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 sudo; \
update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1; \ update-alternatives --install /usr/local/bin/python python /usr/bin/python3.9 1; \
echo 'Defaults>nadeko env_keep+="ASPNETCORE_* DOTNET_* NadekoBot_* shard_id total_shards TZ"' > /etc/sudoers.d/nadeko; \ echo 'Defaults>nadeko env_keep+="ASPNETCORE_* DOTNET_* NadekoBot_* shard_id total_shards TZ"' > /etc/sudoers.d/nadeko; \
curl -Lo /usr/local/bin/yt-dlp https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp; \ curl -Lo /usr/local/bin/yt-dlp https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp; \
chmod a+rx /usr/local/bin/yt-dlp; \ chmod a+rx /usr/local/bin/yt-dlp; \

View File

@@ -1,23 +0,0 @@
version: "3.7"
services:
nadeko:
image: insert-image-name-here:latest
depends_on:
- redis
environment:
TZ: Europe/Paris
NadekoBot_RedisOptions: redis,name=nadeko
#NadekoBot_ShardRunCommand: dotnet
#NadekoBot_ShardRunArguments: /app/NadekoBot.dll {0} {1}
volumes:
- /srv/nadeko/conf:/app/conf:ro
- /srv/nadeko/data:/app/data
redis:
image: redis:4-alpine
sysctls:
- net.core.somaxconn=511
command: redis-server --maxmemory 32M --maxmemory-policy volatile-lru
volumes:
- /srv/nadeko/redis-data:/data

View File

@@ -1,6 +1,6 @@
# How to contribute # How to contribute
1. Make Merge Requests to the [**v5 branch**](https://gitlab.com/nadeko/nadekobot/tree/v5) 1. Make Merge Requests to the [**v5 branch**](https://gitlab.com/kwoth/nadekobot/tree/v5)
2. Keep a single Merge Request to a single feature 2. Keep a single Merge Request to a single feature
3. Fill out the MR template 3. Fill out the MR template

View File

@@ -30,7 +30,7 @@ You can also donate to us through [PayPal][paypal] for one-time donations using
[![img][paypal-button]][paypal] [![img][paypal-button]][paypal]
[gitlab]: https://gitlab.com/nadeko/nadekobot [gitlab]: https://gitlab.com/kwoth/nadekobot
[discord-server]: https://discord.nadeko.bot/ [discord-server]: https://discord.nadeko.bot/
[patreon]: https://www.patreon.com/nadekobot [patreon]: https://www.patreon.com/nadekobot
[patreon-button]: ./assets/patreon.png [patreon-button]: ./assets/patreon.png

View File

@@ -14,7 +14,7 @@
version: "3.7" version: "3.7"
services: services:
nadeko: nadeko:
image: registry.gitlab.com/nadeko/nadekobot:latest image: registry.gitlab.com/kwoth/nadekobot:latest
depends_on: depends_on:
- redis - redis
environment: environment:

View File

@@ -15,12 +15,13 @@
##### Compatible operating systems: ##### Compatible operating systems:
- Ubuntu: 18.04, 20.04, 22.04, 22.10 - Ubuntu: 20.04, 21.04, 21.10, 22.04
- Mint: 19, 20, 21 - Mint: 19, 20
- Debian: 10, 11 - Debian: 10, 11, 12
- CentOS: 7 - RockyLinux: 8, 9
- openSUSE 15 - AlmaLinux: 8, 9
- ~~Fedora: 33, 34, 35~~ (Fedora is Pending Support) - openSUSE Leap: 15.5, 15.6 & Tumbleweed
- Fedora: 38, 39, 40, 41, 42
## Linux From Source ## Linux From Source
@@ -28,7 +29,7 @@
Follow the following few steps only if you're migrating from v3. If not, skip to installation instructions. Follow the following few steps only if you're migrating from v3. If not, skip to installation instructions.
Use the new installer script: `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh` Use the new installer script: `cd ~ && wget -N https://gitlab.com/kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh`
> - Install prerequisites (type `1` and press `enter`) > - Install prerequisites (type `1` and press `enter`)
> - Download (type `2` and press `enter`) > - Download (type `2` and press `enter`)
> - Run (type `3` and press `enter`) > - Run (type `3` and press `enter`)
@@ -38,7 +39,7 @@ Use the new installer script: `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-
Open Terminal (if you're on an installation with a window manager) and navigate to the location where you want to install the bot (for example `cd ~`) Open Terminal (if you're on an installation with a window manager) and navigate to the location where you want to install the bot (for example `cd ~`)
1. Download and run the **new** installer script `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh` 1. Download and run the **new** installer script `cd ~ && wget -N https://gitlab.com/kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh`
2. Install prerequisites (type `1` and press enter) 2. Install prerequisites (type `1` and press enter)
3. Download the bot (type `2` and press enter) 3. Download the bot (type `2` and press enter)
4. Exit the installer (type `6` and press enter) 4. Exit the installer (type `6` and press enter)
@@ -50,13 +51,13 @@ Open Terminal (if you're on an installation with a window manager) and navigate
- `CTRL` + `X` - `CTRL` + `X`
- `Y` - `Y`
- `Enter` - `Enter`
8. Run the installer script again `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh` 8. Run the installer script again `cd ~ && wget -N https://gitlab.com/kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh`
9. Run the bot (type `3` and press enter) 9. Run the bot (type `3` and press enter)
##### Source Update Instructions ##### Source Update Instructions
1. ⚠ Stop the bot ⚠ 1. ⚠ Stop the bot ⚠
2. Update and run the **new** installer script `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh` 2. Update and run the **new** installer script `cd ~ && wget -N https://gitlab.com/kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh`
3. Update the bot (type `2` and press enter) 3. Update the bot (type `2` and press enter)
4. Run the bot (type `3` and press enter) 4. Run the bot (type `3` and press enter)
5. 🎉 5. 🎉
@@ -74,11 +75,11 @@ Open Terminal (if you're on an installation with a window manager) and navigate
3. Make sure your python is version 3+ with `python --version` 3. Make sure your python is version 3+ with `python --version`
- if it's not, you can install python 3 and make it the default with: `sudo apt-get install python3.8 python-is-python3` - if it's not, you can install python 3 and make it the default with: `sudo apt-get install python3.8 python-is-python3`
*You can use nadeko bash script [prerequisites installer](https://gitlab.com/Kwoth/nadeko-bash-installer/-/blob/v5/n-prereq.sh) as a reference* *You can use nadeko bash script [prerequisites installer](https://gitlab.com/kwoth/nadeko-bash-installer/-/blob/v5/n-prereq.sh) as a reference*
##### Installation Instructions ##### Installation Instructions
1. Download the latest release from <https://gitlab.com/nadeko/nadekobot/-/releases> 1. Download the latest release from <https://gitlab.com/kwoth/nadekobot/-/releases>
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it - Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
2. Untar it 2. Untar it
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1! - ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
@@ -102,7 +103,7 @@ Open Terminal (if you're on an installation with a window manager) and navigate
##### Release Update Instructions ##### Release Update Instructions
1. Stop the bot 1. Stop the bot
2. Download the latest release from <https://gitlab.com/nadeko/nadekobot/-/releases> 2. Download the latest release from <https://gitlab.com/kwoth/nadekobot/-/releases>
- Look for the file called "x.x.x-linux-x64-build.tar" (where `X.X.X` is a version, for example 3.0.4) and download it - Look for the file called "x.x.x-linux-x64-build.tar" (where `X.X.X` is a version, for example 3.0.4) and download it
3. Untar it 3. Untar it
- ⚠ Make sure that you change `X.X.X` to the same series of numbers as in step 2! - ⚠ Make sure that you change `X.X.X` to the same series of numbers as in step 2!

View File

@@ -31,7 +31,7 @@ sudo ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
##### Installation Instructions ##### Installation Instructions
1. Download and run the **new** installer script `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh` 1. Download and run the **new** installer script `cd ~ && wget -N https://gitlab.com/kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh`
2. Install prerequisites (type `1` and press enter) 2. Install prerequisites (type `1` and press enter)
3. Download the bot (type `2` and press enter) 3. Download the bot (type `2` and press enter)
4. Exit the installer in order to set up your `creds.yml` 4. Exit the installer in order to set up your `creds.yml`
@@ -49,7 +49,7 @@ sudo ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
##### Update Instructions ##### Update Instructions
1. ⚠ Stop the bot 1. ⚠ Stop the bot
2. Update and run the **new** installer script `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh` 2. Update and run the **new** installer script `cd ~ && wget -N https://gitlab.com/kwoth/nadeko-bash-installer/-/raw/v5/linuxAIO.sh && bash linuxAIO.sh`
3. Update the bot (type `2` and press enter) 3. Update the bot (type `2` and press enter)
4. Run the bot (type `3` and press enter) 4. Run the bot (type `3` and press enter)
5. 🎉 5. 🎉
@@ -60,7 +60,7 @@ sudo ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
##### Installation Instructions ##### Installation Instructions
1. Download the latest release from <https://gitlab.com/nadeko/nadekobot/-/releases> 1. Download the latest release from <https://gitlab.com/kwoth/nadekobot/-/releases>
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it - Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
2. Untar it 2. Untar it
⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1! ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
@@ -84,7 +84,7 @@ sudo ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
##### Update Instructions ##### Update Instructions
1. Stop the bot 1. Stop the bot
2. Download the latest release from <https://gitlab.com/nadeko/nadekobot/-/releases> 2. Download the latest release from <https://gitlab.com/kwoth/nadekobot/-/releases>
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it - Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
3. Untar it 3. Untar it
⚠ Make sure that you change X.XX.X to the same series of numbers as in step 2! ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 2!

View File

@@ -67,7 +67,7 @@ You can still install them manually:
##### Prerequisites ##### Prerequisites
**Install these before proceeding or your bot will not work!** **Install these before proceeding or your bot will not work!**
- [.net 7](https://dotnet.microsoft.com/en-us/download) - needed to compile and run the bot - [.net 8](https://dotnet.microsoft.com/en-us/download) - needed to compile and run the bot
- [git](https://git-scm.com/downloads) - needed to clone the repository (you can also download the zip manually and extract it, but this guide assumes you're using git) - [git](https://git-scm.com/downloads) - needed to clone the repository (you can also download the zip manually and extract it, but this guide assumes you're using git)
- [Redis] (OPTIONAL)- to cache things needed by some features and persist through restarts - [Redis] (OPTIONAL)- to cache things needed by some features and persist through restarts
@@ -75,7 +75,7 @@ You can still install them manually:
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and navigate to the location where you want to install the bot (for example `cd ~/Desktop/`) Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and navigate to the location where you want to install the bot (for example `cd ~/Desktop/`)
1. `git clone https://gitlab.com/nadeko/nadekobot -b v5 --depth 1` 1. `git clone https://gitlab.com/kwoth/nadekobot -b v5 --depth 1`
2. `cd nadekobot` 2. `cd nadekobot`
3. `dotnet publish -c Release -o output/ src/NadekoBot/` 3. `dotnet publish -c Release -o output/ src/NadekoBot/`
4. `cd output` 4. `cd output`
@@ -93,7 +93,11 @@ Open PowerShell as described above and run the following commands:
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully - ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
2. Navigate to your bot's folder, example: 2. Navigate to your bot's folder, example:
- `cd ~/Desktop/nadekobot` - `cd ~/Desktop/nadekobot`
3. Pull the new version 3. Pull the new version, and make sure you're on the v5 branch
- *⚠️ the first 3 lines can be omitted if you're already on v5. If you're updating from v4, you must run them*
- `git remote set-branches origin '*'`
- `git fetch -v --depth=1`
- `git checkout v5`
- `git pull` - `git pull`
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts - ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
4. **Backup** old output in case your data is overwritten 4. **Backup** old output in case your data is overwritten

View File

@@ -36,6 +36,6 @@ If you're unsure whether something is an issue, ask in our support server first.
[macos-guide]: ./guides/osx-guide.md [macos-guide]: ./guides/osx-guide.md
[from-source-guide]: ./guides/from-source.md [from-source-guide]: ./guides/from-source.md
[discord-server]: https://discord.nadeko.bot/ [discord-server]: https://discord.nadeko.bot/
[gitlab]: https://gitlab.com/nadeko/nadekobot [gitlab]: https://gitlab.com/kwoth/nadekobot
[issues]: https://gitlab.com/nadeko/nadekobot/issues [issues]: https://gitlab.com/kwoth/nadekobot/issues
[donate]: ./donate.md [donate]: ./donate.md

View File

@@ -8,7 +8,7 @@ Medusa system allows you to write independent medusae (known as "modules", "cogs
The system itself borrows some design from the current way Nadeko's Modules are written but mostly from never-released `Ayu.Commands` system which was designed to be used for a full Nadeko v3 rewrite. The system itself borrows some design from the current way Nadeko's Modules are written but mostly from never-released `Ayu.Commands` system which was designed to be used for a full Nadeko v3 rewrite.
The medusa base classes used for development are open source [here](https://gitlab.com/nadeko/nadekobot/-/tree/v5/src/Nadeko.Medusa) in case you need reference, as there is no generated documentation at the moment. The medusa base classes used for development are open source [here](https://gitlab.com/kwoth/nadekobot/-/tree/v5/src/Nadeko.Medusa) in case you need reference, as there is no generated documentation at the moment.
### Term list ### Term list
@@ -161,7 +161,7 @@ This section will guide you through how to create a simple custom medusa. You ca
</PackageReference> </PackageReference>
<!-- Note: If you want to use NadekoBot services etc... You will have to manually clone <!-- Note: If you want to use NadekoBot services etc... You will have to manually clone
the https://gitlab.com/nadeko/nadekobot repo locally and reference the NadekoBot.csproj because there is no NadekoBot package atm. the https://gitlab.com/kwoth/nadekobot repo locally and reference the NadekoBot.csproj because there is no NadekoBot package atm.
It is strongly recommended that you checkout a specific tag which matches your version of nadeko, It is strongly recommended that you checkout a specific tag which matches your version of nadeko,
as there could be breaking changes even between minor versions of NadekoBot. as there could be breaking changes even between minor versions of NadekoBot.
For example if you're running NadekoBot 4.1.0 locally for which you want to create a medusa for, For example if you're running NadekoBot 4.1.0 locally for which you want to create a medusa for,

View File

@@ -11,7 +11,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net.Core" Version="3.204.0" /> <PackageReference Include="Discord.Net.Core" Version="3.204.0" />
<PackageReference Include="Serilog" Version="3.1.1" /> <PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="YamlDotNet" Version="15.1.2" /> <PackageReference Include="YamlDotNet" Version="15.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Version)' == '' "> <PropertyGroup Condition=" '$(Version)' == '' ">

View File

@@ -14,7 +14,7 @@
<PackageReference Include="Serilog" Version="3.1.1" /> <PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="YamlDotNet" Version="15.1.2" /> <PackageReference Include="YamlDotNet" Version="15.1.4" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -417,8 +417,7 @@ namespace NadekoBot.Coordinator
{ {
lock (locker) lock (locker)
{ {
if (shardId >= _shardStatuses.Length) ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(shardId, _shardStatuses.Length);
throw new ArgumentOutOfRangeException(nameof(shardId));
return _shardStatuses[shardId]; return _shardStatuses[shardId];
} }

View File

@@ -80,10 +80,10 @@ public sealed class Bot : IBot
// _interactionService = new(Client.Rest); // _interactionService = new(Client.Rest);
Client.Log += Client_Log; Client.Log += Client_Log;
_loadedAssemblies = new[] _loadedAssemblies =
{ [
typeof(Bot).Assembly, // bot typeof(Bot).Assembly // bot
}; ];
} }

View File

@@ -7,19 +7,20 @@ namespace NadekoBot.Db;
public static class GuildConfigExtensions public static class GuildConfigExtensions
{ {
private static List<WarningPunishment> DefaultWarnPunishments private static List<WarningPunishment> DefaultWarnPunishments
=> new() =>
{ [
new() new()
{ {
Count = 3, Count = 3,
Punishment = PunishmentAction.Kick Punishment = PunishmentAction.Kick
}, },
new() new()
{ {
Count = 5, Count = 5,
Punishment = PunishmentAction.Ban Punishment = PunishmentAction.Ban
} }
}; ];
/// <summary> /// <summary>
/// Gets full stream role settings for the guild with the specified id. /// Gets full stream role settings for the guild with the specified id.

View File

@@ -22,8 +22,7 @@ public static class WarningExtensions
string mod, string mod,
int index) int index)
{ {
if (index < 0) ArgumentOutOfRangeException.ThrowIfNegative(index);
throw new ArgumentOutOfRangeException(nameof(index));
var warn = warnings.AsQueryable() var warn = warnings.AsQueryable()
.Where(x => x.GuildId == guildId && x.UserId == userId) .Where(x => x.GuildId == guildId && x.UserId == userId)

View File

@@ -33,10 +33,7 @@ public class Permissionv2 : DbEntity, IIndexed
}; };
public static List<Permissionv2> GetDefaultPermlist public static List<Permissionv2> GetDefaultPermlist
=> new() => [AllowAllPerm];
{
AllowAllPerm
};
} }
public enum PrimaryPermissionType public enum PrimaryPermissionType

View File

@@ -97,7 +97,7 @@ public class GreetService : INService, IReadyExecutor
{ {
var newContent = await _repSvc.ReplaceAsync(toSend, var newContent = await _repSvc.ReplaceAsync(toSend,
new(client: _client, guild: user.Guild, channel: channel, users: user)); new(client: _client, guild: user.Guild, channel: channel, users: user));
var toDelete = await _sender.Response(channel).Text(newContent).SendAsync(); var toDelete = await _sender.Response(channel).Text(newContent).Sanitize(false).SendAsync();
if (conf.BoostMessageDeleteAfter > 0) if (conf.BoostMessageDeleteAfter > 0)
toDelete.DeleteAfter(conf.BoostMessageDeleteAfter); toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
@@ -202,12 +202,6 @@ public class GreetService : INService, IReadyExecutor
if (!users.Any()) if (!users.Any())
return; return;
// var rep = new ReplacementBuilder().WithChannel(channel)
// .WithClient(_client)
// .WithServer(_client, (SocketGuild)channel.Guild)
// .WithManyUsers(users)
// .Build();
var repCtx = new ReplacementContext(client: _client, var repCtx = new ReplacementContext(client: _client,
guild: channel.Guild, guild: channel.Guild,
channel: channel, channel: channel,
@@ -217,11 +211,12 @@ public class GreetService : INService, IReadyExecutor
text = await _repSvc.ReplaceAsync(text, repCtx); text = await _repSvc.ReplaceAsync(text, repCtx);
try try
{ {
var toDelete = await _sender.Response(channel).Text(text).SendAsync(); var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
if (conf.AutoDeleteByeMessagesTimer > 0) if (conf.AutoDeleteByeMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
|| ex.DiscordCode == DiscordErrorCode.MissingPermissions
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel) || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{ {
Log.Warning(ex, Log.Warning(ex,
@@ -243,26 +238,21 @@ public class GreetService : INService, IReadyExecutor
if (users.Count == 0) if (users.Count == 0)
return; return;
// var rep = new ReplacementBuilder()
// .WithChannel(channel)
// .WithClient(_client)
// .WithServer(_client, (SocketGuild)channel.Guild)
// .WithManyUsers(users)
// .Build();
var repCtx = new ReplacementContext(client: _client, var repCtx = new ReplacementContext(client: _client,
guild: channel.Guild, guild: channel.Guild,
channel: channel, channel: channel,
users: users.ToArray()); users: users.ToArray());
var text = SmartText.CreateFrom(conf.ChannelGreetMessageText); var text = SmartText.CreateFrom(conf.ChannelGreetMessageText);
text = await _repSvc.ReplaceAsync(text, repCtx); text = await _repSvc.ReplaceAsync(text, repCtx);
try try
{ {
var toDelete = await _sender.Response(channel).Text(text).SendAsync(); var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
if (conf.AutoDeleteGreetMessagesTimer > 0) if (conf.AutoDeleteGreetMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
|| ex.DiscordCode == DiscordErrorCode.MissingPermissions
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel) || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{ {
Log.Warning(ex, Log.Warning(ex,
@@ -329,13 +319,13 @@ public class GreetService : INService, IReadyExecutor
// if there are no embeds, add an embed with the footer // if there are no embeds, add an embed with the footer
smartText = seta with smartText = seta with
{ {
Embeds = new[] Embeds =
{ [
new SmartEmbedArrayElementText() new SmartEmbedArrayElementText()
{ {
Footer = CreateFooterSource(user) Footer = CreateFooterSource(user)
} }
} ]
}; };
} }
else else
@@ -360,7 +350,7 @@ public class GreetService : INService, IReadyExecutor
} }
} }
await _sender.Response(user).Text(smartText).SendAsync(); await _sender.Response(user).Text(smartText).Sanitize(false).SendAsync();
} }
catch catch
{ {
@@ -573,8 +563,6 @@ public class GreetService : INService, IReadyExecutor
public bool SetBoostMessage(ulong guildId, ref string message) public bool SetBoostMessage(ulong guildId, ref string message)
{ {
message = message.SanitizeMentions();
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set); var conf = uow.GuildConfigsForId(guildId, set => set);
conf.BoostMessage = message; conf.BoostMessage = message;

View File

@@ -175,7 +175,7 @@ public sealed class SomethingOnlyChannelService : IExecOnMessage
// ignore owner and admin // ignore owner and admin
if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator) if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator)
{ {
Log.Information("{Type}-Only Channel: Ignoring owner od admin ({ChannelId})", type, msg.Channel.Id); Log.Information("{Type}-Only Channel: Ignoring owner or admin ({ChannelId})", type, msg.Channel.Id);
return false; return false;
} }

View File

@@ -291,8 +291,7 @@ public class MuteService : INService
public async Task<IRole> GetMuteRole(IGuild guild) public async Task<IRole> GetMuteRole(IGuild guild)
{ {
if (guild is null) ArgumentNullException.ThrowIfNull(guild);
throw new ArgumentNullException(nameof(guild));
const string defaultMuteRoleName = "nadeko-mute"; const string defaultMuteRoleName = "nadeko-mute";

View File

@@ -69,8 +69,7 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
public async Task<string> RemovePlayingAsync(int index) public async Task<string> RemovePlayingAsync(int index)
{ {
if (index < 0) ArgumentOutOfRangeException.ThrowIfNegative(index);
throw new ArgumentOutOfRangeException(nameof(index));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var toRemove = await uow.Set<RotatingPlayingStatus>().AsQueryable().AsNoTracking().Skip(index).FirstOrDefaultAsync(); var toRemove = await uow.Set<RotatingPlayingStatus>().AsQueryable().AsNoTracking().Skip(index).FirstOrDefaultAsync();

View File

@@ -22,8 +22,7 @@ public class PruneService : INService
ArgumentNullException.ThrowIfNull(channel, nameof(channel)); ArgumentNullException.ThrowIfNull(channel, nameof(channel));
var originalAmount = amount; var originalAmount = amount;
if (amount <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
using var cancelSource = new CancellationTokenSource(); using var cancelSource = new CancellationTokenSource();
if (!_pruningGuilds.TryAdd(channel.GuildId, cancelSource)) if (!_pruningGuilds.TryAdd(channel.GuildId, cancelSource))

View File

@@ -250,11 +250,9 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
int group = 0, int group = 0,
int levelReq = 0) int levelReq = 0)
{ {
if (group < 0) ArgumentOutOfRangeException.ThrowIfNegative(group);
throw new ArgumentOutOfRangeException(nameof(group));
if (levelReq < 0) ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
throw new ArgumentOutOfRangeException(nameof(group));
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
@@ -307,10 +305,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
lock (_cacheLock) lock (_cacheLock)
{ {
_cache.AddOrUpdate(msg.Id, _cache.AddOrUpdate(msg.Id,
_ => new() _ => [obj],
{
obj
},
(_, list) => (_, list) =>
{ {
list.RemoveAll(x => x.Emote == emote); list.RemoveAll(x => x.Emote == emote);

View File

@@ -19,7 +19,7 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private const string RELEASES_URL = "https://gitlab.com/api/v4/projects/57687445/releases"; private const string RELEASES_URL = "https://gitlab.com/api/v4/projects/9321079/releases";
public CheckForUpdatesService( public CheckForUpdatesService(
BotConfigService bcs, BotConfigService bcs,
@@ -72,7 +72,7 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
UpdateLastKnownVersion(latestVersion); UpdateLastKnownVersion(latestVersion);
// pull changelog // pull changelog
var changelog = await http.GetStringAsync("https://gitlab.com/nadeko/nadekobot/-/raw/v5/CHANGELOG.md"); var changelog = await http.GetStringAsync("https://gitlab.com/kwoth/nadekobot/-/raw/v5/CHANGELOG.md");
var thisVersionChangelog = GetVersionChangelog(latestVersion, changelog); var thisVersionChangelog = GetVersionChangelog(latestVersion, changelog);
@@ -95,7 +95,7 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
.WithOkColor() .WithOkColor()
.WithAuthor($"NadekoBot v{latest} Released!") .WithAuthor($"NadekoBot v{latest} Released!")
.WithTitle("Changelog") .WithTitle("Changelog")
.WithUrl("https://gitlab.com/nadeko/nadekobot/-/blob/v5/CHANGELOG.md") .WithUrl("https://gitlab.com/kwoth/nadekobot/-/blob/v5/CHANGELOG.md")
.WithDescription(thisVersionChangelog.TrimTo(4096)) .WithDescription(thisVersionChangelog.TrimTo(4096))
.WithFooter( .WithFooter(
"You may disable these messages by typing '.conf bot checkforupdates false'"); "You may disable these messages by typing '.conf bot checkforupdates false'");

View File

@@ -24,7 +24,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
private readonly GuildTimezoneService _tz; private readonly GuildTimezoneService _tz;
private readonly IMemoryCache _memoryCache; private readonly IMemoryCache _memoryCache;
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new(); private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = [];
private readonly UserPunishService _punishService; private readonly UserPunishService _punishService;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
@@ -115,10 +115,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
strs.user_status_change("👤" + Format.Bold(gu.Username), strs.user_status_change("👤" + Format.Bold(gu.Username),
Format.Bold(after.Status.ToString()))); Format.Bold(after.Status.ToString())));
PresenceUpdates.AddOrUpdate(logChannel, PresenceUpdates.AddOrUpdate(logChannel,
new List<string> [str],
{
str
},
(_, list) => (_, list) =>
{ {
list.Add(str); list.Add(str);
@@ -130,10 +127,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
var str = var str =
$"👾`{PrettyCurrentTime(gu.Guild)}`👤__**{gu.Username}**__ is now playing **{after.Activities.FirstOrDefault()?.Name ?? "-"}**."; $"👾`{PrettyCurrentTime(gu.Guild)}`👤__**{gu.Username}**__ is now playing **{after.Activities.FirstOrDefault()?.Name ?? "-"}**.";
PresenceUpdates.AddOrUpdate(logChannel, PresenceUpdates.AddOrUpdate(logChannel,
new List<string> [str],
{
str
},
(_, list) => (_, list) =>
{ {
list.Add(str); list.Add(str);
@@ -881,10 +875,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if (!string.IsNullOrWhiteSpace(str)) if (!string.IsNullOrWhiteSpace(str))
{ {
PresenceUpdates.AddOrUpdate(logChannel, PresenceUpdates.AddOrUpdate(logChannel,
new List<string> [str],
{
str
},
(_, list) => (_, list) =>
{ {
list.Add(str); list.Add(str);

View File

@@ -64,8 +64,7 @@ public class UserPunishService : INService, IReadyExecutor
long weight, long weight,
string reason) string reason)
{ {
if (weight <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(weight);
throw new ArgumentOutOfRangeException(nameof(weight));
var modName = mod.ToString(); var modName = mod.ToString();

View File

@@ -130,8 +130,7 @@ public class VcRoleService : INService
public void AddVcRole(ulong guildId, IRole role, ulong vcId) public void AddVcRole(ulong guildId, IRole role, ulong vcId)
{ {
if (role is null) ArgumentNullException.ThrowIfNull(role);
throw new ArgumentNullException(nameof(role));
var guildVcRoles = VcRoles.GetOrAdd(guildId, new ConcurrentDictionary<ulong, IRole>()); var guildVcRoles = VcRoles.GetOrAdd(guildId, new ConcurrentDictionary<ulong, IRole>());

View File

@@ -356,7 +356,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
if (maybeGuildId is { } guildId) if (maybeGuildId is { } guildId)
{ {
newguildExpressions.AddOrUpdate(guildId, newguildExpressions.AddOrUpdate(guildId,
new[] { expr }, [expr],
(_, old) => (_, old) =>
{ {
var newArray = old.ToArray(); var newArray = old.ToArray();
@@ -389,7 +389,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
expr.Trigger = expr.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention); expr.Trigger = expr.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention);
if (maybeGuildId is { } guildId) if (maybeGuildId is { } guildId)
newguildExpressions.AddOrUpdate(guildId, new[] { expr }, (_, old) => old.With(expr)); newguildExpressions.AddOrUpdate(guildId, [expr], (_, old) => old.With(expr));
else else
return _pubSub.Pub(_gexprAddedKey, expr); return _pubSub.Pub(_gexprAddedKey, expr);

View File

@@ -61,8 +61,7 @@ public sealed class AnimalRace : IDisposable
public async Task<AnimalRacingUser> JoinRace(ulong userId, string userName, long bet = 0) public async Task<AnimalRacingUser> JoinRace(ulong userId, string userName, long bet = 0)
{ {
if (bet < 0) ArgumentOutOfRangeException.ThrowIfNegative(bet);
throw new ArgumentOutOfRangeException(nameof(bet));
var user = new AnimalRacingUser(userName, userId, bet); var user = new AnimalRacingUser(userName, userId, bet);

View File

@@ -17,8 +17,7 @@ public sealed class BankService : IBankService, INService
public async Task<bool> AwardAsync(ulong userId, long amount) public async Task<bool> AwardAsync(ulong userId, long amount)
{ {
if (amount <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
await ctx.GetTable<BankUser>() await ctx.GetTable<BankUser>()
@@ -41,8 +40,7 @@ public sealed class BankService : IBankService, INService
public async Task<bool> TakeAsync(ulong userId, long amount) public async Task<bool> TakeAsync(ulong userId, long amount)
{ {
if (amount <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var rows = await ctx.Set<BankUser>() var rows = await ctx.Set<BankUser>()
@@ -58,8 +56,7 @@ public sealed class BankService : IBankService, INService
public async Task<bool> DepositAsync(ulong userId, long amount) public async Task<bool> DepositAsync(ulong userId, long amount)
{ {
if (amount <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
if (!await _cur.RemoveAsync(userId, amount, new("bank", "deposit"))) if (!await _cur.RemoveAsync(userId, amount, new("bank", "deposit")))
return false; return false;
@@ -86,8 +83,7 @@ public sealed class BankService : IBankService, INService
public async Task<bool> WithdrawAsync(ulong userId, long amount) public async Task<bool> WithdrawAsync(ulong userId, long amount)
{ {
if (amount <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var rows = await ctx.Set<BankUser>() var rows = await ctx.Set<BankUser>()

View File

@@ -50,12 +50,12 @@ public partial class Gambling
bj.GameEnded += Bj_GameEnded; bj.GameEnded += Bj_GameEnded;
bj.Start(); bj.Start();
await Response().Confirm(strs.bj_created).SendAsync(); await Response().NoReply().Confirm(strs.bj_created(ctx.User.ToString())).SendAsync();
} }
else else
{ {
if (await bj.Join(ctx.User, amount)) if (await bj.Join(ctx.User, amount))
await Response().Confirm(strs.bj_joined).SendAsync(); await Response().NoReply().Confirm(strs.bj_joined(ctx.User.ToString())).SendAsync();
else else
{ {
Log.Information("{User} can't join a blackjack game as it's in {BlackjackState} state already", Log.Information("{User} can't join a blackjack game as it's in {BlackjackState} state already",

View File

@@ -49,8 +49,7 @@ public class User : Player
public User(IUser user, long bet) public User(IUser user, long bet)
{ {
if (bet <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bet);
throw new ArgumentOutOfRangeException(nameof(bet));
Bet = bet; Bet = bet;
DiscordUser = user; DiscordUser = user;

View File

@@ -12,9 +12,9 @@ public partial class Gambling
public partial class Connect4Commands : GamblingSubmodule<GamblingService> public partial class Connect4Commands : GamblingSubmodule<GamblingService>
{ {
private static readonly string[] _numbers = private static readonly string[] _numbers =
{ [
":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:" ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:"
}; ];
private int RepostCounter private int RepostCounter
{ {

View File

@@ -16,7 +16,7 @@ public partial class Gambling
private static readonly Regex _fudgeRegex = new(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled); private static readonly Regex _fudgeRegex = new(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);
private static readonly char[] _fateRolls = { '-', ' ', '+' }; private static readonly char[] _fateRolls = ['-', ' ', '+'];
private readonly IImageCache _images; private readonly IImageCache _images;
public DiceRollCommands(IImageCache images) public DiceRollCommands(IImageCache images)

View File

@@ -1,4 +1,4 @@
namespace Nadeko.Econ.Gambling; namespace NadekoBot.Modules.Gambling;
public readonly struct FlipResult public readonly struct FlipResult
{ {

View File

@@ -11,7 +11,7 @@ using NadekoBot.Services.Currency;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using Nadeko.Econ.Gambling.Rps; using NadekoBot.Modules.Gambling.Rps;
using NadekoBot.Common.TypeReaders; using NadekoBot.Common.TypeReaders;
using NadekoBot.Modules.Patronage; using NadekoBot.Modules.Patronage;
@@ -466,7 +466,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return; return;
} }
await Response().Confirm(strs.gifted(N(amount), Format.Bold(receiver.ToString()))).SendAsync(); await Response().Confirm(strs.gifted(N(amount), Format.Bold(receiver.ToString()), ctx.User)).SendAsync();
} }
[Cmd] [Cmd]
@@ -508,7 +508,7 @@ public partial class Gambling : GamblingModule<GamblingService>
} }
await _cs.AddAsync(usr.Id, amount, new("award", ctx.User.ToString()!, msg, ctx.User.Id)); await _cs.AddAsync(usr.Id, amount, new("award", ctx.User.ToString()!, msg, ctx.User.Id));
await Response().Confirm(strs.awarded(N(amount), $"<@{usrId}>")).SendAsync(); await Response().Confirm(strs.awarded(N(amount), $"<@{usrId}>", ctx.User)).SendAsync();
} }
[Cmd] [Cmd]
@@ -776,7 +776,7 @@ public partial class Gambling : GamblingModule<GamblingService>
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var cleanRichest = await uow.Set<DiscordUser>() var cleanRichest = await uow.Set<DiscordUser>()
.GetTopRichest(_client.CurrentUser.Id, 0, 10_000); .GetTopRichest(_client.CurrentUser.Id, 0, 1000);
var sg = (SocketGuild)ctx.Guild!; var sg = (SocketGuild)ctx.Guild!;
return cleanRichest.Where(x => sg.GetUser(x.UserId) is not null).ToList(); return cleanRichest.Where(x => sg.GetUser(x.UserId) is not null).ToList();
@@ -788,9 +788,13 @@ public partial class Gambling : GamblingModule<GamblingService>
} }
} }
var res = Response()
.Paginated();
await Response() await Response()
.Paginated() .Paginated()
.PageItems(GetTopRichest) .PageItems(GetTopRichest)
.TotalElements(900)
.PageSize(9) .PageSize(9)
.CurrentPage(page) .CurrentPage(page)
.Page((toSend, curPage) => .Page((toSend, curPage) =>

View File

@@ -131,8 +131,8 @@ public partial class BetRollConfig
public BetRollPair[] Pairs { get; set; } = Array.Empty<BetRollPair>(); public BetRollPair[] Pairs { get; set; } = Array.Empty<BetRollPair>();
public BetRollConfig() public BetRollConfig()
=> Pairs = new BetRollPair[] => Pairs =
{ [
new() new()
{ {
WhenAbove = 99, WhenAbove = 99,
@@ -148,7 +148,7 @@ public partial class BetRollConfig
WhenAbove = 66, WhenAbove = 66,
MultiplyBy = 2 MultiplyBy = 2
} }
}; ];
} }
[Cloneable] [Cloneable]
@@ -207,7 +207,7 @@ public partial class LuckyLadderSettings
public decimal[] Multipliers { get; set; } public decimal[] Multipliers { get; set; }
public LuckyLadderSettings() public LuckyLadderSettings()
=> Multipliers = new[] { 2.4M, 1.7M, 1.5M, 1.2M, 0.5M, 0.3M, 0.2M, 0.1M }; => Multipliers = [2.4M, 1.7M, 1.5M, 1.2M, 0.5M, 0.3M, 0.2M, 0.1M];
} }
[Cloneable] [Cloneable]
@@ -228,11 +228,11 @@ public sealed partial class WaifuConfig
List of items available for gifting. List of items available for gifting.
If negative is true, gift will instead reduce waifu value. If negative is true, gift will instead reduce waifu value.
""")] """)]
public List<WaifuItemModel> Items { get; set; } = new(); public List<WaifuItemModel> Items { get; set; } = [];
public WaifuConfig() public WaifuConfig()
=> Items = new() => Items =
{ [
new("🥔", 5, "Potato"), new("🥔", 5, "Potato"),
new("🍪", 10, "Cookie"), new("🍪", 10, "Cookie"),
new("🥖", 20, "Bread"), new("🥖", 20, "Bread"),
@@ -269,7 +269,7 @@ public sealed partial class WaifuConfig
new("🚁", 20000, "Helicopter"), new("🚁", 20000, "Helicopter"),
new("🚀", 30000, "Spaceship"), new("🚀", 30000, "Spaceship"),
new("🌕", 50000, "Moon") new("🌕", 50000, "Moon")
}; ];
public class WaifuDecayConfig public class WaifuDecayConfig
{ {

View File

@@ -24,6 +24,7 @@ public abstract class GamblingModule<TService> : NadekoModule<TService>
{ {
if (amount < 1) if (amount < 1)
return false; return false;
if (amount < Config.MinBet) if (amount < Config.MinBet)
{ {
await Response().Error(strs.min_bet_limit(Format.Bold(Config.MinBet.ToString()) + CurrencySign)).SendAsync(); await Response().Error(strs.min_bet_limit(Format.Bold(Config.MinBet.ToString()) + CurrencySign)).SendAsync();

View File

@@ -27,7 +27,7 @@ public partial class Gambling
if (picked > 0) if (picked > 0)
{ {
var msg = await Response().Confirm(strs.picked(N(picked))).SendAsync(); var msg = await Response().NoReply().Confirm(strs.picked(N(picked))).SendAsync();
msg.DeleteAfter(10); msg.DeleteAfter(10);
} }

View File

@@ -20,10 +20,8 @@ public class ShopService : IShopService, INService
public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice) public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice)
{ {
if (index < 0) ArgumentOutOfRangeException.ThrowIfNegative(index);
throw new ArgumentOutOfRangeException(nameof(index)); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(newPrice);
if (newPrice <= 0)
throw new ArgumentOutOfRangeException(nameof(newPrice));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId); var entries = GetEntriesInternal(uow, guildId);
@@ -38,8 +36,8 @@ public class ShopService : IShopService, INService
public async Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName) public async Task<bool> ChangeEntryNameAsync(ulong guildId, int index, string newName)
{ {
if (index < 0) ArgumentOutOfRangeException.ThrowIfNegative(index);
throw new ArgumentOutOfRangeException(nameof(index));
if (string.IsNullOrWhiteSpace(newName)) if (string.IsNullOrWhiteSpace(newName))
throw new ArgumentNullException(nameof(newName)); throw new ArgumentNullException(nameof(newName));
@@ -56,10 +54,8 @@ public class ShopService : IShopService, INService
public async Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2) public async Task<bool> SwapEntriesAsync(ulong guildId, int index1, int index2)
{ {
if (index1 < 0) ArgumentOutOfRangeException.ThrowIfNegative(index1);
throw new ArgumentOutOfRangeException(nameof(index1)); ArgumentOutOfRangeException.ThrowIfNegative(index2);
if (index2 < 0)
throw new ArgumentOutOfRangeException(nameof(index2));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId); var entries = GetEntriesInternal(uow, guildId);
@@ -76,10 +72,8 @@ public class ShopService : IShopService, INService
public async Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex) public async Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex)
{ {
if (fromIndex < 0) ArgumentOutOfRangeException.ThrowIfNegative(fromIndex);
throw new ArgumentOutOfRangeException(nameof(fromIndex)); ArgumentOutOfRangeException.ThrowIfNegative(toIndex);
if (toIndex < 0)
throw new ArgumentOutOfRangeException(nameof(toIndex));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId); var entries = GetEntriesInternal(uow, guildId);

View File

@@ -7,7 +7,7 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using Nadeko.Econ.Gambling; using NadekoBot.Modules.Gambling;
using NadekoBot.Common.TypeReaders; using NadekoBot.Common.TypeReaders;
using Color = SixLabors.ImageSharp.Color; using Color = SixLabors.ImageSharp.Color;
using Image = SixLabors.ImageSharp.Image; using Image = SixLabors.ImageSharp.Image;

View File

@@ -27,10 +27,10 @@ public static class WaifuExtensions
public static IEnumerable<WaifuLbResult> GetTop(this DbSet<WaifuInfo> waifus, int count, int skip = 0) public static IEnumerable<WaifuLbResult> GetTop(this DbSet<WaifuInfo> waifus, int count, int skip = 0)
{ {
if (count < 0) ArgumentOutOfRangeException.ThrowIfNegative(count);
throw new ArgumentOutOfRangeException(nameof(count));
if (count == 0) if (count == 0)
return new List<WaifuLbResult>(); return [];
return waifus.Include(wi => wi.Waifu) return waifus.Include(wi => wi.Waifu)
.Include(wi => wi.Affinity) .Include(wi => wi.Affinity)

View File

@@ -1,7 +1,7 @@
#nullable disable #nullable disable
using Nadeko.Econ.Gambling; using NadekoBot.Modules.Gambling;
using Nadeko.Econ.Gambling.Betdraw; using NadekoBot.Modules.Gambling.Betdraw;
using Nadeko.Econ.Gambling.Rps; using NadekoBot.Modules.Gambling.Rps;
using OneOf; using OneOf;
namespace NadekoBot.Modules.Gambling; namespace NadekoBot.Modules.Gambling;
@@ -14,5 +14,5 @@ public interface IGamblingService
Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount); Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount);
Task<FlipResult[]> FlipAsync(int count); Task<FlipResult[]> FlipAsync(int count);
Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick); Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick);
Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? guessValue, byte? guessColor); Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? maybeGuessValue, byte? maybeGuessColor);
} }

View File

@@ -1,7 +1,7 @@
#nullable disable #nullable disable
using Nadeko.Econ.Gambling; using NadekoBot.Modules.Gambling;
using Nadeko.Econ.Gambling.Betdraw; using NadekoBot.Modules.Gambling.Betdraw;
using Nadeko.Econ.Gambling.Rps; using NadekoBot.Modules.Gambling.Rps;
using NadekoBot.Modules.Gambling.Services; using NadekoBot.Modules.Gambling.Services;
using OneOf; using OneOf;
@@ -20,8 +20,7 @@ public sealed class NewGamblingService : IGamblingService, INService
public async Task<OneOf<LuLaResult, GamblingError>> LulaAsync(ulong userId, long amount) public async Task<OneOf<LuLaResult, GamblingError>> LulaAsync(ulong userId, long amount)
{ {
if (amount < 0) ArgumentOutOfRangeException.ThrowIfNegative(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
if (amount > 0) if (amount > 0)
{ {
@@ -47,8 +46,7 @@ public sealed class NewGamblingService : IGamblingService, INService
public async Task<OneOf<BetrollResult, GamblingError>> BetRollAsync(ulong userId, long amount) public async Task<OneOf<BetrollResult, GamblingError>> BetRollAsync(ulong userId, long amount)
{ {
if (amount < 0) ArgumentOutOfRangeException.ThrowIfNegative(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
if (amount > 0) if (amount > 0)
{ {
@@ -77,11 +75,9 @@ public sealed class NewGamblingService : IGamblingService, INService
public async Task<OneOf<BetflipResult, GamblingError>> BetFlipAsync(ulong userId, long amount, byte guess) public async Task<OneOf<BetflipResult, GamblingError>> BetFlipAsync(ulong userId, long amount, byte guess)
{ {
if (amount < 0) ArgumentOutOfRangeException.ThrowIfNegative(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
if (guess > 1) ArgumentOutOfRangeException.ThrowIfGreaterThan(guess, 1);
throw new ArgumentOutOfRangeException(nameof(guess));
if (amount > 0) if (amount > 0)
{ {
@@ -105,19 +101,18 @@ public sealed class NewGamblingService : IGamblingService, INService
return result; return result;
} }
public async Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? guessValue, byte? guessColor) public async Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? maybeGuessValue, byte? maybeGuessColor)
{ {
if (amount < 0) ArgumentOutOfRangeException.ThrowIfNegative(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
if (guessColor is null && guessValue is null) if (maybeGuessColor is null && maybeGuessValue is null)
throw new ArgumentNullException(); throw new ArgumentNullException();
if (guessColor > 1) if (maybeGuessColor > 1)
throw new ArgumentOutOfRangeException(nameof(guessColor)); throw new ArgumentOutOfRangeException(nameof(maybeGuessColor));
if (guessValue > 1) if (maybeGuessValue > 1)
throw new ArgumentOutOfRangeException(nameof(guessValue)); throw new ArgumentOutOfRangeException(nameof(maybeGuessValue));
if (amount > 0) if (amount > 0)
{ {
@@ -130,7 +125,7 @@ public sealed class NewGamblingService : IGamblingService, INService
} }
var game = new BetdrawGame(); var game = new BetdrawGame();
var result = game.Draw((BetdrawValueGuess?)guessValue, (BetdrawColorGuess?)guessColor, amount); var result = game.Draw((BetdrawValueGuess?)maybeGuessValue, (BetdrawColorGuess?)maybeGuessColor, amount);
var won = (long)result.Won; var won = (long)result.Won;
if (won > 0) if (won > 0)
@@ -143,8 +138,7 @@ public sealed class NewGamblingService : IGamblingService, INService
public async Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount) public async Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount)
{ {
if (amount < 0) ArgumentOutOfRangeException.ThrowIfNegative(amount);
throw new ArgumentOutOfRangeException(nameof(amount));
if (amount > 0) if (amount > 0)
{ {
@@ -170,8 +164,7 @@ public sealed class NewGamblingService : IGamblingService, INService
public Task<FlipResult[]> FlipAsync(int count) public Task<FlipResult[]> FlipAsync(int count)
{ {
if (count < 1) ArgumentOutOfRangeException.ThrowIfLessThan(count, 1);
throw new ArgumentOutOfRangeException(nameof(count));
var game = new BetflipGame(0); var game = new BetflipGame(0);
@@ -242,11 +235,8 @@ public sealed class NewGamblingService : IGamblingService, INService
public async Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick) public async Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick)
{ {
if (amount < 0) ArgumentOutOfRangeException.ThrowIfNegative(amount);
throw new ArgumentOutOfRangeException(nameof(amount)); ArgumentOutOfRangeException.ThrowIfGreaterThan(pick, 2);
if (pick > 2)
throw new ArgumentOutOfRangeException(nameof(pick));
if (amount > 0) if (amount > 0)
{ {

View File

@@ -42,7 +42,7 @@ public sealed class AcrophobiaGame : IDisposable
private readonly SemaphoreSlim _locker = new(1, 1); private readonly SemaphoreSlim _locker = new(1, 1);
private readonly NadekoRandom _rng; private readonly NadekoRandom _rng;
private readonly HashSet<ulong> _usersWhoVoted = new(); private readonly HashSet<ulong> _usersWhoVoted = [];
public AcrophobiaGame(Options options) public AcrophobiaGame(Options options)
{ {

View File

@@ -67,7 +67,7 @@ public class ChatterBotService : IExecOnMessage
if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey)) if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey))
return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory); return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory);
Log.Information("Cleverbot will not work as the api key is missing."); Log.Information("Cleverbot will not work as the api key is missing");
return null; return null;
case ChatBotImplementation.Gpt3: case ChatBotImplementation.Gpt3:
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey)) if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
@@ -80,7 +80,7 @@ public class ChatterBotService : IExecOnMessage
_client.CurrentUser.Username, _client.CurrentUser.Username,
_httpFactory); _httpFactory);
Log.Information("Gpt3 will not work as the api key is missing."); Log.Information("Gpt3 will not work as the api key is missing");
return null; return null;
default: default:
return null; return null;
@@ -128,7 +128,7 @@ public class ChatterBotService : IExecOnMessage
var res = await _perms.CheckPermsAsync(sg, var res = await _perms.CheckPermsAsync(sg,
usrMsg.Channel, usrMsg.Channel,
usrMsg.Author, usrMsg.Author,
"games", CleverBotResponseStr.CLEVERBOT_RESPONSE,
CleverBotResponseStr.CLEVERBOT_RESPONSE); CleverBotResponseStr.CLEVERBOT_RESPONSE);
if (!res.IsAllowed) if (!res.IsAllowed)

View File

@@ -24,8 +24,8 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
}; };
[Comment("List of responses for the .8ball command. A random one will be selected every time")] [Comment("List of responses for the .8ball command. A random one will be selected every time")]
public List<string> EightBallResponses { get; set; } = new() public List<string> EightBallResponses { get; set; } =
{ [
"Most definitely yes.", "Most definitely yes.",
"For sure.", "For sure.",
"Totally!", "Totally!",
@@ -49,52 +49,59 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
"Don't even think about it.", "Don't even think about it.",
"Definitely no.", "Definitely no.",
"NO - It may cause disease contraction!" "NO - It may cause disease contraction!"
}; ];
[Comment("List of animals which will be used for the animal race game (.race)")] [Comment("List of animals which will be used for the animal race game (.race)")]
public List<RaceAnimal> RaceAnimals { get; set; } = new() public List<RaceAnimal> RaceAnimals { get; set; } =
{ [
new() new()
{ {
Icon = "🐼", Icon = "🐼",
Name = "Panda" Name = "Panda"
}, },
new() new()
{ {
Icon = "🐻", Icon = "🐻",
Name = "Bear" Name = "Bear"
}, },
new() new()
{ {
Icon = "🐧", Icon = "🐧",
Name = "Pengu" Name = "Pengu"
}, },
new() new()
{ {
Icon = "🐨", Icon = "🐨",
Name = "Koala" Name = "Koala"
}, },
new() new()
{ {
Icon = "🐬", Icon = "🐬",
Name = "Dolphin" Name = "Dolphin"
}, },
new() new()
{ {
Icon = "🐞", Icon = "🐞",
Name = "Ladybird" Name = "Ladybird"
}, },
new() new()
{ {
Icon = "🦀", Icon = "🦀",
Name = "Crab" Name = "Crab"
}, },
new() new()
{ {
Icon = "🦄", Icon = "🦄",
Name = "Unicorn" Name = "Unicorn"
} }
}; ];
[Comment(@"Which chatbot API should bot use. [Comment(@"Which chatbot API should bot use.
'cleverbot' - bot will use Cleverbot API. 'cleverbot' - bot will use Cleverbot API.

View File

@@ -33,8 +33,8 @@ public sealed class NunchiGame : IDisposable
private readonly SemaphoreSlim _locker = new(1, 1); private readonly SemaphoreSlim _locker = new(1, 1);
private HashSet<(ulong Id, string Name)> participants = new(); private HashSet<(ulong Id, string Name)> participants = [];
private readonly HashSet<(ulong Id, string Name)> _passed = new(); private readonly HashSet<(ulong Id, string Name)> _passed = [];
private Timer killTimer; private Timer killTimer;
public NunchiGame(ulong creatorId, string creatorName) public NunchiGame(ulong creatorId, string creatorName)

View File

@@ -17,9 +17,9 @@ public class TicTacToe
private IGuildUser winner; private IGuildUser winner;
private readonly string[] _numbers = private readonly string[] _numbers =
{ [
":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:"
}; ];
private IUserMessage previousMessage; private IUserMessage previousMessage;
private Timer timeoutTimer; private Timer timeoutTimer;
@@ -42,7 +42,7 @@ public class TicTacToe
_options = options; _options = options;
_sender = sender; _sender = sender;
_users = new[] { firstUser, null }; _users = [firstUser, null];
_state = new int?[,] { { null, null, null }, { null, null, null }, { null, null, null } }; _state = new int?[,] { { null, null, null }, { null, null, null }, { null, null, null } };
phase = Phase.Starting; phase = Phase.Starting;

View File

@@ -37,7 +37,7 @@ public partial class Games
game = new(Strings, _client, channel, (IGuildUser)ctx.User, options, _sender); game = new(Strings, _client, channel, (IGuildUser)ctx.User, options, _sender);
_service.TicTacToeGames.Add(channel.Id, game); _service.TicTacToeGames.Add(channel.Id, game);
await Response().Confirm(strs.ttt_created).SendAsync(); await Response().Confirm(strs.ttt_created(ctx.User)).SendAsync();
game.OnEnded += _ => game.OnEnded += _ =>
{ {

View File

@@ -9,13 +9,13 @@ public class TriviaQuestion
public const int MAX_STRING_LENGTH = 22; public const int MAX_STRING_LENGTH = 22;
//represents the min size to judge levDistance with //represents the min size to judge levDistance with
private static readonly HashSet<Tuple<int, int>> _strictness = new() private static readonly HashSet<Tuple<int, int>> _strictness =
{ [
new(9, 0), new(9, 0),
new(14, 1), new(14, 1),
new(19, 2), new(19, 2),
new(22, 3) new(22, 3)
}; ];
public string Category public string Category
=> _qModel.Category; => _qModel.Category;

View File

@@ -1,16 +1,13 @@
#nullable disable #nullable disable
using Amazon.S3;
using NadekoBot.Modules.Help.Common; using NadekoBot.Modules.Help.Common;
using NadekoBot.Modules.Help.Services; using NadekoBot.Modules.Help.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text; using System.Text;
using System.Text.Json;
using Nadeko.Common.Medusa; using Nadeko.Common.Medusa;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace NadekoBot.Modules.Help; namespace NadekoBot.Modules.Help;
public sealed class Help : NadekoModule<HelpService> public sealed partial class Help : NadekoModule<HelpService>
{ {
public const string PATREON_URL = "https://patreon.com/nadekobot"; public const string PATREON_URL = "https://patreon.com/nadekobot";
public const string PAYPAL_URL = "https://paypal.me/Kwoth"; public const string PAYPAL_URL = "https://paypal.me/Kwoth";
@@ -72,7 +69,7 @@ public sealed class Help : NadekoModule<HelpService>
return; return;
var topLevelModules = new List<ModuleInfo>(); var topLevelModules = new List<ModuleInfo>();
foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).Select(x => x.Key)) foreach (var m in _cmds.Modules.GroupBy(x => x.GetTopLevelModule()).OrderBy(x => x.Key.Name).Select(x => x.Key))
{ {
var result = await _perms.CheckPermsAsync(ctx.Guild, var result = await _perms.CheckPermsAsync(ctx.Guild,
ctx.Channel, ctx.Channel,
@@ -80,6 +77,11 @@ public sealed class Help : NadekoModule<HelpService>
m.Name, m.Name,
null); null);
#if GLOBAL_NADEKO
if (m.Preconditions.Any(x => x is NoPublicBotAttribute))
continue;
#endif
if (result.IsAllowed) if (result.IsAllowed)
topLevelModules.Add(m); topLevelModules.Add(m);
} }
@@ -87,6 +89,7 @@ public sealed class Help : NadekoModule<HelpService>
await Response() await Response()
.Paginated() .Paginated()
.Items(topLevelModules) .Items(topLevelModules)
.PageSize(12)
.CurrentPage(page) .CurrentPage(page)
.AddFooter(false) .AddFooter(false)
.Page((items, _) => .Page((items, _) =>
@@ -99,13 +102,13 @@ public sealed class Help : NadekoModule<HelpService>
return embed; return embed;
} }
items.OrderBy(module => module.Name) items
.ToList() .ToList()
.ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}", .ForEach(module => embed.AddField($"{GetModuleEmoji(module.Name)} {module.Name}",
GetModuleDescription(module.Name) GetModuleDescription(module.Name)
+ "\n" + "\n"
+ Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))), + Format.Code(GetText(strs.module_footer(prefix, module.Name.ToLowerInvariant()))),
true)); true));
return embed; return embed;
}) })
@@ -243,13 +246,16 @@ public sealed class Help : NadekoModule<HelpService>
var succ = new HashSet<CommandInfo>(); var succ = new HashSet<CommandInfo>();
if (opts.View != CommandsOptions.ViewType.All) if (opts.View != CommandsOptions.ViewType.All)
{ {
succ = new((await cmds.Select(async x => succ =
{ [
var pre = await x.CheckPreconditionsAsync(Context, _services); ..(await cmds.Select(async x =>
return (Cmd: x, Succ: pre.IsSuccess); {
}) var pre = await x.CheckPreconditionsAsync(Context, _services);
.WhenAll()).Where(x => x.Succ) return (Cmd: x, Succ: pre.IsSuccess);
.Select(x => x.Cmd)); })
.WhenAll()).Where(x => x.Succ)
.Select(x => x.Cmd)
];
if (opts.View == CommandsOptions.ViewType.Hide) if (opts.View == CommandsOptions.ViewType.Hide)
// if hidden is specified, completely remove these commands from the list // if hidden is specified, completely remove these commands from the list
@@ -310,7 +316,7 @@ public sealed class Help : NadekoModule<HelpService>
{ {
string cmdName; string cmdName;
if (cmd.Aliases.Count > 1) if (cmd.Aliases.Count > 1)
cmdName = Format.Code(prefix +cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]); cmdName = Format.Code(prefix + cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]);
else else
cmdName = Format.Code(prefix + cmd.Aliases.First()); cmdName = Format.Code(prefix + cmd.Aliases.First());
@@ -354,17 +360,16 @@ public sealed class Help : NadekoModule<HelpService>
public async Task H([Leftover] CommandInfo com = null) public async Task H([Leftover] CommandInfo com = null)
{ {
var channel = ctx.Channel; var channel = ctx.Channel;
if (com is null) if (com is null)
{ {
var ch = channel is ITextChannel ? await ctx.User.CreateDMChannelAsync() : channel;
try try
{ {
var ch = channel is ITextChannel ? await ctx.User.CreateDMChannelAsync() : channel;
var data = await GetHelpString(); var data = await GetHelpString();
if (data == default) if (data == default)
return; return;
await Response().Text(data).SendAsync(); await Response().Channel(ch).Text(data).SendAsync();
try try
{ {
await ctx.OkAsync(); await ctx.OkAsync();
@@ -419,90 +424,8 @@ public sealed class Help : NadekoModule<HelpService>
.ToList()); .ToList());
var readableData = JsonConvert.SerializeObject(cmdData, Formatting.Indented); var readableData = JsonConvert.SerializeObject(cmdData, Formatting.Indented);
var uploadData = JsonConvert.SerializeObject(cmdData, Formatting.None);
// for example https://nyc.digitaloceanspaces.com (without your space name) // send the indented file to chat
var serviceUrl = Environment.GetEnvironmentVariable("do_spaces_address");
// generate spaces access key on https://cloud.digitalocean.com/account/api/tokens
// you will get 2 keys, first, shorter one is id, longer one is secret
var accessKey = Environment.GetEnvironmentVariable("do_access_key_id");
var secretAcccessKey = Environment.GetEnvironmentVariable("do_access_key_secret");
// if all env vars are set, upload the unindented file (to save space) there
if (!(serviceUrl is null || accessKey is null || secretAcccessKey is null))
{
var config = new AmazonS3Config
{
ServiceURL = serviceUrl
};
using var dlClient = new AmazonS3Client(accessKey, secretAcccessKey, config);
using (var client = new AmazonS3Client(accessKey, secretAcccessKey, config))
{
await client.PutObjectAsync(new()
{
BucketName = "nadeko-pictures",
ContentType = "application/json",
ContentBody = uploadData,
// either use a path provided in the argument or the default one for public nadeko, other/cmds.json
Key = $"cmds/{StatsService.BotVersion}.json",
CannedACL = S3CannedACL.PublicRead
});
}
var versionListString = "[]";
try
{
using var oldVersionObject = await dlClient.GetObjectAsync(new()
{
BucketName = "nadeko-pictures",
Key = "cmds/versions.json"
});
await using var ms = new MemoryStream();
await oldVersionObject.ResponseStream.CopyToAsync(ms);
versionListString = Encoding.UTF8.GetString(ms.ToArray());
}
catch (Exception)
{
Log.Information("No old version list found. Creating a new one");
}
var versionList = JsonSerializer.Deserialize<List<string>>(versionListString);
if (versionList is not null && !versionList.Contains(StatsService.BotVersion))
{
// save the file with new version added
// versionList.Add(StatsService.BotVersion);
versionListString = JsonSerializer.Serialize(versionList.Prepend(StatsService.BotVersion),
new JsonSerializerOptions
{
WriteIndented = true
});
// upload the updated version list
using var client = new AmazonS3Client(accessKey, secretAcccessKey, config);
await client.PutObjectAsync(new()
{
BucketName = "nadeko-pictures",
ContentType = "application/json",
ContentBody = versionListString,
// either use a path provided in the argument or the default one for public nadeko, other/cmds.json
Key = "cmds/versions.json",
CannedACL = S3CannedACL.PublicRead
});
}
else
{
Log.Warning(
"Version {Version} already exists in the version file. " + "Did you forget to increment it?",
StatsService.BotVersion);
}
}
// also send the file, but indented one, to chat
await using var rDataStream = new MemoryStream(Encoding.ASCII.GetBytes(readableData)); await using var rDataStream = new MemoryStream(Encoding.ASCII.GetBytes(readableData));
await ctx.Channel.SendFileAsync(rDataStream, "cmds.json", GetText(strs.commandlist_regen)); await ctx.Channel.SendFileAsync(rDataStream, "cmds.json", GetText(strs.commandlist_regen));
} }
@@ -519,7 +442,7 @@ public sealed class Help : NadekoModule<HelpService>
=> smc.RespondConfirmAsync(_sender, => smc.RespondConfirmAsync(_sender,
""" """
- In case you don't want or cannot Donate to NadekoBot project, but you - In case you don't want or cannot Donate to NadekoBot project, but you
- NadekoBot is a free and [open source](https://gitlab.com/nadeko/nadekobot) project which means you can run your own "selfhosted" instance on your computer. - NadekoBot is a free and [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own "selfhosted" instance on your computer.
*Keep in mind that running the bot on your computer means that the bot will be offline when you turn off your computer* *Keep in mind that running the bot on your computer means that the bot will be offline when you turn off your computer*

View File

@@ -3,6 +3,7 @@
namespace NadekoBot.Modules; namespace NadekoBot.Modules;
[OwnerOnly] [OwnerOnly]
[NoPublicBot]
public partial class Medusa : NadekoModule<IMedusaLoaderService> public partial class Medusa : NadekoModule<IMedusaLoaderService>
{ {
private readonly IMedusaeRepositoryService _repo; private readonly IMedusaeRepositoryService _repo;

View File

@@ -6,17 +6,62 @@ public class MedusaeRepositoryService : IMedusaeRepositoryService, INService
{ {
// Simulate retrieving data from a database or API // Simulate retrieving data from a database or API
await Task.Delay(100); await Task.Delay(100);
return new List<ModuleItem> return
{ [
new ModuleItem { Name = "RSS Reader", Description = "Keep up to date with your favorite websites", Command = ".meinstall rss" }, new()
new ModuleItem { Name = "Password Manager", Description = "Safely store and manage all your passwords", Command = ".meinstall passwordmanager" }, {
new ModuleItem { Name = "Browser Extension", Description = "Enhance your browsing experience with useful tools", Command = ".meinstall browserextension" }, Name = "RSS Reader",
new ModuleItem { Name = "Video Downloader", Description = "Download videos from popular websites", Command = ".meinstall videodownloader" }, Description = "Keep up to date with your favorite websites",
new ModuleItem { Name = "Virtual Private Network", Description = "Securely browse the web and protect your privacy", Command = ".meinstall vpn" }, Command = ".meinstall rss"
new ModuleItem { Name = "Ad Blocker", Description = "Block annoying ads and improve page load times", Command = ".meinstall adblocker" }, },
new ModuleItem { Name = "Cloud Storage", Description = "Store and share your files online", Command = ".meinstall cloudstorage" }, new()
new ModuleItem { Name = "Social Media Manager", Description = "Manage all your social media accounts in one place", Command = ".meinstall socialmediamanager" }, {
new ModuleItem { Name = "Code Editor", Description = "Write and edit code online", Command = ".meinstall codeeditor" } Name = "Password Manager",
}; Description = "Safely store and manage all your passwords",
Command = ".meinstall passwordmanager"
},
new()
{
Name = "Browser Extension",
Description = "Enhance your browsing experience with useful tools",
Command = ".meinstall browserextension"
},
new()
{
Name = "Video Downloader",
Description = "Download videos from popular websites",
Command = ".meinstall videodownloader"
},
new()
{
Name = "Virtual Private Network",
Description = "Securely browse the web and protect your privacy",
Command = ".meinstall vpn"
},
new()
{
Name = "Ad Blocker",
Description = "Block annoying ads and improve page load times",
Command = ".meinstall adblocker"
},
new()
{
Name = "Cloud Storage",
Description = "Store and share your files online",
Command = ".meinstall cloudstorage"
},
new()
{
Name = "Social Media Manager",
Description = "Manage all your social media accounts in one place",
Command = ".meinstall socialmediamanager"
},
new()
{
Name = "Code Editor",
Description = "Write and edit code online",
Command = ".meinstall codeeditor"
}
];
} }
} }

View File

@@ -28,11 +28,10 @@ public sealed class AyuVoiceStateService : INService
_dnetApiClient = prop.GetValue(_client, null); _dnetApiClient = prop.GetValue(_client, null);
_sendVoiceStateUpdateMethodInfo = _dnetApiClient.GetType() _sendVoiceStateUpdateMethodInfo = _dnetApiClient.GetType()
.GetMethod("SendVoiceStateUpdateAsync", .GetMethod("SendVoiceStateUpdateAsync",
new[] [
{ typeof(ulong), typeof(ulong?), typeof(bool),
typeof(ulong), typeof(ulong?), typeof(bool),
typeof(bool), typeof(RequestOptions) typeof(bool), typeof(RequestOptions)
}); ]);
_client.LeftGuild += ClientOnLeftGuild; _client.LeftGuild += ClientOnLeftGuild;
} }
@@ -55,7 +54,7 @@ public sealed class AyuVoiceStateService : INService
bool isMuted = false) bool isMuted = false)
// return _voiceStateUpdate(guildId, channelId, isDeafened, isMuted); // return _voiceStateUpdate(guildId, channelId, isDeafened, isMuted);
=> (Task)_sendVoiceStateUpdateMethodInfo.Invoke(_dnetApiClient, => (Task)_sendVoiceStateUpdateMethodInfo.Invoke(_dnetApiClient,
new object[] { guildId, channelId, isMuted, isDeafened, null }); [guildId, channelId, isMuted, isDeafened, null]);
private Task SendLeaveVoiceChannelInternalAsync(ulong guildId) private Task SendLeaveVoiceChannelInternalAsync(ulong guildId)
=> InvokeSendVoiceStateUpdateAsync(guildId); => InvokeSendVoiceStateUpdateAsync(guildId);

View File

@@ -11,9 +11,9 @@ public sealed partial class YtLoader : INService
private static readonly byte[] _ytResultJsonEnd = Encoding.UTF8.GetBytes(";<"); private static readonly byte[] _ytResultJsonEnd = Encoding.UTF8.GetBytes(";<");
private static readonly string[] _durationFormats = private static readonly string[] _durationFormats =
{ [
@"m\:ss", @"mm\:ss", @"h\:mm\:ss", @"hh\:mm\:ss", @"hhh\:mm\:ss" @"m\:ss", @"mm\:ss", @"h\:mm\:ss", @"hh\:mm\:ss", @"hhh\:mm\:ss"
}; ];
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;

View File

@@ -201,12 +201,9 @@ public sealed partial class MusicQueue : IMusicQueue
public IQueuedTrackInfo? MoveTrack(int from, int to) public IQueuedTrackInfo? MoveTrack(int from, int to)
{ {
if (from < 0) ArgumentOutOfRangeException.ThrowIfNegative(from);
throw new ArgumentOutOfRangeException(nameof(from)); ArgumentOutOfRangeException.ThrowIfNegative(to);
if (to < 0) ArgumentOutOfRangeException.ThrowIfEqual(to, from);
throw new ArgumentOutOfRangeException(nameof(to));
if (to == from)
throw new ArgumentException($"{nameof(from)} and {nameof(to)} must be different");
lock (_locker) lock (_locker)
{ {
@@ -270,20 +267,8 @@ public sealed partial class MusicQueue : IMusicQueue
{ {
lock (_locker) lock (_locker)
{ {
var list = tracks.ToList(); var list = tracks.ToArray();
rng.Shuffle(list);
for (var i = 0; i < list.Count; i++)
{
var struck = rng.Next(i, list.Count);
(list[struck], list[i]) = (list[i], list[struck]);
// could preserving the index during shuffling be done better?
if (i == index)
index = struck;
else if (struck == index)
index = i;
}
tracks = new(list); tracks = new(list);
} }
} }

View File

@@ -7,9 +7,9 @@ namespace NadekoBot.Modules.Music;
public sealed class YtdlYoutubeResolver : IYoutubeResolver public sealed class YtdlYoutubeResolver : IYoutubeResolver
{ {
private static readonly string[] _durationFormats = private static readonly string[] _durationFormats =
{ [
"ss", "m\\:ss", "mm\\:ss", "h\\:mm\\:ss", "hh\\:mm\\:ss", "hhh\\:mm\\:ss" "ss", "m\\:ss", "mm\\:ss", "h\\:mm\\:ss", "hh\\:mm\\:ss", "hhh\\:mm\\:ss"
}; ];
private static readonly Regex _expiryRegex = new(@"(?:[\?\&]expire\=(?<timestamp>\d+))"); private static readonly Regex _expiryRegex = new(@"(?:[\?\&]expire\=(?<timestamp>\d+))");

View File

@@ -8,8 +8,7 @@ public static class MusicPlaylistExtensions
{ {
public static List<MusicPlaylist> GetPlaylistsOnPage(this DbSet<MusicPlaylist> playlists, int num) public static List<MusicPlaylist> GetPlaylistsOnPage(this DbSet<MusicPlaylist> playlists, int num)
{ {
if (num < 1) ArgumentOutOfRangeException.ThrowIfLessThan(num, 1);
throw new ArgumentOutOfRangeException(nameof(num));
return playlists.AsQueryable().Skip((num - 1) * 20).Take(20).Include(pl => pl.Songs).ToList(); return playlists.AsQueryable().Skip((num - 1) * 20).Take(20).Include(pl => pl.Songs).ToList();
} }

View File

@@ -1,149 +1,156 @@
namespace NadekoBot.Modules.Patronage; using NadekoBot.Modules.Patronage;
[OnlyPublicBot] namespace NadekoBot.Modules.Help;
public partial class Patronage : NadekoModule
public partial class Help
{ {
private readonly PatronageService _service; [OnlyPublicBot]
private readonly PatronageConfig _pConf; public partial class Patronage : NadekoModule
public Patronage(PatronageService service, PatronageConfig pConf)
{ {
_service = service; private readonly PatronageService _service;
_pConf = pConf; private readonly PatronageConfig _pConf;
}
[Cmd] public Patronage(PatronageService service, PatronageConfig pConf)
[Priority(2)]
public Task Patron()
=> InternalPatron(ctx.User);
[Cmd]
[Priority(0)]
[OwnerOnly]
public Task Patron(IUser user)
=> InternalPatron(user);
[Cmd]
[Priority(0)]
[OwnerOnly]
public async Task PatronMessage(PatronTier tierAndHigher, string message)
{
_ = ctx.Channel.TriggerTypingAsync();
var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message);
await Response()
.Confirm(strs.patron_msg_sent(
Format.Code(tierAndHigher.ToString()),
Format.Bold(result.Success.ToString()),
Format.Bold(result.Failed.ToString())))
.SendAsync();
}
// [OwnerOnly]
// public async Task PatronGift(IUser user, int amount)
// {
// // i can't figure out a good way to gift more than one month at the moment.
//
// if (amount < 1)
// return;
//
// var patron = _service.GiftPatronAsync(user, amount);
//
// var eb = _sender.CreateEmbed();
//
// await Response().Embed(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!")
// .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
// .AddField("Amount", $"**{patron.Amount / 100.0f:N1}$**", true)
// .AddField("Until", TimestampTag.FromDateTime(patron.ValidThru.AddDays(1)))).SendAsync();
//
//
// }
private async Task InternalPatron(IUser user)
{
if (!_pConf.Data.IsEnabled)
{ {
await Response().Error(strs.patron_not_enabled).SendAsync(); _service = service;
return; _pConf = pConf;
} }
var patron = await _service.GetPatronAsync(user.Id); [Cmd]
var quotaStats = await _service.GetUserQuotaStatistic(user.Id); [Priority(2)]
public Task Patron()
=> InternalPatron(ctx.User);
var eb = _sender.CreateEmbed() [Cmd]
.WithAuthor(user) [Priority(0)]
.WithTitle(GetText(strs.patron_info)) [OwnerOnly]
.WithOkColor(); public Task Patron(IUser user)
=> InternalPatron(user);
if (quotaStats.Commands.Count == 0 [Cmd]
&& quotaStats.Groups.Count == 0 [Priority(0)]
&& quotaStats.Modules.Count == 0) [OwnerOnly]
public async Task PatronMessage(PatronTier tierAndHigher, string message)
{ {
eb.WithDescription(GetText(strs.no_quota_found)); _ = ctx.Channel.TriggerTypingAsync();
var result = await _service.SendMessageToPatronsAsync(tierAndHigher, message);
await Response()
.Confirm(strs.patron_msg_sent(
Format.Code(tierAndHigher.ToString()),
Format.Bold(result.Success.ToString()),
Format.Bold(result.Failed.ToString())))
.SendAsync();
} }
else
// [OwnerOnly]
// public async Task PatronGift(IUser user, int amount)
// {
// // i can't figure out a good way to gift more than one month at the moment.
//
// if (amount < 1)
// return;
//
// var patron = _service.GiftPatronAsync(user, amount);
//
// var eb = _sender.CreateEmbed();
//
// await Response().Embed(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!")
// .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
// .AddField("Amount", $"**{patron.Amount / 100.0f:N1}$**", true)
// .AddField("Until", TimestampTag.FromDateTime(patron.ValidThru.AddDays(1)))).SendAsync();
//
//
// }
private async Task InternalPatron(IUser user)
{ {
eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true) if (!_pConf.Data.IsEnabled)
.AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true);
if (patron.Tier != PatronTier.None)
eb.AddField(GetText(strs.expires), patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(), true);
eb.AddField(GetText(strs.quotas), "", false);
if (quotaStats.Commands.Count > 0)
{ {
var text = GetQuotaList(quotaStats.Commands); await Response().Error(strs.patron_not_enabled).SendAsync();
if (!string.IsNullOrWhiteSpace(text)) return;
eb.AddField(GetText(strs.commands), text, true);
} }
if (quotaStats.Groups.Count > 0) var patron = await _service.GetPatronAsync(user.Id);
var quotaStats = await _service.GetUserQuotaStatistic(user.Id);
var eb = _sender.CreateEmbed()
.WithAuthor(user)
.WithTitle(GetText(strs.patron_info))
.WithOkColor();
if (quotaStats.Commands.Count == 0
&& quotaStats.Groups.Count == 0
&& quotaStats.Modules.Count == 0)
{ {
var text = GetQuotaList(quotaStats.Groups); eb.WithDescription(GetText(strs.no_quota_found));
if (!string.IsNullOrWhiteSpace(text)) }
eb.AddField(GetText(strs.groups), text, true); else
{
eb.AddField(GetText(strs.tier), Format.Bold(patron.Tier.ToFullName()), true)
.AddField(GetText(strs.pledge), $"**{patron.Amount / 100.0f:N1}$**", true);
if (patron.Tier != PatronTier.None)
eb.AddField(GetText(strs.expires),
patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(),
true);
eb.AddField(GetText(strs.quotas), "", false);
if (quotaStats.Commands.Count > 0)
{
var text = GetQuotaList(quotaStats.Commands);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.commands), text, true);
}
if (quotaStats.Groups.Count > 0)
{
var text = GetQuotaList(quotaStats.Groups);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.groups), text, true);
}
if (quotaStats.Modules.Count > 0)
{
var text = GetQuotaList(quotaStats.Modules);
if (!string.IsNullOrWhiteSpace(text))
eb.AddField(GetText(strs.modules), text, true);
}
} }
if (quotaStats.Modules.Count > 0)
try
{ {
var text = GetQuotaList(quotaStats.Modules); await Response().User(ctx.User).Embed(eb).SendAsync();
if (!string.IsNullOrWhiteSpace(text)) _ = ctx.OkAsync();
eb.AddField(GetText(strs.modules), text, true); }
catch
{
await Response().Error(strs.cant_dm).SendAsync();
} }
} }
private string GetQuotaList(IReadOnlyDictionary<string, FeatureQuotaStats> featureQuotaStats)
{
var text = string.Empty;
foreach (var (key, q) in featureQuotaStats)
{
text += $"\n\t`{key}`\n";
if (q.Hourly != default)
text += $" {GetEmoji(q.Hourly)} {q.Hourly.Cur}/{q.Hourly.Max} per hour\n";
if (q.Daily != default)
text += $" {GetEmoji(q.Daily)} {q.Daily.Cur}/{q.Daily.Max} per day\n";
if (q.Monthly != default)
text += $" {GetEmoji(q.Monthly)} {q.Monthly.Cur}/{q.Monthly.Max} per month\n";
}
try return text;
{
await Response().User(ctx.User).Embed(eb).SendAsync();
_ = ctx.OkAsync();
}
catch
{
await Response().Error(strs.cant_dm).SendAsync();
} }
private string GetEmoji((uint Cur, uint Max) limit)
=> limit.Cur < limit.Max
? "✅"
: "⚠️";
} }
private string GetQuotaList(IReadOnlyDictionary<string, FeatureQuotaStats> featureQuotaStats)
{
var text = string.Empty;
foreach (var (key, q) in featureQuotaStats)
{
text += $"\n\t`{key}`\n";
if (q.Hourly != default)
text += $" {GetEmoji(q.Hourly)} {q.Hourly.Cur}/{q.Hourly.Max} per hour\n";
if (q.Daily != default)
text += $" {GetEmoji(q.Daily)} {q.Daily.Cur}/{q.Daily.Max} per day\n";
if (q.Monthly != default)
text += $" {GetEmoji(q.Monthly)} {q.Monthly.Cur}/{q.Monthly.Max} per month\n";
}
return text;
}
private string GetEmoji((uint Cur, uint Max) limit)
=> limit.Cur < limit.Max
? "✅"
: "⚠️";
} }

View File

@@ -111,7 +111,6 @@ public sealed class PatronageService
var lastDate = lastRun.ToDateOnly(); var lastDate = lastRun.ToDateOnly();
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
await using var tran = await ctx.Database.BeginTransactionAsync();
if ((lastDate.Day == 1 || (lastDate.Month != nowDate.Month)) && nowDate.Day > 1) if ((lastDate.Day == 1 || (lastDate.Month != nowDate.Month)) && nowDate.Day > 1)
{ {
@@ -141,7 +140,6 @@ public sealed class PatronageService
// assumes that the code above runs in less than an hour // assumes that the code above runs in less than an hour
await _cache.AddAsync(_quotaKey, now.ToBinary()); await _cache.AddAsync(_quotaKey, now.ToBinary());
await tran.CommitAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -171,7 +169,6 @@ public sealed class PatronageService
var lastChargeUtc = subscriber.LastCharge.Value.ToUniversalTime(); var lastChargeUtc = subscriber.LastCharge.Value.ToUniversalTime();
var dateInOneMonth = lastChargeUtc.Date.AddMonths(1); var dateInOneMonth = lastChargeUtc.Date.AddMonths(1);
// await using var tran = await ctx.Database.BeginTransactionAsync();
try try
{ {
var dbPatron = await ctx.GetTable<PatronUser>() var dbPatron = await ctx.GetTable<PatronUser>()

View File

@@ -16,8 +16,7 @@ public partial class Permissions
private async Task ListBlacklistInternal(string title, BlacklistType type, int page = 0) private async Task ListBlacklistInternal(string title, BlacklistType type, int page = 0)
{ {
if (page < 0) ArgumentOutOfRangeException.ThrowIfNegative(page);
throw new ArgumentOutOfRangeException(nameof(page));
var list = _service.GetBlacklist(); var list = _service.GetBlacklist();
var allItems = await list.Where(x => x.Type == type) var allItems = await list.Where(x => x.Type == type)

View File

@@ -112,8 +112,7 @@ public sealed class CmdCdService : IExecPreCommand, IReadyExecutor, INService
public void AddCooldown(ulong guildId, string name, int secs) public void AddCooldown(ulong guildId, string name, int secs)
{ {
if (secs <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(secs);
throw new ArgumentOutOfRangeException(nameof(secs));
var sett = _settings.GetOrAdd(guildId, static _ => new()); var sett = _settings.GetOrAdd(guildId, static _ => new());
sett[name] = secs; sett[name] = secs;

View File

@@ -162,7 +162,7 @@ public partial class Searches
.AddField(GetText(strs.episodes), animeData.TotalEpisodes.ToString(), true) .AddField(GetText(strs.episodes), animeData.TotalEpisodes.ToString(), true)
.AddField(GetText(strs.status), animeData.AiringStatus, true) .AddField(GetText(strs.status), animeData.AiringStatus, true)
.AddField(GetText(strs.genres), .AddField(GetText(strs.genres),
string.Join(",\n", animeData.Genres.Any() ? animeData.Genres : new[] { "none" }), string.Join(",\n", animeData.Genres.Any() ? animeData.Genres : ["none"]),
true) true)
.WithFooter($"{GetText(strs.score)} {animeData.AverageScore} / 100"); .WithFooter($"{GetText(strs.score)} {animeData.AverageScore} / 100");
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
@@ -194,7 +194,7 @@ public partial class Searches
.AddField(GetText(strs.chapters), mangaData.TotalChapters.ToString(), true) .AddField(GetText(strs.chapters), mangaData.TotalChapters.ToString(), true)
.AddField(GetText(strs.status), mangaData.PublishingStatus, true) .AddField(GetText(strs.status), mangaData.PublishingStatus, true)
.AddField(GetText(strs.genres), .AddField(GetText(strs.genres),
string.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : new[] { "none" }), string.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : ["none"]),
true) true)
.WithFooter($"{GetText(strs.score)} {mangaData.AverageScore} / 100"); .WithFooter($"{GetText(strs.score)} {mangaData.AverageScore} / 100");

View File

@@ -1,13 +0,0 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Searches;
public class FinnHubSearchResponse
{
[JsonPropertyName("count")]
public int Count { get; set; }
[JsonPropertyName("result")]
public List<FinnHubSearchResult> Result { get; set; }
}

View File

@@ -1,19 +0,0 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Searches;
public class FinnHubSearchResult
{
[JsonPropertyName("description")]
public string Description { get; set; }
[JsonPropertyName("displaySymbol")]
public string DisplaySymbol { get; set; }
[JsonPropertyName("symbol")]
public string Symbol { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
}

View File

@@ -1,55 +0,0 @@
// using System.Net.Http.Json;
//
// namespace NadekoBot.Modules.Searches;
//
// public sealed class PolygonApiClient : IDisposable
// {
// private const string BASE_URL = "https://api.polygon.io/v3";
//
// private readonly HttpClient _httpClient;
// private readonly string _apiKey;
//
// public PolygonApiClient(HttpClient httpClient, string apiKey)
// {
// _httpClient = httpClient;
// _apiKey = apiKey;
// }
//
// public async Task<IReadOnlyCollection<PolygonTickerData>> TickersAsync(string? ticker = null, string? query = null)
// {
// if (string.IsNullOrWhiteSpace(query))
// query = null;
//
// if(query is not null)
// query = Uri.EscapeDataString(query);
//
// var requestString = $"{BASE_URL}/reference/tickers"
// + "?type=CS"
// + "&active=true"
// + "&order=asc"
// + "&limit=1000"
// + $"&apiKey={_apiKey}";
//
// if (!string.IsNullOrWhiteSpace(ticker))
// requestString += $"&ticker={ticker}";
//
// if (!string.IsNullOrWhiteSpace(query))
// requestString += $"&search={query}";
//
//
// var response = await _httpClient.GetFromJsonAsync<PolygonTickerResponse>(requestString);
//
// if (response is null)
// return Array.Empty<PolygonTickerData>();
//
// return response.Results;
// }
//
// // public async Task<PolygonTickerDetailsV3> TickerDetailsV3Async(string ticker)
// // {
// // return new();
// // }
//
// public void Dispose()
// => _httpClient.Dispose();
// }

View File

@@ -1,26 +0,0 @@
// namespace NadekoBot.Modules.Searches;
//
// public sealed class PolygonStockDataService : IStockDataService
// {
// private readonly IHttpClientFactory _httpClientFactory;
// private readonly IBotCredsProvider _credsProvider;
//
// public PolygonStockDataService(IHttpClientFactory httpClientFactory, IBotCredsProvider credsProvider)
// {
// _httpClientFactory = httpClientFactory;
// _credsProvider = credsProvider;
// }
//
// public async Task<IReadOnlyCollection<StockData>> GetStockDataAsync(string? query = null)
// {
// using var httpClient = _httpClientFactory.CreateClient();
// using var client = new PolygonApiClient(httpClient, string.Empty);
// var data = await client.TickersAsync(query: query);
//
// return data.Map(static x => new StockData()
// {
// Name = x.Name,
// Ticker = x.Ticker,
// });
// }
// }

View File

@@ -1,43 +0,0 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Searches;
public class PolygonTickerData
{
[JsonPropertyName("ticker")]
public string Ticker { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("market")]
public string Market { get; set; }
[JsonPropertyName("locale")]
public string Locale { get; set; }
[JsonPropertyName("primary_exchange")]
public string PrimaryExchange { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("active")]
public bool Active { get; set; }
[JsonPropertyName("currency_name")]
public string CurrencyName { get; set; }
[JsonPropertyName("cik")]
public string Cik { get; set; }
[JsonPropertyName("composite_figi")]
public string CompositeFigi { get; set; }
[JsonPropertyName("share_class_figi")]
public string ShareClassFigi { get; set; }
[JsonPropertyName("last_updated_utc")]
public DateTime LastUpdatedUtc { get; set; }
}

View File

@@ -1,13 +0,0 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Searches;
public class PolygonTickerResponse
{
[JsonPropertyName("status")]
public string Status { get; set; }
[JsonPropertyName("results")]
public List<PolygonTickerData> Results { get; set; }
}

View File

@@ -247,10 +247,7 @@ public class FeedsService : INService
foreach (var feed in gc.FeedSubs) foreach (var feed in gc.FeedSubs)
{ {
_subs.AddOrUpdate(feed.Url.ToLower(), _subs.AddOrUpdate(feed.Url.ToLower(),
new List<FeedSub> [feed],
{
feed
},
(_, old) => (_, old) =>
{ {
old.Add(feed); old.Add(feed);
@@ -275,7 +272,7 @@ public class FeedsService : INService
return false; return false;
var toRemove = items[index]; var toRemove = items[index];
_subs.AddOrUpdate(toRemove.Url.ToLower(), _subs.AddOrUpdate(toRemove.Url.ToLower(),
new List<FeedSub>(), [],
(_, old) => (_, old) =>
{ {
old.Remove(toRemove); old.Remove(toRemove);

View File

@@ -2,7 +2,7 @@
public class PlainGoogleScrapeSearchResult : ISearchResult public class PlainGoogleScrapeSearchResult : ISearchResult
{ {
public string? Answer { get; init; } = null!; public required string? Answer { get; init; }
public IReadOnlyCollection<ISearchResultEntry> Entries { get; init; } = null!; public required IReadOnlyCollection<ISearchResultEntry> Entries { get; init; }
public ISearchResultInformation Info { get; init; } = null!; public required ISearchResultInformation Info { get; init; }
} }

View File

@@ -1,5 +1,4 @@
#nullable disable #nullable disable
using Html2Markdown;
using NadekoBot.Modules.Searches.Common; using NadekoBot.Modules.Searches.Common;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -23,8 +22,8 @@ public class SearchesService : INService
Birds Birds
} }
public List<WoWJoke> WowJokes { get; } = new(); public List<WoWJoke> WowJokes { get; } = [];
public List<MagicItem> MagicItems { get; } = new(); public List<MagicItem> MagicItems { get; } = [];
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;
private readonly IImageCache _imgs; private readonly IImageCache _imgs;
@@ -68,7 +67,7 @@ public class SearchesService : INService
_yomamaJokes = File.ReadAllLines("data/yomama.txt").Shuffle().ToList(); _yomamaJokes = File.ReadAllLines("data/yomama.txt").Shuffle().ToList();
else else
{ {
_yomamaJokes = new(); _yomamaJokes = [];
Log.Warning("data/yomama.txt is missing. .yomama command won't work"); Log.Warning("data/yomama.txt is missing. .yomama command won't work");
} }
} }
@@ -229,25 +228,15 @@ public class SearchesService : INService
{ {
var subpath = tag.ToString().ToLowerInvariant(); var subpath = tag.ToString().ToLowerInvariant();
int max; var max = tag switch
switch (tag)
{ {
case ImageTag.Food: ImageTag.Food => 773,
max = 773; ImageTag.Dogs => 750,
break; ImageTag.Cats => 773,
case ImageTag.Dogs: ImageTag.Birds => 578,
max = 750; _ => 100,
break; };
case ImageTag.Cats:
max = 773;
break;
case ImageTag.Birds:
max = 578;
break;
default:
max = 100;
break;
}
return $"https://nadeko-pictures.nyc3.digitaloceanspaces.com/{subpath}/" return $"https://nadeko-pictures.nyc3.digitaloceanspaces.com/{subpath}/"
+ _rng.Next(1, max).ToString("000") + _rng.Next(1, max).ToString("000")
@@ -380,11 +369,11 @@ public class SearchesService : INService
return null; return null;
if (!string.IsNullOrWhiteSpace(data.Img)) if (!string.IsNullOrWhiteSpace(data.Img))
data.Img = await _google.ShortenUrl(data.Img); data.Img = await _google.ShortenUrl(data.Img);
if (!string.IsNullOrWhiteSpace(data.Text)) // if (!string.IsNullOrWhiteSpace(data.Text))
{ // {
var converter = new Converter(); // var converter = new Converter();
data.Text = converter.Convert(data.Text); // data.Text = converter.Convert(data.Text);
} // }
return data; return data;
} }

View File

@@ -202,10 +202,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
_trackCounter[key].Add(info.GuildId); _trackCounter[key].Add(info.GuildId);
else else
{ {
_trackCounter[key] = new() _trackCounter[key] = [info.GuildId];
{
info.GuildId
};
} }
} }
@@ -572,12 +569,12 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
{ {
if (map.TryGetValue(guildId, out var set)) if (map.TryGetValue(guildId, out var set))
return set; return set;
return map[guildId] = new(); return map[guildId] = [];
} }
_shardTrackedStreams[key] = new() _shardTrackedStreams[key] = new()
{ {
{ guildId, new() } { guildId, [] }
}; };
return _shardTrackedStreams[key][guildId]; return _shardTrackedStreams[key][guildId];
} }

View File

@@ -41,10 +41,7 @@ public class PicartoProvider : Provider
public override async Task<StreamData?> GetStreamDataAsync(string login) public override async Task<StreamData?> GetStreamDataAsync(string login)
{ {
var data = await GetStreamDataAsync(new List<string> var data = await GetStreamDataAsync([login]);
{
login
});
return data.FirstOrDefault(); return data.FirstOrDefault();
} }
@@ -52,7 +49,7 @@ public class PicartoProvider : Provider
public override async Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> logins) public override async Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> logins)
{ {
if (logins.Count == 0) if (logins.Count == 0)
return new List<StreamData>(); return [];
using var http = _httpClientFactory.CreateClient(); using var http = _httpClientFactory.CreateClient();
var toReturn = new List<StreamData>(); var toReturn = new List<StreamData>();

View File

@@ -66,10 +66,7 @@ public sealed class TwitchHelixProvider : Provider
public override async Task<StreamData?> GetStreamDataAsync(string login) public override async Task<StreamData?> GetStreamDataAsync(string login)
{ {
var data = await GetStreamDataAsync(new List<string> var data = await GetStreamDataAsync([login]);
{
login
});
return data.FirstOrDefault(); return data.FirstOrDefault();
} }
@@ -125,7 +122,7 @@ public sealed class TwitchHelixProvider : Provider
catch (Exception ex) catch (Exception ex)
{ {
Log.Warning(ex, "Something went wrong retreiving {StreamPlatform} streams", Platform); Log.Warning(ex, "Something went wrong retreiving {StreamPlatform} streams", Platform);
return new List<StreamData>(); return [];
} }
} }
@@ -163,7 +160,7 @@ public sealed class TwitchHelixProvider : Provider
catch (Exception ex) catch (Exception ex)
{ {
Log.Warning(ex, "Something went wrong retreiving {StreamPlatform} streams", Platform); Log.Warning(ex, "Something went wrong retreiving {StreamPlatform} streams", Platform);
return new List<StreamData>(); return [];
} }
} }

View File

@@ -269,7 +269,11 @@ public sealed class RepeaterService : IReadyExecutor, INService
var text = SmartText.CreateFrom(repeater.Message); var text = SmartText.CreateFrom(repeater.Message);
text = await _repSvc.ReplaceAsync(text, repCtx); text = await _repSvc.ReplaceAsync(text, repCtx);
var newMsg = await _sender.Response(channel).Text(text).SendAsync(); var newMsg = await _sender.Response(channel)
.Text(text)
.Sanitize(false)
.SendAsync();
_ = newMsg.AddReactionAsync(new Emoji("🔄")); _ = newMsg.AddReactionAsync(new Emoji("🔄"));
if (_noRedundant.Contains(repeater.Id)) if (_noRedundant.Contains(repeater.Id))
@@ -359,8 +363,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
public async Task<RunningRepeater?> RemoveByIndexAsync(ulong guildId, int index) public async Task<RunningRepeater?> RemoveByIndexAsync(ulong guildId, int index)
{ {
if (index > MAX_REPEATERS * 2) ArgumentOutOfRangeException.ThrowIfGreaterThan(index, MAX_REPEATERS * 2);
throw new ArgumentOutOfRangeException(nameof(index));
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var toRemove = await uow.Set<Repeater>() var toRemove = await uow.Set<Repeater>()

View File

@@ -58,13 +58,13 @@ public class ConverterService : INService, IReadyExecutor
var currencyRates = await GetCurrencyRates(); var currencyRates = await GetCurrencyRates();
var baseType = new ConvertUnit var baseType = new ConvertUnit
{ {
Triggers = new[] { currencyRates.Base }, Triggers = [currencyRates.Base],
Modifier = decimal.One, Modifier = decimal.One,
UnitType = unitTypeString UnitType = unitTypeString
}; };
var units = currencyRates.ConversionRates.Select(u => new ConvertUnit var units = currencyRates.ConversionRates.Select(u => new ConvertUnit
{ {
Triggers = new[] { u.Key }, Triggers = [u.Key],
Modifier = u.Value, Modifier = u.Value,
UnitType = unitTypeString UnitType = unitTypeString
}) })

View File

@@ -85,6 +85,7 @@ public partial class Utility : NadekoModule
await Response() await Response()
.Text(message) .Text(message)
.Channel(channel)
.UserBasedMentions() .UserBasedMentions()
.SendAsync(); .SendAsync();
} }
@@ -479,7 +480,7 @@ public partial class Utility : NadekoModule
try try
{ {
if (tags.Length == 0) if (tags.Length == 0)
tags = new[] { name }; tags = [name];
await ctx.Guild.CreateStickerAsync( await ctx.Guild.CreateStickerAsync(
name, name,

View File

@@ -332,8 +332,7 @@ public class ClubService : INService, IClubService
public List<ClubInfo> GetClubLeaderboardPage(int page) public List<ClubInfo> GetClubLeaderboardPage(int page)
{ {
if (page < 0) ArgumentOutOfRangeException.ThrowIfNegative(page);
throw new ArgumentOutOfRangeException(nameof(page));
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
return uow.Set<ClubInfo>().GetClubLeaderboardPage(page); return uow.Set<ClubInfo>().GetClubLeaderboardPage(page);

View File

@@ -1194,7 +1194,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
} }
//avatar //avatar
if (stats.User.AvatarId is not null && template.User.Icon.Show) if (template.User.Icon.Show)
{ {
try try
{ {
@@ -1469,7 +1469,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
} }
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
// await using var tran = await ctx.Database.BeginTransactionAsync();
try try
{ {
if (await ctx.GetTable<XpShopOwnedItem>() if (await ctx.GetTable<XpShopOwnedItem>()

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings> <ImplicitUsings>true</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>5.0.0</Version> <Version>5.0.5</Version>
<!-- Output/build --> <!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory> <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
@@ -28,22 +28,20 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<Publish>True</Publish> <Publish>True</Publish>
</PackageReference> </PackageReference>
<PackageReference Include="AWSSDK.S3" Version="3.7.307"/>
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/> <PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/>
<PackageReference Include="CommandLineParser" Version="2.9.1"/> <PackageReference Include="CommandLineParser" Version="2.9.1"/>
<PackageReference Include="Discord.Net" Version="3.204.0"/> <PackageReference Include="Discord.Net" Version="3.204.0"/>
<PackageReference Include="CoreCLR-NCalc" Version="3.0.203"/> <PackageReference Include="CoreCLR-NCalc" Version="3.1.246" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/> <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.62.1.3205"/> <PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084"/> <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084"/>
<!-- <PackageReference Include="Grpc.AspNetCore" Version="2.62.0" />--> <!-- <PackageReference Include="Grpc.AspNetCore" Version="2.62.0" />-->
<PackageReference Include="Google.Protobuf" Version="3.26.1"/> <PackageReference Include="Google.Protobuf" Version="3.26.1"/>
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.62.0"/> <PackageReference Include="Grpc.Net.ClientFactory" Version="2.62.0"/>
<PackageReference Include="Grpc.Tools" Version="2.62.0"> <PackageReference Include="Grpc.Tools" Version="2.63.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Html2Markdown" Version="6.2.0.3"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0"/> <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0"/> <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0"/>
@@ -70,14 +68,14 @@
<PackageReference Include="OneOf" Version="3.0.263"/> <PackageReference Include="OneOf" Version="3.0.263"/>
<PackageReference Include="OneOf.SourceGenerator" Version="3.0.263"/> <PackageReference Include="OneOf.SourceGenerator" Version="3.0.263"/>
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1"/> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1"/>
<PackageReference Include="Serilog.Sinks.Seq" Version="7.0.0"/> <PackageReference Include="Serilog.Sinks.Seq" Version="7.0.1" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17"/> <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17"/>
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.8"/> <PackageReference Include="SixLabors.ImageSharp" Version="2.1.8"/>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14"/> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14"/>
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009"/> <PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009"/>
<PackageReference Include="StackExchange.Redis" Version="2.7.33"/> <PackageReference Include="StackExchange.Redis" Version="2.7.33"/>
<PackageReference Include="YamlDotNet" Version="15.1.2"/> <PackageReference Include="YamlDotNet" Version="15.1.4" />
<PackageReference Include="SharpToken" Version="2.0.2"/> <PackageReference Include="SharpToken" Version="2.0.2"/>
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0"/> <PackageReference Include="JetBrains.Annotations" Version="2023.3.0"/>
@@ -93,7 +91,7 @@
<PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0"/> <PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4"/> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4"/>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2"/> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2"/> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2"/>
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/> <PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/>
@@ -102,7 +100,7 @@
<PackageReference Include="TwitchLib.Api" Version="3.4.1"/> <PackageReference Include="TwitchLib.Api" Version="3.4.1"/>
<!-- sqlselectcsv and stock --> <!-- sqlselectcsv and stock -->
<PackageReference Include="CsvHelper" Version="28.0.1"/> <PackageReference Include="CsvHelper" Version="32.0.3" />
</ItemGroup> </ItemGroup>

View File

@@ -43,8 +43,7 @@ public sealed partial class GoogleApiService : IGoogleApiService, INService
if (string.IsNullOrWhiteSpace(keywords)) if (string.IsNullOrWhiteSpace(keywords))
throw new ArgumentNullException(nameof(keywords)); throw new ArgumentNullException(nameof(keywords));
if (count <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);
throw new ArgumentOutOfRangeException(nameof(count));
var match = _plRegex.Match(keywords); var match = _plRegex.Match(keywords);
if (match.Length > 1) if (match.Length > 1)
@@ -62,8 +61,7 @@ public sealed partial class GoogleApiService : IGoogleApiService, INService
if (string.IsNullOrWhiteSpace(id)) if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id)); throw new ArgumentNullException(nameof(id));
if (count <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);
throw new ArgumentOutOfRangeException(nameof(count));
var query = _yt.Search.List("snippet"); var query = _yt.Search.List("snippet");
query.MaxResults = count; query.MaxResults = count;
@@ -82,8 +80,7 @@ public sealed partial class GoogleApiService : IGoogleApiService, INService
if (string.IsNullOrWhiteSpace(keywords)) if (string.IsNullOrWhiteSpace(keywords))
throw new ArgumentNullException(nameof(keywords)); throw new ArgumentNullException(nameof(keywords));
if (count <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);
throw new ArgumentOutOfRangeException(nameof(count));
var query = _yt.Search.List("snippet"); var query = _yt.Search.List("snippet");
query.MaxResults = count; query.MaxResults = count;
@@ -100,8 +97,7 @@ public sealed partial class GoogleApiService : IGoogleApiService, INService
if (string.IsNullOrWhiteSpace(keywords)) if (string.IsNullOrWhiteSpace(keywords))
throw new ArgumentNullException(nameof(keywords)); throw new ArgumentNullException(nameof(keywords));
if (count <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);
throw new ArgumentOutOfRangeException(nameof(count));
var query = _yt.Search.List("snippet"); var query = _yt.Search.List("snippet");
query.MaxResults = count; query.MaxResults = count;
@@ -150,8 +146,7 @@ public sealed partial class GoogleApiService : IGoogleApiService, INService
if (string.IsNullOrWhiteSpace(playlistId)) if (string.IsNullOrWhiteSpace(playlistId))
throw new ArgumentNullException(nameof(playlistId)); throw new ArgumentNullException(nameof(playlistId));
if (count <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);
throw new ArgumentOutOfRangeException(nameof(count));
string nextPageToken = null; string nextPageToken = null;

View File

@@ -112,7 +112,7 @@ public class Localization : ILocalization
{ {
Cmd = key, Cmd = key,
Desc = key, Desc = key,
Usage = new[] { key } Usage = [key]
}; };
} }

View File

@@ -8,13 +8,13 @@ namespace NadekoBot.Common;
public sealed class RedisBotCache : IBotCache public sealed class RedisBotCache : IBotCache
{ {
private static readonly Type[] _supportedTypes = new [] private static readonly Type[] _supportedTypes =
{ [
typeof(bool), typeof(int), typeof(uint), typeof(long), typeof(bool), typeof(int), typeof(uint), typeof(long),
typeof(ulong), typeof(float), typeof(double), typeof(ulong), typeof(float), typeof(double),
typeof(string), typeof(byte[]), typeof(ReadOnlyMemory<byte>), typeof(Memory<byte>), typeof(string), typeof(byte[]), typeof(ReadOnlyMemory<byte>), typeof(Memory<byte>),
typeof(RedisValue), typeof(RedisValue)
}; ];
private static readonly JsonSerializerOptions _opts = new() private static readonly JsonSerializerOptions _opts = new()
{ {

View File

@@ -46,12 +46,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
public void CopyTo(T[] array, int arrayIndex) public void CopyTo(T[] array, int arrayIndex)
{ {
ArgumentNullException.ThrowIfNull(array); ArgumentNullException.ThrowIfNull(array);
ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
if (arrayIndex < 0) ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(arrayIndex, array.Length);
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
if (arrayIndex >= array.Length)
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
CopyToInternal(array, arrayIndex); CopyToInternal(array, arrayIndex);
} }

View File

@@ -6,10 +6,13 @@ namespace NadekoBot.Extensions;
public static class StringExtensions public static class StringExtensions
{ {
private static readonly HashSet<char> _lettersAndDigits = new(Enumerable.Range(48, 10) private static readonly HashSet<char> _lettersAndDigits =
.Concat(Enumerable.Range(65, 26)) [
.Concat(Enumerable.Range(97, 26)) ..Enumerable.Range(48, 10)
.Select(x => (char)x)); .Concat(Enumerable.Range(65, 26))
.Concat(Enumerable.Range(97, 26))
.Select(x => (char)x)
];
private static readonly Regex _filterRegex = new(@"discord(?:\.gg|\.io|\.me|\.li|(?:app)?\.com\/invite)\/(\w+)", private static readonly Regex _filterRegex = new(@"discord(?:\.gg|\.io|\.me|\.li|(?:app)?\.com\/invite)\/(\w+)",
RegexOptions.Compiled | RegexOptions.IgnoreCase); RegexOptions.Compiled | RegexOptions.IgnoreCase);
@@ -43,7 +46,7 @@ public static class StringExtensions
public static string ToTitleCase(this string str) public static string ToTitleCase(this string str)
{ {
var tokens = str.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); var tokens = str.Split([" "], StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < tokens.Length; i++) for (var i = 0; i < tokens.Length; i++)
{ {
var token = tokens[i]; var token = tokens[i];

View File

@@ -38,8 +38,7 @@ public sealed class NadekoRandom : Random
public long NextLong(long minValue, long maxValue) public long NextLong(long minValue, long maxValue)
{ {
if (minValue > maxValue) ArgumentOutOfRangeException.ThrowIfGreaterThan(minValue, maxValue);
throw new ArgumentOutOfRangeException(nameof(maxValue));
if (minValue == maxValue) if (minValue == maxValue)
return minValue; return minValue;
var bytes = new byte[sizeof(long)]; var bytes = new byte[sizeof(long)];

View File

@@ -9,8 +9,7 @@ public sealed class QueueRunner
public QueueRunner(int delayMs = 0, int maxCapacity = -1) public QueueRunner(int delayMs = 0, int maxCapacity = -1)
{ {
if (delayMs < 0) ArgumentOutOfRangeException.ThrowIfNegative(delayMs);
throw new ArgumentOutOfRangeException(nameof(delayMs));
_delayMs = delayMs; _delayMs = delayMs;
_channel = maxCapacity switch _channel = maxCapacity switch

View File

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
namespace NadekoBot.Common; namespace NadekoBot.Common;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
[SuppressMessage("Style", "IDE0022:Use expression body for methods")]
public sealed class NoPublicBotAttribute : PreconditionAttribute public sealed class NoPublicBotAttribute : PreconditionAttribute
{ {
public override Task<PreconditionResult> CheckPermissionsAsync( public override Task<PreconditionResult> CheckPermissionsAsync(

View File

@@ -9,8 +9,7 @@ public sealed class RatelimitAttribute : PreconditionAttribute
public RatelimitAttribute(int seconds) public RatelimitAttribute(int seconds)
{ {
if (seconds <= 0) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(seconds);
throw new ArgumentOutOfRangeException(nameof(seconds));
Seconds = seconds; Seconds = seconds;
} }

View File

@@ -146,14 +146,14 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
Prefix = "."; Prefix = ".";
RotateStatuses = false; RotateStatuses = false;
GroupGreets = false; GroupGreets = false;
DmHelpTextKeywords = new() DmHelpTextKeywords =
{ [
"help", "help",
"commands", "commands",
"cmds", "cmds",
"module", "module",
"can you do" "can you do"
}; ];
} }
// [Comment(@"Whether the prefix will be a suffix, or prefix. // [Comment(@"Whether the prefix will be a suffix, or prefix.
@@ -178,8 +178,8 @@ public sealed partial class BlockedConfig
public BlockedConfig() public BlockedConfig()
{ {
Modules = new(); Modules = [];
Commands = new(); Commands = [];
} }
} }

View File

@@ -12,5 +12,5 @@ public abstract class DbService
public abstract Task SetupAsync(); public abstract Task SetupAsync();
public abstract DbContext CreateRawDbContext(string dbType, string connString); public abstract DbContext CreateRawDbContext(string dbType, string connString);
public abstract DbContext GetDbContext(); public abstract NadekoContext GetDbContext();
} }

View File

@@ -259,10 +259,10 @@ public class Deck
} }
private readonly string[] _regIndicators = private readonly string[] _regIndicators =
{ [
"🇦", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":keycap_ten:", "🇦", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":keycap_ten:",
"🇯", "🇶", "🇰" "🇯", "🇶", "🇰"
}; ];
public Card(CardSuit s, int cardNum) public Card(CardSuit s, int cardNum)
{ {

View File

@@ -1,4 +1,4 @@
namespace Nadeko.Econ.Gambling.Betdraw; namespace NadekoBot.Modules.Gambling.Betdraw;
public enum BetdrawColorGuess public enum BetdrawColorGuess
{ {

View File

@@ -1,4 +1,6 @@
namespace Nadeko.Econ.Gambling.Betdraw; using Nadeko.Econ;
namespace NadekoBot.Modules.Gambling.Betdraw;
public sealed class BetdrawGame public sealed class BetdrawGame
{ {

View File

@@ -1,4 +1,6 @@
namespace Nadeko.Econ.Gambling.Betdraw; using Nadeko.Econ;
namespace NadekoBot.Modules.Gambling.Betdraw;
public readonly struct BetdrawResult public readonly struct BetdrawResult
{ {

View File

@@ -1,4 +1,4 @@
namespace Nadeko.Econ.Gambling.Betdraw; namespace NadekoBot.Modules.Gambling.Betdraw;
public enum BetdrawResultType public enum BetdrawResultType
{ {

Some files were not shown because too many files have changed in this diff Show More