Compare commits

...

395 commits

Author SHA1 Message Date
4b62dd99aa
Revert "Carefree dependency updates"
This reverts commit d9e29f8b95.
2022-07-26 20:56:41 +02:00
d9e29f8b95
Carefree dependency updates 2022-07-26 02:16:19 +02:00
dc800d144d
Move the asp logger to core and and make it a generic ilogger adapter 2022-07-26 02:01:44 +02:00
a3e10b15c1
Prevent users from giving others negative cookies 2022-07-25 15:41:44 +02:00
2946ed523e
Geekbot version 4.4 2022-07-22 19:47:46 +02:00
b0d603e518
Update discord.net to v3 2022-07-22 19:46:32 +02:00
be06870892
Update libsodium 2022-07-22 18:56:26 +02:00
94bdc1081b
Temporarily disable the youtube command 2022-07-22 18:33:56 +02:00
29f44c34bc
Handle possible error with non-existing role properly in the reaction listener 2022-07-22 18:26:48 +02:00
245e88726a
Remove the !hello/greeting command, the api is no longer available 2022-07-22 18:00:54 +02:00
15e1d10839
remove the !corona command 2022-07-22 18:00:12 +02:00
c1b5a4d449
Upgrade to ansible 2.12 in an attempt to fix the deployment 2022-07-19 16:53:35 +02:00
fdd23ad00f
Fix some unchecked access in the giveRole function in the reactionListener that would crash the bot if an expected value wasn't around 2022-07-19 16:22:46 +02:00
7cc9fc92d9
Comment out the "Cleanup Old Container" step from the ansible script 2022-05-22 18:16:54 +02:00
5b40b7b2e7
Downgrade to ansible runner 1.4.7 2022-05-22 18:05:53 +02:00
eefd8452cd
Use ansible-runner docker container from quay.io, thank you red hat for removing it from docker hub... 2022-05-22 17:25:01 +02:00
a3623ccddd
Temporarily don't ignore commands coming from the eevent guild (169844523181015040) 2022-05-22 17:11:09 +02:00
b30c048bac
Fix a bug where users could modify their own karma, because the user check was an object reference check rather than a user-id check 2022-04-15 10:33:09 +01:00
193a651495
Move the error embed from /karma into the embad class as a static method so it can be used by other commands as well 2021-12-27 21:58:49 +08:00
3fa8fac867
Remove forgotten platform target specifier from Startup.csproj 2021-12-27 21:55:44 +08:00
2fe8e2fa4f
Add some additional logging to highlight startup parameters 2021-12-18 17:12:10 +01:00
bdaf16f53f
Remove PlatfromTarget from the csproj files so that the project can run on arm64 2021-12-18 17:09:48 +01:00
0e5785e3a1
Rework the output of the /choose command to show all the choices that the user has entered; which are somewhat lost with / commands, as others can't see what the user entered without an additional mouseclick 2021-11-16 00:40:19 +01:00
d03525d363
Port the choose command to / commands 2021-11-14 23:44:02 +01:00
17cb5951ee
Allow Interaction Commands to run for up to 15 minutes by replying to discord immediately and handling the command in a separate thread 2021-11-14 03:39:36 +01:00
699a93200b
Port the emojify command to / commands 2021-11-14 01:18:36 +01:00
1b396a529c
Make sure that interaction commands without changes are not being patched unnecessarily 2021-11-14 01:16:50 +01:00
0f7f936492
Add explicit nullability for some fields of the Interaction Command record 2021-11-14 01:14:26 +01:00
bcc2742e81
Add retry logic for Post, Patch and Delete to the HttpAbstractions 2021-11-14 01:12:32 +01:00
5d6e5cf2ad
Make the EmojiConverter static 2021-11-13 16:26:14 +01:00
df6672305d
Downgrade Entity Framework from 6-rc2 to 5.0.12 2021-11-10 00:48:42 +01:00
c2c30846fb
Ensure the logger doesn't fail to log based on some stacktrace field during json serialization 2021-11-10 00:48:42 +01:00
e13cf9d830
Remove the single file build flag because it seems to break localization 2021-11-09 18:46:10 +01:00
09af445436
Make sure that the /v1/command endpoint actually ignores the parameter default value during json serialization if its null, instead of making it an empty string 2021-11-09 01:44:31 +01:00
4d97201319
Set parameter default value to null instead of an empty string in the bot command lookup 2021-11-09 01:25:42 +01:00
9cfac1ad38
Remove the runtime identifier from the interactions project 2021-11-09 01:25:32 +01:00
9beef55979
Read the bot commands on startup and provide them to the /v1/commands endpoint 2021-11-09 00:53:09 +01:00
4f4e16d674
Add a way to lookup all bot commands in the bot assembly without using the commandService provided by discord.net 2021-11-09 00:51:37 +01:00
ae1b28ff77
Decouple the WebApi and the Bot and move the startup code into a new project 2021-11-09 00:49:46 +01:00
ee31e66e75
Fix the datetime issue when writing to the database for !cookie and the karma commands as well 2021-11-08 00:15:25 +01:00
c9af82015b
Fix !quote, use ToUniversalTime() on the timestamp saved to the database when saving a new quote 2021-11-08 00:02:12 +01:00
7d4a81dcde
Make sure all DateTimeOffsets that are written to the DB are in UTC 2021-11-07 02:52:35 +01:00
1a1d1406ec
Ignore compiler warning CS8618 in the Commands project 2021-11-07 00:50:53 +01:00
65d84c0ba6
Move CommandPreconditions into Geekbot.Bot 2021-11-07 00:38:57 +01:00
a460041c52
Move all interaction classes to its own project 2021-11-07 00:36:20 +01:00
54cbb00880
Upgrade EntityFramework to .net6-rc2 2021-11-07 00:32:32 +01:00
d0bc5810a9
Cleanup all interaction commands for .net6 2021-11-07 00:16:11 +01:00
47299dd1de
Refactor the ASP Logger to take advantage of new .net6 features 2021-11-07 00:15:15 +01:00
e01a066920
Refactor WebApi Controllers to take advantage of new .net6 features 2021-11-07 00:08:08 +01:00
6b3a3a9ec2
Rewrite the WebApi startup to take advantage of new .net6 features 2021-11-07 00:02:26 +01:00
4395d9e9dd
Port the karma commands to / commands 2021-11-06 19:08:42 +01:00
6d39c2d33f
Add primitive GetAvatarUrl function to a resolved interaction user 2021-11-06 19:07:28 +01:00
31f12a4110
Add command type level to interaction command dictionary 2021-11-06 18:20:16 +01:00
eb648b94d9
Make Embed Types Deserializable 2021-11-06 18:18:09 +01:00
fe1063167f
Set default embed color to DimGray 2021-11-06 18:17:44 +01:00
866c28b76b
Move embed building logic for !urban into the shared command code 2021-11-06 16:53:37 +01:00
10b29cce8a
Add function to convert Geekbot Embeds to Discord.Net Embeds for easier code sharing 2021-11-06 16:52:49 +01:00
34f15402b4
Port !urban to a / command 2021-11-06 16:23:50 +01:00
ea17ce2866
Add helper classes to simplify embed creation for interactions 2021-11-06 16:17:22 +01:00
c15a66255f
Fail if a user tries to execute a non-existing command, instead of letting them know that the command doesn't exist 2021-11-05 17:46:08 +01:00
e74aeb1403
Add simple response function to the InteractionBase to reduce the InteractionResponse copying 2021-11-05 17:45:11 +01:00
5a520ff567
Pass interaction data into all InteractionBase hooks 2021-11-02 21:57:01 +01:00
01df35b12b
Capture interaction exceptions in sentry 2021-11-02 21:56:40 +01:00
44ae2eeaf6
Update sentry SDK to v3.11.0 2021-11-02 21:54:47 +01:00
8c2eabfd21
Fully remove the dependency on Newtonsoft.Json 2021-11-01 01:27:04 +01:00
cf0cd743b8
Get rid of Newtonsoft.Json everywhere but the HTTP abstractions 2021-11-01 01:04:20 +01:00
7b06965f14
Switch from Newtonsoft.Json to System.Text.Json in the logger 2021-11-01 00:34:50 +01:00
6f94de5a14
Reduce ulong to long casts in the roll command 2021-11-01 00:16:42 +01:00
616ac5e430
Use structs instead of enums for interaction option names 2021-10-31 23:50:48 +01:00
913ea23732
Remove OptionChoice type inheritors in favour of just supporting strings as values 2021-10-31 23:23:02 +01:00
e20faa43e1
Port rank for slash commands 2021-10-31 23:21:15 +01:00
772557978b
Set guild language when executing interaction 2021-10-31 22:33:31 +01:00
177c773451
Remove the discord socket client dependency from the webapi 2021-10-31 20:22:42 +01:00
29a2e5c4a2
Don't go into an infinite await when stopping the app if the webapi is running 2021-10-31 20:18:27 +01:00
78c139293f
Move the roll command logic into the shared commands project 2021-10-31 20:16:56 +01:00
89ea6df6e2
Move Localizations into core 2021-10-31 20:15:08 +01:00
29e22acbc0
Move the RollTimeout record to the shared commands project 2021-10-30 15:48:28 +02:00
dd941f5f94
Add new project to share command logic between gateway and slash commands 2021-10-30 15:47:54 +02:00
588c93b87d
Check the users previous roll in the /roll command 2021-10-30 14:47:56 +02:00
a1893c7414
Add DI support for interaction commands 2021-10-30 14:46:23 +02:00
24749d9009
Add and use interaction command hooks for BeforeExecute, AfterExecute, OnException and GetExceptionResponse 2021-10-30 14:43:57 +02:00
9a2bf84a05
Add applicationId/guildId mapping to the interaction registrar 2021-09-20 02:23:44 +02:00
d2b9daac57
Add roll interaction 2021-09-20 02:14:25 +02:00
d17ca4c556
Pass entire Interaction object to interaction commands, instead of just the data 2021-09-20 02:12:57 +02:00
aaea8d0540
Add user property to the interaction member object 2021-09-20 02:11:53 +02:00
d975594d21
Add mention property to the interaction user object 2021-09-20 02:11:16 +02:00
2de6381f9d
Change type of Value in InteractionOption to JsonElement because it's unknown at deserialization time what type it will be 2021-09-20 02:10:28 +02:00
65bb7f6cac
Creat initial interaction command framework 2021-09-20 01:31:24 +02:00
60547140ea
Create all interaction models 2021-09-20 01:28:26 +02:00
209887e237
Add sentry support to the webapi 2021-09-19 16:19:40 +02:00
d81fb2a3d9
Add initial interaction support 2021-09-19 16:11:06 +02:00
85d06b76e0
Add --disable-gateway parameter to the run parameters to stop the bot from connecting to discord. Useful when working on the web-api 2021-09-19 16:06:11 +02:00
447c6d8042
Remove a database call from !quote by delegating the randomness to the database. 2021-09-19 00:58:00 +02:00
1b9d8732d5
Add tracing to the !quote embed builder 2021-09-19 00:57:10 +02:00
954c6c2be3
Remove Guild ID from the tracing tags 2021-09-17 15:55:13 +02:00
3d117aebe1
Split reply and quote embed building trace apart in !quote 2021-09-17 15:12:22 +02:00
0a9099a6d2
Move Sentry Init priority between the discord connection and dependency registration 2021-09-17 14:53:24 +02:00
d16828077d
Set Transaction Status to OK by default 2021-09-17 14:33:55 +02:00
a1f8d033c2
Use the TransactionModuleBase for all commands that haven't implemented GeekbotCommandBase 2021-09-17 14:31:24 +02:00
f02c30e660
Remove the mod command class 2021-09-17 14:30:50 +02:00
833a8a0dd8
Split Transactions from the GeekbotCommandBase 2021-09-17 14:27:46 +02:00
d708525a2f
Add traces to the !quote command 2021-09-17 14:07:19 +02:00
aa826f962d
Add traces to the !roll command 2021-09-17 14:06:34 +02:00
3299ac4eab
Add generic sentry tracing to the main command module 2021-09-17 14:06:10 +02:00
1f518e980c
Move Sentry SDK init to the main init process 2021-09-17 14:04:30 +02:00
5c507b026c
Remove random.org integration 2021-09-17 14:03:35 +02:00
989057a0b0
Migrate from RavenSharp to the SentrySDK 2021-09-17 12:22:26 +02:00
f19ddb30b2
Replace RNGCryptoServiceProvider with System.Security.Cryptography.RandomNumberGenerator 2021-09-17 11:23:20 +02:00
e712403dd9
Upgrade Sumologic, jikan and HtmlAgilityPack 2021-09-17 11:21:42 +02:00
18ece35ffe
Remove System.Timers.Timer ambiguity for .net6-preview7 2021-08-11 23:08:00 +02:00
f22956368b
Remove !gdq 2021-08-11 18:01:19 +02:00
9a55d8447f
Upgrade discord.net to version 2.4.0 2021-08-11 18:01:19 +02:00
90668b6aac
Add padding at the end of things for the !slap command 2021-07-10 00:49:03 +02:00
8d037c786e
Reenable MSBuildEnableWorkloadResolver 2021-07-10 00:41:22 +02:00
86068ecc44
Disable self contained publishing for the test, web and bot dlls 2021-07-10 00:05:59 +02:00
8fcc629106
Disable MSBuildEnableWorkloadResolver in gitlab ci 2021-07-09 20:39:04 +02:00
611b179d62
Add a piece of low fat mozzarella to the !slap command 2021-07-09 20:21:17 +02:00
5a50ba5820
Add total quotes of a user to !stats 2021-04-20 22:57:36 +02:00
8bd8efa66a
Remove the !evergiven command 2021-04-07 22:49:13 +02:00
153ce3dca4
Translate !8ball 2021-03-29 19:02:31 +02:00
5b99ee951b
Evergiven is free once again 2021-03-29 17:45:43 +02:00
9ad39058ac
Update the !evergiven command to directly reflect what istheshipstillstuck.com says 2021-03-29 11:39:50 +02:00
41e0a9f8d7
Add !evergiven to see if the ship is still stuck in the suez canal 2021-03-26 00:11:15 +01:00
49870b6b91
Stop using single-file deployments due to missing locale data 2021-03-20 04:17:14 +01:00
52fe5bdec1
Switch back to a debian container, alpine is missing locale info 2021-03-20 04:06:10 +01:00
f25c9250ec
Switch to an alpine container and single file, self-contained deployments 2021-03-19 01:10:22 +01:00
1c64328587
Upgrade to .net6 preview 2021-03-19 00:21:35 +01:00
d1d57ba714
Refactor karma commands 2021-03-18 23:53:56 +01:00
c77b501b6c
Fix message sent when user is trying give themselves !neutral karma 2021-03-18 12:27:26 +01:00
6c142f41d3
Upgrade discord.net 2021-03-18 12:23:18 +01:00
c1b8394e1b
Add a !neutral command for karma, it does nothing. 2021-03-18 12:06:41 +01:00
eddd005d34
Add translations for !corona 2021-01-25 01:40:51 +01:00
644d877e29
Show country flag when !corona has a country code parameter 2021-01-25 00:49:18 +01:00
bbb9b89422
Add Support for emoji flags in the emoji converter 2021-01-25 00:48:42 +01:00
4fd62e9184
Make sure that the build version suffix is not a number 2021-01-24 23:06:47 +01:00
0434335239
Fix !corona by changing data source to covid19-api.org, added a country code parameter as well 2021-01-24 22:15:15 +01:00
21303bfca8
Remove code in the stats handler that checks if 2021 has started 2021-01-01 18:06:40 +01:00
e495e2df17
Use the correct unit when listing messages in a season 2021-01-01 02:48:28 +01:00
d477a4b056
Update the translations for !rank with the new rank types 2020-12-30 23:34:22 +01:00
8bdf2e9681
Add support for !rank quote 2020-12-30 23:33:53 +01:00
17f62d7607
Ignore the discord bots server when updating stats 2020-12-30 23:23:40 +01:00
01f0d2f43b
Check every 5 minutes if it's 2021 instead of every hour in the stats handler 2020-12-30 23:19:15 +01:00
29bb8035fe
add !rank seasons to the rank command 2020-12-30 23:17:00 +01:00
29c0def713
Start counting messages per quarter starting 1 january 2021 2020-12-29 22:33:19 +01:00
7e792bd782
Fallback to user repo when retrieving a user via the discord gateway fails or times out 2020-12-29 17:12:03 +01:00
714b0008bc
Allow a die to have 145 sides 2020-12-11 22:39:05 +01:00
97d479adc4 Merge branch 'quote-with-id-deprecation' into 'master'
Remove the ability to create quotes from message ids

See merge request dbgit/open/geekbot!19
2020-12-03 09:55:49 +00:00
baf09e2f38
Remove the ability to create quotes from message ids 2020-11-25 15:21:55 +01:00
09dbeb9766
Fix a stupid bug with the !avatar command 2020-11-24 21:40:33 +01:00
4c3b7044ce
Don't count 1 to many sides in the !dice command 2020-11-02 18:33:05 +01:00
2fb815bc97
Don't use embeds for !dog 2020-11-02 18:01:57 +01:00
ffab56d4a5
Delete original message from user after using !emojify 2020-10-23 21:46:59 +02:00
fe51dfe540
Revert last commit 2020-10-09 17:25:53 +02:00
28a5b9322e
Allow Rune#0007 to bypass the ignored servers restriction 2020-10-09 17:10:49 +02:00
41795aa13f
Small wording tweaks to the !anime and !manga commands. They also no long need their description html decoded. 2020-09-24 18:29:21 +02:00
ed7748833a
Format dates in the !anime and !manga commands with the correct culture info 2020-09-24 12:49:55 +02:00
7ef0b6a319
Swap the my anime list wrapper for the !anime and !manga commands 2020-09-24 12:21:21 +02:00
216188f61f
Add a deprecation warning for quoting by message ID 2020-09-23 16:46:13 +02:00
58bd4d17d0
Optimize the !quote database query 2020-09-23 16:28:50 +02:00
ae9b9caeb9
Add random.org for random number generation. 2020-09-23 16:05:43 +02:00
b743539c74
Upgrade to .net5 rc1 and fix all breaking changes in the web api since .net core 2.2 2020-09-22 13:06:57 +02:00
482a74839a
Fix some swiss german grammar (never heard of it either) in the !quote command 2020-09-22 12:24:12 +02:00
cbe2aa23c9
Make sure that examples in the !urban command are not longer than 1024 characters. 2020-09-15 23:25:40 +02:00
d3c284102b
Add an api endpoint to shutdown the bot from the browser 2020-09-15 00:14:27 +02:00
f71349b378
Reduce logging by making the user repo update message a debug log message 2020-09-08 20:20:09 +02:00
81373b7614
Make the ErrorHandler more resilient in case an exception is thrown during the error handling 2020-08-31 21:50:01 +02:00
ce1153a0e2
Upgrade Entity Framework to preview 8 2020-08-31 19:29:03 +02:00
8246c7a862
When json logging is enabled, log it to the console without color or additional timestamp, also log the messages with the correct log level. Remove the logs folder. 2020-08-31 18:46:00 +02:00
546b5450e7
Deal with MTG Gatherer downtime 2020-08-26 23:28:03 +02:00
3c4a5c638b
Upgrade Microsoft.Extensions.* to .NET5 preview 8 2020-08-26 23:15:57 +02:00
a78e92d230
Remove YamlDotNet 2020-08-15 00:40:59 +02:00
79fb7dece6 Merge branch 'rework-localization' into 'master'
Rework Localization

See merge request dbgit/open/geekbot!18
2020-08-14 21:47:44 +00:00
60e36daaec
Translate !stats 2020-08-14 23:34:02 +02:00
33829e91bc
Delete the TranslationHandler and the old translations file. Refactor GeekbotCommandBase to get the server language from guild settings. Create DateLocalization to create a localized relative time remaining string. 2020-08-14 23:15:11 +02:00
078c884df7
Convert Role command to new localization method 2020-08-14 18:11:52 +02:00
90af781c7b
Start Using resource files (.resx) for translations. Create GeekbotCommandBase to reduce command boilerplate. Convert admin, choose, cookies, karma, quote, rank, roll and ship to the new localization method. 2020-08-14 03:30:54 +02:00
12388fd7d0
Curate Media Files 2020-08-14 00:12:14 +02:00
187fd6a04f
Remove code duplication from !quote 2020-08-13 22:46:53 +02:00
726ee77ed4
Fix bug where message links from discord stable would be considered invalid 2020-08-13 20:04:54 +02:00
ad086a5e0c
Make it possible to create quotes from message links 2020-08-13 19:51:56 +02:00
7942308059
Alias "!quote save" with "!quote add" 2020-08-13 18:22:09 +02:00
c893e45004 Merge branch 'TheGreatSplit' into 'master'
The Great Split

