Compare commits

...

94 Commits
5.0.8 ... 5.1.7

Author SHA1 Message Date
Kwoth
c95d1421c6 fix: fixed some command groups incorrectly showing up as modules 2024-08-08 12:15:53 +00:00
Kwoth
f631f16690 fix: fixed one of the migrations which was preventing some bots from starting 2024-08-07 17:45:25 +00:00
Kwoth
d397c2dce8 docs: fixed changelog 2024-08-07 16:46:33 +00:00
Kwoth
273816b8a4 docs: Updated changelog, upped version to 5.1.6 2024-08-07 15:46:53 +00:00
Kwoth
3094c3248b fix: fixed user id in afk message dm 2024-08-07 15:38:54 +00:00
Kwoth
ae6018f0e1 change: Afk messages should now be sent in DMs to prevent abuse 2024-08-07 15:29:39 +00:00
Kwoth
54adbedf9f add: doing .gencmdlist will now generate a data/commandlist.json which is the same as the file you receive in discord 2024-08-06 14:58:13 +00:00
Kwoth
eb29e34f17 dev: More cleanup
add: Added better placeholder links to some commands strings
2024-08-06 13:24:31 +00:00
Kwoth
4a402ee673 change: Removed a lot of old useless/broken commands. Improved .listserver. Full changes in changelog 2024-08-05 15:56:32 +00:00
Kwoth
764babdf06 dev: small cleanup, removed qodana 2024-08-05 14:13:23 +00:00
Kwoth
dd49d202b7 dev: small cleanup in clubs 2024-08-05 13:38:29 +00:00
Kwoth
a396c2d9dd dev: removed unused usings, no functional change 2024-08-05 13:32:15 +00:00
Kwoth
15fe8b5daf dev: Removed some unused code, no functional change 2024-08-05 13:18:44 +00:00
Kwoth
f8da25a6f3 fix: fixed docker build
dev: allow qodana to fail
2024-08-05 12:25:05 +00:00
Kwoth
383acba443 dev: added qodana, trying it out 2024-08-05 12:20:27 +00:00
Kwoth
690c03b396 dev: lmgtfy should now be properly shortened, small refactor of .shorten command 2024-08-04 17:58:04 +00:00
Kwoth
f4ed907134 fix: Possible fix for a broken timestamp tag in remind list 2024-08-03 13:53:42 +00:00
Kwoth
1b0badd8d8 docs: Updated changelog 2024-08-02 02:44:46 +00:00
Kwoth
2c3ada4710 dev: using official version of discord.net
docs: upped version to 5.1.5
dev: removed nuget.config as we no longer rely on myget
dev: Fixed some build warnings
2024-08-01 02:36:49 +00:00
Kwoth
0df3c1a4a1 dev: small cleanup 2024-08-01 01:07:38 +00:00
Kwoth
ac589e0461 change: .define slightly improved and refactored 2024-07-31 13:43:13 +00:00
Kwoth
8f181eed85 change: .wikia slightly changed and refactored 2024-07-31 10:55:34 +00:00
Kwoth
6fefce4c4d dev: refactored .bible and .quran, moved to their own folder and created ReligiousApiService for their logic 2024-07-30 14:18:44 +00:00
Kwoth
d9e080f4b9 change: .reroadd error message improved 2024-07-29 05:33:49 +00:00
Kwoth
762a2eca1f change: .keep will also automatically trigger for any new server the bot joins 2024-07-29 04:31:09 +00:00
Kwoth
2fba771681 docs: slight update to lmgtfy command's strings 2024-07-28 19:53:14 +00:00
Kwoth
b5e2b6f483 dev: v3 .catfact 2024-07-28 10:10:19 +00:00
Kwoth
17e5ff8b89 dev: slightly updated .time 2024-07-28 10:02:06 +00:00
Kwoth
3d287b2afa remove: Removed .rip command 2024-07-28 09:50:06 +00:00
Kwoth
3f33274cec dev: Brough .wiki command to 2018 standards 2024-07-28 09:32:40 +00:00
Kwoth
ee9d8a51bf add: Added .keep command which will add the current guild to the list of keptguilds. This is needed for the future database purge. 2024-07-28 00:33:31 +00:00
Kwoth
80a7678a82 dev: Updated imagesharp package 2024-07-26 22:36:19 +00:00
Kwoth
de8a0e2207 add: Added admin-only .warndelete command 2024-07-26 22:34:53 +00:00
Kwoth
122b3ae0d9 docs: Vastly simplified medusa creation using dotnet templates 2024-07-25 02:10:51 +00:00
Kwoth
b4307f9123 dev: reverted ci change, medusa will keep using myget 2024-07-25 00:59:37 +00:00
Kwoth
5ae18ba1bf dev: added nuget source url to ci 2024-07-24 03:41:16 +00:00
Kwoth
44c8c9f459 dev: medusa package should now be published to nuget 2024-07-23 23:28:31 +00:00
Kwoth
4e177ff198 docs: Updated medusa docs a little bit 2024-07-22 01:26:11 +00:00
Kwoth
ad679a996d docs: Updated some command strings 2024-07-21 23:57:41 +00:00
Kwoth
7d86a5e3eb docs: Updated bot strings to clarify all half and x% usage 2024-07-20 23:07:05 +00:00
Kwoth
214c9a383c change: Updated some bet descriptions to include 'all' 'half' usage instructions 2024-07-20 22:57:15 +00:00
Kwoth
f77e1c6b8c fix: You can once again disable cleverbot responses using fake 'cleverbot:response' module name in permission commands 2024-07-19 16:57:37 +00:00
Kwoth
7e784b9507 change: show a message when .prune fails due to already running error 2024-07-18 13:24:37 +00:00
Kwoth
7a14991ed6 fix: Possible fix for .prune getting stuck after unsuccessful limit hit 2024-07-18 13:20:07 +00:00
Kwoth
4c5c2d7f6e .cleverbot should be available on the public bot now 2024-07-17 15:18:56 +00:00
Kwoth
87b90b47ce added: Added .afk <msg>? command which sets an afk message which will trigger whenever someone pings a user. 2024-07-16 19:09:10 +00:00
Kwoth
9f060243f0 change: .exexport will now send you the file in DMs, to avoid incident.
change: .exexport will now have a timestamped name along with the server id
2024-07-14 16:11:04 +00:00
Kwoth
d3ab32a7ac fix: .coins will no longer show double minus sign for negative changes 2024-07-14 13:38:56 +00:00
Kwoth
7bd081b7cf docs: Updated CHANGELOG.md with the openai changes 2024-07-13 16:02:28 +00:00
Kwoth
3a5b482884 docs: Updated changelog, upped version to 5.1.4
fix: Fixed pipeline
2024-07-13 15:49:52 +00:00
Kwoth
db66264bc6 add: Added support for any openai compatible api for the chatterbot feature
change: Changed games.yml to allow input of the apiUrl (needs to be openai compatible) and modelName as a string.
2024-07-13 15:36:49 +00:00
Kwoth
ae1ddd82d0 fix: Fixed .waifugift help string 2024-07-12 20:50:56 +00:00
Kwoth
8523abd6f1 add: Added multiplier option for waifu gifts. For example .waifugift 3xRose @user will give that user 3 roses 2024-07-12 17:18:47 +00:00
Kwoth
e1892c4ff4 remove: Removed selfhost button from .donate, no idea why it was there in the first place 2024-07-12 16:24:49 +00:00
Kwoth
a50a7b3b0e add: Added user param to .bank balance command, but only Bot Owner can use it 2024-07-11 18:46:48 +00:00
Kwoth
9d2268a925 fix: fixed .clubinfo rank 2024-07-11 00:53:52 +00:00
Kwoth
d77a86c08b add: Added club rank in .clubinfo 2024-07-10 08:31:01 +00:00
Kwoth
d605f685cf docs: Updated command strings to clarify .say and .send usage 2024-07-08 16:24:59 +00:00
Kwoth
bbc1fd28c2 change: .coins cache will expire after an hour 2024-07-07 10:09:48 +00:00
Kwoth
cff8a258d0 fix: .coins will respect the user specified page now 2024-07-07 10:07:35 +00:00
Kwoth
1d760a548e fix: cache .coins result to avoid ratelimits 2024-07-07 10:05:01 +00:00
Kwoth
25fa8a3852 add: Added .coins command which lists top 10 cryptos ordered by marketcap, paginated with 10 per page 2024-07-07 05:23:59 +00:00
Kwoth
ca13684c0d docs: Updated CHANGELOG.
dev: upped version to 5.1.3
2024-07-06 09:05:34 +00:00
Kwoth
0ad6b741e7 add: chatterbot, (but not prompting) will also trigger if the user replies to a bot's message 2024-07-05 04:55:33 +00:00
Kwoth
4ce756d760 fix: if prompt ai responds with chat, it will prevent further further execution of tthat input to prevent the same query running multiple times 2024-07-05 04:48:00 +00:00
Kwoth
5f2813d3af fix: clean the bot username too from invalid characters for openai 2024-07-05 03:36:04 +00:00
Kwoth
1b7458529c fix: bot should now trim the invalid characters from openai message usernames 2024-07-05 03:33:04 +00:00
Kwoth
9c9c8d7490 fix: fixed pipeline 2024-06-29 08:43:50 +00:00
Kwoth
2700bfdce8 fix: Fixed .stickeradd, it now properly supports 300x300 image uploads. closes #434 2024-06-29 08:42:47 +00:00
Kwoth
5498c5ce3f add: Added .quran command, which will show the provided ayah in english and arabic, including recitation by Alafasy 2024-06-29 07:14:13 +00:00
Kwoth
6f444a8da0 fix: fixed pipeline, no functional change 2024-06-29 01:36:38 +00:00
Kwoth
454c14eee1 docs: Updated changelog 2024-06-29 01:25:48 +00:00
Kwoth
30aa8e8186 fix: fixed honeypot not unbanning and pruning 2024-06-29 01:24:14 +00:00
Kwoth
2ca141810c docs: updated pagination, upped version to 5.1.1 2024-06-28 05:17:40 +00:00
Kwoth
9da8e4f1c1 fix: Fixed pagination, for real this time, closes #435,
dev: removed some old creds migration code, incremented creds version
2024-06-27 19:48:39 +00:00
Kwoth
ef471c32bb add: Added .honeypot command
fix: Fixed .betdraw not respecting max bet limit
2024-06-27 04:50:55 +00:00
Kwoth
49d557caec fix: removed an extra in remind string 2024-06-25 16:01:59 +00:00
Kwoth
4366f908f3 dev: Using new stopwatch, some cleanup. No functional change 2024-06-25 07:09:16 +00:00
Kwoth
237e66495b fix: Potential fix for the invalid syntax redis issue, unless the issue is caused by the older version of redis on the server itself 2024-06-25 03:32:21 +00:00
Kwoth
15d4117d7f docs: Updated changelog, version upped to 5.1.0 2024-06-25 00:48:29 +00:00
Kwoth
d7daa5f2af fix: pipeline 2024-06-17 04:18:06 +00:00
Kwoth
b08ff62406 Merge branch 'v5' into 'v5'
Fix for timely reminder text

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

