372 Commits
v1.0.5 ... main

Author SHA1 Message Date
89385a8c89 Merge branch 'preview' 2025-06-17 17:20:20 +03:00
f68b6cb877 Fixed plugin reloading 2025-06-17 17:20:03 +03:00
e9b61cc4be Removed music plugin 2025-06-15 18:57:20 +03:00
062b0d0133 Added Configuration tests 2025-05-30 20:55:41 +03:00
c3dd68576d Added docker compose to build the project 2025-05-30 18:24:47 +03:00
fafa493db7 Added Unit tests for Logger 2025-05-30 18:16:53 +03:00
15b6d224de Fixed icon style for Online Plugins button 2025-05-30 12:15:48 +03:00
81905460da Updated code formatting 2025-05-30 12:09:48 +03:00
11b9515bab Updated UI elements to use the custom ones 2025-05-27 12:39:36 +03:00
fcd3a59d54 Updated the UI for the bot 2025-05-26 21:17:59 +03:00
7de5d88aea Fixed local plugins page buttons in table 2025-05-26 21:04:05 +03:00
dc40f4ebe4 Updated the UI of the application 2025-05-26 20:25:27 +03:00
8ba9448beb Created Poll Maker plugin 2025-05-22 20:18:50 +03:00
f70e8a565b Removed internal commands functionality 2025-05-22 18:53:33 +03:00
14ca51b18a Fixed Help command loading when using the new context 2025-05-22 18:53:22 +03:00
a0177ce145 Fixed PluginLoading to use the context instead of appDomain 2025-05-22 18:07:25 +03:00
e57e941c94 Fixed size of home page 2025-05-22 13:39:10 +03:00
5bfeb47fc8 Updated style of home page 2025-05-16 23:10:22 +03:00
9fce6dcf9d Ignore offline added plugins for the online description in web table 2025-05-16 22:47:05 +03:00
b605321086 Removed module WebApplication 2025-05-14 16:22:29 +03:00
3a714808e2 Updated comments in Notification system 2025-05-06 17:05:50 +03:00
5061a92412 Updated title for NavMenu 2025-05-06 16:49:21 +03:00
296dbf5309 Updated logger 2025-05-06 16:46:51 +03:00
e6976a5a74 Limited the number of logs kept in memory 2025-05-06 14:06:54 +03:00
3e4709148f Updated logger for real time message sending 2025-05-06 13:57:12 +03:00
3a7bd53cfc Updated plugin version control. Added notification system to web ui 2025-05-06 13:11:14 +03:00
2bd368dcce Added Add Local plugin option 2025-04-30 21:32:36 +03:00
0c33422b5c Removed the old WebUI based on MVC 2025-04-28 22:14:18 +03:00
16b6e42a97 Added Help Command and allowed empty collection instead of null for aliases in IDbCommand 2025-04-28 12:08:24 +03:00
7106a928d6 Created Settings Page and Home Page. Fixed load plugin error when the bot restarts 2025-04-28 12:07:51 +03:00
4ea7b25e4d Removed the TODO comment 2025-04-23 12:22:02 +03:00
2cb868d747 Fixed plugin deletion 2025-04-23 12:21:44 +03:00
f10d72d704 Removed unused button from Home 2025-04-23 11:56:14 +03:00
0493dfaeb4 Fixed web rendering issues 2025-04-23 11:55:46 +03:00
2d319f3d34 Updated web ui to razor 2025-04-22 23:40:00 +03:00
c548c6191d Reworked the plugin loader 2025-04-10 19:13:41 +03:00
f19aafcec6 Removed unused usings from PluginsController and Program in the WebUI 2025-04-08 12:05:40 +03:00
2e6b6b9a61 Added plugin details page 2025-04-07 17:39:48 +03:00
f5d48a398d Fixed double message in the loader when an event starts 2025-04-07 16:34:14 +03:00
c8cf887a09 Added a new project for initialization of the main web ui. 2025-04-07 16:33:58 +03:00
4ab8438a7c Updated plugin installation and plugin loading 2025-04-06 20:47:00 +03:00
87c889266b Improved UI experience for the Main page 2025-04-06 18:36:20 +03:00
8aaefac706 Updated the WebUI by creating new page for viewing the installed plugins 2025-04-05 17:02:22 +03:00
a4afb28f36 Redesigned the DiscordBotCore by splitting it into multiple projects. Created a WebUI and preparing to remove the DiscordBot application 2025-04-04 22:07:30 +03:00
62ba5ec63d Updated repository manager for the new backend 2025-04-01 18:47:13 +03:00
a6ed4078ca Fixed leveling system 2025-01-28 13:58:53 +02:00
20165af15a Changed Sqlite to microsoft sqlite implementation 2025-01-28 13:36:49 +02:00
d26c84480a Fixed text in UI 2025-01-26 21:31:22 +02:00
84b19e2069 Updated API for plugins to work with database from remote. Added PluginRepository and removed Installation Scripts 2025-01-26 20:34:34 +02:00
8b2169dc7b Updated docker and repo check for no internet connection 2025-01-14 12:44:07 +02:00
7ebe3e7014 Updated settings change route 2025-01-13 12:27:35 +02:00
49fe637455 Download plugin have progress status 2024-12-25 15:05:20 +02:00
a754b0e5a9 Updated args to enable API 2024-12-19 00:04:39 +02:00
c79c792c43 Added Socket support 2024-12-18 23:58:13 +02:00
424bf2196f Linked Plugin List and Plugin Install endpoints between web and console 2024-12-15 22:49:45 +02:00
a12aa66660 Updated the plugin webpage 2024-12-14 19:11:50 +02:00
dee4793176 Centered the Settings component 2024-12-14 17:39:56 +02:00
54be74b1cb Updated the web UI to use the API. Reworked the looks of web UI 2024-12-14 17:31:36 +02:00
9102cfaa47 Added download and progress endpoints 2024-11-02 19:14:38 +02:00
f2a9982d41 Added API to DiscordBotCore 2024-11-02 15:43:35 +02:00
bd3f79430b Updated gitignore 2024-10-31 17:11:47 +02:00
44d8b4684e Updated WebUI 2024-10-31 17:02:28 +02:00
9e8bfbbe16 Removed the WebUI. Removed the Modules 2024-10-30 23:10:04 +02:00
f8df0f0254 Updated Home page to restart the application in case of fail 2024-10-23 21:58:40 +03:00
5b1d511f77 Fixed UI crash when trying to open other pages then home while bot is offline 2024-10-23 20:15:43 +03:00
cfcfecd4bc Improved web ui 2024-10-23 20:06:36 +03:00
c2dc01cbbb Added some docs 2024-10-22 22:02:55 +03:00
e229362d38 Updated readme 2024-10-22 21:35:40 +03:00
8a2212e47f Added external repo support 2024-10-22 21:30:26 +03:00
0630d2e291 Update README.md 2024-10-17 14:31:42 +03:00
f108a1fe08 updated docker 2024-10-17 00:22:07 +03:00
81eb966752 Added docker 2024-10-17 00:20:26 +03:00
c61a9d5e51 Updated web ui 2024-10-05 16:41:34 +03:00
49403e70fd Added new module for cpp compatibility 2024-09-19 13:44:05 +03:00
be75ef03cb Updated bootstrap and merged gitlab project 2024-09-17 15:30:08 +03:00
aff40dfd63 Update README.md 2024-08-31 18:06:29 +03:00
a584423939 Update README.md 2024-08-31 18:05:54 +03:00
d51526bf22 Updated nav menu 2024-08-31 17:44:58 +03:00
30e92b742c Removed old web ui plugin 2024-08-31 17:27:19 +03:00
f7ba5f94ff Updated the settings page 2024-08-29 21:51:23 +03:00
34a54cd78f Added pages for module and plugin download 2024-08-29 21:32:14 +03:00
046c9bf98b Set the default theme to dark 2024-08-29 20:09:40 +03:00
a23da51c08 Fixed Startup error after downloading the logger 2024-08-29 20:00:25 +03:00
1c002edc6d First steps to Web UI 2024-08-23 19:51:18 +03:00
0a64de2439 Added support for Custom Activity 2024-08-18 17:57:16 +03:00
c080074292 Updated performance in plugin loading 2024-08-18 14:32:13 +03:00
95e8d95c92 Update README.md
Fixed typo
2024-08-16 19:07:17 +03:00
9c98d2e219 Updated Module engine 2024-08-10 20:27:59 +03:00
18a059af0e More fixes to the new config. Module loader reworked 2024-08-06 22:47:59 +03:00
27e25a9166 Added error message for slash command enable fail 2024-08-06 19:09:16 +03:00
721c28c283 Reworked the Config system 2024-08-06 19:07:08 +03:00
8366de28cc Updated AddPlugin command 2024-07-22 19:42:20 +03:00
8c338820c5 Updated ICommandAction.cs and DBEvent.cs. Removed thread request from DBEvent and added special thread request to ICommandAction.cs 2024-07-22 19:20:17 +03:00
08c5febd66 Fixed redirects 2024-07-22 01:26:14 +03:00
4a08a25167 Added readme to modules and plugins folders 2024-07-22 01:25:15 +03:00
66fbaf3e26 Readme update 2024-07-22 01:21:39 +03:00
894d09f6fa removed libs 2024-07-22 01:18:30 +03:00
8ace51c840 Merged projects with plugins and modules 2024-07-22 01:18:00 +03:00
1fd065f4c2 removed gitmodules 2024-07-22 01:03:53 +03:00
1a4b654036 Removed Modules folder 2024-07-22 01:03:34 +03:00
457b2b7364 Added submodule 2024-07-22 00:56:17 +03:00
3f2c98cb11 Removed logger module from main project 2024-07-22 00:54:52 +03:00
224784031c Checkup for internet connection 2024-07-16 11:11:09 +03:00
13900bb3f3 Added autoinstall for modules 2024-07-14 21:24:49 +03:00
3f8590b8f3 Added Core module support. Things are unstable 2024-07-14 19:33:53 +03:00
6599428043 Added SelfUpdate 2024-07-13 22:33:09 +03:00
349c669284 Removed unused dependency 2024-07-11 16:36:42 +03:00
2052eb634a Updated plugin installation 2024-07-06 18:42:45 +03:00
48a133d58c Renamed to IsEnabled 2024-07-04 16:35:17 +03:00
e0eae076f1 Added IPluginManager 2024-07-04 16:13:25 +03:00
79c6daa11a Removed UI and Added IsDisabled plugin property 2024-07-04 16:12:55 +03:00
d186efcdaf Updated PluginManager 2024-07-01 22:20:31 +03:00
8483439555 updated launch options 2024-07-01 22:14:31 +03:00
9aeb406f6f Updated Discord.NET version. Added wsl run option 2024-07-01 15:25:05 +03:00
16147b6bd7 Updated list plugins to be more informative 2024-07-01 13:52:24 +03:00
9b563cc0f6 Updated Logger. Local plugin database has now only executable dependencies stored in the database 2024-07-01 13:07:34 +03:00
fa7e7988d5 Added Filelogging 2024-06-29 22:32:37 +03:00
68886fa5f0 Updated Logger and Plugin Loading 2024-06-20 18:23:48 +03:00
86b951f50f Fixed crash on AppVersion after the removal of Version key in the EnviromentVariables dictionary. Added new SqlDatabase functions: ReadDataArrayAsync and ReadDataAsync with query + parameters of the query. 2024-06-18 17:54:20 +03:00
1881102fb7 Fixed duplicate actions 2024-06-14 17:09:02 +03:00
e5e156f371 Fixed logging 2024-06-08 20:47:15 +03:00
d9d5c05313 Actions are now loaded together with all plugins. Called the LoadPlugins at startup 2024-06-08 19:17:15 +03:00
9a8ddb5388 Updated Dependency to accept DependencyName as parameter 2024-06-07 21:01:10 +03:00
1a5f0cbede Recursive InternalActionOption list 2024-06-06 16:07:31 +03:00
23961a48b0 Updated plugin command to enable branch switching. Updated Script runner 2024-06-06 15:37:33 +03:00
de7c65c27b Run install scripts 2024-06-06 00:39:44 +03:00
bc20101795 Added script dependency 2024-06-06 00:28:19 +03:00
cdd426b03c Fixed bug where bot crashes if the config is empty 2024-06-03 19:52:39 +03:00
83115d72a4 Added themes 2024-05-27 20:10:52 +03:00
d3dd29f4bf Fixed Install/Remove plugin page in UI 2024-05-26 02:05:06 +03:00
5345515512 Started working on UI for windows users. Fixed bug in actions not being loaded 2024-05-26 01:08:38 +03:00
c87d1a89c5 Moved from record to class 2024-05-22 11:04:14 +03:00
4c8fd1a672 External commands 2024-05-21 21:33:42 +03:00
a5e65a5ea5 Autorestart on token check fail 2024-05-21 21:21:48 +03:00
d6398cd1cc Removed usings 2024-05-21 21:00:59 +03:00
5731165d29 Updated logger 2024-05-21 21:00:31 +03:00
8dbbfbfaef Fixed DiscordBotUI 2024-05-17 12:36:02 +03:00
152e09f4af Cleaned up the Console bot. Added PluginNotFoundException 2024-05-12 21:47:59 +03:00
17147d920d Renamed PluginManager to DiscordBotCore.
Revamped the Logger
2024-05-12 20:10:52 +03:00
413d465d7f wait 5 seconds before running an older version of bot 2024-05-10 15:44:10 +03:00
d5f78c831e Update version to 1.0.4.0 2024-05-10 15:00:47 +03:00
7847c6cc8d Merge with preview. Version 1.0.4 2024-05-10 14:57:00 +03:00
82716a4f4f update builder 2024-05-10 14:55:19 +03:00
9476f9ec31 Added InternalActionOption in ICommandAction interface.
Updated ConsoleUtilities and removed obsolete functions.
2024-05-10 14:39:39 +03:00
dc787ac130 fixed maxParallel downloads default value not being automatically selected at first boot 2024-05-09 22:17:53 +03:00
9525394a6e Updated discord.net library to version 3.14.1. Added method in SettingsDictionary to bulk check if keys are in dictionary. 2024-05-08 14:58:15 +03:00
fc93255503 switched plugin update message to logger instead of Console 2024-04-19 01:01:28 +03:00
cadf500400 Fixed some issues with SettingsDictionary 2024-04-19 00:57:28 +03:00
780614e1e7 gitignore updated 2024-04-19 00:57:09 +03:00
f32920c564 The slash commands can now use interactions 2024-04-17 17:45:35 +03:00
123e8e90a1 Fixed some null errors 2024-04-09 20:40:18 +03:00
0323c888b3 Updated readme 2024-04-01 01:36:45 +03:00
1bb6d3b731 Small refactor 2024-04-01 01:35:20 +03:00
4dc5819c4e The bot now checks for update 2024-04-01 01:33:45 +03:00
5d4fa6fba7 plugin list command now shows if the plugin is already installed. 2024-03-26 23:54:44 +02:00
b6675af9cb Updated readme 2024-03-23 22:53:52 +02:00
23a914d2c9 Fixed loading problem on Discord Bot UI 2024-03-23 22:34:55 +02:00
e8822deeac New UI page 2024-03-07 14:15:50 +02:00
29ecdb6883 Updated UI 2024-03-05 23:16:24 +02:00
90aa5875b5 Added Basic UI Functionality 2024-03-03 18:00:43 +02:00
0fccf706a1 Fixed some errors on SettingsDictionary 2024-03-03 15:07:06 +02:00
fd9cd49844 Deleting plugins is now available 2024-02-28 13:57:12 +02:00
3c3c6a1301 Updated 2024-02-27 22:20:25 +02:00
a2179787b9 Plugin Updater 2024-02-27 19:42:59 +02:00
8c06df9110 Removed help app 2024-02-27 11:09:28 +02:00
ef7a2c0896 Formatted code and rebuilt PluginLoader 2024-02-27 11:07:27 +02:00
14f280baef Moved to Json Database for online plugins 2024-02-26 23:36:19 +02:00
196fb6d3d1 Code formatting and renamed DBCommandExecutingArguments to DbCommandExecutingArguments 2024-02-24 23:22:02 +02:00
cc355d7d4f Fixed some names 2023-12-27 18:24:32 +02:00
af90ae5fba Added UI support for LINUX KDE Plasma 2023-12-27 18:03:26 +02:00
c8480b3c83 Added Channel to DBCommandExecutingArguments.cs 2023-12-17 17:14:48 +02:00
fe32ebc4d7 Fixed bug in linux version for downloading the first plugin only whatever the plugin name was 2023-12-17 16:44:52 +02:00
2280957ea9 Fixed some warnings 2023-11-21 22:07:28 +02:00
944d59d9a3 Reformatting code 2023-11-21 22:04:38 +02:00
79ecff971b Playing with tests 2023-11-20 13:43:43 +02:00
1f0e6516fd New SqlDatabase functions 2023-11-07 10:13:22 +02:00
692f3d8f8c Merge remote-tracking branch 'origin/preview' into preview 2023-10-31 17:36:13 +02:00
6d41d51694 Added config to set the max concurrent downloads 2023-10-31 17:35:58 +02:00
5f23bdadcf Fixed error on ubuntu downloading the wrong plugins 2023-10-30 11:07:59 +02:00
d3555b6fca fixed typo 2023-10-25 10:08:13 +03:00
b5cdc0afeb Merged errors with logs 2023-10-22 13:38:40 +03:00
3858156393 Made Entry Class static 2023-10-22 12:52:52 +03:00
6279c5c3a9 Updated Logger and Created Command to change settings variables 2023-10-01 14:11:34 +03:00
f58a57c6cd Improved logging. 2023-09-26 21:46:54 +03:00
d00ebfd7ed Fixed new name in README 2023-09-25 22:14:53 +03:00
ab279bd284 updated README.md 2023-09-25 22:12:44 +03:00
89c4932cd7 Fixed plugin refresh command and added new method for executing tasks without return type in ConsoleUtilities 2023-09-25 22:06:42 +03:00
c577f625c2 New method to execute using a progress bar feedback on process 2023-09-18 23:54:04 +03:00
58624f4037 Improved download speed and started using Spectre.Console package 2023-09-18 23:13:44 +03:00
c9249dc71b Plugin install does not display all logs when reloading plugin list 2023-09-07 14:55:09 +03:00
5e4f1ca35f The plugin install command now automatically refreshes the installed plugins. 2023-09-07 14:43:10 +03:00
0d8fdb5904 Updated log manager 2023-09-07 13:28:49 +03:00
92a18e3495 Removed URLs file from bot config 2023-09-07 12:50:36 +03:00
e929646e8e removed error when invalid plugin. It was called even when a typo was made 2023-08-15 16:42:13 +03:00
6315d13d18 Fixed Internal Actions to refresh after external actions are loaded 2023-08-15 16:17:32 +03:00
ee527bb36f a 2023-08-08 22:21:36 +03:00
86514d1770 Fixed version 2023-08-08 22:20:16 +03:00
9e8ed1e911 Fixed version 2023-08-08 22:19:29 +03:00
f3c3c7939c Fixed some outputs in plugin manager 2023-08-06 17:37:02 +03:00
361ed37362 Reimplemented error handling for SettingsFile 2023-08-06 17:14:57 +03:00
ed3128b940 Created a batch file to build on windows 2023-08-06 14:25:18 +03:00
6bbd68a135 Fixed release Config not found bug 2023-08-05 21:54:05 +03:00
06d322b5b3 Removed temp file creation 2023-08-05 21:53:08 +03:00
5497ee9119 - 2023-08-05 21:33:16 +03:00
0aa78e3560 Changed README.md 2023-08-05 21:32:49 +03:00
41ad37b3bb Fixed a bug when loading empty json file 2023-08-05 21:25:47 +03:00
0104d09509 Delete .github/workflows directory 2023-08-01 21:33:45 +03:00
b4f5e40f12 Moved items and reimplemented SettingsDictionary.cs 2023-08-01 21:28:44 +03:00
7107b17f19 Update README.md 2023-07-31 22:15:24 +03:00
42e1fd917e Removed some logs from console about json files that are opened 2023-07-31 22:10:26 +03:00
9e6fcdbe6f removed submodules 2023-07-31 21:47:43 +03:00
a24d8aa222 Removed dependencies 2023-07-31 21:11:17 +03:00
0e5c9ff14b Fixed help message on /help command 2023-07-31 20:55:38 +03:00
f8977d8840 Update submodules 2023-07-30 23:09:34 +03:00
bb9768f3a1 patch 2023-07-30 22:30:53 +03:00
b3d6930142 Updated functions in Discord Bot 2023-07-30 22:26:43 +03:00
5254be44be Added plugins folder to solution 2023-07-30 18:36:41 +03:00
207e0d6abd Added plugins submodule 2023-07-30 18:32:47 +03:00
968d23380f message 2023-07-30 18:31:06 +03:00
fff9e2e897 - 2023-07-30 18:27:05 +03:00
3e4e777d8d updated project file 2023-07-30 18:22:20 +03:00
7f906d400f - 2023-07-20 20:38:52 +03:00
c1161a3bca Bot no longer exits when a plugin fails to load 2023-07-20 11:32:15 +03:00
ac512e3a27 Updated display when installing a new plugin 2023-07-16 00:18:27 +03:00
730b628fe3 New method in archive manager and shortcut on exit command 2023-07-15 23:23:06 +03:00
701edc5c6a Update README.md 2023-07-10 21:29:36 +03:00
e7688762b8 Moved to API v11 due to the discriminator removal from discord 2023-07-05 21:01:51 +03:00
a7a71bf49a The logger now supports colors 2023-07-05 19:30:38 +03:00
ac7212ca00 Merge remote-tracking branch 'origin/preview' into preview 2023-07-03 14:40:08 +03:00
298e557260 Fixed some text and added some missing texts to commands. Added new command to clear screen and formated code. 2023-07-03 14:39:50 +03:00
7ba791f906 Create dotnet.yml 2023-07-01 17:37:59 +03:00
4a6a12baae Moved to API v3.10.0 2023-06-26 14:55:54 +03:00
f1dda5da3c Added new functions into Functions.cs and added help command for slash Commands. 2023-06-26 14:51:15 +03:00
3ab96e2d0d Fixed some warnings 2023-06-25 21:35:53 +03:00
970c519a32 Fixed loading plugins at startup 2023-06-15 22:12:15 +03:00
188920ec7f Cleaned up after removal of old commands 2023-06-15 21:59:48 +03:00
dcfc4ea32f Removed ConsoleCommandsHandler_OLD.cs 2023-06-15 21:57:22 +03:00
a8c02176d3 Fixed bug in PluginManager at getting info about plugin & new console commands 2023-06-15 21:55:42 +03:00
1665d47a25 Fixed a bug with invalid token bot startup 2023-06-15 14:46:28 +03:00
0b2f1e6ab6 Reimplemented Command Actions. 2023-06-07 20:36:11 +03:00
bcd9245502 update to display the changelogs for application when an update was found 2023-05-28 19:17:06 +03:00
59da9b295b 2023-05-28 19:14:39 +03:00
99d7d5e7e7 self update removed 2023-05-28 19:08:23 +03:00
e4c60f1606 builder added 2023-05-28 17:54:14 +03:00
77f1bef862 Changed from TextType to LogLevel 2023-05-28 17:37:19 +03:00
f16c139362 The bot is running on the main Thread now 2023-05-28 17:18:55 +03:00
c94cdca6eb New plugin downloader based on threads 2023-05-27 16:28:01 +03:00
dcdf80112d Merge branch 'preview' of https://github.com/Wizzy69/SethDiscordBot into preview 2023-05-23 17:00:14 +03:00
eb836c5b74 2023-05-23 17:00:10 +03:00
de680c6771 removed the economy plugin from the sln 2023-05-21 10:28:08 +03:00
Wizzy69
bcef58a46b Removed plugins from the project and reworked the Plugin Manager 2023-04-29 19:35:19 +03:00
Wizzy69
0dc8cdbce5 2023-04-29 18:57:33 +03:00
Wizzy69
dbdbaa9802 2023-04-29 18:57:26 +03:00
Tudor Andrei
5edcf93371 cleaning up PluginManager (phase 1) 2023-04-25 14:27:55 +03:00
Andrei Tudor
b0be76c62b Created new logger 2023-04-20 19:52:55 +03:00
75a77389a8 2023-04-13 19:58:16 +03:00
0bbced3d58 patch 2023-04-09 20:47:21 +03:00
244209093e patch on discord bot UI 2023-04-08 15:54:39 +03:00
54a68d635d 2023-04-08 13:48:53 +03:00
d7a5cb5a64 updated .vscode 2023-04-08 13:45:41 +03:00
6124f89cb0 updated to start the webpage at startup 2023-04-08 13:33:16 +03:00
810a527cc1 Discord Bot web UI first preview 2023-04-08 13:18:25 +03:00
0a2dff0c6d 2023-04-07 10:02:40 +03:00
382c376c03 2023-04-07 10:01:10 +03:00
84b7d663bc fixed README 2023-04-07 09:59:41 +03:00
623232b67e added documentation to the sql database class 2023-04-07 09:42:36 +03:00
d5df6cfb9d update 2023-04-05 20:10:43 +03:00
10b9548c29 Delete test directory 2023-04-01 16:16:02 +03:00
fa1a136ef1 2023-04-01 16:15:49 +03:00
d20cb62139 Updated initial setup 2023-04-01 16:14:04 +03:00
f2418d0395 fixed start error when no config file exists or is null 2023-03-25 11:51:48 +02:00
460a85944a changed to .json file instead of database for settings 2023-03-24 21:52:03 +02:00
7e2fa02d07 2023-03-11 00:07:11 +02:00
873855937f 2023-02-24 16:46:59 +02:00
1cdd2644df 2023-02-24 11:37:21 +02:00
532540b74f 2023-02-24 11:36:43 +02:00
9ba4ca43e2 Update 2023-02-24 11:12:23 +02:00
8bcaf3f254 2023-02-14 11:33:03 +02:00
0d5c90323a 2023-02-12 12:25:53 +02:00
5b01b15216 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2023-01-31 16:08:02 +02:00
4f18f505f4 Updated to allow mention as command prefix. Updated DBCommand 2023-01-31 16:07:53 +02:00
2d3566a01a 2023-01-12 15:21:45 +02:00
22f2cd4e59 linux updater 2023-01-10 19:42:10 +02:00
1683234376 updater for linux is now working 2023-01-10 19:41:03 +02:00
69d99b4189 Updated Logger and message handler. Updated to latest Discord.Net version 2023-01-01 21:55:29 +02:00
4a5e0ef2f3 2022-12-17 12:38:21 +02:00
79731a9704 2022-12-17 12:35:26 +02:00
bd53d099d1 2022-12-09 14:49:05 +02:00
de61f5de88 2022-12-09 14:49:03 +02:00
0527d43dd2 patch 2022-12-09 14:46:10 +02:00
e3511cd96b 2022-11-18 10:45:43 +02:00
d355d3c9b7 2022-11-18 10:38:47 +02:00
5bb13aa4a6 The library can now be used for Windows exclusive bots (Made with WinForm or Wpf) 2022-11-13 16:28:44 +02:00
655f5e2ce0 2022-11-12 12:20:02 +02:00
9014d78a7d 2022-11-04 14:08:35 +02:00
1c026e7f49 patch 2022-11-02 19:42:58 +02:00
d32b3902c9 2022-11-02 18:59:33 +02:00
e5f3aff39a patch (database & slash commands) 2022-11-02 15:35:18 +02:00
11ec02ef68 Changed Config System 2022-10-25 21:37:52 +03:00
cad19935d5 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-10-23 20:11:47 +03:00
47f88f167f patch 2022-10-23 20:11:27 +03:00
9d6c335799 2022-10-23 11:08:46 +03:00
cbaf552e7a 2022-10-22 16:02:05 +03:00
a4975a4578 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-10-22 14:45:35 +03:00
725d02d152 2022-10-22 14:44:55 +03:00
ae7118e89a Update to Discord.Net 3.8.1 (API v10) 2022-10-22 13:55:48 +03:00
cad3bb5b75 2022-10-14 12:44:44 +03:00
269d7d56ff 2022-10-14 12:44:21 +03:00
403c023191 2022-10-14 11:13:09 +03:00
3f7a8e04d4 2022-10-14 11:06:56 +03:00
0abbd24b86 Code cleanup 2022-10-12 20:29:00 +03:00
21f1975fbc 2022-10-09 20:59:16 +03:00
d6f072904e patch 2022-10-09 20:26:39 +03:00
6fc491a0d6 2022-10-07 13:36:15 +03:00
7bc9db03f0 patch 2022-10-03 21:46:34 +03:00
641f0f2856 patch 2022-10-03 21:28:52 +03:00
ef5439d204 2022-10-01 20:59:46 +03:00
c2093c2aca added support for GUI 2022-10-01 20:59:08 +03:00
a39f7bb0c9 patch 2022-10-01 17:14:19 +03:00
6d73ec7f24 Linux compatibility update 2022-09-26 19:30:53 +03:00
24d4bee85b Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-09-25 23:03:48 +03:00
a1bde013e6 2022-09-25 23:03:18 +03:00
23f951167b 2022-09-24 15:00:02 +03:00
26505fd5c9 2022-09-22 10:39:51 +03:00
28fff8db8a 2022-09-16 17:47:01 +03:00
fa15bc2333 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-09-16 17:37:08 +03:00
113c64279f 2022-09-16 17:37:01 +03:00
81cc6709d6 patch 2022-09-10 16:47:00 +03:00
571c0a5360 patch 2022-09-10 16:46:48 +03:00
Wizzy69
eefdea2de9 Delete .idea/.idea.SethDiscordBot/.idea directory 2022-09-09 17:18:28 +03:00
f7e6b0a398 DBCommand interface update & all other plugins were updated 2022-09-03 16:28:03 +03:00
68a83b052a Updater added 2022-08-28 12:59:41 +03:00
d689eee7fa 2022-08-26 10:42:15 +03:00
f6442af30c patch: listplugs, plugin version system and progress bar 2022-08-25 15:15:47 +03:00
b98f57fcf8 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-08-24 21:34:13 +03:00
77aad985fa Added a new ProgressBar type 2022-08-24 21:34:06 +03:00
Wizzy69
f8ebf76f92 Delete .idea/.idea.SethDiscordBot/.idea directory 2022-08-20 20:52:21 +03:00
6da9828e5c improved save method 2022-08-20 17:44:19 +03:00
3ba45790e7 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-08-17 20:31:55 +03:00
e440e97948 2022-08-17 20:31:52 +03:00
Wizzy69
938c73c810 Delete DiscordBotConsoleLauncher directory 2022-08-17 20:31:28 +03:00
a8520c8c96 fixed some bugs 2022-08-17 20:23:06 +03:00
debdc58646 The ability to remove a plugin has been added 2022-08-17 20:12:31 +03:00
8b36c086ef New Command added & Leveling System update 2022-08-17 14:19:46 +03:00
1f5e5d0611 patch 2022-08-17 10:09:02 +03:00
bbc1c601c9 Patching system build 2 2022-08-17 09:58:54 +03:00
5ab3195956 2022-08-16 23:08:56 +03:00
1f1983480a 2022-08-16 16:14:25 +03:00
2fcd86cf12 Merge branch 'preview' of https://github.com/Wizzy69/DiscordBotWithAPI into preview 2022-08-16 16:12:38 +03:00
2c83d00c00 2022-08-16 16:12:14 +03:00
Wizzy69
ccac0ca6d0 Update README.md 2022-07-26 15:15:13 +03:00
4c9c7410f0 2022-07-17 19:31:59 +03:00
eef13db3aa 2022-07-17 19:30:39 +03:00
27bbe899ab 2022-07-17 19:29:58 +03:00
79002de9a5 2022-07-17 19:28:22 +03:00
17f68a8e5e Moved Console activity on another thread 2022-07-17 18:53:56 +03:00
c415fa1c0c Updated display features 2022-07-17 14:21:16 +03:00
b8ec6f42df Fixed some bugs with Command handler and configuration 2022-07-13 19:48:57 +03:00
3f67d7f3f9 updated music commands 2022-07-10 21:57:22 +03:00
2dae8a3a63 2022-07-10 15:07:15 +03:00
47aae730c7 Music Commands 2022-07-09 14:57:49 +03:00
aa808e950a 2022-07-09 10:41:39 +03:00
7dd43b7841 2022-07-09 10:41:33 +03:00
82a3744d48 2022-07-09 10:26:36 +03:00
3839e4d838 patch 2022-07-09 09:48:58 +03:00
a66ebc43d9 updated music 2022-07-09 09:48:45 +03:00
45bbda8185 2022-07-07 13:56:21 +03:00
2db6bf2729 2022-07-07 13:55:32 +03:00
3097eb7fca 2022-07-06 14:43:10 +03:00
efb6ac5192 2022-07-06 14:32:16 +03:00
27fe615447 2022-07-06 14:00:41 +03:00
c40fcdac9d 2022-07-06 13:59:59 +03:00
194 changed files with 7539 additions and 5458 deletions