See merge request dbgit/open/geekbot!17
2020-08-13 15:59:58 +00:00
d68ce459ef
Fix CI Pipeline: tests should be executed from the tests folder (with a lowercase t) 2020-08-13 17:47:28 +02:00
bd117e2595
Fix Translations file path 2020-08-13 17:42:33 +02:00
97ad54df9e
Rename the folder Tests to tests 2020-08-13 17:23:03 +02:00
61ce14a61d
Change .gitlab-ci and dockerfile to fit the new project structure 2020-08-08 22:33:02 +02:00
fc0af492ad
Split Geekbot.net into src/Bot, src/Core, and src/Web 2020-08-08 22:24:01 +02:00
7b6dd2d2f9
Rename the clients namescape to MalClient, because that was the only thing in there, while other clients got their own namespaces 2020-08-08 21:17:02 +02:00
c22d0cf941
Merge WikipediaApi into the main code base 2020-08-08 21:15:17 +02:00
3813290f89
Simplefy version number during in CI pipeline 2020-08-08 20:55:14 +02:00
9003d6249e
Add !mmr to get League of Legends MMR numbers 2020-07-28 22:14:59 +02:00
913b4a5f10
Change the HttpAbstractions so that the caller can provide its own HttpClient instead of creating parameters for every HttpClient Option 2020-07-28 22:14:22 +02:00
4659f793f5
Add !lmgtfy 2020-07-22 14:32:47 +02:00
fff2324232
Fix build warnings in MediaProvider.cs and TranslationHandler.cs 2020-07-22 14:17:08 +02:00
77e912501d
Upgrade to .NET5 preview 7 2020-07-22 14:09:26 +02:00
4cd7ac1d79
Fix broken DM Channel support 2020-07-15 17:10:50 +02:00
5e9cb8a4c1
Make main async 2020-07-15 03:11:36 +02:00
efed2f7120
Add the !corona command 2020-07-15 03:09:43 +02:00
ba0d116f3e
Add an abstraction for http calls 2020-07-15 02:52:13 +02:00
0589b8e91b
Update EntityFrameworkCore and microsoft.extensions.* to .net5 preview 6 2020-07-10 22:32:42 +02:00
c94d73736d
Use greetings.dev instead of hardcoded greetings 2020-06-30 17:54:01 +02:00
580a514ce5
Rework the media provider 2020-06-25 15:12:41 +02:00
cc22774729
Make sure a die can actually land on its highest number 2020-06-21 21:12:34 +02:00
859db4ebdd
Add some unit tests for the dice parser 2020-06-21 15:49:18 +02:00
acd1cee16c
Reformat !dice help 2020-06-21 03:43:11 +02:00
6d44960867
Rewrite the !dice command from scratch 2020-06-21 03:33:05 +02:00
d7e313c9fa
Update readme 2020-06-20 03:43:02 +02:00
279a8975c9
Expose web api on port 12995 again 2020-06-20 03:42:41 +02:00
194bfd3d3b
remove traces of the docker branch from the ci file 2020-06-20 03:27:08 +02:00
8f41999015
Expose the web api directly on port 80 2020-06-20 03:25:28 +02:00
619f63067c
Remove the ability the lookup username history for moderators 2020-06-20 03:23:27 +02:00
56f788878a
Fix Sumologic environment variable in docker 2020-06-20 03:17:17 +02:00
d9f8e9a80e
Add redshift (and DigitalOcean Managed DB) compatibility and start using a string building to create the sql connection string 2020-06-20 03:05:51 +02:00
a4b914d576
Use Ansible for deployment 2020-06-20 02:51:31 +02:00
3213e10b88
Add Sumologic and Sentry to the run parameters 2020-06-20 00:20:00 +02:00
f23b8099f1
Try to create a docker image 2020-06-19 22:20:05 +02:00
e0f17d00ea
Upgrade to .NET 5 2020-06-19 22:19:26 +02:00
a0b1ec44f6
Make it possible to connect to the database with ssl enabled 2020-06-19 21:46:19 +02:00
4655424fb0
Remove prometheus-net from the dependencies 2020-06-19 12:40:17 +02:00
9cc944fcc1
Use a single message for !mtg and show a searching message when the command starts executing 2020-06-19 12:28:22 +02:00
e564e80849
Copy every file in storage by default instead of listing every single one 2020-06-19 12:15:17 +02:00
76bb645394
Inline MTG Emoji converter 2020-06-19 12:08:28 +02:00
1ea851be22
Release Geekbot 4.2 2020-06-19 04:13:26 +02:00
fb676e8918
Add GuildSettingsManager to centrally manage guild settings 2020-06-19 04:10:26 +02:00
83dc2c8e49
Attempt to improve code quality with a fearless refactor
- Completely refactor Program.cs
- Split Handlers into separate files
- Split up the command handler into multiple functions
2020-06-19 03:34:37 +02:00
f7908c2a0c
- Remove accidental leftover from the GreetingProvider
- Add 2 additional aliases for !hello
- Change Romanization to Roman in the !hello embed
2020-06-19 00:10:43 +02:00
baea4528e1
Merge branch 'master' of gitlab.com:dbgit/open/geekbot 2020-06-18 19:19:12 +02:00
12199f4ad4
Add greetings in 299 languages 2020-06-18 19:19:02 +02:00
0898e9b6bd
Add the timeout in !roll back 2020-06-14 17:27:07 +02:00
8018d5e750
Allow configuration to be passed as environment variables 2020-06-01 15:22:41 +02:00
d91c21e607
update readme 2020-06-01 02:20:27 +02:00
6692b3bc77
Don't show a quote id when using '!quote make' 2020-06-01 02:17:15 +02:00
3108a68407
Dependency Upgrades 2020-06-01 02:16:29 +02:00
ff968a490f
Cleanup some dead code and database models 2020-06-01 01:13:45 +02:00
520633c590
Remove all redis leftovers 2020-06-01 01:02:36 +02:00
46fee88f03
Add a migration script for role listeners 2020-06-01 00:52:56 +02:00
33b17b373f
Remove all dependencies on redis 2020-05-30 17:02:17 +02:00
2e501008df
Remove !checkem 2020-05-21 15:55:04 +02:00
656393cc7b
'!quote stats' bug fixes 2020-05-12 00:04:20 +02:00
5cf1248bf0
Add !quote stats (v1) 2020-05-11 23:44:15 +02:00
c031d2bfb4
Remove Prometheus 2020-05-07 13:11:00 +02:00
fc5ff87c8f
Only the ManageMessages is now required to delete a quote 2020-04-19 13:52:44 +02:00
569715f124
fix prometheus metric server startup 2020-04-18 00:05:09 +02:00
ee548390a5
Add Prometheus with 1 metric 2020-04-17 23:48:50 +02:00
2a616f8c5d
Revert "Re-use the database connection everywhere"
This reverts commit 77b3d612f2.
2020-04-06 15:51:28 +02:00
77b3d612f2
Re-use the database connection everywhere 2020-04-06 15:35:28 +02:00
f12bdcf4cd
Translate the ship command 2020-04-04 23:05:18 +02:00
3bd7274d68
Remove some unnecessary text from the dice command 2020-04-04 21:21:15 +02:00
3568b61f38
Use the new Csharp 8 features (pattern matching and using assignments) and cleanup some insignificant resparper complaints 2020-02-08 15:58:17 +01:00
21f813d342
Remove redundant code 2020-02-08 15:42:42 +01:00
dd9cf3c5d7
Upgrade to dotnet core 3.1 2020-02-08 15:32:58 +01:00
cd04549834
Fix deployment to accomodate dotnet core 3 changes 2019-10-26 22:20:22 +02:00
20c75019f9
Upgrade to dotnet core 3 and update dependencies 2019-10-26 21:57:50 +02:00
8dd914e012
Properly log errors when adding a role emoji 2019-10-11 01:08:59 +02:00
b7b4a600cd
Fix message when a role has been added to the whitelist 2019-10-11 00:55:55 +02:00
19df65fc76
Fix out of bounds error in the urban dict. command 2019-09-19 21:27:34 +02:00
309f06370b
Cut urban dictionary word definitions at the 1800 character mark 2019-09-19 13:42:48 +02:00
f1387f824e
Remove the google command 2019-09-19 13:40:02 +02:00
143722eccf
Stop using redis for counting messages 2019-08-05 09:18:16 +02:00
1bfd7c7a12
Ignore the discord bots server aswell 2019-08-05 09:14:58 +02:00
fe4a78b743
Allow failure on sentry during deployment 2019-07-30 00:47:32 +02:00
7a250f6642
Ignore commands on certain serves 2019-07-30 00:42:08 +02:00
ac43d087b1
Add the !cookie alias and allow yaml alliases in the translations file 2019-07-21 13:04:41 +02:00
8fadff4092
Cookies timeout is now at midnight instead of every 24h 2019-06-04 23:00:16 +02:00
288c976674 rename bdcc to bdcb 2019-05-28 20:32:15 +02:00
ced287e492
Allow a server admin to specify a channel for the welcome message 2019-05-21 20:50:50 +02:00
8822e65e6a
Upgrade Discord.net to V2.1 2019-05-21 19:42:04 +02:00
57698c8ce6
Fix mtg issue and update json.net 2019-05-16 22:40:11 +02:00
8f861ee0bc
overload owner refreshuser with a user-id as parameter 2019-05-16 22:17:33 +02:00
e4fd828a13
Make the command documentation slightly more consistent across most all commands 2019-05-16 22:10:56 +02:00
03343918b0
Add a refresh user command to the owner commands 2019-05-16 22:01:35 +02:00
0e1084ccad
Remove Database migration scripts 2019-05-16 22:00:28 +02:00
cf5cb2ea86
Reformat bdcc 2019-05-16 21:54:13 +02:00
761ed7302c
Wrap bdcc in a try catch block 2019-05-15 22:18:02 +02:00
5f3d7f8a73
Add bdcc (Benedict Cumberbatch Name Generator) 2019-05-15 21:41:29 +02:00
f5cd0ffcc8
add unit test for making sure all translations keys exist 2019-05-12 22:05:35 +02:00
aef50aa2de
Revert "Use SortedDictionary for Highscores instead of a simple Dictionary"
This reverts commit 4833ccd6
2019-05-12 17:57:03 +02:00
4833ccd65a
Use SortedDictionary for Highscores instead of a simple Dictionary 2019-05-12 17:29:52 +02:00
3e3cbc257e
Fix bug where Enum.Parse would always pass if a number was given as argument in rank 2019-05-12 17:29:07 +02:00
5109cc03a4
Translate rank command 2019-05-12 16:38:43 +02:00
92c2d173eb
make HighscoreManager transient to solve a bug where the information reported would be wrong 2019-05-12 16:15:42 +02:00
5efb146d18
Fix Unit tests 2019-05-12 15:57:58 +02:00
53f894676c
Revert back to using strings in stead of arrays in the translations 2019-05-12 15:49:00 +02:00
e0e76d7c27
Use YAML for translations 2019-05-12 15:40:28 +02:00
e5742165d1
Parially translate quote 2019-05-12 15:29:22 +02:00
03d1607d64
Remove Poll command 2019-05-12 13:49:01 +02:00
c6271cbaa0
Remove AudioUtils 2019-05-12 13:48:37 +02:00
68a62ab4ec
Add translations for the role command 2019-05-12 13:47:15 +02:00
4b67bb51d1
Reflect cookies command change in translations 2019-05-12 12:31:31 +02:00
a86a33635c
Rename cookie command to cookies 2019-05-12 12:25:42 +02:00
7d7d393a3d
Add tests for FormatDateTimeAsRemaining 2019-05-12 01:59:55 +02:00
f9269b877c
Use typed TestData for unit tests 2019-05-12 01:38:56 +02:00
495288b887
Pass full translation dictionary to TranslationGuildContext and decide there whether to use singular or plural 2019-05-12 01:07:22 +02:00
b309f155be
Make GetDict private in the TranslationHandler 2019-05-12 00:56:22 +02:00
67add36e51
Remove all in-command usages of GetDict for translations 2019-05-12 00:44:25 +02:00
0f0c0684c6
Convert karma command to use new GuildContext for translations 2019-05-12 00:32:07 +02:00
8effc42f92
Rewrite huge parts of the localization class to support plurals and localized date time formating, also created a TranslationGuildContext class for use in commands 2019-05-12 00:17:34 +02:00
4143180b42
Add a better random number generator 2019-05-11 01:18:22 +02:00
fe08eee049
Let users eat their cookies 2019-05-10 22:42:53 +02:00
fd75fef973
Add the ability for users to give cookies away 2019-05-10 22:30:48 +02:00
ba4de946dc
Add translations for cookies/guetzli 2019-05-10 22:06:58 +02:00
d3038b90a8
Add Cookies to the highscore 2019-05-10 21:37:52 +02:00
1884fc5559 Merge branch 'cookies' into 'master'
Cookies 🍪

See merge request open/Geekbot.net!15
2019-05-10 19:20:57 +00:00
efaaf594f0
Make sure that stats works with the inmemory database and add cookies 2019-05-10 21:10:49 +02:00
119f4586c7
Add Some Cookies 2019-05-10 20:51:13 +02:00
b01094f4c2
Remove SAST from pipeline 2019-05-01 18:38:02 +02:00
38fc6c7f93
Copy dab file... 2019-05-01 18:34:42 +02:00
d761e0bf10
Add some dabbing 2019-05-01 18:22:44 +02:00
e4d71a7a99 Merge branch 'sast' into 'master'
Sast

See merge request open/Geekbot.net!14
2019-04-11 09:33:03 +00:00
8f7aa09763 Add SAST to the build pipeline 2019-04-11 09:33:03 +00:00
99ece47925
Release 4.1.0 2019-03-17 18:38:33 +01:00
de408dbfd9
Apply DM Disable attribute to some commands 2019-03-17 18:33:18 +01:00
1e98b44cb7
Add Attribute to disable commands in DMs 2019-03-17 18:32:56 +01:00
3d493fa531
Add kanye command 2019-03-17 17:52:07 +01:00
0ae9dcce67
Update build image for gitlab-ci to match dotnet 2.2 2019-03-04 15:41:09 -05:00
7dbb2ab89f
Tell the user if the mal api refuses to answer 2019-03-03 21:14:17 -05:00
c1ff11ca34
Fix bug in TranslationsHandler where language lookup would fail if user messaged the bot directly 2019-03-03 21:01:47 -05:00
f4897f7f2e
Comment out unused code in Poll.cs 2019-03-03 20:39:31 -05:00
dbd36a62d3
Disable Stats Updater when using the InMemory database 2019-03-03 20:38:45 -05:00
a1e0c35dd4
Upgrade to DotNet Core 2.2 2019-03-03 20:27:50 -05:00
3f083b520d
Add Sentry Release Reporting (attempt 3) 2019-03-02 02:12:03 -05:00
3a6dd8ea10
Add Sentry Release Reporting (attempt 2) 2019-03-02 02:06:46 -05:00
809adee778
Add Sentry Release Reporting (attempt 1) 2019-03-02 01:58:56 -05:00
35a4f4a122
Limit MTG Page Size to 1 card 2019-03-02 01:21:42 -05:00
2b90358ca5
Remove Bugsnag 2019-03-02 00:50:54 -05:00
16f70d52ce
Some Upgrades 2019-03-02 00:48:16 -05:00
ff15e21b5a
Upgrade Gatherer Client and fix bug where card legalities could be an empty collection and throwing and exception as a result 2019-03-02 00:32:59 -05:00
aa059b5558
Update Discord.net 2019-03-02 00:12:35 -05:00
4e4f2e88d5
Fix deployment 2019-02-13 01:06:05 +01:00
4de8447c97
Fix roll command to write successful writes to the database instead of redis 2019-02-13 00:50:31 +01:00
8059f03304
Remove !say completly 2019-01-27 22:33:12 +01:00
2c6b61b7e6
Add parameter to enable db logging, disabled by default 2019-01-27 22:09:02 +01:00
26da438efe
Fix namehistory command 2018-11-29 18:14:41 +01:00
e8546f37f2
Disable the say command 2018-10-30 19:53:50 +09:00
458268bd41
Fix bug in stats where the requestor would always see his own message count 2018-10-08 22:15:07 +02:00
863fcd5e9d
Add bugsnag 2018-09-11 00:13:54 +02:00
99245b9ead
Refactor !rank, add HighscoreManager and add /v1/highscore to the api 2018-09-05 22:55:45 +02:00
a5c70859a4
Reenable Stats Counting 2018-09-05 21:22:56 +02:00
f0814ba685
Temporally disable message counting 2018-09-05 21:15:46 +02:00
fce6be56e2
Add Message migration script 2018-09-05 21:15:13 +02:00
a8c91c0372
Hide owner commands from command endpoint 2018-09-05 21:13:43 +02:00
6d3fc46e34
Add Callback Endpoint to API 2018-09-02 23:59:41 +02:00
d4c0899ba9
Fix dict lookup in globalSettings.cs 2018-09-02 23:59:26 +02:00
0d8c82f8aa
Add Caching to GlobalSettings 2018-09-02 22:42:13 +02:00
5a4e710ef9
NRE fixes in simpleMessageConverter, !urban and errorHandler 2018-09-02 21:58:16 +02:00
6a163366ea
Start counting messages in postgres 2018-08-29 23:01:10 +02:00
449e1e0adc
Update images in ci file 2018-08-29 22:49:13 +02:00
0d0f0550a5
Add source to dotnet restore in ci file 2018-08-29 22:39:35 +02:00
0e06e85447
Change the discord.net release branch 2018-08-29 22:39:35 +02:00
5db3187f0d
Change deployment IP 2018-08-29 22:39:35 +02:00
b0758eb119
Create multiple database contexts instead of one 2018-08-29 21:16:01 +02:00
d1e9992a8c
Add default values to guildsettings model 2018-08-26 18:05:50 +02:00
74619a4434
Solve casting issue 2018-08-26 17:54:26 +02:00
00da991dae
Update !rank error message 2018-08-26 17:39:01 +02:00
9dda829cf9
remove nuget caching during CI 2018-08-26 12:43:18 +02:00
707be4abe2
Trying to fix !rank again 2018-08-26 12:33:04 +02:00
cbe88a1721
Fix !rank 2018-08-26 00:31:59 +02:00
adb1614177
Update Deployment Script 2018-08-26 00:10:19 +02:00
954de7f8db
Revert "Updates to language handler"
This reverts commit 5b56c18
2018-08-25 23:27:44 +02:00
e18a512a9b
Update Migration Script 2018-08-25 23:26:46 +02:00
8a62f94e01
Revert !stats us pull message count from redis 2018-08-25 22:40:02 +02:00
5b56c1822f
Updates to language handler 2018-08-25 21:30:33 +02:00
28052a5ba9
Update logsource in redis migration 2018-07-28 19:12:50 +02:00
18b3436d66
add more awaits and update !roll 2018-07-28 16:31:18 +02:00
95618b1f8b
Make errorhandler and languagehandler async, await all database actions 2018-06-13 22:18:57 +02:00
926a632641
Get guilds from existing redis keys instead of the client 2018-06-02 00:16:32 +02:00
8b5e52c8e0
Actually upgrade to dotnet 2.1 2018-06-01 18:10:22 +02:00
f438914c19
Upgrade to dotnet 2.1 2018-05-30 23:54:00 +02:00
80c2bd8500
Lazyload server language setting in translationHandler 2018-05-26 02:55:18 +02:00
35f0a5c8f8
Abstract away redis even more with AlmostRedis.cs 2018-05-26 02:33:45 +02:00
f53258e348
Log errors with stacktrace in migration, skip 10k+ guilds, prevent migration from running twice by accident 2018-05-23 23:50:11 +02:00
9354e5f83e
Change User Model and Add poll model, port !poll but ended up disabling it 2018-05-19 10:49:01 +02:00
74793c8ef7
Dynamically get discord.net version in !info 2018-05-18 00:34:45 +02:00
6d708627f1
Add Dockerfile 2018-05-17 23:55:44 +02:00
4e4ef3584e
Upgrade to discord.net 2.0 2018-05-17 22:06:58 +02:00
acb2b25e09
Resharper cleanup and fixes and remove useless asp overhead 2018-05-15 01:18:26 +02:00
e0d6a97dca
make database saves async 2018-05-14 18:57:07 +02:00
8c107de92e
Replace nancy with kestrel 2018-05-14 02:33:49 +02:00
3004b19209
Small tweaks to the web api 2018-05-14 00:41:05 +02:00
4a11ce6207
Add Guild Settings to redis migration 2018-05-13 21:33:48 +02:00
08015c6102
Port !stats, !role, !wiki and !slap, fix messages in rank, add roleselfservicemodel to db 2018-05-13 21:06:41 +02:00
a1b5bd1955
Add more run parameters, remove first launch stuff, store token in globals 2018-05-13 18:47:01 +02:00
bb8aee1eda
Port !owner, !admin, !mod, handlers (partial) and add globals to the db 2018-05-13 17:49:13 +02:00
37ac7f56a8
Remove command categories 2018-05-13 15:55:57 +02:00
3fa4115502
Improve migration Script 2018-05-13 14:14:50 +02:00
32ae82ca8d
Add simple migration script 2018-05-11 00:36:29 +02:00
9ecc224ae1
Add ef cli tools 2018-05-10 20:12:31 +02:00
b54b7cd786
port !rank 2018-05-10 17:56:43 +02:00
54bcd541d3
Fix run params 2018-05-10 03:29:11 +02:00
5117e7609f
Port !karma 2018-05-10 02:33:10 +02:00
15034d63a3
Port UserRepository and remove OW stuff 2018-05-10 02:00:26 +02:00
d2f31d0730
Use Postgresql, add db run params, npgsql logging adapter and empty models 2018-05-10 00:00:51 +02:00
3425896c0b
Add database Initializer 2018-05-09 18:51:53 +02:00
177942f7fe
Save quotes to the database after adding them 2018-05-09 02:12:58 +02:00
510b030fec
Begining of implementing the entity framework 2018-05-09 01:21:39 +02:00
404 changed files with 13672 additions and 5899 deletions

38
.deploy.yml Normal file
View file

@ -0,0 +1,38 @@
---
- name: Geekbot Deploy
hosts: all
remote_user: geekbot
vars:
ansible_port: 65432
ansible_python_interpreter: /usr/bin/python3
tasks:
- name: Login to Gitlab Docker Registry
'community.docker.docker_login':
registry_url: "{{ lookup('env', 'CI_REGISTRY') }}"
username: "{{ lookup('env', 'CI_REGISTRY_USER') }}"
password: "{{ lookup('env', 'CI_REGISTRY_PASSWORD') }}"
reauthorize: yes
- name: Replace Prod Container
'community.docker.docker_container':
name: GeekbotProd
image: "{{ lookup('env', 'IMAGE_TAG') }}"
recreate: yes
pull: yes
restart_policy: always
keep_volumes: no
ports:
- "12995:12995"
env:
GEEKBOT_DB_HOST: "{{ lookup('env', 'GEEKBOT_DB_HOST') }}"
GEEKBOT_DB_USER: "{{ lookup('env', 'GEEKBOT_DB_USER') }}"
GEEKBOT_DB_PASSWORD: "{{ lookup('env', 'GEEKBOT_DB_PASSWORD') }}"
GEEKBOT_DB_PORT: "{{ lookup('env', 'GEEKBOT_DB_PORT') }}"
GEEKBOT_DB_DATABASE: "{{ lookup('env', 'GEEKBOT_DB_DATABASE') }}"
GEEKBOT_DB_REQUIRE_SSL: "true"
GEEKBOT_DB_TRUST_CERT: "true"
GEEKBOT_SUMOLOGIC: "{{ lookup('env', 'GEEKBOT_SUMOLOCIG') }}"
GEEKBOT_SENTRY: "{{ lookup('env', 'GEEKBOT_SENTRY') }}"
GEEKBOT_DB_REDSHIFT_COMPAT: "true"
- name: Cleanup Old Container
'community.docker.docker_prune':
images: yes

18
.gitignore vendored
View file

