Compare commits

...

176 Commits

Author SHA1 Message Date
Kwoth
7bff20cc70 Upped version to 3.0.11 2021-12-17 23:41:31 +01:00
Kwoth
29f5dcc359 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-12-17 23:39:08 +01:00
Kwoth
14f2851072 - you should be able to update your .atl now without disabling it
- capitalization of language input in .atl should no longer matter
2021-12-17 23:38:06 +01:00
Kwoth
a2b25f8246 atl no longer pings if in nodelete mode 2021-12-17 16:32:35 +01:00
Kwoth
a38951b5ad atl will no longer post the translation if it's equivalent to the input message
updated packages
2021-12-17 16:26:14 +01:00
Kwoth
4c1b911cb7 Inrole string fix 2021-12-17 15:59:18 +01:00
Kwoth
6c9f231453 Update responses.nl-NL.json (POEditor.com) 2021-12-15 19:09:59 +00:00
Kwoth
83daf3c30f Made .showembed always output lowercase field names and no null values 2021-12-14 19:41:07 +01:00
Kwoth
9be8140d4d Added .showembed <msgid> and .showembed #channel <msgid> which will show you embed json from the specified message 2021-12-13 20:29:45 +01:00
Kwoth
96c9b699aa Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-12-13 19:28:34 +01:00
Kwoth
3c0768a372 .atl / .at reworked 2021-12-13 19:28:22 +01:00
Kwoth
58b22e3d9e Merge branch 'award-patch' into 'v3'
Award no longer misinterprets IDs

See merge request Kwoth/nadekobot!195
2021-12-13 13:11:56 +00:00
Alan Beatty
0474551e2f Award no longer misinterprets IDs 2021-12-13 13:11:56 +00:00
Kwoth
c85bdec396 Merge branch 'qdel-patch' into 'v3'
Clarity on qdel

See merge request Kwoth/nadekobot!197
2021-12-13 13:07:00 +00:00
Alan Beatty
5fa39eaa9f Clarity on qdel 2021-12-13 13:07:00 +00:00
Kwoth
8eaaa35c7a Merge branch 'perm-patch' into 'v3'
Replace `RequireUserPemission` with `UserPerm`

See merge request Kwoth/nadekobot!196
2021-12-13 13:06:28 +00:00
Alan Beatty
1c24f95efa Replace RequireUserPemission with UserPerm 2021-12-13 13:06:28 +00:00
Kwoth
fcc49dbbdb Made .sinfo slightly better 2021-12-11 16:13:52 +01:00
Kwoth
3a317590b4 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-12-11 15:27:37 +01:00
Kwoth
36c013fbb5 Added .qimport and .qexport commands, fixed some response strings 2021-12-11 15:27:32 +01:00
Kwoth
a9ec8049f6 Update responses.id-ID.json (POEditor.com) 2021-12-11 01:04:37 +00:00
Kwoth
ced0d97e3a Update responses.pt-BR.json (POEditor.com) 2021-12-11 00:37:34 +00:00
Kwoth
24d0f57dc3 Update responses.pl-PL.json (POEditor.com) 2021-12-11 00:37:33 +00:00
Kwoth
514ecd6be8 Update responses.fr-FR.json (POEditor.com) 2021-12-11 00:37:32 +00:00
Kwoth
02eb6e172b added slots.currencyFontColor to gambling.yml 2021-12-09 20:54:26 +01:00
Kwoth
d22c579875 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-12-09 02:07:12 +01:00
Kwoth
f70e49fc6a Removed unwanted command 2021-12-09 02:07:03 +01:00
Kwoth
8b410561f9 Update responses.uk-UA.json (POEditor.com) 2021-12-09 01:04:48 +00:00
Kwoth
3c79fd1d6f Update responses.es-ES.json (POEditor.com) 2021-12-09 01:04:46 +00:00
Kwoth
e9f1a9b1dd Update responses.ru-RU.json (POEditor.com) 2021-12-09 01:04:45 +00:00
Kwoth
3c293ae6db Update responses.pt-BR.json (POEditor.com) 2021-12-09 01:04:43 +00:00
Kwoth
a5d9e7de66 Update responses.de-DE.json (POEditor.com) 2021-12-09 01:04:42 +00:00
Kwoth
4d9e48cd41 Update responses.fr-FR.json (POEditor.com) 2021-12-09 01:04:42 +00:00
Kwoth
b7ead22e09 .remindl and .remindrm commands now supports optional 'server' parameter for Administrators which allows them to delete any reminder created on the server 2021-12-09 01:48:39 +01:00
Kwoth
9f219cddbb Updated osx guide with dotnet 5 installation instructions 2021-12-09 01:11:12 +01:00
Kwoth
cf9792f24a Fixed .rule34, closes #320 2021-12-07 23:44:57 +01:00
Kwoth
0187dd57ac Fixed an exception in coordinator preventing some shards from restarting 2021-12-05 08:47:33 +01:00
Kwoth
2d2e54e31e Bot will now check for permissions when trying to apply punishments 2021-12-05 08:47:10 +01:00
Kwoth
cb7c5e48fd Fixed gambling vote reward getting overwritten and reset to 100 every time 2021-12-05 08:20:30 +01:00
Kwoth
771c2745dc .crypto now supports top 5k coins. closes #138 2021-12-04 15:45:04 +01:00
Kwoth
59c0f2f4b3 Added graceful option to die (kill coordinator without killing shards) 2021-12-01 09:47:41 +01:00
Kwoth
219ca39cd1 Added .coordreload which will reload coord.yml when using NadekoBot.Coordinator
- bots with more than 1 shard will now use redis strings provider
2021-12-01 09:41:23 +01:00
Kwoth
1e6d0806d7 Fixed some console log spam which was incorrectly done by streamrole feature, upped version to 3.0.10 in stats 2021-12-01 07:02:01 +01:00
Kwoth
71f1e43272 .xprewsreset now has correct permissions 2021-12-01 05:41:03 +01:00
Kwoth
8499e1da70 Updated changelog, upped version in the stats to 3.0.9 2021-11-24 01:51:34 +01:00
Kwoth
a2ea806bed Removed slot.numbers from images.yml as they're no longer used anywhere, thx ala 2021-11-24 01:49:03 +01:00
Kwoth
732b5dfeed Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-11-21 03:01:18 +01:00
Kwoth
d4dcdc761a .economy should not overflow so easily anymore, and big numbers look nicer 2021-11-21 03:01:04 +01:00
Kwoth
57996ba290 Merge branch 'take-award-patch' into 'v3'
Change award and take to not use ShmartNumber

See merge request Kwoth/nadekobot!192
2021-11-21 01:37:30 +00:00
Alan Beatty
4b29b3a239 Change award and take to not use ShmartNumber 2021-11-21 01:37:30 +00:00
Kwoth
54ac955395 Merge branch 'xpservicepatch' into 'v3'
Remove deprecated method from XpService.

See merge request Kwoth/nadekobot!191
2021-11-21 01:09:45 +00:00
Kwoth
f4fa298866 Merge branch 'plantpatch' into 'v3'
ShmartNumber for .plant

See merge request Kwoth/nadekobot!193
2021-11-21 01:09:14 +00:00
Kwoth
b2fafc964f Merge branch 'v3' into 'v3'
Move plant/pick where they belong

See merge request Kwoth/nadekobot!178
2021-11-21 01:08:46 +00:00
Kwoth
22b452e449 Added .warn weights, improved .warnlog 2021-11-21 02:01:21 +01:00
Alan Beatty
fda385a5e4 ShmartNumber for .plant 2021-11-20 18:36:05 -06:00
Alan Beatty
c28f7cfa07 Remove deprecated method from XpService. 2021-11-20 17:55:33 -06:00
Kwoth
0a029a7847 Added image attachment support for .ea if you omit imageUrl 2021-11-21 00:22:10 +01:00
Kwoth
c050ce2123 Added .emojiadd command 2021-11-21 00:07:19 +01:00
Kwoth
27613410dd Another migration fix for users who manually edited their databasea and are unable to update to v3 due to invalid db state. 2021-11-18 18:01:02 +01:00
Kwoth
1513008b4b Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-11-18 16:41:40 +01:00
Kwoth
bf97cffd84 Fixed cleanup migration if there are waifus which don't have a corresponding entry in DiscordUser 2021-11-18 16:41:27 +01:00
Kwoth
e37d1c46db Merge branch 'patreon-token-refresh' into 'v3'
Patreon Access and Refresh Tokens should now be automatically updated

See merge request Kwoth/nadekobot!189
2021-11-17 18:45:50 +00:00
Kwoth
06c20c6fa4 Patreon Access and Refresh Tokens should now be automatically updated 2021-11-17 18:45:49 +00:00
Kwoth
aa518d60a5 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-11-17 16:27:54 +01:00
Kwoth
d55ce7accc Merge branch 'massban2' into 'v3'
Add an audit log reason to massban

See merge request Kwoth/nadekobot!188
2021-11-14 23:41:04 +00:00
Alan Beatty
502c5cec07 Add an audit log reason to massban 2021-11-13 14:43:30 -06:00
Kwoth
ee5c13607b Add support for hackbans on massban
Closes #307

See merge request Kwoth/nadekobot!187
2021-11-13 20:38:01 +00:00
Alan Beatty
5a681a5194 Add support for hackbans on massban
Closes #307
2021-11-13 20:38:01 +00:00
Kwoth
68395372f0 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-11-09 16:08:58 +01:00
Kwoth
c8e01bd158 Merge branch 'rerorace' into 'v3'
Fix possible race condition for reaction roles

### Description  
This MR aims to fix a possible race condition on the addition of exclusive reaction roles when the reactions are spammed by the user.

### Changes Proposed  
- Add a `ConcurrentHashSet<(ulong, ulong)>` to keep track of exclusive reaction roles that are being processed.
- Added logic that takes the collection above in consideration.
    - If entry is present, quit silently.
    - Else, perform the reaction role stuff then remove the entry.

### Details  
Exclusive reaction roles are meant to be exactly that - exclusive.

Normally, when a user selects an exclusive role they receive that role. If they select another role, their previous role is removed and the new one is added. There is a bug where if the user spams the reactions for a short period of time, Nadeko will eventually assign them multiple roles that are meant to be exclusive with each other. This happens because the events that handle the addition and removal work in a weird way - first they offload the removal of the roles to a `Task.Run()`, which also happens to have a `Task.Delay()` in it (possibly to avoid Discord ratelimits).

Concurrently, it proceeds to add the role that the user picked. The problem with this approach is that the Task that handles the role removal takes long enough for another reaction event to trigger and start the same work for a different reaction role. Then mayhem ensues, with different events concomitantly adding and removing the roles that previous events have removed or added. In the end, the user ends up with multiple exclusive roles they are not supposed to.

This MR fixes this by having a local field that keeps track of the reaction roles that are being currently processed (a `ConcurrentHashSet<T>` where T is a tuple `(ulong, ulong)` - (message ID, user ID)). When a reaction event runs, it adds itself to the concurrent hashset. If another event triggers and tries to add itself while the previous event still hasn't finished, it silently quits so it doesn't interfere with the current event. I was not entirely satisfied with the way this works, so I tried another system that cancels the old events (with a CancellationToken) instead of just making the new events quit, but that resulted in multiple exclusive roles being temporarily assigned to the user (just for a few seconds, but still).

If another approach is preferred, then please do let me know.

### Notes  
- Methods in that entire file need to be broken down into smaller methods.
- The loop that removes old reactions is **very slow**. Using `Task.WhenAll()` instead of awaiting the removals could help improve performance (but could also trigger ratelimits).

See merge request Kwoth/nadekobot!183
2021-11-09 10:41:49 +00:00
Kaoticz
1d57191700 Fix possible race condition for reaction roles 2021-11-09 10:41:49 +00:00
Kwoth
02c7ded457 Merge branch 'hokutochen-v3-patch-18181' into 'v3'
Fixed broken link

See merge request Kwoth/nadekobot!185
2021-11-09 08:32:37 +00:00
Hokuto Chen
12c483d222 fixed broken "Create a Discord Bot application and invite the bot to your server" link 2021-11-09 08:17:15 +00:00
Kwoth
c80898a7bf Fixed an error that would show up in the console when a club image couldn't be drawn in certain circumstances 2021-11-04 17:01:18 +01:00
Kwoth
aae2805785 Updated changelog.md - Fixed 3.0.7 notes title 2021-11-04 09:05:22 +01:00
Kwoth
fc3695d090 Updated changelog.md for 3.0.8 2021-11-04 09:04:49 +01:00
Kwoth
428429ff44 Fixed creating new users in the database when awarding currency 2021-11-03 16:27:48 +01:00
Kwoth
dc344caec6 Version upped to 3.0.8 2021-11-03 15:23:45 +01:00
Kwoth
2a4d55f81d Merge branch 'v3-dev' into 'v3'
Slots redesign nad images moved to images.yml

See merge request Kwoth/nadekobot!181
2021-11-03 14:22:51 +00:00
Kwoth
d090aa23ee Slots redesign nad images moved to images.yml 2021-11-03 14:22:51 +00:00
Kwoth
65062306c6 Merge branch 'tbodt-v3-patch-66982' into 'v3'
Reduce required permissions for deleting quotes

See merge request Kwoth/nadekobot!182
2021-10-30 11:54:03 +00:00
tbodt
9ae3b66fc2 Reduce required permissions for deleting quotes 2021-10-27 01:25:40 +00:00
Kwoth
c4ba43ec6d Merge branch 'v3-dev' into 'v3'
backport of public nsfw module

See merge request Kwoth/nadekobot!176
2021-10-21 23:35:58 +00:00
Kwoth
1141791ce5 backport of public nsfw module 2021-10-21 23:35:58 +00:00
Kwoth
49f1ef7db0 Merge branch 'hokutochen-v3-patch-79292' into 'v3'
Updated images for V3 updater

See merge request Kwoth/nadekobot!179
2021-10-21 23:35:11 +00:00
Hokuto Chen
a70c35e101 Updated images for V3 updater 2021-10-21 23:35:11 +00:00
Yuno Gasai
717543f6c2 Move plant/pick where they belong 2021-10-19 09:28:55 -04:00
Kwoth
b61b1dbfaa Merge branch 'memfix' into 'v3'
Fixed memory counter not refreshing over time

See merge request Kwoth/nadekobot!177
2021-10-17 22:56:10 +00:00
Kaoticz
92365fd22d Fixed memory counter not refreshing over time 2021-10-17 22:56:10 +00:00
Kwoth
24a4745193 Merge branch 'v3-dev' into 'v3'
Created VotesApi project nad re-worked vote rewards handling

See merge request Kwoth/nadekobot!172
2021-10-15 22:06:30 +00:00
Kwoth
1af75fd813 Created VotesApi project nad re-worked vote rewards handling 2021-10-15 22:06:30 +00:00
Kwoth
18160164eb Merge branch 'memsize' into 'v3'
Corrected memory usage on StatusService

See merge request Kwoth/nadekobot!175
2021-10-15 22:04:30 +00:00
Kaoticz
2fd7d97025 Corrected memory usage on StatusService 2021-10-15 22:04:30 +00:00
Kwoth
6ada15049d Merge branch 'ban-kick-patch' into 'v3'
TrimTo to avoid length limit for ban/kick reasons

See merge request Kwoth/nadekobot!174
2021-10-13 17:51:08 +00:00
Alan Beatty
0ebc40b95c TrimTo to avoid length limit for ban/kick reasons 2021-10-12 17:07:46 -05:00
Kwoth
02de25a931 Typo 2021-10-09 19:55:19 +02:00
Kwoth
0b395e9176 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-10-09 19:49:26 +02:00
Kwoth
4532f992cd Fixed field not preserving inline after string replacements. closes #308 2021-10-09 19:48:54 +02:00
Kwoth
34201f0558 Merge branch 'v3' into 'v3'
Docs fix

See merge request Kwoth/nadekobot!168
2021-10-09 17:43:18 +00:00
Hunter T
d2f4d63183 Docs fix 2021-10-09 17:43:18 +00:00
Kwoth
b41c014869 Merge branch 'Kieteyuku-v3-patch-34025' into 'v3'
Use … in TrimTo to decrease necessary deletions

See merge request Kwoth/nadekobot!171
2021-10-09 17:42:24 +00:00
Ene
d348347762 Update StringExtensions.cs 2021-10-08 20:20:50 +00:00
Kwoth
db7cf3d757 Upped version to 3.0.7, Updated changelog 2021-10-05 13:12:28 +02:00
Kwoth
83ea046d5f Fixed %users% and %shard.usercount% placeholders not showing correct member count 2021-10-05 13:09:52 +02:00
Kwoth
d5c94424e9 Merge branch 'v3-docker' into 'v3'
install youtube-dl through pip for a better availability of cdn

See merge request Kwoth/nadekobot!165
2021-10-01 08:26:15 +00:00
Kwoth
ff95b3d00f - .streamsclear re-added. It will remove all followed streams on the server.
- .gifts now have 3 new ✂️ Haircut 🧻 ToiletPaper and 🥀 WiltedRose which **reduce** waifu's value
- Added a new multiplier (waifu.multi.negative_gift_effect default 0.5, changeable via .config gambling or data/gambling.yml)
2021-09-30 08:53:15 +02:00
Veovis
4b5fa3bb04 install youtube-dl through pip for a better availability of cdn 2021-09-27 21:21:44 +02:00
Kwoth
52c9ec670d Upped version to 3.0.6. Slightly updated hangman help string 2021-09-27 10:57:10 +02:00
Kwoth
1ac472c676 Urls will now properly work in embeds. Fixes #306 2021-09-27 10:52:14 +02:00
Kwoth
39f01a3164 Merge branch 'v3-dev' into 'v3'
Hangman Rewrite

See merge request Kwoth/nadekobot!164
2021-09-27 02:00:19 +00:00
Kwoth
486916944b Hangman Rewrite 2021-09-27 02:00:19 +00:00
Kwoth
68e96cd1bb Merge branch 'v3-docker' into 'v3'
V3 docker

See merge request Kwoth/nadekobot!163
2021-09-27 00:10:18 +00:00
Veovis
5a647d100a V3 docker 2021-09-27 00:10:18 +00:00
Kwoth
a562a571e2 - .shopadd will now ignore negative price input
- Make sure bot has manage roles permission for .xprr
- Small cleanup
2021-09-22 23:07:23 +02:00
Kwoth
12146ad2da Make sure the bot has manageroles permission for .aar 2021-09-22 22:41:00 +02:00
Kwoth
453ac3efd2 Re-did migration as the old one was wrong 2021-09-21 22:48:18 +02:00
Kwoth
c9e89e1911 - .logignore now supports targeting channels and users
- .logignore now lists all ignored channels and users when no parameter is provided
- Renamed and cleaned up some log-related fields
2021-09-21 22:39:24 +02:00
Kwoth
611817f78a Yes, it's just a warning 2021-09-21 20:59:05 +02:00
Kwoth
2e66137f3e Fixed an exception which was breaking repeater loop 2021-09-21 20:28:37 +02:00
Kwoth
2d92424dd4 Version upped to 3.0.5 2021-09-20 19:45:13 +02:00
Kwoth
ffba03adbe Added guides on the different ways to run the bot on linux - contributed by Bark Ranger 2021-09-18 06:19:53 +02:00
Kwoth
76ebb87fb5 updated docs formatting and windows from source update guide 2021-09-17 19:02:18 +02:00
Kwoth
4a50c30c56 Fixed .logserver - should no longer throw an exception if you had no logsettings previously 2021-09-17 17:19:11 +02:00
Kwoth
e70a91ae60 Fixed images not automatically reloading on startup if the keys don't exist 2021-09-17 17:10:38 +02:00
Kwoth
3470762b30 woops, missed an extra bracket 2021-09-17 01:08:20 +02:00
Kwoth
2cbb4ecd3d Version upped to 3.0.4 2021-09-16 23:10:19 +02:00
Kwoth
619bee811d Fixed most of the commands which used wrong error color for confirm messages 2021-09-16 23:09:17 +02:00
Kwoth
a09be96200 Added DmHelpTextKeywords to data/bot.yml
- Bot now sends dm help text ONLY if the message contains one of the keywords specified
  - If no keywords are specified, bot will reply to every DM (like before)