25
.dockerignore Normal file
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

102
.gitignore vendored
View File

@@ -29,10 +29,8 @@ x86/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
[Dd]ata/
# Visual Studio 2015/2017 cache/options directory
.vs/
@@ -64,6 +62,9 @@ project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
@@ -362,3 +363,100 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/DiscordBotWebUI/Data
/DiscordBot/Data
/WebUI/Data
/WebUI_Old/Data
/WebUI/bin
/WebUI_Old/bin
Data/

View File

@@ -1,4 +0,0 @@
{
"rpc.enabled": true,
"discord.enabled": true
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v5.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v5.0": {
"CMD_LevelingSystem/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"CMD_LevelingSystem.dll": {}
}
},
"Discord.Net/3.5.0": {
"dependencies": {
"Discord.Net.Commands": "3.5.0",
"Discord.Net.Core": "3.5.0",
"Discord.Net.Interactions": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Discord.Net.Webhook": "3.5.0"
}
},
"Discord.Net.Commands/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Core/3.5.0": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Interactions/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Rest/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Webhook/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.WebSocket/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.5.0"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"CMD_LevelingSystem/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IUtexpvogudb1rllKBWkIEpBVQoToMjtVo81KPkt+gNMe7KtRDcZJgcn6+72viMtyw0e95OJPXFV5VEA/n2OQQ==",
"path": "discord.net/3.5.0",
"hashPath": "discord.net.3.5.0.nupkg.sha512"
},
"Discord.Net.Commands/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ClTv8aiTlitvS48YatRiTLvgE2f2uKgmHNPVBIuvJBHZO2u4bZCzoN1fid+pZn2sbVOkt8uftlLGzz5DSZlFIA==",
"path": "discord.net.commands/3.5.0",
"hashPath": "discord.net.commands.3.5.0.nupkg.sha512"
},
"Discord.Net.Core/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rCzzaznMVQ+bLMxOpYwTyqm9V22kMy6BxlQisSxemHZDe2Jedz3Clp/a0dToACLz+Dlp3u+jYUfCBnTz7L6f4g==",
"path": "discord.net.core/3.5.0",
"hashPath": "discord.net.core.3.5.0.nupkg.sha512"
},
"Discord.Net.Interactions/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wE9+V9DJ7r+1s4euOi4sGPIAt4sD7r+Tk5s9mrlbLCHVQTK4KllAvcrL25bPFI38FuFceREEzFoRlTrekSyB2Q==",
"path": "discord.net.interactions/3.5.0",
"hashPath": "discord.net.interactions.3.5.0.nupkg.sha512"
},
"Discord.Net.Rest/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BnTdLFuuQsKvCv08VQrD4X1Hw2Xp+MELIRQiDiKfG01IiQlRTN+1gc3LB1zXgn5xBvC0HXjHxwV22GrMD9uKHQ==",
"path": "discord.net.rest/3.5.0",
"hashPath": "discord.net.rest.3.5.0.nupkg.sha512"
},
"Discord.Net.Webhook/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vCIGZS+m88sQDuFmdbUqg+2RIXS/NJWx8ei3MX+ZEYiAvOkDgQfkIlEnU1NKpds6ivTt5GFlv6UzcWubb5VJ1w==",
"path": "discord.net.webhook/3.5.0",
"hashPath": "discord.net.webhook.3.5.0.nupkg.sha512"
},
"Discord.Net.WebSocket/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LjBOvcP40vJ+dhOtBDi8haEeblPAKpAIqR04NBzTM1/0RVavJZH89ovfSQIk42ygkiOaDV4E2x0Mmh6DRoIYcw==",
"path": "discord.net.websocket/3.5.0",
"hashPath": "discord.net.websocket.3.5.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v5.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v5.0": {
"CMD_Utils/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"CMD_Utils.dll": {}
}
},
"Discord.Net/3.5.0": {
"dependencies": {
"Discord.Net.Commands": "3.5.0",
"Discord.Net.Core": "3.5.0",
"Discord.Net.Interactions": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Discord.Net.Webhook": "3.5.0"
}
},
"Discord.Net.Commands/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Core/3.5.0": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Interactions/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Rest/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Webhook/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.WebSocket/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.5.0"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"CMD_Utils/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IUtexpvogudb1rllKBWkIEpBVQoToMjtVo81KPkt+gNMe7KtRDcZJgcn6+72viMtyw0e95OJPXFV5VEA/n2OQQ==",
"path": "discord.net/3.5.0",
"hashPath": "discord.net.3.5.0.nupkg.sha512"
},
"Discord.Net.Commands/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ClTv8aiTlitvS48YatRiTLvgE2f2uKgmHNPVBIuvJBHZO2u4bZCzoN1fid+pZn2sbVOkt8uftlLGzz5DSZlFIA==",
"path": "discord.net.commands/3.5.0",
"hashPath": "discord.net.commands.3.5.0.nupkg.sha512"
},
"Discord.Net.Core/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rCzzaznMVQ+bLMxOpYwTyqm9V22kMy6BxlQisSxemHZDe2Jedz3Clp/a0dToACLz+Dlp3u+jYUfCBnTz7L6f4g==",
"path": "discord.net.core/3.5.0",
"hashPath": "discord.net.core.3.5.0.nupkg.sha512"
},
"Discord.Net.Interactions/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wE9+V9DJ7r+1s4euOi4sGPIAt4sD7r+Tk5s9mrlbLCHVQTK4KllAvcrL25bPFI38FuFceREEzFoRlTrekSyB2Q==",
"path": "discord.net.interactions/3.5.0",
"hashPath": "discord.net.interactions.3.5.0.nupkg.sha512"
},
"Discord.Net.Rest/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BnTdLFuuQsKvCv08VQrD4X1Hw2Xp+MELIRQiDiKfG01IiQlRTN+1gc3LB1zXgn5xBvC0HXjHxwV22GrMD9uKHQ==",
"path": "discord.net.rest/3.5.0",
"hashPath": "discord.net.rest.3.5.0.nupkg.sha512"
},
"Discord.Net.Webhook/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vCIGZS+m88sQDuFmdbUqg+2RIXS/NJWx8ei3MX+ZEYiAvOkDgQfkIlEnU1NKpds6ivTt5GFlv6UzcWubb5VJ1w==",
"path": "discord.net.webhook/3.5.0",
"hashPath": "discord.net.webhook.3.5.0.nupkg.sha512"
},
"Discord.Net.WebSocket/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LjBOvcP40vJ+dhOtBDi8haEeblPAKpAIqR04NBzTM1/0RVavJZH89ovfSQIk42ygkiOaDV4E2x0Mmh6DRoIYcw==",
"path": "discord.net.websocket/3.5.0",
"hashPath": "discord.net.websocket.3.5.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v5.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v5.0": {
"EVE_LevelingSystem/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"EVE_LevelingSystem.dll": {}
}
},
"Discord.Net/3.5.0": {
"dependencies": {
"Discord.Net.Commands": "3.5.0",
"Discord.Net.Core": "3.5.0",
"Discord.Net.Interactions": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Discord.Net.Webhook": "3.5.0"
}
},
"Discord.Net.Commands/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Core/3.5.0": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Interactions/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Rest/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Webhook/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.WebSocket/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.5.0"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"EVE_LevelingSystem/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IUtexpvogudb1rllKBWkIEpBVQoToMjtVo81KPkt+gNMe7KtRDcZJgcn6+72viMtyw0e95OJPXFV5VEA/n2OQQ==",
"path": "discord.net/3.5.0",
"hashPath": "discord.net.3.5.0.nupkg.sha512"
},
"Discord.Net.Commands/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ClTv8aiTlitvS48YatRiTLvgE2f2uKgmHNPVBIuvJBHZO2u4bZCzoN1fid+pZn2sbVOkt8uftlLGzz5DSZlFIA==",
"path": "discord.net.commands/3.5.0",
"hashPath": "discord.net.commands.3.5.0.nupkg.sha512"
},
"Discord.Net.Core/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rCzzaznMVQ+bLMxOpYwTyqm9V22kMy6BxlQisSxemHZDe2Jedz3Clp/a0dToACLz+Dlp3u+jYUfCBnTz7L6f4g==",
"path": "discord.net.core/3.5.0",
"hashPath": "discord.net.core.3.5.0.nupkg.sha512"
},
"Discord.Net.Interactions/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wE9+V9DJ7r+1s4euOi4sGPIAt4sD7r+Tk5s9mrlbLCHVQTK4KllAvcrL25bPFI38FuFceREEzFoRlTrekSyB2Q==",
"path": "discord.net.interactions/3.5.0",
"hashPath": "discord.net.interactions.3.5.0.nupkg.sha512"
},
"Discord.Net.Rest/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BnTdLFuuQsKvCv08VQrD4X1Hw2Xp+MELIRQiDiKfG01IiQlRTN+1gc3LB1zXgn5xBvC0HXjHxwV22GrMD9uKHQ==",
"path": "discord.net.rest/3.5.0",
"hashPath": "discord.net.rest.3.5.0.nupkg.sha512"
},
"Discord.Net.Webhook/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vCIGZS+m88sQDuFmdbUqg+2RIXS/NJWx8ei3MX+ZEYiAvOkDgQfkIlEnU1NKpds6ivTt5GFlv6UzcWubb5VJ1w==",
"path": "discord.net.webhook/3.5.0",
"hashPath": "discord.net.webhook.3.5.0.nupkg.sha512"
},
"Discord.Net.WebSocket/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LjBOvcP40vJ+dhOtBDi8haEeblPAKpAIqR04NBzTM1/0RVavJZH89ovfSQIk42ygkiOaDV4E2x0Mmh6DRoIYcw==",
"path": "discord.net.websocket/3.5.0",
"hashPath": "discord.net.websocket.3.5.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v5.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v5.0": {
"MusicCommands/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"MusicCommands.dll": {}
}
},
"Discord.Net/3.5.0": {
"dependencies": {
"Discord.Net.Commands": "3.5.0",
"Discord.Net.Core": "3.5.0",
"Discord.Net.Interactions": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Discord.Net.Webhook": "3.5.0"
}
},
"Discord.Net.Commands/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Core/3.5.0": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Interactions/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Rest/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Webhook/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.WebSocket/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.5.0"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"MusicCommands/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IUtexpvogudb1rllKBWkIEpBVQoToMjtVo81KPkt+gNMe7KtRDcZJgcn6+72viMtyw0e95OJPXFV5VEA/n2OQQ==",
"path": "discord.net/3.5.0",
"hashPath": "discord.net.3.5.0.nupkg.sha512"
},
"Discord.Net.Commands/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ClTv8aiTlitvS48YatRiTLvgE2f2uKgmHNPVBIuvJBHZO2u4bZCzoN1fid+pZn2sbVOkt8uftlLGzz5DSZlFIA==",
"path": "discord.net.commands/3.5.0",
"hashPath": "discord.net.commands.3.5.0.nupkg.sha512"
},
"Discord.Net.Core/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rCzzaznMVQ+bLMxOpYwTyqm9V22kMy6BxlQisSxemHZDe2Jedz3Clp/a0dToACLz+Dlp3u+jYUfCBnTz7L6f4g==",
"path": "discord.net.core/3.5.0",
"hashPath": "discord.net.core.3.5.0.nupkg.sha512"
},
"Discord.Net.Interactions/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wE9+V9DJ7r+1s4euOi4sGPIAt4sD7r+Tk5s9mrlbLCHVQTK4KllAvcrL25bPFI38FuFceREEzFoRlTrekSyB2Q==",
"path": "discord.net.interactions/3.5.0",
"hashPath": "discord.net.interactions.3.5.0.nupkg.sha512"
},
"Discord.Net.Rest/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BnTdLFuuQsKvCv08VQrD4X1Hw2Xp+MELIRQiDiKfG01IiQlRTN+1gc3LB1zXgn5xBvC0HXjHxwV22GrMD9uKHQ==",
"path": "discord.net.rest/3.5.0",
"hashPath": "discord.net.rest.3.5.0.nupkg.sha512"
},
"Discord.Net.Webhook/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vCIGZS+m88sQDuFmdbUqg+2RIXS/NJWx8ei3MX+ZEYiAvOkDgQfkIlEnU1NKpds6ivTt5GFlv6UzcWubb5VJ1w==",
"path": "discord.net.webhook/3.5.0",
"hashPath": "discord.net.webhook.3.5.0.nupkg.sha512"
},
"Discord.Net.WebSocket/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LjBOvcP40vJ+dhOtBDi8haEeblPAKpAIqR04NBzTM1/0RVavJZH89ovfSQIk42ygkiOaDV4E2x0Mmh6DRoIYcw==",
"path": "discord.net.websocket/3.5.0",
"hashPath": "discord.net.websocket.3.5.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v5.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v5.0": {
"StartupEvents/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"StartupEvents.dll": {}
}
},
"Discord.Net/3.5.0": {
"dependencies": {
"Discord.Net.Commands": "3.5.0",
"Discord.Net.Core": "3.5.0",
"Discord.Net.Interactions": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Discord.Net.Webhook": "3.5.0"
}
},
"Discord.Net.Commands/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Core/3.5.0": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Interactions/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0",
"Discord.Net.WebSocket": "3.5.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Rest/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.Webhook/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Discord.Net.WebSocket/3.5.0": {
"dependencies": {
"Discord.Net.Core": "3.5.0",
"Discord.Net.Rest": "3.5.0"
},
"runtime": {
"lib/net5.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.5.0.0",
"fileVersion": "3.5.0.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.5.0"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"StartupEvents/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IUtexpvogudb1rllKBWkIEpBVQoToMjtVo81KPkt+gNMe7KtRDcZJgcn6+72viMtyw0e95OJPXFV5VEA/n2OQQ==",
"path": "discord.net/3.5.0",
"hashPath": "discord.net.3.5.0.nupkg.sha512"
},
"Discord.Net.Commands/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ClTv8aiTlitvS48YatRiTLvgE2f2uKgmHNPVBIuvJBHZO2u4bZCzoN1fid+pZn2sbVOkt8uftlLGzz5DSZlFIA==",
"path": "discord.net.commands/3.5.0",
"hashPath": "discord.net.commands.3.5.0.nupkg.sha512"
},
"Discord.Net.Core/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rCzzaznMVQ+bLMxOpYwTyqm9V22kMy6BxlQisSxemHZDe2Jedz3Clp/a0dToACLz+Dlp3u+jYUfCBnTz7L6f4g==",
"path": "discord.net.core/3.5.0",
"hashPath": "discord.net.core.3.5.0.nupkg.sha512"
},
"Discord.Net.Interactions/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wE9+V9DJ7r+1s4euOi4sGPIAt4sD7r+Tk5s9mrlbLCHVQTK4KllAvcrL25bPFI38FuFceREEzFoRlTrekSyB2Q==",
"path": "discord.net.interactions/3.5.0",
"hashPath": "discord.net.interactions.3.5.0.nupkg.sha512"
},
"Discord.Net.Rest/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BnTdLFuuQsKvCv08VQrD4X1Hw2Xp+MELIRQiDiKfG01IiQlRTN+1gc3LB1zXgn5xBvC0HXjHxwV22GrMD9uKHQ==",
"path": "discord.net.rest/3.5.0",
"hashPath": "discord.net.rest.3.5.0.nupkg.sha512"
},
"Discord.Net.Webhook/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-vCIGZS+m88sQDuFmdbUqg+2RIXS/NJWx8ei3MX+ZEYiAvOkDgQfkIlEnU1NKpds6ivTt5GFlv6UzcWubb5VJ1w==",
"path": "discord.net.webhook/3.5.0",
"hashPath": "discord.net.webhook.3.5.0.nupkg.sha512"
},
"Discord.Net.WebSocket/3.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LjBOvcP40vJ+dhOtBDi8haEeblPAKpAIqR04NBzTM1/0RVavJZH89ovfSQIk42ygkiOaDV4E2x0Mmh6DRoIYcw==",
"path": "discord.net.websocket/3.5.0",
"hashPath": "discord.net.websocket.3.5.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"CMD_Utils/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"CMD_Utils.dll": {}
}
},
"Discord.Net/3.7.2": {
"dependencies": {
"Discord.Net.Commands": "3.7.2",
"Discord.Net.Core": "3.7.2",
"Discord.Net.Interactions": "3.7.2",
"Discord.Net.Rest": "3.7.2",
"Discord.Net.WebSocket": "3.7.2",
"Discord.Net.Webhook": "3.7.2"
}
},
"Discord.Net.Commands/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Core/3.7.2": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net6.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Interactions/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2",
"Discord.Net.Rest": "3.7.2",
"Discord.Net.WebSocket": "3.7.2",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net6.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Rest/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Webhook/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2",
"Discord.Net.Rest": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.WebSocket/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2",
"Discord.Net.Rest": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.7.2"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"CMD_Utils/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAiCLGu5rp6+Z10FjKbbJ6LLpKjbMBGpozixkJlz5LZvuncPx8f4AWFAw7pBecKUuAh983qiZ8CZYZcNXsI4qg==",
"path": "discord.net/3.7.2",
"hashPath": "discord.net.3.7.2.nupkg.sha512"
},
"Discord.Net.Commands/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aOEGP04X64htsTr7ozKj9qHpmvOfitSw5gfR8Tw9TX0+FdswD2LNL2KfOAIaxRKZmRTm34aXQEJrVq0K8AptmQ==",
"path": "discord.net.commands/3.7.2",
"hashPath": "discord.net.commands.3.7.2.nupkg.sha512"
},
"Discord.Net.Core/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-apwswc6LjN4dj3u27SO3Hr56Jzl91wzReahieoD7IQhV+BJQaRxhTRiEEWFTrBzHfeFHEOQ7r6vZnra3zeFhKA==",
"path": "discord.net.core/3.7.2",
"hashPath": "discord.net.core.3.7.2.nupkg.sha512"
},
"Discord.Net.Interactions/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dwGhEdDB0yyo/lGtjwIDVZmsuD52di7lIZWu/sBtvvA05dMgYZq5S6ILdsBXjOyaHeXd+EV4YMlj2VS/rm619w==",
"path": "discord.net.interactions/3.7.2",
"hashPath": "discord.net.interactions.3.7.2.nupkg.sha512"
},
"Discord.Net.Rest/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dyp8YaMBNJ837EH1KNz2PNGZqc2y71WFd1+pdldF+pLQJ3Gf/+V7685paAR7bQw7yFNyqEBR/QRBCNp+QIQ7Wg==",
"path": "discord.net.rest/3.7.2",
"hashPath": "discord.net.rest.3.7.2.nupkg.sha512"
},
"Discord.Net.Webhook/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-da3i/mTq2y7mfj3xlHH14S4PivHbflJCVr8OUikJtQrxBOxvPkqP7ZYk3Y9S28q0K8qik+TUjCcjL5gELKrh/A==",
"path": "discord.net.webhook/3.7.2",
"hashPath": "discord.net.webhook.3.7.2.nupkg.sha512"
},
"Discord.Net.WebSocket/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pYCd6ET44ADaNiyEw82TaJnR7TKYHfrKCytWFWMPL5faJhoh260avZn3Hwunlf331lEQ0f4K1CujPkQbNuq7kQ==",
"path": "discord.net.websocket/3.7.2",
"hashPath": "discord.net.websocket.3.7.2.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

