Compare commits

..

96 Commits

Author SHA1 Message Date
Kwoth
93df4f3bf3 Upped version to 4.3.15, Updated CHANGELOG.md 2023-05-21 00:37:29 +02:00
Kwoth
073b832065 Fixed .rps 'amount' field, closes #415 2023-05-17 17:39:25 +02:00
Kwoth
a01e580e03 Merge branch 'hokutochen-v4-patch-69027' into 'v4'
%img:stuff% Deprecated

See merge request Kwoth/nadekobot!295
2023-05-07 16:14:48 +00:00
Hokuto Chen
6124e2fab5 %img:stuff% Deprecated 2023-05-07 16:14:48 +00:00
Kwoth
4dd31d6a0b Fixed .showembed, closes #410 2023-05-03 02:23:10 +02:00
Kwoth
e8706d4006 Fixed -w 0 for .trivia which allows for infinite games, closes #413 2023-05-02 09:36:27 +02:00
Kwoth
140cc43c98 Merge branch 'hokutochen-v4-patch-43149' into 'v4'
updated updater link

See merge request Kwoth/nadekobot!292
2023-04-06 00:40:30 +00:00
Hokuto Chen
26b7149435 updated updater link 2023-04-06 00:40:29 +00:00
Kwoth
b354ee7269 Merge branch 'v4' into 'v4'
fixed bank award giving error message instead of checkmark

See merge request Kwoth/nadekobot!294
2023-04-06 00:40:02 +00:00
Kamal Tufekcic
b829ca0109 fixed bank award giving error message instead of checkmark 2023-04-06 00:40:02 +00:00
Kwoth
37acdb81e8 Updated CHANGELOG.md, upped version to 4.3.14 2023-04-02 20:46:54 +02:00
Kwoth
a9aea65134 Possible fix for voice issues, thanks to anonymous contributor 2023-04-02 20:37:12 +02:00
Kwoth
5c03c5ba16 .banktake had ok/error responses flipped. No functional change, closes #409 2023-03-23 16:14:45 +01:00
Kwoth
42f00c08fa PermRole should deny messages in threads too, closes #407 2023-03-23 16:13:20 +01:00
Kwoth
598d3b8967 Merge branch 'v4' into 'v4'
Replaced CN API

See merge request Kwoth/nadekobot!291
2023-03-12 05:50:35 +00:00
Kamal Tufekcic
8df41c749b Replaced CN API 2023-03-12 05:50:35 +00:00
Kwoth
0eaa8be2d2 Merge branch 'v4' into 'v4'
Updated LMGTFY to be more offensive like before

See merge request Kwoth/nadekobot!289
2023-03-03 14:54:47 +00:00
Kamal Tufekcic
bdbe76f9f8 Update file Searches.cs 2023-03-03 14:21:59 +00:00
Kamal Tufekcic
08b609a4b4 Update file JokeCommands.cs 2023-02-28 19:50:52 +00:00
Snake26183
42d13b32b2 Merge branch 'v4' of https://gitlab.com/Kwoth/nadekobot into v4 2023-02-28 21:43:53 +00:00
Kwoth
096ada367f Cleanup 2023-02-28 14:49:50 +01:00
Kwoth
0fed33ebda Fixed .h <customeraction> not working with non-ascii characters 2023-02-28 13:22:44 +01:00
snake
cb9e918681 Merge branch 'v4' of https://gitlab.com/Kwoth/nadekobot into v4 2023-02-25 23:39:51 +02:00
Kwoth
bbf167df4d .logserver will now enable newer log events as well, closes #403 2023-02-22 14:43:29 +01:00
Kwoth
595a2b401c Merge branch 'v4' into 'v4'
Fixed incorrect `gpt.model` values, added ChatGPT to docs

See merge request Kwoth/nadekobot!287
2023-02-22 13:37:39 +00:00
Kamal Tufekcic
5713e8414e Fixed incorrect gpt.model values, added ChatGPT to docs 2023-02-22 13:37:39 +00:00
Kwoth
a551caf0da Merge branch 'hokutochen-v4-patch-39233' into 'v4'
removed 21.04 and 21.10 (EOL)

See merge request Kwoth/nadekobot!288
2023-02-21 20:42:14 +00:00
Hokuto Chen
b13f05c4c0 removed 21.04 and 21.10 (EOL) 2023-02-21 20:42:14 +00:00
Kwoth
6d6a3a811f Upped version to 4.2.13, updated changelog 2023-02-20 21:57:42 +01:00
Kwoth
fb4aac9f0d Fixed .log userpresence, closes #402 2023-02-20 21:53:25 +01:00
Kwoth
a98981de86 Changing the searches.yml ytprovider setting should now properly affect music queueing provider, but will require bot restart, closes #401 2023-02-20 02:12:00 +01:00
Kamal Tufekcic
9ef3646711 Update file config-guide.md 2023-02-13 12:26:21 +00:00
Kamal Tufekcic
9c174b8b6f Update file config-guide.md 2023-02-13 12:24:08 +00:00
Kamal Tufekcic
657c1e461c Update file config-guide.md 2023-02-13 12:01:33 +00:00
Kamal Tufekcic
f5a4a698bd Update file GamesConfig.cs 2023-02-13 11:52:33 +00:00
Kwoth
8112337aaf Merge branch 'hokutochen-v4-patch-25972' into 'v4'
fixed withURL not working

See merge request Kwoth/nadekobot!286
2023-02-12 10:20:22 +00:00
Hokuto Chen
73f7394e31 fixed withURL not working 2023-02-12 09:52:56 +00:00
Kwoth
987d88287a Revert update to .net 7 by mistake 2023-02-12 04:36:03 +01:00
Kwoth
9dc783b36f Fixed .betstats not working on european locales.
- Upped version to 4.3.12
2023-02-11 23:04:31 +01:00
Kwoth
13741b8317 Use ytdlp instead of ytdl by default, not retroactive, only new users affected 2023-02-11 22:35:07 +01:00
Kwoth
313ca2674e timed ban should now work with userids (users who are not in the server yet), closes #400 2023-02-11 22:32:36 +01:00
Kwoth
98956481e9 Nhentai removed as it's currently unfixable, closes #396 2023-02-11 22:17:39 +01:00
Kwoth
51e887fe04 Merge branch 'v4' of https://gitlab.com/Kwoth/nadekobot into v4 2023-02-10 20:17:20 +01:00
Kwoth
8ceab64b96 Added default value support to medusa system, thanks to kotz 2023-02-10 20:17:13 +01:00
Kwoth
92b8511cf1 Merge branch 'gpt' into 'v4'
Added GPT-3

See merge request Kwoth/nadekobot!284
2023-02-10 13:10:45 +00:00
Alan Beatty
a6a052571e Added GPT-3 as an alternative to cleverbot in .config games and data/games.yml 2023-02-10 13:10:45 +00:00
Kwoth
699a5e0c8c Added bot_owner_only attribute to medusa system. Upped version to 4..3.11 2023-01-21 00:33:07 +01:00
Kwoth
76fedc5ff1 .jr will no longer fail if the user isn't in the database yet, fixes #399 2023-01-20 22:36:57 +01:00
Kwoth
992aa781fa .translangs title is now a response string 2023-01-20 06:51:29 +01:00
Kwoth
e52bf798cf Added full list of supported languages for .trans command, improved .translangs output 2023-01-20 05:28:13 +01:00
Kwoth
710f4f2ed8 Updated .translangs, it looks better now and makes it clear which shortcode is for which language 2023-01-20 04:43:25 +01:00
Kwoth
dcc27a4a99 Fixed trivia --nohint, fixes #398 2023-01-19 06:55:33 +01:00
Kwoth
032b6bfd57 Added Bot propety for easy access to the ISelfUser to all medusa *Context types 2023-01-15 04:15:51 +01:00
Kwoth
737bbb8ec1 Added bot_perm support to medusa system 2023-01-15 04:07:04 +01:00
Kwoth
e71708f5e8 .h command show now properly show both channel and server user permission requirements 2023-01-15 00:50:25 +01:00
Kwoth
9841d72622 allow multiple [user_perm] attributes on snek commands 2023-01-15 00:15:27 +01:00
Kwoth
391a3f0513 Medusae names should now be case sensitive and will be saved in the config with the proper capitalization 2023-01-15 00:01:42 +01:00
Kwoth
aa3409a9cf Added [user_perm()] support to medusa system 2023-01-14 22:43:13 +01:00
Kwoth
9a80383327 Added filter support to medusa system 2023-01-14 00:37:38 +01:00
Kwoth
7e61f7fb46 Improved how stickeradd works. Optional parameters will be a hidden feature as it's hard to explain. Tags are now also optional. 2023-01-13 22:55:09 +01:00
Kwoth
d526a2fac6 Merge branch 'stickeradd' into 'v4'
See merge request Kwoth/nadekobot!276
2023-01-11 23:39:14 +00:00
Kwoth
80efb954f1 Added .stickeradd command
Updated docs for medusa
2023-01-11 23:39:14 +00:00
Kwoth
67c156e40f Merge branch 'hokutochen-v4-patch-11221' into 'v4'
Fix for DMHelpText

See merge request Kwoth/nadekobot!283
2023-01-10 14:39:24 +00:00
Hokuto Chen
26f76ef7b9 Fix for DMHelpText 2023-01-10 14:39:23 +00:00
Kwoth
90cee1bfa9 Merge branch 'hokutochen-v4-patch-97342' into 'v4'
Removed Debian 9 support. Added debian 11 support

See merge request Kwoth/nadekobot!282
2023-01-10 14:38:21 +00:00
Hokuto Chen
26171a0ff7 Removed Debian 9 support. Added debian 11 support 2023-01-10 14:38:21 +00:00
Kwoth
59447d7aa8 .deletecurrency will now also reset banked currency, closes #395 2023-01-09 02:59:41 +01:00
Kwoth
a4053d0666 Removed Id property from patronuser 2023-01-09 02:48:11 +01:00
Kwoth
fcd016aed3 Reverted PK fix for patron as it's causing migration to fail 2023-01-09 02:43:18 +01:00
Kwoth
719f62a0ac Merge branch 'v4' of https://gitlab.com/Kwoth/nadekobot into v4 2023-01-09 02:05:54 +01:00
Kwoth
9b9fa2f357 - Fixed some potential causes for ratelimit due to default message retry settings
- Fixed a patron rewards bug caused by monthly donation checking not accounting for year increase
- Fixed a patron rewards bug for users who connected the same discord account with multiple patreon accounts
2023-01-09 02:05:33 +01:00
Kwoth
823f4731c3 Update responses.uk-UA.json (POEditor.com) 2022-12-29 14:38:57 +00:00
Kwoth
5feff8f4b2 Update responses.es-ES.json (POEditor.com) 2022-12-29 14:38:56 +00:00
Kwoth
95d20609a8 Update responses.ru-RU.json (POEditor.com) 2022-12-29 14:38:55 +00:00
Kwoth
b416b9f963 Update responses.pt-BR.json (POEditor.com) 2022-12-29 14:38:53 +00:00
Kwoth
a7c48b13a0 Update responses.pl-PL.json (POEditor.com) 2022-12-29 14:38:52 +00:00
Kwoth
003b71ba00 Update responses.it-IT.json (POEditor.com) 2022-12-29 14:38:51 +00:00
Kwoth
89593dcc2c Update responses.id-ID.json (POEditor.com) 2022-12-29 14:38:50 +00:00
Kwoth
fa9352d1f8 Update responses.de-DE.json (POEditor.com) 2022-12-29 14:38:49 +00:00
Kwoth
4a2f7ffc76 Update responses.fr-FR.json (POEditor.com) 2022-12-29 14:38:48 +00:00
Kwoth
fb1555c075 Update responses.nl-NL.json (POEditor.com) 2022-12-29 14:38:47 +00:00
Kwoth
7a0b409d88 Update responses.zh-TW.json (POEditor.com) 2022-12-29 14:38:46 +00:00
Kwoth
c869f2e335 More nullref fixes in streamrole, ref #392 2022-12-23 17:08:13 +01:00
Kwoth
01da7e813e Fixed a nullref in streamrole service 2022-12-23 17:04:08 +01:00
Kwoth
76b7e09905 Greet/bye messages will now get disabled if they're set to a deleted/unknown channel 2022-12-22 21:37:01 +01:00
Kwoth
8dca948224 Fixed some build warnings. Fixed TimeOut punishment not allowing duration 2022-12-22 21:18:26 +01:00
Kwoth
ffcbfe6467 Fixed a bug for .quotedeleteauthor causing the executing user to delete own messages 2022-12-22 21:11:21 +01:00
Kwoth
d5c70def93 Merge branch 'hokutochen-v4-patch-86494' into 'v4'
added 22.04 warning

See merge request Kwoth/nadekobot!277
2022-12-09 03:06:32 +00:00
Hokuto Chen
34dccab16b added 22.04 warning 2022-12-08 19:43:33 +00:00
Kwoth
2fab61c4f8 Merge branch 'feeds' into 'v4'
Feeds custom message

See merge request Kwoth/nadekobot!273
2022-11-22 21:08:42 +00:00
Kwoth
9f96edbb46 You can now specify an optional custom message in .feed and .yun which will be posted along with an update 2022-11-22 21:08:42 +00:00
Kwoth
3c23b58088 Merge branch 'thread_log' into 'v4'
Thread log

See merge request Kwoth/nadekobot!274
2022-11-22 20:41:57 +00:00
Kwoth
e10530bc0e Thread log 2022-11-22 20:41:57 +00:00
Kwoth
f24692e79b Added .doas command which executes the command as if you were the target user 2022-11-19 22:36:18 +01:00
Kwoth
8a6edc17e4 Optimized .waifuinfo 2022-11-15 22:45:00 +01:00
Kwoth
9ce2837f5a Possible optimization for .waifuinfo 2022-11-13 20:40:37 +01:00
113 changed files with 22764 additions and 1089 deletions

View File

@@ -2,6 +2,73 @@
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.15] - 21.05.2023
### Fixed
- Fixed -w 0 in trivia
- Fixed `.rps` amount field in the response
- Fixed `.showembed` output
- Fixed bank award's incorrect output message
## [4.3.14] - 02.04.2023
### Fixed
- Fixed voice hearbeat issue
- `.banktake` had ok/error responses flipped. No functional change
- PermRole should deny messages in threads todo
- Fixed chucknorris jokes
- `.logserver` will now
## [4.3.13] - 20.02.2023
### Fixed
- Fixed `.log userpresence`
- `.q` will now use `yt-dlp` if anything other than `ytProvider: Ytdl` is set in `data/searches.yml`
- Fixed Title links on some embeds
## [4.3.12] - 12.02.2023
### Fixed
- Fixed `.betstats` not working on european locales
- Timed `.ban` will work on users who are not in the server
- Fixed some bugs in the medusa system
## [4.3.11] - 21.01.2023
### Added
- Added `.doas` Bot owner only command
- Added `.stickeradd` command
### Changed
- `.waifuinfo` optimized
- You can now specify an optional custom message in `.feed` and `.yun` which will be posted along with an update
- Greet/bye messages will now get disabled if they're set to a deleted/unknown channel
- Updated response strings
- `.translate` now supports many more languages
- `.translangs` prettier output
### Fixed
- Added logging for thread events
- Fixed a bug for `.quotedeleteauthor` causing the executing user to delete own messages
- Fixed TimeOut punishment not alklowing duration
- Fixed a nullref in streamrole service
- Fixed some potential causes for ratelimit due to default message retry settings
- Fixed a patron rewards bug caused by monthly donation checking not accounting for year increase
- Fixed a patron rewards bug for users who connected the same discord account with multiple patreon accounts
- `.deletecurrency` will now also reset banked currency
- Fixed DMHelpText reply
- `.h` command show now properly show both channel and server user permission requirements
- Many fixes and improvements to medusa system
- Fixed trivia --nohint
- `.joinrace` will no longer fail if the user isn't in the database yet
## [4.3.10] - 10.11.2022 ## [4.3.10] - 10.11.2022
### Added ### Added

View File

@@ -24,6 +24,10 @@ The list below is not complete. Use commands above to see up-to-date list for yo
`trivia.min_win_req` - Restricts a user's ability to make a trivia game with a win requirement less than the set value. `trivia.min_win_req` - Restricts a user's ability to make a trivia game with a win requirement less than the set value.
`trivia.currency_reward` - Sets the amount of currency a user will win if they place first in a completed trivia game. `trivia.currency_reward` - Sets the amount of currency a user will win if they place first in a completed trivia game.
`hangman.currency_reward` - Sets the amount of currency a user will win if they win a game of hangman.
`chatbot` - Sets which chatbot API the bot should use, values: `gpt3`, `cleverbot`.
`gpt.model` - Sets which GPT-3 model the bot should use, values: `ada001`, `babbage001`, `curie001`, `davinci003`.
`gpt.max_tokens` - Sets the limit of tokens GPT-3 can use per call. Find out more about tokens [here](https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them).
*more settings may be available in `data/games.yml` file* *more settings may be available in `data/games.yml` file*

View File

@@ -15,11 +15,14 @@ w# Setting up NadekoBot on Linux
It is recommended that you use **Ubuntu 20.04**, as there have been nearly no problems with it. Also, **32-bit systems are incompatible**. It is recommended that you use **Ubuntu 20.04**, as there have been nearly no problems with it. Also, **32-bit systems are incompatible**.
### Ubuntu 22.04 is ruled as incompatible so double check which ubuntu version you are using.
##### Compatible operating systems: ##### Compatible operating systems:
- Ubuntu: 16.04, 18.04, 20.04, 21.04, 21.10, 22.04 - Ubuntu: 16.04, 18.04, 20.04
- Mint: 19, 20 - Mint: 19, 20
- Debian: 9, 10 - Debian: 10, 11
- CentOS: 7 - CentOS: 7
- openSUSE - openSUSE
- Fedora: 33, 34, 35 - Fedora: 33, 34, 35

View File