- Fixed several commands which used error color for success confirmation messages
2021-09-16 22:52:04 +02:00
Kwoth
81254ed5f6 Added %server.boosters% and %server.boost_level% placeholders 2021-09-16 19:22:05 +02:00
Kwoth
d82e3bd444 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-09-16 19:09:51 +02:00
Kwoth
9011646b02 Update responses.pt-BR.json (POEditor.com) 2021-09-16 17:09:46 +00:00
Kwoth
2a1f45819d .waifugift pagination fixed - maximum number of pages properly scales with the number of items 2021-09-16 19:09:19 +02:00
Kwoth
d2d0cb9e03 - Possible fix for .repeat bug
- Slight adjustment for repeater logic
  - Timer should no longer increase on some repeaters
  - Repeaters should no longer have periods when they're missing from the list
2021-09-16 07:19:08 +02:00
Kwoth
ed039977c2 Release 3.0.3 2021-09-15 19:07:36 +02:00
Kwoth
786ede3290 Added .config games hangman.currency_reward and a property with the same name in games.yml 2021-09-15 01:50:48 +02:00
Kwoth
3edcca4927 Updated CHANGELOG.md, removed an unused enum 2021-09-15 01:30:26 +02:00
Kwoth
0635a0ddc8 Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-09-15 01:30:06 +02:00
Kwoth
cb514e4219 Update responses.es-ES.json (POEditor.com) 2021-09-14 23:29:46 +00:00
Kwoth
cccb37854c Added .imageonlychannel / .imageonly to prevent users from posting anything but images in the channel 2021-09-15 01:26:41 +02:00
Kwoth
35d549f4e6 Fixed .yun command help 2021-09-14 01:27:37 +02:00
Kwoth
c0d81a5d9c as a shortcut for subscribing to a youtube channel's rss feed**** 2021-09-14 01:11:03 +02:00
Kwoth
711d6c7caa Added .youtubeuploadnotif / .yun as a shortcut to a subscribing channel's rss feed 2021-09-14 01:09:31 +02:00
Kwoth
cea944cdb8 Update responses.uk-UA.json (POEditor.com) 2021-09-13 15:42:50 +00:00
Kwoth
2d70ea487e Update responses.ru-RU.json (POEditor.com) 2021-09-13 15:42:49 +00:00
Kwoth
9f65f979fd Update responses.fr-FR.json (POEditor.com) 2021-09-13 15:42:48 +00:00
Kwoth
c12b41ddd1 Added .massban 2021-09-13 17:41:33 +02:00
Kwoth
a5f9ac1540 .boostmsg will now properly show boost, and not greet message 2021-09-13 12:59:47 +02:00
Kwoth
8c5214def2 Ban .warnp will now prune user's messages 2021-09-13 01:30:11 +02:00
Kwoth
467a8bdaf6 Updated CHANGELOG.md 2021-09-12 22:09:32 +02:00
Kwoth
d115261536 Fixed .log commands 2021-09-12 22:07:56 +02:00
Kwoth
0429210a73 .boost should now us the correct channel, and it should send a message for users boosting the same server more than once 2021-09-12 21:05:19 +02:00
Kwoth
8ee1160a00 Added .boost, .boostmsg and .boostdel commands which allow you to have customizable messages when someone boosts your server, with auto-deletion support
- Updated response embed colors in greet commands
- Updated .greetmsg and .byemsg command help to match the new .boost command help
2021-09-12 20:44:35 +02:00
Kwoth
a27ea7a0aa Merge branch 'v3' of https://gitlab.com/kwoth/nadekobot into v3 2021-09-12 20:01:16 +02:00
Kwoth
9396f59a34 Update responses.de-DE.json (POEditor.com) 2021-09-12 17:58:57 +00:00
Kwoth
37cdea4f6a Removed .novel command as it no longer works 2021-09-12 13:31:57 +02:00
Kwoth
d028e23bc1 .timely will now correctly use Ok color 2021-09-12 01:22:13 +02:00
Kwoth
1df947d54b .rero now optionally takes a message id to which to attach the reaction roles 2021-09-12 01:07:19 +02:00
Kwoth
596a5c05e0 docs: Fixed more indentation in osx and migration guides 2021-09-12 00:18:54 +02:00
Kwoth
1d5a702b46 docs: Updated bot sharding guide in advanced creds guide, Added comments to coord.yml, Updated manual migration guide with correct indentation 2021-09-12 00:09:55 +02:00
Kwoth
712cb9300f docs: Fixed up jsons-explained.md (thx ala) 2021-09-11 22:48:48 +02:00
Kwoth
ab0d013035 docs: removed from source sidebar link 2021-09-11 22:28:28 +02:00
Kwoth
46b384f0f4 Docs format and link fixes 2021-09-11 16:52:14 +02:00
Kwoth
1284312a55 Clarified that running the 2.x bot prior to migration is mandatory 2021-09-11 00:58:22 +02:00
Kwoth
42bdc6417c Updated osx guide with instructions on installing wget 2021-09-11 00:47:08 +02:00
Kwoth
3715dd4e00 Fixed version in the changelog 2021-09-11 00:16:14 +02:00
Kwoth
ef13020d96 Updated changelog 2021-09-11 00:14:55 +02:00
Kwoth
dd708d4b7c Slight change to windows updater migration guide 2021-09-11 00:03:52 +02:00
Kwoth
8735dcf94d Upped version to 3.0.1 2021-09-11 00:01:05 +02:00
Kwoth
b6e03dcaed Fixed embedbuilder withimageurl and withurl (thx ene) 2021-09-10 20:43:28 +02:00
Kwoth
1785a27772 docs: Instruct users to run their old bots before updating to v3 2021-09-10 16:59:02 +02:00
Kwoth
9fbe43ec8f docs: removed minify entry from plugins in mkdocs 2021-09-09 20:56:25 +02:00
Kwoth
d3878414e0 docs: Remove the incompatible package 2021-09-09 20:54:32 +02:00
Kwoth
00f9a05cfc Set setup tools to 57.5.0, possible fix for failing build 2021-09-09 20:44:00 +02:00
Kwoth
d9e15a9abd Possible fix for broken docs build 2021-09-09 19:14:38 +02:00
Kwoth
8a5539448e Updated credentials.json references to creds.yml 2021-09-07 16:38:05 +02:00
Kwoth
e005ec11fd Correct version in releases-v3.json, also it's an array 2021-09-07 16:31:37 +02:00
269 changed files with 39899 additions and 16029 deletions

View File