Closes #431

See merge request Kwoth/nadekobot!331
2024-06-13 18:54:21 +00:00
Kwoth
ab93380d7c 5.1 2024-06-13 18:54:21 +00:00
Kwoth
a6adf73ecf fix: Fixed currency rewards in patronage service 2024-05-29 09:54:05 +00:00
Kwoth
d9e52038ac fix: re-added cleanup code for invalid database data in sqlite 2024-05-28 20:34:11 +00:00
Kwoth
be7ddc732b fix: Forgot to remove a testing condition in patreonclient 2024-05-28 20:13:31 +00:00
Kwoth
79b25c8a41 dev: assembly versions for published release files should be correct now 2024-05-25 19:35:31 +00:00
Kwoth
12fa209555 dev: fixed version number 2024-05-21 14:09:26 +00:00
258 changed files with 33282 additions and 4234 deletions

View File

@@ -30,12 +30,17 @@ variables:
build:
stage: build
script:
- "dotnet publish -c Release -r linux-x64 --self-contained -o $LINUX_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r linux-arm64 --self-contained -o $LINUX_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-x64 --self-contained -o $WIN_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-arm64 --self-contained -o $WIN_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-x64 --self-contained -o $MACOS_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-arm64 --self-contained -o $MACOS_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- |
VERSION_STRING=""
if [ -n "$CI_COMMIT_TAG" ]; then
VERSION_STRING="-p:Version=$CI_COMMIT_TAG"
fi
- "dotnet publish -c Release -r linux-x64 --self-contained $VERSION_STRING -o $LINUX_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r linux-arm64 --self-contained $VERSION_STRING -o $LINUX_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-x64 --self-contained $VERSION_STRING -o $WIN_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r win-arm64 --self-contained $VERSION_STRING -o $WIN_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-x64 --self-contained $VERSION_STRING -o $MACOS_X64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
- "dotnet publish -c Release -r osx-arm64 --self-contained $VERSION_STRING -o $MACOS_ARM64_OUTPUT_DIR src/NadekoBot/NadekoBot.csproj"
artifacts:
paths:
- "$LINUX_X64_OUTPUT_DIR/"

View File