@@ -123,7 +123,7 @@ In order to use music commands, you need ffmpeg and youtube-dl installed.
- [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `ffmpeg.exe` file to `NadekoBot/output`. - [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `ffmpeg.exe` file to `NadekoBot/output`.
- [youtube-dl] - Click to download the file, then move `youtube-dl.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `youtube-dl.exe` file to `NadekoBot/system`. - [youtube-dl] - Click to download the file, then move `youtube-dl.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `youtube-dl.exe` file to `NadekoBot/system`.
[Updater]: https://dl.nadeko.bot/ [Updater]: https://dl.nadeko.bot/v3/
[Notepad++]: https://notepad-plus-plus.org/ [Notepad++]: https://notepad-plus-plus.org/
[.net]: https://dotnet.microsoft.com/download/dotnet/5.0 [.net]: https://dotnet.microsoft.com/download/dotnet/5.0
[Redis]: https://github.com/MicrosoftArchive/redis/releases/download/win-3.0.504/Redis-x64-3.0.504.msi [Redis]: https://github.com/MicrosoftArchive/redis/releases/download/win-3.0.504/Redis-x64-3.0.504.msi

View File

@@ -147,6 +147,7 @@ This section will guide you through how to create a simple custom medusa. You ca
<!-- Use latest .net features --> <!-- Use latest .net features -->
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
<EnablePreviewFeatures>true</EnablePreviewFeatures> <EnablePreviewFeatures>true</EnablePreviewFeatures>
<GenerateRequiresPreviewFeaturesAttribute>true</GenerateRequiresPreviewFeaturesAttribute>
<!-- tell .net that this library will be used as a plugin --> <!-- tell .net that this library will be used as a plugin -->
<EnableDynamicLoading>true</EnableDynamicLoading> <EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -89,6 +89,5 @@ Some features have their own specific placeholders which are noted in that featu
- `%rngX-Y%` - Returns a random number between X and Y - `%rngX-Y%` - Returns a random number between X and Y
- `%target%` - Returns anything the user has written after the trigger (only works on Expressions) - `%target%` - Returns anything the user has written after the trigger (only works on Expressions)
- `%img:stuff%` - Returns an `imgur.com` search for "stuff" (only works on Expressions)
![img](https://puu.sh/B7mgI.png) ![img](https://puu.sh/B7mgI.png)

View File

@@ -0,0 +1,10 @@
namespace Nadeko.Snake;
/// <summary>
/// Used as a marker class for bot_perm and user_perm Attributes
/// Has no functionality.
/// </summary>
public abstract class MedusaPermAttribute : Attribute
{
}

View File

@@ -0,0 +1,7 @@
namespace Nadeko.Snake;
[AttributeUsage(AttributeTargets.Method)]
public sealed class bot_owner_onlyAttribute : MedusaPermAttribute
{
}

View File

@@ -0,0 +1,23 @@
using Discord;
namespace Nadeko.Snake;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class bot_permAttribute : MedusaPermAttribute
{
public GuildPermission? GuildPerm { get; }
public ChannelPermission? ChannelPerm { get; }
public bot_permAttribute(GuildPermission perm)
{
GuildPerm = perm;
ChannelPerm = null;
}
public bot_permAttribute(ChannelPermission perm)
{
ChannelPerm = perm;
GuildPerm = null;
}
}

View File

@@ -0,0 +1,22 @@
using Discord;
namespace Nadeko.Snake;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class user_permAttribute : MedusaPermAttribute
{
public GuildPermission? GuildPerm { get; }
public ChannelPermission? ChannelPerm { get; }
public user_permAttribute(GuildPermission perm)
{
GuildPerm = perm;
ChannelPerm = null;
}
public user_permAttribute(ChannelPermission perm)
{
ChannelPerm = perm;
GuildPerm = null;
}
}

View File

@@ -23,6 +23,11 @@ public abstract class AnyContext
/// </summary> /// </summary>
public abstract IUser User { get; } public abstract IUser User { get; }
/// <summary>
/// Bot user
/// </summary>
public abstract ISelfUser Bot { get; }
/// <summary> /// <summary>
/// Provides access to strings used by this medusa /// Provides access to strings used by this medusa
/// </summary> /// </summary>

View File

@@ -10,7 +10,7 @@ public static class MedusaExtensions
embed: embed.Build(), embed: embed.Build(),
options: new() options: new()
{ {
RetryMode = RetryMode.AlwaysRetry RetryMode = RetryMode.Retry502
}); });
// unlocalized // unlocalized

View File

@@ -69,7 +69,7 @@ public sealed class Bot
: GatewayIntents.AllUnprivileged, : GatewayIntents.AllUnprivileged,
LogGatewayIntentWarnings = false, LogGatewayIntentWarnings = false,
FormatUsersInBidirectionalUnicode = false, FormatUsersInBidirectionalUnicode = false,
DefaultRetryMode = RetryMode.AlwaysRetry ^ RetryMode.RetryRatelimit DefaultRetryMode = RetryMode.Retry502
}); });
_commandService = new(new() _commandService = new(new()

View File

@@ -3,7 +3,7 @@ using NadekoBot.Modules.Administration.Services;
namespace Discord; namespace Discord;
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class UserPermAttribute : RequireUserPermissionAttribute public class UserPermAttribute : RequireUserPermissionAttribute
{ {
public UserPermAttribute(GuildPerm permission) public UserPermAttribute(GuildPerm permission)

View File

@@ -54,6 +54,9 @@ go to https://www.patreon.com/portal -> my clients -> create client")]
[Comment(@"Official cleverbot api key.")] [Comment(@"Official cleverbot api key.")]
public string CleverbotApiKey { get; set; } public string CleverbotApiKey { get; set; }
[Comment(@"Official GPT-3 api key.")]
public string Gpt3ApiKey { get; set; }
[Comment(@"Which cache implementation should bot use. [Comment(@"Which cache implementation should bot use.
'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset. 'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset.
'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml")] 'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml")]
@@ -118,7 +121,7 @@ Windows default
public Creds() public Creds()
{ {
Version = 6; Version = 7;
Token = string.Empty; Token = string.Empty;
UsePrivilegedIntents = true; UsePrivilegedIntents = true;
OwnerIds = new List<ulong>(); OwnerIds = new List<ulong>();
@@ -128,6 +131,7 @@ Windows default
Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty); Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty);
BotListToken = string.Empty; BotListToken = string.Empty;
CleverbotApiKey = string.Empty; CleverbotApiKey = string.Empty;
Gpt3ApiKey = string.Empty;
BotCache = BotCacheImplemenation.Memory; BotCache = BotCacheImplemenation.Memory;
RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password="; RedisOptions = "localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=";
Db = new() Db = new()

View File

@@ -14,6 +14,7 @@ public interface IBotCredentials
int TotalShards { get; } int TotalShards { get; }
Creds.PatreonSettings Patreon { get; } Creds.PatreonSettings Patreon { get; }
string CleverbotApiKey { get; } string CleverbotApiKey { get; }
string Gpt3ApiKey { get; }
RestartConfig RestartCommand { get; } RestartConfig RestartCommand { get; }
Creds.VotesSettings Votes { get; } Creds.VotesSettings Votes { get; }
string BotListToken { get; } string BotListToken { get; }

View File

@@ -29,4 +29,7 @@ public enum LogType
VoicePresenceTts, VoicePresenceTts,
UserMuted, UserMuted,
UserWarned, UserWarned,
ThreadDeleted,
ThreadCreated
} }

View File

@@ -5,9 +5,11 @@ public sealed class DmContextAdapter : DmContext
public override IMedusaStrings Strings { get; } public override IMedusaStrings Strings { get; }
public override IDMChannel Channel { get; } public override IDMChannel Channel { get; }
public override IUserMessage Message { get; } public override IUserMessage Message { get; }
public override ISelfUser Bot { get; }
public override IUser User public override IUser User
=> Message.Author; => Message.Author;
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly Lazy<IEmbedBuilderService> _ebs; private readonly Lazy<IEmbedBuilderService> _ebs;
private readonly Lazy<IBotStrings> _botStrings; private readonly Lazy<IBotStrings> _botStrings;
@@ -26,6 +28,7 @@ public sealed class DmContextAdapter : DmContext
Channel = ch; Channel = ch;
Message = ctx.Message; Message = ctx.Message;
Bot = ctx.Client.CurrentUser;
_ebs = new(_services.GetRequiredService<IEmbedBuilderService>()); _ebs = new(_services.GetRequiredService<IEmbedBuilderService>());

View File

@@ -0,0 +1,31 @@
namespace Nadeko.Medusa.Adapters;
public class FilterAdapter : PreconditionAttribute
{
private readonly FilterAttribute _filterAttribute;
private readonly IMedusaStrings _strings;
public FilterAdapter(FilterAttribute filterAttribute,
IMedusaStrings strings)
{
_filterAttribute = filterAttribute;
_strings = strings;
}
public override async Task<PreconditionResult> CheckPermissionsAsync(
ICommandContext context,
CommandInfo command,
IServiceProvider services)
{
var medusaContext = ContextAdapterFactory.CreateNew(context,
_strings,
services);
var result = await _filterAttribute.CheckAsync(medusaContext);
if (!result)
return PreconditionResult.FromError($"Precondition '{_filterAttribute.GetType().Name}' failed.");
return PreconditionResult.FromSuccess();
}
}

View File

@@ -11,6 +11,7 @@ public sealed class GuildContextAdapter : GuildContext
public override IMedusaStrings Strings { get; } public override IMedusaStrings Strings { get; }
public override IGuild Guild { get; } public override IGuild Guild { get; }
public override ITextChannel Channel { get; } public override ITextChannel Channel { get; }
public override ISelfUser Bot { get; }
public override IUserMessage Message public override IUserMessage Message
=> _ctx.Message; => _ctx.Message;
@@ -28,6 +29,7 @@ public sealed class GuildContextAdapter : GuildContext
Strings = strings; Strings = strings;
User = (IGuildUser)ctx.User; User = (IGuildUser)ctx.User;
Bot = ctx.Client.CurrentUser;
_services = services; _services = services;
_ebs = new(_services.GetRequiredService<IEmbedBuilderService>()); _ebs = new(_services.GetRequiredService<IEmbedBuilderService>());

View File

@@ -22,8 +22,6 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
public void AddLoadedMedusa(string name) public void AddLoadedMedusa(string name)
{ {
name = name.Trim().ToLowerInvariant();
ModifyConfig(conf => ModifyConfig(conf =>
{ {
if (conf.Loaded is null) if (conf.Loaded is null)
@@ -36,8 +34,6 @@ public sealed class MedusaConfigService : ConfigServiceBase<MedusaConfig>, IMedu
public void RemoveLoadedMedusa(string name) public void RemoveLoadedMedusa(string name)
{ {
name = name.Trim().ToLowerInvariant();
ModifyConfig(conf => ModifyConfig(conf =>
{ {
if (conf.Loaded is null) if (conf.Loaded is null)

View File

@@ -1,5 +1,6 @@
using Discord.Commands.Builders; using Discord.Commands.Builders;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nadeko.Medusa.Adapters;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -382,6 +383,11 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
{ {
var m = mb.WithName(snekInfo.Name); var m = mb.WithName(snekInfo.Name);
foreach (var f in snekInfo.Filters)
{
m.AddPrecondition(new FilterAdapter(f, strings));
}
foreach (var cmd in snekInfo.Commands) foreach (var cmd in snekInfo.Commands)
{ {
m.AddCommand(cmd.Aliases.First(), m.AddCommand(cmd.Aliases.First(),
@@ -390,7 +396,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
new(cmd), new(cmd),
new(medusaServices), new(medusaServices),
strings), strings),
CreateCommandFactory(medusaName, cmd)); CreateCommandFactory(medusaName, cmd, strings));
} }
foreach (var subInfo in snekInfo.Subsneks) foreach (var subInfo in snekInfo.Subsneks)
@@ -399,7 +405,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
private static readonly RequireContextAttribute _reqGuild = new RequireContextAttribute(ContextType.Guild); private static readonly RequireContextAttribute _reqGuild = new RequireContextAttribute(ContextType.Guild);
private static readonly RequireContextAttribute _reqDm = new RequireContextAttribute(ContextType.DM); private static readonly RequireContextAttribute _reqDm = new RequireContextAttribute(ContextType.DM);
private Action<CommandBuilder> CreateCommandFactory(string medusaName, SnekCommandData cmd) private Action<CommandBuilder> CreateCommandFactory(string medusaName, SnekCommandData cmd, IMedusaStrings strings)
=> (cb) => => (cb) =>
{ {
cb.AddAliases(cmd.Aliases.Skip(1).ToArray()); cb.AddAliases(cmd.Aliases.Skip(1).ToArray());
@@ -409,6 +415,31 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
else if (cmd.ContextType == CommandContextType.Dm) else if (cmd.ContextType == CommandContextType.Dm)
cb.AddPrecondition(_reqDm); cb.AddPrecondition(_reqDm);
foreach (var f in cmd.Filters)
cb.AddPrecondition(new FilterAdapter(f, strings));
foreach (var ubp in cmd.UserAndBotPerms)
{
if (ubp is user_permAttribute up)
{
if (up.GuildPerm is { } gp)
cb.AddPrecondition(new UserPermAttribute(gp));
else if (up.ChannelPerm is { } cp)
cb.AddPrecondition(new UserPermAttribute(cp));
}
else if (ubp is bot_permAttribute bp)
{
if (bp.GuildPerm is { } gp)
cb.AddPrecondition(new BotPermAttribute(gp));
else if (bp.ChannelPerm is { } cp)
cb.AddPrecondition(new BotPermAttribute(cp));
}
else if (ubp is bot_owner_onlyAttribute)
{
cb.AddPrecondition(new OwnerOnlyAttribute());
}
}
cb.WithPriority(cmd.Priority); cb.WithPriority(cmd.Priority);
// using summary to save method name // using summary to save method name
@@ -428,6 +459,9 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
pb.WithIsMultiple(paramData.IsParams) pb.WithIsMultiple(paramData.IsParams)
.WithIsOptional(paramData.IsOptional) .WithIsOptional(paramData.IsOptional)
.WithIsRemainder(paramData.IsLeftover); .WithIsRemainder(paramData.IsLeftover);
if (paramData.IsOptional)
pb.WithDefault(paramData.DefaultValue);
}; };
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
@@ -750,8 +784,10 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
var cmds = new List<SnekCommandData>(); var cmds = new List<SnekCommandData>();
foreach (var method in methodInfos) foreach (var method in methodInfos)
{ {
var filters = method.GetCustomAttributes<FilterAttribute>().ToArray(); var filters = method.GetCustomAttributes<FilterAttribute>(true).ToArray();
var prio = method.GetCustomAttribute<prioAttribute>()?.Priority ?? 0; var userAndBotPerms = method.GetCustomAttributes<MedusaPermAttribute>(true)
.ToArray();
var prio = method.GetCustomAttribute<prioAttribute>(true)?.Priority ?? 0;
var paramInfos = method.GetParameters(); var paramInfos = method.GetParameters();
var cmdParams = new List<ParamData>(); var cmdParams = new List<ParamData>();
@@ -767,6 +803,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
var leftoverAttribute = pi.GetCustomAttribute<leftoverAttribute>(true); var leftoverAttribute = pi.GetCustomAttribute<leftoverAttribute>(true);
var hasDefaultValue = pi.HasDefaultValue; var hasDefaultValue = pi.HasDefaultValue;
var defaultValue = pi.DefaultValue;
var isLeftover = leftoverAttribute != null; var isLeftover = leftoverAttribute != null;
var isParams = pi.GetCustomAttribute<ParamArrayAttribute>() is not null; var isParams = pi.GetCustomAttribute<ParamArrayAttribute>() is not null;
var paramType = pi.ParameterType; var paramType = pi.ParameterType;
@@ -824,11 +861,11 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
throw new ArgumentException("Leftover attribute error."); throw new ArgumentException("Leftover attribute error.");
} }
cmdParams.Add(new ParamData(paramType, paramName, hasDefaultValue, isLeftover, isParams)); cmdParams.Add(new ParamData(paramType, paramName, hasDefaultValue, defaultValue, isLeftover, isParams));
} }
var cmdAttribute = method.GetCustomAttribute<cmdAttribute>()!; var cmdAttribute = method.GetCustomAttribute<cmdAttribute>(true)!;
var aliases = cmdAttribute.Aliases; var aliases = cmdAttribute.Aliases;
if (aliases.Length == 0) if (aliases.Length == 0)
aliases = new[] { method.Name.ToLowerInvariant() }; aliases = new[] { method.Name.ToLowerInvariant() };
@@ -838,6 +875,7 @@ public sealed class MedusaLoaderService : IMedusaLoaderService, IReadyExecutor,
method, method,
instance, instance,
filters, filters,
userAndBotPerms,
cmdContext, cmdContext,
diParams, diParams,
cmdParams, cmdParams,

View File

@@ -4,6 +4,7 @@ public sealed record ParamData(
Type Type, Type Type,
string Name, string Name,
bool IsOptional, bool IsOptional,
object? DefaultValue,
bool IsLeftover, bool IsLeftover,
bool IsParams bool IsParams
); );

View File

@@ -11,6 +11,7 @@ public sealed class SnekCommandData
MethodInfo methodInfo, MethodInfo methodInfo,
Snek module, Snek module,
FilterAttribute[] filters, FilterAttribute[] filters,
MedusaPermAttribute[] userAndBotPerms,
CommandContextType contextType, CommandContextType contextType,
IReadOnlyList<Type> injectedParams, IReadOnlyList<Type> injectedParams,
IReadOnlyList<ParamData> parameters, IReadOnlyList<ParamData> parameters,
@@ -21,6 +22,7 @@ public sealed class SnekCommandData
MethodInfo = methodInfo; MethodInfo = methodInfo;
Module = module; Module = module;
Filters = filters; Filters = filters;
UserAndBotPerms = userAndBotPerms;
ContextType = contextType; ContextType = contextType;
InjectedParams = injectedParams; InjectedParams = injectedParams;
Parameters = parameters; Parameters = parameters;
@@ -28,6 +30,8 @@ public sealed class SnekCommandData
OptionalStrings = strings; OptionalStrings = strings;
} }
public MedusaPermAttribute[] UserAndBotPerms { get; set; }
public CommandStrings OptionalStrings { get; set; } public CommandStrings OptionalStrings { get; set; }
public IReadOnlyCollection<string> Aliases { get; } public IReadOnlyCollection<string> Aliases { get; }

View File

@@ -1,5 +1,6 @@
#nullable disable warnings #nullable disable warnings
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using System.Text.Json.Serialization;
namespace NadekoBot; namespace NadekoBot;
@@ -7,14 +8,14 @@ public sealed record SmartEmbedArrayElementText : SmartEmbedTextBase
{ {
public string Color { get; init; } = string.Empty; public string Color { get; init; } = string.Empty;
public SmartEmbedArrayElementText() : base() public SmartEmbedArrayElementText()
{ {
} }
public SmartEmbedArrayElementText(IEmbed eb) : base(eb) public SmartEmbedArrayElementText(IEmbed eb) : base(eb)
{ {
Color = eb.Color is { } c ? new Rgba32(c.R, c.G, c.B).ToHex() : string.Empty;
} }
protected override EmbedBuilder GetEmbedInternal() protected override EmbedBuilder GetEmbedInternal()
@@ -63,6 +64,7 @@ public abstract record SmartEmbedTextBase : SmartText
public SmartTextEmbedFooter Footer { get; init; } public SmartTextEmbedFooter Footer { get; init; }
public SmartTextEmbedField[] Fields { get; init; } public SmartTextEmbedField[] Fields { get; init; }
[JsonIgnore]
public bool IsValid public bool IsValid
=> !string.IsNullOrWhiteSpace(Title) => !string.IsNullOrWhiteSpace(Title)
|| !string.IsNullOrWhiteSpace(Description) || !string.IsNullOrWhiteSpace(Description)

View File

@@ -1,4 +1,6 @@
#nullable disable #nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot; namespace NadekoBot;
public sealed record SmartEmbedTextArray : SmartText public sealed record SmartEmbedTextArray : SmartText
@@ -6,6 +8,7 @@ public sealed record SmartEmbedTextArray : SmartText
public string Content { get; set; } public string Content { get; set; }
public SmartEmbedArrayElementText[] Embeds { get; set; } public SmartEmbedArrayElementText[] Embeds { get; set; }
[JsonIgnore]
public bool IsValid public bool IsValid
=> Embeds?.All(x => x.IsValid) ?? false; => Embeds?.All(x => x.IsValid) ?? false;

View File

@@ -1,16 +1,20 @@
#nullable disable #nullable disable
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Text.Json.Serialization;
namespace NadekoBot; namespace NadekoBot;
public abstract record SmartText public abstract record SmartText
{ {
[JsonIgnore]
public bool IsEmbed public bool IsEmbed
=> this is SmartEmbedText; => this is SmartEmbedText;
[JsonIgnore]
public bool IsPlainText public bool IsPlainText
=> this is SmartPlainText; => this is SmartPlainText;
[JsonIgnore]
public bool IsEmbedArray public bool IsEmbedArray
=> this is SmartEmbedTextArray; => this is SmartEmbedTextArray;

View File

@@ -46,9 +46,7 @@ public sealed class CommandOrExprTypeReader : NadekoTypeReader<CommandOrExprInfo
public override async ValueTask<TypeReaderResult<CommandOrExprInfo>> ReadAsync(ICommandContext ctx, string input) public override async ValueTask<TypeReaderResult<CommandOrExprInfo>> ReadAsync(ICommandContext ctx, string input)
{ {
input = input.ToUpperInvariant(); if (_exprs.ExpressionExists(ctx.Guild?.Id, input))
if (_exprs.ExpressionExists(ctx.Guild?.Id, input) || _exprs.ExpressionExists(null, input))
return TypeReaderResult.FromSuccess(new CommandOrExprInfo(input, CommandOrExprInfo.Type.Custom)); return TypeReaderResult.FromSuccess(new CommandOrExprInfo(input, CommandOrExprInfo.Type.Custom));
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input); var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input);

View File

@@ -12,10 +12,4 @@ public static class NadekoExpressionExtensions
public static IEnumerable<NadekoExpression> ForId(this DbSet<NadekoExpression> exprs, ulong id) public static IEnumerable<NadekoExpression> ForId(this DbSet<NadekoExpression> exprs, ulong id)
=> exprs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList(); => exprs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList();
public static NadekoExpression GetByGuildIdAndInput(
this DbSet<NadekoExpression> exprs,
ulong? guildId,
string input)
=> exprs.FirstOrDefault(x => x.GuildId == guildId && x.Trigger.ToUpper() == input);
} }

View File

@@ -10,6 +10,7 @@ namespace NadekoBot.Db;
public class WaifuInfoStats public class WaifuInfoStats
{ {
public int WaifuId { get; init; }
public string FullName { get; init; } public string FullName { get; init; }
public long Price { get; init; } public long Price { get; init; }
public string ClaimerName { get; init; } public string ClaimerName { get; init; }
@@ -17,9 +18,6 @@ public class WaifuInfoStats
public int AffinityCount { get; init; } public int AffinityCount { get; init; }
public int DivorceCount { get; init; } public int DivorceCount { get; init; }
public int ClaimCount { get; init; } public int ClaimCount { get; init; }
public List<WaifuItem> Items { get; init; }
public List<string> Claims { get; init; }
public List<string> Fans { get; init; }
} }
public static class WaifuExtensions public static class WaifuExtensions
@@ -103,6 +101,7 @@ public static class WaifuExtensions
.FirstOrDefault()) .FirstOrDefault())
.Select(w => new WaifuInfoStats .Select(w => new WaifuInfoStats
{ {
WaifuId = w.WaifuId,
FullName = FullName =
ctx.Set<DiscordUser>() ctx.Set<DiscordUser>()
.AsQueryable() .AsQueryable()
@@ -135,17 +134,6 @@ public static class WaifuExtensions
&& x.NewId == null && x.NewId == null
&& x.UpdateType == WaifuUpdateType.Claimed), && x.UpdateType == WaifuUpdateType.Claimed),
Price = w.Price, Price = w.Price,
Claims = ctx.WaifuInfo.AsQueryable()
.Include(x => x.Waifu)
.Where(x => x.ClaimerId == w.WaifuId)
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
.ToList(),
Fans = ctx.WaifuInfo.AsQueryable()
.Include(x => x.Waifu)
.Where(x => x.AffinityId == w.WaifuId)
.Select(x => x.Waifu.Username + "#" + x.Waifu.Discriminator)
.ToList(),
Items = w.Items
}) })
.FirstOrDefault(); .FirstOrDefault();

View File

@@ -9,6 +9,8 @@ public class FeedSub : DbEntity
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string Message { get; set; }
public override int GetHashCode() public override int GetHashCode()
=> Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode(); => Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode();

View File

@@ -20,6 +20,10 @@ public class LogSetting : DbEntity
public ulong? ChannelDestroyedId { get; set; } public ulong? ChannelDestroyedId { get; set; }
public ulong? ChannelUpdatedId { get; set; } public ulong? ChannelUpdatedId { get; set; }
public ulong? ThreadDeletedId { get; set; }
public ulong? ThreadCreatedId { get; set; }
public ulong? UserMutedId { get; set; } public ulong? UserMutedId { get; set; }
//userpresence //userpresence

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
public partial class logthread : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<ulong>(
name: "threadcreatedid",
table: "logsettings",
type: "bigint unsigned",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "threaddeletedid",
table: "logsettings",
type: "bigint unsigned",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "threadcreatedid",
table: "logsettings");
migrationBuilder.DropColumn(
name: "threaddeletedid",
table: "logsettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.Mysql
{
public partial class feedtext : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "message",
table: "feedsub",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "message",
table: "feedsub");
}
}
}

View File

@@ -975,6 +975,10 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("int") .HasColumnType("int")
.HasColumnName("guildconfigid"); .HasColumnName("guildconfigid");
b.Property<string>("Message")
.HasColumnType("longtext")
.HasColumnName("message");
b.Property<string>("Url") b.Property<string>("Url")
.IsRequired() .IsRequired()
.HasColumnType("varchar(255)") .HasColumnType("varchar(255)")
@@ -1518,6 +1522,14 @@ namespace NadekoBot.Migrations.Mysql
.HasColumnType("bigint unsigned") .HasColumnType("bigint unsigned")
.HasColumnName("messageupdatedid"); .HasColumnName("messageupdatedid");
b.Property<ulong?>("ThreadCreatedId")
.HasColumnType("bigint unsigned")
.HasColumnName("threadcreatedid");
b.Property<ulong?>("ThreadDeletedId")
.HasColumnType("bigint unsigned")
.HasColumnName("threaddeletedid");
b.Property<ulong?>("UserBannedId") b.Property<ulong?>("UserBannedId")
.HasColumnType("bigint unsigned") .HasColumnType("bigint unsigned")
.HasColumnName("userbannedid"); .HasColumnName("userbannedid");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
public partial class logthread : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "threadcreatedid",
table: "logsettings",
type: "numeric(20,0)",
nullable: true);
migrationBuilder.AddColumn<decimal>(
name: "threaddeletedid",
table: "logsettings",
type: "numeric(20,0)",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "threadcreatedid",
table: "logsettings");
migrationBuilder.DropColumn(
name: "threaddeletedid",
table: "logsettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations.PostgreSql
{
public partial class feedtext : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "message",
table: "feedsub",
type: "text",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "message",
table: "feedsub");
}
}
}

View File

@@ -1023,6 +1023,10 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("guildconfigid"); .HasColumnName("guildconfigid");
b.Property<string>("Message")
.HasColumnType("text")
.HasColumnName("message");
b.Property<string>("Url") b.Property<string>("Url")
.IsRequired() .IsRequired()
.HasColumnType("text") .HasColumnType("text")
@@ -1590,6 +1594,14 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("numeric(20,0)") .HasColumnType("numeric(20,0)")
.HasColumnName("messageupdatedid"); .HasColumnName("messageupdatedid");
b.Property<decimal?>("ThreadCreatedId")
.HasColumnType("numeric(20,0)")
.HasColumnName("threadcreatedid");
b.Property<decimal?>("ThreadDeletedId")
.HasColumnType("numeric(20,0)")
.HasColumnName("threaddeletedid");
b.Property<decimal?>("UserBannedId") b.Property<decimal?>("UserBannedId")
.HasColumnType("numeric(20,0)") .HasColumnType("numeric(20,0)")
.HasColumnName("userbannedid"); .HasColumnName("userbannedid");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
public partial class logthread : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<ulong>(
name: "ThreadCreatedId",
table: "LogSettings",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "ThreadDeletedId",
table: "LogSettings",
type: "INTEGER",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ThreadCreatedId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "ThreadDeletedId",
table: "LogSettings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Migrations
{
public partial class feedtext : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Message",
table: "FeedSub",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Message",
table: "FeedSub");
}
}
}

View File

@@ -37,7 +37,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("AutoPublishChannel"); b.ToTable("AutoPublishChannel", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b => modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b =>
@@ -60,7 +60,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId") b.HasIndex("UserId")
.IsUnique(); .IsUnique();
b.ToTable("BankUsers"); b.ToTable("BankUsers", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
@@ -75,7 +75,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("ClubApplicants"); b.ToTable("ClubApplicants", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
@@ -90,7 +90,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("ClubBans"); b.ToTable("ClubBans", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
@@ -126,7 +126,7 @@ namespace NadekoBot.Migrations
b.HasIndex("OwnerId") b.HasIndex("OwnerId")
.IsUnique(); .IsUnique();
b.ToTable("Clubs"); b.ToTable("Clubs", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b => modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
@@ -185,7 +185,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("DiscordUser"); b.ToTable("DiscordUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b => modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
@@ -219,7 +219,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream"); b.ToTable("FollowedStream", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b => modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
@@ -246,7 +246,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("PatronQuotas"); b.ToTable("PatronQuotas", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b => modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
@@ -272,7 +272,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UniquePlatformUserId") b.HasIndex("UniquePlatformUserId")
.IsUnique(); .IsUnique();
b.ToTable("Patrons"); b.ToTable("Patrons", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.StreamOnlineMessage", b => modelBuilder.Entity("NadekoBot.Db.Models.StreamOnlineMessage", b =>
@@ -298,7 +298,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("StreamOnlineMessages"); b.ToTable("StreamOnlineMessages", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.XpShopOwnedItem", b => modelBuilder.Entity("NadekoBot.Db.Models.XpShopOwnedItem", b =>
@@ -328,7 +328,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId", "ItemType", "ItemKey") b.HasIndex("UserId", "ItemType", "ItemKey")
.IsUnique(); .IsUnique();
b.ToTable("XpShopOwnedItem"); b.ToTable("XpShopOwnedItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
@@ -357,7 +357,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("AntiAltSetting"); b.ToTable("AntiAltSetting", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
@@ -389,7 +389,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("AntiRaidSetting"); b.ToTable("AntiRaidSetting", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
@@ -411,7 +411,7 @@ namespace NadekoBot.Migrations
b.HasIndex("AntiSpamSettingId"); b.HasIndex("AntiSpamSettingId");
b.ToTable("AntiSpamIgnore"); b.ToTable("AntiSpamIgnore", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
@@ -443,7 +443,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("AntiSpamSetting"); b.ToTable("AntiSpamSetting", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
@@ -481,7 +481,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("AutoCommands"); b.ToTable("AutoCommands", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
@@ -509,7 +509,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId"); b.HasIndex("GuildId");
b.ToTable("AutoTranslateChannels"); b.ToTable("AutoTranslateChannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
@@ -537,7 +537,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("ChannelId", "UserId"); b.HasAlternateKey("ChannelId", "UserId");
b.ToTable("AutoTranslateUsers"); b.ToTable("AutoTranslateUsers", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
@@ -563,7 +563,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("BanTemplates"); b.ToTable("BanTemplates", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
@@ -583,7 +583,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Blacklist"); b.ToTable("Blacklist", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
@@ -608,7 +608,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("CommandAlias"); b.ToTable("CommandAlias", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
@@ -633,7 +633,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown"); b.ToTable("CommandCooldown", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
@@ -671,7 +671,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("CurrencyTransactions"); b.ToTable("CurrencyTransactions", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
@@ -696,7 +696,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("DelMsgOnCmdChannel"); b.ToTable("DelMsgOnCmdChannel", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
@@ -722,7 +722,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "Command") b.HasIndex("GuildId", "Command")
.IsUnique(); .IsUnique();
b.ToTable("DiscordPermOverrides"); b.ToTable("DiscordPermOverrides", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
@@ -747,7 +747,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId"); b.HasIndex("XpSettingsId");
b.ToTable("ExcludedItem"); b.ToTable("ExcludedItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
@@ -765,6 +765,9 @@ namespace NadekoBot.Migrations
b.Property<int>("GuildConfigId") b.Property<int>("GuildConfigId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("Message")
.HasColumnType("TEXT");
b.Property<string>("Url") b.Property<string>("Url")
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@@ -773,7 +776,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("GuildConfigId", "Url"); b.HasAlternateKey("GuildConfigId", "Url");
b.ToTable("FeedSub"); b.ToTable("FeedSub", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
@@ -795,7 +798,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilterChannelId"); b.ToTable("FilterChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
@@ -817,7 +820,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord"); b.ToTable("FilteredWord", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
@@ -839,7 +842,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilterLinksChannelId"); b.ToTable("FilterLinksChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
@@ -861,7 +864,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilterWordsChannelId"); b.ToTable("FilterWordsChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GamblingStats", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GamblingStats", b =>
@@ -887,7 +890,7 @@ namespace NadekoBot.Migrations
b.HasIndex("Feature") b.HasIndex("Feature")
.IsUnique(); .IsUnique();
b.ToTable("GamblingStats"); b.ToTable("GamblingStats", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
@@ -909,7 +912,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId"); b.ToTable("GCChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
@@ -935,7 +938,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId", "Number") b.HasIndex("GuildConfigId", "Number")
.IsUnique(); .IsUnique();
b.ToTable("GroupName"); b.ToTable("GroupName", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
@@ -1067,7 +1070,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WarnExpireHours"); b.HasIndex("WarnExpireHours");
b.ToTable("GuildConfigs"); b.ToTable("GuildConfigs", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
@@ -1093,7 +1096,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId", "LogItemId", "ItemType") b.HasIndex("LogSettingId", "LogItemId", "ItemType")
.IsUnique(); .IsUnique();
b.ToTable("IgnoredLogChannels"); b.ToTable("IgnoredLogChannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
@@ -1115,7 +1118,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId"); b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels"); b.ToTable("IgnoredVoicePresenceCHannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
@@ -1141,7 +1144,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ChannelId") b.HasIndex("ChannelId")
.IsUnique(); .IsUnique();
b.ToTable("ImageOnlyChannels"); b.ToTable("ImageOnlyChannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
@@ -1186,6 +1189,12 @@ namespace NadekoBot.Migrations
b.Property<ulong?>("MessageUpdatedId") b.Property<ulong?>("MessageUpdatedId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong?>("ThreadCreatedId")
.HasColumnType("INTEGER");
b.Property<ulong?>("ThreadDeletedId")
.HasColumnType("INTEGER");
b.Property<ulong?>("UserBannedId") b.Property<ulong?>("UserBannedId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@@ -1209,7 +1218,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("LogSettings"); b.ToTable("LogSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b =>
@@ -1246,7 +1255,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("MusicPlayerSettings"); b.ToTable("MusicPlayerSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
@@ -1269,7 +1278,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("MusicPlaylists"); b.ToTable("MusicPlaylists", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
@@ -1291,7 +1300,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId"); b.ToTable("MutedUserId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b =>
@@ -1329,7 +1338,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Expressions"); b.ToTable("Expressions", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
@@ -1351,7 +1360,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId"); b.HasIndex("GuildId");
b.ToTable("NsfwBlacklistedTags"); b.ToTable("NsfwBlacklistedTags", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
@@ -1391,7 +1400,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("Permissions"); b.ToTable("Permissions", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b =>
@@ -1428,7 +1437,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MessageId") b.HasIndex("MessageId")
.IsUnique(); .IsUnique();
b.ToTable("PlantedCurrency"); b.ToTable("PlantedCurrency", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
@@ -1462,7 +1471,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MusicPlaylistId"); b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong"); b.ToTable("PlaylistSong", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b =>
@@ -1488,7 +1497,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("Poll"); b.ToTable("Poll", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b =>
@@ -1513,7 +1522,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId"); b.HasIndex("PollId");
b.ToTable("PollAnswer"); b.ToTable("PollAnswer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b =>
@@ -1538,7 +1547,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId"); b.HasIndex("PollId");
b.ToTable("PollVote"); b.ToTable("PollVote", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
@@ -1574,7 +1583,7 @@ namespace NadekoBot.Migrations
b.HasIndex("Keyword"); b.HasIndex("Keyword");
b.ToTable("Quotes"); b.ToTable("Quotes", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleV2", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleV2", b =>
@@ -1615,7 +1624,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MessageId", "Emote") b.HasIndex("MessageId", "Emote")
.IsUnique(); .IsUnique();
b.ToTable("ReactionRoles"); b.ToTable("ReactionRoles", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
@@ -1649,7 +1658,7 @@ namespace NadekoBot.Migrations
b.HasIndex("When"); b.HasIndex("When");
b.ToTable("Reminders"); b.ToTable("Reminders", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
@@ -1684,7 +1693,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Repeaters"); b.ToTable("Repeaters", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b =>
@@ -1713,7 +1722,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PlatformUserId") b.HasIndex("PlatformUserId")
.IsUnique(); .IsUnique();
b.ToTable("RewardedUsers"); b.ToTable("RewardedUsers", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b =>
@@ -1733,7 +1742,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("RotatingStatus"); b.ToTable("RotatingStatus", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
@@ -1764,7 +1773,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "RoleId") b.HasIndex("GuildId", "RoleId")
.IsUnique(); .IsUnique();
b.ToTable("SelfAssignableRoles"); b.ToTable("SelfAssignableRoles", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b =>
@@ -1807,7 +1816,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("ShopEntry"); b.ToTable("ShopEntry", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b =>
@@ -1829,7 +1838,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ShopEntryId"); b.HasIndex("ShopEntryId");
b.ToTable("ShopEntryItem"); b.ToTable("ShopEntryItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b =>
@@ -1851,7 +1860,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredRole"); b.ToTable("SlowmodeIgnoredRole", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b =>
@@ -1873,7 +1882,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredUser"); b.ToTable("SlowmodeIgnoredUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
@@ -1898,7 +1907,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId"); b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleBlacklistedUser"); b.ToTable("StreamRoleBlacklistedUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
@@ -1930,7 +1939,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("StreamRoleSettings"); b.ToTable("StreamRoleSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
@@ -1955,7 +1964,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId"); b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleWhitelistedUser"); b.ToTable("StreamRoleWhitelistedUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b =>
@@ -1980,7 +1989,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("UnbanTimer"); b.ToTable("UnbanTimer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
@@ -2005,7 +2014,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("UnmuteTimer"); b.ToTable("UnmuteTimer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b =>
@@ -2033,7 +2042,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("UnroleTimer"); b.ToTable("UnroleTimer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
@@ -2073,7 +2082,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId", "GuildId") b.HasIndex("UserId", "GuildId")
.IsUnique(); .IsUnique();
b.ToTable("UserXpStats"); b.ToTable("UserXpStats", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
@@ -2098,7 +2107,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("VcRoleInfo"); b.ToTable("VcRoleInfo", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
@@ -2133,7 +2142,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuId") b.HasIndex("WaifuId")
.IsUnique(); .IsUnique();
b.ToTable("WaifuInfo"); b.ToTable("WaifuInfo", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
@@ -2158,7 +2167,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuInfoId"); b.HasIndex("WaifuInfoId");
b.ToTable("WaifuItem"); b.ToTable("WaifuItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
@@ -2190,7 +2199,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("WaifuUpdates"); b.ToTable("WaifuUpdates", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b =>
@@ -2233,7 +2242,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("Warnings"); b.ToTable("Warnings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b =>
@@ -2264,7 +2273,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("WarningPunishment"); b.ToTable("WarningPunishment", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b =>
@@ -2289,7 +2298,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId"); b.HasIndex("XpSettingsId");
b.ToTable("XpCurrencyReward"); b.ToTable("XpCurrencyReward", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
@@ -2318,7 +2327,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId", "Level") b.HasIndex("XpSettingsId", "Level")
.IsUnique(); .IsUnique();
b.ToTable("XpRoleReward"); b.ToTable("XpRoleReward", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
@@ -2341,7 +2350,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("XpSettings"); b.ToTable("XpSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>

View File

@@ -78,6 +78,7 @@ public class DangerousCommandsService : INService
await ctx.CurrencyTransactions.DeleteAsync(); await ctx.CurrencyTransactions.DeleteAsync();
await ctx.PlantedCurrency.DeleteAsync(); await ctx.PlantedCurrency.DeleteAsync();
await ctx.BankUsers.DeleteAsync();
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }

View File

@@ -191,7 +191,7 @@ public class GreetService : INService, IReadyExecutor
if (conf.AutoDeleteByeMessagesTimer > 0) if (conf.AutoDeleteByeMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions) catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{ {
Log.Warning(ex, "Missing permissions to send a bye message, the bye message will be disabled on server: {GuildId}", channel.GuildId); Log.Warning(ex, "Missing permissions to send a bye message, the bye message will be disabled on server: {GuildId}", channel.GuildId);
await SetBye(channel.GuildId, channel.Id, false); await SetBye(channel.GuildId, channel.Id, false);
@@ -224,7 +224,7 @@ public class GreetService : INService, IReadyExecutor
if (conf.AutoDeleteGreetMessagesTimer > 0) if (conf.AutoDeleteGreetMessagesTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions) catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions || ex.DiscordCode == DiscordErrorCode.UnknownChannel)
{ {
Log.Warning(ex, "Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}", channel.GuildId); Log.Warning(ex, "Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}", channel.GuildId);
await SetGreet(channel.GuildId, channel.Id, false); await SetGreet(channel.GuildId, channel.Id, false);

View File

@@ -19,6 +19,7 @@ public class MuteService : INService
private static readonly OverwritePermissions _denyOverwrite = new(addReactions: PermValue.Deny, private static readonly OverwritePermissions _denyOverwrite = new(addReactions: PermValue.Deny,
sendMessages: PermValue.Deny, sendMessages: PermValue.Deny,
sendMessagesInThreads: PermValue.Deny,
attachFiles: PermValue.Deny); attachFiles: PermValue.Deny);
public event Action<IGuildUser, IUser, MuteType, string> UserMuted = delegate { }; public event Action<IGuildUser, IUser, MuteType, string> UserMuted = delegate { };
@@ -356,24 +357,24 @@ public class MuteService : INService
public async Task TimedBan( public async Task TimedBan(
IGuild guild, IGuild guild,
IUser user, ulong userId,
TimeSpan after, TimeSpan after,
string reason, string reason,
int pruneDays) int pruneDays)
{ {
await guild.AddBanAsync(user.Id, pruneDays, reason); await guild.AddBanAsync(userId, 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));
config.UnbanTimer.Add(new() config.UnbanTimer.Add(new()
{ {
UserId = user.Id, UserId = userId,
UnbanAt = DateTime.UtcNow + after UnbanAt = DateTime.UtcNow + after
}); // add teh unmute timer to the database }); // add teh unmute timer to the database
uow.SaveChanges(); await uow.SaveChangesAsync();
} }
StartUn_Timer(guild.Id, user.Id, after, TimerType.Ban); // start the timer StartUn_Timer(guild.Id, userId, after, TimerType.Ban); // start the timer
} }
public async Task TimedRole( public async Task TimedRole(

View File

@@ -457,6 +457,7 @@ public class ProtectionService : INService
case PunishmentAction.ChatMute: case PunishmentAction.ChatMute:
case PunishmentAction.VoiceMute: case PunishmentAction.VoiceMute:
case PunishmentAction.AddRole: case PunishmentAction.AddRole:
case PunishmentAction.TimeOut:
return true; return true;
default: default:
return false; return false;

View File

@@ -0,0 +1,138 @@
using MessageType = Discord.MessageType;
namespace NadekoBot.Modules.Administration;
public sealed class DoAsUserMessage : IUserMessage
{
private readonly string _message;
private IUserMessage _msg;
private readonly IUser _user;
public DoAsUserMessage(SocketUserMessage msg, IUser user, string message)
{
_msg = msg;
_user = user;
_message = message;
}
public ulong Id => _msg.Id;
public DateTimeOffset CreatedAt => _msg.CreatedAt;
public Task DeleteAsync(RequestOptions? options = null)
{
return _msg.DeleteAsync(options);
}
public Task AddReactionAsync(IEmote emote, RequestOptions? options = null)
{
return _msg.AddReactionAsync(emote, options);
}
public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions? options = null)
{
return _msg.RemoveReactionAsync(emote, user, options);
}
public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions? options = null)
{
return _msg.RemoveReactionAsync(emote, userId, options);
}
public Task RemoveAllReactionsAsync(RequestOptions? options = null)
{
return _msg.RemoveAllReactionsAsync(options);
}
public Task RemoveAllReactionsForEmoteAsync(IEmote emote, RequestOptions? options = null)
{
return _msg.RemoveAllReactionsForEmoteAsync(emote, options);
}
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emoji, int limit,
RequestOptions? options = null)
{
return _msg.GetReactionUsersAsync(emoji, limit, options);
}
public MessageType Type => _msg.Type;
public MessageSource Source => _msg.Source;
public bool IsTTS => _msg.IsTTS;
public bool IsPinned => _msg.IsPinned;
public bool IsSuppressed => _msg.IsSuppressed;
public bool MentionedEveryone => _msg.MentionedEveryone;
public string Content => _message;
public string CleanContent => _msg.CleanContent;
public DateTimeOffset Timestamp => _msg.Timestamp;
public DateTimeOffset? EditedTimestamp => _msg.EditedTimestamp;
public IMessageChannel Channel => _msg.Channel;
public IUser Author => _user;
public IReadOnlyCollection<IAttachment> Attachments => _msg.Attachments;
public IReadOnlyCollection<IEmbed> Embeds => _msg.Embeds;
public IReadOnlyCollection<ITag> Tags => _msg.Tags;
public IReadOnlyCollection<ulong> MentionedChannelIds => _msg.MentionedChannelIds;
public IReadOnlyCollection<ulong> MentionedRoleIds => _msg.MentionedRoleIds;
public IReadOnlyCollection<ulong> MentionedUserIds => _msg.MentionedUserIds;
public MessageActivity Activity => _msg.Activity;
public MessageApplication Application => _msg.Application;
public MessageReference Reference => _msg.Reference;
public IReadOnlyDictionary<IEmote, ReactionMetadata> Reactions => _msg.Reactions;
public IReadOnlyCollection<IMessageComponent> Components => _msg.Components;
public IReadOnlyCollection<IStickerItem> Stickers => _msg.Stickers;
public MessageFlags? Flags => _msg.Flags;
public IMessageInteraction Interaction => _msg.Interaction;
public Task ModifyAsync(Action<MessageProperties> func, RequestOptions? options = null)
{
return _msg.ModifyAsync(func, options);
}
public Task PinAsync(RequestOptions? options = null)
{
return _msg.PinAsync(options);
}
public Task UnpinAsync(RequestOptions? options = null)
{
return _msg.UnpinAsync(options);
}
public Task CrosspostAsync(RequestOptions? options = null)
{
return _msg.CrosspostAsync(options);
}
public string Resolve(TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name,
TagHandling roleHandling = TagHandling.Name,
TagHandling everyoneHandling = TagHandling.Ignore, TagHandling emojiHandling = TagHandling.Name)
{
return _msg.Resolve(userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling);
}
public IUserMessage ReferencedMessage => _msg.ReferencedMessage;
}

View File

@@ -35,6 +35,26 @@ public partial class Administration
_medusaLoader = medusaLoader; _medusaLoader = medusaLoader;
} }
[Cmd]
[OwnerOnly]
public async Task DoAs(IUser user, [Leftover] string message)
{
if (ctx.User is not IGuildUser { GuildPermissions.Administrator: true })
return;
if (ctx.Guild is SocketGuild sg && ctx.Channel is ISocketMessageChannel ch
&& ctx.Message is SocketUserMessage msg)
{
var fakeMessage = new DoAsUserMessage(msg, user, message);
await _cmdHandler.TryRunCommand(sg, ch, fakeMessage);
}
else
{
await ReplyErrorLocalizedAsync(strs.error_occured);
}
}
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]

View File

@@ -9,9 +9,9 @@ using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration; namespace NadekoBot.Modules.Administration;
public sealed class LogCommandService : ILogCommandService, IReadyExecutor public sealed class LogCommandService : ILogCommandService, IReadyExecutor
#if !GLOBAL_NADEKO #if !GLOBAL_NADEKO
, INService // don't load this service on global nadeko , INService // don't load this service on global nadeko
#endif #endif
{ {
public ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; } public ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }
@@ -54,10 +54,10 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
{ {
var guildIds = client.Guilds.Select(x => x.Id).ToList(); var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow.LogSettings.AsQueryable() var configs = uow.LogSettings.AsQueryable()
.AsNoTracking() .AsNoTracking()
.Where(x => guildIds.Contains(x.GuildId)) .Where(x => guildIds.Contains(x.GuildId))
.Include(ls => ls.LogIgnores) .Include(ls => ls.LogIgnores)
.ToList(); .ToList();
GuildLogSettings = configs.ToDictionary(ls => ls.GuildId).ToConcurrent(); GuildLogSettings = configs.ToDictionary(ls => ls.GuildId).ToConcurrent();
} }
@@ -73,12 +73,16 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
_client.GuildMemberUpdated += _client_GuildUserUpdated; _client.GuildMemberUpdated += _client_GuildUserUpdated;
_client.PresenceUpdated += _client_PresenceUpdated;
_client.UserUpdated += _client_UserUpdated; _client.UserUpdated += _client_UserUpdated;
_client.ChannelCreated += _client_ChannelCreated; _client.ChannelCreated += _client_ChannelCreated;
_client.ChannelDestroyed += _client_ChannelDestroyed; _client.ChannelDestroyed += _client_ChannelDestroyed;
_client.ChannelUpdated += _client_ChannelUpdated; _client.ChannelUpdated += _client_ChannelUpdated;
_client.RoleDeleted += _client_RoleDeleted; _client.RoleDeleted += _client_RoleDeleted;
_client.ThreadCreated += _client_ThreadCreated;
_client.ThreadDeleted += _client_ThreadDeleted;
_mute.UserMuted += MuteCommands_UserMuted; _mute.UserMuted += MuteCommands_UserMuted;
_mute.UserUnmuted += MuteCommands_UserUnmuted; _mute.UserUnmuted += MuteCommands_UserUnmuted;
@@ -87,6 +91,126 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
_punishService.OnUserWarned += PunishServiceOnOnUserWarned; _punishService.OnUserWarned += PunishServiceOnOnUserWarned;
} }
private async Task _client_PresenceUpdated(SocketUser user, SocketPresence? before, SocketPresence? after)
{
if (user is not SocketGuildUser gu)
return;
if (!GuildLogSettings.TryGetValue(gu.Guild.Id, out var logSetting)
|| before is null
|| after is null)
return;
ITextChannel? logChannel;
if (!user.IsBot
&& logSetting.LogUserPresenceId is not null
&& (logChannel =
await TryGetLogChannel(gu.Guild, logSetting, LogType.UserPresence)) is not null)
{
if (before.Status != after.Status)
{
var str = "🎭"
+ Format.Code(PrettyCurrentTime(gu.Guild))
+ GetText(logChannel.Guild,
strs.user_status_change("👤" + Format.Bold(gu.Username),
Format.Bold(after.Status.ToString())));
PresenceUpdates.AddOrUpdate(logChannel,
new List<string>
{
str
},
(_, list) =>
{
list.Add(str);
return list;
});
}
else if (before.Activities.FirstOrDefault()?.Name != after.Activities.FirstOrDefault()?.Name)
{
var str =
$"👾`{PrettyCurrentTime(gu.Guild)}`👤__**{gu.Username}**__ is now playing **{after.Activities.FirstOrDefault()?.Name ?? "-"}**.";
PresenceUpdates.AddOrUpdate(logChannel,
new List<string>
{
str
},
(_, list) =>
{
list.Add(str);
return list;
});
}
}
}
private Task _client_ThreadDeleted(Cacheable<SocketThreadChannel, ulong> sch)
{
_ = Task.Run(async () =>
{
try
{
if (sch.HasValue || sch.Value is not IGuildChannel ch)
return;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out var logSetting)
|| logSetting.ThreadDeletedId is null)
return;
ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ThreadDeleted)) is null)
return;
var title = GetText(logChannel.Guild, strs.thread_deleted);
await logChannel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
}
catch (Exception)
{
// ignored
}
});
return Task.CompletedTask;
}
private Task _client_ThreadCreated(SocketThreadChannel sch)
{
_ = Task.Run(async () =>
{
try
{
if (sch.Guild is not IGuildChannel ch)
return;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out var logSetting)
|| logSetting.ThreadCreatedId is null)
return;
ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ThreadCreated)) is null)
return;
var title = GetText(logChannel.Guild, strs.thread_created);
await logChannel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild)));
}
catch (Exception)
{
// ignored
}
});
return Task.CompletedTask;
}
public async Task OnReadyAsync() public async Task OnReadyAsync()
=> await Task.WhenAll(PresenceUpdateTask(), IgnoreMessageIdsClearTask()); => await Task.WhenAll(PresenceUpdateTask(), IgnoreMessageIdsClearTask());
@@ -107,22 +231,24 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
var keys = PresenceUpdates.Keys.ToList(); var keys = PresenceUpdates.Keys.ToList();
await keys.Select(key => await keys.Select(key =>
{ {
if (!((SocketGuild)key.Guild).CurrentUser.GetPermissions(key).SendMessages) if (!((SocketGuild)key.Guild).CurrentUser.GetPermissions(key).SendMessages)
return Task.CompletedTask; return Task.CompletedTask;
if (PresenceUpdates.TryRemove(key, out var msgs)) if (PresenceUpdates.TryRemove(key, out var msgs))
{ {
var title = GetText(key.Guild, strs.presence_updates); var title = GetText(key.Guild, strs.presence_updates);
var desc = string.Join(Environment.NewLine, msgs); var desc = string.Join(Environment.NewLine, msgs);
return key.SendConfirmAsync(_eb, title, desc.TrimTo(2048)!); return key.SendConfirmAsync(_eb, title, desc.TrimTo(2048)!);
} }
return Task.CompletedTask; return Task.CompletedTask;
}) })
.WhenAll(); .WhenAll();
}
catch
{
} }
catch { }
} }
} }
@@ -185,13 +311,13 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
logSetting.UserLeftId = logSetting.UserBannedId = logSetting.UserUnbannedId = logSetting.UserUpdatedId = logSetting.UserLeftId = logSetting.UserBannedId = logSetting.UserUnbannedId = logSetting.UserUpdatedId =
logSetting.ChannelCreatedId = logSetting.ChannelDestroyedId = logSetting.ChannelUpdatedId = logSetting.ChannelCreatedId = logSetting.ChannelDestroyedId = logSetting.ChannelUpdatedId =
logSetting.LogUserPresenceId = logSetting.LogVoicePresenceId = logSetting.UserMutedId = logSetting.LogUserPresenceId = logSetting.LogVoicePresenceId = logSetting.UserMutedId =
logSetting.LogVoicePresenceTTSId = value ? channelId : null; logSetting.LogVoicePresenceTTSId = logSetting.ThreadCreatedId = logSetting.ThreadDeletedId
= logSetting.LogWarnsId = value ? channelId : null;
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
GuildLogSettings.AddOrUpdate(guildId, _ => logSetting, (_, _) => logSetting); GuildLogSettings.AddOrUpdate(guildId, _ => logSetting, (_, _) => logSetting);
} }
private async Task PunishServiceOnOnUserWarned(Warning arg) private async Task PunishServiceOnOnUserWarned(Warning arg)
{ {
if (!GuildLogSettings.TryGetValue(arg.GuildId, out var logSetting) || logSetting.LogWarnsId is null) if (!GuildLogSettings.TryGetValue(arg.GuildId, out var logSetting) || logSetting.LogWarnsId is null)
@@ -204,12 +330,12 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
return; return;
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle($"⚠️ User Warned") .WithTitle($"⚠️ User Warned")
.WithDescription($"<@{arg.UserId}> | {arg.UserId}") .WithDescription($"<@{arg.UserId}> | {arg.UserId}")
.AddField("Mod", arg.Moderator) .AddField("Mod", arg.Moderator)
.AddField("Reason", string.IsNullOrWhiteSpace(arg.Reason) ? "-" : arg.Reason, true) .AddField("Reason", string.IsNullOrWhiteSpace(arg.Reason) ? "-" : arg.Reason, true)
.WithFooter(CurrentTime(g)); .WithFooter(CurrentTime(g));
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }
@@ -237,18 +363,18 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if (before.Username != after.Username) if (before.Username != after.Username)
{ {
embed.WithTitle("👥 " + GetText(g, strs.username_changed)) embed.WithTitle("👥 " + GetText(g, strs.username_changed))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.AddField("Old Name", $"{before.Username}", true) .AddField("Old Name", $"{before.Username}", true)
.AddField("New Name", $"{after.Username}", true) .AddField("New Name", $"{after.Username}", true)
.WithFooter(CurrentTime(g)) .WithFooter(CurrentTime(g))
.WithOkColor(); .WithOkColor();
} }
else if (before.AvatarId != after.AvatarId) else if (before.AvatarId != after.AvatarId)
{ {
embed.WithTitle("👥" + GetText(g, strs.avatar_changed)) embed.WithTitle("👥" + GetText(g, strs.avatar_changed))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithFooter(CurrentTime(g)) .WithFooter(CurrentTime(g))
.WithOkColor(); .WithOkColor();
var bav = before.RealAvatarUrl(); var bav = before.RealAvatarUrl();
if (bav.IsAbsoluteUri) if (bav.IsAbsoluteUri)
@@ -412,10 +538,10 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
} }
var embed = _eb.Create() var embed = _eb.Create()
.WithAuthor(mutes) .WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter(CurrentTime(usr.Guild)) .WithFooter(CurrentTime(usr.Guild))
.WithOkColor(); .WithOkColor();
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }
@@ -459,10 +585,10 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
} }
var embed = _eb.Create() var embed = _eb.Create()
.WithAuthor(mutes) .WithAuthor(mutes)
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter($"{CurrentTime(usr.Guild)}") .WithFooter($"{CurrentTime(usr.Guild)}")
.WithOkColor(); .WithOkColor();
if (!string.IsNullOrWhiteSpace(reason)) if (!string.IsNullOrWhiteSpace(reason))
embed.WithDescription(reason); embed.WithDescription(reason);
@@ -513,11 +639,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
} }
var embed = _eb.Create() var embed = _eb.Create()
.WithAuthor($"🛡 Anti-{protection}") .WithAuthor($"🛡 Anti-{protection}")
.WithTitle(GetText(logChannel.Guild, strs.users) + " " + punishment) .WithTitle(GetText(logChannel.Guild, strs.users) + " " + punishment)
.WithDescription(string.Join("\n", users.Select(u => u.ToString()))) .WithDescription(string.Join("\n", users.Select(u => u.ToString())))
.WithFooter(CurrentTime(logChannel.Guild)) .WithFooter(CurrentTime(logChannel.Guild))
.WithOkColor(); .WithOkColor();
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }
@@ -566,16 +692,16 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
&& (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) is not null) && (logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) is not null)
{ {
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithFooter(CurrentTime(before.Guild)) .WithFooter(CurrentTime(before.Guild))
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
if (before.Nickname != after.Nickname) if (before.Nickname != after.Nickname)
{ {
embed.WithAuthor("👥 " + GetText(logChannel.Guild, strs.nick_change)) embed.WithAuthor("👥 " + GetText(logChannel.Guild, strs.nick_change))
.AddField(GetText(logChannel.Guild, strs.old_nick), .AddField(GetText(logChannel.Guild, strs.old_nick),
$"{before.Nickname}#{before.Discriminator}") $"{before.Nickname}#{before.Discriminator}")
.AddField(GetText(logChannel.Guild, strs.new_nick), .AddField(GetText(logChannel.Guild, strs.new_nick),
$"{after.Nickname}#{after.Discriminator}"); $"{after.Nickname}#{after.Discriminator}");
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }
@@ -585,7 +711,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
{ {
var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => r.Name); var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => r.Name);
embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_add)) embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_add))
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }
@@ -593,59 +719,19 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
{ {
await Task.Delay(1000); await Task.Delay(1000);
var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r) && !IsRoleDeleted(r.Id)) var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r) && !IsRoleDeleted(r.Id))
.Select(r => r.Name) .Select(r => r.Name)
.ToList(); .ToList();
if (diffRoles.Any()) if (diffRoles.Any())
{ {
embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_rem)) embed.WithAuthor("⚔ " + GetText(logChannel.Guild, strs.user_role_rem))
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }
} }
} }
} }
if (!before.IsBot
&& logSetting.LogUserPresenceId is not null
&& (logChannel =
await TryGetLogChannel(before.Guild, logSetting, LogType.UserPresence)) is not null)
{
if (before.Status != after.Status)
{
var str = "🎭"
+ Format.Code(PrettyCurrentTime(after.Guild))
+ GetText(logChannel.Guild,
strs.user_status_change("👤" + Format.Bold(after.Username),
Format.Bold(after.Status.ToString())));
PresenceUpdates.AddOrUpdate(logChannel,
new List<string>
{
str
},
(_, list) =>
{
list.Add(str);
return list;
});
}
else if (before.Activities.FirstOrDefault()?.Name != after.Activities.FirstOrDefault()?.Name)
{
var str =
$"👾`{PrettyCurrentTime(after.Guild)}`👤__**{after.Username}**__ is now playing **{after.Activities.FirstOrDefault()?.Name ?? "-"}**.";
PresenceUpdates.AddOrUpdate(logChannel,
new List<string>
{
str
},
(_, list) =>
{
list.Add(str);
return list;
});
}
}
} }
catch catch
{ {
@@ -684,15 +770,15 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if (before.Name != after.Name) if (before.Name != after.Name)
{ {
embed.WithTitle(" " + GetText(logChannel.Guild, strs.ch_name_change)) embed.WithTitle(" " + GetText(logChannel.Guild, strs.ch_name_change))
.WithDescription($"{after} | {after.Id}") .WithDescription($"{after} | {after.Id}")
.AddField(GetText(logChannel.Guild, strs.ch_old_name), before.Name); .AddField(GetText(logChannel.Guild, strs.ch_old_name), before.Name);
} }
else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
{ {
embed.WithTitle(" " + GetText(logChannel.Guild, strs.ch_topic_change)) embed.WithTitle(" " + GetText(logChannel.Guild, strs.ch_topic_change))
.WithDescription($"{after} | {after.Id}") .WithDescription($"{after} | {after.Id}")
.AddField(GetText(logChannel.Guild, strs.old_topic), beforeTextChannel?.Topic ?? "-") .AddField(GetText(logChannel.Guild, strs.old_topic), beforeTextChannel?.Topic ?? "-")
.AddField(GetText(logChannel.Guild, strs.new_topic), afterTextChannel?.Topic ?? "-"); .AddField(GetText(logChannel.Guild, strs.new_topic), afterTextChannel?.Topic ?? "-");
} }
else else
return; return;
@@ -725,6 +811,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
ITextChannel? logChannel; ITextChannel? logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) is null) if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) is null)
return; return;
string title; string title;
if (ch is IVoiceChannel) if (ch is IVoiceChannel)
title = GetText(logChannel.Guild, strs.voice_chan_destroyed); title = GetText(logChannel.Guild, strs.voice_chan_destroyed);
@@ -732,10 +819,10 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
title = GetText(logChannel.Guild, strs.text_chan_destroyed); title = GetText(logChannel.Guild, strs.text_chan_destroyed);
await logChannel.EmbedAsync(_eb.Create() await logChannel.EmbedAsync(_eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + title) .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild))); .WithFooter(CurrentTime(ch.Guild)));
} }
catch catch
{ {
@@ -768,10 +855,10 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
title = GetText(logChannel.Guild, strs.text_chan_created); title = GetText(logChannel.Guild, strs.text_chan_created);
await logChannel.EmbedAsync(_eb.Create() await logChannel.EmbedAsync(_eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + title) .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(CurrentTime(ch.Guild))); .WithFooter(CurrentTime(ch.Guild)));
} }
catch (Exception) catch (Exception)
{ {
@@ -871,11 +958,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserLeft)) is null) if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserLeft)) is null)
return; return;
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("❌ " + GetText(logChannel.Guild, strs.user_left)) .WithTitle("❌ " + GetText(logChannel.Guild, strs.user_left))
.WithDescription(usr.ToString()) .WithDescription(usr.ToString())
.AddField("Id", usr.Id.ToString()) .AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild)); .WithFooter(CurrentTime(guild));
if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute)) if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
embed.WithThumbnailUrl(usr.GetAvatarUrl()); embed.WithThumbnailUrl(usr.GetAvatarUrl());
@@ -904,17 +991,17 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
return; return;
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("✅ " + GetText(logChannel.Guild, strs.user_joined)) .WithTitle("✅ " + GetText(logChannel.Guild, strs.user_joined))
.WithDescription($"{usr.Mention} `{usr}`") .WithDescription($"{usr.Mention} `{usr}`")
.AddField("Id", usr.Id.ToString()) .AddField("Id", usr.Id.ToString())
.AddField(GetText(logChannel.Guild, strs.joined_server), .AddField(GetText(logChannel.Guild, strs.joined_server),
$"{usr.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}", $"{usr.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}",
true) true)
.AddField(GetText(logChannel.Guild, strs.joined_discord), .AddField(GetText(logChannel.Guild, strs.joined_discord),
$"{usr.CreatedAt:dd.MM.yyyy HH:mm}", $"{usr.CreatedAt:dd.MM.yyyy HH:mm}",
true) true)
.WithFooter(CurrentTime(usr.Guild)); .WithFooter(CurrentTime(usr.Guild));
if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute)) if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
embed.WithThumbnailUrl(usr.GetAvatarUrl()); embed.WithThumbnailUrl(usr.GetAvatarUrl());
@@ -945,11 +1032,11 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) is null) if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) is null)
return; return;
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("♻️ " + GetText(logChannel.Guild, strs.user_unbanned)) .WithTitle("♻️ " + GetText(logChannel.Guild, strs.user_unbanned))
.WithDescription(usr.ToString()!) .WithDescription(usr.ToString()!)
.AddField("Id", usr.Id.ToString()) .AddField("Id", usr.Id.ToString())
.WithFooter(CurrentTime(guild)); .WithFooter(CurrentTime(guild));
if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute)) if (Uri.IsWellFormedUriString(usr.GetAvatarUrl(), UriKind.Absolute))
embed.WithThumbnailUrl(usr.GetAvatarUrl()); embed.WithThumbnailUrl(usr.GetAvatarUrl());
@@ -989,16 +1076,15 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
} }
catch catch
{ {
} }
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned)) .WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned))
.WithDescription(usr.ToString()!) .WithDescription(usr.ToString()!)
.AddField("Id", usr.Id.ToString()) .AddField("Id", usr.Id.ToString())
.AddField("Reason", string.IsNullOrWhiteSpace(reason) ? "-" : reason) .AddField("Reason", string.IsNullOrWhiteSpace(reason) ? "-" : reason)
.WithFooter(CurrentTime(guild)); .WithFooter(CurrentTime(guild));
var avatarUrl = usr.GetAvatarUrl(); var avatarUrl = usr.GetAvatarUrl();
@@ -1044,14 +1130,14 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
var resolvedMessage = msg.Resolve(TagHandling.FullName); var resolvedMessage = msg.Resolve(TagHandling.FullName);
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("🗑 " .WithTitle("🗑 "
+ GetText(logChannel.Guild, strs.msg_del(((ITextChannel)msg.Channel).Name))) + GetText(logChannel.Guild, strs.msg_del(((ITextChannel)msg.Channel).Name)))
.WithDescription(msg.Author.ToString()!) .WithDescription(msg.Author.ToString()!)
.AddField(GetText(logChannel.Guild, strs.content), .AddField(GetText(logChannel.Guild, strs.content),
string.IsNullOrWhiteSpace(resolvedMessage) ? "-" : resolvedMessage) string.IsNullOrWhiteSpace(resolvedMessage) ? "-" : resolvedMessage)
.AddField("Id", msg.Id.ToString()) .AddField("Id", msg.Id.ToString())
.WithFooter(CurrentTime(channel.Guild)); .WithFooter(CurrentTime(channel.Guild));
if (msg.Attachments.Any()) if (msg.Attachments.Any())
{ {
embed.AddField(GetText(logChannel.Guild, strs.attachments), embed.AddField(GetText(logChannel.Guild, strs.attachments),
@@ -1104,19 +1190,19 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
return; return;
var embed = _eb.Create() var embed = _eb.Create()
.WithOkColor() .WithOkColor()
.WithTitle("📝 " .WithTitle("📝 "
+ GetText(logChannel.Guild, + GetText(logChannel.Guild,
strs.msg_update(((ITextChannel)after.Channel).Name))) strs.msg_update(((ITextChannel)after.Channel).Name)))
.WithDescription(after.Author.ToString()!) .WithDescription(after.Author.ToString()!)
.AddField(GetText(logChannel.Guild, strs.old_msg), .AddField(GetText(logChannel.Guild, strs.old_msg),
string.IsNullOrWhiteSpace(before.Content) string.IsNullOrWhiteSpace(before.Content)
? "-" ? "-"
: before.Resolve(TagHandling.FullName)) : before.Resolve(TagHandling.FullName))
.AddField(GetText(logChannel.Guild, strs.new_msg), .AddField(GetText(logChannel.Guild, strs.new_msg),
string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(TagHandling.FullName)) string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(TagHandling.FullName))
.AddField("Id", after.Id.ToString()) .AddField("Id", after.Id.ToString())
.WithFooter(CurrentTime(channel.Guild)); .WithFooter(CurrentTime(channel.Guild));
await logChannel.EmbedAsync(embed); await logChannel.EmbedAsync(embed);
} }

View File

@@ -402,12 +402,21 @@ public partial class Administration
[UserPerm(GuildPerm.BanMembers)] [UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)] [BotPerm(GuildPerm.BanMembers)]
[Priority(1)] [Priority(1)]
public async Task Ban(StoopidTime time, IUser user, [Leftover] string msg = null) public Task Ban(StoopidTime time, IUser user, [Leftover] string msg = null)
=> Ban(time, user.Id, msg);
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
[BotPerm(GuildPerm.BanMembers)]
[Priority(0)]
public async Task Ban(StoopidTime time, ulong userId, [Leftover] string msg = null)
{ {
if (time.Time > TimeSpan.FromDays(49)) if (time.Time > TimeSpan.FromDays(49))
return; return;
var guildUser = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, user.Id); var guildUser = await ((DiscordSocketClient)Context.Client).Rest.GetGuildUserAsync(ctx.Guild.Id, userId);
if (guildUser is not null && !await CheckRoleHierarchy(guildUser)) if (guildUser is not null && !await CheckRoleHierarchy(guildUser))
return; return;
@@ -429,13 +438,14 @@ public partial class Administration
} }
} }
var user = await ctx.Client.GetUserAsync(userId);
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await _mute.TimedBan(ctx.Guild, user, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune); await _mute.TimedBan(ctx.Guild, userId, 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))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
.AddField("ID", user.Id.ToString(), true) .AddField("ID", userId.ToString(), true)
.AddField(GetText(strs.duration), .AddField(GetText(strs.duration),
time.Time.Humanize(3, minUnit: TimeUnit.Minute, culture: Culture), time.Time.Humanize(3, minUnit: TimeUnit.Minute, culture: Culture),
true); true);