@@ -9,6 +9,7 @@
!src/NadekoBot.Generators/**
# Use Ayu stuff
!src/ayu/**
!docker-entrypoint.sh
# ignore bin and obj folders in projects
src/**/bin/*

View File

@@ -91,7 +91,35 @@ upload-windows-updater-release:
name: amazon/aws-cli
entrypoint: [""]
script:
- sed -i "s/INSTALLER_FILE_NAME/$INSTALLER_FILE_NAME/g" releases-v3.json
- sed -i "s/_INSTALLER_FILE_NAME_/$INSTALLER_FILE_NAME/g" releases-v3.json
- sed -i "s/_VERSION_/$CI_COMMIT_TAG/g" releases-v3.json
- aws --version
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/$INSTALLER_FILE_NAME" --acl public-read --body "$INSTALLER_OUTPUT_DIR/$INSTALLER_FILE_NAME"
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/releases-v3.json" --acl public-read --body "releases-v3.json"
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/releases-v3.json" --acl public-read --body "releases-v3.json"
docker-build:
# Use the official docker image.
image: docker:latest
stage: build
services:
- docker:dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
# Default branch leaves tag empty (= latest tag)
# All other branches are tagged with the escaped branch name (commit ref slug)
script:
- |
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
tag=""
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
else
tag=":$CI_COMMIT_REF_SLUG"
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
fi
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
- docker push "$CI_REGISTRY_IMAGE${tag}"
# Run this job in a branch where a Dockerfile exists
rules:
- if: $CI_COMMIT_BRANCH
exists:
- Dockerfile

View File

@@ -4,7 +4,184 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
## Unreleased
-
## [3.0.11] - 17.12.2021
### Added
- `.remindl` and `.remindrm` commands now supports optional 'server' parameter for Administrators which allows them to delete any reminder created on the server
- Added slots.currencyFontColor to gambling.yml
- Added `.qexport` and `.qimport` commands which allow you to export and import quotes just like `.crsexport`
- Added `.showembed <msgid>` and `.showembed #channel <msgid>` which will show you embed json from the specified message
### Changed
- `.at` and `.atl` commands reworked
- Persist restarts
- Will now only translate non-commands
- You can switch between `.at del` and `.at` without clearing the user language registrations
- Disabling `.at` will clear all user language registrations on that channel
- Users can't register languages if the `.at` is not enabled
- Looks much nicer
- Bot will now reply to user messages with a translation if `del` is disabled
- Bot will make an embed with original and translated text with user avatar and name if `del` is enabled
- If the bot is unable to delete messages while having `del` enabled, it will reset back to the no-del behavior for the current session
### Fixed
- `.crypto` now supports top 5000 coins
## [3.0.10] - 01.12.2021
### Changed
- `.warn` now supports weighted warnings
- `.warnlog` will now show current amount and total amount of warnings
### Fixed
- `.xprewsreset` now has correct permissions
### Removed
- Removed slot.numbers from `images.yml` as they're no longer used
## [3.0.9] - 21.11.2021
### Changed
- `.ea` will now use an image attachments if you omit imageUrl
### Added
- Added `.emojiadd` with 3 overloads
- `.ea :customEmoji:` which copies another server's emoji
- `.ea newName :customEmoji:` which copies emoji under a different name
- `.ea emojiName <imagelink.png>` which creates a new emoji from the specified image
- Patreon Access and Refresh Tokens should now be automatically updated once a month as long as the user has provided the necessary credentials in creds.yml file:
- `Patreon.ClientId`
- `Patreon.RefreshToken` (will also get updated once a month but needs an initial value)
- `Patreon.ClientSecret`
- `Patreon.CampaignId`
### Fixed
- Fixed an error that would show up in the console when a club image couldn't be drawn in certain circumstances
## [3.0.8] - 03.11.2021
### Added
- Created VotesApi project nad re-worked vote rewards handling
- Updated votes entries in creds.yml with explanations on how to set up vote links
### Fixed
- Fixed adding currency to users who don't exist in the database
- Memory used by the bot is now correct (thanks to kotz)
- Ban/kick will no longer fail due to too long reasons
- Fixed some fields not preserving inline after string replacements
### Changed
- `images.json` moved to `images.yml`
- Links will use the new cdn url
- Heads and Tails images will be updated if you haven't changed them already
- `.slot` redesigned (and updated entries in `images.yml`)
- Reduced required permissions for .qdel (thanks to tbodt)
## [3.0.7] - 05.10.2021
### Added
- `.streamsclear` re-added. It will remove all followed streams on the server.
- `.gifts` now have 3 new ✂️ Haircut 🧻 ToiletPaper and 🥀 WiltedRose which **reduce** waifu's value
- They are called negative gifts
- They show up at the end of the `.gifts` page and are marked with a broken heart
- They have a separate multiplier (`waifu.multi.negative_gift_effect` default 0.5, changeable via `.config gambling` or `data/gambling.yml`)
- When gifted, the waifu's price will be reduced by the `price * multiplier`
- Negative gifts don't show up in `.waifuinfo` nor is the record of them kept in the database
### Fixed
- Fixed `%users%` and `%shard.usercount%` placeholders not showing correct values
## [3.0.6] - 27.09.2021
### Added
- .logignore now supports ignoring users and channels. Use without parameters to see the ignore list
### Changed
- Hangman rewrite
- Hangman categories are now held in separate .yml files in data/hangman/XYZ.yml where XYZ is the category name
### Fixed
- Fixed an exception which caused repeater queue to break
- Fixed url field not working in embeds
## [3.0.5] - 20.09.2021
### Fixed
- Fixed images not automatically reloading on startup if the keys don't exist
- Fixed `.logserver` - it should no longer throw an exception if you had no logsettings previously
## [3.0.4] - 16.09.2021
### Added
- Fully translated to Brazilian Portuguese 🎉
- Added `%server.boosters%` and `%server.boost_level%` placeholders
- Added `DmHelpTextKeywords` to `data/bot.yml`
- Bot now sends dm help text ONLY if the message contains one of the keywords specified
- If no keywords are specified, bot will reply to every DM (like before)
### Fixed
- Possible fix for `.repeat` bug
- Slight adjustment for repeater logic
- Timer should no longer increase on some repeaters
- Repeaters should no longer have periods when they're missing from the list
- Fixed several commands which used error color for success confirmation messages
## [3.0.3] - 15.09.2021
### Added
- Added `.massban` to ban multiple people at once. 30 second cooldown
- Added `.youtubeuploadnotif` / `.yun` as a shortcut for subscribing to a youtube channel's rss feed
- Added `.imageonlychannel` / `.imageonly` to prevent users from posting anything but images in the channel
- Added `.config games hangman.currency_reward` and a property with the same name in games.yml
- If set, users will gain the specified amount of currency for each hangman win
- Fully translated to Spanish, Russian and Ukrainian 🎉
### Changed
- Ban `.warnp` will now prune user's messages
### Fixed
- `.boostmsg` will now properly show boost, and not greet message
## [3.0.2] - 12.09.2021
### Added
- `.rero` now optionally takes a message id to which to attach the reaction roles
- Fully translated to German 🎉
- Added `.boost`, `.boostmsg` and `.boostdel` commands which allow you to have customizable messages when someone boosts your server, with auto-deletion support
### Changed
- Updated `.greetmsg` and `.byemsg` command help to match the new `.boost` command help
- Updated response embed colors in greet commands
- Success -> green
- Warning or Disable -> yellow.
### Fixed
- `.timely` will now correctly use `Ok` color
- Fixed `.log` commands
### Removed
- Removed `.novel` command as it no longer works
## [3.0.1] - 10.09.2021
### Fixed
- Fixed some issues with the embeds not showing the correct data
## [3.0.0] - 06.09.2021

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /source
COPY src/NadekoBot/*.csproj src/NadekoBot/
@@ -9,14 +9,34 @@ RUN dotnet restore src/NadekoBot/
COPY . .
WORKDIR /source/src/NadekoBot
RUN dotnet --version
RUN dotnet publish -c Release -o /app --no-restore
RUN set -xe; \
dotnet --version; \
dotnet publish -c Release -o /app --no-restore; \
mv /app/data /app/data_init; \
rm -Rf libopus* libsodium* opus.* runtimes/win* runtimes/osx* runtimes/linux-arm* runtimes/linux-mips*; \
find /app -type f -exec chmod -x {} \; ;\
chmod +x /app/NadekoBot
# final stage/image
FROM mcr.microsoft.com/dotnet/runtime:5.0
FROM mcr.microsoft.com/dotnet/runtime:5.0-buster-slim
WORKDIR /app
RUN set -xe; \
useradd -m nadeko; \
apt-get update; \
apt-get install -y libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 python3-pip sudo; \
update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1; \
echo 'Defaults>nadeko env_keep+="ASPNETCORE_* DOTNET_* NadekoBot_* shard_id total_shards TZ"' > /etc/sudoers.d/nadeko; \
pip3 install --upgrade youtube-dl; \
apt-get remove -y python3-pip; \
chmod +x /usr/local/bin/youtube-dl
COPY --from=build /app ./
COPY docker-entrypoint.sh /usr/local/sbin
ENV shard_id=0
ENV total_shards=1
WORKDIR /app
COPY --from=build /app ./
VOLUME [ "app/data", "app/creds.yml", "app/creds_example.yml" ]
ENTRYPOINT dotnet NadekoBot.dll "$shard_id" "$total_shards"
VOLUME [ "app/data" ]
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
CMD dotnet NadekoBot.dll "$shard_id" "$total_shards"

View File

@@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Coordinator", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.Generators", "src\NadekoBot.Generators\NadekoBot.Generators.csproj", "{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot.VotesApi", "src\NadekoBot.VotesApi\NadekoBot.VotesApi.csproj", "{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -62,6 +64,12 @@ Global
{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8}.Release|Any CPU.Build.0 = Release|Any CPU
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -73,6 +81,7 @@ Global
{DB448DD4-C97F-40E9-8BD3-F605FF1FF833} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
{AE9B7F8C-81D7-4401-83A3-643B38258374} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
{3BC3BDF8-1A0B-45EB-AB2B-C0891D4D37B8} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
{3BC82CFE-BEE7-451F-986B-17EDD1570C4F} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F3F555C-855F-4BE8-B526-D062D3E8ACA4}

20
docker-entrypoint.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
set -e;
data_init=/app/data_init
data=/app/data
# populate /app/data if empty
for i in $(ls $data_init)
do
if [ ! -e "$data/$i" ]; then
[ -f "$data_init/$i" ] && cp "$data_init/$i" "$data/$i"
[ -d "$data_init/$i" ] && cp -r "$data_init/$i" "$data/$i"
fi
done
# fix folder permissions
chown -R nadeko:nadeko "$data"
# drop to regular user and launch command
exec sudo -u nadeko "$@"

View File

@@ -1,3 +1,35 @@
# Setting up NadekoBot with Docker
Soon:tm:
# DO NOT USE YET - WORK IN PROGRESS
Upgrade from 2.x to v3 does not work because the file is mount readonly
### Docker Compose
```yml
version: "3.7"
services:
nadeko:
image: registry.gitlab.com/veovis/nadekobot:v3-docker
depends_on:
- redis
environment:
TZ: Europe/Paris
#NadekoBot_RedisOptions: redis,name=nadeko
#NadekoBot_ShardRunCommand: dotnet
#NadekoBot_ShardRunArguments: /app/NadekoBot.dll {0} {1}
volumes:
- /srv/nadeko/conf/creds.yml:/app/creds.yml:ro
- /srv/nadeko/data:/app/data
redis:
image: redis:4-alpine
sysctls:
- net.core.somaxconn=511
command: redis-server --maxmemory 32M --maxmemory-policy volatile-lru
volumes:
- /srv/nadeko/redis-data:/data
```
### Updating
- `cd /srv/nadeko`
- `docker-compose pull`
- `docker-compose up -d`

View File

@@ -16,12 +16,12 @@ Open Terminal (if you're on an installation with a window manager) and navigate
4. Exit the installer in order to set up your `creds.yml`
5. Copy the creds.yml template `cp nadekobot/output/creds_example.yml nadekobot/output/creds.yml`
6. Open `nadekobot/output/creds.yml` with your favorite text editor. We will use nano here
- `nano nadekobot/output/creds.yml`
- `nano nadekobot/output/creds.yml`
7. [Enter your bot's token](../../creds-guide)
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL` + `X`
- `Y`
- `Enter`
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL` + `X`
- `Y`
- `Enter`
8. Run the bot (type `3` and press enter)
##### Update Instructions
@@ -37,51 +37,51 @@ Open Terminal (if you're on an installation with a window manager) and navigate
##### Installation Instructions
1. Download the latest release from <https://gitlab.com/Kwoth/nadekobot/-/releases>
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
2. Untar it
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
- `tar xf X.XX.X-linux-x64-build.tar`
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
- `tar xf X.XX.X-linux-x64-build.tar`
3. Rename the `nadekobot-linux-x64` to `nadekobot`
- `mv nadekobot-linux-x64 nadekobot`
- `mv nadekobot-linux-x64 nadekobot`
4. Move into nadekobot directory and make NadekoBot executable
- `cd nadekobot && chmod +x NadekoBot`
- `cd nadekobot && chmod +x NadekoBot`
5. Copy the creds.yml template
- `cp creds_example.yml creds.yml`
- `cp creds_example.yml creds.yml`
6. Open `creds.yml` with your favorite text editor. We will use nano here
- `nano nadekobot/output/creds.yml`
- `nano nadekobot/output/creds.yml`
8. [Enter your bot's token](#creds-guide)
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL` + `X`
- `Y`
- `Enter`
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL` + `X`
- `Y`
- `Enter`
9. Run the bot
- `./NadekoBot`
- `./NadekoBot`
##### Update Instructions
1. Stop the bot
2. Download the latest release from <https://gitlab.com/Kwoth/nadekobot/-/releases>
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
- Look for the file called "x.x.x-linux-x64-build.tar" (where `X.X.X` is a version, for example 3.0.4) and download it
3. Untar it
- ⚠ Make sure that you change X.XX.X to the same series of numbers as in step 2!
- `tar xf 2.99.8-linux-x64-build.tar`
- ⚠ Make sure that you change `X.X.X` to the same series of numbers as in step 2!
- `tar xf x.x.x-linux-x64-build.tar`
4. Rename the old nadekobot directory to nadekobot-old (remove your old backup first if you have one, or back it up under a different name)
- `rm -rf nadekobot-old 2>/dev/null`
- `mv nadekobot nadekobot-old`
- `rm -rf nadekobot-old 2>/dev/null`
- `mv nadekobot nadekobot-old`
5. Rename the new nadekobot directory to nadekobot
- `mv nadekobot-linux-x64 nadekobot`
- `mv nadekobot-linux-x64 nadekobot`
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
- ⚠ If you've modified said files, back them up instead
- `rm nadekobot-old/data/aliases.yml`
- `rm -r nadekobot-old/data/strings`
- ⚠ If you've modified said files, back them up instead
- `rm nadekobot-old/data/aliases.yml`
- `rm -r nadekobot-old/data/strings`
7. Copy old data
- `cp -RT nadekobot-old/data/ nadekobot/data/`
- `cp -RT nadekobot-old/data/ nadekobot/data`
8. Copy creds.yml
- `cp nadekobot-old/creds.yml nadekobot/`
- `cp nadekobot-old/creds.yml nadekobot/`
9. Move into nadekobot directory and make the NadekoBot executable
- `cd nadekobot && chmod +x NadekoBot`
- `cd nadekobot && chmod +x NadekoBot`
10. Run the bot
- `./NadekoBot`
- `./NadekoBot`
🎉 Enjoy
@@ -95,7 +95,152 @@ mv nadekobot nadekobot-old && \
mv nadekobot-linux-x64 nadekobot && \
rm nadekobot-old/data/aliases.yml && \
rm -r nadekobot-old/data/strings && \
cp -RT nadekobot-old/data/ nadekobot/data/ && \
cp -RT nadekobot-old/data/ nadekobot/data && \
cp nadekobot-old/creds.yml nadekobot/ && \
cd nadekobot && chmod +x NadekoBot
```
## Running Nadeko
While there are two run modes built into the installer, these options only run Nadeko within the current session. Below are 3 methods of running Nadeko as a background process.
### Tmux (Preferred Method)
Using `tmux` is the simplest method, and is therefore recommended for most users.
1. Start a tmux session:
- `tmux`
2. Navigate to the project's root directory
- Project root directory location example: `/home/user/nadekobot/`
3. Enter the `output` directory:
- `cd output`
4. Run the bot using:
- `dotnet NadekoBot.dll`
5. Detatch the tmux session:
- Press `Ctrl` + `B`
- Then press `D`
Nadeko should now be running in the background of your system. To re-open the tmux session to either update, restart, or whatever, execute `tmux a`.
### Systemd
Compared to using tmux, this method requires a little bit more work to set up, but has the benefit of allowing Nadeko to automatically start back up after a system reboot or the execution of the `.die` command.
1. Navigate to the project's root directory
- Project root directory location example: `/home/user/nadekobot/`
2. Use the following command to create a service that will be used to start Nadeko:
```bash
echo "[Unit]
Description=NadekoBot service
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=2
[Service]
Type=simple
User=$USER
WorkingDirectory=$PWD/output
# If you want Nadeko to be compiled prior to every startup, uncomment the lines
# below. Note that it's not neccessary unless you are personally modifying the
# source code.
#ExecStartPre=/usr/bin/dotnet build ../src/NadekoBot/NadekoBot.csproj -c Release -o output/
ExecStart=/usr/bin/dotnet NadekoBot.dll
Restart=on-failure
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=NadekoBot
[Install]
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/nadeko.service
```
3. Make the new service available:
- `sudo systemctl daemon-reload`
4. Start Nadeko:
- `sudo systemctl start nadeko.service && sudo systemctl enable nadeko.service`
### Systemd + Script
This method is similar to the one above, but requires one extra step, with the added benefit of better error logging and control over what happens before and after the startup of Nadeko.
1. Locate the project and move to its parent directory
- Project location example: `/home/user/nadekobot/`
- Parent directory example: `/home/user/`
2. Use the following command to create a service that will be used to execute `NadekoRun.sh`:
```bash
echo "[Unit]
Description=NadekoBot service
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=2
[Service]
Type=simple
User=$USER
WorkingDirectory=$_WORKING_DIR
ExecStart=/bin/bash NadekoRun.sh
Restart=on-failure
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=NadekoBot
[Install]
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/nadeko.service
```
3. Make the new service available:
- `sudo systemctl daemon-reload`
4. Use the following command to create a script that will be used to start Nadeko:
```bash
{
echo '#!/bin/bash'
echo ""
echo "echo \"Running NadekoBot in the background with auto restart\"
youtube-dl -U
# If you want Nadeko to be compiled prior to every startup, uncomment the lines
# below. Note that it's not necessary unless you are personally modifying the
# source code.
#echo \"Compiling NadekoBot...\"
#cd \"$PWD\"/nadekobot
#dotnet build src/NadekoBot/NadekoBot.csproj -c Release -o output/
echo \"Starting NadekoBot...\"
while true; do
if [[ -d $PWD/nadekobot/output ]]; then
cd $PWD/nadekobot/output || {
echo \"Failed to change working directory to $PWD/nadekobot/output\" >&2
echo \"Ensure that the working directory inside of '/etc/systemd/system/nadeko.service' is correct\"
echo \"Exiting...\"
exit 1
}
else
echo \"$PWD/nadekobot/output doesn't exist\"
exit 1
fi
dotnet NadekoBot.dll || {
echo \"An error occurred when trying to start NadekBot\"
echo \"Exiting...\"
exit 1
}
echo \"Waiting for 5 seconds...\"
sleep 5
youtube-dl -U
echo \"Restarting NadekoBot...\"
done
echo \"Stopping NadekoBot...\""
} > NadekoRun.sh
```
5. Start Nadeko:
- `sudo systemctl start nadeko.service && sudo systemctl enable nadeko.service`

View File

@@ -3,10 +3,12 @@
## Windows
1. Run your NadekoBot Updater first, and **make sure your bot is updated to at least 2.46.5**
- **Run your 2.46.5 Bot** and make sure it works, and then **stop it**
- Close your old NadekoBot Updater
2. Get the new NadekoBot v3 Updater [here](https://dl.nadeko.bot/v3)
3. Click on the + icon to add a new bot
4. Next to the path, click on the folder icon and select the folder where your 2.46.5 bot is
- In case you're not sure where it's located, you can open your old updater and see it
- In case you're not sure where it's located, you can open your old updater and see it
5. If you've selected the correct path, you should have an **Update** button available, click it
6. You're done; you can now run your bot, and you can uninstall your old updater if you no longer have 2.x bots
7. 🎉
@@ -14,48 +16,51 @@
## Linux
1. In order to migrate a bot hosted on **Linux**, first update your current version to the latest 2.x version using the 2.x installer, run the bot, and make sure it works. Then:
- Run the **old** installer with `cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/1.9/linuxAIO.sh && bash linuxAIO.sh`
- Run option **1** again
- Run the bot
- Type `.stats` and ensure the version is `2.46.5` or later
- Stop the bot
- Run the **old** installer with `cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/1.9/linuxAIO.sh && bash linuxAIO.sh`
- Run option **1** again
- You **MUST** Run the bot now to ensure database is ready for migration
- Type `.stats` and ensure the version is `2.46.5` or later
- Stop the bot
2. Make sure your bot's folder is called `NadekoBot`
- Run `cd ~ && ls`
- Confirm there is a folder called NadekoBot (not nadekobot, in all lowercase)
- Run `cd ~ && ls`
- Confirm there is a folder called NadekoBot (not nadekobot, in all lowercase)
3. Migrate your bot's data using the new installer:
- Run the **new** installer `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/master/linuxAIO.sh && bash linuxAIO.sh`
- The installer should notify you that your data is ready for migration in a message above the menu.
- Install prerequisites (type `1` and press enter), and make sure it is successful
- Download NadekoBot v3 (type `2` and press enter)
- Run the bot (type `3` and press enter)
- Run the **new** installer `cd ~ && wget -N https://gitlab.com/Kwoth/nadeko-bash-installer/-/raw/master/linuxAIO.sh && bash linuxAIO.sh`
- The installer should notify you that your data is ready for migration in a message above the menu.
- Install prerequisites (type `1` and press enter), and make sure it is successful
- Download NadekoBot v3 (type `2` and press enter)
- Run the bot (type `3` and press enter)
4. Make sure your permissions, custom reactions, credentials, and other data is preserved
- `.stats` to ensure owner id (credentials) is correct
- `.lcr` to see custom reactions
- `.lp` to list permissions
5. 🎉 Enjoy. If you want to learn how to update the bot, click (here)[../linux-guide/#update-instructions].
- `.stats` to ensure owner id (credentials) is correct
- `.lcr` to see custom reactions
- `.lp` to list permissions
5. 🎉 Enjoy. If you want to learn how to update the bot, click [here](../linux-guide/#update-instructions)
## Manual
⚠ NOT RECOMMENDED
⚠ NadekoBot v3 requires [.net 5](https://dotnet.microsoft.com/download/dotnet/5.0)
1. In order to migrate a bot hosted **on Linux or from source on Windows**, first update your current version to the latest 2.x version using the 2.x installer, run the bot, and make sure it works. Then:
1. In order to migrate a bot hosted **on Linux or from source on Windows**
- First update your current version to the latest 2.x version using the 2.x installer
- Then you **must** run the bot to prepare the database for the migration, and make sure the bot works prior to upgrade.
Then:
2. Rename your old nadeko bot folder to `nadekobot_2x`
- `mv NadekoBot nadekobot_2x`
- `mv NadekoBot nadekobot_2x`
3. Build the new version and move old data to the output folder
1. Clone the v3 branch to a separate folder
- `git clone https://gitlab.com/kwoth/nadekobot -b v3 --depth 1`
2. Build the bot
- `dotnet publish -c Release -o output/ src/NadekoBot/`
3. Copy old data
- ⚠ Be sure you copy the correct command for your system!
- **Windows:** `cp -r -fo nadekobot_2x/src/NadekoBot/data nadekobot/src/NadekoBot/data`
- **Linux:** `cp -rf nadekobot_2x/src/NadekoBot/data nadekobot/src/NadekoBot/data`
4. Copy the database
- `cp nadekobot_2x/src/NadekoBot/bin/Release/netcoreapp2.1/data/NadekoBot.db nadekobot/output/data`
5. Copy your credentials
- `cp nadekobot_2x/src/NadekoBot/credentials.json nadekobot/output/`
1. Clone the v3 branch to a separate folder
- `git clone https://gitlab.com/kwoth/nadekobot -b v3 --depth 1`
2. Build the bot
- `dotnet publish -c Release -o output/ src/NadekoBot/`
3. Copy old data
- ⚠ Be sure you copy the correct command for your system!
- **Windows:** `cp -r -fo nadekobot_2x/src/NadekoBot/data nadekobot/src/NadekoBot/data`
- **Linux:** `cp -rf nadekobot_2x/src/NadekoBot/data nadekobot/src/NadekoBot/data`
4. Copy the database
- `cp nadekobot_2x/src/NadekoBot/bin/Release/netcoreapp2.1/data/NadekoBot.db nadekobot/output/data`
5. Copy your credentials
- `cp nadekobot_2x/src/NadekoBot/credentials.json nadekobot/output/`
4. Run the bot
- `cd nadekobot/output`
- `dotnet NadekoBot.dll`
- `cd nadekobot/output`
- `dotnet NadekoBot.dll`
5. That's it. Just make sure that when you're updating the bot, you're properly backing up your old data.

View File

@@ -1,6 +1,33 @@
### Linux From Source
## MacOS From Source
Open Terminal (if you're on an installation with a window manager) and navigate to the location where you want to install the bot (for example `cd ~`)
Open Terminal (if you don't know how to, click on the magnifying glass on the top right corner of your screen and type **Terminal** on the window that pops up) and navigate to the location where you want to install the bot (for example `cd ~`)
##### Installing Homebrew, wget and dotnet
###### Homebrew/wget
*Skip this step if you already have homebrew installed*
- Copy and paste this command, then press Enter:
- `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
- Install wget
- `brew install wget`
###### Dotnet
- Download [.net5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
- Open the `.pkg` file you've downloaded and install it.
- Run this command in Terminal. There might be output. If there is, disregard it. (copy-paste the entire block)
```bash
sudo mkdir /usr/local/bin
sudo mkdir /usr/local/lib
```
- Run this command in Terminal. There won't be any output. (copy-paste the entire block):
```bash
sudo ln -s /usr/local/share/dotnet/dotnet /usr/local/bin
sudo ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/
sudo ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/
```
##### Installation Instructions
@@ -8,11 +35,12 @@ Open Terminal (if you're on an installation with a window manager) and navigate
2. Install prerequisites (type `1` and press enter)
3. Download the bot (type `2` and press enter)
4. Exit the installer in order to set up your `creds.yml`
5. Copy the creds.yml template `cp nadekobot/output/creds_example.yml nadekobot/output/creds.yml`
5. Copy the creds.yml template
`cp nadekobot/output/creds_example.yml nadekobot/output/creds.yml`
6. Open `nadekobot/output/creds.yml` with your favorite text editor. We will use nano here
- `nano nadekobot/output/creds.yml`
- `nano nadekobot/output/creds.yml`
7. [Enter your bot's token](#creds-guide)
- After you're done, you can close nano (and save the file) by inputting, in order
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL`+`X`
- `Y`
- `Enter`
@@ -26,7 +54,7 @@ Open Terminal (if you're on an installation with a window manager) and navigate
4. Run the bot (type `3` and press enter)
5. 🎉
### Linux Release
## MacOS Manual Release installation instructions
##### Installation Instructions
@@ -34,48 +62,48 @@ Open Terminal (if you're on an installation with a window manager) and navigate
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
2. Untar it
⚠ Make sure that you change X.XX.X to the same series of numbers as in step 1!
- `tar xf X.XX.X-linux-x64-build.tar`
- `tar xf X.XX.X-linux-x64-build.tar`
3. Rename the `nadekobot-linux-x64` to `nadekobot`
- `mv nadekobot-linux-x64 nadekobot`
- `mv nadekobot-linux-x64 nadekobot`
4. Move into nadekobot directory and make NadekoBot executable
- `cd nadekobot && chmod +x NadekoBot`
- `cd nadekobot && chmod +x NadekoBot`
5. Copy the creds.yml template
- `cp creds_example.yml creds.yml`
- `cp creds_example.yml creds.yml`
6. Open `creds.yml` with your favorite text editor. We will use nano here
- `nano nadekobot/output/creds.yml`
- `nano nadekobot/output/creds.yml`
8. [Enter your bot's token](#creds-guide)
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL`+`X`
- `Y`
- `Enter`
- After you're done, you can close nano (and save the file) by inputting, in order
- `CTRL`+`X`
- `Y`
- `Enter`
9. Run the bot
- `./NadekoBot`
- `./NadekoBot`
##### Update Instructions
1. Stop the bot
2. Download the latest release from <https://gitlab.com/Kwoth/nadekobot/-/releases>
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
- Look for the file called "X.XX.X-linux-x64-build.tar" (where X.XX.X is a series of numbers) and download it
3. Untar it
⚠ Make sure that you change X.XX.X to the same series of numbers as in step 2!
- `tar xf 2.99.8-linux-x64-build.tar`
- `tar xf 2.99.8-linux-x64-build.tar`
4. Rename the old nadekobot directory to nadekobot-old (remove your old backup first if you have one, or back it up under a different name)
- `rm -rf nadekobot-old 2>/dev/null`
- `mv nadekobot nadekobot-old`
- `rm -rf nadekobot-old 2>/dev/null`
- `mv nadekobot nadekobot-old`
5. Rename the new nadekobot directory to nadekobot
- `mv nadekobot-linux-x64 nadekobot`
- `mv nadekobot-linux-x64 nadekobot`
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
⚠ If you've modified said files, back them up instead
- `rm nadekobot-old/data/aliases.yml`
- `rm -r nadekobot-old/data/strings`
- `rm nadekobot-old/data/aliases.yml`
- `rm -r nadekobot-old/data/strings`
7. Copy old data
- `cp -RT nadekobot-old/data/ nadekobot/data/`
- `cp -RT nadekobot-old/data/ nadekobot/data/`
8. Copy creds.yml
- `cp nadekobot-old/creds.yml nadekobot/`
- `cp nadekobot-old/creds.yml nadekobot/`
9. Move into nadekobot directory and make the NadekoBot executable
- `cd nadekobot && chmod +x NadekoBot`
- `cd nadekobot && chmod +x NadekoBot`
10. Run the bot
- `./NadekoBot`
- `./NadekoBot`
🎉 Enjoy

View File

@@ -21,7 +21,7 @@
#### Prerequisites
- Windows 8 or later (64-bit)
- [Create a Discord Bot application and invite the bot to your server](../../creds-guide.md)
- [Create a Discord Bot application and invite the bot to your server](../creds-guide.md)
**Optional**
@@ -32,12 +32,13 @@
- Download and run the [NadekoBot v3 Updater][Updater].
- Click on the + at the top left to create a new bot.
![NadekoBot Updater](https://i.imgur.com/KZV49uf.png "NadekoBot Updater")
![NadekoBot Updater](https://i.imgur.com/FmR7F7o.png "NadekoBot Updater")
- Give your bot a name and then click **`Go to setup`** at the lower right.
![Create a new bot](https://i.imgur.com/Xnp7iQL.png "Create a new bot")
![Create a new bot](https://i.imgur.com/JxtRk9e.png "Create a new bot")
- Click on **`DOWNLOAD`** at the lower right
![Bot Setup](https://i.imgur.com/6RMXNqw.png "Bot Setup")
![Bot Setup](https://i.imgur.com/HqAl36p.png "Bot Setup")
- Click on **`Install`** next to **`Redis`**.
- **Note: If Redis fails to install, install Redis manually here: [Redis Installer](https://github.com/MicrosoftArchive/redis/releases/tag/win-3.0.504) Download and run the **`.msi`** file.
- If you will use the music module, click on **`Install`** next to **`FFMPEG`** and **`Youtube-DL`**.
- If any dependencies fail to install, you can temporarily disable your Windows Defender/AV until you install them. If you don't want to, then read [the last section of this guide](#Manual-Prerequisite-Installation).
- When installation is finished, click on **`CREDS`** to the left of **`RUN`** at the lower right.
@@ -80,6 +81,7 @@ You can still install them manually:
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and navigate to the location where you want to install the bot (for example `cd ~/Desktop/`)
1. `git clone https://gitlab.com/kwoth/nadekobot -b v3 --depth 1`
2. `cd nadekobot`
3. `dotnet publish -c Release -o output/ src/NadekoBot/`
4. `cd output && cp creds_example.yml creds.yml`
5. Open `creds.yml` with your favorite text editor (Please don't use Notepad or WordPad. You can use Notepad++, VSCode, Atom, Sublime, or something similar)
@@ -91,16 +93,30 @@ Open PowerShell (press windows button on your keyboard and type powershell, it s
Open PowerShell as described above and run the following commands:
1. Navigate to your bot's folder, for example `cd ~/Desktop/nadekobot/src/NadekoBot`
2. Pull the latest updates (this will fail if you have custom code changes).
- If you don't have custom code changes, just run `git pull`
- If you do have custom code changes, You have 3 options
- Undo all changes with `git checkout -- * && git pull`
- Stash changes and try to re-apply them `git stash && git pull && git stash apply`
- Commit your changes and resolve merge conflicts `git add . && git commit -m "My commit message" && git pull`
3. Re-run the bot `dotnet run -c Release`
1. Stop the bot
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
2. Navigate to your bot's folder, example:
- `cd ~/Desktop/nadekobot`
3. Pull the new version
- `git pull`
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
4. **Backup** old output in case your data is overwritten
- `cp -r -fo output/ output-old`
5. Build the bot again
- `dotnet publish -c Release -o output/ src/NadekoBot/`
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
- ⚠ If you've modified said files, back them up instead
- `rm output-old/data/aliases.yml`
- `rm -r output-old/data/strings`
7. Copy old data
- `cp -Recurse .\output-old\data\ .\output\ -Force`
8. Copy creds.yml
- `cp output-old/creds.yml output/`
9. Run the bot
- `cd output`
- `dotnet NadekoBot.dll`
⚠ You're expected to understand that your database will be in `bin/Release/<framework>/data/`, and if `<framework>` gets changed in the future, you will have to move your database manually.
🎉 Enjoy
#### Music prerequisites
In order to use music commands, you need ffmpeg and youtube-dl installed.

View File

@@ -18,7 +18,7 @@ This part is completely optional, **however it's necessary for music and a few o
- Open up `creds.yml` and look for `GoogleAPIKey`, paste your API key after the `:`.
- It should look like this:
```yml
GoogleApiKey: "AIzaSyDSci1sdlWQOWNVj1vlXxxxxxbk0oWMEzM",
GoogleApiKey: "AIzaSyDSci1sdlWQOWNVj1vlXxxxxxbk0oWMEzM"
```
- **MashapeKey**
- Required for Hearthstone cards.
@@ -42,9 +42,8 @@ This part is completely optional, **however it's necessary for music and a few o
- Select `Chat Bot` from the Category dropdown
- Once created, clicking on your application will show a new Client ID field
- Copy it to your creds.yml as shown below
- *(if you're adding it as the last key inside your creds.yml, remove the trailling comma from the example below)*
```yml
TwitchClientId: "516tr61tr1qweqwe86trg3g",
TwitchClientId: "516tr61tr1qweqwe86trg3g"
```
- **LocationIqApiKey**
- Optional. Used only for the `.time` command. https://locationiq.com api key (register and you will receive the token in the email).
@@ -75,43 +74,93 @@ RestartCommand:
For Windows (Source), Linux or OSX, add this to your `creds.yml`
```yml
"RestartCommand": {
"Cmd": "dotnet",
"Args": "run -c Release"
},
RestartCommand:
Cmd: dotnet
Args: "NadekoBot.dll -- {0}"
```
---
#### End Result
**This is an example of how the `creds.yml` looks like with multiple owners, the restart command (optional) and all the API keys (also optional):**
**This is an example of how the `creds.yml` looks like with multiple owners, the restart command (optional) and some of the API keys (also optional):**
```yml
{
"Token": "MTc5MzcyXXX2MDI1ODY3MjY0.ChKs4g.I8J_R9XX0t-QY-0PzXXXiN0-7vo",
"OwnerIds": [
105635123466156544,
145521851676884992,
341420590009417729
],
"GoogleApiKey": "AIzaSyDSci1sdlWQOWNVj1vlXxxxxxbk0oWMEzM",
"MashapeKey": "4UrKpcWXc2mshS8RKi00000y8Kf5p1Q8kI6jsn32bmd8oVWiY7",
"OsuApiKey": "4c8c8fdff8e1234581725db27fd140a7d93320d6",
"CleverbotApiKey": "",
"Db": null,
"TotalShards": 1,
"PatreonAccessToken": "",
"PatreonCampaignId": "334038",
"RestartCommand": {
"Cmd": "NadekoBot.exe"
},
"ShardRunCommand": "",
"ShardRunArguments": "",
"ShardRunPort": null,
"TwitchClientId": null,
"RedisOptions": null
}
# DO NOT CHANGE
version: 1
# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
token: 'MTE5Nzc3MDIxMzE5NTc3NjEw.VlhNCw.BuqJFyzdIUAK1PRf1eK1Cu89Jew'
# List of Ids of the users who have bot owner permissions
# **DO NOT ADD PEOPLE YOU DON'T TRUST**
ownerIds: [
105635123466156544,
145521851676884992,
341420590009417729
]
# The number of shards that the bot will running on.
# Leave at 1 if you don't know what you're doing.
totalShards: 1
# Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it.
# Then, go to APIs and Services -> Credentials and click Create credentials -> API key.
# Used only for Youtube Data Api (at the moment).
googleApiKey: 'AIzaSyDScfdfdfi1sdlWQOWxxxxxbk0oWMEzM'
# Settings for voting system for discordbots. Meant for use on global Nadeko.
votes:
url: ''
key: ''
# Patreon auto reward system settings.
# go to https://www.patreon.com/portal -> my clients -> create client
patreon:
# Access token. You have to manually update this 1st of each month by refreshing the token on https://patreon.com/portal
accessToken: ''
# Unused atm
refreshToken: ''
# Unused atm
clientSecret: ''
# Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type "prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);" in the console. (ctrl + shift + i)
campaignId: ''
# Api key for sending stats to DiscordBotList.
botListToken: ''
# Official cleverbot api key.
cleverbotApiKey: ''
# Redis connection string. Don't change if you don't know what you're doing.
redisOptions: localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password=
# Database options. Don't change if you don't know what you're doing. Leave null for default values
db:
# Database type. Only sqlite supported atm
type: sqlite
# Connection string. Will default to "Data Source=data/NadekoBot.db"
connectionString: Data Source=data/NadekoBot.db
# Address and port of the coordinator endpoint. Leave empty for default.
# Change only if you've changed the coordinator address or port.
coordinatorUrl: http://localhost:3442
# Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)
rapidApiKey: 4UrKpcWXcxxxxxxxxxxxxxxp1Q8kI6jsn32xxxoVWiY7
# https://locationiq.com api key (register and you will receive the token in the email).
# Used only for .time command.
locationIqApiKey:
# https://timezonedb.com api key (register and you will receive the token in the email).
# Used only for .time command
timezoneDbApiKey:
# https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
# Used for cryptocurrency related commands.
coinmarketcapApiKey:
# Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api
osuApiKey: 4c8c8fdffdsfdsfsdfsfa33f3f3140a7d93320d6
# Command and args which will be used to restart the bot.
# Only used if bot is executed directly (NOT through the coordinator)
# placeholders:
# {0} -> shard id
# {1} -> total shards
# Linux default
# cmd: dotnet
# args: "NadekoBot.dll -- {0}"
# Windows default
# cmd: NadekoBot.exe
# args: {0}
restartCommand:
cmd:
args:
```
---
@@ -121,13 +170,13 @@ For Windows (Source), Linux or OSX, add this to your `creds.yml`
Nadeko saves all settings and data in the database file `NadekoBot.db`, located in:
- Windows (Updater): `system/data` (can be easily accessed through the `Data` button on the updater)
- Windows (Source), Linux and OSX: `NadekoBot/src/NadekoBot/bin/Release/netcoreapp2.1/data/NadekoBot.db`
- Windows (Source), Linux and OSX: `nadekobot/output/data/NadekoBot.db`
In order to open it you will need [SQLite Browser](http://sqlitebrowser.org/).
*NOTE: You don't have to worry if you don't have the `NadekoBot.db` file, it gets automatically created once you successfully run the bot for the first time.*
**To make changes:**
**To make changes to the database on windows:**
- Shut your bot down.
- Copy the `NadekoBot.db` file to someplace safe. (Back up)
@@ -146,20 +195,24 @@ In order to open it you will need [SQLite Browser](http://sqlitebrowser.org/).
## Sharding your bot
- **ShardRunCommand**
- Command with which to run shards 1+
- Required if you're sharding your bot on windows using .exe, or in a custom way.
- This internally defaults to `dotnet`
- For example, if you want to shard your NadekoBot which you installed using windows installer, you would want to set it to something like this: `C:\Program Files\NadekoBot\system\NadekoBot.exe`
- **ShardRunArguments**
- Arguments to the shard run command
- Required if you're sharding your bot on windows using .exe, or in a custom way.
- This internally defaults to `run -c Release --no-build -- {0} {1} {2}` which will be enough to run linux and other 'from source' setups
- {0} will be replaced by the `shard ID` of the shard being ran, {1} by the shard 0's process id, and {2} by the port shard communication is happening on
- If shard0 (main window) is closed, all other shards will close too
- For example, if you want to shard your NadekoBot which you installed using windows installer, you would want to set it to `{0} {1} {2}`
- **ShardRunPort**
- Bot uses a random UDP port in [5000, 6000] range for communication between shards
To run a sharded bot, you will want to run `src/NadekoBot.Coordinator` project.
Shards communicate with the coordinator using gRPC
To configure your Coordinator, you will need to edit the `src/NadekoBot.Coordinator/coord.yml` file
```yml
# total number of shards
TotalShards: 3
# How often do shards ping their state back to the coordinator
RecheckIntervalMs: 5000
# Command to run the shard
ShardStartCommand: dotnet
# Arguments to run the shard
# {0} = shard id
# {1} = total number of shards
ShardStartArgs: ../../output/NadekoBot.dll -- {0} {1}
# How long does it take for the shard to be forcefully restarted once it stops reporting its state
UnresponsiveSec: 30
```
[Google Console]: https://console.developers.google.com
[DiscordApp]: https://discordapp.com/developers/applications/me

View File

@@ -1,5 +1,4 @@
mkdocs-material==7.1.4
mkdocs==1.1.2
mkdocs-minify-plugin==0.4.0
mkdocs-git-revision-date-localized-plugin==0.9.2
mkdocs-material-extensions==1.0.1
mkdocs-material>=7.1.4
mkdocs>=1.1.2
mkdocs-git-revision-date-localized-plugin>=0.9.2
mkdocs-material-extensions>=1.0.1

View File

@@ -41,8 +41,6 @@ extra_css:
- stylesheets/theme.css
plugins:
- minify:
minify_html: true
- git-revision-date-localized:
type: date
- search
@@ -78,7 +76,6 @@ nav:
- Windows Guide: guides/windows-guide.md
- Linux Guide: guides/linux-guide.md
- OSX Guide: guides/osx-guide.md
- From Source: guides/from-source.md
- Docker Guide (unsupported): guides/docker-guide.md
- Commands:
- Readme: commands-readme.md

View File

@@ -1 +1 @@
{ "VersionName":"$CI_COMMIT_TAG", "DownloadLink":"https://cdn.nadeko.bot/dl/bot/INSTALLER_FILE_NAME" }
[{ "VersionName":"_VERSION_", "DownloadLink":"https://cdn.nadeko.bot/dl/bot/_INSTALLER_FILE_NAME_" }]

View File

@@ -9,9 +9,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.38.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.41.0" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup>

View File

@@ -171,7 +171,7 @@ namespace NadekoBot.Coordinator
}
}
status.Process?.Dispose();
try { status.Process?.Dispose(); } catch { }
var proc = StartShardProcess(shardId);
_shardStatuses[shardId] = status with

View File

@@ -1,5 +1,12 @@
# total number of shards
TotalShards: 3
# How often do shards ping their state back to the coordinator
RecheckIntervalMs: 5000
# Command to run the shard
ShardStartCommand: dotnet
# Arguments to run the shard
# {0} = shard id
# {1} = total number of shards
ShardStartArgs: run -p "..\NadekoBot\NadekoBot.csproj" --no-build -- {0} {1}
# How long does it take for the shard to be forcefully restarted once it stops reporting its state
UnresponsiveSec: 30

View File

@@ -2,12 +2,10 @@
using System.Globalization;
using System.Linq;
using System.Reflection;
using AngleSharp.Common;
using Discord.Commands;
using NadekoBot.Common.Attributes;
using NadekoBot.Services;
using NadekoBot.Modules;
using YamlDotNet.Serialization;
namespace NadekoBot.Tests
{

View File

@@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
</ItemGroup>

View File

@@ -0,0 +1,25 @@
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

1
src/NadekoBot.VotesApi/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
store/

View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NadekoBot.VotesApi.Controllers;
namespace NadekoBot.VotesApi
{
public class AuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string SchemeName = "AUTHORIZATION_SCHEME";
public const string DiscordsClaim = "DISCORDS_CLAIM";
public const string TopggClaim = "TOPGG_CLAIM";
private readonly IConfiguration _conf;
public AuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IConfiguration conf)
: base(options, logger, encoder, clock)
{
_conf = conf;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new List<Claim>();
if (_conf[ConfKeys.DISCORDS_KEY].Trim() == Request.Headers["Authorization"].ToString().Trim())
claims.Add(new(DiscordsClaim, "true"));
if (_conf[ConfKeys.TOPGG_KEY] == Request.Headers["Authorization"].ToString().Trim())
claims.Add(new Claim(TopggClaim, "true"));
return Task.FromResult(AuthenticateResult.Success(new(new(new ClaimsIdentity(claims)), SchemeName)));
}
}
}

View File

@@ -0,0 +1,8 @@
namespace NadekoBot.VotesApi
{
public static class ConfKeys
{
public const string DISCORDS_KEY = "DiscordsKey";
public const string TOPGG_KEY = "TopGGKey";
}
}

View File

@@ -0,0 +1,26 @@
namespace NadekoBot.VotesApi
{
public class DiscordsVoteWebhookModel
{
/// <summary>
/// The ID of the user who voted
/// </summary>
public string User { get; set; }
/// <summary>
/// The ID of the bot which recieved the vote
/// </summary>
public string Bot { get; set; }
/// <summary>
/// Contains totalVotes, votesMonth, votes24, hasVoted - a list of IDs of users who have voted this month, and
/// Voted24 - a list of IDs of users who have voted today
/// </summary>
public string Votes { get; set; }
/// <summary>
/// The type of event, whether it is a vote event or test event
/// </summary>
public string Type { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace NadekoBot.VotesApi
{
public static class Policies
{
public const string DiscordsAuth = "DiscordsAuth";
public const string TopggAuth = "TopggAuth";
}
}

View File

@@ -0,0 +1,30 @@
namespace NadekoBot.VotesApi
{
public class TopggVoteWebhookModel
{
/// <summary>
/// Discord ID of the bot that received a vote.
/// </summary>
public string Bot { get; set; }
/// <summary>
/// Discord ID of the user who voted.
/// </summary>
public string User { get; set; }
/// <summary>
/// The type of the vote (should always be "upvote" except when using the test button it's "test").
/// </summary>
public string Type { get; set; }
/// <summary>
/// Whether the weekend multiplier is in effect, meaning users votes count as two.
/// </summary>
public bool Weekend { get; set; }
/// <summary>
/// Query string params found on the /bot/:ID/vote page. Example: ?a=1&b=2.
/// </summary>
public string Query { get; set; }
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NadekoBot.VotesApi.Services;
namespace NadekoBot.VotesApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class DiscordsController : ControllerBase
{
private readonly ILogger<TopGgController> _logger;
private readonly IVotesCache _cache;
public DiscordsController(ILogger<TopGgController> logger, IVotesCache cache)
{
_logger = logger;
_cache = cache;
}
[HttpGet("new")]
[Authorize(Policy = Policies.DiscordsAuth)]
public async Task<IEnumerable<Vote>> New()
{
var votes = await _cache.GetNewDiscordsVotesAsync();
if(votes.Count > 0)
_logger.LogInformation("Sending {NewDiscordsVotes} new discords votes.", votes.Count);
return votes;
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NadekoBot.VotesApi.Services;
namespace NadekoBot.VotesApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class TopGgController : ControllerBase
{
private readonly ILogger<TopGgController> _logger;
private readonly IVotesCache _cache;
public TopGgController(ILogger<TopGgController> logger, IVotesCache cache)
{
_logger = logger;
_cache = cache;
}
[HttpGet("new")]
[Authorize(Policy = Policies.TopggAuth)]
public async Task<IEnumerable<Vote>> New()
{
var votes = await _cache.GetNewTopGgVotesAsync();
if(votes.Count > 0)
_logger.LogInformation("Sending {NewTopggVotes} new topgg votes.", votes.Count);
return votes;
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NadekoBot.VotesApi.Services;
namespace NadekoBot.VotesApi.Controllers
{
[ApiController]
public class WebhookController : ControllerBase
{
private readonly ILogger<WebhookController> _logger;
private readonly IVotesCache _votesCache;
private readonly IConfiguration _conf;
public WebhookController(ILogger<WebhookController> logger, IVotesCache votesCache, IConfiguration conf)
{
_logger = logger;
_votesCache = votesCache;
_conf = conf;
}
[HttpPost("/discordswebhook")]
[Authorize(Policy = Policies.DiscordsAuth)]
public async Task<IActionResult> DiscordsWebhook([FromBody]DiscordsVoteWebhookModel data)
{
_logger.LogInformation("User {UserId} has voted for Bot {BotId} on {Platform}",
data.User,
data.Bot,
"discords.com");
await _votesCache.AddNewDiscordsVote(data.User);
return Ok();
}
[HttpPost("/topggwebhook")]
[Authorize(Policy = Policies.TopggAuth)]
public async Task<IActionResult> TopggWebhook([FromBody] TopggVoteWebhookModel data)
{
_logger.LogInformation("User {UserId} has voted for Bot {BotId} on {Platform}",
data.User,
data.Bot,
"top.gg");
await _votesCache.AddNewTopggVote(data.User);
return Ok();
}
}
}

View File

@@ -0,0 +1,20 @@
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["src/NadekoBot.VotesApi/NadekoBot.VotesApi.csproj", "NadekoBot.VotesApi/"]
RUN dotnet restore "src/NadekoBot.VotesApi/NadekoBot.VotesApi.csproj"
COPY . .
WORKDIR "/src/NadekoBot.VotesApi"
RUN dotnet build "NadekoBot.VotesApi.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "NadekoBot.VotesApi.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "NadekoBot.VotesApi.dll"]

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MorseCode.ITask" Version="2.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace NadekoBot.VotesApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:16451",
"sslPort": 44323
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"NadekoBot.VotesApi": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,46 @@
## Votes Api
This api is used if you want your bot to be able to reward users who vote for it on discords.com or top.gg
#### [GET] `/discords/new`
Get the discords votes received after previous call to this endpoint.
Input full url of this endpoint in your creds.yml file under Discords url field.
For example "https://api.my.cool.bot/discords/new"
#### [GET] `/topgg/new`
Get the topgg votes received after previous call to this endpoint.
Input full url of this endpoint in your creds.yml file under Topgg url field.
For example "https://api.my.cool.bot/topgg/new"
#### [POST] `/discordswebhook`
Input this endpoint as the webhook on discords.com bot edit page
model: https://docs.botsfordiscord.com/methods/receiving-votes
For example "https://api.my.cool.bot/topggwebhook"
#### [POST] `/topggwebhook`
Input this endpoint as the webhook https://top.gg/bot/:your-bot-id/webhooks (replace :your-bot-id with your bot's id)
model: https://docs.top.gg/resources/webhooks/#schema
For example "https://api.my.cool.bot/discordswebhook"
Input your super-secret header value in appsettings.json's DiscordsKey and TopGGKey fields
They must match your DiscordsKey and TopGG key respectively, as well as your secrets in the discords.com and top.gg webhook setup pages
Full Example:
⚠ Change TopggKey and DiscordsKey to a secure long string
⚠ You can use https://www.random.org/strings/?num=1&len=20&digits=on&upperalpha=on&loweralpha=on&unique=on&format=html&rnd=new to generate it
`creds.yml`
```yml
votes:
TopggServiceUrl: "https://api.my.cool.bot/topgg"
TopggKey: "my_topgg_key"
DiscordsServiceUrl: "https://api.my.cool.bot/discords"
DiscordsKey: "my_discords_key"
```
`appsettings.json`
```json
...
"DiscordsKey": "my_discords_key",
"TopGGKey": "my_topgg_key",
...
```

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using MorseCode.ITask;
namespace NadekoBot.VotesApi.Services
{
public class FileVotesCache : IVotesCache
{
private const string statsFile = "store/stats.json";
private const string topggFile = "store/topgg.json";
private const string discordsFile = "store/discords.json";
private readonly SemaphoreSlim locker = new SemaphoreSlim(1, 1);
public FileVotesCache()
{
if (!Directory.Exists("store"))
Directory.CreateDirectory("store");
if(!File.Exists(topggFile))
File.WriteAllText(topggFile, "[]");
if(!File.Exists(discordsFile))
File.WriteAllText(discordsFile, "[]");
}
public ITask AddNewTopggVote(string userId)
{
return AddNewVote(topggFile, userId);
}
public ITask AddNewDiscordsVote(string userId)
{
return AddNewVote(discordsFile, userId);
}
private async ITask AddNewVote(string file, string userId)
{
await locker.WaitAsync();
try
{
var votes = await GetVotesAsync(file);
votes.Add(userId);
await File.WriteAllTextAsync(file , JsonSerializer.Serialize(votes));
}
finally
{
locker.Release();
}
}
public async ITask<IList<Vote>> GetNewTopGgVotesAsync()
{
var votes = await EvictTopggVotes();
return votes;
}
public async ITask<IList<Vote>> GetNewDiscordsVotesAsync()
{
var votes = await EvictDiscordsVotes();
return votes;
}
private ITask<List<Vote>> EvictTopggVotes()
=> EvictVotes(topggFile);
private ITask<List<Vote>> EvictDiscordsVotes()
=> EvictVotes(discordsFile);
private async ITask<List<Vote>> EvictVotes(string file)
{
await locker.WaitAsync();
try
{
var ids = await GetVotesAsync(file);
await File.WriteAllTextAsync(file, "[]");
return ids?
.Select(x => (Ok: ulong.TryParse(x, out var r), Id: r))
.Where(x => x.Ok)
.Select(x => new Vote
{
UserId = x.Id
})
.ToList();
}
finally
{
locker.Release();
}
}
private async ITask<IList<string>> GetVotesAsync(string file)
{
await using var fs = File.Open(file, FileMode.Open);
var votes = await JsonSerializer.DeserializeAsync<List<string>>(fs);
return votes;
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using MorseCode.ITask;
namespace NadekoBot.VotesApi.Services
{
public interface IVotesCache
{
ITask<IList<Vote>> GetNewTopGgVotesAsync();
ITask<IList<Vote>> GetNewDiscordsVotesAsync();
ITask AddNewTopggVote(string userId);
ITask AddNewDiscordsVote(string userId);
}
}

View File

@@ -0,0 +1,69 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using NadekoBot.VotesApi.Services;
namespace NadekoBot.VotesApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IVotesCache, FileVotesCache>();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "NadekoBot.VotesApi", Version = "v1" });
});
services
.AddAuthentication(opts =>
{
opts.DefaultScheme = AuthHandler.SchemeName;
opts.AddScheme<AuthHandler>(AuthHandler.SchemeName, AuthHandler.SchemeName);
});
services
.AddAuthorization(opts =>
{
opts.DefaultPolicy = new AuthorizationPolicyBuilder(AuthHandler.SchemeName)
.RequireAssertion(x => false)
.Build();
opts.AddPolicy(Policies.DiscordsAuth, policy => policy.RequireClaim(AuthHandler.DiscordsClaim));
opts.AddPolicy(Policies.TopggAuth, policy => policy.RequireClaim(AuthHandler.TopggClaim));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NadekoBot.VotesApi v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace NadekoBot.VotesApi
{
public class Vote
{
public ulong UserId { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"DiscordsKey": "my_discords_key",
"TopGGKey": "my_topgg_key",
"AllowedHosts": "*"
}

View File

@@ -19,6 +19,7 @@ using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.Configs;
using NadekoBot.Db;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Modules.Searches;
using Serilog;
namespace NadekoBot
@@ -28,7 +29,7 @@ namespace NadekoBot
private readonly IBotCredentials _creds;
private readonly CommandService _commandService;
private readonly DbService _db;
private readonly BotCredsProvider _credsProvider;
private readonly IBotCredsProvider _credsProvider;
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
@@ -95,18 +96,17 @@ namespace NadekoBot
}
var svcs = new ServiceCollection()
.AddTransient<IBotCredentials>(_ => _creds) // bot creds
.AddSingleton(_credsProvider)
.AddTransient<IBotCredentials>(_ => _credsProvider.GetCreds()) // bot creds
.AddSingleton<IBotCredsProvider>(_credsProvider)
.AddSingleton(_db) // database
.AddRedis(_creds.RedisOptions) // redis
.AddSingleton(Client) // discord socket client
.AddSingleton(_commandService)
.AddSingleton(this)
.AddSingleton<IDataCache, RedisCache>()
.AddSingleton<ISeria, JsonSeria>()
.AddSingleton<IPubSub, RedisPubSub>()
.AddSingleton<IConfigSeria, YamlSeria>()
.AddBotStringsServices()
.AddBotStringsServices(_creds.TotalShards)
.AddConfigServices()
.AddConfigMigrators()
.AddMemoryCache()
@@ -132,13 +132,22 @@ namespace NadekoBot
}
else
{
svcs.AddSingleton<ICoordinator, RemoteGrpcCoordinator>()
.AddSingleton<IReadyExecutor>(x => (IReadyExecutor)x.GetRequiredService<ICoordinator>());
svcs.AddSingleton<RemoteGrpcCoordinator>()
.AddSingleton<ICoordinator>(x => x.GetRequiredService<RemoteGrpcCoordinator>())
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RemoteGrpcCoordinator>());
}
svcs.AddSingleton<RedisLocalDataCache>()
.AddSingleton<ILocalDataCache>(x => x.GetRequiredService<RedisLocalDataCache>())
.AddSingleton<RedisImagesCache>()
.AddSingleton<IImageCache>(x => x.GetRequiredService<RedisImagesCache>())
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RedisImagesCache>())
.AddSingleton<IDataCache, RedisCache>();
svcs.Scan(scan => scan
.FromAssemblyOf<IReadyExecutor>()
.AddClasses(classes => classes.AssignableToAny(
.AddClasses(classes => classes
.AssignableToAny(
// services
typeof(INService),

View File

@@ -12,7 +12,7 @@ namespace NadekoBot.Common.Attributes
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo executingCommand, IServiceProvider services)
{
var creds = services.GetRequiredService<BotCredsProvider>().GetCreds();
var creds = services.GetRequiredService<IBotCredsProvider>().GetCreds();
return Task.FromResult((creds.IsOwner(context.User) || context.Client.CurrentUser.Id == context.User.Id ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("Not owner")));
}

View File

@@ -1,26 +0,0 @@
namespace NadekoBot.Common
{
public enum BotConfigEditType
{
/// <summary>
/// The amount of currency awarded to the winner of the trivia game.
/// Default is 0.
/// </summary>
TriviaCurrencyReward,
/// <summary>
/// Users can't start trivia games which have smaller win requirement than specified by this setting.
/// Default is 0.
/// </summary>
MinimumTriviaWinReq,
/// <summary>
/// The amount of XP the user receives when they send a message (which is not too short).
/// Default is 3.
/// </summary>
XpPerMessage,
/// <summary>
/// This value represents how often the user can receive XP from sending messages.
/// Default is 5.
/// </summary>
XpMinutesTimeout,
}
}

View File

@@ -12,7 +12,7 @@ namespace NadekoBot.Common.Configs
public sealed partial class BotConfig : ICloneable<BotConfig>
{
[Comment(@"DO NOT CHANGE")]
public int Version { get; set; }
public int Version { get; set; } = 2;
[Comment(@"Most commands, when executed, have a small colored line
next to the response. The color depends whether the command
@@ -48,6 +48,11 @@ they will receive this message. Leave empty for no response. The string which wi
Supports embeds. How it looks: https://puu.sh/B0BLV.png")]
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
public string DmHelpText { get; set; }
[Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response.
Case insensitive.
Leave empty to reply with DmHelpText to every DM.")]
public List<string> DmHelpTextKeywords { get; set; }
[Comment(@"This is the response for the .h command")]
[YamlMember(ScalarStyle = ScalarStyle.Literal)]
@@ -89,7 +94,6 @@ See RotatingStatuses submodule in Administration.")]
public BotConfig()
{
Version = 1;
var color = new ColorConfig();
Color = color;
DefaultLocale = new CultureInfo("en-US");
@@ -127,6 +131,14 @@ See RotatingStatuses submodule in Administration.")]
Prefix = ".";
RotateStatuses = false;
GroupGreets = false;
DmHelpTextKeywords = new List<string>()
{
"help",
"commands",
"cmds",
"module",
"can you do"
};
}
}

View File

@@ -13,7 +13,7 @@ namespace NadekoBot.Common
OwnerIds = new List<ulong>();
TotalShards = 1;
GoogleApiKey = string.Empty;
Votes = new(string.Empty, string.Empty);
Votes = new(string.Empty, string.Empty, string.Empty, string.Empty);
Patreon = new(string.Empty, string.Empty, string.Empty, string.Empty);
BotListToken = string.Empty;
CleverbotApiKey = string.Empty;
@@ -73,16 +73,6 @@ go to https://www.patreon.com/portal -> my clients -> create client")]
Change only if you've changed the coordinator address or port.")]
public string CoordinatorUrl { get; set; }
[YamlIgnore]
public string PatreonCampaignId => Patreon?.CampaignId;
[YamlIgnore]
public string PatreonAccessToken => Patreon?.AccessToken;
[YamlIgnore]
public string VotesUrl => Votes?.Url;
[YamlIgnore]
public string VotesToken => Votes.Key;
[Comment(@"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)")]
public string RapidApiKey { get; set; }
@@ -126,11 +116,9 @@ Windows default
// todo fixup patreon
public sealed record PatreonSettings
{
[Comment(@"Access token. You have to manually update this 1st of each month by refreshing the token on https://patreon.com/portal")]
public string ClientId { get; set; }
public string AccessToken { get; set; }
[Comment(@"Unused atm")]
public string RefreshToken { get; set; }
[Comment(@"Unused atm")]
public string ClientSecret { get; set; }
[Comment(@"Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type ""prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);"" in the console. (ctrl + shift + i)")]
@@ -143,19 +131,44 @@ Windows default
ClientSecret = clientSecret;
CampaignId = campaignId;
}
public PatreonSettings()
{
}
}
public sealed record VotesSettings
{
[Comment(@"")]
public string Url { get; set; }
[Comment(@"")]
public string Key { get; set; }
[Comment(@"top.gg votes service url
This is the url of your instance of the NadekoBot.Votes api
Example: https://votes.my.cool.bot.com")]
public string TopggServiceUrl { get; set; }
[Comment(@"Authorization header value sent to the TopGG service url with each request
This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file")]
public string TopggKey { get; set; }
[Comment(@"discords.com votes service url
This is the url of your instance of the NadekoBot.Votes api
Example: https://votes.my.cool.bot.com")]
public string DiscordsServiceUrl { get; set; }
[Comment(@"Authorization header value sent to the Discords service url with each request
This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file")]
public string DiscordsKey { get; set; }
public VotesSettings(string url, string key)
public VotesSettings()
{
Url = url;
Key = key;
}
public VotesSettings(string topggServiceUrl, string topggKey, string discordsServiceUrl, string discordsKey)
{
TopggServiceUrl = topggServiceUrl;
TopggKey = topggKey;
DiscordsServiceUrl = discordsServiceUrl;
DiscordsKey = discordsKey;
}
}

View File

@@ -14,11 +14,15 @@ namespace NadekoBot.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddBotStringsServices(this IServiceCollection services)
=> services
.AddSingleton<IStringsSource, LocalFileStringsSource>()
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
.AddSingleton<IBotStrings, BotStrings>();
public static IServiceCollection AddBotStringsServices(this IServiceCollection services, int totalShards)
=> totalShards <= 1
? services
.AddSingleton<IStringsSource, LocalFileStringsSource>()
.AddSingleton<IBotStringsProvider, LocalBotStringsProvider>()
.AddSingleton<IBotStrings, BotStrings>()
: services.AddSingleton<IStringsSource, LocalFileStringsSource>()
.AddSingleton<IBotStringsProvider, RedisBotStringsProvider>()
.AddSingleton<IBotStrings, BotStrings>();
public static IServiceCollection AddConfigServices(this IServiceCollection services)
{

View File

@@ -12,16 +12,14 @@ namespace NadekoBot
string GoogleApiKey { get; }
ICollection<ulong> OwnerIds { get; }
string RapidApiKey { get; }
string PatreonAccessToken { get; }
Creds.DbOptions Db { get; }
string OsuApiKey { get; }
int TotalShards { get; }
string PatreonCampaignId { get; }
Creds.PatreonSettings Patreon { get; }
string CleverbotApiKey { get; }
RestartConfig RestartCommand { get; }
string VotesUrl { get; }
string VotesToken { get; }
Creds.VotesSettings Votes { get; }
string BotListToken { get; }
string RedisOptions { get; }
string LocationIqApiKey { get; }

View File

@@ -1,10 +1,12 @@
using System;
using NadekoBot.Common.Yml;
namespace NadekoBot.Common
{
public class ImageUrls
{
public int Version { get; set; } = 2;
[Comment("DO NOT CHANGE")]
public int Version { get; set; } = 3;
public CoinData Coins { get; set; }
public Uri[] Currency { get; set; }
@@ -25,7 +27,6 @@ namespace NadekoBot.Common
public class SlotData
{
public Uri[] Emojis { get; set; }
public Uri[] Numbers { get; set; }
public Uri Bg { get; set; }
}

View File

@@ -0,0 +1,49 @@
using System;
namespace NadekoBot.Common
{
public class OldImageUrls
{
public int Version { get; set; } = 2;
public CoinData Coins { get; set; }
public Uri[] Currency { get; set; }
public Uri[] Dice { get; set; }
public RategirlData Rategirl { get; set; }
public XpData Xp { get; set; }
//new
public RipData Rip { get; set; }
public SlotData Slots { get; set; }
public class RipData
{
public Uri Bg { get; set; }
public Uri Overlay { get; set; }
}
public class SlotData
{
public Uri[] Emojis { get; set; }
public Uri[] Numbers { get; set; }
public Uri Bg { get; set; }
}
public class CoinData
{
public Uri[] Heads { get; set; }
public Uri[] Tails { get; set; }
}
public class RategirlData
{
public Uri Matrix { get; set; }
public Uri Dot { get; set; }
}
public class XpData
{
public Uri Bg { get; set; }
}
}
}

View File

@@ -89,6 +89,8 @@ namespace NadekoBot.Common.Replacements
_reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
_reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
_reps.TryAdd("%server.members%", () => g != null && g is SocketGuild sg ? sg.MemberCount.ToString() : "?");
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
_reps.TryAdd("%server.time%", () =>
{
TimeZoneInfo to = TimeZoneInfo.Local;
@@ -175,13 +177,13 @@ namespace NadekoBot.Common.Replacements
/*OBSOLETE*/
_reps.TryAdd("%servers%", () => c.Guilds.Count.ToString());
#if !GLOBAL_NADEKO
_reps.TryAdd("%users%", () => c.Guilds.Sum(s => s.Users.Count).ToString());
_reps.TryAdd("%users%", () => c.Guilds.Sum(g => g.MemberCount).ToString());
#endif
/*NEW*/
_reps.TryAdd("%shard.servercount%", () => c.Guilds.Count.ToString());
#if !GLOBAL_NADEKO
_reps.TryAdd("%shard.usercount%", () => c.Guilds.Sum(s => s.Users.Count).ToString());
_reps.TryAdd("%shard.usercount%", () => c.Guilds.Sum(g => g.MemberCount).ToString());
#endif
_reps.TryAdd("%shard.id%", () => c.ShardId.ToString());
return this;