View File

@@ -1,268 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"MusicCommands/1.0.0": {
"dependencies": {
"PluginManager": "1.0.0"
},
"runtime": {
"MusicCommands.dll": {}
}
},
"Discord.Net/3.7.2": {
"dependencies": {
"Discord.Net.Commands": "3.7.2",
"Discord.Net.Core": "3.7.2",
"Discord.Net.Interactions": "3.7.2",
"Discord.Net.Rest": "3.7.2",
"Discord.Net.WebSocket": "3.7.2",
"Discord.Net.Webhook": "3.7.2"
}
},
"Discord.Net.Commands/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.Commands.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Core/3.7.2": {
"dependencies": {
"Newtonsoft.Json": "13.0.1",
"System.Collections.Immutable": "5.0.0",
"System.Interactive.Async": "5.0.0",
"System.ValueTuple": "4.5.0"
},
"runtime": {
"lib/net6.0/Discord.Net.Core.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Interactions/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2",
"Discord.Net.Rest": "3.7.2",
"Discord.Net.WebSocket": "3.7.2",
"Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
"System.Collections.Immutable": "5.0.0",
"System.Reactive": "5.0.0"
},
"runtime": {
"lib/net6.0/Discord.Net.Interactions.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Rest/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.Rest.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.Webhook/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2",
"Discord.Net.Rest": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.Webhook.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Discord.Net.WebSocket/3.7.2": {
"dependencies": {
"Discord.Net.Core": "3.7.2",
"Discord.Net.Rest": "3.7.2"
},
"runtime": {
"lib/net6.0/Discord.Net.WebSocket.dll": {
"assemblyVersion": "3.7.2.0",
"fileVersion": "3.7.2.0"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"runtime": {
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.20.51904"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"System.Collections.Immutable/5.0.0": {},
"System.Interactive.Async/5.0.0": {
"dependencies": {
"System.Linq.Async": "5.0.0"
},
"runtime": {
"lib/netcoreapp3.1/System.Interactive.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Linq.Async/5.0.0": {
"runtime": {
"lib/netcoreapp3.1/System.Linq.Async.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.Reactive/5.0.0": {
"runtime": {
"lib/net5.0/System.Reactive.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.0.0.1"
}
}
},
"System.ValueTuple/4.5.0": {},
"PluginManager/1.0.0": {
"dependencies": {
"Discord.Net": "3.7.2"
},
"runtime": {
"PluginManager.dll": {}
}
}
}
},
"libraries": {
"MusicCommands/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Discord.Net/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAiCLGu5rp6+Z10FjKbbJ6LLpKjbMBGpozixkJlz5LZvuncPx8f4AWFAw7pBecKUuAh983qiZ8CZYZcNXsI4qg==",
"path": "discord.net/3.7.2",
"hashPath": "discord.net.3.7.2.nupkg.sha512"
},
"Discord.Net.Commands/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aOEGP04X64htsTr7ozKj9qHpmvOfitSw5gfR8Tw9TX0+FdswD2LNL2KfOAIaxRKZmRTm34aXQEJrVq0K8AptmQ==",
"path": "discord.net.commands/3.7.2",
"hashPath": "discord.net.commands.3.7.2.nupkg.sha512"
},
"Discord.Net.Core/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-apwswc6LjN4dj3u27SO3Hr56Jzl91wzReahieoD7IQhV+BJQaRxhTRiEEWFTrBzHfeFHEOQ7r6vZnra3zeFhKA==",
"path": "discord.net.core/3.7.2",
"hashPath": "discord.net.core.3.7.2.nupkg.sha512"
},
"Discord.Net.Interactions/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dwGhEdDB0yyo/lGtjwIDVZmsuD52di7lIZWu/sBtvvA05dMgYZq5S6ILdsBXjOyaHeXd+EV4YMlj2VS/rm619w==",
"path": "discord.net.interactions/3.7.2",
"hashPath": "discord.net.interactions.3.7.2.nupkg.sha512"
},
"Discord.Net.Rest/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dyp8YaMBNJ837EH1KNz2PNGZqc2y71WFd1+pdldF+pLQJ3Gf/+V7685paAR7bQw7yFNyqEBR/QRBCNp+QIQ7Wg==",
"path": "discord.net.rest/3.7.2",
"hashPath": "discord.net.rest.3.7.2.nupkg.sha512"
},
"Discord.Net.Webhook/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-da3i/mTq2y7mfj3xlHH14S4PivHbflJCVr8OUikJtQrxBOxvPkqP7ZYk3Y9S28q0K8qik+TUjCcjL5gELKrh/A==",
"path": "discord.net.webhook/3.7.2",
"hashPath": "discord.net.webhook.3.7.2.nupkg.sha512"
},
"Discord.Net.WebSocket/3.7.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pYCd6ET44ADaNiyEw82TaJnR7TKYHfrKCytWFWMPL5faJhoh260avZn3Hwunlf331lEQ0f4K1CujPkQbNuq7kQ==",
"path": "discord.net.websocket/3.7.2",
"hashPath": "discord.net.websocket.3.7.2.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"System.Collections.Immutable/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
"path": "system.collections.immutable/5.0.0",
"hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
},
"System.Interactive.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QaqhQVDiULcu4vm6o89+iP329HcK44cETHOYgy/jfEjtzeFy0ZxmuM7nel9ocjnKxEM4yh1mli7hgh8Q9o+/Iw==",
"path": "system.interactive.async/5.0.0",
"hashPath": "system.interactive.async.5.0.0.nupkg.sha512"
},
"System.Linq.Async/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw==",
"path": "system.linq.async/5.0.0",
"hashPath": "system.linq.async.5.0.0.nupkg.sha512"
},
"System.Reactive/5.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
"path": "system.reactive/5.0.0",
"hashPath": "system.reactive.5.0.0.nupkg.sha512"
},
"System.ValueTuple/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==",
"path": "system.valuetuple/4.5.0",
"hashPath": "system.valuetuple.4.5.0.nupkg.sha512"
},
"PluginManager/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<BaseOutputPath>..\DiscordBot\bin\Debug\net6.0\Data\Plugins\Commands\LevelingSystem</BaseOutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PluginManager\PluginManager.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,48 +0,0 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using PluginManager;
using PluginManager.Interfaces;
using PluginManager.Others;
namespace CMD_LevelingSystem;
internal class Level : DBCommand
{
public string Command => "level";
public string Description => "Display tour current level";
public string Usage => "level";
public bool canUseDM => false;
public bool canUseServer => true;
public bool requireAdmin => false;
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
if (!File.Exists(Config.GetValue<string>("LevelingSystemPath") + $"/{message.Author.Id}.dat"))
{
await context.Channel.SendMessageAsync("You are now unranked !");
return;
}
var user = await Functions.ConvertFromJson<User>(Config.GetValue<string>("LevelingSystemPath") + $"/{message.Author.Id}.dat");
if (user == null)
{
await context.Channel.SendMessageAsync("You are now unranked !");
return;
}
var builder = new EmbedBuilder();
var r = new Random();
builder.WithColor(r.Next(256), r.Next(256), r.Next(256));
builder.AddField("Current Level", user.CurrentLevel, true)
.AddField("Current EXP", user.CurrentEXP, true)
.AddField("Required Exp", user.RequiredEXPToLevelUp, true);
builder.WithTimestamp(DateTimeOffset.Now);
await context.Channel.SendMessageAsync(embed: builder.Build());
}
}

View File

@@ -1,18 +0,0 @@
using Discord.WebSocket;
namespace CMD_LevelingSystem;
public class DiscordUser
{
public string Username { get; set; }
public ushort DiscordTag { get; set; }
public ulong userID { get; set; }
}
public class User
{
public DiscordUser user { get; set; }
public int CurrentLevel { get; set; }
public long CurrentEXP { get; set; }
public long RequiredEXPToLevelUp { get; set; }
}

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<BaseOutputPath></BaseOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\BUILDS\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PluginManager\PluginManager.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,23 +0,0 @@
using Discord.Commands;
using Discord.WebSocket;
using PluginManager.Interfaces;
internal class Echo : DBCommand
{
public string Command => "echo";
public string Description => "Replay with the same message";
public string Usage => "echo [message]";
public bool canUseDM => true;
public bool canUseServer => true;
public bool requireAdmin => false;
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
var m = message.Content.Substring(6);
await message.Channel.SendMessageAsync(m);
}
}

View File

@@ -1,30 +0,0 @@
using Discord.Commands;
using Discord.WebSocket;
using PluginManager.Interfaces;
namespace CMD_Utils;
internal class FlipCoin : DBCommand
{
public string Command => "flip";
public string Description => "Flip a coin";
public string Usage => "flip";
public bool canUseDM => true;
public bool canUseServer => true;
public bool requireAdmin => false;
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
var random = new System.Random();
var r = random.Next(1, 3);
if (r == 1)
await message.Channel.SendMessageAsync("Heads");
else
await message.Channel.SendMessageAsync("Tails");
}
}

View File

@@ -1,45 +0,0 @@
using System.Collections.Generic;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using PluginManager.Interfaces;
using PluginManager.Others;
namespace CMD_Utils;
public class Poll : DBCommand
{
public string Command => "poll";
public string Description => "Create a poll with options";
public string Usage => "poll [This-is-question] [This-is-answer-1] [This-is-answer-2] ... ";
public bool canUseDM => false;
public bool canUseServer => true;
public bool requireAdmin => true;
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
if (isDM) return;
var question = message.Content.Split(' ')[1].Replace('-', ' ');
var answers = Functions.MergeStrings(message.Content.Split(' '), 2).Split(' ');
var embedBuilder = new EmbedBuilder();
embedBuilder.Title = question;
var len = answers.Length;
for (var i = 0; i < len; i++) embedBuilder.AddField($"Answer {i + 1}", answers[i].Replace('-', ' '), true);
var msg = await context.Channel.SendMessageAsync(embed: embedBuilder.Build());
var emotes = new List<IEmote>();
emotes.Add(Emoji.Parse(":one:"));
emotes.Add(Emoji.Parse(":two:"));
emotes.Add(Emoji.Parse(":three:"));
emotes.Add(Emoji.Parse(":four:"));
emotes.Add(Emoji.Parse(":five:"));
emotes.Add(Emoji.Parse(":six:"));
for (var i = 0; i < len; i++) await msg.AddReactionAsync(emotes[i]);
}
}

View File

@@ -1,39 +0,0 @@
using Discord.Commands;
using Discord.WebSocket;
using PluginManager.Interfaces;
public class Random : DBCommand
{
public string Command => "random";
public string Description => "random number between number1 and number2";
public string Usage => "random [number1] [number2]";
public bool canUseDM => true;
public bool canUseServer => true;
public bool requireAdmin => false;
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
try
{
var msg = message.Content;
var a = int.Parse(msg.Split(' ')[1]);
var b = int.Parse(msg.Split(' ')[2]);
if (a > b)
{
var temp = a;
a = b;
b = temp;
}
await message.Channel.SendMessageAsync("Your random generated number is " + new System.Random().Next(a, b));
}
catch
{
await message.Channel.SendMessageAsync("Invalid numbers or no numbers:\nUsage: " + Usage);
}
}
}

Binary file not shown.

View File

@@ -1,4 +0,0 @@
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>

View File

@@ -1,100 +0,0 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using PluginManager.Interfaces;
using PluginManager.Loaders;
using PluginManager.Others;
namespace DiscordBot.Discord.Commands;
/// <summary>
/// The help command
/// </summary>
internal class Help : DBCommand
{
/// <summary>
/// Command name
/// </summary>
public string Command => "help";
/// <summary>
/// Command Description
/// </summary>
public string Description => "This command allows you to check all loadded commands";
/// <summary>
/// Command usage
/// </summary>
public string Usage => "help";
/// <summary>
/// Check if the command can be used <inheritdoca DM <see cref="IChannel" />/>
/// </summary>
public bool canUseDM => true;
/// <summary>
/// Check if the command can be used in a server
/// </summary>
public bool canUseServer => true;
/// <summary>
/// Check if the command require administrator to be executed
/// </summary>
public bool requireAdmin => false;
/// <summary>
/// The main body of the command
/// </summary>
/// <param name="context">The command context</param>
/// <param name="message">The command message</param>
/// <param name="client">The discord bot client</param>
/// <param name="isDM">True if the message was sent from a DM channel, false otherwise</param>
public void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
var args = Functions.GetArguments(message);
if (args.Count != 0)
{
foreach (var item in args)
{
var e = GenerateHelpCommand(item);
if (e != null)
context.Channel.SendMessageAsync(embed: e.Build());
else
context.Channel.SendMessageAsync("Unknown Command " + item);
}
return;
}
var embedBuilder = new EmbedBuilder();
var adminCommands = "";
var normalCommands = "";
var DMCommands = "";
foreach (var cmd in PluginLoader.Commands!)
{
if (cmd.canUseDM) DMCommands += cmd.Command + " ";
if (cmd.requireAdmin)
adminCommands += cmd.Command + " ";
else if (cmd.canUseServer) normalCommands += cmd.Command + " ";
}
embedBuilder.AddField("Admin Commands", adminCommands);
embedBuilder.AddField("Normal Commands", normalCommands);
embedBuilder.AddField("DM Commands", DMCommands);
context.Channel.SendMessageAsync(embed: embedBuilder.Build());
}
private EmbedBuilder GenerateHelpCommand(string command)
{
var embedBuilder = new EmbedBuilder();
var cmd = PluginLoader.Commands.Find(p => p.Command == command);
if (cmd == null) return null;
embedBuilder.AddField("Usage", cmd.Usage);
embedBuilder.AddField("Description", cmd.Description);
return embedBuilder;
}
}

View File

@@ -1,113 +0,0 @@
using System;
using System.Diagnostics;
using Discord.WebSocket;
using PluginManager.Interfaces;
using PluginManager.Others;
using PluginManager.Others.Permissions;
using DiscordLibCommands = Discord.Commands;
using DiscordLib = Discord;
using OperatingSystem = PluginManager.Others.OperatingSystem;
namespace DiscordBot.Discord.Commands;
internal class Restart : DBCommand
{
/// <summary>
/// Command name
/// </summary>
public string Command => "restart";
/// <summary>
/// Command Description
/// </summary>
public string Description => "Restart the bot";
/// <summary>
/// Command usage
/// </summary>
public string Usage => "restart [-p | -c | -args | -cmd] <args>";
/// <summary>
/// Check if the command can be used <inheritdoca DM <see cref="IChannel" />/>
/// </summary>
public bool canUseDM => false;
/// <summary>
/// Check if the command can be used in a server
/// </summary>
public bool canUseServer => true;
/// <summary>
/// Check if the command require administrator to be executed
/// </summary>
public bool requireAdmin => false;
/// <summary>
/// The main body of the command
/// </summary>
/// <param name="context">The command context</param>
/// <param name="message">The command message</param>
/// <param name="client">The discord bot client</param>
/// <param name="isDM">True if the message was sent from a DM channel, false otherwise</param>
public async void Execute(DiscordLibCommands.SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
if (!(message.Author as SocketGuildUser).hasPermission(DiscordLib.GuildPermission.Administrator)) return;
var args = Functions.GetArguments(message);
var OS = Functions.GetOperatingSystem();
if (args.Count == 0)
{
switch (OS)
{
case OperatingSystem.WINDOWS:
Process.Start("./DiscordBot.exe");
break;
case OperatingSystem.LINUX:
case OperatingSystem.MAC_OS:
Process.Start("./DiscordBot");
break;
default:
return;
}
return;
}
switch (args[0])
{
case "-p":
case "-poweroff":
case "-c":
case "-close":
Environment.Exit(0);
break;
case "-cmd":
case "-args":
var cmd = "--args";
if (args.Count > 1)
for (var i = 1; i < args.Count; i++)
cmd += $" {args[i]}";
switch (OS)
{
case OperatingSystem.WINDOWS:
Functions.WriteLogFile("Restarting the bot with the following arguments: \"" + cmd + "\"");
Process.Start("./DiscordBot.exe", cmd);
break;
case OperatingSystem.LINUX:
//case PluginManager.Others.OperatingSystem.MAC_OS: ?? - not tested
Process.Start("./DiscordBot", cmd);
break;
default:
return;
}
Environment.Exit(0);
break;
default:
await context.Channel.SendMessageAsync("Invalid argument. Use `help restart` to see the usage.");
break;
}
}
}

View File

@@ -1,97 +0,0 @@
using System;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using PluginManager;
using PluginManager.Interfaces;
namespace DiscordBot.Discord.Commands;
internal class Settings : DBCommand
{
/// <summary>
/// Command name
/// </summary>
public string Command => "set";
/// <summary>
/// Command Description
/// </summary>
public string Description => "This command allows you change all settings. Use \"set help\" to show details";
/// <summary>
/// Command usage
/// </summary>
public string Usage => "set [keyword] [new Value]";
/// <summary>
/// Check if the command can be used <inheritdoca DM <see cref="IChannel" />/>
/// </summary>
public bool canUseDM => true;
/// <summary>
/// Check if the command can be used in a server
/// </summary>
public bool canUseServer => true;
/// <summary>
/// Check if the command require administrator to be executed
/// </summary>
public bool requireAdmin => true;
/// <summary>
/// The main body of the command
/// </summary>
/// <param name="context">The command context</param>
/// <param name="message">The command message</param>
/// <param name="client">The discord bot client</param>
/// <param name="isDM">True if the message was sent from a DM channel, false otherwise</param>
public async void Execute(SocketCommandContext context, SocketMessage message, DiscordSocketClient client, bool isDM)
{
var channel = message.Channel;
try
{
var content = message.Content;
var data = content.Split(' ');
var keyword = data[1];
if (keyword.ToLower() == "help")
{
await channel.SendMessageAsync("set token [new value] -- set the value of the new token (require restart)");
await channel.SendMessageAsync("set prefix [new value] -- set the value of the new preifx (require restart)");
return;
}
switch (keyword.ToLower())
{
case "token":
if (data.Length != 3)
{
await channel.SendMessageAsync("Invalid token !");
return;
}
Config.SetValue("token", data[2]);
break;
case "prefix":
if (data.Length != 3)
{
await channel.SendMessageAsync("Invalid token !");
return;
}
Config.SetValue("token", data[2]);
break;
default:
return;
}
await channel.SendMessageAsync("Restart required ...");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
await channel.SendMessageAsync("Unknown usage to this command !\nUsage: " + Usage);
}
}
}

View File