@@ -2,6 +2,159 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [5.1.7] - 08.08.2024
### Fixed
- Fixed some command groups incorrectly showing up as modules
## [5.1.6] - 07.08.2024
### Added
- `.serverlist` is now paginated
### Changed
- `.listservers` renamed to `.serverlist`
### Fixed
- `.afk` messages can no longer ping, and the response is moved to DMs to avoid abuse
- Possible fix for `.remind` timestamp
### Removed
- Removed old bloat / semi broken / dumb commands
- `.memelist` / `.memegen` (too inconvenient to use)
- `.activity` (useless owner-only command)
- `.rafflecur` (Just use raffle and then award manually instead)
- `.rollduel` (we had this command?)
- You can no longer bet on `.connect4`
- `.economy` Removed.
- Was buggy and didn't really show the real state of the economy.
- It might come back improved in the future
- `.mal` Removed. Useless information / semi broken
## [5.1.5] - 01.08.2024
### Added
- Added: Added a `.afk <msg>?` command which sets an afk message which will trigger whenever someone pings you
- Message will when you type a message in any channel that the bot sees, or after 8 hours, whichever comes first
- The specified message will be prefixed with "The user is afk: "
- The afk message will disappear 30 seconds after being triggered
### Changed
- Bot now shows a message when .prune fails due to already running error
- Updated some bet descriptions to include 'all' 'half' usage instructions
- Updated some command strings
- dev: Vastly simplified medusa creation using dotnet templates, docs updated
- Slight refactor of .wiki, time, .catfact, .wikia, .define, .bible and .quran commands, no significant change in functionality
### Fixed
- .coins will no longer show double minus sign for negative changes
- You can once again disable cleverbot responses using fake 'cleverbot:response' module name in permission commands
### Removed
- Removed .rip command
## [5.1.4] - 13.07.2024
### Added
- Added `.coins` command which lists top 10 cryptos ordered by marketcap
- Added Clubs rank in the leaderboard to `.clubinfo`
- Bot owners can now check other people's bank balance (Not server owners, only bot owner, the person who is hosting the bot)
- You can now send multiple waifu gifts at once to waifus. For example `.waifugift 3xRose @user` will give that user 3 roses
- The format is `<NUMBER>x<ITEM>`, no spaces
- Added `.boosttest` command
- Added support for any openai compatible api for the chatterbot feature change:
- Changed games.yml to allow input of the apiUrl (needs to be openai compatible) and modelName as a string.
### Changed
- Updated command strings to clarify `.say` and `.send` usages
### Fixed
- Fixed `.waifugift` help string
### Removed
- Removed selfhost button from `.donate` command, no idea why it was there in the first place
## [5.1.3] - 06.07.2024
### Added
- Added `.quran` command, which will show the provided ayah in english and arabic, including recitation by Alafasy
### Changed
- Replying to the bot's message in the channel where chatterbot is enabled will also trigger the ai response, as if you pinged the bot. This only works for chatterbot, but not for nadeko ai command prompts
### Fixed
- Fixed `.stickeradd` it now properly supports 300x300 image uploads.
- Bot should now trim the invalid characters from chatterbot usernames to avoid openai errors
- Fixed prompt triggering chatterbot responses twice
## [5.1.2] - 29.06.2024
### Fixed
- Fixed `.honeypot` not unbanning and not pruning messages
## [5.1.1] - 27.06.2024
### Added
- Added `.honeypot` command, which automatically softbans (ban and immediate unban) any user who posts in that channel.
- Useful to auto softban bots who spam every channel upon joining
- Users who run commands or expressions won't be softbanned.
- Users who have ban member permissions are also excluded.
### Fixed
- Fixed `.betdraw` not respecting maxbet
- Fixed `.xpshop` pagination for real this time?
## [5.1.0] - 25.06.2024
### Added
- Added `.prompt` command, Nadeko Ai Assistant
- You can send natural language questions, queries or execute commands. For example "@Nadeko how's the weather in paris" and it will return `.we Paris` and run it for you.
- In case the bot can't execute a command using your query, It will fall back to your chatter bot, in case you have it enabled in data/games.yml. (Cleverbot or chatgpt)
- (It's far from perfect so please don't ask the bot to do dangerous things like banning or pruning)
- Requires Patreon subscription, after which you'll be able to run it on global @Nadeko bot.
- Selfhosters: If you're selfhosting, you also will need to acquire the api key from <https://dashy.nadeko.bot/me> after pledging on patreon and put it in nadekoAiToken in creds.yml
- Added support for `gpt-4o` in `data/games.yml`
### Changed
- Remind will now show a timestamp tag for durations
- Only `Gpt35Turbo` and `Gpt4o` are valid inputs in games.yml now
- `data/patron.yml` changed. It now has limits. The entire feature limit system has been reworked. Your previous settings will be reset
- A lot of updates to bot strings (thanks Ene)
- Improved cleanup command to delete a lot more data once cleanup is ran, not only guild configs (please don't use this command unless you have your database bakced up and you know 100% what you're doing)
### Fixed
- Fixed xp bg buy button not working, and possibly some other buttons too
- Fixed shopbuy %user% placeholders and updated help text
- All .feed overloads should now work"
- `.xpexclude` should will now work with forums too. If you exclude a forum you won't be able to gain xp in any of the threads.
- Fixed remind not showing correct time (thx cata)
### Removed
- Removed PoE related commands
- dev: Removed patron quota data from the database, it will now be stored in redis
## [5.0.8] - 21.05.2024
### Added

View File