View File

@@ -53,6 +53,7 @@ namespace NadekoBot.Common.Replacements
newEmbedData.Title = Replace(embedData.Title);
newEmbedData.Thumbnail = Replace(embedData.Thumbnail);
newEmbedData.Image = Replace(embedData.Image);
newEmbedData.Url = Replace(embedData.Url);
if (embedData.Author != null)
{
newEmbedData.Author = new SmartTextEmbedAuthor();
@@ -68,6 +69,7 @@ namespace NadekoBot.Common.Replacements
var newF = new SmartTextEmbedField();
newF.Name = Replace(f.Name);
newF.Value = Replace(f.Value);
newF.Inline = f.Inline;
fields.Add(newF);
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Discord;
using NadekoBot.Extensions;
using NadekoBot.Services;
@@ -29,6 +30,47 @@ namespace NadekoBot
(Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) ||
(Fields != null && Fields.Length > 0);
public static SmartEmbedText FromEmbed(IEmbed eb, string plainText = null)
{
var set = new SmartEmbedText();
set.PlainText = plainText;
set.Title = eb.Title;
set.Description = eb.Description;
set.Url = eb.Url;
set.Thumbnail = eb.Thumbnail?.Url;
set.Image = eb.Image?.Url;
set.Author = eb.Author is EmbedAuthor ea
? new()
{
Name = ea.Name,
Url = ea.Url,
IconUrl = ea.IconUrl
}
: null;
set.Footer = eb.Footer is EmbedFooter ef
? new()
{
Text = ef.Text,
IconUrl = ef.IconUrl
}
: null;
if (eb.Fields.Length > 0)
set.Fields = eb
.Fields
.Select(field => new SmartTextEmbedField()
{
Inline = field.Inline,
Name = field.Name,
Value = field.Value,
})
.ToArray();
set.Color = eb.Color?.RawValue ?? 0;
return set;
}
public EmbedBuilder GetEmbed()
{
var embed = new EmbedBuilder()

View File

@@ -0,0 +1,17 @@
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
namespace NadekoBot.Common.TypeReaders
{
public sealed class EmoteTypeReader : NadekoTypeReader<Emote>
{
public override Task<TypeReaderResult> ReadAsync(ICommandContext ctx, string input)
{
if (!Emote.TryParse(input, out var emote))
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Input is not a valid emote"));
return Task.FromResult(TypeReaderResult.FromSuccess(emote));
}
}
}

View File

@@ -20,6 +20,7 @@ namespace NadekoBot.Common.Yml
.WithTypeConverter(new Rgba32Converter())
.WithTypeConverter(new CultureInfoConverter())
.WithTypeConverter(new UriConverter())
.IgnoreUnmatchedProperties()
.Build();
}
}

