Compare commits

..

78 Commits
5.0.6 ... 5.1.4

Author SHA1 Message Date
Kwoth
3a5b482884 docs: Updated changelog, upped version to 5.1.4
fix: Fixed pipeline
2024-07-13 15:49:52 +00:00
Kwoth
db66264bc6 add: Added support for any openai compatible api for the chatterbot feature
change: Changed games.yml to allow input of the apiUrl (needs to be openai compatible) and modelName as a string.
2024-07-13 15:36:49 +00:00
Kwoth
ae1ddd82d0 fix: Fixed .waifugift help string 2024-07-12 20:50:56 +00:00
Kwoth
8523abd6f1 add: Added multiplier option for waifu gifts. For example .waifugift 3xRose @user will give that user 3 roses 2024-07-12 17:18:47 +00:00
Kwoth
e1892c4ff4 remove: Removed selfhost button from .donate, no idea why it was there in the first place 2024-07-12 16:24:49 +00:00
Kwoth
a50a7b3b0e add: Added user param to .bank balance command, but only Bot Owner can use it 2024-07-11 18:46:48 +00:00
Kwoth
9d2268a925 fix: fixed .clubinfo rank 2024-07-11 00:53:52 +00:00
Kwoth
d77a86c08b add: Added club rank in .clubinfo 2024-07-10 08:31:01 +00:00
Kwoth
d605f685cf docs: Updated command strings to clarify .say and .send usage 2024-07-08 16:24:59 +00:00
Kwoth
bbc1fd28c2 change: .coins cache will expire after an hour 2024-07-07 10:09:48 +00:00
Kwoth
cff8a258d0 fix: .coins will respect the user specified page now 2024-07-07 10:07:35 +00:00
Kwoth
1d760a548e fix: cache .coins result to avoid ratelimits 2024-07-07 10:05:01 +00:00
Kwoth
25fa8a3852 add: Added .coins command which lists top 10 cryptos ordered by marketcap, paginated with 10 per page 2024-07-07 05:23:59 +00:00
Kwoth
ca13684c0d docs: Updated CHANGELOG.
dev: upped version to 5.1.3
2024-07-06 09:05:34 +00:00
Kwoth
0ad6b741e7 add: chatterbot, (but not prompting) will also trigger if the user replies to a bot's message 2024-07-05 04:55:33 +00:00
Kwoth
4ce756d760 fix: if prompt ai responds with chat, it will prevent further further execution of tthat input to prevent the same query running multiple times 2024-07-05 04:48:00 +00:00
Kwoth
5f2813d3af fix: clean the bot username too from invalid characters for openai 2024-07-05 03:36:04 +00:00
Kwoth
1b7458529c fix: bot should now trim the invalid characters from openai message usernames 2024-07-05 03:33:04 +00:00
Kwoth
9c9c8d7490 fix: fixed pipeline 2024-06-29 08:43:50 +00:00
Kwoth
2700bfdce8 fix: Fixed .stickeradd, it now properly supports 300x300 image uploads. closes #434 2024-06-29 08:42:47 +00:00
Kwoth
5498c5ce3f add: Added .quran command, which will show the provided ayah in english and arabic, including recitation by Alafasy 2024-06-29 07:14:13 +00:00
Kwoth
6f444a8da0 fix: fixed pipeline, no functional change 2024-06-29 01:36:38 +00:00
Kwoth
454c14eee1 docs: Updated changelog 2024-06-29 01:25:48 +00:00
Kwoth
30aa8e8186 fix: fixed honeypot not unbanning and pruning 2024-06-29 01:24:14 +00:00
Kwoth
2ca141810c docs: updated pagination, upped version to 5.1.1 2024-06-28 05:17:40 +00:00
Kwoth
9da8e4f1c1 fix: Fixed pagination, for real this time, closes #435,
dev: removed some old creds migration code, incremented creds version
2024-06-27 19:48:39 +00:00
Kwoth
ef471c32bb add: Added .honeypot command
fix: Fixed .betdraw not respecting max bet limit
2024-06-27 04:50:55 +00:00
Kwoth
49d557caec fix: removed an extra in remind string 2024-06-25 16:01:59 +00:00
Kwoth
4366f908f3 dev: Using new stopwatch, some cleanup. No functional change 2024-06-25 07:09:16 +00:00
Kwoth
237e66495b fix: Potential fix for the invalid syntax redis issue, unless the issue is caused by the older version of redis on the server itself 2024-06-25 03:32:21 +00:00
Kwoth
15d4117d7f docs: Updated changelog, version upped to 5.1.0 2024-06-25 00:48:29 +00:00
Kwoth
d7daa5f2af fix: pipeline 2024-06-17 04:18:06 +00:00
Kwoth
b08ff62406 Merge branch 'v5' into 'v5'
Fix for timely reminder text

See merge request Kwoth/nadekobot!332
2024-06-15 15:15:11 +00:00
Cata
9aa89d3be8 fix: reminder button will now work correctly and show the correct time 2024-06-15 15:15:11 +00:00
Kwoth
518f2e425e fix: selfhosters shouldn't get a feature limit about reaction roles anymore 2024-06-15 11:07:17 +00:00
Kwoth
8fae6e621d dev: feed code cleaned up a bit 2024-06-14 20:44:07 +00:00
Kwoth
30f3ae1ade fix: .xpexclude should will now work with forums too. If you exclude a forum you won't be able to gain xp in any of the threads. 2024-06-13 20:52:40 +00:00
Kwoth
ef4b1c8868 fix: all .feed overloads should now work, closes #433 2024-06-13 19:03:14 +00:00
Kwoth
7f64d2661f Merge branch 'prompt' into 'v5'
5.1

Closes #431

See merge request Kwoth/nadekobot!331
2024-06-13 18:54:21 +00:00
Kwoth
ab93380d7c 5.1 2024-06-13 18:54:21 +00:00
Kwoth
a6adf73ecf fix: Fixed currency rewards in patronage service 2024-05-29 09:54:05 +00:00
Kwoth
d9e52038ac fix: re-added cleanup code for invalid database data in sqlite 2024-05-28 20:34:11 +00:00
Kwoth
be7ddc732b fix: Forgot to remove a testing condition in patreonclient 2024-05-28 20:13:31 +00:00
Kwoth
79b25c8a41 dev: assembly versions for published release files should be correct now 2024-05-25 19:35:31 +00:00
Kwoth
12fa209555 dev: fixed version number 2024-05-21 14:09:26 +00:00
Kwoth
28a9f5682e docs: Updated changelog 2024-05-21 12:27:09 +00:00
Kwoth
06321380ee add: Added .quteedit command
add: Added an edit button to .quoteshow and .exprshow commands that opens a modal which lets you edit the quote or expr in question
2024-05-21 00:53:42 +00:00
Kwoth
b51ce34190 Merge branch 'v5' of https://gitlab.com/kwoth/nadekobot into v5 2024-05-21 00:17:10 +00:00
Kwoth
317c94979a fix: .langset and .langsetd should no longer allow unsupported languages and nonsense to be typed in 2024-05-21 00:16:57 +00:00
Kwoth
df2fd1b3f1 Merge branch 'v5' into 'v5'
Docs: update supported versions

See merge request Kwoth/nadekobot!329
2024-05-20 16:23:37 +00:00
Cata
35f0228986 docs: update supported versions 2024-05-20 16:23:36 +00:00
Kwoth
f391027c8c docs: Updated changelog 2024-05-20 00:48:35 +00:00
Kwoth
4e570475df add: Added dropdown menu for .cmds help and group help (part of cmds). Group help will no longer be on .h
fix: paginated response replies will no longer ping the author
2024-05-20 00:37:29 +00:00
Kwoth
e2066f433f fix: Fixed .h not working on some commands
add: Added select menu for the .mdls command
dev: Reworked the way interactions are created and sent. It is much better but far from perfect
2024-05-19 23:24:52 +00:00
Kwoth
78b328dc18 dev: added aliases to fix ci 2024-05-19 11:39:33 +00:00
Kwoth
f0cbacd01a Merge branch 'v5' of https://gitlab.com/kwoth/nadekobot into v5 2024-05-19 11:23:04 +00:00
Kwoth
d45c39a5fe fix: .pick command will now show the name of the user who picked the currency 2024-05-18 23:28:55 +00:00
Kwoth
a6010716e8 add: You can now check commands for submodules, for example '.h quote' the same way you can do for command groups like '.h bank' 2024-05-18 23:00:00 +00:00
Kwoth
d2c7563c5c change: Quotes will now use easier to write, alphanumerical ids (same as expressions) 2024-05-18 22:47:25 +00:00
Kwoth
0c167a9382 add: Added .cleanupguilddata command which will delete all guildconfigs, xp stats and other data related to any guild the bot is no longer in. This is a highly destructive and irreversible command.
dev: Added cascade deletes to any tables which have guildconfigs FK, as well as to some other missing places
2024-05-18 22:34:34 +00:00
Kwoth
b9d2e4baf1 Merge branch 'v5' into 'v5'
Missing botperms for setserver commands

See merge request Kwoth/nadekobot!328
2024-05-18 21:42:47 +00:00
Cata
82e230010b fix: added missing botperms for set* commands 2024-05-18 21:42:47 +00:00
Kwoth
03fb1a5ca2 fix: .verbose will now also be respected for expressions 2024-05-18 15:34:16 +00:00
Kwoth
8ac07ce6d7 Merge branch 'v5' of https://gitlab.com/kwoth/nadekobot into v5 2024-05-17 11:30:14 +00:00
Kwoth
12da9f3178 Merge branch 'v5' into 'v5'
setserverbanner and setservericon commands

See merge request Kwoth/nadekobot!327
2024-05-17 11:29:51 +00:00
Cata
ae45329d2b add: setserverbanner and setservericon commands 2024-05-17 11:29:51 +00:00
Kwoth
f499380fe8 dev: Updated tests package 2024-05-17 11:20:46 +00:00
Kwoth
0083e9ef68 dev: Don't run docker build on mrs as it will always fail
dev: Fixed remove-migration.ps1 script
2024-05-17 11:19:02 +00:00
Kwoth
22b7cf5e6c Merge branch 'v5' into 'v5'
docs: Rewritten Docker guide

See merge request Kwoth/nadekobot!326
2024-05-16 20:52:58 +00:00
Plarpoon
f973766c73 docs: Rewritten Docker guide 2024-05-16 20:52:58 +00:00
Kwoth
0efdd3300d Merge branch 'UpdateDocsYtdlp' into 'v5'
Docs: Replaced youtube-dl with yt-dlp

See merge request Kwoth/nadekobot!325
2024-05-16 15:51:23 +00:00
Cata
5016377dd1 docs: Replaced youtube-dl with yt-dlp 2024-05-16 15:51:22 +00:00
Kwoth
2d40a35112 Merge branch 'v5' into 'v5'
dev: move template creds.yml file into docker

See merge request Kwoth/nadekobot!323
2024-05-15 19:57:32 +00:00
Plarpoon
9ea6cd0b32 dev: creds_example.yml will now be in the docker's data dir/volume 2024-05-15 19:57:32 +00:00
Kwoth
a52a246982 fix: xplb and xpglb pagination fixed, closes #430
fix: Page number when there is an unknown number of items while paginating is now correct
fix: .stm and .stma fixed and can now mention everyone as long as the user executing the command also can
dev: Cleaned up/improved some code
2024-05-15 13:44:37 +00:00
Kwoth
803fe5db2f Merge branch 'v5' into 'v5'
Updating Dockerfile

See merge request Kwoth/nadekobot!322
2024-05-15 07:29:08 +00:00
Plarpoon
39980a1374 Updating Dockerfile 2024-05-15 07:29:08 +00:00
Kwoth
d2c4af273b dev: Version upped 2024-05-15 07:13:28 +00:00
179 changed files with 38618 additions and 3134 deletions

View File

@@ -30,12 +30,17 @@ variables:
build:
stage: build
script:
- "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 -o $LINUX_ARM64_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 -o $WIN_ARM64_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 -o $MACOS_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- |
VERSION_STRING=""
if [ -n "$CI_COMMIT_TAG" ]; then
VERSION_STRING="-p:Version=$CI_COMMIT_TAG"
fi
- "dotnet publish -c Release -r linux-x64 --self-contained $VERSION_STRING -o $LINUX_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r linux-arm64 --self-contained $VERSION_STRING -o $LINUX_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-x64 --self-contained $VERSION_STRING -o $WIN_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-arm64 --self-contained $VERSION_STRING -o $WIN_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-x64 --self-contained $VERSION_STRING -o $MACOS_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-arm64 --self-contained $VERSION_STRING -o $MACOS_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
artifacts:
paths:
- "$LINUX_X64_OUTPUT_DIR/"
@@ -123,6 +128,8 @@ publish-medusa-package:
stage: publish-medusa-package
allow_failure: true
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG
script:
- LAST_TAG=$(git describe --tags --abbrev=0)

View File

@@ -2,6 +2,130 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [5.1.4] - 13.07.2024
### Added
- Added `.coins` command which lists top 10 cryptos ordered by marketcap
- Added Clubs rank in the leaderboard to `.clubinfo`
- Bot owners can now check other people's bank balance (Not server owners, only bot owner, the person who is hosting the bot)
- You can now send multiple waifu gifts at once to waifus. For example `.waifugift 3xRose @user` will give that user 3 roses
- The format is `<NUMBER>x<ITEM>`, no spaces
- Added `.boosttest` command
### Changed
- Updated command strings to clarify `.say` and `.send` usages
### Fixed
- Fixed `.waifugift` help string
### Removed
- Removed selfhost button from `.donate` command, no idea why it was there in the first place
## [5.1.3] - 06.07.2024
### Added
- Added `.quran` command, which will show the provided ayah in english and arabic, including recitation by Alafasy
### Changed
- Replying to the bot's message in the channel where chatterbot is enabled will also trigger the ai response, as if you pinged the bot. This only works for chatterbot, but not for nadeko ai command prompts
### Fixed
- Fixed `.stickeradd` it now properly supports 300x300 image uploads.
- Bot should now trim the invalid characters from chatterbot usernames to avoid openai errors
- Fixed prompt triggering chatterbot responses twice
## [5.1.2] - 29.06.2024
### Fixed
- Fixed `.honeypot` not unbanning and not pruning messages
## [5.1.1] - 27.06.2024
### Added
- Added `.honeypot` command, which automatically softbans (ban and immediate unban) any user who posts in that channel.
- Useful to auto softban bots who spam every channel upon joining
- Users who run commands or expressions won't be softbanned.
- Users who have ban member permissions are also excluded.
### Fixed
- Fixed `.betdraw` not respecting maxbet
- Fixed `.xpshop` pagination for real this time?
## [5.1.0] - 25.06.2024
### Added
- Added `.prompt` command, Nadeko Ai Assistant
- You can send natural language questions, queries or execute commands. For example "@Nadeko how's the weather in paris" and it will return `.we Paris` and run it for you.
- In case the bot can't execute a command using your query, It will fall back to your chatter bot, in case you have it enabled in data/games.yml. (Cleverbot or chatgpt)
- (It's far from perfect so please don't ask the bot to do dangerous things like banning or pruning)
- Requires Patreon subscription, after which you'll be able to run it on global @Nadeko bot.
- Selfhosters: If you're selfhosting, you also will need to acquire the api key from <https://dashy.nadeko.bot/me> after pledging on patreon and put it in nadekoAiToken in creds.yml
- Added support for `gpt-4o` in `data/games.yml`
### Changed
- Remind will now show a timestamp tag for durations
- Only `Gpt35Turbo` and `Gpt4o` are valid inputs in games.yml now
- `data/patron.yml` changed. It now has limits. The entire feature limit system has been reworked. Your previous settings will be reset
- A lot of updates to bot strings (thanks Ene)
- Improved cleanup command to delete a lot more data once cleanup is ran, not only guild configs (please don't use this command unless you have your database bakced up and you know 100% what you're doing)
### Fixed
- Fixed xp bg buy button not working, and possibly some other buttons too
- Fixed shopbuy %user% placeholders and updated help text
- All .feed overloads should now work"
- `.xpexclude` should will now work with forums too. If you exclude a forum you won't be able to gain xp in any of the threads.
- Fixed remind not showing correct time (thx cata)
### Removed
- Removed PoE related commands
- dev: Removed patron quota data from the database, it will now be stored in redis
## [5.0.8] - 21.05.2024
### Added
- Added `.setserverbanner` and `.setservericon` commands (thx cata)
- Added overloads section to `.h command` which will show you all versions of command usage with param names
- You can now check commands for submodules, for example `.cmds SelfAssignedRoles` will show brief help for each of the commands in that submodule
- Added dropdown menus for .mdls and .cmds (both module and group versions) which will give you the option to see more detailed help for each specific module, group or command respectively
- Self-Hosters only:
- Added a dangerous cleanup command that you don't have to know about
### Changed
- Quotes will now use alphanumerical ids (like expressions)
### Fixed
- `.verbose` will now be respected for expression errors
- Using `.pick` will now correctly show the name of the user who picked the currency
- Fixed `.h` not working on some commands
- `.langset` and `.langsetd` should no longer allow unsupported languages and nonsense to be typed in
## [5.0.7] - 15.05.2024
### Fixed
- `.streammessage` will once again be able to mention anyone (as long as the user setting the message has the permission to mention everyone)
- `.streammsgall` fixed
- `.xplb` and `.xpglb` pagination fixed
- Fixed page number when the total number of elements is unknown
## [5.0.6] - 14.05.2024
### Changed

View File