View File

@@ -157,7 +157,7 @@ public class UserPunishService : INService, IReadyExecutor
if (minutes == 0) if (minutes == 0)
await guild.AddBanAsync(user, reason: reason, pruneDays: banPrune); await guild.AddBanAsync(user, reason: reason, pruneDays: banPrune);
else else
await _mute.TimedBan(user.Guild, user, TimeSpan.FromMinutes(minutes), reason, banPrune); await _mute.TimedBan(user.Guild, user.Id, TimeSpan.FromMinutes(minutes), reason, banPrune);
break; break;
case PunishmentAction.Softban: case PunishmentAction.Softban:
banPrune = await GetBanPruneAsync(user.GuildId) ?? 7; banPrune = await GetBanPruneAsync(user.GuildId) ?? 7;

View File

@@ -34,14 +34,14 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
"; ";
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(CamelCaseNamingConvention.Instance) .WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithIndentedSequences() .WithIndentedSequences()
.ConfigureDefaultValuesHandling(DefaultValuesHandling .ConfigureDefaultValuesHandling(DefaultValuesHandling
.OmitDefaults) .OmitDefaults)
.DisableAliases() .DisableAliases()
.Build(); .Build();
public int Priority public int Priority
=> 0; => 0;
@@ -57,8 +57,8 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
// 1. expressions are almost never added (compared to how many times they are being looped through) // 1. expressions are almost never added (compared to how many times they are being looped through)
// 2. only need write locks for this as we'll rebuild+replace the array on every edit // 2. only need write locks for this as we'll rebuild+replace the array on every edit
// 3. there's never many of them (at most a thousand, usually < 100) // 3. there's never many of them (at most a thousand, usually < 100)
private NadekoExpression[] globalExpressions; private NadekoExpression[] globalExpressions = Array.Empty<NadekoExpression>();
private ConcurrentDictionary<ulong, NadekoExpression[]> newguildExpressions; private ConcurrentDictionary<ulong, NadekoExpression[]> newguildExpressions = new();
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
@@ -112,20 +112,20 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var guildItems = await uow.Expressions.AsNoTracking() var guildItems = await uow.Expressions.AsNoTracking()
.Where(x => allGuildIds.Contains(x.GuildId.Value)) .Where(x => allGuildIds.Contains(x.GuildId.Value))
.ToListAsync(); .ToListAsync();
newguildExpressions = guildItems.GroupBy(k => k.GuildId!.Value) newguildExpressions = guildItems.GroupBy(k => k.GuildId!.Value)
.ToDictionary(g => g.Key, .ToDictionary(g => g.Key,
g => g.Select(x => g => g.Select(x =>
{ {
x.Trigger = x.Trigger.Replace(MENTION_PH, _bot.Mention); x.Trigger = x.Trigger.Replace(MENTION_PH, _bot.Mention);
return x; return x;
}) })
.ToArray()) .ToArray())
.ToConcurrent(); .ToConcurrent();
_disabledGlobalExpressionGuilds = new (await uow.GuildConfigs _disabledGlobalExpressionGuilds = new(await uow.GuildConfigs
.Where(x => x.DisableGlobalExpressions) .Where(x => x.DisableGlobalExpressions)
.Select(x => x.GuildId) .Select(x => x.GuildId)
.ToListAsyncLinqToDB()); .ToListAsyncLinqToDB());
@@ -133,14 +133,14 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
lock (_gexprWriteLock) lock (_gexprWriteLock)
{ {
var globalItems = uow.Expressions.AsNoTracking() var globalItems = uow.Expressions.AsNoTracking()
.Where(x => x.GuildId == null || x.GuildId == 0) .Where(x => x.GuildId == null || x.GuildId == 0)
.AsEnumerable() .AsEnumerable()
.Select(x => .Select(x =>
{ {
x.Trigger = x.Trigger.Replace(MENTION_PH, _bot.Mention); x.Trigger = x.Trigger.Replace(MENTION_PH, _bot.Mention);
return x; return x;
}) })
.ToArray(); .ToArray();
globalExpressions = globalItems; globalExpressions = globalItems;
} }
@@ -509,9 +509,25 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
public bool ExpressionExists(ulong? guildId, string input) public bool ExpressionExists(ulong? guildId, string input)
{ {
using var uow = _db.GetDbContext(); input = input.ToLowerInvariant();
var expr = uow.Expressions.GetByGuildIdAndInput(guildId, input);
return expr is not null; var gexprs = globalExpressions;
foreach (var t in gexprs)
{
if (t.Trigger == input)
return true;
}
if (guildId is ulong gid && newguildExpressions.TryGetValue(gid, out var guildExprs))
{
foreach (var t in guildExprs)
{
if (t.Trigger == input)
return true;
}
}
return false;
} }
public string ExportExpressions(ulong? guildId) public string ExportExpressions(ulong? guildId)
@@ -542,17 +558,17 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
{ {
var trigger = entry.Key; var trigger = entry.Key;
await uow.Expressions.AddRangeAsync(entry.Value.Where(expr => !string.IsNullOrWhiteSpace(expr.Res)) await uow.Expressions.AddRangeAsync(entry.Value.Where(expr => !string.IsNullOrWhiteSpace(expr.Res))
.Select(expr => new NadekoExpression .Select(expr => new NadekoExpression
{ {
GuildId = guildId, GuildId = guildId,
Response = expr.Res, Response = expr.Res,
Reactions = expr.React?.Join("@@@"), Reactions = expr.React?.Join("@@@"),
Trigger = trigger, Trigger = trigger,
AllowTarget = expr.At, AllowTarget = expr.At,
ContainsAnywhere = expr.Ca, ContainsAnywhere = expr.Ca,
DmResponse = expr.Dm, DmResponse = expr.Dm,
AutoDeleteTrigger = expr.Ad AutoDeleteTrigger = expr.Ad
})); }));
} }
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();