View File

@@ -1,4 +1,5 @@
using NadekoBot.Db.Models;
using System;
using NadekoBot.Db.Models;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Discord;
@@ -138,14 +139,15 @@ WHERE UserId={userId};");
// just update the amount, there is no new user data
if (!updatedUserData)
{
ctx.Database.ExecuteSqlInterpolated($@"
var rows = ctx.Database.ExecuteSqlInterpolated($@"
UPDATE OR IGNORE DiscordUser
SET CurrencyAmount=CurrencyAmount+{amount}
WHERE UserId={userId};
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount)
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount});
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
");
}
else
{
@@ -157,8 +159,8 @@ SET CurrencyAmount=CurrencyAmount+{amount},
AvatarId={avatarId}
WHERE UserId={userId};
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount)
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount});
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
");
}
return true;
@@ -167,7 +169,7 @@ VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount});
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
{
return users
.Sum(x => x.CurrencyAmount);
.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
}
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)

View File

@@ -48,12 +48,12 @@ namespace NadekoBot.Db
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
{
// todo split query
return configs
.AsQueryable()
.Include(gc => gc.CommandCooldowns)
.Include(gc => gc.FollowedStreams)
.Include(gc => gc.StreamRole)
.Include(gc => gc.NsfwBlacklistedTags)
.Include(gc => gc.XpSettings)
.ThenInclude(x => x.ExclusionList)
.Include(gc => gc.DelMsgOnCmdChannels)
@@ -113,33 +113,24 @@ namespace NadekoBot.Db
return config;
}
public static GuildConfig LogSettingsFor(this NadekoContext ctx, ulong guildId)
public static LogSetting LogSettingsFor(this NadekoContext ctx, ulong guildId)
{
var config = ctx
.GuildConfigs
var logSetting = ctx.LogSettings
.AsQueryable()
.Include(gc => gc.LogSetting)
.ThenInclude(gc => gc.IgnoredChannels)
.FirstOrDefault(x => x.GuildId == guildId);
.Include(x => x.LogIgnores)
.Where(x => x.GuildId == guildId)
.FirstOrDefault();
if (config is null)
if (logSetting is null)
{
ctx.GuildConfigs.Add((config = new GuildConfig
ctx.LogSettings.Add(logSetting = new ()
{
GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist,
WarningsInitialized = true,
WarnPunishments = DefaultWarnPunishments,
}));
GuildId = guildId
});
ctx.SaveChanges();
}
if (!config.WarningsInitialized)
{
config.WarningsInitialized = true;
config.WarnPunishments = DefaultWarnPunishments;
}
return config;
return logSetting;
}
public static IEnumerable<GuildConfig> Permissionsv2ForAll(this DbSet<GuildConfig> configs, List<ulong> include)