@ -1,16 +1,10 @@
Geekbot.net/bin
Geekbot.net/obj
Geekbot.net/tmp/
Tests/bin
Tests/obj
Backup/
/*/**/bin
/*/**/obj
src/Bot/tmp/
src/Bot/Logs/*
!/src/Bot/Logs/.keep
.vs/
UpgradeLog.htm
.idea
.vscode
Geekbot.net/Logs/*
!/Geekbot.net/Logs/.keep
Geekbot.net.sln.DotSettings.user
Geekbot.net/temp/
WikipediaApi/bin/
WikipediaApi/obj/
app

View file

@ -1,54 +1,69 @@
stages:
- build
- deploy
before_script:
- set -e
- set -u
- set -o pipefail
build:
stage: build
image: microsoft/dotnet:2.0.3-sdk-stretch
variables:
NUGET_PACKAGES: "${CI_PROJECT_DIR}/.nugetcache"
cache:
paths:
- .nugetcache
artifacts:
expire_in: 1h
paths:
- Geekbot.net/Binaries/
script:
- dotnet restore
- dotnet test Tests
- dotnet publish --version-suffix ${CI_COMMIT_SHA:0:8} --configuration Release -o Binaries ./
deploy:
stage: deploy
image: instrumentisto/rsync-ssh
only:
- master
dependencies:
- build
environment:
name: Production
url: https://discordapp.com/oauth2/authorize?client_id=171249478546882561&scope=bot&permissions=1416834054
before_script:
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n StrictHostKeyChecking no" > ~/.ssh/config'
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- chmod 700 ~/.ssh
script:
- rsync -rav -e "ssh -p 65432" ./Geekbot.net/Binaries/* www-data@31.220.42.224:$DEPPATH
- ssh -p 65432 www-data@31.220.42.224 "sudo systemctl restart geekbot.service"
mirror:
stage: deploy
image: bravissimolabs/alpine-git:latest
only:
- master
script:
- git push https://runebaas:$TOKEN@github.com/pizzaandcoffee/Geekbot.net.git origin/master:master -f
stages:
- build
- docker
- deploy
- ops
variables:
VERSION: 4.4.0-V$CI_COMMIT_SHORT_SHA
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
Build:
stage: build
image: mcr.microsoft.com/dotnet/sdk:6.0
artifacts:
expire_in: 1h
paths:
- app
script:
- dotnet restore
- dotnet test tests
- dotnet publish --version-suffix "$VERSION" -r linux-x64 -c Release -p:DebugType=embedded --no-self-contained -o ./app ./src/Startup/
Package:
stage: docker
image: docker
only:
- master
services:
- docker:stable-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
Deploy:
stage: deploy
image: quay.io/ansible/ansible-runner:stable-2.12-latest
only:
- master
variables:
ANSIBLE_NOCOWS: 1
before_script:
- mkdir /root/.ssh
- cp $SSH_PRIVATE_KEY /root/.ssh/id_ed25519
- cp $SSH_PUBLIC_KEY /root/.ssh/id_ed25519.pub
- chmod -R 600 /root/.ssh
- ssh-keyscan -p 65432 $PROD_IP > /root/.ssh/known_hosts
script:
- ansible-galaxy collection install -r ansible-requirements.yml
- ansible-playbook -i $PROD_IP, .deploy.yml
Sentry:
stage: ops
image: getsentry/sentry-cli
allow_failure: true
only:
- master
script:
- sentry-cli releases new -p geekbot $VERSION
- sentry-cli releases set-commits --auto $VERSION
- sentry-cli releases deploys $VERSION new -e Production
Github Mirror:
stage: ops
image: runebaas/rsync-ssh-git
only:
- master
script:
- git push https://runebaas:$TOKEN@github.com/pizzaandcoffee/Geekbot.net.git origin/master:master -f

7
Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:6.0
COPY ./app /app/
EXPOSE 12995/tcp
WORKDIR /app
ENTRYPOINT ./Geekbot

View file

@ -3,11 +3,19 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.0.0
MinimumVisualStudioVersion = 10.0.0.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geekbot.net", "Geekbot.net/Geekbot.net.csproj", "{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "tests\Tests.csproj", "{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{47671723-52A9-4668-BBC5-2BA76AE3B288}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WikipediaApi", "WikipediaApi\WikipediaApi.csproj", "{1084D499-EF94-4834-9E6A-B2AD81B60078}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "src\Web\Web.csproj", "{0A63D5DC-6325-4F53-8ED2-9843239B76CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bot", "src\Bot\Bot.csproj", "{DBF79896-9F7F-443D-B336-155E276DFF16}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands", "src\Commands\Commands.csproj", "{7C771DFE-912A-4276-B0A6-047E09603F1E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Interactions", "src\Interactions\Interactions.csproj", "{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Startup", "src\Startup\Startup.csproj", "{A691B018-4B19-4A7A-A0F6-DBB17641254F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -15,18 +23,34 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Release|Any CPU.Build.0 = Release|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Release|Any CPU.Build.0 = Release|Any CPU
{1084D499-EF94-4834-9E6A-B2AD81B60078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1084D499-EF94-4834-9E6A-B2AD81B60078}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1084D499-EF94-4834-9E6A-B2AD81B60078}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1084D499-EF94-4834-9E6A-B2AD81B60078}.Release|Any CPU.Build.0 = Release|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Release|Any CPU.Build.0 = Release|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Release|Any CPU.Build.0 = Release|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Release|Any CPU.Build.0 = Release|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Release|Any CPU.Build.0 = Release|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Release|Any CPU.Build.0 = Release|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,12 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">200</s:Int64>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -1,196 +0,0 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Localization;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Admin
{
[Group("admin")]
[RequireUserPermission(GuildPermission.Administrator)]
public class Admin : ModuleBase
{
private readonly DiscordSocketClient _client;
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly ITranslationHandler _translation;
public Admin(IDatabase redis, DiscordSocketClient client, IErrorHandler errorHandler,
ITranslationHandler translationHandler)
{
_redis = redis;
_client = client;
_errorHandler = errorHandler;
_translation = translationHandler;
}
[Command("welcome", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Set a Welcome Message (use '$user' to mention the new joined user).")]
public async Task SetWelcomeMessage([Remainder] [Summary("message")] string welcomeMessage)
{
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("WelcomeMsg", welcomeMessage)});
var formatedMessage = welcomeMessage.Replace("$user", Context.User.Mention);
await ReplyAsync($"Welcome message has been changed\r\nHere is an example of how it would look:\r\n{formatedMessage}");
}
[Command("modchannel", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Set a channel for moderation purposes")]
public async Task SelectModChannel([Summary("#Channel")] ISocketMessageChannel channel)
{
try
{
var sb = new StringBuilder();
sb.AppendLine("Successfully saved mod channel, you can now do the following");
sb.AppendLine("- `!admin showleave true` - send message to mod channel when someone leaves");
sb.AppendLine("- `!admin showdel true` - send message to mod channel when someone deletes a message");
await channel.SendMessageAsync(sb.ToString());
_redis.HashSet($"{Context.Guild.Id}:Settings",
new[] {new HashEntry("ModChannel", channel.Id.ToString())});
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context, "That channel doesn't seem to be valid");
}
}
[Command("showleave", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Notify modchannel when someone leaves")]
public async Task ShowLeave([Summary("true/false")] bool enabled)
{
var modChannelId = ulong.Parse(_redis.HashGet($"{Context.Guild.Id}:Settings", "ModChannel"));
try
{
var modChannel = (ISocketMessageChannel) _client.GetChannel(modChannelId);
if (enabled)
{
await modChannel.SendMessageAsync("Saved - now sending messages here when someone leaves");
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("ShowLeave", true)});
}
else
{
await modChannel.SendMessageAsync("Saved - stopping sending messages here when someone leaves");
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("ShowLeave", false)});
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"Modchannel doesn't seem to exist, please set one with `!admin modchannel [channelId]`");
}
}
[Command("showdel", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Notify modchannel when someone deletes a message")]
public async Task ShowDelete([Summary("true/false")] bool enabled)
{
var modChannelId = ulong.Parse(_redis.HashGet($"{Context.Guild.Id}:Settings", "ModChannel"));
try
{
var modChannel = (ISocketMessageChannel) _client.GetChannel(modChannelId);
if (enabled)
{
await modChannel.SendMessageAsync(
"Saved - now sending messages here when someone deletes a message");
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("ShowDelete", true)});
}
else
{
await modChannel.SendMessageAsync(
"Saved - stopping sending messages here when someone deletes a message");
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("ShowDelete", false)});
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"Modchannel doesn't seem to exist, please set one with `!admin modchannel [channelId]`");
}
}
[Command("setlang", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Change the bots language")]
public async Task SetLanguage([Summary("language")] string languageRaw)
{
try
{
var language = languageRaw.ToUpper();
var success = _translation.SetLanguage(Context.Guild.Id, language);
if (success)
{
var trans = _translation.GetDict(Context);
await ReplyAsync(trans["NewLanguageSet"]);
return;
}
await ReplyAsync(
$"That doesn't seem to be a supported language\r\nSupported Languages are {string.Join(", ", _translation.GetSupportedLanguages())}");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("wiki", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Change the wikipedia instance (use lang code in xx.wikipedia.org)")]
public async Task SetWikiLanguage([Summary("language")] string languageRaw)
{
try
{
var language = languageRaw.ToLower();
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("WikiLang", language) });
await ReplyAsync($"Now using the {language} wikipedia");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("lang", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Change the bots language")]
public async Task GetLanguage()
{
try
{
var trans = _translation.GetDict(Context);
await ReplyAsync(trans["GetLanguage"]);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("ping", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Enable the ping reply.")]
public async Task TogglePing()
{
try
{
bool.TryParse(_redis.HashGet($"{Context.Guild.Id}:Settings", "ping"), out var current);
_redis.HashSet($"{Context.Guild.Id}:Settings", new[] {new HashEntry("ping", current ? "false" : "true") });
await ReplyAsync(!current ? "i will reply to ping now" : "No more pongs...");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,89 +0,0 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.UserRepository;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Admin
{
[Group("mod")]
[RequireUserPermission(GuildPermission.KickMembers)]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireUserPermission(GuildPermission.ManageRoles)]
public class Mod : ModuleBase
{
private readonly DiscordSocketClient _client;
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly IUserRepository _userRepository;
public Mod(IUserRepository userRepositry, IErrorHandler errorHandler, IDatabase redis,
DiscordSocketClient client)
{
_userRepository = userRepositry;
_errorHandler = errorHandler;
_redis = redis;
_client = client;
}
[Command("namehistory", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("See past usernames of an user")]
public async Task UsernameHistory([Summary("@user")] IUser user)
{
try
{
var userRepo = _userRepository.Get(user.Id);
var sb = new StringBuilder();
sb.AppendLine($":bust_in_silhouette: {user.Username} has been known as:");
foreach (var name in userRepo.UsedNames) sb.AppendLine($"- `{name}`");
await ReplyAsync(sb.ToString());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
$"I don't have enough permissions do that");
}
}
[Command("kick", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Ban a user")]
public async Task Kick([Summary("@user")] IUser userNormal,
[Summary("reason")] [Remainder] string reason = "none")
{
try
{
var user = (IGuildUser) userNormal;
if (reason == "none") reason = "No reason provided";
await user.GetOrCreateDMChannelAsync().Result.SendMessageAsync(
$"You have been kicked from {Context.Guild.Name} for the following reason: \"{reason}\"");
await user.KickAsync();
try
{
var modChannelId = ulong.Parse(_redis.HashGet($"{Context.Guild.Id}:Settings", "ModChannel"));
var modChannel = (ISocketMessageChannel) _client.GetChannel(modChannelId);
var eb = new EmbedBuilder();
eb.Title = ":x: User Kicked";
eb.AddInlineField("User", user.Username);
eb.AddInlineField("By Mod", Context.User.Username);
eb.AddField("Reason", reason);
await modChannel.SendMessageAsync("", false, eb.Build());
}
catch
{
await ReplyAsync($"{user.Username} was kicked for the following reason: \"{reason}\"");
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context, "I don't have enough permissions to kick someone");
}
}
}
}

View file

@ -1,94 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Logger;
using Geekbot.net.Lib.UserRepository;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Admin
{
[Group("owner")]
[RequireOwner]
public class Owner : ModuleBase
{
private readonly DiscordSocketClient _client;
private readonly IErrorHandler _errorHandler;
private readonly IGeekbotLogger _logger;
private readonly IDatabase _redis;
private readonly IUserRepository _userRepository;
public Owner(IDatabase redis, DiscordSocketClient client, IGeekbotLogger logger, IUserRepository userRepositry, IErrorHandler errorHandler)
{
_redis = redis;
_client = client;
_logger = logger;
_userRepository = userRepositry;
_errorHandler = errorHandler;
}
[Command("youtubekey", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Set the youtube api key")]
public async Task SetYoutubeKey([Summary("API Key")] string key)
{
_redis.StringSet("youtubeKey", key);
await ReplyAsync("Apikey has been set");
}
[Command("game", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Set the game that the bot is playing")]
public async Task SetGame([Remainder] [Summary("Game")] string key)
{
_redis.StringSet("Game", key);
await _client.SetGameAsync(key);
_logger.Information(LogSource.Geekbot, $"Changed game to {key}");
await ReplyAsync($"Now Playing {key}");
}
[Command("popuserrepo", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Populate user cache")]
public async Task PopUserRepoCommand()
{
var success = 0;
var failed = 0;
try
{
_logger.Warning(LogSource.UserRepository, "Populating User Repositry");
await ReplyAsync("Starting Population of User Repository");
foreach (var guild in _client.Guilds)
{
_logger.Information(LogSource.UserRepository, $"Populating users from {guild.Name}");
foreach (var user in guild.Users)
{
var succeded = await _userRepository.Update(user);
var inc = succeded ? success++ : failed++;
}
}
_logger.Warning(LogSource.UserRepository, "Finished Updating User Repositry");
await ReplyAsync(
$"Successfully Populated User Repository with {success} Users in {_client.Guilds.Count} Guilds (Failed: {failed})");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"Couldn't complete User Repository, see console for more info");
}
}
[Command("error", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Throw an error un purpose")]
public void PurposefulError()
{
var e = new Exception("Error Generated by !owner error");
_errorHandler.HandleCommandException(e, Context);
}
}
}

View file

@ -1,193 +0,0 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Net;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.ReactionListener;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Admin
{
[Group("role")]
public class Role : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly IReactionListener _reactionListener;
public Role(IErrorHandler errorHandler, IDatabase redis, IReactionListener reactionListener)
{
_errorHandler = errorHandler;
_redis = redis;
_reactionListener = reactionListener;
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get a list of all available roles.")]
public async Task GetAllRoles()
{
try
{
var roles = _redis.HashGetAll($"{Context.Guild.Id}:RoleWhitelist");
if (roles.Length == 0)
{
await ReplyAsync("There are no roles configured for this server");
return;
}
var sb = new StringBuilder();
sb.AppendLine($"**Self Service Roles on {Context.Guild.Name}**");
sb.AppendLine("To get a role, use `!role name`");
foreach (var role in roles) sb.AppendLine($"- {role.Name}");
await ReplyAsync(sb.ToString());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get a role by mentioning it.")]
public async Task GiveRole([Summary("roleNickname")] string roleNameRaw)
{
try
{
var roleName = roleNameRaw.ToLower();
if (_redis.HashExists($"{Context.Guild.Id}:RoleWhitelist", roleName))
{
var guildUser = (IGuildUser) Context.User;
var roleId = ulong.Parse(_redis.HashGet($"{Context.Guild.Id}:RoleWhitelist", roleName));
var role = Context.Guild.Roles.First(r => r.Id == roleId);
if (role == null)
{
await ReplyAsync("That role doesn't seem to exist");
return;
}
if (guildUser.RoleIds.Contains(roleId))
{
await guildUser.RemoveRoleAsync(role);
await ReplyAsync($"Removed you from {role.Name}");
return;
}
await guildUser.AddRoleAsync(role);
await ReplyAsync($"Added you to {role.Name}");
return;
}
await ReplyAsync("That role doesn't seem to exist");
}
catch (HttpException e)
{
_errorHandler.HandleHttpException(e, Context);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.ManageRoles)]
[Command("add", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Add a role to the whitelist.")]
public async Task AddRole([Summary("@role")] IRole role, [Summary("alias")] string roleName)
{
try
{
if (role.IsManaged)
{
await ReplyAsync("You can't add a role that is managed by discord");
return;
}
if (role.Permissions.ManageRoles
|| role.Permissions.Administrator
|| role.Permissions.ManageGuild
|| role.Permissions.BanMembers
|| role.Permissions.KickMembers)
{
await ReplyAsync(
"Woah, i don't think you want to add that role to self service as it contains some dangerous permissions");
return;
}
_redis.HashSet($"{Context.Guild.Id}:RoleWhitelist",
new[] {new HashEntry(roleName.ToLower(), role.Id.ToString())});
await ReplyAsync($"Added {role.Name} to the whitelist");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.ManageRoles)]
[Command("remove", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Remove a role from the whitelist.")]
public async Task RemoveRole([Summary("roleNickname")] string roleName)
{
try
{
var success = _redis.HashDelete($"{Context.Guild.Id}:RoleWhitelist", roleName.ToLower());
if (success)
{
await ReplyAsync($"Removed {roleName} from the whitelist");
return;
}
await ReplyAsync("There is not whitelisted role with that name...");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.ManageRoles)]
[Remarks(CommandCategories.Admin)]
[Summary("Give a role by clicking on an emoji")]
[Command("listen", RunMode = RunMode.Async)]
public async Task AddListener([Summary("messageID")] string messageId, [Summary("Emoji")] string emoji, [Summary("@role")] IRole role)
{
try
{
var message = (IUserMessage) await Context.Channel.GetMessageAsync(ulong.Parse(messageId));
IEmote emote;
if (!emoji.StartsWith('<'))
{
var emo = new Emoji(emoji);
emote = emo;
}
else
{
emote = Emote.Parse(emoji);
}
await message.AddReactionAsync(emote);
await _reactionListener.AddRoleToListener(messageId, emote, role);
await Context.Message.DeleteAsync();
}
catch (HttpException e)
{
await Context.Channel.SendMessageAsync("Custom emojis from other servers are not supported");
Console.WriteLine(e);
}
catch (Exception e)
{
await Context.Channel.SendMessageAsync("Something went wrong... please try again on a new message");
Console.WriteLine(e);
}
}
}
}

View file

@ -1,36 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
namespace Geekbot.net.Commands.Admin
{
public class Say : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public Say(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[RequireUserPermission(GuildPermission.Administrator)]
[Command("say", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Say Something.")]
public async Task Echo([Remainder] [Summary("What?")] string echo)
{
try
{
await Context.Message.DeleteAsync();
await ReplyAsync(echo);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,101 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib.Audio;
using Geekbot.net.Lib.ErrorHandling;
namespace Geekbot.net.Commands.Audio
{
public class Voice : ModuleBase
{
private readonly IAudioUtils _audioUtils;
private readonly IErrorHandler _errorHandler;
public Voice(IErrorHandler errorHandler, IAudioUtils audioUtils)
{
_errorHandler = errorHandler;
_audioUtils = audioUtils;
}
// [Command("join")]
public async Task JoinChannel()
{
try
{
// Get the audio channel
var channel = (Context.User as IGuildUser)?.VoiceChannel;
if (channel == null)
{
await Context.Channel.SendMessageAsync("You must be in a voice channel.");
return;
}
var audioClient = await channel.ConnectAsync();
_audioUtils.StoreAudioClient(Context.Guild.Id, audioClient);
await ReplyAsync($"Connected to {channel.Name}");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
// [Command("disconnect")]
public async Task DisconnectChannel()
{
try
{
var audioClient = _audioUtils.GetAudioClient(Context.Guild.Id);
if (audioClient == null)
{
await Context.Channel.SendMessageAsync("I'm not in a voice channel at the moment");
return;
}
await audioClient.StopAsync();
await ReplyAsync("Disconnected from channel!");
_audioUtils.Cleanup(Context.Guild.Id);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
_audioUtils.Cleanup(Context.Guild.Id);
}
}
// [Command("ytplay")]
public async Task ytplay(string url)
{
try
{
if (!url.Contains("youtube"))
{
await ReplyAsync("I can only play youtube videos");
return;
}
var audioClient = _audioUtils.GetAudioClient(Context.Guild.Id);
if (audioClient == null)
{
await ReplyAsync("I'm not in a voice channel at the moment");
return;
}
var message = await Context.Channel.SendMessageAsync("Just a second, i'm still a bit slow at this");
var ffmpeg = _audioUtils.CreateStreamFromYoutube(url, Context.Guild.Id);
var output = ffmpeg.StandardOutput.BaseStream;
await message.ModifyAsync(msg => msg.Content = "**Playing!** Please note that this feature is experimental");
var discord = audioClient.CreatePCMStream(Discord.Audio.AudioApplication.Mixed);
await output.CopyToAsync(discord);
await discord.FlushAsync();
_audioUtils.Cleanup(Context.Guild.Id);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
_audioUtils.Cleanup(Context.Guild.Id);
}
}
}
}

View file

@ -1,72 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.UserRepository;
namespace Geekbot.net.Commands.Games
{
[Group("battletag")]
public class BattleTag : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IUserRepository _userRepository;
public BattleTag(IErrorHandler errorHandler, IUserRepository userRepository)
{
_errorHandler = errorHandler;
_userRepository = userRepository;
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Games)]
[Summary("Get your battletag")]
public async Task BattleTagCmd()
{
try
{
var tag = _userRepository.GetUserSetting(Context.User.Id, "BattleTag");
if (!string.IsNullOrEmpty(tag))
await ReplyAsync($"Your BattleTag is {tag}");
else
await ReplyAsync("You haven't set your BattleTag, set it with `!battletag user#1234`");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Games)]
[Summary("Save your battletag")]
public async Task BattleTagCmd([Summary("Battletag")] string tag)
{
try
{
if (IsValidTag(tag))
{
_userRepository.SaveUserSetting(Context.User.Id, "BattleTag", tag);
await ReplyAsync("Saved!");
}
else
{
await ReplyAsync("That doesn't seem to be a valid battletag");
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
public static bool IsValidTag(string tag)
{
var splited = tag.Split("#");
if (splited.Length != 2) return false;
if (!int.TryParse(splited[1], out var discriminator)) return false;
return splited[1].Length == 4 || splited[1].Length == 5;
}
}
}

View file

@ -1,131 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.UserRepository;
using OverwatchAPI;
using OverwatchAPI.Config;
namespace Geekbot.net.Commands.Games
{
[Group("ow")]
public class Overwatch : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IUserRepository _userRepository;
public Overwatch(IErrorHandler errorHandler, IUserRepository userRepository)
{
_errorHandler = errorHandler;
_userRepository = userRepository;
}
[Command("profile", RunMode = RunMode.Async)]
[Summary("Get someones overwatch profile. EU on PC only. Default battletag is your own (if set).")]
[Remarks(CommandCategories.Games)]
public async Task OwProfile()
{
try
{
var tag = _userRepository.GetUserSetting(Context.User.Id, "BattleTag");
if (string.IsNullOrEmpty(tag))
{
await ReplyAsync("You have no battle Tag saved, use `!battletag`");
return;
}
var profile = await CreateProfile(tag);
if (profile == null)
{
await ReplyAsync("That player doesn't seem to exist");
return;
}
await ReplyAsync("", false, profile.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("profile", RunMode = RunMode.Async)]
[Summary("Get someones overwatch profile. EU on PC only. Default battletag is your own (if set).")]
[Remarks(CommandCategories.Games)]
public async Task OwProfile([Summary("BattleTag")] string tag)
{
try
{
if (!BattleTag.IsValidTag(tag))
{
await ReplyAsync("That doesn't seem to be a valid battletag...");
return;
}
var profile = await CreateProfile(tag);
if (profile == null)
{
await ReplyAsync("That player doesn't seem to exist");
return;
}
await ReplyAsync("", false, profile.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("profile", RunMode = RunMode.Async)]
[Summary("Get someones overwatch profile. EU on PC only.")]
[Remarks(CommandCategories.Games)]
public async Task OwProfile([Summary("@someone")] IUser user)
{
try
{
var tag = _userRepository.GetUserSetting(user.Id, "BattleTag");
if (string.IsNullOrEmpty(tag))
{
await ReplyAsync("This user didn't set a battletag");
return;
}
var profile = await CreateProfile(tag);
if (profile == null)
{
await ReplyAsync("That player doesn't seem to exist");
return;
}
await ReplyAsync("", false, profile.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private async Task<EmbedBuilder> CreateProfile(string battletag)
{
var owConfig = new OverwatchConfig.Builder().WithPlatforms(Platform.Pc);
using (var owClient = new OverwatchClient(owConfig))
{
var player = await owClient.GetPlayerAsync(battletag);
if (player.Username == null) return null;
var eb = new EmbedBuilder();
eb.WithAuthor(new EmbedAuthorBuilder()
.WithIconUrl(player.ProfilePortraitUrl)
.WithName(player.Username));
eb.Url = player.ProfileUrl;
eb.AddInlineField("Level", player.PlayerLevel);
eb.AddInlineField("Current Rank",
player.CompetitiveRank > 0 ? player.CompetitiveRank.ToString() : "Unranked");
return eb;
}
}
}
}

View file

@ -1,64 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Localization;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Games
{
public class Roll : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly ITranslationHandler _translation;
public Roll(IDatabase redis, IErrorHandler errorHandler, ITranslationHandler translation)
{
_redis = redis;
_translation = translation;
_errorHandler = errorHandler;
}
[Command("roll", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Fun)]
[Summary("Guess which number the bot will roll (1-100")]
public async Task RollCommand([Remainder] [Summary("guess")] string stuff = "noGuess")
{
try
{
var number = new Random().Next(1, 100);
var guess = 1000;
int.TryParse(stuff, out guess);
var transDict = _translation.GetDict(Context);
if (guess <= 100 && guess > 0)
{
var prevRoll = _redis.HashGet($"{Context.Guild.Id}:RollsPrevious", Context.Message.Author.Id);
if (!prevRoll.IsNullOrEmpty && prevRoll.ToString() == guess.ToString())
{
await ReplyAsync(string.Format(transDict["NoPrevGuess"], Context.Message.Author.Mention));
return;
}
_redis.HashSet($"{Context.Guild.Id}:RollsPrevious",
new[] {new HashEntry(Context.Message.Author.Id, guess)});
await ReplyAsync(string.Format(transDict["Rolled"], Context.Message.Author.Mention, number, guess));
if (guess == number)
{
await ReplyAsync(string.Format(transDict["Gratz"], Context.Message.Author));
_redis.HashIncrement($"{Context.Guild.Id}:Rolls", Context.User.Id.ToString());
}
}
else
{
await ReplyAsync(string.Format(transDict["RolledNoGuess"], Context.Message.Author.Mention, number));
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,68 +0,0 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Integrations.Google
{
public class Google : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Google(IErrorHandler errorHandler, IDatabase redis)
{
_errorHandler = errorHandler;
_redis = redis;
}
[Command("google", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Google Something.")]
public async Task AskGoogle([Remainder, Summary("SearchText")] string searchText)
{
try
{
using (var client = new WebClient())
{
var apiKey = _redis.StringGet("googleGraphKey");
if (!apiKey.HasValue)
{
await ReplyAsync("No Google API key has been set, please contact my owner");
return;
}
var url = new Uri($"https://kgsearch.googleapis.com/v1/entities:search?languages=en&limit=1&query={searchText}&key={apiKey}");
var responseString = client.DownloadString(url);
var response = JsonConvert.DeserializeObject<GoogleKgApiResponseDto>(responseString);
if (!response.ItemListElement.Any())
{
await ReplyAsync("No results were found...");
return;
}
var data = response.ItemListElement.First().Result;
var eb = new EmbedBuilder();
eb.Title = data.Name;
if(!string.IsNullOrEmpty(data.Description)) eb.WithDescription(data.Description);
if(!string.IsNullOrEmpty(data.DetailedDtoDescription?.Url)) eb.WithUrl(data.DetailedDtoDescription.Url);
if(!string.IsNullOrEmpty(data.DetailedDtoDescription?.ArticleBody)) eb.AddField("Details", data.DetailedDtoDescription.ArticleBody);
if(!string.IsNullOrEmpty(data.Image?.ContentUrl)) eb.WithThumbnailUrl(data.Image.ContentUrl);
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,9 +0,0 @@
namespace Geekbot.net.Commands.Integrations.Google
{
public class GoogleKgApiDetailedDto
{
public string ArticleBody { get; set; }
public string Url { get; set; }
public string License { get; set; }
}
}

View file

@ -1,8 +0,0 @@
namespace Geekbot.net.Commands.Integrations.Google
{
public class GoogleKgApiElementDto
{
public GoogleKgApiResultDto Result { get; set; }
public double ResultScore { get; set; }
}
}

View file

@ -1,8 +0,0 @@
namespace Geekbot.net.Commands.Integrations.Google
{
public class GoogleKgApiImageDto
{
public string ContentUrl { get; set; }
public string Url { get; set; }
}
}

View file

@ -1,9 +0,0 @@
using System.Collections.Generic;
namespace Geekbot.net.Commands.Integrations.Google
{
public class GoogleKgApiResponseDto
{
public List<GoogleKgApiElementDto> ItemListElement { get; set; }
}
}

View file

@ -1,10 +0,0 @@
namespace Geekbot.net.Commands.Integrations.Google
{
public class GoogleKgApiResultDto
{
public string Name { get; set; }
public string Description { get; set; }
public GoogleKgApiImageDto Image { get; set; }
public GoogleKgApiDetailedDto DetailedDtoDescription { get; set; }
}
}

View file

@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Converters;
using Geekbot.net.Lib.ErrorHandling;
using MtgApiManager.Lib.Service;
namespace Geekbot.net.Commands.Integrations
{
public class Magicthegathering : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IMtgManaConverter _manaConverter;
public Magicthegathering(IErrorHandler errorHandler, IMtgManaConverter manaConverter)
{
_errorHandler = errorHandler;
_manaConverter = manaConverter;
}
[Command("mtg", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Games)]
[Summary("Find a Magic The Gathering Card.")]
public async Task GetCard([Remainder] [Summary("name")] string cardName)
{
try
{
var service = new CardService();
var result = service.Where(x => x.Name, cardName);
var card = result.All().Value.FirstOrDefault();
if (card == null)
{
await ReplyAsync("I couldn't find that card...");
return;
}
var eb = new EmbedBuilder();
eb.Title = card.Name;
eb.Description = card.Type;
if (card.Colors != null) eb.WithColor(GetColor(card.Colors));
if (card.ImageUrl != null) eb.ImageUrl = card.ImageUrl.ToString();
if (!string.IsNullOrEmpty(card.Text)) eb.AddField("Text", _manaConverter.ConvertMana(card.Text));
if (!string.IsNullOrEmpty(card.Flavor)) eb.AddField("Flavor", card.Flavor);
if (!string.IsNullOrEmpty(card.SetName)) eb.AddInlineField("Set", card.SetName);
if (!string.IsNullOrEmpty(card.Power)) eb.AddInlineField("Power", card.Power);
if (!string.IsNullOrEmpty(card.Loyalty)) eb.AddInlineField("Loyality", card.Loyalty);
if (!string.IsNullOrEmpty(card.Toughness)) eb.AddInlineField("Thoughness", card.Toughness);
if (!string.IsNullOrEmpty(card.ManaCost)) eb.AddInlineField("Cost", _manaConverter.ConvertMana(card.ManaCost));
if (!string.IsNullOrEmpty(card.Rarity)) eb.AddInlineField("Rarity", card.Rarity);
if (card.Legalities != null)
eb.AddField("Legality", string.Join(", ", card.Legalities.Select(e => e.Format)));
await ReplyAsync("", false, eb.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private Color GetColor(IEnumerable<string> colors)
{
var color = colors.FirstOrDefault();
switch (color)
{
case "Black":
return new Color(203, 194, 191);
case "White":
return new Color(255, 251, 213);
case "Blue":
return new Color(170, 224, 250);
case "Red":
return new Color(250, 170, 143);
case "Green":
return new Color(155, 211, 174);
default:
return new Color(204, 194, 212);
}
}
}
}

View file

@ -1,121 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Web;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Clients;
using Geekbot.net.Lib.ErrorHandling;
namespace Geekbot.net.Commands.Integrations
{
public class Mal : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IMalClient _malClient;
public Mal(IMalClient malClient, IErrorHandler errorHandler)
{
_malClient = malClient;
_errorHandler = errorHandler;
}
[Command("anime", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Show Info about an Anime.")]
public async Task SearchAnime([Remainder] [Summary("AnimeName")] string animeName)
{
try
{
if (_malClient.IsLoggedIn())
{
var anime = await _malClient.GetAnime(animeName);
if (anime != null)
{
var eb = new EmbedBuilder();
var description = HttpUtility.HtmlDecode(anime.Synopsis)
.Replace("<br />", "")
.Replace("[i]", "*")
.Replace("[/i]", "*");
eb.Title = anime.Title;
eb.Description = description;
eb.ImageUrl = anime.Image;
eb.AddInlineField("Premiered", $"{anime.StartDate}");
eb.AddInlineField("Ended", anime.EndDate == "0000-00-00" ? "???" : anime.EndDate);
eb.AddInlineField("Status", anime.Status);
eb.AddInlineField("Episodes", anime.Episodes);
eb.AddInlineField("MAL Score", anime.Score);
eb.AddInlineField("Type", anime.Type);
eb.AddField("MAL Link", $"https://myanimelist.net/anime/{anime.Id}");
await ReplyAsync("", false, eb.Build());
}
else
{
await ReplyAsync("No anime found with that name...");
}
}
else
{
await ReplyAsync(
"Unfortunally i'm not connected to MyAnimeList.net, please tell my senpai to connect me");
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("manga", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Show Info about a Manga.")]
public async Task SearchManga([Remainder] [Summary("MangaName")] string mangaName)
{
try
{
if (_malClient.IsLoggedIn())
{
var manga = await _malClient.GetManga(mangaName);
if (manga != null)
{
var eb = new EmbedBuilder();
var description = HttpUtility.HtmlDecode(manga.Synopsis)
.Replace("<br />", "")
.Replace("[i]", "*")
.Replace("[/i]", "*");
eb.Title = manga.Title;
eb.Description = description;
eb.ImageUrl = manga.Image;
eb.AddInlineField("Premiered", $"{manga.StartDate}");
eb.AddInlineField("Ended", manga.EndDate == "0000-00-00" ? "???" : manga.EndDate);
eb.AddInlineField("Status", manga.Status);
eb.AddInlineField("Volumes", manga.Volumes);
eb.AddInlineField("Chapters", manga.Chapters);
eb.AddInlineField("MAL Score", manga.Score);
eb.AddField("MAL Link", $"https://myanimelist.net/manga/{manga.Id}");
await ReplyAsync("", false, eb.Build());
}
else
{
await ReplyAsync("No manga found with that name...");
}
}
else
{
await ReplyAsync(
"Unfortunally i'm not connected to MyAnimeList.net, please tell my senpai to connect me");
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,12 +0,0 @@
namespace Geekbot.net.Commands.Integrations.UbranDictionary
{
internal class UrbanListItemDto
{
public string Definition { get; set; }
public string Permalink { get; set; }
public string ThumbsUp { get; set; }
public string Word { get; set; }
public string Example { get; set; }
public string ThumbsDown { get; set; }
}
}

View file

@ -1,10 +0,0 @@
using System.Collections.Generic;
namespace Geekbot.net.Commands.Integrations.UbranDictionary
{
internal class UrbanResponseDto
{
public string[] Tags { get; set; }
public List<UrbanListItemDto> List { get; set; }
}
}

View file

@ -1,67 +0,0 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
namespace Geekbot.net.Commands.Integrations.UbranDictionary
{
public class UrbanDictionary : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public UrbanDictionary(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("urban", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Lookup something on urban dictionary")]
public async Task UrbanDefine([Remainder] [Summary("word")] string word)
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.urbandictionary.com");
var response = await client.GetAsync($"/v0/define?term={word}");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var definitions = JsonConvert.DeserializeObject<UrbanResponseDto>(stringResponse);
if (definitions.List.Count == 0)
{
await ReplyAsync("That word hasn't been defined...");
return;
}
var definition = definitions.List.First(e => !string.IsNullOrWhiteSpace(e.Example));
var eb = new EmbedBuilder();
eb.WithAuthor(new EmbedAuthorBuilder
{
Name = definition.Word,
Url = definition.Permalink
});
eb.WithColor(new Color(239, 255, 0));
if (!string.IsNullOrEmpty(definition.Definition)) eb.Description = definition.Definition;
if (!string.IsNullOrEmpty(definition.Example)) eb.AddField("Example", definition.Example ?? "(no example given...)");
if (!string.IsNullOrEmpty(definition.ThumbsUp)) eb.AddInlineField("Upvotes", definition.ThumbsUp);
if (!string.IsNullOrEmpty(definition.ThumbsDown)) eb.AddInlineField("Downvotes", definition.ThumbsDown);
if (definitions.Tags.Length > 0) eb.AddField("Tags", string.Join(", ", definitions.Tags));
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,60 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Integrations
{
public class Youtube : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Youtube(IDatabase redis, IErrorHandler errorHandler)
{
_redis = redis;
_errorHandler = errorHandler;
}
[Command("yt", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Search for something on youtube.")]
public async Task Yt([Remainder] [Summary("Title")] string searchQuery)
{
var key = _redis.StringGet("youtubeKey");
if (key.IsNullOrEmpty)
{
await ReplyAsync("No youtube key set, please tell my senpai to set one");
return;
}
try
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer
{
ApiKey = key.ToString(),
ApplicationName = GetType().ToString()
});
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = searchQuery;
searchListRequest.MaxResults = 2;
var searchListResponse = await searchListRequest.ExecuteAsync();
var result = searchListResponse.Items[0];
await ReplyAsync(
$"\"{result.Snippet.Title}\" from \"{result.Snippet.ChannelTitle}\" https://youtu.be/{result.Id.VideoId}");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,54 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
namespace Geekbot.net.Commands.Randomness.Cat
{
public class Cat : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public Cat(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("cat", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Return a random image of a cat.")]
public async Task Say()
{
try
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("https://aws.random.cat");
var response = await client.GetAsync("/meow");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var catFile = JsonConvert.DeserializeObject<CatResponseDto>(stringResponse);
var eb = new EmbedBuilder();
eb.ImageUrl = catFile.File;
await ReplyAsync("", false, eb.Build());
}
catch
{
await ReplyAsync("Seems like the dog cought the cat (error occured)");
}
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,7 +0,0 @@
namespace Geekbot.net.Commands.Randomness.Cat
{
internal class CatResponseDto
{
public string File { get; set; }
}
}

View file

@ -1,73 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Media;
namespace Geekbot.net.Commands.Randomness
{
public class CheckEm : ModuleBase
{
private readonly IMediaProvider _checkEmImages;
private readonly IErrorHandler _errorHandler;
public CheckEm(IMediaProvider mediaProvider, IErrorHandler errorHandler)
{
_checkEmImages = mediaProvider;
_errorHandler = errorHandler;
}
[Command("checkem", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Check for dubs")]
public async Task MuhDubs()
{
try
{
var number = new Random().Next(10000000, 99999999);
var dubtriqua = "";
var ns = GetIntArray(number);
if (ns[7] == ns[6])
{
dubtriqua = "DUBS";
if (ns[6] == ns[5])
{
dubtriqua = "TRIPS";
if (ns[5] == ns[4])
dubtriqua = "QUADS";
}
}
var sb = new StringBuilder();
sb.AppendLine($"Check em {Context.User.Mention}");
sb.AppendLine($"**{number}**");
if (!string.IsNullOrEmpty(dubtriqua))
sb.AppendLine($":tada: {dubtriqua} :tada:");
sb.AppendLine(_checkEmImages.GetCheckem());
await ReplyAsync(sb.ToString());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private int[] GetIntArray(int num)
{
var listOfInts = new List<int>();
while (num > 0)
{
listOfInts.Add(num % 10);
num = num / 10;
}
listOfInts.Reverse();
return listOfInts.ToArray();
}
}
}

View file

@ -1,7 +0,0 @@
namespace Geekbot.net.Commands.Randomness.Chuck
{
internal class ChuckNorrisJokeResponseDto
{
public string Value { get; set; }
}
}

View file

@ -1,53 +0,0 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
namespace Geekbot.net.Commands.Randomness.Chuck
{
public class ChuckNorrisJokes : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public ChuckNorrisJokes(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("chuck", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("A random chuck norris joke")]
public async Task Say()
{
try
{
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
var response = await client.GetAsync("https://api.chucknorris.io/jokes/random");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<ChuckNorrisJokeResponseDto>(stringResponse);
await ReplyAsync(data.Value);
}
catch (HttpRequestException)
{
await ReplyAsync("Api down...");
}
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,7 +0,0 @@
namespace Geekbot.net.Commands.Randomness.Dad
{
internal class DadJokeResponseDto
{
public string Joke { get; set; }
}
}

View file

@ -1,53 +0,0 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
namespace Geekbot.net.Commands.Randomness.Dad
{
public class DadJokes : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public DadJokes(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("dad", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("A random dad joke")]
public async Task Say()
{
try
{
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
var response = await client.GetAsync("https://icanhazdadjoke.com/");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<DadJokeResponseDto>(stringResponse);
await ReplyAsync(data.Joke);
}
catch (HttpRequestException)
{
await ReplyAsync("Api down...");
}
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,54 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
namespace Geekbot.net.Commands.Randomness.Dog
{
public class Dog : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public Dog(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("dog", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Return a random image of a dog.")]
public async Task Say()
{
try
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://random.dog");
var response = await client.GetAsync("/woof.json");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var dogFile = JsonConvert.DeserializeObject<DogResponseDto>(stringResponse);
var eb = new EmbedBuilder();
eb.ImageUrl = dogFile.Url;
await ReplyAsync("", false, eb.Build());
}
catch (HttpRequestException e)
{
await ReplyAsync($"Seems like the dog got lost (error occured)\r\n{e.Message}");
}
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,7 +0,0 @@
namespace Geekbot.net.Commands.Randomness.Dog
{
internal class DogResponseDto
{
public string Url { get; set; }
}
}

View file

@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
namespace Geekbot.net.Commands.Randomness
{
public class EightBall : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public EightBall(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("8ball", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Ask 8Ball a Question.")]
public async Task Ball([Remainder] [Summary("Question")] string echo)
{
try
{
var replies = new List<string>
{
"It is certain",
"It is decidedly so",
"Without a doubt",
"Yes, definitely",
"You may rely on it",
"As I see it, yes",
"Most likely",
"Outlook good",
"Yes",
"Signs point to yes",
"Reply hazy try again",
"Ask again later",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
"Don't count on it",
"My reply is no",
"My sources say no",
"Outlook not so good",
"Very doubtful"
};
var answer = new Random().Next(replies.Count);
await ReplyAsync(replies[answer]);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,40 +0,0 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
namespace Geekbot.net.Commands.Randomness
{
public class Gdq : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public Gdq(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("gdq", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Games)]
[Summary("Get a quote from the GDQ donation generator.")]
public async Task GetQuote()
{
try
{
using (var client = new WebClient())
{
var url = new Uri("http://taskinoz.com/gdq/api/");
var response = client.DownloadString(url);
await ReplyAsync(response);
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,91 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Randomness
{
public class Ship : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Ship(IDatabase redis, IErrorHandler errorHandler)
{
_redis = redis;
_errorHandler = errorHandler;
}
[Command("Ship", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Fun)]
[Summary("Ask the Shipping meter")]
public async Task Command([Summary("@User1")] IUser user1, [Summary("@User2")] IUser user2)
{
try
{
var dbstring = "";
if (user1.Id > user2.Id)
dbstring = $"{user1.Id}-{user2.Id}";
else
dbstring = $"{user2.Id}-{user1.Id}";
var dbval = _redis.HashGet($"{Context.Guild.Id}:Ships", dbstring);
var shippingRate = 0;
if (dbval.IsNullOrEmpty)
{
shippingRate = new Random().Next(1, 100);
_redis.HashSet($"{Context.Guild.Id}:Ships", dbstring, shippingRate);
}
else
{
shippingRate = int.Parse(dbval.ToString());
}
var reply = ":heartpulse: **Matchmaking** :heartpulse:\r\n";
reply = reply + $":two_hearts: {user1.Mention} :heart: {user2.Mention} :two_hearts:\r\n";
reply = reply + $"0% [{BlockCounter(shippingRate)}] 100% - {DeterminateSuccess(shippingRate)}";
await ReplyAsync(reply);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private string DeterminateSuccess(int rate)
{
if (rate < 20)
return "Not gonna happen";
if (rate >= 20 && rate < 40)
return "Not such a good idea";
if (rate >= 40 && rate < 60)
return "There might be a chance";
if (rate >= 60 && rate < 80)
return "Almost a match, but could work";
return rate >= 80 ? "It's a match" : "a";
}
private string BlockCounter(int rate)
{
var amount = Math.Floor(decimal.Floor(rate / 10));
Console.WriteLine(amount);
var blocks = "";
for (var i = 1; i <= 10; i++)
if (i <= amount)
{
blocks = blocks + ":white_medium_small_square:";
if (i == amount)
blocks = blocks + $" {rate}% ";
}
else
{
blocks = blocks + ":black_medium_small_square:";
}
return blocks;
}
}
}

View file

@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Randomness
{
public class Slap : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Slap(IErrorHandler errorHandler, IDatabase redis)
{
_errorHandler = errorHandler;
_redis = redis;
}
[Command("slap", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Fun)]
[Summary("slap someone")]
public async Task Slapper([Summary("@user")] IUser user)
{
try
{
if (user.Id == Context.User.Id)
{
await ReplyAsync("Why would you slap yourself?");
return;
}
var things = new List<string>
{
"thing",
"rubber chicken",
"leek stick",
"large trout",
"flat hand",
"strip of bacon",
"feather",
"piece of pizza",
"moldy banana",
"sharp retort",
"printed version of wikipedia",
"panda paw",
"spiked sledgehammer",
"monstertruck",
"dirty toilet brush",
"sleeping seagull",
"sunflower",
"mousepad",
"lolipop",
"bottle of rum",
"cheese slice",
"critical 1",
"natural 20",
"mjölnir (aka mewmew)",
"kamehameha",
"copy of Twilight",
"med pack (get ready for the end boss)",
"derp",
"condom (used)",
"gremlin fed after midnight",
"wet baguette",
"exploding kitten",
"shiny piece of shit",
"mismatched pair of socks",
"horcrux",
"tuna",
"suggestion",
"teapot",
"candle",
"dictionary",
"powerless banhammer"
};
_redis.HashIncrement($"{Context.Guild.Id}:SlapsRecieved", user.Id.ToString());
_redis.HashIncrement($"{Context.Guild.Id}:SlapsGiven", Context.User.Id.ToString());
await ReplyAsync($"{Context.User.Username} slapped {user.Username} with a {things[new Random().Next(things.Count - 1)]}");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,129 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Localization;
using StackExchange.Redis;
namespace Geekbot.net.Commands.User
{
public class Karma : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly ITranslationHandler _translation;
public Karma(IDatabase redis, IErrorHandler errorHandler, ITranslationHandler translation)
{
_redis = redis;
_errorHandler = errorHandler;
_translation = translation;
}
[Command("good", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Karma)]
[Summary("Increase Someones Karma")]
public async Task Good([Summary("@someone")] IUser user)
{
try
{
var transDict = _translation.GetDict(Context);
var lastKarmaFromRedis = _redis.HashGet($"{Context.Guild.Id}:KarmaTimeout", Context.User.Id.ToString());
var lastKarma = ConvertToDateTimeOffset(lastKarmaFromRedis.ToString());
if (user.Id == Context.User.Id)
{
await ReplyAsync(string.Format(transDict["CannotChangeOwn"], Context.User.Username));
}
else if (TimeoutFinished(lastKarma))
{
await ReplyAsync(string.Format(transDict["WaitUntill"], Context.User.Username,
GetTimeLeft(lastKarma)));
}
else
{
var newKarma = _redis.HashIncrement($"{Context.Guild.Id}:Karma", user.Id.ToString());
_redis.HashSet($"{Context.Guild.Id}:KarmaTimeout",
new[] {new HashEntry(Context.User.Id.ToString(), DateTimeOffset.Now.ToString("u"))});
var eb = new EmbedBuilder();
eb.WithAuthor(new EmbedAuthorBuilder()
.WithIconUrl(user.GetAvatarUrl())
.WithName(user.Username));
eb.WithColor(new Color(138, 219, 146));
eb.Title = transDict["Increased"];
eb.AddInlineField(transDict["By"], Context.User.Username);
eb.AddInlineField(transDict["Amount"], "+1");
eb.AddInlineField(transDict["Current"], newKarma);
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("bad", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Karma)]
[Summary("Decrease Someones Karma")]
public async Task Bad([Summary("@someone")] IUser user)
{
try
{
var transDict = _translation.GetDict(Context);
var lastKarmaFromRedis = _redis.HashGet($"{Context.Guild.Id}:KarmaTimeout", Context.User.Id.ToString());
var lastKarma = ConvertToDateTimeOffset(lastKarmaFromRedis.ToString());
if (user.Id == Context.User.Id)
{
await ReplyAsync(string.Format(transDict["CannotChangeOwn"], Context.User.Username));
}
else if (TimeoutFinished(lastKarma))
{
await ReplyAsync(string.Format(transDict["WaitUntill"], Context.User.Username,
GetTimeLeft(lastKarma)));
}
else
{
var newKarma = _redis.HashDecrement($"{Context.Guild.Id}:Karma", user.Id.ToString());
_redis.HashSet($"{Context.Guild.Id}:KarmaTimeout",
new[] {new HashEntry(Context.User.Id.ToString(), DateTimeOffset.Now.ToString())});
var eb = new EmbedBuilder();
eb.WithAuthor(new EmbedAuthorBuilder()
.WithIconUrl(user.GetAvatarUrl())
.WithName(user.Username));
eb.WithColor(new Color(138, 219, 146));
eb.Title = transDict["Decreased"];
eb.AddInlineField(transDict["By"], Context.User.Username);
eb.AddInlineField(transDict["Amount"], "-1");
eb.AddInlineField(transDict["Current"], newKarma);
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private DateTimeOffset ConvertToDateTimeOffset(string dateTimeOffsetString)
{
return string.IsNullOrEmpty(dateTimeOffsetString) ? DateTimeOffset.Now.Subtract(new TimeSpan(7, 18, 0, 0)) : DateTimeOffset.Parse(dateTimeOffsetString);
}
private bool TimeoutFinished(DateTimeOffset lastKarma)
{
return lastKarma.AddMinutes(3) > DateTimeOffset.Now;
}
private string GetTimeLeft(DateTimeOffset lastKarma)
{
var dt = lastKarma.AddMinutes(3).Subtract(DateTimeOffset.Now);
return $"{dt.Minutes} Minutes and {dt.Seconds} Seconds";
}
}
}

View file

@ -1,149 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Converters;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Logger;
using Geekbot.net.Lib.UserRepository;
using StackExchange.Redis;
namespace Geekbot.net.Commands.User.Ranking
{
public class Rank : ModuleBase
{
private readonly IEmojiConverter _emojiConverter;
private readonly IErrorHandler _errorHandler;
private readonly IGeekbotLogger _logger;
private readonly IDatabase _redis;
private readonly IUserRepository _userRepository;
private readonly DiscordSocketClient _client;
public Rank(IDatabase redis, IErrorHandler errorHandler, IGeekbotLogger logger, IUserRepository userRepository,
IEmojiConverter emojiConverter, DiscordSocketClient client)
{
_redis = redis;
_errorHandler = errorHandler;
_logger = logger;
_userRepository = userRepository;
_emojiConverter = emojiConverter;
_client = client;
}
[Command("rank", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Statistics)]
[Summary("get user top 10 in messages or karma")]
public async Task RankCmd([Summary("type")] string typeUnformated = "messages", [Summary("amount")] int amount = 10)
{
try
{
var type = typeUnformated.ToCharArray().First().ToString().ToUpper() + typeUnformated.Substring(1);
if (!type.Equals("Messages") && !type.Equals("Karma") && !type.Equals("Rolls"))
{
await ReplyAsync("Valid types are '`messages`' '`karma`', '`rolls`'");
return;
}
var replyBuilder = new StringBuilder();
if (amount > 20)
{
replyBuilder.AppendLine(":warning: Limiting to 20");
amount = 20;
}
var messageList = _redis.HashGetAll($"{Context.Guild.Id}:{type}");
if (messageList.Length == 0)
{
await ReplyAsync($"No {type.ToLowerInvariant()} found on this server");
return;
}
var sortedList = messageList.OrderByDescending(e => e.Value).ToList();
var guildMessages = (int) sortedList.First().Value;
var theBot = sortedList.FirstOrDefault(e => e.Name.ToString().Equals(_client.CurrentUser.Id.ToString()));
if (!string.IsNullOrEmpty(theBot.Name))
{
sortedList.Remove(theBot);
}
if (type == "Messages") sortedList.RemoveAt(0);
var highscoreUsers = new Dictionary<RankUserPolyfillDto, int>();
var listLimiter = 1;
var failedToRetrieveUser = false;
foreach (var user in sortedList)
{
if (listLimiter > amount) break;
try
{
var guildUser = _userRepository.Get((ulong) user.Name);
if (guildUser.Username != null)
{
highscoreUsers.Add(new RankUserPolyfillDto
{
Username = guildUser.Username,
Discriminator = guildUser.Discriminator
}, (int) user.Value);
}
else
{
highscoreUsers.Add(new RankUserPolyfillDto
{
Id = user.Name
}, (int) user.Value);
failedToRetrieveUser = true;
}
listLimiter++;
}
catch (Exception e)
{
_logger.Warning(LogSource.Geekbot, $"Could not retrieve user {user.Name}", e);
}
}
if (failedToRetrieveUser) replyBuilder.AppendLine(":warning: Couldn't get all userdata\n");
replyBuilder.AppendLine($":bar_chart: **{type} Highscore for {Context.Guild.Name}**");
var highscorePlace = 1;
foreach (var user in highscoreUsers)
{
replyBuilder.Append(highscorePlace < 11
? $"{_emojiConverter.NumberToEmoji(highscorePlace)} "
: $"`{highscorePlace}.` ");
replyBuilder.Append(user.Key.Username != null
? $"**{user.Key.Username}#{user.Key.Discriminator}**"
: $"**{user.Key.Id}**");
switch (type)
{
case "Messages":
var percent = Math.Round((double) (100 * user.Value) / guildMessages, 2);
replyBuilder.Append($" - {percent}% of total - {user.Value} messages");
break;
case "Karma":
replyBuilder.Append($" - {user.Value} Karma");
break;
case "Rolls":
replyBuilder.Append($" - {user.Value} Guessed");
break;
}
replyBuilder.Append("\n");
highscorePlace++;
}
await ReplyAsync(replyBuilder.ToString());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,74 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Levels;
using StackExchange.Redis;
namespace Geekbot.net.Commands.User
{
public class Stats : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly ILevelCalc _levelCalc;
private readonly IDatabase _redis;
public Stats(IDatabase redis, IErrorHandler errorHandler, ILevelCalc levelCalc)
{
_redis = redis;
_errorHandler = errorHandler;
_levelCalc = levelCalc;
}
[Command("stats", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Statistics)]
[Summary("Get information about this user")]
public async Task User([Summary("@someone")] IUser user = null)
{
try
{
var userInfo = user ?? Context.Message.Author;
var userGuildInfo = (IGuildUser) userInfo;
var createdAt = userInfo.CreatedAt;
var joinedAt = userGuildInfo.JoinedAt.Value;
var age = Math.Floor((DateTime.Now - createdAt).TotalDays);
var joinedDayAgo = Math.Floor((DateTime.Now - joinedAt).TotalDays);
var messages = (int) _redis.HashGet($"{Context.Guild.Id}:Messages", userInfo.Id.ToString());
var guildMessages = (int) _redis.HashGet($"{Context.Guild.Id}:Messages", 0.ToString());
var level = _levelCalc.GetLevel(messages);
var percent = Math.Round((double) (100 * messages) / guildMessages, 2);
var eb = new EmbedBuilder();
eb.WithAuthor(new EmbedAuthorBuilder()
.WithIconUrl(userInfo.GetAvatarUrl())
.WithName(userInfo.Username));
eb.WithColor(new Color(221, 255, 119));
var karma = _redis.HashGet($"{Context.Guild.Id}:Karma", userInfo.Id.ToString());
var correctRolls = _redis.HashGet($"{Context.Guild.Id}:Rolls", userInfo.Id.ToString());
eb.AddInlineField("Discordian Since",
$"{createdAt.Day}.{createdAt.Month}.{createdAt.Year} ({age} days)")
.AddInlineField("Joined Server",
$"{joinedAt.Day}.{joinedAt.Month}.{joinedAt.Year} ({joinedDayAgo} days)")
.AddInlineField("Karma", karma.ToString() ?? "0")
.AddInlineField("Level", level)
.AddInlineField("Messages Sent", messages)
.AddInlineField("Server Total", $"{percent}%");
if (!correctRolls.IsNullOrEmpty)
eb.AddInlineField("Guessed Rolls", correctRolls);
await ReplyAsync("", false, eb.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Newtonsoft.Json;
namespace Geekbot.net.Commands.Utils.Changelog
{
public class Changelog : ModuleBase
{
private readonly DiscordSocketClient _client;
private readonly IErrorHandler _errorHandler;
public Changelog(IErrorHandler errorHandler, DiscordSocketClient client)
{
_errorHandler = errorHandler;
_client = client;
}
[Command("changelog", RunMode = RunMode.Async)]
[Alias("updates")]
[Remarks(CommandCategories.Helpers)]
[Summary("Show the latest 5 updates")]
public async Task GetChangelog()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent",
"http://developer.github.com/v3/#user-agent-required");
var response = await client.GetAsync("/repos/pizzaandcoffee/geekbot.net/commits");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var commits = JsonConvert.DeserializeObject<List<CommitDto>>(stringResponse);
var eb = new EmbedBuilder();
eb.WithColor(new Color(143, 165, 102));
eb.WithAuthor(new EmbedAuthorBuilder
{
IconUrl = _client.CurrentUser.GetAvatarUrl(),
Name = "Latest Updates",
Url = "https://geekbot.pizzaandcoffee.rocks/updates"
});
var sb = new StringBuilder();
foreach (var commit in commits.Take(10))
sb.AppendLine($"- {commit.Commit.Message} ({commit.Commit.Author.Date:yyyy-MM-dd})");
eb.Description = sb.ToString();
eb.WithFooter(new EmbedFooterBuilder
{
Text = $"List generated from github commits on {DateTime.Now:yyyy-MM-dd}"
});
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,11 +0,0 @@
using System;
namespace Geekbot.net.Commands.Utils.Changelog
{
public class CommitAuthorDto
{
public string Name { get; set; }
public string Email { get; set; }
public DateTimeOffset Date { get; set; }
}
}

View file

@ -1,7 +0,0 @@
namespace Geekbot.net.Commands.Utils.Changelog
{
public class CommitDto
{
public CommitInfoDto Commit { get; set; }
}
}

View file

@ -1,8 +0,0 @@
namespace Geekbot.net.Commands.Utils.Changelog
{
public class CommitInfoDto
{
public CommitAuthorDto Author { get; set; }
public string Message { get; set; }
}
}

View file

@ -1,40 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Localization;
namespace Geekbot.net.Commands.Utils
{
public class Choose : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly ITranslationHandler _translation;
public Choose(IErrorHandler errorHandler, ITranslationHandler translation)
{
_errorHandler = errorHandler;
_translation = translation;
}
[Command("choose", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Let the bot choose for you, seperate options with a semicolon.")]
public async Task Command([Remainder] [Summary("option1;option2")]
string choices)
{
try
{
var transDict = _translation.GetDict(Context);
var choicesArray = choices.Split(';');
var choice = new Random().Next(choicesArray.Length);
await ReplyAsync(string.Format(transDict["Choice"], choicesArray[choice]));
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,115 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands.Utils.Dice
{
public class Dice : ModuleBase
{
[Command("dice", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Roll a dice.")]
public async Task RollCommand([Remainder] [Summary("diceType")] string diceType = "1d20")
{
var splitedDices = diceType.Split("+");
var dices = new List<DiceTypeDto>();
var mod = 0;
foreach (var i in splitedDices)
{
var dice = ToDice(i);
if (dice.Sides != 0 && dice.Times != 0)
{
dices.Add(dice);
}
else if (dice.Mod != 0)
{
if (mod != 0)
{
await ReplyAsync("You can only have one mod");
return;
}
mod = dice.Mod;
}
}
if (!dices.Any())
{
await ReplyAsync(
"That is not a valid dice, examples are: 1d20, 1d6, 2d6, 1d6+2, 1d6+2d8+1d20+6, etc...");
return;
}
if (dices.Any(d => d.Times > 20))
{
await ReplyAsync("You can't throw more than 20 dices");
return;
}
if (dices.Any(d => d.Sides > 144))
{
await ReplyAsync("A dice can't have more than 144 sides");
return;
}
var rep = new StringBuilder();
rep.AppendLine($":game_die: {Context.User.Mention}");
rep.Append("**Result:** ");
var resultStrings = new List<string>();
var total = 0;
var extraText = "";
foreach (var dice in dices)
{
var results = new List<int>();
for (var i = 0; i < dice.Times; i++)
{
var roll = new Random().Next(1, dice.Sides);
total += roll;
results.Add(roll);
if (roll == dice.Sides) extraText = "**Critical Hit!**";
if (roll == 1) extraText = "**Critical Fail!**";
}
resultStrings.Add($"{dice.DiceType} ({string.Join(",", results)})");
}
rep.Append(string.Join(" + ", resultStrings));
if (mod != 0)
{
rep.Append($" + {mod}");
total += mod;
}
rep.AppendLine();
rep.AppendLine($"**Total:** {total}");
if (extraText != "") rep.AppendLine(extraText);
await ReplyAsync(rep.ToString());
}
private DiceTypeDto ToDice(string dice)
{
var diceParts = dice.Split('d');
if (diceParts.Length == 2
&& int.TryParse(diceParts[0], out var times)
&& int.TryParse(diceParts[1], out var max))
return new DiceTypeDto
{
DiceType = dice,
Times = times,
Sides = max
};
if (dice.Length == 1
&& int.TryParse(diceParts[0], out var mod))
return new DiceTypeDto
{
Mod = mod
};
return new DiceTypeDto();
}
}
}

View file

@ -1,10 +0,0 @@
namespace Geekbot.net.Commands.Utils.Dice
{
internal class DiceTypeDto
{
public string DiceType { get; set; }
public int Times { get; set; }
public int Sides { get; set; }
public int Mod { get; set; }
}
}

View file

@ -1,179 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Converters;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.UserRepository;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Utils.Poll
{
[Group("poll")]
public class Poll : ModuleBase
{
private readonly IEmojiConverter _emojiConverter;
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly IUserRepository _userRepository;
public Poll(IErrorHandler errorHandler, IDatabase redis, IEmojiConverter emojiConverter,
IUserRepository userRepository)
{
_errorHandler = errorHandler;
_redis = redis;
_emojiConverter = emojiConverter;
_userRepository = userRepository;
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Check status of the current poll")]
public async Task Dflt()
{
try
{
var currentPoll = GetCurrentPoll();
if (currentPoll.Question == null || currentPoll.IsFinshed)
{
await ReplyAsync(
"There is no poll in this channel ongoing at the moment\r\nYou can create one with `!poll create question;option1;option2;option3`");
return;
}
await ReplyAsync("There is a poll running at the moment");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("create", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Create a poll")]
public async Task Create([Remainder] [Summary("question;option1;option2")]
string rawPollString)
{
try
{
var currentPoll = GetCurrentPoll();
if (currentPoll.Question != null && !currentPoll.IsFinshed)
{
await ReplyAsync("You have not finished you last poll yet. To finish it use `!poll end`");
return;
}
var pollList = rawPollString.Split(';').ToList();
if (pollList.Count <= 2)
{
await ReplyAsync(
"You need a question with atleast 2 options, a valid creation would look like this `question;option1;option2`");
return;
}
var eb = new EmbedBuilder();
eb.Title = $"Poll by {Context.User.Username}";
var question = pollList[0];
eb.Description = question;
pollList.RemoveAt(0);
var i = 1;
pollList.ForEach(option =>
{
eb.AddInlineField($"Option {_emojiConverter.NumberToEmoji(i)}", option);
i++;
});
var pollMessage = await ReplyAsync("", false, eb.Build());
i = 1;
pollList.ForEach(option =>
{
pollMessage.AddReactionAsync(new Emoji(_emojiConverter.NumberToEmoji(i)));
i++;
});
var poll = new PollDataDto
{
Creator = Context.User.Id,
MessageId = pollMessage.Id,
IsFinshed = false,
Question = question,
Options = pollList
};
var pollJson = JsonConvert.SerializeObject(poll);
_redis.HashSet($"{Context.Guild.Id}:Polls", new[] {new HashEntry(Context.Channel.Id, pollJson)});
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("end", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("End the current poll")]
public async Task End()
{
try
{
var currentPoll = GetCurrentPoll();
if (currentPoll.Question == null || currentPoll.IsFinshed)
{
await ReplyAsync("There is no ongoing poll at the moment");
return;
}
var results = await GetPollResults(currentPoll);
var sb = new StringBuilder();
sb.AppendLine("**Poll Results**");
sb.AppendLine(currentPoll.Question);
foreach (var result in results) sb.AppendLine($"{result.VoteCount} - {result.Option}");
await ReplyAsync(sb.ToString());
currentPoll.IsFinshed = true;
var pollJson = JsonConvert.SerializeObject(currentPoll);
_redis.HashSet($"{Context.Guild.Id}:Polls", new[] {new HashEntry(Context.Channel.Id, pollJson)});
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private PollDataDto GetCurrentPoll()
{
try
{
var currentPoll = _redis.HashGet($"{Context.Guild.Id}:Polls", Context.Channel.Id);
return JsonConvert.DeserializeObject<PollDataDto>(currentPoll.ToString());
}
catch
{
return new PollDataDto();
}
}
private async Task<List<PollResultDto>> GetPollResults(PollDataDto poll)
{
var message = (IUserMessage) await Context.Channel.GetMessageAsync(poll.MessageId);
var results = new List<PollResultDto>();
foreach (var r in message.Reactions)
try
{
var option = int.Parse(r.Key.Name.ToCharArray()[0].ToString());
var result = new PollResultDto
{
Option = poll.Options[option - 1],
VoteCount = r.Value.ReactionCount
};
results.Add(result);
}
catch {}
results.Sort((x, y) => y.VoteCount.CompareTo(x.VoteCount));
return results;
}
}
}

View file

@ -1,13 +0,0 @@
using System.Collections.Generic;
namespace Geekbot.net.Commands.Utils.Poll
{
internal class PollDataDto
{
public ulong Creator { get; set; }
public ulong MessageId { get; set; }
public bool IsFinshed { get; set; }
public string Question { get; set; }
public List<string> Options { get; set; }
}
}

View file

@ -1,8 +0,0 @@
namespace Geekbot.net.Commands.Utils.Poll
{
internal class PollResultDto
{
public string Option { get; set; }
public int VoteCount { get; set; }
}
}

View file

@ -1,240 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Polyfills;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands.Utils.Quote
{
[Group("quote")]
public class Quote : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Quote(IDatabase redis, IErrorHandler errorHandler)
{
_redis = redis;
_errorHandler = errorHandler;
}
[Command]
[Remarks(CommandCategories.Quotes)]
[Summary("Return a random quoute from the database")]
public async Task GetRandomQuote()
{
try
{
var randomQuotes = _redis.SetMembers($"{Context.Guild.Id}:Quotes");
if (!randomQuotes.Any())
{
await ReplyAsync("This server doesn't seem to have any quotes yet. You can add a quote with `!quote save @user` or `!quote save <messageId>`");
return;
}
var randomNumber = new Random().Next(randomQuotes.Length - 1);
var randomQuote = randomQuotes[randomNumber];
var quote = JsonConvert.DeserializeObject<QuoteObjectDto>(randomQuote);
var embed = QuoteBuilder(quote, randomNumber + 1);
await ReplyAsync("", false, embed.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context, "Whoops, seems like the quote was to edgy to return");
}
}
[Command("save")]
[Remarks(CommandCategories.Quotes)]
[Summary("Save a quote from the last sent message by @user")]
public async Task SaveQuote([Summary("@user")] IUser user)
{
try
{
if (user.Id == Context.Message.Author.Id)
{
await ReplyAsync("You can't save your own quotes...");
return;
}
if (user.IsBot)
{
await ReplyAsync("You can't save quotes by a bot...");
return;
}
var lastMessage = await GetLastMessageByUser(user);
if (lastMessage == null) return;
var quote = CreateQuoteObject(lastMessage);
var quoteStore = JsonConvert.SerializeObject(quote);
_redis.SetAdd($"{Context.Guild.Id}:Quotes", quoteStore);
var embed = QuoteBuilder(quote);
await ReplyAsync("**Quote Added**", false, embed.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"I counldn't find a quote from that user :disappointed:");
}
}
[Command("save")]
[Remarks(CommandCategories.Quotes)]
[Summary("Save a quote from a message id")]
public async Task SaveQuote([Summary("messageId")] ulong messageId)
{
try
{
var message = await Context.Channel.GetMessageAsync(messageId);
if (message.Author.Id == Context.Message.Author.Id)
{
await ReplyAsync("You can't save your own quotes...");
return;
}
if (message.Author.IsBot)
{
await ReplyAsync("You can't save quotes by a bot...");
return;
}
var quote = CreateQuoteObject(message);
var quoteStore = JsonConvert.SerializeObject(quote);
_redis.SetAdd($"{Context.Guild.Id}:Quotes", quoteStore);
var embed = QuoteBuilder(quote);
await ReplyAsync("**Quote Added**", false, embed.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"I couldn't find a message with that id :disappointed:");
}
}
[Command("make")]
[Remarks(CommandCategories.Quotes)]
[Summary("Create a quote from the last sent message by @user")]
public async Task ReturnSpecifiedQuote([Summary("@user")] IUser user)
{
try
{
var lastMessage = await GetLastMessageByUser(user);
if (lastMessage == null) return;
var quote = CreateQuoteObject(lastMessage);
var embed = QuoteBuilder(quote);
await ReplyAsync("", false, embed.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"I counldn't find a quote from that user :disappointed:");
}
}
[Command("make")]
[Remarks(CommandCategories.Quotes)]
[Summary("Create a quote from a message id")]
public async Task ReturnSpecifiedQuote([Summary("messageId")] ulong messageId)
{
try
{
var message = await Context.Channel.GetMessageAsync(messageId);
var quote = CreateQuoteObject(message);
var embed = QuoteBuilder(quote);
await ReplyAsync("", false, embed.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"I couldn't find a message with that id :disappointed:");
}
}
[Command("remove")]
[RequireUserPermission(GuildPermission.KickMembers)]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[Remarks(CommandCategories.Quotes)]
[Summary("Remove a quote (required mod permissions)")]
public async Task RemoveQuote([Summary("quoteId")] int id)
{
try
{
var quotes = _redis.SetMembers($"{Context.Guild.Id}:Quotes");
var success = _redis.SetRemove($"{Context.Guild.Id}:Quotes", quotes[id - 1]);
if (success)
{
var quote = JsonConvert.DeserializeObject<QuoteObjectDto>(quotes[id - 1]);
var embed = QuoteBuilder(quote);
await ReplyAsync($"**Removed #{id}**", false, embed.Build());
}
else
{
await ReplyAsync("I couldn't find a quote with that id :disappointed:");
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"I couldn't find a quote with that id :disappointed:");
}
}
private async Task<IMessage> GetLastMessageByUser(IUser user)
{
try
{
var list = Context.Channel.GetMessagesAsync().Flatten();
await list;
return list.Result
.First(msg => msg.Author.Id == user.Id
&& msg.Embeds.Count == 0
&& msg.Id != Context.Message.Id
&& !msg.Content.ToLower().StartsWith("!"));
}
catch
{
await ReplyAsync($"No quoteable message have been sent by {user.Username} in this channel");
return null;
}
}
private EmbedBuilder QuoteBuilder(QuoteObjectDto quote, int id = 0)
{
var user = Context.Client.GetUserAsync(quote.UserId).Result ?? new UserPolyfillDto { Username = "Unknown User" };
var eb = new EmbedBuilder();
eb.WithColor(new Color(143, 167, 232));
eb.Title = id == 0 ? "" : $"#{id} | ";
eb.Title += $"{user.Username} @ {quote.Time.Day}.{quote.Time.Month}.{quote.Time.Year}";
eb.Description = quote.Quote;
eb.ThumbnailUrl = user.GetAvatarUrl();
if (quote.Image != null) eb.ImageUrl = quote.Image;
return eb;
}
private QuoteObjectDto CreateQuoteObject(IMessage message)
{
string image;
try
{
image = message.Attachments.First().Url;
}
catch (Exception)
{
image = null;
}
return new QuoteObjectDto
{
UserId = message.Author.Id,
Time = message.Timestamp.DateTime,
Quote = message.Content,
Image = image
};
}
}
}

View file

@ -1,87 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<ApplicationIcon>derp.ico</ApplicationIcon>
<Version>3.7.0</Version>
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
<Version Condition=" '$(VersionSuffix)' != '' ">$(Version)-$(VersionSuffix)</Version>
<Company>Pizza and Coffee Studios</Company>
<Authors>Pizza and Coffee Studios</Authors>
<Description>A Discord bot</Description>
<RepositoryUrl>https://github.com/pizzaandcoffee/Geekbot.net</RepositoryUrl>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.2.1" />
<PackageReference Include="Discord.Net">
<Version>1.0.2</Version>
</PackageReference>
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.33.0.1202" />
<PackageReference Include="HtmlAgilityPack" Version="1.8.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.1" />
<PackageReference Include="MtgApiManager.Lib" Version="1.1.0" />
<PackageReference Include="MyAnimeListSharp" Version="1.3.4" />
<PackageReference Include="Nancy" Version="2.0.0-clinteastwood" />
<PackageReference Include="Nancy.Hosting.Self" Version="2.0.0-clinteastwood" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="NLog" Version="4.5.3" />
<PackageReference Include="NLog.Config" Version="4.5.3" />
<PackageReference Include="Overwatch.Net" Version="3.1.0" />
<PackageReference Include="PokeApi.NET" Version="1.1.0" />
<PackageReference Include="SharpRaven" Version="2.3.2" />
<PackageReference Include="StackExchange.Redis">
<Version>1.2.6</Version>
</PackageReference>
<PackageReference Include="SumoLogic.Logging.NLog" Version="1.0.0.2" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Json">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime.Serialization.Primitives">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup>
<ItemGroup>
<None Update="Storage\checkEmPics">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\croissant">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\fortunes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pandas">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pumpkin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\squirrel">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\turtles">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pinguins">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\foxes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Lib\Localization\Translations.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Lib\Converters\MtgManaEmojis.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WikipediaApi\WikipediaApi.csproj" />
</ItemGroup>
</Project>

View file

@ -1,214 +0,0 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib.Logger;
using Geekbot.net.Lib.ReactionListener;
using Geekbot.net.Lib.UserRepository;
using StackExchange.Redis;
namespace Geekbot.net
{
public class Handlers
{
private readonly IDiscordClient _client;
private readonly IGeekbotLogger _logger;
private readonly IDatabase _redis;
private readonly IServiceProvider _servicesProvider;
private readonly CommandService _commands;
private readonly IUserRepository _userRepository;
private readonly IReactionListener _reactionListener;
public Handlers(IDiscordClient client, IGeekbotLogger logger, IDatabase redis, IServiceProvider servicesProvider, CommandService commands, IUserRepository userRepository, IReactionListener reactionListener)
{
_client = client;
_logger = logger;
_redis = redis;
_servicesProvider = servicesProvider;
_commands = commands;
_userRepository = userRepository;
_reactionListener = reactionListener;
}
//
// Incoming Messages
//
public Task RunCommand(SocketMessage messageParam)
{
try
{
if (!(messageParam is SocketUserMessage message)) return Task.CompletedTask;
if (message.Author.IsBot) return Task.CompletedTask;
var argPos = 0;
var lowCaseMsg = message.ToString().ToLower();
if (lowCaseMsg.StartsWith("hui"))
{
message.Channel.SendMessageAsync("hui!!!");
return Task.CompletedTask;
}
if (lowCaseMsg.StartsWith("ping ") || lowCaseMsg.Equals("ping"))
{
bool.TryParse(_redis.HashGet($"{((SocketGuildChannel) message.Channel).Guild.Id}:Settings", "ping"), out var allowPings);
if (allowPings)
{
message.Channel.SendMessageAsync("pong");
return Task.CompletedTask;
}
}
if (!(message.HasCharPrefix('!', ref argPos) ||
message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return Task.CompletedTask;
var context = new CommandContext(_client, message);
var commandExec = _commands.ExecuteAsync(context, argPos, _servicesProvider);
_logger.Information(LogSource.Command,
context.Message.Content.Split(" ")[0].Replace("!", ""),
SimpleConextConverter.ConvertContext(context));
return Task.CompletedTask;
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to Process Message", e);
return Task.CompletedTask;
}
}
public Task UpdateStats(SocketMessage message)
{
try
{
if (message == null) return Task.CompletedTask;
if (message.Channel.Name.StartsWith('@'))
{
_logger.Information(LogSource.Message, $"[DM-Channel] {message.Content}", SimpleConextConverter.ConvertSocketMessage(message));
return Task.CompletedTask;
}
var channel = (SocketGuildChannel) message.Channel;
_redis.HashIncrementAsync($"{channel.Guild.Id}:Messages", message.Author.Id.ToString());
_redis.HashIncrementAsync($"{channel.Guild.Id}:Messages", 0.ToString());
if (message.Author.IsBot) return Task.CompletedTask;
_logger.Information(LogSource.Message, message.Content, SimpleConextConverter.ConvertSocketMessage(message));
}
catch (Exception e)
{
_logger.Error(LogSource.Message, "Could not process message stats", e);
}
return Task.CompletedTask;
}
//
// User Stuff
//
public Task UserJoined(SocketGuildUser user)
{
try
{
if (!user.IsBot)
{
var message = _redis.HashGet($"{user.Guild.Id}:Settings", "WelcomeMsg");
if (!message.IsNullOrEmpty)
{
message = message.ToString().Replace("$user", user.Mention);
user.Guild.DefaultChannel.SendMessageAsync(message);
}
}
_userRepository.Update(user);
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send welcome message", e);
}
return Task.CompletedTask;
}
public Task UserUpdated(SocketUser oldUser, SocketUser newUser)
{
_userRepository.Update(newUser);
return Task.CompletedTask;
}
public async Task UserLeft(SocketGuildUser user)
{
try
{
var sendLeftEnabled = _redis.HashGet($"{user.Guild.Id}:Settings", "ShowLeave");
if (sendLeftEnabled.ToString() == "1")
{
var modChannel = ulong.Parse(_redis.HashGet($"{user.Guild.Id}:Settings", "ModChannel"));
if (!string.IsNullOrEmpty(modChannel.ToString()))
{
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(modChannel);
await modChannelSocket.SendMessageAsync($"{user.Username}#{user.Discriminator} left the server");
}
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send leave message", e);
}
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
}
//
// Message Stuff
//
public async Task MessageDeleted(Cacheable<IMessage, ulong> message, ISocketMessageChannel channel)
{
try
{
var guild = ((IGuildChannel) channel).Guild;
var sendLeftEnabled = _redis.HashGet($"{guild.Id}:Settings", "ShowDelete");
if (sendLeftEnabled.ToString() == "1")
{
var modChannel = ulong.Parse(_redis.HashGet($"{guild.Id}:Settings", "ModChannel"));
if (!string.IsNullOrEmpty(modChannel.ToString()) && modChannel != channel.Id)
{
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(modChannel);
var sb = new StringBuilder();
if (message.Value != null)
{
sb.AppendLine(
$"The following message from {message.Value.Author.Username}#{message.Value.Author.Discriminator} was deleted in <#{channel.Id}>");
sb.AppendLine(message.Value.Content);
}
else
{
sb.AppendLine("Someone deleted a message, the message was not cached...");
}
await modChannelSocket.SendMessageAsync(sb.ToString());
}
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to send delete message...", e);
}
}
//
// Reactions
//
public Task ReactionAdded(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.GiveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
public Task ReactionRemoved(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.RemoveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
}
}

View file

@ -1,96 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using Discord.Audio;
namespace Geekbot.net.Lib.Audio
{
public class AudioUtils : IAudioUtils
{
private string _tempFolderPath;
private Dictionary<ulong, IAudioClient> _audioClients;
public AudioUtils()
{
_audioClients = new Dictionary<ulong, IAudioClient>();
_tempFolderPath = Path.GetFullPath("./tmp/");
if (Directory.Exists(_tempFolderPath))
{
Directory.Delete(_tempFolderPath, true);
}
Directory.CreateDirectory(_tempFolderPath);
}
public IAudioClient GetAudioClient(ulong guildId)
{
return _audioClients[guildId];
}
public void StoreAudioClient(ulong guildId, IAudioClient client)
{
_audioClients[guildId] = client;
}
public Process CreateStreamFromFile(string path)
{
var ffmpeg = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true,
};
return Process.Start(ffmpeg);
}
public Process CreateStreamFromYoutube(string url, ulong guildId)
{
var ytdlMediaUrl = GetYoutubeMediaUrl(url);
DownloadMediaUrl(ytdlMediaUrl, guildId);
return CreateStreamFromFile($"{_tempFolderPath}{guildId}");
}
public void Cleanup(ulong guildId)
{
File.Delete($"{_tempFolderPath}{guildId}");
}
private string GetYoutubeMediaUrl(string url)
{
var ytdl = new ProcessStartInfo()
{
FileName = "youtube-dl",
Arguments = $"-f bestaudio -g {url}",
UseShellExecute = false,
RedirectStandardOutput = true
};
var output = Process.Start(ytdl).StandardOutput.ReadToEnd();
if (string.IsNullOrWhiteSpace(output))
{
throw new Exception("Could not get Youtube Media URL");
}
return output;
}
private void DownloadMediaUrl(string url, ulong guildId)
{
using (var web = new WebClient())
{
web.DownloadFile(url, $"{_tempFolderPath}{guildId}");
}
// var ffmpeg = new ProcessStartInfo
// {
// FileName = "ffmpeg",
// Arguments = $"-i \"{_tempFolderPath}{guildId}\" -c:a mp3 -b:a 256k {_tempFolderPath}{guildId}.mp3",
// UseShellExecute = false,
// RedirectStandardOutput = true,
// };
// Process.Start(ffmpeg).WaitForExit();
// File.Delete($"{_tempFolderPath}{guildId}");
return;
}
}
}

View file

@ -1,15 +0,0 @@
using System.Diagnostics;
using Discord.Audio;
namespace Geekbot.net.Lib.Audio
{
public interface IAudioUtils
{
IAudioClient GetAudioClient(ulong guildId);
void StoreAudioClient(ulong guildId, IAudioClient client);
Process CreateStreamFromFile(string path);
Process CreateStreamFromYoutube(string url, ulong guildId);
void Cleanup(ulong guildId);
}
}

View file

@ -1,12 +0,0 @@
using System.Threading.Tasks;
using MyAnimeListSharp.Core;
namespace Geekbot.net.Lib.Clients
{
public interface IMalClient
{
bool IsLoggedIn();
Task<AnimeEntry> GetAnime(string query);
Task<MangaEntry> GetManga(string query);
}
}

View file

@ -1,70 +0,0 @@
using System.Threading.Tasks;
using Geekbot.net.Lib.Logger;
using MyAnimeListSharp.Auth;
using MyAnimeListSharp.Core;
using MyAnimeListSharp.Facade.Async;
using StackExchange.Redis;
namespace Geekbot.net.Lib.Clients
{
public class MalClient : IMalClient
{
private readonly IDatabase _redis;
private readonly IGeekbotLogger _logger;
private ICredentialContext _credentials;
private AnimeSearchMethodsAsync _animeSearch;
private MangaSearchMethodsAsync _mangaSearch;
public MalClient(IDatabase redis, IGeekbotLogger logger)
{
_redis = redis;
_logger = logger;
ReloadClient();
}
public bool ReloadClient()
{
var malCredentials = _redis.HashGetAll("malCredentials");
if (malCredentials.Length != 0)
{
_credentials = new CredentialContext();
foreach (var c in malCredentials)
{
switch (c.Name)
{
case "Username":
_credentials.UserName = c.Value;
break;
case "Password":
_credentials.Password = c.Value;
break;
}
}
_animeSearch = new AnimeSearchMethodsAsync(_credentials);
_mangaSearch = new MangaSearchMethodsAsync(_credentials);
_logger.Debug(LogSource.Geekbot, "Logged in to MAL");
return true;
}
_logger.Debug(LogSource.Geekbot, "No MAL Credentials Set!");
return false;
}
public bool IsLoggedIn()
{
return _credentials != null;
}
public async Task<AnimeEntry> GetAnime(string query)
{
var response = await _animeSearch.SearchDeserializedAsync(query);
return response.Entries.Count == 0 ? null : response.Entries[0];
}
public async Task<MangaEntry> GetManga(string query)
{
var response = await _mangaSearch.SearchDeserializedAsync(query);
return response.Entries.Count == 0 ? null : response.Entries[0];
}
}
}

View file

@ -1,15 +0,0 @@
namespace Geekbot.net.Lib
{
public static class CommandCategories
{
public const string Randomness = "Randomness";
public const string Karma = "Karma";
public const string Quotes = "Quotes";
public const string Fun = "Fun";
public const string Statistics = "Statistics";
public const string Helpers = "Helpers";
public const string Games = "Games";
public const string Admin = "Admin";
public const string Uncategorized = "Uncategorized";
}
}

View file

@ -1,16 +0,0 @@
using System.Reflection;
namespace Geekbot.net.Lib
{
public static class Constants
{
public const string Name = "Geekbot";
public static string BotVersion()
{
return typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
}
public const double ApiVersion = 1;
}
}

View file

@ -1,93 +0,0 @@
using System.Collections;
using System.Text;
namespace Geekbot.net.Lib.Converters
{
public class EmojiConverter : IEmojiConverter
{
public string NumberToEmoji(int number)
{
if (number == 10)
{
return "🔟";
}
var emojiMap = new[]
{
":zero:",
":one:",
":two:",
":three:",
":four:",
":five:",
":six:",
":seven:",
":eight:",
":nine:"
};
var numbers = number.ToString().ToCharArray();
var returnString = new StringBuilder();
foreach (var n in numbers)
{
returnString.Append(emojiMap[int.Parse(n.ToString())]);
}
return returnString.ToString();
}
public string TextToEmoji(string text)
{
var emojiMap = new Hashtable
{
['A'] = ":regional_indicator_a: ",
['B'] = ":b: ",
['C'] = ":regional_indicator_c: ",
['D'] = ":regional_indicator_d: ",
['E'] = ":regional_indicator_e: ",
['F'] = ":regional_indicator_f: ",
['G'] = ":regional_indicator_g: ",
['H'] = ":regional_indicator_h: ",
['I'] = ":regional_indicator_i: ",
['J'] = ":regional_indicator_j: ",
['K'] = ":regional_indicator_k: ",
['L'] = ":regional_indicator_l: ",
['M'] = ":regional_indicator_m: ",
['N'] = ":regional_indicator_n: ",
['O'] = ":regional_indicator_o: ",
['P'] = ":regional_indicator_p: ",
['Q'] = ":regional_indicator_q: ",
['R'] = ":regional_indicator_r: ",
['S'] = ":regional_indicator_s: ",
['T'] = ":regional_indicator_t: ",
['U'] = ":regional_indicator_u: ",
['V'] = ":regional_indicator_v: ",
['W'] = ":regional_indicator_w: ",
['X'] = ":regional_indicator_x: ",
['Y'] = ":regional_indicator_y: ",
['Z'] = ":regional_indicator_z: ",
['!'] = ":exclamation: ",
['?'] = ":question: ",
['#'] = ":hash: ",
['*'] = ":star2: ",
['+'] = ":heavy_plus_sign: ",
['0'] = ":zero: ",
['1'] = ":one: ",
['2'] = ":two: ",
['3'] = ":three: ",
['4'] = ":four: ",
['5'] = ":five: ",
['6'] = ":six: ",
['7'] = ":seven: ",
['8'] = ":eight: ",
['9'] = ":nine: ",
[' '] = ""
};
var letters = text.ToUpper().ToCharArray();
var returnString = new StringBuilder();
foreach (var n in letters)
{
var emoji = emojiMap[n] ?? n;
returnString.Append(emoji);
}
return returnString.ToString();
}
}
}

View file

@ -1,8 +0,0 @@
namespace Geekbot.net.Lib.Converters
{
public interface IEmojiConverter
{
string NumberToEmoji(int number);
string TextToEmoji(string text);
}
}

View file

@ -1,33 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Utf8Json;
namespace Geekbot.net.Lib.Converters
{
public class MtgManaConverter : IMtgManaConverter
{
private Dictionary<string, string> _manaDict;
public MtgManaConverter()
{
// these emotes can be found at https://discord.gg/bz8HyA7
var mtgEmojis = File.ReadAllText(Path.GetFullPath("./Lib/Converters/MtgManaEmojis.json"));
_manaDict = JsonSerializer.Deserialize<Dictionary<string, string>>(mtgEmojis);
}
public string ConvertMana(string mana)
{
var rgx = Regex.Matches(mana, @"(\{(.*?)\})");
foreach (Match manaTypes in rgx)
{
var m = _manaDict.GetValueOrDefault(manaTypes.Value);
if (!string.IsNullOrEmpty(m))
{
mana = mana.Replace(manaTypes.Value, m);
}
}
return mana;
}
}
}

View file

@ -1,50 +0,0 @@
{
"{0}": "<:mtg_0:415216130043412482>",
"{1}": "<:mtg_1:415216130253389835>",
"{2}": "<:mtg_2:415216130031091713>",
"{3}": "<:mtg_3:415216130467037194>",
"{4}": "<:mtg_4:415216130026635295>",
"{5}": "<:mtg_5:415216130492203008>",
"{6}": "<:mtg_6:415216130458779658>",
"{7}": "<:mtg_7:415216130190475265>",
"{8}": "<:mtg_8:415216130517630986>",
"{9}": "<:mtg_9:415216130500722689>",
"{10": "<:mtg_10:415216130450391051>",
"{11}": "<:mtg_11:415216130811101185>",
"{12}": "<:mtg_12:415216130525888532>",
"{13}": "<:mtg_13:415216130517631000>",
"{14}": "<:mtg_14:415216130165178370>",
"{15}": "<:mtg_15:415216130576089108>",
"{16}": "<:mtg_16:415216130358247425>",
"{17}": "<:mtg_17:415216130601517056>",
"{18}": "<:mtg_18:415216130462842891>",
"{19}": "<:mtg_19:415216130614099988>",
"{20}": "<:mtg_20:415216130656043038>",
"{W}": "<:mtg_white:415216131515744256>",
"{U}": "<:mtg_blue:415216130521694209>",
"{B}": "<:mtg_black:415216130873884683>",
"{R}": "<:mtg_red:415216131322806272>",
"{G}": "<:mtg_green:415216131180331009>",
"{S}": "<:mtg_s:415216131293446144>",
"{T}": "<:mtg_tap:415258392727257088>",
"{C}": "<:mtg_colorless:415216130706374666>",
"{2/W}": "<:mtg_2w:415216130446065664>",
"{2/U}": "<:mtg_2u:415216130429550592>",
"{2/B}": "<:mtg_2b:415216130160984065>",
"{2/R}": "<:mtg_2r:415216130454716436>",
"{2/G}": "<:mtg_2g:415216130420899840>",
"{W/U}": "<:mtg_wu:415216130970484736>",
"{W/B}": "<:mtg_wb:415216131222011914>",
"{U/R}": "<:mtg_ur:415216130962096128>",
"{U/B}": "<:mtg_ub:415216130865758218>",
"{R/W}": "<:mtg_rw:415216130878210057>",
"{G/W}": "<:mtg_gw:415216130567962646>",
"{G/U}": "<:mtg_gu:415216130739666945>",
"{B/R}": "<:mtg_br:415216130580283394>",
"{B/G}": "<:mtg_bg:415216130781609994>",
"{U/P}": "<:mtg_up:415216130861432842>",
"{R/P}": "<:mtg_rp:415216130597322783>",
"{G/P}": "<:mtg_gp:415216130760769546>",
"{W/P}": "<:mtg_wp:415216131541041172>",
"{B/P}": "<:mtg_bp:415216130664169482>"
}

View file

@ -1,102 +0,0 @@
using System;
using System.Net;
using Discord.Commands;
using Discord.Net;
using Geekbot.net.Lib.Localization;
using Geekbot.net.Lib.Logger;
using SharpRaven;
using SharpRaven.Data;
namespace Geekbot.net.Lib.ErrorHandling
{
public class ErrorHandler : IErrorHandler
{
private readonly IGeekbotLogger _logger;
private readonly ITranslationHandler _translation;
private readonly IRavenClient _raven;
private readonly bool _errorsInChat;
public ErrorHandler(IGeekbotLogger logger, ITranslationHandler translation, bool errorsInChat)
{
_logger = logger;
_translation = translation;
_errorsInChat = errorsInChat;
var sentryDsn = Environment.GetEnvironmentVariable("SENTRY");
if (!string.IsNullOrEmpty(sentryDsn))
{
_raven = new RavenClient(sentryDsn) { Release = Constants.BotVersion() };
_logger.Information(LogSource.Geekbot, $"Command Errors will be logged to Sentry: {sentryDsn}");
}
else
{
_raven = null;
}
}
public void HandleCommandException(Exception e, ICommandContext context, string errorMessage = "def")
{
try
{
var errorString = errorMessage == "def" ? _translation.GetString(context.Guild.Id, "errorHandler", "SomethingWentWrong") : errorMessage;
var errorObj = SimpleConextConverter.ConvertContext(context);
if (e.Message.Contains("50007")) return;
if (e.Message.Contains("50013")) return;
_logger.Error(LogSource.Geekbot, "An error ocured", e, errorObj);
if (!string.IsNullOrEmpty(errorMessage))
{
if (_errorsInChat)
{
var resStackTrace = string.IsNullOrEmpty(e.InnerException?.ToString()) ? e.StackTrace : e.InnerException?.ToString();
if (!string.IsNullOrEmpty(resStackTrace))
{
var maxLen = Math.Min(resStackTrace.Length, 1850);
context.Channel.SendMessageAsync($"{e.Message}\r\n```\r\n{resStackTrace.Substring(0, maxLen)}\r\n```");
}
else
{
context.Channel.SendMessageAsync(e.Message);
}
}
else
{
context.Channel.SendMessageAsync(errorString);
}
}
if (_raven == null) return;
var sentryEvent = new SentryEvent(e)
{
Tags =
{
["discord_server"] = errorObj.Guild.Name,
["discord_user"] = errorObj.User.Name
},
Message = errorObj.Message.Content,
Extra = errorObj
};
_raven.Capture(sentryEvent);
}
catch (Exception ex)
{
context.Channel.SendMessageAsync("Something went really really wrong here");
_logger.Error(LogSource.Geekbot, "Errorception", ex);
}
}
public async void HandleHttpException(HttpException e, ICommandContext context)
{
var errorStrings = _translation.GetDict(context, "httpErrors");
switch(e.HttpCode)
{
case HttpStatusCode.Forbidden:
await context.Channel.SendMessageAsync(errorStrings["403"]);
break;
}
}
}
}

View file

@ -1,12 +0,0 @@
using System;
using Discord.Commands;
using Discord.Net;
namespace Geekbot.net.Lib.ErrorHandling
{
public interface IErrorHandler
{
void HandleCommandException(Exception e, ICommandContext context, string errorMessage = "def");
void HandleHttpException(HttpException e, ICommandContext context);
}
}

View file

@ -1,7 +0,0 @@
namespace Geekbot.net.Lib.Levels
{
public interface ILevelCalc
{
int GetLevel(int experience);
}
}

View file

@ -1,14 +0,0 @@
using System.Collections.Generic;
using Discord.Commands;
namespace Geekbot.net.Lib.Localization
{
public interface ITranslationHandler
{
string GetString(ulong guildId, string command, string stringName);
Dictionary<string, string> GetDict(ICommandContext context);
Dictionary<string, string> GetDict(ICommandContext context, string command);
bool SetLanguage(ulong guildId, string language);
List<string> GetSupportedLanguages();
}
}

View file

@ -1,156 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib.Logger;
using StackExchange.Redis;
using Utf8Json;
namespace Geekbot.net.Lib.Localization
{
public class TranslationHandler : ITranslationHandler
{
private readonly IGeekbotLogger _logger;
private readonly IDatabase _redis;
private Dictionary<string, Dictionary<string, Dictionary<string, string>>> _translations;
private Dictionary<ulong, string> _serverLanguages;
private List<string> _supportedLanguages;
public TranslationHandler(IReadOnlyCollection<SocketGuild> clientGuilds, IDatabase redis, IGeekbotLogger logger)
{
_logger = logger;
_redis = redis;
_logger.Information(LogSource.Geekbot, "Loading Translations");
LoadTranslations();
LoadServerLanguages(clientGuilds);
}
private void LoadTranslations()
{
try
{
var translationFile = File.ReadAllText(Path.GetFullPath("./Lib/Localization/Translations.json"));
var rawTranslations = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, string>>>>(translationFile);
var sortedPerLanguage = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
foreach (var command in rawTranslations)
{
foreach (var str in command.Value)
{
foreach (var lang in str.Value)
{
if (!sortedPerLanguage.ContainsKey(lang.Key))
{
var commandDict = new Dictionary<string, Dictionary<string, string>>();
var strDict = new Dictionary<string, string>();
strDict.Add(str.Key, lang.Value);
commandDict.Add(command.Key, strDict);
sortedPerLanguage.Add(lang.Key, commandDict);
}
if (!sortedPerLanguage[lang.Key].ContainsKey(command.Key))
{
var strDict = new Dictionary<string, string>();
strDict.Add(str.Key, lang.Value);
sortedPerLanguage[lang.Key].Add(command.Key, strDict);
}
if (!sortedPerLanguage[lang.Key][command.Key].ContainsKey(str.Key))
{
sortedPerLanguage[lang.Key][command.Key].Add(str.Key, lang.Value);
}
}
}
}
_translations = sortedPerLanguage;
_supportedLanguages = new List<string>();
foreach (var lang in sortedPerLanguage)
{
_supportedLanguages.Add(lang.Key);
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Failed to load Translations", e);
Environment.Exit(GeekbotExitCode.TranslationsFailed.GetHashCode());
}
}
private void LoadServerLanguages(IReadOnlyCollection<SocketGuild> clientGuilds)
{
_serverLanguages = new Dictionary<ulong, string>();
foreach (var guild in clientGuilds)
{
var language = _redis.HashGet($"{guild.Id}:Settings", "Language");
if (string.IsNullOrEmpty(language) || !_supportedLanguages.Contains(language))
{
_serverLanguages[guild.Id] = "EN";
}
else
{
_serverLanguages[guild.Id] = language.ToString();
}
}
}
public string GetString(ulong guildId, string command, string stringName)
{
var translation = _translations[_serverLanguages[guildId]][command][stringName];
if (!string.IsNullOrWhiteSpace(translation)) return translation;
translation = _translations[command][stringName]["EN"];
if (string.IsNullOrWhiteSpace(translation))
{
_logger.Warning(LogSource.Geekbot, $"No translation found for {command} - {stringName}");
}
return translation;
}
public Dictionary<string, string> GetDict(ICommandContext context)
{
try
{
var command = context.Message.Content.Split(' ').First().TrimStart('!').ToLower();
return _translations[_serverLanguages[context.Guild.Id]][command];
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "No translations for command found", e);
return new Dictionary<string, string>();
}
}
public Dictionary<string, string> GetDict(ICommandContext context, string command)
{
try
{
return _translations[_serverLanguages[context.Guild.Id]][command];
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "No translations for command found", e);
return new Dictionary<string, string>();
}
}
public bool SetLanguage(ulong guildId, string language)
{
try
{
if (!_supportedLanguages.Contains(language)) return false;
_redis.HashSet($"{guildId}:Settings", new[]{ new HashEntry("Language", language) });
_serverLanguages[guildId] = language;
return true;
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Error while changing language", e);
return false;
}
}
public List<string> GetSupportedLanguages()
{
return _supportedLanguages;
}
}
}

View file

@ -1,100 +0,0 @@
{
"admin": {
"NewLanguageSet": {
"EN": "I will reply in english from now on",
"CHDE": "I werd ab jetzt uf schwiizerdüütsch antworte, äuuä"
},
"GetLanguage": {
"EN": "I'm talking english",
"CHDE": "I red schwiizerdüütsch"
}
},
"errorHandler": {
"SomethingWentWrong": {
"EN": "Something went wrong :confused:",
"CHDE": "Öppis isch schief gange :confused:"
}
},
"httpErrors": {
"403": {
"EN": "Seems like i don't have enough permission to that :confused:",
"CHDE": "Gseht danach us das ich nid gnueg recht han zum das mache :confused:"
}
},
"choose": {
"Choice": {
"EN": "I Choose **{0}**",
"CHDE": "I nimme **{0}**"
}
},
"good": {
"CannotChangeOwn": {
"EN": "Sorry {0}, but you can't give yourself karma",
"CHDE": "Sorry {0}, aber du chasch dr selber kei karma geh"
},
"WaitUntill": {
"EN": "Sorry {0}, but you have to wait {1} before you can give karma again...",
"CHDE": "Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch geh..."
},
"Increased": {
"EN": "Karma gained",
"CHDE": "Karma becho"
},
"By": {
"EN": "By",
"CHDE": "Vo"
},
"Amount": {
"EN": "Amount",
"CHDE": "Mengi"
},
"Current": {
"EN": "Current",
"CHDE": "Jetzt"
}
},
"bad": {
"CannotChangeOwn": {
"EN": "Sorry {0}, but you can't lower your own karma",
"CHDE": "Sorry {0}, aber du chasch dr din eigete karma nid weg neh"
},
"WaitUntill": {
"EN": "Sorry {0}, but you have to wait {1} before you can lower karma again...",
"CHDE": "Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch senke..."
},
"Decreased": {
"EN": "Karma lowered",
"CHDE": "Karma gsenkt"
},
"By": {
"EN": "By",
"CHDE": "Vo"
},
"Amount": {
"EN": "Amount",
"CHDE": "Mengi"
},
"Current": {
"EN": "Current",
"CHDE": "Jetzt"
}
},
"roll": {
"Rolled": {
"EN": "{0}, you rolled {1}, your guess was {2}",
"CHDE": "{0}, du hesch {1} grollt und hesch {2} grate"
},
"Gratz": {
"EN": "Congratulations {0}, your guess was correct!",
"CHDE": "Gratuliere {0}, du hesch richtig grate!"
},
"RolledNoGuess": {
"EN": "{0}, you rolled {1}",
"CHDE": "{0}, du hesch {1} grollt"
},
"NoPrevGuess": {
"EN": ":red_circle: {0}, you can't guess the same number again",
"CHDE": ":red_circle: {0}, du chasch nid nomol es gliche rate"
}
}
}

View file

@ -1,74 +0,0 @@
using System;
using Newtonsoft.Json;
namespace Geekbot.net.Lib.Logger
{
public class GeekbotLogger : IGeekbotLogger
{
private readonly bool _logAsJson;
private readonly NLog.Logger _logger;
private readonly JsonSerializerSettings _serializerSettings;
public GeekbotLogger(RunParameters runParameters, bool sumologicActive)
{
_logAsJson = sumologicActive || runParameters.LogJson;
_logger = LoggerFactory.CreateNLog(runParameters, sumologicActive);
_serializerSettings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
NullValueHandling = NullValueHandling.Include
};
Information(LogSource.Geekbot, "Using GeekbotLogger");
}
public void Trace(LogSource source, string message, object extra = null)
{
_logger.Trace(CreateLogString("Trace", source, message, null, extra));
}
public void Debug(LogSource source, string message, object extra = null)
{
if (_logAsJson) _logger.Info(CreateLogString("Debug", source, message, null, extra));
else _logger.Debug(CreateLogString("Debug", source, message, null, extra));
}
public void Information(LogSource source, string message, object extra = null)
{
_logger.Info(CreateLogString("Information", source, message, null, extra));
}
public void Warning(LogSource source, string message, Exception stackTrace = null, object extra = null)
{
if (_logAsJson) _logger.Info(CreateLogString("Warning", source, message, stackTrace, extra));
else _logger.Warn(CreateLogString("Warning", source, message, stackTrace, extra));
}
public void Error(LogSource source, string message, Exception stackTrace, object extra = null)
{
if (_logAsJson) _logger.Info(CreateLogString("Error", source, message, stackTrace, extra));
else _logger.Error(stackTrace, CreateLogString("Error", source, message, stackTrace, extra));
}
private string CreateLogString(string type, LogSource source, string message, Exception stackTrace = null, object extra = null)
{
if (_logAsJson)
{
var logObject = new GeekbotLoggerObject
{
Timestamp = DateTime.Now,
Type = type,
Source = source,
Message = message,
StackTrace = stackTrace,
Extra = extra
};
return JsonConvert.SerializeObject(logObject, Formatting.None, _serializerSettings);
}
if (source != LogSource.Message) return $"[{source}] - {message}";
var m = (MessageDto) extra;
return $"[{source}] - [{m?.Guild.Name} - {m?.Channel.Name}] {m?.User.Name}: {m?.Message.Content}";
}
}
}

View file

@ -1,14 +0,0 @@
using System;
namespace Geekbot.net.Lib.Logger
{
public class GeekbotLoggerObject
{
public DateTime Timestamp { get; set; }
public string Type { get; set; }
public LogSource Source { get; set; }
public string Message { get; set; }
public Exception StackTrace { get; set; }
public object Extra { get; set; }
}
}

View file

@ -1,20 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Geekbot.net.Lib.Logger
{
[JsonConverter(typeof(StringEnumConverter))]
public enum LogSource
{
Geekbot,
Rest,
Gateway,
Discord,
Redis,
Message,
UserRepository,
Command,
Api,
Other
}
}

View file

@ -1,14 +0,0 @@
namespace Geekbot.net.Lib.Media
{
public interface IMediaProvider
{
string GetCheckem();
string GetPanda();
string GetCrossant();
string GetSquirrel();
string GetPumpkin();
string GetTurtle();
string GetPinguin();
string GetFox();
}
}

View file

@ -1,133 +0,0 @@
using System;
using System.IO;
using Geekbot.net.Lib.Logger;
namespace Geekbot.net.Lib.Media
{
public class MediaProvider : IMediaProvider
{
private readonly Random _random;
private readonly IGeekbotLogger _logger;
private string[] _checkemImages;
private string[] _pandaImages;
private string[] _croissantImages;
private string[] _squirrelImages;
private string[] _pumpkinImages;
private string[] _turtlesImages;
private string[] _pinguinImages;
private string[] _foxImages;
public MediaProvider(IGeekbotLogger logger)
{
_random = new Random();
_logger = logger;
logger.Information(LogSource.Geekbot, "Loading Media Files");
LoadCheckem();
LoadPandas();
BakeCroissants();
LoadSquirrels();
LoadPumpkins();
LoadTurtles();
LoadPinguins();
LoadFoxes();
}
private void LoadCheckem()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/checkEmPics"));
_checkemImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_checkemImages.Length} CheckEm Images");
}
private void LoadPandas()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pandas"));
_pandaImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_pandaImages.Length} Panda Images");
}
private void BakeCroissants()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/croissant"));
_croissantImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_croissantImages.Length} Croissant Images");
}
private void LoadSquirrels()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/squirrel"));
_squirrelImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_squirrelImages.Length} Squirrel Images");
}
private void LoadPumpkins()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pumpkin"));
_pumpkinImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_pumpkinImages.Length} Pumpkin Images");
}
private void LoadTurtles()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/turtles"));
_turtlesImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_turtlesImages.Length} Turtle Images");
}
private void LoadPinguins()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pinguins"));
_pinguinImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_pinguinImages.Length} Pinguin Images");
}
private void LoadFoxes()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/foxes"));
_foxImages = rawLinks.Split("\n");
_logger.Trace(LogSource.Geekbot, $"Loaded {_foxImages.Length} Foxes Images");
}
public string GetCheckem()
{
return _checkemImages[_random.Next(0, _checkemImages.Length)];
}
public string GetPanda()
{
return _pandaImages[_random.Next(0, _pandaImages.Length)];
}
public string GetCrossant()
{
return _croissantImages[_random.Next(0, _croissantImages.Length)];
}
public string GetSquirrel()
{
return _squirrelImages[_random.Next(0, _squirrelImages.Length)];
}
public string GetPumpkin()
{
return _pumpkinImages[_random.Next(0, _pumpkinImages.Length)];
}
public string GetTurtle()
{
return _turtlesImages[_random.Next(0, _turtlesImages.Length)];
}
public string GetPinguin()
{
return _pinguinImages[_random.Next(0, _pinguinImages.Length)];
}
public string GetFox()
{
return _foxImages[_random.Next(0, _foxImages.Length)];
}
}
}

View file

@ -1,31 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
namespace Geekbot.net.Lib.Polyfills
{
internal class UserPolyfillDto : IUser
{
public ulong Id { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public string Mention { get; set; }
public Game? Game { get; set; }
public UserStatus Status { get; set; }
public string AvatarId { get; set; }
public string Discriminator { get; set; }
public ushort DiscriminatorValue { get; set; }
public bool IsBot { get; set; }
public bool IsWebhook { get; set; }
public string Username { get; set; }
public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
{
return "https://discordapp.com/assets/6debd47ed13483642cf09e832ed0bc1b.png";
}
public Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null)
{
throw new NotImplementedException();
}
}
}

View file

@ -1,14 +0,0 @@
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace Geekbot.net.Lib.ReactionListener
{
public interface IReactionListener
{
bool IsListener(ulong id);
Task AddRoleToListener(string messageId, IEmote emoji, IRole role);
void RemoveRole(ISocketMessageChannel channel, SocketReaction reaction);
void GiveRole(ISocketMessageChannel message, SocketReaction reaction);
}
}

View file

@ -1,90 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using StackExchange.Redis;
namespace Geekbot.net.Lib.ReactionListener
{
public class ReactionListener : IReactionListener
{
private readonly IDatabase _database;
private Dictionary<string, Dictionary<IEmote, ulong>> _listener;
public ReactionListener(IDatabase database)
{
_database = database;
LoadListeners();
}
private Task LoadListeners()
{
var ids = _database.SetMembers("MessageIds");
_listener = new Dictionary<string, Dictionary<IEmote, ulong>>();
foreach (var id in ids)
{
var reactions = _database.HashGetAll($"Messages:{id}");
var messageId = id;
var emojiDict = new Dictionary<IEmote, ulong>();
foreach (var r in reactions)
{
IEmote emote;
if (!r.Name.ToString().StartsWith('<'))
{
var emo = new Emoji(r.Name);
emote = emo;
}
else
{
emote = Emote.Parse(r.Name);
}
emojiDict.Add(emote, ulong.Parse(r.Value));
}
_listener.Add(messageId, emojiDict);
}
return Task.CompletedTask;
}
public bool IsListener(ulong id)
{
return _listener.ContainsKey(id.ToString());
}
public Task AddRoleToListener(string messageId, IEmote emoji, IRole role)
{
if (_database.SetMembers("MessageIds").All(e => e.ToString() != messageId))
{
_database.SetAdd("MessageIds", messageId);
}
_database.HashSet($"Messages:{messageId}", new[] {new HashEntry(emoji.ToString(), role.Id.ToString())});
_database.SetAdd("MessageIds", messageId);
if (_listener.ContainsKey(messageId))
{
_listener[messageId].Add(emoji, role.Id);
return Task.CompletedTask;
}
var dict = new Dictionary<IEmote, ulong>();
dict.Add(emoji, role.Id);
_listener.Add(messageId, dict);
return Task.CompletedTask;
}
public async void RemoveRole(ISocketMessageChannel channel, SocketReaction reaction)
{
var roleId = _listener[reaction.MessageId.ToString()][reaction.Emote];
var guild = (SocketGuildChannel) channel;
var role = guild.Guild.GetRole(roleId);
await ((IGuildUser) reaction.User.Value).RemoveRoleAsync(role);
}
public async void GiveRole(ISocketMessageChannel channel, SocketReaction reaction)
{
var roleId = _listener[reaction.MessageId.ToString()][reaction.Emote];
var guild = (SocketGuildChannel) channel;
var role = guild.Guild.GetRole(roleId);
await ((IGuildUser) reaction.User.Value).AddRoleAsync(role);
}
}
}

View file

@ -1,26 +0,0 @@
using System;
using CommandLine;
namespace Geekbot.net.Lib
{
public class RunParameters
{
[Option('V', "verbose", Default = false, HelpText = "Prints all messages to standard output.")]
public bool Verbose { get; set; }
[Option('r', "reset", Default = false, HelpText = "Resets the bot")]
public bool Reset { get; set; }
[Option('j', "log-json", Default = false, HelpText = "Logs messages as json")]
public bool LogJson { get; set; }
[Option("disable-api", Default = false, HelpText = "Disables the web api")]
public bool DisableApi { get; set; }
[Option('e', "expose-errors", Default = false, HelpText = "Shows internal errors in the chat")]
public bool ExposeErrors { get; set; }
[Option("token", Default = null, HelpText = "Set a new bot token")]
public string Token { get; set; }
}
}

View file

@ -1,13 +0,0 @@
using System.Threading.Tasks;
using Discord.WebSocket;
namespace Geekbot.net.Lib.UserRepository
{
public interface IUserRepository
{
Task<bool> Update(SocketUser user);
UserRepositoryUser Get(ulong userId);
string GetUserSetting(ulong userId, string setting);
bool SaveUserSetting(ulong userId, string setting, string value);
}
}

View file

@ -1,125 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord.WebSocket;
using Geekbot.net.Lib.Logger;
using StackExchange.Redis;
using Utf8Json;
namespace Geekbot.net.Lib.UserRepository
{
public class UserRepository : IUserRepository
{
private readonly IDatabase _redis;
private readonly IGeekbotLogger _logger;
public UserRepository(IDatabase redis, IGeekbotLogger logger)
{
_redis = redis;
_logger = logger;
}
public Task<bool> Update(SocketUser user)
{
try
{
var savedUser = Get(user.Id);
savedUser.Id = user.Id;
savedUser.Username = user.Username;
savedUser.Discriminator = user.Discriminator;
savedUser.AvatarUrl = user.GetAvatarUrl() ?? "0";
savedUser.IsBot = user.IsBot;
savedUser.Joined = user.CreatedAt;
if(savedUser.UsedNames == null) savedUser.UsedNames = new List<string>();
if (!savedUser.UsedNames.Contains(user.Username))
{
savedUser.UsedNames.Add(user.Username);
}
Store(savedUser);
_logger.Information(LogSource.UserRepository, "Updated User", savedUser);
return Task.FromResult(true);
}
catch (Exception e)
{
_logger.Warning(LogSource.UserRepository, $"Failed to update user: {user.Username}#{user.Discriminator} ({user.Id})", e);
return Task.FromResult(false);
}
}
private void Store(UserRepositoryUser user)
{
_redis.HashSetAsync($"Users:{user.Id.ToString()}", new[]
{
new HashEntry("Id", user.Id.ToString()),
new HashEntry("Username", user.Username),
new HashEntry("Discriminator", user.Discriminator),
new HashEntry("AvatarUrl", user.AvatarUrl),
new HashEntry("IsBot", user.IsBot),
new HashEntry("Joined", user.Joined.ToString()),
new HashEntry("UsedNames", JsonSerializer.Serialize(user.UsedNames))
});
}
public UserRepositoryUser Get(ulong userId)
{
try
{
var user = _redis.HashGetAll($"Users:{userId.ToString()}");
for (var i = 1; i < 11; i++)
{
if (user.Length != 0) break;
user = _redis.HashGetAll($"Users:{(userId + (ulong) i).ToString()}");
}
var dto = new UserRepositoryUser();
foreach (var a in user.ToDictionary())
{
switch (a.Key)
{
case "Id":
dto.Id = ulong.Parse(a.Value);
break;
case "Username":
dto.Username = a.Value.ToString();
break;
case "Discriminator":
dto.Discriminator = a.Value.ToString();
break;
case "AvatarUrl":
dto.AvatarUrl = a.Value != "0" ? a.Value.ToString() : null;
break;
case "IsBot":
dto.IsBot = a.Value == 1;
break;
case "Joined":
dto.Joined = DateTimeOffset.Parse(a.Value.ToString());
break;
case "UsedNames":
dto.UsedNames = JsonSerializer.Deserialize<List<string>>(a.Value.ToString()) ?? new List<string>();
break;
}
}
return dto;
}
catch (Exception e)
{
_logger.Warning(LogSource.UserRepository, $"Failed to get {userId} from repository", e);
return new UserRepositoryUser();
}
}
public string GetUserSetting(ulong userId, string setting)
{
return _redis.HashGet($"Users:{userId}", setting);
}
public bool SaveUserSetting(ulong userId, string setting, string value)
{
_redis.HashSet($"Users:{userId}", new[]
{
new HashEntry(setting, value)
});
return true;
}
}
}

View file

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
namespace Geekbot.net.Lib.UserRepository
{
public class UserRepositoryUser
{
public ulong Id { get; set; }
public string Username { get; set; }
public string Discriminator { get; set; }
public string AvatarUrl { get; set; }
public bool IsBot { get; set; }
public DateTimeOffset Joined { get; set; }
public List<string> UsedNames { get; set; }
}
}

View file

View file

@ -1,224 +0,0 @@
using System;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using CommandLine;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Audio;
using Geekbot.net.Lib.Clients;
using Geekbot.net.Lib.Converters;
using Geekbot.net.Lib.ErrorHandling;
using Geekbot.net.Lib.Levels;
using Geekbot.net.Lib.Localization;
using Geekbot.net.Lib.Logger;
using Geekbot.net.Lib.Media;
using Geekbot.net.Lib.ReactionListener;
using Geekbot.net.Lib.UserRepository;
using Microsoft.Extensions.DependencyInjection;
using Nancy.Hosting.Self;
using StackExchange.Redis;
using WikipediaApi;
namespace Geekbot.net
{
internal class Program
{
private DiscordSocketClient _client;
private CommandService _commands;
private IDatabase _redis;
private IServiceCollection _services;
private IServiceProvider _servicesProvider;
private RedisValue _token;
private GeekbotLogger _logger;
private IUserRepository _userRepository;
private bool _firstStart;
private RunParameters _runParameters;
private static void Main(string[] args)
{
RunParameters runParameters = null;
Parser.Default.ParseArguments<RunParameters>(args)
.WithParsed(e => runParameters = e)
.WithNotParsed(_ => Environment.Exit(GeekbotExitCode.InvalidArguments.GetHashCode()));
var logo = new StringBuilder();
logo.AppendLine(@" ____ _____ _____ _ ______ ___ _____");
logo.AppendLine(@" / ___| ____| ____| |/ / __ ) / _ \\_ _|");
logo.AppendLine(@"| | _| _| | _| | ' /| _ \| | | || |");
logo.AppendLine(@"| |_| | |___| |___| . \| |_) | |_| || |");
logo.AppendLine(@" \____|_____|_____|_|\_\____/ \___/ |_|");
logo.AppendLine("=========================================");
Console.WriteLine(logo.ToString());
var sumologicActive = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GEEKBOT_SUMO"));
var logger = new GeekbotLogger(runParameters, sumologicActive);
logger.Information(LogSource.Geekbot, "Starting...");
try
{
new Program().MainAsync(runParameters, logger).GetAwaiter().GetResult();
}
catch (Exception e)
{
logger.Error(LogSource.Geekbot, "RIP", e);
}
}
private async Task MainAsync(RunParameters runParameters, GeekbotLogger logger)
{
_logger = logger;
_runParameters = runParameters;
logger.Information(LogSource.Geekbot, "Initing Stuff");
var discordLogger = new DiscordLogger(logger);
_client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Verbose,
MessageCacheSize = 1000
});
_client.Log += discordLogger.Log;
_commands = new CommandService();
try
{
var redisMultiplexer = ConnectionMultiplexer.Connect("127.0.0.1:6379");
_redis = redisMultiplexer.GetDatabase(6);
logger.Information(LogSource.Redis, $"Connected to db {_redis.Database}");
}
catch (Exception e)
{
logger.Error(LogSource.Redis, "Redis Connection Failed", e);
Environment.Exit(GeekbotExitCode.RedisConnectionFailed.GetHashCode());
}
_token = runParameters.Token ?? _redis.StringGet("discordToken");
if (_token.IsNullOrEmpty)
{
Console.Write("Your bot Token: ");
var newToken = Console.ReadLine();
_redis.StringSet("discordToken", newToken);
_redis.StringSet("Game", "Ping Pong");
_token = newToken;
_firstStart = true;
}
_services = new ServiceCollection();
_userRepository = new UserRepository(_redis, logger);
var fortunes = new FortunesProvider(logger);
var mediaProvider = new MediaProvider(logger);
var malClient = new MalClient(_redis, logger);
var levelCalc = new LevelCalc();
var emojiConverter = new EmojiConverter();
var mtgManaConverter = new MtgManaConverter();
var wikipediaClient = new WikipediaClient();
var audioUtils = new AudioUtils();
_services.AddSingleton<IDatabase>(_redis);
_services.AddSingleton<IUserRepository>(_userRepository);
_services.AddSingleton<IGeekbotLogger>(logger);
_services.AddSingleton<ILevelCalc>(levelCalc);
_services.AddSingleton<IEmojiConverter>(emojiConverter);
_services.AddSingleton<IFortunesProvider>(fortunes);
_services.AddSingleton<IMediaProvider>(mediaProvider);
_services.AddSingleton<IMalClient>(malClient);
_services.AddSingleton<IMtgManaConverter>(mtgManaConverter);
_services.AddSingleton<IWikipediaClient>(wikipediaClient);
_services.AddSingleton<IAudioUtils>(audioUtils);
logger.Information(LogSource.Geekbot, "Connecting to Discord");
await Login();
await Task.Delay(-1);
}
private async Task Login()
{
try
{
await _client.LoginAsync(TokenType.Bot, _token);
await _client.StartAsync();
var isConneted = await IsConnected();
if (isConneted)
{
await _client.SetGameAsync(_redis.StringGet("Game"));
_logger.Information(LogSource.Geekbot, $"Now Connected as {_client.CurrentUser.Username} to {_client.Guilds.Count} Servers");
_logger.Information(LogSource.Geekbot, "Registering Stuff");
var translationHandler = new TranslationHandler(_client.Guilds, _redis, _logger);
var errorHandler = new ErrorHandler(_logger, translationHandler, _runParameters.ExposeErrors);
var reactionListener = new ReactionListener(_redis);
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
_services.AddSingleton(_commands);
_services.AddSingleton<IErrorHandler>(errorHandler);
_services.AddSingleton<ITranslationHandler>(translationHandler);
_services.AddSingleton(_client);
_services.AddSingleton<IReactionListener>(reactionListener);
_servicesProvider = _services.BuildServiceProvider();
var handlers = new Handlers(_client, _logger, _redis, _servicesProvider, _commands, _userRepository, reactionListener);
_client.MessageReceived += handlers.RunCommand;
_client.MessageReceived += handlers.UpdateStats;
_client.MessageDeleted += handlers.MessageDeleted;
_client.UserJoined += handlers.UserJoined;
_client.UserUpdated += handlers.UserUpdated;
_client.UserLeft += handlers.UserLeft;
_client.ReactionAdded += handlers.ReactionAdded;
_client.ReactionRemoved += handlers.ReactionRemoved;
if (_firstStart || _runParameters.Reset)
{
_logger.Information(LogSource.Geekbot, "Finishing setup");
await FinishSetup();
_logger.Information(LogSource.Geekbot, "Setup finished");
}
if (!_runParameters.DisableApi)
{
StartWebApi();
}
_logger.Information(LogSource.Geekbot, "Done and ready for use");
}
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Could not connect...", e);
Environment.Exit(GeekbotExitCode.CouldNotLogin.GetHashCode());
}
}
private async Task<bool> IsConnected()
{
while (!_client.ConnectionState.Equals(ConnectionState.Connected))
await Task.Delay(25);
return true;
}
private void StartWebApi()
{
_logger.Information(LogSource.Api, "Starting Webserver");
var webApiUrl = new Uri("http://localhost:12995");
new NancyHost(webApiUrl).Start();
_logger.Information(LogSource.Api, $"Webserver now running on {webApiUrl}");
}
private async Task<Task> FinishSetup()
{
try
{
// ToDo: Set bot avatar
var appInfo = await _client.GetApplicationInfoAsync();
_redis.StringSet("botOwner", appInfo.Owner.Id);
}
catch (Exception e)
{
_logger.Warning(LogSource.Geekbot, "Setup Failed, couldn't retrieve discord application data", e);
}
return Task.CompletedTask;
}
}
}

View file

@ -1,122 +0,0 @@
http://s19.postimg.org/pcq2kwzoj/4cb.png
http://s19.postimg.org/cvetk0f4z/5_Dim_Dy6p.jpg
http://s19.postimg.org/5hzfl1v37/1310151998600.jpg
http://s19.postimg.org/53y3lgazn/1324181141954.jpg
http://s19.postimg.org/724rjg3hf/1392512742365.png
http://s19.postimg.org/3rgejkdk3/1393501296733.png
http://s19.postimg.org/a6ffg8k9v/1401667341503.jpg
http://s19.postimg.org/qiph5yylf/1419231572452.jpg
http://s19.postimg.org/fqwi4m8ir/1427600681401.png
http://s19.postimg.org/4c00zzw6b/1447813628974.png
http://s19.postimg.org/uuio8puw3/b5_3q_ycaaavxtf.jpg
http://s19.postimg.org/bghu913fn/check_em_by_boyboy99100_d57xp3y.png
http://s19.postimg.org/s1pgooujn/l_Hkppjs.jpg
http://s19.postimg.org/m08itft0j/checkem.jpg
https://old.postimg.org/image/6vx33rb1b/
https://old.postimg.org/image/wxiaz1mov/
https://old.postimg.org/image/azqfizx27/
https://old.postimg.org/image/6iy2kbiu7/
https://old.postimg.org/image/k8slt45y7/
https://old.postimg.org/image/t7ruxmplr/
https://old.postimg.org/image/ssbzqvean/
https://old.postimg.org/image/kbchfy9lr/
https://old.postimg.org/image/dl0lk9btr/
https://old.postimg.org/image/e5k80oufz/
https://old.postimg.org/image/er005baqn/
https://old.postimg.org/image/bfk2uzcin/
https://old.postimg.org/image/556fp0jkv/
https://old.postimg.org/image/i0efbryu7/
https://old.postimg.org/image/943n7u87z/
https://old.postimg.org/image/xn5op5cm7/
https://old.postimg.org/image/3l5p4d0kf/
https://old.postimg.org/image/5boq5ui3j/
https://old.postimg.org/image/ru082bqcf/
https://old.postimg.org/image/ytea1oqan/
https://old.postimg.org/image/vu7dekgtb/
https://old.postimg.org/image/hl7qwi2an/
https://old.postimg.org/image/5aescfg9r/
https://old.postimg.org/image/9gzmrrfvj/
https://old.postimg.org/image/50bv6tr1b/
https://old.postimg.org/image/afkl7silb/
https://old.postimg.org/image/nrdsgzllr/
https://old.postimg.org/image/s32e5zsin/
https://old.postimg.org/image/5sej60v8f/
https://old.postimg.org/image/lgfqctau7/
https://old.postimg.org/image/tn7q4e0wv/
https://old.postimg.org/image/8612arz1b/
https://old.postimg.org/image/w5tf52mn3/
https://old.postimg.org/image/zdxwi48wv/
https://old.postimg.org/image/lphwghd0f/
https://old.postimg.org/image/uzu0k0nq7/
https://old.postimg.org/image/3vqzsxjbz/
https://old.postimg.org/image/5d7uqqyov/
https://old.postimg.org/image/dntnyku8v/
https://old.postimg.org/image/dsxf891jz/
https://old.postimg.org/image/3nyrioizj/
https://old.postimg.org/image/6zx2bzaqn/
https://old.postimg.org/image/wu6v1raqn/
https://old.postimg.org/image/hb9f4n2fz/
https://old.postimg.org/image/p7yhqm3a7/
https://old.postimg.org/image/oelvxzx9b/
https://old.postimg.org/image/vcq03xvdr/
https://old.postimg.org/image/b08t1yqlb/
https://old.postimg.org/image/6yrpwayan/
https://old.postimg.org/image/btleukwm7/
https://old.postimg.org/image/62ztuldzz/
https://old.postimg.org/image/w3iq9pxr3/
https://old.postimg.org/image/byp6493xb/
https://old.postimg.org/image/xp2lf9xcv/
https://old.postimg.org/image/j9p9u49pb/
https://old.postimg.org/image/hvxmytafz/
https://old.postimg.org/image/5eqzbnfa7/
https://old.postimg.org/image/do2uq290f/
https://old.postimg.org/image/54o261q1r/
https://old.postimg.org/image/94qm4jr4v/
https://old.postimg.org/image/lee88y0pr/
https://old.postimg.org/image/bncb58cv3/
https://old.postimg.org/image/5246j7me7/
https://old.postimg.org/image/4uby8ym1r/
https://old.postimg.org/image/qn996tj4v/
https://old.postimg.org/image/c1dn4twyn/
https://old.postimg.org/image/6rd9ra23j/
https://lehcark14.files.wordpress.com/2008/08/botan16.jpg
http://i.imgur.com/p9vALew.jpg
http://i.imgur.com/4a9l2Rm.png
http://i.imgur.com/RNtixMQ.jpg
https://pbs.twimg.com/media/Cro9aIGUEAAkXCP.jpg
http://s16.postimg.org/empvloimd/Check_em_Guts.png
https://s18.postimg.io/qgbhe7u09/1424491645996.gif
http://s19.postimg.org/hhemlt7xf/3eb.jpg
http://s19.postimg.org/cwsg6vo83/8aa.png
http://s19.postimg.org/rh9j1pj6r/28mohl4.png
http://s19.postimg.org/zba4n3qzn/86d.jpg
http://s19.postimg.org/cb3hart5v/2016_09_16_08_58_45.png
http://s19.postimg.org/m9ofx92lf/bb1.jpg
http://s19.postimg.org/maydqo4f7/e8b.jpg
http://s19.postimg.org/yqzoy5n4z/fbe.png
http://s19.postimg.org/xd822unvn/giphy.gif
http://s19.postimg.org/c4udlf9er/l_TU3eup.jpg
https://66.media.tumblr.com/cc893a0ee40d73d083da3df4bdaf45cc/tumblr_mx8psiFduG1t1g1k8o1_500.gif
http://i.imgur.com/swbXHSy.gif
http://img1.reactor.cc/pics/post/full/Anime-Touhou-Project-Yakumo-Yukari-%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F-1303807.jpeg
http://i.imgur.com/ftGLHE0.png
http://i.imgur.com/JELDhKQ.png
http://imgur.com/yBJound
http://i.imgur.com/f7gAVPJ.png
http://i.imgur.com/HxWyo2Z.jpg
http://i.imgur.com/8Eb9CxQ.png
http://i.imgur.com/kOECcjz.png
http://i.imgur.com/MJLu7oJ.jpg
http://i.imgur.com/itG3rPM.jpg
http://i.imgur.com/G83Go9t.jpg
http://i.imgur.com/jI2dBnU.jpg
http://i.imgur.com/FtALzg0.jpg
http://i.imgur.com/GwZpJEv.gif
http://i.imgur.com/TYGRD3B.gif
http://i.imgur.com/P6TxLS3.png
http://i.imgur.com/phTVTdn.jpg
http://i.imgur.com/thhR6UE.jpg
http://i.imgur.com/KbROufx.jpg
http://i.imgur.com/sQqWbcm.jpg
http://i.imgur.com/YYpis53.png
http://i.imgur.com/kwaRd54.gif

View file

@ -1,17 +0,0 @@
https://i2.wp.com/epicureandculture.com/wp-content/uploads/2014/12/shutterstock_172040546.jpg
http://www.bakespace.com/images/large/5d79070cf21b2f33c3a1dd4336cb27d2.jpeg
http://food.fnr.sndimg.com/content/dam/images/food/fullset/2015/5/7/1/SD1B43_croissants-recipe_s4x3.jpg.rend.hgtvcom.616.462.suffix/1431052139248.jpeg
http://img.taste.com.au/u-Bwjfm_/taste/2016/11/mini-croissants-with-3-fillings-14692-1.jpeg
https://media.newyorker.com/photos/590974702179605b11ad8096/16:9/w_1200,h_630,c_limit/Gopnik-TheMurkyMeaningsofStraightenedOutCroissants.jpg
http://bt.static-redmouse.ch/sites/bielertagblatt.ch/files/styles/bt_article_showroom_landscape/hash/84/c9/84c9aed08415265911ec05c46d25d3ef.jpg?itok=hP5PnHaT
https://www.dermann.at/wp-content/uploads/Schokocroissant_HPBild_1400x900px.jpeg
https://www.bettybossi.ch/static/rezepte/x/bb_bkxx060101_0360a_x.jpg
http://www.engel-beck.ch/uploads/pics/tete-de-moine-gipfel-.jpg
https://storage.cpstatic.ch/storage/og_image/laugengipfel--425319.jpg
https://www.backhaus-kutzer.de/fileadmin/templates/Resources/Public/img/produkte/suesses-gebaeck/Milchhoernchen.png
https://www.kuechengoetter.de/uploads/media/1000x524/00/36390-vanillekipferl-0.jpg?v=1-0
https://c1.staticflickr.com/3/2835/10874180753_2b2916e3ce_b.jpg
http://www.mistercool.ch/wp-content/uploads/2017/02/Gipfel-mit-Cerealien-7168.png
https://scontent-sea1-1.cdninstagram.com/t51.2885-15/s480x480/e35/c40.0.999.999/15099604_105396696611384_2866237281000226816_n.jpg?ig_cache_key=MTM4MzQxOTU1MDc5NjUxNzcwMA%3D%3D.2.c
http://www.lecrobag.de/wp-content/uploads/2014/03/Wurst_2014_l.jpg
https://www.thecookierookie.com/wp-content/uploads/2017/02/sheet-pan-chocolate-croissants-collage1.jpeg

View file

@ -1,29 +0,0 @@
https://i.ytimg.com/vi/qF6OOGuT_hI/maxresdefault.jpg
https://www.hd-wallpapersdownload.com/script/bulk-upload/desktop-funny-fox-wallpaper.jpg
http://moziru.com/images/drawn-fox-funny-18.jpg
https://static.tumblr.com/bb34d8f163098ad1daafcffbdbb03975/rk23uap/Nwwp0rmi2/tumblr_static_tumblr_static__640.jpg
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQoHUFOnZ3wJ2kT1skNdztFXXSvpU8bEoGS1alNZiuyLXvGJhcY
http://childrenstorytales.com/wp-content/uploads/2011/03/how-to-draw-a-red-fox-in-the-snow.jpg
https://www.popsci.com/sites/popsci.com/files/styles/1000_1x_/public/import/2013/images/2013/09/redfoxyawn.jpg?itok=yRkSVe8T
https://hdqwalls.com/wallpapers/wild-fox-art.jpg
https://ae01.alicdn.com/kf/HTB1Q9dpLpXXXXbhXpXXq6xXFXXXl/new-cute-fox-toy-lifelike-soft-long-yellow-fox-doll-gift-about-73cm.jpg_640x640.jpg
https://i.imgur.com/ktK9yXX.jpg
https://res.cloudinary.com/teepublic/image/private/s--yTx2ncFA--/t_Preview/b_rgb:c8e0ec,c_limit,f_auto,h_313,q_90,w_313/v1506478249/production/designs/1932607_0
http://4.bp.blogspot.com/-Hz-o_KYj3Xk/Vlm2mwbztjI/AAAAAAAA8Ss/jbH5ovjmC9A/s1600/ScreenShot5502.jpg
https://i.pinimg.com/originals/1e/d5/2f/1ed52f70873a95ac02fa074e48edfb71.jpg
https://i.imgur.com/2vCrtap.jpg
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSfukWGu_IBaDeJOMBqOhVAwsDfqEPw0BFpCn5_-Iyr_xjd7zi9
https://cdn.pixabay.com/photo/2017/01/31/18/36/animal-2026297_960_720.png
https://i.pinimg.com/originals/e2/63/67/e26367a0844633b2a697b0a9d69e8cc9.jpg
https://i.ebayimg.com/images/g/BvkAAOSwqxdTqrip/s-l300.jpg
https://res.cloudinary.com/teepublic/image/private/s--1R53bger--/t_Preview/b_rgb:eae0c7,c_limit,f_jpg,h_630,q_90,w_630/v1481013120/production/designs/914528_1.jpg
https://i.pinimg.com/originals/97/fe/69/97fe698462afde7b4209ccefeecbce71.jpg
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6G0ch6g-wG1TuDJ6BbkOFelMNnkgFXC6CxOw7qSNjoFkx-BCe
https://wallpaperscraft.com/image/fox_forest_grass_117190_540x960.jpg
https://image.freepik.com/free-vector/cartoon-flat-illustration-funny-cute-fox_6317-1174.jpg
https://orig00.deviantart.net/2feb/f/2013/137/a/f/fox_and_curious_squirrel_by_tamarar-d65ju8d.jpg
https://res.cloudinary.com/teepublic/image/private/s--dICeNmBx--/t_Preview/b_rgb:6e2229,c_limit,f_jpg,h_630,q_90,w_630/v1505243196/production/designs/1890493_1.jpg
https://vignette.wikia.nocookie.net/puppyinmypocketfanon/images/4/49/L-Baby-Fox.jpg/revision/latest?cb=20130421001806
http://7-themes.com/data_images/out/69/7009194-fox-puppy.jpg
http://www.tehcute.com/pics/201401/little-fox-big.jpg
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR6QXB1APLdUsyzO39kPvhnC9cOvcwzEtsxown9QjWilWppia2mwg

View file

@ -1,13 +0,0 @@
https://i.ytimg.com/vi/Qr6sULJnu2o/maxresdefault.jpg
https://www.apex-expeditions.com/wp-content/uploads/2015/08/newzealandSlider_Macquarie_ElephantSealKingPenguins_GRiehle_1366x601.jpg
https://www.birdlife.org/sites/default/files/styles/1600/public/slide.jpg?itok=HRhQfA1S
http://experimentexchange.com/wp-content/uploads/2016/07/penguins-fact.jpg
http://images.mentalfloss.com/sites/default/files/styles/mf_image_16x9/public/istock-511366776.jpg?itok=cWhdWNZ8&resize=1100x619
https://www.thevaporplace.ch/media/catalog/product/cache/1/thumbnail/800x800/9df78eab33525d08d6e5fb8d27136e95/a/t/atopack_penguin-15.jpg
https://www.superfastbusiness.com/wp-content/uploads/2015/10/real-time-penguin-algorithm-featured.jpg
http://www.antarctica.gov.au/__data/assets/image/0011/147737/varieties/antarctic.jpg
https://vignette.wikia.nocookie.net/robloxcreepypasta/images/1/11/AAEAAQAAAAAAAAdkAAAAJDc3YzkyYjJhLTYyZjctNDY2Mi04M2VjLTg4NjY4ZjgwYzRmNg.png/revision/latest?cb=20180207200526
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR3xV0lhpZuhT8Nmm6LaITsppZ7VfWcWXuyu2cPHrlv_dt_M92K5g
http://goboiano.com/wp-content/uploads/2017/04/Penguin-Kemeno-Friends-Waifu.jpg
https://cdn.yoast.com/app/uploads/2015/10/Penguins_1200x628.png
https://images.justwatch.com/backdrop/8611153/s1440/pingu

View file

@ -1,23 +0,0 @@
https://i.pinimg.com/736x/0a/a7/8a/0aa78af25e114836e1a42585fb7b09ed--funny-pumpkins-pumkin-carving.jpg
http://wdy.h-cdn.co/assets/16/31/980x1470/gallery-1470321728-shot-two-021.jpg
https://i.pinimg.com/736x/6c/62/bf/6c62bfa73a19ffd9fc6f2d720d5e9764--cool-pumpkin-carving-carving-pumpkins.jpg
http://images6.fanpop.com/image/photos/38900000/Jack-o-Lantern-halloween-38991566-500-415.jpg
http://ghk.h-cdn.co/assets/15/37/1441834730-pumpkin-carve-2.jpg
http://diy.sndimg.com/content/dam/images/diy/fullset/2011/7/26/1/iStock-10761186_halloween-pumpkin-in-garden_s4x3.jpg.rend.hgtvcom.966.725.suffix/1420851319631.jpeg
http://ghk.h-cdn.co/assets/cm/15/11/54ffe537af882-snail-pumpkin-de.jpg
https://www.digsdigs.com/photos/2009/10/100-halloween-pumpkin-carving-ideas-12.jpg
http://diy.sndimg.com/content/dam/images/diy/fullset/2010/6/4/0/CI-Kyle-Nishioka_big-teeth-Jack-O-Lantern_s4x3.jpg.rend.hgtvcom.966.725.suffix/1420699522718.jpeg
https://twistedsifter.files.wordpress.com/2011/10/most-amazing-pumpkin-carving-ray-villafane-10.jpg?w=521&h=739
https://i.pinimg.com/736x/09/c4/b1/09c4b187b266c1f65332294f66009944--funny-pumpkins-halloween-pumpkins.jpg
http://www.evilmilk.com/pictures/The_Pumpkin_Man.jpg
http://cache.lovethispic.com/uploaded_images/blogs/13-Funny-Pumpkin-Carvings-5773-9.JPG
http://ihappyhalloweenpictures.com/wp-content/uploads/2016/10/funny-halloween-pumpkin.jpg
http://www.smallhomelove.com/wp-content/uploads/2012/08/leg-eating-pumpkin.jpg
https://cdn.shopify.com/s/files/1/0773/6789/articles/Halloween_Feature_8ff7a7c4-2cb3-4584-a85f-5d4d1e6ca26e.jpg?v=1476211360
http://4vector.com/i/free-vector-pumpkin-boy-color-version-clip-art_107714_Pumpkin_Boy_Color_Version_clip_art_hight.png
https://i.pinimg.com/736x/59/8a/0f/598a0fbf789631b76c1ffd4443194d8e--halloween-pumpkins-fall-halloween.jpg
https://i.pinimg.com/originals/8f/86/f9/8f86f95457467872b371ba697d341961.jpg
http://nerdist.com/wp-content/uploads/2015/08/taleshalloween1.jpg
http://www.designbolts.com/wp-content/uploads/2014/09/Scary-Pumpkin_Grin_stencil-Ideas.jpg
http://vignette2.wikia.nocookie.net/scoobydoo/images/7/75/Pumpkin_monsters_%28Witch%27s_Ghost%29.png/revision/latest?cb=20140520070213
https://taholtorf.files.wordpress.com/2013/10/36307-1920x1280.jpg

View file

@ -1,45 +0,0 @@
http://orig14.deviantart.net/6016/f/2010/035/c/b/first_squirrel_assassin_by_shotokanteddy.jpg
https://thumbs-prod.si-cdn.com/eoEYA_2Hau4795uKoecUZZgz-3w=/800x600/filters:no_upscale()/https://public-media.smithsonianmag.com/filer/52/f9/52f93262-c29b-4a4f-b031-0c7ad145ed5f/42-33051942.jpg
http://images5.fanpop.com/image/photos/30700000/Squirrel-squirrels-30710732-400-300.jpg
https://www.lovethegarden.com/sites/default/files/files/Red%20%26%20Grey%20Squirrel%20picture%20side%20by%20side-LR.jpg
http://i.dailymail.co.uk/i/pix/2016/02/24/16/158F7E7C000005DC-3462228-image-a-65_1456331226865.jpg
http://2.bp.blogspot.com/-egfnMhUb8tg/T_dAIu1m6cI/AAAAAAAAPPU/v4x9q4WqWl8/s640/cute-squirrel-hey-watcha-thinkin-about.jpg
https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Squirrel_posing.jpg/287px-Squirrel_posing.jpg
https://i.pinimg.com/736x/51/db/9b/51db9bad4a87d445d321923c7d56b501--red-squirrel-animal-kingdom.jpg
https://metrouk2.files.wordpress.com/2016/10/ad_223291521.jpg?w=620&h=949&crop=1
http://www.redsquirrelsunited.org.uk/wp-content/uploads/2016/07/layer-slider.jpg
http://images.mentalfloss.com/sites/default/files/squirrel-hero.jpg?resize=1100x740
https://i.pinimg.com/736x/ce/9c/59/ce9c5990b193046400d98724595cdaf3--red-squirrel-chipmunks.jpg
https://www.brooklynpaper.com/assets/photos/40/30/dtg-squirrel-attacks-prospect-park-patrons-2017-07-28-bk01_z.jpg
http://www.freakingnews.com/pictures/16000/Squirrel-Shark-16467.jpg
http://img09.deviantart.net/5c1c/i/2013/138/0/6/barbarian_squirel_by_coucoucmoa-d64r9m4.jpg
https://i.pinimg.com/736x/b4/5c/0d/b45c0d00b1a57e9f84f27f13cb019001--baby-squirrel-red-squirrel.jpg
https://i.pinimg.com/736x/0f/75/87/0f7587bb613ab524763afe8c9a532e5c--cute-squirrel-squirrels.jpg
http://cdn.images.express.co.uk/img/dynamic/128/590x/Grey-squirrel-828838.jpg
http://www.lovethispic.com/uploaded_images/79964-Squirrel-Smelling-A-Flower.jpg
https://i.pinimg.com/736x/23/d5/f9/23d5f9868f7d76c79c49bef53ae08f7f--squirrel-funny-red-squirrel.jpg
http://stories.barkpost.com/wp-content/uploads/2016/01/squirrel-3-copy.jpg
https://i.ytimg.com/vi/pzUs0DdzK3Y/hqdefault.jpg
https://www.askideas.com/media/41/I-Swear-It-Wasnt-Me-Funny-Squirrel-Meme-Picture-For-Facebook.jpg
https://i.pinimg.com/736x/2d/54/d8/2d54d8d2a9b3ab9d3e78544b75afd88e--funny-animal-pictures-humorous-pictures.jpg
http://www.funny-animalpictures.com/media/content/items/images/funnysquirrels0012_O.jpg
http://funny-pics.co/wp-content/uploads/funny-squirrel-and-coffee-picture.jpg
https://pbs.twimg.com/media/Bi4Ij6CIgAAgEdZ.jpg
http://www.funnyjunksite.com/pictures/wp-content/uploads/2015/06/Funny-Superman-Squirrels.jpg
https://i.pinimg.com/736x/bf/35/00/bf3500104f8394909d116259d1f0575e--funny-squirrel-squirrel-girl.jpg
http://quotespill.com/wp-content/uploads/2017/07/Squirrel-Meme-Draw-me-like-one-of-your-french-squirrrels-min.jpg
https://i.pinimg.com/736x/e2/16/bb/e216bba53f80fc8e0111d371e9850159--funny-squirrels-cute-squirrel.jpg
https://i.pinimg.com/736x/52/43/c9/5243c93377245be1f686218c266d775c--funny-squirrel-baby-squirrel.jpg
https://i.pinimg.com/736x/0c/be/1d/0cbe1da8ad2c0cf3882a806b6fd88965--cute-pictures-funny-animal-pictures.jpg
https://i.pinimg.com/736x/e5/08/67/e508670aa00ca3c896eccb81c4f6e2a8--funny-squirrel-baby-squirrel.jpg
https://i.pinimg.com/736x/1c/7d/4f/1c7d4f067a10066aad802ce5ac468d71--group-boards-a-squirrel.jpg
http://funny-pics.co/wp-content/uploads/funny-squirrel-on-a-branch.jpg
http://loldamn.com/wp-content/uploads/2016/06/funny-squirrel-playing-water-bending.jpg
https://cdn.trendhunterstatic.com/thumbs/squirrel-photography.jpeg
https://i.pinimg.com/736x/d6/42/12/d64212cc6221916db4173962bf6c131a--cute-squirrel-baby-squirrel.jpg
https://i.pinimg.com/236x/10/13/58/101358f2afc2c7d6b6a668046e7b8382--funny-animal-pictures-funny-animals.jpg
https://i.pinimg.com/736x/da/0d/fe/da0dfe93bb26887795f906e8fa97d68e--secret-squirrel-cute-squirrel.jpg
http://2.bp.blogspot.com/-HLieBqEuQoM/UDkRmeyzB5I/AAAAAAAABHs/RtsEynn5t6Y/s1600/hd-squirrel-wallpaper-with-a-brown-squirrel-eating-watermelon-wallpapers-backgrounds-pictures-photos.jpg
http://www.city-data.com/forum/members/brenda-starz-328928-albums-brenda-s-funny-squirrel-comment-pic-s-pic5075-punk-squirrels.jpg
http://img15.deviantart.net/9c50/i/2011/213/c/9/just_taking_it_easy_by_lou_in_canada-d42do3d.jpg
http://3.bp.blogspot.com/-AwsSk76R2Is/USQa3-dszKI/AAAAAAAABUQ/KF_F8HbtP1U/w1200-h630-p-k-no-nu/crazySquirrel.jpg

View file

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
namespace Geekbot.net.WebApi.Help
{
public class CommandDto
{
public string Name { get; set; }
public string Category { get; set; }
public string Summary { get; set; }
public bool IsAdminCommand { get; set; }
public Array Aliases { get; set; }
public List<CommandParamDto> Params { get; set; }
}
}

View file

@ -1,9 +0,0 @@
namespace Geekbot.net.WebApi.Help
{
public class CommandParamDto
{
public string Summary { get; set; }
public string Default { get; set; }
public string Type { get; set; }
}
}

View file

@ -1,48 +0,0 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Nancy;
namespace Geekbot.net.WebApi.Help
{
public class HelpController : NancyModule
{
public HelpController()
{
Get("/v1/commands", args =>
{
var commands = GetCommands().Result;
var commandList = (from cmd in commands.Commands
let cmdParamsObj = cmd.Parameters.Select(cmdParam => new CommandParamDto
{
Summary = cmdParam.Summary,
Default = cmdParam.DefaultValue?.ToString() ?? null,
Type = cmdParam.Type?.ToString()
})
.ToList()
let param = string.Join(", !", cmd.Aliases)
select new CommandDto
{
Name = cmd.Name,
Summary = cmd.Summary,
Category = cmd.Remarks ?? CommandCategories.Uncategorized,
IsAdminCommand = (param.Contains("admin")),
Aliases = cmd.Aliases.ToArray(),
Params = cmdParamsObj
}).ToList();
return Response.AsJson(commandList);
});
}
private async Task<CommandService> GetCommands()
{
var commands = new CommandService();
await commands.AddModulesAsync(Assembly.GetEntryAssembly());
return commands;
}
}
}

View file

@ -1,9 +0,0 @@
namespace Geekbot.net.WebApi.Status
{
public class ApiStatusDto
{
public string GeekbotVersion { get; set; }
public string ApiVersion { get; set; }
public string Status { get; set; }
}
}

View file

@ -1,22 +0,0 @@
using Geekbot.net.Lib;
using Nancy;
namespace Geekbot.net.WebApi.Status
{
public class StatusController : NancyModule
{
public StatusController()
{
Get("/", args =>
{
var responseBody = new ApiStatusDto
{
GeekbotVersion = Constants.BotVersion(),
ApiVersion = Constants.ApiVersion.ToString(),
Status = "Online"
};
return Response.AsJson(responseBody);
});
}
}
}

View file

@ -1,23 +0,0 @@
using System.Diagnostics;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.TinyIoc;
namespace Geekbot.net.WebApi
{
public class WebConfig : DefaultNancyBootstrapper
{
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
//CORS Enable
pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "GET")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type")
.WithHeader("Last-Modified", Process.GetCurrentProcess().StartTime.ToString());
});
}
}
}

View file

@ -1,75 +0,0 @@
using System.Collections.Generic;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Converters;
using Xunit;
namespace Tests.Lib
{
public class EmojiConverterTest
{
public static IEnumerable<object[]> NumberToEmojiTestData
{
get
{
yield return new object[]
{
2,
":two:"
};
yield return new object[]
{
10,
"🔟"
};
yield return new object[]
{
15,
":one::five:"
};
yield return new object[]
{
null,
":zero:"
};
}
}
[Theory, MemberData(nameof(NumberToEmojiTestData))]
public void NumberToEmoji(int number, string expectedResult)
{
var emojiConverter = new EmojiConverter();
var result = emojiConverter.NumberToEmoji(number);
Assert.Equal(result, expectedResult);
}
public static IEnumerable<object[]> TextToEmojiTestData
{
get
{
yield return new object[]
{
"test",
":regional_indicator_t: :regional_indicator_e: :regional_indicator_s: :regional_indicator_t: "
};
yield return new object[]
{
"Best3+?",
":b: :regional_indicator_e: :regional_indicator_s: :regional_indicator_t: :three: :heavy_plus_sign: :question: "
};
}
}
[Theory, MemberData(nameof(TextToEmojiTestData))]
public void TextToEmoji(string text, string expectedResult)
{
var emojiConverter = new EmojiConverter();
var result = emojiConverter.TextToEmoji(text);
Assert.Equal(result, expectedResult);
}
}
}

View file

@ -1,49 +0,0 @@
using System.Collections.Generic;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Levels;
using Xunit;
namespace Tests.Lib
{
public class LevelCalcTest
{
public static IEnumerable<object[]> LevelCalcTestData
{
get
{
yield return new object[]
{
500,
13
};
yield return new object[]
{
41659,
55
};
yield return new object[]
{
0,
1
};
yield return new object[]
{
4000000,
101
};
}
}
[Theory, MemberData(nameof(LevelCalcTestData))]
public void GetLevel(int messages, int expectedResult)
{
var levelCalc = new LevelCalc();
var result = levelCalc.GetLevel(messages);
Assert.Equal(result, expectedResult);
}
}
}

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