Compare commits

...

451 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
83f3c61661
Log errors as info when logging to sumologic 2018-05-08 00:22:14 +02:00
d671817b0d
Make constants static and use non buffered sumologic client 2018-05-06 23:28:44 +02:00
4d39850373
A dice can now have 144 sides instead of 120 2018-05-06 23:09:48 +02:00
59ee6b289a
Include git sha in binary 2018-05-06 03:24:09 +02:00
7238e7177d
Bug fixes in !quote 2018-05-06 02:43:23 +02:00
7eb62cb267
Serialize LogSource as string instead of numerical representation 2018-05-06 02:05:52 +02:00
fe5e2cb80f
Create Enum for exit codes 2018-05-06 02:00:45 +02:00
2b85caeb29
Use LogSource Enum for logger and improve logging messages 2018-05-06 01:47:13 +02:00
0fe273151c
Use Owner Attribute instead of manual inline checking 2018-05-06 00:59:06 +02:00
26a0c89cf7 Merge branch 'refactoring' into 'master'
Refactoring

See merge request open/Geekbot.net!12
2018-05-04 21:37:57 +00:00
49392703f2
Bump version to 3.7 2018-05-04 23:34:47 +02:00
7958c87de5
Fix every other command that was not working 2018-05-04 01:14:43 +02:00
0b7303d576
fix !google 2018-05-04 00:59:49 +02:00
290b85fdb6
Fix !changelog, fix bug in logger and typo in !mod namehistory 2018-05-04 00:55:32 +02:00
8599c815d7
Add audio stuff back 2018-05-04 00:54:01 +02:00
0828130ae8
Fixes 2018-05-03 21:20:49 +02:00
ce1a3d057a Merge branch 'refactoring' into 'master'
Refactoring

See merge request open/Geekbot.net!11
2018-05-02 23:10:39 +00:00
e3adf55742
Refaction all files into component based folders 2018-05-03 00:56:06 +02:00
55e152f4aa
Update Readme and add token parameter 2018-05-02 23:33:16 +02:00
2701f641cf
Add commandline parser 2018-05-02 20:19:11 +02:00
d3fcfc8a5c
Replace Serilog with NLog 2018-05-01 22:58:02 +02:00
ec084a124b
Only format as json when logging to sumologic 2018-05-01 21:07:27 +02:00
f3156176dd
More explicit logging configuration 2018-05-01 16:50:48 +02:00
fa5797cd22
use newtonsoft in logger 2018-05-01 02:41:54 +02:00
276e9bfe8e
add null checking in !urban 2018-05-01 00:19:03 +02:00
cdb104cacc
Resharper suggestions 2018-04-30 23:44:19 +02:00
b81284bfe7
Add !chuck and !dad 2018-04-28 19:27:41 +02:00
b97fca787c
Ability to change wikipedia instance and make !wiki nicer 2018-04-28 17:38:45 +02:00
f5fd9ba017
Bug fixes in !wiki and errorHandler 2018-04-28 02:46:30 +02:00
4548c6083a
Dependency updates and mtg color fixes 2018-04-28 01:28:48 +02:00
846c928f5f
Add wikipedia api client, add wikipedia command, show errors in chat when debugging 2018-04-28 01:01:48 +02:00
f4ced55d15
Disable voice features 2018-04-04 01:41:37 +02:00
8974d6df7e
Add in some foxes aswell 2018-03-28 22:14:07 +02:00
3f02f90d70
Enable cat command again 2018-03-28 21:45:29 +02:00
f127e0c02b
Disable !cat 2018-03-28 21:29:42 +02:00
6bc6fb69af
Now with 100% more pinguins 2018-03-28 21:28:16 +02:00
75cbcfff76
Add a check to help 2018-03-02 23:35:14 +01:00
440f3a97c2
Revive ping pong 2018-03-02 23:05:14 +01:00
8189d78cea
Optimize dictionary in mtgmanaconverter 2018-02-20 16:26:56 +01:00
e158e2196f
Revert SimpleContextConverter 2018-02-20 09:34:26 +01:00
a616d42543
Add colorless mana to mtg mana converter 2018-02-19 22:55:22 +01:00
4c1cdc3612
Merge Rolebot into Geekbot 2018-02-19 22:31:40 +01:00
3e859e8533
Add real mana icons to mtg command 2018-02-19 21:35:45 +01:00
0498998630
Add more things to slap 2018-02-16 08:36:26 +01:00
1c88dea796
Don't error in rank when no entries are found or bot is not included 2018-02-15 00:07:11 +01:00
d40d89506e
Upgrade UTF8JSON to 1.3.7 2018-02-14 23:40:44 +01:00
acc8947782
Make random more random than the google bot 2018-02-14 23:01:28 +01:00
cabf942362
unpin 2018-02-14 11:37:54 +01:00
03ed80d3d4
Log command usage 2018-02-13 23:42:31 +01:00
d750513bc8
Merge branch 'master' of ssh://git.boerlage.me:31022/open/Geekbot.net 2018-02-13 22:29:58 +01:00
6fd36e0bb2
Show mana costs as emojis in mtg 2018-02-13 22:29:37 +01:00
46696549d7
Check correct string to ignore errors *facepalm* 2018-02-04 17:36:55 +01:00
8d23420031
Remove test warnings 2018-02-04 14:52:30 +01:00
ff1619a62f
Ignore error 50007 and 50013 2018-02-04 14:47:10 +01:00
91d178049b
Error when removing non-existing role from whitelist 2018-02-03 15:14:11 +01:00
ffaedfa962
Add space to emojis in emojihandler 2018-01-29 23:27:28 +01:00
359 changed files with 14204 additions and 5250 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

16
.gitignore vendored
View file

@ -1,12 +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
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 --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,9 +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}") = "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
@ -13,14 +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
{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,159 +0,0 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[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("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);
}
}
}
}

View file

@ -1,71 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
[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;
if (splited[1].Length == 4 || splited[1].Length == 5) return true;
return false;
}
}
}

View file

@ -1,58 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Newtonsoft.Json;
namespace Geekbot.net.Commands
{
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("http://random.cat");
var response = await client.GetAsync("/meow.php");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var catFile = JsonConvert.DeserializeObject<CatResponse>(stringResponse);
var eb = new EmbedBuilder();
eb.ImageUrl = catFile.file;
await ReplyAsync("", false, eb.Build());
}
catch (HttpRequestException e)
{
await ReplyAsync($"Seems like the dog cought the cat (error occured)\r\n{e.Message}");
}
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private class CatResponse
{
public string file { get; set; }
}
}
}

View file

@ -1,89 +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 Newtonsoft.Json;
namespace Geekbot.net.Commands
{
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<Commit>>(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);
}
}
private class Commit
{
public string sha { get; set; }
public CommitInfo commit { get; set; }
public Uri html_url { get; set; }
}
private class CommitInfo
{
public commitAuthor author { get; set; }
public string message { get; set; }
}
private class commitAuthor
{
public string name { get; set; }
public string email { get; set; }
public DateTimeOffset date { get; set; }
}
}
}

View file

@ -1,74 +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.Media;
namespace Geekbot.net.Commands
{
public class CheckEm : ModuleBase
{
private readonly IMediaProvider _checkEmImages;
private readonly IErrorHandler _errorHandler;
private readonly Random _rnd;
public CheckEm(Random RandomClient, IMediaProvider mediaProvider, IErrorHandler errorHandler)
{
_rnd = RandomClient;
_checkEmImages = mediaProvider;
_errorHandler = errorHandler;
}
[Command("checkem", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Check for dubs")]
public async Task MuhDubs()
{
try
{
var number = _rnd.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,40 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
public class Choose : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly Random _rnd;
private readonly ITranslationHandler _translation;
public Choose(Random RandomClient, IErrorHandler errorHandler, ITranslationHandler translation)
{
_rnd = RandomClient;
_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 = _rnd.Next(choicesArray.Length);
await ReplyAsync(string.Format(transDict["Choice"], choicesArray[choice]));
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,130 +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
{
public class Dice : ModuleBase
{
private readonly Random _rnd;
public Dice(Random RandomClient)
{
_rnd = RandomClient;
}
[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 > 120))
{
await ReplyAsync("A dice can't have more than 120 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 = _rnd.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();
}
}
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,58 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Newtonsoft.Json;
namespace Geekbot.net.Commands
{
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<DogResponse>(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);
}
}
private class DogResponse
{
public string url { get; set; }
}
}
}

View file

@ -1,60 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
public class EightBall : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly Random _rnd;
public EightBall(Random RandomClient, IErrorHandler errorHandler)
{
_rnd = RandomClient;
_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 = _rnd.Next(replies.Count);
await ReplyAsync(replies[answer]);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,41 +0,0 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
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,102 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Net;
using Geekbot.net.Lib;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
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 = Utf8Json.JsonSerializer.Deserialize<GoogleKGApiResponse>(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.detailedDescription?.url)) eb.WithUrl(data.detailedDescription.url);
if(!string.IsNullOrEmpty(data.detailedDescription?.articleBody)) eb.AddField("Details", data.detailedDescription.articleBody);
if(!string.IsNullOrEmpty(data.image?.contentUrl)) eb.WithThumbnailUrl(data.image.contentUrl);
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
public class GoogleKGApiResponse
{
public List<GoogleKGApiElement> itemListElement { get; set; }
public class GoogleKGApiElement
{
public GoogleKGApiResult result { get; set; }
public double resultScore { get; set; }
}
public class GoogleKGApiResult
{
public string name { get; set; }
public string description { get; set; }
public GoogleKGApiImage image { get; set; }
public GoogleKGApiDetailed detailedDescription { get; set; }
}
public class GoogleKGApiImage
{
public string contentUrl { get; set; }
public string url { get; set; }
}
public class GoogleKGApiDetailed
{
public string articleBody { get; set; }
public string url { get; set; }
public string license { get; set; }
}
}
}
}

View file

@ -1,129 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
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)
{
if (string.IsNullOrEmpty(dateTimeOffsetString))
return DateTimeOffset.Now.Subtract(new TimeSpan(7, 18, 0, 0));
return 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,88 +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 MtgApiManager.Lib.Service;
namespace Geekbot.net.Commands
{
public class Magicthegathering : ModuleBase
{
private readonly IErrorHandler _errorHandler;
public Magicthegathering(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[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", 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", 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(177, 171, 170);
case "White":
return new Color(255, 252, 214);
case "Blue":
return new Color(156, 189, 204);
case "Red":
return new Color(204, 156, 140);
case "Green":
return new Color(147, 181, 159);
default:
return new Color(255, 252, 214);
}
}
}
}

View file

@ -1,87 +0,0 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[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 to give {user.Username} that role");
}
}
[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,131 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using OverwatchAPI;
using OverwatchAPI.Config;
using Serilog;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[Group("ow")]
public class Overwatch : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IUserRepository _userRepository;
public Overwatch(IErrorHandler errorHandler, IDatabase redis, 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().WithRegions(Region.Eu).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,117 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Serilog;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[Group("owner")]
[RequireUserPermission(GuildPermission.Administrator)]
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)
{
var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result;
if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString()))
{
await ReplyAsync(
$"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})");
return;
}
_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)
{
var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result;
if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString()))
{
await ReplyAsync(
$"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})");
return;
}
_redis.StringSet("Game", key);
await _client.SetGameAsync(key);
_logger.Information("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()
{
try
{
var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result;
if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString()))
{
await ReplyAsync(
$"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})");
return;
}
}
catch (Exception)
{
await ReplyAsync(
$"Sorry, only the botowner can do this");
return;
}
var success = 0;
var failed = 0;
try
{
_logger.Warning("UserRepository", "Populating User Repositry");
await ReplyAsync("Starting Population of User Repository");
foreach (var guild in _client.Guilds)
{
_logger.Information("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("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");
}
}
}
}

View file