@@ -1,149 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using PluginManager;
using static PluginManager.Others.Functions;
namespace DiscordBot.Discord.Core;
internal class Boot
{
/// <summary>
/// The bot prefix
/// </summary>
public readonly string botPrefix;
/// <summary>
/// The bot token
/// </summary>
public readonly string botToken;
/// <summary>
/// The bot client
/// </summary>
public DiscordSocketClient client;
/// <summary>
/// The bot command handler
/// </summary>
private CommandHandler commandServiceHandler;
/// <summary>
/// The command service
/// </summary>
private CommandService service;
/// <summary>
/// The main Boot constructor
/// </summary>
/// <param name="botToken">The bot token</param>
/// <param name="botPrefix">The bot prefix</param>
public Boot(string botToken, string botPrefix)
{
this.botPrefix = botPrefix;
this.botToken = botToken;
}
/// <summary>
/// Checks if the bot is ready
/// </summary>
/// <value> true if the bot is ready, othwerwise false </value>
public bool isReady { get; private set; }
/// <summary>
/// The start method for the bot. This method is used to load the bot
/// </summary>
/// <returns>Task</returns>
public async Task Awake()
{
client = new DiscordSocketClient();
service = new CommandService();
CommonTasks();
await client.LoginAsync(TokenType.Bot, botToken);
await client.StartAsync();
commandServiceHandler = new CommandHandler(client, service, botPrefix);
await commandServiceHandler.InstallCommandsAsync();
await Task.Delay(2000);
while (!isReady) ;
}
private void CommonTasks()
{
if (client == null) return;
client.LoggedOut += Client_LoggedOut;
client.Log += Log;
client.LoggedIn += LoggedIn;
client.Ready += Ready;
}
private Task Client_LoggedOut()
{
WriteLogFile("Successfully Logged Out");
Log(new LogMessage(LogSeverity.Info, "Boot", "Successfully logged out from discord !"));
return Task.CompletedTask;
}
private Task Ready()
{
Console.Title = "ONLINE";
isReady = true;
new Thread(() =>
{
while (true)
{
Config.SaveConfig();
Thread.Sleep(10000);
}
}
).Start();
return Task.CompletedTask;
}
private Task LoggedIn()
{
Console.Title = "CONNECTED";
WriteLogFile("The bot has been logged in at " + DateTime.Now.ToShortDateString() + " (" +
DateTime.Now.ToShortTimeString() + ")"
);
return Task.CompletedTask;
}
private Task Log(LogMessage message)
{
switch (message.Severity)
{
case LogSeverity.Error:
case LogSeverity.Critical:
WriteErrFile(message.Message);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[ERROR] " + message.Message);
Console.ForegroundColor = ConsoleColor.White;
break;
case LogSeverity.Info:
case LogSeverity.Debug:
WriteLogFile(message.Message);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("[INFO] " + message.Message);
Console.ForegroundColor = ConsoleColor.White;
break;
}
return Task.CompletedTask;
}
}

View File

@@ -1,131 +0,0 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using PluginManager.Loaders;
using PluginManager.Others;
using PluginManager.Others.Permissions;
namespace DiscordBot.Discord.Core;
internal class CommandHandler
{
private readonly string botPrefix;
private readonly DiscordSocketClient client;
private readonly CommandService commandService;
/// <summary>
/// Command handler constructor
/// </summary>
/// <param name="client">The discord bot client</param>
/// <param name="commandService">The discord bot command service</param>
/// <param name="botPrefix">The prefix to watch for</param>
public CommandHandler(DiscordSocketClient client, CommandService commandService, string botPrefix)
{
this.client = client;
this.commandService = commandService;
this.botPrefix = botPrefix;
}
/// <summary>
/// The method to initialize all commands
/// </summary>
/// <returns></returns>
public async Task InstallCommandsAsync()
{
client.MessageReceived += MessageHandler;
await commandService.AddModulesAsync(Assembly.GetEntryAssembly(), null);
}
/// <summary>
/// The message handler for the bot
/// </summary>
/// <param name="Message">The message got from the user in discord chat</param>
/// <returns></returns>
private async Task MessageHandler(SocketMessage Message)
{
try
{
if (Message as SocketUserMessage == null) return;
var message = Message as SocketUserMessage;
if (message == null) return;
if (!message.Content.StartsWith(botPrefix)) return;
var argPos = 0;
if (message.HasMentionPrefix(client.CurrentUser, ref argPos))
{
await message.Channel.SendMessageAsync("Can not exec mentioned commands !");
return;
}
if (message.Author.IsBot) return;
var context = new SocketCommandContext(client, message);
await commandService.ExecuteAsync(
context,
argPos,
null
);
var plugin = PluginLoader.Commands!.Where(p => p.Command == message.Content.Split(' ')[0].Substring(botPrefix.Length)).FirstOrDefault();
if (plugin != null)
{
if (message.Channel == await message.Author.CreateDMChannelAsync())
{
if (plugin.canUseDM)
{
if (plugin.requireAdmin)
{
if (message.Author.isAdmin())
{
plugin.Execute(context, message, client, true);
Functions.WriteLogFile($"[{message.Author.Id}] Executed command (DM) : " + plugin.Command);
return;
}
await message.Channel.SendMessageAsync("This command is for administrators only !");
return;
}
plugin.Execute(context, message, client, true);
Functions.WriteLogFile($"[{message.Author.Id}] Executed command (DM) : " + plugin.Command);
return;
}
await message.Channel.SendMessageAsync("This command is not for DMs");
return;
}
if (plugin.canUseServer)
{
if (plugin.requireAdmin)
{
if (message.Author.isAdmin())
{
plugin.Execute(context, message, client, false);
Functions.WriteLogFile($"[{message.Author.Id}] Executed command : " + plugin.Command);
return;
}
await message.Channel.SendMessageAsync("This command is for administrators only !");
return;
}
plugin.Execute(context, message, client, false);
Functions.WriteLogFile($"[{message.Author.Id}] Executed command : " + plugin.Command);
}
}
}
catch
{
}
}
}

View File

@@ -1,48 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<ApplicationIcon />
<StartupObject />
<SignAssembly>False</SignAssembly>
<IsPublishable>True</IsPublishable>
<AssemblyVersion>1.0.0.1</AssemblyVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>none</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Data\**" />
<Compile Remove="obj\**" />
<Compile Remove="Output\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Data\**" />
<EmbeddedResource Remove="obj\**" />
<EmbeddedResource Remove="Output\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Data\**" />
<None Remove="obj\**" />
<None Remove="Output\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PluginManager\PluginManager.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,312 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DiscordBot.Discord.Core;
using PluginManager;
using PluginManager.Items;
using PluginManager.Online;
using PluginManager.Others;
namespace DiscordBot;
public class Program
{
private static bool loadPluginsOnStartup;
private static bool listPluginsAtStartup;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
[Obsolete]
public static void Main(string[] args)
{
Directory.CreateDirectory("./Data/Resources");
Directory.CreateDirectory("./Data/Plugins/Commands");
Directory.CreateDirectory("./Data/Plugins/Events");
PreLoadComponents().Wait();
if (!Config.ContainsKey("token") || Config.GetValue<string>("token") == null || Config.GetValue<string>("token")?.Length != 70)
{
Console.WriteLine("Please insert your token");
Console.Write("Token = ");
var token = Console.ReadLine();
if (token?.Length == 59 || token?.Length == 70)
Config.AddValueToVariables("token", token, true);
else
Console.WriteLine("Invalid token");
Console.WriteLine("Please insert your prefix (max. 1 character long):");
Console.WriteLine("For a prefix longer then one character, the first character will be saved and the others will be ignored.\n No spaces or numbers allowed");
Console.Write("Prefix = ");
var prefix = Console.ReadLine()![0];
if (prefix == ' ' || char.IsDigit(prefix)) return;
Config.AddValueToVariables("prefix", prefix.ToString(), false);
}
if (!Config.ContainsKey("prefix") || Config.GetValue<string>("prefix") == default)
{
Console.WriteLine("Please insert your prefix (max. 1 character long):");
Console.WriteLine("For a prefix longer then one character, the first character will be saved and the others will be ignored.\n No spaces or numbers allowed");
Console.Write("Prefix = ");
var prefix = Console.ReadLine()![0];
if (prefix == ' ') return;
Config.AddValueToVariables("prefix", prefix.ToString(), false);
}
HandleInput(args).Wait();
}
/// <summary>
/// The main loop for the discord bot
/// </summary>
/// <param name="discordbooter">The discord booter used to start the application</param>
private static Task NoGUI(Boot discordbooter)
{
var consoleCommandsHandler = new ConsoleCommandsHandler(discordbooter.client);
if (loadPluginsOnStartup) consoleCommandsHandler.HandleCommand("lp");
if (listPluginsAtStartup) consoleCommandsHandler.HandleCommand("listplugs");
Config.SaveConfig();
while (true)
{
Console.ForegroundColor = ConsoleColor.White;
var cmd = Console.ReadLine();
if (!consoleCommandsHandler.HandleCommand(cmd))
Console.WriteLine("Failed to run command " + cmd);
}
}
/// <summary>
/// Start the bot without user interface
/// </summary>
/// <returns>Returns the boot loader for the Discord Bot</returns>
private static async Task<Boot> StartNoGUI()
{
Console.Clear();
Console.ForegroundColor = ConsoleColor.DarkYellow;
List<string> startupMessageList = await ServerCom.ReadTextFromFile("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/StartupMessage");
foreach (var message in startupMessageList) Console.WriteLine(message);
Console.WriteLine($"Running on version: {Config.GetValue<string>("Version") ?? System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()}");
Console.WriteLine($"Git URL: {Config.GetValue<string>("GitURL") ?? " Could not find Git URL"}");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"============================ LOG ============================");
try
{
var token = Config.GetValue<string>("token");
var prefix = Config.GetValue<string>("prefix");
var discordbooter = new Boot(token, prefix);
await discordbooter.Awake();
return discordbooter;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
/// <summary>
/// Clear folder
/// </summary>
/// <param name="d">Directory path</param>
private static Task ClearFolder(string d)
{
var files = Directory.GetFiles(d);
var fileNumb = files.Length;
for (var i = 0; i < fileNumb; i++)
{
File.Delete(files[i]);
Console.WriteLine("Deleting : " + files[i]);
}
return Task.CompletedTask;
}
/// <summary>
/// Handle user input arguments from the startup of the application
/// </summary>
/// <param name="args">The arguments</param>
private static async Task HandleInput(string[] args)
{
var len = args.Length;
if (len == 3 && args[0] == "/download")
{
var url = args[1];
var location = args[2];
await ServerCom.DownloadFileAsync(url, location);
return;
}
if (len > 0 && (args.Contains("--cmd") || args.Contains("--args") || args.Contains("--nomessage")))
{
if (args.Contains("lp") || args.Contains("loadplugins")) loadPluginsOnStartup = true;
if (args.Contains("listplugs")) listPluginsAtStartup = true;
len = 0;
}
if (len == 0 || (args[0] != "--exec" && args[0] != "--execute"))
{
var b = await StartNoGUI();
await NoGUI(b);
return;
}
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Execute command interface noGUI\n\n");
Console.WriteLine(
"\tCommand name\t\t\t\tDescription\n" +
"-- help | -help\t\t ------ \tDisplay the help message\n" +
"--reset-full\t\t ------ \tReset all files (clear files)\n" +
"--reset-logs\t\t ------ \tClear up the output folder\n" +
"--start\t\t ------ \tStart the bot\n" +
"exit\t\t\t ------ \tClose the application"
);
while (true)
{
Console.ForegroundColor = ConsoleColor.White;
Console.Write("> ");
var message = Console.ReadLine().Split(' ');
switch (message[0])
{
case "--help":
case "-help":
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(
"\tCommand name\t\t\t\tDescription\n" +
"-- help | -help\t\t ------ \tDisplay the help message\n" +
"--reset-full\t\t ------ \tReset all files (clear files)\n" +
"--reset-settings\t ------ \tReset only bot settings\n" +
"--reset-logs\t\t ------ \tClear up the output folder\n" +
"--start\t\t ------ \tStart the bot\n" +
"exit\t\t\t ------ \tClose the application"
);
break;
case "--reset-full":
await ClearFolder("./Data/Resources/");
await ClearFolder("./Output/Logs/");
await ClearFolder("./Output/Errors");
await ClearFolder("./Data/Languages/");
await ClearFolder("./Data/Plugins/Commands");
await ClearFolder("./Data/Plugins/Events");
Console.WriteLine("Successfully cleared all folders");
break;
case "--reset-logs":
await ClearFolder("./Output/Logs");
await ClearFolder("./Output/Errors");
Console.WriteLine("Successfully cleard logs folder");
break;
case "--exit":
case "exit":
Environment.Exit(0);
break;
case "--start":
var booter = await StartNoGUI();
await NoGUI(booter);
return;
default:
Console.WriteLine("Failed to execute command " + message[0]);
break;
}
}
}
private static async Task PreLoadComponents()
{
await Config.LoadConfig();
if (Config.ContainsKey("DeleteLogsAtStartup"))
if (Config.GetValue<bool>("DeleteLogsAtStartup"))
foreach (var file in Directory.GetFiles("./Output/Logs/"))
File.Delete(file);
List<string> OnlineDefaultKeys = await ServerCom.ReadTextFromFile("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/SetupKeys");
Config.PluginConfig.Load();
if (!Config.ContainsKey("Version"))
Config.AddValueToVariables("Version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), false);
else
Config.SetValue("Version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString());
foreach (var key in OnlineDefaultKeys)
{
if (key.Length <= 3 || !key.Contains(' ')) continue;
string[] s = key.Split(' ');
try
{
Config.GetAndAddValueToVariable(s[0], s[1], s[2].Equals("true", StringComparison.CurrentCultureIgnoreCase));
}
catch (Exception ex)
{
Functions.WriteErrFile(ex.Message);
}
}
List<string> onlineSettingsList = await ServerCom.ReadTextFromFile("https://raw.githubusercontent.com/Wizzy69/installer/discord-bot-files/OnlineData");
foreach (var key in onlineSettingsList)
{
if (key.Length <= 3 || !key.Contains(' ')) continue;
string[] s = key.Split(' ');
switch (s[0])
{
case "CurrentVersion":
string newVersion = s[1];
if (!newVersion.Equals(Config.GetValue<string>("Version")))
{
Console.WriteLine("A new version has been released on github page.");
Console.WriteLine("Download the new version using the following link wrote in yellow");
Console_Utilities.WriteColorText("&y" + Config.GetValue<string>("GitURL") + "&c");
Console.WriteLine();
Console.WriteLine("Your product will work just fine on this outdated version, but an update is recommended.\n" +
"From now on, this version is no longer supported"
);
Console_Utilities.WriteColorText("&rUse at your own risk&c");
Console_Utilities.WriteColorText("&mCurrent Version: " + Config.GetValue<string>("Version") + "&c");
Console_Utilities.WriteColorText("&gNew Version: " + newVersion + "&c");
Console.WriteLine("\n\n");
await Task.Delay(1000);
int waitTime = 20; //wait time to proceed
Console.Write($"The bot will start in {waitTime} seconds");
while (waitTime > 0)
{
await Task.Delay(1000);
waitTime--;
Console.SetCursorPosition("The bot will start in ".Length, Console.CursorTop);
Console.Write(" ");
Console.SetCursorPosition("The bot will start in ".Length, Console.CursorTop);
Console.Write(waitTime + " seconds");
}
}
break;
}
}
Config.SaveConfig();
}
}

View File

@@ -0,0 +1,113 @@
using DiscordBotCore.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace DiscordBotCore.Configuration;
public class Configuration : ConfigurationBase
{
private readonly bool _EnableAutoAddOnGetWithDefault;
private Configuration(ILogger logger, string diskLocation, bool enableAutoAddOnGetWithDefault): base(logger, diskLocation)
{
_EnableAutoAddOnGetWithDefault = enableAutoAddOnGetWithDefault;
}
public override async Task SaveToFile()
{
var json = JsonConvert.SerializeObject(_InternalDictionary, Formatting.Indented);
await File.WriteAllTextAsync(_DiskLocation, json);
}
public override T Get<T>(string key, T defaultValue)
{
T value = base.Get(key, defaultValue);
if (_EnableAutoAddOnGetWithDefault && value.Equals(defaultValue))
{
Add(key, defaultValue);
}
return value;
}
public override List<T> GetList<T>(string key, List<T> defaultValue)
{
List<T> value = base.GetList(key, defaultValue);
if (_EnableAutoAddOnGetWithDefault && value.All(defaultValue.Contains))
{
Add(key, defaultValue);
}
return value;
}
public override void LoadFromFile()
{
if (!File.Exists(_DiskLocation))
{
SaveToFile().Wait();
return;
}
string jsonContent = File.ReadAllText(_DiskLocation);
var jObject = JsonConvert.DeserializeObject<JObject>(jsonContent);
if (jObject is null)
{
SaveToFile().Wait();
return;
}
_InternalDictionary.Clear();
foreach (var kvp in jObject)
{
AddPairToDictionary(kvp, _InternalDictionary);
}
}
private void AddPairToDictionary(KeyValuePair<string, JToken> kvp, IDictionary<string, object> dict)
{
if (kvp.Value is JObject nestedJObject)
{
dict[kvp.Key] = nestedJObject.ToObject<Dictionary<string, object>>();
foreach (var nestedKvp in nestedJObject)
{
AddPairToDictionary(nestedKvp, dict[kvp.Key] as Dictionary<string, object>);
}
}
else if (kvp.Value is JArray nestedJArray)
{
dict[kvp.Key] = nestedJArray.ToObject<List<object>>();
}
else
{
if (kvp.Value.Type == JTokenType.Integer)
dict[kvp.Key] = kvp.Value.Value<int>();
else if (kvp.Value.Type == JTokenType.Float)
dict[kvp.Key] = kvp.Value.Value<float>();
else if (kvp.Value.Type == JTokenType.Boolean)
dict[kvp.Key] = kvp.Value.Value<bool>();
else if (kvp.Value.Type == JTokenType.String)
dict[kvp.Key] = kvp.Value.Value<string>();
else if (kvp.Value.Type == JTokenType.Date)
dict[kvp.Key] = kvp.Value.Value<DateTime>();
else
dict[kvp.Key] = kvp.Value;
}
}
/// <summary>
/// Create a new Settings Dictionary from a file
/// </summary>
/// <param name="baseFile">The file location</param>
/// <param name="enableAutoAddOnGetWithDefault">Set this to true if you want to update the dictionary with default values on get</param>
public static Configuration CreateFromFile(ILogger logger, string baseFile, bool enableAutoAddOnGetWithDefault)
{
var settings = new Configuration(logger, baseFile, enableAutoAddOnGetWithDefault);
settings.LoadFromFile();
return settings;
}
}

View File

@@ -0,0 +1,167 @@
using System.Collections;
using System.Net.Mime;
using DiscordBotCore.Logging;
namespace DiscordBotCore.Configuration;
public abstract class ConfigurationBase : IConfiguration
{
protected readonly IDictionary<string, object> _InternalDictionary = new Dictionary<string, object>();
protected readonly string _DiskLocation;
protected readonly ILogger _Logger;
protected ConfigurationBase(ILogger logger, string diskLocation)
{
this._DiskLocation = diskLocation;
this._Logger = logger;
}
public virtual void Add(string key, object? value)
{
if (_InternalDictionary.ContainsKey(key))
return;
if (value is null)
return;
_InternalDictionary.Add(key, value);
}
public virtual void Set(string key, object value)
{
_InternalDictionary[key] = value;
}
public virtual object Get(string key)
{
return _InternalDictionary[key];
}
public virtual T Get<T>(string key, T defaulobject)
{
if (_InternalDictionary.TryGetValue(key, out var value))
{
return (T)Convert.ChangeType(value, typeof(T));
}
return defaulobject;
}
public virtual T? Get<T>(string key)
{
if (_InternalDictionary.TryGetValue(key, out var value))
{
return (T)Convert.ChangeType(value, typeof(T));
}
return default;
}
public virtual IDictionary<TSubKey, TSubValue> GetDictionary<TSubKey, TSubValue>(string key)
{
if (_InternalDictionary.TryGetValue(key, out var value))
{
if (value is not IDictionary)
{
throw new Exception("The value is not a dictionary");
}
var dictionary = new Dictionary<TSubKey, TSubValue>();
foreach (DictionaryEntry item in (IDictionary)value)
{
dictionary.Add((TSubKey)Convert.ChangeType(item.Key, typeof(TSubKey)), (TSubValue)Convert.ChangeType(item.Value, typeof(TSubValue)));
}
return dictionary;
}
return new Dictionary<TSubKey, TSubValue>();
}
public virtual List<T> GetList<T>(string key, List<T> defaulobject)
{
if(_InternalDictionary.TryGetValue(key, out var value))
{
if (value is not IList)
{
throw new Exception("The value is not a list");
}
var list = new List<T>();
foreach (object? item in (IList)value)
{
list.Add((T)Convert.ChangeType(item, typeof(T)));
}
return list;
}
_Logger.Log($"Key '{key}' not found in settings dictionary. Adding default value.", LogType.Warning);
return defaulobject;
}
public virtual void Remove(string key)
{
_InternalDictionary.Remove(key);
}
public virtual IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _InternalDictionary.GetEnumerator();
}
public virtual void Clear()
{
_InternalDictionary.Clear();
}
public virtual bool ContainsKey(string key)
{
return _InternalDictionary.ContainsKey(key);
}
public virtual IEnumerable<KeyValuePair<string, object>> Where(Func<KeyValuePair<string, object>, bool> predicate)
{
return _InternalDictionary.Where(predicate);
}
public virtual IEnumerable<KeyValuePair<string, object>> Where(Func<KeyValuePair<string, object>, int, bool> predicate)
{
return _InternalDictionary.Where(predicate);
}
public virtual IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<string, object>, TResult> selector)
{
return _InternalDictionary.Select(selector);
}
public virtual IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<string, object>, int, TResult> selector)
{
return _InternalDictionary.Select(selector);
}
public virtual KeyValuePair<string, object> FirstOrDefault(Func<KeyValuePair<string, object>, bool> predicate)
{
return _InternalDictionary.FirstOrDefault(predicate);
}
public virtual KeyValuePair<string, object> FirstOrDefault()
{
return _InternalDictionary.FirstOrDefault();
}
public virtual bool ContainsAllKeys(params string[] keys)
{
return keys.All(ContainsKey);
}
public virtual bool TryGetValue(string key, out object? value)
{
return _InternalDictionary.TryGetValue(key, out value);
}
public abstract Task SaveToFile();
public abstract void LoadFromFile();
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DiscordBotCore.Logging\DiscordBotCore.Logging.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,132 @@
namespace DiscordBotCore.Configuration;
public interface IConfiguration
{
/// <summary>
/// Adds an element to the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
void Add(string key, object value);
/// <summary>
/// Sets the value of a key in the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
void Set(string key, object value);
/// <summary>
/// Gets the value of a key in the custom settings dictionary. If the T type is different then the object type, it will try to convert it.
/// </summary>
/// <param name="key">The key</param>
/// <param name="defaultObject">The default value to be returned if the searched value is not found</param>
/// <typeparam name="T">The type of the returned value</typeparam>
/// <returns></returns>
T Get<T>(string key, T defaultObject);
/// <summary>
/// Gets the value of a key in the custom settings dictionary. If the T type is different then the object type, it will try to convert it.
/// </summary>
/// <param name="key">The key</param>
/// <typeparam name="T">The type of the returned value</typeparam>
/// <returns></returns>
T? Get<T>(string key);
/// <summary>
/// Get a list of values from the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="defaultObject">The default list to be returned if nothing is found</param>
/// <typeparam name="T">The type of the returned value</typeparam>
/// <returns></returns>
List<T> GetList<T>(string key, List<T> defaultObject);
/// <summary>
/// Remove a key from the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
void Remove(string key);
/// <summary>
/// Get the enumerator of the custom settings dictionary
/// </summary>
/// <returns></returns>
IEnumerator<KeyValuePair<string, object>> GetEnumerator();
/// <summary>
/// Clear the custom settings dictionary
/// </summary>
void Clear();
/// <summary>
/// Check if the custom settings dictionary contains a key
/// </summary>
/// <param name="key">The key</param>
/// <returns></returns>
bool ContainsKey(string key);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="predicate">The predicate</param>
/// <returns></returns>
IEnumerable<KeyValuePair<string, object>> Where(Func<KeyValuePair<string, object>, bool> predicate);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="predicate">The predicate</param>
IEnumerable<KeyValuePair<string, object>> Where(Func<KeyValuePair<string, object>, int, bool> predicate);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="selector">The predicate</param>
IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<string, object>, TResult> selector);
/// <summary>
/// Filter the custom settings dictionary based on a predicate
/// </summary>
/// <param name="selector">The predicate</param>
IEnumerable<TResult> Where<TResult>(Func<KeyValuePair<string, object>, int, TResult> selector);
/// <summary>
/// Get the first element of the custom settings dictionary based on a predicate
/// </summary>
/// <param name="predicate">The predicate</param>
KeyValuePair<string, object> FirstOrDefault(Func<KeyValuePair<string, object>, bool> predicate);
/// <summary>
/// Get the first element of the custom settings dictionary
/// </summary>
/// <returns></returns>
KeyValuePair<string, object> FirstOrDefault();
/// <summary>
/// Checks if the custom settings dictionary contains all the keys
/// </summary>
/// <param name="keys">A list of keys</param>
/// <returns></returns>
bool ContainsAllKeys(params string[] keys);
/// <summary>
/// Try to get the value of a key in the custom settings dictionary
/// </summary>
/// <param name="key">The key</param>
/// <param name="value">The value</param>
/// <returns></returns>
bool TryGetValue(string key, out object? value);
/// <summary>
/// Save the custom settings dictionary to a file
/// </summary>
/// <returns></returns>
Task SaveToFile();
/// <summary>
/// Load the custom settings dictionary from a file
/// </summary>
/// <returns></returns>
void LoadFromFile();
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.3" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,693 @@
using System.Data;
using Microsoft.Data.Sqlite;
namespace DiscordBotCore.Database.Sqlite;
public class SqlDatabase
{
private readonly SqliteConnection _Connection;
/// <summary>
/// Initialize a SQL connection by specifying its private path
/// </summary>
/// <param name="fileName">The path to the database (it is starting from ./Data/Resources/)</param>
public SqlDatabase(string fileName)
{
var connectionString = $"Data Source={fileName}";
_Connection = new SqliteConnection(connectionString);
}
/// <summary>
/// Open the SQL Connection. To close use the Stop() method
/// </summary>
/// <returns></returns>
public async Task Open()
{
await _Connection.OpenAsync();
}
/// <summary>
/// <para>
/// Insert into a specified table some values
/// </para>
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="values">The values to be inserted (in the correct order and number)</param>
/// <returns></returns>
public async Task InsertAsync(string tableName, params string[] values)
{
var query = $"INSERT INTO {tableName} VALUES (";
for (var i = 0; i < values.Length; i++)
{
query += $"'{values[i]}'";
if (i != values.Length - 1)
query += ", ";
}
query += ")";
var command = new SqliteCommand(query, _Connection);
await command.ExecuteNonQueryAsync();
}
/// <summary>
/// <para>
/// Insert into a specified table some values
/// </para>
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="values">The values to be inserted (in the correct order and number)</param>
/// <returns></returns>
public void Insert(string tableName, params string[] values)
{
var query = $"INSERT INTO {tableName} VALUES (";
for (var i = 0; i < values.Length; i++)
{
query += $"'{values[i]}'";
if (i != values.Length - 1)
query += ", ";
}
query += ")";
var command = new SqliteCommand(query, _Connection);
command.ExecuteNonQuery();
}
/// <summary>
/// Remove every row in a table that has a certain propery
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="KeyName">The column name that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the specified column</param>
/// <returns></returns>
public async Task RemoveKeyAsync(string tableName, string KeyName, string KeyValue)
{
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
var command = new SqliteCommand(query, _Connection);
await command.ExecuteNonQueryAsync();
}
/// <summary>
/// Remove every row in a table that has a certain propery
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="KeyName">The column name that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the specified column</param>
/// <returns></returns>
public void RemoveKey(string tableName, string KeyName, string KeyValue)
{
var query = $"DELETE FROM {tableName} WHERE {KeyName} = '{KeyValue}'";
var command = new SqliteCommand(query, _Connection);
command.ExecuteNonQuery();
}
/// <summary>
/// Check if the key exists in the table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="keyName">The column that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the specified column</param>
/// <returns></returns>
public async Task<bool> KeyExistsAsync(string tableName, string keyName, string KeyValue)
{
var query = $"SELECT * FROM {tableName} where {keyName} = '{KeyValue}'";
if (await ReadDataAsync(query) is not null)
return true;
return false;
}
/// <summary>
/// Check if the key exists in the table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="keyName">The column that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the specified column</param>
/// <returns></returns>
public bool KeyExists(string tableName, string keyName, string KeyValue)
{
var query = $"SELECT * FROM {tableName} where {keyName} = '{KeyValue}'";
if (ReadData(query) is not null)
return true;
return false;
}
/// <summary>
/// Set value of a column in a table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="keyName">The column that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the column specified</param>
/// <param name="ResultColumnName">The column that has to be modified</param>
/// <param name="ResultColumnValue">The new value that will replace the old value from the column specified</param>
public async Task SetValueAsync(
string tableName, string keyName, string KeyValue, string ResultColumnName,
string ResultColumnValue)
{
if (!await TableExistsAsync(tableName))
throw new Exception($"Table {tableName} does not exist");
await ExecuteAsync(
$"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'"
);
}
/// <summary>
/// Set value of a column in a table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="keyName">The column that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the column specified</param>
/// <param name="ResultColumnName">The column that has to be modified</param>
/// <param name="ResultColumnValue">The new value that will replace the old value from the column specified</param>
public void SetValue(
string tableName, string keyName, string KeyValue, string ResultColumnName,
string ResultColumnValue)
{
if (!TableExists(tableName))
throw new Exception($"Table {tableName} does not exist");
Execute($"UPDATE {tableName} SET {ResultColumnName}='{ResultColumnValue}' WHERE {keyName}='{KeyValue}'");
}
/// <summary>
/// Get value from a column in a table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="keyName">The column that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the specified column</param>
/// <param name="ResultColumnName">The column that has the result</param>
/// <returns>A string that has the requested value (can be null if nothing found)</returns>
public async Task<string?> GetValueAsync(
string tableName, string keyName, string KeyValue,
string ResultColumnName)
{
if (!await TableExistsAsync(tableName))
throw new Exception($"Table {tableName} does not exist");
return await ReadDataAsync($"SELECT {ResultColumnName} FROM {tableName} WHERE {keyName}='{KeyValue}'");
}
/// <summary>
/// Get value from a column in a table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="keyName">The column that the search is made by</param>
/// <param name="KeyValue">The value that is searched in the specified column</param>
/// <param name="ResultColumnName">The column that has the result</param>
/// <returns>A string that has the requested value (can be null if nothing found)</returns>
public string? GetValue(string tableName, string keyName, string KeyValue, string ResultColumnName)
{
if (!TableExists(tableName))
throw new Exception($"Table {tableName} does not exist");
return ReadData($"SELECT {ResultColumnName} FROM {tableName} WHERE {keyName}='{KeyValue}'");
}
/// <summary>
/// Stop the connection to the SQL Database
/// </summary>
/// <returns></returns>
public async void Stop()
{
await _Connection.CloseAsync();
}
/// <summary>
/// Change the structure of a table by adding new columns
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="columns">The columns to be added</param>
/// <param name="TYPE">The type of the columns (TEXT, INTEGER, FLOAT, etc)</param>
/// <returns></returns>
public async Task AddColumnsToTableAsync(string tableName, string[] columns, string TYPE = "TEXT")
{
var command = _Connection.CreateCommand();
command.CommandText = $"SELECT * FROM {tableName}";
var reader = await command.ExecuteReaderAsync();
var tableColumns = new List<string>();
for (var i = 0; i < reader.FieldCount; i++)
tableColumns.Add(reader.GetName(i));
foreach (var column in columns)
if (!tableColumns.Contains(column))
{
command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {column} {TYPE}";
await command.ExecuteNonQueryAsync();
}
}
/// <summary>
/// Change the structure of a table by adding new columns
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="columns">The columns to be added</param>
/// <param name="TYPE">The type of the columns (TEXT, INTEGER, FLOAT, etc)</param>
/// <returns></returns>
public void AddColumnsToTable(string tableName, string[] columns, string TYPE = "TEXT")
{
var command = _Connection.CreateCommand();
command.CommandText = $"SELECT * FROM {tableName}";
var reader = command.ExecuteReader();
var tableColumns = new List<string>();
for (var i = 0; i < reader.FieldCount; i++)
tableColumns.Add(reader.GetName(i));
foreach (var column in columns)
if (!tableColumns.Contains(column))
{
command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {column} {TYPE}";
command.ExecuteNonQuery();
}
}
/// <summary>
/// Check if a table exists
/// </summary>
/// <param name="tableName">The table name</param>
/// <returns>True if the table exists, false if not</returns>
public async Task<bool> TableExistsAsync(string tableName)
{
var cmd = _Connection.CreateCommand();
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
var result = await cmd.ExecuteScalarAsync();
if (result == null)
return false;
return true;
}
/// <summary>
/// Check if a table exists
/// </summary>
/// <param name="tableName">The table name</param>
/// <returns>True if the table exists, false if not</returns>
public bool TableExists(string tableName)
{
var cmd = _Connection.CreateCommand();
cmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}'";
var result = cmd.ExecuteScalar();
if (result == null)
return false;
return true;
}
/// <summary>
/// Create a table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="columns">The columns of the table</param>
/// <returns></returns>
public async Task CreateTableAsync(string tableName, params string[] columns)
{
var cmd = _Connection.CreateCommand();
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
await cmd.ExecuteNonQueryAsync();
}
/// <summary>
/// Create a table
/// </summary>
/// <param name="tableName">The table name</param>
/// <param name="columns">The columns of the table</param>
/// <returns></returns>
public void CreateTable(string tableName, params string[] columns)
{
var cmd = _Connection.CreateCommand();
cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({string.Join(", ", columns)})";
cmd.ExecuteNonQuery();
}
/// <summary>
/// Execute a custom query
/// </summary>
/// <param name="query">The query</param>
/// <returns>The number of rows that the query modified</returns>
public async Task<int> ExecuteAsync(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
var answer = await command.ExecuteNonQueryAsync();
return answer;
}
/// <summary>
/// Execute a custom query
/// </summary>
/// <param name="query">The query</param>
/// <returns>The number of rows that the query modified</returns>
public int Execute(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
_Connection.Open();
var command = new SqliteCommand(query, _Connection);
var r = command.ExecuteNonQuery();
return r;
}
/// <summary>
/// Read data from the result table and return the first row
/// </summary>
/// <param name="query">The query</param>
/// <returns>The result is a string that has all values separated by space character</returns>
public async Task<string?> ReadDataAsync(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
var reader = await command.ExecuteReaderAsync();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return string.Join<object>(" ", values);
}
return null;
}
/// <summary>
/// Read data from the result table and return the first row
/// </summary>
/// <param name="query">The query</param>
/// <param name="parameters">The parameters of the query</param>
/// <returns>The result is a string that has all values separated by space character</returns>
public async Task<string?> ReadDataAsync(string query, params KeyValuePair<string, object>[] parameters)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
foreach (var parameter in parameters)
{
var p = CreateParameter(parameter);
if (p is not null)
command.Parameters.Add(p);
}
var reader = await command.ExecuteReaderAsync();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return string.Join<object>(" ", values);
}
return null;
}
/// <summary>
/// Read data from the result table and return the first row
/// </summary>
/// <param name="query">The query</param>
/// <returns>The result is a string that has all values separated by space character</returns>
public string? ReadData(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
_Connection.Open();
var command = new SqliteCommand(query, _Connection);
var reader = command.ExecuteReader();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return string.Join<object>(" ", values);
}
return null;
}
/// <summary>
/// Read data from the result table and return the first row
/// </summary>
/// <param name="query">The query</param>
/// <returns>The first row as separated items</returns>
public async Task<object[]?> ReadDataArrayAsync(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
var reader = await command.ExecuteReaderAsync();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return values;
}
return null;
}
public async Task<object[]?> ReadDataArrayAsync(string query, params KeyValuePair<string, object>[] parameters)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
foreach (var parameter in parameters)
{
var p = CreateParameter(parameter);
if (p is not null)
command.Parameters.Add(p);
}
var reader = await command.ExecuteReaderAsync();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return values;
}
return null;
}
/// <summary>
/// Read data from the result table and return the first row
/// </summary>
/// <param name="query">The query</param>
/// <returns>The first row as separated items</returns>
public object[]? ReadDataArray(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
_Connection.Open();
var command = new SqliteCommand(query, _Connection);
var reader = command.ExecuteReader();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return values;
}
return null;
}
/// <summary>
/// Read all rows from the result table and return them as a list of string arrays. The string arrays contain the
/// values of each row
/// </summary>
/// <param name="query">The query</param>
/// <returns>A list of string arrays representing the values that the query returns</returns>
public async Task<List<string[]>?> ReadAllRowsAsync(string query)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
var reader = await command.ExecuteReaderAsync();
if (!reader.HasRows)
return null;
List<string[]> rows = new();
while (await reader.ReadAsync())
{
var values = new string[reader.FieldCount];
reader.GetValues(values);
rows.Add(values);
}
if (rows.Count == 0) return null;
return rows;
}
/// <summary>
/// Create a parameter for a query
/// </summary>
/// <param name="name">The name of the parameter</param>
/// <param name="value">The value of the parameter</param>
/// <returns>The SQLiteParameter that has the name, value and DBType set according to your inputs</returns>
private static SqliteParameter? CreateParameter(string name, object value)
{
var parameter = new SqliteParameter();
parameter.ParameterName = name;
parameter.Value = value;
if (value is string)
parameter.DbType = DbType.String;
else if (value is int)
parameter.DbType = DbType.Int32;
else if (value is long)
parameter.DbType = DbType.Int64;
else if (value is float)
parameter.DbType = DbType.Single;
else if (value is double)
parameter.DbType = DbType.Double;
else if (value is bool)
parameter.DbType = DbType.Boolean;
else if (value is DateTime)
parameter.DbType = DbType.DateTime;
else if (value is byte[])
parameter.DbType = DbType.Binary;
else if (value is Guid)
parameter.DbType = DbType.Guid;
else if (value is decimal)
parameter.DbType = DbType.Decimal;
else if (value is TimeSpan)
parameter.DbType = DbType.Time;
else if (value is DateTimeOffset)
parameter.DbType = DbType.DateTimeOffset;
else if (value is ushort)
parameter.DbType = DbType.UInt16;
else if (value is uint)
parameter.DbType = DbType.UInt32;
else if (value is ulong)
parameter.DbType = DbType.UInt64;
else if (value is sbyte)
parameter.DbType = DbType.SByte;
else if (value is short)
parameter.DbType = DbType.Int16;
else if (value is byte)
parameter.DbType = DbType.Byte;
else if (value is char)
parameter.DbType = DbType.StringFixedLength;
else if (value is char[])
parameter.DbType = DbType.StringFixedLength;
else
return null;
return parameter;
}
/// <summary>
/// Create a parameter for a query. The function automatically detects the type of the value.
/// </summary>
/// <param name="parameterValues">The parameter raw inputs. The Key is name and the Value is the value of the parameter</param>
/// <returns>The SQLiteParameter that has the name, value and DBType set according to your inputs</returns>
private static SqliteParameter? CreateParameter(KeyValuePair<string, object> parameterValues)
{
return CreateParameter(parameterValues.Key, parameterValues.Value);
}
/// <summary>
/// Execute a query with parameters
/// </summary>
/// <param name="query">The query to execute</param>
/// <param name="parameters">The parameters of the query</param>
/// <returns>The number of rows that the query modified in the database</returns>
public async Task<int> ExecuteNonQueryAsync(string query, params KeyValuePair<string, object>[] parameters)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
foreach (var parameter in parameters)
{
var p = CreateParameter(parameter);
if (p is not null)
command.Parameters.Add(p);
}
return await command.ExecuteNonQueryAsync();
}
/// <summary>
/// Execute a query with parameters that returns a specific type of object. The function will return the first row of the result transformed into the specified type.
/// </summary>
/// <param name="query">The query to execute</param>
/// <param name="convertor">The convertor function that will convert each row of the response into an object of <typeparamref name="T"/></param>
/// <param name="parameters">The parameters of the query</param>
/// <typeparam name="T">The return object type</typeparam>
/// <returns>An object of type T that represents the output of the convertor function based on the array of objects that the first row of the result has</returns>
public async Task<T?> ReadObjectOfTypeAsync<T>(string query, Func<object[], T> convertor, params KeyValuePair<string, object>[] parameters)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
foreach (var parameter in parameters)
{
var p = CreateParameter(parameter);
if (p is not null)
command.Parameters.Add(p);
}
var reader = await command.ExecuteReaderAsync();
var values = new object[reader.FieldCount];
if (reader.Read())
{
reader.GetValues(values);
return convertor(values);
}
return default;
}
/// <summary>
/// Execute a query with parameters that returns a specific type of object. The function will return a list of objects of the specified type.
/// </summary>
/// <param name="query">The query to execute</param>
/// <param name="convertor">The convertor from object[] to T</param>
/// <param name="parameters">The parameters of the query</param>
/// <typeparam name="T">The expected object type</typeparam>
/// <returns>A list of objects of type T that represents each line of the output of the specified query, converted to T</returns>
public async Task<List<T>> ReadListOfTypeAsync<T>(string query, Func<object[], T> convertor,
params KeyValuePair<string, object>[] parameters)
{
if (!_Connection.State.HasFlag(ConnectionState.Open))
await _Connection.OpenAsync();
var command = new SqliteCommand(query, _Connection);
foreach (var parameter in parameters)
{
var p = CreateParameter(parameter);
if (p is not null)
command.Parameters.Add(p);
}
var reader = await command.ExecuteReaderAsync();
//
if (!reader.HasRows)
return null;
List<T> rows = new();
while (await reader.ReadAsync())
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
rows.Add(convertor(values));
}
return rows;
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,10 @@
namespace DiscordBotCore.Logging;
public interface ILogMessage
{
public string Message { get; protected set; }
public DateTime ThrowTime { get; protected set; }
public string SenderName { get; protected set; }
public LogType LogMessageType { get; protected set; }
}