View File

@@ -77,28 +77,24 @@ public partial class Gambling
{ {
if (await _bank.TakeAsync(userId, amount)) if (await _bank.TakeAsync(userId, amount))
{ {
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount), await ctx.OkAsync();
_client.GetUser(userId)?.ToString()
?? userId.ToString(),
CurrencySign));
return; return;
} }
await ctx.OkAsync(); await ReplyErrorLocalizedAsync(strs.take_fail(N(amount),
_client.GetUser(userId)?.ToString()
?? userId.ToString(),
CurrencySign));
} }
private async Task BankAwardInternalAsync(long amount, ulong userId) private async Task BankAwardInternalAsync(long amount, ulong userId)
{ {
if (await _bank.AwardAsync(userId, amount)) if (await _bank.AwardAsync(userId, amount))
{ {
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount), await ctx.OkAsync();
_client.GetUser(userId)?.ToString()
?? userId.ToString(),
CurrencySign));
return; return;
} }
await ctx.OkAsync();
} }
[Cmd] [Cmd]

View File

@@ -88,10 +88,6 @@ public class GameStatusEvent : ICurrencyEvent
await msg.ModifyAsync(m => await msg.ModifyAsync(m =>
{ {
m.Embed = GetEmbed(PotSize).Build(); m.Embed = GetEmbed(PotSize).Build();
},
new()
{
RetryMode = RetryMode.AlwaysRetry
}); });
} }