@@ -8,7 +8,6 @@ COPY src/NadekoBot/*.csproj src/NadekoBot/
COPY src/NadekoBot.Coordinator/*.csproj src/NadekoBot.Coordinator/
COPY src/NadekoBot.Generators/*.csproj src/NadekoBot.Generators/
COPY src/NadekoBot.Voice/*.csproj src/NadekoBot.Voice/
COPY NuGet.Config ./
# Restore the dependencies for the NadekoBot project
RUN dotnet restore src/NadekoBot/

View File

@@ -12,7 +12,6 @@ ProjectSection(SolutionItems) = preProject
README.md = README.md
.gitlab-ci.yml = .gitlab-ci.yml
Dockerfile = Dockerfile
NuGet.Config = NuGet.Config
migrate.ps1 = migrate.ps1
remove-migration.ps1 = remove-migration.ps1
EndProjectSection

View File

@@ -1,6 +0,0 @@
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="nadeko.bot" value="https://www.myget.org/F/nadeko/api/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

View File

@@ -1,8 +1,55 @@
# Creating A Medusa
## Theory
## Getting started
### Introduction
This section will guide you through how to create a simple custom medusa. You can find the entirety of this code hosted [here](https://gitlab.com/nadeko/example_medusa)
#### Prerequisite
- [.net8 sdk](https://dotnet.microsoft.com/en-us/download) installed
- Optional: use [vscode](https://code.visualstudio.com/download) to write code
#### Guide
- Open your favorite terminal and navigate to a folder where you will keep your project .
- Create a new folder and move into it
- `mkdir example_medusa `
- `cd example_medusa`
- Install nadeko-medusa template
- `dotnet new install nadeko-medusa`
- Make a new Nadeko Medusa project
- `dotnet new nadeko-medusa`
### Build it
- Build your Medusa into a dll that Nadeko can load. In your terminal, type:
- `dotnet publish -o bin/medusae/example_medusa /p:DebugType=embedded`
- Done. You can now try it out in action.
### Try it out
- Copy the `bin/medusae/example_medusa` folder into your NadekoBot's `data/medusae/` folder. (Nadeko version 4.1.0+)
- Load it with `.meload example_medusa`
- In the channel your bot can see, run the following commands to try it out
- `.hello` and
- `.hello @<someone>`
- Check its information with
- `.meinfo example_medusa`
- Unload it
- `.meunload example_medusa`
- :tada: Congrats! You've just made your first medusa! :tada:
## Theory
Medusa system allows you to write independent medusae (known as "modules", "cogs" or "plugins" in other software) which you can then load, unload and update at will without restarting the bot.
@@ -99,9 +146,9 @@ If you don't want any auxiliary files, and you don't want to bother making new .
If you update your response strings .yml file(s) while the medusa is loaded and running, running `.stringsreload` will reload the responses without the need to reload the medusa or restart the bot.
#### Config
#### Bot medusa config file
- Medusa config is kept in `medusae/medusa.yml` file
- Medusa config is kept in `data/medusae/medusa.yml` file in NadekoBot installation folder
- At the moment this config only keeps track of which medusae are currently loaded (they will also be always loaded at startup)
- If a medusa is causing issues and you're unable to unload it, you can remove it from the `loaded:` list in this config file and restart the bot. It won't be loaded next time the bot is started up
@@ -115,138 +162,4 @@ To make sure your medusa can be properly unloaded/reloaded you must:
- If you are still having issues, you can always run `.meunload` followed by a bot restart, or if you want to find what is causing the medusa unloadability issues, you can check the [microsoft's assembly unloadability debugging guide](https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability)
## Practice
This section will guide you through how to create a simple custom medusa. You can find the entirety of this code hosted [here](https://gitlab.com/nadeko/example_medusa)
#### Prerequisite
- [.net6 sdk](https://dotnet.microsoft.com/en-us/download) installed
- Optional: use [vscode](https://code.visualstudio.com/download) to write code
#### Guide
- Open your favorite terminal and navigate to a folder where you will keep your project .
- Create a new folder
- `mkdir example_medusa`
- Create a new .net class library
- `dotnet new classlib`
- Open the current folder with your favorite editor/IDE. In this case we'll use VsCode
- `code .`
- Remove the `Class1.cs` file
- Replace the contents of the `.csproj` file with the following contents
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<!-- Reduces some boilerplate in your .cs files -->
<ImplicitUsings>enable</ImplicitUsings>
<!-- Use latest .net features -->
<LangVersion>preview</LangVersion>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<GenerateRequiresPreviewFeaturesAttribute>true</GenerateRequiresPreviewFeaturesAttribute>
<!-- tell .net that this library will be used as a plugin -->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<!-- Base medusa package. You MUST reference this in order to have a working medusa -->
<!-- Also, this package comes from MyGet, which requires you to have a NuGet.Config file next to your .csproj -->
<PackageReference Include="Nadeko.Medusa" Version="4.3.9">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- Note: If you want to use NadekoBot services etc... You will have to manually clone
the https://gitlab.com/kwoth/nadekobot repo locally and reference the NadekoBot.csproj because there is no NadekoBot package atm.
It is strongly recommended that you checkout a specific tag which matches your version of nadeko,
as there could be breaking changes even between minor versions of NadekoBot.
For example if you're running NadekoBot 4.1.0 locally for which you want to create a medusa for,
you should do "git checkout 4.1.0" in your NadekoBot solution and then reference the NadekoBot.csproj
-->
</ItemGroup>
<!-- Copy shortcut and full strings to output (if they exist) -->
<ItemGroup>
<None Update="res.yml;cmds.yml;strings/**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
```
- Create a `MySnek.cs` file and add the following contents
```cs
using Nadeko.Snake;
using NadekoBot;
using Discord;
public sealed class MySnek : Snek
{
[cmd]
public async Task Hello(AnyContext ctx)
{
await ctx.Channel.SendMessageAsync($"Hello everyone!");
}
[cmd]
public async Task Hello(AnyContext ctx, IUser target)
{
await ctx.ConfirmLocalizedAsync("hello", target);
}
}
```
- Create `res.yml` and `cmds.yml` files with the following contents
`res.yml`
```yml
medusa.description: "This is my medusa's description"
hello: "Hello {0}, from res.yml!"
```
`cmds.yml`
```yml
hello:
desc: "This is a basic hello command"
args:
- ""
- "@Someone"
```
- Add `NuGet.Config` file which will let you use the base Nadeko.Medusa package. This file should always look like this and you shouldn't change it
```xml
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="nadeko.bot" value="https://www.myget.org/F/nadeko/api/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
```
### Build it
- Build your Medusa into a dll that Nadeko can load. In your terminal, type:
- `dotnet publish -o bin/medusae/example_medusa /p:DebugType=embedded`
- Done. You can now try it out in action.
### Try it out
- Copy the `bin/medusae/example_medusa` folder into your NadekoBot's `data/medusae/` folder. (Nadeko version 4.1.0+)
- Load it with `.meload example_medusa`
- In the channel your bot can see, run the following commands to try it out
- `.hello` and
- `.hello @<someone>`
- Check its information with
- `.meinfo example_medusa`
- Unload it
- `.meunload example_medusa`
- Congrats! You've just made your first medusa!

View File

@@ -1,5 +1,4 @@
using Discord;
using NadekoBot;
namespace NadekoBot.Medusa;

View File

@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net.Core" Version="3.204.0" />
<PackageReference Include="Discord.Net.Core" Version="3.15.3" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="YamlDotNet" Version="15.1.4" />
</ItemGroup>

View File

@@ -77,7 +77,6 @@ csharp_style_var_when_type_is_apparent = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = true:suggestion
@@ -181,9 +180,9 @@ dotnet_naming_rule.private_readonly_field.symbols = private_readonly_field
dotnet_naming_rule.private_readonly_field.style = begins_with_underscore
dotnet_naming_rule.private_readonly_field.severity = warning
dotnet_naming_rule.private_field.symbols = private_field
dotnet_naming_rule.private_field.style = camel_case
dotnet_naming_rule.private_field.severity = warning
# dotnet_naming_rule.private_field.symbols = private_field
# dotnet_naming_rule.private_field.style = camel_case
# dotnet_naming_rule.private_field.severity = warning
dotnet_naming_rule.const_fields.symbols = const_fields
dotnet_naming_rule.const_fields.style = all_upper

View File

@@ -88,12 +88,12 @@ public sealed class Bot : IBot
public IReadOnlyList<ulong> GetCurrentGuildIds()
=> Client.Guilds.Select(x => x.Id).ToList();
=> Client.Guilds.Select(x => x.Id).ToList().ToList();
private void AddServices()
{
var startingGuildIdList = GetCurrentGuildIds();
var sw = Stopwatch.StartNew();
var startTime = Stopwatch.GetTimestamp();
var bot = Client.CurrentUser;
using (var uow = _db.GetDbContext())
@@ -114,7 +114,7 @@ public sealed class Bot : IBot
// svcs.Components.Remove<IPlanner, Planner>();
// svcs.Components.Add<IPlanner, RemovablePlanner>();
svcs.AddSingleton<IBotCredentials, IBotCredentials>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<IBotCredentials>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<DbService, DbService>(_db);
svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
svcs.AddSingleton<DiscordSocketClient>(Client);
@@ -160,9 +160,8 @@ public sealed class Bot : IBot
{
LoadTypeReaders(a);
}
sw.Stop();
Log.Information("All services loaded in {ServiceLoadTime:F2}s", sw.Elapsed.TotalSeconds);
Log.Information("All services loaded in {ServiceLoadTime:F2}s", Stopwatch.GetElapsedTime(startTime) .TotalSeconds);
}
private void LoadTypeReaders(Assembly assembly)
@@ -259,7 +258,7 @@ public sealed class Bot : IBot
if (ShardId == 0)
await _db.SetupAsync();
var sw = Stopwatch.StartNew();
var startTime = Stopwatch.GetTimestamp();
await LoginAsync(_creds.Token);
@@ -274,8 +273,7 @@ public sealed class Bot : IBot
Helpers.ReadErrorAndExit(9);
}
sw.Stop();
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s", Client.ShardId, sw.Elapsed.TotalSeconds);
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s", Client.ShardId, Stopwatch.GetElapsedTime(startTime).TotalSeconds);
var commandHandler = Services.GetRequiredService<CommandHandler>();
// start handling messages received in commandhandler

View File

@@ -182,15 +182,6 @@ public static class GuildConfigExtensions
.SelectMany(gc => gc.FollowedStreams)
.ToList();
public static void SetCleverbotEnabled(this DbSet<GuildConfig> configs, ulong id, bool cleverbotEnabled)
{
var conf = configs.FirstOrDefault(gc => gc.GuildId == id);
if (conf is null)
return;
conf.CleverbotEnabled = cleverbotEnabled;
}
public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId)
{

View File

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

View File

@@ -1,6 +1,4 @@
#nullable disable
using System.ComponentModel.DataAnnotations.Schema;
namespace NadekoBot.Db.Models;

View File

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

View File

@@ -53,14 +53,13 @@ public abstract class NadekoContext : DbContext
public DbSet<PatronUser> Patrons { get; set; }
public DbSet<PatronQuota> PatronQuotas { get; set; }
public DbSet<StreamOnlineMessage> StreamOnlineMessages { get; set; }
public DbSet<StickyRole> StickyRoles { get; set; }
public DbSet<TodoModel> Todos { get; set; }
public DbSet<ArchivedTodoListModel> TodosArchive { get; set; }
public DbSet<HoneypotChannel> HoneyPotChannels { get; set; }
// todo add guild colors
// public DbSet<GuildColors> GuildColors { get; set; }
@@ -597,16 +596,6 @@ public abstract class NadekoContext : DbContext
});
// quotes are per user id
modelBuilder.Entity<PatronQuota>(pq =>
{
pq.HasIndex(x => x.UserId).IsUnique(false);
pq.HasKey(x => new
{
x.UserId,
x.FeatureType,
x.Feature
});
});
#endregion

View File

@@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Db.Models;
namespace NadekoBot.Migrations;
@@ -50,5 +49,9 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
""");
builder.Sql("""
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
""");
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1388,6 +1388,25 @@ namespace NadekoBot.Migrations.Mysql
b.ToTable("guildconfigs", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.HoneypotChannel", b =>
{
b.Property<ulong>("GuildId")
.ValueGeneratedOnAdd()
.HasColumnType("bigint unsigned")
.HasColumnName("guildid");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<ulong>("GuildId"));
b.Property<ulong>("ChannelId")
.HasColumnType("bigint unsigned")
.HasColumnName("channelid");
b.HasKey("GuildId")
.HasName("pk_honeypotchannels");
b.ToTable("honeypotchannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
@@ -1718,41 +1737,6 @@ namespace NadekoBot.Migrations.Mysql
b.ToTable("expressions", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
{
b.Property<ulong>("UserId")
.HasColumnType("bigint unsigned")
.HasColumnName("userid");
b.Property<int>("FeatureType")
.HasColumnType("int")
.HasColumnName("featuretype");
b.Property<string>("Feature")
.HasColumnType("varchar(255)")
.HasColumnName("feature");
b.Property<uint>("DailyCount")
.HasColumnType("int unsigned")
.HasColumnName("dailycount");
b.Property<uint>("HourlyCount")
.HasColumnType("int unsigned")
.HasColumnName("hourlycount");
b.Property<uint>("MonthlyCount")
.HasColumnType("int unsigned")
.HasColumnName("monthlycount");
b.HasKey("UserId", "FeatureType", "Feature")
.HasName("pk_patronquotas");
b.HasIndex("UserId")
.HasDatabaseName("ix_patronquotas_userid");
b.ToTable("patronquotas", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
{
b.Property<ulong>("UserId")

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1387,6 +1387,23 @@ namespace NadekoBot.Migrations.PostgreSql
b.ToTable("guildconfigs", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.HoneypotChannel", b =>
{
b.Property<decimal>("GuildId")
.ValueGeneratedOnAdd()
.HasColumnType("numeric(20,0)")
.HasColumnName("guildid");
b.Property<decimal>("ChannelId")
.HasColumnType("numeric(20,0)")
.HasColumnName("channelid");
b.HasKey("GuildId")
.HasName("pk_honeypotchannels");
b.ToTable("honeypotchannels", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
@@ -1717,41 +1734,6 @@ namespace NadekoBot.Migrations.PostgreSql
b.ToTable("expressions", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
{
b.Property<decimal>("UserId")
.HasColumnType("numeric(20,0)")
.HasColumnName("userid");
b.Property<int>("FeatureType")
.HasColumnType("integer")
.HasColumnName("featuretype");
b.Property<string>("Feature")
.HasColumnType("text")
.HasColumnName("feature");
b.Property<long>("DailyCount")
.HasColumnType("bigint")
.HasColumnName("dailycount");
b.Property<long>("HourlyCount")
.HasColumnType("bigint")
.HasColumnName("hourlycount");
b.Property<long>("MonthlyCount")
.HasColumnType("bigint")
.HasColumnName("monthlycount");
b.HasKey("UserId", "FeatureType", "Feature")
.HasName("pk_patronquotas");
b.HasIndex("UserId")
.HasDatabaseName("ix_patronquotas_userid");
b.ToTable("patronquotas", (string)null);
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
{
b.Property<decimal>("UserId")

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View File

@@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
@@ -11,6 +10,8 @@ namespace NadekoBot.Migrations
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
MigrationQueries.GuildConfigCleanup(migrationBuilder);
migrationBuilder.DropForeignKey(
name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId",
table: "AntiRaidSetting");

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1033,6 +1033,20 @@ namespace NadekoBot.Migrations
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Db.Models.HoneypotChannel", b =>
{
b.Property<ulong>("GuildId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.HasKey("GuildId");
b.ToTable("HoneyPotChannels");
});
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
@@ -1279,33 +1293,6 @@ namespace NadekoBot.Migrations
b.ToTable("Expressions");
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
{
b.Property<ulong>("UserId")
.HasColumnType("INTEGER");
b.Property<int>("FeatureType")
.HasColumnType("INTEGER");
b.Property<string>("Feature")
.HasColumnType("TEXT");
b.Property<uint>("DailyCount")
.HasColumnType("INTEGER");
b.Property<uint>("HourlyCount")
.HasColumnType("INTEGER");
b.Property<uint>("MonthlyCount")
.HasColumnType("INTEGER");
b.HasKey("UserId", "FeatureType", "Feature");
b.HasIndex("UserId");
b.ToTable("PatronQuotas");
});
modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
{
b.Property<ulong>("UserId")

View File

@@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Administration._common.results;

View File

@@ -4,7 +4,6 @@ using System.Net;
using System.Threading.Channels;
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
namespace NadekoBot.Modules.Administration.Services;

View File

@@ -1,9 +1,11 @@
namespace NadekoBot.Modules.Administration.DangerousCommands;
using NadekoBot.Modules.Administration.DangerousCommands;
namespace NadekoBot.Modules.Administration;
public partial class Administration
{
[Group]
public class CleanupCommands : CleanupModuleBase
public partial class CleanupCommands : CleanupModuleBase
{
private readonly ICleanupService _svc;
@@ -27,5 +29,15 @@ public partial class Administration
.Confirm($"{result.GuildCount} guilds' data remain in the database.")
.SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task Keep()
{
var result = await _svc.KeepGuild(Context.Guild.Id);
await Response().Text("This guild's bot data will be saved.").SendAsync();
}
}
}

View File

@@ -1,6 +1,7 @@
using LinqToDB;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
using LinqToDB.Mapping;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
@@ -61,23 +62,93 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
}));
}
// delete guild configs
await ctx.GetTable<GuildConfig>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete guild xp
await ctx.GetTable<UserXpStats>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete expressions
await ctx.GetTable<NadekoExpression>()
.Where(x => x.GuildId != null
&& !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId.Value))
.DeleteAsync();
// delete quotes
await ctx.GetTable<Quote>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete planted currencies
await ctx.GetTable<PlantedCurrency>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete image only channels
await ctx.GetTable<ImageOnlyChannel>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete reaction roles
await ctx.GetTable<ReactionRoleV2>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
// delete ignored users
await ctx.GetTable<DiscordPermOverride>()
.Where(x => x.GuildId != null
&& !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId.Value))
.DeleteAsync();
// delete perm overrides
await ctx.GetTable<DiscordPermOverride>()
.Where(x => x.GuildId != null
&& !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId.Value))
.DeleteAsync();
// delete repeaters
await ctx.GetTable<Repeater>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
return new()
{
GuildCount = guildIds.Keys.Count,
};
}
public async Task<bool> KeepGuild(ulong guildId)
{
await using var db = _db.GetDbContext();
await using var ctx = db.CreateLinqToDBContext();
var table = ctx.CreateTable<KeptGuilds>(tableOptions: TableOptions.CheckExistence);
if (await table.AnyAsyncLinqToDB(x => x.GuildId == guildId))
return false;
await table.InsertAsync(() => new()
{
GuildId = guildId
});
return true;
}
private ValueTask OnKeepReport(KeepReport report)
{
guildIds[report.ShardId] = report.GuildIds;
@@ -87,11 +158,18 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
public async Task OnReadyAsync()
{
await _pubSub.Sub(_keepTriggerKey, OnKeepTrigger);
_client.JoinedGuild += ClientOnJoinedGuild;
if (_client.ShardId == 0)
await _pubSub.Sub(_keepReportKey, OnKeepReport);
}
private async Task ClientOnJoinedGuild(SocketGuild arg)
{
await KeepGuild(arg.Id);
}
private ValueTask OnKeepTrigger(bool arg)
{
_pubSub.Pub(_keepReportKey,
@@ -103,4 +181,10 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
return default;
}
}
public class KeptGuilds
{
[PrimaryKey]
public ulong GuildId { get; set; }
}

View File

@@ -3,4 +3,5 @@
public interface ICleanupService
{
Task<KeepResult?> DeleteMissingGuildDataAsync();
Task<bool> KeepGuild(ulong guildId);
}

View File

@@ -1,6 +1,4 @@
#nullable disable
using NadekoBot.Db;
namespace NadekoBot.Modules.Administration.Services;
public class GameVoiceChannelService : INService

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Administration.Services;

View File

@@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using System.Threading.Channels;

View File

@@ -45,20 +45,23 @@ public partial class Administration
var progressMsg = await Response().Pending(strs.prune_progress(0, 100)).SendAsync();
var progress = GetProgressTracker(progressMsg);
PruneResult result;
if (opts.Safe)
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
100,
x => x.Author.Id == user.Id && !x.IsPinned,
progress,
opts.After);
else
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
100,
x => x.Author.Id == user.Id,
progress,
opts.After);
ctx.Message.DeleteAfter(3);
await SendResult(result);
await progressMsg.DeleteAsync();
}
@@ -83,19 +86,21 @@ public partial class Administration
var progressMsg = await Response().Pending(strs.prune_progress(0, count)).SendAsync();
var progress = GetProgressTracker(progressMsg);
PruneResult result;
if (opts.Safe)
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
x => !x.IsPinned && x.Id != progressMsg.Id,
progress,
opts.After);
else
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
x => x.Id != progressMsg.Id,
progress,
opts.After);
await SendResult(result);
await progressMsg.DeleteAsync();
}
@@ -155,9 +160,10 @@ public partial class Administration
var progressMsg = await Response().Pending(strs.prune_progress(0, count)).SendAsync();
var progress = GetProgressTracker(progressMsg);
PruneResult result;
if (opts.Safe)
{
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks && !m.IsPinned,
progress,
@@ -166,7 +172,7 @@ public partial class Administration
}
else
{
await _service.PruneWhere((ITextChannel)ctx.Channel,
result = await _service.PruneWhere((ITextChannel)ctx.Channel,
count,
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks,
progress,
@@ -174,6 +180,7 @@ public partial class Administration
);
}
await SendResult(result);
await progressMsg.DeleteAsync();
}
@@ -194,5 +201,27 @@ public partial class Administration
await Response().Confirm(strs.prune_cancelled).SendAsync();
}
private async Task SendResult(PruneResult result)
{
switch (result)
{
case PruneResult.Success:
break;
case PruneResult.AlreadyRunning:
var msg = await Response().Pending(strs.prune_already_running).SendAsync();
msg.DeleteAfter(5);
break;
case PruneResult.FeatureLimit:
var msg2 = await Response().Pending(strs.feature_limit_reached_owner).SendAsync();
msg2.DeleteAfter(10);
break;
default:
Log.Error("Unhandled result received in prune: {Result}", result);
await Response().Error(strs.error_occured).SendAsync();
break;
}
}
}
}

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
#nullable disable
using NadekoBot.Modules.Patronage;
using NadekoBot.Db.Models;
using OneOf;
using OneOf.Types;
@@ -18,7 +17,7 @@ public interface IReactionRoleService
/// <param name="group"></param>
/// <param name="levelReq"></param>
/// <returns>The result of the operation</returns>
Task<OneOf<Success, FeatureLimit>> AddReactionRole(
Task<OneOf<Success, Error>> AddReactionRole(
IGuild guild,
IMessage msg,
string emote,

View File

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

View File

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

View File

@@ -3,7 +3,6 @@ using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using NadekoBot.Db.Models;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
namespace NadekoBot.Modules.Administration;

View File

@@ -546,7 +546,7 @@ public partial class Administration
text = await repSvc.ReplaceAsync(text, repCtx);
await Response().Channel(ch).Text(text).SendAsync();
await ctx.OkAsync();;
await ctx.OkAsync();
}
[Cmd]

View File

@@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Administration.Services;

View File

@@ -1,7 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Db.Models;

View File

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

View File

@@ -1,5 +1,4 @@
#nullable disable
using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Common.ModuleBehaviors;

View File

@@ -273,6 +273,31 @@ public partial class Administration
.SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public Task WarnDelete(IGuildUser user, int index)
=> WarnDelete(user.Id, index);
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async Task WarnDelete(ulong userId, int index)
{
if (--index < 0)
return;
var warn = await _service.WarnDelete(userId, index);
if (warn is null)
{
await Response().Error(strs.warning_not_found).SendAsync();
return;
}
await Response().Confirm(strs.warning_deleted(Format.Bold(index.ToString()))).SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.BanMembers)]
@@ -286,6 +311,7 @@ public partial class Administration
{
if (index < 0)
return;
var success = await _service.WarnClearAsync(ctx.Guild.Id, userId, index, ctx.User.ToString());
var userStr = Format.Bold((ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString());
if (index == 0)

View File

@@ -4,7 +4,6 @@ using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Db;
using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Db.Models;
using Newtonsoft.Json;
@@ -89,9 +88,10 @@ public class UserPunishService : INService, IReadyExecutor
{
ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
previousCount = uow.Set<Warning>().ForId(guildId, userId)
.Where(w => !w.Forgiven && w.UserId == userId)
.Sum(x => x.Weight);
previousCount = uow.Set<Warning>()
.ForId(guildId, userId)
.Where(w => !w.Forgiven && w.UserId == userId)
.Sum(x => x.Weight);
uow.Set<Warning>().Add(warn);
@@ -103,7 +103,7 @@ public class UserPunishService : INService, IReadyExecutor
var totalCount = previousCount + weight;
var p = ps.Where(x => x.Count > previousCount && x.Count <= totalCount)
.MaxBy(x => x.Count);
.MaxBy(x => x.Count);
if (p is not null)
{
@@ -244,33 +244,33 @@ public class UserPunishService : INService, IReadyExecutor
{
await using var uow = _db.GetDbContext();
var cleared = await uow.Set<Warning>()
.Where(x => uow.Set<GuildConfig>()
.Any(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Clear)
&& x.Forgiven == false
&& x.DateAdded
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>()
.Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours)
.First()))
.UpdateAsync(_ => new()
{
Forgiven = true,
ForgivenBy = "expiry"
});
.Where(x => uow.Set<GuildConfig>()
.Any(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Clear)
&& x.Forgiven == false
&& x.DateAdded
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>()
.Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours)
.First()))
.UpdateAsync(_ => new()
{
Forgiven = true,
ForgivenBy = "expiry"
});
var deleted = await uow.Set<Warning>()
.Where(x => uow.Set<GuildConfig>()
.Any(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Delete)
&& x.DateAdded
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>()
.Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours)
.First()))
.DeleteAsync();
.Where(x => uow.Set<GuildConfig>()
.Any(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Delete)
&& x.DateAdded
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>()
.Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours)
.First()))
.DeleteAsync();
if (cleared > 0 || deleted > 0)
{
@@ -293,21 +293,21 @@ public class UserPunishService : INService, IReadyExecutor
if (config.WarnExpireAction == WarnExpireAction.Clear)
{
await uow.Set<Warning>()
.Where(x => x.GuildId == guildId
&& x.Forgiven == false
&& x.DateAdded < DateTime.UtcNow.AddHours(-config.WarnExpireHours))
.UpdateAsync(_ => new()
{
Forgiven = true,
ForgivenBy = "expiry"
});
.Where(x => x.GuildId == guildId
&& x.Forgiven == false
&& x.DateAdded < DateTime.UtcNow.AddHours(-config.WarnExpireHours))
.UpdateAsync(_ => new()
{
Forgiven = true,
ForgivenBy = "expiry"
});
}
else if (config.WarnExpireAction == WarnExpireAction.Delete)
{
await uow.Set<Warning>()
.Where(x => x.GuildId == guildId
&& x.DateAdded < DateTime.UtcNow.AddHours(-config.WarnExpireHours))
.DeleteAsync();
.Where(x => x.GuildId == guildId
&& x.DateAdded < DateTime.UtcNow.AddHours(-config.WarnExpireHours))
.DeleteAsync();
}
await uow.SaveChangesAsync();
@@ -425,8 +425,8 @@ public class UserPunishService : INService, IReadyExecutor
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(guildId, gc => gc.Include(x => x.WarnPunishments))
.WarnPunishments.OrderBy(x => x.Count)
.ToArray();
.WarnPunishments.OrderBy(x => x.Count)
.ToArray();
}
public (IReadOnlyCollection<(string Original, ulong? Id, string Reason)> Bans, int Missing) MassKill(
@@ -436,20 +436,20 @@ public class UserPunishService : INService, IReadyExecutor
var gusers = guild.Users;
//get user objects and reasons
var bans = people.Split("\n")
.Select(x =>
{
var split = x.Trim().Split(" ");
.Select(x =>
{
var split = x.Trim().Split(" ");
var reason = string.Join(" ", split.Skip(1));
var reason = string.Join(" ", split.Skip(1));
if (ulong.TryParse(split[0], out var id))
return (Original: split[0], Id: id, Reason: reason);
if (ulong.TryParse(split[0], out var id))
return (Original: split[0], Id: id, Reason: reason);
return (Original: split[0],
gusers.FirstOrDefault(u => u.ToString().ToLowerInvariant() == x)?.Id,
Reason: reason);
})
.ToArray();
return (Original: split[0],
gusers.FirstOrDefault(u => u.ToString().ToLowerInvariant() == x)?.Id,
Reason: reason);
})
.ToArray();
//if user is null, means that person couldn't be found
var missing = bans.Count(x => !x.Id.HasValue);
@@ -483,11 +483,12 @@ public class UserPunishService : INService, IReadyExecutor
}
else if (template is null)
{
uow.Set<BanTemplate>().Add(new()
{
GuildId = guildId,
Text = text
});
uow.Set<BanTemplate>()
.Add(new()
{
GuildId = guildId,
Text = text
});
}
else
template.Text = text;
@@ -499,31 +500,31 @@ public class UserPunishService : INService, IReadyExecutor
{
await using var ctx = _db.GetDbContext();
await ctx.Set<BanTemplate>()
.ToLinqToDBTable()
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
Text = null,
DateAdded = DateTime.UtcNow,
PruneDays = pruneDays
},
old => new()
{
PruneDays = pruneDays
},
() => new()
{
GuildId = guildId
});
.ToLinqToDBTable()
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
Text = null,
DateAdded = DateTime.UtcNow,
PruneDays = pruneDays
},
old => new()
{
PruneDays = pruneDays
},
() => new()
{
GuildId = guildId
});
}
public async Task<int?> GetBanPruneAsync(ulong guildId)
{
await using var ctx = _db.GetDbContext();
return await ctx.Set<BanTemplate>()
.Where(x => x.GuildId == guildId)
.Select(x => x.PruneDays)
.FirstOrDefaultAsyncLinqToDB();
.Where(x => x.GuildId == guildId)
.Select(x => x.PruneDays)
.FirstOrDefaultAsyncLinqToDB();
}
public Task<SmartText> GetBanUserDmEmbed(
@@ -554,18 +555,18 @@ public class UserPunishService : INService, IReadyExecutor
banReason = string.IsNullOrWhiteSpace(banReason) ? "-" : banReason;
var repCtx = new ReplacementContext(client, guild)
.WithOverride("%ban.mod%", () => moderator.ToString())
.WithOverride("%ban.mod.fullname%", () => moderator.ToString())
.WithOverride("%ban.mod.name%", () => moderator.Username)
.WithOverride("%ban.mod.discrim%", () => moderator.Discriminator)
.WithOverride("%ban.user%", () => target.ToString())
.WithOverride("%ban.user.fullname%", () => target.ToString())
.WithOverride("%ban.user.name%", () => target.Username)
.WithOverride("%ban.user.discrim%", () => target.Discriminator)
.WithOverride("%reason%", () => banReason)
.WithOverride("%ban.reason%", () => banReason)
.WithOverride("%ban.duration%",
() => duration?.ToString(@"d\.hh\:mm") ?? "perma");
.WithOverride("%ban.mod%", () => moderator.ToString())
.WithOverride("%ban.mod.fullname%", () => moderator.ToString())
.WithOverride("%ban.mod.name%", () => moderator.Username)
.WithOverride("%ban.mod.discrim%", () => moderator.Discriminator)
.WithOverride("%ban.user%", () => target.ToString())
.WithOverride("%ban.user.fullname%", () => target.ToString())
.WithOverride("%ban.user.name%", () => target.Username)
.WithOverride("%ban.user.discrim%", () => target.Discriminator)
.WithOverride("%reason%", () => banReason)
.WithOverride("%ban.reason%", () => banReason)
.WithOverride("%ban.duration%",
() => duration?.ToString(@"d\.hh\:mm") ?? "perma");
// if template isn't set, use the old message style
@@ -594,4 +595,24 @@ public class UserPunishService : INService, IReadyExecutor
var output = SmartText.CreateFrom(template);
return await _repSvc.ReplaceAsync(output, repCtx);
}
public async Task<Warning> WarnDelete(ulong userId, int index)
{
await using var uow = _db.GetDbContext();
var warn = await uow.GetTable<Warning>()
.Where(x => x.UserId == userId)
.OrderByDescending(x => x.DateAdded)
.Skip(index)
.FirstOrDefaultAsyncLinqToDB();
if (warn is not null)
{
await uow.GetTable<Warning>()
.Where(x => x.Id == warn.Id)
.DeleteAsync();
}
return warn;
}
}

View File

@@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db;
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Administration.Services;

View File

@@ -398,15 +398,14 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
var serialized = _service.ExportExpressions(ctx.Guild?.Id);
await using var stream = await serialized.ToStream();
await ctx.Channel.SendFileAsync(stream, "exprs-export.yml");
await ctx.User.SendFileAsync(stream, $"exprs-export_{DateTime.UtcNow:yyyy-MM-dd-HH-mm-ss}_{(ctx.Guild?.Id.ToString() ?? "global")}.yml");
}
[Cmd]
#if GLOBAL_NADEKO
[OwnerOnly]
#endif
public async Task ExprsImport([Leftover] string input = null)
{
// todo cooldown on public bot for 1 day, limit 100
if (!AdminInGuildOrOwnerInDm())
{
await Response().Error(strs.expr_insuff_perms).SendAsync();

View File

@@ -2,7 +2,6 @@
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.Yml;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using System.Runtime.CompilerServices;
using LinqToDB.EntityFrameworkCore;

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