View File

@@ -0,0 +1,13 @@
namespace DiscordBotCore.Logging;
public interface ILogger
{
List<ILogMessage> LogMessages { get; protected set; }
event Action<ILogMessage>? OnLogReceived;
void Log(string message);
void Log(string message, LogType logType);
void Log(string message, object sender);
void Log(string message, object sender, LogType type);
void LogException(Exception exception, object sender, bool logFullStack = false);
}

View File

@@ -0,0 +1,75 @@
namespace DiscordBotCore.Logging;
internal sealed class LogMessage : ILogMessage
{
private static readonly string _DefaultLogMessageSender = "\b";
public string Message { get; set; }
public DateTime ThrowTime { get; set; }
public string SenderName { get; set; }
public LogType LogMessageType { get; set; }
public LogMessage(string message, LogType logMessageType)
{
Message = message;
LogMessageType = logMessageType;
ThrowTime = DateTime.Now;
SenderName = string.Empty;
}
public LogMessage(string message, object sender)
{
Message = message;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
ThrowTime = DateTime.Now;
LogMessageType = LogType.Info;
}
public LogMessage(string message, object sender, DateTime throwTime)
{
Message = message;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
ThrowTime = throwTime;
LogMessageType = LogType.Info;
}
public LogMessage(string message, object sender, LogType logMessageType)
{
Message = message;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
ThrowTime = DateTime.Now;
LogMessageType = logMessageType;
}
public LogMessage(string message, DateTime throwTime, object sender, LogType logMessageType)
{
Message = message;
ThrowTime = throwTime;
SenderName = sender is string && sender as string == string.Empty ? _DefaultLogMessageSender : sender.GetType().FullName ?? sender.GetType().Name;
LogMessageType = logMessageType;
}
public LogMessage WithMessage(string message)
{
this.Message = message;
return this;
}
public LogMessage WithCurrentThrowTime()
{
this.ThrowTime = DateTime.Now;
return this;
}
public LogMessage WithMessageType(LogType logType)
{
this.LogMessageType = logType;
return this;
}
public static LogMessage CreateFromException(Exception exception, object Sender, bool logFullStack)
{
LogMessage message = new LogMessage(logFullStack? exception.ToString() : exception.Message, Sender, LogType.Error);
return message;
}
}

View File

@@ -0,0 +1,9 @@
namespace DiscordBotCore.Logging;
public enum LogType
{
Info,
Warning,
Error,
Critical
}

View File

@@ -0,0 +1,62 @@
namespace DiscordBotCore.Logging;
public sealed class Logger : ILogger
{
private readonly string _LogFile;
private readonly string _LogMessageFormat;
private readonly int _MaxHistorySize;
private readonly List<string> _logMessageProperties = typeof(ILogMessage)
.GetProperties()
.Select(p => p.Name)
.ToList();
public List<ILogMessage> LogMessages { get; set; }
public event Action<ILogMessage>? OnLogReceived;
public Logger(string logFolder, string logMessageFormat, int maxHistorySize)
{
this._LogMessageFormat = logMessageFormat;
this._LogFile = Path.Combine(logFolder, $"{DateTime.Now:yyyy-MM-dd}.log");
this._MaxHistorySize = maxHistorySize;
LogMessages = new List<ILogMessage>();
}
private string GenerateLogMessage(ILogMessage message)
{
string messageAsString = new string(_LogMessageFormat);
foreach (var prop in _logMessageProperties)
{
Type messageType = typeof(ILogMessage);
messageAsString = messageAsString.Replace("{" + prop + "}", messageType.GetProperty(prop)?.GetValue(message)?.ToString());
}
return messageAsString;
}
private async Task LogToFile(string message)
{
await using var streamWriter = new StreamWriter(_LogFile, true);
await streamWriter.WriteLineAsync(message);
}
private async void Log(ILogMessage message)
{
var messageAsString = GenerateLogMessage(message);
OnLogReceived?.Invoke(message);
LogMessages.Add(message);
if (LogMessages.Count > _MaxHistorySize)
{
LogMessages.RemoveAt(0);
}
await LogToFile(messageAsString);
}
public void Log(string message) => Log(new LogMessage(message, string.Empty, LogType.Info));
public void Log(string message, LogType logType) => Log(new LogMessage(message, logType));
public void Log(string message, object sender) => Log(new LogMessage(message, sender));
public void Log(string message, object sender, LogType type) => Log(new LogMessage(message, sender, type));
public void LogException(Exception exception, object sender, bool logFullStack = false) => Log(LogMessage.CreateFromException(exception, sender, logFullStack));
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using DiscordBotCore.Networking.Helpers;
namespace DiscordBotCore.Networking;
public class FileDownloader
{
private readonly string _DownloadUrl;
private readonly string _DownloadLocation;
private readonly HttpClient _HttpClient;
public FileDownloader(string downloadUrl, string downloadLocation)
{
_DownloadUrl = downloadUrl;
_DownloadLocation = downloadLocation;
_HttpClient = new HttpClient();
}
public async Task DownloadFile(Action<float> progressCallback)
{
await using var fileStream = new FileStream(_DownloadLocation, FileMode.Create, FileAccess.Write, FileShare.None);
await _HttpClient.DownloadFileAsync(_DownloadUrl, fileStream, new Progress<float>(progressCallback));
}
}

View File

@@ -0,0 +1,91 @@
namespace DiscordBotCore.Networking.Helpers;
internal static class OnlineFunctions
{
/// <summary>
/// Copy one Stream to another <see langword="async" />
/// </summary>
/// <param name="stream">The base stream</param>
/// <param name="destination">The destination stream</param>
/// <param name="bufferSize">The buffer to read</param>
/// <param name="progress">The progress</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <exception cref="ArgumentNullException">Triggered if any <see cref="Stream" /> is empty</exception>
/// <exception cref="ArgumentOutOfRangeException">Triggered if <paramref name="bufferSize" /> is less then or equal to 0</exception>
/// <exception cref="InvalidOperationException">Triggered if <paramref name="stream" /> is not readable</exception>
/// <exception cref="ArgumentException">Triggered in <paramref name="destination" /> is not writable</exception>
private static async Task CopyToOtherStreamAsync(
this Stream stream, Stream destination, int bufferSize,
IProgress<long>? progress = null,
CancellationToken cancellationToken = default)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize));
if (!stream.CanRead) throw new InvalidOperationException("The stream is not readable.");
if (!destination.CanWrite)
throw new ArgumentException("Destination stream is not writable", nameof(destination));
var buffer = new byte[bufferSize];
long totalBytesRead = 0;
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
.ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
totalBytesRead += bytesRead;
progress?.Report(totalBytesRead);
}
}
/// <summary>
/// Downloads a <see cref="Stream" /> and saves it to another <see cref="Stream" />.
/// </summary>
/// <param name="client">The <see cref="HttpClient" /> that is used to download the file</param>
/// <param name="url">The url to the file</param>
/// <param name="destination">The <see cref="Stream" /> to save the downloaded data</param>
/// <param name="progress">The <see cref="IProgress{T}" /> that is used to track the download progress</param>
/// <param name="cancellation">The cancellation token</param>
/// <returns></returns>
internal static async Task DownloadFileAsync(
this HttpClient client, string url, Stream destination,
IProgress<float>? progress = null,
IProgress<long>? downloadedBytes = null, int bufferSize = 81920,
CancellationToken cancellation = default)
{
using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellation))
{
var contentLength = response.Content.Headers.ContentLength;
using (var download = await response.Content.ReadAsStreamAsync(cancellation))
{
// Ignore progress reporting when no progress reporter was
// passed or when the content length is unknown
if (progress == null || !contentLength.HasValue)
{
await download.CopyToAsync(destination, cancellation);
if (!contentLength.HasValue)
progress?.Report(100f);
return;
}
// Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
// total ... 100%
// downloaded ... x%
// x = downloaded * 100 / total => x = downloaded / total * 100
var relativeProgress = new Progress<long>(totalBytesDownloaded =>
{
progress?.Report(totalBytesDownloaded / (float)contentLength.Value * 100);
downloadedBytes?.Report(totalBytesDownloaded);
}
);
// Use extension method to report progress while downloading
await download.CopyToOtherStreamAsync(destination, bufferSize, relativeProgress, cancellation);
progress.Report(100f);
}
}
}
}