@ -1,191 +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 Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[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 PollData
{
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 PollData GetCurrentPoll()
{
try
{
var currentPoll = _redis.HashGet($"{Context.Guild.Id}:Polls", Context.Channel.Id);
return JsonConvert.DeserializeObject<PollData>(currentPoll.ToString());
}
catch
{
return new PollData();
}
}
private async Task<List<PollResult>> getPollResults(PollData poll)
{
var message = (IUserMessage) await Context.Channel.GetMessageAsync(poll.MessageId);
var results = new List<PollResult>();
foreach (var r in message.Reactions)
try
{
var option = int.Parse(r.Key.Name.ToCharArray()[0].ToString());
var result = new PollResult
{
Option = poll.Options[option - 1],
VoteCount = r.Value.ReactionCount
};
results.Add(result);
}
catch {}
results.Sort((x, y) => y.VoteCount.CompareTo(x.VoteCount));
return results;
}
private class PollData
{
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; }
}
private class PollResult
{
public string Option { get; set; }
public int VoteCount { get; set; }
}
}
}

View file

@ -1,231 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[Group("quote")]
public class Quote : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Quote(IDatabase redis, IErrorHandler errorHandler, Random random)
{
_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");
var randomNumber = new Random().Next(randomQuotes.Length - 1);
var randomQuote = randomQuotes[randomNumber];
var quote = JsonConvert.DeserializeObject<QuoteObject>(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);
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);
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<QuoteObject>(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)
{
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("!"));
}
private EmbedBuilder quoteBuilder(QuoteObject quote, int id = 0)
{
var user = Context.Client.GetUserAsync(quote.userId).Result;
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 QuoteObject createQuoteObject(IMessage message)
{
string image;
try
{
image = message.Attachments.First().Url;
}
catch (Exception)
{
image = null;
}
return new QuoteObject
{
userId = message.Author.Id,
time = message.Timestamp.DateTime,
quote = message.Content,
image = image
};
}
}
public class QuoteObject
{
public ulong userId { get; set; }
public string quote { get; set; }
public DateTime time { get; set; }
public string image { get; set; }
}
}

View file

@ -1,58 +0,0 @@
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Media;
namespace Geekbot.net.Commands
{
public class RandomAnimals : ModuleBase
{
private readonly IMediaProvider _mediaProvider;
public RandomAnimals(IMediaProvider mediaProvider)
{
_mediaProvider = mediaProvider;
}
[Command("panda", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random panda image")]
public async Task panda()
{
await ReplyAsync(_mediaProvider.getPanda());
}
[Command("croissant", RunMode = RunMode.Async)]
[Alias("gipfeli")]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random croissant image")]
public async Task croissant()
{
await ReplyAsync(_mediaProvider.getCrossant());
}
[Command("pumpkin", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random pumpkin image")]
public async Task pumpkin()
{
await ReplyAsync(_mediaProvider.getPumpkin());
}
[Command("squirrel", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random squirrel image")]
public async Task squirrel()
{
await ReplyAsync(_mediaProvider.getSquirrel());
}
[Command("turtle", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random turtle image")]
public async Task turtle()
{
await ReplyAsync(_mediaProvider.getTurtle());
}
}
}

View file

@ -1,145 +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 Serilog;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
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}");
var sortedList = messageList.OrderByDescending(e => e.Value).ToList();
var guildMessages = (int) sortedList.First().Value;
sortedList.Remove(sortedList.Single(e => e.Name.ToString().Equals(_client.CurrentUser.Id.ToString())));
if (type == "Messages") sortedList.RemoveAt(0);
var highscoreUsers = new Dictionary<RankUserPolyfill, 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 RankUserPolyfill
{
Username = guildUser.Username,
Discriminator = guildUser.Discriminator
}, (int) user.Value);
}
else
{
highscoreUsers.Add(new RankUserPolyfill
{
Id = user.Name
}, (int) user.Value);
failedToRetrieveUser = true;
}
listLimiter++;
}
catch (Exception e)
{
_logger.Warning("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);
}
}
}
internal class RankUserPolyfill
{
public string Username { get; set; }
public string Discriminator { get; set; }
public string Id { get; set; }
}
}

View file

@ -1,146 +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 StackExchange.Redis;
namespace Geekbot.net.Commands
{
[Group("role")]
public class Role : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Role(IErrorHandler errorHandler, IDatabase redis)
{
_errorHandler = errorHandler;
_redis = redis;
}
[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.Administrator)]
[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.Administrator)]
[Command("remove", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Remove a role from the whitelist.")]
public async Task removeRole([Summary("roleNickname")] string roleName)
{
try
{
_redis.HashDelete($"{Context.Guild.Id}:RoleWhitelist", roleName);
await ReplyAsync($"Removed {roleName} from the whitelist");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,64 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
public class Roll : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly Random _rnd;
private readonly ITranslationHandler _translation;
public Roll(IDatabase redis, Random RandomClient, IErrorHandler errorHandler, ITranslationHandler translation)
{
_redis = redis;
_rnd = RandomClient;
_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 = _rnd.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,35 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
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,94 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
public class Ship : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
private readonly Random _rnd;
public Ship(IDatabase redis, Random randomClient, IErrorHandler errorHandler)
{
_redis = redis;
_rnd = randomClient;
_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 = _rnd.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";
if (rate >= 80)
return "It's a match";
return "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,72 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
public class Slap : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly Random _random;
private readonly IDatabase _redis;
public Slap(IErrorHandler errorHandler, Random random, IDatabase redis)
{
_errorHandler = errorHandler;
_random = random;
_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"
};
_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[_random.Next(things.Count - 1)]}");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,72 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
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,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Newtonsoft.Json;
namespace Geekbot.net.Commands
{
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<UrbanResponse>(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));
eb.Description = definition.definition;
eb.AddField("Example", definition.example ?? "(no example given...)");
eb.AddInlineField("Upvotes", definition.thumbs_up);
eb.AddInlineField("Downvotes", definition.thumbs_down);
if (definitions.tags.Length > 0) eb.AddField("Tags", string.Join(", ", definitions.tags));
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
private class UrbanResponse
{
public string[] tags { get; set; }
public string result_type { get; set; }
public List<UrbanListItem> list { get; set; }
}
private class UrbanListItem
{
public string definition { get; set; }
public string permalink { get; set; }
public string thumbs_up { get; set; }
public string author { get; set; }
public string word { get; set; }
public string defid { get; set; }
public string current_vote { get; set; }
public string example { get; set; }
public string thumbs_down { get; set; }
}
}
}

View file

@ -1,102 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
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(
"User must be in a voice channel, or a voice channel must be passed as an argument.");
return;
}
// For the next step with transmitting audio, you would want to pass this Audio Client in to a service.
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,59 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
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,119 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Web;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
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,73 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<ApplicationIcon>derp.ico</ApplicationIcon>
<Version>1.1.0</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="Discord.Net">
<Version>1.0.2</Version>
</PackageReference>
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.29.1.991" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
<PackageReference Include="MtgApiManager.Lib" Version="1.0.0.2" />
<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="10.0.3" />
<PackageReference Include="Overwatch.Net" Version="3.0.0" />
<PackageReference Include="PokeApi.NET" Version="1.1.0" />
<PackageReference Include="Serilog" Version="2.6.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1-dev-00757" />
<PackageReference Include="Serilog.Sinks.Literate" Version="3.0.1-dev-00044" />
<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.1-dev-00771" />
<PackageReference Include="Serilog.Sinks.SumoLogic" Version="2.3.0" />
<PackageReference Include="SharpRaven" Version="2.2.0" />
<PackageReference Include="StackExchange.Redis">
<Version>1.2.6</Version>
</PackageReference>
<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.1" />
</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\Translations.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\turtles">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -1,186 +0,0 @@
using System;
using System.Collections;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Serilog;
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;
public Handlers(IDiscordClient client, IGeekbotLogger logger, IDatabase redis, IServiceProvider servicesProvider, CommandService commands, IUserRepository userRepository)
{
_client = client;
_logger = logger;
_redis = redis;
_servicesProvider = servicesProvider;
_commands = commands;
_userRepository = userRepository;
}
//
// 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.Equals("ping") || lowCaseMsg.StartsWith("ping "))
{
message.Channel.SendMessageAsync("pong");
return Task.CompletedTask;
}
if (lowCaseMsg.StartsWith("hui"))
{
message.Channel.SendMessageAsync("hui!!!");
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);
return Task.CompletedTask;
}
catch (Exception e)
{
_logger.Error("Geekbot", "Failed to run commands", e);
return Task.CompletedTask;
}
}
public Task UpdateStats(SocketMessage message)
{
try
{
if (message == null) return Task.CompletedTask;
if (message.Channel.Name.StartsWith('@'))
{
_logger.Information("Message", "DM-Channel - {message.Channel.Name} - {message.Content}");
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("Message", message.Content, SimpleConextConverter.ConvertSocketMessage(message));
// _logger.Information($"[Message] {channel.Guild.Name} ({channel.Guild.Id}) - {message.Channel} ({message.Channel.Id}) - {message.Author.Username}#{message.Author.Discriminator} ({message.Author.Id}) - {message.Content}");
}
catch (Exception e)
{
_logger.Error("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("Geekbot", $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
}
catch (Exception e)
{
_logger.Error("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("Geekbot", "Failed to send leave message", e);
}
_logger.Information("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("Geekbot", "Failed to send delete message...", e);
}
}
}
}

View file

@ -1,106 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using Discord.Audio;
using Discord.Net;
namespace Geekbot.net.Lib
{
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;
}
}
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,15 +0,0 @@
namespace Geekbot.net.Lib
{
public 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,9 +0,0 @@
namespace Geekbot.net.Lib
{
public class Constants
{
public const string Name = "Geekbot";
public const double BotVersion = 3.5;
public const double ApiVersion = 1;
}
}

View file

@ -1,99 +0,0 @@
using System.Collections;
using System.Text;
namespace Geekbot.net.Lib
{
public class EmojiConverter : IEmojiConverter
{
public string numberToEmoji(int number)
{
if (number == 10)
{
return "🔟";
}
var emojiMap = new string[]
{
":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();
}
}
public interface IEmojiConverter
{
string numberToEmoji(int number);
string textToEmoji(string text);
}
}

View file

@ -1,90 +0,0 @@
using System;
using System.Net;
using System.Runtime.InteropServices.ComTypes;
using System.Security.Principal;
using Discord.Commands;
using Discord.Net;
using Nancy.Extensions;
using Serilog;
using SharpRaven;
using SharpRaven.Data;
using SharpRaven.Utilities;
using Utf8Json;
namespace Geekbot.net.Lib
{
public class ErrorHandler : IErrorHandler
{
private readonly IGeekbotLogger _logger;
private readonly ITranslationHandler _translation;
private readonly IRavenClient _raven;
public ErrorHandler(IGeekbotLogger logger, ITranslationHandler translation)
{
_logger = logger;
_translation = translation;
var sentryDsn = Environment.GetEnvironmentVariable("SENTRY");
if (!string.IsNullOrEmpty(sentryDsn))
{
_raven = new RavenClient(sentryDsn);
_logger.Information("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);
_logger.Error("Geekbot", "An error ocured", e, errorObj);
if (!string.IsNullOrEmpty(errorMessage))
{
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)
{
_logger.Error("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;
}
}
}
public interface IErrorHandler
{
void HandleCommandException(Exception e, ICommandContext Context, string errorMessage = "def");
void HandleHttpException(HttpException e, ICommandContext Context);
}
}

View file

@ -1,80 +0,0 @@
using System;
using System.Threading.Tasks;
using Serilog;
using Utf8Json;
using Utf8Json.Formatters;
using Utf8Json.Resolvers;
namespace Geekbot.net.Lib
{
public class GeekbotLogger : IGeekbotLogger
{
private readonly ILogger _serilog;
public GeekbotLogger()
{
_serilog = LoggerFactory.createLogger();
//JsonSerializer.SetDefaultResolver(StandardResolver.AllowPrivateExcludeNullSnakeCase);
Information("Geekbot", "Using GeekbotLogger");
}
public void Debug(string source, string message, object extra = null)
{
HandleLogObject("Debug", source, message, null, extra);
}
public void Information(string source, string message, object extra = null)
{
HandleLogObject("Information", source, message, null, extra);
}
public void Warning(string source, string message, Exception stackTrace = null, object extra = null)
{
HandleLogObject("Warning", source, message, stackTrace, extra);
}
public void Error(string source, string message, Exception stackTrace, object extra = null)
{
HandleLogObject("Error", source, message, stackTrace, extra);
}
private Task HandleLogObject(string type, string source, string message, Exception stackTrace = null, object extra = null)
{
var logJson = CreateLogObject(type, source, message, null, extra);
// fuck serilog
_serilog.Information(logJson + "}");
return Task.CompletedTask;
}
private string CreateLogObject(string type, string source, string message, Exception stackTrace = null, object extra = null)
{
var logObject = new GeekbotLoggerObject()
{
Timestamp = DateTime.Now,
Type = type,
Source = source,
Message = message,
StackTrace = stackTrace,
Extra = extra
};
return JsonSerializer.ToJsonString(logObject);
}
}
public class GeekbotLoggerObject
{
public DateTime Timestamp { get; set; }
public string Type { get; set; }
public string Source { get; set; }
public string Message { get; set; }
public Exception StackTrace { get; set; }
public object Extra { get; set; }
}
public interface IGeekbotLogger
{
void Debug(string source, string message, object extra = null);
void Information(string source, string message, object extra = null);
void Warning(string source, string message, Exception stackTrace = null, object extra = null);
void Error(string source, string message, Exception stackTrace, object extra = null);
}
}

View file

@ -1,28 +0,0 @@
using System;
using Serilog;
using Serilog.Formatting.Json;
using Serilog.Sinks.SumoLogic;
namespace Geekbot.net.Lib
{
public class LoggerFactory
{
public static ILogger createLogger()
{
var loggerCreation = new LoggerConfiguration();
var template = "{Message}{NewLine}";
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GEEKBOT_SUMO")))
{
Console.WriteLine("Logging Geekbot Logs to Sumologic");
loggerCreation.WriteTo.SumoLogic(Environment.GetEnvironmentVariable("GEEKBOT_SUMO"),
outputTemplate: template);
}
else
{
loggerCreation.WriteTo.LiterateConsole(outputTemplate: template);
loggerCreation.WriteTo.RollingFile("Logs/geekbot-{Date}.txt", shared: true, outputTemplate: template);
}
return loggerCreation.CreateLogger();
}
}
}

View file

@ -1,78 +0,0 @@
using System.Threading.Tasks;
using MyAnimeListSharp.Auth;
using MyAnimeListSharp.Core;
using MyAnimeListSharp.Facade.Async;
using Serilog;
using StackExchange.Redis;
namespace Geekbot.net.Lib
{
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("Geekbot", "Logged in to MAL");
return true;
}
_logger.Debug("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];
}
}
public interface IMalClient
{
bool reloadClient();
bool isLoggedIn();
Task<AnimeEntry> getAnime(string query);
Task<MangaEntry> getManga(string query);
}
}

View file

@ -1,40 +0,0 @@
using System;
using System.IO;
using Serilog;
namespace Geekbot.net.Lib.Media
{
internal class FortunesProvider : IFortunesProvider
{
private readonly string[] fortuneArray;
private readonly Random rnd;
private readonly int totalFortunes;
public FortunesProvider(Random rnd, IGeekbotLogger logger)
{
var path = Path.GetFullPath("./Storage/fortunes");
if (File.Exists(path))
{
var rawFortunes = File.ReadAllText(path);
fortuneArray = rawFortunes.Split("%");
totalFortunes = fortuneArray.Length;
this.rnd = rnd;
logger.Debug("Geekbot", "Loaded {totalFortunes} Fortunes");
}
else
{
logger.Information("Geekbot", $"Fortunes File not found at {path}");
}
}
public string GetRandomFortune()
{
return fortuneArray[rnd.Next(0, totalFortunes)];
}
}
public interface IFortunesProvider
{
string GetRandomFortune();
}
}

View file

@ -1,117 +0,0 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Serilog;
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;
public MediaProvider(Random rnd, IGeekbotLogger logger)
{
_random = rnd;
_logger = logger;
logger.Information("Geekbot", "Loading Media Files");
LoadCheckem();
LoadPandas();
BakeCroissants();
LoadSquirrels();
LoadPumpkins();
LoadTurtles();
}
private void LoadCheckem()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/checkEmPics"));
_checkemImages = rawLinks.Split("\n");
_logger.Debug("Geekbot", $"Loaded {_checkemImages.Length} CheckEm Images");
}
private void LoadPandas()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pandas"));
_pandaImages = rawLinks.Split("\n");
_logger.Debug("Geekbot", $"Loaded {_pandaImages.Length} Panda Images");
}
private void BakeCroissants()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/croissant"));
_croissantImages = rawLinks.Split("\n");
_logger.Debug("Geekbot", $"Loaded {_croissantImages.Length} Croissant Images");
}
private void LoadSquirrels()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/squirrel"));
_squirrelImages = rawLinks.Split("\n");
_logger.Debug("Geekbot", $"Loaded {_squirrelImages.Length} Squirrel Images");
}
private void LoadPumpkins()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pumpkin"));
_pumpkinImages = rawLinks.Split("\n");
_logger.Debug("Geekbot", $"Loaded {_pumpkinImages.Length} Pumpkin Images");
}
private void LoadTurtles()
{
var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/turtles"));
_turtlesImages = rawLinks.Split("\n");
_logger.Debug("Geekbot", $"Loaded {_turtlesImages.Length} Turtle 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 interface IMediaProvider
{
string getCheckem();
string getPanda();
string getCrossant();
string getSquirrel();
string getPumpkin();
string getTurtle();
}
}

View file

@ -1,97 +0,0 @@
using System;
using Discord.Commands;
using Discord.WebSocket;
namespace Geekbot.net.Lib
{
public class SimpleConextConverter
{
public static MessageDto ConvertContext(ICommandContext context)
{
return new MessageDto()
{
Message = new MessageDto.MessageContent()
{
Content = context.Message.Content,
Id = context.Message.Id.ToString(),
Attachments = context.Message.Attachments.Count,
ChannelMentions = context.Message.MentionedChannelIds.Count,
UserMentions = context.Message.MentionedUserIds.Count,
RoleMentions = context.Message.MentionedRoleIds.Count
},
User = new MessageDto.IdAndName()
{
Id = context.User.Id.ToString(),
Name = $"{context.User.Username}#{context.User.Discriminator}"
},
Guild = new MessageDto.IdAndName()
{
Id = context.Guild.Id.ToString(),
Name = context.Guild.Name
},
Channel = new MessageDto.IdAndName()
{
Id = context.Channel.Id.ToString(),
Name = context.Channel.Name
}
};
}
public static MessageDto ConvertSocketMessage(SocketMessage message)
{
var channel = (SocketGuildChannel) message.Channel;
return new MessageDto()
{
Message = new MessageDto.MessageContent()
{
Content = message.Content,
Id = message.Id.ToString(),
Attachments = message.Attachments.Count,
ChannelMentions = message.MentionedChannels.Count,
UserMentions = message.MentionedUsers.Count,
RoleMentions = message.MentionedRoles.Count
},
User = new MessageDto.IdAndName()
{
Id = message.Author.Id.ToString(),
Name = $"{message.Author.Username}#{message.Author.Discriminator}"
},
Guild = new MessageDto.IdAndName()
{
Id = channel.Guild.Id.ToString(),
Name = channel.Guild.Name
},
Channel = new MessageDto.IdAndName()
{
Id = channel.Id.ToString(),
Name = channel.Name
},
};
}
}
public class MessageDto
{
public MessageContent Message { get; set; }
public IdAndName User { get; set; }
public IdAndName Guild { get; set; }
public IdAndName Channel { get; set; }
public class MessageContent
{
public string Content { get; set; }
public string Id { get; set; }
public int Attachments { get; set; }
public int ChannelMentions { get; set; }
public int UserMentions { get; set; }
public int RoleMentions { get; set; }
}
public class IdAndName
{
public string Id { get; set; }
public string Name { get; set; }
}
}
}

View file

@ -1,164 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Discord.Commands;
using Discord.WebSocket;
using Serilog;
using StackExchange.Redis;
namespace Geekbot.net.Lib
{
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("Geekbot", "Loading Translations");
LoadTranslations();
LoadServerLanguages(clientGuilds);
}
private void LoadTranslations()
{
try
{
var translationFile = File.ReadAllText(Path.GetFullPath("./Storage/Translations.json"));
var rawTranslations = Utf8Json.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("Geekbot", "Failed to load Translations", e);
Environment.Exit(110);
}
}
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("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("Geekbot", "lol nope", 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("Geekbot", "lol nope", 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 HashEntry[]{ new HashEntry("Language", language), });
_serverLanguages[guildId] = language;
return true;
}
catch (Exception e)
{
_logger.Error("Geekbot", "Error while changing language", e);
return false;
}
}
public List<string> GetSupportedLanguages()
{
return _supportedLanguages;
}
}
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,146 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Discord.WebSocket;
using Serilog;
using StackExchange.Redis;
using Utf8Json;
namespace Geekbot.net.Lib
{
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("UserRepository", "Updated User", savedUser);
return Task.FromResult(true);
}
catch (Exception e)
{
_logger.Warning("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 HashEntry[]
{
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 (int 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("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 HashEntry[]
{
new HashEntry(setting, value)
});
return true;
}
}
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; }
}
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