View File

@@ -79,10 +79,6 @@ public class ReactionEvent : ICurrencyEvent
await msg.ModifyAsync(m => await msg.ModifyAsync(m =>
{ {
m.Embed = GetEmbed(PotSize).Build(); m.Embed = GetEmbed(PotSize).Build();
},
new()
{
RetryMode = RetryMode.AlwaysRetry
}); });
} }

View File

@@ -840,7 +840,7 @@ public partial class Gambling : GamblingModule<GamblingService>
else if (result.Result == RpsResultType.Win) else if (result.Result == RpsResultType.Win)
{ {
if ((long)result.Won > 0) if ((long)result.Won > 0)
embed.AddField(GetText(strs.won), N(amount.Value)); embed.AddField(GetText(strs.won), N((long)result.Won));
msg = GetText(strs.rps_win(ctx.User.Mention, msg = GetText(strs.rps_win(ctx.User.Mention,
GetRpsPick(pick), GetRpsPick(pick),

View File

@@ -244,19 +244,28 @@ public partial class Gambling
var waifuItems = _service.GetWaifuItems().ToDictionary(x => x.ItemEmoji, x => x); var waifuItems = _service.GetWaifuItems().ToDictionary(x => x.ItemEmoji, x => x);
var nobody = GetText(strs.nobody); var nobody = GetText(strs.nobody);
var itemsStr = !wi.Items.Any() var itemList = await _service.GetItems(wi.WaifuId);
var itemsStr = !itemList.Any()
? "-" ? "-"
: string.Join("\n", : string.Join("\n",
wi.Items.Where(x => waifuItems.TryGetValue(x.ItemEmoji, out _)) itemList.Where(x => waifuItems.TryGetValue(x.ItemEmoji, out _))
.OrderBy(x => waifuItems[x.ItemEmoji].Price) .OrderBy(x => waifuItems[x.ItemEmoji].Price)
.GroupBy(x => x.ItemEmoji) .GroupBy(x => x.ItemEmoji)
.Select(x => $"{x.Key} x{x.Count(),-3}") .Select(x => $"{x.Key} x{x.Count(),-3}")
.Chunk(2) .Chunk(2)
.Select(x => string.Join(" ", x))); .Select(x => string.Join(" ", x)));
var claimsNames = (await _service.GetClaimNames(wi.WaifuId));
var claimsStr = claimsNames
.Shuffle()
.Take(30)
.Join('\n');
var fansList = await _service.GetFansNames(wi.WaifuId);
var fansStr = fansList
.Select((x) => claimsNames.Contains(x) ? $"{x} 💞" : x).Join('\n');
var fansStr = wi.Fans.Shuffle().Take(30).Select(x => wi.Claims.Contains(x) ? $"{x} 💞" : x).Join('\n');
if (string.IsNullOrWhiteSpace(fansStr)) if (string.IsNullOrWhiteSpace(fansStr))
fansStr = "-"; fansStr = "-";
@@ -275,9 +284,9 @@ public partial class Gambling
.AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true) .AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true)
.AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true) .AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true)
.AddField("\u200B", "\u200B", true) .AddField("\u200B", "\u200B", true)
.AddField(GetText(strs.fans(wi.Fans.Count)), fansStr, true) .AddField(GetText(strs.fans(fansList.Count)), fansStr, true)
.AddField($"Waifus ({wi.ClaimCount})", .AddField($"Waifus ({wi.ClaimCount})",
wi.ClaimCount == 0 ? nobody : string.Join("\n", wi.Claims.Shuffle().Take(30)), wi.ClaimCount == 0 ? nobody : claimsStr,
true) true)
.AddField(GetText(strs.gifts), itemsStr, true); .AddField(GetText(strs.gifts), itemsStr, true);

View File

@@ -414,11 +414,8 @@ public class WaifuService : INService, IReadyExecutor
AffinityName = null, AffinityName = null,
ClaimCount = 0, ClaimCount = 0,
ClaimerName = null, ClaimerName = null,
Claims = new(),
Fans = new(),
DivorceCount = 0, DivorceCount = 0,
FullName = null, FullName = null,
Items = new(),
Price = 1 Price = 1
}; };
} }
@@ -426,14 +423,6 @@ public class WaifuService : INService, IReadyExecutor
return wi; return wi;
} }
public async Task<WaifuInfoStats> GetFullWaifuInfoAsync(IGuildUser target)
{
await using var uow = _db.GetDbContext();
_ = uow.GetOrCreateUser(target);
return await GetFullWaifuInfoAsync(target.Id);
}
public string GetClaimTitle(int count) public string GetClaimTitle(int count)
{ {
ClaimTitle title; ClaimTitle title;
@@ -557,4 +546,38 @@ public class WaifuService : INService, IReadyExecutor
} }
} }
} }
public async Task<IReadOnlyCollection<string>> GetClaimNames(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<DiscordUser>()
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.ClaimerId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
}
public async Task<IReadOnlyCollection<string>> GetFansNames(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<DiscordUser>()
.Where(x => ctx.GetTable<WaifuInfo>()
.Where(wi => wi.AffinityId == waifuId)
.Select(wi => wi.WaifuId)
.Contains(x.Id))
.Select(x => $"{x.Username}#{x.Discriminator}")
.ToListAsyncEF();
}
public async Task<IReadOnlyCollection<WaifuItem>> GetItems(int waifuId)
{
await using var ctx = _db.GetDbContext();
return await ctx.GetTable<WaifuItem>()
.Where(x => x.WaifuInfoId == ctx.GetTable<WaifuInfo>()
.Where(x => x.WaifuId == waifuId)
.Select(x => x.Id)
.FirstOrDefault())
.ToListAsyncEF();
}
} }