View File

@@ -9,6 +9,11 @@ namespace NadekoBot.Db
{
public static class QuoteExtensions
{
public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
{
return quotes.AsQueryable().Where(x => x.GuildId == guildId);
}
public static IEnumerable<Quote> GetGroup(this DbSet<Quote> quotes, ulong guildId, int page, OrderType order)
{
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);

View File

@@ -18,5 +18,12 @@ namespace NadekoBot.Db
.OrderBy(x => x.DateAdded)
.Skip(page * 10)
.Take(10);
public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
=> reminders.AsQueryable()
.Where(x => x.ServerId == serverId)
.OrderBy(x => x.DateAdded)
.Skip(page * 10)
.Take(10);
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace NadekoBot.Services.Database.Models
{
public class AutoTranslateChannel : DbEntity
{
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
public bool AutoDelete { get; set; }
public IList<AutoTranslateUser> Users { get; set; } = new List<AutoTranslateUser>();
}
}

View File

@@ -0,0 +1,11 @@
namespace NadekoBot.Services.Database.Models
{
public class AutoTranslateUser : DbEntity
{
public int ChannelId { get; set; }
public AutoTranslateChannel Channel { get; set; }
public ulong UserId { get; set; }
public string Source { get; set; }
public string Target { get; set; }
}
}

View File

@@ -29,11 +29,18 @@ namespace NadekoBot.Services.Database.Models
public bool SendChannelGreetMessage { get; set; }
public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
#region Boost Message
public bool SendBoostMessage { get; set; }
public string BoostMessage { get; set; } = "%user% just boosted this server!";
public ulong BoostMessageChannelId { get; set; }
public int BoostMessageDeleteAfter { get; set; }
#endregion
public bool SendChannelByeMessage { get; set; }
public string ChannelByeMessageText { get; set; } = "%user% has left!";
public LogSetting LogSetting { get; set; } = new LogSetting();
//self assignable roles
public bool ExclusiveSelfAssignedRoles { get; set; }
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
@@ -84,7 +91,6 @@ namespace NadekoBot.Services.Database.Models
public bool WarningsInitialized { get; set; }
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
public HashSet<NsfwBlacklitedTag> NsfwBlacklistedTags { get; set; } = new HashSet<NsfwBlacklitedTag>();
public List<ShopEntry> ShopEntries { get; set; }
public ulong? GameVoiceChannel { get; set; } = null;

View File

@@ -0,0 +1,16 @@
namespace NadekoBot.Services.Database.Models
{
public class IgnoredLogItem : DbEntity
{
public int LogSettingId { get; set; }
public LogSetting LogSetting { get; set; }
public ulong LogItemId { get; set; }
public IgnoredItemType ItemType { get; set; }
}
public enum IgnoredItemType
{
Channel,
User,
}
}

View File

@@ -1,8 +1,8 @@
namespace NadekoBot.Services.Database.Models
{
public class IgnoredLogChannel : DbEntity
public class ImageOnlyChannel : DbEntity
{
public LogSetting LogSetting { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
}
}
}

View File

@@ -2,12 +2,11 @@
namespace NadekoBot.Services.Database.Models
{
public class LogSetting : DbEntity
{
public HashSet<IgnoredLogChannel> IgnoredChannels { get; set; } = new HashSet<IgnoredLogChannel>();
public HashSet<IgnoredVoicePresenceChannel> IgnoredVoicePresenceChannelIds { get; set; } = new HashSet<IgnoredVoicePresenceChannel>();
public List<IgnoredLogItem> LogIgnores { get; set; } = new List<IgnoredLogItem>();
public ulong GuildId { get; set; }
public ulong? LogOtherId { get; set; }
public ulong? MessageUpdatedId { get; set; }
public ulong? MessageDeletedId { get; set; }

View File

@@ -0,0 +1,16 @@
using System;
namespace NadekoBot.Services.Database.Models
{
public class NsfwBlacklistedTag : DbEntity
{
public ulong GuildId { get; set; }
public string Tag { get; set; }
public override int GetHashCode()
=> Tag.GetHashCode(StringComparison.InvariantCulture);
public override bool Equals(object obj)
=> obj is NsfwBlacklistedTag x && x.Tag == Tag;
}
}

View File

@@ -1,21 +0,0 @@
using System;
namespace NadekoBot.Services.Database.Models
{
public class NsfwBlacklitedTag : DbEntity
{
public string Tag { get; set; }
public override int GetHashCode()
{
return Tag.GetHashCode(StringComparison.InvariantCulture);
}
public override bool Equals(object obj)
{
return obj is NsfwBlacklitedTag x
? x.Tag == Tag
: false;
}
}
}

View File

@@ -8,5 +8,6 @@
public bool Forgiven { get; set; }
public string ForgivenBy { get; set; }
public string Moderator { get; set; }
public int Weight { get; set; }
}
}

View File

@@ -2,11 +2,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using NadekoBot.Services.Database.Models;
using NadekoBot.Services;
using System;
using System.IO;
using Microsoft.Extensions.Logging;
using NadekoBot.Common;
using NadekoBot.Db.Models;
namespace NadekoBot.Services.Database
@@ -44,8 +42,8 @@ namespace NadekoBot.Services.Database
//logging
public DbSet<LogSetting> LogSettings { get; set; }
public DbSet<IgnoredLogChannel> IgnoredLogChannels { get; set; }
public DbSet<IgnoredVoicePresenceChannel> IgnoredVoicePresenceCHannels { get; set; }
public DbSet<IgnoredLogItem> IgnoredLogChannels { get; set; }
public DbSet<RotatingPlayingStatus> RotatingStatus { get; set; }
public DbSet<BlacklistEntry> Blacklist { get; set; }
@@ -60,6 +58,10 @@ namespace NadekoBot.Services.Database
public DbSet<Repeater> Repeaters { get; set; }
public DbSet<Poll> Poll { get; set; }
public DbSet<WaifuInfo> WaifuInfo { get; set; }
public DbSet<ImageOnlyChannel> ImageOnlyChannels { get; set; }
public DbSet<NsfwBlacklistedTag> NsfwBlacklistedTags { get; set; }
public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; }
public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
{
@@ -196,10 +198,16 @@ namespace NadekoBot.Services.Database
#endregion
#region Warnings
var warn = modelBuilder.Entity<Warning>();
warn.HasIndex(x => x.GuildId);
warn.HasIndex(x => x.UserId);
warn.HasIndex(x => x.DateAdded);
modelBuilder.Entity<Warning>(warn =>
{
warn.HasIndex(x => x.GuildId);
warn.HasIndex(x => x.UserId);
warn.HasIndex(x => x.DateAdded);
warn.Property(x => x.Weight)
.HasDefaultValue(1);
});
#endregion
#region PatreonRewards
@@ -328,6 +336,55 @@ namespace NadekoBot.Services.Database
.HasDefaultValue(100);
#endregion
#region Reaction roles
modelBuilder.Entity<ReactionRoleMessage>(rrm => rrm
.HasMany(x => x.ReactionRoles)
.WithOne()
.OnDelete(DeleteBehavior.Cascade));
#endregion
#region LogSettings
modelBuilder.Entity<LogSetting>(ls => ls
.HasIndex(x => x.GuildId)
.IsUnique());
modelBuilder.Entity<LogSetting>(ls => ls
.HasMany(x => x.LogIgnores)
.WithOne(x => x.LogSetting)
.OnDelete(DeleteBehavior.Cascade));
modelBuilder.Entity<IgnoredLogItem>(ili => ili
.HasIndex(x => new { x.LogSettingId, x.LogItemId, x.ItemType })
.IsUnique());
#endregion
modelBuilder.Entity<ImageOnlyChannel>(ioc => ioc
.HasIndex(x => x.ChannelId)
.IsUnique());
modelBuilder.Entity<NsfwBlacklistedTag>(nbt => nbt
.HasIndex(x => x.GuildId)
.IsUnique(false));
var atch = modelBuilder.Entity<AutoTranslateChannel>();
atch.HasIndex(x => x.GuildId)
.IsUnique(false);
atch.HasIndex(x => x.ChannelId)
.IsUnique();
atch
.HasMany(x => x.Users)
.WithOne(x => x.Channel)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<AutoTranslateUser>(atu => atu
.HasAlternateKey(x => new { x.ChannelId, x.UserId }));
}
}
}

View File

@@ -17,6 +17,12 @@ namespace NadekoBot.Migrations
migrationBuilder.Sql("DELETE FROM FilterChannelId WHERE GuildConfigId NOT IN (SELECT Id from GuildConfigs)");
migrationBuilder.Sql("DELETE FROM CommandCooldown WHERE GuildConfigId NOT IN (SELECT Id from GuildConfigs)");
// fix for users who edited their waifuinfo table manually and are unable to update
migrationBuilder.Sql("DELETE FROM WaifuInfo where WaifuId not in (SELECT Id from DiscordUser);");
// fix for users who deleted clubs manually and are unable to update now
migrationBuilder.Sql("UPDATE DiscordUser SET ClubId = null WHERE ClubId is not null and ClubId not in (SELECT Id from Clubs);");
migrationBuilder.DropColumn(
name: "ChannelCreated",
table: "LogSettings");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class rerocascade : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ReactionRole_ReactionRoleMessage_ReactionRoleMessageId",
table: "ReactionRole");
migrationBuilder.AddForeignKey(
name: "FK_ReactionRole_ReactionRoleMessage_ReactionRoleMessageId",
table: "ReactionRole",
column: "ReactionRoleMessageId",
principalTable: "ReactionRoleMessage",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ReactionRole_ReactionRoleMessage_ReactionRoleMessageId",
table: "ReactionRole");
migrationBuilder.AddForeignKey(
name: "FK_ReactionRole_ReactionRoleMessage_ReactionRoleMessageId",
table: "ReactionRole",
column: "ReactionRoleMessageId",
principalTable: "ReactionRoleMessage",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class boostmessages : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "BoostMessage",
table: "GuildConfigs",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<ulong>(
name: "BoostMessageChannelId",
table: "GuildConfigs",
type: "INTEGER",
nullable: false,
defaultValue: 0ul);
migrationBuilder.AddColumn<int>(
name: "BoostMessageDeleteAfter",
table: "GuildConfigs",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<bool>(
name: "SendBoostMessage",
table: "GuildConfigs",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BoostMessage",
table: "GuildConfigs");
migrationBuilder.DropColumn(
name: "BoostMessageChannelId",
table: "GuildConfigs");
migrationBuilder.DropColumn(
name: "BoostMessageDeleteAfter",
table: "GuildConfigs");
migrationBuilder.DropColumn(
name: "SendBoostMessage",
table: "GuildConfigs");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class logsettingsindependence : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<ulong>(
name: "GuildId",
table: "LogSettings",
type: "INTEGER",
nullable: false,
defaultValue: 0ul);
migrationBuilder.Sql(
@"UPDATE LogSettings SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE LogSettingId = LogSettings.Id);
DELETE FROM LogSettings WHERE GuildId = 0;");
migrationBuilder.DropForeignKey(
name: "FK_GuildConfigs_LogSettings_LogSettingId",
table: "GuildConfigs");
migrationBuilder.DropIndex(
name: "IX_GuildConfigs_LogSettingId",
table: "GuildConfigs");
migrationBuilder.DropColumn(
name: "LogSettingId",
table: "GuildConfigs");
migrationBuilder.CreateIndex(
name: "IX_LogSettings_GuildId",
table: "LogSettings",
column: "GuildId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_LogSettings_GuildId",
table: "LogSettings");
migrationBuilder.DropColumn(
name: "GuildId",
table: "LogSettings");
migrationBuilder.AddColumn<int>(
name: "LogSettingId",
table: "GuildConfigs",
type: "INTEGER",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_GuildConfigs_LogSettingId",
table: "GuildConfigs",
column: "LogSettingId");
migrationBuilder.AddForeignKey(
name: "FK_GuildConfigs_LogSettings_LogSettingId",
table: "GuildConfigs",
column: "LogSettingId",
principalTable: "LogSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class imageonlychannels : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ImageOnlyChannels",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ImageOnlyChannels", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_ImageOnlyChannels_ChannelId",
table: "ImageOnlyChannels",
column: "ChannelId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ImageOnlyChannels");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class logignoreuserchannel : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DELETE FROM IgnoredLogChannels WHERE LogSettingId is NULL");
migrationBuilder.DropForeignKey(
name: "FK_IgnoredLogChannels_LogSettings_LogSettingId",
table: "IgnoredLogChannels");
migrationBuilder.DropIndex(
name: "IX_IgnoredLogChannels_LogSettingId",
table: "IgnoredLogChannels");
migrationBuilder.RenameColumn(
name: "ChannelId",
table: "IgnoredLogChannels",
newName: "LogItemId");
migrationBuilder.AlterColumn<int>(
name: "LogSettingId",
table: "IgnoredLogChannels",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "INTEGER",
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "ItemType",
table: "IgnoredLogChannels",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_IgnoredLogChannels_LogSettingId_LogItemId_ItemType",
table: "IgnoredLogChannels",
columns: new[] { "LogSettingId", "LogItemId", "ItemType" },
unique: true);
migrationBuilder.AddForeignKey(
name: "FK_IgnoredLogChannels_LogSettings_LogSettingId",
table: "IgnoredLogChannels",
column: "LogSettingId",
principalTable: "LogSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_IgnoredLogChannels_LogSettings_LogSettingId",
table: "IgnoredLogChannels");
migrationBuilder.DropIndex(
name: "IX_IgnoredLogChannels_LogSettingId_LogItemId_ItemType",
table: "IgnoredLogChannels");
migrationBuilder.DropColumn(
name: "ItemType",
table: "IgnoredLogChannels");
migrationBuilder.RenameColumn(
name: "LogItemId",
table: "IgnoredLogChannels",
newName: "ChannelId");
migrationBuilder.AlterColumn<int>(
name: "LogSettingId",
table: "IgnoredLogChannels",
type: "INTEGER",
nullable: true,
oldClrType: typeof(int),
oldType: "INTEGER");
migrationBuilder.CreateIndex(
name: "IX_IgnoredLogChannels_LogSettingId",
table: "IgnoredLogChannels",
column: "LogSettingId");
migrationBuilder.AddForeignKey(
name: "FK_IgnoredLogChannels_LogSettings_LogSettingId",
table: "IgnoredLogChannels",
column: "LogSettingId",
principalTable: "LogSettings",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class nsfwblacklisttags : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "NsfwBlacklistedTags",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
Tag = table.Column<string>(type: "TEXT", nullable: true),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_NsfwBlacklistedTags", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_NsfwBlacklistedTags_GuildId",
table: "NsfwBlacklistedTags",
column: "GuildId");
migrationBuilder.Sql(@"INSERT INTO NsfwBlacklistedTags(Id, GuildId, Tag, DateAdded)
SELECT
Id,
(SELECT GuildId From GuildConfigs WHERE Id=GuildConfigId),
Tag,
DateAdded
FROM NsfwBlacklitedTag
WHERE GuildConfigId in (SELECT Id from GuildConfigs);");
migrationBuilder.DropTable(
name: "NsfwBlacklitedTag");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "NsfwBlacklistedTags");
migrationBuilder.CreateTable(
name: "NsfwBlacklitedTag",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
GuildConfigId = table.Column<int>(type: "INTEGER", nullable: true),
Tag = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_NsfwBlacklitedTag", x => x.Id);
table.ForeignKey(
name: "FK_NsfwBlacklitedTag_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_NsfwBlacklitedTag_GuildConfigId",
table: "NsfwBlacklitedTag",
column: "GuildConfigId");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class weightedwarnings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "Weight",
table: "Warnings",
type: "INTEGER",
nullable: false,
defaultValue: 1);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Weight",
table: "Warnings");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class atlrework : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AutoTranslateChannels",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
AutoDelete = table.Column<bool>(type: "INTEGER", nullable: false),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AutoTranslateChannels", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AutoTranslateUsers",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ChannelId = table.Column<int>(type: "INTEGER", nullable: false),
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
Source = table.Column<string>(type: "TEXT", nullable: true),
Target = table.Column<string>(type: "TEXT", nullable: true),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AutoTranslateUsers", x => x.Id);
table.UniqueConstraint("AK_AutoTranslateUsers_ChannelId_UserId", x => new { x.ChannelId, x.UserId });
table.ForeignKey(
name: "FK_AutoTranslateUsers_AutoTranslateChannels_ChannelId",
column: x => x.ChannelId,
principalTable: "AutoTranslateChannels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AutoTranslateChannels_ChannelId",
table: "AutoTranslateChannels",
column: "ChannelId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AutoTranslateChannels_GuildId",
table: "AutoTranslateChannels",
column: "GuildId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AutoTranslateUsers");
migrationBuilder.DropTable(
name: "AutoTranslateChannels");
}
}
}