View file

@ -1,240 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Media;
using Microsoft.Extensions.DependencyInjection;
using Nancy.Hosting.Self;
using Serilog;
using StackExchange.Redis;
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 IGeekbotLogger logger;
private IUserRepository userRepository;
private string[] args;
private bool firstStart = false;
private static void Main(string[] args)
{
var logo = new StringBuilder();
logo.AppendLine(@" ____ _____ _____ _ ______ ___ _____");
logo.AppendLine(@" / ___| ____| ____| |/ / __ ) / _ \\_ _|");
logo.AppendLine(@"| | _| _| | _| | ' /| _ \| | | || |");
logo.AppendLine(@"| |_| | |___| |___| . \| |_) | |_| || |");
logo.AppendLine(@" \____|_____|_____|_|\_\____/ \___/ |_|");
logo.AppendLine("=========================================");
Console.WriteLine(logo.ToString());
var logger = new GeekbotLogger();
logger.Information("Geekbot", "Starting...");
try
{
new Program().MainAsync(args, logger).GetAwaiter().GetResult();
}
catch (Exception e)
{
logger.Error("Geekbot", "RIP", e);
}
}
private async Task MainAsync(string[] args, IGeekbotLogger logger)
{
this.logger = logger;
this.args = args;
logger.Information("Geekbot", "Initing Stuff");
client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Verbose,
MessageCacheSize = 1000
});
client.Log += DiscordLogger;
commands = new CommandService();
try
{
var redisMultiplexer = ConnectionMultiplexer.Connect("127.0.0.1:6379");
redis = redisMultiplexer.GetDatabase(6);
logger.Information("Redis", $"Connected to db {redis.Database}");
}
catch (Exception e)
{
logger.Error("Redis", "Redis Connection Failed", e);
Environment.Exit(102);
}
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 randomClient = new Random();
var fortunes = new FortunesProvider(randomClient, logger);
var mediaProvider = new MediaProvider(randomClient, logger);
var malClient = new MalClient(redis, logger);
var levelCalc = new LevelCalc();
var emojiConverter = new EmojiConverter();
var audioUtils = new AudioUtils();
services.AddSingleton(redis);
services.AddSingleton<IGeekbotLogger>(logger);
services.AddSingleton<IUserRepository>(userRepository);
services.AddSingleton<ILevelCalc>(levelCalc);
services.AddSingleton<IEmojiConverter>(emojiConverter);
services.AddSingleton<IAudioUtils>(audioUtils);
services.AddSingleton(randomClient);
services.AddSingleton<IFortunesProvider>(fortunes);
services.AddSingleton<IMediaProvider>(mediaProvider);
services.AddSingleton<IMalClient>(malClient);
logger.Information("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("Geekbot", $"Now Connected as {client.CurrentUser.Username} to {client.Guilds.Count} Servers");
logger.Information("Geekbot", "Registering Stuff");
var translationHandler = new TranslationHandler(client.Guilds, redis, logger);
var errorHandler = new ErrorHandler(logger, translationHandler);
await commands.AddModulesAsync(Assembly.GetEntryAssembly());
services.AddSingleton(commands);
services.AddSingleton<IErrorHandler>(errorHandler);
services.AddSingleton<ITranslationHandler>(translationHandler);
services.AddSingleton<DiscordSocketClient>(client);
servicesProvider = services.BuildServiceProvider();
var handlers = new Handlers(client, logger, redis, servicesProvider, commands, userRepository);
client.MessageReceived += handlers.RunCommand;
client.MessageReceived += handlers.UpdateStats;
client.MessageDeleted += handlers.MessageDeleted;
client.UserJoined += handlers.UserJoined;
client.UserUpdated += handlers.UserUpdated;
client.UserLeft += handlers.UserLeft;
if (firstStart || args.Contains("--reset"))
{
logger.Information("Geekbot", "Finishing setup");
await FinishSetup();
logger.Information("Geekbot", "Setup finished");
}
if (!args.Contains("--disable-api"))
{
startWebApi();
}
logger.Information("Geekbot", "Done and ready for use");
}
}
catch (Exception e)
{
logger.Error("Discord", "Could not connect...", e);
Environment.Exit(103);
}
}
private async Task<bool> isConnected()
{
while (!client.ConnectionState.Equals(ConnectionState.Connected))
await Task.Delay(25);
return true;
}
private void startWebApi()
{
logger.Information("API", "Starting Webserver");
var webApiUrl = new Uri("http://localhost:12995");
new NancyHost(webApiUrl).Start();
logger.Information("API", $"Webserver now running on {webApiUrl}");
}
private async Task<Task> FinishSetup()
{
var appInfo = await client.GetApplicationInfoAsync();
logger.Information("Setup", $"Just a moment while i setup everything {appInfo.Owner.Username}");
try
{
redis.StringSet("botOwner", appInfo.Owner.Id);
var req = HttpWebRequest.Create(appInfo.IconUrl);
using (var stream = req.GetResponse().GetResponseStream())
{
await client.CurrentUser.ModifyAsync(User =>
{
User.Avatar = new Image(stream);
User.Username = appInfo.Name.ToString();
});
}
logger.Information("Setup", "Everything done, enjoy!");
}
catch (Exception e)
{
logger.Warning("Setup", "Oha, it seems like something went wrong while running the setup, geekbot will work never the less though", e);
}
return Task.CompletedTask;
}
private Task DiscordLogger(LogMessage message)
{
var logMessage = $"[{message.Source}] {message.Message}";
switch (message.Severity)
{
case LogSeverity.Verbose:
case LogSeverity.Debug:
logger.Debug(message.Source, message.Message);
break;
case LogSeverity.Info:
logger.Information(message.Source, message.Message);
break;
case LogSeverity.Critical:
case LogSeverity.Error:
case LogSeverity.Warning:
if (logMessage.Contains("VOICE_STATE_UPDATE")) break;
logger.Error(message.Source, message.Message, message.Exception);
break;
default:
logger.Information(message.Source, $"{logMessage} --- {message.Severity}");
break;
}
return Task.CompletedTask;
}
}
}

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,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,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,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Nancy;
using Geekbot.net.Lib;
namespace Geekbot.net.WebApi
{
public class HelpController : NancyModule
{
public HelpController()
{
Get("/v1/commands", args =>
{
var commands = getCommands().Result;
var commandList = new List<CommandDto>();
foreach (var cmd in commands.Commands)
{
var cmdParamsObj = new List<CommandParamDto>();
foreach (var cmdParam in cmd.Parameters)
{
var singleParamObj = new CommandParamDto()
{
Summary = cmdParam.Summary,
Default = cmdParam?.DefaultValue?.ToString() ?? null,
Type = cmdParam?.Type?.ToString()
};
cmdParamsObj.Add(singleParamObj);
}
var param = string.Join(", !", cmd.Aliases);
var cmdObj = new CommandDto()
{
Name = cmd.Name,
Summary = cmd.Summary,
Category = cmd.Remarks ?? CommandCategories.Uncategorized,
IsAdminCommand = (param.Contains("admin")),
Aliases = cmd.Aliases.ToArray(),
Params = cmdParamsObj
};
commandList.Add(cmdObj);
}
return Response.AsJson(commandList);
});
}
private async Task<CommandService> getCommands()
{
var commands = new CommandService();
await commands.AddModulesAsync(Assembly.GetEntryAssembly());
return commands;
}
}
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; }
}
public class CommandParamDto
{
public string Summary { get; set; }
public string Default { get; set; }
public string Type { get; set; }
}
}