View File

@@ -1,6 +1,7 @@
#nullable disable #nullable disable
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models; using NadekoBot.Db.Models;
using NadekoBot.Modules.Games.Common;
using NadekoBot.Modules.Games.Common.ChatterBot; using NadekoBot.Modules.Games.Common.ChatterBot;
using NadekoBot.Modules.Permissions; using NadekoBot.Modules.Permissions;
using NadekoBot.Modules.Permissions.Common; using NadekoBot.Modules.Permissions.Common;
@@ -27,6 +28,7 @@ public class ChatterBotService : IExecOnMessage
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly IPatronageService _ps; private readonly IPatronageService _ps;
private readonly CmdCdService _ccs; private readonly CmdCdService _ccs;
private readonly GamesConfigService _gcs;
public ChatterBotService( public ChatterBotService(
DiscordSocketClient client, DiscordSocketClient client,
@@ -38,7 +40,8 @@ public class ChatterBotService : IExecOnMessage
IBotCredentials creds, IBotCredentials creds,
IEmbedBuilderService eb, IEmbedBuilderService eb,
IPatronageService ps, IPatronageService ps,
CmdCdService cmdCdService) CmdCdService cmdCdService,
GamesConfigService gcs)
{ {
_client = client; _client = client;
_perms = perms; _perms = perms;
@@ -49,6 +52,7 @@ public class ChatterBotService : IExecOnMessage
_httpFactory = factory; _httpFactory = factory;
_ps = ps; _ps = ps;
_ccs = cmdCdService; _ccs = cmdCdService;
_gcs = gcs;
_flKey = new FeatureLimitKey() _flKey = new FeatureLimitKey()
{ {
@@ -64,11 +68,26 @@ public class ChatterBotService : IExecOnMessage
public IChatterBotSession CreateSession() public IChatterBotSession CreateSession()
{ {
if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey)) switch (_gcs.Data.ChatBot)
return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory); {
case ChatBotImplementation.Cleverbot:
if (!string.IsNullOrWhiteSpace(_creds.CleverbotApiKey))
return new OfficialCleverbotSession(_creds.CleverbotApiKey, _httpFactory);
Log.Information("Cleverbot will not work as the api key is missing."); Log.Information("Cleverbot will not work as the api key is missing.");
return null; return null;
case ChatBotImplementation.Gpt3:
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
return new OfficialGpt3Session(_creds.Gpt3ApiKey,
_gcs.Data.ChatGpt.Model,
_gcs.Data.ChatGpt.MaxTokens,
_httpFactory);
Log.Information("Gpt3 will not work as the api key is missing.");
return null;
default:
return null;
}
} }
public string PrepareMessage(IUserMessage msg, out IChatterBotSession cleverbot) public string PrepareMessage(IUserMessage msg, out IChatterBotSession cleverbot)
@@ -185,7 +204,7 @@ public class ChatterBotService : IExecOnMessage
title: null, title: null,
response.SanitizeMentions(true) response.SanitizeMentions(true)
// , footer: counter > 0 ? counter.ToString() : null // , footer: counter > 0 ? counter.ToString() : null
); );
Log.Information(@"CleverBot Executed Log.Information(@"CleverBot Executed
Server: {GuildName} [{GuildId}] Server: {GuildName} [{GuildId}]

View File

@@ -1,8 +0,0 @@
#nullable disable
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class ChatterBotResponse
{
public string Convo_id { get; set; }
public string BotSay { get; set; }
}

View File

@@ -0,0 +1,30 @@
#nullable disable
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class Gpt3Response
{
[JsonPropertyName("choices")]
public Choice[] Choices { get; set; }
}
public class Choice
{
public string Text { get; set; }
}
public class Gpt3ApiRequest
{
[JsonPropertyName("model")]
public string Model { get; init; }
[JsonPropertyName("prompt")]
public string Prompt { get; init; }
[JsonPropertyName("temperature")]
public int Temperature { get; init; }
[JsonPropertyName("max_tokens")]
public int MaxTokens { get; init; }
}

View File

@@ -0,0 +1,69 @@
#nullable disable
using Newtonsoft.Json;
using System.Net.Http.Json;
namespace NadekoBot.Modules.Games.Common.ChatterBot;
public class OfficialGpt3Session : IChatterBotSession
{
private string Uri
=> $"https://api.openai.com/v1/completions";
private readonly string _apiKey;
private readonly string _model;
private readonly int _maxTokens;
private readonly IHttpClientFactory _httpFactory;
public OfficialGpt3Session(
string apiKey,
Gpt3Model model,
int maxTokens,
IHttpClientFactory factory)
{
_apiKey = apiKey;
_httpFactory = factory;
switch (model)
{
case Gpt3Model.Ada001:
_model = "text-ada-001";
break;
case Gpt3Model.Babbage001:
_model = "text-babbage-001";
break;
case Gpt3Model.Curie001:
_model = "text-curie-001";
break;
case Gpt3Model.Davinci003:
_model = "text-davinci-003";
break;
}
_maxTokens = maxTokens;
}
public async Task<string> Think(string input)
{
using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
var data = await http.PostAsJsonAsync(Uri, new Gpt3ApiRequest()
{
Model = _model,
Prompt = input,
MaxTokens = _maxTokens,
Temperature = 1,
});
var dataString = await data.Content.ReadAsStringAsync();
try
{
var response = JsonConvert.DeserializeObject<Gpt3Response>(dataString);
return response?.Choices[0]?.Text;
}
catch
{
Log.Warning("Unexpected GPT-3 response received: {ResponseString}", dataString);
return null;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Games.Common;
public sealed partial class GamesConfig : ICloneable<GamesConfig> public sealed partial class GamesConfig : ICloneable<GamesConfig>
{ {
[Comment("DO NOT CHANGE")] [Comment("DO NOT CHANGE")]
public int Version { get; set; } public int Version { get; set; } = 2;
[Comment("Hangman related settings (.hangman command)")] [Comment("Hangman related settings (.hangman command)")]
public HangmanConfig Hangman { get; set; } = new() public HangmanConfig Hangman { get; set; } = new()
@@ -95,6 +95,27 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
Name = "Unicorn" Name = "Unicorn"
} }
}; };
[Comment(@"Which chatbot API should bot use.
'cleverbot' - bot will use Cleverbot API.
'gpt3' - bot will use GPT-3 API")]
public ChatBotImplementation ChatBot { get; set; } = ChatBotImplementation.Gpt3;
public ChatGptConfig ChatGpt { get; set; } = new();
}
[Cloneable]
public sealed partial class ChatGptConfig
{
[Comment(@"Which GPT-3 Model should bot use.
'ada001' - cheapest and fastest
'babbage001' - 2nd option
'curie001' - 3rd option
'davinci003' - Most expensive, slowest")]
public Gpt3Model Model { get; set; } = Gpt3Model.Ada001;
[Comment(@"The maximum number of tokens to use per GPT-3 API call")]
public int MaxTokens { get; set; } = 100;
} }
[Cloneable] [Cloneable]
@@ -121,3 +142,17 @@ public sealed partial class RaceAnimal
public string Icon { get; set; } public string Icon { get; set; }
public string Name { get; set; } public string Name { get; set; }
} }
public enum ChatBotImplementation
{
Cleverbot,
Gpt3
}
public enum Gpt3Model
{
Ada001,
Babbage001,
Curie001,
Davinci003
}

View File

@@ -29,6 +29,20 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
ConfigPrinters.ToString, ConfigPrinters.ToString,
val => val >= 0); val => val >= 0);
AddParsedProp("chatbot",
gs => gs.ChatBot,
ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString);
AddParsedProp("gpt.model",
gs => gs.ChatGpt.Model,
ConfigParsers.InsensitiveEnum,
ConfigPrinters.ToString);
AddParsedProp("gpt.max_tokens",
gs => gs.ChatGpt.MaxTokens,
int.TryParse,
ConfigPrinters.ToString,
val => val > 0);
Migrate(); Migrate();
} }
@@ -45,5 +59,14 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
}; };
}); });
} }
if (data.Version < 2)
{
ModifyConfig(c =>
{
c.Version = 2;
c.ChatBot = ChatBotImplementation.Cleverbot;
});
}
} }
} }

View File

@@ -23,7 +23,6 @@ public class GamesService : INService, IReadyExecutor
//channelId, game //channelId, game
public ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new(); public ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new();
public ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new();
public Dictionary<ulong, TicTacToe> TicTacToeGames { get; } = new(); public Dictionary<ulong, TicTacToe> TicTacToeGames { get; } = new();
public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new(); public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new();
public ConcurrentDictionary<ulong, NunchiGame> NunchiGames { get; } = new(); public ConcurrentDictionary<ulong, NunchiGame> NunchiGames { get; } = new();

View File

@@ -74,11 +74,7 @@ public class TypingGame
var time = _options.StartTime; var time = _options.StartTime;
var msg = await Channel.SendMessageAsync($"Starting new typing contest in **{time}**...", var msg = await Channel.SendMessageAsync($"Starting new typing contest in **{time}**...");
options: new()
{
RetryMode = RetryMode.AlwaysRetry
});
do do
{ {

View File

@@ -36,7 +36,7 @@ public partial class Games
var (opts, _) = OptionsParser.ParseFrom(new TriviaOptions(), args); var (opts, _) = OptionsParser.ParseFrom(new TriviaOptions(), args);
var config = _gamesConfig.Data; var config = _gamesConfig.Data;
if (config.Trivia.MinimumWinReq > 0 && config.Trivia.MinimumWinReq > opts.WinRequirement) if (opts.WinRequirement != 0 && config.Trivia.MinimumWinReq > 0 && config.Trivia.MinimumWinReq > opts.WinRequirement)
return; return;
var trivia = new TriviaGame(opts, _cache); var trivia = new TriviaGame(opts, _cache);

View File

@@ -131,8 +131,11 @@ public sealed class TriviaGame
hintSent = true; hintSent = true;
// start a new countdown of the same length // start a new countdown of the same length
halfGuessTimerTask = TimeOutFactory(); halfGuessTimerTask = TimeOutFactory();
// send a hint out if (!_opts.NoHint)
await OnHint(this, question); {
// send a hint out
await OnHint(this, question);
}
continue; continue;
} }
@@ -154,7 +157,7 @@ public sealed class TriviaGame
var isWin = false; var isWin = false;
// if user won the game, tell the game to stop // if user won the game, tell the game to stop
if (val >= _opts.WinRequirement) if (_opts.WinRequirement != 0 && val >= _opts.WinRequirement)
{ {
_isStopped = true; _isStopped = true;
isWin = true; isWin = true;

View File

@@ -45,7 +45,7 @@ public class HelpService : IExecNoCommand, INService
// only send dm help text if it contains one of the keywords, if they're specified // only send dm help text if it contains one of the keywords, if they're specified
// if they're not, then reply to every DM // if they're not, then reply to every DM
if (settings.DmHelpTextKeywords.Any() && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k))) if (settings.DmHelpTextKeywords is not null && !settings.DmHelpTextKeywords.Any(k => msg.Content.Contains(k)))
return Task.CompletedTask; return Task.CompletedTask;
var rep = new ReplacementBuilder().WithOverride("%prefix%", () => _bss.Data.Prefix) var rep = new ReplacementBuilder().WithOverride("%prefix%", () => _bss.Data.Prefix)
@@ -152,17 +152,21 @@ public class HelpService : IExecNoCommand, INService
.Any(x => x is OnlyPublicBotAttribute)) .Any(x => x is OnlyPublicBotAttribute))
toReturn.Add("Only Public Bot"); toReturn.Add("Only Public Bot");
var userPerm = (UserPermAttribute)cmd.Preconditions.FirstOrDefault(ca => ca is UserPermAttribute); var userPermString = cmd.Preconditions
.Where(ca => ca is UserPermAttribute)
.Cast<UserPermAttribute>()
.Select(userPerm =>
{
if (userPerm.ChannelPermission is { } cPerm)
return GetPreconditionString(cPerm);
var userPermString = string.Empty; if (userPerm.GuildPermission is { } gPerm)
if (userPerm is not null) return GetPreconditionString(gPerm);
{
if (userPerm.ChannelPermission is { } cPerm)
userPermString = GetPreconditionString(cPerm);
if (userPerm.GuildPermission is { } gPerm) return string.Empty;
userPermString = GetPreconditionString(gPerm); })
} .Where(x => !string.IsNullOrWhiteSpace(x))
.Join('\n');
if (overrides is null) if (overrides is null)
{ {

View File

@@ -1,5 +1,6 @@
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NadekoBot.Modules.Searches;
namespace NadekoBot.Modules.Music; namespace NadekoBot.Modules.Music;
@@ -27,11 +28,12 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;
public YtdlYoutubeResolver(ITrackCacher trackCacher, IGoogleApiService google) public YtdlYoutubeResolver(ITrackCacher trackCacher, IGoogleApiService google, SearchesConfigService scs)
{ {
_trackCacher = trackCacher; _trackCacher = trackCacher;
_google = google; _google = google;
_ytdlPlaylistOperation = new("-4 " _ytdlPlaylistOperation = new("-4 "
+ "--geo-bypass " + "--geo-bypass "
+ "--encoding UTF8 " + "--encoding UTF8 "
@@ -44,7 +46,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
+ "--no-check-certificate " + "--no-check-certificate "
+ "-i " + "-i "
+ "--yes-playlist " + "--yes-playlist "
+ "-- \"{0}\""); + "-- \"{0}\"", scs.Data.YtProvider != YoutubeSearcher.Ytdl);
_ytdlIdOperation = new("-4 " _ytdlIdOperation = new("-4 "
+ "--geo-bypass " + "--geo-bypass "
@@ -56,7 +58,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
+ "--get-thumbnail " + "--get-thumbnail "
+ "--get-duration " + "--get-duration "
+ "--no-check-certificate " + "--no-check-certificate "
+ "-- \"{0}\""); + "-- \"{0}\"", scs.Data.YtProvider != YoutubeSearcher.Ytdl);
_ytdlSearchOperation = new("-4 " _ytdlSearchOperation = new("-4 "
+ "--geo-bypass " + "--geo-bypass "
@@ -69,7 +71,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
+ "--get-duration " + "--get-duration "
+ "--no-check-certificate " + "--no-check-certificate "
+ "--default-search " + "--default-search "
+ "\"ytsearch:\" -- \"{0}\""); + "\"ytsearch:\" -- \"{0}\"", scs.Data.YtProvider != YoutubeSearcher.Ytdl);
} }
private YtTrackData ResolveYtdlData(string ytdlOutputString) private YtTrackData ResolveYtdlData(string ytdlOutputString)

View File

@@ -22,6 +22,6 @@ public interface ISearchImagesService
ValueTask<bool> ToggleBlacklistTag(ulong guildId, string tag); ValueTask<bool> ToggleBlacklistTag(ulong guildId, string tag);
ValueTask<string[]> GetBlacklistedTags(ulong guildId); ValueTask<string[]> GetBlacklistedTags(ulong guildId);
Task<UrlReply> Butts(); Task<UrlReply> Butts();
Task<Gallery> GetNhentaiByIdAsync(uint id); // Task<Gallery> GetNhentaiByIdAsync(uint id);
Task<Gallery> GetNhentaiBySearchAsync(string search); // Task<Gallery> GetNhentaiBySearchAsync(string search);
} }