View File

@@ -0,0 +1,89 @@
using DiscordBotCore.Networking.Helpers;
namespace DiscordBotCore.Networking;
public class ParallelDownloadExecutor
{
private readonly List<Task> _listOfTasks;
private readonly HttpClient _httpClient;
private Action? OnFinishAction { get; set; }
public ParallelDownloadExecutor(List<Task> listOfTasks)
{
_httpClient = new HttpClient();
_listOfTasks = listOfTasks;
}
public ParallelDownloadExecutor()
{
_httpClient = new HttpClient();
_listOfTasks = new List<Task>();
}
public async Task StartTasks()
{
await Task.WhenAll(_listOfTasks);
OnFinishAction?.Invoke();
}
public async Task ExecuteAllTasks(int maxDegreeOfParallelism = 4)
{
using var semaphore = new SemaphoreSlim(maxDegreeOfParallelism);
var tasks = _listOfTasks.Select(async task =>
{
await semaphore.WaitAsync();
try
{
await task;
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks);
OnFinishAction?.Invoke();
}
public void SetFinishAction(Action action)
{
OnFinishAction = action;
}
public void AddTask(string downloadLink, string downloadLocation)
{
if (string.IsNullOrEmpty(downloadLink) || string.IsNullOrEmpty(downloadLocation))
throw new ArgumentException("Download link or location cannot be null or empty.");
if (Directory.Exists(Path.GetDirectoryName(downloadLocation)) == false)
{
Directory.CreateDirectory(Path.GetDirectoryName(downloadLocation));
}
var task = CreateDownloadTask(downloadLink, downloadLocation, null);
_listOfTasks.Add(task);
}
public void AddTask(string downloadLink, string downloadLocation, Action<float> progressCallback)
{
if (string.IsNullOrEmpty(downloadLink) || string.IsNullOrEmpty(downloadLocation))
throw new ArgumentException("Download link or location cannot be null or empty.");
if (Directory.Exists(Path.GetDirectoryName(downloadLocation)) == false)
{
Directory.CreateDirectory(Path.GetDirectoryName(downloadLocation));
}
var task = CreateDownloadTask(downloadLink, downloadLocation, new Progress<float>(progressCallback));
_listOfTasks.Add(task);
}
private Task CreateDownloadTask(string downloadLink, string downloadLocation, IProgress<float> progress)
{
var fileStream = new FileStream(downloadLocation, FileMode.Create, FileAccess.Write, FileShare.None);
return _httpClient.DownloadFileAsync(downloadLink, fileStream, progress);
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.17.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DiscordBotCore.Logging\DiscordBotCore.Logging.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Helpers\Execution\DbSlashCommand\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,26 @@
using Discord.Commands;
using Discord.WebSocket;
using DiscordBotCore.Logging;
namespace DiscordBotCore.PluginCore.Helpers.Execution.DbCommand;
public class DbCommandExecutingArgument : IDbCommandExecutingArgument
{
public SocketCommandContext Context { get; init; }
public string CleanContent { get; init; }
public string CommandUsed { get; init; }
public string[]? Arguments { get; init; }
public ILogger Logger { get; init; }
public DirectoryInfo PluginBaseDirectory { get; init; }
public DbCommandExecutingArgument(ILogger logger, SocketCommandContext context, string cleanContent, string commandUsed, string[]? arguments, DirectoryInfo pluginBaseDirectory)
{
this.Logger = logger;
this.Context = context;
this.CleanContent = cleanContent;
this.CommandUsed = commandUsed;
this.Arguments = arguments;
this.PluginBaseDirectory = pluginBaseDirectory;
}
}

View File

@@ -0,0 +1,16 @@
using Discord.Commands;
using Discord.WebSocket;
using DiscordBotCore.Logging;
namespace DiscordBotCore.PluginCore.Helpers.Execution.DbCommand;
public interface IDbCommandExecutingArgument
{
ILogger Logger { get; init; }
string CleanContent { get; init; }
string CommandUsed { get; init; }
string[]? Arguments { get; init; }
SocketCommandContext Context { get; init; }
public DirectoryInfo PluginBaseDirectory { get; init; }
}

View File

@@ -0,0 +1,20 @@
using Discord.WebSocket;
using DiscordBotCore.Logging;
namespace DiscordBotCore.PluginCore.Helpers.Execution.DbEvent;
public class DbEventExecutingArgument : IDbEventExecutingArgument
{
public ILogger Logger { get; }
public DiscordSocketClient Client { get; }
public string BotPrefix { get; }
public DirectoryInfo PluginBaseDirectory { get; }
public DbEventExecutingArgument(ILogger logger, DiscordSocketClient client, string botPrefix, DirectoryInfo pluginBaseDirectory)
{
Logger = logger;
Client = client;
BotPrefix = botPrefix;
PluginBaseDirectory = pluginBaseDirectory;
}
}

View File

@@ -0,0 +1,12 @@
using Discord.WebSocket;
using DiscordBotCore.Logging;
namespace DiscordBotCore.PluginCore.Helpers.Execution.DbEvent;
public interface IDbEventExecutingArgument
{
public ILogger Logger { get; }
public DiscordSocketClient Client { get; }
public string BotPrefix { get; }
public DirectoryInfo PluginBaseDirectory { get; }
}

View File

@@ -0,0 +1,45 @@
using DiscordBotCore.PluginCore.Helpers.Execution.DbCommand;
namespace DiscordBotCore.PluginCore.Interfaces;
public interface IDbCommand
{
/// <summary>
/// Command to be executed
/// It's CaSe SeNsItIvE
/// </summary>
string Command { get; }
/// <summary>
/// Command aliases. Users may use this to execute the command
/// </summary>
List<string> Aliases { get; }
/// <summary>
/// Command description
/// </summary>
string Description { get; }
/// <summary>
/// The usage for your command.
/// It will be displayed when users type help
/// </summary>
string Usage { get; }
/// <summary>
/// true if the command requre admin, otherwise false
/// </summary>
bool RequireAdmin { get; }
/// <summary>
/// The main body of the command. This is what is executed when user calls the command in Server
/// </summary>
/// <param name="args">The Discord Context</param>
Task ExecuteServer(IDbCommandExecutingArgument args) => Task.CompletedTask;
/// <summary>
/// The main body of the command. This is what is executed when user calls the command in DM
/// </summary>
/// <param name="args">The Discord Context</param>
Task ExecuteDm(IDbCommandExecutingArgument args) => Task.CompletedTask;
}

View File

@@ -0,0 +1,22 @@
using DiscordBotCore.PluginCore.Helpers.Execution.DbEvent;
namespace DiscordBotCore.PluginCore.Interfaces;
public interface IDbEvent
{
/// <summary>
/// The name of the event
/// </summary>
string Name { get; }
/// <summary>
/// The description of the event
/// </summary>
string Description { get; }
/// <summary>
/// The method that is invoked when the event is loaded into memory
/// </summary>
/// <param name="args">The arguments for the start method</param>
void Start(IDbEventExecutingArgument args);
}

View File

@@ -0,0 +1,22 @@
using Discord;
using Discord.WebSocket;
using DiscordBotCore.Logging;
namespace DiscordBotCore.PluginCore.Interfaces;
public interface IDbSlashCommand
{
string Name { get; }
string Description { get; }
bool CanUseDm { get; }
bool HasInteraction { get; }
List<SlashCommandOptionBuilder> Options { get; }
void ExecuteServer(ILogger logger, SocketSlashCommand context)
{ }
void ExecuteDm(ILogger logger, SocketSlashCommand context) { }
Task ExecuteInteraction(ILogger logger, SocketInteraction interaction) => Task.CompletedTask;
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DiscordBotCore.Configuration\DiscordBotCore.Configuration.csproj" />
<ProjectReference Include="..\DiscordBotCore.Logging\DiscordBotCore.Logging.csproj" />
<ProjectReference Include="..\DiscordBotCore.PluginCore\DiscordBotCore.PluginCore.csproj" />
<ProjectReference Include="..\DiscordBotCore.PluginManagement\DiscordBotCore.PluginManagement.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
namespace DiscordBotCore.PluginManagement.Loading.Exceptions;
public class PluginNotFoundException : Exception
{
public PluginNotFoundException(string pluginName) : base($"Plugin {pluginName} was not found") { }
public PluginNotFoundException(string pluginName, string url, string branch) :
base ($"Plugin {pluginName} was not found on {url} (branch: {branch}") { }
}

View File

@@ -0,0 +1,27 @@
using Discord.WebSocket;
using DiscordBotCore.PluginCore.Interfaces;
namespace DiscordBotCore.PluginManagement.Loading;
public interface IPluginLoader
{
public IReadOnlyList<IDbCommand> Commands { get; }
public IReadOnlyList<IDbEvent> Events { get; }
public IReadOnlyList<IDbSlashCommand> SlashCommands { get; }
/// <summary>
/// Sets the Discord client for the plugin loader. This is used to initialize the slash commands and events.
/// </summary>
/// <param name="discordSocketClient">The socket client that represents the running Discord Bot</param>
public void SetDiscordClient(DiscordSocketClient discordSocketClient);
/// <summary>
/// Loads all the plugins that are installed.
/// </summary>
public Task LoadPlugins();
/// <summary>
/// Unload all plugins from the plugin manager.
/// </summary>
public Task UnloadAllPlugins();
}

View File

@@ -0,0 +1,371 @@
using System.Collections.ObjectModel;
using System.Reflection;
using Discord;
using Discord.WebSocket;
using DiscordBotCore.Configuration;
using DiscordBotCore.Logging;
using DiscordBotCore.PluginCore.Helpers.Execution.DbEvent;
using DiscordBotCore.PluginCore.Interfaces;
using DiscordBotCore.PluginManagement.Loading.Exceptions;
using DiscordBotCore.Utilities.Responses;
namespace DiscordBotCore.PluginManagement.Loading;
public class PluginLoader : IPluginLoader
{
private static readonly string _HelpCommandNamespaceFullName = "DiscordBotCore.Commands.HelpCommand";
private readonly IPluginManager _PluginManager;
private readonly ILogger _Logger;
private readonly IConfiguration _Configuration;
private DiscordSocketClient? _DiscordClient;
private PluginLoaderContext? PluginLoaderContext;
private readonly List<IDbCommand> _Commands = new List<IDbCommand>();
private readonly List<IDbEvent> _Events = new List<IDbEvent>();
private readonly List<IDbSlashCommand> _SlashCommands = new List<IDbSlashCommand>();
private readonly List<SocketApplicationCommand> _ApplicationCommands = new List<SocketApplicationCommand>();
public PluginLoader(IPluginManager pluginManager, ILogger logger, IConfiguration configuration)
{
_PluginManager = pluginManager;
_Logger = logger;
_Configuration = configuration;
}
public IReadOnlyList<IDbCommand> Commands => _Commands;
public IReadOnlyList<IDbEvent> Events => _Events;
public IReadOnlyList<IDbSlashCommand> SlashCommands => _SlashCommands;
public void SetDiscordClient(DiscordSocketClient discordSocketClient)
{
if (_DiscordClient is not null && discordSocketClient == _DiscordClient)
{
_Logger.Log("A client is already set. Please set the client only once.", this, LogType.Warning);
return;
}
if (discordSocketClient.LoginState != LoginState.LoggedIn)
{
_Logger.Log("The client must be logged in before setting it.", this, LogType.Error);
return;
}
_DiscordClient = discordSocketClient;
}
public async Task LoadPlugins()
{
if (PluginLoaderContext is not null)
{
_Logger.Log("The plugins are already loaded", this, LogType.Error);
return;
}
_Events.Clear();
_Commands.Clear();
_SlashCommands.Clear();
_ApplicationCommands.Clear();
await LoadPluginFiles();
LoadEverythingOfType<IDbEvent>();
var helpCommand = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(assembly => assembly.DefinedTypes.Any(type => type.FullName == _HelpCommandNamespaceFullName)
&& assembly.FullName != null
&& assembly.FullName.StartsWith("DiscordBotCore"));
if (helpCommand is not null)
{
var helpCommandType = helpCommand.DefinedTypes.FirstOrDefault(type => type.FullName == _HelpCommandNamespaceFullName &&
typeof(IDbCommand).IsAssignableFrom(type));
if (helpCommandType is not null)
{
InitializeType<IDbCommand>(helpCommandType);
}
}
LoadEverythingOfType<IDbCommand>();
LoadEverythingOfType<IDbSlashCommand>();
_Logger.Log("Loaded plugins", this);
}
public async Task UnloadAllPlugins()
{
if (PluginLoaderContext is null)
{
_Logger.Log("The plugins are not loaded. Please load the plugins before unloading them.", this, LogType.Error);
return;
}
await UnloadSlashCommands();
PluginLoaderContext.Unload();
PluginLoaderContext = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
private async Task UnloadSlashCommands()
{
if (_DiscordClient is null)
{
_Logger.Log("The client is not set. Please set the client before unloading slash commands.", this, LogType.Error);
return;
}
foreach (SocketApplicationCommand command in _ApplicationCommands)
{
await command.DeleteAsync();
}
_ApplicationCommands.Clear();
_Logger.Log("Unloaded all slash commands", this);
}
private async Task LoadPluginFiles()
{
var installedPlugins = await _PluginManager.GetInstalledPlugins();
if (installedPlugins.Count == 0)
{
_Logger.Log("No plugin files found. Please check the plugin files.", this, LogType.Error);
return;
}
var files = installedPlugins.Where(plugin => plugin.IsEnabled).Select(plugin => plugin.FilePath);
PluginLoaderContext = new PluginLoaderContext(_Logger, "PluginLoader");
foreach (var file in files)
{
string fullFilePath = Path.GetFullPath(file);
if (string.IsNullOrEmpty(fullFilePath))
{
_Logger.Log("The file path is empty. Please check the plugin file path.", PluginLoaderContext, LogType.Error);
continue;
}
if (!File.Exists(fullFilePath))
{
_Logger.Log("The file does not exist. Please check the plugin file path.", PluginLoaderContext, LogType.Error);
continue;
}
try
{
PluginLoaderContext.LoadFromAssemblyPath(fullFilePath);
}
catch (Exception ex)
{
_Logger.LogException(ex, this);
}
}
_Logger.Log($"Loaded {PluginLoaderContext.Assemblies.Count()} assemblies", this);
}
private void LoadEverythingOfType<T>()
{
if (PluginLoaderContext is null)
{
_Logger.Log("The plugins are not loaded. Please load the plugins before loading them.", this, LogType.Error);
return;
}
var types = PluginLoaderContext.Assemblies
.SelectMany(s => s.GetTypes())
.Where(p => typeof(T).IsAssignableFrom(p) && !p.IsInterface);
foreach (var type in types)
{
InitializeType<T>(type);
}
}
private void InitializeType<T>(Type type)
{
T? plugin = (T?)Activator.CreateInstance(type);
if (plugin is null)
{
_Logger.Log($"Failed to create instance of plugin with type {type.FullName} [{type.Assembly}]", this, LogType.Error);
}
switch (plugin)
{
case IDbEvent dbEvent:
InitializeEvent(dbEvent);
break;
case IDbCommand dbCommand:
InitializeDbCommand(dbCommand);
break;
case IDbSlashCommand dbSlashCommand:
InitializeSlashCommand(dbSlashCommand);
break;
default:
throw new PluginNotFoundException($"Unknown plugin type {plugin.GetType().FullName}");
}
}
private void InitializeDbCommand(IDbCommand command)
{
_Commands.Add(command);
_Logger.Log("Command loaded: " + command.Command, this);
}
private void InitializeEvent(IDbEvent eEvent)
{
if (!TryStartEvent(eEvent))
{
return;
}
_Events.Add(eEvent);
_Logger.Log("Event loaded: " + eEvent, this);
}
private async void InitializeSlashCommand(IDbSlashCommand slashCommand)
{
bool result = await TryStartSlashCommand(slashCommand);
if (!result)
{
return;
}
if (_DiscordClient is null)
{
return;
}
if (slashCommand.HasInteraction)
{
_DiscordClient.InteractionCreated += interaction => slashCommand.ExecuteInteraction(_Logger, interaction);
}
_SlashCommands.Add(slashCommand);
_Logger.Log("Slash command loaded: " + slashCommand.Name, this);
}
private bool TryStartEvent(IDbEvent dbEvent)
{
string? botPrefix = _Configuration.Get<string>("prefix");
if (string.IsNullOrEmpty(botPrefix))
{
_Logger.Log("Bot prefix is not set. Please set the bot prefix in the configuration.", this, LogType.Error);
return false;
}
if (_DiscordClient is null)
{
_Logger.Log("Discord client is not set. Please set the discord client before starting events.", this, LogType.Error);
return false;
}
string? resourcesFolder = _Configuration.Get<string>("ResourcesFolder");
if (string.IsNullOrEmpty(resourcesFolder))
{
_Logger.Log("Resources folder is not set. Please set the resources folder in the configuration.", this, LogType.Error);
return false;
}
if (!Directory.Exists(resourcesFolder))
{
_Logger.Log("Resources folder does not exist. Please create the resources folder.", this, LogType.Error);
return false;
}
string? eventConfigDirectory = Path.Combine(resourcesFolder, dbEvent.GetType().Assembly.GetName().Name);
Directory.CreateDirectory(eventConfigDirectory);
IDbEventExecutingArgument args = new DbEventExecutingArgument(
_Logger,
_DiscordClient,
botPrefix,
new DirectoryInfo(eventConfigDirectory));
dbEvent.Start(args);
return true;
}
private async Task<bool> TryStartSlashCommand(IDbSlashCommand? dbSlashCommand)
{
if (dbSlashCommand is null)
{
_Logger.Log("The loaded slash command was null. Please check the plugin.", this, LogType.Error);
return false;
}
if (_DiscordClient is null)
{
_Logger.Log("The client is not set. Please set the client before starting slash commands.", this, LogType.Error);
return false;
}
if (_DiscordClient.Guilds.Count == 0)
{
_Logger.Log("The client is not connected to any guilds. Please check the client.", this, LogType.Error);
return false;
}
var builder = new SlashCommandBuilder();
builder.WithName(dbSlashCommand.Name);
builder.WithDescription(dbSlashCommand.Description);
builder.Options = dbSlashCommand.Options;
if (dbSlashCommand.CanUseDm)
builder.WithContextTypes(InteractionContextType.BotDm, InteractionContextType.Guild);
else
builder.WithContextTypes(InteractionContextType.Guild);
List<ulong> serverIds = _Configuration.GetList("ServerIds", new List<ulong>());
if (serverIds.Any())
{
foreach(ulong guildId in serverIds)
{
IResponse<SocketApplicationCommand> result = await EnableSlashCommandPerGuild(guildId, builder);
if (!result.IsSuccess)
{
_Logger.Log($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}", this, LogType.Error);
continue;
}
if (result.Data is null)
{
continue;
}
_ApplicationCommands.Add(result.Data);
}
return true;
}
var command = await _DiscordClient.CreateGlobalApplicationCommandAsync(builder.Build());
_ApplicationCommands.Add(command);
return true;
}
private async Task<IResponse<SocketApplicationCommand>> EnableSlashCommandPerGuild(ulong guildId, SlashCommandBuilder builder)
{
SocketGuild? guild = _DiscordClient?.GetGuild(guildId);
if (guild is null)
{
_Logger.Log("Failed to get guild with ID " + guildId, this, LogType.Error);
return Response<SocketApplicationCommand>.Failure("Failed to get guild with ID " + guildId);
}
var command = await guild.CreateApplicationCommandAsync(builder.Build());
return Response<SocketApplicationCommand>.Success(command);
}
}

View File

@@ -0,0 +1,21 @@
using System.Reflection;
using System.Runtime.Loader;
using DiscordBotCore.Logging;
namespace DiscordBotCore.PluginManagement.Loading;
public class PluginLoaderContext : AssemblyLoadContext
{
private readonly ILogger _logger;
public PluginLoaderContext(ILogger logger, string name) : base(name: name, isCollectible: true)
{
_logger = logger;
}
protected override Assembly? Load(AssemblyName assemblyName)
{
//_logger.Log("Assembly load requested: " + assemblyName.Name, this);
return base.Load(assemblyName);
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DiscordBotCore.Configuration\DiscordBotCore.Configuration.csproj" />
<ProjectReference Include="..\DiscordBotCore.Logging\DiscordBotCore.Logging.csproj" />
<ProjectReference Include="..\DiscordBotCore.Networking\DiscordBotCore.Networking.csproj" />
<ProjectReference Include="..\DiscordBotCore.Utilities\DiscordBotCore.Utilities.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using DiscordBotCore.PluginManagement.Models;
namespace DiscordBotCore.PluginManagement.Helpers;
public interface IPluginRepository
{
public Task<List<OnlinePlugin>> GetAllPlugins(int operatingSystem, bool includeNotApproved);
public Task<OnlinePlugin?> GetPluginById(int pluginId);
public Task<OnlinePlugin?> GetPluginByName(string pluginName, int operatingSystem, bool includeNotApproved);
public Task<List<OnlineDependencyInfo>> GetDependenciesForPlugin(int pluginId);
}

View File

@@ -0,0 +1,9 @@
namespace DiscordBotCore.PluginManagement.Helpers;
public interface IPluginRepositoryConfiguration
{
public string BaseUrl { get; }
public string PluginRepositoryLocation { get; }
public string DependenciesRepositoryLocation { get; }
}

View File

@@ -0,0 +1,161 @@
using System.Net.Mime;
using DiscordBotCore.Logging;
using DiscordBotCore.PluginManagement.Models;
using DiscordBotCore.Utilities;
using Microsoft.AspNetCore.Http.Extensions;
namespace DiscordBotCore.PluginManagement.Helpers;
public class PluginRepository : IPluginRepository
{
private readonly IPluginRepositoryConfiguration _pluginRepositoryConfiguration;
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public PluginRepository(IPluginRepositoryConfiguration pluginRepositoryConfiguration, ILogger logger)
{
_pluginRepositoryConfiguration = pluginRepositoryConfiguration;
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(_pluginRepositoryConfiguration.BaseUrl);
_logger = logger;
}
public async Task<List<OnlinePlugin>> GetAllPlugins(int operatingSystem, bool includeNotApproved)
{
string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.PluginRepositoryLocation,
"get-all-plugins", new Dictionary<string, string>
{
{ "operatingSystem", operatingSystem.ToString() },
{ "includeNotApproved", includeNotApproved.ToString() }
});
try
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
return [];
}
string content = await response.Content.ReadAsStringAsync();
List<OnlinePlugin> plugins = await JsonManager.ConvertFromJson<List<OnlinePlugin>>(content);
return plugins;
}
catch (HttpRequestException exception)
{
_logger.LogException(exception,this);
return [];
}
}
public async Task<OnlinePlugin?> GetPluginById(int pluginId)
{
string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.PluginRepositoryLocation,
"get-by-id", new Dictionary<string, string>
{
{ "pluginId", pluginId.ToString() },
{ "includeNotApproved", "false" }
});
try
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
return null;
}
string content = await response.Content.ReadAsStringAsync();
OnlinePlugin plugin = await JsonManager.ConvertFromJson<OnlinePlugin>(content);
return plugin;
}
catch (HttpRequestException exception)
{
_logger.LogException(exception, this);
return null;
}
}
public async Task<OnlinePlugin?> GetPluginByName(string pluginName, int operatingSystem, bool includeNotApproved)
{
string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.PluginRepositoryLocation,
"get-by-name", new Dictionary<string, string>
{
{ "pluginName", pluginName },
{ "operatingSystem", operatingSystem.ToString() },
{ "includeNotApproved", includeNotApproved.ToString() }
});
try
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
_logger.Log($"Plugin {pluginName} not found");
return null;
}
string content = await response.Content.ReadAsStringAsync();
OnlinePlugin plugin = await JsonManager.ConvertFromJson<OnlinePlugin>(content);
return plugin;
}
catch (HttpRequestException exception)
{
_logger.LogException(exception, this);
return null;
}
}
public async Task<List<OnlineDependencyInfo>> GetDependenciesForPlugin(int pluginId)
{
string url = CreateUrlWithQueryParams(_pluginRepositoryConfiguration.DependenciesRepositoryLocation,
"get-by-plugin-id", new Dictionary<string, string>
{
{ "pluginId", pluginId.ToString() }
});
try
{
HttpResponseMessage response = await _httpClient.GetAsync(url);
if(!response.IsSuccessStatusCode)
{
_logger.Log($"Failed to get dependencies for plugin with ID {pluginId}");
return [];
}
string content = await response.Content.ReadAsStringAsync();
List<OnlineDependencyInfo> dependencies = await JsonManager.ConvertFromJson<List<OnlineDependencyInfo>>(content);
return dependencies;
}
catch(HttpRequestException exception)
{
_logger.LogException(exception, this);
return [];
}
}
private string CreateUrlWithQueryParams(string baseUrl, string endpoint, Dictionary<string, string> queryParams)
{
QueryBuilder queryBuilder = new QueryBuilder();
foreach (var(key,value) in queryParams)
{
queryBuilder.Add(key, value);
}
string queryString = queryBuilder.ToQueryString().ToString();
string url = baseUrl + endpoint + queryString;
return url;
}
}