@@ -1,16 +1,25 @@
# Use the .NET 8.0 SDK as the base image for the build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /source
# Copy the .csproj files for each project
COPY src/Nadeko.Medusa/*.csproj src/Nadeko.Medusa/
COPY src/NadekoBot/*.csproj src/NadekoBot/
COPY src/NadekoBot.Coordinator/*.csproj src/NadekoBot.Coordinator/
COPY src/NadekoBot.Generators/*.csproj src/NadekoBot.Generators/
COPY src/NadekoBot.Voice/*.csproj src/NadekoBot.Voice/
COPY NuGet.Config ./
# Restore the dependencies for the NadekoBot project
RUN dotnet restore src/NadekoBot/
# Copy the rest of the source code
COPY . .
# Set the working directory to the NadekoBot project
WORKDIR /source/src/NadekoBot
# Build and publish the NadekoBot project, then clean up unnecessary files
RUN set -xe; \
dotnet --version; \
dotnet publish -c Release -o /app --no-restore; \
@@ -19,28 +28,33 @@ RUN set -xe; \
find /app -type f -exec chmod -x {} \; ;\
chmod +x /app/NadekoBot
# final stage/image
# Use the .NET 8.0 runtime as the base image for the final stage
FROM mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR /app
# Create a new user, install dependencies, and set up sudoers file
RUN set -xe; \
useradd -m nadeko; \
apt-get update; \
apt-get install -y --no-install-recommends libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 sudo; \
update-alternatives --install /usr/local/bin/python python /usr/bin/python3.9 1; \
apt-get install -y --no-install-recommends libsqlite3-0 curl ffmpeg sudo python3; \
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; \
chmod a+rx /usr/local/bin/yt-dlp; \
apt-get autoremove -y; \
apt-get autoclean -y
# Copy the built application and the entrypoint script from the build stage
COPY --from=build /app ./
COPY docker-entrypoint.sh /usr/local/sbin
# Set environment variables
ENV shard_id=0
ENV total_shards=1
ENV NadekoBot__creds=/app/data/creds.yml
# Define the data directory as a volume
VOLUME [ "/app/data" ]
# Set the entrypoint and default command
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
CMD dotnet NadekoBot.dll "$shard_id" "$total_shards"
CMD dotnet NadekoBot.dll "$shard_id" "$total_shards"

View File

@@ -1,46 +1,76 @@
# Setting up NadekoBot with Docker
# Deploying NadekoBot with Docker: A Comprehensive Guide
# WORK IN PROGRESS
## Getting Started
### Installation
Ensure Docker and Docker Compose are installed on your system. If not, follow the official Docker guides for your specific operating system:
- [Docker Installation Guide](https://docs.docker.com/engine/install/)
- [Docker Compose Installation Guide](https://docs.docker.com/compose/install/)
## Step-by-Step Installation
1. **Choose Your Workspace:** Select a directory where you'll set up your NadekoBot stack. Use your terminal to navigate to this directory. For the purpose of this guide, we'll use `/opt/stacks/nadekobot/` as an example, but you can choose any directory that suits your needs.
2. **Create a Docker Compose File:** In this directory, create a Docker Compose file named `docker-compose.yml`. You can use any text editor for this task. For instance, to use the `nano` editor, type `nano docker-compose.yml`.
3. **Configure Your Docker Compose File:** Populate your Docker Compose file with the following configuration:
1. Create a `/srv/nadeko` folder
- `mkdir -p /srv/nadeko`
2. Create a `docker-compose.yml`
- nano `docker-compose.yml`
- copy the following contents into it:
##### docker-compose.yml
```yml
version: "3.7"
services:
nadeko:
image: registry.gitlab.com/kwoth/nadekobot:latest
depends_on:
- redis
container_name: nadeko
restart: unless-stopped
environment:
TZ: Europe/Paris
NadekoBot_RedisOptions: redis,name=nadeko
#NadekoBot_ShardRunCommand: dotnet
#NadekoBot_ShardRunArguments: /app/NadekoBot.dll {0} {1}
TZ: Europe/Rome
volumes:
- /srv/nadeko/conf/creds.yml:/app/creds.yml: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
- /opt/stacks/nadekobot/conf/creds.yml:/app/data/creds.yml
- /opt/stacks/nadekobot/data:/app/data
networks: {}
```
3. Save your file and run docker compose
- `docker-compose up`
4. Edit creds in `/srv/nadeko/conf/creds.yml`
5. Run it again with
- `docker-compose up`
### Updating
- `cd /srv/nadeko`
- `docker-compose pull`
- `docker-compose up -d`
4. **Prepare Your Credentials File:** Before running Docker Compose, ensure the `creds.yml` file exists in the `/opt/stacks/nadekobot/conf/` directory. If it's missing, create it using `touch /opt/stacks/nadekobot/conf/creds.yml`. You may need to use `sudo`. Remember to replace `/opt/stacks/nadekobot/` with your chosen directory.
5. **Edit Your Credentials File:** Populate the `creds.yml` file in `/opt/stacks/nadekobot/conf/creds.yml` with your bot's credentials. You can use any text editor for this task. For instance, to use the `nano` editor, type `nano /opt/stacks/nadekobot/conf/creds.yml`. You may need to use `sudo`. Again, replace `/opt/stacks/nadekobot/` with your chosen directory.
6. **Launch Your Bot:** Now, you're ready to run Docker Compose. Use the following command: `docker-compose up -d`.
## Keeping Your Bot Up-to-Date
There are two methods to update your NadekoBot:
### Manual Update
1. **Navigate to Your Directory:** Use `cd /path/to/your/directory` to go to the directory containing your Docker Compose file.
2. **Pull the Latest Images:** Use `docker-compose pull` to fetch the latest images.
3. **Restart Your Containers:** Use `docker-compose up -d` to restart the containers.
### Automatic Update with Watchtower
If you prefer an automated update process, consider using Watchtower. Watchtower automatically updates your Docker containers to the latest versions.
To use Watchtower with NadekoBot, you need to add a specific label to the service in your Docker Compose file. Here's how your Docker Compose file should look:
```yml
services:
nadeko:
image: registry.gitlab.com/kwoth/nadekobot:latest
container_name: nadeko
restart: unless-stopped
labels:
- com.centurylinklabs.watchtower.enable=true
environment:
TZ: Europe/Rome
volumes:
- /opt/stacks/nadekobot/conf/creds.yml:/app/data/creds.yml
- /opt/stacks/nadekobot/data:/app/data
networks: {}
```
Remember to replace `/opt/stacks/nadekobot/` with your chosen directory in the Docker Compose file.
To install and run Watchtower, follow the guide provided by Containrrr:
- [Watchtower Installation and Usage Guide](https://containrrr.dev/watchtower/)

View File

@@ -15,8 +15,8 @@
##### Compatible operating systems:
- Ubuntu: 20.04, 21.04, 21.10, 22.04
- Mint: 19, 20
- Ubuntu: 20.04, 22.04, 24.04
- Mint: 19, 20, 21
- Debian: 10, 11, 12
- RockyLinux: 8, 9
- AlmaLinux: 8, 9
@@ -70,11 +70,11 @@ Open Terminal (if you're on an installation with a window manager) and navigate
1. (Optional) Installing Redis
- ubuntu installation command: `sudo apt-get install redis-server`
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `youtube-dl` (which in turn requires python3)
- ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `yt-dlp` (which in turn requires python3)
- Ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
- yt-dlp installation command: `sudo wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp && sudo chmod a+rx /usr/local/bin/yt-dlp`
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`
*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
@@ -268,7 +268,7 @@ This method is similar to the one above, but requires one extra step, with the a
echo '#!/bin/bash'
echo ""
echo "echo \"Running NadekoBot in the background with auto restart\"
youtube-dl -U
yt-dlp -U
# If you want Nadeko to be compiled prior to every startup, uncomment the lines
# below. Note that it's not necessary unless you are personally modifying the
@@ -300,7 +300,7 @@ This method is similar to the one above, but requires one extra step, with the a
echo \"Waiting for 5 seconds...\"
sleep 5
youtube-dl -U
yt-dlp -U
echo \"Restarting NadekoBot...\"
done

View File

@@ -1,4 +1,4 @@
dotnet ef migrations remove -c SqliteContext -f
dotnet ef migrations remove -c PostgreSqlContext -f
dotnet ef migrations remove -c MysqlContext -f
dotnet ef migrations remove -c SqliteContext -f -p src/NadekoBot/NadekoBot.csproj
dotnet ef migrations remove -c PostgreSqlContext -f -p src/NadekoBot/NadekoBot.csproj
dotnet ef migrations remove -c MysqlContext -f -p src/NadekoBot/NadekoBot.csproj

View File

@@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
</ItemGroup>

View File

@@ -181,9 +181,9 @@ dotnet_naming_rule.private_readonly_field.symbols = private_readonly_field
dotnet_naming_rule.private_readonly_field.style = begins_with_underscore
dotnet_naming_rule.private_readonly_field.severity = warning
dotnet_naming_rule.private_field.symbols = private_field
dotnet_naming_rule.private_field.style = camel_case
dotnet_naming_rule.private_field.severity = warning
# dotnet_naming_rule.private_field.symbols = private_field
# dotnet_naming_rule.private_field.style = camel_case
# dotnet_naming_rule.private_field.severity = warning
dotnet_naming_rule.const_fields.symbols = const_fields
dotnet_naming_rule.const_fields.style = all_upper

View File

@@ -93,7 +93,7 @@ public sealed class Bot : IBot
private void AddServices()
{
var startingGuildIdList = GetCurrentGuildIds();
var sw = Stopwatch.StartNew();
var startTime = Stopwatch.GetTimestamp();
var bot = Client.CurrentUser;
using (var uow = _db.GetDbContext())
@@ -160,9 +160,8 @@ public sealed class Bot : IBot
{
LoadTypeReaders(a);
}
sw.Stop();
Log.Information("All services loaded in {ServiceLoadTime:F2}s", sw.Elapsed.TotalSeconds);
Log.Information("All services loaded in {ServiceLoadTime:F2}s", Stopwatch.GetElapsedTime(startTime) .TotalSeconds);
}
private void LoadTypeReaders(Assembly assembly)
@@ -259,7 +258,7 @@ public sealed class Bot : IBot
if (ShardId == 0)
await _db.SetupAsync();
var sw = Stopwatch.StartNew();
var startTime = Stopwatch.GetTimestamp();
await LoginAsync(_creds.Token);
@@ -274,8 +273,7 @@ public sealed class Bot : IBot
Helpers.ReadErrorAndExit(9);
}
sw.Stop();
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s", Client.ShardId, sw.Elapsed.TotalSeconds);
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s", Client.ShardId, Stopwatch.GetElapsedTime(startTime).TotalSeconds);
var commandHandler = Services.GetRequiredService<CommandHandler>();
// start handling messages received in commandhandler

View File

@@ -20,48 +20,48 @@ public static class DiscordUserExtensions
string discrim,
string avatarId)
=> ctx.GetTable<DiscordUser>()
.InsertOrUpdate(
() => new()
{
UserId = userId,
Username = username,
Discriminator = discrim,
AvatarId = avatarId,
TotalXp = 0,
CurrencyAmount = 0
},
old => new()
{
Username = username,
Discriminator = discrim,
AvatarId = avatarId
},
() => new()
{
UserId = userId
});
.InsertOrUpdate(
() => new()
{
UserId = userId,
Username = username,
Discriminator = discrim,
AvatarId = avatarId,
TotalXp = 0,
CurrencyAmount = 0
},
old => new()
{
Username = username,
Discriminator = discrim,
AvatarId = avatarId
},
() => new()
{
UserId = userId
});
public static Task EnsureUserCreatedAsync(
this DbContext ctx,
ulong userId)
=> ctx.GetTable<DiscordUser>()
.InsertOrUpdateAsync(
() => new()
{
UserId = userId,
Username = "Unknown",
Discriminator = "????",
AvatarId = string.Empty,
TotalXp = 0,
CurrencyAmount = 0
},
old => new()
{
},
() => new()
{
UserId = userId
});
.InsertOrUpdateAsync(
() => new()
{
UserId = userId,
Username = "Unknown",
Discriminator = "????",
AvatarId = string.Empty,
TotalXp = 0,
CurrencyAmount = 0
},
old => new()
{
},
() => new()
{
UserId = userId
});
//temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
public static DiscordUser GetOrCreateUser(
@@ -83,25 +83,29 @@ public static class DiscordUserExtensions
public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
=> users.AsQueryable()
.Where(x => x.TotalXp
> users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
.Count()
.Where(x => x.TotalXp
> users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
.Count()
+ 1;
public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page, int perPage)
=> users.AsQueryable().OrderByDescending(x => x.TotalXp).Skip(page * perPage).Take(perPage).AsEnumerable()
.ToArray();
public static async Task<IReadOnlyCollection<DiscordUser>> GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page, int perPage)
=> await users.ToLinqToDBTable()
.OrderByDescending(x => x.TotalXp)
.Skip(page * perPage)
.Take(perPage)
.ToArrayAsyncLinqToDB();
public static Task<List<DiscordUser>> GetTopRichest(
this DbSet<DiscordUser> users,
ulong botId,
int page = 0, int perPage = 9)
int page = 0,
int perPage = 9)
=> users.AsQueryable()
.Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
.OrderByDescending(c => c.CurrencyAmount)
.Skip(page * perPage)
.Take(perPage)
.ToListAsyncLinqToDB();
.Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
.OrderByDescending(c => c.CurrencyAmount)
.Skip(page * perPage)
.Take(perPage)
.ToListAsyncLinqToDB();
public static async Task<long> GetUserCurrencyAsync(this DbSet<DiscordUser> users, ulong userId)
=> (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0;
@@ -118,8 +122,8 @@ public static class DiscordUserExtensions
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
=> users.AsQueryable()
.Where(x => x.UserId != botId)
.OrderByDescending(x => x.CurrencyAmount)
.Take(users.Count() / 100 == 0 ? 1 : users.Count() / 100)
.Sum(x => x.CurrencyAmount);
.Where(x => x.UserId != botId)
.OrderByDescending(x => x.CurrencyAmount)
.Take(users.Count() / 100 == 0 ? 1 : users.Count() / 100)
.Sum(x => x.CurrencyAmount);
}

View File

@@ -2,7 +2,6 @@
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db.Models;
namespace NadekoBot.Db;
@@ -27,33 +26,33 @@ public static class UserXpExtensions
return usr;
}
public static List<UserXpStats> GetUsersFor(this DbSet<UserXpStats> xps, ulong guildId, int page)
=> xps.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Skip(page * 9)
.Take(9)
.ToList();
public static async Task<IReadOnlyCollection<UserXpStats>> GetUsersFor(
this DbSet<UserXpStats> xps,
ulong guildId,
int page)
=> await xps.ToLinqToDBTable()
.Where(x => x.GuildId == guildId)
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Skip(page * 9)
.Take(9)
.ToArrayAsyncLinqToDB();
public static List<UserXpStats> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
=> xps.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Take(count)
.ToList();
public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
=> await xps.ToLinqToDBTable()
.Where(x => x.GuildId == guildId)
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Take(count)
.ToListAsyncLinqToDB();
public static int GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
=> xps.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId
&& x.Xp + x.AwardedXp
> xps.AsQueryable()
.Where(y => y.UserId == userId && y.GuildId == guildId)
.Select(y => y.Xp + y.AwardedXp)
.FirstOrDefault())
.Count()
public static async Task<int> GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
=> await xps.ToLinqToDBTable()
.Where(x => x.GuildId == guildId
&& x.Xp + x.AwardedXp
> xps.AsQueryable()
.Where(y => y.UserId == userId && y.GuildId == guildId)
.Select(y => y.Xp + y.AwardedXp)
.FirstOrDefault())
.CountAsyncLinqToDB()
+ 1;
public static void ResetGuildUserXp(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
@@ -61,12 +60,11 @@ public static class UserXpExtensions
public static void ResetGuildXp(this DbSet<UserXpStats> xps, ulong guildId)
=> xps.Delete(x => x.GuildId == guildId);
public static async Task<LevelStats> GetLevelDataFor(this ITable<UserXpStats> userXp, ulong guildId, ulong userId)
=> await userXp
.Where(x => x.GuildId == guildId && x.UserId == userId)
.FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
? new(uxs.Xp + uxs.AwardedXp)
: new(0);
=> await userXp
.Where(x => x.GuildId == guildId && x.UserId == userId)
.FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
? new(uxs.Xp + uxs.AwardedXp)
: new(0);
}

View File

@@ -3,6 +3,8 @@ namespace NadekoBot.Db.Models;
public class DelMsgOnCmdChannel : DbEntity
{
public int GuildConfigId { get; set; }
public ulong ChannelId { get; set; }
public bool State { get; set; }

View File

@@ -3,6 +3,7 @@ namespace NadekoBot.Db.Models;
public class GuildConfig : DbEntity
{
// public bool Keep { get; set; }
public ulong GuildId { get; set; }
public string Prefix { get; set; }

View File

@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace NadekoBot.Db.Models;
public class HoneypotChannel
{
[Key]
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
}

View File

@@ -1,8 +0,0 @@
#nullable disable
namespace NadekoBot.Db.Models;
public class IgnoredVoicePresenceChannel : DbEntity
{
public LogSetting LogSetting { get; set; }
public ulong ChannelId { get; set; }
}

View File

@@ -40,6 +40,9 @@ public class StreamRoleSettings : DbEntity
public class StreamRoleBlacklistedUser : DbEntity
{
public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; }
public string Username { get; set; }
@@ -57,6 +60,9 @@ public class StreamRoleBlacklistedUser : DbEntity
public class StreamRoleWhitelistedUser : DbEntity
{
public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; }
public string Username { get; set; }

View File

@@ -2,8 +2,9 @@
public class AntiAltSetting
{
public int Id { get; set; }
public int GuildConfigId { get; set; }
public int Id { get; set; }
public TimeSpan MinAge { get; set; }
public PunishmentAction Action { get; set; }
public int ActionDurationMinutes { get; set; }

View File

@@ -1,12 +1,13 @@
#nullable disable
using System.ComponentModel.DataAnnotations.Schema;
namespace NadekoBot.Db.Models;
public class AntiRaidSetting : DbEntity
{
public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public int UserThreshold { get; set; }
public int Seconds { get; set; }
public PunishmentAction Action { get; set; }

View File

@@ -4,8 +4,7 @@
public class AntiSpamSetting : DbEntity
{
public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public PunishmentAction Action { get; set; }
public int MessageThreshold { get; set; } = 3;
public int MuteTime { get; set; }

View File

@@ -14,17 +14,3 @@ public class FilterChannelId : DbEntity
public override int GetHashCode()
=> ChannelId.GetHashCode();
}
public class FilterWordsChannelId : DbEntity
{
public ulong ChannelId { get; set; }
public bool Equals(FilterWordsChannelId other)
=> ChannelId == other.ChannelId;
public override bool Equals(object obj)
=> obj is FilterWordsChannelId fci && Equals(fci);
public override int GetHashCode()
=> ChannelId.GetHashCode();
}

View File

@@ -0,0 +1,17 @@
#nullable disable
namespace NadekoBot.Db.Models;
public class FilterWordsChannelId : DbEntity
{
public int? GuildConfigId { get; set; }
public ulong ChannelId { get; set; }
public bool Equals(FilterWordsChannelId other)
=> ChannelId == other.ChannelId;
public override bool Equals(object obj)
=> obj is FilterWordsChannelId fci && Equals(fci);
public override int GetHashCode()
=> ChannelId.GetHashCode();
}

View File

@@ -1,30 +1,6 @@
#nullable disable
namespace NadekoBot.Db.Models;
/// <summary>
/// Contains data about usage of Patron-Only commands per user
/// in order to provide support for quota limitations
/// (allow user x who is pledging amount y to use the specified command only
/// x amount of times in the specified time period)
/// </summary>
public class PatronQuota
{
public ulong UserId { get; set; }
public FeatureType FeatureType { get; set; }
public string Feature { get; set; }
public uint HourlyCount { get; set; }
public uint DailyCount { get; set; }
public uint MonthlyCount { get; set; }
}
public enum FeatureType
{
Command,
Group,
Module,
Limit
}
public class PatronUser
{
public string UniquePlatformUserId { get; set; }

View File

@@ -51,6 +51,8 @@ public class XpCurrencyReward : DbEntity
public class ExcludedItem : DbEntity
{
public XpSettings XpSettings { get; set; }
public ulong ItemId { get; set; }
public ExcludedItemType ItemType { get; set; }

View File

@@ -28,7 +28,6 @@ public abstract class NadekoContext : DbContext
//logging
public DbSet<LogSetting> LogSettings { get; set; }
public DbSet<IgnoredVoicePresenceChannel> IgnoredVoicePresenceCHannels { get; set; }
public DbSet<IgnoredLogItem> IgnoredLogChannels { get; set; }
public DbSet<RotatingPlayingStatus> RotatingStatus { get; set; }
@@ -54,15 +53,14 @@ public abstract class NadekoContext : DbContext
public DbSet<PatronUser> Patrons { get; set; }
public DbSet<PatronQuota> PatronQuotas { get; set; }
public DbSet<StreamOnlineMessage> StreamOnlineMessages { get; set; }
public DbSet<StickyRole> StickyRoles { get; set; }
public DbSet<TodoModel> Todos { get; set; }
public DbSet<ArchivedTodoListModel> TodosArchive { get; set; }
public DbSet<HoneypotChannel> HoneyPotChannels { get; set; }
// todo add guild colors
// public DbSet<GuildColors> GuildColors { get; set; }
@@ -86,15 +84,84 @@ public abstract class NadekoContext : DbContext
#region GuildConfig
var configEntity = modelBuilder.Entity<GuildConfig>();
configEntity.HasIndex(c => c.GuildId)
.IsUnique();
configEntity.Property(x => x.VerboseErrors)
.HasDefaultValue(true);
modelBuilder.Entity<AntiSpamSetting>().HasOne(x => x.GuildConfig).WithOne(x => x.AntiSpamSetting);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.DelMsgOnCmdChannels)
.WithOne()
.HasForeignKey(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<AntiRaidSetting>().HasOne(x => x.GuildConfig).WithOne(x => x.AntiRaidSetting);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FollowedStreams)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.GenerateCurrencyChannelIds)
.WithOne(x => x.GuildConfig)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.Permissions)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.CommandCooldowns)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterInvitesChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterLinksChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilteredWords)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterWordsChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.MutedUsers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiRaidSetting)
.WithOne()
.HasForeignKey<AntiRaidSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
// start antispam
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiSpamSetting)
.WithOne()
.HasForeignKey<AntiSpamSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<AntiSpamSetting>()
.HasMany(x => x.IgnoredChannels)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// end antispam
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiAltSetting)
@@ -102,6 +169,98 @@ public abstract class NadekoContext : DbContext
.HasForeignKey<AntiAltSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnmuteTimers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnbanTimer)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnroleTimer)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.VcRoleInfos)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.CommandAliases)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.WarnPunishments)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SlowmodeIgnoredRoles)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SlowmodeIgnoredUsers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// start shop
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.ShopEntries)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ShopEntry>()
.HasMany(x => x.Items)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// end shop
// start streamrole
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.StreamRole)
.WithOne(x => x.GuildConfig)
.HasForeignKey<StreamRoleSettings>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StreamRoleSettings>()
.HasMany(x => x.Whitelist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StreamRoleSettings>()
.HasMany(x => x.Blacklist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
// end streamrole
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.XpSettings)
.WithOne(x => x.GuildConfig)
.HasForeignKey<XpSettings>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FeedSubs)
.WithOne(x => x.GuildConfig)
.HasForeignKey(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SelfAssignableRoleGroupNames)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<FeedSub>()
.HasAlternateKey(x => new
{
@@ -117,11 +276,6 @@ public abstract class NadekoContext : DbContext
#endregion
#region streamrole
modelBuilder.Entity<StreamRoleSettings>().HasOne(x => x.GuildConfig).WithOne(x => x.StreamRole);
#endregion
#region Self Assignable Roles
@@ -217,12 +371,6 @@ public abstract class NadekoContext : DbContext
#endregion
#region XpSettings
modelBuilder.Entity<XpSettings>().HasOne(x => x.GuildConfig).WithOne(x => x.XpSettings);
#endregion
#region XpRoleReward
modelBuilder.Entity<XpRoleReward>()
@@ -233,6 +381,21 @@ public abstract class NadekoContext : DbContext
})
.IsUnique();
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.RoleRewards)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.CurrencyRewards)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.ExclusionList)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
#endregion
#region Club
@@ -331,9 +494,9 @@ public abstract class NadekoContext : DbContext
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
modelBuilder.Entity<BanTemplate>()
.Property(x => x.PruneDays)
.HasDefaultValue(null)
.IsRequired(false);
.Property(x => x.PruneDays)
.HasDefaultValue(null)
.IsRequired(false);
#endregion
@@ -433,19 +596,9 @@ public abstract class NadekoContext : DbContext
});
// quotes are per user id
modelBuilder.Entity<PatronQuota>(pq =>
{
pq.HasIndex(x => x.UserId).IsUnique(false);
pq.HasKey(x => new
{
x.UserId,
x.FeatureType,
x.Feature
});
});
#endregion
#region Xp Item Shop
modelBuilder.Entity<XpShopOwnedItem>(
@@ -453,76 +606,77 @@ public abstract class NadekoContext : DbContext
{
// user can own only one of each item
x.HasIndex(model => new
{
model.UserId,
model.ItemType,
model.ItemKey
})
.IsUnique();
{
model.UserId,
model.ItemType,
model.ItemKey
})
.IsUnique();
});
#endregion
#region AutoPublish
modelBuilder.Entity<AutoPublishChannel>(apc => apc
.HasIndex(x => x.GuildId)
.IsUnique());
.HasIndex(x => x.GuildId)
.IsUnique());
#endregion
#region GamblingStats
modelBuilder.Entity<GamblingStats>(gs => gs
.HasIndex(x => x.Feature)
.IsUnique());
.HasIndex(x => x.Feature)
.IsUnique());
#endregion
#region Sticky Roles
modelBuilder.Entity<StickyRole>(sr => sr.HasIndex(x => new
{
x.GuildId,
x.UserId
}).IsUnique());
{
x.GuildId,
x.UserId
})
.IsUnique());
#endregion
#region Giveaway
modelBuilder.Entity<GiveawayModel>()
.HasMany(x => x.Participants)
.WithOne()
.HasForeignKey(x => x.GiveawayId)
.OnDelete(DeleteBehavior.Cascade);
.HasMany(x => x.Participants)
.WithOne()
.HasForeignKey(x => x.GiveawayId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GiveawayUser>(gu => gu
.HasIndex(x => new
{
x.GiveawayId,
x.UserId
})
.IsUnique());
.HasIndex(x => new
{
x.GiveawayId,
x.UserId
})
.IsUnique());
#endregion
#region Todo
modelBuilder.Entity<TodoModel>()
.HasKey(x => x.Id);
modelBuilder.Entity<TodoModel>()
.HasIndex(x => x.UserId)
.IsUnique(false);
.HasIndex(x => x.UserId)
.IsUnique(false);
modelBuilder.Entity<ArchivedTodoListModel>()
.HasMany(x => x.Items)
.WithOne()
.HasForeignKey(x => x.ArchiveId)
.OnDelete(DeleteBehavior.Cascade);
.HasMany(x => x.Items)
.WithOne()
.HasForeignKey(x => x.ArchiveId)
.OnDelete(DeleteBehavior.Cascade);
#endregion
}

View File

@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Db.Models;
namespace NadekoBot.Migrations;
@@ -39,4 +40,15 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
throw new NotSupportedException("This database provider doesn't have an implementation for MigrateRero");
}
}
public static void GuildConfigCleanup(MigrationBuilder builder)
{
builder.Sql($"""
DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL;
""");
builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
""");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,703 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
/// <inheritdoc />
public partial class guidlconfigcleanup : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting");
migrationBuilder.DropForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore");
migrationBuilder.DropForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting");
migrationBuilder.DropForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias");
migrationBuilder.DropForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown");
migrationBuilder.DropForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel");
migrationBuilder.DropForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem");
migrationBuilder.DropForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid");
migrationBuilder.DropForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword");
migrationBuilder.DropForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid");
migrationBuilder.DropForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid");
migrationBuilder.DropForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream");
migrationBuilder.DropForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid");
migrationBuilder.DropForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid");
migrationBuilder.DropForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions");
migrationBuilder.DropForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry");
migrationBuilder.DropForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser");
migrationBuilder.DropForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
table: "streamroleblacklisteduser");
migrationBuilder.DropForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
table: "streamrolewhitelisteduser");
migrationBuilder.DropForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer");
migrationBuilder.DropForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer");
migrationBuilder.DropForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer");
migrationBuilder.DropForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo");
migrationBuilder.DropForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment");
migrationBuilder.DropTable(
name: "ignoredvoicepresencechannels");
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamrolewhitelisteduser",
type: "int",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "int",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamroleblacklisteduser",
type: "int",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "int",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "guildconfigid",
table: "delmsgoncmdchannel",
type: "int",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "int",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore",
column: "antispamsettingid",
principalTable: "antispamsetting",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem",
column: "xpsettingsid",
principalTable: "xpsettings",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem",
column: "shopentryid",
principalTable: "shopentry",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
table: "streamroleblacklisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
table: "streamrolewhitelisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting");
migrationBuilder.DropForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore");
migrationBuilder.DropForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting");
migrationBuilder.DropForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias");
migrationBuilder.DropForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown");
migrationBuilder.DropForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel");
migrationBuilder.DropForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem");
migrationBuilder.DropForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid");
migrationBuilder.DropForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword");
migrationBuilder.DropForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid");
migrationBuilder.DropForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid");
migrationBuilder.DropForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream");
migrationBuilder.DropForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid");
migrationBuilder.DropForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid");
migrationBuilder.DropForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions");
migrationBuilder.DropForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry");
migrationBuilder.DropForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser");
migrationBuilder.DropForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
table: "streamroleblacklisteduser");
migrationBuilder.DropForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
table: "streamrolewhitelisteduser");
migrationBuilder.DropForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer");
migrationBuilder.DropForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer");
migrationBuilder.DropForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer");
migrationBuilder.DropForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo");
migrationBuilder.DropForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment");
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamrolewhitelisteduser",
type: "int",
nullable: true,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamroleblacklisteduser",
type: "int",
nullable: true,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<int>(
name: "guildconfigid",
table: "delmsgoncmdchannel",
type: "int",
nullable: true,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.CreateTable(
name: "ignoredvoicepresencechannels",
columns: table => new
{
id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
logsettingid = table.Column<int>(type: "int", nullable: true),
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_ignoredvoicepresencechannels", x => x.id);
table.ForeignKey(
name: "fk_ignoredvoicepresencechannels_logsettings_logsettingid",
column: x => x.logsettingid,
principalTable: "logsettings",
principalColumn: "id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "ix_ignoredvoicepresencechannels_logsettingid",
table: "ignoredvoicepresencechannels",
column: "logsettingid");
migrationBuilder.AddForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore",
column: "antispamsettingid",
principalTable: "antispamsetting",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem",
column: "xpsettingsid",
principalTable: "xpsettings",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem",
column: "shopentryid",
principalTable: "shopentry",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
table: "streamroleblacklisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
table: "streamrolewhitelisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
/// <inheritdoc />
public partial class removepatronlimits : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "patronquotas");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "patronquotas",
columns: table => new
{
userid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
featuretype = table.Column<int>(type: "int", nullable: false),
feature = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
dailycount = table.Column<uint>(type: "int unsigned", nullable: false),
hourlycount = table.Column<uint>(type: "int unsigned", nullable: false),
monthlycount = table.Column<uint>(type: "int unsigned", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_patronquotas", x => new { x.userid, x.featuretype, x.feature });
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "ix_patronquotas_userid",
table: "patronquotas",
column: "userid");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
/// <inheritdoc />
public partial class honeypot : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "honeypotchannels",
columns: table => new
{
guildid = table.Column<ulong>(type: "bigint unsigned", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_honeypotchannels", x => x.guildid);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "honeypotchannels");
}
}
}

View File

@@ -673,7 +673,7 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("datetime(6)")
.HasColumnName("dateadded");
b.Property<int?>("GuildConfigId")
b.Property<int>("GuildConfigId")
.HasColumnType("int")
.HasColumnName("guildconfigid");
@@ -1388,6 +1388,25 @@ namespace NadekoBot.Migrations.Mysql
b.ToTable("guildconfigs", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.HoneypotChannel", b =>
{
b.Property<ulong>("GuildId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint unsigned")
.HasColumnName("guildid");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<ulong>("GuildId"));
b.Property<ulong>("ChannelId")
.HasColumnType("bigint unsigned")
.HasColumnName("channelid");
b.HasKey("GuildId")
.HasName("pk_honeypotchannels");
b.ToTable("honeypotchannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
@@ -1423,36 +1442,6 @@ namespace NadekoBot.Migrations.Mysql
b.ToTable("ignoredlogchannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("id");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<ulong>("ChannelId")
.HasColumnType("bigint unsigned")
.HasColumnName("channelid");
b.Property<DateTime?>("DateAdded")
.HasColumnType("datetime(6)")
.HasColumnName("dateadded");
b.Property<int?>("LogSettingId")
.HasColumnType("int")
.HasColumnName("logsettingid");
b.HasKey("Id")
.HasName("pk_ignoredvoicepresencechannels");
b.HasIndex("LogSettingId")
.HasDatabaseName("ix_ignoredvoicepresencechannels_logsettingid");
b.ToTable("ignoredvoicepresencechannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.ImageOnlyChannel", b =>
{
b.Property<int>("Id")
@@ -1748,41 +1737,6 @@ namespace NadekoBot.Migrations.Mysql
b.ToTable("expressions", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
{
b.Property<ulong>("UserId")
.HasColumnType("bigint unsigned")
.HasColumnName("userid");
b.Property<int>("FeatureType")
.HasColumnType("int")
.HasColumnName("featuretype");
b.Property<string>("Feature")
.HasColumnType("varchar(255)")
.HasColumnName("feature");
b.Property<uint>("DailyCount")
.HasColumnType("int unsigned")
.HasColumnName("dailycount");
b.Property<uint>("HourlyCount")
.HasColumnType("int unsigned")
.HasColumnName("hourlycount");
b.Property<uint>("MonthlyCount")
.HasColumnType("int unsigned")
.HasColumnName("monthlycount");
b.HasKey("UserId", "FeatureType", "Feature")
.HasName("pk_patronquotas");
b.HasIndex("UserId")
.HasDatabaseName("ix_patronquotas_userid");
b.ToTable("patronquotas", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
{
b.Property<ulong>("UserId")
@@ -2510,7 +2464,7 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("datetime(6)")
.HasColumnName("dateadded");
b.Property<int?>("StreamRoleSettingsId")
b.Property<int>("StreamRoleSettingsId")
.HasColumnType("int")
.HasColumnName("streamrolesettingsid");
@@ -2587,7 +2541,7 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("datetime(6)")
.HasColumnName("dateadded");
b.Property<int?>("StreamRoleSettingsId")
b.Property<int>("StreamRoleSettingsId")
.HasColumnType("int")
.HasColumnName("streamrolesettingsid");
@@ -3226,14 +3180,12 @@ namespace NadekoBot.Migrations.Mysql
modelBuilder.Entity("NadekoBot.Db.Models.AntiRaidSetting", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithOne("AntiRaidSetting")
.HasForeignKey("NadekoBot.Db.Models.AntiRaidSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_antiraidsetting_guildconfigs_guildconfigid");
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Db.Models.AntiSpamIgnore", b =>
@@ -3241,19 +3193,18 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.AntiSpamSetting", null)
.WithMany("IgnoredChannels")
.HasForeignKey("AntiSpamSettingId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_antispamignore_antispamsetting_antispamsettingid");
});
modelBuilder.Entity("NadekoBot.Db.Models.AntiSpamSetting", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithOne("AntiSpamSetting")
.HasForeignKey("NadekoBot.Db.Models.AntiSpamSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_antispamsetting_guildconfigs_guildconfigid");
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Db.Models.AutoTranslateUser", b =>
@@ -3326,6 +3277,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("CommandAliases")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_commandalias_guildconfigs_guildconfigid");
});
@@ -3334,6 +3286,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_commandcooldown_guildconfigs_guildconfigid");
});
@@ -3342,6 +3295,8 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("DelMsgOnCmdChannels")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_delmsgoncmdchannel_guildconfigs_guildconfigid");
});
@@ -3358,10 +3313,13 @@ namespace NadekoBot.Migrations.Mysql
modelBuilder.Entity("NadekoBot.Db.Models.ExcludedItem", b =>
{
b.HasOne("NadekoBot.Db.Models.XpSettings", null)
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
.WithMany("ExclusionList")
.HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_excludeditem_xpsettings_xpsettingsid");
b.Navigation("XpSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.FeedSub", b =>
@@ -3381,6 +3339,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filterchannelid_guildconfigs_guildconfigid");
});
@@ -3389,6 +3348,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterLinksChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filterlinkschannelid_guildconfigs_guildconfigid");
});
@@ -3397,6 +3357,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filterwordschannelid_guildconfigs_guildconfigid");
});
@@ -3405,6 +3366,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filteredword_guildconfigs_guildconfigid");
});
@@ -3413,6 +3375,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_followedstream_guildconfigs_guildconfigid");
});
@@ -3421,6 +3384,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_gcchannelid_guildconfigs_guildconfigid");
b.Navigation("GuildConfig");
@@ -3460,21 +3424,12 @@ namespace NadekoBot.Migrations.Mysql
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId")
.HasConstraintName("fk_ignoredvoicepresencechannels_logsettings_logsettingid");
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Db.Models.MutedUserId", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_muteduserid_guildconfigs_guildconfigid");
});
@@ -3483,6 +3438,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("Permissions")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_permissions_guildconfigs_guildconfigid");
});
@@ -3500,6 +3456,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("ShopEntries")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_shopentry_guildconfigs_guildconfigid");
});
@@ -3508,6 +3465,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.ShopEntry", null)
.WithMany("Items")
.HasForeignKey("ShopEntryId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_shopentryitem_shopentry_shopentryid");
});
@@ -3516,6 +3474,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredRoles")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_slowmodeignoredrole_guildconfigs_guildconfigid");
});
@@ -3524,15 +3483,20 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredUsers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_slowmodeignoreduser_guildconfigs_guildconfigid");
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleBlacklistedUser", b =>
{
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", null)
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
.WithMany("Blacklist")
.HasForeignKey("StreamRoleSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~");
b.Navigation("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleSettings", b =>
@@ -3549,10 +3513,14 @@ namespace NadekoBot.Migrations.Mysql
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleWhitelistedUser", b =>
{
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", null)
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
.WithMany("Whitelist")
.HasForeignKey("StreamRoleSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~");
b.Navigation("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
@@ -3569,6 +3537,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnbanTimer")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_unbantimer_guildconfigs_guildconfigid");
});
@@ -3577,6 +3546,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnmuteTimers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_unmutetimer_guildconfigs_guildconfigid");
});
@@ -3585,6 +3555,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnroleTimer")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_unroletimer_guildconfigs_guildconfigid");
});
@@ -3593,6 +3564,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("VcRoleInfos")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_vcroleinfo_guildconfigs_guildconfigid");
});
@@ -3663,6 +3635,7 @@ namespace NadekoBot.Migrations.Mysql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("WarnPunishments")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_warningpunishment_guildconfigs_guildconfigid");
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,702 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
/// <inheritdoc />
public partial class guidlconfigcleanup : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting");
migrationBuilder.DropForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore");
migrationBuilder.DropForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting");
migrationBuilder.DropForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias");
migrationBuilder.DropForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown");
migrationBuilder.DropForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel");
migrationBuilder.DropForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem");
migrationBuilder.DropForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid");
migrationBuilder.DropForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword");
migrationBuilder.DropForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid");
migrationBuilder.DropForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid");
migrationBuilder.DropForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream");
migrationBuilder.DropForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid");
migrationBuilder.DropForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid");
migrationBuilder.DropForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions");
migrationBuilder.DropForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry");
migrationBuilder.DropForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser");
migrationBuilder.DropForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesett~",
table: "streamroleblacklisteduser");
migrationBuilder.DropForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesett~",
table: "streamrolewhitelisteduser");
migrationBuilder.DropForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer");
migrationBuilder.DropForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer");
migrationBuilder.DropForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer");
migrationBuilder.DropForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo");
migrationBuilder.DropForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment");
migrationBuilder.DropTable(
name: "ignoredvoicepresencechannels");
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamrolewhitelisteduser",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamroleblacklisteduser",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "guildconfigid",
table: "delmsgoncmdchannel",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore",
column: "antispamsettingid",
principalTable: "antispamsetting",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem",
column: "xpsettingsid",
principalTable: "xpsettings",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem",
column: "shopentryid",
principalTable: "shopentry",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesett~",
table: "streamroleblacklisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesett~",
table: "streamrolewhitelisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting");
migrationBuilder.DropForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore");
migrationBuilder.DropForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting");
migrationBuilder.DropForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias");
migrationBuilder.DropForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown");
migrationBuilder.DropForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel");
migrationBuilder.DropForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem");
migrationBuilder.DropForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid");
migrationBuilder.DropForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword");
migrationBuilder.DropForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid");
migrationBuilder.DropForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid");
migrationBuilder.DropForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream");
migrationBuilder.DropForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid");
migrationBuilder.DropForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid");
migrationBuilder.DropForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions");
migrationBuilder.DropForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry");
migrationBuilder.DropForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole");
migrationBuilder.DropForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser");
migrationBuilder.DropForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesett~",
table: "streamroleblacklisteduser");
migrationBuilder.DropForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesett~",
table: "streamrolewhitelisteduser");
migrationBuilder.DropForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer");
migrationBuilder.DropForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer");
migrationBuilder.DropForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer");
migrationBuilder.DropForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo");
migrationBuilder.DropForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment");
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamrolewhitelisteduser",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<int>(
name: "streamrolesettingsid",
table: "streamroleblacklisteduser",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<int>(
name: "guildconfigid",
table: "delmsgoncmdchannel",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.CreateTable(
name: "ignoredvoicepresencechannels",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
logsettingid = table.Column<int>(type: "integer", nullable: true),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_ignoredvoicepresencechannels", x => x.id);
table.ForeignKey(
name: "fk_ignoredvoicepresencechannels_logsettings_logsettingid",
column: x => x.logsettingid,
principalTable: "logsettings",
principalColumn: "id");
});
migrationBuilder.CreateIndex(
name: "ix_ignoredvoicepresencechannels_logsettingid",
table: "ignoredvoicepresencechannels",
column: "logsettingid");
migrationBuilder.AddForeignKey(
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
table: "antiraidsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_antispamignore_antispamsetting_antispamsettingid",
table: "antispamignore",
column: "antispamsettingid",
principalTable: "antispamsetting",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_antispamsetting_guildconfigs_guildconfigid",
table: "antispamsetting",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "fk_commandalias_guildconfigs_guildconfigid",
table: "commandalias",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_commandcooldown_guildconfigs_guildconfigid",
table: "commandcooldown",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
table: "delmsgoncmdchannel",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_excludeditem_xpsettings_xpsettingsid",
table: "excludeditem",
column: "xpsettingsid",
principalTable: "xpsettings",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filterchannelid_guildconfigs_guildconfigid",
table: "filterchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filteredword_guildconfigs_guildconfigid",
table: "filteredword",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
table: "filterlinkschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
table: "filterwordschannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_followedstream_guildconfigs_guildconfigid",
table: "followedstream",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_gcchannelid_guildconfigs_guildconfigid",
table: "gcchannelid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_muteduserid_guildconfigs_guildconfigid",
table: "muteduserid",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_permissions_guildconfigs_guildconfigid",
table: "permissions",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_shopentry_guildconfigs_guildconfigid",
table: "shopentry",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_shopentryitem_shopentry_shopentryid",
table: "shopentryitem",
column: "shopentryid",
principalTable: "shopentry",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
table: "slowmodeignoredrole",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
table: "slowmodeignoreduser",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesett~",
table: "streamroleblacklisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesett~",
table: "streamrolewhitelisteduser",
column: "streamrolesettingsid",
principalTable: "streamrolesettings",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_unbantimer_guildconfigs_guildconfigid",
table: "unbantimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_unmutetimer_guildconfigs_guildconfigid",
table: "unmutetimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_unroletimer_guildconfigs_guildconfigid",
table: "unroletimer",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
table: "vcroleinfo",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
migrationBuilder.AddForeignKey(
name: "fk_warningpunishment_guildconfigs_guildconfigid",
table: "warningpunishment",
column: "guildconfigid",
principalTable: "guildconfigs",
principalColumn: "id");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
/// <inheritdoc />
public partial class removepatronlimits : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "patronquotas");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "patronquotas",
columns: table => new
{
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
featuretype = table.Column<int>(type: "integer", nullable: false),
feature = table.Column<string>(type: "text", nullable: false),
dailycount = table.Column<long>(type: "bigint", nullable: false),
hourlycount = table.Column<long>(type: "bigint", nullable: false),
monthlycount = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_patronquotas", x => new { x.userid, x.featuretype, x.feature });
});
migrationBuilder.CreateIndex(
name: "ix_patronquotas_userid",
table: "patronquotas",
column: "userid");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
/// <inheritdoc />
public partial class honeypot : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "honeypotchannels",
columns: table => new
{
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_honeypotchannels", x => x.guildid);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "honeypotchannels");
}
}
}

View File

@@ -672,7 +672,7 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("timestamp without time zone")
.HasColumnName("dateadded");
b.Property<int?>("GuildConfigId")
b.Property<int>("GuildConfigId")
.HasColumnType("integer")
.HasColumnName("guildconfigid");
@@ -1387,6 +1387,23 @@ namespace NadekoBot.Migrations.PostgreSql
b.ToTable("guildconfigs", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.HoneypotChannel", b =>
{
b.Property<decimal>("GuildId")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)")
.HasColumnName("guildid");
b.Property<decimal>("ChannelId")
.HasColumnType("numeric(20,0)")
.HasColumnName("channelid");
b.HasKey("GuildId")
.HasName("pk_honeypotchannels");
b.ToTable("honeypotchannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
@@ -1422,36 +1439,6 @@ namespace NadekoBot.Migrations.PostgreSql
b.ToTable("ignoredlogchannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<decimal>("ChannelId")
.HasColumnType("numeric(20,0)")
.HasColumnName("channelid");
b.Property<DateTime?>("DateAdded")
.HasColumnType("timestamp without time zone")
.HasColumnName("dateadded");
b.Property<int?>("LogSettingId")
.HasColumnType("integer")
.HasColumnName("logsettingid");
b.HasKey("Id")
.HasName("pk_ignoredvoicepresencechannels");
b.HasIndex("LogSettingId")
.HasDatabaseName("ix_ignoredvoicepresencechannels_logsettingid");
b.ToTable("ignoredvoicepresencechannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.ImageOnlyChannel", b =>
{
b.Property<int>("Id")
@@ -1747,41 +1734,6 @@ namespace NadekoBot.Migrations.PostgreSql
b.ToTable("expressions", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
{
b.Property<decimal>("UserId")
.HasColumnType("numeric(20,0)")
.HasColumnName("userid");
b.Property<int>("FeatureType")
.HasColumnType("integer")
.HasColumnName("featuretype");
b.Property<string>("Feature")
.HasColumnType("text")
.HasColumnName("feature");
b.Property<long>("DailyCount")
.HasColumnType("bigint")
.HasColumnName("dailycount");
b.Property<long>("HourlyCount")
.HasColumnType("bigint")
.HasColumnName("hourlycount");
b.Property<long>("MonthlyCount")
.HasColumnType("bigint")
.HasColumnName("monthlycount");
b.HasKey("UserId", "FeatureType", "Feature")
.HasName("pk_patronquotas");
b.HasIndex("UserId")
.HasDatabaseName("ix_patronquotas_userid");
b.ToTable("patronquotas", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
{
b.Property<decimal>("UserId")
@@ -2507,7 +2459,7 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("timestamp without time zone")
.HasColumnName("dateadded");
b.Property<int?>("StreamRoleSettingsId")
b.Property<int>("StreamRoleSettingsId")
.HasColumnType("integer")
.HasColumnName("streamrolesettingsid");
@@ -2584,7 +2536,7 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("timestamp without time zone")
.HasColumnName("dateadded");
b.Property<int?>("StreamRoleSettingsId")
b.Property<int>("StreamRoleSettingsId")
.HasColumnType("integer")
.HasColumnName("streamrolesettingsid");
@@ -3223,14 +3175,12 @@ namespace NadekoBot.Migrations.PostgreSql
modelBuilder.Entity("NadekoBot.Db.Models.AntiRaidSetting", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithOne("AntiRaidSetting")
.HasForeignKey("NadekoBot.Db.Models.AntiRaidSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_antiraidsetting_guildconfigs_guildconfigid");
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Db.Models.AntiSpamIgnore", b =>
@@ -3238,19 +3188,18 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.AntiSpamSetting", null)
.WithMany("IgnoredChannels")
.HasForeignKey("AntiSpamSettingId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_antispamignore_antispamsetting_antispamsettingid");
});
modelBuilder.Entity("NadekoBot.Db.Models.AntiSpamSetting", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithOne("AntiSpamSetting")
.HasForeignKey("NadekoBot.Db.Models.AntiSpamSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_antispamsetting_guildconfigs_guildconfigid");
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Db.Models.AutoTranslateUser", b =>
@@ -3323,6 +3272,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("CommandAliases")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_commandalias_guildconfigs_guildconfigid");
});
@@ -3331,6 +3281,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_commandcooldown_guildconfigs_guildconfigid");
});
@@ -3339,6 +3290,8 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("DelMsgOnCmdChannels")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_delmsgoncmdchannel_guildconfigs_guildconfigid");
});
@@ -3355,10 +3308,13 @@ namespace NadekoBot.Migrations.PostgreSql
modelBuilder.Entity("NadekoBot.Db.Models.ExcludedItem", b =>
{
b.HasOne("NadekoBot.Db.Models.XpSettings", null)
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
.WithMany("ExclusionList")
.HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_excludeditem_xpsettings_xpsettingsid");
b.Navigation("XpSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.FeedSub", b =>
@@ -3378,6 +3334,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filterchannelid_guildconfigs_guildconfigid");
});
@@ -3386,6 +3343,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterLinksChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filterlinkschannelid_guildconfigs_guildconfigid");
});
@@ -3394,6 +3352,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filterwordschannelid_guildconfigs_guildconfigid");
});
@@ -3402,6 +3361,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_filteredword_guildconfigs_guildconfigid");
});
@@ -3410,6 +3370,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_followedstream_guildconfigs_guildconfigid");
});
@@ -3418,6 +3379,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_gcchannelid_guildconfigs_guildconfigid");
b.Navigation("GuildConfig");
@@ -3457,21 +3419,12 @@ namespace NadekoBot.Migrations.PostgreSql
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId")
.HasConstraintName("fk_ignoredvoicepresencechannels_logsettings_logsettingid");
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Db.Models.MutedUserId", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_muteduserid_guildconfigs_guildconfigid");
});
@@ -3480,6 +3433,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("Permissions")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_permissions_guildconfigs_guildconfigid");
});
@@ -3497,6 +3451,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("ShopEntries")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_shopentry_guildconfigs_guildconfigid");
});
@@ -3505,6 +3460,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.ShopEntry", null)
.WithMany("Items")
.HasForeignKey("ShopEntryId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_shopentryitem_shopentry_shopentryid");
});
@@ -3513,6 +3469,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredRoles")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_slowmodeignoredrole_guildconfigs_guildconfigid");
});
@@ -3521,15 +3478,20 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredUsers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_slowmodeignoreduser_guildconfigs_guildconfigid");
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleBlacklistedUser", b =>
{
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", null)
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
.WithMany("Blacklist")
.HasForeignKey("StreamRoleSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_streamroleblacklisteduser_streamrolesettings_streamrolesett~");
b.Navigation("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleSettings", b =>
@@ -3546,10 +3508,14 @@ namespace NadekoBot.Migrations.PostgreSql
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleWhitelistedUser", b =>
{
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", null)
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
.WithMany("Whitelist")
.HasForeignKey("StreamRoleSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_streamrolewhitelisteduser_streamrolesettings_streamrolesett~");
b.Navigation("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
@@ -3566,6 +3532,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnbanTimer")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_unbantimer_guildconfigs_guildconfigid");
});
@@ -3574,6 +3541,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnmuteTimers")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_unmutetimer_guildconfigs_guildconfigid");
});
@@ -3582,6 +3550,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnroleTimer")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_unroletimer_guildconfigs_guildconfigid");
});
@@ -3590,6 +3559,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("VcRoleInfos")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_vcroleinfo_guildconfigs_guildconfigid");
});
@@ -3660,6 +3630,7 @@ namespace NadekoBot.Migrations.PostgreSql
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("WarnPunishments")
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_warningpunishment_guildconfigs_guildconfigid");
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,703 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
/// <inheritdoc />
public partial class guidlconfigcleanup : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
MigrationQueries.GuildConfigCleanup(migrationBuilder);
migrationBuilder.DropForeignKey(
name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId",
table: "AntiRaidSetting");
migrationBuilder.DropForeignKey(
name: "FK_AntiSpamIgnore_AntiSpamSetting_AntiSpamSettingId",
table: "AntiSpamIgnore");
migrationBuilder.DropForeignKey(
name: "FK_AntiSpamSetting_GuildConfigs_GuildConfigId",
table: "AntiSpamSetting");
migrationBuilder.DropForeignKey(
name: "FK_CommandAlias_GuildConfigs_GuildConfigId",
table: "CommandAlias");
migrationBuilder.DropForeignKey(
name: "FK_CommandCooldown_GuildConfigs_GuildConfigId",
table: "CommandCooldown");
migrationBuilder.DropForeignKey(
name: "FK_DelMsgOnCmdChannel_GuildConfigs_GuildConfigId",
table: "DelMsgOnCmdChannel");
migrationBuilder.DropForeignKey(
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
table: "ExcludedItem");
migrationBuilder.DropForeignKey(
name: "FK_FilterChannelId_GuildConfigs_GuildConfigId",
table: "FilterChannelId");
migrationBuilder.DropForeignKey(
name: "FK_FilteredWord_GuildConfigs_GuildConfigId",
table: "FilteredWord");
migrationBuilder.DropForeignKey(
name: "FK_FilterLinksChannelId_GuildConfigs_GuildConfigId",
table: "FilterLinksChannelId");
migrationBuilder.DropForeignKey(
name: "FK_FilterWordsChannelId_GuildConfigs_GuildConfigId",
table: "FilterWordsChannelId");
migrationBuilder.DropForeignKey(
name: "FK_FollowedStream_GuildConfigs_GuildConfigId",
table: "FollowedStream");
migrationBuilder.DropForeignKey(
name: "FK_GCChannelId_GuildConfigs_GuildConfigId",
table: "GCChannelId");
migrationBuilder.DropForeignKey(
name: "FK_MutedUserId_GuildConfigs_GuildConfigId",
table: "MutedUserId");
migrationBuilder.DropForeignKey(
name: "FK_Permissions_GuildConfigs_GuildConfigId",
table: "Permissions");
migrationBuilder.DropForeignKey(
name: "FK_ShopEntry_GuildConfigs_GuildConfigId",
table: "ShopEntry");
migrationBuilder.DropForeignKey(
name: "FK_ShopEntryItem_ShopEntry_ShopEntryId",
table: "ShopEntryItem");
migrationBuilder.DropForeignKey(
name: "FK_SlowmodeIgnoredRole_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredRole");
migrationBuilder.DropForeignKey(
name: "FK_SlowmodeIgnoredUser_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredUser");
migrationBuilder.DropForeignKey(
name: "FK_StreamRoleBlacklistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser");
migrationBuilder.DropForeignKey(
name: "FK_StreamRoleWhitelistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser");
migrationBuilder.DropForeignKey(
name: "FK_UnbanTimer_GuildConfigs_GuildConfigId",
table: "UnbanTimer");
migrationBuilder.DropForeignKey(
name: "FK_UnmuteTimer_GuildConfigs_GuildConfigId",
table: "UnmuteTimer");
migrationBuilder.DropForeignKey(
name: "FK_UnroleTimer_GuildConfigs_GuildConfigId",
table: "UnroleTimer");
migrationBuilder.DropForeignKey(
name: "FK_VcRoleInfo_GuildConfigs_GuildConfigId",
table: "VcRoleInfo");
migrationBuilder.DropForeignKey(
name: "FK_WarningPunishment_GuildConfigs_GuildConfigId",
table: "WarningPunishment");
migrationBuilder.DropTable(
name: "IgnoredVoicePresenceCHannels");
migrationBuilder.AlterColumn<int>(
name: "StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "INTEGER",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "INTEGER",
oldNullable: true);
migrationBuilder.AlterColumn<int>(
name: "GuildConfigId",
table: "DelMsgOnCmdChannel",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "INTEGER",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId",
table: "AntiRaidSetting",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AntiSpamIgnore_AntiSpamSetting_AntiSpamSettingId",
table: "AntiSpamIgnore",
column: "AntiSpamSettingId",
principalTable: "AntiSpamSetting",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AntiSpamSetting_GuildConfigs_GuildConfigId",
table: "AntiSpamSetting",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_CommandAlias_GuildConfigs_GuildConfigId",
table: "CommandAlias",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_CommandCooldown_GuildConfigs_GuildConfigId",
table: "CommandCooldown",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_DelMsgOnCmdChannel_GuildConfigs_GuildConfigId",
table: "DelMsgOnCmdChannel",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
table: "ExcludedItem",
column: "XpSettingsId",
principalTable: "XpSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_FilterChannelId_GuildConfigs_GuildConfigId",
table: "FilterChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_FilteredWord_GuildConfigs_GuildConfigId",
table: "FilteredWord",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_FilterLinksChannelId_GuildConfigs_GuildConfigId",
table: "FilterLinksChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_FilterWordsChannelId_GuildConfigs_GuildConfigId",
table: "FilterWordsChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_FollowedStream_GuildConfigs_GuildConfigId",
table: "FollowedStream",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_GCChannelId_GuildConfigs_GuildConfigId",
table: "GCChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_MutedUserId_GuildConfigs_GuildConfigId",
table: "MutedUserId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_Permissions_GuildConfigs_GuildConfigId",
table: "Permissions",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_ShopEntry_GuildConfigs_GuildConfigId",
table: "ShopEntry",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_ShopEntryItem_ShopEntry_ShopEntryId",
table: "ShopEntryItem",
column: "ShopEntryId",
principalTable: "ShopEntry",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_SlowmodeIgnoredRole_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredRole",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_SlowmodeIgnoredUser_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredUser",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_StreamRoleBlacklistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser",
column: "StreamRoleSettingsId",
principalTable: "StreamRoleSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_StreamRoleWhitelistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser",
column: "StreamRoleSettingsId",
principalTable: "StreamRoleSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_UnbanTimer_GuildConfigs_GuildConfigId",
table: "UnbanTimer",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_UnmuteTimer_GuildConfigs_GuildConfigId",
table: "UnmuteTimer",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_UnroleTimer_GuildConfigs_GuildConfigId",
table: "UnroleTimer",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_VcRoleInfo_GuildConfigs_GuildConfigId",
table: "VcRoleInfo",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_WarningPunishment_GuildConfigs_GuildConfigId",
table: "WarningPunishment",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId",
table: "AntiRaidSetting");
migrationBuilder.DropForeignKey(
name: "FK_AntiSpamIgnore_AntiSpamSetting_AntiSpamSettingId",
table: "AntiSpamIgnore");
migrationBuilder.DropForeignKey(
name: "FK_AntiSpamSetting_GuildConfigs_GuildConfigId",
table: "AntiSpamSetting");
migrationBuilder.DropForeignKey(
name: "FK_CommandAlias_GuildConfigs_GuildConfigId",
table: "CommandAlias");
migrationBuilder.DropForeignKey(
name: "FK_CommandCooldown_GuildConfigs_GuildConfigId",
table: "CommandCooldown");
migrationBuilder.DropForeignKey(
name: "FK_DelMsgOnCmdChannel_GuildConfigs_GuildConfigId",
table: "DelMsgOnCmdChannel");
migrationBuilder.DropForeignKey(
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
table: "ExcludedItem");
migrationBuilder.DropForeignKey(
name: "FK_FilterChannelId_GuildConfigs_GuildConfigId",
table: "FilterChannelId");
migrationBuilder.DropForeignKey(
name: "FK_FilteredWord_GuildConfigs_GuildConfigId",
table: "FilteredWord");
migrationBuilder.DropForeignKey(
name: "FK_FilterLinksChannelId_GuildConfigs_GuildConfigId",
table: "FilterLinksChannelId");
migrationBuilder.DropForeignKey(
name: "FK_FilterWordsChannelId_GuildConfigs_GuildConfigId",
table: "FilterWordsChannelId");
migrationBuilder.DropForeignKey(
name: "FK_FollowedStream_GuildConfigs_GuildConfigId",
table: "FollowedStream");
migrationBuilder.DropForeignKey(
name: "FK_GCChannelId_GuildConfigs_GuildConfigId",
table: "GCChannelId");
migrationBuilder.DropForeignKey(
name: "FK_MutedUserId_GuildConfigs_GuildConfigId",
table: "MutedUserId");
migrationBuilder.DropForeignKey(
name: "FK_Permissions_GuildConfigs_GuildConfigId",
table: "Permissions");
migrationBuilder.DropForeignKey(
name: "FK_ShopEntry_GuildConfigs_GuildConfigId",
table: "ShopEntry");
migrationBuilder.DropForeignKey(
name: "FK_ShopEntryItem_ShopEntry_ShopEntryId",
table: "ShopEntryItem");
migrationBuilder.DropForeignKey(
name: "FK_SlowmodeIgnoredRole_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredRole");
migrationBuilder.DropForeignKey(
name: "FK_SlowmodeIgnoredUser_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredUser");
migrationBuilder.DropForeignKey(
name: "FK_StreamRoleBlacklistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser");
migrationBuilder.DropForeignKey(
name: "FK_StreamRoleWhitelistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser");
migrationBuilder.DropForeignKey(
name: "FK_UnbanTimer_GuildConfigs_GuildConfigId",
table: "UnbanTimer");
migrationBuilder.DropForeignKey(
name: "FK_UnmuteTimer_GuildConfigs_GuildConfigId",
table: "UnmuteTimer");
migrationBuilder.DropForeignKey(
name: "FK_UnroleTimer_GuildConfigs_GuildConfigId",
table: "UnroleTimer");
migrationBuilder.DropForeignKey(
name: "FK_VcRoleInfo_GuildConfigs_GuildConfigId",
table: "VcRoleInfo");
migrationBuilder.DropForeignKey(
name: "FK_WarningPunishment_GuildConfigs_GuildConfigId",
table: "WarningPunishment");
migrationBuilder.AlterColumn<int>(
name: "StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser",
type: "INTEGER",
nullable: true,
oldClrType: typeof(int),
oldType: "INTEGER");
migrationBuilder.AlterColumn<int>(
name: "StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser",
type: "INTEGER",
nullable: true,
oldClrType: typeof(int),
oldType: "INTEGER");
migrationBuilder.AlterColumn<int>(
name: "GuildConfigId",
table: "DelMsgOnCmdChannel",
type: "INTEGER",
nullable: true,
oldClrType: typeof(int),
oldType: "INTEGER");
migrationBuilder.CreateTable(
name: "IgnoredVoicePresenceCHannels",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
LogSettingId = table.Column<int>(type: "INTEGER", nullable: true),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_IgnoredVoicePresenceCHannels", x => x.Id);
table.ForeignKey(
name: "FK_IgnoredVoicePresenceCHannels_LogSettings_LogSettingId",
column: x => x.LogSettingId,
principalTable: "LogSettings",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_IgnoredVoicePresenceCHannels_LogSettingId",
table: "IgnoredVoicePresenceCHannels",
column: "LogSettingId");
migrationBuilder.AddForeignKey(
name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId",
table: "AntiRaidSetting",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_AntiSpamIgnore_AntiSpamSetting_AntiSpamSettingId",
table: "AntiSpamIgnore",
column: "AntiSpamSettingId",
principalTable: "AntiSpamSetting",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_AntiSpamSetting_GuildConfigs_GuildConfigId",
table: "AntiSpamSetting",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_CommandAlias_GuildConfigs_GuildConfigId",
table: "CommandAlias",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_CommandCooldown_GuildConfigs_GuildConfigId",
table: "CommandCooldown",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_DelMsgOnCmdChannel_GuildConfigs_GuildConfigId",
table: "DelMsgOnCmdChannel",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_ExcludedItem_XpSettings_XpSettingsId",
table: "ExcludedItem",
column: "XpSettingsId",
principalTable: "XpSettings",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_FilterChannelId_GuildConfigs_GuildConfigId",
table: "FilterChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_FilteredWord_GuildConfigs_GuildConfigId",
table: "FilteredWord",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_FilterLinksChannelId_GuildConfigs_GuildConfigId",
table: "FilterLinksChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_FilterWordsChannelId_GuildConfigs_GuildConfigId",
table: "FilterWordsChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_FollowedStream_GuildConfigs_GuildConfigId",
table: "FollowedStream",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_GCChannelId_GuildConfigs_GuildConfigId",
table: "GCChannelId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_MutedUserId_GuildConfigs_GuildConfigId",
table: "MutedUserId",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Permissions_GuildConfigs_GuildConfigId",
table: "Permissions",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_ShopEntry_GuildConfigs_GuildConfigId",
table: "ShopEntry",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_ShopEntryItem_ShopEntry_ShopEntryId",
table: "ShopEntryItem",
column: "ShopEntryId",
principalTable: "ShopEntry",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_SlowmodeIgnoredRole_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredRole",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_SlowmodeIgnoredUser_GuildConfigs_GuildConfigId",
table: "SlowmodeIgnoredUser",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_StreamRoleBlacklistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleBlacklistedUser",
column: "StreamRoleSettingsId",
principalTable: "StreamRoleSettings",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_StreamRoleWhitelistedUser_StreamRoleSettings_StreamRoleSettingsId",
table: "StreamRoleWhitelistedUser",
column: "StreamRoleSettingsId",
principalTable: "StreamRoleSettings",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_UnbanTimer_GuildConfigs_GuildConfigId",
table: "UnbanTimer",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_UnmuteTimer_GuildConfigs_GuildConfigId",
table: "UnmuteTimer",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_UnroleTimer_GuildConfigs_GuildConfigId",
table: "UnroleTimer",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_VcRoleInfo_GuildConfigs_GuildConfigId",
table: "VcRoleInfo",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_WarningPunishment_GuildConfigs_GuildConfigId",
table: "WarningPunishment",
column: "GuildConfigId",
principalTable: "GuildConfigs",
principalColumn: "Id");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
/// <inheritdoc />
public partial class removepatronlimits : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PatronQuotas");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PatronQuotas",
columns: table => new
{
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
FeatureType = table.Column<int>(type: "INTEGER", nullable: false),
Feature = table.Column<string>(type: "TEXT", nullable: false),
DailyCount = table.Column<uint>(type: "INTEGER", nullable: false),
HourlyCount = table.Column<uint>(type: "INTEGER", nullable: false),
MonthlyCount = table.Column<uint>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PatronQuotas", x => new { x.UserId, x.FeatureType, x.Feature });
});
migrationBuilder.CreateIndex(
name: "IX_PatronQuotas_UserId",
table: "PatronQuotas",
column: "UserId");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
/// <inheritdoc />
public partial class honeypot : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "HoneyPotChannels",
columns: table => new
{
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_HoneyPotChannels", x => x.GuildId);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "HoneyPotChannels");
}
}
}

View File

@@ -501,7 +501,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("GuildConfigId")
b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER");
b.Property<bool>("State")
@@ -1033,6 +1033,20 @@ namespace NadekoBot.Migrations
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Db.Models.HoneypotChannel", b =>
{
b.Property<ulong>("GuildId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.HasKey("GuildId");
b.ToTable("HoneyPotChannels");
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
@@ -1059,28 +1073,6 @@ namespace NadekoBot.Migrations
b.ToTable("IgnoredLogChannels");
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("LogSettingId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Db.Models.ImageOnlyChannel", b =>
{
b.Property<int>("Id")
@@ -1301,33 +1293,6 @@ namespace NadekoBot.Migrations
b.ToTable("Expressions");
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
{
b.Property<ulong>("UserId")
.HasColumnType("INTEGER");
b.Property<int>("FeatureType")
.HasColumnType("INTEGER");
b.Property<string>("Feature")
.HasColumnType("TEXT");
b.Property<uint>("DailyCount")
.HasColumnType("INTEGER");
b.Property<uint>("HourlyCount")
.HasColumnType("INTEGER");
b.Property<uint>("MonthlyCount")
.HasColumnType("INTEGER");
b.HasKey("UserId", "FeatureType", "Feature");
b.HasIndex("UserId");
b.ToTable("PatronQuotas");
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
{
b.Property<ulong>("UserId")
@@ -1867,7 +1832,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("StreamRoleSettingsId")
b.Property<int>("StreamRoleSettingsId")
.HasColumnType("INTEGER");
b.Property<ulong>("UserId")
@@ -1924,7 +1889,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("StreamRoleSettingsId")
b.Property<int>("StreamRoleSettingsId")
.HasColumnType("INTEGER");
b.Property<ulong>("UserId")
@@ -2397,31 +2362,28 @@ namespace NadekoBot.Migrations
modelBuilder.Entity("NadekoBot.Db.Models.AntiRaidSetting", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithOne("AntiRaidSetting")
.HasForeignKey("NadekoBot.Db.Models.AntiRaidSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Db.Models.AntiSpamIgnore", b =>
{
b.HasOne("NadekoBot.Db.Models.AntiSpamSetting", null)
.WithMany("IgnoredChannels")
.HasForeignKey("AntiSpamSettingId");
.HasForeignKey("AntiSpamSettingId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.AntiSpamSetting", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithOne("AntiSpamSetting")
.HasForeignKey("NadekoBot.Db.Models.AntiSpamSetting", "GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Db.Models.AutoTranslateUser", b =>
@@ -2487,21 +2449,25 @@ namespace NadekoBot.Migrations
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("CommandAliases")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.CommandCooldown", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.DelMsgOnCmdChannel", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("DelMsgOnCmdChannels")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
@@ -2516,9 +2482,12 @@ namespace NadekoBot.Migrations
modelBuilder.Entity("NadekoBot.Db.Models.ExcludedItem", b =>
{
b.HasOne("NadekoBot.Db.Models.XpSettings", null)
b.HasOne("NadekoBot.Db.Models.XpSettings", "XpSettings")
.WithMany("ExclusionList")
.HasForeignKey("XpSettingsId");
.HasForeignKey("XpSettingsId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("XpSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.FeedSub", b =>
@@ -2536,42 +2505,48 @@ namespace NadekoBot.Migrations
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.FilterLinksChannelId", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterLinksChannelIds")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.FilterWordsChannelId", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.FilteredWord", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.GCChannelId", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
.WithMany("GenerateCurrencyChannelIds")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("GuildConfig");
});
@@ -2607,27 +2582,20 @@ namespace NadekoBot.Migrations
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId");
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Db.Models.MutedUserId", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("MutedUsers")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.Permissionv2", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("Permissions")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.PlaylistSong", b =>
@@ -2642,35 +2610,43 @@ namespace NadekoBot.Migrations
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("ShopEntries")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntryItem", b =>
{
b.HasOne("NadekoBot.Db.Models.ShopEntry", null)
.WithMany("Items")
.HasForeignKey("ShopEntryId");
.HasForeignKey("ShopEntryId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.SlowmodeIgnoredRole", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredRoles")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.SlowmodeIgnoredUser", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("SlowmodeIgnoredUsers")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleBlacklistedUser", b =>
{
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", null)
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
.WithMany("Blacklist")
.HasForeignKey("StreamRoleSettingsId");
.HasForeignKey("StreamRoleSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleSettings", b =>
@@ -2686,9 +2662,13 @@ namespace NadekoBot.Migrations
modelBuilder.Entity("NadekoBot.Db.Models.StreamRoleWhitelistedUser", b =>
{
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", null)
b.HasOne("NadekoBot.Db.Models.StreamRoleSettings", "StreamRoleSettings")
.WithMany("Whitelist")
.HasForeignKey("StreamRoleSettingsId");
.HasForeignKey("StreamRoleSettingsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("StreamRoleSettings");
});
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
@@ -2703,28 +2683,32 @@ namespace NadekoBot.Migrations
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnbanTimer")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.UnmuteTimer", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnmuteTimers")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.UnroleTimer", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("UnroleTimer")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.VcRoleInfo", b =>
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("VcRoleInfos")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.WaifuInfo", b =>
@@ -2786,7 +2770,8 @@ namespace NadekoBot.Migrations
{
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
.WithMany("WarnPunishments")
.HasForeignKey("GuildConfigId");
.HasForeignKey("GuildConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Db.Models.XpCurrencyReward", b =>

View File

@@ -1,5 +1,6 @@
#nullable disable
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Modules.Administration._common.results;
using NadekoBot.Modules.Administration.Services;
namespace NadekoBot.Modules.Administration;
@@ -405,4 +406,94 @@ public partial class Administration : NadekoModule<AdministrationService>
await Response().Confirm(strs.autopublish_disable).SendAsync();
}
}
[Cmd]
[UserPerm(GuildPerm.ManageNicknames)]
[BotPerm(GuildPerm.ChangeNickname)]
[Priority(0)]
public async Task SetNick([Leftover] string newNick = null)
{
if (string.IsNullOrWhiteSpace(newNick))
return;
var curUser = await ctx.Guild.GetCurrentUserAsync();
await curUser.ModifyAsync(u => u.Nickname = newNick);
await Response().Confirm(strs.bot_nick(Format.Bold(newNick) ?? "-")).SendAsync();
}
[Cmd]
[BotPerm(GuildPerm.ManageNicknames)]
[UserPerm(GuildPerm.ManageNicknames)]
[Priority(1)]
public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null)
{
var sg = (SocketGuild)ctx.Guild;
if (sg.OwnerId == gu.Id
|| gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position))
{
await Response().Error(strs.insuf_perms_i).SendAsync();
return;
}
await gu.ModifyAsync(u => u.Nickname = newNick);
await Response()
.Confirm(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"))
.SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPermission.ManageGuild)]
[BotPerm(GuildPermission.ManageGuild)]
public async Task SetServerBanner([Leftover] string img = null)
{
// Tier2 or higher is required to set a banner.
if (ctx.Guild.PremiumTier is PremiumTier.Tier1 or PremiumTier.None) return;
var result = await _service.SetServerBannerAsync(ctx.Guild, img);
switch (result)
{
case SetServerBannerResult.Success:
await Response().Confirm(strs.set_srvr_banner).SendAsync();
break;
case SetServerBannerResult.InvalidFileType:
await Response().Error(strs.srvr_banner_invalid).SendAsync();
break;
case SetServerBannerResult.Toolarge:
await Response().Error(strs.srvr_banner_too_large).SendAsync();
break;
case SetServerBannerResult.InvalidURL:
await Response().Error(strs.srvr_banner_invalid_url).SendAsync();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPermission.ManageGuild)]
[BotPerm(GuildPermission.ManageGuild)]
public async Task SetServerIcon([Leftover] string img = null)
{
var result = await _service.SetServerIconAsync(ctx.Guild, img);
switch (result)
{
case SetServerIconResult.Success:
await Response().Confirm(strs.set_srvr_icon).SendAsync();
break;
case SetServerIconResult.InvalidFileType:
await Response().Error(strs.srvr_banner_invalid).SendAsync();
break;
case SetServerIconResult.InvalidURL:
await Response().Error(strs.srvr_banner_invalid_url).SendAsync();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}

View File

@@ -2,6 +2,7 @@
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Administration._common.results;
namespace NadekoBot.Modules.Administration.Services;
@@ -13,17 +14,20 @@ public class AdministrationService : INService
private readonly DbService _db;
private readonly IReplacementService _repSvc;
private readonly ILogCommandService _logService;
private readonly IHttpClientFactory _httpFactory;
public AdministrationService(
IBot bot,
CommandHandler cmdHandler,
DbService db,
IReplacementService repSvc,
ILogCommandService logService)
ILogCommandService logService,
IHttpClientFactory factory)
{
_db = db;
_repSvc = repSvc;
_logService = logService;
_httpFactory = factory;
DeleteMessagesOnCommand = new(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
@@ -158,4 +162,45 @@ public class AdministrationService : INService
await umsg.EditAsync(text);
}
public async Task<SetServerBannerResult> SetServerBannerAsync(IGuild guild, string img)
{
if (!IsValidUri(img)) return SetServerBannerResult.InvalidURL;
var uri = new Uri(img);
using var http = _httpFactory.CreateClient();
using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
if (!sr.IsImage()) return SetServerBannerResult.InvalidFileType;
if (sr.GetContentLength() > 8.Megabytes())
{
return SetServerBannerResult.Toolarge;
}
await using var imageStream = await sr.Content.ReadAsStreamAsync();
await guild.ModifyAsync(x => x.Banner = new Image(imageStream));
return SetServerBannerResult.Success;
}
public async Task<SetServerIconResult> SetServerIconAsync(IGuild guild, string img)
{
if (!IsValidUri(img)) return SetServerIconResult.InvalidURL;
var uri = new Uri(img);
using var http = _httpFactory.CreateClient();
using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
if (!sr.IsImage()) return SetServerIconResult.InvalidFileType;
await using var imageStream = await sr.Content.ReadAsStreamAsync();
await guild.ModifyAsync(x => x.Icon = new Image(imageStream));
return SetServerIconResult.Success;
}
private bool IsValidUri(string img) => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute);
}

View File

@@ -0,0 +1,31 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public partial class Administration
{
[Group]
public class CleanupCommands : CleanupModuleBase
{
private readonly ICleanupService _svc;
public CleanupCommands(ICleanupService svc)
=> _svc = svc;
[Cmd]
[OwnerOnly]
[RequireContext(ContextType.DM)]
public async Task CleanupGuildData()
{
var result = await _svc.DeleteMissingGuildDataAsync();
if (result is null)
{
await ctx.ErrorAsync();
return;
}
await Response()
.Confirm($"{result.GuildCount} guilds' data remain in the database.")
.SendAsync();
}
}
}

View File

@@ -0,0 +1,155 @@
using LinqToDB;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
{
private readonly IPubSub _pubSub;
private TypedKey<KeepReport> _keepReportKey = new("cleanup:report");
private TypedKey<bool> _keepTriggerKey = new("cleanup:trigger");
private readonly DiscordSocketClient _client;
private ConcurrentDictionary<int, ulong[]> guildIds = new();
private readonly IBotCredsProvider _creds;
private readonly DbService _db;
public CleanupService(
IPubSub pubSub,
DiscordSocketClient client,
IBotCredsProvider creds,
DbService db)
{
_pubSub = pubSub;
_client = client;
_creds = creds;
_db = db;
}
public async Task<KeepResult?> DeleteMissingGuildDataAsync()
{
guildIds = new();
var totalShards = _creds.GetCreds().TotalShards;
await _pubSub.Pub(_keepTriggerKey, true);
var counter = 0;
while (guildIds.Keys.Count < totalShards)
{
await Task.Delay(1000);
counter++;
if (counter >= 5)
break;
}
if (guildIds.Keys.Count < totalShards)
return default;
var allIds = guildIds.SelectMany(x => x.Value)
.ToArray();
await using var ctx = _db.GetDbContext();
await using var linqCtx = ctx.CreateLinqToDBContext();
await using var tempTable = linqCtx.CreateTempTable<CleanupId>();
foreach (var chunk in allIds.Chunk(20000))
{
await tempTable.BulkCopyAsync(chunk.Select(x => new CleanupId()
{
GuildId = x
}));
}
// delete guild configs
await ctx.GetTable<GuildConfig>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete guild xp
await ctx.GetTable<UserXpStats>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete expressions
await ctx.GetTable<NadekoExpression>()
.Where(x => x.GuildId != null && !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId.Value))
.DeleteAsync();
// delete quotes
await ctx.GetTable<Quote>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete planted currencies
await ctx.GetTable<PlantedCurrency>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete image only channels
await ctx.GetTable<ImageOnlyChannel>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete reaction roles
await ctx.GetTable<ReactionRoleV2>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete ignored users
await ctx.GetTable<DiscordPermOverride>()
.Where(x => x.GuildId != null && !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId.Value))
.DeleteAsync();
// delete perm overrides
await ctx.GetTable<DiscordPermOverride>()
.Where(x => x.GuildId != null && !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId.Value))
.DeleteAsync();
// delete repeaters
await ctx.GetTable<Repeater>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
return new()
{
GuildCount = guildIds.Keys.Count,
};
}
private ValueTask OnKeepReport(KeepReport report)
{
guildIds[report.ShardId] = report.GuildIds;
return default;
}
public async Task OnReadyAsync()
{
await _pubSub.Sub(_keepTriggerKey, OnKeepTrigger);
if (_client.ShardId == 0)
await _pubSub.Sub(_keepReportKey, OnKeepReport);
}
private ValueTask OnKeepTrigger(bool arg)
{
_pubSub.Pub(_keepReportKey,
new KeepReport()
{
ShardId = _client.ShardId,
GuildIds = _client.GetGuildIds(),
});
return default;
}
}

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class CleanupId
{
[Key]
public ulong GuildId { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public interface ICleanupService
{
Task<KeepResult?> DeleteMissingGuildDataAsync();
}

View File

@@ -0,0 +1,7 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class KeepReport
{
public required int ShardId { get; init; }
public required ulong[] GuildIds { get; init; }
}

View File

@@ -0,0 +1,6 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
public sealed class KeepResult
{
public required int GuildCount { get; init; }
}

View File

@@ -225,5 +225,19 @@ public partial class Administration
if (!enabled)
await Response().Pending(strs.greetdmmsg_enable($"`{prefix}greetdm`")).SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
[Ratelimit(5)]
public async Task BoostTest([Leftover] IGuildUser? user = null)
{
user ??= (IGuildUser)ctx.User;
await _service.BoostTest((ITextChannel)ctx.Channel, user);
var enabled = _service.GetBoostEnabled(ctx.Guild.Id);
if (!enabled)
await Response().Pending(strs.boostmsg_enable($"`{prefix}boost`")).SendAsync();
}
}
}

View File

@@ -242,7 +242,7 @@ public class GreetService : INService, IReadyExecutor
guild: channel.Guild,
channel: channel,
users: users.ToArray());
var text = SmartText.CreateFrom(conf.ChannelGreetMessageText);
text = await _repSvc.ReplaceAsync(text, repCtx);
try
@@ -630,6 +630,13 @@ public class GreetService : INService, IReadyExecutor
return conf.SendChannelByeMessage;
}
public bool GetBoostEnabled(ulong guildId)
{
using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set);
return conf.SendBoostMessage;
}
#endregion
#region Test Messages

View File

@@ -0,0 +1,95 @@
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
using System.Threading.Channels;
namespace NadekoBot.Modules.Administration.Honeypot;
public sealed class HoneyPotService : IHoneyPotService, IReadyExecutor, IExecNoCommand, INService
{
private readonly DbService _db;
private readonly CommandHandler _handler;
private ConcurrentHashSet<ulong> _channels = new();
private Channel<SocketGuildUser> _punishments = Channel.CreateBounded<SocketGuildUser>(
new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = false,
});
public HoneyPotService(DbService db, CommandHandler handler)
{
_db = db;
_handler = handler;
}
public async Task<bool> ToggleHoneypotChannel(ulong guildId, ulong channelId)
{
await using var uow = _db.GetDbContext();
var deleted = await uow.HoneyPotChannels
.Where(x => x.GuildId == guildId)
.DeleteWithOutputAsync();
if (deleted.Length > 0)
{
_channels.TryRemove(deleted[0].ChannelId);
return false;
}
await uow.HoneyPotChannels
.ToLinqToDBTable()
.InsertAsync(() => new HoneypotChannel
{
GuildId = guildId,
ChannelId = channelId
});
_channels.Add(channelId);
return true;
}
public async Task OnReadyAsync()
{
await using var uow = _db.GetDbContext();
var channels = await uow.HoneyPotChannels
.Select(x => x.ChannelId)
.ToListAsyncLinqToDB();
_channels = new(channels);
while (await _punishments.Reader.WaitToReadAsync())
{
while (_punishments.Reader.TryRead(out var user))
{
try
{
Log.Information("Honeypot caught user {User} [{UserId}]", user, user.Id);
await user.BanAsync(pruneDays: 1);
await user.Guild.RemoveBanAsync(user.Id);
}
catch (Exception e)
{
Log.Warning(e, "Failed banning {User} due to {Error}", user, e.Message);
}
await Task.Delay(1000);
}
}
}
public async Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg)
{
if (_channels.Contains(msg.Channel.Id) && msg.Author is SocketGuildUser sgu)
{
if (!sgu.GuildPermissions.BanMembers)
await _punishments.Writer.WriteAsync(sgu);
}
}
}

View File

@@ -0,0 +1,29 @@
using NadekoBot.Modules.Administration.Honeypot;
namespace NadekoBot.Modules.Administration;
public partial class Administration
{
[Group]
public partial class HoneypotCommands : NadekoModule
{
private readonly IHoneyPotService _service;
public HoneypotCommands(IHoneyPotService service)
=> _service = service;
[Cmd]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.Administrator)]
[RequireBotPermission(GuildPermission.BanMembers)]
public async Task Honeypot()
{
var enabled = await _service.ToggleHoneypotChannel(ctx.Guild.Id, ctx.Channel.Id);
if (enabled)
await Response().Confirm(strs.honeypot_on).SendAsync();
else
await Response().Confirm(strs.honeypot_off).SendAsync();
}
}
}

View File

@@ -0,0 +1,6 @@
namespace NadekoBot.Modules.Administration.Honeypot;
public interface IHoneyPotService
{
public Task<bool> ToggleHoneypotChannel(ulong guildId, ulong channelId);
}

View File

@@ -61,7 +61,13 @@ public partial class Administration
}
else
{
ci = new(name);
ci = new CultureInfo(name);
if (!_supportedLocales.ContainsKey(ci.Name))
{
await LanguagesList();
return;
}
_localization.SetGuildCulture(ctx.Guild, ci);
}
@@ -97,7 +103,12 @@ public partial class Administration
}
else
{
ci = new(name);
ci = new CultureInfo(name);
if (!_supportedLocales.ContainsKey(ci.Name))
{
await LanguagesList();
return;
}
_localization.SetDefaultCulture(ci);
}

View File

@@ -45,23 +45,43 @@ public partial class Administration
var progressMsg = await Response().Pending(strs.prune_progress(0, 100)).SendAsync();
var progress = GetProgressTracker(progressMsg);
PruneResult result;
if (opts.Safe)
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
100,
x => x.Author.Id == user.Id && !x.IsPinned,
progress,
opts.After);
else
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
100,
x => x.Author.Id == user.Id,
progress,
opts.After);
ctx.Message.DeleteAfter(3);
await SendResult(result);
await progressMsg.DeleteAsync();
}
private async Task SendResult(PruneResult result)
{
switch (result)
{
case PruneResult.Success:
break;
case PruneResult.AlreadyRunning:
break;
case PruneResult.FeatureLimit:
await Response().Pending(strs.feature_limit_reached_owner).SendAsync();
break;
default:
throw new ArgumentOutOfRangeException(nameof(result), result, null);
}
}
// prune x
[Cmd]
[RequireContext(ContextType.Guild)]
@@ -83,19 +103,21 @@ public partial class Administration
var progressMsg = await Response().Pending(strs.prune_progress(0, count)).SendAsync();
var progress = GetProgressTracker(progressMsg);
PruneResult result;
if (opts.Safe)
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
x => !x.IsPinned && x.Id != progressMsg.Id,
progress,
opts.After);
else
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
x => x.Id != progressMsg.Id,
progress,
opts.After);
await SendResult(result);
await progressMsg.DeleteAsync();
}
@@ -155,9 +177,10 @@ public partial class Administration
var progressMsg = await Response().Pending(strs.prune_progress(0, count)).SendAsync();
var progress = GetProgressTracker(progressMsg);
PruneResult result;
if (opts.Safe)
{
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks && !m.IsPinned,
progress,
@@ -166,7 +189,7 @@ public partial class Administration
}
else
{
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks,
progress,
@@ -174,6 +197,7 @@ public partial class Administration
);
}
await SendResult(result);
await progressMsg.DeleteAsync();
}

View File

@@ -0,0 +1,9 @@
#nullable disable
namespace NadekoBot.Modules.Administration.Services;
public enum PruneResult
{
Success,
AlreadyRunning,
FeatureLimit,
}

View File

@@ -1,4 +1,6 @@
#nullable disable
using NadekoBot.Modules.Patronage;
namespace NadekoBot.Modules.Administration.Services;
public class PruneService : INService
@@ -7,11 +9,15 @@ public class PruneService : INService
private readonly ConcurrentDictionary<ulong, CancellationTokenSource> _pruningGuilds = new();
private readonly TimeSpan _twoWeeks = TimeSpan.FromDays(14);
private readonly ILogCommandService _logService;
private readonly IPatronageService _ps;
public PruneService(ILogCommandService logService)
=> _logService = logService;
public PruneService(ILogCommandService logService, IPatronageService ps)
{
_logService = logService;
_ps = ps;
}
public async Task PruneWhere(
public async Task<PruneResult> PruneWhere(
ITextChannel channel,
int amount,
Func<IMessage, bool> predicate,
@@ -26,8 +32,13 @@ public class PruneService : INService
using var cancelSource = new CancellationTokenSource();
if (!_pruningGuilds.TryAdd(channel.GuildId, cancelSource))
return;
return PruneResult.AlreadyRunning;
if (!await _ps.LimitHitAsync(LimitedFeatureName.Prune, channel.Guild.OwnerId))
{
return PruneResult.FeatureLimit;
}
try
{
var now = DateTime.UtcNow;
@@ -47,7 +58,7 @@ public class PruneService : INService
.ToArray();
if (!msgs.Any())
return;
return PruneResult.Success;
lastMessage = msgs[^1];
@@ -88,6 +99,8 @@ public class PruneService : INService
{
_pruningGuilds.TryRemove(channel.GuildId, out _);
}
return PruneResult.Success;
}
public async Task<bool> CancelAsync(ulong guildId)

View File

@@ -18,7 +18,7 @@ public interface IReactionRoleService
/// <param name="group"></param>
/// <param name="levelReq"></param>
/// <returns>The result of the operation</returns>
Task<OneOf<Success, FeatureLimit>> AddReactionRole(
Task<OneOf<Success, Error>> AddReactionRole(
IGuild guild,
IMessage msg,
string emote,

View File

@@ -55,12 +55,10 @@ public partial class Administration
await res.Match(
_ => ctx.OkAsync(),
fl =>
async fl =>
{
_ = msg.RemoveReactionAsync(emote, ctx.Client.CurrentUser);
return !fl.IsPatronLimit
? Response().Error(strs.limit_reached(fl.Quota)).SendAsync()
: Response().Pending(strs.feature_limit_reached_owner(fl.Quota, fl.Name)).SendAsync();
await Response().Pending(strs.feature_limit_reached_owner).SendAsync();
});
}

View File

@@ -21,22 +21,16 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
private readonly SemaphoreSlim _assignementLock = new(1, 1);
private readonly IPatronageService _ps;
private static readonly FeatureLimitKey _reroFLKey = new()
{
Key = "rero:max_count",
PrettyName = "Reaction Role"
};
public ReactionRolesService(
DiscordSocketClient client,
IPatronageService ps,
DbService db,
IBotCredentials creds,
IPatronageService ps)
IBotCredentials creds)
{
_db = db;
_ps = ps;
_client = client;
_creds = creds;
_ps = ps;
_cache = new();
}
@@ -242,7 +236,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
/// <param name="group"></param>
/// <param name="levelReq"></param>
/// <returns>The result of the operation</returns>
public async Task<OneOf<Success, FeatureLimit>> AddReactionRole(
public async Task<OneOf<Success, Error>> AddReactionRole(
IGuild guild,
IMessage msg,
string emote,
@@ -261,9 +255,12 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
.Where(x => x.GuildId == guild.Id)
.CountAsync();
var result = await _ps.TryGetFeatureLimitAsync(_reroFLKey, guild.OwnerId, 50);
if (result.Quota != -1 && activeReactionRoles >= result.Quota)
return result;
var limit = await _ps.GetUserLimit(LimitedFeatureName.ReactionRole, guild.OwnerId);
if (!_creds.IsOwner(guild.OwnerId) && (activeReactionRoles >= limit.Quota && limit.Quota >= 0))
{
return new Error();
}
await ctx.GetTable<ReactionRoleV2>()
.InsertOrUpdateAsync(() => new()

View File

@@ -459,42 +459,7 @@ public partial class Administration
await Response().Confirm(strs.bot_name(Format.Bold(newName))).SendAsync();
}
[Cmd]
[UserPerm(GuildPerm.ManageNicknames)]
[BotPerm(GuildPerm.ChangeNickname)]
[Priority(0)]
public async Task SetNick([Leftover] string newNick = null)
{
if (string.IsNullOrWhiteSpace(newNick))
return;
var curUser = await ctx.Guild.GetCurrentUserAsync();
await curUser.ModifyAsync(u => u.Nickname = newNick);
await Response().Confirm(strs.bot_nick(Format.Bold(newNick) ?? "-")).SendAsync();
}
[Cmd]
[BotPerm(GuildPerm.ManageNicknames)]
[UserPerm(GuildPerm.ManageNicknames)]
[Priority(1)]
public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null)
{
var sg = (SocketGuild)ctx.Guild;
if (sg.OwnerId == gu.Id
|| gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position))
{
await Response().Error(strs.insuf_perms_i).SendAsync();
return;
}
await gu.ModifyAsync(u => u.Nickname = newNick);
await Response()
.Confirm(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"))
.SendAsync();
}
[Cmd]
[OwnerOnly]
public async Task SetStatus([Leftover] SettableUserStatus status)

View File

@@ -0,0 +1,14 @@
// namespace NadekoBot.Modules.Administration;
//
// public partial class Administration
// {
// [Group]
// public partial class TicketCommands : NadekoModule
// {
// [Cmd]
// public async Task Ticket()
// {
//
// }
// }
// }

View File

@@ -0,0 +1,9 @@
namespace NadekoBot.Modules.Administration._common.results;
public enum SetServerBannerResult
{
Success,
InvalidFileType,
Toolarge,
InvalidURL
}

View File

@@ -0,0 +1,8 @@
namespace NadekoBot.Modules.Administration._common.results;
public enum SetServerIconResult
{
Success,
InvalidFileType,
InvalidURL
}

View File

@@ -1,5 +1,7 @@
#nullable disable
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.NadekoExpressions;
[Name("Expressions")]
@@ -34,12 +36,12 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.expr_new))
.WithDescription($"#{new kwum(ex.Id)}")
.AddField(GetText(strs.trigger), key)
.AddField(GetText(strs.response),
message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
.WithOkColor()
.WithTitle(GetText(strs.expr_new))
.WithDescription($"#{new kwum(ex.Id)}")
.AddField(GetText(strs.trigger), key)
.AddField(GetText(strs.response),
message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
.SendAsync();
}
@@ -68,9 +70,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
[Cmd]
public async Task ExprAdd(string key, [Leftover] string message)
public async Task ExprAdd(string trigger, [Leftover] string response)
{
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
if (string.IsNullOrWhiteSpace(response) || string.IsNullOrWhiteSpace(trigger))
{
return;
}
@@ -81,7 +83,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
return;
}
await ExprAddInternalAsync(key, message);
await ExprAddInternalAsync(trigger, response);
}
[Cmd]
@@ -93,8 +95,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
return;
}
if ((channel is null && !_creds.IsOwner(ctx.User))
|| (channel is not null && !((IGuildUser)ctx.User).GuildPermissions.Administrator))
if (!IsValidExprEditor())
{
await Response().Error(strs.expr_insuff_perms).SendAsync();
return;
@@ -105,12 +106,12 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
{
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.expr_edited))
.WithDescription($"#{id}")
.AddField(GetText(strs.trigger), ex.Trigger)
.AddField(GetText(strs.response),
message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
.WithOkColor()
.WithTitle(GetText(strs.expr_edited))
.WithDescription($"#{id}")
.AddField(GetText(strs.trigger), ex.Trigger)
.AddField(GetText(strs.response),
message.Length > 1024 ? GetText(strs.redacted_too_long) : message))
.SendAsync();
}
else
@@ -119,6 +120,10 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
}
}
private bool IsValidExprEditor()
=> (ctx.Guild is not null && ((IGuildUser)ctx.User).GuildPermissions.Administrator)
|| (ctx.Guild is null && _creds.IsOwner(ctx.User));
[Cmd]
[Priority(1)]
public async Task ExprList(int page = 1)
@@ -132,7 +137,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
.OrderBy(x => x.Trigger)
.ToArray();
if (allExpressions is null || !allExpressions.Any())
if (!allExpressions.Any())
{
await Response().Error(strs.expr_no_found).SendAsync();
return;
@@ -171,16 +176,48 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
return;
}
var inter = CreateEditInteraction(id, found);
await Response()
.Interaction(IsValidExprEditor() ? inter : null)
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithDescription($"#{id}")
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
.AddField(GetText(strs.response),
found.Response.TrimTo(1000).Replace("](", "]\\(")))
.WithOkColor()
.WithDescription($"#{id}")
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
.AddField(GetText(strs.response),
found.Response.TrimTo(1000).Replace("](", "]\\(")))
.SendAsync();
}
private NadekoInteractionBase CreateEditInteraction(kwum id, NadekoExpression found)
{
var modal = new ModalBuilder()
.WithCustomId("expr:edit_modal")
.WithTitle($"Edit expression {id}")
.AddTextInput(new TextInputBuilder()
.WithLabel(GetText(strs.response))
.WithValue(found.Response)
.WithMinLength(1)
.WithCustomId("expr:edit_modal:response")
.WithStyle(TextInputStyle.Paragraph));
var inter = _inter.Create(ctx.User.Id,
new ButtonBuilder()
.WithEmote(Emoji.Parse("📝"))
.WithLabel("Edit")
.WithStyle(ButtonStyle.Primary)
.WithCustomId("test"),
modal,
async (sm) =>
{
var msg = sm.Data.Components.FirstOrDefault()?.Value;
await ExprEdit(id, msg);
}
);
return inter;
}
public async Task ExprDeleteInternalAsync(kwum id)
{
var ex = await _service.DeleteAsync(ctx.Guild?.Id, id);
@@ -189,11 +226,11 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
{
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.expr_deleted))
.WithDescription($"#{id}")
.AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
.AddField(GetText(strs.response), ex.Response.TrimTo(1024)))
.WithOkColor()
.WithTitle(GetText(strs.expr_deleted))
.WithDescription($"#{id}")
.AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
.AddField(GetText(strs.response), ex.Response.TrimTo(1024)))
.SendAsync();
}
else
@@ -340,8 +377,8 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
public async Task ExprClear()
{
if (await PromptUserConfirmAsync(_sender.CreateEmbed()
.WithTitle("Expression clear")
.WithDescription("This will delete all expressions on this server.")))
.WithTitle("Expression clear")
.WithDescription("This will delete all expressions on this server.")))
{
var count = _service.DeleteAllExpressions(ctx.Guild.Id);
await Response().Confirm(strs.exprs_cleared(count)).SendAsync();

View File

@@ -6,6 +6,7 @@ using NadekoBot.Db;
using NadekoBot.Db.Models;
using System.Runtime.CompilerServices;
using LinqToDB.EntityFrameworkCore;
using NadekoBot.Modules.Permissions.Services;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@@ -77,6 +78,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
private bool ready;
private ConcurrentHashSet<ulong> _disabledGlobalExpressionGuilds;
private readonly PermissionService _pc;
public NadekoExpressionsService(
DbService db,
@@ -87,7 +89,8 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
IPubSub pubSub,
IMessageSenderService sender,
IReplacementService repSvc,
IPermissionChecker permChecker)
IPermissionChecker permChecker,
PermissionService pc)
{
_db = db;
_client = client;
@@ -98,6 +101,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
_sender = sender;
_repSvc = repSvc;
_permChecker = permChecker;
_pc = pc;
_rng = new NadekoRandom();
_pubSub.Sub(_exprsReloadedKey, OnExprsShouldReload);
@@ -138,6 +142,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
var globalItems = uow.Set<NadekoExpression>()
.AsNoTracking()
.Where(x => x.GuildId == null || x.GuildId == 0)
.Where(x => x.Trigger != null)
.AsEnumerable()
.Select(x =>
{
@@ -254,26 +259,30 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
"ACTUALEXPRESSIONS",
expr.Trigger
);
if (!result.IsAllowed)
{
if (result.TryPickT3(out var disallowed, out _))
var cache = _pc.GetCacheFor(guild.Id);
if (cache.Verbose)
{
var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
Format.Bold(disallowed.PermText)),
sg.Id);
try
if (result.TryPickT3(out var disallowed, out _))
{
await _sender.Response(msg.Channel)
.Error(permissionMessage)
.SendAsync();
}
catch
{
}
var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
Format.Bold(disallowed.PermText)),
sg.Id);
Log.Information("{PermissionMessage}", permissionMessage);
try
{
await _sender.Response(msg.Channel)
.Error(permissionMessage)
.SendAsync();
}
catch
{
}
Log.Information("{PermissionMessage}", permissionMessage);
}
}
return true;

View File

@@ -73,6 +73,27 @@ public partial class Gambling
await Response().Error(strs.cant_dm).SendAsync();
}
}
[Cmd]
[OwnerOnly]
public async Task BankBalance([Leftover] IUser user)
{
var bal = await _bank.GetBalanceAsync(user.Id);
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));
try
{
await Response().User(ctx.User).Embed(eb).SendAsync();
await ctx.OkAsync();
}
catch
{
await Response().Error(strs.cant_dm).SendAsync();
}
}
private async Task BankTakeInternalAsync(long amount, ulong userId)
{

View File

@@ -3,6 +3,7 @@ using NadekoBot.Common.TypeReaders;
using NadekoBot.Modules.Gambling.Common;
using NadekoBot.Modules.Gambling.Common.Blackjack;
using NadekoBot.Modules.Gambling.Services;
using NadekoBot.Modules.Utility;
namespace NadekoBot.Modules.Gambling;

View File

@@ -17,7 +17,8 @@ public partial class Gambling
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
private readonly IImageCache _images;
public DrawCommands(IImageCache images, GamblingConfigService gcs) : base(gcs)
public DrawCommands(IImageCache images, GamblingConfigService gcs)
: base(gcs)
=> _images = images;
private async Task InternalDraw(int count, ulong? guildId = null)
@@ -56,8 +57,8 @@ public partial class Gambling
i.Dispose();
var eb = _sender.CreateEmbed()
.WithOkColor();
.WithOkColor();
var toSend = string.Empty;
if (cardObjects.Count == 5)
eb.AddField(GetText(strs.hand_value), Deck.GetHandValue(cardObjects), true);
@@ -71,7 +72,7 @@ public partial class Gambling
if (count > 1)
eb.AddField(GetText(strs.cards), count.ToString(), true);
await using var imageStream = await img.ToStreamAsync();
await ctx.Channel.SendFileAsync(imageStream,
imgName,
@@ -84,7 +85,7 @@ public partial class Gambling
var cardBytes = await File.ReadAllBytesAsync($"data/images/cards/{cardName}.jpg");
return Image.Load<Rgba32>(cardBytes);
}
private async Task<Image<Rgba32>> GetCardImageAsync(Deck.Card currentCard)
{
var cardName = currentCard.ToString().ToLowerInvariant().Replace(' ', '_');
@@ -98,7 +99,7 @@ public partial class Gambling
{
if (num < 1)
return;
if (num > 10)
num = 10;
@@ -110,7 +111,7 @@ public partial class Gambling
{
if (num < 1)
return;
if (num > 10)
num = 10;
@@ -136,19 +137,29 @@ public partial class Gambling
[Cmd]
[RequireContext(ContextType.Guild)]
public Task BetDraw([OverrideTypeReader(typeof(BalanceTypeReader))] long amount, InputValueGuess val, InputColorGuess? col = null)
public Task BetDraw(
[OverrideTypeReader(typeof(BalanceTypeReader))]
long amount,
InputValueGuess val,
InputColorGuess? col = null)
=> BetDrawInternal(amount, val, col);
[Cmd]
[RequireContext(ContextType.Guild)]
public Task BetDraw([OverrideTypeReader(typeof(BalanceTypeReader))] long amount, InputColorGuess col, InputValueGuess? val = null)
public Task BetDraw(
[OverrideTypeReader(typeof(BalanceTypeReader))]
long amount,
InputColorGuess col,
InputValueGuess? val = null)
=> BetDrawInternal(amount, val, col);
public async Task BetDrawInternal(long amount, InputValueGuess? val, InputColorGuess? col)
{
if (amount <= 0)
if (!await CheckBetMandatory(amount))
{
return;
}
var res = await _service.BetDrawAsync(ctx.User.Id,
amount,
(byte?)val,
@@ -161,13 +172,13 @@ public partial class Gambling
}
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithAuthor(ctx.User)
.WithDescription(result.Card.GetEmoji())
.AddField(GetText(strs.guess), GetGuessInfo(val, col), true)
.AddField(GetText(strs.card), GetCardInfo(result.Card), true)
.AddField(GetText(strs.won), N((long)result.Won), false)
.WithImageUrl("attachment://card.png");
.WithOkColor()
.WithAuthor(ctx.User)
.WithDescription(result.Card.GetEmoji())
.AddField(GetText(strs.guess), GetGuessInfo(val, col), true)
.AddField(GetText(strs.card), GetCardInfo(result.Card), true)
.AddField(GetText(strs.won), N((long)result.Won), false)
.WithImageUrl("attachment://card.png");
using var img = await GetCardImageAsync(result.Card);
await using var imgStream = await img.ToStreamAsync();
@@ -189,9 +200,10 @@ public partial class Gambling
InputColorGuess.Black => "B ⚫",
_ => "❓"
};
return $"{val} / {col}";
}
private string GetCardInfo(RegularCard card)
{
var val = (int)card.Value switch
@@ -208,7 +220,7 @@ public partial class Gambling
RegularSuit.Diamonds or RegularSuit.Hearts => "R 🔴",
_ => "B ⚫"
};
return $"{val} / {col}";
}

View File

@@ -1,7 +1,6 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Gambling.Bank;
using NadekoBot.Modules.Gambling.Common;
@@ -14,6 +13,7 @@ using System.Text;
using NadekoBot.Modules.Gambling.Rps;
using NadekoBot.Common.TypeReaders;
using NadekoBot.Modules.Patronage;
using NadekoBot.Modules.Utility;
namespace NadekoBot.Modules.Gambling;
@@ -27,9 +27,9 @@ public partial class Gambling : GamblingModule<GamblingService>
private readonly DownloadTracker _tracker;
private readonly GamblingConfigService _configService;
private readonly IBankService _bank;
private readonly IPatronageService _ps;
private readonly IRemindService _remind;
private readonly GamblingTxTracker _gamblingTxTracker;
private readonly IPatronageService _ps;
private IUserMessage rdMsg;
@@ -41,8 +41,8 @@ public partial class Gambling : GamblingModule<GamblingService>
DownloadTracker tracker,
GamblingConfigService configService,
IBankService bank,
IPatronageService ps,
IRemindService remind,
IPatronageService patronage,
GamblingTxTracker gamblingTxTracker)
: base(configService)
{
@@ -51,9 +51,9 @@ public partial class Gambling : GamblingModule<GamblingService>
_cs = currency;
_client = client;
_bank = bank;
_ps = ps;
_remind = remind;
_gamblingTxTracker = gamblingTxTracker;
_ps = patronage;
_enUsCulture = new CultureInfo("en-US", false).NumberFormat;
_enUsCulture.NumberDecimalDigits = 0;
@@ -74,7 +74,7 @@ public partial class Gambling : GamblingModule<GamblingService>
var stats = await _gamblingTxTracker.GetAllAsync();
var eb = _sender.CreateEmbed()
.WithOkColor();
.WithOkColor();
var str = "` Feature `` Bet ``Paid Out`` RoI `\n";
str += "――――――――――――――――――――\n";
@@ -119,26 +119,20 @@ public partial class Gambling : GamblingModule<GamblingService>
// [21:03] Bob Page: Kinda remids me of US economy
var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.economy_state))
.AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
.AddField(GetText(strs.currency_planted), N(ec.Planted))
.AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
.AddField(GetText(strs.bot_currency), N(ec.Bot))
.AddField(GetText(strs.bank_accounts), N(ec.Bank))
.AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
.WithOkColor();
.WithTitle(GetText(strs.economy_state))
.AddField(GetText(strs.currency_owned), N(ec.Cash - ec.Bot))
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
.AddField(GetText(strs.currency_planted), N(ec.Planted))
.AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
.AddField(GetText(strs.bot_currency), N(ec.Bot))
.AddField(GetText(strs.bank_accounts), N(ec.Bank))
.AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank))
.WithOkColor();
// ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table
await Response().Embed(embed).SendAsync();
}
private static readonly FeatureLimitKey _timelyKey = new FeatureLimitKey()
{
Key = "timely:extra_percent",
PrettyName = "Timely"
};
private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
{
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative);
@@ -154,18 +148,27 @@ public partial class Gambling : GamblingModule<GamblingService>
await smc.RespondConfirmAsync(_sender, GetText(strs.remind_timely(tt)), ephemeral: true);
}
private NadekoInteraction CreateRemindMeInteraction(int period)
{
return _inter
// Creates timely reminder button, parameter in hours.
private NadekoInteractionBase CreateRemindMeInteraction(int period)
=> _inter
.Create(ctx.User.Id,
new SimpleInteraction<DateTime>(
new ButtonBuilder(
label: "Remind me",
emote: Emoji.Parse("⏰"),
customId: "timely:remind_me"),
RemindTimelyAction,
DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
}
new ButtonBuilder(
label: "Remind me",
emote: Emoji.Parse("⏰"),
customId: "timely:remind_me"),
(smc) => RemindTimelyAction(smc, DateTime.UtcNow.Add(TimeSpan.FromHours(period)))
);
// Creates timely reminder button, parameter in milliseconds.
private NadekoInteractionBase CreateRemindMeInteraction(double ms)
=> _inter
.Create(ctx.User.Id,
new ButtonBuilder(
label: "Remind me",
emote: Emoji.Parse("⏰"),
customId: "timely:remind_me"),
(smc) => RemindTimelyAction(smc, DateTime.UtcNow.Add(TimeSpan.FromMilliseconds(ms)))
);
[Cmd]
public async Task Timely()
@@ -178,25 +181,31 @@ public partial class Gambling : GamblingModule<GamblingService>
return;
}
var inter = CreateRemindMeInteraction(period);
if (await _service.ClaimTimelyAsync(ctx.User.Id, period) is { } rem)
if (await _service.ClaimTimelyAsync(ctx.User.Id, period) is { } remainder)
{
// Get correct time form remainder
var interaction = CreateRemindMeInteraction(remainder.TotalMilliseconds);
// Removes timely button if there is a timely reminder in DB
if (_service.UserHasTimelyReminder(ctx.User.Id))
{
inter = null;
interaction = null;
}
var now = DateTime.UtcNow;
var relativeTag = TimestampTag.FromDateTime(now.Add(rem), TimestampTagStyles.Relative);
await Response().Pending(strs.timely_already_claimed(relativeTag)).Interaction(inter).SendAsync();
var relativeTag = TimestampTag.FromDateTime(now.Add(remainder), TimestampTagStyles.Relative);
await Response().Pending(strs.timely_already_claimed(relativeTag)).Interaction(interaction).SendAsync();
return;
}
var result = await _ps.TryGetFeatureLimitAsync(_timelyKey, ctx.User.Id, 0);
var patron = await _ps.GetPatronAsync(ctx.User.Id);
val = (int)(val * (1 + (result.Quota! * 0.01f)));
var percentBonus = (_ps.PercentBonus(patron) / 100f);
val += (int)(val * percentBonus);
var inter = CreateRemindMeInteraction(period);
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
@@ -311,9 +320,9 @@ public partial class Gambling : GamblingModule<GamblingService>
}
var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
?? $"{userId}")))
.WithOkColor();
.WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
?? $"{userId}")))
.WithOkColor();
var sb = new StringBuilder();
foreach (var tr in trs)
@@ -408,7 +417,7 @@ public partial class Gambling : GamblingModule<GamblingService>
await Response().Confirm(strs.has(Format.Code(userId.ToString()), cur)).SendAsync();
}
private async Task BankAction(SocketMessageComponent smc, object _)
private async Task BankAction(SocketMessageComponent smc)
{
var balance = await _bank.GetBalanceAsync(ctx.User.Id);
@@ -418,12 +427,12 @@ public partial class Gambling : GamblingModule<GamblingService>
.Pipe(text => smc.RespondConfirmAsync(_sender, text, ephemeral: true));
}
private NadekoInteraction CreateCashInteraction()
=> _inter.Create<object>(ctx.User.Id,
new(new(
customId: "cash:bank_show_balance",
emote: new Emoji("🏦")),
BankAction));
private NadekoInteractionBase CreateCashInteraction()
=> _inter.Create(ctx.User.Id,
new ButtonBuilder(
customId: "cash:bank_show_balance",
emote: new Emoji("🏦")),
BankAction);
[Cmd]
[Priority(1)]
@@ -732,10 +741,10 @@ public partial class Gambling : GamblingModule<GamblingService>
}
var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User)
.WithDescription(Format.Bold(str))
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
.WithOkColor();
.WithAuthor(ctx.User)
.WithDescription(Format.Bold(str))
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
.WithOkColor();
await Response().Embed(eb).SendAsync();
}
@@ -766,7 +775,7 @@ public partial class Gambling : GamblingModule<GamblingService>
}
async Task<IEnumerable<DiscordUser>> GetTopRichest(int curPage)
async Task<IReadOnlyCollection<DiscordUser>> GetTopRichest(int curPage)
{
if (opts.Clean)
{
@@ -787,7 +796,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return await uow.Set<DiscordUser>().GetTopRichest(_client.CurrentUser.Id, curPage);
}
}
var res = Response()
.Paginated();
@@ -799,8 +808,9 @@ public partial class Gambling : GamblingModule<GamblingService>
.CurrentPage(page)
.Page((toSend, curPage) =>
{
var embed = _sender.CreateEmbed().WithOkColor()
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
if (!toSend.Any())
{
@@ -894,6 +904,7 @@ public partial class Gambling : GamblingModule<GamblingService>
private static readonly ImmutableArray<string> _emojis =
new[] { "⬆", "↖", "⬅", "↙", "⬇", "↘", "➡", "↗" }.ToImmutableArray();
[Cmd]
public async Task LuckyLadder([OverrideTypeReader(typeof(BalanceTypeReader))] long amount)
{
@@ -923,11 +934,11 @@ public partial class Gambling : GamblingModule<GamblingService>
}
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithDescription(sb.ToString())
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
.AddField(GetText(strs.won), $"{(long)result.Won}", true)
.WithAuthor(ctx.User);
.WithOkColor()
.WithDescription(sb.ToString())
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
.AddField(GetText(strs.won), $"{(long)result.Won}", true)
.WithAuthor(ctx.User);
await Response().Embed(eb).SendAsync();

View File

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

View File

@@ -247,7 +247,14 @@ public partial class Gambling
}
else
{
var cmd = entry.Command.Replace("%you%", ctx.User.Id.ToString());
var buyer = (IGuildUser)ctx.User;
var cmd = entry.Command
.Replace("%you%", buyer.Mention)
.Replace("%you.mention%", buyer.Mention)
.Replace("%you.username%", buyer.Username)
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
.Replace("%you.nick%", buyer.DisplayName);
var eb = _sender.CreateEmbed()
.WithPendingColor()
.WithTitle("Executing shop command")
@@ -259,6 +266,7 @@ public partial class Gambling
GetProfitAmount(entry.Price),
new("shop", "sell", entry.Name));
await Task.Delay(250);
await _cmdHandler.TryRunCommand(guild,
channel,
new DoAsUserMessage(

View File

@@ -9,6 +9,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using NadekoBot.Modules.Gambling;
using NadekoBot.Common.TypeReaders;
using NadekoBot.Modules.Utility;
using Color = SixLabors.ImageSharp.Color;
using Image = SixLabors.ImageSharp.Image;
@@ -76,9 +77,12 @@ public partial class Gambling
.WithOkColor();
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
var si = new SimpleInteraction<long>(bb, (_, amount) => Slot(amount), amount);
var inter = _inter.Create(ctx.User.Id, bb, smc =>
{
smc.DeferAsync();
return Slot(amount);
});
var inter = _inter.Create(ctx.User.Id, si);
var msg = await ctx.Channel.SendFileAsync(imgStream,
"result.png",
embed: eb.Build(),

View File

@@ -3,6 +3,7 @@ using NadekoBot.Modules.Gambling.Common;
using NadekoBot.Modules.Gambling.Common.Waifu;
using NadekoBot.Modules.Gambling.Services;
using NadekoBot.Db.Models;
using TwitchLib.Api.Helix.Models.Teams;
namespace NadekoBot.Modules.Gambling;
@@ -21,8 +22,8 @@ public partial class Gambling
{
var price = _service.GetResetPrice(ctx.User);
var embed = _sender.CreateEmbed()
.WithTitle(GetText(strs.waifu_reset_confirm))
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
.WithTitle(GetText(strs.waifu_reset_confirm))
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
if (!await PromptUserConfirmAsync(embed))
return;
@@ -307,24 +308,26 @@ public partial class Gambling
fansStr = "-";
var embed = _sender.CreateEmbed()
.WithOkColor()
.WithTitle(GetText(strs.waifu)
+ " "
+ (wi.FullName ?? name ?? targetId.ToString())
+ " - \"the "
+ _service.GetClaimTitle(wi.ClaimCount)
+ "\"")
.AddField(GetText(strs.price), N(wi.Price), true)
.AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true)
.AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true)
.AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true)
.AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true)
.AddField("\u200B", "\u200B", true)
.AddField(GetText(strs.fans(fansList.Count)), fansStr, true)
.AddField($"Waifus ({wi.ClaimCount})",
wi.ClaimCount == 0 ? nobody : claimsStr,
true)
.AddField(GetText(strs.gifts), itemsStr, true);
.WithOkColor()
.WithTitle(GetText(strs.waifu)
+ " "
+ (wi.FullName ?? name ?? targetId.ToString())
+ " - \"the "
+ _service.GetClaimTitle(wi.ClaimCount)
+ "\"")
.AddField(GetText(strs.price), N(wi.Price), true)
.AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true)
.AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true)
.AddField(GetText(strs.changes_of_heart),
$"{wi.AffinityCount} - \"the {affInfo}\"",
true)
.AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true)
.AddField("\u200B", "\u200B", true)
.AddField(GetText(strs.fans(fansList.Count)), fansStr, true)
.AddField($"Waifus ({wi.ClaimCount})",
wi.ClaimCount == 0 ? nobody : claimsStr,
true)
.AddField(GetText(strs.gifts), itemsStr, true);
await Response().Embed(embed).SendAsync();
}
@@ -348,7 +351,7 @@ public partial class Gambling
.Page((items, _) =>
{
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
items
.ToList()
.ForEach(x => embed.AddField(
@@ -364,30 +367,27 @@ public partial class Gambling
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(0)]
public async Task WaifuGift(string itemName, [Leftover] IUser waifu)
public async Task WaifuGift(MultipleWaifuItems items, [Leftover] IUser waifu)
{
if (waifu.Id == ctx.User.Id)
return;
var allItems = _service.GetWaifuItems();
var item = allItems.FirstOrDefault(x => x.Name.ToLowerInvariant() == itemName.ToLowerInvariant());
if (item is null)
{
await Response().Error(strs.waifu_gift_not_exist).SendAsync();
return;
}
var sucess = await _service.GiftWaifuAsync(ctx.User, waifu, item);
var sucess = await _service.GiftWaifuAsync(ctx.User, waifu, items.Item, items.Count);
if (sucess)
{
await Response()
.Confirm(strs.waifu_gift(Format.Bold(item + " " + item.ItemEmoji),
.Confirm(strs.waifu_gift(Format.Bold($"{GetCountString(items)}{items.Item} {items.Item.ItemEmoji}"),
Format.Bold(waifu.ToString())))
.SendAsync();
}
else
await Response().Error(strs.not_enough(CurrencySign)).SendAsync();
}
private static string GetCountString(MultipleWaifuItems items)
=> items.Count > 1
? $"{items.Count}x "
: string.Empty;
}
}

View File

@@ -7,6 +7,7 @@ using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Gambling.Common;
using NadekoBot.Modules.Gambling.Common.Waifu;
using SixLabors.ImageSharp;
namespace NadekoBot.Modules.Gambling.Services;
@@ -89,9 +90,14 @@ public class WaifuService : INService, IReadyExecutor
if (waifu is null)
return settings.Waifu.MinPrice;
var divorces = uow.Set<WaifuUpdate>().Count(x
=> x.Old != null && x.Old.UserId == user.Id && x.UpdateType == WaifuUpdateType.Claimed && x.New == null);
var affs = uow.Set<WaifuUpdate>().AsQueryable()
var divorces = uow.Set<WaifuUpdate>()
.Count(x
=> x.Old != null
&& x.Old.UserId == user.Id
&& x.UpdateType == WaifuUpdateType.Claimed
&& x.New == null);
var affs = uow.Set<WaifuUpdate>()
.AsQueryable()
.Where(w => w.User.UserId == user.Id
&& w.UpdateType == WaifuUpdateType.AffinityChanged
&& w.New != null)
@@ -110,12 +116,14 @@ public class WaifuService : INService, IReadyExecutor
if (!await _cs.RemoveAsync(user.Id, price, new("waifu", "reset")))
return false;
var affs = uow.Set<WaifuUpdate>().AsQueryable()
var affs = uow.Set<WaifuUpdate>()
.AsQueryable()
.Where(w => w.User.UserId == user.Id
&& w.UpdateType == WaifuUpdateType.AffinityChanged
&& w.New != null);
var divorces = uow.Set<WaifuUpdate>().AsQueryable()
var divorces = uow.Set<WaifuUpdate>()
.AsQueryable()
.Where(x => x.Old != null
&& x.Old.UserId == user.Id
&& x.UpdateType == WaifuUpdateType.Claimed
@@ -158,20 +166,22 @@ public class WaifuService : INService, IReadyExecutor
result = WaifuClaimResult.NotEnoughFunds;
else
{
uow.Set<WaifuInfo>().Add(w = new()
{
Waifu = waifu,
Claimer = claimer,
Affinity = null,
Price = amount
});
uow.Set<WaifuUpdate>().Add(new()
{
User = waifu,
Old = null,
New = claimer,
UpdateType = WaifuUpdateType.Claimed
});
uow.Set<WaifuInfo>()
.Add(w = new()
{
Waifu = waifu,
Claimer = claimer,
Affinity = null,
Price = amount
});
uow.Set<WaifuUpdate>()
.Add(new()
{
User = waifu,
Old = null,
New = claimer,
UpdateType = WaifuUpdateType.Claimed
});
result = WaifuClaimResult.Success;
}
}
@@ -186,13 +196,14 @@ public class WaifuService : INService, IReadyExecutor
w.Price = amount + (amount / 4);
result = WaifuClaimResult.Success;
uow.Set<WaifuUpdate>().Add(new()
{
User = w.Waifu,
Old = oldClaimer,
New = w.Claimer,
UpdateType = WaifuUpdateType.Claimed
});
uow.Set<WaifuUpdate>()
.Add(new()
{
User = w.Waifu,
Old = oldClaimer,
New = w.Claimer,
UpdateType = WaifuUpdateType.Claimed
});
}
}
else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity
@@ -206,13 +217,14 @@ public class WaifuService : INService, IReadyExecutor
w.Price = amount;
result = WaifuClaimResult.Success;
uow.Set<WaifuUpdate>().Add(new()
{
User = w.Waifu,
Old = oldClaimer,
New = w.Claimer,
UpdateType = WaifuUpdateType.Claimed
});
uow.Set<WaifuUpdate>()
.Add(new()
{
User = w.Waifu,
Old = oldClaimer,
New = w.Claimer,
UpdateType = WaifuUpdateType.Claimed
});
}
}
else
@@ -241,29 +253,31 @@ public class WaifuService : INService, IReadyExecutor
remaining = await _cache.GetRatelimitAsync(GetAffinityKey(user.Id),
30.Minutes());
if (remaining is not null)
{
}
else if (w is null)
{
var thisUser = uow.GetOrCreateUser(user);
uow.Set<WaifuInfo>().Add(new()
{
Affinity = newAff,
Waifu = thisUser,
Price = 1,
Claimer = null
});
uow.Set<WaifuInfo>()
.Add(new()
{
Affinity = newAff,
Waifu = thisUser,
Price = 1,
Claimer = null
});
success = true;
uow.Set<WaifuUpdate>().Add(new()
{
User = thisUser,
Old = null,
New = newAff,
UpdateType = WaifuUpdateType.AffinityChanged
});
uow.Set<WaifuUpdate>()
.Add(new()
{
User = thisUser,
Old = null,
New = newAff,
UpdateType = WaifuUpdateType.AffinityChanged
});
}
else
{
@@ -272,13 +286,14 @@ public class WaifuService : INService, IReadyExecutor
w.Affinity = newAff;
success = true;
uow.Set<WaifuUpdate>().Add(new()
{
User = w.Waifu,
Old = oldAff,
New = newAff,
UpdateType = WaifuUpdateType.AffinityChanged
});
uow.Set<WaifuUpdate>()
.Add(new()
{
User = w.Waifu,
Old = oldAff,
New = newAff,
UpdateType = WaifuUpdateType.AffinityChanged
});
}
await uow.SaveChangesAsync();
@@ -301,10 +316,10 @@ public class WaifuService : INService, IReadyExecutor
private static TypedKey<long> GetDivorceKey(ulong userId)
=> new($"waifu:divorce_cd:{userId}");
private static TypedKey<long> GetAffinityKey(ulong userId)
=> new($"waifu:affinity:{userId}");
public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId)
{
DivorceResult result;
@@ -343,13 +358,14 @@ public class WaifuService : INService, IReadyExecutor
var oldClaimer = w.Claimer;
w.Claimer = null;
uow.Set<WaifuUpdate>().Add(new()
{
User = w.Waifu,
Old = oldClaimer,
New = null,
UpdateType = WaifuUpdateType.Claimed
});
uow.Set<WaifuUpdate>()
.Add(new()
{
User = w.Waifu,
Old = oldClaimer,
New = null,
UpdateType = WaifuUpdateType.Claimed
});
}
await uow.SaveChangesAsync();
@@ -358,40 +374,54 @@ public class WaifuService : INService, IReadyExecutor
return (w, result, amount, remaining);
}
public async Task<bool> GiftWaifuAsync(IUser from, IUser giftedWaifu, WaifuItemModel itemObj)
public async Task<bool> GiftWaifuAsync(
IUser from,
IUser giftedWaifu,
WaifuItemModel itemObj,
int count)
{
if (!await _cs.RemoveAsync(from, itemObj.Price, new("waifu", "item")))
ArgumentOutOfRangeException.ThrowIfLessThan(count, 1, nameof(count));
if (!await _cs.RemoveAsync(from, itemObj.Price * count, new("waifu", "item")))
return false;
var totalValue = itemObj.Price * count;
await using var uow = _db.GetDbContext();
var w = uow.Set<WaifuInfo>().ByWaifuUserId(giftedWaifu.Id, set => set.Include(x => x.Items).Include(x => x.Claimer));
var w = uow.Set<WaifuInfo>()
.ByWaifuUserId(giftedWaifu.Id,
set => set
.Include(x => x.Items)
.Include(x => x.Claimer));
if (w is null)
{
uow.Set<WaifuInfo>().Add(w = new()
{
Affinity = null,
Claimer = null,
Price = 1,
Waifu = uow.GetOrCreateUser(giftedWaifu)
});
uow.Set<WaifuInfo>()
.Add(w = new()
{
Affinity = null,
Claimer = null,
Price = 1,
Waifu = uow.GetOrCreateUser(giftedWaifu)
});
}
if (!itemObj.Negative)
{
w.Items.Add(new()
{
Name = itemObj.Name.ToLowerInvariant(),
ItemEmoji = itemObj.ItemEmoji
});
w.Items.AddRange(Enumerable.Range(0, count)
.Select((_) => new WaifuItem()
{
Name = itemObj.Name.ToLowerInvariant(),
ItemEmoji = itemObj.ItemEmoji
}));
if (w.Claimer?.UserId == from.Id)
w.Price += (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect);
w.Price += (long)(totalValue * _gss.Data.Waifu.Multipliers.GiftEffect);
else
w.Price += itemObj.Price / 2;
w.Price += totalValue / 2;
}
else
{
w.Price -= (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect);
w.Price -= (long)(totalValue * _gss.Data.Waifu.Multipliers.NegativeGiftEffect);
if (w.Price < 1)
w.Price = 1;
}
@@ -492,6 +522,7 @@ public class WaifuService : INService, IReadyExecutor
}
private static readonly TypedKey<long> _waifuDecayKey = $"waifu:last_decay";
public async Task OnReadyAsync()
{
// only decay waifu values from shard 0
@@ -513,7 +544,7 @@ public class WaifuService : INService, IReadyExecutor
var nowB = now.ToBinary();
var result = await _cache.GetAsync(_waifuDecayKey);
if (result.TryGetValue(out var val))
{
var lastDecay = DateTime.FromBinary(val);
@@ -533,7 +564,6 @@ public class WaifuService : INService, IReadyExecutor
{
Price = (long)(old.Price * multi)
});
}
catch (Exception ex)
{
@@ -550,33 +580,35 @@ public class WaifuService : INService, IReadyExecutor
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<DiscordUser>()
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.ClaimerId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.ClaimerId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
}
public async Task<IReadOnlyCollection<string>> GetFansNames(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<DiscordUser>()
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.AffinityId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.AffinityId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
}
public async Task<IReadOnlyCollection<WaifuItem>> GetItems(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<WaifuItem>()
.Where(x => x.WaifuInfoId == ctx.GetTable<WaifuInfo>()
.Where(x => x.WaifuId == waifuId)
.Select(x => x.Id)
.FirstOrDefault())
.ToListAsyncEF();
.Where(x => x.WaifuInfoId
== ctx.GetTable<WaifuInfo>()
.Where(x => x.WaifuId == waifuId)
.Select(x => x.Id)
.FirstOrDefault())
.ToListAsyncEF();
}
}

View File

@@ -0,0 +1,6 @@
#nullable disable
using NadekoBot.Modules.Gambling.Common;
namespace NadekoBot.Modules.Gambling;
public record class MultipleWaifuItems(int Count, WaifuItemModel Item);

View File

@@ -0,0 +1,47 @@
#nullable disable
using NadekoBot.Common.TypeReaders;
using NadekoBot.Modules.Gambling.Services;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.Gambling;
public partial class MultipleWaifuItemsTypeReader : NadekoTypeReader<MultipleWaifuItems>
{
private readonly WaifuService _service;
[GeneratedRegex(@"(?:(?<count>\d+)[x*])?(?<item>.+)")]
private static partial Regex ItemRegex();
public MultipleWaifuItemsTypeReader(WaifuService service)
{
_service = service;
}
public override ValueTask<TypeReaderResult<MultipleWaifuItems>> ReadAsync(ICommandContext ctx, string input)
{
input = input.ToLowerInvariant();
var match = ItemRegex().Match(input);
if (!match.Success)
{
return new(Discord.Commands.TypeReaderResult.FromError(CommandError.ParseFailed, "Invalid input."));
}
var count = 1;
if (match.Groups["count"].Success)
{
if (!int.TryParse(match.Groups["count"].Value, out count) || count < 1)
{
return new(Discord.Commands.TypeReaderResult.FromError(CommandError.ParseFailed, "Invalid count."));
}
}
var itemName = match.Groups["item"].Value?.ToLowerInvariant();
var allItems = _service.GetWaifuItems();
var item = allItems.FirstOrDefault(x => x.Name.ToLowerInvariant() == itemName);
if (item is null)
{
return new(Discord.Commands.TypeReaderResult.FromError(CommandError.ParseFailed, "Waifu gift does not exist."));
}
return new(Discord.Commands.TypeReaderResult.FromSuccess(new MultipleWaifuItems(count, item)));
}
}

View File

@@ -18,7 +18,8 @@ public partial class Games
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]
public async Task Cleverbot()
[NoPublicBot]
public async Task CleverBot()
{
var channel = (ITextChannel)ctx.Channel;
@@ -30,7 +31,7 @@ public partial class Games
await uow.SaveChangesAsync();
}
await Response().Confirm(strs.cleverbot_disabled).SendAsync();
await Response().Confirm(strs.chatbot_disabled).SendAsync();
return;
}
@@ -42,7 +43,7 @@ public partial class Games
await uow.SaveChangesAsync();
}
await Response().Confirm(strs.cleverbot_enabled).SendAsync();
await Response().Confirm(strs.chatbot_enabled).SendAsync();
}
}
}

View File

@@ -1,6 +1,5 @@
#nullable disable
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Games.Common;
using NadekoBot.Modules.Games.Common.ChatterBot;
using NadekoBot.Modules.Patronage;
@@ -15,43 +14,32 @@ public class ChatterBotService : IExecOnMessage
public int Priority
=> 1;
private readonly FeatureLimitKey _flKey;
private readonly DiscordSocketClient _client;
private readonly IPermissionChecker _perms;
private readonly CommandHandler _cmd;
private readonly IBotCredentials _creds;
private readonly IHttpClientFactory _httpFactory;
private readonly IPatronageService _ps;
private readonly GamesConfigService _gcs;
private readonly IMessageSenderService _sender;
public readonly IPatronageService _ps;
public ChatterBotService(
DiscordSocketClient client,
IPermissionChecker perms,
IBot bot,
CommandHandler cmd,
IPatronageService ps,
IHttpClientFactory factory,
IBotCredentials creds,
IPatronageService ps,
GamesConfigService gcs,
IMessageSenderService sender)
{
_client = client;
_perms = perms;
_cmd = cmd;
_creds = creds;
_sender = sender;
_httpFactory = factory;
_ps = ps;
_perms = perms;
_gcs = gcs;
_flKey = new FeatureLimitKey()
{
Key = CleverBotResponseStr.CLEVERBOT_RESPONSE,
PrettyName = "Cleverbot Replies"
};
_ps = ps;
ChatterBotGuilds = new(bot.AllGuildConfigs
.Where(gc => gc.CleverbotEnabled)
@@ -69,37 +57,39 @@ public class ChatterBotService : IExecOnMessage
Log.Information("Cleverbot will not work as the api key is missing");
return null;
case ChatBotImplementation.Gpt3:
case ChatBotImplementation.OpenAi:
var data = _gcs.Data;
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
return new OfficialGpt3Session(_creds.Gpt3ApiKey,
_gcs.Data.ChatGpt.ModelName,
_gcs.Data.ChatGpt.ChatHistory,
_gcs.Data.ChatGpt.MaxTokens,
_gcs.Data.ChatGpt.MinTokens,
_gcs.Data.ChatGpt.PersonalityPrompt,
return new OpenAiApiSession(
data.ChatGpt.ApiUrl,
_creds.Gpt3ApiKey,
data.ChatGpt.ModelName,
data.ChatGpt.ChatHistory,
data.ChatGpt.MaxTokens,
data.ChatGpt.MinTokens,
data.ChatGpt.PersonalityPrompt,
_client.CurrentUser.Username,
_httpFactory);
Log.Information("Gpt3 will not work as the api key is missing");
Log.Information("Openai Api will likely not work as the api key is missing");
return null;
default:
return null;
}
}
public string PrepareMessage(IUserMessage msg, out IChatterBotSession cleverbot)
public IChatterBotSession GetOrCreateSession(ulong guildId)
{
var channel = msg.Channel as ITextChannel;
cleverbot = null;
if (ChatterBotGuilds.TryGetValue(guildId, out var lazyChatBot))
return lazyChatBot.Value;
if (channel is null)
return null;
if (!ChatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyCleverbot))
return null;
cleverbot = lazyCleverbot.Value;
lazyChatBot = new(() => CreateSession(), true);
ChatterBotGuilds.TryAdd(guildId, lazyChatBot);
return lazyChatBot.Value;
}
public string PrepareMessage(IUserMessage msg)
{
var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
@@ -108,6 +98,8 @@ public class ChatterBotService : IExecOnMessage
message = msg.Content[normalMention.Length..].Trim();
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
message = msg.Content[nickMention.Length..].Trim();
else if (msg.ReferencedMessage?.Author.Id == nadekoId)
message = msg.Content;
else
return null;
@@ -119,13 +111,31 @@ public class ChatterBotService : IExecOnMessage
if (guild is not SocketGuild sg)
return false;
var channel = usrMsg.Channel as ITextChannel;
if (channel is null)
return false;
if (!ChatterBotGuilds.TryGetValue(channel.Guild.Id, out var lazyChatBot))
return false;
var chatBot = lazyChatBot.Value;
var message = PrepareMessage(usrMsg);
if (message is null)
return false;
return await RunChatterBot(sg, usrMsg, channel, chatBot, message);
}
public async Task<bool> RunChatterBot(
SocketGuild guild,
IUserMessage usrMsg,
ITextChannel channel,
IChatterBotSession chatBot,
string message)
{
try
{
var message = PrepareMessage(usrMsg, out var cbs);
if (message is null || cbs is null)
return false;
var res = await _perms.CheckPermsAsync(sg,
var res = await _perms.CheckPermsAsync(guild,
usrMsg.Channel,
usrMsg.Author,
CleverBotResponseStr.CLEVERBOT_RESPONSE,
@@ -134,59 +144,33 @@ public class ChatterBotService : IExecOnMessage
if (!res.IsAllowed)
return false;
var channel = (ITextChannel)usrMsg.Channel;
var conf = _ps.GetConfig();
if (!_creds.IsOwner(sg.OwnerId) && conf.IsEnabled)
if (!await _ps.LimitHitAsync(LimitedFeatureName.ChatBot, usrMsg.Author.Id, 2048 / 2))
{
var quota = await _ps.TryGetFeatureLimitAsync(_flKey, sg.OwnerId, 0);
uint? daily = quota.Quota is int dVal and < 0
? (uint)-dVal
: null;
uint? monthly = quota.Quota is int mVal and >= 0
? (uint)mVal
: null;
var maybeLimit = await _ps.TryIncrementQuotaCounterAsync(sg.OwnerId,
sg.OwnerId == usrMsg.Author.Id,
FeatureType.Limit,
_flKey.Key,
null,
daily,
monthly);
if (maybeLimit.TryPickT1(out var ql, out var counters))
{
if (ql.Quota == 0)
{
await _sender.Response(channel)
.Error(null,
text:
"In order to use the cleverbot feature, the owner of this server should be [Patron Tier X](https://patreon.com/join/nadekobot) on patreon.",
footer:
"You may disable the cleverbot feature, and this message via '.cleverbot' command")
.SendAsync();
return true;
}
await _sender.Response(channel)
.Error(
null!,
$"You've reached your quota limit of **{ql.Quota}** responses {ql.QuotaPeriod.ToFullName()} for the cleverbot feature.",
footer: "You may wait for the quota reset or .")
.SendAsync();
return true;
}
// limit exceeded
return false;
}
_ = channel.TriggerTypingAsync();
var response = await cbs.Think(message, usrMsg.Author.ToString());
await _sender.Response(channel)
.Confirm(response)
.SendAsync();
var response = await chatBot.Think(message, usrMsg.Author.ToString());
if (response.TryPickT0(out var result, out var error))
{
// calculate the diff in case we overestimated user's usage
var inTokens = (result.TokensIn - 2048) / 2;
// add the output tokens to the limit
await _ps.LimitForceHit(LimitedFeatureName.ChatBot,
usrMsg.Author.Id,
(inTokens) + (result.TokensOut / 2 * 3));
await _sender.Response(channel)
.Confirm(result.Text)
.SendAsync();
}
else
{
Log.Warning("Error in chatterbot: {Error}", error.Value);
}
Log.Information("""
CleverBot Executed

View File

@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class Choice
{
[JsonPropertyName("message")]
public Message Message { get; init; }
}

View File

@@ -1,46 +0,0 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class Gpt3Response
{
[JsonPropertyName("choices")]
public Choice[] Choices { get; set; }
}
public class Choice
{
[JsonPropertyName("message")]
public Message Message { get; init; }
}
public class Message {
[JsonPropertyName("content")]
public string Content { get; init; }
}
public class Gpt3ApiRequest
{
[JsonPropertyName("model")]
public string Model { get; init; }
[JsonPropertyName("messages")]
public List<GPTMessage> Messages { get; init; }
[JsonPropertyName("temperature")]
public int Temperature { get; init; }
[JsonPropertyName("max_tokens")]
public int MaxTokens { get; init; }
}
public class GPTMessage
{
[JsonPropertyName("role")]
public string Role {get; init;}
[JsonPropertyName("content")]
public string Content {get; init;}
[JsonPropertyName("name")]
public string Name {get; init;}
}

View File

@@ -1,7 +1,10 @@
#nullable disable
using OneOf;
using OneOf.Types;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public interface IChatterBotSession
{
Task<string> Think(string input, string username);
Task<OneOf<ThinkResult, Error<string>>> Think(string input, string username);
}

View File

@@ -0,0 +1,8 @@
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class Message {
[JsonPropertyName("content")]
public string Content { get; init; }
}

View File

@@ -1,5 +1,7 @@
#nullable disable
using Newtonsoft.Json;
using OneOf;
using OneOf.Types;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
@@ -18,7 +20,7 @@ public class OfficialCleverbotSession : IChatterBotSession
_httpFactory = factory;
}
public async Task<string> Think(string input, string username)
public async Task<OneOf<ThinkResult, Error<string>>> Think(string input, string username)
{
using var http = _httpFactory.CreateClient();
var dataString = await http.GetStringAsync(string.Format(QueryString, input, cs ?? ""));
@@ -27,12 +29,17 @@ public class OfficialCleverbotSession : IChatterBotSession
var data = JsonConvert.DeserializeObject<CleverbotResponse>(dataString);
cs = data?.Cs;
return data?.Output;
return new ThinkResult
{
Text = data?.Output,
TokensIn = 2,
TokensOut = 1
};
}
catch
{
Log.Warning("Unexpected cleverbot response received: {ResponseString}", dataString);
return null;
Log.Warning("Unexpected response from CleverBot: {ResponseString}", dataString);
return new Error<string>("Unexpected CleverBot response received");
}
}
}

View File

@@ -1,105 +0,0 @@
#nullable disable
using Newtonsoft.Json;
using System.Net.Http.Json;
using SharpToken;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OfficialGpt3Session : IChatterBotSession
{
private string Uri
=> $"https://api.openai.com/v1/chat/completions";
private readonly string _apiKey;
private readonly string _model;
private readonly int _maxHistory;
private readonly int _maxTokens;
private readonly int _minTokens;
private readonly string _nadekoUsername;
private readonly GptEncoding _encoding;
private List<GPTMessage> messages = new();
private readonly IHttpClientFactory _httpFactory;
public OfficialGpt3Session(
string apiKey,
ChatGptModel model,
int chatHistory,
int maxTokens,
int minTokens,
string personality,
string nadekoUsername,
IHttpClientFactory factory)
{
_apiKey = apiKey;
_httpFactory = factory;
switch (model)
{
case ChatGptModel.Gpt35Turbo:
_model = "gpt-3.5-turbo";
break;
case ChatGptModel.Gpt4:
_model = "gpt-4";
break;
case ChatGptModel.Gpt432k:
_model = "gpt-4-32k";
break;
}
_maxHistory = chatHistory;
_maxTokens = maxTokens;
_minTokens = minTokens;
_nadekoUsername = nadekoUsername;
_encoding = GptEncoding.GetEncodingForModel(_model);
messages.Add(new GPTMessage(){Role = "user", Content = personality, Name = _nadekoUsername});
}
public async Task<string> Think(string input, string username)
{
messages.Add(new GPTMessage(){Role = "user", Content = input, Name = username});
while(messages.Count > _maxHistory + 2){
messages.RemoveAt(1);
}
int tokensUsed = 0;
foreach(GPTMessage message in messages){
tokensUsed += _encoding.Encode(message.Content).Count;
}
tokensUsed *= 2; //Unsure why this is the case, but the token count chatgpt reports back is double what I calculate.
//check if we have the minimum number of tokens available to use. Remove messages until we have enough, otherwise exit out and inform the user why.
while(_maxTokens - tokensUsed <= _minTokens){
if(messages.Count > 2){
int tokens = _encoding.Encode(messages[1].Content).Count * 2;
tokensUsed -= tokens;
messages.RemoveAt(1);
}
else{
return "Token count exceeded, please increase the number of tokens in the bot config and restart.";
}
}
using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
var data = await http.PostAsJsonAsync(Uri, new Gpt3ApiRequest()
{
Model = _model,
Messages = messages,
MaxTokens = _maxTokens - tokensUsed,
Temperature = 1,
});
var dataString = await data.Content.ReadAsStringAsync();
try
{
var response = JsonConvert.DeserializeObject<Gpt3Response>(dataString);
string message = response?.Choices[0]?.Message?.Content;
//Can't rely on the return to except, now that we need to add it to the messages list.
_ = message ?? throw new ArgumentNullException(nameof(message));
messages.Add(new GPTMessage(){Role = "assistant", Content = message, Name = _nadekoUsername});
return message;
}
catch
{
Log.Warning("Unexpected GPT-3 response received: {ResponseString}", dataString);
return null;
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OpenAiApiMessage
{
[JsonPropertyName("role")]
public string Role { get; init; }
[JsonPropertyName("content")]
public string Content { get; init; }
[JsonPropertyName("name")]
public string Name { get; init; }
}

View File

@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OpenAiApiRequest
{
[JsonPropertyName("model")]
public string Model { get; init; }
[JsonPropertyName("messages")]
public List<OpenAiApiMessage> Messages { get; init; }
[JsonPropertyName("temperature")]
public int Temperature { get; init; }
[JsonPropertyName("max_tokens")]
public int MaxTokens { get; init; }
}

View File

@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OpenAiApiUsageData
{
[JsonPropertyName("prompt_tokens")]
public int PromptTokens { get; set; }
[JsonPropertyName("completion_tokens")]
public int CompletionTokens { get; set; }
[JsonPropertyName("total_tokens")]
public int TotalTokens { get; set; }
}

View File

@@ -0,0 +1,13 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OpenAiCompletionResponse
{
[JsonPropertyName("choices")]
public Choice[] Choices { get; set; }
[JsonPropertyName("usage")]
public OpenAiApiUsageData Usage { get; set; }
}

View File

@@ -0,0 +1,147 @@
#nullable disable
using Newtonsoft.Json;
using OneOf.Types;
using SharpToken;
using System.Net.Http.Json;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public partial class OpenAiApiSession : IChatterBotSession
{
private readonly string _baseUrl;
private readonly string _apiKey;
private readonly string _model;
private readonly int _maxHistory;
private readonly int _maxTokens;
private readonly int _minTokens;
private readonly string _nadekoUsername;
private readonly GptEncoding _encoding;
private List<OpenAiApiMessage> messages = new();
private readonly IHttpClientFactory _httpFactory;
public OpenAiApiSession(
string url,
string apiKey,
string model,
int chatHistory,
int maxTokens,
int minTokens,
string personality,
string nadekoUsername,
IHttpClientFactory factory)
{
if (string.IsNullOrWhiteSpace(url) || !Uri.TryCreate(url, UriKind.Absolute, out _))
{
throw new ArgumentException("Invalid OpenAi api url provided", nameof(url));
}
_baseUrl = url.TrimEnd('/');
_apiKey = apiKey;
_model = model;
_httpFactory = factory;
_maxHistory = chatHistory;
_maxTokens = maxTokens;
_minTokens = minTokens;
_nadekoUsername = UsernameCleaner().Replace(nadekoUsername, "");
_encoding = GptEncoding.GetEncodingForModel("gpt-4o");
if (!string.IsNullOrWhiteSpace(personality))
{
messages.Add(new()
{
Role = "system",
Content = personality,
Name = _nadekoUsername
});
}
}
[GeneratedRegex("[^a-zA-Z0-9_-]")]
private static partial Regex UsernameCleaner();
public async Task<OneOf.OneOf<ThinkResult, Error<string>>> Think(string input, string username)
{
username = UsernameCleaner().Replace(username, "");
messages.Add(new()
{
Role = "user",
Content = input,
Name = username
});
while (messages.Count > _maxHistory + 2)
{
messages.RemoveAt(1);
}
var tokensUsed = messages.Sum(message => _encoding.Encode(message.Content).Count);
tokensUsed *= 2;
//check if we have the minimum number of tokens available to use. Remove messages until we have enough, otherwise exit out and inform the user why.
while (_maxTokens - tokensUsed <= _minTokens)
{
if (messages.Count > 2)
{
var tokens = _encoding.Encode(messages[1].Content).Count * 2;
tokensUsed -= tokens;
messages.RemoveAt(1);
}
else
{
return new Error<string>(
"Token count exceeded, please increase the number of tokens in the bot config and restart.");
}
}
using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
var data = await http.PostAsJsonAsync($"{_baseUrl}/v1/chat/completions",
new OpenAiApiRequest()
{
Model = _model,
Messages = messages,
MaxTokens = _maxTokens - tokensUsed,
Temperature = 1,
});
var dataString = await data.Content.ReadAsStringAsync();
try
{
var response = JsonConvert.DeserializeObject<OpenAiCompletionResponse>(dataString);
// Log.Information("Received response: {Response} ", dataString);
var res = response?.Choices?[0];
var message = res?.Message?.Content;
if (message is null)
{
return new Error<string>("ChatGpt: Received no response.");
}
messages.Add(new()
{
Role = "assistant",
Content = message,
Name = _nadekoUsername
});
return new ThinkResult()
{
Text = message,
TokensIn = response.Usage.PromptTokens,
TokensOut = response.Usage.CompletionTokens
};
}
catch
{
Log.Warning("Unexpected response received from OpenAI: {ResponseString}", dataString);
return new Error<string>("Unexpected response received");
}
}
}

View File

@@ -0,0 +1,11 @@
#nullable disable
using System.CodeDom;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public sealed class ThinkResult
{
public string Text { get; set; }
public int TokensIn { get; set; }
public int TokensOut { get; set; }
}

View File

@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Games.Common;
public sealed partial class GamesConfig : ICloneable<GamesConfig>
{
[Comment("DO NOT CHANGE")]
public int Version { get; set; } = 3;
public int Version { get; set; } = 5;
[Comment("Hangman related settings (.hangman command)")]
public HangmanConfig Hangman { get; set; } = new()
@@ -103,10 +103,13 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
}
];
[Comment(@"Which chatbot API should bot use.
'cleverbot' - bot will use Cleverbot API.
'gpt3' - bot will use GPT-3 API")]
public ChatBotImplementation ChatBot { get; set; } = ChatBotImplementation.Gpt3;
[Comment(
"""
Which chatbot API should bot use.
'cleverbot' - bot will use Cleverbot API.
'openai' - bot will use OpenAi API
""")]
public ChatBotImplementation ChatBot { get; set; } = ChatBotImplementation.OpenAi;
public ChatGptConfig ChatGpt { get; set; } = new();
}
@@ -114,22 +117,41 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
[Cloneable]
public sealed partial class ChatGptConfig
{
[Comment(@"Which GPT-3 Model should bot use.
gpt35turbo - cheapest
gpt4 - 30x more expensive, higher quality
gp432k - same model as above, but with a 32k token limit")]
public ChatGptModel ModelName { get; set; } = ChatGptModel.Gpt35Turbo;
[Comment("""
Url to any openai api compatible url.
Make sure to modify the modelName appropriately
DO NOT add /v1/chat/completions suffix to the url
""")]
public string ApiUrl { get; set; } = "https://api.openai.com";
[Comment(@"How should the chat bot behave, what's its personality? (Usage of this counts towards the max tokens)")]
public string PersonalityPrompt { get; set; } = "You are a chat bot willing to have a conversation with anyone about anything.";
[Comment("""
Which GPT Model should bot use.
gpt-3.5-turbo - cheapest
gpt-4o - more expensive, higher quality
[Comment(@"The maximum number of messages in a conversation that can be remembered. (This will increase the number of tokens used)")]
If you are using another openai compatible api, you may use any of the models supported by that api
""")]
public string ModelName { get; set; } = "gpt-3.5-turbo";
[Comment("""
How should the chatbot behave, what's its personality?
This will be sent as a system message.
Usage of this counts towards the max tokens.
""")]
public string PersonalityPrompt { get; set; } =
"You are a chat bot willing to have a conversation with anyone about anything.";
[Comment(
"""
The maximum number of messages in a conversation that can be remembered.
This will increase the number of tokens used.
""")]
public int ChatHistory { get; set; } = 5;
[Comment(@"The maximum number of tokens to use per GPT-3 API call")]
[Comment(@"The maximum number of tokens to use per OpenAi API call")]
public int MaxTokens { get; set; } = 100;
[Comment(@"The minimum number of tokens to use per GPT-3 API call, such that chat history is removed to make room.")]
[Comment(@"The minimum number of tokens to use per GPT API call, such that chat history is removed to make room.")]
public int MinTokens { get; set; } = 30;
}
@@ -147,9 +169,9 @@ public sealed partial class TriviaConfig
public long CurrencyReward { get; set; }
[Comment("""
Users won't be able to start trivia games which have
a smaller win requirement than the one specified by this setting.
""")]
Users won't be able to start trivia games which have
a smaller win requirement than the one specified by this setting.
""")]
public int MinimumWinReq { get; set; } = 1;
}
@@ -163,12 +185,11 @@ public sealed partial class RaceAnimal
public enum ChatBotImplementation
{
Cleverbot,
Gpt3
}
OpenAi = 1,
public enum ChatGptModel
{
Gpt35Turbo,
Gpt4,
Gpt432k
[Obsolete]
Gpt = 1,
[Obsolete]
Gpt3 = 1,
}

View File

@@ -32,29 +32,21 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
gs => gs.ChatBot,
ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString);
AddParsedProp("gpt.apiUrl",
gs => gs.ChatGpt.ApiUrl,
ConfigParsers.String,
ConfigPrinters.ToString);
AddParsedProp("gpt.modelName",
gs => gs.ChatGpt.ModelName,
ConfigParsers.InsensitiveEnum,
ConfigParsers.String,
ConfigPrinters.ToString);
AddParsedProp("gpt.personality",
gs => gs.ChatGpt.PersonalityPrompt,
ConfigParsers.String,
ConfigPrinters.ToString);
AddParsedProp("gpt.chathistory",
gs => gs.ChatGpt.ChatHistory,
int.TryParse,
ConfigPrinters.ToString,
val => val > 0);
AddParsedProp("gpt.max_tokens",
gs => gs.ChatGpt.MaxTokens,
int.TryParse,
ConfigPrinters.ToString,
val => val > 0);
AddParsedProp("gpt.min_tokens",
gs => gs.ChatGpt.MinTokens,
int.TryParse,
ConfigPrinters.ToString,
val => val > 0);
Migrate();
}
@@ -73,21 +65,55 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
});
}
if (data.Version < 2)
{
ModifyConfig(c =>
{
c.Version = 2;
c.ChatBot = ChatBotImplementation.Cleverbot;
});
}
if (data.Version < 3)
{
ModifyConfig(c =>
{
c.Version = 3;
c.ChatGpt.ModelName = ChatGptModel.Gpt35Turbo;
c.ChatGpt.ModelName = "gpt35turbo";
});
}
if (data.Version < 4)
{
ModifyConfig(c =>
{
c.Version = 4;
#pragma warning disable CS0612 // Type or member is obsolete
c.ChatGpt.ModelName =
c.ChatGpt.ModelName.Equals("gpt4", StringComparison.OrdinalIgnoreCase)
|| c.ChatGpt.ModelName.Equals("gpt432k", StringComparison.OrdinalIgnoreCase)
? "gpt-4o"
: "gpt-3.5-turbo";
#pragma warning restore CS0612 // Type or member is obsolete
});
}
if (data.Version < 5)
{
ModifyConfig(c =>
{
c.Version = 5;
c.ChatBot = c.ChatBot == ChatBotImplementation.OpenAi
? ChatBotImplementation.OpenAi
: c.ChatBot;
if (c.ChatGpt.ModelName.Equals("gpt4o", StringComparison.OrdinalIgnoreCase))
{
c.ChatGpt.ModelName = "gpt-4o";
}
else if (c.ChatGpt.ModelName.Equals("gpt35turbo", StringComparison.OrdinalIgnoreCase))
{
c.ChatGpt.ModelName = "gpt-3.5-turbo";
}
else
{
Log.Warning(
"Unknown OpenAI api model name: {ModelName}. "
+ "It will be reset to 'gpt-3.5-turbo' only this time",
c.ChatGpt.ModelName);
c.ChatGpt.ModelName = "gpt-3.5-turbo";
}
});
}
}

View File

@@ -86,11 +86,31 @@ public sealed partial class Help : NadekoModule<HelpService>
topLevelModules.Add(m);
}
var menu = new SelectMenuBuilder()
.WithPlaceholder("Select a module to see its commands")
.WithCustomId("cmds:modules_select");
foreach (var m in topLevelModules)
menu.AddOption(m.Name, m.Name, GetModuleEmoji(m.Name));
var inter = _inter.Create(ctx.User.Id,
menu,
async (smc) =>
{
await smc.DeferAsync();
var val = smc.Data.Values.FirstOrDefault();
if (val is null)
return;
await Commands(val);
});
await Response()
.Paginated()
.Items(topLevelModules)
.PageSize(12)
.CurrentPage(page)
.Interaction(inter)
.AddFooter(false)
.Page((items, _) =>
{
@@ -221,10 +241,28 @@ public sealed partial class Help : NadekoModule<HelpService>
// order by name
var allowed = new List<CommandInfo>();
foreach (var cmd in _cmds.Commands
.Where(c => c.Module.GetTopLevelModule()
.Name
.StartsWith(module, StringComparison.InvariantCultureIgnoreCase)))
var mdls = _cmds.Commands
.Where(c => c.Module.GetTopLevelModule()
.Name
.StartsWith(module, StringComparison.InvariantCultureIgnoreCase))
.ToArray();
if (mdls.Length == 0)
{
var group = _cmds.Modules
.Where(x => x.Parent is not null)
.FirstOrDefault(x => string.Equals(x.Name.Replace("Commands", ""),
module,
StringComparison.InvariantCultureIgnoreCase));
if (group is not null)
{
await Group(group);
return;
}
}
foreach (var cmd in mdls)
{
var result = await _perms.CheckPermsAsync(ctx.Guild,
ctx.Channel,
@@ -236,6 +274,7 @@ public sealed partial class Help : NadekoModule<HelpService>
allowed.Add(cmd);
}
var cmds = allowed.OrderBy(c => c.Aliases[0])
.DistinctBy(x => x.Aliases[0])
.ToList();
@@ -275,55 +314,97 @@ public sealed partial class Help : NadekoModule<HelpService>
return;
}
var cnt = 0;
var groups = cmdsWithGroup.GroupBy(_ => cnt++ / 48).ToArray();
var sb = new SelectMenuBuilder()
.WithCustomId("cmds:submodule_select")
.WithPlaceholder("Select a submodule to see detailed commands");
var groups = cmdsWithGroup.ToArray();
var embed = _sender.CreateEmbed().WithOkColor();
foreach (var g in groups)
{
var last = g.Count();
for (var i = 0; i < last; i++)
{
var transformed = g.ElementAt(i)
.Select(x =>
{
//if cross is specified, and the command doesn't satisfy the requirements, cross it out
if (opts.View == CommandsOptions.ViewType.Cross)
{
return $"{(succ.Contains(x) ? "" : "")} {prefix + x.Aliases[0]}";
}
sb.AddOption(g.Key, g.Key);
var transformed = g
.Select(x =>
{
//if cross is specified, and the command doesn't satisfy the requirements, cross it out
if (opts.View == CommandsOptions.ViewType.Cross)
{
return $"{(succ.Contains(x) ? "" : "")} {prefix + x.Aliases[0]}";
}
if (x.Aliases.Count == 1)
return prefix + x.Aliases[0];
return prefix + x.Aliases[0] + " | " + prefix + x.Aliases[1];
});
if (x.Aliases.Count == 1)
return prefix + x.Aliases[0];
embed.AddField(g.ElementAt(i).Key, "" + string.Join("\n", transformed) + "", true);
}
return prefix + x.Aliases[0] + " | " + prefix + x.Aliases[1];
});
embed.AddField(g.Key, "" + string.Join("\n", transformed) + "", true);
}
embed.WithFooter(GetText(strs.commands_instr(prefix)));
await Response().Embed(embed).SendAsync();
var inter = _inter.Create(ctx.User.Id,
sb,
async (smc) =>
{
var groupName = smc.Data.Values.FirstOrDefault();
var mdl = _cmds.Modules.FirstOrDefault(x
=> string.Equals(x.Name.Replace("Commands", ""), groupName, StringComparison.InvariantCultureIgnoreCase));
await smc.DeferAsync();
await Group(mdl);
}
);
await Response().Embed(embed).Interaction(inter).SendAsync();
}
private async Task Group(ModuleInfo group)
{
var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor();
var menu = new SelectMenuBuilder()
.WithCustomId("cmds:group_select")
.WithPlaceholder("Select a command to see its details");
foreach (var cmd in group.Commands.DistinctBy(x => x.Aliases[0]))
{
string cmdName;
if (cmd.Aliases.Count > 1)
cmdName = Format.Code(prefix + cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]);
else
cmdName = Format.Code(prefix + cmd.Aliases.First());
eb.AddField(cmdName, cmd.RealSummary(_strings, _medusae, Culture, prefix));
menu.AddOption(prefix + cmd.Aliases[0], cmd.Aliases[0]);
}
await Response().Embed(eb).SendAsync();
var inter = _inter.Create(ctx.User.Id,
menu,
async (smc) =>
{
await smc.DeferAsync();
await H(smc.Data.Values.FirstOrDefault());
});
await Response()
.Paginated()
.Items(group.Commands.DistinctBy(x => x.Aliases[0]).ToArray())
.PageSize(25)
.Interaction(inter)
.Page((items, _) =>
{
var eb = _sender.CreateEmbed()
.WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor();
foreach (var cmd in items)
{
string cmdName;
if (cmd.Aliases.Count > 1)
cmdName = Format.Code(prefix + cmd.Aliases[0]) + " | " + Format.Code(prefix + cmd.Aliases[1]);
else
cmdName = Format.Code(prefix + cmd.Aliases.First());
eb.AddField(cmdName, cmd.RealSummary(_strings, _medusae, Culture, prefix));
}
return eb;
})
.SendAsync();
}
[Cmd]
@@ -343,8 +424,9 @@ public sealed partial class Help : NadekoModule<HelpService>
var group = _cmds.Modules
.SelectMany(x => x.Submodules)
.Where(x => !string.IsNullOrWhiteSpace(x.Group))
.FirstOrDefault(x => x.Group.Equals(fail, StringComparison.InvariantCultureIgnoreCase));
.FirstOrDefault(x => string.Equals(x.Group,
fail,
StringComparison.InvariantCultureIgnoreCase));
if (group is not null)
{
@@ -424,7 +506,7 @@ public sealed partial class Help : NadekoModule<HelpService>
.ToList());
var readableData = JsonConvert.SerializeObject(cmdData, Formatting.Indented);
// send the indented file to chat
await using var rDataStream = new MemoryStream(Encoding.ASCII.GetBytes(readableData));
await ctx.Channel.SendFileAsync(rDataStream, "cmds.json", GetText(strs.commandlist_regen));
@@ -437,31 +519,10 @@ public sealed partial class Help : NadekoModule<HelpService>
"https://nadekobot.readthedocs.io/en/latest/"))
.SendAsync();
private Task SelfhostAction(SocketMessageComponent smc, object _)
=> smc.RespondConfirmAsync(_sender,
"""
- In case you don't want or cannot Donate to NadekoBot project, but you
- 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*
- You can find the selfhosting guides by using the `.guide` command and clicking on the second link that pops up.
- If you decide to selfhost the bot, still consider [supporting the project](https://patreon.com/join/nadekobot) to keep the development going :)
""",
true);
[Cmd]
[OnlyPublicBot]
public async Task Donate()
{
var selfhostInter = _inter.Create(ctx.User.Id,
new SimpleInteraction<object>(new ButtonBuilder(
emote: new Emoji("🖥️"),
customId: "donate:selfhosting",
label: "Selfhosting"),
SelfhostAction));
var eb = _sender.CreateEmbed()
.WithOkColor()
.WithTitle("Thank you for considering to donate to the NadekoBot project!");
@@ -489,24 +550,15 @@ public sealed partial class Help : NadekoModule<HelpService>
**Step 3** ⏰ Wait a short while (usually 1-3 minutes) ⏰
Nadeko will DM you the welcome instructions, and you may will receive your rewards!
Nadeko will DM you the welcome instructions, and you will receive your rewards!
🎉 **Enjoy!** 🎉
""")
.AddField("Troubleshooting",
"""
*In case you didn't receive the rewards within 5 minutes:*
`1.` Make sure your DMs are open to everyone. Maybe your pledge was processed successfully but the bot was unable to DM you. Use the `.patron` command to check your status.
`2.` Make sure you've connected the CORRECT Discord account. Quite often users log in to different Discord accounts in their browser. You may also try disconnecting and reconnecting your account.
`3.` Make sure your payment has been processed and not declined by Patreon.
`4.` If any of the previous steps don't help, you can join the nadeko support server <https://discord.nadeko.bot> and ask for help in the #help channel
""");
""");
try
{
await Response()
.Channel(await ctx.User.CreateDMChannelAsync())
.Embed(eb)
.Interaction(selfhostInter)
.SendAsync();
_ = ctx.OkAsync();

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