View File

@@ -14,7 +14,7 @@ namespace NadekoBot.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "5.0.7");
.HasAnnotation("ProductVersion", "5.0.8");
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
{
@@ -340,6 +340,62 @@ namespace NadekoBot.Migrations
b.ToTable("AutoCommands");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("AutoDelete")
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ChannelId")
.IsUnique();
b.HasIndex("GuildId");
b.ToTable("AutoTranslateChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("ChannelId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<string>("Source")
.HasColumnType("TEXT");
b.Property<string>("Target")
.HasColumnType("TEXT");
b.Property<ulong>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasAlternateKey("ChannelId", "UserId");
b.ToTable("AutoTranslateUsers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
{
b.Property<int>("Id")
@@ -741,6 +797,15 @@ namespace NadekoBot.Migrations
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages")
.HasColumnType("INTEGER");
b.Property<string>("BoostMessage")
.HasColumnType("TEXT");
b.Property<ulong>("BoostMessageChannelId")
.HasColumnType("INTEGER");
b.Property<int>("BoostMessageDeleteAfter")
.HasColumnType("INTEGER");
b.Property<ulong>("ByeMessageChannelId")
.HasColumnType("INTEGER");
@@ -786,9 +851,6 @@ namespace NadekoBot.Migrations
b.Property<string>("Locale")
.HasColumnType("TEXT");
b.Property<int?>("LogSettingId")
.HasColumnType("INTEGER");
b.Property<string>("MuteRoleName")
.HasColumnType("TEXT");
@@ -801,6 +863,9 @@ namespace NadekoBot.Migrations
b.Property<string>("Prefix")
.HasColumnType("TEXT");
b.Property<bool>("SendBoostMessage")
.HasColumnType("INTEGER");
b.Property<bool>("SendChannelByeMessage")
.HasColumnType("INTEGER");
@@ -833,31 +898,33 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId")
.IsUnique();
b.HasIndex("LogSettingId");
b.HasIndex("WarnExpireHours");
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("LogSettingId")
b.Property<int>("ItemType")
.HasColumnType("INTEGER");
b.Property<ulong>("LogItemId")
.HasColumnType("INTEGER");
b.Property<int>("LogSettingId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.HasIndex("LogSettingId", "LogItemId", "ItemType")
.IsUnique();
b.ToTable("IgnoredLogChannels");
});
@@ -884,6 +951,29 @@ namespace NadekoBot.Migrations
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("ImageOnlyChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
{
b.Property<int>("Id")
@@ -902,6 +992,9 @@ namespace NadekoBot.Migrations
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<ulong?>("LogOtherId")
.HasColumnType("INTEGER");
@@ -940,6 +1033,9 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("LogSettings");
});
@@ -1022,7 +1118,7 @@ namespace NadekoBot.Migrations
b.ToTable("MutedUserId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklitedTag", b =>
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@@ -1031,7 +1127,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");
b.Property<int?>("GuildConfigId")
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<string>("Tag")
@@ -1039,9 +1135,9 @@ namespace NadekoBot.Migrations
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.HasIndex("GuildId");
b.ToTable("NsfwBlacklitedTag");
b.ToTable("NsfwBlacklistedTags");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
@@ -1927,6 +2023,11 @@ namespace NadekoBot.Migrations
b.Property<ulong>("UserId")
.HasColumnType("INTEGER");
b.Property<int>("Weight")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasDefaultValue(1);
b.HasKey("Id");
b.HasIndex("DateAdded");
@@ -2149,6 +2250,17 @@ namespace NadekoBot.Migrations
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.AutoTranslateChannel", "Channel")
.WithMany("Users")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Channel");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
@@ -2233,20 +2345,13 @@ namespace NadekoBot.Migrations
b.Navigation("GuildConfig");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId");
b.Navigation("LogSetting");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredChannels")
.HasForeignKey("LogSettingId");
.WithMany("LogIgnores")
.HasForeignKey("LogSettingId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("LogSetting");
});
@@ -2254,7 +2359,7 @@ namespace NadekoBot.Migrations
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredVoicePresenceChannelIds")
.WithMany()
.HasForeignKey("LogSettingId");
b.Navigation("LogSetting");
@@ -2267,13 +2372,6 @@ namespace NadekoBot.Migrations
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklitedTag", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
.WithMany("NsfwBlacklistedTags")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", null)
@@ -2307,7 +2405,8 @@ namespace NadekoBot.Migrations
{
b.HasOne("NadekoBot.Services.Database.Models.ReactionRoleMessage", null)
.WithMany("ReactionRoles")
.HasForeignKey("ReactionRoleMessageId");
.HasForeignKey("ReactionRoleMessageId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b =>
@@ -2509,6 +2608,11 @@ namespace NadekoBot.Migrations
b.Navigation("IgnoredChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
{
b.Navigation("Users");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.Navigation("AntiAltSetting");
@@ -2539,8 +2643,6 @@ namespace NadekoBot.Migrations
b.Navigation("MutedUsers");
b.Navigation("NsfwBlacklistedTags");
b.Navigation("Permissions");
b.Navigation("ReactionRoleMessages");
@@ -2570,9 +2672,7 @@ namespace NadekoBot.Migrations
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
{
b.Navigation("IgnoredChannels");
b.Navigation("IgnoredVoicePresenceChannelIds");
b.Navigation("LogIgnores");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>

View File

@@ -13,13 +13,31 @@ namespace NadekoBot.Modules.Administration
{
public partial class Administration : NadekoModule<AdministrationService>
{
private readonly ImageOnlyChannelService _imageOnly;
public Administration(ImageOnlyChannelService imageOnly)
{
_imageOnly = imageOnly;
}
public enum List
{
List = 0,
Ls = 0
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.Administrator)]
public async Task ImageOnlyChannel(StoopidTime time = null)
{
var newValue = _imageOnly.ToggleImageOnlyChannel(ctx.Guild.Id, ctx.Channel.Id);
if (newValue)
await ReplyConfirmLocalizedAsync(strs.imageonly_enable);
else
await ReplyPendingLocalizedAsync(strs.imageonly_disable);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]

View File

@@ -16,6 +16,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task AutoAssignRole([Leftover] IRole role)
{
var guser = (IGuildUser) ctx.User;
@@ -47,6 +48,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task AutoAssignRole()
{
if (!_service.TryGetRoles(ctx.Guild.Id, out var roles))

View File

@@ -7,6 +7,7 @@ using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions;
using NadekoBot.Modules.Administration.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -18,12 +19,6 @@ namespace NadekoBot.Modules.Administration
[NoPublicBot]
public class LogCommands : NadekoSubmodule<ILogCommandService>
{
public enum EnableDisable
{
Enable,
Disable
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
@@ -43,14 +38,51 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly]
public async Task LogIgnore()
{
var channel = (ITextChannel)ctx.Channel;
var settings = _service.GetGuildLogSettings(ctx.Guild.Id);
var removed = _service.LogIgnore(ctx.Guild.Id, ctx.Channel.Id);
var chs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.Channel).ToList()
?? new List<IgnoredLogItem>();
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
?? new List<IgnoredLogItem>();
var eb = _eb.Create(ctx)
.WithOkColor()
.AddField(GetText(strs.log_ignored_channels),
chs.Count == 0 ? "-" : string.Join('\n', chs.Select(x => $"{x.LogItemId} | <#{x.LogItemId}>")))
.AddField(GetText(strs.log_ignored_users),
usrs.Count == 0 ? "-" : string.Join('\n', usrs.Select(x => $"{x.LogItemId} | <@{x.LogItemId}>")));
await ctx.Channel.EmbedAsync(eb);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task LogIgnore([Leftover]ITextChannel target)
{
target ??= (ITextChannel)ctx.Channel;
var removed = _service.LogIgnore(ctx.Guild.Id, target.Id, IgnoredItemType.Channel);
if (!removed)
await ReplyConfirmLocalizedAsync(strs.log_ignore(Format.Bold(channel.Mention + "(" + channel.Id + ")"))).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync(strs.log_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")"))).ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync(strs.log_not_ignore(Format.Bold(channel.Mention + "(" + channel.Id + ")"))).ConfigureAwait(false);
await ReplyConfirmLocalizedAsync(strs.log_not_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")"))).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[OwnerOnly]
public async Task LogIgnore([Leftover]IUser target)
{
var removed = _service.LogIgnore(ctx.Guild.Id, target.Id, IgnoredItemType.User);
if (!removed)
await ReplyConfirmLocalizedAsync(strs.log_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")"))).ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync(strs.log_not_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")"))).ConfigureAwait(false);
}
[NadekoCommand, Aliases]

View File

@@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Administration
return;
await _service.TimedMute(user, ctx.User, time.Time, reason: reason).ConfigureAwait(false);
await ReplyErrorLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
await ReplyConfirmLocalizedAsync(strs.user_muted_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
}
catch (Exception ex)
{
@@ -150,7 +150,7 @@ namespace NadekoBot.Modules.Administration
return;
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Chat, reason: reason).ConfigureAwait(false);
await ReplyErrorLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
await ReplyConfirmLocalizedAsync(strs.user_chat_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
}
catch (Exception ex)
{
@@ -209,7 +209,7 @@ namespace NadekoBot.Modules.Administration
return;
await _service.TimedMute(user, ctx.User, time.Time, MuteType.Voice, reason: reason).ConfigureAwait(false);
await ReplyErrorLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
await ReplyConfirmLocalizedAsync(strs.user_voice_mute_time(Format.Bold(user.ToString()), (int)time.Time.TotalMinutes));
}
catch
{

View File

@@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Administration
if (msg is null)
return;
await ReplyErrorLocalizedAsync(strs.reprm(msg));
await ReplyConfirmLocalizedAsync(strs.reprm(msg));
}
}
}

View File

@@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration
return;
}
await ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Alt"));
await ReplyConfirmLocalizedAsync(strs.protection_not_running("Anti-Alt"));
}
[NadekoCommand, Aliases]
@@ -69,11 +69,11 @@ namespace NadekoBot.Modules.Administration
{
if (_service.TryStopAntiRaid(ctx.Guild.Id))
{
return ReplyErrorLocalizedAsync(strs.prot_disable("Anti-Raid"));
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Raid"));
}
else
{
return ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Raid"));
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Raid"));
}
}
@@ -145,11 +145,11 @@ namespace NadekoBot.Modules.Administration
{
if (_service.TryStopAntiSpam(ctx.Guild.Id))
{
return ReplyErrorLocalizedAsync(strs.prot_disable("Anti-Spam"));
return ReplyConfirmLocalizedAsync(strs.prot_disable("Anti-Spam"));
}
else
{
return ReplyErrorLocalizedAsync(strs.protection_not_running("Anti-Spam"));
return ReplyPendingLocalizedAsync(strs.protection_not_running("Anti-Spam"));
}
}

View File

@@ -27,13 +27,13 @@ namespace NadekoBot.Modules.Administration
_services = services;
}
public async Task InternalReactionRoles(bool exclusive, params string[] input)
public async Task InternalReactionRoles(bool exclusive, ulong? messageId, params string[] input)
{
var msgs = await ((SocketTextChannel)ctx.Channel).GetMessagesAsync().FlattenAsync().ConfigureAwait(false);
var prev = (IUserMessage)msgs.FirstOrDefault(x => x is IUserMessage && x.Id != ctx.Message.Id);
if (prev is null)
return;
var target = messageId is ulong msgId
? await ctx.Channel.GetMessageAsync(msgId).ConfigureAwait(false)
: (await ctx.Channel.GetMessagesAsync(2).FlattenAsync().ConfigureAwait(false))
.Skip(1)
.FirstOrDefault();
if (input.Length % 2 != 0)
return;
@@ -69,7 +69,7 @@ namespace NadekoBot.Modules.Administration
{
try
{
await prev.AddReactionAsync(x.emote, new RequestOptions()
await target.AddReactionAsync(x.emote, new RequestOptions()
{
RetryMode = RetryMode.Retry502 | RetryMode.RetryRatelimit
}).ConfigureAwait(false);
@@ -86,8 +86,8 @@ namespace NadekoBot.Modules.Administration
if (_service.Add(ctx.Guild.Id, new ReactionRoleMessage()
{
Exclusive = exclusive,
MessageId = prev.Id,
ChannelId = prev.Channel.Id,
MessageId = target.Id,
ChannelId = target.Channel.Id,
ReactionRoles = all.Select(x =>
{
return new ReactionRole()
@@ -106,6 +106,24 @@ namespace NadekoBot.Modules.Administration
}
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(0)]
public Task ReactionRoles(ulong messageId, params string[] input) =>
InternalReactionRoles(false, messageId, input);
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
[UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)]
[Priority(1)]
public Task ReactionRoles(ulong messageId, Exclude _, params string[] input) =>
InternalReactionRoles(true, messageId, input);
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[NoPublicBot]
@@ -113,7 +131,7 @@ namespace NadekoBot.Modules.Administration
[BotPerm(GuildPerm.ManageRoles)]
[Priority(0)]
public Task ReactionRoles(params string[] input) =>
InternalReactionRoles(false, input);
InternalReactionRoles(false, null, input);
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
@@ -122,7 +140,7 @@ namespace NadekoBot.Modules.Administration
[BotPerm(GuildPerm.ManageRoles)]
[Priority(1)]
public Task ReactionRoles(Exclude _, params string[] input) =>
InternalReactionRoles(true, input);
InternalReactionRoles(true, null, input);
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
@@ -171,7 +189,7 @@ namespace NadekoBot.Modules.Administration
index--;
var rr = rrs[index];
_service.Remove(ctx.Guild.Id, index);
await ReplyErrorLocalizedAsync(strs.reaction_role_removed(index + 1));
await ReplyConfirmLocalizedAsync(strs.reaction_role_removed(index + 1));
}
[NadekoCommand, Aliases]

View File

@@ -24,11 +24,11 @@ namespace NadekoBot.Modules.Administration
if (newVal)
{
await ReplyErrorLocalizedAsync(strs.adsarm_enable(Prefix));
await ReplyConfirmLocalizedAsync(strs.adsarm_enable(Prefix));
}
else
{
await ReplyErrorLocalizedAsync(strs.adsarm_disable(Prefix));
await ReplyConfirmLocalizedAsync(strs.adsarm_disable(Prefix));
}
}

View File

@@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Administration
};
_service.AddNewAutoCommand(cmd);
await ReplyErrorLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
await ReplyConfirmLocalizedAsync(strs.autocmd_add(Format.Code(Format.Sanitize(cmdText)), cmd.Interval));
}
[NadekoCommand, Aliases]
@@ -226,7 +226,7 @@ namespace NadekoBot.Modules.Administration
if (enabled)
await ReplyConfirmLocalizedAsync(strs.fwdm_start).ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync(strs.fwdm_stop).ConfigureAwait(false);
await ReplyPendingLocalizedAsync(strs.fwdm_stop).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
@@ -238,7 +238,7 @@ namespace NadekoBot.Modules.Administration
if (enabled)
await ReplyConfirmLocalizedAsync(strs.fwall_start).ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync(strs.fwall_stop).ConfigureAwait(false);
await ReplyPendingLocalizedAsync(strs.fwall_stop).ConfigureAwait(false);
}
@@ -318,7 +318,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task Die()
public async Task Die(bool graceful = false)
{
try
{
@@ -329,7 +329,7 @@ namespace NadekoBot.Modules.Administration
// ignored
}
await Task.Delay(2000).ConfigureAwait(false);
_coord.Die();
_coord.Die(graceful);
}
[NadekoCommand, Aliases]
@@ -376,7 +376,7 @@ namespace NadekoBot.Modules.Administration
var curUser = await ctx.Guild.GetCurrentUserAsync().ConfigureAwait(false);
await curUser.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
await ReplyErrorLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
await ReplyConfirmLocalizedAsync(strs.bot_nick(Format.Bold(newNick) ?? "-"));
}
[NadekoCommand, Aliases]
@@ -395,7 +395,7 @@ namespace NadekoBot.Modules.Administration
await gu.ModifyAsync(u => u.Nickname = newNick).ConfigureAwait(false);
await ReplyErrorLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
await ReplyConfirmLocalizedAsync(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-"));
}
[NadekoCommand, Aliases]
@@ -496,7 +496,7 @@ namespace NadekoBot.Modules.Administration
public async Task ImagesReload()
{
await _service.ReloadImagesAsync();
await ReplyErrorLocalizedAsync(strs.images_loading);
await ReplyConfirmLocalizedAsync(strs.images_loading);
}
[NadekoCommand, Aliases]
@@ -506,6 +506,14 @@ namespace NadekoBot.Modules.Administration
_strings.Reload();
await ReplyConfirmLocalizedAsync(strs.bot_strings_reloaded).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[OwnerOnly]
public async Task CoordReload()
{
await _coord.Reload();
await ctx.OkAsync();
}
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)
{

View File

@@ -12,6 +12,62 @@ namespace NadekoBot.Modules.Administration
[Group]
public class ServerGreetCommands : NadekoSubmodule<GreetSettingsService>
{
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task Boost()
{
var enabled = await _service.ToggleBoost(ctx.Guild.Id, ctx.Channel.Id);
if (enabled)
await ReplyConfirmLocalizedAsync(strs.boost_on).ConfigureAwait(false);
else
await ReplyPendingLocalizedAsync(strs.boost_off).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task BoostDel(int timer = 30)
{
if (timer < 0 || timer > 600)
return;
await _service.SetBoostDel(ctx.Guild.Id, timer);
if (timer > 0)
await ReplyConfirmLocalizedAsync(strs.boostdel_on(timer));
else
await ReplyPendingLocalizedAsync(strs.boostdel_off).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public Task BoostMsg()
{
var boostMessage = _service.GetBoostMessage(ctx.Guild.Id);
return ReplyConfirmLocalizedAsync(strs.boostmsg_cur(boostMessage?.SanitizeMentions()));
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task BoostMsg([Leftover] string text)
{
if (string.IsNullOrWhiteSpace(text))
{
await BoostMsg().ConfigureAwait(false);
return;
}
var sendBoostEnabled = _service.SetBoostMessage(ctx.Guild.Id, ref text);
await ReplyConfirmLocalizedAsync(strs.boostmsg_new).ConfigureAwait(false);
if (!sendBoostEnabled)
await ReplyPendingLocalizedAsync(strs.boostmsg_enable($"`{Prefix}boost`"));
}
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
@@ -23,9 +79,9 @@ namespace NadekoBot.Modules.Administration
await _service.SetGreetDel(ctx.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0)
await ReplyErrorLocalizedAsync(strs.greetdel_on(timer));
await ReplyConfirmLocalizedAsync(strs.greetdel_on(timer));
else
await ReplyConfirmLocalizedAsync(strs.greetdel_off).ConfigureAwait(false);
await ReplyPendingLocalizedAsync(strs.greetdel_off).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
@@ -38,7 +94,7 @@ namespace NadekoBot.Modules.Administration
if (enabled)
await ReplyConfirmLocalizedAsync(strs.greet_on).ConfigureAwait(false);
else
await ReplyConfirmLocalizedAsync(strs.greet_off).ConfigureAwait(false);
await ReplyPendingLocalizedAsync(strs.greet_off).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
@@ -46,8 +102,8 @@ namespace NadekoBot.Modules.Administration
[UserPerm(GuildPerm.ManageGuild)]
public Task GreetMsg()
{
string greetMsg = _service.GetGreetMsg(ctx.Guild.Id);
return ReplyErrorLocalizedAsync(strs.greetmsg_cur(greetMsg?.SanitizeMentions()));
var greetMsg = _service.GetGreetMsg(ctx.Guild.Id);
return ReplyConfirmLocalizedAsync(strs.greetmsg_cur(greetMsg?.SanitizeMentions()));
}
[NadekoCommand, Aliases]
@@ -65,7 +121,7 @@ namespace NadekoBot.Modules.Administration
await ReplyConfirmLocalizedAsync(strs.greetmsg_new).ConfigureAwait(false);
if (!sendGreetEnabled)
await ReplyErrorLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
await ReplyPendingLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
}
[NadekoCommand, Aliases]
@@ -87,7 +143,7 @@ namespace NadekoBot.Modules.Administration
public Task GreetDmMsg()
{
var dmGreetMsg = _service.GetDmGreetMsg(ctx.Guild.Id);
return ReplyErrorLocalizedAsync(strs.greetdmmsg_cur(dmGreetMsg?.SanitizeMentions()));
return ReplyConfirmLocalizedAsync(strs.greetdmmsg_cur(dmGreetMsg?.SanitizeMentions()));
}
[NadekoCommand, Aliases]
@@ -105,7 +161,7 @@ namespace NadekoBot.Modules.Administration
await ReplyConfirmLocalizedAsync(strs.greetdmmsg_new).ConfigureAwait(false);
if (!sendGreetEnabled)
await ReplyErrorLocalizedAsync(strs.greetdmmsg_enable($"`{Prefix}greetdm`"));
await ReplyPendingLocalizedAsync(strs.greetdmmsg_enable($"`{Prefix}greetdm`"));
}
[NadekoCommand, Aliases]
@@ -127,7 +183,7 @@ namespace NadekoBot.Modules.Administration
public Task ByeMsg()
{
var byeMsg = _service.GetByeMessage(ctx.Guild.Id);
return ReplyErrorLocalizedAsync(strs.byemsg_cur(byeMsg?.SanitizeMentions()));
return ReplyConfirmLocalizedAsync(strs.byemsg_cur(byeMsg?.SanitizeMentions()));
}
[NadekoCommand, Aliases]
@@ -145,7 +201,7 @@ namespace NadekoBot.Modules.Administration
await ReplyConfirmLocalizedAsync(strs.byemsg_new).ConfigureAwait(false);
if (!sendByeEnabled)
await ReplyErrorLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
await ReplyPendingLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
}
[NadekoCommand, Aliases]
@@ -156,9 +212,9 @@ namespace NadekoBot.Modules.Administration
await _service.SetByeDel(ctx.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0)
await ReplyErrorLocalizedAsync(strs.byedel_on(timer));
await ReplyConfirmLocalizedAsync(strs.byedel_on(timer));
else
await ReplyConfirmLocalizedAsync(strs.byedel_off).ConfigureAwait(false);
await ReplyPendingLocalizedAsync(strs.byedel_off).ConfigureAwait(false);
}
@@ -174,7 +230,7 @@ namespace NadekoBot.Modules.Administration
var enabled = _service.GetByeEnabled(ctx.Guild.Id);
if (!enabled)
{
await ReplyErrorLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
await ReplyPendingLocalizedAsync(strs.byemsg_enable($"`{Prefix}bye`"));
}
}
@@ -190,7 +246,7 @@ namespace NadekoBot.Modules.Administration
var enabled = _service.GetGreetEnabled(ctx.Guild.Id);
if (!enabled)
{
await ReplyErrorLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
await ReplyPendingLocalizedAsync(strs.greetmsg_enable($"`{Prefix}greet`"));
}
}
@@ -210,7 +266,7 @@ namespace NadekoBot.Modules.Administration
await ctx.WarningAsync();
var enabled = _service.GetGreetDmEnabled(ctx.Guild.Id);
if (!enabled)
await ReplyErrorLocalizedAsync(strs.greetdmmsg_enable($"`{Prefix}greetdm`"));
await ReplyPendingLocalizedAsync(strs.greetdmmsg_enable($"`{Prefix}greetdm`"));
}
}
}