View File

@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
namespace DiscordBotCore.PluginManagement.Helpers;
public class PluginRepositoryConfiguration : IPluginRepositoryConfiguration
{
public static PluginRepositoryConfiguration Default => new ("http://localhost:8080/api/v1/",
"plugin/",
"dependency/");
public string BaseUrl { get; }
public string PluginRepositoryLocation { get; }
public string DependenciesRepositoryLocation { get; }
[JsonConstructor]
public PluginRepositoryConfiguration(string baseUrl, string pluginRepositoryLocation, string dependenciesRepositoryLocation)
{
BaseUrl = baseUrl;
PluginRepositoryLocation = pluginRepositoryLocation;
DependenciesRepositoryLocation = dependenciesRepositoryLocation;
}
}

View File

@@ -0,0 +1,20 @@
using DiscordBotCore.PluginManagement.Models;
using DiscordBotCore.Utilities.Responses;
namespace DiscordBotCore.PluginManagement;
public interface IPluginManager
{
Task<List<OnlinePlugin>> GetPluginsList();
Task<IResponse<OnlinePlugin>> GetPluginDataByName(string pluginName);
Task<IResponse<OnlinePlugin>> GetPluginDataById(int pluginId);
Task<IResponse<bool>> AppendPluginToDatabase(LocalPlugin pluginData);
Task<List<LocalPlugin>> GetInstalledPlugins();
Task<IResponse<string>> GetDependencyLocation(string dependencyName);
Task<IResponse<string>> GetDependencyLocation(string dependencyName, string pluginName);
string GenerateDependencyRelativePath(string pluginName, string dependencyPath);
Task<IResponse<bool>> InstallPlugin(OnlinePlugin plugin, IProgress<float> progress);
Task SetEnabledStatus(string pluginName, bool status);
Task<IResponse<bool>> UninstallPluginByName(string pluginName);
Task<IResponse<LocalPlugin>> GetLocalPluginByName(string pluginName);
}

View File

@@ -0,0 +1,46 @@
using System.Text.Json.Serialization;
namespace DiscordBotCore.PluginManagement.Models;
public class LocalPlugin
{
public string PluginName { get; private set; }
public string PluginVersion { get; private set; }
public string FilePath { get; private set; }
public Dictionary<string, string> ListOfExecutableDependencies {get; private set;}
public bool IsOfflineAdded { get; internal set; }
public bool IsEnabled { get; internal set; }
[JsonConstructor]
public LocalPlugin(string pluginName, string pluginVersion, string filePath, Dictionary<string, string> listOfExecutableDependencies, bool isOfflineAdded, bool isEnabled)
{
PluginName = pluginName;
PluginVersion = pluginVersion;
ListOfExecutableDependencies = listOfExecutableDependencies;
FilePath = filePath;
IsOfflineAdded = isOfflineAdded;
IsEnabled = isEnabled;
}
private LocalPlugin(string pluginName, string pluginVersion, string filePath,
Dictionary<string, string> listOfExecutableDependencies)
{
PluginName = pluginName;
PluginVersion = pluginVersion;
ListOfExecutableDependencies = listOfExecutableDependencies;
FilePath = filePath;
IsOfflineAdded = false;
IsEnabled = true;
}
public static LocalPlugin FromOnlineInfo(OnlinePlugin plugin, List<OnlineDependencyInfo> dependencies, string downloadLocation)
{
LocalPlugin localPlugin = new LocalPlugin(
plugin.Name, plugin.Version, downloadLocation,
dependencies.Where(dependency => dependency.IsExecutable)
.ToDictionary(dependency => dependency.DependencyName, dependency => dependency.DownloadLocation)
);
return localPlugin;
}
}

View File

@@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
namespace DiscordBotCore.PluginManagement.Models;
public class OnlineDependencyInfo
{
public string DependencyName { get; private set; }
[JsonPropertyName("dependencyLink")]
public string DownloadLink { get; private set; }
[JsonPropertyName("dependencyLocation")]
public string DownloadLocation { get; private set; }
public bool IsExecutable { get; private set; }
[JsonConstructor]
public OnlineDependencyInfo(string dependencyName, string downloadLink, string downloadLocation, bool isExecutable)
{
DependencyName = dependencyName;
DownloadLink = downloadLink;
DownloadLocation = downloadLocation;
IsExecutable = isExecutable;
}
}

View File

@@ -0,0 +1,29 @@
using System.Text.Json.Serialization;
namespace DiscordBotCore.PluginManagement.Models;
public class OnlinePlugin
{
public int Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public string Version { get; private set; }
public string Author { get; private set; }
public string DownloadLink { get; private set; }
public int OperatingSystem { get; private set; }
public bool IsApproved { get; private set; }
[JsonConstructor]
public OnlinePlugin(int id, string name, string description, string version,
string author, string downloadLink, int operatingSystem, bool isApproved)
{
Id = id;
Name = name;
Description = description;
Version = version;
Author = author;
DownloadLink = downloadLink;
OperatingSystem = operatingSystem;
IsApproved = isApproved;
}
}

View File

@@ -0,0 +1,316 @@
using System.Diagnostics;
using DiscordBotCore.Logging;
using DiscordBotCore.Networking;
using DiscordBotCore.PluginManagement.Helpers;
using DiscordBotCore.PluginManagement.Models;
using DiscordBotCore.Utilities;
using DiscordBotCore.Configuration;
using DiscordBotCore.Utilities.Responses;
using OperatingSystem = DiscordBotCore.Utilities.OperatingSystem;
namespace DiscordBotCore.PluginManagement;
public sealed class PluginManager : IPluginManager
{
private static readonly string _LibrariesBaseFolder = "Libraries";
private readonly IPluginRepository _PluginRepository;
private readonly ILogger _Logger;
private readonly IConfiguration _Configuration;
public PluginManager(IPluginRepository pluginRepository, ILogger logger, IConfiguration configuration)
{
_PluginRepository = pluginRepository;
_Logger = logger;
_Configuration = configuration;
}
public async Task<List<OnlinePlugin>> GetPluginsList()
{
int os = OperatingSystem.GetOperatingSystemInt();
var onlinePlugins = await _PluginRepository.GetAllPlugins(os, false);
if (!onlinePlugins.Any())
{
_Logger.Log($"No plugins found for operatingSystem: {OperatingSystem.GetOperatingSystemString((OperatingSystem.OperatingSystemEnum)os)}", LogType.Warning);
return [];
}
return onlinePlugins;
}
public async Task<IResponse<OnlinePlugin>> GetPluginDataByName(string pluginName)
{
int os = OperatingSystem.GetOperatingSystemInt();
var plugin = await _PluginRepository.GetPluginByName(pluginName, os, false);
if (plugin is null)
{
return Response<OnlinePlugin>.Failure($"Plugin {pluginName} not found in the repository for operating system {OperatingSystem.GetOperatingSystemString((OperatingSystem.OperatingSystemEnum)os)}.");
}
return Response<OnlinePlugin>.Success(plugin);
}
public async Task<IResponse<OnlinePlugin>> GetPluginDataById(int pluginId)
{
var plugin = await _PluginRepository.GetPluginById(pluginId);
if (plugin is null)
{
return Response<OnlinePlugin>.Failure($"Plugin {pluginId} not found in the repository.");
}
return Response<OnlinePlugin>.Success(plugin);
}
private async Task<IResponse<bool>> RemovePluginFromDatabase(string pluginName)
{
string? pluginDatabaseFile = _Configuration.Get<string>("PluginDatabase");
if (pluginDatabaseFile is null)
{
return Response.Failure("PluginDatabase file path is not present in the config file");
}
List<LocalPlugin> installedPlugins = await JsonManager.ConvertFromJson<List<LocalPlugin>>(await File.ReadAllTextAsync(pluginDatabaseFile));
installedPlugins.RemoveAll(p => p.PluginName == pluginName);
await JsonManager.SaveToJsonFile(pluginDatabaseFile, installedPlugins);
return Response.Success();
}
public async Task<IResponse<bool>> AppendPluginToDatabase(LocalPlugin pluginData)
{
string? pluginDatabaseFile = _Configuration.Get<string>("PluginDatabase");
if (pluginDatabaseFile is null)
{
return Response.Failure("PluginDatabase file path is not present in the config file");
}
List<LocalPlugin> installedPlugins = await GetInstalledPlugins();
foreach (var dependency in pluginData.ListOfExecutableDependencies)
{
pluginData.ListOfExecutableDependencies[dependency.Key] = dependency.Value;
}
if (installedPlugins.Any(plugin => plugin.PluginName == pluginData.PluginName))
{
_Logger.Log($"Plugin {pluginData.PluginName} already exists in the database. Updating...", this, LogType.Info);
installedPlugins.RemoveAll(p => p.PluginName == pluginData.PluginName);
}
installedPlugins.Add(pluginData);
await JsonManager.SaveToJsonFile(pluginDatabaseFile, installedPlugins);
return Response.Success();
}
public async Task<List<LocalPlugin>> GetInstalledPlugins()
{
string? pluginDatabaseFile = _Configuration.Get<string>("PluginDatabase");
if (pluginDatabaseFile is null)
{
_Logger.Log("Plugin database file path is not present in the config file", this, LogType.Warning);
return [];
}
if (!File.Exists(pluginDatabaseFile))
{
_Logger.Log("Plugin database file not found", this, LogType.Warning);
await CreateEmptyPluginDatabase();
return [];
}
return await JsonManager.ConvertFromJson<List<LocalPlugin>>(await File.ReadAllTextAsync(pluginDatabaseFile));
}
public async Task<IResponse<string>> GetDependencyLocation(string dependencyName)
{
List<LocalPlugin> installedPlugins = await GetInstalledPlugins();
foreach (var plugin in installedPlugins)
{
if (plugin.ListOfExecutableDependencies.TryGetValue(dependencyName, out var dependencyPath))
{
string relativePath = GenerateDependencyRelativePath(plugin.PluginName, dependencyPath);
return Response<string>.Success(relativePath);
}
}
return Response<string>.Failure($"Dependency {dependencyName} not found in the installed plugins.");
}
public async Task<IResponse<string>> GetDependencyLocation(string dependencyName, string pluginName)
{
List<LocalPlugin> installedPlugins = await GetInstalledPlugins();
foreach (var plugin in installedPlugins)
{
if (plugin.PluginName == pluginName && plugin.ListOfExecutableDependencies.ContainsKey(dependencyName))
{
string dependencyPath = plugin.ListOfExecutableDependencies[dependencyName];
string relativePath = GenerateDependencyRelativePath(pluginName, dependencyPath);
return Response<string>.Success(relativePath);
}
}
return Response<string>.Failure($"Dependency {dependencyName} not found in the installed plugins.");
}
public string GenerateDependencyRelativePath(string pluginName, string dependencyPath)
{
string relative = $"./{_LibrariesBaseFolder}/{pluginName}/{dependencyPath}";
return relative;
}
public async Task<IResponse<bool>> InstallPlugin(OnlinePlugin plugin, IProgress<float> progress)
{
string? pluginsFolder = _Configuration.Get<string>("PluginFolder");
if (pluginsFolder is null)
{
return Response.Failure("Plugin folder path is not present in the config file");
}
var localPluginResponse = await GetLocalPluginByName(plugin.Name);
if (localPluginResponse is { IsSuccess: true, Data: not null })
{
var response = await IsNewVersion(localPluginResponse.Data.PluginVersion, plugin.Version);
if (!response.IsSuccess)
{
return response;
}
}
List<OnlineDependencyInfo> dependencies = await _PluginRepository.GetDependenciesForPlugin(plugin.Id);
string downloadLocation = $"{pluginsFolder}/{plugin.Name}.dll";
IProgress<float> downloadProgress = new Progress<float>(progress.Report);
FileDownloader fileDownloader = new FileDownloader(plugin.DownloadLink, downloadLocation);
await fileDownloader.DownloadFile(downloadProgress.Report);
ParallelDownloadExecutor executor = new ParallelDownloadExecutor();
foreach (var dependency in dependencies)
{
string dependencyLocation = GenerateDependencyRelativePath(plugin.Name, dependency.DownloadLocation);
executor.AddTask(dependency.DownloadLink, dependencyLocation, progress.Report);
}
await executor.ExecuteAllTasks();
LocalPlugin localPlugin = LocalPlugin.FromOnlineInfo(plugin, dependencies, downloadLocation);
var result = await AppendPluginToDatabase(localPlugin);
return result;
}
public async Task SetEnabledStatus(string pluginName, bool status)
{
var plugins = await GetInstalledPlugins();
var plugin = plugins.Find(p => p.PluginName == pluginName);
if (plugin == null)
return;
plugin.IsEnabled = status;
await RemovePluginFromDatabase(pluginName);
await AppendPluginToDatabase(plugin);
}
public async Task<IResponse<bool>> UninstallPluginByName(string pluginName)
{
var localPluginResponse = await GetLocalPluginByName(pluginName);
if (!localPluginResponse.IsSuccess)
{
return Response.Failure(localPluginResponse.Message);
}
var localPlugin = localPluginResponse.Data;
if (localPlugin is null)
{
return Response.Failure($"Plugin {pluginName} not found in the database");
}
File.Delete(localPlugin.FilePath);
if (Directory.Exists($"./{_LibrariesBaseFolder}/{pluginName}"))
{
foreach (var file in Directory.EnumerateFiles($"./{_LibrariesBaseFolder}/{pluginName}"))
{
File.Delete(file);
}
}
var response = await RemovePluginFromDatabase(pluginName);
return response;
}
public async Task<IResponse<LocalPlugin>> GetLocalPluginByName(string pluginName)
{
List<LocalPlugin> installedPlugins = await GetInstalledPlugins();
var plugin = installedPlugins.Find(p => p.PluginName == pluginName);
if (plugin is null)
{
return Response<LocalPlugin>.Failure($"Plugin {pluginName} not found in the database");
}
return Response<LocalPlugin>.Success(plugin);
}
private async Task<IResponse<bool>> IsNewVersion(string currentVersion, string newVersion)
{
// currentVersion = "1.0.0"
// newVersion = "1.0.1"
var currentVersionParts = currentVersion.Split('.').Select(int.Parse).ToArray();
var newVersionParts = newVersion.Split('.').Select(int.Parse).ToArray();
if (currentVersionParts.Length != 3 || newVersionParts.Length != 3)
{
return Response.Failure("Invalid version format");
}
for (int i = 0; i < 3; i++)
{
if (newVersionParts[i] > currentVersionParts[i])
{
return Response.Success();
}
else if (newVersionParts[i] < currentVersionParts[i])
{
return Response.Failure("Current version is newer");
}
}
return Response.Failure("Versions are the same");
}
private async Task<bool> CreateEmptyPluginDatabase()
{
string ? pluginDatabaseFile = _Configuration.Get<string>("PluginDatabase");
if (pluginDatabaseFile is null)
{
_Logger.Log("Plugin database file path is not present in the config file", this, LogType.Warning);
return false;
}
if (File.Exists(pluginDatabaseFile))
{
_Logger.Log("Plugin database file already exists", this, LogType.Warning);
return false;
}
List<LocalPlugin> installedPlugins = new List<LocalPlugin>();
await JsonManager.SaveToJsonFile(pluginDatabaseFile, installedPlugins);
_Logger.Log("Plugin database file created", this, LogType.Info);
return true;
}
}

View File

@@ -0,0 +1,189 @@
using System.IO.Compression;
using DiscordBotCore.Logging;
using DiscordBotCore.Configuration;
namespace DiscordBotCore.Utilities;
public class ArchiveManager
{
private readonly ILogger _Logger;
private readonly IConfiguration _Configuration;
public ArchiveManager(ILogger logger, IConfiguration configuration)
{
_Logger = logger;
_Configuration = configuration;
}
public void CreateFromFile(string file, string folder)
{
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
var archiveName = folder + Path.GetFileNameWithoutExtension(file) + ".zip";
if (File.Exists(archiveName))
File.Delete(archiveName);
using ZipArchive archive = ZipFile.Open(archiveName, ZipArchiveMode.Create);
archive.CreateEntryFromFile(file, Path.GetFileName(file));
}
/// <summary>
/// Read a file from a zip archive. The output is a byte array
/// </summary>
/// <param name="fileName">The file name in the archive</param>
/// <param name="archName">The archive location on the disk</param>
/// <returns>An array of bytes that represents the Stream value from the file that was read inside the archive</returns>
public async Task<byte[]?> ReadAllBytes(string fileName, string archName)
{
string? archiveFolderBasePath = _Configuration.Get<string>("ArchiveFolder");
if(archiveFolderBasePath is null)
throw new Exception("Archive folder not found");
Directory.CreateDirectory(archiveFolderBasePath);
archName = Path.Combine(archiveFolderBasePath, archName);
if (!File.Exists(archName))
throw new Exception("Failed to load file !");
using var zip = ZipFile.OpenRead(archName);
var entry = zip.Entries.FirstOrDefault(entry => entry.FullName == fileName || entry.Name == fileName);
if (entry is null) throw new Exception("File not found in archive");
await using var memoryStream = new MemoryStream();
var stream = entry.Open();
await stream.CopyToAsync(memoryStream);
var data = memoryStream.ToArray();
stream.Close();
memoryStream.Close();
Console.WriteLine("Read file from archive: " + fileName);
Console.WriteLine("Size: " + data.Length);
return data;
}
/// <summary>
/// Read data from a file that is inside an archive (ZIP format)
/// </summary>
/// <param name="fileName">The file name that is inside the archive or its full path</param>
/// <param name="archFile">The archive location from the PAKs folder</param>
/// <returns>A string that represents the content of the file or null if the file does not exists or it has no content</returns>
public async Task<string?> ReadFromPakAsync(string fileName, string archFile)
{
string? archiveFolderBasePath = _Configuration.Get<string>("ArchiveFolder");
if(archiveFolderBasePath is null)
throw new Exception("Archive folder not found");
Directory.CreateDirectory(archiveFolderBasePath);
archFile = Path.Combine(archiveFolderBasePath, archFile);
if (!File.Exists(archFile))
throw new Exception("Failed to load file !");
try
{
string? textValue = null;
using (var fs = new FileStream(archFile, FileMode.Open))
using (var zip = new ZipArchive(fs, ZipArchiveMode.Read))
{
foreach (var entry in zip.Entries)
if (entry.Name == fileName || entry.FullName == fileName)
using (var s = entry.Open())
using (var reader = new StreamReader(s))
{
textValue = await reader.ReadToEndAsync();
reader.Close();
s.Close();
fs.Close();
}
}
return textValue;
}
catch (Exception ex)
{
_Logger.LogException(ex, this);
await Task.Delay(100);
return await ReadFromPakAsync(fileName, archFile);
}
}
/// <summary>
/// Extract zip to location
/// </summary>
/// <param name="zip">The zip location</param>
/// <param name="folder">The target location</param>
/// <param name="progress">The progress that is updated as a file is processed</param>
/// <param name="type">The type of progress</param>
/// <returns></returns>
public async Task ExtractArchive(
string zip, string folder, IProgress<float> progress,
UnzipProgressType type)
{
Directory.CreateDirectory(folder);
using var archive = ZipFile.OpenRead(zip);
var totalZipFiles = archive.Entries.Count();
if (type == UnzipProgressType.PercentageFromNumberOfFiles)
{
var currentZipFile = 0;
foreach (var entry in archive.Entries)
{
if (entry.FullName.EndsWith("/")) // it is a folder
Directory.CreateDirectory(Path.Combine(folder, entry.FullName));
else
try
{
entry.ExtractToFile(Path.Combine(folder, entry.FullName), true);
}
catch (Exception ex)
{
_Logger.LogException(ex, this);
}
currentZipFile++;
await Task.Delay(10);
if (progress != null)
progress.Report((float)currentZipFile / totalZipFiles * 100);
}
}
else if (type == UnzipProgressType.PercentageFromTotalSize)
{
ulong zipSize = 0;
foreach (var entry in archive.Entries)
zipSize += (ulong)entry.CompressedLength;
ulong currentSize = 0;
foreach (var entry in archive.Entries)
{
if (entry.FullName.EndsWith("/"))
{
Directory.CreateDirectory(Path.Combine(folder, entry.FullName));
continue;
}
try
{
string path = Path.Combine(folder, entry.FullName);
Directory.CreateDirectory(Path.GetDirectoryName(path));
entry.ExtractToFile(path, true);
currentSize += (ulong)entry.CompressedLength;
}
catch (Exception ex)
{
_Logger.LogException(ex, this);
}
await Task.Delay(10);
if (progress != null)
progress.Report((float)currentSize / zipSize * 100);
}
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DiscordBotCore.Configuration\DiscordBotCore.Configuration.csproj" />
<ProjectReference Include="..\DiscordBotCore.Logging\DiscordBotCore.Logging.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,102 @@
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DiscordBotCore.Utilities;
public static class JsonManager
{
public static async Task<string> ConvertToJson<T>(List<T> data, string[] propertyNamesToUse)
{
if (data == null) throw new ArgumentNullException(nameof(data));
if (propertyNamesToUse == null) throw new ArgumentNullException(nameof(propertyNamesToUse));
// Use reflection to filter properties dynamically
var filteredData = data.Select(item =>
{
if (item == null) return null;
var type = typeof(T);
var propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// Create a dictionary with specified properties and their values
var selectedProperties = propertyInfos
.Where(p => propertyNamesToUse.Contains(p.Name))
.ToDictionary(p => p.Name, p => p.GetValue(item));
return selectedProperties;
}).ToList();
// Serialize the filtered data to JSON
var options = new JsonSerializerOptions
{
WriteIndented = true, // For pretty-print JSON; remove if not needed
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
return await Task.FromResult(JsonSerializer.Serialize(filteredData, options));
}
public static async Task<string> ConvertToJsonString<T>(T Data)
{
var str = new MemoryStream();
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions
{
WriteIndented = false,
});
var result = Encoding.ASCII.GetString(str.ToArray());
await str.FlushAsync();
str.Close();
return result;
}
/// <summary>
/// Save to JSON file
/// </summary>
/// <typeparam name="T">The class type</typeparam>
/// <param name="file">The file path</param>
/// <param name="Data">The values</param>
/// <returns></returns>
public static async Task SaveToJsonFile<T>(string file, T Data)
{
var str = new MemoryStream();
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions
{
WriteIndented = true,
}
);
await File.WriteAllBytesAsync(file, str.ToArray());
await str.FlushAsync();
str.Close();
}
/// <summary>
/// Convert json text or file to some kind of data
/// </summary>
/// <typeparam name="T">The data type</typeparam>
/// <param name="input">The file or json text</param>
/// <returns></returns>
public static async Task<T> ConvertFromJson<T>(string input)
{
Stream text;
if (File.Exists(input))
text = new MemoryStream(await File.ReadAllBytesAsync(input));
else
text = new MemoryStream(Encoding.ASCII.GetBytes(input));
text.Position = 0;
JsonSerializerOptions options = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
};
var obj = await JsonSerializer.DeserializeAsync<T>(text, options);
await text.FlushAsync();
text.Close();
return (obj ?? default)!;
}
}

View File

@@ -0,0 +1,132 @@
namespace DiscordBotCore.Utilities;
public class OneOf<T0, T1>
{
public T0 Item0 { get; }
public T1 Item1 { get; }
public object? Value => Item0 != null ? Item0 : Item1;
public OneOf(T0 item0)
{
Item0 = item0;
}
public OneOf(T1 item1)
{
Item1 = item1;
}
public static implicit operator OneOf<T0, T1>(T0 item0) => new OneOf<T0, T1>(item0);
public static implicit operator OneOf<T0, T1>(T1 item1) => new OneOf<T0, T1>(item1);
public void Match(Action<T0> item0, Action<T1> item1)
{
if (Item0 != null)
item0(Item0);
else
item1(Item1);
}
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1)
{
return Item0 != null ? item0(Item0) : item1(Item1);
}
public Type GetActualType()
{
return Item0 != null ? Item0.GetType() : Item1.GetType();
}
}
public class OneOf<T0, T1, T2>
{
public T0 Item0 { get; }
public T1 Item1 { get; }
public T2 Item2 { get; }
public OneOf(T0 item0)
{
Item0 = item0;
}
public OneOf(T1 item1)
{
Item1 = item1;
}
public OneOf(T2 item2)
{
Item2 = item2;
}
public static implicit operator OneOf<T0, T1, T2>(T0 item0) => new OneOf<T0, T1, T2>(item0);
public static implicit operator OneOf<T0, T1, T2>(T1 item1) => new OneOf<T0, T1, T2>(item1);
public static implicit operator OneOf<T0, T1, T2>(T2 item2) => new OneOf<T0, T1, T2>(item2);
public void Match(Action<T0> item0, Action<T1> item1, Action<T2> item2)
{
if (Item0 != null)
item0(Item0);
else if (Item1 != null)
item1(Item1);
else
item2(Item2);
}
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<T2, TResult> item2)
{
return Item0 != null ? item0(Item0) : Item1 != null ? item1(Item1) : item2(Item2);
}
}
public class OneOf<T0, T1, T2, T3>
{
public T0 Item0 { get; }
public T1 Item1 { get; }
public T2 Item2 { get; }
public T3 Item3 { get; }
public OneOf(T0 item0)
{
Item0 = item0;
}
public OneOf(T1 item1)
{
Item1 = item1;
}
public OneOf(T2 item2)
{
Item2 = item2;
}
public OneOf(T3 item3)
{
Item3 = item3;
}
public static implicit operator OneOf<T0, T1, T2, T3>(T0 item0) => new OneOf<T0, T1, T2, T3>(item0);
public static implicit operator OneOf<T0, T1, T2, T3>(T1 item1) => new OneOf<T0, T1, T2, T3>(item1);
public static implicit operator OneOf<T0, T1, T2, T3>(T2 item2) => new OneOf<T0, T1, T2, T3>(item2);
public static implicit operator OneOf<T0, T1, T2, T3>(T3 item3) => new OneOf<T0, T1, T2, T3>(item3);
public void Match(Action<T0> item0, Action<T1> item1, Action<T2> item2, Action<T3> item3)
{
if (Item0 != null)
item0(Item0);
else if (Item1 != null)
item1(Item1);
else if (Item2 != null)
item2(Item2);
else
item3(Item3);
}
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<T2, TResult> item2, Func<T3, TResult> item3)
{
return Item0 != null ? item0(Item0) : Item1 != null ? item1(Item1) : Item2 != null ? item2(Item2) : item3(Item3);
}
}