View File

@@ -1,9 +1,9 @@
using NadekoBot.Modules.Searches.Common; // using NadekoBot.Modules.Searches.Common;
//
namespace NadekoBot.Modules.Nsfw; // namespace NadekoBot.Modules.Nsfw;
//
public interface INhentaiService // public interface INhentaiService
{ // {
Task<Gallery?> GetAsync(uint id); // Task<Gallery?> GetAsync(uint id);
Task<IReadOnlyList<uint>> GetIdsBySearchAsync(string search); // Task<IReadOnlyList<uint>> GetIdsBySearchAsync(string search);
} // }

View File

@@ -1,115 +1,115 @@
using AngleSharp.Html.Dom; // using AngleSharp.Html.Dom;
using AngleSharp.Html.Parser; // using AngleSharp.Html.Parser;
using NadekoBot.Modules.Searches.Common; // using NadekoBot.Modules.Searches.Common;
//
namespace NadekoBot.Modules.Nsfw; // namespace NadekoBot.Modules.Nsfw;
//
public sealed class NhentaiScraperService : INhentaiService, INService // public sealed class NhentaiScraperService : INhentaiService, INService
{ // {
private readonly IHttpClientFactory _httpFactory; // private readonly IHttpClientFactory _httpFactory;
//
private static readonly HtmlParser _htmlParser = new(new() // private static readonly HtmlParser _htmlParser = new(new()
{ // {
IsScripting = false, // IsScripting = false,
IsEmbedded = false, // IsEmbedded = false,
IsSupportingProcessingInstructions = false, // IsSupportingProcessingInstructions = false,
IsKeepingSourceReferences = false, // IsKeepingSourceReferences = false,
IsNotSupportingFrames = true // IsNotSupportingFrames = true
}); // });
//
public NhentaiScraperService(IHttpClientFactory httpFactory) // public NhentaiScraperService(IHttpClientFactory httpFactory)
{ // {
_httpFactory = httpFactory; // _httpFactory = httpFactory;
} // }
//
private HttpClient GetHttpClient() // private HttpClient GetHttpClient()
{ // {
var http = _httpFactory.CreateClient(); // var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36"); // http.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36");
http.DefaultRequestHeaders.Add("Cookie", "cf_clearance=I5pR71P4wJkRBFTLFjBndI.GwfKwT.Gx06uS8XNmRJo-1657214595-0-150; csrftoken=WMWRLtsQtBVQYvYkbqXKJHI9T1JwWCdd3tNhoxHn7aHLUYHAqe60XFUKAoWsJtda"); // http.DefaultRequestHeaders.Add("Cookie", "cf_clearance=I5pR71P4wJkRBFTLFjBndI.GwfKwT.Gx06uS8XNmRJo-1657214595-0-150; csrftoken=WMWRLtsQtBVQYvYkbqXKJHI9T1JwWCdd3tNhoxHn7aHLUYHAqe60XFUKAoWsJtda");
return http; // return http;
} // }
//
public async Task<Gallery?> GetAsync(uint id) // public async Task<Gallery?> GetAsync(uint id)
{ // {
using var http = GetHttpClient(); // using var http = GetHttpClient();
try // try
{ // {
var url = $"https://nhentai.net/g/{id}/"; // var url = $"https://nhentai.net/g/{id}/";
var strRes = await http.GetStringAsync(url); // var strRes = await http.GetStringAsync(url);
var doc = await _htmlParser.ParseDocumentAsync(strRes); // var doc = await _htmlParser.ParseDocumentAsync(strRes);
//
var title = doc.QuerySelector("#info .title")?.TextContent; // var title = doc.QuerySelector("#info .title")?.TextContent;
var fullTitle = doc.QuerySelector("meta[itemprop=\"name\"]")?.Attributes["content"]?.Value // var fullTitle = doc.QuerySelector("meta[itemprop=\"name\"]")?.Attributes["content"]?.Value
?? title; // ?? title;
var thumb = (doc.QuerySelector("#cover a img") as IHtmlImageElement)?.Dataset["src"]; // var thumb = (doc.QuerySelector("#cover a img") as IHtmlImageElement)?.Dataset["src"];
//
var tagsElem = doc.QuerySelector("#tags"); // var tagsElem = doc.QuerySelector("#tags");
//
var pageCount = tagsElem?.QuerySelector("a.tag[href^=\"/search/?q=pages\"] span")?.TextContent; // var pageCount = tagsElem?.QuerySelector("a.tag[href^=\"/search/?q=pages\"] span")?.TextContent;
var likes = doc.QuerySelector(".buttons .btn-disabled.btn.tooltip span span")?.TextContent?.Trim('(', ')'); // var likes = doc.QuerySelector(".buttons .btn-disabled.btn.tooltip span span")?.TextContent?.Trim('(', ')');
var uploadedAt = (tagsElem?.QuerySelector(".tag-container .tags time.nobold") as IHtmlTimeElement)?.DateTime; // var uploadedAt = (tagsElem?.QuerySelector(".tag-container .tags time.nobold") as IHtmlTimeElement)?.DateTime;
//
var tags = tagsElem?.QuerySelectorAll(".tag-container .tags > a.tag[href^=\"/tag\"]") // var tags = tagsElem?.QuerySelectorAll(".tag-container .tags > a.tag[href^=\"/tag\"]")
.Cast<IHtmlAnchorElement>() // .Cast<IHtmlAnchorElement>()
.Select(x => new Tag() // .Select(x => new Tag()
{ // {
Name = x.QuerySelector("span:first-child")?.TextContent, // Name = x.QuerySelector("span:first-child")?.TextContent,
Url = $"https://nhentai.net{x.PathName}" // Url = $"https://nhentai.net{x.PathName}"
}) // })
.ToArray(); // .ToArray();
//
if (string.IsNullOrWhiteSpace(fullTitle)) // if (string.IsNullOrWhiteSpace(fullTitle))
return null; // return null;
//
if (!int.TryParse(pageCount, out var pc)) // if (!int.TryParse(pageCount, out var pc))
return null; // return null;
//
if (!int.TryParse(likes, out var lc)) // if (!int.TryParse(likes, out var lc))
return null; // return null;
//
if (!DateTime.TryParse(uploadedAt, out var ua)) // if (!DateTime.TryParse(uploadedAt, out var ua))
return null; // return null;
//
return new Gallery(id, // return new Gallery(id,
url, // url,
fullTitle, // fullTitle,
title, // title,
thumb, // thumb,
pc, // pc,
lc, // lc,
ua, // ua,
tags); // tags);
} // }
catch (HttpRequestException) // catch (HttpRequestException)
{ // {
Log.Warning("Nhentai with id {NhentaiId} not found", id); // Log.Warning("Nhentai with id {NhentaiId} not found", id);
return null; // return null;
} // }
} // }
//
public async Task<IReadOnlyList<uint>> GetIdsBySearchAsync(string search) // public async Task<IReadOnlyList<uint>> GetIdsBySearchAsync(string search)
{ // {
using var http = GetHttpClient(); // using var http = GetHttpClient();
try // try
{ // {
var url = $"https://nhentai.net/search/?q={Uri.EscapeDataString(search)}&sort=popular-today"; // var url = $"https://nhentai.net/search/?q={Uri.EscapeDataString(search)}&sort=popular-today";
var strRes = await http.GetStringAsync(url); // var strRes = await http.GetStringAsync(url);
var doc = await _htmlParser.ParseDocumentAsync(strRes); // var doc = await _htmlParser.ParseDocumentAsync(strRes);
//
var elems = doc.QuerySelectorAll(".container .gallery a") // var elems = doc.QuerySelectorAll(".container .gallery a")
.Cast<IHtmlAnchorElement>() // .Cast<IHtmlAnchorElement>()
.Where(x => x.PathName.StartsWith("/g/")) // .Where(x => x.PathName.StartsWith("/g/"))
.Select(x => x.PathName[3..^1]) // .Select(x => x.PathName[3..^1])
.Select(uint.Parse) // .Select(uint.Parse)
.ToArray(); // .ToArray();
//
return elems; // return elems;
} // }
catch (HttpRequestException) // catch (HttpRequestException)
{ // {
Log.Warning("Nhentai search for {NhentaiSearch} failed", search); // Log.Warning("Nhentai search for {NhentaiSearch} failed", search);
return Array.Empty<uint>(); // return Array.Empty<uint>();
} // }
} // }
} // }

View File

@@ -360,67 +360,65 @@ public partial class NSFW : NadekoModule<ISearchImagesService>
} }
} }
[Cmd] // [RequireNsfw(Group = "nsfw_or_dm")]
[RequireContext(ContextType.Guild)] // [RequireContext(ContextType.DM, Group = "nsfw_or_dm")]
[RequireNsfw(Group = "nsfw_or_dm")] // [Priority(1)]
[RequireContext(ContextType.DM, Group = "nsfw_or_dm")] // public async Task Nhentai(uint id)
[Priority(1)] // {
public async Task Nhentai(uint id) // var g = await _service.GetNhentaiByIdAsync(id);
{ //
var g = await _service.GetNhentaiByIdAsync(id); // if (g is null)
// {
if (g is null) // await ReplyErrorLocalizedAsync(strs.not_found);
{ // return;
await ReplyErrorLocalizedAsync(strs.not_found); // }
return; //
} // await SendNhentaiGalleryInternalAsync(g);
// }
await SendNhentaiGalleryInternalAsync(g); //
} // [Cmd]
// [RequireContext(ContextType.Guild)]
[Cmd] // [RequireNsfw(Group = "nsfw_or_dm")]
[RequireContext(ContextType.Guild)] // [RequireContext(ContextType.DM, Group = "nsfw_or_dm")]
[RequireNsfw(Group = "nsfw_or_dm")] // [Priority(0)]
[RequireContext(ContextType.DM, Group = "nsfw_or_dm")] // public async Task Nhentai([Leftover] string query)
[Priority(0)] // {
public async Task Nhentai([Leftover] string query) // var g = await _service.GetNhentaiBySearchAsync(query);
{ //
var g = await _service.GetNhentaiBySearchAsync(query); // if (g is null)
// {
if (g is null) // await ReplyErrorLocalizedAsync(strs.not_found);
{ // return;
await ReplyErrorLocalizedAsync(strs.not_found); // }
return; //
} // await SendNhentaiGalleryInternalAsync(g);
// }
await SendNhentaiGalleryInternalAsync(g); //
} // private async Task SendNhentaiGalleryInternalAsync(Gallery g)
// {
private async Task SendNhentaiGalleryInternalAsync(Gallery g) // var count = 0;
{ // var tagString = g.Tags.Shuffle()
var count = 0; // .Select(tag => $"[{tag.Name}]({tag.Url})")
var tagString = g.Tags.Shuffle() // .TakeWhile(tag => (count += tag.Length) < 1000)
.Select(tag => $"[{tag.Name}]({tag.Url})") // .Join(" ");
.TakeWhile(tag => (count += tag.Length) < 1000) //
.Join(" "); // var embed = _eb.Create()
// .WithTitle(g.Title)
var embed = _eb.Create() // .WithDescription(g.FullTitle)
.WithTitle(g.Title) // .WithImageUrl(g.Thumbnail)
.WithDescription(g.FullTitle) // .WithUrl(g.Url)
.WithImageUrl(g.Thumbnail) // .AddField(GetText(strs.favorites), g.Likes, true)
.WithUrl(g.Url) // .AddField(GetText(strs.pages), g.PageCount, true)
.AddField(GetText(strs.favorites), g.Likes, true) // .AddField(GetText(strs.tags),
.AddField(GetText(strs.pages), g.PageCount, true) // string.IsNullOrWhiteSpace(tagString)
.AddField(GetText(strs.tags), // ? "?"
string.IsNullOrWhiteSpace(tagString) // : tagString,
? "?" // true)
: tagString, // .WithFooter(g.UploadedAt.ToString("f"))
true) // .WithOkColor();
.WithFooter(g.UploadedAt.ToString("f")) //
.WithOkColor(); // await ctx.Channel.EmbedAsync(embed);
// }
await ctx.Channel.EmbedAsync(embed);
}
private async Task InternalDapiCommand( private async Task InternalDapiCommand(
string[] tags, string[] tags,

View File

@@ -19,17 +19,15 @@ public class SearchImagesService : ISearchImagesService, INService
private readonly SearchImageCacher _cache; private readonly SearchImageCacher _cache;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly DbService _db; private readonly DbService _db;
private readonly INhentaiService _nh;
private readonly object _taglock = new(); private readonly object _taglock = new();
public SearchImagesService( public SearchImagesService(
DbService db, DbService db,
SearchImageCacher cacher, SearchImageCacher cacher,
IHttpClientFactory httpFactory, IHttpClientFactory httpFactory
INhentaiService nh) )
{ {
_nh = nh;
_db = db; _db = db;
_rng = new NadekoRandom(); _rng = new NadekoRandom();
_cache = cacher; _cache = cacher;
@@ -277,6 +275,7 @@ public class SearchImagesService : ISearchImagesService, INService
} }
} }
/*
#region Nhentai #region Nhentai
public Task<Gallery?> GetNhentaiByIdAsync(uint id) public Task<Gallery?> GetNhentaiByIdAsync(uint id)
@@ -294,4 +293,5 @@ public class SearchImagesService : ISearchImagesService, INService
} }
#endregion #endregion
*/
} }

View File

@@ -16,7 +16,7 @@ public partial class Searches
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
public Task YtUploadNotif(string url, [Leftover] ITextChannel channel = null) public Task YtUploadNotif(string url, ITextChannel channel = null, [Leftover] string message = null)
{ {
var m = _ytChannelRegex.Match(url); var m = _ytChannelRegex.Match(url);
if (!m.Success) if (!m.Success)
@@ -24,13 +24,13 @@ public partial class Searches
var channelId = m.Groups["channelid"].Value; var channelId = m.Groups["channelid"].Value;
return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel); return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel, message);
} }
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
public async Task Feed(string url, [Leftover] ITextChannel channel = null) public async Task Feed(string url, ITextChannel channel = null, [Leftover] string message = null)
{ {
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)
|| (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)) || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps))
@@ -51,7 +51,10 @@ public partial class Searches
return; return;
} }
var result = _service.AddFeed(ctx.Guild.Id, channel.Id, url); if (ctx.User is not IGuildUser gu || !gu.GuildPermissions.Administrator)
message = message?.SanitizeMentions(true);
var result = _service.AddFeed(ctx.Guild.Id, channel.Id, url, message);
if (result == FeedAddResult.Success) if (result == FeedAddResult.Success)
{ {
await ReplyConfirmLocalizedAsync(strs.feed_added); await ReplyConfirmLocalizedAsync(strs.feed_added);

View File

@@ -162,7 +162,6 @@ public class FeedsService : INService
} }
} }
embed.WithTitle(title.TrimTo(256)); embed.WithTitle(title.TrimTo(256));
var desc = feedItem.Description?.StripHtml(); var desc = feedItem.Description?.StripHtml();
@@ -171,15 +170,15 @@ public class FeedsService : INService
//send the created embed to all subscribed channels //send the created embed to all subscribed channels
var feedSendTasks = kvp.Value var feedSendTasks = kvp.Value
.Where(x => x.GuildConfig is not null) .Where(x => x.GuildConfig is not null)
.Select(x => _client.GetGuild(x.GuildConfig.GuildId) .Select(x => _client.GetGuild(x.GuildConfig.GuildId)
?.GetTextChannel(x.ChannelId)) ?.GetTextChannel(x.ChannelId)
.Where(x => x is not null) ?.EmbedAsync(embed, x.Message))
.Select(x => x.EmbedAsync(embed)); .Where(x => x is not null);
allSendTasks.Add(feedSendTasks.WhenAll()); allSendTasks.Add(feedSendTasks.WhenAll());
// as data retrieval was sucessful, reset error counter // as data retrieval was successful, reset error counter
ClearErrors(rssUrl); ClearErrors(rssUrl);
} }
} }
@@ -207,7 +206,7 @@ public class FeedsService : INService
.ToList(); .ToList();
} }
public FeedAddResult AddFeed(ulong guildId, ulong channelId, string rssFeed) public FeedAddResult AddFeed(ulong guildId, ulong channelId, string rssFeed, string message)
{ {
ArgumentNullException.ThrowIfNull(rssFeed, nameof(rssFeed)); ArgumentNullException.ThrowIfNull(rssFeed, nameof(rssFeed));

View File

@@ -39,7 +39,7 @@ public partial class Searches
[Cmd] [Cmd]
public async Task MagicItem() public async Task MagicItem()
{ {
if (!_service.WowJokes.Any()) if (!_service.MagicItems.Any())
{ {
await ReplyErrorLocalizedAsync(strs.magicitems_not_loaded); await ReplyErrorLocalizedAsync(strs.magicitems_not_loaded);
return; return;

View File

@@ -1,4 +1,4 @@
#nullable disable #nullable disable
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Modules.Administration.Services; using NadekoBot.Modules.Administration.Services;
using NadekoBot.Modules.Searches.Common; using NadekoBot.Modules.Searches.Common;
@@ -199,7 +199,7 @@ public partial class Searches : NadekoModule<SearchesService>
if (!await ValidateQuery(ffs)) if (!await ValidateQuery(ffs))
return; return;
var shortenedUrl = await _google.ShortenUrl($"https://lmgtfy.com/?q={Uri.EscapeDataString(ffs)}"); var shortenedUrl = await _google.ShortenUrl($"https://letmegooglethat.com/?q={Uri.EscapeDataString(ffs)}");
await SendConfirmAsync($"<{shortenedUrl}>"); await SendConfirmAsync($"<{shortenedUrl}>");
} }
@@ -325,7 +325,7 @@ public partial class Searches : NadekoModule<SearchesService>
return _eb.Create() return _eb.Create()
.WithOkColor() .WithOkColor()
.WithUrl(item.Permalink) .WithUrl(item.Permalink)
.WithAuthor(item.Word) .WithTitle(item.Word)
.WithDescription(item.Definition); .WithDescription(item.Definition);
}, },
items.Length, items.Length,

View File

@@ -296,8 +296,8 @@ public class SearchesService : INService
public async Task<string> GetChuckNorrisJoke() public async Task<string> GetChuckNorrisJoke()
{ {
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var response = await http.GetStringAsync(new Uri("http://api.icndb.com/jokes/random/")); var response = await http.GetStringAsync(new Uri("https://api.chucknorris.io/jokes/random"));
return JObject.Parse(response)["value"]["joke"] + " 😆"; return JObject.Parse(response)["value"] + " 😆";
} }
public async Task<MtgData> GetMtgCardAsync(string search) public async Task<MtgData> GetMtgCardAsync(string search)

View File

@@ -220,5 +220,5 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
} }
public IEnumerable<string> GetLanguages() public IEnumerable<string> GetLanguages()
=> _google.Languages.Select(x => x.Key); => _google.Languages.GroupBy(x => x.Value).Select(x => $"{x.AsEnumerable().Select(y => y.Key).Join(", ")}");
} }

View File

@@ -77,6 +77,19 @@ public partial class Searches
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Translangs() public async Task Translangs()
=> await ctx.Channel.SendTableAsync(_service.GetLanguages(), str => $"{str,-15}"); {
var langs = _service.GetLanguages().ToList();
var eb = _eb.Create()
.WithTitle(GetText(strs.supported_languages))
.WithOkColor();
foreach (var chunk in langs.Chunk(15))
{
eb.AddField("󠀁", chunk.Join("\n"), isInline: true);
}
await ctx.Channel.EmbedAsync(eb);
}
} }
} }