View file

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

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 System.Threading.Tasks;
using Geekbot.net.Lib;
using Xunit;
namespace Tests.Lib
{
public class EmojiConverter_test
{
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 async Task 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 async Task 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 System.Threading.Tasks;
using Geekbot.net.Lib;
using Xunit;
namespace Tests.Lib
{
public class LevelCalc_test
{
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 async Task GetLevel(int messages, int expectedResult)
{
var levelCalc = new LevelCalc();
var result = levelCalc.GetLevel(messages);
Assert.Equal(result, expectedResult);
}
}
}

View file

@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Geekbot.net\Geekbot.net.csproj" />
</ItemGroup>
</Project>

3
ansible-requirements.yml Normal file
View file

@ -0,0 +1,3 @@
collections:
- name: community.docker
version: 2.7.0

View file

@ -1,37 +1,28 @@
[![pipeline status](https://git.boerlage.me/open/Geekbot.net/badges/master/pipeline.svg)](https://git.boerlage.me/open/Geekbot.net/commits/master)
[![pipeline status](https://gitlab.com/dbgit/open/geekbot/badges/master/pipeline.svg)](https://gitlab.com/dbgit/open/geekbot/commits/master)
# [Geekbot.net](https://geekbot.pizzaandcoffee.rocks/)
A General Purpose Discord Bot written in DotNet Core.
A General Purpose Discord Bot written in C#
You can invite Geekbot to your server with [this link](https://discordapp.com/oauth2/authorize?client_id=171249478546882561&scope=bot&permissions=1416834054)
## Technologies
* DotNet Core 2
* Redis
* .NET 5
* PostgreSQL
* Discord.net
* ffmpeg
## Running
Make sure redis is running
You can start geekbot with: `dotnet run`
Run these commands
On your first run geekbot will ask for your bot token.
* `dotnet restore`
* `dotnet run`
You might need to pass some additional configuration (e.g. database credentials), these can be passed as commandline arguments or environment variables.
On your first run geekbot will ask for your bot token, everything else is taken care of.
For a list of commandline arguments and environment variables use `dotnet run -- -h`
### Launch Parameters
| Parameter | Description |
| --- | --- |
| `--verbose` | Show more log information |
| `--disable-api` | Disables the webapi on startup |
| `--reset` | Resets certain parts of the bot |
| `--migrate` | Migrates the database from V3.1 to the new format from V3.2<br> (make sure to backup before running this) |
All Environment Variables must be prefixed with `GEEKBOT_`
## Contributing

33
src/Bot/Bot.csproj Normal file
View file

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
<RootNamespace>Geekbot.Bot</RootNamespace>
<AssemblyName>Geekbot.Bot</AssemblyName>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.0-DEV</Version>
<NoWarn>NU1701</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.45.0.1929" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.36" />
<PackageReference Include="JikanDotNet" Version="1.6.0" />
<PackageReference Include="MtgApiManager.Lib" Version="1.2.2" />
<PackageReference Include="PokeApi.NET" Version="1.1.2" />
<PackageReference Include="Sentry" Version="3.11.0" />
</ItemGroup>
<ItemGroup>
<Content Include="Storage\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Commands\Commands.csproj" />
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
</Project>

127
src/Bot/BotStartup.cs Normal file
View file

@ -0,0 +1,127 @@
using System.Reflection;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.Bot.Handlers;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.GlobalSettings;
using Geekbot.Core.GuildSettingsManager;
using Geekbot.Core.Logger;
using Geekbot.Core.Logger.Adapters;
using Geekbot.Core.ReactionListener;
using Geekbot.Core.UserRepository;
using Microsoft.Extensions.DependencyInjection;
namespace Geekbot.Bot;
public class BotStartup
{
private readonly IServiceCollection _serviceCollection;
private readonly GeekbotLogger _logger;
private readonly RunParameters _runParameters;
private readonly IGlobalSettings _globalSettings;
private DiscordSocketClient _client;
public BotStartup(IServiceCollection serviceCollection, GeekbotLogger logger, RunParameters runParameters, IGlobalSettings globalSettings)
{
_serviceCollection = serviceCollection;
_logger = logger;
_runParameters = runParameters;
_globalSettings = globalSettings;
}
public async Task Start()
{
_logger.Information(LogSource.Geekbot, "Connecting to Discord");
SetupDiscordClient();
await Login();
await _client.SetGameAsync(_globalSettings.GetKey("Game"));
_logger.Information(LogSource.Geekbot, $"Now Connected as {_client.CurrentUser.Username} to {_client.Guilds.Count} Servers");
_logger.Information(LogSource.Geekbot, "Registering Gateway Handlers");
await RegisterHandlers();
_logger.Information(LogSource.Geekbot, "Done and ready for use");
await Task.Delay(-1);
}
private void SetupDiscordClient()
{
_client = new DiscordSocketClient(new DiscordSocketConfig
{
GatewayIntents = GatewayIntents.DirectMessageReactions |
GatewayIntents.DirectMessages |
GatewayIntents.GuildMessageReactions |
GatewayIntents.GuildMessages |
GatewayIntents.GuildWebhooks |
GatewayIntents.GuildIntegrations |
GatewayIntents.GuildEmojis |
GatewayIntents.GuildBans |
GatewayIntents.Guilds |
GatewayIntents.GuildMembers,
LogLevel = LogSeverity.Verbose,
MessageCacheSize = 1000,
});
var discordLogger = new DiscordLogger(_logger);
_client.Log += discordLogger.Log;
}
private async Task Login()
{
try
{
var token = await GetToken();
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
while (!_client.ConnectionState.Equals(ConnectionState.Connected)) await Task.Delay(25);
}
catch (Exception e)
{
_logger.Error(LogSource.Geekbot, "Could not connect to Discord", e);
Environment.Exit(GeekbotExitCode.CouldNotLogin.GetHashCode());
}
}
private async Task<string> GetToken()
{
var token = _runParameters.Token ?? _globalSettings.GetKey("DiscordToken");
if (string.IsNullOrEmpty(token))
{
Console.Write("Your bot Token: ");
var newToken = Console.ReadLine();
await _globalSettings.SetKey("DiscordToken", newToken);
await _globalSettings.SetKey("Game", "Ping Pong");
token = newToken;
}
return token;
}
private async Task RegisterHandlers()
{
var applicationInfo = await _client.GetApplicationInfoAsync();
_serviceCollection.AddSingleton<DiscordSocketClient>(_client);
var serviceProvider = _serviceCollection.BuildServiceProvider();
var commands = new CommandService();
await commands.AddModulesAsync(Assembly.GetAssembly(typeof(BotStartup)), serviceProvider);
var commandHandler = new CommandHandler(_client, _logger, serviceProvider, commands, applicationInfo, serviceProvider.GetService<IGuildSettingsManager>());
var userHandler = new UserHandler(serviceProvider.GetService<IUserRepository>(), _logger, serviceProvider.GetService<DatabaseContext>(), _client);
var reactionHandler = new ReactionHandler(serviceProvider.GetService<IReactionListener>());
var statsHandler = new StatsHandler(_logger, serviceProvider.GetService<DatabaseContext>());
var messageDeletedHandler = new MessageDeletedHandler(serviceProvider.GetService<DatabaseContext>(), _logger, _client);
_client.MessageReceived += commandHandler.RunCommand;
_client.MessageDeleted += messageDeletedHandler.HandleMessageDeleted;
_client.UserJoined += userHandler.Joined;
_client.UserUpdated += userHandler.Updated;
_client.UserLeft += userHandler.Left;
_client.ReactionAdded += reactionHandler.Added;
_client.ReactionRemoved += reactionHandler.Removed;
if (!_runParameters.InMemory) _client.MessageReceived += statsHandler.UpdateStats;
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
namespace Geekbot.Bot.CommandPreconditions
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DisableInDirectMessageAttribute : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
var result = context.Guild.Id != 0 ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("Command unavailable in Direct Messaging");
return Task.FromResult(result);
}
}
}

View file

@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Resources;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.Bot.CommandPreconditions;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.GuildSettingsManager;
using Localization = Geekbot.Core.Localization;
namespace Geekbot.Bot.Commands.Admin
{
[Group("admin")]
[RequireUserPermission(GuildPermission.Administrator)]
[DisableInDirectMessage]
public class Admin : GeekbotCommandBase
{
private readonly DiscordSocketClient _client;
public Admin(DiscordSocketClient client, IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager)
{
_client = client;
}
[Command("welcome", RunMode = RunMode.Async)]
[Summary("Set a Welcome Message (use '$user' to mention the new joined user).")]
public async Task SetWelcomeMessage([Remainder, Summary("message")] string welcomeMessage)
{
GuildSettings.WelcomeMessage = welcomeMessage;
await GuildSettingsManager.UpdateSettings(GuildSettings);
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("welcomechannel", RunMode = RunMode.Async)]
[Summary("Set a channel for the welcome messages (by default it uses the top most channel)")]
public async Task SelectWelcomeChannel([Summary("#Channel")] ISocketMessageChannel channel)
{
try
{
var m = await channel.SendMessageAsync("...");
GuildSettings.WelcomeChannel = channel.Id.AsLong();
await GuildSettingsManager.UpdateSettings(GuildSettings);
await m.DeleteAsync();
await ReplyAsync("Successfully saved the welcome channel");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context, "That channel doesn't seem to exist or i don't have write permissions");
}
}
[Command("modchannel", RunMode = RunMode.Async)]
[Summary("Set a channel for moderation purposes")]
public async Task SelectModChannel([Summary("#Channel")] ISocketMessageChannel channel)
{
try
{
var m = await channel.SendMessageAsync("verifying...");
GuildSettings.ModChannel = channel.Id.AsLong();
await GuildSettingsManager.UpdateSettings(GuildSettings);
var sb = new StringBuilder();
sb.AppendLine("Successfully saved mod channel, you can now do the following");
sb.AppendLine("- `!admin showleave` - send message to mod channel when someone leaves");
sb.AppendLine("- `!admin showdel` - send message to mod channel when someone deletes a message");
await m.ModifyAsync(e => e.Content = sb.ToString());
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context, "That channel doesn't seem to exist or i don't have write permissions");
}
}
[Command("showleave", RunMode = RunMode.Async)]
[Summary("Toggle - notify modchannel when someone leaves")]
public async Task ShowLeave()
{
try
{
var modChannel = await GetModChannel(GuildSettings.ModChannel.AsUlong());
if (modChannel == null) return;
GuildSettings.ShowLeave = !GuildSettings.ShowLeave;
await GuildSettingsManager.UpdateSettings(GuildSettings);
await modChannel.SendMessageAsync(GuildSettings.ShowLeave
? "Saved - now sending messages here when someone leaves"
: "Saved - stopping sending messages here when someone leaves"
);
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("showdel", RunMode = RunMode.Async)]
[Summary("Toggle - notify modchannel when someone deletes a message")]
public async Task ShowDelete()
{
try
{
var modChannel = await GetModChannel(GuildSettings.ModChannel.AsUlong());
if (modChannel == null) return;
GuildSettings.ShowDelete = !GuildSettings.ShowDelete;
await GuildSettingsManager.UpdateSettings(GuildSettings);
await modChannel.SendMessageAsync(GuildSettings.ShowDelete
? "Saved - now sending messages here when someone deletes a message"
: "Saved - stopping sending messages here when someone deletes a message"
);
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("setlang", RunMode = RunMode.Async)]
[Summary("Change the bots language")]
public async Task SetLanguage([Summary("language")] string language)
{
try
{
var availableLanguages = new List<string>();
availableLanguages.Add("en-GB"); // default
availableLanguages.AddRange(GetAvailableCultures().Select(culture => culture.Name));
if (availableLanguages.Contains(language))
{
GuildSettings.Language = language;
await GuildSettingsManager.UpdateSettings(GuildSettings);
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(language.ToLower() == "chde" ? "de-CH" : language);
await ReplyAsync(Localization.Admin.NewLanguageSet);
return;
}
await ReplyAsync($"That doesn't seem to be a supported language\nSupported Languages are {string.Join(", ", availableLanguages)}");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("wiki", RunMode = RunMode.Async)]
[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();
GuildSettings.WikiLang = language;
await GuildSettingsManager.UpdateSettings(GuildSettings);
await ReplyAsync($"Now using the {language} wikipedia");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("ping", RunMode = RunMode.Async)]
[Summary("Enable the ping reply.")]
public async Task TogglePing()
{
try
{
// var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
GuildSettings.Ping = !GuildSettings.Ping;
await GuildSettingsManager.UpdateSettings(GuildSettings);
await ReplyAsync(GuildSettings.Ping ? "i will reply to ping now" : "No more pongs...");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("hui", RunMode = RunMode.Async)]
[Summary("Enable the ping reply.")]
public async Task ToggleHui()
{
try
{
// var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
GuildSettings.Hui = !GuildSettings.Hui;
await GuildSettingsManager.UpdateSettings(GuildSettings);
await ReplyAsync(GuildSettings.Hui ? "i will reply to hui now" : "No more hui's...");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
private async Task<ISocketMessageChannel> GetModChannel(ulong channelId)
{
try
{
if (channelId == ulong.MinValue) throw new Exception();
var modChannel = (ISocketMessageChannel) _client.GetChannel(channelId);
if (modChannel == null) throw new Exception();
return modChannel;
}
catch
{
await ReplyAsync("Modchannel doesn't seem to exist, please set one with `!admin modchannel [channelId]`");
return null;
}
}
private IEnumerable<CultureInfo> GetAvailableCultures()
{
var result = new List<CultureInfo>();
var rm = new ResourceManager(typeof(Localization.Admin));
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
foreach (var culture in cultures)
{
try
{
if (culture.Equals(CultureInfo.InvariantCulture)) continue; //do not use "==", won't work
var rs = rm.GetResourceSet(culture, true, false);
if (rs != null)
{
result.Add(culture);
}
}
catch (CultureNotFoundException)
{
//NOP
}
}
return result;
}
}
}

View file

@ -0,0 +1,127 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.GlobalSettings;
using Geekbot.Core.GuildSettingsManager;
using Geekbot.Core.Logger;
using Geekbot.Core.UserRepository;
namespace Geekbot.Bot.Commands.Admin.Owner
{
[Group("owner")]
[RequireOwner]
public class Owner : GeekbotCommandBase
{
private readonly DiscordSocketClient _client;
private readonly IGlobalSettings _globalSettings;
private readonly IGeekbotLogger _logger;
private readonly IUserRepository _userRepository;
public Owner(DiscordSocketClient client, IGeekbotLogger logger, IUserRepository userRepositry, IErrorHandler errorHandler, IGlobalSettings globalSettings,
IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager)
{
_client = client;
_logger = logger;
_userRepository = userRepositry;
_globalSettings = globalSettings;
}
[Command("youtubekey", RunMode = RunMode.Async)]
[Summary("Set the youtube api key")]
public async Task SetYoutubeKey([Summary("API-Key")] string key)
{
await _globalSettings.SetKey("YoutubeKey", key);
await ReplyAsync("Apikey has been set");
}
[Command("game", RunMode = RunMode.Async)]
[Summary("Set the game that the bot is playing")]
public async Task SetGame([Remainder] [Summary("Game")] string key)
{
await _globalSettings.SetKey("Game", key);
await _client.SetGameAsync(key);
_logger.Information(LogSource.Geekbot, $"Changed game to {key}");
await ReplyAsync($"Now Playing {key}");
}
[Command("popuserrepo", RunMode = RunMode.Async)]
[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)
{
await ErrorHandler.HandleCommandException(e, Context,
"Couldn't complete User Repository, see console for more info");
}
}
[Command("refreshuser", RunMode = RunMode.Async)]
[Summary("Refresh a user in the user cache")]
public async Task PopUserRepoCommand([Summary("@someone")] IUser user)
{
try
{
await _userRepository.Update(user as SocketUser);
await ReplyAsync($"Refreshed: {user.Username}#{user.Discriminator}");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("refreshuser", RunMode = RunMode.Async)]
[Summary("Refresh a user in the user cache")]
public async Task PopUserRepoCommand([Summary("user-id")] ulong userId)
{
try
{
var user = _client.GetUser(userId);
await _userRepository.Update(user);
await ReplyAsync($"Refreshed: {user.Username}#{user.Discriminator}");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("error", RunMode = RunMode.Async)]
[Summary("Throw an error un purpose")]
public async Task PurposefulError()
{
try
{
throw new Exception("Error Generated by !owner error");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,197 @@
using System;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Net;
using Geekbot.Bot.CommandPreconditions;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.Database.Models;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.GuildSettingsManager;
using Geekbot.Core.ReactionListener;
using Localization = Geekbot.Core.Localization;
namespace Geekbot.Bot.Commands.Admin
{
[Group("role")]
[DisableInDirectMessage]
public class Role : GeekbotCommandBase
{
private readonly DatabaseContext _database;
private readonly IReactionListener _reactionListener;
public Role(DatabaseContext database, IErrorHandler errorHandler, IReactionListener reactionListener, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager)
{
_database = database;
_reactionListener = reactionListener;
}
[Command(RunMode = RunMode.Async)]
[Summary("Get a list of all available roles.")]
public async Task GetAllRoles()
{
try
{
var roles = _database.RoleSelfService.Where(g => g.GuildId.Equals(Context.Guild.Id.AsLong())).ToList();
if (roles.Count == 0)
{
await ReplyAsync(Localization.Role.NoRolesConfigured);
return;
}
var sb = new StringBuilder();
sb.AppendLine(string.Format(Localization.Role.ListHeader, Context.Guild.Name));
sb.AppendLine(Localization.Role.ListInstruction);
foreach (var role in roles) sb.AppendLine($"- {role.WhiteListName}");
await ReplyAsync(sb.ToString());
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command(RunMode = RunMode.Async)]
[Summary("Get a role by mentioning it.")]
public async Task GiveRole([Summary("role-nickname")] string roleNameRaw)
{
try
{
var roleName = roleNameRaw.ToLower();
var roleFromDb = _database.RoleSelfService.FirstOrDefault(e =>
e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.WhiteListName.Equals(roleName));
if (roleFromDb != null)
{
var guildUser = (IGuildUser) Context.User;
var role = Context.Guild.Roles.First(r => r.Id == roleFromDb.RoleId.AsUlong());
if (role == null)
{
await ReplyAsync(Localization.Role.RoleNotFound);
return;
}
if (guildUser.RoleIds.Contains(role.Id))
{
await guildUser.RemoveRoleAsync(role);
await ReplyAsync(string.Format(Localization.Role.RemovedUserFromRole, role.Name));
return;
}
await guildUser.AddRoleAsync(role);
await ReplyAsync(string.Format(Localization.Role.AddedUserFromRole, role.Name));
return;
}
await ReplyAsync(Localization.Role.RoleNotFound);
}
catch (HttpException e)
{
if (e.HttpCode == HttpStatusCode.Forbidden)
{
await ReplyAsync(Localization.Internal.Http403);
}
else
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.ManageRoles)]
[Command("add", RunMode = RunMode.Async)]
[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(Localization.Role.CannotAddManagedRole);
return;
}
if (role.Permissions.ManageRoles
|| role.Permissions.Administrator
|| role.Permissions.ManageGuild
|| role.Permissions.BanMembers
|| role.Permissions.KickMembers)
{
await ReplyAsync(Localization.Role.CannotAddDangerousRole);
return;
}
_database.RoleSelfService.Add(new RoleSelfServiceModel
{
GuildId = Context.Guild.Id.AsLong(),
RoleId = role.Id.AsLong(),
WhiteListName = roleName
});
await _database.SaveChangesAsync();
await ReplyAsync(string.Format(Localization.Role.AddedRoleToWhitelist, role.Name));
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.ManageRoles)]
[Command("remove", RunMode = RunMode.Async)]
[Summary("Remove a role from the whitelist.")]
public async Task RemoveRole([Summary("role-nickname")] string roleName)
{
try
{
var roleFromDb = _database.RoleSelfService.FirstOrDefault(e =>
e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.WhiteListName.Equals(roleName));
if (roleFromDb != null)
{
_database.RoleSelfService.Remove(roleFromDb);
await _database.SaveChangesAsync();
await ReplyAsync(string.Format(Localization.Role.RemovedRoleFromWhitelist, roleName));
return;
}
await ReplyAsync(Localization.Role.RoleNotFound);
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.ManageRoles)]
[Summary("Give a role by clicking on an emoji")]
[Command("listen", RunMode = RunMode.Async)]
public async Task AddListener([Summary("message-ID")] string messageIdStr, [Summary("Emoji")] string emoji, [Summary("@role")] IRole role)
{
try
{
var messageId = ulong.Parse(messageIdStr);
var message = (IUserMessage) await Context.Channel.GetMessageAsync(messageId);
var emote = _reactionListener.ConvertStringToEmote(emoji);
await message.AddReactionAsync(emote);
await _reactionListener.AddRoleToListener(messageId, Context.Guild.Id, emoji, role);
await Context.Message.DeleteAsync();
}
catch (HttpException)
{
await Context.Channel.SendMessageAsync("Custom emojis from other servers are not supported");
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -3,12 +3,14 @@ using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using PokeAPI;
namespace Geekbot.net.Commands
namespace Geekbot.Bot.Commands.Games
{
public class Pokedex : ModuleBase
public class Pokedex : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
@ -18,9 +20,8 @@ namespace Geekbot.net.Commands
}
[Command("pokedex", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("A Pokedex Tool")]
public async Task GetPokemon([Summary("pokemonName")] string pokemonName)
public async Task GetPokemon([Summary("pokemon-name")] string pokemonName)
{
try
{
@ -36,38 +37,38 @@ namespace Geekbot.net.Commands
return;
}
var embed = await pokemonEmbedBuilder(pokemon);
var embed = await PokemonEmbedBuilder(pokemon);
await ReplyAsync("", false, embed.Build());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
await _errorHandler.HandleCommandException(e, Context);
}
}
private async Task<EmbedBuilder> pokemonEmbedBuilder(Pokemon pokemon)
private async Task<EmbedBuilder> PokemonEmbedBuilder(Pokemon pokemon)
{
var eb = new EmbedBuilder();
var species = await DataFetcher.GetApiObject<PokemonSpecies>(pokemon.ID);
eb.Title = $"#{pokemon.ID} {toUpper(pokemon.Name)}";
eb.Title = $"#{pokemon.ID} {ToUpper(pokemon.Name)}";
eb.Description = species.FlavorTexts[1].FlavorText;
eb.ThumbnailUrl = pokemon.Sprites.FrontMale ?? pokemon.Sprites.FrontFemale;
eb.AddInlineField(getSingularOrPlural(pokemon.Types.Length, "Type"),
string.Join(", ", pokemon.Types.Select(t => toUpper(t.Type.Name))));
eb.AddInlineField(getSingularOrPlural(pokemon.Abilities.Length, "Ability"),
string.Join(", ", pokemon.Abilities.Select(t => toUpper(t.Ability.Name))));
eb.AddInlineField(GetSingularOrPlural(pokemon.Types.Length, "Type"),
string.Join(", ", pokemon.Types.Select(t => ToUpper(t.Type.Name))));
eb.AddInlineField(GetSingularOrPlural(pokemon.Abilities.Length, "Ability"),
string.Join(", ", pokemon.Abilities.Select(t => ToUpper(t.Ability.Name))));
eb.AddInlineField("Height", pokemon.Height);
eb.AddInlineField("Weight", pokemon.Mass);
return eb;
}
private string getSingularOrPlural(int lenght, string word)
private string GetSingularOrPlural(int lenght, string word)
{
if (lenght == 1) return word;
return word.EndsWith("y") ? $"{word.Remove(word.Length - 1)}ies" : $"{word}s";
}
private string toUpper(string s)
private string ToUpper(string s)
{
if (string.IsNullOrEmpty(s)) return string.Empty;
return char.ToUpper(s[0]) + s.Substring(1);

View file

@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.GuildSettingsManager;
using Geekbot.Core.KvInMemoryStore;
using Geekbot.Core.RandomNumberGenerator;
using Sentry;
namespace Geekbot.Bot.Commands.Games.Roll
{
public class Roll : GeekbotCommandBase
{
private readonly IKvInMemoryStore _kvInMemoryStore;
private readonly DatabaseContext _database;
private readonly IRandomNumberGenerator _randomNumberGenerator;
public Roll(IKvInMemoryStore kvInMemoryStore, IErrorHandler errorHandler, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator, IGuildSettingsManager guildSettingsManager)
: base(errorHandler, guildSettingsManager)
{
_kvInMemoryStore = kvInMemoryStore;
_database = database;
_randomNumberGenerator = randomNumberGenerator;
}
[Command("roll", RunMode = RunMode.Async)]
[Summary("Guess which number the bot will roll (1-100")]
public async Task RollCommand([Remainder] [Summary("guess")] string stuff = null)
{
try
{
var res = await new Geekbot.Commands.Roll.Roll(_kvInMemoryStore, _database, _randomNumberGenerator)
.RunFromGateway(
Context.Guild.Id,
Context.User.Id,
Context.User.Username,
stuff ?? "0"
);
await ReplyAsync(res);
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
Transaction.Status = SpanStatus.InternalError;
}
}
}
}

View file

@ -0,0 +1,61 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Integrations.LolMmr
{
public class LolMmr : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public LolMmr(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("mmr", RunMode = RunMode.Async)]
[Summary("Get the League of Legends MMR for a specified summoner")]
public async Task GetMmr([Remainder] [Summary("summoner")] string summonerName)
{
try
{
LolMmrDto data;
try
{
var name = HttpUtility.UrlEncode(summonerName.ToLower());
var httpClient = HttpAbstractions.CreateDefaultClient();
// setting the user agent in accordance with the whatismymmr.com api rules
httpClient.DefaultRequestHeaders.Remove("User-Agent");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Linux:rocks.pizzaandcoffee.geekbot:v0.0.0");
data = await HttpAbstractions.Get<LolMmrDto>(new Uri($"https://euw.whatismymmr.com/api/v1/summoner?name={name}"), httpClient);
}
catch (HttpRequestException e)
{
if (e.StatusCode != HttpStatusCode.NotFound) throw e;
await Context.Channel.SendMessageAsync("Player not found");
return;
}
var sb = new StringBuilder();
sb.AppendLine($"**MMR for {summonerName}**");
sb.AppendLine($"Normal: {data.Normal?.Avg ?? 0}");
sb.AppendLine($"Ranked: {data.Ranked?.Avg ?? 0}");
sb.AppendLine($"ARAM: {data.ARAM?.Avg ?? 0}");
await Context.Channel.SendMessageAsync(sb.ToString());
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Integrations.LolMmr
{
public class LolMmrDto
{
[JsonPropertyName("ranked")]
public LolMrrInfoDto Ranked { get; set; }
[JsonPropertyName("normal")]
public LolMrrInfoDto Normal { get; set; }
[JsonPropertyName("aram")]
public LolMrrInfoDto ARAM { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Integrations.LolMmr
{
public class LolMrrInfoDto
{
[JsonPropertyName("avg")]
public decimal? Avg { get; set; }
}
}

View file

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.Converters;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using MtgApiManager.Lib.Service;
namespace Geekbot.Bot.Commands.Integrations
{
public class MagicTheGathering : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IMtgManaConverter _manaConverter;
public MagicTheGathering(IErrorHandler errorHandler, IMtgManaConverter manaConverter)
{
_errorHandler = errorHandler;
_manaConverter = manaConverter;
}
[Command("mtg", RunMode = RunMode.Async)]
[Summary("Find a Magic The Gathering Card.")]
public async Task GetCard([Remainder] [Summary("card-name")] string cardName)
{
try
{
var message = await Context.Channel.SendMessageAsync($":mag: Looking up \"{cardName}\", please wait...");
var service = new CardService();
var result = service
.Where(x => x.Name, cardName)
// fewer cards less risk of deserialization problems, don't need more than one anyways...
.Where(x => x.PageSize, 1);
var cards = await result.AllAsync();
if (!cards.IsSuccess)
{
await message.ModifyAsync(properties => properties.Content = $":warning: The Gatherer reacted in an unexpected way: {cards.Exception.Message}");
return;
}
var card = cards.Value.FirstOrDefault();
if (card == null)
{
await message.ModifyAsync(properties => properties.Content = ":red_circle: I couldn't find a card with that name...");
return;
}
var eb = new EmbedBuilder
{
Title = card.Name,
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 && card.Legalities.Count > 0)
eb.AddField("Legality", string.Join(", ", card.Legalities.Select(e => e.Format)));
await message.ModifyAsync(properties =>
{
properties.Content = string.Empty;
properties.Embed = eb.Build();
});
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
private Color GetColor(IEnumerable<string> colors)
{
var color = colors.FirstOrDefault();
return color switch
{
"Black" => new Color(203, 194, 191),
"White" => new Color(255, 251, 213),
"Blue" => new Color(170, 224, 250),
"Red" => new Color(250, 170, 143),
"Green" => new Color(155, 211, 174),
_ => new Color(204, 194, 212)
};
}
}
}

View file

@ -0,0 +1,108 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.GuildSettingsManager;
using JikanDotNet;
namespace Geekbot.Bot.Commands.Integrations
{
public class Mal : GeekbotCommandBase
{
private readonly IJikan _client;
public Mal(IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager)
{
_client = new Jikan();
}
[Command("anime", RunMode = RunMode.Async)]
[Summary("Show Info about an Anime.")]
public async Task SearchAnime([Remainder] [Summary("anime-name")] string animeName)
{
try
{
var results = await _client.SearchAnime(animeName);
var anime = results.Results.FirstOrDefault();
if (anime != null)
{
var eb = new EmbedBuilder
{
Title = anime.Title,
Description = anime.Description,
ImageUrl = anime.ImageURL
};
eb.AddInlineField("Premiere", FormatDate(anime.StartDate))
.AddInlineField("Ended", anime.Airing ? "-" : FormatDate(anime.EndDate))
.AddInlineField("Episodes", anime.Episodes)
.AddInlineField("MAL Score", anime.Score)
.AddInlineField("Type", anime.Type)
.AddField("MAL Link", $"https://myanimelist.net/anime/{anime.MalId}");
await ReplyAsync("", false, eb.Build());
}
else
{
await ReplyAsync("No anime found with that name...");
}
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("manga", RunMode = RunMode.Async)]
[Summary("Show Info about a Manga.")]
public async Task SearchManga([Remainder] [Summary("manga-name")] string mangaName)
{
try
{
var results = await _client.SearchManga(mangaName);
var manga = results.Results.FirstOrDefault();
if (manga != null)
{
var eb = new EmbedBuilder
{
Title = manga.Title,
Description = manga.Description,
ImageUrl = manga.ImageURL
};
eb.AddInlineField("Premiere", FormatDate(manga.StartDate))
.AddInlineField("Ended", manga.Publishing ? "-" : FormatDate(manga.EndDate))
.AddInlineField("Volumes", manga.Volumes)
.AddInlineField("Chapters", manga.Chapters)
.AddInlineField("MAL Score", manga.Score)
.AddField("MAL Link", $"https://myanimelist.net/manga/{manga.MalId}");
await ReplyAsync("", false, eb.Build());
}
else
{
await ReplyAsync("No manga found with that name...");
}
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
private string FormatDate(DateTime? dateTime)
{
if (!dateTime.HasValue)
{
return DateTime.MinValue.ToString("d", Thread.CurrentThread.CurrentUICulture);
}
return dateTime.Value.ToString("d", Thread.CurrentThread.CurrentUICulture);
}
}
}

View file

@ -0,0 +1,39 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Integrations
{
public class UrbanDictionary : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public UrbanDictionary(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("urban", RunMode = RunMode.Async)]
[Summary("Lookup something on urban dictionary")]
public async Task UrbanDefine([Remainder] [Summary("word")] string word)
{
try
{
var eb = await Geekbot.Commands.UrbanDictionary.UrbanDictionary.Run(word);
if (eb == null)
{
await ReplyAsync("That word hasn't been defined...");
return;
}
await ReplyAsync(string.Empty, false, eb.ToDiscordNetEmbed().Build());
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,113 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.WikipediaClient;
using Geekbot.Core.WikipediaClient.Page;
using HtmlAgilityPack;
namespace Geekbot.Bot.Commands.Integrations
{
public class Wikipedia : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IWikipediaClient _wikipediaClient;
private readonly DatabaseContext _database;
public Wikipedia(IErrorHandler errorHandler, IWikipediaClient wikipediaClient, DatabaseContext database)
{
_errorHandler = errorHandler;
_wikipediaClient = wikipediaClient;
_database = database;
}
[Command("wiki", RunMode = RunMode.Async)]
[Summary("Get an article from wikipedia.")]
public async Task GetPreview([Remainder] [Summary("article")] string articleName)
{
try
{
var wikiLang = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(Context.Guild.Id.AsLong()))?.WikiLang;
if (string.IsNullOrEmpty(wikiLang))
{
wikiLang = "en";
}
var article = await _wikipediaClient.GetPreview(articleName.Replace(" ", "_"), wikiLang);
if (article.Type != PageTypes.Standard)
{
switch (article.Type)
{
case PageTypes.Disambiguation:
await ReplyAsync($"**__Disambiguation__**\r\n{DisambiguationExtractor(article.ExtractHtml)}");
break;
case PageTypes.MainPage:
await ReplyAsync("The main page is not supported");
break;
case PageTypes.NoExtract:
await ReplyAsync($"This page has no summary, here is the link: {article.ContentUrls.Desktop.Page}");
break;
case PageTypes.Standard:
break;
default:
await ReplyAsync($"This page type is currently not supported, here is the link: {article.ContentUrls.Desktop.Page}");
break;
}
return;
}
var eb = new EmbedBuilder
{
Title = article.Title,
Description = article.Description,
ImageUrl = article.Thumbnail?.Source.ToString(),
Url = article.ContentUrls.Desktop.Page.ToString(),
Color = new Color(246,246,246),
Timestamp = article.Timestamp,
Footer = new EmbedFooterBuilder
{
Text = "Last Edit",
IconUrl = "http://icons.iconarchive.com/icons/sykonist/popular-sites/256/Wikipedia-icon.png"
}
};
eb.AddField("Description", article.Extract);
if (article.Coordinates != null) eb.AddField("Coordinates", $"{article.Coordinates.Lat} Lat {article.Coordinates.Lon} Lon");
await ReplyAsync("", false, eb.Build());
}
catch (HttpRequestException)
{
await ReplyAsync("I couldn't find that article");
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
private string DisambiguationExtractor(string extractHtml)
{
var doc = new HtmlDocument();
doc.LoadHtml(extractHtml);
var nodes = doc.DocumentNode.SelectNodes("//li");
if (nodes == null) return "(List is to long to show)";
var sb = new StringBuilder();
foreach (var node in nodes)
{
var split = node.InnerText.Split(',');
var title = split.First();
var desc = string.Join(",", split.Skip(1));
sb.AppendLine($"• **{title}** -{desc}");
}
return sb.ToString();
}
}
}

View file

@ -0,0 +1,59 @@
using Discord.Commands;
using Geekbot.Core;
// using Geekbot.Core.ErrorHandling;
// using Geekbot.Core.GlobalSettings;
// using Google.Apis.Services;
// using Google.Apis.YouTube.v3;
namespace Geekbot.Bot.Commands.Integrations
{
public class Youtube : TransactionModuleBase
{
// private readonly IGlobalSettings _globalSettings;
// private readonly IErrorHandler _errorHandler;
// public Youtube(IGlobalSettings globalSettings, IErrorHandler errorHandler)
// {
// _globalSettings = globalSettings;
// _errorHandler = errorHandler;
// }
[Command("yt", RunMode = RunMode.Async)]
[Summary("Search for something on youtube.")]
public async Task Yt([Remainder] [Summary("title")] string searchQuery)
{
await ReplyAsync("The youtube command is temporarily disabled");
// var key = _globalSettings.GetKey("YoutubeKey");
// if (string.IsNullOrEmpty(key))
// {
// await ReplyAsync("No youtube key set, please tell my senpai to set one");
// return;
// }
//
// try
// {
// var youtubeService = new YouTubeService(new BaseClientService.Initializer
// {
// ApiKey = key,
// 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)
// {
// await _errorHandler.HandleCommandException(e, Context);
// }
}
}
}

View file

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.RandomNumberGenerator;
namespace Geekbot.Bot.Commands.Randomness
{
public class BenedictCumberbatchNameGenerator : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IRandomNumberGenerator _randomNumberGenerator;
public BenedictCumberbatchNameGenerator(IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator)
{
_errorHandler = errorHandler;
_randomNumberGenerator = randomNumberGenerator;
}
[Command("bdcb", RunMode = RunMode.Async)]
[Summary("Benedict Cumberbatch Name Generator")]
public async Task GetQuote()
{
try
{
var firstnameList = new List<string>
{
"Bumblebee", "Bandersnatch", "Broccoli", "Rinkydink", "Bombadil", "Boilerdang", "Bandicoot", "Fragglerock", "Muffintop", "Congleton", "Blubberdick", "Buffalo", "Benadryl",
"Butterfree", "Burberry", "Whippersnatch", "Buttermilk", "Beezlebub", "Budapest", "Boilerdang", "Blubberwhale", "Bumberstump", "Bulbasaur", "Cogglesnatch", "Liverswort",
"Bodybuild", "Johnnycash", "Bendydick", "Burgerking", "Bonaparte", "Bunsenburner", "Billiardball", "Bukkake", "Baseballmitt", "Blubberbutt", "Baseballbat", "Rumblesack",
"Barister", "Danglerack", "Rinkydink", "Bombadil", "Honkytonk", "Billyray", "Bumbleshack", "Snorkeldink", "Beetlejuice", "Bedlington", "Bandicoot", "Boobytrap", "Blenderdick",
"Bentobox", "Pallettown", "Wimbledon", "Buttercup", "Blasphemy", "Syphilis", "Snorkeldink", "Brandenburg", "Barbituate", "Snozzlebert", "Tiddleywomp", "Bouillabaisse",
"Wellington", "Benetton", "Bendandsnap", "Timothy", "Brewery", "Bentobox", "Brandybuck", "Benjamin", "Buckminster", "Bourgeoisie", "Bakery", "Oscarbait", "Buckyball",
"Bourgeoisie", "Burlington", "Buckingham", "Barnoldswick", "Bumblesniff", "Butercup", "Bubblebath", "Fiddlestick", "Bulbasaur", "Bumblebee", "Bettyboop", "Botany", "Cadbury",
"Brendadirk", "Buckingham", "Barnabus", "Barnacle", "Billybong", "Botany", "Benddadick", "Benderchick"
};
var lastnameList = new List<string>
{
"Coddleswort", "Crumplesack", "Curdlesnoot", "Calldispatch", "Humperdinck", "Rivendell", "Cuttlefish", "Lingerie", "Vegemite", "Ampersand", "Cumberbund", "Candycrush",
"Clombyclomp", "Cragglethatch", "Nottinghill", "Cabbagepatch", "Camouflage", "Creamsicle", "Curdlemilk", "Upperclass", "Frumblesnatch", "Crumplehorn", "Talisman", "Candlestick",
"Chesterfield", "Bumbersplat", "Scratchnsniff", "Snugglesnatch", "Charizard", "Carrotstick", "Cumbercooch", "Crackerjack", "Crucifix", "Cuckatoo", "Cockletit", "Collywog",
"Capncrunch", "Covergirl", "Cumbersnatch", "Countryside", "Coggleswort", "Splishnsplash", "Copperwire", "Animorph", "Curdledmilk", "Cheddarcheese", "Cottagecheese", "Crumplehorn",
"Snickersbar", "Banglesnatch", "Stinkyrash", "Cameltoe", "Chickenbroth", "Concubine", "Candygram", "Moldyspore", "Chuckecheese", "Cankersore", "Crimpysnitch", "Wafflesmack",
"Chowderpants", "Toodlesnoot", "Clavichord", "Cuckooclock", "Oxfordshire", "Cumbersome", "Chickenstrips", "Battleship", "Commonwealth", "Cunningsnatch", "Custardbath",
"Kryptonite", "Curdlesnoot", "Cummerbund", "Coochyrash", "Crackerdong", "Crackerdong", "Curdledong", "Crackersprout", "Crumplebutt", "Colonist", "Coochierash", "Anglerfish",
"Cumbersniff", "Charmander", "Scratch-n-sniff", "Cumberbitch", "Pumpkinpatch", "Cramplesnutch", "Lumberjack", "Bonaparte", "Cul-de-sac", "Cankersore", "Cucumbercatch", "Contradict"
};
var lastname = lastnameList[_randomNumberGenerator.Next(0, lastnameList.Count - 1)];
var firstname = firstnameList[_randomNumberGenerator.Next(0, firstnameList.Count - 1)];
await ReplyAsync($"{firstname} {lastname}");
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Cat
{
public class Cat : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public Cat(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("cat", RunMode = RunMode.Async)]
[Summary("Return a random image of a cat.")]
public async Task Say()
{
try
{
var response = await HttpAbstractions.Get<CatResponseDto>(new Uri("https://aws.random.cat/meow"));
var eb = new EmbedBuilder
{
ImageUrl = response.File
};
await ReplyAsync("", false, eb.Build());
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Randomness.Cat
{
internal class CatResponseDto
{
[JsonPropertyName("file")]
public string File { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Randomness.Chuck
{
internal class ChuckNorrisJokeResponseDto
{
[JsonPropertyName("value")]
public string Value { get; set; }
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Chuck
{
public class ChuckNorrisJokes : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public ChuckNorrisJokes(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("chuck", RunMode = RunMode.Async)]
[Summary("A random chuck norris joke")]
public async Task Say()
{
try
{
try
{
var response = await HttpAbstractions.Get<ChuckNorrisJokeResponseDto>(new Uri("https://api.chucknorris.io/jokes/random"));
await ReplyAsync(response.Value);
}
catch (HttpRequestException)
{
await ReplyAsync("Api down...");
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Randomness.Dad
{
internal class DadJokeResponseDto
{
[JsonPropertyName("joke")]
public string Joke { get; set; }
}
}

View file

@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Dad
{
public class DadJokes : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public DadJokes(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("dad", RunMode = RunMode.Async)]
[Summary("A random dad joke")]
public async Task Say()
{
try
{
var response = await HttpAbstractions.Get<DadJokeResponseDto>(new Uri("https://icanhazdadjoke.com/"));
await ReplyAsync(response.Joke);
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,34 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Dog
{
public class Dog : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public Dog(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("dog", RunMode = RunMode.Async)]
[Summary("Return a random image of a dog.")]
public async Task Say()
{
try
{
var response = await HttpAbstractions.Get<DogResponseDto>(new Uri("http://random.dog/woof.json"));
await ReplyAsync(response.Url);
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Randomness.Dog
{
internal class DogResponseDto
{
[JsonPropertyName("url")]
public string Url { get; set; }
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.GuildSettingsManager;
using Localization = Geekbot.Core.Localization;
namespace Geekbot.Bot.Commands.Randomness
{
public class EightBall : GeekbotCommandBase
{
public EightBall(IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager)
{
}
[Command("8ball", RunMode = RunMode.Async)]
[Summary("Ask 8Ball a Question.")]
public async Task Ball([Remainder] [Summary("question")] string echo)
{
try
{
var enumerator = Localization.EightBall.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true).GetEnumerator();
var replies = new List<string>();
while (enumerator.MoveNext())
{
replies.Add(enumerator.Value?.ToString());
}
var answer = new Random().Next(replies.Count);
await ReplyAsync(replies[answer]);
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -1,11 +1,11 @@
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.net.Lib;
using Geekbot.net.Lib.Media;
using Geekbot.Core;
using Geekbot.Core.Media;
namespace Geekbot.net.Commands
namespace Geekbot.Bot.Commands.Randomness
{
public class Fortune : ModuleBase
public class Fortune : TransactionModuleBase
{
private readonly IFortunesProvider _fortunes;
@ -15,7 +15,6 @@ namespace Geekbot.net.Commands
}
[Command("fortune", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random fortune")]
public async Task GetAFortune()
{

View file

@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Kanye
{
public class Kanye : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
public Kanye(IErrorHandler errorHandler)
{
_errorHandler = errorHandler;
}
[Command("kanye", RunMode = RunMode.Async)]
[Summary("A random kayne west quote")]
public async Task Say()
{
try
{
var response = await HttpAbstractions.Get<KanyeResponseDto>(new Uri("https://api.kanye.rest/"));
await ReplyAsync(response.Quote);
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Geekbot.Bot.Commands.Randomness.Kanye
{
public class KanyeResponseDto
{
[JsonPropertyName("quote")]
public string Quote { get; set; }
}
}

View file

@ -0,0 +1,81 @@
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.Media;
namespace Geekbot.Bot.Commands.Randomness
{
public class RandomAnimals : TransactionModuleBase
{
private readonly IMediaProvider _mediaProvider;
public RandomAnimals(IMediaProvider mediaProvider)
{
_mediaProvider = mediaProvider;
}
[Command("panda", RunMode = RunMode.Async)]
[Summary("Get a random panda image")]
public async Task Panda()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Panda)));
}
[Command("croissant", RunMode = RunMode.Async)]
[Alias("gipfeli")]
[Summary("Get a random croissant image")]
public async Task Croissant()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Croissant)));
}
[Command("pumpkin", RunMode = RunMode.Async)]
[Summary("Get a random pumpkin image")]
public async Task Pumpkin()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Pumpkin)));
}
[Command("squirrel", RunMode = RunMode.Async)]
[Summary("Get a random squirrel image")]
public async Task Squirrel()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Squirrel)));
}
[Command("turtle", RunMode = RunMode.Async)]
[Summary("Get a random turtle image")]
public async Task Turtle()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Turtle)));
}
[Command("penguin", RunMode = RunMode.Async)]
[Alias("pengu")]
[Summary("Get a random penguin image")]
public async Task Penguin()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Penguin)));
}
[Command("fox", RunMode = RunMode.Async)]
[Summary("Get a random fox image")]
public async Task Fox()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Fox)));
}
[Command("dab", RunMode = RunMode.Async)]
[Summary("Get a random dab image")]
public async Task Dab()
{
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Dab)));
}
private static Embed Eb(string image)
{
return new EmbedBuilder {ImageUrl = image}.Build();
}
}
}

View file

@ -0,0 +1,103 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.Database.Models;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.GuildSettingsManager;
using Geekbot.Core.RandomNumberGenerator;
using Localization = Geekbot.Core.Localization;
namespace Geekbot.Bot.Commands.Randomness
{
public class Ship : GeekbotCommandBase
{
private readonly IRandomNumberGenerator _randomNumberGenerator;
private readonly DatabaseContext _database;
public Ship(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager)
{
_database = database;
_randomNumberGenerator = randomNumberGenerator;
}
[Command("Ship", RunMode = RunMode.Async)]
[Summary("Ask the Shipping meter")]
public async Task Command([Summary("@user1")] IUser user1, [Summary("@user2")] IUser user2)
{
try
{
var userKeys = user1.Id < user2.Id
? new Tuple<long, long>(user1.Id.AsLong(), user2.Id.AsLong())
: new Tuple<long, long>(user2.Id.AsLong(), user1.Id.AsLong());
var dbval = _database.Ships.FirstOrDefault(s =>
s.FirstUserId.Equals(userKeys.Item1) &&
s.SecondUserId.Equals(userKeys.Item2));
var shippingRate = 0;
if (dbval == null)
{
shippingRate = _randomNumberGenerator.Next(1, 100);
_database.Ships.Add(new ShipsModel()
{
FirstUserId = userKeys.Item1,
SecondUserId = userKeys.Item2,
Strength = shippingRate
});
await _database.SaveChangesAsync();
}
else
{
shippingRate = dbval.Strength;
}
var reply = $":heartpulse: **{Localization.Ship.Matchmaking}** :heartpulse:\r\n";
reply += $":two_hearts: {user1.Mention} :heart: {user2.Mention} :two_hearts:\r\n";
reply += $"0% [{BlockCounter(shippingRate)}] 100% - {DeterminateSuccess(shippingRate)}";
await ReplyAsync(reply);
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
private string DeterminateSuccess(int rate)
{
return (rate / 20) switch
{
0 => Localization.Ship.NotGoingToHappen,
1 => Localization.Ship.NotSuchAGoodIdea,
2 => Localization.Ship.ThereMightBeAChance,
3 => Localization.Ship.CouldWork,
4 => Localization.Ship.ItsAMatch,
_ => "nope"
};
}
private string BlockCounter(int rate)
{
var amount = rate / 10;
Console.WriteLine(amount);
var blocks = "";
for (var i = 1; i <= 10; i++)
if (i <= amount)
{
blocks += ":white_medium_small_square:";
if (i == amount)
blocks += $" {rate}% ";
}
else
{
blocks += ":black_medium_small_square:";
}
return blocks;
}
}
}

View file

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.Database.Models;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
namespace Geekbot.Bot.Commands.Randomness
{
public class Slap : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly DatabaseContext _database;
public Slap(IErrorHandler errorHandler, DatabaseContext database)
{
_errorHandler = errorHandler;
_database = database;
}
[Command("slap", RunMode = RunMode.Async)]
[Summary("slap someone")]
public async Task Slapper([Summary("@someone")] 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",
"piece of low fat mozzarella",
// For some reason it never picks the last one
// Adding this workaround, because i'm to lazy to actually fix it at the time of writing this
"padding"
};
await ReplyAsync($"{Context.User.Username} slapped {user.Username} with a {things[new Random().Next(0, things.Count - 1)]}");
await UpdateRecieved(user.Id);
await UpdateGiven(Context.User.Id);
await _database.SaveChangesAsync();
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
private async Task UpdateGiven(ulong userId)
{
var user = await GetUser(userId);
user.Given++;
_database.Slaps.Update(user);
}
private async Task UpdateRecieved(ulong userId)
{
var user = await GetUser(userId);
user.Recieved++;
_database.Slaps.Update(user);
}
private async Task<SlapsModel> GetUser(ulong userId)
{
var user = _database.Slaps.FirstOrDefault(e =>
e.GuildId.Equals(Context.Guild.Id.AsLong()) &&
e.UserId.Equals(userId.AsLong())
);
if (user != null) return user;
_database.Slaps.Add(new SlapsModel
{
GuildId = Context.Guild.Id.AsLong(),
UserId = userId.AsLong(),
Recieved = 0,
Given = 0
});
await _database.SaveChangesAsync();
return _database.Slaps.FirstOrDefault(e =>
e.GuildId.Equals(Context.Guild.Id.AsLong()) &&
e.UserId.Equals(userId.AsLong()));
}
}
}

View file

@ -0,0 +1,164 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Bot.CommandPreconditions;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.Database.Models;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.GuildSettingsManager;
using Geekbot.Core.RandomNumberGenerator;
using Localization = Geekbot.Core.Localization;
namespace Geekbot.Bot.Commands.Rpg
{
[DisableInDirectMessage]
[Group("cookies")]
[Alias("cookie")]
public class Cookies : GeekbotCommandBase
{
private readonly DatabaseContext _database;
private readonly IRandomNumberGenerator _randomNumberGenerator;
public Cookies(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator, IGuildSettingsManager guildSettingsManager)
: base(errorHandler, guildSettingsManager)
{
_database = database;
_randomNumberGenerator = randomNumberGenerator;
}
[Command("get", RunMode = RunMode.Async)]
[Summary("Get a cookie every 24 hours")]
public async Task GetCookies()
{
try
{
var actor = await GetUser(Context.User.Id);
var timeoutDays = 1;
if (actor.LastPayout?.AddDays(timeoutDays) > DateTime.Now.ToUniversalTime())
{
var remaining = actor.LastPayout.Value.AddDays(timeoutDays) - DateTimeOffset.Now.ToUniversalTime();
var formattedWaitTime = DateLocalization.FormatDateTimeAsRemaining(remaining);
await ReplyAsync(string.Format(Localization.Cookies.WaitForMoreCookies, formattedWaitTime));
return;
}
actor.Cookies += 10;
actor.LastPayout = DateTimeOffset.Now.ToUniversalTime();
await SetUser(actor);
await ReplyAsync(string.Format(Localization.Cookies.GetCookies, 10, actor.Cookies));
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("jar", RunMode = RunMode.Async)]
[Summary("Look at your cookie jar")]
public async Task PeekIntoCookieJar()
{
try
{
var actor = await GetUser(Context.User.Id);
await ReplyAsync(string.Format(Localization.Cookies.InYourJar, actor.Cookies));
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("give", RunMode = RunMode.Async)]
[Summary("Give cookies to someone")]
public async Task GiveACookie([Summary("@someone")] IUser user, [Summary("amount")] int amount = 1)
{
try
{
var giver = await GetUser(Context.User.Id);
if (amount < 1)
{
await ReplyAsync(Localization.Cookies.CantTakeCookies);
return;
}
if (giver.Cookies < amount)
{
await ReplyAsync(Localization.Cookies.NotEnoughToGive);
return;
}
var taker = await GetUser(user.Id);
giver.Cookies -= amount;
taker.Cookies += amount;
await SetUser(giver);
await SetUser(taker);
await ReplyAsync(string.Format(Localization.Cookies.Given, amount, user.Username));
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
[Command("eat", RunMode = RunMode.Async)]
[Summary("Eat a cookie")]
public async Task EatACookie()
{
try
{
var actor = await GetUser(Context.User.Id);
if (actor.Cookies < 5)
{
await ReplyAsync(Localization.Cookies.NotEnoughCookiesToEat);
return;
}
var amount = _randomNumberGenerator.Next(1, 5);
actor.Cookies -= amount;
await SetUser(actor);
await ReplyAsync(string.Format(Localization.Cookies.AteCookies, amount, actor.Cookies));
}
catch (Exception e)
{
await ErrorHandler.HandleCommandException(e, Context);
}
}
private async Task<CookiesModel> GetUser(ulong userId)
{
var user = _database.Cookies.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId);
return user;
}
private async Task SetUser(CookiesModel user)
{
_database.Cookies.Update(user);
await _database.SaveChangesAsync();
}
private async Task<CookiesModel> CreateNewRow(ulong userId)
{
var user = new CookiesModel()
{
GuildId = Context.Guild.Id.AsLong(),
UserId = userId.AsLong(),
Cookies = 0,
LastPayout = DateTimeOffset.MinValue.ToUniversalTime()
};
var newUser = _database.Cookies.Add(user).Entity;
await _database.SaveChangesAsync();
return newUser;
}
}
}

View file

@ -3,28 +3,32 @@ using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
using Geekbot.Bot.CommandPreconditions;
using Geekbot.Core;
using Geekbot.Core.Database;
using Geekbot.Core.ErrorHandling;
using Geekbot.Core.Extensions;
using Geekbot.Core.Levels;
namespace Geekbot.net.Commands
namespace Geekbot.Bot.Commands.User
{
public class GuildInfo : ModuleBase
public class GuildInfo : TransactionModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly DatabaseContext _database;
private readonly ILevelCalc _levelCalc;
private readonly IDatabase _redis;
public GuildInfo(IDatabase redis, ILevelCalc levelCalc, IErrorHandler errorHandler)
public GuildInfo(DatabaseContext database, ILevelCalc levelCalc, IErrorHandler errorHandler)
{
_redis = redis;
_database = database;
_levelCalc = levelCalc;
_errorHandler = errorHandler;
}
[Command("serverstats", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Statistics)]
[Summary("Show some info about the bot.")]
public async Task getInfo()
[DisableInDirectMessage]
public async Task GetInfo()
{
try
{
@ -37,8 +41,10 @@ namespace Geekbot.net.Commands
var created = Context.Guild.CreatedAt;
var age = Math.Floor((DateTime.Now - created).TotalDays);
var messages = _redis.HashGet($"{Context.Guild.Id}:Messages", 0.ToString());
var level = _levelCalc.GetLevel((int) messages);
var messages = _database.Messages
.Where(e => e.GuildId == Context.Guild.Id.AsLong())
.Sum(e => e.MessageCount);
var level = _levelCalc.GetLevel(messages);
eb.AddField("Server Age", $"{created.Day}/{created.Month}/{created.Year} ({age} days)");
eb.AddInlineField("Level", level)
@ -48,15 +54,8 @@ namespace Geekbot.net.Commands
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
await _errorHandler.HandleCommandException(e, Context);
}
}
public static string FirstCharToUpper(string input)
{
if (string.IsNullOrEmpty(input))
throw new ArgumentException("ARGH!");
return input.First().ToString().ToUpper() + input.Substring(1);
}
}
}

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