View File

@@ -0,0 +1,46 @@
namespace DiscordBotCore.Utilities;
public class OperatingSystem
{
public enum OperatingSystemEnum : int
{
Windows = 0,
Linux = 1,
MacOs = 2
}
public static OperatingSystemEnum GetOperatingSystem()
{
if(System.OperatingSystem.IsLinux()) return OperatingSystemEnum.Linux;
if(System.OperatingSystem.IsWindows()) return OperatingSystemEnum.Windows;
if(System.OperatingSystem.IsMacOS()) return OperatingSystemEnum.MacOs;
throw new PlatformNotSupportedException();
}
public static string GetOperatingSystemString(OperatingSystemEnum os)
{
return os switch
{
OperatingSystemEnum.Windows => "Windows",
OperatingSystemEnum.Linux => "Linux",
OperatingSystemEnum.MacOs => "MacOS",
_ => throw new ArgumentOutOfRangeException()
};
}
public static OperatingSystemEnum GetOperatingSystemFromString(string os)
{
return os.ToLower() switch
{
"windows" => OperatingSystemEnum.Windows,
"linux" => OperatingSystemEnum.Linux,
"macos" => OperatingSystemEnum.MacOs,
_ => throw new ArgumentOutOfRangeException()
};
}
public static int GetOperatingSystemInt()
{
return (int) GetOperatingSystem();
}
}

View File

@@ -0,0 +1,258 @@
namespace DiscordBotCore.Utilities;
public class Option2<T0, T1, TError> where TError : Exception
{
private readonly int _Index;
private T0 Item0 { get; } = default!;
private T1 Item1 { get; } = default!;
private TError Error { get; } = default!;
public Option2(T0 item0)
{
Item0 = item0;
_Index = 0;
}
public Option2(T1 item1)
{
Item1 = item1;
_Index = 1;
}
public Option2(TError error)
{
Error = error;
_Index = 2;
}
public static implicit operator Option2<T0, T1, TError>(T0 item0) => new Option2<T0, T1, TError>(item0);
public static implicit operator Option2<T0, T1, TError>(T1 item1) => new Option2<T0, T1, TError>(item1);
public static implicit operator Option2<T0, T1, TError>(TError error) => new Option2<T0, T1, TError>(error);
public void Match(Action<T0> item0, Action<T1> item1, Action<TError> error)
{
switch (_Index)
{
case 0:
item0(Item0);
break;
case 1:
item1(Item1);
break;
case 2:
error(Error);
break;
default:
throw new InvalidOperationException();
}
}
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<TError, TResult> error)
{
return _Index switch
{
0 => item0(Item0),
1 => item1(Item1),
2 => error(Error),
_ => throw new InvalidOperationException(),
};
}
public override string ToString()
{
return _Index switch
{
0 => $"Option2<{typeof(T0).Name}>: {Item0}",
1 => $"Option2<{typeof(T1).Name}>: {Item1}",
2 => $"Option2<{typeof(TError).Name}>: {Error}",
_ => "Invalid Option2"
};
}
}
public class Option3<T0, T1, T2, TError> where TError : Exception
{
private readonly int _Index;
private T0 Item0 { get; } = default!;
private T1 Item1 { get; } = default!;
private T2 Item2 { get; } = default!;
private TError Error { get; } = default!;
public Option3(T0 item0)
{
Item0 = item0;
_Index = 0;
}
public Option3(T1 item1)
{
Item1 = item1;
_Index = 1;
}
public Option3(T2 item2)
{
Item2 = item2;
_Index = 2;
}
public Option3(TError error)
{
Error = error;
_Index = 3;
}
public static implicit operator Option3<T0, T1, T2, TError>(T0 item0) => new Option3<T0, T1, T2, TError>(item0);
public static implicit operator Option3<T0, T1, T2, TError>(T1 item1) => new Option3<T0, T1, T2, TError>(item1);
public static implicit operator Option3<T0, T1, T2, TError>(T2 item2) => new Option3<T0, T1, T2, TError>(item2);
public static implicit operator Option3<T0, T1, T2, TError>(TError error) => new Option3<T0, T1, T2, TError>(error);
public void Match(Action<T0> item0, Action<T1> item1, Action<T2> item2, Action<TError> error)
{
switch (_Index)
{
case 0:
item0(Item0);
break;
case 1:
item1(Item1);
break;
case 2:
item2(Item2);
break;
case 3:
error(Error);
break;
default:
throw new InvalidOperationException();
}
}
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<T2, TResult> item2, Func<TError, TResult> error)
{
return _Index switch
{
0 => item0(Item0),
1 => item1(Item1),
2 => item2(Item2),
3 => error(Error),
_ => throw new InvalidOperationException(),
};
}
public override string ToString()
{
return _Index switch
{
0 => $"Option3<{typeof(T0).Name}>: {Item0}",
1 => $"Option3<{typeof(T1).Name}>: {Item1}",
2 => $"Option3<{typeof(T2).Name}>: {Item2}",
3 => $"Option3<{typeof(TError).Name}>: {Error}",
_ => "Invalid Option3"
};
}
}
public class Option4<T0, T1, T2, T3, TError> where TError : Exception
{
private readonly int _Index;
private T0 Item0 { get; } = default!;
private T1 Item1 { get; } = default!;
private T2 Item2 { get; } = default!;
private T3 Item3 { get; } = default!;
private TError Error { get; } = default!;
public Option4(T0 item0)
{
Item0 = item0;
_Index = 0;
}
public Option4(T1 item1)
{
Item1 = item1;
_Index = 1;
}
public Option4(T2 item2)
{
Item2 = item2;
_Index = 2;
}
public Option4(T3 item3)
{
Item3 = item3;
_Index = 3;
}
public Option4(TError error)
{
Error = error;
_Index = 4;
}
public static implicit operator Option4<T0, T1, T2, T3, TError>(T0 item0) => new Option4<T0, T1, T2, T3, TError>(item0);
public static implicit operator Option4<T0, T1, T2, T3, TError>(T1 item1) => new Option4<T0, T1, T2, T3, TError>(item1);
public static implicit operator Option4<T0, T1, T2, T3, TError>(T2 item2) => new Option4<T0, T1, T2, T3, TError>(item2);
public static implicit operator Option4<T0, T1, T2, T3, TError>(T3 item3) => new Option4<T0, T1, T2, T3, TError>(item3);
public static implicit operator Option4<T0, T1, T2, T3, TError>(TError error) => new Option4<T0, T1, T2, T3, TError>(error);
public void Match(Action<T0> item0, Action<T1> item1, Action<T2> item2, Action<T3> item3, Action<TError> error)
{
switch (_Index)
{
case 0:
item0(Item0);
break;
case 1:
item1(Item1);
break;
case 2:
item2(Item2);
break;
case 3:
item3(Item3);
break;
case 4:
error(Error);
break;
default:
throw new InvalidOperationException();
}
}
public TResult Match<TResult>(Func<T0, TResult> item0, Func<T1, TResult> item1, Func<T2, TResult> item2, Func<T3, TResult> item3, Func<TError, TResult> error)
{
return _Index switch
{
0 => item0(Item0),
1 => item1(Item1),
2 => item2(Item2),
3 => item3(Item3),
4 => error(Error),
_ => throw new InvalidOperationException(),
};
}
public override string ToString()
{
return _Index switch
{
0 => $"Option4<{typeof(T0).Name}>: {Item0}",
1 => $"Option4<{typeof(T1).Name}>: {Item1}",
2 => $"Option4<{typeof(T2).Name}>: {Item2}",
3 => $"Option4<{typeof(T3).Name}>: {Item3}",
4 => $"Option4<{typeof(TError).Name}>: {Error}",
_ => "Invalid Option4"
};
}
}

View File

@@ -0,0 +1,8 @@
namespace DiscordBotCore.Utilities.Responses;
public interface IResponse<out T>
{
public bool IsSuccess { get; }
public string Message { get; }
public T? Data { get; }
}

View File

@@ -0,0 +1,45 @@
namespace DiscordBotCore.Utilities.Responses;
public class Response : IResponse<bool>
{
public bool IsSuccess => Data;
public string Message { get; }
public bool Data { get; }
private Response(bool result)
{
Data = result;
Message = string.Empty;
}
private Response(string message)
{
Data = false;
Message = message;
}
public static Response Success() => new Response(true);
public static Response Failure(string message) => new Response(message);
}
public class Response<T> : IResponse<T> where T : class
{
public bool IsSuccess => Data is not null;
public string Message { get; }
public T? Data { get; }
private Response(T data)
{
Data = data;
Message = string.Empty;
}
private Response(string message)
{
Data = null;
Message = message;
}
public static Response<T> Success(T data) => new Response<T>(data);
public static Response<T> Failure(string message) => new Response<T>(message);
}

View File

@@ -0,0 +1,80 @@
namespace DiscordBotCore.Utilities;
public class Result
{
private bool? _Result;
private Exception? Exception { get; }
private Result(Exception exception)
{
_Result = null;
Exception = exception;
}
private Result(bool result)
{
_Result = result;
Exception = null;
}
public bool IsSuccess => _Result.HasValue && _Result.Value;
public void HandleException(Action<Exception> action)
{
if(IsSuccess)
{
return;
}
action(Exception!);
}
public static Result Success() => new Result(true);
public static Result Failure(Exception ex) => new Result(ex);
public static Result Failure(string message) => new Result(new Exception(message));
public void Match(Action successAction, Action<Exception> exceptionAction)
{
if (_Result.HasValue && _Result.Value)
{
successAction();
}
else
{
exceptionAction(Exception!);
}
}
public TResult Match<TResult>(Func<TResult> successAction, Func<Exception,TResult> errorAction)
{
return IsSuccess ? successAction() : errorAction(Exception!);
}
}
public class Result<T>
{
private readonly OneOf<T, Exception> _Result;
private Result(OneOf<T, Exception> result)
{
_Result = result;
}
public static Result<T> From (T value) => new Result<T>(new OneOf<T, Exception>(value));
public static implicit operator Result<T>(Exception exception) => new Result<T>(new OneOf<T, Exception>(exception));
public void Match(Action<T> valueAction, Action<Exception> exceptionAction)
{
_Result.Match(valueAction, exceptionAction);
}
public TResult Match<TResult>(Func<T, TResult> valueFunc, Func<Exception, TResult> exceptionFunc)
{
return _Result.Match(valueFunc, exceptionFunc);
}
}

View File

@@ -0,0 +1,7 @@
namespace DiscordBotCore.Utilities;
public enum UnzipProgressType
{
PercentageFromNumberOfFiles,
PercentageFromTotalSize
}

View File

@@ -0,0 +1,183 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using DiscordBotCore.Configuration;
using DiscordBotCore.Logging;
using DiscordBotCore.Others;
using DiscordBotCore.PluginCore.Helpers;
using DiscordBotCore.PluginCore.Helpers.Execution.DbCommand;
using DiscordBotCore.PluginCore.Interfaces;
using DiscordBotCore.PluginManagement.Loading;
namespace DiscordBotCore.Bot;
internal class CommandHandler : ICommandHandler
{
private static readonly string _DefaultPrefix = ";";
private readonly CommandService _commandService;
private readonly ILogger _logger;
private readonly IPluginLoader _pluginLoader;
private readonly IConfiguration _configuration;
/// <summary>
/// Command handler constructor
/// </summary>
/// <param name="pluginLoader">The plugin loader</param>
/// <param name="commandService">The discord bot command service</param>
/// <param name="botPrefix">The prefix to watch for</param>
/// <param name="logger">The logger</param>
public CommandHandler(ILogger logger, IPluginLoader pluginLoader, IConfiguration configuration, CommandService commandService)
{
_commandService = commandService;
_logger = logger;
_pluginLoader = pluginLoader;
_configuration = configuration;
}
/// <summary>
/// The method to initialize all commands
/// </summary>
/// <returns></returns>
public async Task InstallCommandsAsync(DiscordSocketClient client)
{
client.MessageReceived += (message) => MessageHandler(client, message);
client.SlashCommandExecuted += Client_SlashCommandExecuted;
await _commandService.AddModulesAsync(Assembly.GetEntryAssembly(), null);
}
private Task Client_SlashCommandExecuted(SocketSlashCommand arg)
{
try
{
var plugin = _pluginLoader.SlashCommands.FirstOrDefault(p => p.Name == arg.Data.Name);
if (plugin is null)
throw new Exception("Failed to run command !");
if (arg.Channel is SocketDMChannel)
plugin.ExecuteDm(_logger, arg);
else plugin.ExecuteServer(_logger, arg);
}
catch (Exception ex)
{
_logger.LogException(ex, this);
}
return Task.CompletedTask;
}
/// <summary>
/// The message handler for the bot
/// </summary>
/// <param name="Message">The message got from the user in discord chat</param>
/// <returns></returns>
private async Task MessageHandler(DiscordSocketClient socketClient, SocketMessage socketMessage)
{
try
{
if (socketMessage.Author.IsBot)
return;
if (socketMessage as SocketUserMessage == null)
return;
var message = socketMessage as SocketUserMessage;
if (message is null)
return;
var argPos = 0;
string botPrefix = this._configuration.Get<string>("prefix", _DefaultPrefix);
if (!message.Content.StartsWith(botPrefix) && !message.HasMentionPrefix(socketClient.CurrentUser, ref argPos))
return;
var context = new SocketCommandContext(socketClient, message);
await _commandService.ExecuteAsync(context, argPos, null);
IDbCommand? plugin;
var cleanMessage = "";
if (message.HasMentionPrefix(socketClient.CurrentUser, ref argPos))
{
var mentionPrefix = "<@" + socketClient.CurrentUser.Id + ">";
plugin = _pluginLoader.Commands!
.FirstOrDefault(plug => plug.Command ==
message.Content.Substring(mentionPrefix.Length + 1)
.Split(' ')[0] ||
plug.Aliases.Contains(message.CleanContent
.Substring(mentionPrefix.Length + 1)
.Split(' ')[0]
)
);
cleanMessage = message.Content.Substring(mentionPrefix.Length + 1);
}
else
{
plugin = _pluginLoader.Commands!
.FirstOrDefault(p => p.Command ==
message.Content.Split(' ')[0].Substring(botPrefix.Length) ||
p.Aliases.Contains(
message.Content.Split(' ')[0]
.Substring(botPrefix.Length)
)
);
cleanMessage = message.Content.Substring(botPrefix.Length);
}
if (plugin is null)
{
return;
}
if (plugin.RequireAdmin && !context.Message.Author.IsAdmin())
{
return;
}
var split = cleanMessage.Split(' ');
string[]? argsClean = null;
if (split.Length > 1)
{
argsClean = string.Join(' ', split, 1, split.Length - 1).Split(' ');
}
DbCommandExecutingArgument cmd = new(_logger,
context,
cleanMessage,
split[0],
argsClean,
new DirectoryInfo(Path.Combine(_configuration.Get<string>("ResourcesFolder"), plugin.Command)));
_logger.Log(
$"User ({context.User.Username}) from Guild \"{context.Guild.Name}\" executed command \"{cmd.CleanContent}\"",
this,
LogType.Info
);
if (context.Channel is SocketDMChannel)
{
await plugin.ExecuteDm(cmd);
}
else
{
await plugin.ExecuteServer(cmd);
}
}
catch (Exception ex)
{
_logger.LogException(ex, this);
}
}
}

View File

@@ -0,0 +1,144 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using DiscordBotCore.Configuration;
using DiscordBotCore.Logging;
using DiscordBotCore.PluginManagement.Loading;
namespace DiscordBotCore.Bot;
public class DiscordBotApplication : IDiscordBotApplication
{
internal static IPluginLoader _InternalPluginLoader;
private CommandHandler _CommandServiceHandler;
private CommandService _Service;
private readonly ILogger _Logger;
private readonly IConfiguration _Configuration;
private readonly IPluginLoader _PluginLoader;
public bool IsReady { get; private set; }
public DiscordSocketClient Client { get; private set; }
/// <summary>
/// The main Boot constructor
/// </summary>
public DiscordBotApplication(ILogger logger, IConfiguration configuration, IPluginLoader pluginLoader)
{
this._Logger = logger;
this._Configuration = configuration;
this._PluginLoader = pluginLoader;
_InternalPluginLoader = pluginLoader;
}
public async Task StopAsync()
{
if (!IsReady)
{
_Logger.Log("Can not stop the bot. It is not yet initialized.", this, LogType.Error);
return;
}
await _PluginLoader.UnloadAllPlugins();
await Client.LogoutAsync();
await Client.StopAsync();
Client.Log -= Log;
Client.LoggedIn -= LoggedIn;
Client.Ready -= Ready;
Client.Disconnected -= Client_Disconnected;
await Client.DisposeAsync();
IsReady = false;
}
/// <summary>
/// The start method for the bot. This method is used to load the bot
/// </summary>
public async Task StartAsync()
{
var config = new DiscordSocketConfig
{
AlwaysDownloadUsers = true,
//Disable system clock checkup (for responses at slash commands)
UseInteractionSnowflakeDate = false,
GatewayIntents = GatewayIntents.All
};
DiscordSocketClient client = new DiscordSocketClient(config);
_Service = new CommandService();
client.Log += Log;
client.LoggedIn += LoggedIn;
client.Ready += Ready;
client.Disconnected += Client_Disconnected;
Client = client;
await client.LoginAsync(TokenType.Bot, _Configuration.Get<string>("token"));
await client.StartAsync();
_CommandServiceHandler = new CommandHandler(_Logger, _PluginLoader, _Configuration, _Service);
await _CommandServiceHandler.InstallCommandsAsync(client);
while (!IsReady)
{
await Task.Delay(100);
}
}
private async Task Client_Disconnected(Exception arg)
{
if (arg.Message.Contains("401"))
{
_Configuration.Set("token", string.Empty);
_Logger.Log("The token is invalid.", this, LogType.Critical);
await _Configuration.SaveToFile();
}
}
private Task Ready()
{
IsReady = true;
return Task.CompletedTask;
}
private Task LoggedIn()
{
_Logger.Log("Successfully Logged In", this);
_PluginLoader.SetDiscordClient(Client);
return Task.CompletedTask;
}
private Task Log(LogMessage message)
{
switch (message.Severity)
{
case LogSeverity.Error:
case LogSeverity.Critical:
_Logger.Log(message.Message, this, LogType.Error);
break;
case LogSeverity.Info:
case LogSeverity.Debug:
_Logger.Log(message.Message, this, LogType.Info);
break;
}
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Discord.WebSocket;
namespace DiscordBotCore.Bot;
internal interface ICommandHandler
{
/// <summary>
/// The method to initialize all commands
/// </summary>
/// <returns></returns>
Task InstallCommandsAsync(DiscordSocketClient client);
}

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