View File

@@ -30,7 +30,7 @@ public partial class SearchesConfig : ICloneable<SearchesConfig>
- `ytdlp` - recommended easy, uses `yt-dlp`. Requires `yt-dlp` to be installed and it's path added to env variables - `ytdlp` - recommended easy, uses `yt-dlp`. Requires `yt-dlp` to be installed and it's path added to env variables
- `invidious` - recommended advanced, uses invidious api. Requires at least one invidious instance specified in the `invidiousInstances` property")] - `invidious` - recommended advanced, uses invidious api. Requires at least one invidious instance specified in the `invidiousInstances` property")]
public YoutubeSearcher YtProvider { get; set; } = YoutubeSearcher.Ytdl; public YoutubeSearcher YtProvider { get; set; } = YoutubeSearcher.Ytdlp;
[Comment(@"Set the searx instance urls in case you want to use 'searx' for either img or web search. [Comment(@"Set the searx instance urls in case you want to use 'searx' for either img or web search.
Nadeko will use a random one for each request. Nadeko will use a random one for each request.

View File

@@ -199,7 +199,7 @@ public sealed class PatronageService
} }
else else
{ {
if (dbPatron.LastCharge.Month < lastChargeUtc.Month) if (dbPatron.LastCharge.Month < lastChargeUtc.Month || dbPatron.LastCharge.Year < lastChargeUtc.Year)
{ {
// user is charged again for this month // user is charged again for this month
// if his sub would end in teh future, extend it by one month. // if his sub would end in teh future, extend it by one month.

View File

@@ -270,7 +270,7 @@ public partial class Utility
if (userId == ctx.User.Id || hasManageMessages) if (userId == ctx.User.Id || hasManageMessages)
{ {
var deleted = await _qs.DeleteAllAuthorQuotesAsync(ctx.Guild.Id, ctx.User.Id); var deleted = await _qs.DeleteAllAuthorQuotesAsync(ctx.Guild.Id, userId);
await ReplyConfirmLocalizedAsync(strs.quotes_deleted_count(deleted)); await ReplyConfirmLocalizedAsync(strs.quotes_deleted_count(deleted));
} }
else else

View File

@@ -1,12 +1,9 @@
#nullable disable using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db; using NadekoBot.Db;
using NadekoBot.Modules.Utility.Common; using NadekoBot.Modules.Utility.Common;
using NadekoBot.Modules.Utility.Common.Exceptions; using NadekoBot.Modules.Utility.Common.Exceptions;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Diagnostics;
using System.Net; using System.Net;
using Nadeko.Common;
namespace NadekoBot.Modules.Utility.Services; namespace NadekoBot.Modules.Utility.Services;
@@ -31,15 +28,16 @@ public class StreamRoleService : IReadyExecutor, INService
_queueRunner = new QueueRunner(); _queueRunner = new QueueRunner();
} }
private Task OnPresenceUpdate(SocketUser user, SocketPresence oldPresence, SocketPresence newPresence) private Task OnPresenceUpdate(SocketUser user, SocketPresence? oldPresence, SocketPresence? newPresence)
{ {
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
if (oldPresence.Activities.Count != newPresence.Activities.Count) if (oldPresence?.Activities?.Count != newPresence?.Activities?.Count)
{ {
var guildUsers = _client.Guilds var guildUsers = _client.Guilds
.Select(x => x.GetUser(user.Id)); .Select(x => x.GetUser(user.Id))
.Where(x => x is not null);
foreach (var guildUser in guildUsers) foreach (var guildUser in guildUsers)
{ {
@@ -131,7 +129,7 @@ public class StreamRoleService : IReadyExecutor, INService
/// <param name="guild">Guild Id</param> /// <param name="guild">Guild Id</param>
/// <param name="keyword">Keyword to set</param> /// <param name="keyword">Keyword to set</param>
/// <returns>The keyword set</returns> /// <returns>The keyword set</returns>
public async Task<string> SetKeyword(IGuild guild, string keyword) public async Task<string?> SetKeyword(IGuild guild, string? keyword)
{ {
keyword = keyword?.Trim().ToLowerInvariant(); keyword = keyword?.Trim().ToLowerInvariant();
@@ -221,15 +219,15 @@ public class StreamRoleService : IReadyExecutor, INService
await RescanUsers(guild); await RescanUsers(guild);
} }
private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null) private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole? addRole = null)
=> await _queueRunner.EnqueueAsync(() => RescanUserInternal(user, setting, addRole)); => await _queueRunner.EnqueueAsync(() => RescanUserInternal(user, setting, addRole));
private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole addRole = null) private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole? addRole = null)
{ {
if (user.IsBot) if (user.IsBot)
return; return;
var g = (StreamingGame)user.Activities.FirstOrDefault(a var g = (StreamingGame?)user.Activities.FirstOrDefault(a
=> a is StreamingGame => a is StreamingGame
&& (string.IsNullOrWhiteSpace(setting.Keyword) && (string.IsNullOrWhiteSpace(setting.Keyword)
|| a.Name.ToUpperInvariant().Contains(setting.Keyword.ToUpperInvariant()) || a.Name.ToUpperInvariant().Contains(setting.Keyword.ToUpperInvariant())

View File

@@ -402,6 +402,103 @@ public partial class Utility : NadekoModule
await ctx.OkAsync(); await ctx.OkAsync();
} }
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
public async Task StickerAdd(string name = null, string description = null, params string[] tags)
{
string format;
Stream stream;
if (ctx.Message.Stickers.Count is 1 && ctx.Message.Stickers.First() is SocketSticker ss)
{
name ??= ss.Name;
description = ss.Description;
tags = tags is null or { Length: 0 } ? ss.Tags.ToArray() : tags;
format = FormatToExtension(ss.Format);
using var http = _httpFactory.CreateClient();
stream = await http.GetStreamAsync(ss.GetStickerUrl());
}
// else if (ctx.Message.Attachments.FirstOrDefault() is { } attachment)
// {
// var url = attachment?.Url;
//
// if (url is null)
// return;
//
// if (name is null)
// {
// await ReplyErrorLocalizedAsync(strs.sticker_missing_name);
// return;
// }
//
// format = Path.GetExtension(attachment.Filename);
//
// if (attachment is not { Width: 300, Height: 300 })
// {
// await ReplyErrorLocalizedAsync(strs.sticker_invalid_size);
// return;
// }
//
// using var http = _httpFactory.CreateClient();
//
// using var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
// if (res.GetContentLength() > 512.Kilobytes().Bytes)
// {
// await ReplyErrorLocalizedAsync(strs.invalid_emoji_link);
// return;
// }
//
// stream = await res.Content.ReadAsStreamAsync();
// }
else
{
await ReplyErrorLocalizedAsync(strs.sticker_error);
return;
}
try
{
if (tags.Length == 0)
tags = new[] { name };
await ctx.Guild.CreateStickerAsync(name,
string.IsNullOrWhiteSpace(description) ? "Missing description" : description,
tags,
stream,
$"{name}.{format}");
await ctx.OkAsync();
}
catch (Exception ex)
{
Log.Warning(ex, "Error occurred while adding a sticker: {Message}", ex.Message);
await ReplyErrorLocalizedAsync(strs.error_occured);
}
finally
{
await stream.DisposeAsync();
}
}
private static string FormatToExtension(StickerFormatType format)
{
switch (format)
{
case StickerFormatType.None:
case StickerFormatType.Png:
case StickerFormatType.Apng:
return "png";
case StickerFormatType.Lottie:
return "lottie";
default:
throw new ArgumentException(nameof (format));
}
}
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public async Task ListServers(int page = 1) public async Task ListServers(int page = 1)
@@ -453,15 +550,20 @@ public partial class Utility : NadekoModule
return; return;
} }
var embed = msg.Embeds.FirstOrDefault(); if (!msg.Embeds.Any())
if (embed is null)
{ {
await ReplyErrorLocalizedAsync(strs.not_found); await ReplyErrorLocalizedAsync(strs.not_found);
return; return;
} }
var json = SmartEmbedText.FromEmbed(embed, msg.Content).ToJson(_showEmbedSerializerOptions); var json = new SmartEmbedTextArray()
await SendConfirmAsync(Format.Sanitize(json).Replace("](", "]\\(")); {
Content = msg.Content,
Embeds = msg.Embeds
.Map(x => new SmartEmbedArrayElementText(x))
}.ToJson(_showEmbedSerializerOptions);
await SendConfirmAsync(Format.Code(json, "json").Replace("](", "]\\("));
} }
[Cmd] [Cmd]

View File

@@ -302,7 +302,7 @@ public class CommandHandler : INService, IReadyExecutor
} }
public Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommandAsync( public Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommandAsync(
CommandContext context, ICommandContext context,
string input, string input,
int argPos, int argPos,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
@@ -311,7 +311,7 @@ public class CommandHandler : INService, IReadyExecutor
public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand( public async Task<(bool Success, string Error, CommandInfo Info)> ExecuteCommand(
CommandContext context, ICommandContext context,
string input, string input,
IServiceProvider services, IServiceProvider services,
MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)

View File

@@ -88,6 +88,9 @@ public sealed class CurrencyService : ICurrencyService, INService
long amount, long amount,
TxData txData) TxData txData)
{ {
if (amount == 0)
return true;
var wallet = await GetWalletAsync(userId); var wallet = await GetWalletAsync(userId);
var result = await wallet.Take(amount, txData); var result = await wallet.Take(amount, txData);
if(result) if(result)

View File

@@ -104,8 +104,7 @@ public sealed class GamblingTxTracker : ITxTracker, INService, IReadyExecutor
public async Task<IReadOnlyCollection<GamblingStats>> GetAllAsync() public async Task<IReadOnlyCollection<GamblingStats>> GetAllAsync()
{ {
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx.Set<GamblingStats>()
.GetTable<GamblingStats>() .ToListAsyncEF();
.ToListAsync();
} }
} }

View File

@@ -184,8 +184,11 @@ public sealed class BotCredsProvider : IBotCredsProvider
var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME)); var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME));
if (creds.Version <= 5) if (creds.Version <= 5)
{ {
creds.Version = 6;
creds.BotCache = BotCacheImplemenation.Redis; creds.BotCache = BotCacheImplemenation.Redis;
}
if (creds.Version <= 6)
{
creds.Version = 7;
File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds)); File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds));
} }
} }

View File

@@ -10,143 +10,11 @@ using System.Xml;
namespace NadekoBot.Services; namespace NadekoBot.Services;
public class GoogleApiService : IGoogleApiService, INService public sealed partial class GoogleApiService : IGoogleApiService, INService
{ {
private static readonly Regex private static readonly Regex
_plRegex = new("(?:youtu\\.be\\/|list=)(?<id>[\\da-zA-Z\\-_]*)", RegexOptions.Compiled); _plRegex = new("(?:youtu\\.be\\/|list=)(?<id>[\\da-zA-Z\\-_]*)", RegexOptions.Compiled);
public IReadOnlyDictionary<string, string> Languages { get; } = new Dictionary<string, string>
{
{ "afrikaans", "af" },
{ "albanian", "sq" },
{ "arabic", "ar" },
{ "armenian", "hy" },
{ "azerbaijani", "az" },
{ "basque", "eu" },
{ "belarusian", "be" },
{ "bengali", "bn" },
{ "bulgarian", "bg" },
{ "catalan", "ca" },
{ "chinese-traditional", "zh-TW" },
{ "chinese-simplified", "zh-CN" },
{ "chinese", "zh-CN" },
{ "croatian", "hr" },
{ "czech", "cs" },
{ "danish", "da" },
{ "dutch", "nl" },
{ "english", "en" },
{ "esperanto", "eo" },
{ "estonian", "et" },
{ "filipino", "tl" },
{ "finnish", "fi" },
{ "french", "fr" },
{ "galician", "gl" },
{ "german", "de" },
{ "georgian", "ka" },
{ "greek", "el" },
{ "haitian Creole", "ht" },
{ "hebrew", "iw" },
{ "hindi", "hi" },
{ "hungarian", "hu" },
{ "icelandic", "is" },
{ "indonesian", "id" },
{ "irish", "ga" },
{ "italian", "it" },
{ "japanese", "ja" },
{ "korean", "ko" },
{ "lao", "lo" },
{ "latin", "la" },
{ "latvian", "lv" },
{ "lithuanian", "lt" },
{ "macedonian", "mk" },
{ "malay", "ms" },
{ "maltese", "mt" },
{ "norwegian", "no" },
{ "persian", "fa" },
{ "polish", "pl" },
{ "portuguese", "pt" },
{ "romanian", "ro" },
{ "russian", "ru" },
{ "serbian", "sr" },
{ "slovak", "sk" },
{ "slovenian", "sl" },
{ "spanish", "es" },
{ "swahili", "sw" },
{ "swedish", "sv" },
{ "tamil", "ta" },
{ "telugu", "te" },
{ "thai", "th" },
{ "turkish", "tr" },
{ "ukrainian", "uk" },
{ "urdu", "ur" },
{ "vietnamese", "vi" },
{ "welsh", "cy" },
{ "yiddish", "yi" },
{ "af", "af" },
{ "sq", "sq" },
{ "ar", "ar" },
{ "hy", "hy" },
{ "az", "az" },
{ "eu", "eu" },
{ "be", "be" },
{ "bn", "bn" },
{ "bg", "bg" },
{ "ca", "ca" },
{ "zh-tw", "zh-TW" },
{ "zh-cn", "zh-CN" },
{ "hr", "hr" },
{ "cs", "cs" },
{ "da", "da" },
{ "nl", "nl" },
{ "en", "en" },
{ "eo", "eo" },
{ "et", "et" },
{ "tl", "tl" },
{ "fi", "fi" },
{ "fr", "fr" },
{ "gl", "gl" },
{ "de", "de" },
{ "ka", "ka" },
{ "el", "el" },
{ "ht", "ht" },
{ "iw", "iw" },
{ "hi", "hi" },
{ "hu", "hu" },
{ "is", "is" },
{ "id", "id" },
{ "ga", "ga" },
{ "it", "it" },
{ "ja", "ja" },
{ "ko", "ko" },
{ "lo", "lo" },
{ "la", "la" },
{ "lv", "lv" },
{ "lt", "lt" },
{ "mk", "mk" },
{ "ms", "ms" },
{ "mt", "mt" },
{ "no", "no" },
{ "fa", "fa" },
{ "pl", "pl" },
{ "pt", "pt" },
{ "ro", "ro" },
{ "ru", "ru" },
{ "sr", "sr" },
{ "sk", "sk" },
{ "sl", "sl" },
{ "es", "es" },
{ "sw", "sw" },
{ "sv", "sv" },
{ "ta", "ta" },
{ "te", "te" },
{ "th", "th" },
{ "tr", "tr" },
{ "uk", "uk" },
{ "ur", "ur" },
{ "vi", "vi" },
{ "cy", "cy" },
{ "yi", "yi" }
};
private readonly YouTubeService _yt; private readonly YouTubeService _yt;
private readonly UrlshortenerService _sh; private readonly UrlshortenerService _sh;
@@ -155,7 +23,7 @@ public class GoogleApiService : IGoogleApiService, INService
private readonly IBotCredsProvider _creds; private readonly IBotCredsProvider _creds;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
public GoogleApiService(IBotCredsProvider creds, IHttpClientFactory factory) public GoogleApiService(IBotCredsProvider creds, IHttpClientFactory factory) : this()
{ {
_creds = creds; _creds = creds;
_httpFactory = factory; _httpFactory = factory;

View File

@@ -0,0 +1,158 @@
namespace NadekoBot.Services;
public sealed partial class GoogleApiService
{
private const string SUPPORTED = @"afrikaans af
albanian sq
amharic am
arabic ar
armenian hy
assamese as
aymara ay
azerbaijani az
bambara bm
basque eu
belarusian be
bengali bn
bhojpuri bho
bosnian bs
bulgarian bg
catalan ca
cebuano ceb
chinese zh-CN
chinese-trad zh-TW
corsican co
croatian hr
czech cs
danish da
dhivehi dv
dogri doi
dutch nl
english en
esperanto eo
estonian et
ewe ee
filipino fil
finnish fi
french fr
frisian fy
galician gl
georgian ka
german de
greek el
guarani gn
gujarati gu
haitian ht
hausa ha
hawaiian haw
hebrew he
hindi hi
hmong hmn
hungarian hu
icelandic is
igbo ig
ilocano ilo
indonesian id
irish ga
italian it
japanese ja
javanese jv
kannada kn
kazakh kk
khmer km
kinyarwanda rw
konkani gom
korean ko
krio kri
kurdish ku
kurdish-sor ckb
kyrgyz ky
lao lo
latin la
latvian lv
lingala ln
lithuanian lt
luganda lg
luxembourgish lb
macedonian mk
maithili mai
malagasy mg
malay ms
malayalam ml
maltese mt
maori mi
marathi mr
meiteilon mni-Mtei
mizo lus
mongolian mn
myanmar my
nepali ne
norwegian no
nyanja ny
odia or
oromo om
pashto ps
persian fa
polish pl
portuguese pt
punjabi pa
quechua qu
romanian ro
russian ru
samoan sm
sanskrit sa
scots gd
sepedi nso
serbian sr
sesotho st
shona sn
sindhi sd
sinhala si
slovak sk
slovenian sl
somali so
spanish es
sundanese su
swahili sw
swedish sv
tagalog tl
tajik tg
tamil ta
tatar tt
telugu te
thai th
tigrinya ti
tsonga ts
turkish tr
turkmen tk
twi ak
ukrainian uk
urdu ur
uyghur ug
uzbek uz
vietnamese vi
welsh cy
xhosa xh
yiddish yi
yoruba yo
zulu zu";
public IReadOnlyDictionary<string, string> Languages { get; }
private GoogleApiService()
{
var langs = SUPPORTED.Split("\n")
.Select(x => x.Split(' '))
.ToDictionary(x => x[0].Trim(), x => x[1].Trim());
foreach (var (_, v) in langs.ToArray())
{
langs.Add(v, v);
}
Languages = langs;
}
}

View File

@@ -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.10"; public const string BOT_VERSION = "4.3.15";
public string Author public string Author
=> "Kwoth#2452"; => "Kwoth#2452";

View File

@@ -23,11 +23,7 @@ public static class MessageChannelExtensions
embeds: embeds is null embeds: embeds is null
? null ? null
: embeds as Embed[] ?? embeds.ToArray(), : embeds as Embed[] ?? embeds.ToArray(),
components: components, components: components);
options: new()
{
RetryMode = RetryMode.AlwaysRetry
});
} }
public static async Task<IUserMessage> SendAsync( public static async Task<IUserMessage> SendAsync(
@@ -155,27 +151,6 @@ public static class MessageChannelExtensions
string? footer = null) string? footer = null)
=> ch.SendAsync(eb, MessageType.Error, title, text, url, footer); => ch.SendAsync(eb, MessageType.Error, title, text, url, footer);
// weird stuff
public static Task<IUserMessage> SendTableAsync<T>(
this IMessageChannel ch,
string seed,
IEnumerable<T> items,
Func<T, string> howToPrint,
int columns = 3)
=> ch.SendMessageAsync($@"{seed}```css
{items.Chunk(columns)
.Select(ig => string.Concat(ig.Select(howToPrint)))
.Join("\n")}
```");
public static Task<IUserMessage> SendTableAsync<T>(
this IMessageChannel ch,
IEnumerable<T> items,
Func<T, string> howToPrint,
int columns = 3)
=> ch.SendTableAsync("", items, howToPrint, columns);
public static Task SendPaginatedConfirmAsync( public static Task SendPaginatedConfirmAsync(
this ICommandContext ctx, this ICommandContext ctx,
int currentPage, int currentPage,

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