mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5bee5e63d2 | ||
|
815e318610 | ||
|
634c6c99ee | ||
|
6b37b49439 | ||
|
f42afa7eae | ||
|
ccae1c59e9 | ||
|
b0d8137a7a | ||
|
e78a7d0efa | ||
|
1da19a51f6 | ||
|
91eed9dbd8 | ||
|
7ba345b0fc | ||
|
5d775c9589 | ||
|
2bd8ead10c | ||
|
1148ba3e6e | ||
|
5498bec8cc | ||
|
9eed0c6be5 | ||
|
acf6b7cf58 | ||
|
8598419c5f | ||
|
758093eb32 | ||
|
f44dd03f1a | ||
|
8ac5ec9f57 | ||
|
5209ba802a | ||
|
adfce6670c | ||
|
f8fbc71985 | ||
|
d4e2516a17 | ||
|
f62a67e2e6 | ||
|
7895b5e702 | ||
|
28c8ccfb5f | ||
|
79026e0c27 | ||
|
8b228b840a | ||
|
f61c1a159d | ||
|
2cdcdb2b23 | ||
|
8d0a3ecb20 | ||
|
97f1405a94 | ||
|
0622236523 | ||
|
0f240925e8 | ||
|
d583e2b99a | ||
|
2d72f6f498 | ||
|
5143e42dff | ||
|
790c36df8d | ||
|
15709bc8fb | ||
|
f280f72227 | ||
|
4069368beb | ||
|
e036a2d3c9 | ||
|
62a16f3faf | ||
|
231451f978 | ||
|
2bed4e7eac | ||
|
22eea3fa7b | ||
|
8b1767078e | ||
|
6654ccdad5 | ||
|
99827b0fa1 | ||
|
0451551ddf | ||
|
75d0eb631d | ||
|
96ce7e23bb | ||
|
aac475be62 | ||
|
55fae53f6d | ||
|
879f196f8b | ||
|
796086538a | ||
|
3178074828 | ||
|
0a9d53d380 | ||
|
d7fbf44d53 | ||
|
cdd07d0559 | ||
|
80a41c1f38 | ||
|
1631394638 | ||
|
7eb4895570 | ||
|
9b55028ba6 | ||
|
53363d2840 | ||
|
ca5a870bf8 | ||
|
7f5e065c4d | ||
|
86b214163a | ||
|
9dbb08d85f | ||
|
89ca56c77c | ||
|
9e96679099 | ||
|
1280d2b397 | ||
|
c731127607 | ||
|
c15930306a | ||
|
701501d678 |
201
CHANGELOG.md
201
CHANGELOG.md
@@ -2,6 +2,207 @@
|
|||||||
|
|
||||||
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
|
## [5.3.4] - 14.01.2025
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `.fish` commands
|
||||||
|
- `.fish` - Attempt to catch a fish - different fish live in different places, at different times and during different times of the day
|
||||||
|
- `.fishlist` - Look at your fish catalogue - shows how many of each fish you caught and what was the highest quality - for each caught fish, it also shows its required spot, time of day and weather
|
||||||
|
- `.fishspot` - Shows information about the current fish spot, time of day and weather
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `.timely` fixed captcha sometimes generating only 2 characters
|
||||||
|
|
||||||
|
## [5.3.3] - 15.12.2024
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `.notify` commands are no longer owner only, they now require Admin permissions
|
||||||
|
- `.notify` messages can now mention anyone
|
||||||
|
|
||||||
|
## [5.3.2] - 14.12.2024
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `.banner` should be working properly now with both server and global user banners
|
||||||
|
|
||||||
|
## [5.3.1] - 13.12.2024
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- `.translate` will now use 2 embeds, to allow for longer messages
|
||||||
|
- Added role icon to `.inrole`, if it exists
|
||||||
|
- `.honeypot` will now add a 'Honeypot' as a ban reason.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `.winlb` looks better, has a title, shows 9 entries now
|
||||||
|
- `.sar ex` help updated
|
||||||
|
- `.banner` partially fixed, it still can't show global banners, but it will show guild ones correctly, in a good enough size
|
||||||
|
- `.sclr` will now show correct color hexes without alpha
|
||||||
|
- `.dmcmd` will now correctly block commands in dms, not globally
|
||||||
|
|
||||||
|
## [5.3.0] - 10.12.2024
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `.minesweeper` / `.mw` command - spoiler-based minesweeper minigame. Just for fun
|
||||||
|
- Added `.temprole` command - add a role to a user for a certain amount of time, after which the role will be removed
|
||||||
|
- Added `.xplevelset` - you can now set a level for a user in your server
|
||||||
|
- Added `.winlb` command - leaderboard of top gambling wins
|
||||||
|
- Added `.notify` command
|
||||||
|
- Specify an event to be notified about, and the bot will post the specified message in the current channel when the
|
||||||
|
event occurs
|
||||||
|
- A few events supported right now:
|
||||||
|
- `UserLevelUp` when user levels up in the server
|
||||||
|
- `AddRoleReward` when a role is added to a user through .xpreward system
|
||||||
|
- `RemoveRoleReward` when a role is removed from a user through .xpreward system
|
||||||
|
- `Protection` when antialt, antiraid or antispam protection is triggered
|
||||||
|
- Added `.banner` command to see someone's banner
|
||||||
|
- Selfhosters:
|
||||||
|
- Added `.dmmod` and `.dmcmd` - you can now disable or enable whether commands or modules can be executed in bot's
|
||||||
|
DMs
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Giveaway improvements
|
||||||
|
- Now mentions winners in a separate message
|
||||||
|
- Shows the timestamp of when the giveaway ends
|
||||||
|
- Xp Changes
|
||||||
|
- Removed awarded xp (the number in the brackets on the xp card)
|
||||||
|
- Awarded xp, (or the new level set) now directly apply to user's real xp
|
||||||
|
- Server xp notifications are now set by the server admin/manager in a specified channel
|
||||||
|
- `.sclr show` will now show hex code of the current color
|
||||||
|
- Queueing a song will now restart the playback if the queue is on the last track and stopped (there were no more tracks
|
||||||
|
to play)
|
||||||
|
- `.translate` will now use 2 embeds instead of 1
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- .setstream and .setactivity will now pause .ropl (rotating statuses)
|
||||||
|
- Fixed `.sar ex` help description
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- `.xpnotify` command, superseded by `.notify`, although as of right now you can't post user's level up in the same
|
||||||
|
channel user last typed, because you have to specify a channel where the notify messages will be posted
|
||||||
|
|
||||||
|
## [5.2.4] - 27.11.2024
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- More fixes for .sclr
|
||||||
|
- `.iamn` fixed
|
||||||
|
|
||||||
|
## [5.2.3] - 27.11.2024
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `.iam` Fixed
|
||||||
|
- `.sclr` will now properly change color on many commands it didn't work previously
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `.rps` now also has bet amount in the result, like other gambling commands
|
||||||
|
|
||||||
|
## [5.2.2] - 27.11.2024
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Button roles are now non-exclusive by default
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed sar migration, again (this time correctly)
|
||||||
|
- Fixed `.sclr` not updating unless bot is restarted, the changes should be immediate now for warn and error
|
||||||
|
- Fixed group buttons exclusivity message always saying groups are exclusive
|
||||||
|
|
||||||
|
## [5.2.1] - 26.11.2024
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed old self assigned missing
|
||||||
|
|
||||||
|
## [5.2.0] - 26.11.2024
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `.todo undone` command to unmark a todo as done
|
||||||
|
- Added Button Roles!
|
||||||
|
- `.btr a` to add a button role to the specified message
|
||||||
|
- `.btr list` to list all button roles on the server
|
||||||
|
- `.btr rm` to remove a button role from the specified message
|
||||||
|
- `.btr rma` to remove all button roles on the specified message
|
||||||
|
- `.btr excl` to toggle exclusive button roles (only 1 role per message or any number)
|
||||||
|
- Use `.h btr` for more info
|
||||||
|
- Added `.wrongsong` which will delete the last queued song.
|
||||||
|
- Useful in case you made a mistake, or the bot queued a wrong song
|
||||||
|
- It will reset after a shuffle or fairplay toggle, or similar events.
|
||||||
|
- Added Server color Commands!
|
||||||
|
- Every Server can now set their own colors for ok/error/pending embed (the default green/red/yellow color on the
|
||||||
|
left side of the message the bot sends)
|
||||||
|
- Use `.h .sclr` to see the list of commands
|
||||||
|
- `.sclr show` will show the current server colors
|
||||||
|
- `.sclr ok <color hex>` to set ok color
|
||||||
|
- `.sclr warn <color hex>` to set warn color
|
||||||
|
- `.sclr error <color hex>` to set error color
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Self Assigned Roles reworked! Use `.h .sar` for the list of commands
|
||||||
|
- `.sar autodel`
|
||||||
|
- Toggles the automatic deletion of the user's message and Nadeko's confirmations for .iam and .iamn commands.
|
||||||
|
- `.sar ad`
|
||||||
|
- Adds a role to the list of self-assignable roles. You can also specify a group.
|
||||||
|
- If 'Exclusive self-assignable roles' feature is enabled (.sar exclusive), users will be able to pick one role
|
||||||
|
per group.
|
||||||
|
- `.sar groupname`
|
||||||
|
- Sets a self assignable role group name. Provide no name to remove.
|
||||||
|
- `.sar remove`
|
||||||
|
- Removes a specified role from the list of self-assignable roles.
|
||||||
|
- `.sar list`
|
||||||
|
- Lists self-assignable roles. Shows 20 roles per page.
|
||||||
|
- `.sar exclusive`
|
||||||
|
- Toggles whether self-assigned roles are exclusive. While enabled, users can only have one self-assignable role
|
||||||
|
per group.
|
||||||
|
- `.sar rolelvlreq`
|
||||||
|
- Set a level requirement on a self-assignable role.
|
||||||
|
- `.sar grouprolereq`
|
||||||
|
- Set a role that users have to have in order to assign a self-assignable role from the specified group.
|
||||||
|
- `.sar groupdelete`
|
||||||
|
- Deletes a self-assignable role group
|
||||||
|
- `.iam` and `.iamn` are unchanged
|
||||||
|
- Removed patron limits from Reaction Roles. Anyone can have as many reros as they like.
|
||||||
|
- `.timely` captcha made stronger and cached per user.
|
||||||
|
- `.bsreset` price reduced by 90%
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed `.sinfo` for servers on other shard
|
||||||
|
|
||||||
|
## [5.1.20] - 10.11.2024
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `.rakeback` command, get a % of house edge back as claimable currency
|
||||||
|
- Added `.snipe` command to quickly get a copy of a posted message as an embed
|
||||||
|
- You can reply to a message to snipe that message
|
||||||
|
- Or just type .snipe and the bot will snipe the last message in the channel with content or image
|
||||||
|
- Added `.betstatsreset` / `.bsreset` command to reset your stats for a fee
|
||||||
|
- Added `.gamblestatsreset` / `.gsreset` owner-only command to reset bot stats for all games
|
||||||
|
- Added `.waifuclaims` command which lists all of your claimed waifus
|
||||||
|
- Added and changed `%bot.time%` and `%bot.date%` placeholders. They use timestamp tags now
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `.divorce` no longer has a cooldown
|
||||||
|
- `.betroll` has a 2% better payout
|
||||||
|
- `.slot` payout balanced out (less volatile), reduced jackpot win but increased other wins,
|
||||||
|
- now has a new symbol, wheat
|
||||||
|
- worse around 1% in total (now shares the top spot with .bf)
|
||||||
|
|
||||||
## [5.1.19] - 04.11.2024
|
## [5.1.19] - 04.11.2024
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Config
|
# Config
|
||||||
`.config` is the new `.bce`, it gives you a fast and easy way to edit most bot settings/values. Use `.h .config` for explanation.
|
`.config` gives you a fast and easy way to edit most bot settings/values. Use `.h .config` for explanation.
|
||||||
|
|
||||||
Use `.config` to see the list of editable config files
|
Use `.config` to see the list of editable config files
|
||||||
Use `.config <config-name>` to see the list of settable properties on that config
|
Use `.config <config-name>` to see the list of settable properties on that config
|
||||||
|
@@ -1,27 +1,30 @@
|
|||||||
## Creds Guide
|
## Creds Guide
|
||||||
|
|
||||||
This document aims to guide you through the process of creating a Discord account for your bot
|
This guide will show you how to create your own discord bot, invite it to your server, and copy it's credentials to your `creds.yml` in order to run your bot.
|
||||||
(the Discord Bot application), and inviting that account into your Discord server.
|
|
||||||
|
- Start by opening your creds.yml
|
||||||
|
- If you're on a windows installer version, click on the creds button next to your bot's RUN button.
|
||||||
|
- If you're on linux from source or windows from source version, open `nadekobot/output/creds.yml`. Please use visual studio code, notepad++ or another code editor. Usage of notepad is discouraged.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Go to [the Discord developer application page][DiscordApp].
|
1. Go to [the Discord developer application page][DiscordApp].
|
||||||
- Log in with your Discord account.
|
2. Log in with your Discord account.
|
||||||
- Click **New Application**.
|
3. Click **New Application**.
|
||||||
- Fill out the `Name` field however you like.
|
3. Fill out the `Name` field however you like, accept the terms, and confirm.
|
||||||
- Go to the **Bot** tab on the left sidebar.
|
1. Go to the **Bot** tab on the left sidebar.
|
||||||
- Click on the `Add a Bot` button and confirm that you do want to add a bot to this app.
|
1. Click on the `Add a Bot` button and confirm that you do want to add a bot to this app.
|
||||||
- **Optional:** Add bot's avatar and description.
|
1. **Optional:** Add bot's avatar and description.
|
||||||
- Copy your Token to `creds.yml` as shown above.
|
1. Copy your Token to `creds.yml` as shown above.
|
||||||
- Scroll down to the **`Privileged Gateway Intents`** section
|
1. Scroll down to the **`Privileged Gateway Intents`** section
|
||||||
- **Enable the following:**
|
- Enable the following:
|
||||||
- **PRESENCE INTENT**
|
- **PRESENCE INTENT**
|
||||||
- **SERVER MEMBERS INTENT**
|
- **SERVER MEMBERS INTENT**
|
||||||
- **MESSAGE CONTENT INTENT**
|
- **MESSAGE CONTENT INTENT**
|
||||||
|
|
||||||
These are required for a number of features to function properly, and all should be on.
|
These are required for a number of features to function properly, and all should be on.
|
||||||
|
|
||||||
##### Getting Owner ID*(s)*:
|
##### Getting Owner ID
|
||||||
|
|
||||||
- Go to your Discord server and attempt to mention yourself, but put a backslash at the start
|
- Go to your Discord server and attempt to mention yourself, but put a backslash at the start
|
||||||
*(to make it slightly easier, add the backslash after the mention has been typed)*.
|
*(to make it slightly easier, add the backslash after the mention has been typed)*.
|
||||||
|
@@ -7,10 +7,9 @@ Donations go a long way in helping us keep the project alive, and we appreciate
|
|||||||
|
|
||||||
Donating to us also gives you the following benefits:
|
Donating to us also gives you the following benefits:
|
||||||
|
|
||||||
- A hoisted **Donators role** in our [Discord server][discord-server]
|
- A hoisted **Patron** role in [Nadeko Discord server][discord-server]
|
||||||
- Access to exclusive **#noticed** text and voice channels
|
- Access to exclusive **#noticed** text and voice channels
|
||||||
- **1000 flowers** on the public bot per dollar donated (after fees)
|
- **3000 flowers** on the public bot per dollar donated (after fees)
|
||||||
- **Expressions** on the public bot for [Patreon pledges][patreon] of $5 or higher
|
|
||||||
|
|
||||||
## Patreon
|
## Patreon
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ You can set up a monthly pledge on [Patreon][patreon] and support the project's
|
|||||||
You can also donate to us through [PayPal][paypal] for one-time donations using the button below, or by donating to `nadekodiscordbot@gmail.com`.
|
You can also donate to us through [PayPal][paypal] for one-time donations using the button below, or by donating to `nadekodiscordbot@gmail.com`.
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
Mention your Discord tag (Username#1234) in the payment note to receive flower rewards.
|
Mention your Discord username or user id in the payment note to receive flower rewards.
|
||||||
|
|
||||||
[![img][paypal-button]][paypal]
|
[![img][paypal-button]][paypal]
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
## Setting Up NadekoBot on Windows With the Updater
|
## Setting Up NadekoBot on Windows With the Updater
|
||||||
|
|
||||||
| Table of Contents|
|
| Table of Contents|
|
||||||
| :---------------------------------------------------------------------------------------------------------------------------|
|
| :-|
|
||||||
| [Prerequisites](#prerequisites) |
|
| [Prerequisites](#prerequisites) |
|
||||||
| [Setup](#setup) |
|
| [Setup](#setup) |
|
||||||
| [Starting the Bot](#starting-the-bot) |
|
| [Starting the Bot](#starting-the-bot) |
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
|
|
||||||
- Windows 10 or later (64-bit)
|
- Windows 10 or later (64-bit)
|
||||||
- [Create a Discord Bot application and invite the bot to your server](../creds-guide.md)
|
|
||||||
|
|
||||||
**Optional**
|
**Optional**
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@
|
|||||||

|

|
||||||
- Click on **`DOWNLOAD`** at the lower right
|
- Click on **`DOWNLOAD`** at the lower right
|
||||||

|

|
||||||
- **Note: Redis is optional. install Redis manually here: [Redis] Download and run the **`.msi`** file.**
|
- If you want to use the music module, click on **`Install`** next to **`FFMPEG`** and **`Youtube-DLP`**.
|
||||||
- If you will use the music module, click on **`Install`** next to **`FFMPEG`** and **`Youtube-DLP`**.
|
|
||||||
- If any dependencies fail to install, you can temporarily disable your Windows Defender/AV until you install them. If you don't want to, then read [the last section of this guide](#Manual-Prerequisite-Installation).
|
- If any dependencies fail to install, you can temporarily disable your Windows Defender/AV until you install them. If you don't want to, then read [the last section of this guide](#Manual-Prerequisite-Installation).
|
||||||
- When installation is finished, click on **`CREDS`** to the left of **`RUN`** at the lower right.
|
- When installation is finished, click on **`CREDS`** to the left of **`RUN`** at the lower right.
|
||||||
- Follow the guide on how to [Set up the creds.yml](../../creds-guide) file.
|
- Follow the guide on how to [Set up the creds.yml](../../creds-guide) file.
|
||||||
@@ -39,8 +37,6 @@
|
|||||||
|
|
||||||
- Either click on **`RUN`** button in the updater or run the bot via its desktop shortcut.
|
- Either click on **`RUN`** button in the updater or run the bot via its desktop shortcut.
|
||||||
|
|
||||||
### If you get a "No owner channels created..." message. Please follow the creds guide again [**HERE**](../../creds-guide).
|
|
||||||
|
|
||||||
#### Updating Nadeko
|
#### Updating Nadeko
|
||||||
|
|
||||||
- Make sure Nadeko is closed and not running
|
- Make sure Nadeko is closed and not running
|
||||||
@@ -52,71 +48,6 @@
|
|||||||
- Launch the bot
|
- Launch the bot
|
||||||
- You've updated and are running again, easy as that!
|
- You've updated and are running again, easy as that!
|
||||||
|
|
||||||
#### Manual Prerequisite Installation
|
|
||||||
|
|
||||||
You can still install them manually:
|
|
||||||
|
|
||||||
- [Redis] (OPTIONAL) - Download and run the **`.msi`** file
|
|
||||||
- [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, then just move the `ffmpeg.exe` file to NadekoBot/system
|
|
||||||
- [youtube-dlp] - Click to download the `yt-dlp.exe` file then put `yt-dlp.exe` in a path that's in your PATH environment variable. If you don't know what that is, then just move the `yt-dlp.exe` file to NadekoBot/system
|
|
||||||
|
|
||||||
## **⚠ IF YOU ARE FOLLOWING THE GUIDE ABOVE, IGNORE THIS SECTION ⚠**
|
|
||||||
|
|
||||||
### Windows From Source
|
|
||||||
|
|
||||||
##### Prerequisites
|
|
||||||
|
|
||||||
**Install these before proceeding or your bot will not work!**
|
|
||||||
- [.net 8](https://dotnet.microsoft.com/en-us/download) - needed to compile and run the bot
|
|
||||||
- [git](https://git-scm.com/downloads) - needed to clone the repository (you can also download the zip manually and extract it, but this guide assumes you're using git)
|
|
||||||
- [Redis] (OPTIONAL)- to cache things needed by some features and persist through restarts
|
|
||||||
|
|
||||||
##### Installation Instructions
|
|
||||||
|
|
||||||
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and navigate to the location where you want to install the bot (for example `cd ~/Desktop/`)
|
|
||||||
|
|
||||||
1. `git clone https://gitlab.com/kwoth/nadekobot -b v5 --depth 1`
|
|
||||||
2. `cd nadekobot`
|
|
||||||
3. `dotnet publish -c Release -o output/ src/NadekoBot/`
|
|
||||||
4. `cd output`
|
|
||||||
5. `cp creds_example.yml creds.yml`
|
|
||||||
6. Open `creds.yml` with your favorite text editor (Please don't use Notepad or WordPad. You can use Notepad++, VSCode, Atom, Sublime, or something similar)
|
|
||||||
7. [Enter your bot's token](#creds-guide)
|
|
||||||
8. Run the bot `dotnet NadekoBot.dll`
|
|
||||||
9. 🎉
|
|
||||||
|
|
||||||
##### Update Instructions
|
|
||||||
|
|
||||||
Open PowerShell as described above and run the following commands:
|
|
||||||
|
|
||||||
1. Stop the bot
|
|
||||||
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
|
|
||||||
2. Navigate to your bot's folder, example:
|
|
||||||
- `cd ~/Desktop/nadekobot`
|
|
||||||
3. Pull the new version, and make sure you're on the v5 branch
|
|
||||||
- *⚠️ the first 3 lines can be omitted if you're already on v5. If you're updating from v4, you must run them*
|
|
||||||
- `git remote set-branches origin '*'`
|
|
||||||
- `git fetch -v --depth=1`
|
|
||||||
- `git checkout v5`
|
|
||||||
- `git pull`
|
|
||||||
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
|
|
||||||
4. **Backup** old output in case your data is overwritten
|
|
||||||
- `cp -r -fo output/ output-old`
|
|
||||||
5. Build the bot again
|
|
||||||
- `dotnet publish -c Release -o output/ src/NadekoBot/`
|
|
||||||
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
|
|
||||||
- ⚠ If you've modified said files, back them up instead
|
|
||||||
- `rm output-old/data/aliases.yml`
|
|
||||||
- `rm -r output-old/data/strings`
|
|
||||||
7. Copy old data
|
|
||||||
- `cp -Recurse .\output-old\data\ .\output\ -Force`
|
|
||||||
8. Copy creds.yml
|
|
||||||
- `cp output-old/creds.yml output/`
|
|
||||||
9. Run the bot
|
|
||||||
- `cd output`
|
|
||||||
- `dotnet NadekoBot.dll`
|
|
||||||
|
|
||||||
🎉 Enjoy
|
|
||||||
|
|
||||||
#### Music prerequisites
|
#### Music prerequisites
|
||||||
In order to use music commands, you need ffmpeg and yt-dlp installed.
|
In order to use music commands, you need ffmpeg and yt-dlp installed.
|
||||||
|
77
docs/guides/windows-source-guide.md
Normal file
77
docs/guides/windows-source-guide.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
## Setting Up NadekoBot on Windows from source
|
||||||
|
|
||||||
|
1. Prerequisites
|
||||||
|
|
||||||
|
- Windows 10 or later (64-bit)
|
||||||
|
- [.net 8 sdk](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||||
|
- If you want nadeko to play music: [Visual C++ 2010 (x86)] and [Visual C++ 2017 (x64)] (both are required, you may install them later)
|
||||||
|
- [git](https://git-scm.com/downloads) - needed to clone the repository (you can also download the zip manually and extract it, but this guide assumes you're using git)
|
||||||
|
- **Optional** Any code editor, for example [Visual Studio Code](https://code.visualstudio.com/Download)
|
||||||
|
- You'll need to at least modify creds.yml, notepad is inadequate
|
||||||
|
|
||||||
|
|
||||||
|
##### Installation Instructions
|
||||||
|
|
||||||
|
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and
|
||||||
|
|
||||||
|
|
||||||
|
0. Navigate to the location where you want to install the bot
|
||||||
|
- for example, type `cd ~/Desktop/` and press enter
|
||||||
|
1. `git clone https://gitlab.com/kwoth/nadekobot -b v5 --depth 1`
|
||||||
|
2. `cd nadekobot`
|
||||||
|
3. `dotnet publish -c Release -o output/ src/NadekoBot/`
|
||||||
|
4. `cd output`
|
||||||
|
5. `cp creds_example.yml creds.yml`
|
||||||
|
6. "You're done installing, you may now proceed to set up your bot's credentials by following the [#creds-guide]
|
||||||
|
- Once done, come back here and run the last command
|
||||||
|
8. Run the bot `dotnet NadekoBot.dll`
|
||||||
|
9. 🎉 Enjoy
|
||||||
|
|
||||||
|
##### Update Instructions
|
||||||
|
|
||||||
|
Open PowerShell as described above and run the following commands:
|
||||||
|
|
||||||
|
1. Stop the bot
|
||||||
|
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
|
||||||
|
2. Navigate to your bot's folder, example:
|
||||||
|
- `cd ~/Desktop/nadekobot`
|
||||||
|
3. Pull the new version, and make sure you're on the v5 branch
|
||||||
|
- *⚠️ If you're on v4, you must run these commands, if not, you may skip them.*
|
||||||
|
- `git remote set-branches origin '*'`
|
||||||
|
- `git fetch -v --depth=1`
|
||||||
|
- `git checkout v5`
|
||||||
|
- `git pull`
|
||||||
|
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
|
||||||
|
4. **Backup** old output in case your data is overwritten
|
||||||
|
- `cp -r -fo output/ output-old`
|
||||||
|
5. Build the bot again
|
||||||
|
- `dotnet publish -c Release -o output/ src/NadekoBot/`
|
||||||
|
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
|
||||||
|
- ⚠ If you've modified said files, back them up instead
|
||||||
|
- `rm output-old/data/aliases.yml`
|
||||||
|
- `rm -r output-old/data/strings`
|
||||||
|
7. Copy old data, and new strings
|
||||||
|
- `cp -Recurse -Force .\output-old\data\ .\output\`
|
||||||
|
- `cp -Recurse -Force src/NadekoBot/data/strings/ output/data/`
|
||||||
|
8. Copy creds.yml
|
||||||
|
- `cp output-old/creds.yml output/`
|
||||||
|
9. Run the bot
|
||||||
|
- `cd output`
|
||||||
|
- `dotnet NadekoBot.dll`
|
||||||
|
|
||||||
|
🎉 Enjoy
|
||||||
|
|
||||||
|
#### Music prerequisites
|
||||||
|
In order to use music commands, you need ffmpeg and yt-dlp installed.
|
||||||
|
- [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `ffmpeg.exe` file to `NadekoBot/output`.
|
||||||
|
- [youtube-dlp] - Click to download the `yt-dlp.exe` file, then move `yt-dlp.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `yt-dlp.exe` file to `NadekoBot/system`.
|
||||||
|
|
||||||
|
[Updater]: https://dl.nadeko.bot/v3/
|
||||||
|
[Notepad++]: https://notepad-plus-plus.org/
|
||||||
|
[.net]: https://dotnet.microsoft.com/download/dotnet/8.0
|
||||||
|
[Redis]: https://github.com/MicrosoftArchive/redis/releases/download/win-3.0.504/Redis-x64-3.0.504.msi
|
||||||
|
[Visual C++ 2010 (x86)]: https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe
|
||||||
|
[Visual C++ 2017 (x64)]: https://aka.ms/vs/15/release/vc_redist.x64.exe
|
||||||
|
[ffmpeg-32bit]: https://cdn.nadeko.bot/dl/ffmpeg-32.zip
|
||||||
|
[ffmpeg-64bit]: https://cdn.nadeko.bot/dl/ffmpeg-64.zip
|
||||||
|
[youtube-dlp]: https://github.com/yt-dlp/yt-dlp/releases
|
@@ -16,6 +16,7 @@ To self-host your own Nadeko, use the guides below:
|
|||||||
- [:material-microsoft-windows: Windows guide][windows-guide]
|
- [:material-microsoft-windows: Windows guide][windows-guide]
|
||||||
- [:material-linux: Linux guide][linux-guide]
|
- [:material-linux: Linux guide][linux-guide]
|
||||||
- [:material-apple: Mac OS guide][macos-guide]
|
- [:material-apple: Mac OS guide][macos-guide]
|
||||||
|
- [:material-microsoft-windows: Windows (from source) guide][windows-source-guide]
|
||||||
|
|
||||||
In case you need any help, join our [Discord server][discord-server] where we may provide support.
|
In case you need any help, join our [Discord server][discord-server] where we may provide support.
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ If you're unsure whether something is an issue, ask in our support server first.
|
|||||||
[invite]: https://invite.nadeko.bot/
|
[invite]: https://invite.nadeko.bot/
|
||||||
[commands]: https://nadeko.bot/commands/
|
[commands]: https://nadeko.bot/commands/
|
||||||
[windows-guide]: ./guides/windows-guide.md
|
[windows-guide]: ./guides/windows-guide.md
|
||||||
|
[windows-source-guide]: ./guides/windows-source-guide.md
|
||||||
[linux-guide]: ./guides/linux-guide.md
|
[linux-guide]: ./guides/linux-guide.md
|
||||||
[macos-guide]: ./guides/osx-guide.md
|
[macos-guide]: ./guides/osx-guide.md
|
||||||
[from-source-guide]: ./guides/from-source.md
|
[from-source-guide]: ./guides/from-source.md
|
||||||
|
@@ -75,6 +75,7 @@ nav:
|
|||||||
- Windows Guide: guides/windows-guide.md
|
- Windows Guide: guides/windows-guide.md
|
||||||
- Linux Guide: guides/linux-guide.md
|
- Linux Guide: guides/linux-guide.md
|
||||||
- OSX Guide: guides/osx-guide.md
|
- OSX Guide: guides/osx-guide.md
|
||||||
|
- Windows Guide (from source): guides/windows-source-guide.md
|
||||||
- Docker Guide (unsupported): guides/docker-guide.md
|
- Docker Guide (unsupported): guides/docker-guide.md
|
||||||
- Commands:
|
- Commands:
|
||||||
- Readme: commands-readme.md
|
- Readme: commands-readme.md
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net.Core" Version="3.15.3" />
|
<PackageReference Include="Discord.Net.Core" Version="3.16.0" />
|
||||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||||
<PackageReference Include="YamlDotNet" Version="15.1.4" />
|
<PackageReference Include="YamlDotNet" Version="15.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
88
src/NadekoBot.Tests/FishTests.cs
Normal file
88
src/NadekoBot.Tests/FishTests.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// using System;
|
||||||
|
// using System.Collections.Generic;
|
||||||
|
// using System.Diagnostics;
|
||||||
|
// using System.IO;
|
||||||
|
// using System.Linq;
|
||||||
|
// using Nadeko.Common;
|
||||||
|
// using NadekoBot.Modules.Games;
|
||||||
|
// using NUnit.Framework;
|
||||||
|
//
|
||||||
|
// namespace NadekoBot.Tests;
|
||||||
|
//
|
||||||
|
// public class FishTests
|
||||||
|
// {
|
||||||
|
// [Test]
|
||||||
|
// public void TestWeather()
|
||||||
|
// {
|
||||||
|
// var fs = new FishService(null, null);
|
||||||
|
//
|
||||||
|
// var rng = new Random();
|
||||||
|
//
|
||||||
|
// // output = @"ro+dD:bN0uVqV3ZOAv6r""EFeA'A]u]uSyz2Qd'r#0Vf:5zOX\VgSsF8LgRCL/uOW";
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// var output = "";
|
||||||
|
// for (var i = 0; i < 64; i++)
|
||||||
|
// {
|
||||||
|
// var c = (char)rng.Next(33, 123);
|
||||||
|
// output += c;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// output = "";
|
||||||
|
// var weathers = new List<FishingWeather>();
|
||||||
|
// for (var i = 0; i < 1_000_000; i++)
|
||||||
|
// {
|
||||||
|
// var w = fs.GetWeather(DateTime.UtcNow.AddHours(6 * i), output);
|
||||||
|
// weathers.Add(w);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var vals = weathers.GroupBy(x => x)
|
||||||
|
// .ToDictionary(x => x.Key, x => x.Count());
|
||||||
|
//
|
||||||
|
// var str = weathers.Select(x => (int)x).Join("");
|
||||||
|
// var maxLength = MaxLength(str);
|
||||||
|
//
|
||||||
|
// if (maxLength < 12)
|
||||||
|
// {
|
||||||
|
// foreach (var v in vals)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine($"{v.Key}: {v.Value}");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Console.WriteLine(output);
|
||||||
|
// Console.WriteLine(maxLength);
|
||||||
|
//
|
||||||
|
// File.WriteAllText("data.txt", weathers.Select(x => (int)x).Join(""));
|
||||||
|
//
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // string with same characters
|
||||||
|
// static int MaxLength(String s)
|
||||||
|
// {
|
||||||
|
// int ans = 1, temp = 1;
|
||||||
|
//
|
||||||
|
// // Traverse the string
|
||||||
|
// for (int i = 1; i < s.Length; i++)
|
||||||
|
// {
|
||||||
|
// // If character is same as
|
||||||
|
// // previous increment temp value
|
||||||
|
// if (s[i] == s[i - 1])
|
||||||
|
// {
|
||||||
|
// ++temp;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// ans = Math.Max(ans, temp);
|
||||||
|
// temp = 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ans = Math.Max(ans, temp);
|
||||||
|
//
|
||||||
|
// // Return the required answer
|
||||||
|
// return ans;
|
||||||
|
// }
|
||||||
|
// }
|
@@ -1,4 +1,5 @@
|
|||||||
using Nadeko.Common;
|
using System;
|
||||||
|
using Nadeko.Common;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace NadekoBot.Tests
|
namespace NadekoBot.Tests
|
||||||
@@ -120,5 +121,12 @@ namespace NadekoBot.Tests
|
|||||||
num = new kwum(int.MaxValue);
|
num = new kwum(int.MaxValue);
|
||||||
Assert.AreEqual("3zzzzzz", num.ToString());
|
Assert.AreEqual("3zzzzzz", num.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPower()
|
||||||
|
{
|
||||||
|
var num = new kwum((int)Math.Pow(32, 2));
|
||||||
|
Assert.AreEqual("322", num.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -357,3 +357,4 @@ resharper_arrange_redundant_parentheses_highlighting = hint
|
|||||||
# IDE0011: Add braces
|
# IDE0011: Add braces
|
||||||
dotnet_diagnostic.IDE0011.severity = warning
|
dotnet_diagnostic.IDE0011.severity = warning
|
||||||
|
|
||||||
|
resharper_arrange_type_member_modifiers_highlighting = hint
|
@@ -218,12 +218,12 @@ public sealed class Bot : IBot
|
|||||||
catch (HttpException ex)
|
catch (HttpException ex)
|
||||||
{
|
{
|
||||||
LoginErrorHandler.Handle(ex);
|
LoginErrorHandler.Handle(ex);
|
||||||
Helpers.ReadErrorAndExit(3);
|
Helpers.ReadErrorAndExit(101);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LoginErrorHandler.Handle(ex);
|
LoginErrorHandler.Handle(ex);
|
||||||
Helpers.ReadErrorAndExit(4);
|
Helpers.ReadErrorAndExit(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
await clientReady.Task.ConfigureAwait(false);
|
await clientReady.Task.ConfigureAwait(false);
|
||||||
@@ -275,7 +275,7 @@ public sealed class Bot : IBot
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Error adding services");
|
Log.Error(ex, "Error adding services");
|
||||||
Helpers.ReadErrorAndExit(9);
|
Helpers.ReadErrorAndExit(103);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s",
|
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s",
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
#nullable disable
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NadekoBot.Db.Models;
|
|
||||||
|
|
||||||
namespace NadekoBot.Db;
|
|
||||||
|
|
||||||
public static class SelfAssignableRolesExtensions
|
|
||||||
{
|
|
||||||
public static bool DeleteByGuildAndRoleId(this DbSet<SelfAssignedRole> roles, ulong guildId, ulong roleId)
|
|
||||||
{
|
|
||||||
var role = roles.FirstOrDefault(s => s.GuildId == guildId && s.RoleId == roleId);
|
|
||||||
|
|
||||||
if (role is null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
roles.Remove(role);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IReadOnlyCollection<SelfAssignedRole> GetFromGuild(this DbSet<SelfAssignedRole> roles, ulong guildId)
|
|
||||||
=> roles.AsQueryable().Where(s => s.GuildId == guildId).ToArray();
|
|
||||||
}
|
|
@@ -1,5 +1,4 @@
|
|||||||
#nullable disable
|
using LinqToDB;
|
||||||
using LinqToDB;
|
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
@@ -8,6 +7,9 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public static class UserXpExtensions
|
public static class UserXpExtensions
|
||||||
{
|
{
|
||||||
|
public static async Task<UserXpStats?> GetGuildUserXp(this ITable<UserXpStats> table, ulong guildId, ulong userId)
|
||||||
|
=> await table.FirstOrDefaultAsyncLinqToDB(x => x.GuildId == guildId && x.UserId == userId);
|
||||||
|
|
||||||
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
|
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
|
||||||
{
|
{
|
||||||
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
||||||
@@ -18,7 +20,6 @@ public static class UserXpExtensions
|
|||||||
{
|
{
|
||||||
Xp = 0,
|
Xp = 0,
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
NotifyOnLevelUp = XpNotificationLocation.None,
|
|
||||||
GuildId = guildId
|
GuildId = guildId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -29,17 +30,17 @@ public static class UserXpExtensions
|
|||||||
public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
||||||
=> await xps.ToLinqToDBTable()
|
=> await xps.ToLinqToDBTable()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
.OrderByDescending(x => x.Xp)
|
||||||
.Take(count)
|
.Take(count)
|
||||||
.ToListAsyncLinqToDB();
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
public static async Task<int> GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
public static async Task<int> GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
||||||
=> await xps.ToLinqToDBTable()
|
=> await xps.ToLinqToDBTable()
|
||||||
.Where(x => x.GuildId == guildId
|
.Where(x => x.GuildId == guildId
|
||||||
&& x.Xp + x.AwardedXp
|
&& x.Xp
|
||||||
> xps.AsQueryable()
|
> xps.AsQueryable()
|
||||||
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
||||||
.Select(y => y.Xp + y.AwardedXp)
|
.Select(y => y.Xp)
|
||||||
.FirstOrDefault())
|
.FirstOrDefault())
|
||||||
.CountAsyncLinqToDB()
|
.CountAsyncLinqToDB()
|
||||||
+ 1;
|
+ 1;
|
||||||
@@ -51,6 +52,6 @@ public static class UserXpExtensions
|
|||||||
=> await userXp
|
=> await userXp
|
||||||
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
.FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
|
.FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
|
||||||
? new(uxs.Xp + uxs.AwardedXp)
|
? new(uxs.Xp)
|
||||||
: new(0);
|
: new(0);
|
||||||
}
|
}
|
@@ -4,38 +4,28 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public readonly struct LevelStats
|
public readonly struct LevelStats
|
||||||
{
|
{
|
||||||
public const int XP_REQUIRED_LVL_1 = 36;
|
|
||||||
|
|
||||||
public long Level { get; }
|
public long Level { get; }
|
||||||
public long LevelXp { get; }
|
public long LevelXp { get; }
|
||||||
public long RequiredXp { get; }
|
public long RequiredXp { get; }
|
||||||
public long TotalXp { get; }
|
public long TotalXp { get; }
|
||||||
|
|
||||||
public LevelStats(long xp)
|
public LevelStats(long totalXp)
|
||||||
{
|
{
|
||||||
if (xp < 0)
|
if (totalXp < 0)
|
||||||
xp = 0;
|
totalXp = 0;
|
||||||
|
|
||||||
TotalXp = xp;
|
TotalXp = totalXp;
|
||||||
|
Level = GetLevelByTotalXp(totalXp);
|
||||||
const int baseXp = XP_REQUIRED_LVL_1;
|
LevelXp = totalXp - GetTotalXpReqForLevel(Level);
|
||||||
|
RequiredXp = (9 * (Level + 1)) + 27;
|
||||||
var required = baseXp;
|
|
||||||
var totalXp = 0;
|
|
||||||
var lvl = 1;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
required = (int)(baseXp + (baseXp / 4.0 * (lvl - 1)));
|
|
||||||
|
|
||||||
if (required + totalXp > xp)
|
|
||||||
break;
|
|
||||||
|
|
||||||
totalXp += required;
|
|
||||||
lvl++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Level = lvl - 1;
|
public static LevelStats CreateForLevel(long level)
|
||||||
LevelXp = xp - totalXp;
|
=> new(GetTotalXpReqForLevel(level));
|
||||||
RequiredXp = required;
|
|
||||||
}
|
public static long GetTotalXpReqForLevel(long level)
|
||||||
|
=> ((9 * level * level) + (63 * level)) / 2;
|
||||||
|
|
||||||
|
public static long GetLevelByTotalXp(long totalXp)
|
||||||
|
=> (long)((-7.0 / 2) + (1 / 6.0 * Math.Sqrt((8 * totalXp) + 441)));
|
||||||
}
|
}
|
@@ -9,7 +9,8 @@ public class FollowedStream : DbEntity
|
|||||||
Picarto = 3,
|
Picarto = 3,
|
||||||
Youtube = 4,
|
Youtube = 4,
|
||||||
Facebook = 5,
|
Facebook = 5,
|
||||||
Trovo = 6
|
Trovo = 6,
|
||||||
|
Kick = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
#nullable disable
|
|
||||||
namespace NadekoBot.Db.Models;
|
|
||||||
|
|
||||||
public class GroupName : DbEntity
|
|
||||||
{
|
|
||||||
public int GuildConfigId { get; set; }
|
|
||||||
public GuildConfig GuildConfig { get; set; }
|
|
||||||
|
|
||||||
public int Number { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
}
|
|
@@ -5,14 +5,15 @@ namespace NadekoBot.Db.Models;
|
|||||||
public class GuildColors
|
public class GuildColors
|
||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
|
|
||||||
[Length(0, 9)]
|
[MaxLength(9)]
|
||||||
public string? OkColor { get; set; }
|
public string? OkColor { get; set; }
|
||||||
|
|
||||||
[Length(0, 9)]
|
[MaxLength(9)]
|
||||||
public string? ErrorColor { get; set; }
|
public string? ErrorColor { get; set; }
|
||||||
|
|
||||||
[Length(0, 9)]
|
[MaxLength(9)]
|
||||||
public string? PendingColor { get; set; }
|
public string? PendingColor { get; set; }
|
||||||
}
|
}
|
@@ -13,28 +13,11 @@ public class GuildConfig : DbEntity
|
|||||||
|
|
||||||
public string AutoAssignRoleIds { get; set; }
|
public string AutoAssignRoleIds { get; set; }
|
||||||
|
|
||||||
// //greet stuff
|
//todo FUTURE: DELETE, UNUSED
|
||||||
// public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
|
|
||||||
// public int AutoDeleteByeMessagesTimer { get; set; } = 30;
|
|
||||||
//
|
|
||||||
// public ulong GreetMessageChannelId { get; set; }
|
|
||||||
// public ulong ByeMessageChannelId { get; set; }
|
|
||||||
//
|
|
||||||
// public bool SendDmGreetMessage { get; set; }
|
|
||||||
// public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
|
||||||
//
|
|
||||||
// public bool SendChannelGreetMessage { get; set; }
|
|
||||||
// public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
|
||||||
//
|
|
||||||
// public bool SendChannelByeMessage { get; set; }
|
|
||||||
// public string ChannelByeMessageText { get; set; } = "%user% has left!";
|
|
||||||
// public bool SendBoostMessage { get; set; }
|
|
||||||
// pulic int BoostMessageDeleteAfter { get; set; }
|
|
||||||
|
|
||||||
//self assignable roles
|
|
||||||
public bool ExclusiveSelfAssignedRoles { get; set; }
|
public bool ExclusiveSelfAssignedRoles { get; set; }
|
||||||
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
|
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
|
||||||
|
|
||||||
|
|
||||||
//stream notifications
|
//stream notifications
|
||||||
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
|
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
|
||||||
|
|
||||||
@@ -53,29 +36,37 @@ public class GuildConfig : DbEntity
|
|||||||
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
|
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
|
||||||
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
|
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
|
||||||
|
|
||||||
//public bool FilterLinks { get; set; }
|
|
||||||
//public HashSet<FilterLinksChannelId> FilterLinksChannels { get; set; } = new HashSet<FilterLinksChannelId>();
|
|
||||||
|
|
||||||
public bool FilterWords { get; set; }
|
public bool FilterWords { get; set; }
|
||||||
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
|
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
|
||||||
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
|
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
|
||||||
|
|
||||||
|
// mute
|
||||||
public HashSet<MutedUserId> MutedUsers { get; set; } = new();
|
public HashSet<MutedUserId> MutedUsers { get; set; } = new();
|
||||||
|
|
||||||
public string MuteRoleName { get; set; }
|
public string MuteRoleName { get; set; }
|
||||||
|
|
||||||
|
// chatterbot
|
||||||
public bool CleverbotEnabled { get; set; }
|
public bool CleverbotEnabled { get; set; }
|
||||||
|
|
||||||
|
// protection
|
||||||
public AntiRaidSetting AntiRaidSetting { get; set; }
|
public AntiRaidSetting AntiRaidSetting { get; set; }
|
||||||
public AntiSpamSetting AntiSpamSetting { get; set; }
|
public AntiSpamSetting AntiSpamSetting { get; set; }
|
||||||
public AntiAltSetting AntiAltSetting { get; set; }
|
public AntiAltSetting AntiAltSetting { get; set; }
|
||||||
|
|
||||||
|
// time
|
||||||
public string Locale { get; set; }
|
public string Locale { get; set; }
|
||||||
public string TimeZoneId { get; set; }
|
public string TimeZoneId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
// timers
|
||||||
public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
|
public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
|
||||||
public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
|
public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
|
||||||
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
|
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
|
||||||
|
|
||||||
|
// vcrole
|
||||||
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
|
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
|
||||||
|
|
||||||
|
// aliases
|
||||||
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
|
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
|
||||||
public bool WarningsInitialized { get; set; }
|
public bool WarningsInitialized { get; set; }
|
||||||
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
|
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
|
||||||
@@ -91,15 +82,10 @@ public class GuildConfig : DbEntity
|
|||||||
public List<FeedSub> FeedSubs { get; set; } = new();
|
public List<FeedSub> FeedSubs { get; set; } = new();
|
||||||
public bool NotifyStreamOffline { get; set; }
|
public bool NotifyStreamOffline { get; set; }
|
||||||
public bool DeleteStreamOnlineMessage { get; set; }
|
public bool DeleteStreamOnlineMessage { get; set; }
|
||||||
public List<GroupName> SelfAssignableRoleGroupNames { get; set; }
|
|
||||||
public int WarnExpireHours { get; set; }
|
public int WarnExpireHours { get; set; }
|
||||||
public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
|
public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
|
||||||
|
|
||||||
public bool DisableGlobalExpressions { get; set; } = false;
|
public bool DisableGlobalExpressions { get; set; } = false;
|
||||||
|
|
||||||
#region Boost Message
|
|
||||||
|
|
||||||
public bool StickyRoles { get; set; }
|
public bool StickyRoles { get; set; }
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
24
src/NadekoBot/Db/Models/Notify.cs
Normal file
24
src/NadekoBot/Db/Models/Notify.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public class Notify
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
public NotifyType Type { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(10_000)]
|
||||||
|
public string Message { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NotifyType
|
||||||
|
{
|
||||||
|
LevelUp = 0,
|
||||||
|
Protection = 1, Prot = 1,
|
||||||
|
AddRoleReward = 2,
|
||||||
|
RemoveRoleReward = 3,
|
||||||
|
}
|
18
src/NadekoBot/Db/Models/SarGroup.cs
Normal file
18
src/NadekoBot/Db/Models/SarGroup.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public sealed class SarGroup
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int GroupNumber { get; set; }
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong? RoleReq { get; set; }
|
||||||
|
public ICollection<Sar> Roles { get; set; } = [];
|
||||||
|
public bool IsExclusive { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
27
src/NadekoBot/Db/Models/btnrole/ButtonRole.cs
Normal file
27
src/NadekoBot/Db/Models/btnrole/ButtonRole.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public sealed class ButtonRole
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string ButtonId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
public ulong MessageId { get; set; }
|
||||||
|
|
||||||
|
public int Position { get; set; }
|
||||||
|
public ulong RoleId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Emote { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string Label { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool Exclusive { get; set; }
|
||||||
|
}
|
@@ -1,11 +1,24 @@
|
|||||||
#nullable disable
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace NadekoBot.Db.Models;
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
public class SelfAssignedRole : DbEntity
|
public sealed class Sar
|
||||||
{
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
public ulong RoleId { get; set; }
|
public ulong RoleId { get; set; }
|
||||||
|
|
||||||
public int Group { get; set; }
|
public int SarGroupId { get; set; }
|
||||||
public int LevelRequirement { get; set; }
|
public int LevelReq { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SarAutoDelete
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public bool IsEnabled { get; set; } = false;
|
||||||
}
|
}
|
12
src/NadekoBot/Db/Models/roles/TempRole.cs
Normal file
12
src/NadekoBot/Db/Models/roles/TempRole.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public class TempRole
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public bool Remove { get; set; }
|
||||||
|
public ulong RoleId { get; set; }
|
||||||
|
public ulong UserId { get; set; }
|
||||||
|
|
||||||
|
public DateTime ExpiresAt { get; set; }
|
||||||
|
}
|
@@ -6,6 +6,4 @@ public class UserXpStats : DbEntity
|
|||||||
public ulong UserId { get; set; }
|
public ulong UserId { get; set; }
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
public long Xp { get; set; }
|
public long Xp { get; set; }
|
||||||
public long AwardedXp { get; set; }
|
|
||||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
namespace NadekoBot.Db.Models;
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
public enum XpNotificationLocation
|
public enum XpNotificationLocation
|
||||||
{
|
{
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
public DbSet<Quote> Quotes { get; set; }
|
public DbSet<Quote> Quotes { get; set; }
|
||||||
public DbSet<Reminder> Reminders { get; set; }
|
public DbSet<Reminder> Reminders { get; set; }
|
||||||
public DbSet<SelfAssignedRole> SelfAssignableRoles { get; set; }
|
|
||||||
public DbSet<MusicPlaylist> MusicPlaylists { get; set; }
|
public DbSet<MusicPlaylist> MusicPlaylists { get; set; }
|
||||||
public DbSet<NadekoExpression> Expressions { get; set; }
|
public DbSet<NadekoExpression> Expressions { get; set; }
|
||||||
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
|
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
|
||||||
@@ -74,19 +74,122 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#region UserBetStats
|
// load all entities from current assembly
|
||||||
|
modelBuilder.ApplyConfigurationsFromAssembly(typeof(NadekoContext).Assembly);
|
||||||
|
|
||||||
modelBuilder.Entity<UserBetStats>()
|
#region Notify
|
||||||
.HasIndex(x => new { x.UserId, x.Game })
|
|
||||||
|
modelBuilder.Entity<Notify>(e =>
|
||||||
|
{
|
||||||
|
e.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
Event = x.Type
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TempRoles
|
||||||
|
|
||||||
|
modelBuilder.Entity<TempRole>(e =>
|
||||||
|
{
|
||||||
|
e.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.UserId,
|
||||||
|
x.RoleId
|
||||||
|
});
|
||||||
|
|
||||||
|
e.HasIndex(x => x.ExpiresAt);
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GuildColors
|
||||||
|
|
||||||
|
modelBuilder.Entity<GuildColors>()
|
||||||
|
.HasIndex(x => x.GuildId)
|
||||||
|
.IsUnique(true);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Button Roles
|
||||||
|
|
||||||
|
modelBuilder.Entity<ButtonRole>(br =>
|
||||||
|
{
|
||||||
|
br.HasIndex(x => x.GuildId)
|
||||||
|
.IsUnique(false);
|
||||||
|
|
||||||
|
br.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.RoleId,
|
||||||
|
x.MessageId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region New Sar
|
||||||
|
|
||||||
|
modelBuilder.Entity<SarGroup>(sg =>
|
||||||
|
{
|
||||||
|
sg.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.GroupNumber
|
||||||
|
});
|
||||||
|
|
||||||
|
sg.HasMany(x => x.Roles)
|
||||||
|
.WithOne()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<Sar>()
|
||||||
|
.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.RoleId
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<SarAutoDelete>()
|
||||||
|
.HasIndex(x => x.GuildId)
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Rakeback
|
||||||
|
|
||||||
|
modelBuilder.Entity<Rakeback>()
|
||||||
|
.HasKey(x => x.UserId);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UserBetStats
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserBetStats>(ubs =>
|
||||||
|
{
|
||||||
|
ubs.HasIndex(x => new
|
||||||
|
{
|
||||||
|
x.UserId,
|
||||||
|
x.Game
|
||||||
|
})
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
ubs.HasIndex(x => x.MaxWin)
|
||||||
|
.IsUnique(false);
|
||||||
|
});
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Flag Translate
|
#region Flag Translate
|
||||||
|
|
||||||
modelBuilder.Entity<FlagTranslateChannel>()
|
modelBuilder.Entity<FlagTranslateChannel>()
|
||||||
.HasIndex(x => new { x.GuildId, x.ChannelId })
|
.HasIndex(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.ChannelId
|
||||||
|
})
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -279,11 +382,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
.HasForeignKey(x => x.GuildConfigId)
|
.HasForeignKey(x => x.GuildConfigId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
modelBuilder.Entity<GuildConfig>()
|
|
||||||
.HasMany(x => x.SelfAssignableRoleGroupNames)
|
|
||||||
.WithOne()
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
|
|
||||||
modelBuilder.Entity<FeedSub>()
|
modelBuilder.Entity<FeedSub>()
|
||||||
.HasAlternateKey(x => new
|
.HasAlternateKey(x => new
|
||||||
{
|
{
|
||||||
@@ -312,21 +410,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Self Assignable Roles
|
|
||||||
|
|
||||||
var selfassignableRolesEntity = modelBuilder.Entity<SelfAssignedRole>();
|
|
||||||
|
|
||||||
selfassignableRolesEntity.HasIndex(s => new
|
|
||||||
{
|
|
||||||
s.GuildId,
|
|
||||||
s.RoleId
|
|
||||||
})
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
selfassignableRolesEntity.Property(x => x.Group).HasDefaultValue(0);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region MusicPlaylists
|
#region MusicPlaylists
|
||||||
|
|
||||||
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
|
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
|
||||||
@@ -403,7 +486,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
xps.HasIndex(x => x.UserId);
|
xps.HasIndex(x => x.UserId);
|
||||||
xps.HasIndex(x => x.GuildId);
|
xps.HasIndex(x => x.GuildId);
|
||||||
xps.HasIndex(x => x.Xp);
|
xps.HasIndex(x => x.Xp);
|
||||||
xps.HasIndex(x => x.AwardedXp);
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -509,23 +591,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region GroupName
|
|
||||||
|
|
||||||
modelBuilder.Entity<GroupName>()
|
|
||||||
.HasIndex(x => new
|
|
||||||
{
|
|
||||||
x.GuildConfigId,
|
|
||||||
x.Number
|
|
||||||
})
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
modelBuilder.Entity<GroupName>()
|
|
||||||
.HasOne(x => x.GuildConfig)
|
|
||||||
.WithMany(x => x.SelfAssignableRoleGroupNames)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region BanTemplate
|
#region BanTemplate
|
||||||
|
|
||||||
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
|
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
|
||||||
|
@@ -5,9 +5,52 @@ namespace NadekoBot.Migrations;
|
|||||||
|
|
||||||
public static class MigrationQueries
|
public static class MigrationQueries
|
||||||
{
|
{
|
||||||
|
public static void MergeAwardedXp(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
UPDATE UserXpStats
|
||||||
|
SET Xp = AwardedXp + Xp,
|
||||||
|
AwardedXp = 0
|
||||||
|
WHERE AwardedXp > 0;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MigrateSar(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
if (migrationBuilder.IsNpgsql())
|
||||||
|
return;
|
||||||
|
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
INSERT INTO GroupName (Number, GuildConfigId)
|
||||||
|
SELECT DISTINCT "Group", GC.Id
|
||||||
|
FROM SelfAssignableRoles as SAR
|
||||||
|
INNER JOIN GuildConfigs as GC
|
||||||
|
ON SAR.GuildId = GC.GuildId
|
||||||
|
WHERE SAR.GuildId not in (SELECT GuildConfigs.GuildId from GroupName LEFT JOIN GuildConfigs ON GroupName.GuildConfigId = GuildConfigs.Id);
|
||||||
|
|
||||||
|
INSERT INTO SarGroup (Id, GroupNumber, Name, IsExclusive, GuildId)
|
||||||
|
SELECT GN.Id, GN.Number, GN.Name, GC.ExclusiveSelfAssignedRoles, GC.GuildId
|
||||||
|
FROM GroupName as GN
|
||||||
|
INNER JOIN GuildConfigs as GC ON GN.GuildConfigId = GC.Id;
|
||||||
|
|
||||||
|
INSERT INTO Sar (GuildId, RoleId, SarGroupId, LevelReq)
|
||||||
|
SELECT SAR.GuildId, SAR.RoleId, (SELECT Id FROM SarGroup WHERE SG.Number = SarGroup.GroupNumber AND SG.GuildId = SarGroup.GuildId), MIN(SAR.LevelRequirement)
|
||||||
|
FROM SelfAssignableRoles as SAR
|
||||||
|
INNER JOIN (SELECT GuildId, gn.Number FROM GroupName as gn
|
||||||
|
INNER JOIN GuildConfigs as gc ON gn.GuildConfigId =gc.Id
|
||||||
|
) as SG
|
||||||
|
ON SG.GuildId = SAR.GuildId
|
||||||
|
WHERE SG.Number IN (SELECT GroupNumber FROM SarGroup WHERE Sar.GuildId = SarGroup.GuildId)
|
||||||
|
GROUP BY SAR.GuildId, SAR.RoleId;
|
||||||
|
|
||||||
|
INSERT INTO SarAutoDelete (GuildId, IsEnabled)
|
||||||
|
SELECT GuildId, AutoDeleteSelfAssignedRoleMessages FROM GuildConfigs WHERE AutoDeleteSelfAssignedRoleMessages = TRUE;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
public static void UpdateUsernames(MigrationBuilder migrationBuilder)
|
public static void UpdateUsernames(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql("UPDATE DiscordUser SET Username = '??' + Username WHERE Discriminator = '????';");
|
migrationBuilder.Sql("UPDATE DiscordUser SET Username = '??' || Username WHERE Discriminator = '????';");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
||||||
|
@@ -12,8 +12,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
MigrationQueries.UpdateUsernames(migrationBuilder);
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(
|
||||||
name: "discriminator",
|
name: "discriminator",
|
||||||
table: "discorduser");
|
table: "discorduser");
|
||||||
|
3919
src/NadekoBot/Migrations/PostgreSql/20241105073004_rakeback.Designer.cs
generated
Normal file
3919
src/NadekoBot/Migrations/PostgreSql/20241105073004_rakeback.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class rakeback : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "rakeback",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
amount = table.Column<decimal>(type: "numeric", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_rakeback", x => x.userid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "rakeback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3947
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.Designer.cs
generated
Normal file
3947
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
170
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.cs
Normal file
170
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class sarrework : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "sarautodelete",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
isenabled = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_sarautodelete", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "sargroup",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
groupnumber = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
rolereq = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
|
||||||
|
isexclusive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_sargroup", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_sargroup_guildid_groupnumber",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.guildid,
|
||||||
|
x.groupnumber
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "sar",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
sargroupid = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
levelreq = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_sar", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_sar_guildid_roleid",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.guildid,
|
||||||
|
x.roleid
|
||||||
|
});
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_sar_sargroup_sargroupid",
|
||||||
|
column: x => x.sargroupid,
|
||||||
|
principalTable: "sargroup",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_sar_sargroupid",
|
||||||
|
table: "sar",
|
||||||
|
column: "sargroupid");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_sarautodelete_guildid",
|
||||||
|
table: "sarautodelete",
|
||||||
|
column: "guildid",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
MigrationQueries.MigrateSar(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "groupname");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "selfassignableroles");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "sar");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "sarautodelete");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "sargroup");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "groupname",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildconfigid = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||||
|
name = table.Column<string>(type: "text", nullable: true),
|
||||||
|
number = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_groupname", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_groupname_guildconfigs_guildconfigid",
|
||||||
|
column: x => x.guildconfigid,
|
||||||
|
principalTable: "guildconfigs",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "selfassignableroles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||||
|
group = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
levelrequirement = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_selfassignableroles", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_groupname_guildconfigid_number",
|
||||||
|
table: "groupname",
|
||||||
|
columns: new[] { "guildconfigid", "number" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_selfassignableroles_guildid_roleid",
|
||||||
|
table: "selfassignableroles",
|
||||||
|
columns: new[] { "guildid", "roleid" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4048
src/NadekoBot/Migrations/PostgreSql/20241126033634_btnroles_guildcolors.Designer.cs
generated
Normal file
4048
src/NadekoBot/Migrations/PostgreSql/20241126033634_btnroles_guildcolors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class btnroles_guildcolors : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "buttonrole",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
buttonid = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
messageid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
position = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
emote = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
label = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
exclusive = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_buttonrole", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_buttonrole_roleid_messageid", x => new { x.roleid, x.messageid });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "guildcolors",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
okcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true),
|
||||||
|
errorcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true),
|
||||||
|
pendingcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_guildcolors", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_buttonrole_guildid",
|
||||||
|
table: "buttonrole",
|
||||||
|
column: "guildid");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_guildcolors_guildid",
|
||||||
|
table: "guildcolors",
|
||||||
|
column: "guildid",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "buttonrole");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "guildcolors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4117
src/NadekoBot/Migrations/PostgreSql/20241207150050_awardedxp-temprole-notify.Designer.cs
generated
Normal file
4117
src/NadekoBot/Migrations/PostgreSql/20241207150050_awardedxp-temprole-notify.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class awardedxptemprolenotify : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_userxpstats_awardedxp",
|
||||||
|
table: "userxpstats");
|
||||||
|
|
||||||
|
MigrationQueries.MergeAwardedXp(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "awardedxp",
|
||||||
|
table: "userxpstats");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "notifyonlevelup",
|
||||||
|
table: "userxpstats");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "notify",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
type = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
message = table.Column<string>(type: "character varying(10000)", maxLength: 10000, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_notify", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_notify_guildid_type", x => new { x.guildid, x.type });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "temprole",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
remove = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
expiresat = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_temprole", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_temprole_guildid_userid_roleid", x => new { x.guildid, x.userid, x.roleid });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_userbetstats_maxwin",
|
||||||
|
table: "userbetstats",
|
||||||
|
column: "maxwin");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_temprole_expiresat",
|
||||||
|
table: "temprole",
|
||||||
|
column: "expiresat");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "notify");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "temprole");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_userbetstats_maxwin",
|
||||||
|
table: "userbetstats");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "awardedxp",
|
||||||
|
table: "userxpstats",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "notifyonlevelup",
|
||||||
|
table: "userxpstats",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_userxpstats_awardedxp",
|
||||||
|
table: "userxpstats",
|
||||||
|
column: "awardedxp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4151
src/NadekoBot/Migrations/PostgreSql/20250113135504_fishes.Designer.cs
generated
Normal file
4151
src/NadekoBot/Migrations/PostgreSql/20250113135504_fishes.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
src/NadekoBot/Migrations/PostgreSql/20250113135504_fishes.cs
Normal file
39
src/NadekoBot/Migrations/PostgreSql/20250113135504_fishes.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class fishes : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "fishcatch",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
fishid = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
count = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
maxstars = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_fishcatch", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_fishcatch_userid_fishid", x => new { x.userid, x.fishid });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "fishcatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -451,6 +451,69 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("blacklist", (string)null);
|
b.ToTable("blacklist", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.ButtonRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ButtonId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("buttonid");
|
||||||
|
|
||||||
|
b.Property<decimal>("ChannelId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
|
b.Property<string>("Emote")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("emote");
|
||||||
|
|
||||||
|
b.Property<bool>("Exclusive")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exclusive");
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<string>("Label")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("label");
|
||||||
|
|
||||||
|
b.Property<decimal>("MessageId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("messageid");
|
||||||
|
|
||||||
|
b.Property<int>("Position")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("position");
|
||||||
|
|
||||||
|
b.Property<decimal>("RoleId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("roleid");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_buttonrole");
|
||||||
|
|
||||||
|
b.HasAlternateKey("RoleId", "MessageId")
|
||||||
|
.HasName("ak_buttonrole_roleid_messageid");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
|
.HasDatabaseName("ix_buttonrole_guildid");
|
||||||
|
|
||||||
|
b.ToTable("buttonrole", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("ClubId")
|
b.Property<int>("ClubId")
|
||||||
@@ -1202,7 +1265,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("giveawayuser", (string)null);
|
b.ToTable("giveawayuser", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildColors", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -1211,30 +1274,33 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<string>("ErrorColor")
|
||||||
.HasColumnType("timestamp without time zone")
|
.HasMaxLength(9)
|
||||||
.HasColumnName("dateadded");
|
.HasColumnType("character varying(9)")
|
||||||
|
.HasColumnName("errorcolor");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildconfigid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("OkColor")
|
||||||
.HasColumnType("text")
|
.HasMaxLength(9)
|
||||||
.HasColumnName("name");
|
.HasColumnType("character varying(9)")
|
||||||
|
.HasColumnName("okcolor");
|
||||||
|
|
||||||
b.Property<int>("Number")
|
b.Property<string>("PendingColor")
|
||||||
.HasColumnType("integer")
|
.HasMaxLength(9)
|
||||||
.HasColumnName("number");
|
.HasColumnType("character varying(9)")
|
||||||
|
.HasColumnName("pendingcolor");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_groupname");
|
.HasName("pk_guildcolors");
|
||||||
|
|
||||||
b.HasIndex("GuildConfigId", "Number")
|
b.HasIndex("GuildId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_groupname_guildconfigid_number");
|
.HasDatabaseName("ix_guildcolors_guildid");
|
||||||
|
|
||||||
b.ToTable("groupname", (string)null);
|
b.ToTable("guildcolors", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
||||||
@@ -1751,6 +1817,42 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("expressions", (string)null);
|
b.ToTable("expressions", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.Notify", 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<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<string>("Message")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10000)
|
||||||
|
.HasColumnType("character varying(10000)")
|
||||||
|
.HasColumnName("message");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("type");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_notify");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "Type")
|
||||||
|
.HasName("ak_notify_guildid_type");
|
||||||
|
|
||||||
|
b.ToTable("notify", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<decimal>("UserId")
|
b.Property<decimal>("UserId")
|
||||||
@@ -2200,7 +2302,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("rotatingstatus", (string)null);
|
b.ToTable("rotatingstatus", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.SelfAssignedRole", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -2209,36 +2311,98 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
|
||||||
.HasColumnType("timestamp without time zone")
|
|
||||||
.HasColumnName("dateadded");
|
|
||||||
|
|
||||||
b.Property<int>("Group")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasDefaultValue(0)
|
|
||||||
.HasColumnName("group");
|
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<int>("LevelRequirement")
|
b.Property<int>("LevelReq")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("levelrequirement");
|
.HasColumnName("levelreq");
|
||||||
|
|
||||||
b.Property<decimal>("RoleId")
|
b.Property<decimal>("RoleId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("roleid");
|
.HasColumnName("roleid");
|
||||||
|
|
||||||
|
b.Property<int>("SarGroupId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("sargroupid");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_selfassignableroles");
|
.HasName("pk_sar");
|
||||||
|
|
||||||
b.HasIndex("GuildId", "RoleId")
|
b.HasAlternateKey("GuildId", "RoleId")
|
||||||
|
.HasName("ak_sar_guildid_roleid");
|
||||||
|
|
||||||
|
b.HasIndex("SarGroupId")
|
||||||
|
.HasDatabaseName("ix_sar_sargroupid");
|
||||||
|
|
||||||
|
b.ToTable("sar", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarAutoDelete", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("isenabled");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_sarautodelete");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_selfassignableroles_guildid_roleid");
|
.HasDatabaseName("ix_sarautodelete_guildid");
|
||||||
|
|
||||||
b.ToTable("selfassignableroles", (string)null);
|
b.ToTable("sarautodelete", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("GroupNumber")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("groupnumber");
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<bool>("IsExclusive")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("isexclusive");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<decimal?>("RoleReq")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("rolereq");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_sargroup");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "GroupNumber")
|
||||||
|
.HasName("ak_sargroup_guildid_groupnumber");
|
||||||
|
|
||||||
|
b.ToTable("sargroup", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
@@ -2574,6 +2738,47 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("streamrolewhitelisteduser", (string)null);
|
b.ToTable("streamrolewhitelisteduser", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.TempRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp without time zone")
|
||||||
|
.HasColumnName("expiresat");
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<bool>("Remove")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("remove");
|
||||||
|
|
||||||
|
b.Property<decimal>("RoleId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("roleid");
|
||||||
|
|
||||||
|
b.Property<decimal>("UserId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("userid");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_temprole");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "UserId", "RoleId")
|
||||||
|
.HasName("ak_temprole_guildid_userid_roleid");
|
||||||
|
|
||||||
|
b.HasIndex("ExpiresAt")
|
||||||
|
.HasDatabaseName("ix_temprole_expiresat");
|
||||||
|
|
||||||
|
b.ToTable("temprole", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -2730,10 +2935,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<long>("AwardedXp")
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("awardedxp");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp without time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
@@ -2742,10 +2943,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("notifyonlevelup");
|
|
||||||
|
|
||||||
b.Property<decimal>("UserId")
|
b.Property<decimal>("UserId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("userid");
|
.HasColumnName("userid");
|
||||||
@@ -2757,9 +2954,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_userxpstats");
|
.HasName("pk_userxpstats");
|
||||||
|
|
||||||
b.HasIndex("AwardedXp")
|
|
||||||
.HasDatabaseName("ix_userxpstats_awardedxp");
|
|
||||||
|
|
||||||
b.HasIndex("GuildId")
|
b.HasIndex("GuildId")
|
||||||
.HasDatabaseName("ix_userxpstats_guildid");
|
.HasDatabaseName("ix_userxpstats_guildid");
|
||||||
|
|
||||||
@@ -3180,6 +3374,40 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("xpshopowneditem", (string)null);
|
b.ToTable("xpshopowneditem", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Modules.Games.FishCatch", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("count");
|
||||||
|
|
||||||
|
b.Property<int>("FishId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("fishid");
|
||||||
|
|
||||||
|
b.Property<int>("MaxStars")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("maxstars");
|
||||||
|
|
||||||
|
b.Property<decimal>("UserId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("userid");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_fishcatch");
|
||||||
|
|
||||||
|
b.HasAlternateKey("UserId", "FishId")
|
||||||
|
.HasName("ak_fishcatch_userid_fishid");
|
||||||
|
|
||||||
|
b.ToTable("fishcatch", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.GreetSettings", b =>
|
modelBuilder.Entity("NadekoBot.Services.GreetSettings", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -3227,6 +3455,23 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("greetsettings", (string)null);
|
b.ToTable("greetsettings", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Rakeback", b =>
|
||||||
|
{
|
||||||
|
b.Property<decimal>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("userid");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("numeric")
|
||||||
|
.HasColumnName("amount");
|
||||||
|
|
||||||
|
b.HasKey("UserId")
|
||||||
|
.HasName("pk_rakeback");
|
||||||
|
|
||||||
|
b.ToTable("rakeback", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.UserBetStats", b =>
|
modelBuilder.Entity("NadekoBot.Services.UserBetStats", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -3271,6 +3516,9 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_userbetstats");
|
.HasName("pk_userbetstats");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin")
|
||||||
|
.HasDatabaseName("ix_userbetstats_maxwin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_userbetstats_userid_game");
|
.HasDatabaseName("ix_userbetstats_userid_game");
|
||||||
@@ -3510,18 +3758,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasConstraintName("fk_giveawayuser_giveawaymodel_giveawayid");
|
.HasConstraintName("fk_giveawayuser_giveawaymodel_giveawayid");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
|
|
||||||
.WithMany("SelfAssignableRoleGroupNames")
|
|
||||||
.HasForeignKey("GuildConfigId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_groupname_guildconfigs_guildconfigid");
|
|
||||||
|
|
||||||
b.Navigation("GuildConfig");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
||||||
@@ -3561,6 +3797,16 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasConstraintName("fk_playlistsong_musicplaylists_musicplaylistid");
|
.HasConstraintName("fk_playlistsong_musicplaylists_musicplaylistid");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Db.Models.SarGroup", null)
|
||||||
|
.WithMany("Roles")
|
||||||
|
.HasForeignKey("SarGroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_sar_sargroup_sargroupid");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||||
@@ -3837,8 +4083,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
b.Navigation("Permissions");
|
b.Navigation("Permissions");
|
||||||
|
|
||||||
b.Navigation("SelfAssignableRoleGroupNames");
|
|
||||||
|
|
||||||
b.Navigation("ShopEntries");
|
b.Navigation("ShopEntries");
|
||||||
|
|
||||||
b.Navigation("SlowmodeIgnoredRoles");
|
b.Navigation("SlowmodeIgnoredRoles");
|
||||||
@@ -3868,6 +4112,11 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.Navigation("Songs");
|
b.Navigation("Songs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Roles");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Items");
|
b.Navigation("Items");
|
||||||
|
@@ -11,8 +11,6 @@ namespace NadekoBot.Migrations
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
MigrationQueries.UpdateUsernames(migrationBuilder);
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(
|
||||||
name: "Discriminator",
|
name: "Discriminator",
|
||||||
table: "DiscordUser");
|
table: "DiscordUser");
|
||||||
|
3025
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.Designer.cs
generated
Normal file
3025
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.cs
Normal file
34
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class rakeback : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Rakeback",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Amount = table.Column<decimal>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Rakeback", x => x.UserId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Rakeback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3045
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.Designer.cs
generated
Normal file
3045
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
163
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.cs
Normal file
163
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.cs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class sarrework : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SarAutoDelete",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SarAutoDelete", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SarGroup",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GroupNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
RoleReq = table.Column<ulong>(type: "INTEGER", nullable: true),
|
||||||
|
IsExclusive = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SarGroup", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_SarGroup_GuildId_GroupNumber",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.GroupNumber
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Sar",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
SarGroupId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
LevelReq = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Sar", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_Sar_GuildId_RoleId",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.RoleId
|
||||||
|
});
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Sar_SarGroup_SarGroupId",
|
||||||
|
column: x => x.SarGroupId,
|
||||||
|
principalTable: "SarGroup",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Sar_SarGroupId",
|
||||||
|
table: "Sar",
|
||||||
|
column: "SarGroupId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SarAutoDelete_GuildId",
|
||||||
|
table: "SarAutoDelete",
|
||||||
|
column: "GuildId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
MigrationQueries.MigrateSar(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GroupName");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SelfAssignableRoles");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Sar");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SarAutoDelete");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SarGroup");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GroupName",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildConfigId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Number = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GroupName", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GroupName_GuildConfigs_GuildConfigId",
|
||||||
|
column: x => x.GuildConfigId,
|
||||||
|
principalTable: "GuildConfigs",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SelfAssignableRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
Group = table.Column<int>(type: "INTEGER", nullable: false, defaultValue: 0),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
LevelRequirement = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SelfAssignableRoles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GroupName_GuildConfigId_Number",
|
||||||
|
table: "GroupName",
|
||||||
|
columns: new[] { "GuildConfigId", "Number" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SelfAssignableRoles_GuildId_RoleId",
|
||||||
|
table: "SelfAssignableRoles",
|
||||||
|
columns: new[] { "GuildId", "RoleId" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3122
src/NadekoBot/Migrations/Sqlite/20241126033626_btnroles_guildcolors.Designer.cs
generated
Normal file
3122
src/NadekoBot/Migrations/Sqlite/20241126033626_btnroles_guildcolors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class btnroles_guildcolors : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ButtonRole",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
ButtonId = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
MessageId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Position = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Emote = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
Label = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||||
|
Exclusive = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ButtonRole", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_ButtonRole_RoleId_MessageId", x => new { x.RoleId, x.MessageId });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GuildColors",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
OkColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true),
|
||||||
|
ErrorColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true),
|
||||||
|
PendingColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GuildColors", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ButtonRole_GuildId",
|
||||||
|
table: "ButtonRole",
|
||||||
|
column: "GuildId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GuildColors_GuildId",
|
||||||
|
table: "GuildColors",
|
||||||
|
column: "GuildId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ButtonRole");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GuildColors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3173
src/NadekoBot/Migrations/Sqlite/20241207150041_awardedxp-temprole-notify.Designer.cs
generated
Normal file
3173
src/NadekoBot/Migrations/Sqlite/20241207150041_awardedxp-temprole-notify.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class awardedxptemprolenotify : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_UserXpStats_AwardedXp",
|
||||||
|
table: "UserXpStats");
|
||||||
|
|
||||||
|
MigrationQueries.MergeAwardedXp(migrationBuilder);
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AwardedXp",
|
||||||
|
table: "UserXpStats");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "NotifyOnLevelUp",
|
||||||
|
table: "UserXpStats");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Notify",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Type = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Message = table.Column<string>(type: "TEXT", maxLength: 10000, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Notify", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_Notify_GuildId_Type", x => new { x.GuildId, x.Type });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TempRole",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Remove = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ExpiresAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TempRole", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_TempRole_GuildId_UserId_RoleId", x => new { x.GuildId, x.UserId, x.RoleId });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserBetStats_MaxWin",
|
||||||
|
table: "UserBetStats",
|
||||||
|
column: "MaxWin");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TempRole_ExpiresAt",
|
||||||
|
table: "TempRole",
|
||||||
|
column: "ExpiresAt");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Notify");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TempRole");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_UserBetStats_MaxWin",
|
||||||
|
table: "UserBetStats");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "AwardedXp",
|
||||||
|
table: "UserXpStats",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "NotifyOnLevelUp",
|
||||||
|
table: "UserXpStats",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserXpStats_AwardedXp",
|
||||||
|
table: "UserXpStats",
|
||||||
|
column: "AwardedXp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3198
src/NadekoBot/Migrations/Sqlite/20250113135453_fishes.Designer.cs
generated
Normal file
3198
src/NadekoBot/Migrations/Sqlite/20250113135453_fishes.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
src/NadekoBot/Migrations/Sqlite/20250113135453_fishes.cs
Normal file
38
src/NadekoBot/Migrations/Sqlite/20250113135453_fishes.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class fishes : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FishCatch",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
FishId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Count = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
MaxStars = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FishCatch", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_FishCatch_UserId_FishId", x => new { x.UserId, x.FishId });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FishCatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -335,6 +335,54 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("Blacklist");
|
b.ToTable("Blacklist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.ButtonRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ButtonId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Emote")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Exclusive")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Label")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("MessageId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Position")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("RoleId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("RoleId", "MessageId");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId");
|
||||||
|
|
||||||
|
b.ToTable("ButtonRole");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("ClubId")
|
b.Property<int>("ClubId")
|
||||||
@@ -894,30 +942,33 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("GiveawayUser");
|
b.ToTable("GiveawayUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildColors", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<string>("ErrorColor")
|
||||||
|
.HasMaxLength(9)
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("OkColor")
|
||||||
|
.HasMaxLength(9)
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("Number")
|
b.Property<string>("PendingColor")
|
||||||
.HasColumnType("INTEGER");
|
.HasMaxLength(9)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("GuildConfigId", "Number")
|
b.HasIndex("GuildId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("GroupName");
|
b.ToTable("GuildColors");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
||||||
@@ -1305,6 +1356,33 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("Expressions");
|
b.ToTable("Expressions");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.Notify", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Message")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10000)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "Type");
|
||||||
|
|
||||||
|
b.ToTable("Notify");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<ulong>("UserId")
|
b.Property<ulong>("UserId")
|
||||||
@@ -1640,35 +1718,80 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("RotatingStatus");
|
b.ToTable("RotatingStatus");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.SelfAssignedRole", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<int>("Group")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER")
|
|
||||||
.HasDefaultValue(0);
|
|
||||||
|
|
||||||
b.Property<ulong>("GuildId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("LevelRequirement")
|
b.Property<int>("LevelReq")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<ulong>("RoleId")
|
b.Property<ulong>("RoleId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SarGroupId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("GuildId", "RoleId")
|
b.HasAlternateKey("GuildId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("SarGroupId");
|
||||||
|
|
||||||
|
b.ToTable("Sar");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarAutoDelete", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("SelfAssignableRoles");
|
b.ToTable("SarAutoDelete");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("GroupNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("IsExclusive")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong?>("RoleReq")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "GroupNumber");
|
||||||
|
|
||||||
|
b.ToTable("SarGroup");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
@@ -1917,6 +2040,36 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("StreamRoleWhitelistedUser");
|
b.ToTable("StreamRoleWhitelistedUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.TempRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("Remove")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("RoleId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("ExpiresAt");
|
||||||
|
|
||||||
|
b.ToTable("TempRole");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.TodoModel", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -2031,18 +2184,12 @@ namespace NadekoBot.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<long>("AwardedXp")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<ulong>("GuildId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<ulong>("UserId")
|
b.Property<ulong>("UserId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@@ -2051,8 +2198,6 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("AwardedXp");
|
|
||||||
|
|
||||||
b.HasIndex("GuildId");
|
b.HasIndex("GuildId");
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
@@ -2363,6 +2508,31 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("XpShopOwnedItem");
|
b.ToTable("XpShopOwnedItem");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Modules.Games.FishCatch", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("FishId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MaxStars")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("UserId", "FishId");
|
||||||
|
|
||||||
|
b.ToTable("FishCatch");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.GreetSettings", b =>
|
modelBuilder.Entity("NadekoBot.Services.GreetSettings", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -2399,6 +2569,20 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("GreetSettings");
|
b.ToTable("GreetSettings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Rakeback", b =>
|
||||||
|
{
|
||||||
|
b.Property<ulong>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Rakeback");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Services.UserBetStats", b =>
|
modelBuilder.Entity("NadekoBot.Services.UserBetStats", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -2431,6 +2615,8 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
@@ -2646,17 +2832,6 @@ namespace NadekoBot.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
|
|
||||||
.WithMany("SelfAssignableRoleGroupNames")
|
|
||||||
.HasForeignKey("GuildConfigId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("GuildConfig");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
||||||
@@ -2692,6 +2867,15 @@ namespace NadekoBot.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Db.Models.SarGroup", null)
|
||||||
|
.WithMany("Roles")
|
||||||
|
.HasForeignKey("SarGroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||||
@@ -2946,8 +3130,6 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.Navigation("Permissions");
|
b.Navigation("Permissions");
|
||||||
|
|
||||||
b.Navigation("SelfAssignableRoleGroupNames");
|
|
||||||
|
|
||||||
b.Navigation("ShopEntries");
|
b.Navigation("ShopEntries");
|
||||||
|
|
||||||
b.Navigation("SlowmodeIgnoredRoles");
|
b.Navigation("SlowmodeIgnoredRoles");
|
||||||
@@ -2977,6 +3159,11 @@ namespace NadekoBot.Migrations
|
|||||||
b.Navigation("Songs");
|
b.Navigation("Songs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Roles");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Items");
|
b.Navigation("Items");
|
||||||
|
@@ -46,7 +46,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
[BotPerm(GuildPerm.ManageGuild)]
|
[BotPerm(GuildPerm.ManageGuild)]
|
||||||
public async Task ImageOnlyChannel(StoopidTime time = null)
|
public async Task ImageOnlyChannel(ParsedTimespan timespan = null)
|
||||||
{
|
{
|
||||||
var newValue = await _somethingOnly.ToggleImageOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
var newValue = await _somethingOnly.ToggleImageOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
||||||
if (newValue)
|
if (newValue)
|
||||||
@@ -59,7 +59,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
[BotPerm(GuildPerm.ManageGuild)]
|
[BotPerm(GuildPerm.ManageGuild)]
|
||||||
public async Task LinkOnlyChannel(StoopidTime time = null)
|
public async Task LinkOnlyChannel(ParsedTimespan timespan = null)
|
||||||
{
|
{
|
||||||
var newValue = await _somethingOnly.ToggleLinkOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
var newValue = await _somethingOnly.ToggleLinkOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
||||||
if (newValue)
|
if (newValue)
|
||||||
@@ -72,10 +72,10 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(ChannelPerm.ManageChannels)]
|
[UserPerm(ChannelPerm.ManageChannels)]
|
||||||
[BotPerm(ChannelPerm.ManageChannels)]
|
[BotPerm(ChannelPerm.ManageChannels)]
|
||||||
public async Task Slowmode(StoopidTime time = null)
|
public async Task Slowmode(ParsedTimespan timespan = null)
|
||||||
{
|
{
|
||||||
var seconds = (int?)time?.Time.TotalSeconds ?? 0;
|
var seconds = (int?)timespan?.Time.TotalSeconds ?? 0;
|
||||||
if (time is not null && (time.Time < TimeSpan.FromSeconds(0) || time.Time > TimeSpan.FromHours(6)))
|
if (timespan is not null && (timespan.Time < TimeSpan.FromSeconds(0) || timespan.Time > TimeSpan.FromHours(6)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await ((ITextChannel)ctx.Channel).ModifyAsync(tcp =>
|
await ((ITextChannel)ctx.Channel).ModifyAsync(tcp =>
|
||||||
@@ -96,7 +96,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
var guild = (SocketGuild)ctx.Guild;
|
var guild = (SocketGuild)ctx.Guild;
|
||||||
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.server_delmsgoncmd))
|
.WithTitle(GetText(strs.server_delmsgoncmd))
|
||||||
.WithDescription(enabled ? "✅" : "❌");
|
.WithDescription(enabled ? "✅" : "❌");
|
||||||
@@ -298,18 +298,18 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(ChannelPerm.ManageMessages)]
|
[UserPerm(ChannelPerm.ManageMessages)]
|
||||||
[BotPerm(ChannelPerm.ManageMessages)]
|
[BotPerm(ChannelPerm.ManageMessages)]
|
||||||
public Task Delete(ulong messageId, StoopidTime time = null)
|
public Task Delete(ulong messageId, ParsedTimespan timespan = null)
|
||||||
=> Delete((ITextChannel)ctx.Channel, messageId, time);
|
=> Delete((ITextChannel)ctx.Channel, messageId, timespan);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Delete(ITextChannel channel, ulong messageId, StoopidTime time = null)
|
public async Task Delete(ITextChannel channel, ulong messageId, ParsedTimespan timespan = null)
|
||||||
=> await InternalMessageAction(channel, messageId, time, msg => msg.DeleteAsync());
|
=> await InternalMessageAction(channel, messageId, timespan, msg => msg.DeleteAsync());
|
||||||
|
|
||||||
private async Task InternalMessageAction(
|
private async Task InternalMessageAction(
|
||||||
ITextChannel channel,
|
ITextChannel channel,
|
||||||
ulong messageId,
|
ulong messageId,
|
||||||
StoopidTime time,
|
ParsedTimespan timespan,
|
||||||
Func<IMessage, Task> func)
|
Func<IMessage, Task> func)
|
||||||
{
|
{
|
||||||
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
|
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
|
||||||
@@ -334,13 +334,13 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time is null)
|
if (timespan is null)
|
||||||
await msg.DeleteAsync();
|
await msg.DeleteAsync();
|
||||||
else if (time.Time <= TimeSpan.FromDays(7))
|
else if (timespan.Time <= TimeSpan.FromDays(7))
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await Task.Delay(time.Time);
|
await Task.Delay(timespan.Time);
|
||||||
await msg.DeleteAsync();
|
await msg.DeleteAsync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using NadekoBot.Modules.Administration._common.results;
|
using NadekoBot.Modules.Administration._common.results;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration.Services;
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
public class AdministrationService : INService
|
public class AdministrationService : INService
|
||||||
{
|
{
|
||||||
|
@@ -136,7 +136,7 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
await using var linqCtx = ctx.CreateLinqToDBContext();
|
await using var linqCtx = ctx.CreateLinqToDBContext();
|
||||||
await using var tempTable = linqCtx.CreateTempTable<CleanupId>();
|
await using var tempTable = linqCtx.CreateTempTable<CleanupId>();
|
||||||
|
|
||||||
foreach (var chunk in allIds.Chunk(20000))
|
foreach (var chunk in allIds.Chunk(10000))
|
||||||
{
|
{
|
||||||
await tempTable.BulkCopyAsync(chunk.Select(x => new CleanupId()
|
await tempTable.BulkCopyAsync(chunk.Select(x => new CleanupId()
|
||||||
{
|
{
|
||||||
@@ -187,13 +187,6 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
.Contains(x.GuildId))
|
.Contains(x.GuildId))
|
||||||
.DeleteAsync();
|
.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
|
// delete perm overrides
|
||||||
await ctx.GetTable<DiscordPermOverride>()
|
await ctx.GetTable<DiscordPermOverride>()
|
||||||
.Where(x => x.GuildId != null
|
.Where(x => x.GuildId != null
|
||||||
@@ -219,6 +212,54 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
.Contains(x.GuildId))
|
.Contains(x.GuildId))
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete sar
|
||||||
|
await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete warnings
|
||||||
|
await ctx.GetTable<Warning>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete warn punishments
|
||||||
|
await ctx.GetTable<WarningPunishment>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete sticky roles
|
||||||
|
await ctx.GetTable<StickyRole>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete at channels
|
||||||
|
await ctx.GetTable<AutoTranslateChannel>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete ban templates
|
||||||
|
await ctx.GetTable<BanTemplate>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete reminders
|
||||||
|
await ctx.GetTable<Reminder>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.ServerId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete button roles
|
||||||
|
await ctx.GetTable<ButtonRole>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
GuildCount = guildIds.Keys.Count,
|
GuildCount = guildIds.Keys.Count,
|
||||||
|
@@ -42,9 +42,9 @@ public partial class Administration
|
|||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return _sender.CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
|
return CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
|
||||||
|
|
||||||
return _sender.CreateEmbed()
|
return CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithFooter(sql)
|
.WithFooter(sql)
|
||||||
.WithTitle(string.Join(" ║ ", result.ColumnNames))
|
.WithTitle(string.Join(" ║ ", result.ColumnNames))
|
||||||
@@ -99,7 +99,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle(GetText(strs.sql_confirm_exec))
|
.WithTitle(GetText(strs.sql_confirm_exec))
|
||||||
.WithDescription(Format.Code(sql));
|
.WithDescription(Format.Code(sql));
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ public partial class Administration
|
|||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task PurgeUser(ulong userId)
|
public async Task PurgeUser(ulong userId)
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
|
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
|
||||||
|
|
||||||
if (!await PromptUserConfirmAsync(embed))
|
if (!await PromptUserConfirmAsync(embed))
|
||||||
|
@@ -71,7 +71,7 @@ public sealed class HoneyPotService : IHoneyPotService, IReadyExecutor, IExecNoC
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Log.Information("Honeypot caught user {User} [{UserId}]", user, user.Id);
|
Log.Information("Honeypot caught user {User} [{UserId}]", user, user.Id);
|
||||||
await user.BanAsync(pruneDays: 1);
|
await user.BanAsync(pruneDays: 1, reason: "Honeypot");
|
||||||
await user.Guild.RemoveBanAsync(user.Id);
|
await user.Guild.RemoveBanAsync(user.Id);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@@ -123,7 +123,7 @@ public partial class Administration
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task LanguagesList()
|
public async Task LanguagesList()
|
||||||
=> await Response().Embed(_sender.CreateEmbed()
|
=> await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.lang_list))
|
.WithTitle(GetText(strs.lang_list))
|
||||||
.WithDescription(string.Join("\n",
|
.WithDescription(string.Join("\n",
|
||||||
|
@@ -72,18 +72,18 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
|
[UserPerm(GuildPerm.ManageRoles | GuildPerm.MuteMembers)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task Mute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
public async Task Mute(ParsedTimespan timespan, IGuildUser user, [Leftover] string reason = "")
|
||||||
{
|
{
|
||||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
if (timespan.Time < TimeSpan.FromMinutes(1) || timespan.Time > TimeSpan.FromDays(49))
|
||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.TimedMute(user, ctx.User, time.Time, reason: reason);
|
await _service.TimedMute(user, ctx.User, timespan.Time, reason: reason);
|
||||||
await Response().Confirm(strs.user_muted_time(Format.Bold(user.ToString()),
|
await Response().Confirm(strs.user_muted_time(Format.Bold(user.ToString()),
|
||||||
(int)time.Time.TotalMinutes)).SendAsync();
|
(int)timespan.Time.TotalMinutes)).SendAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -133,18 +133,18 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task ChatMute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
public async Task ChatMute(ParsedTimespan timespan, IGuildUser user, [Leftover] string reason = "")
|
||||||
{
|
{
|
||||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
if (timespan.Time < TimeSpan.FromMinutes(1) || timespan.Time > TimeSpan.FromDays(49))
|
||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason);
|
await _service.TimedMute(user, ctx.User, timespan.Time, MuteType.Chat, reason);
|
||||||
await Response().Confirm(strs.user_chat_mute_time(Format.Bold(user.ToString()),
|
await Response().Confirm(strs.user_chat_mute_time(Format.Bold(user.ToString()),
|
||||||
(int)time.Time.TotalMinutes)).SendAsync();
|
(int)timespan.Time.TotalMinutes)).SendAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -193,18 +193,18 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.MuteMembers)]
|
[UserPerm(GuildPerm.MuteMembers)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task VoiceMute(StoopidTime time, IGuildUser user, [Leftover] string reason = "")
|
public async Task VoiceMute(ParsedTimespan timespan, IGuildUser user, [Leftover] string reason = "")
|
||||||
{
|
{
|
||||||
if (time.Time < TimeSpan.FromMinutes(1) || time.Time > TimeSpan.FromDays(49))
|
if (timespan.Time < TimeSpan.FromMinutes(1) || timespan.Time > TimeSpan.FromDays(49))
|
||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
if (!await VerifyMutePermissions((IGuildUser)ctx.User, user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason);
|
await _service.TimedMute(user, ctx.User, timespan.Time, MuteType.Voice, reason);
|
||||||
await Response().Confirm(strs.user_voice_mute_time(Format.Bold(user.ToString()),
|
await Response().Confirm(strs.user_voice_mute_time(Format.Bold(user.ToString()),
|
||||||
(int)time.Time.TotalMinutes)).SendAsync();
|
(int)timespan.Time.TotalMinutes)).SendAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -122,7 +122,7 @@ public class MuteService : INService
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_ = Task.Run(() => _sender.Response(user)
|
_ = Task.Run(() => _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(_sender.CreateEmbed(user?.GuildId)
|
||||||
.WithDescription($"You've been muted in {user.Guild} server")
|
.WithDescription($"You've been muted in {user.Guild} server")
|
||||||
.AddField("Mute Type", type.ToString())
|
.AddField("Mute Type", type.ToString())
|
||||||
.AddField("Moderator", mod.ToString())
|
.AddField("Moderator", mod.ToString())
|
||||||
@@ -140,7 +140,7 @@ public class MuteService : INService
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_ = Task.Run(() => _sender.Response(user)
|
_ = Task.Run(() => _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(_sender.CreateEmbed(user.GuildId)
|
||||||
.WithDescription($"You've been unmuted in {user.Guild} server")
|
.WithDescription($"You've been unmuted in {user.Guild} server")
|
||||||
.AddField("Unmute Type", type.ToString())
|
.AddField("Unmute Type", type.ToString())
|
||||||
.AddField("Moderator", mod.ToString())
|
.AddField("Moderator", mod.ToString())
|
||||||
|
23
src/NadekoBot/Modules/Administration/Notify/INotifyModel.cs
Normal file
23
src/NadekoBot/Modules/Administration/Notify/INotifyModel.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public interface INotifyModel
|
||||||
|
{
|
||||||
|
static abstract string KeyName { get; }
|
||||||
|
static abstract NotifyType NotifyType { get; }
|
||||||
|
IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements();
|
||||||
|
|
||||||
|
public virtual bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public interface INotifySubscriber
|
||||||
|
{
|
||||||
|
Task NotifyAsync<T>(T data, bool isShardLocal = false)
|
||||||
|
where T : struct, INotifyModel;
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
public record struct AddRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.reward.addrole";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.AddRoleReward;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var model = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.user%", g => g.GetUser(model.UserId)?.ToString() ?? model.UserId.ToString() },
|
||||||
|
{ "%event.role%", g => g.GetRole(model.RoleId)?.ToString() ?? model.RoleId.ToString() },
|
||||||
|
{ "%event.level%", g => model.Level.ToString() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public record struct LevelUpNotifyModel(
|
||||||
|
ulong GuildId,
|
||||||
|
ulong ChannelId,
|
||||||
|
ulong UserId,
|
||||||
|
long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.levelup";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.LevelUp;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var data = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.level%", g => data.Level.ToString() },
|
||||||
|
{ "%event.user%", g => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,34 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
|
public record struct ProtectionNotifyModel(ulong GuildId, ProtectionType ProtType, ulong UserId) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.protection";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.Protection;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var data = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.type%", g => data.ProtType.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
public record struct RemoveRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.reward.removerole";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.RemoveRoleReward;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var model = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.user%", g => g.GetUser(model.UserId)?.ToString() ?? model.UserId.ToString() },
|
||||||
|
{ "%event.role%", g => g.GetRole(model.RoleId)?.ToString() ?? model.RoleId.ToString() },
|
||||||
|
{ "%event.level%", g => model.Level.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
114
src/NadekoBot/Modules/Administration/Notify/NotifyCommands.cs
Normal file
114
src/NadekoBot/Modules/Administration/Notify/NotifyCommands.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public partial class Administration
|
||||||
|
{
|
||||||
|
public class NotifyCommands : NadekoModule<NotifyService>
|
||||||
|
{
|
||||||
|
[Cmd]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task Notify()
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.Items(Enum.GetValues<NotifyType>())
|
||||||
|
.PageSize(5)
|
||||||
|
.Page((items, page) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.notify_available));
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
eb.AddField(item.ToString(), GetText(GetDescription(item)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocStr GetDescription(NotifyType item)
|
||||||
|
=> item switch
|
||||||
|
{
|
||||||
|
NotifyType.LevelUp => strs.notify_desc_levelup,
|
||||||
|
NotifyType.Protection => strs.notify_desc_protection,
|
||||||
|
NotifyType.AddRoleReward => strs.notify_desc_addrolerew,
|
||||||
|
NotifyType.RemoveRoleReward => strs.notify_desc_removerolerew,
|
||||||
|
_ => strs.notify_desc_not_found
|
||||||
|
};
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task Notify(NotifyType nType, [Leftover] string? message = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
|
{
|
||||||
|
// show msg
|
||||||
|
var conf = await _service.GetNotifyAsync(ctx.Guild.Id, nType);
|
||||||
|
if (conf is null)
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.notify_msg_not_set).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.notify_msg))
|
||||||
|
.WithDescription(conf.Message.TrimTo(2048))
|
||||||
|
.AddField(GetText(strs.notify_type), conf.Type.ToString(), true)
|
||||||
|
.AddField(GetText(strs.channel),
|
||||||
|
$"""
|
||||||
|
<#{conf.ChannelId}>
|
||||||
|
`{conf.ChannelId}`
|
||||||
|
""",
|
||||||
|
true);
|
||||||
|
|
||||||
|
await Response().Embed(eb).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
|
||||||
|
await Response().Confirm(strs.notify_on($"<#{ctx.Channel.Id}>", Format.Bold(nType.ToString()))).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task NotifyList(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var notifs = await _service.GetForGuildAsync(ctx.Guild.Id);
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var notif in notifs)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"""
|
||||||
|
- **{notif.Type}**
|
||||||
|
<#{notif.ChannelId}> `{notif.ChannelId}`
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifs.Count == 0)
|
||||||
|
sb.AppendLine(GetText(strs.notify_none));
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Confirm(GetText(strs.notify_list), text: sb.ToString())
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task NotifyClear(NotifyType nType)
|
||||||
|
{
|
||||||
|
await _service.DisableAsync(ctx.Guild.Id, nType);
|
||||||
|
await Response().Confirm(strs.notify_off(nType)).SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public static class NotifyKeys
|
||||||
|
{
|
||||||
|
public static TypedKey<LevelUpNotifyModel> LevelUp { get; } = new("notify:levelup");
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public static class NotifyModelExtensions
|
||||||
|
{
|
||||||
|
public static TypedKey<T> GetTypedKey<T>(this T model)
|
||||||
|
where T : struct, INotifyModel
|
||||||
|
=> new(T.KeyName);
|
||||||
|
}
|
227
src/NadekoBot/Modules/Administration/Notify/NotifyService.cs
Normal file
227
src/NadekoBot/Modules/Administration/Notify/NotifyService.cs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Generators;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public sealed class NotifyService : IReadyExecutor, INotifySubscriber, INService
|
||||||
|
{
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly IMessageSenderService _mss;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IBotCreds _creds;
|
||||||
|
private readonly IReplacementService _repSvc;
|
||||||
|
private readonly IPubSub _pubSub;
|
||||||
|
private ConcurrentDictionary<NotifyType, ConcurrentDictionary<ulong, Notify>> _events = new();
|
||||||
|
|
||||||
|
public NotifyService(
|
||||||
|
DbService db,
|
||||||
|
IMessageSenderService mss,
|
||||||
|
DiscordSocketClient client,
|
||||||
|
IBotCreds creds,
|
||||||
|
IReplacementService repSvc,
|
||||||
|
IPubSub pubSub)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_mss = mss;
|
||||||
|
_client = client;
|
||||||
|
_creds = creds;
|
||||||
|
_repSvc = repSvc;
|
||||||
|
_pubSub = pubSub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
_events = (await uow.GetTable<Notify>()
|
||||||
|
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
|
||||||
|
_creds.TotalShards,
|
||||||
|
_client.ShardId))
|
||||||
|
.ToListAsyncLinqToDB())
|
||||||
|
.GroupBy(x => x.Type)
|
||||||
|
.ToDictionary(x => x.Key, x => x.ToDictionary(x => x.GuildId).ToConcurrent())
|
||||||
|
.ToConcurrent();
|
||||||
|
|
||||||
|
|
||||||
|
await SubscribeToEvent<LevelUpNotifyModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SubscribeToEvent<T>()
|
||||||
|
where T : struct, INotifyModel
|
||||||
|
{
|
||||||
|
await _pubSub.Sub(new TypedKey<T>(T.KeyName), async (model) => await OnEvent(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task NotifyAsync<T>(T data, bool isShardLocal = false)
|
||||||
|
where T : struct, INotifyModel
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isShardLocal)
|
||||||
|
{
|
||||||
|
await OnEvent(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _pubSub.Pub(data.GetTypedKey(), data);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex,
|
||||||
|
"Unknown error occurred while trying to triger {NotifyEvent} for {NotifyModel}",
|
||||||
|
T.KeyName,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnEvent<T>(T model)
|
||||||
|
where T : struct, INotifyModel
|
||||||
|
{
|
||||||
|
if (_events.TryGetValue(T.NotifyType, out var subs))
|
||||||
|
{
|
||||||
|
if (model.TryGetGuildId(out var gid))
|
||||||
|
{
|
||||||
|
if (!subs.TryGetValue(gid, out var conf))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await HandleNotifyEvent(conf, model);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in subs.Keys.ToArray())
|
||||||
|
{
|
||||||
|
if (subs.TryGetValue(key, out var notif))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await HandleNotifyEvent(notif, model);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex,
|
||||||
|
"Error occured while sending notification {NotifyEvent} to guild {GuildId}: {ErrorMessage}",
|
||||||
|
T.NotifyType,
|
||||||
|
key,
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleNotifyEvent(Notify conf, INotifyModel model)
|
||||||
|
{
|
||||||
|
var guild = _client.GetGuild(conf.GuildId);
|
||||||
|
var channel = guild?.GetTextChannel(conf.ChannelId);
|
||||||
|
|
||||||
|
if (guild is null || channel is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IUser? user = null;
|
||||||
|
if (model.TryGetUserId(out var userId))
|
||||||
|
{
|
||||||
|
user = guild.GetUser(userId) ?? _client.GetUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rctx = new ReplacementContext(guild: guild, channel: channel, user: user);
|
||||||
|
|
||||||
|
var st = SmartText.CreateFrom(conf.Message);
|
||||||
|
foreach (var modelRep in model.GetReplacements())
|
||||||
|
{
|
||||||
|
rctx.WithOverride(modelRep.Key, () => modelRep.Value(guild));
|
||||||
|
}
|
||||||
|
|
||||||
|
st = await _repSvc.ReplaceAsync(st, rctx);
|
||||||
|
if (st is SmartPlainText spt)
|
||||||
|
{
|
||||||
|
await _mss.Response(channel)
|
||||||
|
.Confirm(spt.Text)
|
||||||
|
.SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _mss.Response(channel)
|
||||||
|
.Text(st)
|
||||||
|
.Sanitize(false)
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task EnableAsync(
|
||||||
|
ulong guildId,
|
||||||
|
ulong channelId,
|
||||||
|
NotifyType nType,
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
await uow.GetTable<Notify>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
Type = nType,
|
||||||
|
Message = message,
|
||||||
|
},
|
||||||
|
(_) => new()
|
||||||
|
{
|
||||||
|
Message = message,
|
||||||
|
ChannelId = channelId
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
Type = nType
|
||||||
|
});
|
||||||
|
|
||||||
|
var eventDict = _events.GetOrAdd(nType, _ => new());
|
||||||
|
eventDict[guildId] = new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
Type = nType,
|
||||||
|
Message = message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DisableAsync(ulong guildId, NotifyType nType)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var deleted = await uow.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Type == nType)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
if (deleted == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_events.TryGetValue(nType, out var guildsDict))
|
||||||
|
return;
|
||||||
|
|
||||||
|
guildsDict.TryRemove(guildId, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<Notify>> GetForGuildAsync(ulong guildId, int page = 0)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(page);
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var list = await ctx.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderBy(x => x.Type)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Notify?> GetNotifyAsync(ulong guildId, NotifyType nType)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
return await ctx.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Type == nType)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
}
|
||||||
|
}
|
@@ -36,7 +36,7 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task DiscordPermOverrideReset()
|
public async Task DiscordPermOverrideReset()
|
||||||
{
|
{
|
||||||
var result = await PromptUserConfirmAsync(_sender.CreateEmbed()
|
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.perm_override_all_confirm)));
|
.WithDescription(GetText(strs.perm_override_all_confirm)));
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ public partial class Administration
|
|||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
var eb = _sender.CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
|
var eb = CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
|
||||||
|
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
eb.WithDescription(GetText(strs.perm_override_page_none));
|
eb.WithDescription(GetText(strs.perm_override_page_none));
|
||||||
|
@@ -6,7 +6,7 @@ namespace NadekoBot.Modules.Administration;
|
|||||||
public partial class Administration
|
public partial class Administration
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class PlayingRotateCommands : NadekoModule<PlayingRotateService>
|
public partial class PlayingRotateCommands : NadekoModule<IBotActivityService>
|
||||||
{
|
{
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
|
@@ -28,17 +28,17 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task AntiAlt(
|
public async Task AntiAlt(
|
||||||
StoopidTime minAge,
|
ParsedTimespan minAge,
|
||||||
PunishmentAction action,
|
PunishmentAction action,
|
||||||
[Leftover] StoopidTime punishTime = null)
|
[Leftover] ParsedTimespan punishTimespan = null)
|
||||||
{
|
{
|
||||||
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
||||||
var punishTimeMinutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
var punishTimeMinutes = (int?)punishTimespan?.Time.TotalMinutes ?? 0;
|
||||||
|
|
||||||
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var minutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
var minutes = (int?)punishTimespan?.Time.TotalMinutes ?? 0;
|
||||||
if (action is PunishmentAction.TimeOut && minutes < 1)
|
if (action is PunishmentAction.TimeOut && minutes < 1)
|
||||||
minutes = 1;
|
minutes = 1;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ public partial class Administration
|
|||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task AntiAlt(StoopidTime minAge, PunishmentAction action, [Leftover] IRole role)
|
public async Task AntiAlt(ParsedTimespan minAge, PunishmentAction action, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
var minAgeMinutes = (int)minAge.Time.TotalMinutes;
|
||||||
|
|
||||||
@@ -86,8 +86,8 @@ public partial class Administration
|
|||||||
int userThreshold,
|
int userThreshold,
|
||||||
int seconds,
|
int seconds,
|
||||||
PunishmentAction action,
|
PunishmentAction action,
|
||||||
[Leftover] StoopidTime punishTime)
|
[Leftover] ParsedTimespan punishTimespan)
|
||||||
=> InternalAntiRaid(userThreshold, seconds, action, punishTime);
|
=> InternalAntiRaid(userThreshold, seconds, action, punishTimespan);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
@@ -100,7 +100,7 @@ public partial class Administration
|
|||||||
int userThreshold,
|
int userThreshold,
|
||||||
int seconds = 10,
|
int seconds = 10,
|
||||||
PunishmentAction action = PunishmentAction.Mute,
|
PunishmentAction action = PunishmentAction.Mute,
|
||||||
StoopidTime punishTime = null)
|
ParsedTimespan punishTimespan = null)
|
||||||
{
|
{
|
||||||
if (action == PunishmentAction.AddRole)
|
if (action == PunishmentAction.AddRole)
|
||||||
{
|
{
|
||||||
@@ -120,13 +120,13 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (punishTime is not null)
|
if (punishTimespan is not null)
|
||||||
{
|
{
|
||||||
if (!_service.IsDurationAllowed(action))
|
if (!_service.IsDurationAllowed(action))
|
||||||
await Response().Error(strs.prot_cant_use_time).SendAsync();
|
await Response().Error(strs.prot_cant_use_time).SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
var time = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
var time = (int?)punishTimespan?.Time.TotalMinutes ?? 0;
|
||||||
if (time is < 0 or > 60 * 24)
|
if (time is < 0 or > 60 * 24)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -170,8 +170,8 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] StoopidTime punishTime)
|
public Task AntiSpam(int messageCount, PunishmentAction action, [Leftover] ParsedTimespan punishTimespan)
|
||||||
=> InternalAntiSpam(messageCount, action, punishTime);
|
=> InternalAntiSpam(messageCount, action, punishTimespan);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
@@ -183,19 +183,19 @@ public partial class Administration
|
|||||||
private async Task InternalAntiSpam(
|
private async Task InternalAntiSpam(
|
||||||
int messageCount,
|
int messageCount,
|
||||||
PunishmentAction action,
|
PunishmentAction action,
|
||||||
StoopidTime timeData = null,
|
ParsedTimespan timespanData = null,
|
||||||
IRole role = null)
|
IRole role = null)
|
||||||
{
|
{
|
||||||
if (messageCount is < 2 or > 10)
|
if (messageCount is < 2 or > 10)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (timeData is not null)
|
if (timespanData is not null)
|
||||||
{
|
{
|
||||||
if (!_service.IsDurationAllowed(action))
|
if (!_service.IsDurationAllowed(action))
|
||||||
await Response().Error(strs.prot_cant_use_time).SendAsync();
|
await Response().Error(strs.prot_cant_use_time).SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
var time = (int?)timeData?.Time.TotalMinutes ?? 0;
|
var time = (int?)timespanData?.Time.TotalMinutes ?? 0;
|
||||||
if (time is < 0 or > 60 * 24)
|
if (time is < 0 or > 60 * 24)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active));
|
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active));
|
||||||
|
|
||||||
if (spam is not null)
|
if (spam is not null)
|
||||||
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);
|
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);
|
||||||
|
@@ -22,6 +22,7 @@ public class ProtectionService : INService
|
|||||||
private readonly MuteService _mute;
|
private readonly MuteService _mute;
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
private readonly UserPunishService _punishService;
|
private readonly UserPunishService _punishService;
|
||||||
|
private readonly INotifySubscriber _notifySub;
|
||||||
|
|
||||||
private readonly Channel<PunishQueueItem> _punishUserQueue =
|
private readonly Channel<PunishQueueItem> _punishUserQueue =
|
||||||
Channel.CreateUnbounded<PunishQueueItem>(new()
|
Channel.CreateUnbounded<PunishQueueItem>(new()
|
||||||
@@ -35,12 +36,14 @@ public class ProtectionService : INService
|
|||||||
IBot bot,
|
IBot bot,
|
||||||
MuteService mute,
|
MuteService mute,
|
||||||
DbService db,
|
DbService db,
|
||||||
UserPunishService punishService)
|
UserPunishService punishService,
|
||||||
|
INotifySubscriber notifySub)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_mute = mute;
|
_mute = mute;
|
||||||
_db = db;
|
_db = db;
|
||||||
_punishService = punishService;
|
_punishService = punishService;
|
||||||
|
_notifySub = notifySub;
|
||||||
|
|
||||||
var ids = client.GetGuildIds();
|
var ids = client.GetGuildIds();
|
||||||
using (var uow = db.GetDbContext())
|
using (var uow = db.GetDbContext())
|
||||||
@@ -175,6 +178,9 @@ public class ProtectionService : INService
|
|||||||
alts.RoleId,
|
alts.RoleId,
|
||||||
user);
|
user);
|
||||||
|
|
||||||
|
await _notifySub.NotifyAsync(new ProtectionNotifyModel(user.Guild.Id,
|
||||||
|
ProtectionType.Alting,
|
||||||
|
user.Id));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,6 +200,8 @@ public class ProtectionService : INService
|
|||||||
var settings = stats.AntiRaidSettings;
|
var settings = stats.AntiRaidSettings;
|
||||||
|
|
||||||
await PunishUsers(settings.Action, ProtectionType.Raiding, settings.PunishDuration, null, users);
|
await PunishUsers(settings.Action, ProtectionType.Raiding, settings.PunishDuration, null, users);
|
||||||
|
await _notifySub.NotifyAsync(
|
||||||
|
new ProtectionNotifyModel(user.Guild.Id, ProtectionType.Raiding, users[0].Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(1000 * stats.AntiRaidSettings.Seconds);
|
await Task.Delay(1000 * stats.AntiRaidSettings.Seconds);
|
||||||
@@ -246,6 +254,10 @@ public class ProtectionService : INService
|
|||||||
settings.MuteTime,
|
settings.MuteTime,
|
||||||
settings.RoleId,
|
settings.RoleId,
|
||||||
(IGuildUser)msg.Author);
|
(IGuildUser)msg.Author);
|
||||||
|
|
||||||
|
await _notifySub.NotifyAsync(new ProtectionNotifyModel(channel.GuildId,
|
||||||
|
ProtectionType.Spamming,
|
||||||
|
msg.Author.Id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -113,7 +113,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
await progressMsg.ModifyAsync(props =>
|
await progressMsg.ModifyAsync(props =>
|
||||||
{
|
{
|
||||||
props.Embed = _sender.CreateEmbed()
|
props.Embed = CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
||||||
.Build();
|
.Build();
|
||||||
|
302
src/NadekoBot/Modules/Administration/Role/ButtonRolesCommands.cs
Normal file
302
src/NadekoBot/Modules/Administration/Role/ButtonRolesCommands.cs
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
using NadekoBot.Common.TypeReaders.Models;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
using System.Text;
|
||||||
|
using ContextType = Discord.Commands.ContextType;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public partial class Administration
|
||||||
|
{
|
||||||
|
[Group("btr")]
|
||||||
|
public partial class ButtonRoleCommands : NadekoModule<ButtonRolesService>
|
||||||
|
{
|
||||||
|
private List<ActionRowBuilder> GetActionRows(IReadOnlyList<ButtonRole> roles)
|
||||||
|
{
|
||||||
|
var rows = roles.Select((x, i) => (Index: i, ButtonRole: x))
|
||||||
|
.GroupBy(x => x.Index / 5)
|
||||||
|
.Select(x => x.Select(y => y.ButtonRole))
|
||||||
|
.Select(x =>
|
||||||
|
{
|
||||||
|
var ab = new ActionRowBuilder()
|
||||||
|
.WithComponents(x.Select(y =>
|
||||||
|
{
|
||||||
|
var curRole = ctx.Guild.GetRole(y.RoleId);
|
||||||
|
var label = string.IsNullOrWhiteSpace(y.Label)
|
||||||
|
? curRole?.ToString() ?? "?missing " + y.RoleId
|
||||||
|
: y.Label;
|
||||||
|
|
||||||
|
var btnEmote = EmoteTypeReader.TryParse(y.Emote, out var e)
|
||||||
|
? e
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return new ButtonBuilder()
|
||||||
|
.WithCustomId(y.ButtonId)
|
||||||
|
.WithEmote(btnEmote)
|
||||||
|
.WithLabel(label)
|
||||||
|
.WithStyle(ButtonStyle.Secondary)
|
||||||
|
.Build() as IMessageComponent;
|
||||||
|
})
|
||||||
|
.ToList());
|
||||||
|
|
||||||
|
return ab;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<MessageLink?> CreateMessageLinkAsync(ulong messageId)
|
||||||
|
{
|
||||||
|
var msg = await ctx.Channel.GetMessageAsync(messageId);
|
||||||
|
if (msg is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new MessageLink(ctx.Guild, ctx.Channel, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleAdd(ulong messageId, IEmote emote, [Leftover] IRole role)
|
||||||
|
{
|
||||||
|
var link = await CreateMessageLinkAsync(messageId);
|
||||||
|
|
||||||
|
if (link is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.invalid_message_id).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await BtnRoleAdd(link, emote, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleAdd(MessageLink link, IEmote emote, [Leftover] IRole role)
|
||||||
|
{
|
||||||
|
if (link.Message is not IUserMessage msg || !msg.IsAuthor(ctx.Client))
|
||||||
|
{
|
||||||
|
await Response().Error(strs.invalid_message_link).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await CheckRoleHierarchy(role))
|
||||||
|
{
|
||||||
|
await Response().Error(strs.hierarchy).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = await _service.AddButtonRole(ctx.Guild.Id, link.Channel.Id, role.Id, link.Message.Id, emote);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_message_max).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles = await _service.GetButtonRoles(ctx.Guild.Id, link.Message.Id);
|
||||||
|
|
||||||
|
var rows = GetActionRows(roles);
|
||||||
|
|
||||||
|
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().WithRows(rows).Build()));
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemove(ulong messageId, IRole role)
|
||||||
|
=> BtnRoleRemove(messageId, role.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemove(MessageLink link, IRole role)
|
||||||
|
=> BtnRoleRemove(link.Message.Id, role.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemove(MessageLink link, ulong roleId)
|
||||||
|
=> BtnRoleRemove(link.Message.Id, roleId);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleRemove(ulong messageId, ulong roleId)
|
||||||
|
{
|
||||||
|
var removed = await _service.RemoveButtonRole(ctx.Guild.Id, messageId, roleId);
|
||||||
|
if (removed is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles = await _service.GetButtonRoles(ctx.Guild.Id, messageId);
|
||||||
|
|
||||||
|
var ch = await ctx.Guild.GetTextChannelAsync(removed.ChannelId);
|
||||||
|
|
||||||
|
if (ch is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = await ch.GetMessageAsync(removed.MessageId) as IUserMessage;
|
||||||
|
|
||||||
|
if (msg is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows = GetActionRows(roles);
|
||||||
|
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().WithRows(rows).Build()));
|
||||||
|
await Response().Confirm(strs.btnrole_removed).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemoveAll(MessageLink link)
|
||||||
|
=> BtnRoleRemoveAll(link.Message.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleRemoveAll(ulong messageId)
|
||||||
|
{
|
||||||
|
var succ = await _service.RemoveButtonRoles(ctx.Guild.Id, messageId);
|
||||||
|
|
||||||
|
if (succ.Count == 0)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = succ[0];
|
||||||
|
|
||||||
|
var ch = await ctx.Guild.GetTextChannelAsync(info.ChannelId);
|
||||||
|
if (ch is null)
|
||||||
|
{
|
||||||
|
await Response().Pending(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = await ch.GetMessageAsync(info.MessageId) as IUserMessage;
|
||||||
|
if (msg is null)
|
||||||
|
{
|
||||||
|
await Response().Pending(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().Build()));
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleList()
|
||||||
|
{
|
||||||
|
var btnRoles = await _service.GetButtonRoles(ctx.Guild.Id, null);
|
||||||
|
|
||||||
|
var groups = btnRoles
|
||||||
|
.GroupBy(x => (x.ChannelId, x.MessageId))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.Items(groups)
|
||||||
|
.PageSize(1)
|
||||||
|
.AddFooter(false)
|
||||||
|
.Page(async (items, page) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var item = items.FirstOrDefault();
|
||||||
|
if (item == default)
|
||||||
|
{
|
||||||
|
eb.WithPendingColor()
|
||||||
|
.WithDescription(GetText(strs.btnrole_none));
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (cid, msgId) = item.Key;
|
||||||
|
|
||||||
|
var str = new StringBuilder();
|
||||||
|
|
||||||
|
var ch = await ctx.Client.GetChannelAsync(cid) as IMessageChannel;
|
||||||
|
|
||||||
|
str.AppendLine($"Channel: {ch?.ToString() ?? cid.ToString()}");
|
||||||
|
str.AppendLine($"Message: {msgId}");
|
||||||
|
|
||||||
|
if (ch is not null)
|
||||||
|
{
|
||||||
|
var msg = await ch.GetMessageAsync(msgId);
|
||||||
|
if (msg is not null)
|
||||||
|
{
|
||||||
|
str.AppendLine(new MessageLink(ctx.Guild, ch, msg).ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str.AppendLine("---");
|
||||||
|
|
||||||
|
foreach (var x in item.AsEnumerable())
|
||||||
|
{
|
||||||
|
var role = ctx.Guild.GetRole(x.RoleId);
|
||||||
|
|
||||||
|
str.AppendLine($"{x.Emote} {(role?.ToString() ?? x.RoleId.ToString())}");
|
||||||
|
}
|
||||||
|
|
||||||
|
eb.WithDescription(str.ToString());
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleExclusive(MessageLink link, PermissionAction exclusive)
|
||||||
|
=> BtnRoleExclusive(link.Message.Id, exclusive);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleExclusive(ulong messageId, PermissionAction exclusive)
|
||||||
|
{
|
||||||
|
var res = await _service.SetExclusiveButtonRoles(ctx.Guild.Id, messageId, exclusive.Value);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclusive.Value)
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.btnrole_exclusive).SendAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.btnrole_multiple).SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
187
src/NadekoBot/Modules/Administration/Role/ButtonRolesService.cs
Normal file
187
src/NadekoBot/Modules/Administration/Role/ButtonRolesService.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using LinqToDB.SqlQuery;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NCalc;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
|
public sealed class ButtonRolesService : INService, IReadyExecutor
|
||||||
|
{
|
||||||
|
private const string BTN_PREFIX = "n:btnrole:";
|
||||||
|
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IBotCreds _creds;
|
||||||
|
|
||||||
|
public ButtonRolesService(IBotCreds creds, DiscordSocketClient client, DbService db)
|
||||||
|
{
|
||||||
|
_creds = creds;
|
||||||
|
_client = client;
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
_client.InteractionCreated += OnInteraction;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnInteraction(SocketInteraction inter)
|
||||||
|
{
|
||||||
|
if (inter is not SocketMessageComponent smc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!smc.Data.CustomId.StartsWith(BTN_PREFIX))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await inter.DeferAsync();
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var buttonRole = await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.ButtonId == smc.Data.CustomId && x.MessageId == smc.Message.Id)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
|
if (buttonRole is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var guild = _client.GetGuild(buttonRole.GuildId);
|
||||||
|
if (guild is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var role = guild.GetRole(buttonRole.RoleId);
|
||||||
|
if (role is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (smc.User is not IGuildUser user)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (user.GetRoles().Any(x => x.Id == role.Id))
|
||||||
|
{
|
||||||
|
await user.RemoveRoleAsync(role.Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonRole.Exclusive)
|
||||||
|
{
|
||||||
|
var otherRoles = await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == smc.GuildId && x.MessageId == smc.Message.Id)
|
||||||
|
.Select(x => x.RoleId)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
await user.RemoveRolesAsync(otherRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
await user.AddRoleAsync(role.Id);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Unable to handle button role interaction for user {UserId}", inter.User.Id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AddButtonRole(
|
||||||
|
ulong guildId,
|
||||||
|
ulong channelId,
|
||||||
|
ulong roleId,
|
||||||
|
ulong messageId,
|
||||||
|
IEmote emote
|
||||||
|
)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
// up to 25 per message
|
||||||
|
if (await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.MessageId == messageId)
|
||||||
|
.CountAsyncLinqToDB()
|
||||||
|
>= 25)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
var emoteStr = emote.ToString()!;
|
||||||
|
var guid = Guid.NewGuid();
|
||||||
|
await uow.GetTable<ButtonRole>()
|
||||||
|
.InsertOrUpdateAsync(() => new ButtonRole()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
RoleId = roleId,
|
||||||
|
MessageId = messageId,
|
||||||
|
Position =
|
||||||
|
uow
|
||||||
|
.GetTable<ButtonRole>()
|
||||||
|
.Any(x => x.MessageId == messageId)
|
||||||
|
? uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.MessageId == messageId)
|
||||||
|
.Max(x => x.Position)
|
||||||
|
: 1,
|
||||||
|
Emote = emoteStr,
|
||||||
|
Label = string.Empty,
|
||||||
|
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}",
|
||||||
|
Exclusive = uow.GetTable<ButtonRole>()
|
||||||
|
.Any(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
&& uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
.All(x => x.Exclusive)
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
Emote = emoteStr,
|
||||||
|
Label = string.Empty,
|
||||||
|
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}"
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
RoleId = roleId,
|
||||||
|
MessageId = messageId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ButtonRole>> RemoveButtonRoles(ulong guildId, ulong messageId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ButtonRole?> RemoveButtonRole(ulong guildId, ulong messageId, ulong roleId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var deleted = await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId && x.RoleId == roleId)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
|
||||||
|
return deleted.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ButtonRole>> GetButtonRoles(ulong guildId, ulong? messageId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && (messageId == null || x.MessageId == messageId))
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetExclusiveButtonRoles(ulong guildId, ulong messageId, bool exclusive)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
.UpdateAsync((_) => new()
|
||||||
|
{
|
||||||
|
Exclusive = exclusive
|
||||||
|
}) > 0;
|
||||||
|
}
|
||||||
|
}
|
@@ -80,7 +80,7 @@ public partial class Administration
|
|||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var content = string.Empty;
|
var content = string.Empty;
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
#nullable disable
|
using LinqToDB;
|
||||||
using LinqToDB;
|
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Modules.Patronage;
|
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using OneOf.Types;
|
using OneOf.Types;
|
||||||
using OneOf;
|
using OneOf;
|
||||||
@@ -18,18 +16,15 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
|
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
|
||||||
private readonly object _cacheLock = new();
|
private readonly object _cacheLock = new();
|
||||||
private readonly SemaphoreSlim _assignementLock = new(1, 1);
|
private readonly SemaphoreSlim _assignementLock = new(1, 1);
|
||||||
private readonly IPatronageService _ps;
|
|
||||||
|
|
||||||
public ReactionRolesService(
|
public ReactionRolesService(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
IPatronageService ps,
|
|
||||||
DbService db,
|
DbService db,
|
||||||
IBotCreds creds)
|
IBotCreds creds)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_client = client;
|
_client = client;
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
_ps = ps;
|
|
||||||
_cache = new();
|
_cache = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +52,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
var guild = _client.GetGuild(rero.GuildId);
|
var guild = _client.GetGuild(rero.GuildId);
|
||||||
var role = guild?.GetRole(rero.RoleId);
|
var role = guild?.GetRole(rero.RoleId);
|
||||||
|
|
||||||
if (role is null)
|
if (guild is null || role is null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var user = guild.GetUser(userId) as IGuildUser
|
var user = guild.GetUser(userId) as IGuildUser
|
||||||
@@ -96,7 +91,8 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
{
|
{
|
||||||
if (user.RoleIds.Contains(role.Id))
|
if (user.RoleIds.Contains(role.Id))
|
||||||
{
|
{
|
||||||
await user.RemoveRoleAsync(role.Id, new RequestOptions()
|
await user.RemoveRoleAsync(role.Id,
|
||||||
|
new RequestOptions()
|
||||||
{
|
{
|
||||||
AuditLogReason = $"Reaction role"
|
AuditLogReason = $"Reaction role"
|
||||||
});
|
});
|
||||||
@@ -210,7 +206,8 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.AddRoleAsync(role.Id, new()
|
await user.AddRoleAsync(role.Id,
|
||||||
|
new()
|
||||||
{
|
{
|
||||||
AuditLogReason = "Reaction role"
|
AuditLogReason = "Reaction role"
|
||||||
});
|
});
|
||||||
@@ -244,23 +241,10 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
int levelReq = 0)
|
int levelReq = 0)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(group);
|
ArgumentOutOfRangeException.ThrowIfNegative(group);
|
||||||
|
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
|
ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
|
||||||
|
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
await using var tran = await ctx.Database.BeginTransactionAsync();
|
|
||||||
var activeReactionRoles = await ctx.GetTable<ReactionRoleV2>()
|
|
||||||
.Where(x => x.GuildId == guild.Id)
|
|
||||||
.CountAsync();
|
|
||||||
|
|
||||||
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>()
|
await ctx.GetTable<ReactionRoleV2>()
|
||||||
.InsertOrUpdateAsync(() => new()
|
.InsertOrUpdateAsync(() => new()
|
||||||
{
|
{
|
||||||
@@ -286,8 +270,6 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
Emote = emote,
|
Emote = emote,
|
||||||
});
|
});
|
||||||
|
|
||||||
await tran.CommitAsync();
|
|
||||||
|
|
||||||
var obj = new ReactionRoleV2()
|
var obj = new ReactionRoleV2()
|
||||||
{
|
{
|
||||||
GuildId = guild.Id,
|
GuildId = guild.Id,
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using NadekoBot.Common.TypeReaders.Models;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using Color = SixLabors.ImageSharp.Color;
|
using Color = SixLabors.ImageSharp.Color;
|
||||||
|
|
||||||
@@ -13,13 +15,18 @@ public partial class Administration
|
|||||||
Excl
|
Excl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly TempRoleService _tempRoleService;
|
||||||
private readonly IServiceProvider _services;
|
private readonly IServiceProvider _services;
|
||||||
private StickyRolesService _stickyRoleSvc;
|
private StickyRolesService _stickyRoleSvc;
|
||||||
|
|
||||||
public RoleCommands(IServiceProvider services, StickyRolesService stickyRoleSvc)
|
public RoleCommands(
|
||||||
|
IServiceProvider services,
|
||||||
|
StickyRolesService stickyRoleSvc,
|
||||||
|
TempRoleService tempRoleService)
|
||||||
{
|
{
|
||||||
_services = services;
|
_services = services;
|
||||||
_stickyRoleSvc = stickyRoleSvc;
|
_stickyRoleSvc = stickyRoleSvc;
|
||||||
|
_tempRoleService = tempRoleService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -34,13 +41,16 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await targetUser.AddRoleAsync(roleToAdd, new RequestOptions()
|
await targetUser.AddRoleAsync(roleToAdd,
|
||||||
|
new RequestOptions()
|
||||||
{
|
{
|
||||||
AuditLogReason = $"Added by [{ctx.User.Username}]"
|
AuditLogReason = $"Added by [{ctx.User.Username}]"
|
||||||
});
|
});
|
||||||
|
|
||||||
await Response().Confirm(strs.setrole(Format.Bold(roleToAdd.Name),
|
await Response()
|
||||||
Format.Bold(targetUser.ToString()))).SendAsync();
|
.Confirm(strs.setrole(Format.Bold(roleToAdd.Name),
|
||||||
|
Format.Bold(targetUser.ToString())))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -62,8 +72,10 @@ public partial class Administration
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await targetUser.RemoveRoleAsync(roleToRemove);
|
await targetUser.RemoveRoleAsync(roleToRemove);
|
||||||
await Response().Confirm(strs.remrole(Format.Bold(roleToRemove.Name),
|
await Response()
|
||||||
Format.Bold(targetUser.ToString()))).SendAsync();
|
.Confirm(strs.remrole(Format.Bold(roleToRemove.Name),
|
||||||
|
Format.Bold(targetUser.ToString())))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -174,12 +186,11 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task RoleColor(Color color, [Leftover] IRole role)
|
public async Task RoleColor(Rgba32 color, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var rgba32 = color.ToPixel<Rgba32>();
|
await role.ModifyAsync(r => r.Color = new Discord.Color(color.R, color.G, color.B));
|
||||||
await role.ModifyAsync(r => r.Color = new Discord.Color(rgba32.R, rgba32.G, rgba32.B));
|
|
||||||
await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync();
|
await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
@@ -205,5 +216,29 @@ public partial class Administration
|
|||||||
await Response().Confirm(strs.sticky_roles_disabled).SendAsync();
|
await Response().Confirm(strs.sticky_roles_disabled).SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
public async Task TempRole(ParsedTimespan timespan, IUser user, [Leftover] IRole role)
|
||||||
|
{
|
||||||
|
if (!await CheckRoleHierarchy(role))
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Error(strs.hierarchy)
|
||||||
|
.SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _tempRoleService.AddTempRoleAsync(ctx.Guild.Id, role.Id, user.Id, timespan.Time);
|
||||||
|
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Confirm(strs.temp_role_added(user.Mention,
|
||||||
|
Format.Bold(role.Name),
|
||||||
|
TimestampTag.FromDateTime(DateTime.UtcNow.Add(timespan.Time), TimestampTagStyles.Relative)))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
140
src/NadekoBot/Modules/Administration/Role/TempRoleService.cs
Normal file
140
src/NadekoBot/Modules/Administration/Role/TempRoleService.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public class TempRoleService : IReadyExecutor, INService
|
||||||
|
{
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IBotCreds _creds;
|
||||||
|
|
||||||
|
private TaskCompletionSource<bool> _tcs = new();
|
||||||
|
|
||||||
|
public TempRoleService(
|
||||||
|
DbService db,
|
||||||
|
DiscordSocketClient client,
|
||||||
|
IBotCreds creds)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_client = client;
|
||||||
|
_creds = creds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddTempRoleAsync(
|
||||||
|
ulong guildId,
|
||||||
|
ulong roleId,
|
||||||
|
ulong userId,
|
||||||
|
TimeSpan duration)
|
||||||
|
{
|
||||||
|
if (duration == TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
await uow.GetTable<TempRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||||
|
.DeleteAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var until = DateTime.UtcNow.Add(duration);
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
await ctx.GetTable<TempRole>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
RoleId = roleId,
|
||||||
|
UserId = userId,
|
||||||
|
Remove = false,
|
||||||
|
ExpiresAt = until
|
||||||
|
},
|
||||||
|
(old) => new()
|
||||||
|
{
|
||||||
|
ExpiresAt = until,
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
UserId = userId,
|
||||||
|
RoleId = roleId
|
||||||
|
});
|
||||||
|
|
||||||
|
_tcs.TrySetResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
var latest = await _db.GetDbContext()
|
||||||
|
.GetTable<TempRole>()
|
||||||
|
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
|
||||||
|
_creds.TotalShards,
|
||||||
|
_client.ShardId))
|
||||||
|
.OrderBy(x => x.ExpiresAt)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
|
if (latest == default)
|
||||||
|
{
|
||||||
|
await _tcs.Task;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
if (latest.ExpiresAt > now)
|
||||||
|
{
|
||||||
|
await Task.WhenAny(Task.Delay(latest.ExpiresAt - now), _tcs.Task);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var deleted = await _db.GetDbContext()
|
||||||
|
.GetTable<TempRole>()
|
||||||
|
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId,
|
||||||
|
_creds.TotalShards,
|
||||||
|
_client.ShardId)
|
||||||
|
&& x.ExpiresAt <= now)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
|
||||||
|
foreach (var d in deleted)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await RemoveRole(d);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Log.Warning("Unable to remove temp role {RoleId} from user {UserId}",
|
||||||
|
d.RoleId,
|
||||||
|
d.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Unexpected error occurred in temprole loop");
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveRole(TempRole tempRole)
|
||||||
|
{
|
||||||
|
var guild = _client.GetGuild(tempRole.GuildId);
|
||||||
|
|
||||||
|
var role = guild?.GetRole(tempRole.RoleId);
|
||||||
|
if (role is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var user = guild?.GetUser(tempRole.UserId);
|
||||||
|
if (user is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await user.RemoveRoleAsync(role);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,37 +1,135 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration.Services;
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
public sealed class PlayingRotateService : INService, IReadyExecutor
|
public sealed class BotActivityService : IBotActivityService, IReadyExecutor, INService
|
||||||
{
|
{
|
||||||
private readonly BotConfigService _bss;
|
private readonly TypedKey<ActivityPubData> _activitySetKey = new("activity.set");
|
||||||
private readonly SelfService _selfService;
|
|
||||||
private readonly IReplacementService _repService;
|
|
||||||
// private readonly Replacer _rep;
|
|
||||||
private readonly DbService _db;
|
|
||||||
private readonly DiscordSocketClient _client;
|
|
||||||
|
|
||||||
public PlayingRotateService(
|
private readonly IPubSub _pubSub;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly IReplacementService _rep;
|
||||||
|
private readonly BotConfigService _bss;
|
||||||
|
|
||||||
|
public BotActivityService(
|
||||||
|
IPubSub pubSub,
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
DbService db,
|
DbService db,
|
||||||
BotConfigService bss,
|
IReplacementService rep,
|
||||||
IEnumerable<IPlaceholderProvider> phProviders,
|
BotConfigService bss)
|
||||||
SelfService selfService,
|
|
||||||
IReplacementService repService)
|
|
||||||
{
|
{
|
||||||
_db = db;
|
_pubSub = pubSub;
|
||||||
_bss = bss;
|
|
||||||
_selfService = selfService;
|
|
||||||
_repService = repService;
|
|
||||||
_client = client;
|
_client = client;
|
||||||
|
_db = db;
|
||||||
|
_rep = rep;
|
||||||
|
_bss = bss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> RemovePlayingAsync(int index)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
||||||
|
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var toRemove = await uow.Set<RotatingPlayingStatus>()
|
||||||
|
.AsQueryable()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Skip(index)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (toRemove is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
uow.Remove(toRemove);
|
||||||
|
await uow.SaveChangesAsync();
|
||||||
|
return toRemove.Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddPlaying(ActivityType activityType, string status)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var toAdd = new RotatingPlayingStatus
|
||||||
|
{
|
||||||
|
Status = status,
|
||||||
|
Type = (NadekoBot.Db.DbActivityType)activityType
|
||||||
|
};
|
||||||
|
uow.Add(toAdd);
|
||||||
|
await uow.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisableRotatePlaying()
|
||||||
|
{
|
||||||
|
_bss.ModifyConfig(bs => { bs.RotateStatuses = false; });
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToggleRotatePlaying()
|
||||||
|
{
|
||||||
|
var enabled = false;
|
||||||
|
_bss.ModifyConfig(bs => { enabled = bs.RotateStatuses = !bs.RotateStatuses; });
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<RotatingPlayingStatus> GetRotatingStatuses()
|
||||||
|
{
|
||||||
|
using var uow = _db.GetDbContext();
|
||||||
|
return uow.Set<RotatingPlayingStatus>().AsNoTracking().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SetActivityAsync(string game, ActivityType? type)
|
||||||
|
=> _pubSub.Pub(_activitySetKey,
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = game,
|
||||||
|
Link = null,
|
||||||
|
Type = type
|
||||||
|
});
|
||||||
|
|
||||||
|
public Task SetStreamAsync(string name, string link)
|
||||||
|
=> _pubSub.Pub(_activitySetKey,
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Link = link,
|
||||||
|
Type = ActivityType.Streaming
|
||||||
|
});
|
||||||
|
|
||||||
|
private sealed class ActivityPubData
|
||||||
|
{
|
||||||
|
public string Name { get; init; }
|
||||||
|
public string Link { get; init; }
|
||||||
|
public ActivityType? Type { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
|
await _pubSub.Sub(_activitySetKey,
|
||||||
|
async data =>
|
||||||
|
{
|
||||||
|
if (_client.ShardId == 0)
|
||||||
|
{
|
||||||
|
DisableRotatePlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (data.Type is { } activityType)
|
||||||
|
{
|
||||||
|
await _client.SetGameAsync(data.Name, data.Link, activityType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _client.SetCustomStatusAsync(data.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Error setting activity");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (_client.ShardId != 0)
|
if (_client.ShardId != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -57,8 +155,8 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
? rotatingStatuses[index = 0]
|
? rotatingStatuses[index = 0]
|
||||||
: rotatingStatuses[index++];
|
: rotatingStatuses[index++];
|
||||||
|
|
||||||
var statusText = await _repService.ReplaceAsync(playingStatus.Status, new (client: _client));
|
var statusText = await _rep.ReplaceAsync(playingStatus.Status, new(client: _client));
|
||||||
await _selfService.SetActivityAsync(statusText, (ActivityType)playingStatus.Type);
|
await SetActivityAsync(statusText, (ActivityType)playingStatus.Type);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -66,44 +164,4 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> RemovePlayingAsync(int index)
|
|
||||||
{
|
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
|
||||||
|
|
||||||
await using var uow = _db.GetDbContext();
|
|
||||||
var toRemove = await uow.Set<RotatingPlayingStatus>().AsQueryable().AsNoTracking().Skip(index).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
if (toRemove is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
uow.Remove(toRemove);
|
|
||||||
await uow.SaveChangesAsync();
|
|
||||||
return toRemove.Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AddPlaying(ActivityType activityType, string status)
|
|
||||||
{
|
|
||||||
await using var uow = _db.GetDbContext();
|
|
||||||
var toAdd = new RotatingPlayingStatus
|
|
||||||
{
|
|
||||||
Status = status,
|
|
||||||
Type = (NadekoBot.Db.DbActivityType)activityType
|
|
||||||
};
|
|
||||||
uow.Add(toAdd);
|
|
||||||
await uow.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ToggleRotatePlaying()
|
|
||||||
{
|
|
||||||
var enabled = false;
|
|
||||||
_bss.ModifyConfig(bs => { enabled = bs.RotateStatuses = !bs.RotateStatuses; });
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyList<RotatingPlayingStatus> GetRotatingStatuses()
|
|
||||||
{
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
return uow.Set<RotatingPlayingStatus>().AsNoTracking().ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
|
public interface IBotActivityService
|
||||||
|
{
|
||||||
|
Task SetActivityAsync(string game, ActivityType? type);
|
||||||
|
Task SetStreamAsync(string name, string link);
|
||||||
|
bool ToggleRotatePlaying();
|
||||||
|
Task AddPlaying(ActivityType statusType, string status);
|
||||||
|
Task<string> RemovePlayingAsync(int index);
|
||||||
|
IReadOnlyList<RotatingPlayingStatus> GetRotatingStatuses();
|
||||||
|
}
|
@@ -24,19 +24,22 @@ public partial class Administration
|
|||||||
private readonly IMedusaLoaderService _medusaLoader;
|
private readonly IMedusaLoaderService _medusaLoader;
|
||||||
private readonly ICoordinator _coord;
|
private readonly ICoordinator _coord;
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
|
private readonly IBotActivityService _bas;
|
||||||
|
|
||||||
public SelfCommands(
|
public SelfCommands(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
DbService db,
|
DbService db,
|
||||||
IBotStrings strings,
|
IBotStrings strings,
|
||||||
ICoordinator coord,
|
ICoordinator coord,
|
||||||
IMedusaLoaderService medusaLoader)
|
IMedusaLoaderService medusaLoader,
|
||||||
|
IBotActivityService bas)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_db = db;
|
_db = db;
|
||||||
_strings = strings;
|
_strings = strings;
|
||||||
_coord = coord;
|
_coord = coord;
|
||||||
_medusaLoader = medusaLoader;
|
_medusaLoader = medusaLoader;
|
||||||
|
_bas = bas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +65,7 @@ public partial class Administration
|
|||||||
var (added, updated) = await _service.RefreshUsersAsync(users);
|
var (added, updated) = await _service.RefreshUsersAsync(users);
|
||||||
|
|
||||||
await message.ModifyAsync(x =>
|
await message.ModifyAsync(x =>
|
||||||
x.Embed = _sender.CreateEmbed()
|
x.Embed = CreateEmbed()
|
||||||
.WithDescription(GetText(strs.cache_users_done(added, updated)))
|
.WithDescription(GetText(strs.cache_users_done(added, updated)))
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.Build()
|
.Build()
|
||||||
@@ -115,7 +118,7 @@ public partial class Administration
|
|||||||
_service.AddNewAutoCommand(cmd);
|
_service.AddNewAutoCommand(cmd);
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.scadd))
|
.WithTitle(GetText(strs.scadd))
|
||||||
.AddField(GetText(strs.server),
|
.AddField(GetText(strs.server),
|
||||||
@@ -343,7 +346,7 @@ public partial class Administration
|
|||||||
if (string.IsNullOrWhiteSpace(str))
|
if (string.IsNullOrWhiteSpace(str))
|
||||||
str = GetText(strs.no_shards_on_page);
|
str = GetText(strs.no_shards_on_page);
|
||||||
|
|
||||||
return _sender.CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}");
|
return CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}");
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
@@ -496,7 +499,7 @@ public partial class Administration
|
|||||||
// var rep = new ReplacementBuilder().WithDefault(Context).Build();
|
// var rep = new ReplacementBuilder().WithDefault(Context).Build();
|
||||||
|
|
||||||
var repCtx = new ReplacementContext(ctx);
|
var repCtx = new ReplacementContext(ctx);
|
||||||
await _service.SetActivityAsync(game is null ? game : await repSvc.ReplaceAsync(game, repCtx), type);
|
await _bas.SetActivityAsync(game is null ? game : await repSvc.ReplaceAsync(game, repCtx), type);
|
||||||
|
|
||||||
await Response().Confirm(strs.set_activity).SendAsync();
|
await Response().Confirm(strs.set_activity).SendAsync();
|
||||||
}
|
}
|
||||||
@@ -518,7 +521,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
name ??= "";
|
name ??= "";
|
||||||
|
|
||||||
await _service.SetStreamAsync(name, url);
|
await _bas.SetStreamAsync(name, url);
|
||||||
|
|
||||||
await Response().Confirm(strs.set_stream).SendAsync();
|
await Response().Confirm(strs.set_stream).SendAsync();
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
private readonly IMessageSenderService _sender;
|
private readonly IMessageSenderService _sender;
|
||||||
|
|
||||||
//keys
|
//keys
|
||||||
private readonly TypedKey<ActivityPubData> _activitySetKey;
|
|
||||||
private readonly TypedKey<string> _guildLeaveKey;
|
private readonly TypedKey<string> _guildLeaveKey;
|
||||||
|
|
||||||
public SelfService(
|
public SelfService(
|
||||||
@@ -51,11 +50,8 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
_bss = bss;
|
_bss = bss;
|
||||||
_pubSub = pubSub;
|
_pubSub = pubSub;
|
||||||
_sender = sender;
|
_sender = sender;
|
||||||
_activitySetKey = new("activity.set");
|
|
||||||
_guildLeaveKey = new("guild.leave");
|
_guildLeaveKey = new("guild.leave");
|
||||||
|
|
||||||
HandleStatusChanges();
|
|
||||||
|
|
||||||
_pubSub.Sub(_guildLeaveKey,
|
_pubSub.Sub(_guildLeaveKey,
|
||||||
async input =>
|
async input =>
|
||||||
{
|
{
|
||||||
@@ -71,7 +67,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
if (server.OwnerId != _client.CurrentUser.Id)
|
if (server.OwnerId != _client.CurrentUser.Id)
|
||||||
{
|
{
|
||||||
await server.LeaveAsync();
|
await server.LeaveAsync();
|
||||||
Log.Information("Left server {Name} [{Id}]", server.Name, server.Id);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -395,49 +390,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
return channelId is not null;
|
return channelId is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleStatusChanges()
|
|
||||||
=> _pubSub.Sub(_activitySetKey,
|
|
||||||
async data =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (data.Type is { } activityType)
|
|
||||||
await _client.SetGameAsync(data.Name, data.Link, activityType);
|
|
||||||
else
|
|
||||||
await _client.SetCustomStatusAsync(data.Name);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning(ex, "Error setting activity");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public Task SetActivityAsync(string game, ActivityType? type)
|
|
||||||
=> _pubSub.Pub(_activitySetKey,
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Name = game,
|
|
||||||
Link = null,
|
|
||||||
Type = type
|
|
||||||
});
|
|
||||||
|
|
||||||
public Task SetStreamAsync(string name, string link)
|
|
||||||
=> _pubSub.Pub(_activitySetKey,
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Link = link,
|
|
||||||
Type = ActivityType.Streaming
|
|
||||||
});
|
|
||||||
|
|
||||||
private sealed class ActivityPubData
|
|
||||||
{
|
|
||||||
public string Name { get; init; }
|
|
||||||
public string Link { get; init; }
|
|
||||||
public ActivityType? Type { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the specified <paramref name="users"/> to the database. If a database user with placeholder name
|
/// Adds the specified <paramref name="users"/> to the database. If a database user with placeholder name
|
||||||
/// and discriminator is present in <paramref name="users"/>, their name and discriminator get updated accordingly.
|
/// and discriminator is present in <paramref name="users"/>, their name and discriminator get updated accordingly.
|
||||||
|
@@ -6,16 +6,136 @@ namespace NadekoBot.Modules.Administration;
|
|||||||
|
|
||||||
public partial class Administration
|
public partial class Administration
|
||||||
{
|
{
|
||||||
[Group]
|
public partial class SelfAssignedRolesHelpers : NadekoModule<SelfAssignedRolesService>
|
||||||
|
{
|
||||||
|
private readonly SarAssignerService _sas;
|
||||||
|
|
||||||
|
public SelfAssignedRolesHelpers(SarAssignerService sas)
|
||||||
|
{
|
||||||
|
_sas = sas;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Iam([Leftover] IRole role)
|
||||||
|
{
|
||||||
|
var guildUser = (IGuildUser)ctx.User;
|
||||||
|
|
||||||
|
var group = await _service.GetRoleGroup(ctx.Guild.Id, role.Id);
|
||||||
|
|
||||||
|
IUserMessage msg = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (group is null)
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<SarAssignResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
await _sas.Add(new()
|
||||||
|
{
|
||||||
|
Group = group,
|
||||||
|
RoleId = role.Id,
|
||||||
|
User = guildUser,
|
||||||
|
CompletionTask = tcs
|
||||||
|
});
|
||||||
|
|
||||||
|
var res = await tcs.Task;
|
||||||
|
|
||||||
|
if (res.TryPickT0(out _, out var error))
|
||||||
|
{
|
||||||
|
msg = await Response()
|
||||||
|
.Confirm(strs.self_assign_success(Format.Bold(role.Name)))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var resStr = error.Match(
|
||||||
|
_ => strs.error_occured,
|
||||||
|
lvlReq => strs.self_assign_not_level(Format.Bold(lvlReq.Level.ToString())),
|
||||||
|
roleRq => strs.self_assign_role_req(Format.Bold(ctx.Guild.GetRole(roleRq.RoleId).ToString()
|
||||||
|
?? "missing role " + roleRq.RoleId),
|
||||||
|
group.Name),
|
||||||
|
_ => strs.self_assign_already(Format.Bold(role.Name)),
|
||||||
|
_ => strs.self_assign_perms);
|
||||||
|
|
||||||
|
msg = await Response().Error(resStr).SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var ad = _service.GetAutoDelete(ctx.Guild.Id);
|
||||||
|
|
||||||
|
if (ad)
|
||||||
|
{
|
||||||
|
msg?.DeleteAfter(3);
|
||||||
|
ctx.Message.DeleteAfter(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Iamnot([Leftover] IRole role)
|
||||||
|
{
|
||||||
|
var guildUser = (IGuildUser)ctx.User;
|
||||||
|
|
||||||
|
IUserMessage msg = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!guildUser.RoleIds.Contains(role.Id))
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_not_have(Format.Bold(role.Name))).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var group = await _service.GetRoleGroup(ctx.Guild.Id, role.Id);
|
||||||
|
|
||||||
|
if (group is null || group.Roles.All(x => x.RoleId != role.Id))
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role.Position >= ((SocketGuild)ctx.Guild).CurrentUser.Roles.Max(x => x.Position))
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_perms).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await guildUser.RemoveRoleAsync(role);
|
||||||
|
msg = await Response().Confirm(strs.self_assign_remove(Format.Bold(role.Name))).SendAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var ad = _service.GetAutoDelete(ctx.Guild.Id);
|
||||||
|
if (ad)
|
||||||
|
{
|
||||||
|
msg?.DeleteAfter(3);
|
||||||
|
ctx.Message.DeleteAfter(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Group("sar")]
|
||||||
public partial class SelfAssignedRolesCommands : NadekoModule<SelfAssignedRolesService>
|
public partial class SelfAssignedRolesCommands : NadekoModule<SelfAssignedRolesService>
|
||||||
{
|
{
|
||||||
|
private readonly SarAssignerService _sas;
|
||||||
|
|
||||||
|
public SelfAssignedRolesCommands(SarAssignerService sas)
|
||||||
|
{
|
||||||
|
_sas = sas;
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageMessages)]
|
[UserPerm(GuildPerm.ManageMessages)]
|
||||||
[BotPerm(GuildPerm.ManageMessages)]
|
[BotPerm(GuildPerm.ManageMessages)]
|
||||||
public async Task AdSarm()
|
public async Task SarAutoDelete()
|
||||||
{
|
{
|
||||||
var newVal = _service.ToggleAdSarm(ctx.Guild.Id);
|
var newVal = await _service.ToggleAutoDelete(ctx.Guild.Id);
|
||||||
|
|
||||||
if (newVal)
|
if (newVal)
|
||||||
await Response().Confirm(strs.adsarm_enable(prefix)).SendAsync();
|
await Response().Confirm(strs.adsarm_enable(prefix)).SendAsync();
|
||||||
@@ -28,40 +148,34 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public Task Asar([Leftover] IRole role)
|
public Task SarAdd([Leftover] IRole role)
|
||||||
=> Asar(0, role);
|
=> SarAdd(0, role);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Asar(int group, [Leftover] IRole role)
|
public async Task SarAdd(int group, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var guser = (IGuildUser)ctx.User;
|
if (!await CheckRoleHierarchy(role))
|
||||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var succ = _service.AddNew(ctx.Guild.Id, role, group);
|
await _service.AddAsync(ctx.Guild.Id, role.Id, group);
|
||||||
|
|
||||||
if (succ)
|
|
||||||
{
|
|
||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.role_added(Format.Bold(role.Name), Format.Bold(group.ToString())))
|
.Confirm(strs.role_added(Format.Bold(role.Name), Format.Bold(group.ToString())))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
await Response().Error(strs.role_in_list(Format.Bold(role.Name))).SendAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Sargn(int group, [Leftover] string name = null)
|
public async Task SarGroupName(int group, [Leftover] string name = null)
|
||||||
{
|
{
|
||||||
var set = await _service.SetNameAsync(ctx.Guild.Id, group, name);
|
var set = await _service.SetGroupNameAsync(ctx.Guild.Id, group, name);
|
||||||
|
|
||||||
if (set)
|
if (set)
|
||||||
{
|
{
|
||||||
@@ -70,19 +184,19 @@ public partial class Administration
|
|||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await Response().Confirm(strs.group_name_removed(Format.Bold(group.ToString()))).SendAsync();
|
await Response().Confirm(strs.group_name_removed(Format.Bold(group.ToString()))).SendAsync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task Rsar([Leftover] IRole role)
|
public async Task SarRemove([Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var guser = (IGuildUser)ctx.User;
|
var guser = (IGuildUser)ctx.User;
|
||||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var success = _service.RemoveSar(role.Guild.Id, role.Id);
|
var success = await _service.RemoveAsync(role.Guild.Id, role.Id);
|
||||||
if (!success)
|
if (!success)
|
||||||
await Response().Error(strs.self_assign_not).SendAsync();
|
await Response().Error(strs.self_assign_not).SendAsync();
|
||||||
else
|
else
|
||||||
@@ -91,59 +205,81 @@ public partial class Administration
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Lsar(int page = 1)
|
public async Task SarList(int page = 1)
|
||||||
{
|
{
|
||||||
if (--page < 0)
|
if (--page < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (exclusive, roles, groups) = _service.GetRoles(ctx.Guild);
|
var groups = await _service.GetSarsAsync(ctx.Guild.Id);
|
||||||
|
|
||||||
|
var gDict = groups.ToDictionary(x => x.Id, x => x);
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Paginated()
|
.Paginated()
|
||||||
.Items(roles.OrderBy(x => x.Model.Group).ToList())
|
.Items(groups.SelectMany(x => x.Roles).ToList())
|
||||||
.PageSize(20)
|
.PageSize(20)
|
||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page(async (items, _) =>
|
||||||
{
|
{
|
||||||
var rolesStr = new StringBuilder();
|
|
||||||
var roleGroups = items
|
var roleGroups = items
|
||||||
.GroupBy(x => x.Model.Group)
|
.GroupBy(x => x.SarGroupId)
|
||||||
.OrderBy(x => x.Key);
|
.OrderBy(x => x.Key);
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.self_assign_list(groups.Sum(x => x.Roles.Count))));
|
||||||
|
|
||||||
foreach (var kvp in roleGroups)
|
foreach (var kvp in roleGroups)
|
||||||
{
|
{
|
||||||
string groupNameText;
|
var group = gDict[kvp.Key];
|
||||||
if (!groups.TryGetValue(kvp.Key, out var name))
|
|
||||||
groupNameText = Format.Bold(GetText(strs.self_assign_group(kvp.Key)));
|
|
||||||
else
|
|
||||||
groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}");
|
|
||||||
|
|
||||||
rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫");
|
var groupNameText = "";
|
||||||
foreach (var (model, role) in kvp.AsEnumerable())
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(group.Name))
|
||||||
|
groupNameText += $" **{group.Name}**";
|
||||||
|
|
||||||
|
groupNameText = $"`{group.GroupNumber}` {groupNameText}";
|
||||||
|
|
||||||
|
var rolesStr = new StringBuilder();
|
||||||
|
|
||||||
|
if (group.IsExclusive)
|
||||||
{
|
{
|
||||||
if (role is null)
|
rolesStr.AppendLine(Format.Italics(GetText(strs.choose_one)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.RoleReq is ulong rrId)
|
||||||
{
|
{
|
||||||
|
var rr = ctx.Guild.GetRole(rrId);
|
||||||
|
|
||||||
|
if (rr is null)
|
||||||
|
{
|
||||||
|
await _service.SetGroupRoleReq(group.GuildId, group.GroupNumber, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// first character is invisible space
|
rolesStr.AppendLine(
|
||||||
if (model.LevelRequirement == 0)
|
Format.Italics(GetText(strs.requires_role(Format.Bold(rr.Name)))));
|
||||||
rolesStr.AppendLine(" " + role.Name);
|
|
||||||
else
|
|
||||||
rolesStr.AppendLine(" " + role.Name + $" (lvl {model.LevelRequirement}+)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var sar in kvp)
|
||||||
|
{
|
||||||
|
var roleName = (ctx.Guild.GetRole(sar.RoleId)?.Name ?? (sar.RoleId + " (deleted)"));
|
||||||
|
rolesStr.Append("- " + Format.Code(roleName));
|
||||||
|
|
||||||
|
if (sar.LevelReq > 0)
|
||||||
|
{
|
||||||
|
rolesStr.Append($" *[lvl {sar.LevelReq}+]*");
|
||||||
|
}
|
||||||
|
|
||||||
rolesStr.AppendLine();
|
rolesStr.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _sender.CreateEmbed()
|
|
||||||
.WithOkColor()
|
eb.AddField(groupNameText, rolesStr, false);
|
||||||
.WithTitle(Format.Bold(GetText(strs.self_assign_list(roles.Count()))))
|
}
|
||||||
.WithDescription(rolesStr.ToString())
|
|
||||||
.WithFooter(exclusive
|
return eb;
|
||||||
? GetText(strs.self_assign_are_exclusive)
|
|
||||||
: GetText(strs.self_assign_are_not_exclusive));
|
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
@@ -152,25 +288,36 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task Togglexclsar()
|
public async Task SarExclusive(int groupNumber)
|
||||||
|
{
|
||||||
|
var areExclusive = await _service.SetGroupExclusivityAsync(ctx.Guild.Id, groupNumber);
|
||||||
|
|
||||||
|
if (areExclusive is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.sar_group_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areExclusive is true)
|
||||||
{
|
{
|
||||||
var areExclusive = _service.ToggleEsar(ctx.Guild.Id);
|
|
||||||
if (areExclusive)
|
|
||||||
await Response().Confirm(strs.self_assign_excl).SendAsync();
|
await Response().Confirm(strs.self_assign_excl).SendAsync();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await Response().Confirm(strs.self_assign_no_excl).SendAsync();
|
await Response().Confirm(strs.self_assign_no_excl).SendAsync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task RoleLevelReq(int level, [Leftover] IRole role)
|
public async Task SarRoleLevelReq(int level, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
if (level < 0)
|
if (level < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var succ = _service.SetLevelReq(ctx.Guild.Id, role, level);
|
var succ = await _service.SetRoleLevelReq(ctx.Guild.Id, role.Id, level);
|
||||||
|
|
||||||
if (!succ)
|
if (!succ)
|
||||||
{
|
{
|
||||||
@@ -186,54 +333,35 @@ public partial class Administration
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Iam([Leftover] IRole role)
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
public async Task SarGroupRoleReq(int groupNumber, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var guildUser = (IGuildUser)ctx.User;
|
var succ = await _service.SetGroupRoleReq(ctx.Guild.Id, groupNumber, role.Id);
|
||||||
|
|
||||||
var (result, autoDelete, extra) = await _service.Assign(guildUser, role);
|
if (!succ)
|
||||||
|
|
||||||
IUserMessage msg;
|
|
||||||
if (result == SelfAssignedRolesService.AssignResult.ErrNotAssignable)
|
|
||||||
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.AssignResult.ErrLvlReq)
|
|
||||||
msg = await Response().Error(strs.self_assign_not_level(Format.Bold(extra.ToString()))).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.AssignResult.ErrAlreadyHave)
|
|
||||||
msg = await Response().Error(strs.self_assign_already(Format.Bold(role.Name))).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.AssignResult.ErrNotPerms)
|
|
||||||
msg = await Response().Error(strs.self_assign_perms).SendAsync();
|
|
||||||
else
|
|
||||||
msg = await Response().Confirm(strs.self_assign_success(Format.Bold(role.Name))).SendAsync();
|
|
||||||
|
|
||||||
if (autoDelete)
|
|
||||||
{
|
{
|
||||||
msg.DeleteAfter(3);
|
await Response().Error(strs.sar_group_not_found).SendAsync();
|
||||||
ctx.Message.DeleteAfter(3);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Confirm(strs.self_assign_group_role_req(
|
||||||
|
Format.Bold(groupNumber.ToString()),
|
||||||
|
Format.Bold(role.Name)))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Iamnot([Leftover] IRole role)
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
|
public async Task SarGroupDelete(int groupNumber)
|
||||||
{
|
{
|
||||||
var guildUser = (IGuildUser)ctx.User;
|
var succ = await _service.DeleteRoleGroup(ctx.Guild.Id, groupNumber);
|
||||||
|
if (succ)
|
||||||
var (result, autoDelete) = await _service.Remove(guildUser, role);
|
await Response().Confirm(strs.sar_group_deleted(Format.Bold(groupNumber.ToString()))).SendAsync();
|
||||||
|
|
||||||
IUserMessage msg;
|
|
||||||
if (result == SelfAssignedRolesService.RemoveResult.ErrNotAssignable)
|
|
||||||
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.RemoveResult.ErrNotHave)
|
|
||||||
msg = await Response().Error(strs.self_assign_not_have(Format.Bold(role.Name))).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.RemoveResult.ErrNotPerms)
|
|
||||||
msg = await Response().Error(strs.self_assign_perms).SendAsync();
|
|
||||||
else
|
else
|
||||||
msg = await Response().Confirm(strs.self_assign_remove(Format.Bold(role.Name))).SendAsync();
|
await Response().Error(strs.sar_group_not_found).SendAsync();
|
||||||
|
|
||||||
if (autoDelete)
|
|
||||||
{
|
|
||||||
msg.DeleteAfter(3);
|
|
||||||
ctx.Message.DeleteAfter(3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,233 +1,335 @@
|
|||||||
#nullable disable
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
using OneOf;
|
||||||
|
using OneOf.Types;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration.Services;
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
public class SelfAssignedRolesService : INService
|
public class SelfAssignedRolesService : INService, IReadyExecutor
|
||||||
{
|
{
|
||||||
public enum AssignResult
|
|
||||||
{
|
|
||||||
Assigned, // successfully removed
|
|
||||||
ErrNotAssignable, // not assignable (error)
|
|
||||||
ErrAlreadyHave, // you already have that role (error)
|
|
||||||
ErrNotPerms, // bot doesn't have perms (error)
|
|
||||||
ErrLvlReq // you are not required level (error)
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RemoveResult
|
|
||||||
{
|
|
||||||
Removed, // successfully removed
|
|
||||||
ErrNotAssignable, // not assignable (error)
|
|
||||||
ErrNotHave, // you don't have a role you want to remove (error)
|
|
||||||
ErrNotPerms // bot doesn't have perms (error)
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IBotCreds _creds;
|
||||||
|
|
||||||
public SelfAssignedRolesService(DbService db)
|
private ConcurrentHashSet<ulong> _sarAds = new();
|
||||||
=> _db = db;
|
|
||||||
|
|
||||||
public bool AddNew(ulong guildId, IRole role, int group)
|
public SelfAssignedRolesService(DbService db, DiscordSocketClient client, IBotCreds creds)
|
||||||
{
|
{
|
||||||
using var uow = _db.GetDbContext();
|
_db = db;
|
||||||
var roles = uow.Set<SelfAssignedRole>().GetFromGuild(guildId);
|
_client = client;
|
||||||
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
|
_creds = creds;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
uow.Set<SelfAssignedRole>().Add(new()
|
public async Task AddAsync(ulong guildId, ulong roleId, int groupNumber)
|
||||||
{
|
{
|
||||||
Group = group,
|
await using var ctx = _db.GetDbContext();
|
||||||
RoleId = role.Id,
|
|
||||||
GuildId = role.Guild.Id
|
await ctx.GetTable<SarGroup>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
GroupNumber = groupNumber,
|
||||||
|
IsExclusive = false
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
GroupNumber = groupNumber
|
||||||
});
|
});
|
||||||
uow.SaveChanges();
|
|
||||||
|
await ctx.GetTable<Sar>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
RoleId = roleId,
|
||||||
|
LevelReq = 0,
|
||||||
|
GuildId = guildId,
|
||||||
|
SarGroupId = ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.Select(x => x.Id)
|
||||||
|
.First()
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
SarGroupId = ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.Select(x => x.Id)
|
||||||
|
.First()
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
RoleId = roleId,
|
||||||
|
GuildId = guildId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> RemoveAsync(ulong guildId, ulong roleId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var deleted = await ctx.GetTable<Sar>()
|
||||||
|
.Where(x => x.RoleId == roleId && x.GuildId == guildId)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
return deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetGroupNameAsync(ulong guildId, int groupNumber, string? name)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var changes = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.UpdateAsync(x => new()
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
});
|
||||||
|
|
||||||
|
return changes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<SarGroup>> GetSarsAsync(ulong guildId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var sgs = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.LoadWith(x => x.Roles)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
return sgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetRoleLevelReq(ulong guildId, ulong roleId, int levelReq)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var changes = await ctx.GetTable<Sar>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.RoleId == roleId)
|
||||||
|
.UpdateAsync(_ => new()
|
||||||
|
{
|
||||||
|
LevelReq = levelReq,
|
||||||
|
});
|
||||||
|
|
||||||
|
return changes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetGroupRoleReq(ulong guildId, int groupNumber, ulong? roleId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var changes = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.UpdateAsync(_ => new()
|
||||||
|
{
|
||||||
|
RoleReq = roleId
|
||||||
|
});
|
||||||
|
|
||||||
|
return changes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool?> SetGroupExclusivityAsync(ulong guildId, int groupNumber)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var changes = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.UpdateWithOutputAsync(old => new()
|
||||||
|
{
|
||||||
|
IsExclusive = !old.IsExclusive
|
||||||
|
},
|
||||||
|
(o, n) => n.IsExclusive);
|
||||||
|
|
||||||
|
if (changes.Length == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SarGroup?> GetRoleGroup(ulong guildId, ulong roleId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var group = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Roles.Any(x => x.RoleId == roleId))
|
||||||
|
.LoadWith(x => x.Roles)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteRoleGroup(ulong guildId, int groupNumber)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var deleted = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
return deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ToggleAutoDelete(ulong guildId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var delted = await ctx.GetTable<SarAutoDelete>()
|
||||||
|
.DeleteAsync(x => x.GuildId == guildId);
|
||||||
|
|
||||||
|
if (delted > 0)
|
||||||
|
{
|
||||||
|
_sarAds.TryRemove(guildId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.GetTable<SarAutoDelete>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
IsEnabled = true,
|
||||||
|
GuildId = guildId,
|
||||||
|
},
|
||||||
|
(_) => new()
|
||||||
|
{
|
||||||
|
IsEnabled = true
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId
|
||||||
|
});
|
||||||
|
|
||||||
|
_sarAds.Add(guildId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToggleAdSarm(ulong guildId)
|
public bool GetAutoDelete(ulong guildId)
|
||||||
|
=> _sarAds.Contains(guildId);
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
bool newval;
|
await using var uow = _db.GetDbContext();
|
||||||
using var uow = _db.GetDbContext();
|
var guilds = await uow.GetTable<SarAutoDelete>()
|
||||||
var config = uow.GuildConfigsForId(guildId, set => set);
|
.Where(x => x.IsEnabled && Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
|
||||||
newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages;
|
.Select(x => x.GuildId)
|
||||||
uow.SaveChanges();
|
.ToListAsyncLinqToDB();
|
||||||
return newval;
|
|
||||||
|
_sarAds = new(guilds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(AssignResult Result, bool AutoDelete, object extra)> Assign(IGuildUser guildUser, IRole role)
|
public sealed class SarAssignerService : INService, IReadyExecutor
|
||||||
{
|
{
|
||||||
LevelStats userLevelData;
|
private readonly XpService _xp;
|
||||||
await using (var uow = _db.GetDbContext())
|
private readonly DbService _db;
|
||||||
|
|
||||||
|
private readonly Channel<SarAssignerDataItem> _channel =
|
||||||
|
Channel.CreateBounded<SarAssignerDataItem>(100);
|
||||||
|
|
||||||
|
|
||||||
|
public SarAssignerService(XpService xp, DbService db)
|
||||||
{
|
{
|
||||||
var stats = uow.GetOrCreateUserXpStats(guildUser.Guild.Id, guildUser.Id);
|
_xp = xp;
|
||||||
userLevelData = new(stats.Xp + stats.AwardedXp);
|
_db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (autoDelete, exclusive, roles) = GetAdAndRoles(guildUser.Guild.Id);
|
public async Task OnReadyAsync()
|
||||||
|
|
||||||
var theRoleYouWant = roles.FirstOrDefault(r => r.RoleId == role.Id);
|
|
||||||
if (theRoleYouWant is null)
|
|
||||||
return (AssignResult.ErrNotAssignable, autoDelete, null);
|
|
||||||
if (theRoleYouWant.LevelRequirement > userLevelData.Level)
|
|
||||||
return (AssignResult.ErrLvlReq, autoDelete, theRoleYouWant.LevelRequirement);
|
|
||||||
if (guildUser.RoleIds.Contains(role.Id))
|
|
||||||
return (AssignResult.ErrAlreadyHave, autoDelete, null);
|
|
||||||
|
|
||||||
var roleIds = roles.Where(x => x.Group == theRoleYouWant.Group).Select(x => x.RoleId).ToArray();
|
|
||||||
if (exclusive)
|
|
||||||
{
|
{
|
||||||
var sameRoles = guildUser.RoleIds.Where(r => roleIds.Contains(r));
|
var reader = _channel.Reader;
|
||||||
|
while (true)
|
||||||
foreach (var roleId in sameRoles)
|
|
||||||
{
|
{
|
||||||
var sameRole = guildUser.Guild.GetRole(roleId);
|
var item = await reader.ReadAsync();
|
||||||
if (sameRole is not null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await guildUser.RemoveRoleAsync(sameRole);
|
|
||||||
await Task.Delay(300);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await guildUser.AddRoleAsync(role);
|
var sar = item.Group.Roles.First(x => x.RoleId == item.RoleId);
|
||||||
|
|
||||||
|
if (item.User.RoleIds.Contains(item.RoleId))
|
||||||
|
{
|
||||||
|
item.CompletionTask.TrySetResult(new SarAlreadyHasRole());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Group.RoleReq is { } rid)
|
||||||
|
{
|
||||||
|
if (!item.User.RoleIds.Contains(rid))
|
||||||
|
{
|
||||||
|
item.CompletionTask.TrySetResult(new SarRoleRequirement(rid));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// passed
|
||||||
|
}
|
||||||
|
|
||||||
|
// check level requirement
|
||||||
|
if (sar.LevelReq > 0)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var xpStats = await ctx.GetTable<UserXpStats>().GetGuildUserXp(sar.GuildId, item.User.Id);
|
||||||
|
var lvlData = new LevelStats(xpStats?.Xp ?? 0);
|
||||||
|
|
||||||
|
if (lvlData.Level < sar.LevelReq)
|
||||||
|
{
|
||||||
|
item.CompletionTask.TrySetResult(new SarLevelRequirement(sar.LevelReq));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// passed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Group.IsExclusive)
|
||||||
|
{
|
||||||
|
var rolesToRemove = item.Group.Roles
|
||||||
|
.Where(x => item.User.RoleIds.Contains(x.RoleId))
|
||||||
|
.Select(x => x.RoleId)
|
||||||
|
.ToArray();
|
||||||
|
if (rolesToRemove.Length > 0)
|
||||||
|
await item.User.RemoveRolesAsync(rolesToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
await item.User.AddRoleAsync(item.RoleId);
|
||||||
|
|
||||||
|
item.CompletionTask.TrySetResult(new Success());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return (AssignResult.ErrNotPerms, autoDelete, ex);
|
Log.Error(ex, "Unknown error ocurred in SAR runner: {Error}", ex.Message);
|
||||||
|
item.CompletionTask.TrySetResult(new Error());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (AssignResult.Assigned, autoDelete, null);
|
public async Task Add(SarAssignerDataItem item)
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> SetNameAsync(ulong guildId, int group, string name)
|
|
||||||
{
|
{
|
||||||
var set = false;
|
await _channel.Writer.WriteAsync(item);
|
||||||
await using var uow = _db.GetDbContext();
|
}
|
||||||
var gc = uow.GuildConfigsForId(guildId, y => y.Include(x => x.SelfAssignableRoleGroupNames));
|
|
||||||
var toUpdate = gc.SelfAssignableRoleGroupNames.FirstOrDefault(x => x.Number == group);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
}
|
||||||
|
|
||||||
|
public sealed class SarAssignerDataItem
|
||||||
{
|
{
|
||||||
if (toUpdate is not null)
|
public required SarGroup Group { get; init; }
|
||||||
gc.SelfAssignableRoleGroupNames.Remove(toUpdate);
|
public required IGuildUser User { get; init; }
|
||||||
|
public required ulong RoleId { get; init; }
|
||||||
|
public required TaskCompletionSource<SarAssignResult> CompletionTask { get; init; }
|
||||||
}
|
}
|
||||||
else if (toUpdate is null)
|
|
||||||
|
[GenerateOneOf]
|
||||||
|
public sealed partial class SarAssignResult
|
||||||
|
: OneOfBase<Success, Error, SarLevelRequirement, SarRoleRequirement, SarAlreadyHasRole, SarInsuffPerms>
|
||||||
{
|
{
|
||||||
gc.SelfAssignableRoleGroupNames.Add(new()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Number = group
|
|
||||||
});
|
|
||||||
set = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
toUpdate.Name = name;
|
|
||||||
set = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await uow.SaveChangesAsync();
|
public record class SarLevelRequirement(int Level);
|
||||||
|
|
||||||
return set;
|
public record class SarRoleRequirement(ulong RoleId);
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(RemoveResult Result, bool AutoDelete)> Remove(IGuildUser guildUser, IRole role)
|
public record class SarAlreadyHasRole();
|
||||||
{
|
|
||||||
var (autoDelete, _, roles) = GetAdAndRoles(guildUser.Guild.Id);
|
|
||||||
|
|
||||||
if (roles.FirstOrDefault(r => r.RoleId == role.Id) is null)
|
public record class SarInsuffPerms();
|
||||||
return (RemoveResult.ErrNotAssignable, autoDelete);
|
|
||||||
if (!guildUser.RoleIds.Contains(role.Id))
|
|
||||||
return (RemoveResult.ErrNotHave, autoDelete);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await guildUser.RemoveRoleAsync(role);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return (RemoveResult.ErrNotPerms, autoDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (RemoveResult.Removed, autoDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemoveSar(ulong guildId, ulong roleId)
|
|
||||||
{
|
|
||||||
bool success;
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
success = uow.Set<SelfAssignedRole>().DeleteByGuildAndRoleId(guildId, roleId);
|
|
||||||
uow.SaveChanges();
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool AutoDelete, bool Exclusive, IReadOnlyCollection<SelfAssignedRole>) GetAdAndRoles(ulong guildId)
|
|
||||||
{
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var gc = uow.GuildConfigsForId(guildId, set => set);
|
|
||||||
var autoDelete = gc.AutoDeleteSelfAssignedRoleMessages;
|
|
||||||
var exclusive = gc.ExclusiveSelfAssignedRoles;
|
|
||||||
var roles = uow.Set<SelfAssignedRole>().GetFromGuild(guildId);
|
|
||||||
|
|
||||||
return (autoDelete, exclusive, roles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetLevelReq(ulong guildId, IRole role, int level)
|
|
||||||
{
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var roles = uow.Set<SelfAssignedRole>().GetFromGuild(guildId);
|
|
||||||
var sar = roles.FirstOrDefault(x => x.RoleId == role.Id);
|
|
||||||
if (sar is not null)
|
|
||||||
{
|
|
||||||
sar.LevelRequirement = level;
|
|
||||||
uow.SaveChanges();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ToggleEsar(ulong guildId)
|
|
||||||
{
|
|
||||||
bool areExclusive;
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var config = uow.GuildConfigsForId(guildId, set => set);
|
|
||||||
|
|
||||||
areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
|
|
||||||
uow.SaveChanges();
|
|
||||||
return areExclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool Exclusive, IReadOnlyCollection<(SelfAssignedRole Model, IRole Role)> Roles, IDictionary<int, string>
|
|
||||||
GroupNames
|
|
||||||
) GetRoles(IGuild guild)
|
|
||||||
{
|
|
||||||
var exclusive = false;
|
|
||||||
|
|
||||||
IReadOnlyCollection<(SelfAssignedRole Model, IRole Role)> roles;
|
|
||||||
IDictionary<int, string> groupNames;
|
|
||||||
using (var uow = _db.GetDbContext())
|
|
||||||
{
|
|
||||||
var gc = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.SelfAssignableRoleGroupNames));
|
|
||||||
exclusive = gc.ExclusiveSelfAssignedRoles;
|
|
||||||
groupNames = gc.SelfAssignableRoleGroupNames.ToDictionary(x => x.Number, x => x.Name);
|
|
||||||
var roleModels = uow.Set<SelfAssignedRole>().GetFromGuild(guild.Id);
|
|
||||||
roles = roleModels.Select(x => (Model: x, Role: guild.GetRole(x.RoleId)))
|
|
||||||
.ToList();
|
|
||||||
uow.Set<SelfAssignedRole>().RemoveRange(roles.Where(x => x.Role is null).Select(x => x.Model).ToArray());
|
|
||||||
uow.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (exclusive, roles.Where(x => x.Role is not null).ToList(), groupNames);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -35,7 +35,7 @@ public partial class Administration
|
|||||||
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
|
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
|
||||||
?? new List<IgnoredLogItem>();
|
?? new List<IgnoredLogItem>();
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.AddField(GetText(strs.log_ignored_channels),
|
.AddField(GetText(strs.log_ignored_channels),
|
||||||
chs.Count == 0
|
chs.Count == 0
|
||||||
|
@@ -42,7 +42,7 @@ public partial class Administration
|
|||||||
.Items(timezoneStrings)
|
.Items(timezoneStrings)
|
||||||
.PageSize(timezonesPerPage)
|
.PageSize(timezonesPerPage)
|
||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) => _sender.CreateEmbed()
|
.Page((items, _) => CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.timezones_available))
|
.WithTitle(GetText(strs.timezones_available))
|
||||||
.WithDescription(string.Join("\n", items)))
|
.WithDescription(string.Join("\n", items)))
|
||||||
|
@@ -23,27 +23,6 @@ public partial class Administration
|
|||||||
_mute = mute;
|
_mute = mute;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> CheckRoleHierarchy(IGuildUser target)
|
|
||||||
{
|
|
||||||
var curUser = ((SocketGuild)ctx.Guild).CurrentUser;
|
|
||||||
var ownerId = ctx.Guild.OwnerId;
|
|
||||||
var modMaxRole = ((IGuildUser)ctx.User).GetRoles().Max(r => r.Position);
|
|
||||||
var targetMaxRole = target.GetRoles().Max(r => r.Position);
|
|
||||||
var botMaxRole = curUser.GetRoles().Max(r => r.Position);
|
|
||||||
// bot can't punish a user who is higher in the hierarchy. Discord will return 403
|
|
||||||
// moderator can be owner, in which case role hierarchy doesn't matter
|
|
||||||
// otherwise, moderator has to have a higher role
|
|
||||||
if (botMaxRole <= targetMaxRole
|
|
||||||
|| (ctx.User.Id != ownerId && targetMaxRole >= modMaxRole)
|
|
||||||
|| target.Id == ownerId)
|
|
||||||
{
|
|
||||||
await Response().Error(strs.hierarchy).SendAsync();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
@@ -65,7 +44,7 @@ public partial class Administration
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _sender.Response(user)
|
await _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithErrorColor()
|
.WithErrorColor()
|
||||||
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
|
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
|
||||||
.AddField(GetText(strs.moderator), ctx.User.ToString())
|
.AddField(GetText(strs.moderator), ctx.User.ToString())
|
||||||
@@ -85,7 +64,7 @@ public partial class Administration
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Warning(ex, "Exception occured while warning a user");
|
Log.Warning(ex, "Exception occured while warning a user");
|
||||||
var errorEmbed = _sender.CreateEmbed()
|
var errorEmbed = CreateEmbed()
|
||||||
.WithErrorColor()
|
.WithErrorColor()
|
||||||
.WithDescription(GetText(strs.cant_apply_punishment));
|
.WithDescription(GetText(strs.cant_apply_punishment));
|
||||||
|
|
||||||
@@ -96,7 +75,7 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed().WithOkColor();
|
var embed = CreateEmbed().WithOkColor();
|
||||||
if (punishment is null)
|
if (punishment is null)
|
||||||
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
|
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
|
||||||
else
|
else
|
||||||
@@ -205,7 +184,7 @@ public partial class Administration
|
|||||||
.Page((warnings, page) =>
|
.Page((warnings, page) =>
|
||||||
{
|
{
|
||||||
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
|
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
|
||||||
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
|
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
|
||||||
|
|
||||||
if (!warnings.Any())
|
if (!warnings.Any())
|
||||||
embed.WithDescription(GetText(strs.warnings_none));
|
embed.WithDescription(GetText(strs.warnings_none));
|
||||||
@@ -266,7 +245,7 @@ public partial class Administration
|
|||||||
+ $" | {total} ({all} - {forgiven})";
|
+ $" | {total} ({all} - {forgiven})";
|
||||||
});
|
});
|
||||||
|
|
||||||
return _sender.CreateEmbed()
|
return CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.warnings_list))
|
.WithTitle(GetText(strs.warnings_list))
|
||||||
.WithDescription(string.Join("\n", ws));
|
.WithDescription(string.Join("\n", ws));
|
||||||
@@ -334,7 +313,7 @@ public partial class Administration
|
|||||||
int number,
|
int number,
|
||||||
AddRole _,
|
AddRole _,
|
||||||
IRole role,
|
IRole role,
|
||||||
StoopidTime time = null)
|
ParsedTimespan timespan = null)
|
||||||
{
|
{
|
||||||
var punish = PunishmentAction.AddRole;
|
var punish = PunishmentAction.AddRole;
|
||||||
|
|
||||||
@@ -345,12 +324,12 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, time, role);
|
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, timespan, role);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (time is null)
|
if (timespan is null)
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
||||||
@@ -362,7 +341,7 @@ public partial class Administration
|
|||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
||||||
Format.Bold(number.ToString()),
|
Format.Bold(number.ToString()),
|
||||||
Format.Bold(time.Input)))
|
Format.Bold(timespan.Input)))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,7 +349,7 @@ public partial class Administration
|
|||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
public async Task WarnPunish(int number, PunishmentAction punish, StoopidTime time = null)
|
public async Task WarnPunish(int number, PunishmentAction punish, ParsedTimespan timespan = null)
|
||||||
{
|
{
|
||||||
// this should never happen. Addrole has its own method with higher priority
|
// this should never happen. Addrole has its own method with higher priority
|
||||||
// also disallow warn punishment for getting warned
|
// also disallow warn punishment for getting warned
|
||||||
@@ -378,15 +357,15 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// you must specify the time for timeout
|
// you must specify the time for timeout
|
||||||
if (punish is PunishmentAction.TimeOut && time is null)
|
if (punish is PunishmentAction.TimeOut && timespan is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
var success = await _service.WarnPunish(ctx.Guild.Id, number, punish, timespan);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (time is null)
|
if (timespan is null)
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
.Confirm(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
||||||
@@ -398,7 +377,7 @@ public partial class Administration
|
|||||||
await Response()
|
await Response()
|
||||||
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
.Confirm(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
||||||
Format.Bold(number.ToString()),
|
Format.Bold(number.ToString()),
|
||||||
Format.Bold(time.Input)))
|
Format.Bold(timespan.Input)))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,17 +417,17 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
[BotPerm(GuildPerm.BanMembers)]
|
[BotPerm(GuildPerm.BanMembers)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public Task Ban(StoopidTime time, IUser user, [Leftover] string msg = null)
|
public Task Ban(ParsedTimespan timespan, IUser user, [Leftover] string msg = null)
|
||||||
=> Ban(time, user.Id, msg);
|
=> Ban(timespan, user.Id, msg);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
[BotPerm(GuildPerm.BanMembers)]
|
[BotPerm(GuildPerm.BanMembers)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Ban(StoopidTime time, ulong userId, [Leftover] string msg = null)
|
public async Task Ban(ParsedTimespan timespan, ulong userId, [Leftover] string msg = null)
|
||||||
{
|
{
|
||||||
if (time.Time > TimeSpan.FromDays(49))
|
if (timespan.Time > TimeSpan.FromDays(49))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var guildUser = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
var guildUser = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
||||||
@@ -465,7 +444,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
|
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
|
||||||
var smartText =
|
var smartText =
|
||||||
await _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, time.Time);
|
await _service.GetBanUserDmEmbed(Context, guildUser, defaultMessage, msg, timespan.Time);
|
||||||
if (smartText is not null)
|
if (smartText is not null)
|
||||||
await Response().User(guildUser).Text(smartText).SendAsync();
|
await Response().User(guildUser).Text(smartText).SendAsync();
|
||||||
}
|
}
|
||||||
@@ -477,14 +456,14 @@ public partial class Administration
|
|||||||
|
|
||||||
var user = await ctx.Client.GetUserAsync(userId);
|
var user = await ctx.Client.GetUserAsync(userId);
|
||||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
await _mute.TimedBan(ctx.Guild, userId, timespan.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
|
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
|
||||||
.AddField("ID", userId.ToString(), true)
|
.AddField("ID", userId.ToString(), true)
|
||||||
.AddField(GetText(strs.duration),
|
.AddField(GetText(strs.duration),
|
||||||
time.Time.ToPrettyStringHm(),
|
timespan.Time.ToPrettyStringHm(),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
if (dmFailed)
|
if (dmFailed)
|
||||||
@@ -507,7 +486,7 @@ public partial class Administration
|
|||||||
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField("ID", userId.ToString(), true))
|
.AddField("ID", userId.ToString(), true))
|
||||||
@@ -544,7 +523,7 @@ public partial class Administration
|
|||||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -622,7 +601,7 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
[BotPerm(GuildPerm.BanMembers)]
|
[BotPerm(GuildPerm.BanMembers)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public Task BanMessageTest(StoopidTime duration, [Leftover] string reason = null)
|
public Task BanMessageTest(ParsedTimespan duration, [Leftover] string reason = null)
|
||||||
=> InternalBanMessageTest(reason, duration.Time);
|
=> InternalBanMessageTest(reason, duration.Time);
|
||||||
|
|
||||||
private async Task InternalBanMessageTest(string reason, TimeSpan? duration)
|
private async Task InternalBanMessageTest(string reason, TimeSpan? duration)
|
||||||
@@ -739,7 +718,7 @@ public partial class Administration
|
|||||||
try { await ctx.Guild.RemoveBanAsync(user); }
|
try { await ctx.Guild.RemoveBanAsync(user); }
|
||||||
catch { await ctx.Guild.RemoveBanAsync(user); }
|
catch { await ctx.Guild.RemoveBanAsync(user); }
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("☣ " + GetText(strs.sb_user))
|
.WithTitle("☣ " + GetText(strs.sb_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -794,7 +773,7 @@ public partial class Administration
|
|||||||
|
|
||||||
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
|
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.kicked_user))
|
.WithTitle(GetText(strs.kicked_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -811,7 +790,7 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.ModerateMembers)]
|
[UserPerm(GuildPerm.ModerateMembers)]
|
||||||
[BotPerm(GuildPerm.ModerateMembers)]
|
[BotPerm(GuildPerm.ModerateMembers)]
|
||||||
[Priority(2)]
|
[Priority(2)]
|
||||||
public async Task Timeout(IUser globalUser, StoopidTime time, [Leftover] string msg = null)
|
public async Task Timeout(IUser globalUser, ParsedTimespan timespan, [Leftover] string msg = null)
|
||||||
{
|
{
|
||||||
var user = await ctx.Guild.GetUserAsync(globalUser.Id);
|
var user = await ctx.Guild.GetUserAsync(globalUser.Id);
|
||||||
|
|
||||||
@@ -827,7 +806,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
|
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
|
||||||
await _sender.Response(user)
|
await _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(dmMessage))
|
.WithDescription(dmMessage))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
@@ -837,9 +816,9 @@ public partial class Administration
|
|||||||
dmFailed = true;
|
dmFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.SetTimeOutAsync(time.Time);
|
await user.SetTimeOutAsync(timespan.Time);
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⏳ " + GetText(strs.timedout_user))
|
.WithTitle("⏳ " + GetText(strs.timedout_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -900,7 +879,7 @@ public partial class Administration
|
|||||||
if (string.IsNullOrWhiteSpace(missStr))
|
if (string.IsNullOrWhiteSpace(missStr))
|
||||||
missStr = "-";
|
missStr = "-";
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
||||||
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||||
.WithPendingColor();
|
.WithPendingColor();
|
||||||
@@ -920,7 +899,7 @@ public partial class Administration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_ban_completed(
|
GetText(strs.mass_ban_completed(
|
||||||
banning.Count())))
|
banning.Count())))
|
||||||
@@ -948,7 +927,7 @@ public partial class Administration
|
|||||||
|
|
||||||
//send a message but don't wait for it
|
//send a message but don't wait for it
|
||||||
var banningMessageTask = Response()
|
var banningMessageTask = Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_kill_in_progress(bans.Count())))
|
GetText(strs.mass_kill_in_progress(bans.Count())))
|
||||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||||
@@ -969,7 +948,7 @@ public partial class Administration
|
|||||||
//wait for the message and edit it
|
//wait for the message and edit it
|
||||||
var banningMessage = await banningMessageTask;
|
var banningMessage = await banningMessageTask;
|
||||||
|
|
||||||
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_kill_completed(bans.Count())))
|
GetText(strs.mass_kill_completed(bans.Count())))
|
||||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||||
|
@@ -68,7 +68,7 @@ public partial class Administration
|
|||||||
else
|
else
|
||||||
text = GetText(strs.no_vcroles);
|
text = GetText(strs.no_vcroles);
|
||||||
|
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.vc_role_list))
|
.WithTitle(GetText(strs.vc_role_list))
|
||||||
.WithDescription(text)).SendAsync();
|
.WithDescription(text)).SendAsync();
|
||||||
|
@@ -35,7 +35,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
|
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.expr_new))
|
.WithTitle(GetText(strs.expr_new))
|
||||||
.WithDescription($"#{new kwum(ex.Id)}")
|
.WithDescription($"#{new kwum(ex.Id)}")
|
||||||
@@ -105,7 +105,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
if (ex is not null)
|
if (ex is not null)
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.expr_edited))
|
.WithTitle(GetText(strs.expr_edited))
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
@@ -160,7 +160,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
: " // " + string.Join(" ", ex.GetReactions())))
|
: " // " + string.Join(" ", ex.GetReactions())))
|
||||||
.Join('\n');
|
.Join('\n');
|
||||||
|
|
||||||
return _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
return CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Interaction(IsValidExprEditor() ? inter : null)
|
.Interaction(IsValidExprEditor() ? inter : null)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
|
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
|
||||||
@@ -225,7 +225,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
if (ex is not null)
|
if (ex is not null)
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.expr_deleted))
|
.WithTitle(GetText(strs.expr_deleted))
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
@@ -376,7 +376,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task ExprClear()
|
public async Task ExprClear()
|
||||||
{
|
{
|
||||||
if (await PromptUserConfirmAsync(_sender.CreateEmbed()
|
if (await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithTitle("Expression clear")
|
.WithTitle("Expression clear")
|
||||||
.WithDescription("This will delete all expressions on this server.")))
|
.WithDescription("This will delete all expressions on this server.")))
|
||||||
{
|
{
|
||||||
|
@@ -6,9 +6,9 @@ namespace NadekoBot.Modules.Gambling.Common.AnimalRacing;
|
|||||||
|
|
||||||
public sealed class AnimalRace : IDisposable
|
public sealed class AnimalRace : IDisposable
|
||||||
{
|
{
|
||||||
public const double BASE_MULTIPLIER = 0.82;
|
public const double BASE_MULTIPLIER = 0.87;
|
||||||
public const double MAX_MULTIPLIER = 0.94;
|
public const double MAX_MULTIPLIER = 0.94;
|
||||||
public const double MULTI_PER_USER = 0.01;
|
public const double MULTI_PER_USER = 0.005;
|
||||||
|
|
||||||
public enum Phase
|
public enum Phase
|
||||||
{
|
{
|
||||||
|
@@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class AnimalRacingCommands : GamblingSubmodule<AnimalRaceService>
|
public partial class AnimalRacingCommands : GamblingModule<AnimalRaceService>
|
||||||
{
|
{
|
||||||
private readonly ICurrencyService _cs;
|
private readonly ICurrencyService _cs;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
@@ -74,7 +74,7 @@ public partial class Gambling
|
|||||||
if (race.FinishedUsers[0].Bet > 0)
|
if (race.FinishedUsers[0].Bet > 0)
|
||||||
{
|
{
|
||||||
return Response()
|
return Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.animal_race))
|
.WithTitle(GetText(strs.animal_race))
|
||||||
.WithDescription(GetText(strs.animal_race_won_money(
|
.WithDescription(GetText(strs.animal_race_won_money(
|
||||||
@@ -132,7 +132,7 @@ public partial class Gambling
|
|||||||
raceMessage = await Response().Confirm(text).SendAsync();
|
raceMessage = await Response().Confirm(text).SendAsync();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await msg.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithTitle(GetText(strs.animal_race))
|
.WithTitle(GetText(strs.animal_race))
|
||||||
.WithDescription(text)
|
.WithDescription(text)
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
|
@@ -59,7 +59,7 @@ public partial class Gambling
|
|||||||
{
|
{
|
||||||
var bal = await _bank.GetBalanceAsync(ctx.User.Id);
|
var bal = await _bank.GetBalanceAsync(ctx.User.Id);
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.bank_balance(N(bal))));
|
.WithDescription(GetText(strs.bank_balance(N(bal))));
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ public partial class Gambling
|
|||||||
{
|
{
|
||||||
var bal = await _bank.GetBalanceAsync(user.Id);
|
var bal = await _bank.GetBalanceAsync(user.Id);
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));
|
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));
|
||||||
|
|
||||||
|
255
src/NadekoBot/Modules/Gambling/BetStatsCommands.cs
Normal file
255
src/NadekoBot/Modules/Gambling/BetStatsCommands.cs
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Gambling;
|
||||||
|
|
||||||
|
public partial class Gambling
|
||||||
|
{
|
||||||
|
[Group]
|
||||||
|
public sealed class BetStatsCommands : GamblingModule<UserBetStatsService>
|
||||||
|
{
|
||||||
|
private readonly GamblingTxTracker _gamblingTxTracker;
|
||||||
|
private readonly IBotCache _cache;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
|
public BetStatsCommands(
|
||||||
|
GamblingTxTracker gamblingTxTracker,
|
||||||
|
GamblingConfigService gcs,
|
||||||
|
IBotCache cache,
|
||||||
|
IUserService userService)
|
||||||
|
: base(gcs)
|
||||||
|
{
|
||||||
|
_gamblingTxTracker = gamblingTxTracker;
|
||||||
|
_cache = cache;
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task BetStatsReset(GamblingGame? game = null)
|
||||||
|
{
|
||||||
|
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
||||||
|
|
||||||
|
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||||
|
.WithDescription(
|
||||||
|
$"""
|
||||||
|
Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
|
||||||
|
|
||||||
|
It will cost you {N(price)}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var success = await _service.ResetStatsAsync(ctx.User.Id, game);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Error(strs.not_enough(CurrencySign))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetGameName(GamblingGame? game)
|
||||||
|
{
|
||||||
|
if (game is null)
|
||||||
|
return "all games";
|
||||||
|
|
||||||
|
return game.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(3)]
|
||||||
|
public async Task BetStats()
|
||||||
|
=> await BetStats(ctx.User, null);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(2)]
|
||||||
|
public async Task BetStats(GamblingGame game)
|
||||||
|
=> await BetStats(ctx.User, game);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(1)]
|
||||||
|
public async Task BetStats([Leftover] IUser user)
|
||||||
|
=> await BetStats(user, null);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task BetStats(IUser user, GamblingGame? game)
|
||||||
|
{
|
||||||
|
var stats = await _gamblingTxTracker.GetUserStatsAsync(user.Id, game);
|
||||||
|
|
||||||
|
if (stats.Count == 0)
|
||||||
|
stats = new()
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
TotalBet = 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(user)
|
||||||
|
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
||||||
|
.AddField("Biggest Win", N(stats.Max(x => x.MaxWin)), true)
|
||||||
|
.AddField("Biggest Bet", N(stats.Max(x => x.MaxBet)), true)
|
||||||
|
.AddField("# Bets", stats.Sum(x => x.WinCount + x.LoseCount), true)
|
||||||
|
.AddField("Payout",
|
||||||
|
(stats.Sum(x => x.PaidOut) / stats.Sum(x => x.TotalBet)).ToString("P2", Culture),
|
||||||
|
true);
|
||||||
|
if (game == null)
|
||||||
|
{
|
||||||
|
var favGame = stats.MaxBy(x => x.WinCount + x.LoseCount);
|
||||||
|
eb.AddField("Favorite Game",
|
||||||
|
favGame.Game + "\n" + Format.Italics((favGame.WinCount + favGame.LoseCount) + " plays"),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eb.WithDescription(game.ToString())
|
||||||
|
.AddField("# Wins", stats.Sum(x => x.WinCount), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Embed(eb)
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly record struct WinLbStat(
|
||||||
|
int Rank,
|
||||||
|
string User,
|
||||||
|
GamblingGame Game,
|
||||||
|
long MaxWin);
|
||||||
|
|
||||||
|
private TypedKey<List<WinLbStat>> GetWinLbKey(int page)
|
||||||
|
=> new($"winlb:{page}");
|
||||||
|
|
||||||
|
private async Task<IReadOnlyCollection<WinLbStat>> GetCachedWinLbAsync(int page)
|
||||||
|
{
|
||||||
|
return await _cache.GetOrAddAsync(GetWinLbKey(page),
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
var items = await _service.GetWinLbAsync(page);
|
||||||
|
|
||||||
|
if (items.Count == 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var outputItems = new List<WinLbStat>(items.Count);
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var x = items[i];
|
||||||
|
var user = (await ctx.Client.GetUserAsync(x.UserId, CacheMode.CacheOnly))?.ToString()
|
||||||
|
?? (await _userService.GetUserAsync(x.UserId))?.Username
|
||||||
|
?? x.UserId.ToString();
|
||||||
|
|
||||||
|
if (user.StartsWith("??"))
|
||||||
|
user = x.UserId.ToString();
|
||||||
|
|
||||||
|
outputItems.Add(new WinLbStat(i + 1 + (page * 9), user, x.Game, x.MaxWin));
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputItems;
|
||||||
|
},
|
||||||
|
expiry: TimeSpan.FromMinutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task WinLb(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.PageItems(p => GetCachedWinLbAsync(p))
|
||||||
|
.PageSize(9)
|
||||||
|
.Page((items, curPage) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithTitle(GetText(strs.winlb))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (items.Count == 0)
|
||||||
|
{
|
||||||
|
eb.WithDescription(GetText(strs.empty_page));
|
||||||
|
return eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
eb.AddField($"#{item.Rank} {item.User}",
|
||||||
|
$"{N(item.MaxWin)}\n`{item.Game.ToString().ToLower()}`",
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task GambleStats()
|
||||||
|
{
|
||||||
|
var stats = await _gamblingTxTracker.GetAllAsync();
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
||||||
|
str += "――――――――――――――――――――\n";
|
||||||
|
foreach (var stat in stats)
|
||||||
|
{
|
||||||
|
var perc = (stat.PaidOut / stat.Bet).ToString("P2", Culture);
|
||||||
|
str += $"`{stat.Feature.PadBoth(9)}`"
|
||||||
|
+ $"|`{stat.Bet.ToString("N0").PadLeft(8, ' ')}`"
|
||||||
|
+ $"|`{stat.PaidOut.ToString("N0").PadLeft(8, ' ')}`"
|
||||||
|
+ $"|`{perc.PadLeft(6, ' ')}`\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
var bet = stats.Sum(x => x.Bet);
|
||||||
|
var paidOut = stats.Sum(x => x.PaidOut);
|
||||||
|
|
||||||
|
if (bet == 0)
|
||||||
|
bet = 1;
|
||||||
|
|
||||||
|
var tPerc = (paidOut / bet).ToString("P2", Culture);
|
||||||
|
str += "――――――――――――――――――――\n";
|
||||||
|
str += $"` {("TOTAL").PadBoth(7)}` "
|
||||||
|
+ $"|**{N(bet).PadLeft(8, ' ')}**"
|
||||||
|
+ $"|**{N(paidOut).PadLeft(8, ' ')}**"
|
||||||
|
+ $"|`{tPerc.PadLeft(6, ' ')}`";
|
||||||
|
|
||||||
|
eb.WithDescription(str);
|
||||||
|
|
||||||
|
await Response().Embed(eb).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task GambleStatsReset()
|
||||||
|
{
|
||||||
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
|
.WithDescription(
|
||||||
|
"""
|
||||||
|
Are you sure?
|
||||||
|
This will completely reset Gambling Stats.
|
||||||
|
|
||||||
|
This action is irreversible.
|
||||||
|
""")))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await GambleStats();
|
||||||
|
await _service.ResetGamblingStatsAsync();
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
|
|
||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
public partial class BlackJackCommands : GamblingSubmodule<BlackJackService>
|
public partial class BlackJackCommands : GamblingModule<BlackJackService>
|
||||||
{
|
{
|
||||||
public enum BjAction
|
public enum BjAction
|
||||||
{
|
{
|
||||||
@@ -95,7 +95,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
var cStr = string.Concat(c.Select(x => x[..^1] + " "));
|
var cStr = string.Concat(c.Select(x => x[..^1] + " "));
|
||||||
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
|
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("BlackJack")
|
.WithTitle("BlackJack")
|
||||||
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);
|
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);
|
||||||
|
@@ -9,7 +9,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class Connect4Commands : GamblingSubmodule<GamblingService>
|
public partial class Connect4Commands : GamblingModule<GamblingService>
|
||||||
{
|
{
|
||||||
private static readonly string[] _numbers =
|
private static readonly string[] _numbers =
|
||||||
[
|
[
|
||||||
@@ -132,7 +132,7 @@ public partial class Gambling
|
|||||||
else
|
else
|
||||||
title = GetText(strs.connect4_draw);
|
title = GetText(strs.connect4_draw);
|
||||||
|
|
||||||
return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
return msg.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithTitle(title)
|
.WithTitle(title)
|
||||||
.WithDescription(GetGameStateText(game))
|
.WithDescription(GetGameStateText(game))
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
@@ -142,7 +142,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
private async Task Game_OnGameStateUpdated(Connect4Game game)
|
private async Task Game_OnGameStateUpdated(Connect4Game game)
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
|
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
|
||||||
.WithDescription(GetGameStateText(game))
|
.WithDescription(GetGameStateText(game))
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
@@ -38,7 +38,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
var fileName = $"dice.{format.FileExtensions.First()}";
|
var fileName = $"dice.{format.FileExtensions.First()}";
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.AddField(GetText(strs.roll2), gen)
|
.AddField(GetText(strs.roll2), gen)
|
||||||
@@ -115,7 +115,7 @@ public partial class Gambling
|
|||||||
d.Dispose();
|
d.Dispose();
|
||||||
|
|
||||||
var imageName = $"dice.{format.FileExtensions.First()}";
|
var imageName = $"dice.{format.FileExtensions.First()}";
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
|
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
|
||||||
@@ -141,7 +141,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
for (var i = 0; i < n1; i++)
|
for (var i = 0; i < n1; i++)
|
||||||
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
||||||
@@ -170,7 +170,7 @@ public partial class Gambling
|
|||||||
arr[i] = rng.Next(1, n2 + 1);
|
arr[i] = rng.Next(1, n2 + 1);
|
||||||
|
|
||||||
var sum = arr.Sum();
|
var sum = arr.Sum();
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user