View File

@@ -1,9 +1,7 @@
using System;
using Discord;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common;
using NadekoBot.Common.Collections;
using NadekoBot.Common.Replacements;
using NadekoBot.Services;
@@ -24,14 +22,11 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DbService _db;
private readonly ILogCommandService _logService;
private readonly IEmbedBuilderService _eb;
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db,
ILogCommandService logService, IEmbedBuilderService eb)
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db, ILogCommandService logService)
{
_db = db;
_logService = logService;
_eb = eb;
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(bot.AllGuildConfigs
.Where(g => g.DeleteMessageOnCommand)

View File

@@ -0,0 +1,187 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Threading.Channels;
using System.Threading.Tasks;
using Discord;
using Discord.Net;
using Discord.WebSocket;
using LinqToDB;
using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Common.Collections;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Services;
using Serilog;
namespace NadekoBot.Modules.Administration.Services
{
public sealed class ImageOnlyChannelService : IEarlyBehavior
{
private readonly IMemoryCache _ticketCache;
private readonly DiscordSocketClient _client;
private readonly DbService _db;
private readonly ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> _enabledOn;
private Channel<IUserMessage> _deleteQueue = Channel.CreateBounded<IUserMessage>(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = false,
});
public ImageOnlyChannelService(IMemoryCache ticketCache, DiscordSocketClient client, DbService db)
{
_ticketCache = ticketCache;
_client = client;
_db = db;
var uow = _db.GetDbContext();
_enabledOn = uow.ImageOnlyChannels
.ToList()
.GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(x => x.ChannelId)))
.ToConcurrent();
_ = Task.Run(DeleteQueueRunner);
_client.ChannelDestroyed += ClientOnChannelDestroyed;
}
private Task ClientOnChannelDestroyed(SocketChannel ch)
{
if (ch is not IGuildChannel gch)
return Task.CompletedTask;
if (_enabledOn.TryGetValue(gch.GuildId, out var channels) && channels.TryRemove(ch.Id))
ToggleImageOnlyChannel(gch.GuildId, ch.Id, true);
return Task.CompletedTask;
}
private async Task DeleteQueueRunner()
{
while (true)
{
var toDelete = await _deleteQueue.Reader.ReadAsync();
try
{
await toDelete.DeleteAsync();
await Task.Delay(1000);
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
{
// disable if bot can't delete messages in the channel
ToggleImageOnlyChannel(((ITextChannel)toDelete.Channel).GuildId, toDelete.Channel.Id, true);
}
}
}
public bool ToggleImageOnlyChannel(ulong guildId, ulong channelId, bool forceDisable = false)
{
var newState = false;
using var uow = _db.GetDbContext();
if (forceDisable
|| (_enabledOn.TryGetValue(guildId, out var channels)
&& channels.TryRemove(channelId)))
{
uow.ImageOnlyChannels.Delete(x => x.ChannelId == channelId);
}
else
{
uow.ImageOnlyChannels.Add(new()
{
GuildId = guildId,
ChannelId = channelId
});
channels = _enabledOn.GetOrAdd(guildId, new ConcurrentHashSet<ulong>());
channels.Add(channelId);
newState = true;
}
uow.SaveChanges();
return newState;
}
public async Task<bool> RunBehavior(IGuild guild, IUserMessage msg)
{
if (msg.Channel is not ITextChannel tch)
return false;
if (msg.Attachments.Any(x => x is { Height: > 0, Width: > 0 }))
return false;
if (!_enabledOn.TryGetValue(tch.GuildId, out var chs)
|| !chs.Contains(msg.Channel.Id))
return false;
var user = await tch.Guild.GetUserAsync(msg.Author.Id)
?? await _client.Rest.GetGuildUserAsync(tch.GuildId, msg.Author.Id);
if (user is null)
return false;
// ignore owner and admin
if (user.Id == tch.Guild.OwnerId || user.GuildPermissions.Administrator)
{
Log.Information("Image-Only: Ignoring owner od admin ({ChannelId})", msg.Channel.Id);
return false;
}
// ignore users higher in hierarchy
var botUser = await tch.Guild.GetCurrentUserAsync();
if (user.GetRoles().Max(x => x.Position) >= botUser.GetRoles().Max(x => x.Position))
return false;
// can't modify channel perms if not admin apparently
if (!botUser.GuildPermissions.ManageGuild)
{
ToggleImageOnlyChannel( tch.GuildId, tch.Id, true);;
return false;
}
var shouldLock = AddUserTicket(tch.GuildId, msg.Author.Id);
if (shouldLock)
{
await tch.AddPermissionOverwriteAsync(msg.Author, new(sendMessages: PermValue.Deny));
Log.Warning("Image-Only: User {User} [{UserId}] has been banned from typing in the channel [{ChannelId}]",
msg.Author,
msg.Author.Id,
msg.Channel.Id);
}
try
{
await _deleteQueue.Writer.WriteAsync(msg);
}
catch (Exception ex)
{
Log.Error(ex, "Error deleting message {MessageId} in image-only channel {ChannelId}.",
msg.Id,
tch.Id);
}
return true;
}
private bool AddUserTicket(ulong guildId, ulong userId)
{
var old = _ticketCache.GetOrCreate($"{guildId}_{userId}", entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1);
return 0;
});
_ticketCache.Set($"{guildId}_{userId}", ++old);
// if this is the third time that the user posts a
// non image in an image-only channel on this server
return old > 2;
}
public int Priority { get; } = 0;
}
}

View File

@@ -6,6 +6,8 @@ using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NadekoBot.Common.Collections;
@@ -21,7 +23,7 @@ namespace NadekoBot.Modules.Administration.Services
{
void AddDeleteIgnore(ulong xId);
Task LogServer(ulong guildId, ulong channelId, bool actionValue);
bool LogIgnore(ulong guildId, ulong channelId);
bool LogIgnore(ulong guildId, ulong itemId, IgnoredItemType itemType);
LogSetting GetGuildLogSettings(ulong guildId);
bool Log(ulong guildId, ulong? channelId, LogType type);
}
@@ -37,7 +39,7 @@ namespace NadekoBot.Modules.Administration.Services
return Task.CompletedTask;
}
public bool LogIgnore(ulong guildId, ulong channelId)
public bool LogIgnore(ulong guildId, ulong itemId, IgnoredItemType itemType)
{
return false;
}
@@ -93,15 +95,15 @@ namespace NadekoBot.Modules.Administration.Services
{
var guildIds = client.Guilds.Select(x => x.Id).ToList();
var configs = uow
.Set<GuildConfig>()
.LogSettings
.AsQueryable()
.Include(gc => gc.LogSetting)
.ThenInclude(ls => ls.IgnoredChannels)
.AsNoTracking()
.Where(x => guildIds.Contains(x.GuildId))
.Include(ls => ls.LogIgnores)
.ToList();
GuildLogSettings = configs
.ToDictionary(g => g.GuildId, g => g.LogSetting)
.ToDictionary(ls => ls.GuildId)
.ToConcurrent();
}
@@ -165,23 +167,23 @@ namespace NadekoBot.Modules.Administration.Services
_ignoreMessageIds.Add(messageId);
}
public bool LogIgnore(ulong gid, ulong cid)
public bool LogIgnore(ulong gid, ulong itemId, IgnoredItemType itemType)
{
int removed = 0;
using (var uow = _db.GetDbContext())
{
var config = uow.LogSettingsFor(gid);
LogSetting logSetting = GuildLogSettings.GetOrAdd(gid, (id) => config.LogSetting);
removed = logSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == cid);
config.LogSetting.IgnoredChannels.RemoveWhere(ilc => ilc.ChannelId == cid);
var logSetting = uow.LogSettingsFor(gid);
removed = logSetting.LogIgnores
.RemoveAll(x => x.ItemType == itemType && itemId == x.LogItemId);
if (removed == 0)
{
var toAdd = new IgnoredLogChannel {ChannelId = cid};
logSetting.IgnoredChannels.Add(toAdd);
config.LogSetting.IgnoredChannels.Add(toAdd);
var toAdd = new IgnoredLogItem { LogItemId = itemId, ItemType = itemType};
logSetting.LogIgnores.Add(toAdd);
}
uow.SaveChanges();
GuildLogSettings.AddOrUpdate(gid, logSetting, (_, _) => logSetting);
}
return removed > 0;
@@ -209,11 +211,10 @@ namespace NadekoBot.Modules.Administration.Services
public async Task LogServer(ulong guildId, ulong channelId, bool value)
{
LogSetting logSetting;
using (var uow = _db.GetDbContext())
{
logSetting = uow.LogSettingsFor(guildId).LogSetting;
GuildLogSettings.AddOrUpdate(guildId, (id) => logSetting, (id, old) => logSetting);
var logSetting = uow.LogSettingsFor(guildId);
logSetting.LogOtherId =
logSetting.MessageUpdatedId =
logSetting.MessageDeletedId =
@@ -230,8 +231,9 @@ namespace NadekoBot.Modules.Administration.Services
logSetting.UserMutedId =
logSetting.LogVoicePresenceTTSId =
(value ? channelId : (ulong?) null);
;
await uow.SaveChangesAsync();
GuildLogSettings.AddOrUpdate(guildId, (id) => logSetting, (id, old) => logSetting);
}
}
@@ -301,7 +303,7 @@ namespace NadekoBot.Modules.Administration.Services
ulong? channelId = null;
using (var uow = _db.GetDbContext())
{
var logSetting = uow.LogSettingsFor(gid).LogSetting;
var logSetting = uow.LogSettingsFor(gid);
GuildLogSettings.AddOrUpdate(gid, (id) => logSetting, (id, old) => logSetting);
switch (type)
{
@@ -582,7 +584,8 @@ namespace NadekoBot.Modules.Administration.Services
{
try
{
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting))
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting)
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == after.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
@@ -684,7 +687,7 @@ namespace NadekoBot.Modules.Administration.Services
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out LogSetting logSetting)
|| (logSetting.ChannelUpdatedId is null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == after.Id))
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == after.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
@@ -735,7 +738,7 @@ namespace NadekoBot.Modules.Administration.Services
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting)
|| (logSetting.ChannelDestroyedId is null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == ch.Id))
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == ch.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
@@ -774,7 +777,7 @@ namespace NadekoBot.Modules.Administration.Services
return;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out LogSetting logSetting)
|| (logSetting.ChannelCreatedId is null))
|| logSetting.ChannelCreatedId is null)
return;
ITextChannel logChannel;
@@ -819,7 +822,8 @@ namespace NadekoBot.Modules.Administration.Services
return;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting)
|| (logSetting.LogVoicePresenceId is null))
|| (logSetting.LogVoicePresenceId is null)
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == iusr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
@@ -864,49 +868,6 @@ namespace NadekoBot.Modules.Administration.Services
return Task.CompletedTask;
}
//private Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
//{
// var _ = Task.Run(async () =>
// {
// try
// {
// var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild;
// if (guild is null)
// return;
// if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting)
// || (logSetting.LogUserPresenceId is null)
// || before.Status == after.Status)
// return;
// ITextChannel logChannel;
// if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) is null)
// return;
// string str = "";
// if (before.Status != after.Status)
// str = "🎭" + Format.Code(PrettyCurrentTime(g)) +
// GetText(logChannel.Guild, strs.user_status_change(,
// "👤" + Format.Bold(usr.Username),
// Format.Bold(after.Status.ToString()));
// //if (before.Game?.Name != after.Game?.Name)
// //{
// // if (str != "")
// // str += "\n";
// // str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**.";
// //}
// PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
// }
// catch
// {
// // ignored
// }
// });
// return Task.CompletedTask;
//}
private Task _client_UserLeft(IGuildUser usr)
{
var _ = Task.Run(async () =>
@@ -914,7 +875,8 @@ namespace NadekoBot.Modules.Administration.Services
try
{
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out LogSetting logSetting)
|| (logSetting.UserLeftId is null))
|| (logSetting.UserLeftId is null)
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
@@ -989,7 +951,8 @@ namespace NadekoBot.Modules.Administration.Services
try
{
if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting)
|| (logSetting.UserUnbannedId is null))
|| (logSetting.UserUnbannedId is null)
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
@@ -1023,7 +986,8 @@ namespace NadekoBot.Modules.Administration.Services
try
{
if (!GuildLogSettings.TryGetValue(guild.Id, out LogSetting logSetting)
|| (logSetting.UserBannedId is null))
|| (logSetting.UserBannedId is null)
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == usr.Id && ilc.ItemType == IgnoredItemType.User))
return;
ITextChannel logChannel;
@@ -1071,7 +1035,7 @@ namespace NadekoBot.Modules.Administration.Services
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting)
|| (logSetting.MessageDeletedId is null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id))
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == channel.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
@@ -1129,7 +1093,7 @@ namespace NadekoBot.Modules.Administration.Services
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out LogSetting logSetting)
|| (logSetting.MessageUpdatedId is null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id))
|| logSetting.LogIgnores.Any(ilc => ilc.LogItemId == channel.Id && ilc.ItemType == IgnoredItemType.Channel))
return;
ITextChannel logChannel;
@@ -1238,7 +1202,7 @@ namespace NadekoBot.Modules.Administration.Services
{
using (var uow = _db.GetDbContext())
{
var newLogSetting = uow.LogSettingsFor(guildId).LogSetting;
var newLogSetting = uow.LogSettingsFor(guildId);
switch (logChannelType)
{
case LogType.Other:

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