mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f1c7d7437a | ||
|
9a013db25f | ||
|
6f75161c80 | ||
|
3f56e5b651 | ||
|
4875abbe86 | ||
|
83863f3a6c | ||
|
f07abad1ec | ||
|
b6909a4120 | ||
|
aaf7f04216 | ||
|
628871b0da | ||
|
a50ad09c8d | ||
|
1358ff50a4 | ||
|
ee9fede3b1 | ||
|
1d7e9e8471 | ||
|
d2fe7f8d11 | ||
|
75958efe17 | ||
|
7746d2aca1 | ||
|
e631df3326 | ||
|
ba44fdb55f | ||
|
59a1e56dad | ||
|
cd6fe46c2b | ||
|
fb4f470b44 | ||
|
b69e25edf4 | ||
|
9eae27bc15 | ||
|
bed36f8784 | ||
|
623f5ccd5e | ||
|
6da8201c2d | ||
|
d1c24d4721 | ||
|
7e5055268a | ||
|
e84e33b94f | ||
|
f21c96bdf4 | ||
|
430daf9b19 | ||
|
7c8756096d | ||
|
7d9e456fe5 | ||
|
948db31384 | ||
|
df1a775fec | ||
|
6c169e057b | ||
|
b164da38e3 | ||
|
dc229ea2b3 | ||
|
b853495d65 | ||
|
0ff02cede9 | ||
|
f7e0e635e6 | ||
|
a065189023 | ||
|
1affeb0683 | ||
|
6b3c9f01ca | ||
|
e9eb6ff2ad | ||
|
0170536d1c | ||
|
c65e769128 | ||
|
998779203a | ||
|
e0e4d697c3 | ||
|
e9c7293014 | ||
|
cf876a4148 | ||
|
6b9a858f28 | ||
|
38e3badb87 | ||
|
13d2fbd560 | ||
|
8d3f2f186a | ||
|
9572b9dc43 | ||
|
ca32086089 | ||
|
57f839dbcd | ||
|
71d6eeb9dd | ||
|
8c51cf8537 | ||
|
b683026cf3 | ||
|
bca2bc5af1 | ||
|
b385a83bdd | ||
|
3bf0286c81 | ||
|
98272f66e7 | ||
|
cf3788c6ea | ||
|
4b3fc53cb6 | ||
|
4e17dca856 | ||
|
82d89148f3 | ||
|
cc4c09b4d7 | ||
|
616f01f8b2 | ||
|
56f89a02bc | ||
|
48ce988d20 | ||
|
119b1cdec2 | ||
|
43fa5a22f5 | ||
|
3c715a29ca | ||
|
31e1cbb19f | ||
|
8e464e9f09 | ||
|
a190a3d933 | ||
|
bedba98130 | ||
|
d69f8435f6 | ||
|
8440b34338 | ||
|
a2715740c1 | ||
|
36b7fd2352 | ||
|
6563cb507a | ||
|
cc6128997e | ||
|
e942da4470 | ||
|
ab2bcdf00d | ||
|
d7c1dad4f0 | ||
|
8bd6b887b8 |
@@ -1,6 +1,11 @@
|
|||||||
# Ignore all files
|
# Ignore all files
|
||||||
*
|
*
|
||||||
|
|
||||||
|
# Don't ignore nugetconfig
|
||||||
|
!./NuGet.Config
|
||||||
|
# Don't ignore src projects
|
||||||
|
!src/Nadeko.Econ/**
|
||||||
|
!src/Nadeko.Common/**
|
||||||
# Use Nadeko.Medusa project
|
# Use Nadeko.Medusa project
|
||||||
!src/Nadeko.Medusa/**
|
!src/Nadeko.Medusa/**
|
||||||
# Use NadekoBot project
|
# Use NadekoBot project
|
||||||
|
176
CHANGELOG.md
176
CHANGELOG.md
@@ -2,6 +2,91 @@
|
|||||||
|
|
||||||
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
|
## [4.3.6] - 08.09.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `.expraddserver` (.exas) which will server as a server-only alternative to '.exa' in case users want to override default Admin permissions with .dpo
|
||||||
|
- Added .banprune command which sets how many days worth of messages will be pruned when bot (soft)bans a person either through a command or another punishment feature.
|
||||||
|
- Added .qdelauth - Delete all quotes by the specified author on this server. If you target yourself - no permission required
|
||||||
|
- Added `.timeout` command
|
||||||
|
- Added an option to award currency based on received xp
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Reminders now have embed support, but plaintext field is not supported.
|
||||||
|
- User friendlier errors when parsing a number in a command fails
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Awarded xp is now correctly used in level up calculations
|
||||||
|
|
||||||
|
## [4.3.5] - 17.08.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a 'Use' button when a user already owns an item
|
||||||
|
- Added a 'Pull Again' button to slots
|
||||||
|
- Added `.roleinfo` command
|
||||||
|
- Added `.emojiremove` command
|
||||||
|
- Added `.threadcreate` and `.threaddelete` commands
|
||||||
|
- Added `.bank seize` / `.bank award` owner only commands
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Running a .timely command early now shows a pending color
|
||||||
|
- .xp system is once again no longer opt in for servers
|
||||||
|
- It's still opt-in for global and requires users to run .xp at least once in order to start gaining global xp
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed users not getting club xp
|
||||||
|
|
||||||
|
## [4.3.4] - 07.08.2022
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed users getting XP out of nowhere while voice xp is enabled
|
||||||
|
|
||||||
|
## [4.3.3] - 06.08.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `betroll` option to `.bettest` command
|
||||||
|
- Added `.xpshopbuy` and `.xpshopuse` convenience commands
|
||||||
|
- Added an optional preview url to teh xp shop item config model which will be shown instead of the real Url
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated position of Username and Club name on the .xp card
|
||||||
|
- Improved text visibility on the .xp card
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Possibly fixed .trivia not stopping bug
|
||||||
|
- Fixed very low payout rate on `.betroll`
|
||||||
|
- Fixed an issue with youtube song resolver which caused invalid data to be cached
|
||||||
|
- Added client id to the cache key as a potential fix for VoiceXp 'bug'. The solution may be to use different redis instances for each bot, or to switch from botCache: from 'redis' to 'memory' in creds.yml
|
||||||
|
- Bot owner should now be able to buy items from the xpshop when patron requirement is set
|
||||||
|
- Fixed youtube-dl caching invalid data. Please use yt-dlp instead
|
||||||
|
|
||||||
|
## [4.3.2] - 28.07.2022
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed Reaction Roles not working properly with animated emojis
|
||||||
|
- Fixed `.slot` alignment
|
||||||
|
- Fixed `mysql` and `postgresql` reactionrole migration
|
||||||
|
- Fixed repeat loop with `postgresql` db provider
|
||||||
|
- Fixed `.bank withdraw <expression>` will now correctly use bank amount for calculation
|
||||||
|
- [dev] Fixed medusa Reply*LocalizedAsync not working with placeholders
|
||||||
|
|
||||||
|
## [4.3.1] - 27.07.2022
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Check for updates will run once per hour as it was supposed to
|
||||||
|
|
||||||
## [4.3.0] - 27.07.2022
|
## [4.3.0] - 27.07.2022
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -15,9 +100,10 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
- They payouts are very good, but seven always loses
|
- They payouts are very good, but seven always loses
|
||||||
- Added `.lula` command. Plays the same as `.wof` but looks much nicer, and is easily customizable from gambling.yml without any changes to the sourcecode needed.
|
- Added `.lula` command. Plays the same as `.wof` but looks much nicer, and is easily customizable from gambling.yml without any changes to the sourcecode needed.
|
||||||
- Added `.repeatskip` command which makes the next repeat trigger not post anything
|
- Added `.repeatskip` command which makes the next repeat trigger not post anything
|
||||||
- Added `.imageonly` which will make the bot only allow link posts in the channel. Exclusive with `.imageonly`
|
- Added `.linkonly` which will make the bot only allow link posts in the channel. Exclusive with `.imageonly`
|
||||||
- Added release notifications. Bot owners will now receive new release notifications in dms if they have `checkForUpdates` set to `true` in data/bot.yml
|
- Added release notifications. Bot owners will now receive new release notifications in dms if they have `checkForUpdates` set to `true` in data/bot.yml
|
||||||
- You can also configure it via `.conf bot checkforupdates <true/false>`
|
- You can also configure it via `.conf bot checkfor
|
||||||
|
- updates <true/false>`
|
||||||
- Added `.xpshop` which lets bot owners add xp backgrounds and xp frames for sale by configuring `data/xp.yml`
|
- Added `.xpshop` which lets bot owners add xp backgrounds and xp frames for sale by configuring `data/xp.yml`
|
||||||
- You can also toggle xpshop feature via `.conf xp shop.is_enabled`
|
- You can also toggle xpshop feature via `.conf xp shop.is_enabled`
|
||||||
|
|
||||||
@@ -46,7 +132,7 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
- [dev] No longer using generator and partial methods for commands
|
- [dev] No longer using generator and partial methods for commands
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- `.slot` will now show correct multipliers if they've been modified
|
- `.slot` will now show correct multipliers if they've been modified
|
||||||
- Fix patron errors showing up even with permissions disabling the command
|
- Fix patron errors showing up even with permissions disabling the command
|
||||||
- Fixed an issue with voice xp breaking xp gain.
|
- Fixed an issue with voice xp breaking xp gain.
|
||||||
@@ -101,14 +187,14 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed `.draw` command
|
- Fixed `.draw` command
|
||||||
|
|
||||||
## [4.2.10] - 29.06.2022
|
## [4.2.10] - 29.06.2022
|
||||||
|
|
||||||
- Fixed currency generation working only once
|
- Fixed currency generation working only once
|
||||||
|
|
||||||
## [4.2.9] - 25.06.2022
|
## [4.2.9] - 25.06.2022
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed `creds_example.yml` misssing from output directory
|
- Fixed `creds_example.yml` misssing from output directory
|
||||||
@@ -268,7 +354,7 @@ Added `.patron` and `.patronmessage` commands
|
|||||||
- `ytdataapi` will use the official google api (requires `GoogleApiKey` specified in `creds.yml`) and YoutubeDataApi enabled in the dev console
|
- `ytdataapi` will use the official google api (requires `GoogleApiKey` specified in `creds.yml`) and YoutubeDataApi enabled in the dev console
|
||||||
- `ytdl` will use `youtube-dl` program from the host machine. It must be downloaded and it's location must be added to path env variable.
|
- `ytdl` will use `youtube-dl` program from the host machine. It must be downloaded and it's location must be added to path env variable.
|
||||||
- `ytdlp` will use `yt-dlp` program from the host machine. Same as `youtube-dl` - must be in path env variable.
|
- `ytdlp` will use `yt-dlp` program from the host machine. Same as `youtube-dl` - must be in path env variable.
|
||||||
- `invidious` will use one of invidious instances specified in the `invidiousInstances` property. Very good.
|
- `invidious` will use one of invidious instances specified in the `invidiousInstances` property. Very good.
|
||||||
|
|
||||||
- `.google`, `.youtube` and `.image` moved to the new Search group
|
- `.google`, `.youtube` and `.image` moved to the new Search group
|
||||||
|
|
||||||
@@ -288,19 +374,19 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- `.feed` urls which error for more than 100 times will be automatically removed.
|
- `.feed` urls which error for more than 100 times will be automatically removed.
|
||||||
- `.ve` is now enabled by default
|
- `.ve` is now enabled by default
|
||||||
|
|
||||||
- [dev] nadeko interaction slightly improved to make it less nonsense (they still don't make sense)
|
- [dev] nadeko interaction slightly improved to make it less nonsense (they still don't make sense)
|
||||||
- [dev] RewardedUsers table slightly changed to make it more general
|
- [dev] RewardedUsers table slightly changed to make it more general
|
||||||
- [dev] renamed `// todo`s which aren't planned soon to `// FUTURE`
|
- [dev] renamed `// todo`s which aren't planned soon to `// FUTURE`
|
||||||
- [dev] currency rewards have been reimplemented and moved to a separate service
|
- [dev] currency rewards have been reimplemented and moved to a separate service
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- `.rh` no longer needs quotes for multi word roles
|
- `.rh` no longer needs quotes for multi word roles
|
||||||
- `.deletexp` will now properly delete server xp too
|
- `.deletexp` will now properly delete server xp too
|
||||||
- Fixed `.crypto` sparklines
|
- Fixed `.crypto` sparklines
|
||||||
- [dev] added support for configs to properly parse enums without case sensitivity (ConfigParsers.InsensitiveEnum)
|
- [dev] added support for configs to properly parse enums without case sensitivity (ConfigParsers.InsensitiveEnum)
|
||||||
- [dev] Fixed a bug in .gencmdlist
|
- [dev] Fixed a bug in .gencmdlist
|
||||||
- [dev] small fixes to creds provider
|
- [dev] small fixes to creds provider
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
@@ -333,7 +419,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
|
|
||||||
## [4.1.3] - 06.05.2022
|
## [4.1.3] - 06.05.2022
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added support for embed arrays in commands such as .say, .greet, .bye, etc...
|
- Added support for embed arrays in commands such as .say, .greet, .bye, etc...
|
||||||
- Website to create them is live at eb.nadeko.bot (old one is moved to oldeb.nadeko.bot)
|
- Website to create them is live at eb.nadeko.bot (old one is moved to oldeb.nadeko.bot)
|
||||||
@@ -346,18 +432,18 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Users can deposit, withdraw and check the balance of their currency in the bank.
|
- Users can deposit, withdraw and check the balance of their currency in the bank.
|
||||||
- Users can't check other user's bank balances.
|
- Users can't check other user's bank balances.
|
||||||
- Added a button on a .$ command which, when clicked, sends you a message with your bank balance that only you can see.
|
- Added a button on a .$ command which, when clicked, sends you a message with your bank balance that only you can see.
|
||||||
- Added `.h <command group>`
|
- Added `.h <command group>`
|
||||||
- Using this command will list all commands in the specified group
|
- Using this command will list all commands in the specified group
|
||||||
- Atm only .bank is a proper group (`.h bank`)
|
- Atm only .bank is a proper group (`.h bank`)
|
||||||
- Added "Bank Accounts" entry to `.economy`
|
- Added "Bank Accounts" entry to `.economy`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Reaction roles rewritten completely
|
- Reaction roles rewritten completely
|
||||||
- Supports multiple exclusivity groups per message
|
- Supports multiple exclusivity groups per message
|
||||||
- Supports level requirements
|
- Supports level requirements
|
||||||
- However they can only be added one by one
|
- However they can only be added one by one
|
||||||
- Use the following commands for more information
|
- Use the following commands for more information
|
||||||
- `.h .reroa`
|
- `.h .reroa`
|
||||||
- `.h .reroli`
|
- `.h .reroli`
|
||||||
- `.h .rerot`
|
- `.h .rerot`
|
||||||
@@ -388,7 +474,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- To change the db nadeko will use, simply change the `db type` in `creds.yml`
|
- To change the db nadeko will use, simply change the `db type` in `creds.yml`
|
||||||
- There is no migration code right now, which means that if you want to switch to another system you'll either have to manually export/import your database or start fresh
|
- There is no migration code right now, which means that if you want to switch to another system you'll either have to manually export/import your database or start fresh
|
||||||
- Medusa system
|
- Medusa system
|
||||||
- A massive new feature which allows developers to create custom modules/plugins/cogs
|
- A massive new feature which allows developers to create custom modules/plugins/cogs
|
||||||
- They can be load/unloaded/updated at runtime without restarting the bot
|
- They can be load/unloaded/updated at runtime without restarting the bot
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@@ -424,7 +510,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed the `id` which shows up when you add a new Expression
|
- Fixed the `id` which shows up when you add a new Expression
|
||||||
- Fixed some strings which were still referring to "CustomReaction(s)" instead of "Expression(s)"
|
- Fixed some strings which were still referring to "CustomReaction(s)" instead of "Expression(s)"
|
||||||
|
|
||||||
## [4.0.3] - 04.03.2022
|
## [4.0.3] - 04.03.2022
|
||||||
|
|
||||||
@@ -450,20 +536,20 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
### Added
|
### Added
|
||||||
- Added `.deleteemptyservers` command
|
- Added `.deleteemptyservers` command
|
||||||
- Added `.curtr <id>` which lets you see full information about one of your own transactions with the specified id
|
- Added `.curtr <id>` which lets you see full information about one of your own transactions with the specified id
|
||||||
- Added trovo.live support for stream notifications (`.stadd`)
|
- Added trovo.live support for stream notifications (`.stadd`)
|
||||||
- Added unclaimed waifu decay functionality
|
- Added unclaimed waifu decay functionality
|
||||||
- Added 3 new settings to `data/gambling.yml` to control it:
|
- Added 3 new settings to `data/gambling.yml` to control it:
|
||||||
- waifu.decay.percent - How much % to subtract from unclaimed waifu
|
- waifu.decay.percent - How much % to subtract from unclaimed waifu
|
||||||
- waifu.decay.hourInterval - How often to decay the price
|
- waifu.decay.hourInterval - How often to decay the price
|
||||||
- waifu.decay.minPrice - Unclaimed waifus with price lower than the one specified here will not be affected by the decay
|
- waifu.decay.minPrice - Unclaimed waifus with price lower than the one specified here will not be affected by the decay
|
||||||
- Added `currency.transactionsLifetime` to `data/gambling.yml` Any transaction older than the number of days specified will be automatically deleted
|
- Added `currency.transactionsLifetime` to `data/gambling.yml` Any transaction older than the number of days specified will be automatically deleted
|
||||||
- Added `.stock` command to check stock prices and charts
|
- Added `.stock` command to check stock prices and charts
|
||||||
- Re-added `.qap / .queueautoplay`
|
- Re-added `.qap / .queueautoplay`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- CustomReactions module (and customreactions db table) has been renamed to Expressions.
|
- CustomReactions module (and customreactions db table) has been renamed to Expressions.
|
||||||
- This was done to remove confusion about how it relates to discord Reactions (it doesn't, it was created and named before discord reactions existed)
|
- This was done to remove confusion about how it relates to discord Reactions (it doesn't, it was created and named before discord reactions existed)
|
||||||
- Expression command now start with ex/expr and end with the name of the action or setting.
|
- Expression command now start with ex/expr and end with the name of the action or setting.
|
||||||
- For example `.exd` (`.dcr`) is expression delete, `.exa` (`.acr`)
|
- For example `.exd` (`.dcr`) is expression delete, `.exa` (`.acr`)
|
||||||
- Permissions (`.lp`) be automatically updated with "ACTUALEXPRESSIONS", "EXPRESSIONS" instead of "ACTUALCUSTOMREACTIONS" and "CUSTOMREACTIONS"
|
- Permissions (`.lp`) be automatically updated with "ACTUALEXPRESSIONS", "EXPRESSIONS" instead of "ACTUALCUSTOMREACTIONS" and "CUSTOMREACTIONS"
|
||||||
- Permissions for `.ecr` (now `.exe`), `.scr` (now `.exs`), `.dcr` (now `.exd`), `.acr` (now `.exa`), `.lcr` (now `.exl`) will be automatically updated
|
- Permissions for `.ecr` (now `.exe`), `.scr` (now `.exs`), `.dcr` (now `.exd`), `.acr` (now `.exa`), `.lcr` (now `.exl`) will be automatically updated
|
||||||
@@ -480,8 +566,8 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- [dev] Added Type, Extra, OtherId fields to the database
|
- [dev] Added Type, Extra, OtherId fields to the database
|
||||||
- [dev] CommandStrings will now use methodname as the key, and **not** the command name (first entry in aliases.yml)
|
- [dev] CommandStrings will now use methodname as the key, and **not** the command name (first entry in aliases.yml)
|
||||||
- In other words aliases.yml and commands.en-US.yml will use the same keys (once again)
|
- In other words aliases.yml and commands.en-US.yml will use the same keys (once again)
|
||||||
- [dev] Reorganized module and submodule folders
|
- [dev] Reorganized module and submodule folders
|
||||||
- [dev] Permissionv2 db table renamed to Permissions
|
- [dev] Permissionv2 db table renamed to Permissions
|
||||||
- [dev] Moved FilterWordsChannelId to a separate table
|
- [dev] Moved FilterWordsChannelId to a separate table
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
@@ -496,9 +582,9 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Fixed embed color when disabling `.antialt`
|
- Fixed embed color when disabling `.antialt`
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Removed `.bce` - use `.config` or `.config bot` specifically for bot config
|
- Removed `.bce` - use `.config` or `.config bot` specifically for bot config
|
||||||
- Removed obsolete placeholders: %users% %servers% %userfull% %username% %userdiscrim% %useravatar% %id% %uid% %chname% %cid% %sid% %members% %server_time% %shardid% %time% %mention%
|
- Removed obsolete placeholders: %users% %servers% %userfull% %username% %userdiscrim% %useravatar% %id% %uid% %chname% %cid% %sid% %members% %server_time% %shardid% %time% %mention%
|
||||||
- Removed some obsolete commands and strings
|
- Removed some obsolete commands and strings
|
||||||
- Removed code which migrated 2.x to v3 credentials, settings, etc...
|
- Removed code which migrated 2.x to v3 credentials, settings, etc...
|
||||||
|
|
||||||
## [3.0.13] - 14.01.2022
|
## [3.0.13] - 14.01.2022
|
||||||
@@ -537,7 +623,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Looks much nicer
|
- Looks much nicer
|
||||||
- Bot will now reply to user messages with a translation if `del` is disabled
|
- Bot will now reply to user messages with a translation if `del` is disabled
|
||||||
- Bot will make an embed with original and translated text with user avatar and name if `del` is enabled
|
- Bot will make an embed with original and translated text with user avatar and name if `del` is enabled
|
||||||
- If the bot is unable to delete messages while having `del` enabled, it will reset back to the no-del behavior for the current session
|
- If the bot is unable to delete messages while having `del` enabled, it will reset back to the no-del behavior for the current session
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- `.crypto` now supports top 5000 coins
|
- `.crypto` now supports top 5000 coins
|
||||||
@@ -551,7 +637,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
### Fixed
|
### Fixed
|
||||||
- `.xprewsreset` now has correct permissions
|
- `.xprewsreset` now has correct permissions
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Removed slot.numbers from `images.yml` as they're no longer used
|
- Removed slot.numbers from `images.yml` as they're no longer used
|
||||||
|
|
||||||
## [3.0.9] - 21.11.2021
|
## [3.0.9] - 21.11.2021
|
||||||
@@ -561,7 +647,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added `.emojiadd` with 3 overloads
|
- Added `.emojiadd` with 3 overloads
|
||||||
- `.ea :customEmoji:` which copies another server's emoji
|
- `.ea :customEmoji:` which copies another server's emoji
|
||||||
- `.ea newName :customEmoji:` which copies emoji under a different name
|
- `.ea newName :customEmoji:` which copies emoji under a different name
|
||||||
- `.ea emojiName <imagelink.png>` which creates a new emoji from the specified image
|
- `.ea emojiName <imagelink.png>` which creates a new emoji from the specified image
|
||||||
- Patreon Access and Refresh Tokens should now be automatically updated once a month as long as the user has provided the necessary credentials in creds.yml file:
|
- Patreon Access and Refresh Tokens should now be automatically updated once a month as long as the user has provided the necessary credentials in creds.yml file:
|
||||||
@@ -575,7 +661,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
|
|
||||||
## [3.0.8] - 03.11.2021
|
## [3.0.8] - 03.11.2021
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Created VotesApi project nad re-worked vote rewards handling
|
- Created VotesApi project nad re-worked vote rewards handling
|
||||||
- Updated votes entries in creds.yml with explanations on how to set up vote links
|
- Updated votes entries in creds.yml with explanations on how to set up vote links
|
||||||
|
|
||||||
@@ -600,10 +686,10 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- They are called negative gifts
|
- They are called negative gifts
|
||||||
- They show up at the end of the `.gifts` page and are marked with a broken heart
|
- They show up at the end of the `.gifts` page and are marked with a broken heart
|
||||||
- They have a separate multiplier (`waifu.multi.negative_gift_effect` default 0.5, changeable via `.config gambling` or `data/gambling.yml`)
|
- They have a separate multiplier (`waifu.multi.negative_gift_effect` default 0.5, changeable via `.config gambling` or `data/gambling.yml`)
|
||||||
- When gifted, the waifu's price will be reduced by the `price * multiplier`
|
- When gifted, the waifu's price will be reduced by the `price * multiplier`
|
||||||
- Negative gifts don't show up in `.waifuinfo` nor is the record of them kept in the database
|
- Negative gifts don't show up in `.waifuinfo` nor is the record of them kept in the database
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed `%users%` and `%shard.usercount%` placeholders not showing correct values
|
- Fixed `%users%` and `%shard.usercount%` placeholders not showing correct values
|
||||||
|
|
||||||
## [3.0.6] - 27.09.2021
|
## [3.0.6] - 27.09.2021
|
||||||
@@ -644,7 +730,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Possible fix for `.repeat` bug
|
- Possible fix for `.repeat` bug
|
||||||
- Slight adjustment for repeater logic
|
- Slight adjustment for repeater logic
|
||||||
- Timer should no longer increase on some repeaters
|
- Timer should no longer increase on some repeaters
|
||||||
- Repeaters should no longer have periods when they're missing from the list
|
- Repeaters should no longer have periods when they're missing from the list
|
||||||
- Fixed several commands which used error color for success confirmation messages
|
- Fixed several commands which used error color for success confirmation messages
|
||||||
|
|
||||||
## [3.0.3] - 15.09.2021
|
## [3.0.3] - 15.09.2021
|
||||||
@@ -705,7 +791,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Explanations on how to get the keys are added as the comments
|
- Explanations on how to get the keys are added as the comments
|
||||||
- Code cleanup
|
- Code cleanup
|
||||||
- Command attributes cleaned up
|
- Command attributes cleaned up
|
||||||
- Removed dummy Remarks and Usages attributes as hey were unused for a few patches but stayed in the code to avoid big git diffsmigration code has ran and it can be safely removed
|
- Removed dummy Remarks and Usages attributes as hey were unused for a few patches but stayed in the code to avoid big git diffsmigration code has ran and it can be safely removed
|
||||||
- There are 2 projects: NadekoBot and NadekoBot.Coordinator
|
- There are 2 projects: NadekoBot and NadekoBot.Coordinator
|
||||||
- You can directly run NadekoBot as the regular bot with one shard
|
- You can directly run NadekoBot as the regular bot with one shard
|
||||||
- Run NadekoBot.Coordinator if you want more control over your shards and a grpc api for coordinator with which you can start, restart, kill and see status of shards
|
- Run NadekoBot.Coordinator if you want more control over your shards and a grpc api for coordinator with which you can start, restart, kill and see status of shards
|
||||||
@@ -726,7 +812,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
|
|
||||||
## [2.46.2] - 14.07.2021
|
## [2.46.2] - 14.07.2021
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed .save for local songs
|
- Fixed .save for local songs
|
||||||
- Fixed .lq for local songs if the song names are too long
|
- Fixed .lq for local songs if the song names are too long
|
||||||
@@ -797,7 +883,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added `.crsexport` and `.crsimport`
|
- Added `.crsexport` and `.crsimport`
|
||||||
- Allows for quick export/import of server or global custom reactions
|
- Allows for quick export/import of server or global custom reactions
|
||||||
- Requires admin permissions for server crs, and owner for global crs
|
- Requires admin permissions for server crs, and owner for global crs
|
||||||
- Explanation of the fields is in the comment at the top of the `.crsexport` .yml file
|
- Explanation of the fields is in the comment at the top of the `.crsexport` .yml file
|
||||||
@@ -830,7 +916,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Re-added `%music.playing%` and `%music.queued%` (#290)
|
- Re-added `%music.playing%` and `%music.queued%` (#290)
|
||||||
- Added `%music.servers%` which shows how many servers have a song queued up to play
|
- Added `%music.servers%` which shows how many servers have a song queued up to play
|
||||||
ℹ️ ^ Only available to `.ropl` / `.adpl` feature atm
|
ℹ️ ^ Only available to `.ropl` / `.adpl` feature atm
|
||||||
- `.autodc` re-added
|
- `.autodc` re-added
|
||||||
- `.qrp`, `.vol`, `.smch` `.autodc` will now persist
|
- `.qrp`, `.vol`, `.smch` `.autodc` will now persist
|
||||||
@@ -850,7 +936,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Removing last song in the queue will no longer reset queue index
|
- Removing last song in the queue will no longer reset queue index
|
||||||
- Having `.rpl` disabled will now correctly stop after the last song, closes #292
|
- Having `.rpl` disabled will now correctly stop after the last song, closes #292
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- `.sad` removed. It's more or less useless. Use `.qrp` and `.autodc` now for similar effect
|
- `.sad` removed. It's more or less useless. Use `.qrp` and `.autodc` now for similar effect
|
||||||
|
|
||||||
@@ -865,7 +951,7 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
|
|
||||||
- Minor perf improvement for filter checks
|
- Minor perf improvement for filter checks
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- `.qs` result urls are now valid
|
- `.qs` result urls are now valid
|
||||||
- Custom reactions with "`-`" as a response should once again disable that custom reaction completely
|
- Custom reactions with "`-`" as a response should once again disable that custom reaction completely
|
||||||
@@ -881,8 +967,8 @@ Note: Results of each `.youtube` query will be cached for 1 hour to improve perf
|
|||||||
- Much faster starting and skipping once the songs are in the queue
|
- Much faster starting and skipping once the songs are in the queue
|
||||||
- Higher quality audio (no stuttering too!)
|
- Higher quality audio (no stuttering too!)
|
||||||
- Local tracks will now have durations if you have ffprobe installed (comes with ffmpeg)
|
- Local tracks will now have durations if you have ffprobe installed (comes with ffmpeg)
|
||||||
- Bot supports joining a different vc without skipping the song if you use `.j`
|
- Bot supports joining a different vc without skipping the song if you use `.j`
|
||||||
- ⚠️ **DO NOT DRAG THE BOT** to another vc, as it's not properly supported atm, and you will have to do `.play` after dragging it)
|
- ⚠️ **DO NOT DRAG THE BOT** to another vc, as it's not properly supported atm, and you will have to do `.play` after dragging it)
|
||||||
- `.j` makes the bot join your voice channel
|
- `.j` makes the bot join your voice channel
|
||||||
- `.p` is now alias of play, pause is `.pause`
|
- `.p` is now alias of play, pause is `.pause`
|
||||||
- `.qs` should work without google api key now for most users as it is using a custom loader
|
- `.qs` should work without google api key now for most users as it is using a custom loader
|
||||||
|
@@ -2,10 +2,13 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
|||||||
WORKDIR /source
|
WORKDIR /source
|
||||||
|
|
||||||
COPY src/Nadeko.Medusa/*.csproj src/Nadeko.Medusa/
|
COPY src/Nadeko.Medusa/*.csproj src/Nadeko.Medusa/
|
||||||
|
COPY src/Nadeko.Econ/*.csproj src/Nadeko.Econ/
|
||||||
|
COPY src/Nadeko.Common/*.csproj src/Nadeko.Common/
|
||||||
COPY src/NadekoBot/*.csproj src/NadekoBot/
|
COPY src/NadekoBot/*.csproj src/NadekoBot/
|
||||||
COPY src/NadekoBot.Coordinator/*.csproj src/NadekoBot.Coordinator/
|
COPY src/NadekoBot.Coordinator/*.csproj src/NadekoBot.Coordinator/
|
||||||
COPY src/NadekoBot.Generators/*.csproj src/NadekoBot.Generators/
|
COPY src/NadekoBot.Generators/*.csproj src/NadekoBot.Generators/
|
||||||
COPY src/ayu/Ayu.Discord.Voice/*.csproj src/ayu/Ayu.Discord.Voice/
|
COPY src/ayu/Ayu.Discord.Voice/*.csproj src/ayu/Ayu.Discord.Voice/
|
||||||
|
COPY NuGet.Config ./
|
||||||
RUN dotnet restore src/NadekoBot/
|
RUN dotnet restore src/NadekoBot/
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -39,6 +42,7 @@ COPY docker-entrypoint.sh /usr/local/sbin
|
|||||||
|
|
||||||
ENV shard_id=0
|
ENV shard_id=0
|
||||||
ENV total_shards=1
|
ENV total_shards=1
|
||||||
|
ENV NadekoBot__creds=/app/data/creds.yml
|
||||||
|
|
||||||
VOLUME [ "/app/data" ]
|
VOLUME [ "/app/data" ]
|
||||||
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
|
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
|
||||||
|
@@ -1,8 +1,3 @@
|
|||||||
[](https://discord.gg/nadekobot)
|
|
||||||
[](http://nadekobot.readthedocs.io/en/v4/?badge=v4)
|
|
||||||
[](https://top.gg/bot/116275390695079945)
|
|
||||||
|
|
||||||
|
|
||||||
[](https://nadeko.bot/)
|
[](https://nadeko.bot/)
|
||||||
|
|
||||||
[](https://invite.nadeko.bot/)
|
[](https://invite.nadeko.bot/)
|
||||||
|
@@ -13,7 +13,15 @@ do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# fix folder permissions
|
# creds.yml migration
|
||||||
|
if [ -f /app/creds.yml ]; then
|
||||||
|
echo "Default location for creds.yml is now /app/data/creds.yml."
|
||||||
|
echo "Please move your creds.yml and update your docker-compose.yml accordingly."
|
||||||
|
|
||||||
|
export Nadeko_creds=/app/creds.yml
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ensure nadeko can write on /app/data
|
||||||
chown -R nadeko:nadeko "$data"
|
chown -R nadeko:nadeko "$data"
|
||||||
|
|
||||||
# drop to regular user and launch command
|
# drop to regular user and launch command
|
||||||
|
@@ -33,17 +33,17 @@ These are required for a number of features to function properly, and all should
|
|||||||
For a single owner, it should look like this:
|
For a single owner, it should look like this:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
OwnerIds:
|
OwnerIds:
|
||||||
- 105635576866156544
|
- 105635576866156544
|
||||||
```
|
```
|
||||||
|
|
||||||
For multiple owners, it should look like this:
|
For multiple owners, it should look like this:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
OwnerIds:
|
OwnerIds:
|
||||||
- 105635123466156544
|
- 105635123466156544
|
||||||
- 145521851676884992
|
- 145521851676884992
|
||||||
- 341420590009417729
|
- 341420590009417729
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Setting up NadekoBot on Linux
|
w# Setting up NadekoBot on Linux
|
||||||
|
|
||||||
| Table of Contents |
|
| Table of Contents |
|
||||||
| :-------------------------------------------------- |
|
| :-------------------------------------------------- |
|
||||||
@@ -69,7 +69,7 @@ Open Terminal (if you're on an installation with a window manager) and navigate
|
|||||||
|
|
||||||
###### Prerequisites
|
###### Prerequisites
|
||||||
|
|
||||||
1. Nadeko requires redis to function
|
1. (Optional) Installing Redis
|
||||||
- ubuntu installation command: `sudo apt-get install redis-server`
|
- ubuntu installation command: `sudo apt-get install redis-server`
|
||||||
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `youtube-dl` (which in turn requires python3)
|
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `youtube-dl` (which in turn requires python3)
|
||||||
- ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
|
- ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
|
||||||
@@ -160,15 +160,30 @@ If you are presented with the installer main menu, exit it by choosing Option `8
|
|||||||
|
|
||||||
The above command will create a new session named **nadeko** *(you can replace “nadeko” with anything you prefer, it's your session name)*.
|
The above command will create a new session named **nadeko** *(you can replace “nadeko” with anything you prefer, it's your session name)*.
|
||||||
|
|
||||||
2. Navigate to the project's root directory
|
2. Run the installer: `bash linuxAIO.sh`
|
||||||
- Project root directory location example: `cd /home/user/nadekobot/`
|
|
||||||
3. Enter the `output` directory:
|
3. There are a few options when it comes to running Nadeko.
|
||||||
- `cd output`
|
|
||||||
4. Run the bot using:
|
- Run `3` to *Run the bot normally*
|
||||||
- `dotnet NadekoBot.dll`
|
- Run `4` to *Run the bot with Auto Restart* (This is may or may not work)
|
||||||
5. Detatch the tmux session:
|
|
||||||
|
4. If option `4` was selected, you have the following options
|
||||||
|
```
|
||||||
|
1. Run Auto Restart normally without updating NadekoBot.
|
||||||
|
2. Run Auto Restart and update NadekoBot.
|
||||||
|
3. Exit
|
||||||
|
|
||||||
|
Choose:
|
||||||
|
[1] to Run NadekoBot with Auto Restart on "die" command without updating.
|
||||||
|
[2] to Run with Auto Updating on restart after using "die" command.
|
||||||
|
```
|
||||||
|
- Run `1` to update the bot upon restart. (This is done using the `.die` command)
|
||||||
|
- Run `2` to restart the bot without updating. (This is also done using the `.die` command)
|
||||||
|
|
||||||
|
5. That's it! to detatch the tmux session:
|
||||||
- Press `Ctrl` + `B`
|
- Press `Ctrl` + `B`
|
||||||
- Then press `D`
|
- Then press `D`
|
||||||
|
|
||||||
Now check your Discord server, the bot should be online. Nadeko should now be running in the background of your system.
|
Now check your Discord server, the bot should be online. Nadeko should now be running in the background of your system.
|
||||||
|
|
||||||
To re-open the tmux session to either update, restart, or whatever, execute `tmux a -t nadeko`. *(Make sure to replace "nadeko" with your session name. If you didn't change it, leave it as it.)*
|
To re-open the tmux session to either update, restart, or whatever, execute `tmux a -t nadeko`. *(Make sure to replace "nadeko" with your session name. If you didn't change it, leave it as it.)*
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
**Optional**
|
**Optional**
|
||||||
|
|
||||||
- [Notepad++] (makes it easier to edit your credentials)
|
- [Visual Studio Code](https://code.visualstudio.com/Download) (Highly suggested if you plan on editing files)
|
||||||
- [Visual C++ 2010 (x86)] and [Visual C++ 2017 (x64)] (both are required if you want Nadeko to play music - restart Windows after installation)
|
- [Visual C++ 2010 (x86)] and [Visual C++ 2017 (x64)] (both are required if you want Nadeko to play music - restart Windows after installation)
|
||||||
|
|
||||||
#### Setup
|
#### Setup
|
||||||
|
@@ -25,20 +25,6 @@ public class NadekoRandom : Random
|
|||||||
_rng.GetBytes(bytes);
|
_rng.GetBytes(bytes);
|
||||||
return Math.Abs(BitConverter.ToInt32(bytes, 0)) % maxValue;
|
return Math.Abs(BitConverter.ToInt32(bytes, 0)) % maxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte Next(byte minValue, byte maxValue)
|
|
||||||
{
|
|
||||||
if (minValue > maxValue)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(maxValue));
|
|
||||||
|
|
||||||
if (minValue == maxValue)
|
|
||||||
return minValue;
|
|
||||||
|
|
||||||
var bytes = new byte[1];
|
|
||||||
_rng.GetBytes(bytes);
|
|
||||||
|
|
||||||
return (byte)((bytes[0] % (maxValue - minValue)) + minValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Next(int minValue, int maxValue)
|
public override int Next(int minValue, int maxValue)
|
||||||
{
|
{
|
||||||
|
19
src/Nadeko.Common/ShmartBankAmount.cs
Normal file
19
src/Nadeko.Common/ShmartBankAmount.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Nadeko.Common;
|
||||||
|
|
||||||
|
public readonly struct ShmartBankAmount
|
||||||
|
{
|
||||||
|
public long Amount { get; }
|
||||||
|
public ShmartBankAmount(long amount)
|
||||||
|
{
|
||||||
|
Amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator ShmartBankAmount(long num)
|
||||||
|
=> new(num);
|
||||||
|
|
||||||
|
public static implicit operator long(ShmartBankAmount num)
|
||||||
|
=> num.Amount;
|
||||||
|
|
||||||
|
public static implicit operator ShmartBankAmount(int num)
|
||||||
|
=> new(num);
|
||||||
|
}
|
@@ -3,12 +3,10 @@
|
|||||||
public readonly struct ShmartNumber : IEquatable<ShmartNumber>
|
public readonly struct ShmartNumber : IEquatable<ShmartNumber>
|
||||||
{
|
{
|
||||||
public long Value { get; }
|
public long Value { get; }
|
||||||
public string? Input { get; }
|
|
||||||
|
|
||||||
public ShmartNumber(long val, string? input = null)
|
public ShmartNumber(long val)
|
||||||
{
|
{
|
||||||
Value = val;
|
Value = val;
|
||||||
Input = input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator ShmartNumber(long num)
|
public static implicit operator ShmartNumber(long num)
|
||||||
|
@@ -12,7 +12,7 @@ public sealed class BetflipGame
|
|||||||
|
|
||||||
public BetflipResult Flip(byte guess, decimal amount)
|
public BetflipResult Flip(byte guess, decimal amount)
|
||||||
{
|
{
|
||||||
var side = _rng.Next(0, 2);
|
var side = (byte)_rng.Next(0, 2);
|
||||||
if (side == guess)
|
if (side == guess)
|
||||||
{
|
{
|
||||||
return new BetflipResult()
|
return new BetflipResult()
|
||||||
|
@@ -13,7 +13,7 @@ public sealed class BetrollGame
|
|||||||
|
|
||||||
public BetrollResult Roll(decimal amount = 0)
|
public BetrollResult Roll(decimal amount = 0)
|
||||||
{
|
{
|
||||||
var roll = _rng.Next(0, 101);
|
var roll = _rng.Next(1, 101);
|
||||||
|
|
||||||
for (var i = 0; i < _thresholdPairs.Length; i++)
|
for (var i = 0; i < _thresholdPairs.Length; i++)
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
namespace Nadeko.Econ.Gambling;
|
namespace Nadeko.Econ.Gambling;
|
||||||
|
|
||||||
|
//here is a payout chart
|
||||||
|
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
|
||||||
|
//thanks to judge for helping me with this
|
||||||
public class SlotGame
|
public class SlotGame
|
||||||
{
|
{
|
||||||
private static readonly NadekoRandom _rng = new NadekoRandom();
|
private static readonly NadekoRandom _rng = new NadekoRandom();
|
||||||
@@ -8,9 +11,9 @@ public class SlotGame
|
|||||||
{
|
{
|
||||||
var rolls = new[]
|
var rolls = new[]
|
||||||
{
|
{
|
||||||
_rng.Next(0, 6),
|
(byte)_rng.Next(0, 6),
|
||||||
_rng.Next(0, 6),
|
(byte)_rng.Next(0, 6),
|
||||||
_rng.Next(0, 6)
|
(byte)_rng.Next(0, 6)
|
||||||
};
|
};
|
||||||
|
|
||||||
ref var a = ref rolls[0];
|
ref var a = ref rolls[0];
|
||||||
|
@@ -47,7 +47,7 @@ public static class MedusaExtensions
|
|||||||
=> ctx.Message.AddReactionAsync(new Emoji("🤔"));
|
=> ctx.Message.AddReactionAsync(new Emoji("🤔"));
|
||||||
|
|
||||||
public static Task<IUserMessage> ErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
public static Task<IUserMessage> ErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||||
=> ctx.SendErrorAsync(ctx.GetText(key));
|
=> ctx.SendErrorAsync(ctx.GetText(key, args));
|
||||||
|
|
||||||
public static Task<IUserMessage> PendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
public static Task<IUserMessage> PendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||||
=> ctx.SendPendingAsync(ctx.GetText(key, args));
|
=> ctx.SendPendingAsync(ctx.GetText(key, args));
|
||||||
@@ -56,11 +56,11 @@ public static class MedusaExtensions
|
|||||||
=> ctx.SendConfirmAsync(ctx.GetText(key, args));
|
=> ctx.SendConfirmAsync(ctx.GetText(key, args));
|
||||||
|
|
||||||
public static Task<IUserMessage> ReplyErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
public static Task<IUserMessage> ReplyErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||||
=> ctx.SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key)}");
|
=> ctx.SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
|
||||||
|
|
||||||
public static Task<IUserMessage> ReplyPendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
public static Task<IUserMessage> ReplyPendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||||
=> ctx.SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key)}");
|
=> ctx.SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
|
||||||
|
|
||||||
public static Task<IUserMessage> ReplyConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
public static Task<IUserMessage> ReplyConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||||
=> ctx.SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key)}");
|
=> ctx.SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
|
||||||
}
|
}
|
@@ -10,6 +10,7 @@ public interface IEmbedBuilder
|
|||||||
IEmbedBuilder WithFooter(string text, string? iconUrl = null);
|
IEmbedBuilder WithFooter(string text, string? iconUrl = null);
|
||||||
IEmbedBuilder WithAuthor(string name, string? iconUrl = null, string? url = null);
|
IEmbedBuilder WithAuthor(string name, string? iconUrl = null, string? url = null);
|
||||||
IEmbedBuilder WithColor(EmbedColor color);
|
IEmbedBuilder WithColor(EmbedColor color);
|
||||||
|
IEmbedBuilder WithDiscordColor(Color color);
|
||||||
Embed Build();
|
Embed Build();
|
||||||
IEmbedBuilder WithUrl(string url);
|
IEmbedBuilder WithUrl(string url);
|
||||||
IEmbedBuilder WithImageUrl(string url);
|
IEmbedBuilder WithImageUrl(string url);
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net.Core" Version="3.103.0" />
|
<PackageReference Include="Discord.Net.Core" Version="3.104.0" />
|
||||||
<PackageReference Include="Serilog" Version="2.11.0" />
|
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -3,14 +3,12 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using NadekoBot.Common.Configs;
|
using NadekoBot.Common.Configs;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
using NadekoBot.Modules.Administration;
|
|
||||||
using NadekoBot.Modules.Utility;
|
using NadekoBot.Modules.Utility;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Nadeko.Common;
|
|
||||||
using RunMode = Discord.Commands.RunMode;
|
using RunMode = Discord.Commands.RunMode;
|
||||||
|
|
||||||
namespace NadekoBot;
|
namespace NadekoBot;
|
||||||
@@ -35,13 +33,13 @@ public sealed class Bot
|
|||||||
private readonly IBotCredsProvider _credsProvider;
|
private readonly IBotCredsProvider _credsProvider;
|
||||||
// private readonly InteractionService _interactionService;
|
// private readonly InteractionService _interactionService;
|
||||||
|
|
||||||
public Bot(int shardId, int? totalShards)
|
public Bot(int shardId, int? totalShards, string credPath = null)
|
||||||
{
|
{
|
||||||
if (shardId < 0)
|
if (shardId < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(shardId));
|
throw new ArgumentOutOfRangeException(nameof(shardId));
|
||||||
|
|
||||||
ShardId = shardId;
|
ShardId = shardId;
|
||||||
_credsProvider = new BotCredsProvider(totalShards);
|
_credsProvider = new BotCredsProvider(totalShards, credPath);
|
||||||
_creds = _credsProvider.GetCreds();
|
_creds = _credsProvider.GetCreds();
|
||||||
|
|
||||||
_db = new(_credsProvider);
|
_db = new(_credsProvider);
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace NadekoBot;
|
||||||
|
|
||||||
|
public interface INadekoInteractionService
|
||||||
|
{
|
||||||
|
public NadekoInteraction Create<T>(
|
||||||
|
ulong userId,
|
||||||
|
SimpleInteraction<T> inter);
|
||||||
|
}
|
@@ -1,29 +0,0 @@
|
|||||||
namespace NadekoBot;
|
|
||||||
|
|
||||||
public sealed class NadekoButtonActionInteraction : NadekoButtonOwnInteraction
|
|
||||||
{
|
|
||||||
private readonly NadekoInteractionData _data;
|
|
||||||
private readonly Func<SocketMessageComponent, Task> _action;
|
|
||||||
|
|
||||||
public NadekoButtonActionInteraction(
|
|
||||||
DiscordSocketClient client,
|
|
||||||
ulong authorId,
|
|
||||||
NadekoInteractionData data,
|
|
||||||
Func<SocketMessageComponent, Task> action
|
|
||||||
)
|
|
||||||
: base(client, authorId)
|
|
||||||
{
|
|
||||||
_data = data;
|
|
||||||
_action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string Name
|
|
||||||
=> _data.CustomId;
|
|
||||||
protected override IEmote Emote
|
|
||||||
=> _data.Emote;
|
|
||||||
protected override string? Text
|
|
||||||
=> _data.Text;
|
|
||||||
|
|
||||||
public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
|
|
||||||
=> _action(smc);
|
|
||||||
}
|
|
@@ -1,25 +1,30 @@
|
|||||||
namespace NadekoBot;
|
namespace NadekoBot;
|
||||||
|
|
||||||
public abstract class NadekoButtonInteraction
|
public sealed class NadekoInteraction
|
||||||
{
|
{
|
||||||
// improvements:
|
private readonly ulong _authorId;
|
||||||
// - state in OnAction
|
private readonly ButtonBuilder _button;
|
||||||
// - configurable delay
|
private readonly Func<SocketMessageComponent, Task> _onClick;
|
||||||
// -
|
private readonly bool _onlyAuthor;
|
||||||
protected abstract string Name { get; }
|
|
||||||
protected abstract IEmote Emote { get; }
|
|
||||||
protected virtual string? Text { get; } = null;
|
|
||||||
|
|
||||||
public DiscordSocketClient Client { get; }
|
public DiscordSocketClient Client { get; }
|
||||||
|
|
||||||
protected readonly TaskCompletionSource<bool> _interactionCompletedSource;
|
private readonly TaskCompletionSource<bool> _interactionCompletedSource;
|
||||||
|
|
||||||
protected IUserMessage message = null!;
|
private IUserMessage message = null!;
|
||||||
|
|
||||||
protected NadekoButtonInteraction(DiscordSocketClient client)
|
public NadekoInteraction(DiscordSocketClient client,
|
||||||
|
ulong authorId,
|
||||||
|
ButtonBuilder button,
|
||||||
|
Func<SocketMessageComponent, Task> onClick,
|
||||||
|
bool onlyAuthor)
|
||||||
{
|
{
|
||||||
Client = client;
|
_authorId = authorId;
|
||||||
|
_button = button;
|
||||||
|
_onClick = onClick;
|
||||||
|
_onlyAuthor = onlyAuthor;
|
||||||
_interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
_interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
||||||
|
Client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunAsync(IUserMessage msg)
|
public async Task RunAsync(IUserMessage msg)
|
||||||
@@ -27,29 +32,25 @@ public abstract class NadekoButtonInteraction
|
|||||||
message = msg;
|
message = msg;
|
||||||
|
|
||||||
Client.InteractionCreated += OnInteraction;
|
Client.InteractionCreated += OnInteraction;
|
||||||
await Task.WhenAny(Task.Delay(10_000), _interactionCompletedSource.Task);
|
await Task.WhenAny(Task.Delay(15_000), _interactionCompletedSource.Task);
|
||||||
Client.InteractionCreated -= OnInteraction;
|
Client.InteractionCreated -= OnInteraction;
|
||||||
|
|
||||||
await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
|
await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract ValueTask<bool> Validate(SocketMessageComponent smc);
|
private Task OnInteraction(SocketInteraction arg)
|
||||||
private async Task OnInteraction(SocketInteraction arg)
|
|
||||||
{
|
{
|
||||||
if (arg is not SocketMessageComponent smc)
|
if (arg is not SocketMessageComponent smc)
|
||||||
return;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
if (smc.Message.Id != message.Id)
|
if (smc.Message.Id != message.Id)
|
||||||
return;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
if (smc.Data.CustomId != Name)
|
if (_onlyAuthor && smc.User.Id != _authorId)
|
||||||
return;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
if (!await Validate(smc))
|
if (smc.Data.CustomId != _button.CustomId)
|
||||||
{
|
return Task.CompletedTask;
|
||||||
await smc.DeferAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -63,21 +64,19 @@ public abstract class NadekoButtonInteraction
|
|||||||
await smc.DeferAsync();
|
await smc.DeferAsync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public virtual MessageComponent CreateComponent()
|
public MessageComponent CreateComponent()
|
||||||
{
|
{
|
||||||
var comp = new ComponentBuilder()
|
var comp = new ComponentBuilder()
|
||||||
.WithButton(GetButtonBuilder());
|
.WithButton(_button);
|
||||||
|
|
||||||
return comp.Build();
|
return comp.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ButtonBuilder GetButtonBuilder()
|
public Task ExecuteOnActionAsync(SocketMessageComponent smc)
|
||||||
=> new ButtonBuilder(style: ButtonStyle.Secondary, emote: Emote, customId: Name, label: Text);
|
=> _onClick(smc);
|
||||||
|
}
|
||||||
public abstract Task ExecuteOnActionAsync(SocketMessageComponent smc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is all so wrong ...
|
|
@@ -1,43 +0,0 @@
|
|||||||
// namespace NadekoBot;
|
|
||||||
//
|
|
||||||
// public class NadekoButtonInteractionArray : NadekoButtonInteraction
|
|
||||||
// {
|
|
||||||
// private readonly ButtonBuilder[] _bbs;
|
|
||||||
// private readonly NadekoButtonInteraction[] _inters;
|
|
||||||
//
|
|
||||||
// public NadekoButtonInteractionArray(params NadekoButtonInteraction[] inters)
|
|
||||||
// : base(inters[0].Client)
|
|
||||||
// {
|
|
||||||
// _inters = inters;
|
|
||||||
// _bbs = inters.Map(x => x.GetButtonBuilder());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// protected override string Name
|
|
||||||
// => throw new NotSupportedException();
|
|
||||||
// protected override IEmote Emote
|
|
||||||
// => throw new NotSupportedException();
|
|
||||||
//
|
|
||||||
// protected override ValueTask<bool> Validate(SocketMessageComponent smc)
|
|
||||||
// => new(true);
|
|
||||||
//
|
|
||||||
// public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
|
|
||||||
// {
|
|
||||||
// for (var i = 0; i < _bbs.Length; i++)
|
|
||||||
// {
|
|
||||||
// if (_bbs[i].CustomId == smc.Data.CustomId)
|
|
||||||
// return _inters[i].ExecuteOnActionAsync(smc);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return Task.CompletedTask;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public override MessageComponent CreateComponent()
|
|
||||||
// {
|
|
||||||
// var comp = new ComponentBuilder();
|
|
||||||
//
|
|
||||||
// foreach (var bb in _bbs)
|
|
||||||
// comp.WithButton(bb);
|
|
||||||
//
|
|
||||||
// return comp.Build();
|
|
||||||
// }
|
|
||||||
// }
|
|
@@ -1,42 +0,0 @@
|
|||||||
namespace NadekoBot;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builder class for NadekoInteractions
|
|
||||||
/// </summary>
|
|
||||||
public class NadekoInteractionBuilder
|
|
||||||
{
|
|
||||||
private NadekoInteractionData? iData;
|
|
||||||
private Func<SocketMessageComponent, Task>? action;
|
|
||||||
// private bool isOwn;
|
|
||||||
|
|
||||||
public NadekoInteractionBuilder WithData<T>(in T data)
|
|
||||||
where T : NadekoInteractionData
|
|
||||||
{
|
|
||||||
iData = data;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public NadekoOwnInteractionBuiler WithIsOwn(bool isOwn = true)
|
|
||||||
// {
|
|
||||||
// this.isOwn = isOwn;
|
|
||||||
// return this;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
public NadekoInteractionBuilder WithAction(in Func<SocketMessageComponent, Task> fn)
|
|
||||||
{
|
|
||||||
this.action = fn;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NadekoButtonActionInteraction Build(DiscordSocketClient client, ulong userId)
|
|
||||||
{
|
|
||||||
if (iData is null)
|
|
||||||
throw new InvalidOperationException("You have to specify the data before building the interaction");
|
|
||||||
|
|
||||||
if (action is null)
|
|
||||||
throw new InvalidOperationException("You have to specify the action before building the interaction");
|
|
||||||
|
|
||||||
return new(client, userId, iData, action);
|
|
||||||
}
|
|
||||||
}
|
|
20
src/NadekoBot/Common/Interaction/NadekoInteractionService.cs
Normal file
20
src/NadekoBot/Common/Interaction/NadekoInteractionService.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace NadekoBot;
|
||||||
|
|
||||||
|
public class NadekoInteractionService : INadekoInteractionService, INService
|
||||||
|
{
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public NadekoInteractionService(DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NadekoInteraction Create<T>(
|
||||||
|
ulong userId,
|
||||||
|
SimpleInteraction<T> inter)
|
||||||
|
=> new NadekoInteraction(_client,
|
||||||
|
userId,
|
||||||
|
inter.Button,
|
||||||
|
inter.TriggerAsync,
|
||||||
|
onlyAuthor: true);
|
||||||
|
}
|
@@ -1,15 +0,0 @@
|
|||||||
namespace NadekoBot;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction which only the author can use
|
|
||||||
/// </summary>
|
|
||||||
public abstract class NadekoButtonOwnInteraction : NadekoButtonInteraction
|
|
||||||
{
|
|
||||||
protected readonly ulong _authorId;
|
|
||||||
|
|
||||||
protected NadekoButtonOwnInteraction(DiscordSocketClient client, ulong authorId) : base(client)
|
|
||||||
=> _authorId = authorId;
|
|
||||||
|
|
||||||
protected override ValueTask<bool> Validate(SocketMessageComponent smc)
|
|
||||||
=> new(smc.User.Id == _authorId);
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
namespace NadekoBot.Common;
|
|
||||||
|
|
||||||
public abstract class NInteraction
|
|
||||||
{
|
|
||||||
private readonly DiscordSocketClient _client;
|
|
||||||
private readonly ulong _userId;
|
|
||||||
private readonly Func<SocketMessageComponent, Task> _action;
|
|
||||||
|
|
||||||
protected abstract NadekoInteractionData Data { get; }
|
|
||||||
|
|
||||||
public NInteraction(
|
|
||||||
DiscordSocketClient client,
|
|
||||||
ulong userId,
|
|
||||||
Func<SocketMessageComponent, Task> action)
|
|
||||||
{
|
|
||||||
_client = client;
|
|
||||||
_userId = userId;
|
|
||||||
_action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NadekoButtonInteraction GetInteraction()
|
|
||||||
=> new NadekoInteractionBuilder()
|
|
||||||
.WithData(Data)
|
|
||||||
.WithAction(_action)
|
|
||||||
.Build(_client, _userId);
|
|
||||||
}
|
|
@@ -18,6 +18,7 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
public CommandHandler _cmdHandler { get; set; }
|
public CommandHandler _cmdHandler { get; set; }
|
||||||
public ILocalization _localization { get; set; }
|
public ILocalization _localization { get; set; }
|
||||||
public IEmbedBuilderService _eb { get; set; }
|
public IEmbedBuilderService _eb { get; set; }
|
||||||
|
public INadekoInteractionService _inter { get; set; }
|
||||||
|
|
||||||
protected string prefix
|
protected string prefix
|
||||||
=> _cmdHandler.GetPrefix(ctx.Guild);
|
=> _cmdHandler.GetPrefix(ctx.Guild);
|
||||||
@@ -36,7 +37,7 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
string error,
|
string error,
|
||||||
string url = null,
|
string url = null,
|
||||||
string footer = null,
|
string footer = null,
|
||||||
NadekoButtonInteraction inter = null)
|
NadekoInteraction inter = null)
|
||||||
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
|
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
|
||||||
|
|
||||||
public Task<IUserMessage> SendConfirmAsync(
|
public Task<IUserMessage> SendConfirmAsync(
|
||||||
@@ -47,32 +48,32 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
|
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
|
||||||
|
|
||||||
//
|
//
|
||||||
public Task<IUserMessage> SendErrorAsync(string text, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> SendErrorAsync(string text, NadekoInteraction inter = null)
|
||||||
=> ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
|
=> ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
|
||||||
public Task<IUserMessage> SendConfirmAsync(string text, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> SendConfirmAsync(string text, NadekoInteraction inter = null)
|
||||||
=> ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
|
=> ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
|
||||||
public Task<IUserMessage> SendPendingAsync(string text, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> SendPendingAsync(string text, NadekoInteraction inter = null)
|
||||||
=> ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
|
=> ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
|
||||||
|
|
||||||
|
|
||||||
// localized normal
|
// localized normal
|
||||||
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||||
=> SendErrorAsync(GetText(str), inter);
|
=> SendErrorAsync(GetText(str), inter);
|
||||||
|
|
||||||
public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||||
=> SendPendingAsync(GetText(str), inter);
|
=> SendPendingAsync(GetText(str), inter);
|
||||||
|
|
||||||
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||||
=> SendConfirmAsync(GetText(str), inter);
|
=> SendConfirmAsync(GetText(str), inter);
|
||||||
|
|
||||||
// localized replies
|
// localized replies
|
||||||
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||||
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||||
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||||
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||||
|
|
||||||
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
||||||
|
@@ -0,0 +1,94 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using NadekoBot.Db;
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
using NCalc;
|
||||||
|
using OneOf;
|
||||||
|
|
||||||
|
namespace NadekoBot.Common.TypeReaders;
|
||||||
|
|
||||||
|
public class BaseShmartInputAmountReader
|
||||||
|
{
|
||||||
|
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
||||||
|
protected readonly DbService _db;
|
||||||
|
protected readonly GamblingConfigService _gambling;
|
||||||
|
|
||||||
|
public BaseShmartInputAmountReader(DbService db, GamblingConfigService gambling)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_gambling = gambling;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<OneOf<long, OneOf.Types.Error<string>>> ReadAsync(ICommandContext context, string input)
|
||||||
|
{
|
||||||
|
var i = input.Trim().ToUpperInvariant();
|
||||||
|
|
||||||
|
i = i.Replace("K", "000");
|
||||||
|
|
||||||
|
//can't add m because it will conflict with max atm
|
||||||
|
|
||||||
|
if (await TryHandlePercentage(context, i) is long num)
|
||||||
|
{
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var expr = new Expression(i, EvaluateOptions.IgnoreCase);
|
||||||
|
expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context).GetAwaiter().GetResult();
|
||||||
|
return (long)decimal.Parse(expr.Evaluate().ToString()!);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return new OneOf.Types.Error<string>($"Invalid input: {input}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EvaluateParam(string name, ParameterArgs args, ICommandContext ctx)
|
||||||
|
{
|
||||||
|
switch (name.ToUpperInvariant())
|
||||||
|
{
|
||||||
|
case "PI":
|
||||||
|
args.Result = Math.PI;
|
||||||
|
break;
|
||||||
|
case "E":
|
||||||
|
args.Result = Math.E;
|
||||||
|
break;
|
||||||
|
case "ALL":
|
||||||
|
case "ALLIN":
|
||||||
|
args.Result = await Cur(ctx);
|
||||||
|
break;
|
||||||
|
case "HALF":
|
||||||
|
args.Result = await Cur(ctx) / 2;
|
||||||
|
break;
|
||||||
|
case "MAX":
|
||||||
|
args.Result = await Max(ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual async Task<long> Cur(ICommandContext ctx)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.DiscordUser.GetUserCurrencyAsync(ctx.User.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual async Task<long> Max(ICommandContext ctx)
|
||||||
|
{
|
||||||
|
var settings = _gambling.Data;
|
||||||
|
var max = settings.MaxBet;
|
||||||
|
return max == 0 ? await Cur(ctx) : max;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<long?> TryHandlePercentage(ICommandContext ctx, string input)
|
||||||
|
{
|
||||||
|
var m = _percentRegex.Match(input);
|
||||||
|
|
||||||
|
if (m.Captures.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!long.TryParse(m.Groups["num"].ToString(), out var percent))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return (long)(await Cur(ctx) * (percent / 100.0f));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Modules.Gambling.Bank;
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
|
||||||
|
namespace NadekoBot.Common.TypeReaders;
|
||||||
|
|
||||||
|
public sealed class ShmartBankAmountTypeReader : NadekoTypeReader<ShmartBankAmount>
|
||||||
|
{
|
||||||
|
private readonly IBankService _bank;
|
||||||
|
private readonly ShmartBankInputAmountReader _tr;
|
||||||
|
|
||||||
|
public ShmartBankAmountTypeReader(IBankService bank, DbService db, GamblingConfigService gambling)
|
||||||
|
{
|
||||||
|
_bank = bank;
|
||||||
|
_tr = new ShmartBankInputAmountReader(bank, db, gambling);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async ValueTask<TypeReaderResult<ShmartBankAmount>> ReadAsync(ICommandContext ctx, string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
return TypeReaderResult.FromError<ShmartBankAmount>(CommandError.ParseFailed, "Input is empty.");
|
||||||
|
|
||||||
|
var result = await _tr.ReadAsync(ctx, input);
|
||||||
|
|
||||||
|
if (result.TryPickT0(out var val, out var err))
|
||||||
|
{
|
||||||
|
return TypeReaderResult.FromSuccess<ShmartBankAmount>(new(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeReaderResult.FromError<ShmartBankAmount>(CommandError.Unsuccessful, err.Value);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
using NadekoBot.Modules.Gambling.Bank;
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
|
||||||
|
namespace NadekoBot.Common.TypeReaders;
|
||||||
|
|
||||||
|
public sealed class ShmartBankInputAmountReader : BaseShmartInputAmountReader
|
||||||
|
{
|
||||||
|
private readonly IBankService _bank;
|
||||||
|
|
||||||
|
public ShmartBankInputAmountReader(IBankService bank, DbService db, GamblingConfigService gambling)
|
||||||
|
: base(db, gambling)
|
||||||
|
{
|
||||||
|
_bank = bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<long> Cur(ICommandContext ctx)
|
||||||
|
=> _bank.GetBalanceAsync(ctx.User.Id);
|
||||||
|
|
||||||
|
protected override Task<long> Max(ICommandContext ctx)
|
||||||
|
=> Cur(ctx);
|
||||||
|
}
|
@@ -0,0 +1,29 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
|
||||||
|
namespace NadekoBot.Common.TypeReaders;
|
||||||
|
|
||||||
|
public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||||
|
{
|
||||||
|
private readonly BaseShmartInputAmountReader _tr;
|
||||||
|
|
||||||
|
public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling)
|
||||||
|
{
|
||||||
|
_tr = new BaseShmartInputAmountReader(db, gambling);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async ValueTask<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext ctx, string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input))
|
||||||
|
return TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, "Input is empty.");
|
||||||
|
|
||||||
|
var result = await _tr.ReadAsync(ctx, input);
|
||||||
|
|
||||||
|
if (result.TryPickT0(out var val, out var err))
|
||||||
|
{
|
||||||
|
return TypeReaderResult.FromSuccess<ShmartNumber>(new(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeReaderResult.FromError<ShmartNumber>(CommandError.Unsuccessful, err.Value);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,100 +0,0 @@
|
|||||||
#nullable disable
|
|
||||||
using NadekoBot.Db;
|
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
|
||||||
using NCalc;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Nadeko.Common;
|
|
||||||
|
|
||||||
namespace NadekoBot.Common.TypeReaders;
|
|
||||||
|
|
||||||
public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
|
||||||
{
|
|
||||||
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
|
||||||
private readonly DbService _db;
|
|
||||||
private readonly GamblingConfigService _gambling;
|
|
||||||
|
|
||||||
public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling)
|
|
||||||
{
|
|
||||||
_db = db;
|
|
||||||
_gambling = gambling;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ValueTask<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext context, string input)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(input))
|
|
||||||
return new(TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, "Input is empty."));
|
|
||||||
|
|
||||||
var i = input.Trim().ToUpperInvariant();
|
|
||||||
|
|
||||||
i = i.Replace("K", "000");
|
|
||||||
|
|
||||||
//can't add m because it will conflict with max atm
|
|
||||||
|
|
||||||
if (TryHandlePercentage(context, i, out var num))
|
|
||||||
return new(TypeReaderResult.FromSuccess(new ShmartNumber(num, i)));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var expr = new Expression(i, EvaluateOptions.IgnoreCase);
|
|
||||||
expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context);
|
|
||||||
var lon = (long)decimal.Parse(expr.Evaluate().ToString());
|
|
||||||
return new(TypeReaderResult.FromSuccess(new ShmartNumber(lon, input)));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return ValueTask.FromResult(
|
|
||||||
TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, $"Invalid input: {input}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EvaluateParam(string name, ParameterArgs args, ICommandContext ctx)
|
|
||||||
{
|
|
||||||
switch (name.ToUpperInvariant())
|
|
||||||
{
|
|
||||||
case "PI":
|
|
||||||
args.Result = Math.PI;
|
|
||||||
break;
|
|
||||||
case "E":
|
|
||||||
args.Result = Math.E;
|
|
||||||
break;
|
|
||||||
case "ALL":
|
|
||||||
case "ALLIN":
|
|
||||||
args.Result = Cur(ctx);
|
|
||||||
break;
|
|
||||||
case "HALF":
|
|
||||||
args.Result = Cur(ctx) / 2;
|
|
||||||
break;
|
|
||||||
case "MAX":
|
|
||||||
args.Result = Max(ctx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long Cur(ICommandContext ctx)
|
|
||||||
{
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
return uow.DiscordUser.GetUserCurrency(ctx.User.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long Max(ICommandContext ctx)
|
|
||||||
{
|
|
||||||
var settings = _gambling.Data;
|
|
||||||
var max = settings.MaxBet;
|
|
||||||
return max == 0 ? Cur(ctx) : max;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryHandlePercentage(ICommandContext ctx, string input, out long num)
|
|
||||||
{
|
|
||||||
num = 0;
|
|
||||||
var m = _percentRegex.Match(input);
|
|
||||||
if (m.Captures.Count != 0)
|
|
||||||
{
|
|
||||||
if (!long.TryParse(m.Groups["num"].ToString(), out var percent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
num = (long)(Cur(ctx) * (percent / 100.0f));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -108,8 +108,8 @@ public static class DiscordUserExtensions
|
|||||||
.Take(count)
|
.Take(count)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
public static long GetUserCurrency(this DbSet<DiscordUser> users, ulong userId)
|
public static async Task<long> GetUserCurrencyAsync(this DbSet<DiscordUser> users, ulong userId)
|
||||||
=> users.AsNoTracking().FirstOrDefault(x => x.UserId == userId)?.CurrencyAmount ?? 0;
|
=> (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0;
|
||||||
|
|
||||||
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
||||||
{
|
{
|
||||||
|
@@ -49,7 +49,8 @@ public enum PunishmentAction
|
|||||||
ChatMute,
|
ChatMute,
|
||||||
VoiceMute,
|
VoiceMute,
|
||||||
AddRole,
|
AddRole,
|
||||||
Warn
|
Warn,
|
||||||
|
TimeOut
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AntiSpamIgnore : DbEntity
|
public class AntiSpamIgnore : DbEntity
|
||||||
|
@@ -5,4 +5,5 @@ public class BanTemplate : DbEntity
|
|||||||
{
|
{
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
|
public int? PruneDays { get; set; }
|
||||||
}
|
}
|
@@ -17,8 +17,6 @@ public class DiscordUser : DbEntity
|
|||||||
public bool IsClubAdmin { get; set; }
|
public bool IsClubAdmin { get; set; }
|
||||||
|
|
||||||
public long TotalXp { get; set; }
|
public long TotalXp { get; set; }
|
||||||
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
|
|
||||||
public DateTime LastXpGain { get; set; } = DateTime.MinValue;
|
|
||||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||||
|
|
||||||
public long CurrencyAmount { get; set; }
|
public long CurrencyAmount { get; set; }
|
||||||
|
@@ -8,7 +8,6 @@ public class UserXpStats : DbEntity
|
|||||||
public long Xp { get; set; }
|
public long Xp { get; set; }
|
||||||
public long AwardedXp { get; set; }
|
public long AwardedXp { get; set; }
|
||||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||||
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum XpNotificationLocation { None, Dm, Channel }
|
public enum XpNotificationLocation { None, Dm, Channel }
|
@@ -13,6 +13,6 @@ public class XpShopOwnedItem : DbEntity
|
|||||||
|
|
||||||
public enum XpShopItemType
|
public enum XpShopItemType
|
||||||
{
|
{
|
||||||
Background,
|
Background = 0,
|
||||||
Frame,
|
Frame = 1,
|
||||||
}
|
}
|
@@ -10,10 +10,6 @@ public sealed class MysqlContext : NadekoContext
|
|||||||
|
|
||||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||||
=> "NULL";
|
=> "NULL";
|
||||||
protected override string DiscordUserLastXpGainDefaultValue
|
|
||||||
=> "(UTC_TIMESTAMP - INTERVAL 1 year)";
|
|
||||||
protected override string LastLevelUpDefaultValue
|
|
||||||
=> "(UTC_TIMESTAMP)";
|
|
||||||
|
|
||||||
public MysqlContext(string connStr = "Server=localhost", string version = "8.0")
|
public MysqlContext(string connStr = "Server=localhost", string version = "8.0")
|
||||||
{
|
{
|
||||||
|
@@ -65,8 +65,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
#region Mandatory Provider-Specific Values
|
#region Mandatory Provider-Specific Values
|
||||||
|
|
||||||
protected abstract string CurrencyTransactionOtherIdDefaultValue { get; }
|
protected abstract string CurrencyTransactionOtherIdDefaultValue { get; }
|
||||||
protected abstract string DiscordUserLastXpGainDefaultValue { get; }
|
|
||||||
protected abstract string LastLevelUpDefaultValue { get; }
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -166,12 +164,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
du.Property(x => x.NotifyOnLevelUp)
|
du.Property(x => x.NotifyOnLevelUp)
|
||||||
.HasDefaultValue(XpNotificationLocation.None);
|
.HasDefaultValue(XpNotificationLocation.None);
|
||||||
|
|
||||||
du.Property(x => x.LastXpGain)
|
|
||||||
.HasDefaultValueSql(DiscordUserLastXpGainDefaultValue);
|
|
||||||
|
|
||||||
du.Property(x => x.LastLevelUp)
|
|
||||||
.HasDefaultValueSql(LastLevelUpDefaultValue);
|
|
||||||
|
|
||||||
du.Property(x => x.TotalXp)
|
du.Property(x => x.TotalXp)
|
||||||
.HasDefaultValue(0);
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
@@ -213,9 +205,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
})
|
})
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
xps.Property(x => x.LastLevelUp)
|
|
||||||
.HasDefaultValueSql(LastLevelUpDefaultValue);
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -341,6 +330,10 @@ public abstract class NadekoContext : DbContext
|
|||||||
#region BanTemplate
|
#region BanTemplate
|
||||||
|
|
||||||
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
|
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
|
||||||
|
modelBuilder.Entity<BanTemplate>()
|
||||||
|
.Property(x => x.PruneDays)
|
||||||
|
.HasDefaultValue(null)
|
||||||
|
.IsRequired(false);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@@ -8,10 +8,6 @@ public sealed class PostgreSqlContext : NadekoContext
|
|||||||
|
|
||||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||||
=> "NULL";
|
=> "NULL";
|
||||||
protected override string DiscordUserLastXpGainDefaultValue
|
|
||||||
=> "timezone('utc', now()) - interval '-1 year'";
|
|
||||||
protected override string LastLevelUpDefaultValue
|
|
||||||
=> "timezone('utc', now())";
|
|
||||||
|
|
||||||
public PostgreSqlContext(string connStr = "Host=localhost")
|
public PostgreSqlContext(string connStr = "Host=localhost")
|
||||||
{
|
{
|
||||||
@@ -20,6 +16,8 @@ public sealed class PostgreSqlContext : NadekoContext
|
|||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||||
|
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
optionsBuilder
|
optionsBuilder
|
||||||
.UseLowerCaseNamingConvention()
|
.UseLowerCaseNamingConvention()
|
||||||
|
@@ -9,10 +9,6 @@ public sealed class SqliteContext : NadekoContext
|
|||||||
|
|
||||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||||
=> "NULL";
|
=> "NULL";
|
||||||
protected override string DiscordUserLastXpGainDefaultValue
|
|
||||||
=> "datetime('now', '-1 years')";
|
|
||||||
protected override string LastLevelUpDefaultValue
|
|
||||||
=> "datetime('now')";
|
|
||||||
|
|
||||||
public SqliteContext(string connectionString = "Data Source=data/NadekoBot.db", int commandTimeout = 60)
|
public SqliteContext(string connectionString = "Data Source=data/NadekoBot.db", int commandTimeout = 60)
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace NadekoBot.Migrations;
|
namespace NadekoBot.Migrations;
|
||||||
|
|
||||||
@@ -6,11 +7,36 @@ public static class MigrationQueries
|
|||||||
{
|
{
|
||||||
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql(
|
if (migrationBuilder.IsMySql())
|
||||||
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded)
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
@"INSERT IGNORE into reactionroles(guildid, channelid, messageid, emote, roleid, `group`, levelreq, dateadded)
|
||||||
select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded
|
select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded
|
||||||
from reactionrole
|
from reactionrole
|
||||||
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
||||||
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
|
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
|
||||||
|
}
|
||||||
|
else if (migrationBuilder.IsSqlite())
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(
|
||||||
|
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded)
|
||||||
|
select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded
|
||||||
|
from reactionrole
|
||||||
|
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
||||||
|
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
|
||||||
|
}
|
||||||
|
else if (migrationBuilder.IsNpgsql())
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(@"insert into reactionroles(guildid, channelid, messageid, emote, roleid, ""group"", levelreq, dateadded)
|
||||||
|
select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded
|
||||||
|
from reactionrole
|
||||||
|
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
||||||
|
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id
|
||||||
|
ON CONFLICT DO NOTHING;");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("This database provider doesn't have an implementation for MigrateRero");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
3542
src/NadekoBot/Migrations/MySql/20220808141855_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
3542
src/NadekoBot/Migrations/MySql/20220808141855_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.Mysql
|
||||||
|
{
|
||||||
|
public partial class removeobsoletexpcolumns : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "lastlevelup",
|
||||||
|
table: "userxpstats");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "lastlevelup",
|
||||||
|
table: "discorduser");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "lastxpgain",
|
||||||
|
table: "discorduser");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "lastlevelup",
|
||||||
|
table: "userxpstats",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "(UTC_TIMESTAMP)");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "lastlevelup",
|
||||||
|
table: "discorduser",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "(UTC_TIMESTAMP)");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "lastxpgain",
|
||||||
|
table: "discorduser",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "(UTC_TIMESTAMP - INTERVAL 1 year)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3546
src/NadekoBot/Migrations/MySql/20220831142722_banprune.Designer.cs
generated
Normal file
3546
src/NadekoBot/Migrations/MySql/20220831142722_banprune.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
src/NadekoBot/Migrations/MySql/20220831142722_banprune.cs
Normal file
25
src/NadekoBot/Migrations/MySql/20220831142722_banprune.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.Mysql
|
||||||
|
{
|
||||||
|
public partial class banprune : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "prunedays",
|
||||||
|
table: "bantemplates",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "prunedays",
|
||||||
|
table: "bantemplates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -168,18 +168,6 @@ namespace NadekoBot.Migrations.Mysql
|
|||||||
.HasDefaultValue(false)
|
.HasDefaultValue(false)
|
||||||
.HasColumnName("isclubadmin");
|
.HasColumnName("isclubadmin");
|
||||||
|
|
||||||
b.Property<DateTime>("LastLevelUp")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("lastlevelup")
|
|
||||||
.HasDefaultValueSql("(UTC_TIMESTAMP)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastXpGain")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("lastxpgain")
|
|
||||||
.HasDefaultValueSql("(UTC_TIMESTAMP - INTERVAL 1 year)");
|
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
b.Property<int>("NotifyOnLevelUp")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("int")
|
.HasColumnType("int")
|
||||||
@@ -687,6 +675,10 @@ namespace NadekoBot.Migrations.Mysql
|
|||||||
.HasColumnType("bigint unsigned")
|
.HasColumnType("bigint unsigned")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<int?>("PruneDays")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("prunedays");
|
||||||
|
|
||||||
b.Property<string>("Text")
|
b.Property<string>("Text")
|
||||||
.HasColumnType("longtext")
|
.HasColumnType("longtext")
|
||||||
.HasColumnName("text");
|
.HasColumnName("text");
|
||||||
@@ -2565,12 +2557,6 @@ namespace NadekoBot.Migrations.Mysql
|
|||||||
.HasColumnType("bigint unsigned")
|
.HasColumnType("bigint unsigned")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<DateTime>("LastLevelUp")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("lastlevelup")
|
|
||||||
.HasDefaultValueSql("(UTC_TIMESTAMP)");
|
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
b.Property<int>("NotifyOnLevelUp")
|
||||||
.HasColumnType("int")
|
.HasColumnType("int")
|
||||||
.HasColumnName("notifyonlevelup");
|
.HasColumnName("notifyonlevelup");
|
||||||
|
3686
src/NadekoBot/Migrations/Postgresql/20220808142559_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
3686
src/NadekoBot/Migrations/Postgresql/20220808142559_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3690
src/NadekoBot/Migrations/Postgresql/20220831142735_banprune.Designer.cs
generated
Normal file
3690
src/NadekoBot/Migrations/Postgresql/20220831142735_banprune.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
public partial class banprune : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "prunedays",
|
||||||
|
table: "bantemplates",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "prunedays",
|
||||||
|
table: "bantemplates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -36,7 +36,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("balance");
|
.HasColumnName("balance");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("UserId")
|
b.Property<decimal>("UserId")
|
||||||
@@ -101,7 +101,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
@@ -163,7 +163,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("currencyamount");
|
.HasColumnName("currencyamount");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Discriminator")
|
b.Property<string>("Discriminator")
|
||||||
@@ -176,18 +176,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasDefaultValue(false)
|
.HasDefaultValue(false)
|
||||||
.HasColumnName("isclubadmin");
|
.HasColumnName("isclubadmin");
|
||||||
|
|
||||||
b.Property<DateTime>("LastLevelUp")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("lastlevelup")
|
|
||||||
.HasDefaultValueSql("timezone('utc', now())");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastXpGain")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("lastxpgain")
|
|
||||||
.HasDefaultValueSql("timezone('utc', now()) - interval '-1 year'");
|
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
b.Property<int>("NotifyOnLevelUp")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
@@ -243,7 +231,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -322,7 +310,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("amountcents");
|
.HasColumnName("amountcents");
|
||||||
|
|
||||||
b.Property<DateTime>("LastCharge")
|
b.Property<DateTime>("LastCharge")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("lastcharge");
|
.HasColumnName("lastcharge");
|
||||||
|
|
||||||
b.Property<string>("UniquePlatformUserId")
|
b.Property<string>("UniquePlatformUserId")
|
||||||
@@ -330,7 +318,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("uniqueplatformuserid");
|
.HasColumnName("uniqueplatformuserid");
|
||||||
|
|
||||||
b.Property<DateTime>("ValidThru")
|
b.Property<DateTime>("ValidThru")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("validthru");
|
.HasColumnName("validthru");
|
||||||
|
|
||||||
b.HasKey("UserId")
|
b.HasKey("UserId")
|
||||||
@@ -357,7 +345,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("MessageId")
|
b.Property<decimal>("MessageId")
|
||||||
@@ -388,7 +376,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<bool>("IsUsing")
|
b.Property<bool>("IsUsing")
|
||||||
@@ -471,7 +459,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("action");
|
.HasColumnName("action");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<int>("GuildConfigId")
|
||||||
@@ -518,7 +506,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
@@ -544,7 +532,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("action");
|
.HasColumnName("action");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<int>("GuildConfigId")
|
||||||
@@ -595,7 +583,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("commandtext");
|
.HasColumnName("commandtext");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal?>("GuildId")
|
b.Property<decimal?>("GuildId")
|
||||||
@@ -642,7 +630,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -676,7 +664,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Source")
|
b.Property<string>("Source")
|
||||||
@@ -710,13 +698,17 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<int?>("PruneDays")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("prunedays");
|
||||||
|
|
||||||
b.Property<string>("Text")
|
b.Property<string>("Text")
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
.HasColumnName("text");
|
.HasColumnName("text");
|
||||||
@@ -741,7 +733,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("ItemId")
|
b.Property<decimal>("ItemId")
|
||||||
@@ -768,7 +760,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -806,7 +798,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("commandname");
|
.HasColumnName("commandname");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -840,7 +832,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("amount");
|
.HasColumnName("amount");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Extra")
|
b.Property<string>("Extra")
|
||||||
@@ -890,7 +882,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -924,7 +916,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("command");
|
.HasColumnName("command");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal?>("GuildId")
|
b.Property<decimal?>("GuildId")
|
||||||
@@ -955,7 +947,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("ItemId")
|
b.Property<decimal>("ItemId")
|
||||||
@@ -993,7 +985,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<int>("GuildConfigId")
|
||||||
@@ -1028,7 +1020,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1054,7 +1046,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1088,7 +1080,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1118,7 +1110,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1148,7 +1140,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1174,7 +1166,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<int>("GuildConfigId")
|
||||||
@@ -1253,7 +1245,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("cleverbotenabled");
|
.HasColumnName("cleverbotenabled");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<bool>("DeleteMessageOnCommand")
|
b.Property<bool>("DeleteMessageOnCommand")
|
||||||
@@ -1381,7 +1373,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("ItemType")
|
b.Property<int>("ItemType")
|
||||||
@@ -1420,7 +1412,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("LogSettingId")
|
b.Property<int?>("LogSettingId")
|
||||||
@@ -1450,7 +1442,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -1493,7 +1485,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelupdatedid");
|
.HasColumnName("channelupdatedid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -1629,7 +1621,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("authorid");
|
.HasColumnName("authorid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
@@ -1652,7 +1644,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1694,7 +1686,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("containsanywhere");
|
.HasColumnName("containsanywhere");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<bool>("DmResponse")
|
b.Property<bool>("DmResponse")
|
||||||
@@ -1733,7 +1725,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -1763,7 +1755,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -1825,7 +1817,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -1867,7 +1859,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("MusicPlaylistId")
|
b.Property<int?>("MusicPlaylistId")
|
||||||
@@ -1917,7 +1909,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -1948,7 +1940,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("Index")
|
b.Property<int>("Index")
|
||||||
@@ -1982,7 +1974,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("PollId")
|
b.Property<int?>("PollId")
|
||||||
@@ -2025,7 +2017,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("authorname");
|
.HasColumnName("authorname");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -2068,7 +2060,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Emote")
|
b.Property<string>("Emote")
|
||||||
@@ -2123,7 +2115,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<bool>("IsPrivate")
|
b.Property<bool>("IsPrivate")
|
||||||
@@ -2143,7 +2135,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("userid");
|
.HasColumnName("userid");
|
||||||
|
|
||||||
b.Property<DateTime>("When")
|
b.Property<DateTime>("When")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("when");
|
.HasColumnName("when");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
@@ -2169,7 +2161,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("channelid");
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
b.Property<DateTime>("DateAdded")
|
b.Property<DateTime>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
@@ -2216,11 +2208,11 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("amountrewardedthismonth");
|
.HasColumnName("amountrewardedthismonth");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<DateTime>("LastReward")
|
b.Property<DateTime>("LastReward")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("lastreward");
|
.HasColumnName("lastreward");
|
||||||
|
|
||||||
b.Property<string>("PlatformUserId")
|
b.Property<string>("PlatformUserId")
|
||||||
@@ -2251,7 +2243,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Status")
|
b.Property<string>("Status")
|
||||||
@@ -2278,7 +2270,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("Group")
|
b.Property<int>("Group")
|
||||||
@@ -2323,7 +2315,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("authorid");
|
.HasColumnName("authorid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2373,7 +2365,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("ShopEntryId")
|
b.Property<int?>("ShopEntryId")
|
||||||
@@ -2403,7 +2395,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2433,7 +2425,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2463,7 +2455,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("StreamRoleSettingsId")
|
b.Property<int?>("StreamRoleSettingsId")
|
||||||
@@ -2501,7 +2493,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("addroleid");
|
.HasColumnName("addroleid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<bool>("Enabled")
|
b.Property<bool>("Enabled")
|
||||||
@@ -2540,7 +2532,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("StreamRoleSettingsId")
|
b.Property<int?>("StreamRoleSettingsId")
|
||||||
@@ -2574,7 +2566,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2582,7 +2574,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("guildconfigid");
|
.HasColumnName("guildconfigid");
|
||||||
|
|
||||||
b.Property<DateTime>("UnbanAt")
|
b.Property<DateTime>("UnbanAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("unbanat");
|
.HasColumnName("unbanat");
|
||||||
|
|
||||||
b.Property<decimal>("UserId")
|
b.Property<decimal>("UserId")
|
||||||
@@ -2608,7 +2600,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2616,7 +2608,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("guildconfigid");
|
.HasColumnName("guildconfigid");
|
||||||
|
|
||||||
b.Property<DateTime>("UnmuteAt")
|
b.Property<DateTime>("UnmuteAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("unmuteat");
|
.HasColumnName("unmuteat");
|
||||||
|
|
||||||
b.Property<decimal>("UserId")
|
b.Property<decimal>("UserId")
|
||||||
@@ -2642,7 +2634,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2654,7 +2646,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("roleid");
|
.HasColumnName("roleid");
|
||||||
|
|
||||||
b.Property<DateTime>("UnbanAt")
|
b.Property<DateTime>("UnbanAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("unbanat");
|
.HasColumnName("unbanat");
|
||||||
|
|
||||||
b.Property<decimal>("UserId")
|
b.Property<decimal>("UserId")
|
||||||
@@ -2684,19 +2676,13 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("awardedxp");
|
.HasColumnName("awardedxp");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<DateTime>("LastLevelUp")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("lastlevelup")
|
|
||||||
.HasDefaultValueSql("timezone('utc', now())");
|
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
b.Property<int>("NotifyOnLevelUp")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("notifyonlevelup");
|
.HasColumnName("notifyonlevelup");
|
||||||
@@ -2741,7 +2727,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -2783,7 +2769,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("claimerid");
|
.HasColumnName("claimerid");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<long>("Price")
|
b.Property<long>("Price")
|
||||||
@@ -2823,7 +2809,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("ItemEmoji")
|
b.Property<string>("ItemEmoji")
|
||||||
@@ -2857,7 +2843,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("NewId")
|
b.Property<int?>("NewId")
|
||||||
@@ -2901,7 +2887,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<bool>("Forgiven")
|
b.Property<bool>("Forgiven")
|
||||||
@@ -2963,7 +2949,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("count");
|
.HasColumnName("count");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int?>("GuildConfigId")
|
b.Property<int?>("GuildConfigId")
|
||||||
@@ -3005,7 +2991,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnName("amount");
|
.HasColumnName("amount");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("Level")
|
b.Property<int>("Level")
|
||||||
@@ -3035,7 +3021,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("Level")
|
b.Property<int>("Level")
|
||||||
@@ -3074,7 +3060,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<int>("GuildConfigId")
|
||||||
|
2843
src/NadekoBot/Migrations/Sqlite/20220808141842_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
2843
src/NadekoBot/Migrations/Sqlite/20220808141842_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class removeobsoletexpcolumns : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LastLevelUp",
|
||||||
|
table: "UserXpStats");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LastLevelUp",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LastXpGain",
|
||||||
|
table: "DiscordUser");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastLevelUp",
|
||||||
|
table: "UserXpStats",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "datetime('now')");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastLevelUp",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "datetime('now')");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastXpGain",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValueSql: "datetime('now', '-1 years')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2846
src/NadekoBot/Migrations/Sqlite/20220831142504_banprune.Designer.cs
generated
Normal file
2846
src/NadekoBot/Migrations/Sqlite/20220831142504_banprune.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
src/NadekoBot/Migrations/Sqlite/20220831142504_banprune.cs
Normal file
25
src/NadekoBot/Migrations/Sqlite/20220831142504_banprune.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class banprune : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "PruneDays",
|
||||||
|
table: "BanTemplates",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PruneDays",
|
||||||
|
table: "BanTemplates");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -134,16 +134,6 @@ namespace NadekoBot.Migrations
|
|||||||
.HasColumnType("INTEGER")
|
.HasColumnType("INTEGER")
|
||||||
.HasDefaultValue(false);
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
b.Property<DateTime>("LastLevelUp")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasDefaultValueSql("datetime('now')");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastXpGain")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasDefaultValueSql("datetime('now', '-1 years')");
|
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
b.Property<int>("NotifyOnLevelUp")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER")
|
.HasColumnType("INTEGER")
|
||||||
@@ -539,6 +529,9 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<ulong>("GuildId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("PruneDays")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("Text")
|
b.Property<string>("Text")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
@@ -2003,11 +1996,6 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<ulong>("GuildId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTime>("LastLevelUp")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasDefaultValueSql("datetime('now')");
|
|
||||||
|
|
||||||
b.Property<int>("NotifyOnLevelUp")
|
b.Property<int>("NotifyOnLevelUp")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
@@ -344,4 +344,36 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
|
|
||||||
await ctx.OkAsync();
|
await ctx.OkAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[BotPerm(ChannelPermission.CreatePublicThreads)]
|
||||||
|
[UserPerm(ChannelPermission.CreatePublicThreads)]
|
||||||
|
public async Task ThreadCreate([Leftover] string name)
|
||||||
|
{
|
||||||
|
if (ctx.Channel is not SocketTextChannel stc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await stc.CreateThreadAsync(name, message: ctx.Message.ReferencedMessage);
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[BotPerm(ChannelPermission.ManageThreads)]
|
||||||
|
[UserPerm(ChannelPermission.ManageThreads)]
|
||||||
|
public async Task ThreadDelete([Leftover] string name)
|
||||||
|
{
|
||||||
|
if (ctx.Channel is not SocketTextChannel stc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var t = stc.Threads.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
|
if (t is null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await t.DeleteAsync();
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -358,9 +358,10 @@ public class MuteService : INService
|
|||||||
IGuild guild,
|
IGuild guild,
|
||||||
IUser user,
|
IUser user,
|
||||||
TimeSpan after,
|
TimeSpan after,
|
||||||
string reason)
|
string reason,
|
||||||
|
int pruneDays)
|
||||||
{
|
{
|
||||||
await guild.AddBanAsync(user.Id, 0, reason);
|
await guild.AddBanAsync(user.Id, pruneDays, reason);
|
||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
var config = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.UnbanTimer));
|
var config = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.UnbanTimer));
|
||||||
|
@@ -38,10 +38,14 @@ public partial class Administration
|
|||||||
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var minutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||||
|
if (action is PunishmentAction.TimeOut && minutes < 1)
|
||||||
|
minutes = 1;
|
||||||
|
|
||||||
await _service.StartAntiAltAsync(ctx.Guild.Id,
|
await _service.StartAntiAltAsync(ctx.Guild.Id,
|
||||||
minAgeMinutes,
|
minAgeMinutes,
|
||||||
action,
|
action,
|
||||||
(int?)punishTime?.Time.TotalMinutes ?? 0);
|
minutes);
|
||||||
|
|
||||||
await ctx.OkAsync();
|
await ctx.OkAsync();
|
||||||
}
|
}
|
||||||
@@ -56,6 +60,9 @@ public partial class Administration
|
|||||||
if (minAgeMinutes < 1)
|
if (minAgeMinutes < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (action == PunishmentAction.TimeOut)
|
||||||
|
return;
|
||||||
|
|
||||||
await _service.StartAntiAltAsync(ctx.Guild.Id, minAgeMinutes, action, roleId: role.Id);
|
await _service.StartAntiAltAsync(ctx.Guild.Id, minAgeMinutes, action, roleId: role.Id);
|
||||||
|
|
||||||
await ctx.OkAsync();
|
await ctx.OkAsync();
|
||||||
@@ -122,6 +129,9 @@ public partial class Administration
|
|||||||
var time = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
var time = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||||
if (time is < 0 or > 60 * 24)
|
if (time is < 0 or > 60 * 24)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(action is PunishmentAction.TimeOut && time < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
var stats = await _service.StartAntiRaidAsync(ctx.Guild.Id, userThreshold, seconds, action, time);
|
var stats = await _service.StartAntiRaidAsync(ctx.Guild.Id, userThreshold, seconds, action, time);
|
||||||
|
|
||||||
@@ -187,6 +197,9 @@ public partial class Administration
|
|||||||
if (time is < 0 or > 60 * 24)
|
if (time is < 0 or > 60 * 24)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (action is PunishmentAction.TimeOut && time < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
var stats = await _service.StartAntiSpamAsync(ctx.Guild.Id, messageCount, action, time, role?.Id);
|
var stats = await _service.StartAntiSpamAsync(ctx.Guild.Id, messageCount, action, time, role?.Id);
|
||||||
|
|
||||||
await SendConfirmAsync(GetText(strs.prot_enable("Anti-Spam")),
|
await SendConfirmAsync(GetText(strs.prot_enable("Anti-Spam")),
|
||||||
|
@@ -58,7 +58,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(IGuildUser, IRole)> GetUserAndRoleAsync(
|
private async Task<(IGuildUser, IRole)> GetUserAndRoleAsync(
|
||||||
SocketReaction r,
|
ulong userId,
|
||||||
ReactionRoleV2 rero)
|
ReactionRoleV2 rero)
|
||||||
{
|
{
|
||||||
var guild = _client.GetGuild(rero.GuildId);
|
var guild = _client.GetGuild(rero.GuildId);
|
||||||
@@ -67,8 +67,8 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
if (role is null)
|
if (role is null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var user = guild.GetUser(r.UserId) as IGuildUser
|
var user = guild.GetUser(userId) as IGuildUser
|
||||||
?? await _client.Rest.GetGuildUserAsync(guild.Id, r.UserId);
|
?? await _client.Rest.GetGuildUserAsync(guild.Id, userId);
|
||||||
|
|
||||||
if (user is null)
|
if (user is null)
|
||||||
return default;
|
return default;
|
||||||
@@ -77,20 +77,23 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Task ClientOnReactionRemoved(
|
private Task ClientOnReactionRemoved(
|
||||||
Cacheable<IUserMessage, ulong> msg,
|
Cacheable<IUserMessage, ulong> cmsg,
|
||||||
Cacheable<IMessageChannel, ulong> ch,
|
Cacheable<IMessageChannel, ulong> ch,
|
||||||
SocketReaction r)
|
SocketReaction r)
|
||||||
{
|
{
|
||||||
if (!_cache.TryGetValue(msg.Id, out var reros))
|
if (!_cache.TryGetValue(cmsg.Id, out var reros))
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var rero = reros.FirstOrDefault(x => x.Emote == r.Emote.Name || x.Emote == r.Emote.ToString());
|
var emote = await GetFixedEmoteAsync(cmsg, r.Emote);
|
||||||
|
|
||||||
|
var rero = reros.FirstOrDefault(x => x.Emote == emote.Name
|
||||||
|
|| x.Emote == emote.ToString());
|
||||||
if (rero is null)
|
if (rero is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (user, role) = await GetUserAndRoleAsync(r, rero);
|
var (user, role) = await GetUserAndRoleAsync(r.UserId, rero);
|
||||||
|
|
||||||
if (user.IsBot)
|
if (user.IsBot)
|
||||||
return;
|
return;
|
||||||
@@ -112,6 +115,24 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// had to add this because for some reason, reactionremoved event's reaction doesn't have IsAnimated set,
|
||||||
|
// causing the .ToString() to be wrong on animated custom emotes
|
||||||
|
private async Task<IEmote> GetFixedEmoteAsync(
|
||||||
|
Cacheable<IUserMessage, ulong> cmsg,
|
||||||
|
IEmote inputEmote)
|
||||||
|
{
|
||||||
|
// this should only run for emote
|
||||||
|
if (inputEmote is not Emote e)
|
||||||
|
return inputEmote;
|
||||||
|
|
||||||
|
// try to get the message and pull
|
||||||
|
var msg = await cmsg.GetOrDownloadAsync();
|
||||||
|
|
||||||
|
var emote = msg.Reactions.Keys.FirstOrDefault(x => e.Equals(x));
|
||||||
|
return emote ?? inputEmote;
|
||||||
|
}
|
||||||
|
|
||||||
private Task ClientOnReactionAdded(
|
private Task ClientOnReactionAdded(
|
||||||
Cacheable<IUserMessage, ulong> msg,
|
Cacheable<IUserMessage, ulong> msg,
|
||||||
Cacheable<IMessageChannel, ulong> ch,
|
Cacheable<IMessageChannel, ulong> ch,
|
||||||
@@ -126,7 +147,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
if (rero is null)
|
if (rero is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (user, role) = await GetUserAndRoleAsync(r, rero);
|
var (user, role) = await GetUserAndRoleAsync(r.UserId, rero);
|
||||||
|
|
||||||
if (user.IsBot)
|
if (user.IsBot)
|
||||||
return;
|
return;
|
||||||
|
@@ -27,7 +27,7 @@ public sealed class CheckForUpdatesService : INService, IReadyExecutor
|
|||||||
if (_client.ShardId != 0)
|
if (_client.ShardId != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(15));
|
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
|
||||||
while (await timer.WaitForNextTickAsync())
|
while (await timer.WaitForNextTickAsync())
|
||||||
{
|
{
|
||||||
var conf = _bcs.Data;
|
var conf = _bcs.Data;
|
||||||
|
@@ -345,6 +345,10 @@ public partial class Administration
|
|||||||
if (punish is PunishmentAction.AddRole or PunishmentAction.Warn)
|
if (punish is PunishmentAction.AddRole or PunishmentAction.Warn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// you must specify the time for timeout
|
||||||
|
if (punish is PunishmentAction.TimeOut && time is null)
|
||||||
|
return;
|
||||||
|
|
||||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
@@ -425,7 +429,8 @@ public partial class Administration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _mute.TimedBan(ctx.Guild, user, time.Time, (ctx.User + " | " + msg).TrimTo(512));
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
|
await _mute.TimedBan(ctx.Guild, user, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
||||||
var toSend = _eb.Create()
|
var toSend = _eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
@@ -451,7 +456,8 @@ public partial class Administration
|
|||||||
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
var user = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
|
||||||
if (user is null)
|
if (user is null)
|
||||||
{
|
{
|
||||||
await ctx.Guild.AddBanAsync(userId, 7, (ctx.User + " | " + msg).TrimTo(512));
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
|
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
@@ -486,7 +492,8 @@ public partial class Administration
|
|||||||
dmFailed = true;
|
dmFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.Guild.AddBanAsync(user, 7, (ctx.User + " | " + msg).TrimTo(512));
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
|
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
var toSend = _eb.Create()
|
var toSend = _eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
@@ -500,6 +507,26 @@ public partial class Administration
|
|||||||
await ctx.Channel.EmbedAsync(toSend);
|
await ctx.Channel.EmbedAsync(toSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
|
[BotPerm(GuildPerm.BanMembers)]
|
||||||
|
public async Task BanPrune(int days)
|
||||||
|
{
|
||||||
|
if (days < 0 || days > 7)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.invalid_input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _service.SetBanPruneAsync(ctx.Guild.Id, days);
|
||||||
|
|
||||||
|
if (days == 0)
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.ban_prune_disabled);
|
||||||
|
else
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.ban_prune(days));
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
@@ -651,7 +678,8 @@ public partial class Administration
|
|||||||
dmFailed = true;
|
dmFailed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.Guild.AddBanAsync(user, 7, ("Softban | " + ctx.User + " | " + msg).TrimTo(512));
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
|
await ctx.Guild.AddBanAsync(user, banPrune, ("Softban | " + ctx.User + " | " + msg).TrimTo(512));
|
||||||
try { await ctx.Guild.RemoveBanAsync(user); }
|
try { await ctx.Guild.RemoveBanAsync(user); }
|
||||||
catch { await ctx.Guild.RemoveBanAsync(user); }
|
catch { await ctx.Guild.RemoveBanAsync(user); }
|
||||||
|
|
||||||
@@ -718,6 +746,49 @@ public partial class Administration
|
|||||||
|
|
||||||
await ctx.Channel.EmbedAsync(toSend);
|
await ctx.Channel.EmbedAsync(toSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[UserPerm(GuildPerm.ModerateMembers)]
|
||||||
|
[BotPerm(GuildPerm.ModerateMembers)]
|
||||||
|
[Priority(2)]
|
||||||
|
public async Task Timeout(IUser globalUser, StoopidTime time, [Leftover] string msg = null)
|
||||||
|
{
|
||||||
|
var user = await ctx.Guild.GetUserAsync(globalUser.Id);
|
||||||
|
|
||||||
|
if (user is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!await CheckRoleHierarchy(user))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dmFailed = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
|
||||||
|
await user.EmbedAsync(_eb.Create(ctx)
|
||||||
|
.WithPendingColor()
|
||||||
|
.WithDescription(dmMessage));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
dmFailed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await user.SetTimeOutAsync(time.Time);
|
||||||
|
|
||||||
|
var toSend = _eb.Create()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle("⏳ " + GetText(strs.timedout_user))
|
||||||
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
|
.AddField("ID", user.Id.ToString(), true);
|
||||||
|
|
||||||
|
if (dmFailed)
|
||||||
|
toSend.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
|
||||||
|
|
||||||
|
await ctx.Channel.EmbedAsync(toSend);
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
@@ -775,11 +846,12 @@ public partial class Administration
|
|||||||
|
|
||||||
var banningMessage = await ctx.Channel.EmbedAsync(toSend);
|
var banningMessage = await ctx.Channel.EmbedAsync(toSend);
|
||||||
|
|
||||||
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
foreach (var toBan in banning)
|
foreach (var toBan in banning)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ctx.Guild.AddBanAsync(toBan.Id, 7, $"{ctx.User} | Massban");
|
await ctx.Guild.AddBanAsync(toBan.Id, banPrune, $"{ctx.User} | Massban");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -818,10 +890,11 @@ public partial class Administration
|
|||||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||||
.WithPendingColor());
|
.WithPendingColor());
|
||||||
|
|
||||||
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
//do the banning
|
//do the banning
|
||||||
await Task.WhenAll(bans.Where(x => x.Id.HasValue)
|
await Task.WhenAll(bans.Where(x => x.Id.HasValue)
|
||||||
.Select(x => ctx.Guild.AddBanAsync(x.Id.Value,
|
.Select(x => ctx.Guild.AddBanAsync(x.Id.Value,
|
||||||
7,
|
banPrune,
|
||||||
x.Reason,
|
x.Reason,
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using LinqToDB;
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Common.TypeReaders.Models;
|
using NadekoBot.Common.TypeReaders.Models;
|
||||||
@@ -127,6 +128,7 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
if (!await CheckPermission(guild, p))
|
if (!await CheckPermission(guild, p))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int banPrune;
|
||||||
switch (p)
|
switch (p)
|
||||||
{
|
{
|
||||||
case PunishmentAction.Mute:
|
case PunishmentAction.Mute:
|
||||||
@@ -151,13 +153,15 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
await user.KickAsync(reason);
|
await user.KickAsync(reason);
|
||||||
break;
|
break;
|
||||||
case PunishmentAction.Ban:
|
case PunishmentAction.Ban:
|
||||||
|
banPrune = await GetBanPruneAsync(user.GuildId) ?? 7;
|
||||||
if (minutes == 0)
|
if (minutes == 0)
|
||||||
await guild.AddBanAsync(user, reason: reason, pruneDays: 7);
|
await guild.AddBanAsync(user, reason: reason, pruneDays: banPrune);
|
||||||
else
|
else
|
||||||
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason);
|
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason, banPrune);
|
||||||
break;
|
break;
|
||||||
case PunishmentAction.Softban:
|
case PunishmentAction.Softban:
|
||||||
await guild.AddBanAsync(user, 7, $"Softban | {reason}");
|
banPrune = await GetBanPruneAsync(user.GuildId) ?? 7;
|
||||||
|
await guild.AddBanAsync(user, banPrune, $"Softban | {reason}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await guild.RemoveBanAsync(user);
|
await guild.RemoveBanAsync(user);
|
||||||
@@ -193,6 +197,9 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
case PunishmentAction.Warn:
|
case PunishmentAction.Warn:
|
||||||
await Warn(guild, user.Id, mod, 1, reason);
|
await Warn(guild, user.Id, mod, 1, reason);
|
||||||
break;
|
break;
|
||||||
|
case PunishmentAction.TimeOut:
|
||||||
|
await user.SetTimeOutAsync(TimeSpan.FromMinutes(minutes));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +231,8 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
return botUser.GuildPermissions.MuteMembers;
|
return botUser.GuildPermissions.MuteMembers;
|
||||||
case PunishmentAction.AddRole:
|
case PunishmentAction.AddRole:
|
||||||
return botUser.GuildPermissions.ManageRoles;
|
return botUser.GuildPermissions.ManageRoles;
|
||||||
|
case PunishmentAction.TimeOut:
|
||||||
|
return botUser.GuildPermissions.ModerateMembers;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -351,7 +360,7 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
await uow.Warnings.ForgiveAll(guildId, userId, moderator);
|
await uow.Warnings.ForgiveAll(guildId, userId, moderator);
|
||||||
else
|
else
|
||||||
toReturn = uow.Warnings.Forgive(guildId, userId, moderator, index - 1);
|
toReturn = uow.Warnings.Forgive(guildId, userId, moderator, index - 1);
|
||||||
uow.SaveChanges();
|
await uow.SaveChangesAsync();
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,6 +381,9 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
if (punish is PunishmentAction.AddRole && role is null)
|
if (punish is PunishmentAction.AddRole && role is null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (punish is PunishmentAction.TimeOut && time is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
using var uow = _db.GetDbContext();
|
using var uow = _db.GetDbContext();
|
||||||
var ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
var ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
||||||
var toDelete = ps.Where(x => x.Count == number);
|
var toDelete = ps.Where(x => x.Count == number);
|
||||||
@@ -481,6 +493,37 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
uow.SaveChanges();
|
uow.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SetBanPruneAsync(ulong guildId, int? pruneDays)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
await ctx.BanTemplates
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
Text = null,
|
||||||
|
DateAdded = DateTime.UtcNow,
|
||||||
|
PruneDays = pruneDays
|
||||||
|
},
|
||||||
|
old => new()
|
||||||
|
{
|
||||||
|
PruneDays = pruneDays
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int?> GetBanPruneAsync(ulong guildId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
return await ctx.BanTemplates
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.Select(x => x.PruneDays)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
}
|
||||||
|
|
||||||
public SmartText GetBanUserDmEmbed(
|
public SmartText GetBanUserDmEmbed(
|
||||||
ICommandContext context,
|
ICommandContext context,
|
||||||
IGuildUser target,
|
IGuildUser target,
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using Nadeko.Common;
|
|
||||||
|
|
||||||
namespace NadekoBot.Modules.NadekoExpressions;
|
namespace NadekoBot.Modules.NadekoExpressions;
|
||||||
|
|
||||||
[Name("Expressions")]
|
[Name("Expressions")]
|
||||||
@@ -25,15 +23,10 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
=> (ctx.Guild is null && _creds.IsOwner(ctx.User))
|
=> (ctx.Guild is null && _creds.IsOwner(ctx.User))
|
||||||
|| (ctx.Guild is not null && ((IGuildUser)ctx.User).GuildPermissions.Administrator);
|
|| (ctx.Guild is not null && ((IGuildUser)ctx.User).GuildPermissions.Administrator);
|
||||||
|
|
||||||
[Cmd]
|
private async Task ExprAddInternalAsync(string key, string message)
|
||||||
public async Task ExprAdd(string key, [Leftover] string message)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||||
return;
|
|
||||||
|
|
||||||
if (!AdminInGuildOrOwnerInDm())
|
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,12 +41,43 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
|
message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
|
public async Task ExprAddServer(string key, [Leftover] string message)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ExprAddInternalAsync(key, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task ExprAdd(string key, [Leftover] string message)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AdminInGuildOrOwnerInDm())
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ExprAddInternalAsync(key, message);
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task ExprEdit(kwum id, [Leftover] string message)
|
public async Task ExprEdit(kwum id, [Leftover] string message)
|
||||||
{
|
{
|
||||||
var channel = ctx.Channel as ITextChannel;
|
var channel = ctx.Channel as ITextChannel;
|
||||||
if (string.IsNullOrWhiteSpace(message) || id < 0)
|
if (string.IsNullOrWhiteSpace(message) || id < 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((channel is null && !_creds.IsOwner(ctx.User))
|
if ((channel is null && !_creds.IsOwner(ctx.User))
|
||||||
|| (channel is not null && !((IGuildUser)ctx.User).GuildPermissions.Administrator))
|
|| (channel is not null && !((IGuildUser)ctx.User).GuildPermissions.Administrator))
|
||||||
@@ -74,7 +98,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
|
message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -82,7 +108,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
public async Task ExprList(int page = 1)
|
public async Task ExprList(int page = 1)
|
||||||
{
|
{
|
||||||
if (--page < 0 || page > 999)
|
if (--page < 0 || page > 999)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var expressions = _service.GetExpressionsFor(ctx.Guild?.Id);
|
var expressions = _service.GetExpressionsFor(ctx.Guild?.Id);
|
||||||
|
|
||||||
@@ -153,7 +181,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
.AddField(GetText(strs.response), ex.Response.TrimTo(1024)));
|
.AddField(GetText(strs.response), ex.Response.TrimTo(1024)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -192,7 +222,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
succ.Add(emojiStr);
|
succ.Add(emojiStr);
|
||||||
|
|
||||||
if (succ.Count >= 3)
|
if (succ.Count >= 3)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
@@ -336,4 +368,4 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
|
|
||||||
await ctx.OkAsync();
|
await ctx.OkAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,4 @@
|
|||||||
using Nadeko.Common;
|
using NadekoBot.Modules.Gambling.Bank;
|
||||||
using NadekoBot.Modules.Gambling.Bank;
|
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
|
||||||
@@ -12,10 +11,14 @@ public partial class Gambling
|
|||||||
public partial class BankCommands : GamblingModule<IBankService>
|
public partial class BankCommands : GamblingModule<IBankService>
|
||||||
{
|
{
|
||||||
private readonly IBankService _bank;
|
private readonly IBankService _bank;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
public BankCommands(GamblingConfigService gcs, IBankService bank) : base(gcs)
|
public BankCommands(GamblingConfigService gcs,
|
||||||
|
IBankService bank,
|
||||||
|
DiscordSocketClient client) : base(gcs)
|
||||||
{
|
{
|
||||||
_bank = bank;
|
_bank = bank;
|
||||||
|
_client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -35,7 +38,7 @@ public partial class Gambling
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task BankWithdraw(ShmartNumber amount)
|
public async Task BankWithdraw(ShmartBankAmount amount)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
return;
|
return;
|
||||||
@@ -69,5 +72,50 @@ public partial class Gambling
|
|||||||
await ReplyErrorLocalizedAsync(strs.cant_dm);
|
await ReplyErrorLocalizedAsync(strs.cant_dm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task BankTakeInternalAsync(long amount, ulong userId)
|
||||||
|
{
|
||||||
|
if (await _bank.TakeAsync(userId, amount))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount),
|
||||||
|
_client.GetUser(userId)?.ToString()
|
||||||
|
?? userId.ToString(),
|
||||||
|
CurrencySign));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BankAwardInternalAsync(long amount, ulong userId)
|
||||||
|
{
|
||||||
|
if (await _bank.AwardAsync(userId, amount))
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount),
|
||||||
|
_client.GetUser(userId)?.ToString()
|
||||||
|
?? userId.ToString(),
|
||||||
|
CurrencySign));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
[Priority(1)]
|
||||||
|
public async Task BankTake(long amount, [Leftover] IUser user)
|
||||||
|
=> await BankTakeInternalAsync(amount, user.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task BankTake(long amount, ulong userId)
|
||||||
|
=> await BankTakeInternalAsync(amount, userId);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task BankAward(long amount, [Leftover] IUser user)
|
||||||
|
=> await BankAwardInternalAsync(amount, user.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,6 +15,48 @@ public sealed class BankService : IBankService, INService
|
|||||||
_db = db;
|
_db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AwardAsync(ulong userId, long amount)
|
||||||
|
{
|
||||||
|
if (amount <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
await ctx.BankUsers
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
Balance = amount
|
||||||
|
},
|
||||||
|
(old) => new()
|
||||||
|
{
|
||||||
|
Balance = old.Balance + amount
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
UserId = userId
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> TakeAsync(ulong userId, long amount)
|
||||||
|
{
|
||||||
|
if (amount <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var rows = await ctx.BankUsers
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.Where(x => x.UserId == userId && x.Balance >= amount)
|
||||||
|
.UpdateAsync((old) => new()
|
||||||
|
{
|
||||||
|
Balance = old.Balance - amount
|
||||||
|
});
|
||||||
|
|
||||||
|
return rows > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> DepositAsync(ulong userId, long amount)
|
public async Task<bool> DepositAsync(ulong userId, long amount)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
|
@@ -5,4 +5,6 @@ public interface IBankService
|
|||||||
Task<bool> DepositAsync(ulong userId, long amount);
|
Task<bool> DepositAsync(ulong userId, long amount);
|
||||||
Task<bool> WithdrawAsync(ulong userId, long amount);
|
Task<bool> WithdrawAsync(ulong userId, long amount);
|
||||||
Task<long> GetBalanceAsync(ulong userId);
|
Task<long> GetBalanceAsync(ulong userId);
|
||||||
|
Task<bool> AwardAsync(ulong userId, long amount);
|
||||||
|
Task<bool> TakeAsync(ulong userId, long amount);
|
||||||
}
|
}
|
@@ -1,13 +0,0 @@
|
|||||||
#nullable disable
|
|
||||||
namespace NadekoBot.Modules.Gambling;
|
|
||||||
|
|
||||||
public class CashInteraction : NInteraction
|
|
||||||
{
|
|
||||||
protected override NadekoInteractionData Data
|
|
||||||
=> new NadekoInteractionData(new Emoji("🏦"), "cash:bank_show_balance");
|
|
||||||
|
|
||||||
public CashInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action)
|
|
||||||
: base(client, userId, action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -99,39 +99,20 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
PrettyName = "Timely"
|
PrettyName = "Timely"
|
||||||
};
|
};
|
||||||
|
|
||||||
public class RemindMeInteraction : NInteraction
|
private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
|
||||||
{
|
{
|
||||||
public RemindMeInteraction(
|
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative);
|
||||||
[NotNull] DiscordSocketClient client,
|
|
||||||
ulong userId,
|
|
||||||
[NotNull] Func<SocketMessageComponent, Task> action)
|
|
||||||
: base(client, userId, action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override NadekoInteractionData Data
|
await _remind.AddReminderAsync(ctx.User.Id,
|
||||||
=> new NadekoInteractionData(
|
ctx.User.Id,
|
||||||
Emote: Emoji.Parse("⏰"),
|
ctx.Guild.Id,
|
||||||
CustomId: "timely:remind_me",
|
true,
|
||||||
Text: "Remind me"
|
when,
|
||||||
);
|
GetText(strs.timely_time));
|
||||||
|
|
||||||
|
await smc.RespondConfirmAsync(_eb, GetText(strs.remind_timely(tt)), ephemeral: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Func<SocketMessageComponent, Task> RemindTimelyAction(DateTime when)
|
|
||||||
=> async smc =>
|
|
||||||
{
|
|
||||||
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative);
|
|
||||||
|
|
||||||
await _remind.AddReminderAsync(ctx.User.Id,
|
|
||||||
ctx.User.Id,
|
|
||||||
ctx.Guild.Id,
|
|
||||||
true,
|
|
||||||
when,
|
|
||||||
GetText(strs.timely_time));
|
|
||||||
|
|
||||||
await smc.RespondConfirmAsync(_eb, GetText(strs.remind_timely(tt)), ephemeral: true);
|
|
||||||
};
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task Timely()
|
public async Task Timely()
|
||||||
{
|
{
|
||||||
@@ -147,7 +128,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
var relativeTag = TimestampTag.FromDateTime(now.Add(rem), TimestampTagStyles.Relative);
|
var relativeTag = TimestampTag.FromDateTime(now.Add(rem), TimestampTagStyles.Relative);
|
||||||
await ReplyErrorLocalizedAsync(strs.timely_already_claimed(relativeTag));
|
await ReplyPendingLocalizedAsync(strs.timely_already_claimed(relativeTag));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,11 +138,17 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
||||||
|
|
||||||
var inter = new RemindMeInteraction(_client,
|
var inter = _inter
|
||||||
ctx.User.Id,
|
.Create(ctx.User.Id,
|
||||||
RemindTimelyAction(DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
|
new SimpleInteraction<DateTime>(
|
||||||
|
new ButtonBuilder(
|
||||||
await ReplyConfirmLocalizedAsync(strs.timely(N(val), period), inter.GetInteraction());
|
label: "Remind me",
|
||||||
|
emote: Emoji.Parse("⏰"),
|
||||||
|
customId: "timely:remind_me"),
|
||||||
|
RemindTimelyAction,
|
||||||
|
DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
|
||||||
|
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.timely(N(val), period), inter);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -362,7 +349,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), cur));
|
await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), cur));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task BankAction(SocketMessageComponent smc)
|
private async Task BankAction(SocketMessageComponent smc, object _)
|
||||||
{
|
{
|
||||||
var balance = await _bank.GetBalanceAsync(ctx.User.Id);
|
var balance = await _bank.GetBalanceAsync(ctx.User.Id);
|
||||||
|
|
||||||
@@ -372,8 +359,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
.Pipe(text => smc.RespondConfirmAsync(_eb, text, ephemeral: true));
|
.Pipe(text => smc.RespondConfirmAsync(_eb, text, ephemeral: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private NadekoButtonInteraction CreateCashInteraction()
|
private NadekoInteraction CreateCashInteraction()
|
||||||
=> new CashInteraction(_client, ctx.User.Id, BankAction).GetInteraction();
|
=> _inter.Create<object>(ctx.User.Id,
|
||||||
|
new(new(
|
||||||
|
customId: "cash:bank_show_balance",
|
||||||
|
emote: new Emoji("🏦")),
|
||||||
|
BankAction));
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
@@ -663,7 +654,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
if (win > 0)
|
if (win > 0)
|
||||||
{
|
{
|
||||||
str = GetText(strs.br_win(N(win), result.Threshold + (result.Roll == 100 ? " 👑" : "")));
|
str = GetText(strs.br_win(N(win), result.Threshold + (result.Roll == 100 ? " 👑" : "")));
|
||||||
await _cs.AddAsync(ctx.User, win, new("betroll", "win"));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -876,11 +866,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
public enum GambleTestTarget
|
public enum GambleTestTarget
|
||||||
{
|
{
|
||||||
Slot,
|
Slot,
|
||||||
|
Betroll,
|
||||||
|
Betflip,
|
||||||
|
BetflipT,
|
||||||
BetDraw,
|
BetDraw,
|
||||||
BetDrawHL,
|
BetDrawHL,
|
||||||
BetDrawRB,
|
BetDrawRB,
|
||||||
Betflip,
|
|
||||||
BetflipT,
|
|
||||||
Lula,
|
Lula,
|
||||||
Rps,
|
Rps,
|
||||||
}
|
}
|
||||||
@@ -921,6 +912,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
GambleTestTarget.BetflipT => (await _gs.BetFlipAsync(ctx.User.Id, 0, 1)).AsT0.Multiplier,
|
GambleTestTarget.BetflipT => (await _gs.BetFlipAsync(ctx.User.Id, 0, 1)).AsT0.Multiplier,
|
||||||
GambleTestTarget.Lula => (await _gs.LulaAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
GambleTestTarget.Lula => (await _gs.LulaAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||||
GambleTestTarget.Rps => (await _gs.RpsAsync(ctx.User.Id, 0, (byte)(i % 3))).AsT0.Multiplier,
|
GambleTestTarget.Rps => (await _gs.RpsAsync(ctx.User.Id, 0, (byte)(i % 3))).AsT0.Multiplier,
|
||||||
|
GambleTestTarget.Betroll => (await _gs.BetRollAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(target))
|
_ => throw new ArgumentOutOfRangeException(nameof(target))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -142,7 +142,7 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
var onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id);
|
var onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id);
|
||||||
decimal planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount);
|
decimal planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount);
|
||||||
var waifus = uow.WaifuInfo.GetTotalValue();
|
var waifus = uow.WaifuInfo.GetTotalValue();
|
||||||
var bot = uow.DiscordUser.GetUserCurrency(_client.CurrentUser.Id);
|
var bot = await uow.DiscordUser.GetUserCurrencyAsync(_client.CurrentUser.Id);
|
||||||
decimal bank = await uow.GetTable<BankUser>()
|
decimal bank = await uow.GetTable<BankUser>()
|
||||||
.SumAsyncLinqToDB(x => x.Balance);
|
.SumAsyncLinqToDB(x => x.Balance);
|
||||||
|
|
||||||
|
@@ -27,12 +27,6 @@ public partial class Gambling
|
|||||||
private static decimal totalBet;
|
private static decimal totalBet;
|
||||||
private static decimal totalPaidOut;
|
private static decimal totalPaidOut;
|
||||||
|
|
||||||
private static readonly ConcurrentHashSet<ulong> _runningUsers = new();
|
|
||||||
|
|
||||||
//here is a payout chart
|
|
||||||
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
|
|
||||||
//thanks to judge for helping me with this
|
|
||||||
|
|
||||||
private readonly IImageCache _images;
|
private readonly IImageCache _images;
|
||||||
private readonly FontProvider _fonts;
|
private readonly FontProvider _fonts;
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
@@ -73,16 +67,6 @@ public partial class Gambling
|
|||||||
|
|
||||||
await ctx.Channel.EmbedAsync(embed);
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
public sealed class SlotInteraction : NInteraction
|
|
||||||
{
|
|
||||||
public SlotInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action) : base(client, userId, action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override NadekoInteractionData Data { get; } = new(Emoji.Parse("🔁"),
|
|
||||||
"slot:again",
|
|
||||||
"Pull Again");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task Slot(ShmartNumber amount)
|
public async Task Slot(ShmartNumber amount)
|
||||||
@@ -94,43 +78,34 @@ public partial class Gambling
|
|||||||
|
|
||||||
await ctx.Channel.TriggerTypingAsync();
|
await ctx.Channel.TriggerTypingAsync();
|
||||||
|
|
||||||
if (!_runningUsers.Add(ctx.User.Id))
|
if (await InternalSlotAsync(amount) is not SlotResult result)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (await InternalSlotAsync(amount) is not SlotResult result)
|
|
||||||
{
|
|
||||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg = GetSlotMessageInternal(result);
|
|
||||||
|
|
||||||
using var image = await GenerateSlotImageAsync(amount, result);
|
|
||||||
await using var imgStream = await image.ToStreamAsync();
|
|
||||||
|
|
||||||
|
|
||||||
var eb = _eb.Create(ctx)
|
|
||||||
.WithAuthor(ctx.User)
|
|
||||||
.WithDescription(Format.Bold(msg))
|
|
||||||
.WithImageUrl($"attachment://result.png")
|
|
||||||
.WithOkColor();
|
|
||||||
|
|
||||||
// var inter = slotInteraction.GetInteraction();
|
|
||||||
await ctx.Channel.SendFileAsync(imgStream,
|
|
||||||
"result.png",
|
|
||||||
embed: eb.Build()
|
|
||||||
// components: inter.CreateComponent()
|
|
||||||
);
|
|
||||||
|
|
||||||
// await inter.RunAsync(resMsg);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await Task.Delay(1000);
|
|
||||||
_runningUsers.TryRemove(ctx.User.Id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var text = GetSlotMessageTextInternal(result);
|
||||||
|
|
||||||
|
using var image = await GenerateSlotImageAsync(amount, result);
|
||||||
|
await using var imgStream = await image.ToStreamAsync();
|
||||||
|
|
||||||
|
|
||||||
|
var eb = _eb.Create(ctx)
|
||||||
|
.WithAuthor(ctx.User)
|
||||||
|
.WithDescription(Format.Bold(text))
|
||||||
|
.WithImageUrl($"attachment://result.png")
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
|
||||||
|
var si = new SimpleInteraction<ShmartNumber>(bb, (_, amount) => Slot(amount), amount);
|
||||||
|
|
||||||
|
var inter = _inter.Create(ctx.User.Id, si);
|
||||||
|
var msg = await ctx.Channel.SendFileAsync(imgStream,
|
||||||
|
"result.png",
|
||||||
|
embed: eb.Build(),
|
||||||
|
components: inter.CreateComponent()
|
||||||
|
);
|
||||||
|
await inter.RunAsync(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private SlotInteraction CreateSlotInteractionIntenal(long amount)
|
// private SlotInteraction CreateSlotInteractionIntenal(long amount)
|
||||||
@@ -181,7 +156,7 @@ public partial class Gambling
|
|||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private string GetSlotMessageInternal(SlotResult result)
|
private string GetSlotMessageTextInternal(SlotResult result)
|
||||||
{
|
{
|
||||||
var multi = result.Multiplier.ToString("0.##");
|
var multi = result.Multiplier.ToString("0.##");
|
||||||
var msg = result.WinType switch
|
var msg = result.WinType switch
|
||||||
@@ -201,7 +176,6 @@ public partial class Gambling
|
|||||||
|
|
||||||
if (!maybeResult.TryPickT0(out var result, out var error))
|
if (!maybeResult.TryPickT0(out var result, out var error))
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +209,7 @@ public partial class Gambling
|
|||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
WrappingLength = 140,
|
WrappingLength = 140,
|
||||||
Origin = new(227, 92)
|
Origin = new(298, 100)
|
||||||
},
|
},
|
||||||
((long)result.Won).ToString(),
|
((long)result.Won).ToString(),
|
||||||
fontColor));
|
fontColor));
|
||||||
@@ -247,7 +221,7 @@ public partial class Gambling
|
|||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
WrappingLength = 135,
|
WrappingLength = 135,
|
||||||
Origin = new(129, 472)
|
Origin = new(196, 480)
|
||||||
},
|
},
|
||||||
amount.ToString(),
|
amount.ToString(),
|
||||||
fontColor));
|
fontColor));
|
||||||
@@ -256,8 +230,7 @@ public partial class Gambling
|
|||||||
{
|
{
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
WrappingLength = 135,
|
Origin = new(393, 480)
|
||||||
Origin = new(325, 472)
|
|
||||||
},
|
},
|
||||||
ownedAmount.ToString(),
|
ownedAmount.ToString(),
|
||||||
fontColor));
|
fontColor));
|
||||||
|
@@ -57,123 +57,133 @@ public sealed class TriviaGame
|
|||||||
// loop until game is stopped
|
// loop until game is stopped
|
||||||
// each iteration is one round
|
// each iteration is one round
|
||||||
var firstRun = true;
|
var firstRun = true;
|
||||||
while (!_isStopped)
|
try
|
||||||
{
|
{
|
||||||
if (errorCount >= 5)
|
while (!_isStopped)
|
||||||
{
|
{
|
||||||
Log.Warning("Trivia errored 5 times and will quit");
|
if (errorCount >= 5)
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for 3 seconds before posting the next question
|
|
||||||
if (firstRun)
|
|
||||||
{
|
|
||||||
firstRun = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Task.Delay(3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
var maybeQuestion = await _questionPool.GetQuestionAsync();
|
|
||||||
|
|
||||||
if(!(maybeQuestion is TriviaQuestion question))
|
|
||||||
{
|
|
||||||
// if question is null (ran out of question, or other bugg ) - stop
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentQuestion = question;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// clear out all of the past guesses
|
|
||||||
while (_inputs.Reader.TryRead(out _)) ;
|
|
||||||
|
|
||||||
await OnQuestion(this, question);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning(ex, "Error executing OnQuestion: {Message}", ex.Message);
|
|
||||||
errorCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// just keep looping through user inputs until someone guesses the answer
|
|
||||||
// or the timer expires
|
|
||||||
var halfGuessTimerTask = TimeOutFactory();
|
|
||||||
var hintSent = false;
|
|
||||||
var guessed = false;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var readTask = _inputs.Reader.ReadAsync().AsTask();
|
|
||||||
|
|
||||||
// wait for either someone to attempt to guess
|
|
||||||
// or for timeout
|
|
||||||
var task = await Task.WhenAny(readTask, halfGuessTimerTask);
|
|
||||||
|
|
||||||
// if the task which completed is the timeout task
|
|
||||||
if (task == halfGuessTimerTask)
|
|
||||||
{
|
{
|
||||||
// if hint is already sent, means time expired
|
Log.Warning("Trivia errored 5 times and will quit");
|
||||||
// break (end the round)
|
await OnEnded(this);
|
||||||
if (hintSent)
|
break;
|
||||||
break;
|
}
|
||||||
|
|
||||||
// else, means half time passed, send a hint
|
// wait for 3 seconds before posting the next question
|
||||||
hintSent = true;
|
if (firstRun)
|
||||||
// start a new countdown of the same length
|
{
|
||||||
halfGuessTimerTask = TimeOutFactory();
|
firstRun = false;
|
||||||
// send a hint out
|
}
|
||||||
await OnHint(this, question);
|
else
|
||||||
|
{
|
||||||
|
await Task.Delay(3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var maybeQuestion = await _questionPool.GetQuestionAsync();
|
||||||
|
|
||||||
|
if (!(maybeQuestion is TriviaQuestion question))
|
||||||
|
{
|
||||||
|
// if question is null (ran out of question, or other bugg ) - stop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentQuestion = question;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// clear out all of the past guesses
|
||||||
|
while (_inputs.Reader.TryRead(out _))
|
||||||
|
;
|
||||||
|
|
||||||
|
await OnQuestion(this, question);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Error executing OnQuestion: {Message}", ex.Message);
|
||||||
|
errorCount++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, read task is successful, and we're gonna
|
|
||||||
// get the user input data
|
|
||||||
var (user, input) = await readTask;
|
|
||||||
|
|
||||||
// check the guess
|
// just keep looping through user inputs until someone guesses the answer
|
||||||
if (question.IsAnswerCorrect(input))
|
// or the timer expires
|
||||||
|
var halfGuessTimerTask = TimeOutFactory();
|
||||||
|
var hintSent = false;
|
||||||
|
var guessed = false;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
// add 1 point to the user
|
var readTask = _inputs.Reader.ReadAsync().AsTask();
|
||||||
var val = _users.AddOrUpdate(user.Id, 1, (_, points) => ++points);
|
|
||||||
guessed = true;
|
|
||||||
|
|
||||||
// reset inactivity counter
|
// wait for either someone to attempt to guess
|
||||||
inactivity = 0;
|
// or for timeout
|
||||||
|
var task = await Task.WhenAny(readTask, halfGuessTimerTask);
|
||||||
|
|
||||||
var isWin = false;
|
// if the task which completed is the timeout task
|
||||||
// if user won the game, tell the game to stop
|
if (task == halfGuessTimerTask)
|
||||||
if (val >= _opts.WinRequirement)
|
|
||||||
{
|
{
|
||||||
_isStopped = true;
|
// if hint is already sent, means time expired
|
||||||
isWin = true;
|
// break (end the round)
|
||||||
|
if (hintSent)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// else, means half time passed, send a hint
|
||||||
|
hintSent = true;
|
||||||
|
// start a new countdown of the same length
|
||||||
|
halfGuessTimerTask = TimeOutFactory();
|
||||||
|
// send a hint out
|
||||||
|
await OnHint(this, question);
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call onguess
|
// otherwise, read task is successful, and we're gonna
|
||||||
await OnGuess(this, user, question, isWin);
|
// get the user input data
|
||||||
break;
|
var (user, input) = await readTask;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!guessed)
|
// check the guess
|
||||||
{
|
if (question.IsAnswerCorrect(input))
|
||||||
await OnTimeout(this, question);
|
{
|
||||||
|
// add 1 point to the user
|
||||||
if (_opts.Timeout != 0 && ++inactivity >= _opts.Timeout)
|
var val = _users.AddOrUpdate(user.Id, 1, (_, points) => ++points);
|
||||||
|
guessed = true;
|
||||||
|
|
||||||
|
// reset inactivity counter
|
||||||
|
inactivity = 0;
|
||||||
|
|
||||||
|
var isWin = false;
|
||||||
|
// if user won the game, tell the game to stop
|
||||||
|
if (val >= _opts.WinRequirement)
|
||||||
|
{
|
||||||
|
_isStopped = true;
|
||||||
|
isWin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call onguess
|
||||||
|
await OnGuess(this, user, question, isWin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!guessed)
|
||||||
{
|
{
|
||||||
Log.Information("Trivia game is stopping due to inactivity");
|
await OnTimeout(this, question);
|
||||||
break;
|
|
||||||
|
if (_opts.Timeout != 0 && ++inactivity >= _opts.Timeout)
|
||||||
|
{
|
||||||
|
Log.Information("Trivia game is stopping due to inactivity");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
// make sure game is set as ended
|
}
|
||||||
_isStopped = true;
|
finally
|
||||||
|
{
|
||||||
await OnEnded(this);
|
// make sure game is set as ended
|
||||||
|
_isStopped = true;
|
||||||
|
_ = OnEnded(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<(ulong User, int points)> GetLeaderboard()
|
public IReadOnlyList<(ulong User, int points)> GetLeaderboard()
|
||||||
|
@@ -424,7 +424,7 @@ public partial class Help : NadekoModule<HelpService>
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
Log.Information("No old version list found. Creating a new one.");
|
Log.Information("No old version list found. Creating a new one");
|
||||||
}
|
}
|
||||||
|
|
||||||
var versionList = JsonSerializer.Deserialize<List<string>>(versionListString);
|
var versionList = JsonSerializer.Deserialize<List<string>>(versionListString);
|
||||||
@@ -469,7 +469,7 @@ public partial class Help : NadekoModule<HelpService>
|
|||||||
"https://nadekobot.readthedocs.io/en/latest/"));
|
"https://nadekobot.readthedocs.io/en/latest/"));
|
||||||
|
|
||||||
|
|
||||||
private Task SelfhostAction(SocketMessageComponent smc)
|
private Task SelfhostAction(SocketMessageComponent smc, object _)
|
||||||
=> smc.RespondConfirmAsync(_eb,
|
=> smc.RespondConfirmAsync(_eb,
|
||||||
@"- In case you don't want or cannot Donate to NadekoBot project, but you
|
@"- In case you don't want or cannot Donate to NadekoBot project, but you
|
||||||
- NadekoBot is a completely free and fully [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own ""selfhosted"" instance on your computer or server for free.
|
- NadekoBot is a completely free and fully [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own ""selfhosted"" instance on your computer or server for free.
|
||||||
@@ -484,7 +484,13 @@ public partial class Help : NadekoModule<HelpService>
|
|||||||
[OnlyPublicBot]
|
[OnlyPublicBot]
|
||||||
public async Task Donate()
|
public async Task Donate()
|
||||||
{
|
{
|
||||||
var selfhostInter = new DonateSelfhostingInteraction(_client, ctx.User.Id, SelfhostAction);
|
// => new NadekoInteractionData(new Emoji("🖥️"), "donate:selfhosting", "Selfhosting");
|
||||||
|
var selfhostInter = _inter.Create(ctx.User.Id,
|
||||||
|
new SimpleInteraction<object>(new ButtonBuilder(
|
||||||
|
emote: new Emoji("🖥️"),
|
||||||
|
customId: "donate:selfhosting",
|
||||||
|
label: "Selfhosting"),
|
||||||
|
SelfhostAction));
|
||||||
|
|
||||||
var eb = _eb.Create(ctx)
|
var eb = _eb.Create(ctx)
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
@@ -525,7 +531,7 @@ Nadeko will DM you the welcome instructions, and you may start using the patron-
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await (await ctx.User.CreateDMChannelAsync()).EmbedAsync(eb, inter: selfhostInter.GetInteraction());
|
await (await ctx.User.CreateDMChannelAsync()).EmbedAsync(eb, inter: selfhostInter);
|
||||||
_ = ctx.OkAsync();
|
_ = ctx.OkAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
namespace NadekoBot.Modules.Help;
|
|
||||||
|
|
||||||
public class DonateSelfhostingInteraction : NInteraction
|
|
||||||
{
|
|
||||||
protected override NadekoInteractionData Data
|
|
||||||
=> new NadekoInteractionData(new Emoji("🖥️"), "donate:selfhosting", "Selfhosting");
|
|
||||||
|
|
||||||
public DonateSelfhostingInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action)
|
|
||||||
: base(client, userId, action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
namespace NadekoBot.Modules.Help;
|
|
||||||
|
|
||||||
public class DonateTroubleshootInteraction : NInteraction
|
|
||||||
{
|
|
||||||
protected override NadekoInteractionData Data
|
|
||||||
=> new NadekoInteractionData(new Emoji("❓"), "donate:troubleshoot", "Troubleshoot");
|
|
||||||
|
|
||||||
public DonateTroubleshootInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action)
|
|
||||||
: base(client, userId, action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +1,4 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using Nadeko.Common;
|
|
||||||
using NadekoBot.Modules.Music.Services;
|
using NadekoBot.Modules.Music.Services;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
|
@@ -274,7 +274,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
|||||||
Log.Information("Resolving youtube song by search term: {YoutubeQuery}", query);
|
Log.Information("Resolving youtube song by search term: {YoutubeQuery}", query);
|
||||||
|
|
||||||
var cachedData = await _trackCacher.GetCachedDataByQueryAsync(query, MusicPlatform.Youtube);
|
var cachedData = await _trackCacher.GetCachedDataByQueryAsync(query, MusicPlatform.Youtube);
|
||||||
if (cachedData is null)
|
if (cachedData is null || string.IsNullOrWhiteSpace(cachedData.Title))
|
||||||
{
|
{
|
||||||
var stringData = await _ytdlSearchOperation.GetDataAsync(query);
|
var stringData = await _ytdlSearchOperation.GetDataAsync(query);
|
||||||
var trackData = ResolveYtdlData(stringData);
|
var trackData = ResolveYtdlData(stringData);
|
||||||
|
@@ -97,6 +97,33 @@ public partial class Utility
|
|||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
await ctx.Channel.EmbedAsync(embed);
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||||
|
public async Task RoleInfo([Leftover] SocketRole role)
|
||||||
|
{
|
||||||
|
if (role.IsEveryone)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
|
||||||
|
.AddMilliseconds(role.Id >> 22);
|
||||||
|
var usercount = role.Members.LongCount();
|
||||||
|
var embed = _eb.Create()
|
||||||
|
.WithTitle(role.Name.TrimTo(128))
|
||||||
|
.WithDescription(role.Permissions.ToList().Join(" | "))
|
||||||
|
.AddField(GetText(strs.id), role.Id.ToString(), true)
|
||||||
|
.AddField(GetText(strs.created_at), $"{createdAt:dd.MM.yyyy HH:mm}", true)
|
||||||
|
.AddField(GetText(strs.users), usercount.ToString(), true)
|
||||||
|
.AddField(GetText(strs.color), $"#{role.Color.R:X2}{role.Color.G:X2}{role.Color.B:X2}", true)
|
||||||
|
.AddField(GetText(strs.mentionable), role.IsMentionable.ToString(), true)
|
||||||
|
.AddField(GetText(strs.hoisted), role.IsHoisted.ToString(), true)
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(role.GetIconUrl()))
|
||||||
|
embed = embed.WithThumbnailUrl(role.GetIconUrl());
|
||||||
|
|
||||||
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
@@ -110,9 +137,11 @@ public partial class Utility
|
|||||||
var embed = _eb.Create().AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true);
|
var embed = _eb.Create().AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true);
|
||||||
if (!string.IsNullOrWhiteSpace(user.Nickname))
|
if (!string.IsNullOrWhiteSpace(user.Nickname))
|
||||||
embed.AddField(GetText(strs.nickname), user.Nickname, true);
|
embed.AddField(GetText(strs.nickname), user.Nickname, true);
|
||||||
|
|
||||||
|
var joinedAt = GetJoinedAt(user);
|
||||||
|
|
||||||
embed.AddField(GetText(strs.id), user.Id.ToString(), true)
|
embed.AddField(GetText(strs.id), user.Id.ToString(), true)
|
||||||
.AddField(GetText(strs.joined_server), $"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}", true)
|
.AddField(GetText(strs.joined_server), $"{joinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}", true)
|
||||||
.AddField(GetText(strs.joined_discord), $"{user.CreatedAt:dd.MM.yyyy HH:mm}", true)
|
.AddField(GetText(strs.joined_discord), $"{user.CreatedAt:dd.MM.yyyy HH:mm}", true)
|
||||||
.AddField(GetText(strs.roles),
|
.AddField(GetText(strs.roles),
|
||||||
$"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions(true)}",
|
$"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions(true)}",
|
||||||
@@ -140,6 +169,18 @@ public partial class Utility
|
|||||||
await ctx.Channel.EmbedAsync(embed);
|
await ctx.Channel.EmbedAsync(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DateTimeOffset? GetJoinedAt(IGuildUser user)
|
||||||
|
{
|
||||||
|
var joinedAt = user.JoinedAt;
|
||||||
|
if (user.GuildId != 117523346618318850)
|
||||||
|
return joinedAt;
|
||||||
|
|
||||||
|
if (user.Id == 351244576092192778)
|
||||||
|
return new DateTimeOffset(2019, 12, 25, 9, 33, 0, TimeSpan.Zero);
|
||||||
|
|
||||||
|
return joinedAt;
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
|
@@ -715,8 +715,8 @@ public sealed class PatronageService
|
|||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Name = key.PrettyName,
|
Name = key.PrettyName,
|
||||||
Quota = defaultValue,
|
Quota = 0,
|
||||||
IsPatronLimit = false,
|
IsPatronLimit = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
|
6
src/NadekoBot/Modules/Utility/Quote/IQuoteService.cs
Normal file
6
src/NadekoBot/Modules/Utility/Quote/IQuoteService.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace NadekoBot.Modules.Utility;
|
||||||
|
|
||||||
|
public interface IQuoteService
|
||||||
|
{
|
||||||
|
Task<int> DeleteAllAuthorQuotesAsync(ulong guildId, ulong userId);
|
||||||
|
}
|
@@ -1,5 +1,4 @@
|
|||||||
#nullable disable warnings
|
#nullable disable warnings
|
||||||
using Nadeko.Common;
|
|
||||||
using NadekoBot.Common.Yml;
|
using NadekoBot.Common.Yml;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
@@ -22,23 +21,25 @@ public partial class Utility
|
|||||||
";
|
";
|
||||||
|
|
||||||
private static readonly ISerializer _exportSerializer = new SerializerBuilder()
|
private static readonly ISerializer _exportSerializer = new SerializerBuilder()
|
||||||
.WithEventEmitter(args
|
.WithEventEmitter(args
|
||||||
=> new MultilineScalarFlowStyleEmitter(args))
|
=> new MultilineScalarFlowStyleEmitter(args))
|
||||||
.WithNamingConvention(
|
.WithNamingConvention(
|
||||||
CamelCaseNamingConvention.Instance)
|
CamelCaseNamingConvention.Instance)
|
||||||
.WithIndentedSequences()
|
.WithIndentedSequences()
|
||||||
.ConfigureDefaultValuesHandling(DefaultValuesHandling
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling
|
||||||
.OmitDefaults)
|
.OmitDefaults)
|
||||||
.DisableAliases()
|
.DisableAliases()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
private readonly IHttpClientFactory _http;
|
private readonly IHttpClientFactory _http;
|
||||||
|
private readonly IQuoteService _qs;
|
||||||
|
|
||||||
public QuoteCommands(DbService db, IHttpClientFactory http)
|
public QuoteCommands(DbService db, IQuoteService qs, IHttpClientFactory http)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_http = http;
|
_http = http;
|
||||||
|
_qs = qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -108,7 +109,7 @@ public partial class Utility
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task QuoteShow(int id)
|
public async Task QuoteShow(int id)
|
||||||
{
|
{
|
||||||
Quote quote;
|
Quote? quote;
|
||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
quote = uow.Quotes.GetById(id);
|
quote = uow.Quotes.GetById(id);
|
||||||
@@ -127,13 +128,13 @@ public partial class Utility
|
|||||||
|
|
||||||
private async Task ShowQuoteData(Quote data)
|
private async Task ShowQuoteData(Quote data)
|
||||||
=> await ctx.Channel.EmbedAsync(_eb.Create(ctx)
|
=> await ctx.Channel.EmbedAsync(_eb.Create(ctx)
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.quote_id($"#{data.Id}")))
|
.WithTitle(GetText(strs.quote_id($"#{data.Id}")))
|
||||||
.AddField(GetText(strs.trigger), data.Keyword)
|
.AddField(GetText(strs.trigger), data.Keyword)
|
||||||
.AddField(GetText(strs.response),
|
.AddField(GetText(strs.response),
|
||||||
Format.Sanitize(data.Text).Replace("](", "]\\("))
|
Format.Sanitize(data.Text).Replace("](", "]\\("))
|
||||||
.WithFooter(
|
.WithFooter(
|
||||||
GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})"))));
|
GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})"))));
|
||||||
|
|
||||||
private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor)
|
private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor)
|
||||||
{
|
{
|
||||||
@@ -256,6 +257,28 @@ public partial class Utility
|
|||||||
await SendErrorAsync(response);
|
await SendErrorAsync(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public Task QuoteDeleteAuthor(IUser user)
|
||||||
|
=> QuoteDeleteAuthor(user.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task QuoteDeleteAuthor(ulong userId)
|
||||||
|
{
|
||||||
|
var hasManageMessages = ((IGuildUser)ctx.Message.Author).GuildPermissions.ManageMessages;
|
||||||
|
|
||||||
|
if (userId == ctx.User.Id || hasManageMessages)
|
||||||
|
{
|
||||||
|
var deleted = await _qs.DeleteAllAuthorQuotesAsync(ctx.Guild.Id, ctx.User.Id);
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.quotes_deleted_count(deleted));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageMessages)]
|
[UserPerm(GuildPerm.ManageMessages)]
|
||||||
@@ -288,7 +311,7 @@ public partial class Utility
|
|||||||
}
|
}
|
||||||
|
|
||||||
var exprsDict = quotes.GroupBy(x => x.Keyword)
|
var exprsDict = quotes.GroupBy(x => x.Keyword)
|
||||||
.ToDictionary(x => x.Key, x => x.Select(ExportedQuote.FromModel));
|
.ToDictionary(x => x.Key, x => x.Select(ExportedQuote.FromModel));
|
||||||
|
|
||||||
var text = PREPEND_EXPORT + _exportSerializer.Serialize(exprsDict).UnescapeUnicodeCodePoints();
|
var text = PREPEND_EXPORT + _exportSerializer.Serialize(exprsDict).UnescapeUnicodeCodePoints();
|
||||||
|
|
||||||
@@ -303,7 +326,7 @@ public partial class Utility
|
|||||||
#if GLOBAL_NADEKO
|
#if GLOBAL_NADEKO
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
#endif
|
#endif
|
||||||
public async Task QuotesImport([Leftover] string input = null)
|
public async Task QuotesImport([Leftover] string? input = null)
|
||||||
{
|
{
|
||||||
input = input?.Trim();
|
input = input?.Trim();
|
||||||
|
|
||||||
@@ -357,14 +380,14 @@ public partial class Utility
|
|||||||
{
|
{
|
||||||
var keyword = entry.Key;
|
var keyword = entry.Key;
|
||||||
await uow.Quotes.AddRangeAsync(entry.Value.Where(quote => !string.IsNullOrWhiteSpace(quote.Txt))
|
await uow.Quotes.AddRangeAsync(entry.Value.Where(quote => !string.IsNullOrWhiteSpace(quote.Txt))
|
||||||
.Select(quote => new Quote
|
.Select(quote => new Quote
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Keyword = keyword,
|
Keyword = keyword,
|
||||||
Text = quote.Txt,
|
Text = quote.Txt,
|
||||||
AuthorId = quote.Aid,
|
AuthorId = quote.Aid,
|
||||||
AuthorName = quote.An
|
AuthorName = quote.An
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
|
32
src/NadekoBot/Modules/Utility/Quote/QuoteService.cs
Normal file
32
src/NadekoBot/Modules/Utility/Quote/QuoteService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#nullable disable warnings
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Utility;
|
||||||
|
|
||||||
|
public sealed class QuoteService : IQuoteService, INService
|
||||||
|
{
|
||||||
|
private readonly DbService _db;
|
||||||
|
|
||||||
|
public QuoteService(DbService db)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete all quotes created by the author in a guild
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guildId">ID of the guild</param>
|
||||||
|
/// <param name="userId">ID of the user</param>
|
||||||
|
/// <returns>Number of deleted qutoes</returns>
|
||||||
|
public async Task<int> DeleteAllAuthorQuotesAsync(ulong guildId, ulong userId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var deleted = await ctx.GetTable<Quote>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.AuthorId == userId)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
}
|
@@ -159,14 +159,27 @@ public class RemindService : INService, IReadyExecutor
|
|||||||
if (ch is null)
|
if (ch is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await ch.EmbedAsync(_eb.Create()
|
var st = SmartText.CreateFrom(r.Message);
|
||||||
.WithOkColor()
|
|
||||||
.WithTitle("Reminder")
|
if (st is SmartEmbedText set)
|
||||||
.AddField("Created At",
|
{
|
||||||
r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?")
|
await ch.SendMessageAsync(null, embed: set.GetEmbed().Build());
|
||||||
.AddField("By",
|
}
|
||||||
(await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString()),
|
else if (st is SmartEmbedTextArray seta)
|
||||||
r.Message);
|
{
|
||||||
|
await ch.SendMessageAsync(null, embeds: seta.GetEmbedBuilders().Map(x => x.Build()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ch.EmbedAsync(_eb.Create()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle("Reminder")
|
||||||
|
.AddField("Created At",
|
||||||
|
r.DateAdded.HasValue ? r.DateAdded.Value.ToLongDateString() : "?")
|
||||||
|
.AddField("By",
|
||||||
|
(await ch.GetUserAsync(r.UserId))?.ToString() ?? r.UserId.ToString()),
|
||||||
|
r.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@@ -367,6 +367,41 @@ public partial class Utility : NadekoModule
|
|||||||
await ConfirmLocalizedAsync(strs.emoji_added(em.ToString()));
|
await ConfirmLocalizedAsync(strs.emoji_added(em.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
||||||
|
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task EmojiRemove(params Emote[] emotes)
|
||||||
|
{
|
||||||
|
if (emotes.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var g = (SocketGuild)ctx.Guild;
|
||||||
|
|
||||||
|
var fails = new List<Emote>();
|
||||||
|
foreach (var emote in emotes)
|
||||||
|
{
|
||||||
|
var guildEmote = g.Emotes.FirstOrDefault(x => x.Id == emote.Id);
|
||||||
|
if (guildEmote is null)
|
||||||
|
{
|
||||||
|
fails.Add(emote);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await ctx.Guild.DeleteEmoteAsync(guildEmote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fails.Count > 0)
|
||||||
|
{
|
||||||
|
await ReplyPendingLocalizedAsync(strs.emoji_not_removed(fails.Select(x => x.ToString()).Join(" ")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task ListServers(int page = 1)
|
public async Task ListServers(int page = 1)
|
||||||
|
@@ -334,14 +334,15 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
|
|
||||||
public enum XpShopInputType
|
public enum XpShopInputType
|
||||||
{
|
{
|
||||||
F = 0,
|
Backgrounds = 0,
|
||||||
Frs = 0,
|
B = 0,
|
||||||
Fs = 0,
|
Bg = 0,
|
||||||
Frames = 0,
|
Bgs = 0,
|
||||||
B = 1,
|
Frames = 1,
|
||||||
Bg = 1,
|
F = 1,
|
||||||
Bgs = 1,
|
Fr = 1,
|
||||||
Backgrounds = 1
|
Frs = 1,
|
||||||
|
Fs = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -352,9 +353,12 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
await ReplyErrorLocalizedAsync(strs.xp_shop_disabled);
|
await ReplyErrorLocalizedAsync(strs.xp_shop_disabled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendConfirmAsync(GetText(strs.available_commands), $@"`{prefix}xpshop bgs`
|
await SendConfirmAsync(GetText(strs.available_commands),
|
||||||
`{prefix}xpshop frames`");
|
$@"`{prefix}xpshop bgs`
|
||||||
|
`{prefix}xpshop frames`
|
||||||
|
|
||||||
|
*{GetText(strs.xpshop_website)}*");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -394,13 +398,18 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(item.Name)
|
.WithTitle(item.Name)
|
||||||
.AddField(GetText(strs.price), Gambling.Gambling.N(item.Price, culture), true)
|
.AddField(GetText(strs.price), Gambling.Gambling.N(item.Price, culture), true)
|
||||||
// .AddField(GetText(strs.buy), $"{prefix}xpbuy {key}", true)
|
.WithImageUrl(string.IsNullOrWhiteSpace(item.Preview)
|
||||||
.WithImageUrl(item.Url.ToString());
|
? item.Url
|
||||||
|
: item.Preview);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(item.Desc))
|
if (!string.IsNullOrWhiteSpace(item.Desc))
|
||||||
eb.WithDescription(item.Desc);
|
eb.AddField(GetText(strs.desc), item.Desc);
|
||||||
|
|
||||||
var tier = _service.GetXpShopTierRequirement();
|
if (key == "default")
|
||||||
|
eb.WithDescription(GetText(strs.xpshop_website));
|
||||||
|
|
||||||
|
|
||||||
|
var tier = _service.GetXpShopTierRequirement(type);
|
||||||
if (tier != PatronTier.None)
|
if (tier != PatronTier.None)
|
||||||
{
|
{
|
||||||
eb.WithFooter(GetText(strs.xp_shop_buy_required_tier(tier.ToString())));
|
eb.WithFooter(GetText(strs.xp_shop_buy_required_tier(tier.ToString())));
|
||||||
@@ -423,8 +432,7 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
var button = new ButtonBuilder(ownedItem.IsUsing
|
var button = new ButtonBuilder(ownedItem.IsUsing
|
||||||
? GetText(strs.in_use)
|
? GetText(strs.in_use)
|
||||||
: GetText(strs.use),
|
: GetText(strs.use),
|
||||||
"XP_SHOP_USE",
|
"xpshop:use",
|
||||||
ButtonStyle.Primary,
|
|
||||||
emote: Emoji.Parse("👐"),
|
emote: Emoji.Parse("👐"),
|
||||||
isDisabled: ownedItem.IsUsing);
|
isDisabled: ownedItem.IsUsing);
|
||||||
|
|
||||||
@@ -438,8 +446,7 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var button = new ButtonBuilder(GetText(strs.buy),
|
var button = new ButtonBuilder(GetText(strs.buy),
|
||||||
"XP_SHOP_BUY",
|
"xpshop:buy",
|
||||||
ButtonStyle.Primary,
|
|
||||||
emote: Emoji.Parse("💰"));
|
emote: Emoji.Parse("💰"));
|
||||||
|
|
||||||
var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
|
var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
|
||||||
@@ -454,6 +461,53 @@ public partial class Xp : NadekoModule<XpService>
|
|||||||
1,
|
1,
|
||||||
addPaginatedFooter: false);
|
addPaginatedFooter: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task XpShopBuy(XpShopInputType type, string key)
|
||||||
|
{
|
||||||
|
var result = await _service.BuyShopItemAsync(ctx.User.Id, (XpShopItemType)type, key);
|
||||||
|
|
||||||
|
NadekoInteraction GetUseInteraction()
|
||||||
|
{
|
||||||
|
return _inter.Create(ctx.User.Id,
|
||||||
|
new SimpleInteraction<object>(
|
||||||
|
new ButtonBuilder(label: "Use", customId: "xpshop:use_item", emote: Emoji.Parse("👐")),
|
||||||
|
async (smc, _) => await XpShopUse(type, key)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != BuyResult.Success)
|
||||||
|
{
|
||||||
|
var _ = result switch
|
||||||
|
{
|
||||||
|
BuyResult.XpShopDisabled => await ReplyErrorLocalizedAsync(strs.xp_shop_disabled),
|
||||||
|
BuyResult.InsufficientFunds => await ReplyErrorLocalizedAsync(strs.not_enough(_gss.Data.Currency.Sign)),
|
||||||
|
BuyResult.AlreadyOwned => await ReplyErrorLocalizedAsync(strs.xpshop_already_owned, GetUseInteraction()),
|
||||||
|
BuyResult.UnknownItem => await ReplyErrorLocalizedAsync(strs.xpshop_item_not_found),
|
||||||
|
BuyResult.InsufficientPatronTier => await ReplyErrorLocalizedAsync(strs.patron_insuff_tier),
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.xpshop_buy_success(type.ToString().ToLowerInvariant(),
|
||||||
|
key.ToLowerInvariant()),
|
||||||
|
GetUseInteraction());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task XpShopUse(XpShopInputType type, string key)
|
||||||
|
{
|
||||||
|
var result = await _service.UseShopItemAsync(ctx.User.Id, (XpShopItemType)type, key);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.xp_shop_item_cant_use);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task OnShopUse(SocketMessageComponent smc, (string? key, XpShopItemType type)? maybeState)
|
private async Task OnShopUse(SocketMessageComponent smc, (string? key, XpShopItemType type)? maybeState)
|
||||||
{
|
{
|
||||||
|
@@ -10,7 +10,7 @@ namespace NadekoBot.Modules.Xp;
|
|||||||
public sealed partial class XpConfig : ICloneable<XpConfig>
|
public sealed partial class XpConfig : ICloneable<XpConfig>
|
||||||
{
|
{
|
||||||
[Comment(@"DO NOT CHANGE")]
|
[Comment(@"DO NOT CHANGE")]
|
||||||
public int Version { get; set; } = 3;
|
public int Version { get; set; } = 5;
|
||||||
|
|
||||||
[Comment(@"How much XP will the users receive per message")]
|
[Comment(@"How much XP will the users receive per message")]
|
||||||
public int XpPerMessage { get; set; } = 3;
|
public int XpPerMessage { get; set; } = 3;
|
||||||
@@ -26,6 +26,9 @@ public sealed partial class XpConfig : ICloneable<XpConfig>
|
|||||||
|
|
||||||
[Comment(@"The maximum amount of minutes the bot will keep track of a user in a voice channel")]
|
[Comment(@"The maximum amount of minutes the bot will keep track of a user in a voice channel")]
|
||||||
public int VoiceMaxMinutes { get; set; } = 720;
|
public int VoiceMaxMinutes { get; set; } = 720;
|
||||||
|
|
||||||
|
[Comment(@"The amount of currency users will receive for each point of global xp that they earn")]
|
||||||
|
public float CurrencyPerXp { get; set; } = 0;
|
||||||
|
|
||||||
[Comment(@"Xp Shop config")]
|
[Comment(@"Xp Shop config")]
|
||||||
public ShopConfig Shop { get; set; } = new();
|
public ShopConfig Shop { get; set; } = new();
|
||||||
@@ -37,9 +40,13 @@ True -> Users can access the xp shop using .xpshop command
|
|||||||
False -> Users can't access the xp shop")]
|
False -> Users can't access the xp shop")]
|
||||||
public bool IsEnabled { get; set; } = false;
|
public bool IsEnabled { get; set; } = false;
|
||||||
|
|
||||||
[Comment(@"Which patron tier do users need in order to use the .xpshop command
|
[Comment(@"Which patron tier do users need in order to use the .xpshop bgs command
|
||||||
Leave at 'None' if patron system is disabled or you don't want any restrictions")]
|
Leave at 'None' if patron system is disabled or you don't want any restrictions")]
|
||||||
public PatronTier TierRequirement { get; set; } = PatronTier.None;
|
public PatronTier BgsTierRequirement { get; set; } = PatronTier.None;
|
||||||
|
|
||||||
|
[Comment(@"Which patron tier do users need in order to use the .xpshop frames command
|
||||||
|
Leave at 'None' if patron system is disabled or you don't want any restrictions")]
|
||||||
|
public PatronTier FramesTierRequirement { get; set; } = PatronTier.None;
|
||||||
|
|
||||||
[Comment(@"Frames available for sale. Keys are unique IDs.
|
[Comment(@"Frames available for sale. Keys are unique IDs.
|
||||||
Do not change keys as they are not publicly visible. Only change properties (name, price, id)
|
Do not change keys as they are not publicly visible. Only change properties (name, price, id)
|
||||||
@@ -71,6 +78,9 @@ To remove an item from the shop, but keep previous purchases, set the price to -
|
|||||||
[Comment(@"Direct url to the .png image which will be applied to the user's XP card")]
|
[Comment(@"Direct url to the .png image which will be applied to the user's XP card")]
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
[Comment(@"Optional preview url which will show instead of the real URL in the shop ")]
|
||||||
|
public string Preview { get; set; }
|
||||||
|
|
||||||
[Comment(@"Optional description of the item")]
|
[Comment(@"Optional description of the item")]
|
||||||
public string Desc { get; set; }
|
public string Desc { get; set; }
|
||||||
}
|
}
|
||||||
|
@@ -52,11 +52,11 @@ public sealed class XpConfigService : ConfigServiceBase<XpConfig>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.Version < 3)
|
if (data.Version < 6)
|
||||||
{
|
{
|
||||||
ModifyConfig(c =>
|
ModifyConfig(c =>
|
||||||
{
|
{
|
||||||
c.Version = 3;
|
c.Version = 6;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -176,6 +176,16 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
var gxps = new List<UserXpStats>(globalToAdd.Count);
|
var gxps = new List<UserXpStats>(globalToAdd.Count);
|
||||||
await using (var ctx = _db.GetDbContext())
|
await using (var ctx = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
|
var conf = _xpConfig.Data;
|
||||||
|
if (conf.CurrencyPerXp > 0)
|
||||||
|
{
|
||||||
|
foreach (var user in globalToAdd)
|
||||||
|
{
|
||||||
|
var amount = user.Value.XpAmount * conf.CurrencyPerXp;
|
||||||
|
await _cs.AddAsync(user.Key, (long)(amount), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update global user xp in batches
|
// update global user xp in batches
|
||||||
// group by xp amount and update the same amounts at the same time
|
// group by xp amount and update the same amounts at the same time
|
||||||
foreach (var group in globalToAdd.GroupBy(x => x.Value.XpAmount, x => x.Key))
|
foreach (var group in globalToAdd.GroupBy(x => x.Value.XpAmount, x => x.Key))
|
||||||
@@ -188,9 +198,15 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
},
|
},
|
||||||
(_, n) => n);
|
(_, n) => n);
|
||||||
|
|
||||||
|
await ctx.Clubs
|
||||||
|
.Where(x => x.Members.Any(m => group.Contains(m.UserId)))
|
||||||
|
.UpdateAsync(old => new()
|
||||||
|
{
|
||||||
|
Xp = old.Xp + (group.Key * old.Members.Count(m => group.Contains(m.UserId)))
|
||||||
|
});
|
||||||
|
|
||||||
dus.AddRange(items);
|
dus.AddRange(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update guild user xp in batches
|
// update guild user xp in batches
|
||||||
foreach (var (guildId, toAdd) in guildToAdd)
|
foreach (var (guildId, toAdd) in guildToAdd)
|
||||||
{
|
{
|
||||||
@@ -205,8 +221,43 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
Xp = old.Xp + group.Key
|
Xp = old.Xp + group.Key
|
||||||
},
|
},
|
||||||
(_, n) => n);
|
(_, n) => n);
|
||||||
|
|
||||||
gxps.AddRange(items);
|
gxps.AddRange(items);
|
||||||
|
|
||||||
|
var missingUserIds = group.Where(userId => !items.Any(x => x.UserId == userId)).ToArray();
|
||||||
|
foreach (var userId in missingUserIds)
|
||||||
|
{
|
||||||
|
await ctx
|
||||||
|
.UserXpStats
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.InsertOrUpdateAsync(() => new UserXpStats()
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
GuildId = guildId,
|
||||||
|
Xp = group.Key,
|
||||||
|
DateAdded = DateTime.UtcNow,
|
||||||
|
AwardedXp = 0,
|
||||||
|
NotifyOnLevelUp = XpNotificationLocation.None
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
UserId = userId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingUserIds.Length > 0)
|
||||||
|
{
|
||||||
|
var missingItems = await ctx.UserXpStats
|
||||||
|
.ToLinqToDBTable()
|
||||||
|
.Where(x => missingUserIds.Contains(x.UserId))
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
|
|
||||||
|
gxps.AddRange(missingItems);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,8 +286,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
if (guildToAdd.TryGetValue(du.GuildId, out var users)
|
if (guildToAdd.TryGetValue(du.GuildId, out var users)
|
||||||
&& users.TryGetValue(du.UserId, out var xpGainData))
|
&& users.TryGetValue(du.UserId, out var xpGainData))
|
||||||
{
|
{
|
||||||
var oldLevel = new LevelStats(du.Xp - xpGainData.XpAmount);
|
var oldLevel = new LevelStats(du.Xp - xpGainData.XpAmount + du.AwardedXp);
|
||||||
var newLevel = new LevelStats(du.Xp);
|
var newLevel = new LevelStats(du.Xp + du.AwardedXp);
|
||||||
|
|
||||||
if (oldLevel.Level < newLevel.Level)
|
if (oldLevel.Level < newLevel.Level)
|
||||||
{
|
{
|
||||||
@@ -593,12 +644,12 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
{
|
{
|
||||||
if (ShouldTrackVoiceChannel(channel))
|
if (ShouldTrackVoiceChannel(channel))
|
||||||
{
|
{
|
||||||
foreach (var user in channel.Users)
|
foreach (var user in channel.ConnectedUsers)
|
||||||
await ScanUserForVoiceXp(user, channel);
|
await ScanUserForVoiceXp(user, channel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var user in channel.Users)
|
foreach (var user in channel.ConnectedUsers)
|
||||||
await UserLeftVoiceChannel(user, channel);
|
await UserLeftVoiceChannel(user, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,13 +668,13 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldTrackVoiceChannel(SocketVoiceChannel channel)
|
private bool ShouldTrackVoiceChannel(SocketVoiceChannel channel)
|
||||||
=> channel.Users.Where(UserParticipatingInVoiceChannel).Take(2).Count() >= 2;
|
=> channel.ConnectedUsers.Where(UserParticipatingInVoiceChannel).Take(2).Count() >= 2;
|
||||||
|
|
||||||
private bool UserParticipatingInVoiceChannel(SocketGuildUser user)
|
private bool UserParticipatingInVoiceChannel(SocketGuildUser user)
|
||||||
=> !user.IsDeafened && !user.IsMuted && !user.IsSelfDeafened && !user.IsSelfMuted;
|
=> !user.IsDeafened && !user.IsMuted && !user.IsSelfDeafened && !user.IsSelfMuted;
|
||||||
|
|
||||||
private TypedKey<long> GetVoiceXpKey(ulong userId)
|
private TypedKey<long> GetVoiceXpKey(ulong userId)
|
||||||
=> new($"xp:vc_join:{userId}");
|
=> new($"xp:{_client.CurrentUser.Id}:vc_join:{userId}");
|
||||||
|
|
||||||
private async Task UserJoinedVoiceChannel(SocketGuildUser user)
|
private async Task UserJoinedVoiceChannel(SocketGuildUser user)
|
||||||
{
|
{
|
||||||
@@ -634,6 +685,18 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
TimeSpan.FromMinutes(_xpConfig.Data.VoiceMaxMinutes),
|
TimeSpan.FromMinutes(_xpConfig.Data.VoiceMaxMinutes),
|
||||||
overwrite: false);
|
overwrite: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private void UserJoinedVoiceChannel(SocketGuildUser user)
|
||||||
|
// {
|
||||||
|
// var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
|
||||||
|
// var value = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
//
|
||||||
|
// _cache.Redis.GetDatabase()
|
||||||
|
// .StringSet(key,
|
||||||
|
// value,
|
||||||
|
// TimeSpan.FromMinutes(_xpConfig.Data.VoiceMaxMinutes),
|
||||||
|
// when: When.NotExists);
|
||||||
|
// }
|
||||||
|
|
||||||
private async Task UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
private async Task UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
||||||
{
|
{
|
||||||
@@ -654,6 +717,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
|
|
||||||
if (actualXp > 0)
|
if (actualXp > 0)
|
||||||
{
|
{
|
||||||
|
Log.Information("Adding {Amount} voice xp to {User}", actualXp, user.ToString());
|
||||||
await _xpGainQueue.Writer.WriteAsync(new()
|
await _xpGainQueue.Writer.WriteAsync(new()
|
||||||
{
|
{
|
||||||
Guild = channel.Guild,
|
Guild = channel.Guild,
|
||||||
@@ -663,6 +727,38 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* private void UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
||||||
|
{
|
||||||
|
var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
|
||||||
|
var value = _cache.Redis.GetDatabase().StringGet(key);
|
||||||
|
_cache.Redis.GetDatabase().KeyDelete(key);
|
||||||
|
|
||||||
|
// Allow for if this function gets called multiple times when a user leaves a channel.
|
||||||
|
if (value.IsNull)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!value.TryParse(out long startUnixTime))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dateStart = DateTimeOffset.FromUnixTimeSeconds(startUnixTime);
|
||||||
|
var dateEnd = DateTimeOffset.UtcNow;
|
||||||
|
var minutes = (dateEnd - dateStart).TotalMinutes;
|
||||||
|
var xp = _xpConfig.Data.VoiceXpPerMinute * minutes;
|
||||||
|
var actualXp = (int)Math.Floor(xp);
|
||||||
|
|
||||||
|
if (actualXp > 0)
|
||||||
|
{
|
||||||
|
_addMessageXp.Enqueue(new()
|
||||||
|
{
|
||||||
|
Guild = channel.Guild,
|
||||||
|
User = user,
|
||||||
|
XpAmount = actualXp
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
private bool ShouldTrackXp(SocketGuildUser user, ulong channelId)
|
private bool ShouldTrackXp(SocketGuildUser user, ulong channelId)
|
||||||
{
|
{
|
||||||
@@ -767,8 +863,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
return Enumerable.Empty<ulong>();
|
return Enumerable.Empty<ulong>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TypedKey<bool> GetUserRewKey(ulong userId)
|
private TypedKey<bool> GetUserRewKey(ulong userId)
|
||||||
=> new($"xp:user_gain:{userId}");
|
=> new($"xp:{_client.CurrentUser.Id}:user_gain:{userId}");
|
||||||
|
|
||||||
private async Task<bool> SetUserRewardedAsync(ulong userId)
|
private async Task<bool> SetUserRewardedAsync(ulong userId)
|
||||||
=> await _c.AddAsync(GetUserRewKey(userId),
|
=> await _c.AddAsync(GetUserRewKey(userId),
|
||||||
@@ -887,6 +983,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
throw new ArgumentNullException(nameof(bgBytes));
|
throw new ArgumentNullException(nameof(bgBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outlinePen = new Pen(Color.Black, 1f);
|
||||||
|
|
||||||
using var img = Image.Load<Rgba32>(bgBytes, out var imageFormat);
|
using var img = Image.Load<Rgba32>(bgBytes, out var imageFormat);
|
||||||
if (template.User.Name.Show)
|
if (template.User.Name.Show)
|
||||||
{
|
{
|
||||||
@@ -909,7 +1007,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
Origin = new(template.User.Name.Pos.X, template.User.Name.Pos.Y + 8)
|
Origin = new(template.User.Name.Pos.X, template.User.Name.Pos.Y + 8)
|
||||||
},
|
},
|
||||||
"@" + username,
|
"@" + username,
|
||||||
template.User.Name.Color);
|
Brushes.Solid(template.User.Name.Color),
|
||||||
|
outlinePen);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,7 +1028,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
Origin = new(template.Club.Name.Pos.X + 50, template.Club.Name.Pos.Y - 8)
|
Origin = new(template.Club.Name.Pos.X + 50, template.Club.Name.Pos.Y - 8)
|
||||||
},
|
},
|
||||||
clubName,
|
clubName,
|
||||||
template.Club.Name.Color));
|
Brushes.Solid(template.Club.Name.Color),
|
||||||
|
outlinePen));
|
||||||
}
|
}
|
||||||
|
|
||||||
Font GetTruncatedFont(
|
Font GetTruncatedFont(
|
||||||
@@ -987,7 +1087,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var pen = new Pen(Color.Black, 1.25f);
|
|
||||||
|
|
||||||
var global = stats.Global;
|
var global = stats.Global;
|
||||||
var guild = stats.Guild;
|
var guild = stats.Guild;
|
||||||
@@ -1012,7 +1111,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
},
|
},
|
||||||
$"{global.LevelXp}/{global.RequiredXp}",
|
$"{global.LevelXp}/{global.RequiredXp}",
|
||||||
Brushes.Solid(template.User.Xp.Global.Color),
|
Brushes.Solid(template.User.Xp.Global.Color),
|
||||||
pen));
|
outlinePen));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.User.Xp.Guild.Show)
|
if (template.User.Xp.Guild.Show)
|
||||||
@@ -1026,7 +1125,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
},
|
},
|
||||||
$"{guild.LevelXp}/{guild.RequiredXp}",
|
$"{guild.LevelXp}/{guild.RequiredXp}",
|
||||||
Brushes.Solid(template.User.Xp.Guild.Color),
|
Brushes.Solid(template.User.Xp.Guild.Color),
|
||||||
pen));
|
outlinePen));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats.FullGuildStats.AwardedXp != 0 && template.User.Xp.Awarded.Show)
|
if (stats.FullGuildStats.AwardedXp != 0 && template.User.Xp.Awarded.Show)
|
||||||
@@ -1038,10 +1137,11 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
img.Mutate(x => x.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})",
|
img.Mutate(x => x.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})",
|
||||||
_fonts.NotoSans.CreateFont(template.User.Xp.Awarded.FontSize, FontStyle.Bold),
|
_fonts.NotoSans.CreateFont(template.User.Xp.Awarded.FontSize, FontStyle.Bold),
|
||||||
Brushes.Solid(template.User.Xp.Awarded.Color),
|
Brushes.Solid(template.User.Xp.Awarded.Color),
|
||||||
pen,
|
outlinePen,
|
||||||
new(awX, awY)));
|
new(awX, awY)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rankPen = new Pen(Color.White, 1);
|
||||||
//ranking
|
//ranking
|
||||||
if (template.User.GlobalRank.Show)
|
if (template.User.GlobalRank.Show)
|
||||||
{
|
{
|
||||||
@@ -1054,10 +1154,15 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
globalRankStr,
|
globalRankStr,
|
||||||
68);
|
68);
|
||||||
|
|
||||||
img.Mutate(x => x.DrawText(globalRankStr,
|
img.Mutate(x => x.DrawText(
|
||||||
globalRankFont,
|
new TextOptions(globalRankFont)
|
||||||
template.User.GlobalRank.Color,
|
{
|
||||||
new(template.User.GlobalRank.Pos.X, template.User.GlobalRank.Pos.Y)));
|
Origin = new(template.User.GlobalRank.Pos.X, template.User.GlobalRank.Pos.Y)
|
||||||
|
},
|
||||||
|
globalRankStr,
|
||||||
|
Brushes.Solid(template.User.GlobalRank.Color),
|
||||||
|
rankPen
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.User.GuildRank.Show)
|
if (template.User.GuildRank.Show)
|
||||||
@@ -1071,10 +1176,15 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
guildRankStr,
|
guildRankStr,
|
||||||
43);
|
43);
|
||||||
|
|
||||||
img.Mutate(x => x.DrawText(guildRankStr,
|
img.Mutate(x => x.DrawText(
|
||||||
guildRankFont,
|
new TextOptions(guildRankFont)
|
||||||
template.User.GuildRank.Color,
|
{
|
||||||
new(template.User.GuildRank.Pos.X, template.User.GuildRank.Pos.Y)));
|
Origin = new(template.User.GuildRank.Pos.X, template.User.GuildRank.Pos.Y)
|
||||||
|
},
|
||||||
|
guildRankStr,
|
||||||
|
Brushes.Solid(template.User.GuildRank.Color),
|
||||||
|
rankPen
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
//avatar
|
//avatar
|
||||||
@@ -1335,13 +1445,17 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
var conf = _xpConfig.Data;
|
var conf = _xpConfig.Data;
|
||||||
|
|
||||||
if (!conf.Shop.IsEnabled)
|
if (!conf.Shop.IsEnabled)
|
||||||
return BuyResult.UnknownItem;
|
return BuyResult.XpShopDisabled;
|
||||||
|
|
||||||
if (conf.Shop.TierRequirement != PatronTier.None)
|
var req = type == XpShopItemType.Background
|
||||||
|
? conf.Shop.BgsTierRequirement
|
||||||
|
: conf.Shop.FramesTierRequirement;
|
||||||
|
|
||||||
|
if (req != PatronTier.None && !_creds.IsOwner(userId))
|
||||||
{
|
{
|
||||||
var patron = await _ps.GetPatronAsync(userId);
|
var patron = await _ps.GetPatronAsync(userId);
|
||||||
|
|
||||||
if ((int)patron.Tier < (int)conf.Shop.TierRequirement)
|
if ((int)patron.Tier < (int)req)
|
||||||
return BuyResult.InsufficientPatronTier;
|
return BuyResult.InsufficientPatronTier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1468,8 +1582,12 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PatronTier GetXpShopTierRequirement()
|
public PatronTier GetXpShopTierRequirement(Xp.XpShopInputType type)
|
||||||
=> _xpConfig.Data.Shop.TierRequirement;
|
=> type switch
|
||||||
|
{
|
||||||
|
Xp.XpShopInputType.F => _xpConfig.Data.Shop.FramesTierRequirement,
|
||||||
|
_ => _xpConfig.Data.Shop.BgsTierRequirement,
|
||||||
|
};
|
||||||
|
|
||||||
public bool IsShopEnabled()
|
public bool IsShopEnabled()
|
||||||
=> _xpConfig.Data.Shop.IsEnabled;
|
=> _xpConfig.Data.Shop.IsEnabled;
|
||||||
@@ -1478,6 +1596,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
public enum BuyResult
|
public enum BuyResult
|
||||||
{
|
{
|
||||||
Success,
|
Success,
|
||||||
|
XpShopDisabled,
|
||||||
AlreadyOwned,
|
AlreadyOwned,
|
||||||
InsufficientFunds,
|
InsufficientFunds,
|
||||||
UnknownItem,
|
UnknownItem,
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.4" />
|
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.4" />
|
||||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageReference Include="CsvHelper" Version="28.0.1" />
|
<PackageReference Include="CsvHelper" Version="28.0.1" />
|
||||||
<PackageReference Include="Discord.Net" Version="3.103.0" />
|
<PackageReference Include="Discord.Net" Version="3.104.0" />
|
||||||
<PackageReference Include="CoreCLR-NCalc" Version="2.2.110" />
|
<PackageReference Include="CoreCLR-NCalc" Version="2.2.110" />
|
||||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.57.0.2749" />
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.57.0.2749" />
|
||||||
|
@@ -25,6 +25,6 @@ if (args.Length > 0 && args[0] != "run")
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogSetup.SetupLogger(shardId);
|
LogSetup.SetupLogger(shardId);
|
||||||
Log.Information("Pid: {ProcessId}", pid);
|
Log.Information("Pid: {ProcessId}", pid);
|
||||||
|
|
||||||
await new Bot(shardId, totalShards).RunAndBlockAsync();
|
await new Bot(shardId, totalShards, Environment.GetEnvironmentVariable("NadekoBot__creds")).RunAndBlockAsync();
|
@@ -279,6 +279,7 @@ public class CommandHandler : INService, IReadyExecutor
|
|||||||
// if it errored
|
// if it errored
|
||||||
if (error is not null)
|
if (error is not null)
|
||||||
{
|
{
|
||||||
|
error = HumanizeError(error);
|
||||||
LogErroredExecution(error, usrMsg, channel as ITextChannel, blockTime, startTime);
|
LogErroredExecution(error, usrMsg, channel as ITextChannel, blockTime, startTime);
|
||||||
|
|
||||||
if (guild is not null)
|
if (guild is not null)
|
||||||
@@ -292,6 +293,15 @@ public class CommandHandler : INService, IReadyExecutor
|
|||||||
await _behaviorHandler.RunOnNoCommandAsync(guild, usrMsg);
|
await _behaviorHandler.RunOnNoCommandAsync(guild, usrMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string HumanizeError(string error)
|
||||||
|
{
|
||||||
|
if (error.Contains("parse int", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| error.Contains("parse float"))
|
||||||
|
return "Invalid number specified. Make sure you're specifying parameters in the correct order.";
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
public Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommandAsync(
|
public Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommandAsync(
|
||||||
CommandContext context,
|
CommandContext context,
|
||||||
string input,
|
string input,
|
||||||
|
@@ -27,7 +27,7 @@ public class DefaultWallet : IWallet
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> Take(long amount, TxData txData)
|
public async Task<bool> Take(long amount, TxData? txData)
|
||||||
{
|
{
|
||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative.");
|
throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative.");
|
||||||
@@ -44,24 +44,27 @@ public class DefaultWallet : IWallet
|
|||||||
|
|
||||||
if (changed == 0)
|
if (changed == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
await ctx
|
if (txData is not null)
|
||||||
.GetTable<CurrencyTransaction>()
|
{
|
||||||
.InsertAsync(() => new()
|
await ctx
|
||||||
{
|
.GetTable<CurrencyTransaction>()
|
||||||
Amount = -amount,
|
.InsertAsync(() => new()
|
||||||
Note = txData.Note,
|
{
|
||||||
UserId = userId,
|
Amount = -amount,
|
||||||
Type = txData.Type,
|
Note = txData.Note,
|
||||||
Extra = txData.Extra,
|
UserId = userId,
|
||||||
OtherId = txData.OtherId,
|
Type = txData.Type,
|
||||||
DateAdded = DateTime.UtcNow
|
Extra = txData.Extra,
|
||||||
});
|
OtherId = txData.OtherId,
|
||||||
|
DateAdded = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Add(long amount, TxData txData)
|
public async Task Add(long amount, TxData? txData)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
||||||
@@ -92,16 +95,19 @@ public class DefaultWallet : IWallet
|
|||||||
await tran.CommitAsync();
|
await tran.CommitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.GetTable<CurrencyTransaction>()
|
if (txData is not null)
|
||||||
.InsertAsync(() => new()
|
{
|
||||||
{
|
await ctx.GetTable<CurrencyTransaction>()
|
||||||
Amount = amount,
|
.InsertAsync(() => new()
|
||||||
UserId = userId,
|
{
|
||||||
Note = txData.Note,
|
Amount = amount,
|
||||||
Type = txData.Type,
|
UserId = userId,
|
||||||
Extra = txData.Extra,
|
Note = txData.Note,
|
||||||
OtherId = txData.OtherId,
|
Type = txData.Type,
|
||||||
DateAdded = DateTime.UtcNow
|
Extra = txData.Extra,
|
||||||
});
|
OtherId = txData.OtherId,
|
||||||
|
DateAdded = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,31 +5,35 @@ public interface IWallet
|
|||||||
public ulong UserId { get; }
|
public ulong UserId { get; }
|
||||||
|
|
||||||
public Task<long> GetBalance();
|
public Task<long> GetBalance();
|
||||||
public Task<bool> Take(long amount, TxData txData);
|
public Task<bool> Take(long amount, TxData? txData);
|
||||||
public Task Add(long amount, TxData txData);
|
public Task Add(long amount, TxData? txData);
|
||||||
|
|
||||||
public async Task<bool> Transfer(
|
public async Task<bool> Transfer(
|
||||||
long amount,
|
long amount,
|
||||||
IWallet to,
|
IWallet to,
|
||||||
TxData txData)
|
TxData? txData)
|
||||||
{
|
{
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
|
||||||
|
|
||||||
var succ = await Take(amount,
|
if (txData is not null)
|
||||||
txData with
|
txData = txData with
|
||||||
{
|
{
|
||||||
OtherId = to.UserId
|
OtherId = to.UserId
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var succ = await Take(amount, txData);
|
||||||
|
|
||||||
if (!succ)
|
if (!succ)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
await to.Add(amount,
|
if (txData is not null)
|
||||||
txData with
|
txData = txData with
|
||||||
{
|
{
|
||||||
OtherId = UserId
|
OtherId = UserId
|
||||||
});
|
};
|
||||||
|
|
||||||
|
await to.Add(amount, txData);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -67,6 +67,9 @@ public sealed class DiscordEmbedBuilderWrapper : IEmbedBuilder
|
|||||||
_ => throw new ArgumentOutOfRangeException(nameof(color), "Unsupported EmbedColor type")
|
_ => throw new ArgumentOutOfRangeException(nameof(color), "Unsupported EmbedColor type")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public IEmbedBuilder WithDiscordColor(Color color)
|
||||||
|
=> Wrap(embed.WithColor(color));
|
||||||
|
|
||||||
public Embed Build()
|
public Embed Build()
|
||||||
=> embed.Build();
|
=> embed.Build();
|
||||||
|
|
||||||
|
@@ -18,11 +18,9 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
|||||||
private const string CREDS_FILE_NAME = "creds.yml";
|
private const string CREDS_FILE_NAME = "creds.yml";
|
||||||
private const string CREDS_EXAMPLE_FILE_NAME = "creds_example.yml";
|
private const string CREDS_EXAMPLE_FILE_NAME = "creds_example.yml";
|
||||||
|
|
||||||
private string CredsPath
|
private string CredsPath { get; }
|
||||||
=> Path.Combine(Directory.GetCurrentDirectory(), CREDS_FILE_NAME);
|
|
||||||
|
|
||||||
private string CredsExamplePath
|
private string CredsExamplePath { get; }
|
||||||
=> Path.Combine(Directory.GetCurrentDirectory(), CREDS_EXAMPLE_FILE_NAME);
|
|
||||||
|
|
||||||
private readonly int? _totalShards;
|
private readonly int? _totalShards;
|
||||||
|
|
||||||
@@ -34,9 +32,21 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
|||||||
private readonly object _reloadLock = new();
|
private readonly object _reloadLock = new();
|
||||||
private readonly IDisposable _changeToken;
|
private readonly IDisposable _changeToken;
|
||||||
|
|
||||||
public BotCredsProvider(int? totalShards = null)
|
public BotCredsProvider(int? totalShards = null, string credPath = null)
|
||||||
{
|
{
|
||||||
_totalShards = totalShards;
|
_totalShards = totalShards;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(credPath))
|
||||||
|
{
|
||||||
|
CredsPath = credPath;
|
||||||
|
CredsExamplePath = Path.Combine(Path.GetDirectoryName(credPath), CREDS_EXAMPLE_FILE_NAME);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CredsPath = Path.Combine(Directory.GetCurrentDirectory(), CREDS_FILE_NAME);
|
||||||
|
CredsExamplePath = Path.Combine(Directory.GetCurrentDirectory(), CREDS_EXAMPLE_FILE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!File.Exists(CredsExamplePath))
|
if (!File.Exists(CredsExamplePath))
|
||||||
@@ -59,8 +69,8 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
|||||||
|
|
||||||
_config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true)
|
_config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true)
|
||||||
.AddEnvironmentVariables("NadekoBot_")
|
.AddEnvironmentVariables("NadekoBot_")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_changeToken = ChangeToken.OnChange(() => _config.GetReloadToken(), Reload);
|
_changeToken = ChangeToken.OnChange(() => _config.GetReloadToken(), Reload);
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
@@ -121,14 +131,14 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
|||||||
|
|
||||||
ymlData = Yaml.Serializer.Serialize(creds);
|
ymlData = Yaml.Serializer.Serialize(creds);
|
||||||
File.WriteAllText(CREDS_FILE_NAME, ymlData);
|
File.WriteAllText(CREDS_FILE_NAME, ymlData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string OldCredsJsonPath
|
private string OldCredsJsonPath
|
||||||
=> Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
|
=> Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
|
||||||
|
|
||||||
private string OldCredsJsonBackupPath
|
private string OldCredsJsonBackupPath
|
||||||
=> Path.Combine(Directory.GetCurrentDirectory(), "credentials.json.bak");
|
=> Path.Combine(Directory.GetCurrentDirectory(), "credentials.json.bak");
|
||||||
|
|
||||||
private void MigrateCredentials()
|
private void MigrateCredentials()
|
||||||
{
|
{
|
||||||
if (File.Exists(OldCredsJsonPath))
|
if (File.Exists(OldCredsJsonPath))
|
||||||
@@ -167,8 +177,8 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
|||||||
|
|
||||||
Log.Warning(
|
Log.Warning(
|
||||||
"Data from credentials.json has been moved to creds.yml\nPlease inspect your creds.yml for correctness");
|
"Data from credentials.json has been moved to creds.yml\nPlease inspect your creds.yml for correctness");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(CREDS_FILE_NAME))
|
if (File.Exists(CREDS_FILE_NAME))
|
||||||
{
|
{
|
||||||
var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME));
|
var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME));
|
||||||
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
|
|||||||
|
|
||||||
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
||||||
{
|
{
|
||||||
public const string BOT_VERSION = "4.3.0";
|
public const string BOT_VERSION = "4.3.6";
|
||||||
|
|
||||||
public string Author
|
public string Author
|
||||||
=> "Kwoth#2452";
|
=> "Kwoth#2452";
|
||||||
@@ -139,7 +139,7 @@ public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
|||||||
textChannels = guilds.Sum(static g => g.Channels.Count(static cx => cx is ITextChannel));
|
textChannels = guilds.Sum(static g => g.Channels.Count(static cx => cx is ITextChannel));
|
||||||
voiceChannels = guilds.Sum(static g => g.Channels.Count(static cx => cx is IVoiceChannel));
|
voiceChannels = guilds.Sum(static g => g.Channels.Count(static cx => cx is IVoiceChannel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
InitializeChannelCount();
|
InitializeChannelCount();
|
||||||
|
@@ -33,7 +33,7 @@ public static class MessageChannelExtensions
|
|||||||
public static async Task<IUserMessage> SendAsync(
|
public static async Task<IUserMessage> SendAsync(
|
||||||
this IMessageChannel channel,
|
this IMessageChannel channel,
|
||||||
string? plainText,
|
string? plainText,
|
||||||
NadekoButtonInteraction? inter,
|
NadekoInteraction? inter,
|
||||||
Embed? embed = null,
|
Embed? embed = null,
|
||||||
IReadOnlyCollection<Embed>? embeds = null,
|
IReadOnlyCollection<Embed>? embeds = null,
|
||||||
bool sanitizeAll = false)
|
bool sanitizeAll = false)
|
||||||
@@ -72,7 +72,7 @@ public static class MessageChannelExtensions
|
|||||||
IEmbedBuilder? embed,
|
IEmbedBuilder? embed,
|
||||||
string plainText = "",
|
string plainText = "",
|
||||||
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
||||||
NadekoButtonInteraction? inter = null)
|
NadekoInteraction? inter = null)
|
||||||
=> ch.SendAsync(plainText,
|
=> ch.SendAsync(plainText,
|
||||||
inter,
|
inter,
|
||||||
embed: embed?.Build(),
|
embed: embed?.Build(),
|
||||||
@@ -83,7 +83,7 @@ public static class MessageChannelExtensions
|
|||||||
IEmbedBuilderService eb,
|
IEmbedBuilderService eb,
|
||||||
string text,
|
string text,
|
||||||
MessageType type,
|
MessageType type,
|
||||||
NadekoButtonInteraction? inter = null)
|
NadekoInteraction? inter = null)
|
||||||
{
|
{
|
||||||
var builder = eb.Create().WithDescription(text);
|
var builder = eb.Create().WithDescription(text);
|
||||||
|
|
||||||
|
@@ -56,7 +56,7 @@ public static class SocketMessageComponentExtensions
|
|||||||
IEmbedBuilder? embed,
|
IEmbedBuilder? embed,
|
||||||
string plainText = "",
|
string plainText = "",
|
||||||
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
||||||
NadekoButtonInteraction? inter = null,
|
NadekoInteraction? inter = null,
|
||||||
bool ephemeral = false)
|
bool ephemeral = false)
|
||||||
=> smc.RespondAsync(plainText,
|
=> smc.RespondAsync(plainText,
|
||||||
embed: embed?.Build(),
|
embed: embed?.Build(),
|
||||||
@@ -69,7 +69,7 @@ public static class SocketMessageComponentExtensions
|
|||||||
string text,
|
string text,
|
||||||
MessageType type,
|
MessageType type,
|
||||||
bool ephemeral = false,
|
bool ephemeral = false,
|
||||||
NadekoButtonInteraction? inter = null)
|
NadekoInteraction? inter = null)
|
||||||
{
|
{
|
||||||
var builder = eb.Create().WithDescription(text);
|
var builder = eb.Create().WithDescription(text);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user