diff --git a/.deploy.yml b/.deploy.yml deleted file mode 100644 index 5fb394d..0000000 --- a/.deploy.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -- 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 diff --git a/.gitignore b/.gitignore index 066c4b4..fe7e3d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,16 @@ -/*/**/bin -/*/**/obj -src/Bot/tmp/ -src/Bot/Logs/* -!/src/Bot/Logs/.keep +Geekbot.net/bin +Geekbot.net/obj +Geekbot.net/tmp/ +Tests/bin +Tests/obj +Backup/ .vs/ +UpgradeLog.htm .idea .vscode +Geekbot.net/Logs/* +!/Geekbot.net/Logs/.keep Geekbot.net.sln.DotSettings.user -app +Geekbot.net/temp/ +WikipediaApi/bin/ +WikipediaApi/obj/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a774f5e..8c7a530 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,69 +1,65 @@ stages: - build - - docker - - deploy - ops + - deploy -variables: - VERSION: 4.4.0-V$CI_COMMIT_SHORT_SHA - IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG +before_script: + - set -e + - set -u + - set -o pipefail -Build: +build: stage: build - image: mcr.microsoft.com/dotnet/sdk:6.0 + image: mcr.microsoft.com/dotnet/core/sdk:2.2 artifacts: expire_in: 1h paths: - - app + - Geekbot.net/Binaries/ 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/ + - dotnet restore -s https://api.nuget.org/v3/index.json -s https://www.myget.org/F/discord-net/api/v3/index.json + - dotnet test Tests + - dotnet publish --version-suffix ${CI_COMMIT_SHA:0:8} --configuration Release -o Binaries ./ -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: +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 + dependencies: + - build + script: + - sentry-cli releases new -p geekbot 4.1.0-${CI_COMMIT_SHA:0:8} + - sentry-cli releases set-commits --auto 4.1.0-${CI_COMMIT_SHA:0:8} + - sentry-cli releases deploys 4.1.0-${CI_COMMIT_SHA:0:8} new -e Production -Github Mirror: - stage: ops +deploy: + stage: deploy image: runebaas/rsync-ssh-git only: - master + dependencies: + - build + - sentry + 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/* geekbot@$DEPIP:$DEPPATH + - ssh -p 65432 geekbot@$DEPIP "sudo systemctl restart geekbot.service" + +mirror: + stage: deploy + image: runebaas/rsync-ssh-git + only: + - master + dependencies: + - build + - sentry script: - git push https://runebaas:$TOKEN@github.com/pizzaandcoffee/Geekbot.net.git origin/master:master -f diff --git a/Dockerfile b/Dockerfile index 39529ff..7594bec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM mcr.microsoft.com/dotnet/aspnet:6.0 +FROM microsoft/dotnet:2.1-aspnetcore-runtime -COPY ./app /app/ +COPY Geekbot.net/Binaries /app/ EXPOSE 12995/tcp WORKDIR /app -ENTRYPOINT ./Geekbot +ENTRYPOINT ./run.sh diff --git a/Geekbot.net.sln b/Geekbot.net.sln index f33d887..b542f25 100644 --- a/Geekbot.net.sln +++ b/Geekbot.net.sln @@ -3,19 +3,11 @@ 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}") = "Tests", "tests\Tests.csproj", "{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geekbot.net", "Geekbot.net/Geekbot.net.csproj", "{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{47671723-52A9-4668-BBC5-2BA76AE3B288}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}" 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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WikipediaApi", "WikipediaApi\WikipediaApi.csproj", "{1084D499-EF94-4834-9E6A-B2AD81B60078}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,34 +15,18 @@ 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 + {1084D499-EF94-4834-9E6A-B2AD81B60078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1084D499-EF94-4834-9E6A-B2AD81B60078}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1084D499-EF94-4834-9E6A-B2AD81B60078}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1084D499-EF94-4834-9E6A-B2AD81B60078}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Geekbot.net/Commands/Admin/Admin.cs b/Geekbot.net/Commands/Admin/Admin.cs new file mode 100644 index 0000000..6377c60 --- /dev/null +++ b/Geekbot.net/Commands/Admin/Admin.cs @@ -0,0 +1,268 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Localization; + +namespace Geekbot.net.Commands.Admin +{ + [Group("admin")] + [RequireUserPermission(GuildPermission.Administrator)] + [DisableInDirectMessage] + public class Admin : GeekbotBase + { + private readonly DiscordSocketClient _client; + private readonly IErrorHandler _errorHandler; + private readonly DatabaseContext _database; + private readonly ITranslationHandler _translation; + + public Admin(DatabaseContext database, DiscordSocketClient client, IErrorHandler errorHandler, + ITranslationHandler translationHandler) + { + _database = database; + _client = client; + _errorHandler = errorHandler; + _translation = translationHandler; + } + + [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) + { + var guild = await GetGuildSettings(Context.Guild.Id); + guild.WelcomeMessage = welcomeMessage; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + + 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("..."); + + var guild = await GetGuildSettings(Context.Guild.Id); + guild.WelcomeChannel = channel.Id.AsLong(); + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + + 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..."); + + var guild = await GetGuildSettings(Context.Guild.Id); + guild.ModChannel = channel.Id.AsLong(); + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + + 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 guild = await GetGuildSettings(Context.Guild.Id); + var modChannel = await GetModChannel(guild.ModChannel.AsUlong()); + if (modChannel == null) return; + + guild.ShowLeave = !guild.ShowLeave; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + await modChannel.SendMessageAsync(guild.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 guild = await GetGuildSettings(Context.Guild.Id); + var modChannel = await GetModChannel(guild.ModChannel.AsUlong()); + if (modChannel == null) return; + + guild.ShowDelete = !guild.ShowDelete; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + await modChannel.SendMessageAsync(guild.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 languageRaw) + { + try + { + var language = languageRaw.ToUpper(); + var success = await _translation.SetLanguage(Context.Guild.Id, language); + if (success) + { + var guild = await GetGuildSettings(Context.Guild.Id); + guild.Language = language; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + + var transContext = await _translation.GetGuildContext(Context); + await ReplyAsync(transContext.GetString("NewLanguageSet")); + return; + } + + await ReplyAsync( + $"That doesn't seem to be a supported language\r\nSupported Languages are {string.Join(", ", _translation.SupportedLanguages)}"); + } + 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(); + var guild = await GetGuildSettings(Context.Guild.Id); + guild.WikiLang = language; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + + 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 = await GetGuildSettings(Context.Guild.Id); + guild.Ping = !guild.Ping; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + await ReplyAsync(guild.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 = await GetGuildSettings(Context.Guild.Id); + guild.Hui = !guild.Hui; + _database.GuildSettings.Update(guild); + await _database.SaveChangesAsync(); + await ReplyAsync(guild.Hui ? "i will reply to hui now" : "No more hui's..."); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + + private async Task GetGuildSettings(ulong guildId) + { + var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildId.AsLong())); + if (guild != null) return guild; + Console.WriteLine("Adding non-exist Guild Settings to database"); + _database.GuildSettings.Add(new GuildSettingsModel() + { + GuildId = guildId.AsLong(), + Hui = false, + Ping = false, + Language = "EN", + ShowDelete = false, + ShowLeave = false, + WikiLang = "en" + }); + await _database.SaveChangesAsync(); + return _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildId.AsLong())); + } + + private async Task 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; + } + } + + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Admin/Mod.cs b/Geekbot.net/Commands/Admin/Mod.cs new file mode 100644 index 0000000..a2a2f4e --- /dev/null +++ b/Geekbot.net/Commands/Admin/Mod.cs @@ -0,0 +1,58 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.UserRepository; + +namespace Geekbot.net.Commands.Admin +{ + [Group("mod")] + [RequireUserPermission(GuildPermission.KickMembers)] + [RequireUserPermission(GuildPermission.ManageMessages)] + [RequireUserPermission(GuildPermission.ManageRoles)] + [DisableInDirectMessage] + public class Mod : GeekbotBase + { + private readonly DiscordSocketClient _client; + private readonly IErrorHandler _errorHandler; + private readonly IUserRepository _userRepository; + + public Mod(IUserRepository userRepositry, IErrorHandler errorHandler, DiscordSocketClient client) + { + _userRepository = userRepositry; + _errorHandler = errorHandler; + _client = client; + } + + [Command("namehistory", RunMode = RunMode.Async)] + [Summary("See past usernames of an user")] + public async Task UsernameHistory([Summary("@someone")] IUser user) + { + try + { + var userRepo = _userRepository.Get(user.Id); + if (userRepo != null && userRepo.UsedNames != null) + { + var sb = new StringBuilder(); + sb.AppendLine($":bust_in_silhouette: {user.Username} has been known as:"); + foreach (var name in userRepo.UsedNames) sb.AppendLine($"- `{name.Name}`"); + await ReplyAsync(sb.ToString()); + } + else + { + await ReplyAsync($"No name changes found for {user.Username}"); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context, + $"I don't have enough permissions do that"); + } + } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Admin/Owner/Owner.cs b/Geekbot.net/Commands/Admin/Owner/Owner.cs similarity index 83% rename from src/Bot/Commands/Admin/Owner/Owner.cs rename to Geekbot.net/Commands/Admin/Owner/Owner.cs index 64292e9..e395a44 100644 --- a/src/Bot/Commands/Admin/Owner/Owner.cs +++ b/Geekbot.net/Commands/Admin/Owner/Owner.cs @@ -3,30 +3,30 @@ 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; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.GlobalSettings; +using Geekbot.net.Lib.Logger; +using Geekbot.net.Lib.UserRepository; -namespace Geekbot.Bot.Commands.Admin.Owner +namespace Geekbot.net.Commands.Admin.Owner { [Group("owner")] [RequireOwner] - public class Owner : GeekbotCommandBase + public class Owner : GeekbotBase { private readonly DiscordSocketClient _client; + private readonly IErrorHandler _errorHandler; 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) + public Owner(DiscordSocketClient client, IGeekbotLogger logger, IUserRepository userRepositry, IErrorHandler errorHandler, IGlobalSettings globalSettings) { _client = client; _logger = logger; _userRepository = userRepositry; + _errorHandler = errorHandler; _globalSettings = globalSettings; } @@ -74,7 +74,7 @@ namespace Geekbot.Bot.Commands.Admin.Owner } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context, + await _errorHandler.HandleCommandException(e, Context, "Couldn't complete User Repository, see console for more info"); } } @@ -90,10 +90,10 @@ namespace Geekbot.Bot.Commands.Admin.Owner } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + 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) @@ -106,7 +106,7 @@ namespace Geekbot.Bot.Commands.Admin.Owner } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -120,7 +120,7 @@ namespace Geekbot.Bot.Commands.Admin.Owner } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } } diff --git a/src/Bot/Commands/Admin/Role.cs b/Geekbot.net/Commands/Admin/Role.cs similarity index 67% rename from src/Bot/Commands/Admin/Role.cs rename to Geekbot.net/Commands/Admin/Role.cs index fb8ef68..22b4249 100644 --- a/src/Bot/Commands/Admin/Role.cs +++ b/Geekbot.net/Commands/Admin/Role.cs @@ -1,33 +1,32 @@ 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; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.ReactionListener; -namespace Geekbot.Bot.Commands.Admin +namespace Geekbot.net.Commands.Admin { [Group("role")] [DisableInDirectMessage] - public class Role : GeekbotCommandBase + public class Role : GeekbotBase { private readonly DatabaseContext _database; + private readonly IErrorHandler _errorHandler; private readonly IReactionListener _reactionListener; - public Role(DatabaseContext database, IErrorHandler errorHandler, IReactionListener reactionListener, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager) + public Role(DatabaseContext database, IErrorHandler errorHandler, IReactionListener reactionListener) { _database = database; + _errorHandler = errorHandler; _reactionListener = reactionListener; } @@ -40,19 +39,19 @@ namespace Geekbot.Bot.Commands.Admin var roles = _database.RoleSelfService.Where(g => g.GuildId.Equals(Context.Guild.Id.AsLong())).ToList(); if (roles.Count == 0) { - await ReplyAsync(Localization.Role.NoRolesConfigured); + await ReplyAsync(Context.Translations.GetString("NoRolesConfigured")); return; } var sb = new StringBuilder(); - sb.AppendLine(string.Format(Localization.Role.ListHeader, Context.Guild.Name)); - sb.AppendLine(Localization.Role.ListInstruction); + sb.AppendLine(Context.Translations.GetString("ListHeader", Context.Guild.Name)); + sb.AppendLine(Context.Translations.GetString("ListInstruction")); foreach (var role in roles) sb.AppendLine($"- {role.WhiteListName}"); await ReplyAsync(sb.ToString()); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -71,38 +70,31 @@ namespace Geekbot.Bot.Commands.Admin var role = Context.Guild.Roles.First(r => r.Id == roleFromDb.RoleId.AsUlong()); if (role == null) { - await ReplyAsync(Localization.Role.RoleNotFound); + await ReplyAsync(Context.Translations.GetString("RoleNotFound")); return; } if (guildUser.RoleIds.Contains(role.Id)) { await guildUser.RemoveRoleAsync(role); - await ReplyAsync(string.Format(Localization.Role.RemovedUserFromRole, role.Name)); + await ReplyAsync(Context.Translations.GetString("RemovedUserFromRole", role.Name)); return; } await guildUser.AddRoleAsync(role); - await ReplyAsync(string.Format(Localization.Role.AddedUserFromRole, role.Name)); + await ReplyAsync(Context.Translations.GetString("AddedUserFromRole", role.Name)); return; } - await ReplyAsync(Localization.Role.RoleNotFound); + await ReplyAsync(Context.Translations.GetString("RoleNotFound")); } catch (HttpException e) { - if (e.HttpCode == HttpStatusCode.Forbidden) - { - await ReplyAsync(Localization.Internal.Http403); - } - else - { - await ErrorHandler.HandleCommandException(e, Context); - } + await _errorHandler.HandleHttpException(e, Context); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -115,7 +107,7 @@ namespace Geekbot.Bot.Commands.Admin { if (role.IsManaged) { - await ReplyAsync(Localization.Role.CannotAddManagedRole); + await ReplyAsync(Context.Translations.GetString("CannotAddManagedRole")); return; } @@ -125,7 +117,7 @@ namespace Geekbot.Bot.Commands.Admin || role.Permissions.BanMembers || role.Permissions.KickMembers) { - await ReplyAsync(Localization.Role.CannotAddDangerousRole); + await ReplyAsync(Context.Translations.GetString("CannotAddDangerousRole")); return; } @@ -136,11 +128,11 @@ namespace Geekbot.Bot.Commands.Admin WhiteListName = roleName }); await _database.SaveChangesAsync(); - await ReplyAsync(string.Format(Localization.Role.AddedRoleToWhitelist, role.Name)); + await ReplyAsync(Context.Translations.GetString("CannotAddDangerousRole", role.Name)); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -157,40 +149,49 @@ namespace Geekbot.Bot.Commands.Admin { _database.RoleSelfService.Remove(roleFromDb); await _database.SaveChangesAsync(); - await ReplyAsync(string.Format(Localization.Role.RemovedRoleFromWhitelist, roleName)); + await ReplyAsync(Context.Translations.GetString("RemovedRoleFromWhitelist", roleName)); return; } - await ReplyAsync(Localization.Role.RoleNotFound); + await ReplyAsync(Context.Translations.GetString("RoleNotFound")); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + 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) + public async Task AddListener([Summary("message-ID")] string messageId, [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); - + var message = (IUserMessage) await Context.Channel.GetMessageAsync(ulong.Parse(messageId)); + IEmote emote; + if (!emoji.StartsWith('<')) + { + var emo = new Emoji(emoji); + emote = emo; + } + else + { + emote = Emote.Parse(emoji); + } await message.AddReactionAsync(emote); - await _reactionListener.AddRoleToListener(messageId, Context.Guild.Id, emoji, role); + await _reactionListener.AddRoleToListener(messageId, emote, role); await Context.Message.DeleteAsync(); } - catch (HttpException) + catch (HttpException e) { await Context.Channel.SendMessageAsync("Custom emojis from other servers are not supported"); + Console.WriteLine(e); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await Context.Channel.SendMessageAsync("Something went wrong... please try again on a new message"); + Console.WriteLine(e); } } } diff --git a/src/Bot/Commands/Games/Pokedex.cs b/Geekbot.net/Commands/Games/Pokedex.cs similarity index 91% rename from src/Bot/Commands/Games/Pokedex.cs rename to Geekbot.net/Commands/Games/Pokedex.cs index 325ee8c..32fd8eb 100644 --- a/src/Bot/Commands/Games/Pokedex.cs +++ b/Geekbot.net/Commands/Games/Pokedex.cs @@ -3,14 +3,14 @@ using System.Linq; using System.Threading.Tasks; using Discord; using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.Extensions; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; using PokeAPI; -namespace Geekbot.Bot.Commands.Games +namespace Geekbot.net.Commands.Games { - public class Pokedex : TransactionModuleBase + public class Pokedex : GeekbotBase { private readonly IErrorHandler _errorHandler; diff --git a/Geekbot.net/Commands/Games/Roll.cs b/Geekbot.net/Commands/Games/Roll.cs new file mode 100644 index 0000000..965f9c1 --- /dev/null +++ b/Geekbot.net/Commands/Games/Roll.cs @@ -0,0 +1,96 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.AlmostRedis; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.RandomNumberGenerator; +using StackExchange.Redis; + +namespace Geekbot.net.Commands.Games +{ + public class Roll : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + private readonly IAlmostRedis _redis; + private readonly DatabaseContext _database; + private readonly IRandomNumberGenerator _randomNumberGenerator; + + public Roll(IAlmostRedis redis, IErrorHandler errorHandler, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator) + { + _redis = redis; + _database = database; + _randomNumberGenerator = randomNumberGenerator; + _errorHandler = errorHandler; + } + + [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 number = _randomNumberGenerator.Next(1, 100); + var guess = 1000; + int.TryParse(stuff, out guess); + if (guess <= 100 && guess > 0) + { + var prevRoll = _redis.Db.HashGet($"{Context.Guild.Id}:RollsPrevious2", Context.Message.Author.Id).ToString()?.Split('|'); + if (prevRoll?.Length == 2) + { + if (prevRoll[0] == guess.ToString() && DateTime.Parse(prevRoll[1]) > DateTime.Now.AddDays(-1)) + { + await ReplyAsync(Context.Translations.GetString("NoPrevGuess", Context.Message.Author.Mention)); + return; + } + } + + _redis.Db.HashSet($"{Context.Guild.Id}:RollsPrevious2", new[] {new HashEntry(Context.Message.Author.Id, $"{guess}|{DateTime.Now}")}); + + await ReplyAsync(Context.Translations.GetString("Rolled", Context.Message.Author.Mention, number, guess)); + if (guess == number) + { + await ReplyAsync(Context.Translations.GetString("Gratz", Context.Message.Author)); + _redis.Db.HashIncrement($"{Context.Guild.Id}:Rolls", Context.User.Id.ToString()); + var user = await GetUser(Context.User.Id); + user.Rolls += 1; + _database.Rolls.Update(user); + await _database.SaveChangesAsync(); + } + } + else + { + await ReplyAsync(Context.Translations.GetString("RolledNoGuess", Context.Message.Author.Mention, number)); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + + private async Task GetUser(ulong userId) + { + var user = _database.Rolls.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId); + return user; + } + + private async Task CreateNewRow(ulong userId) + { + var user = new RollsModel() + { + GuildId = Context.Guild.Id.AsLong(), + UserId = userId.AsLong(), + Rolls = 0 + }; + var newUser = _database.Rolls.Add(user).Entity; + await _database.SaveChangesAsync(); + return newUser; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Google/Google.cs b/Geekbot.net/Commands/Integrations/Google/Google.cs new file mode 100644 index 0000000..76c3795 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Google/Google.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.GlobalSettings; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Integrations.Google +{ + public class Google : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + private readonly IGlobalSettings _globalSettings; + + public Google(IErrorHandler errorHandler, IGlobalSettings globalSettings) + { + _errorHandler = errorHandler; + _globalSettings = globalSettings; + } + + [Command("google", RunMode = RunMode.Async)] + [Summary("Google Something.")] + public async Task AskGoogle([Remainder, Summary("search-text")] string searchText) + { + try + { + using (var client = new WebClient()) + { + var apiKey = _globalSettings.GetKey("GoogleGraphKey"); + if (string.IsNullOrEmpty(apiKey)) + { + await ReplyAsync("No Google API key has been set, please contact my owner"); + return; + } + + var url = new Uri($"https://kgsearch.googleapis.com/v1/entities:search?languages=en&limit=1&query={searchText}&key={apiKey}"); + var responseString = client.DownloadString(url); + var response = JsonConvert.DeserializeObject(responseString); + + if (!response.ItemListElement.Any()) + { + await ReplyAsync("No results were found..."); + return; + } + + var data = response.ItemListElement.First().Result; + var eb = new EmbedBuilder + { + Title = data.Name + }; + if(!string.IsNullOrEmpty(data.Description)) eb.WithDescription(data.Description); + if(!string.IsNullOrEmpty(data.DetailedDtoDescription?.Url)) eb.WithUrl(data.DetailedDtoDescription.Url); + if(!string.IsNullOrEmpty(data.DetailedDtoDescription?.ArticleBody)) eb.AddField("Details", data.DetailedDtoDescription.ArticleBody); + if(!string.IsNullOrEmpty(data.Image?.ContentUrl)) eb.WithThumbnailUrl(data.Image.ContentUrl); + + await ReplyAsync("", false, eb.Build()); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Google/GoogleKgApiDetailedDto.cs b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiDetailedDto.cs new file mode 100644 index 0000000..031d1e7 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiDetailedDto.cs @@ -0,0 +1,9 @@ +namespace Geekbot.net.Commands.Integrations.Google +{ + public class GoogleKgApiDetailedDto + { + public string ArticleBody { get; set; } + public string Url { get; set; } + public string License { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Google/GoogleKgApiElementDto.cs b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiElementDto.cs new file mode 100644 index 0000000..a48b184 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiElementDto.cs @@ -0,0 +1,8 @@ +namespace Geekbot.net.Commands.Integrations.Google +{ + public class GoogleKgApiElementDto + { + public GoogleKgApiResultDto Result { get; set; } + public double ResultScore { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Google/GoogleKgApiImageDto.cs b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiImageDto.cs new file mode 100644 index 0000000..fe7cdaa --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiImageDto.cs @@ -0,0 +1,8 @@ +namespace Geekbot.net.Commands.Integrations.Google +{ + public class GoogleKgApiImageDto + { + public string ContentUrl { get; set; } + public string Url { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Google/GoogleKgApiResponseDto.cs b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiResponseDto.cs new file mode 100644 index 0000000..af337db --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiResponseDto.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Geekbot.net.Commands.Integrations.Google +{ + public class GoogleKgApiResponseDto + { + public List ItemListElement { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Google/GoogleKgApiResultDto.cs b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiResultDto.cs new file mode 100644 index 0000000..465f1d7 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Google/GoogleKgApiResultDto.cs @@ -0,0 +1,10 @@ +namespace Geekbot.net.Commands.Integrations.Google +{ + public class GoogleKgApiResultDto + { + public string Name { get; set; } + public string Description { get; set; } + public GoogleKgApiImageDto Image { get; set; } + public GoogleKgApiDetailedDto DetailedDtoDescription { get; set; } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/MagicTheGathering.cs b/Geekbot.net/Commands/Integrations/MagicTheGathering.cs similarity index 60% rename from src/Bot/Commands/Integrations/MagicTheGathering.cs rename to Geekbot.net/Commands/Integrations/MagicTheGathering.cs index 359b41e..a9d2251 100644 --- a/src/Bot/Commands/Integrations/MagicTheGathering.cs +++ b/Geekbot.net/Commands/Integrations/MagicTheGathering.cs @@ -4,15 +4,15 @@ 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 Geekbot.net.Lib; +using Geekbot.net.Lib.Converters; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; using MtgApiManager.Lib.Service; -namespace Geekbot.Bot.Commands.Integrations +namespace Geekbot.net.Commands.Integrations { - public class MagicTheGathering : TransactionModuleBase + public class MagicTheGathering : GeekbotBase { private readonly IErrorHandler _errorHandler; private readonly IMtgManaConverter _manaConverter; @@ -29,34 +29,22 @@ namespace Geekbot.Bot.Commands.Integrations { 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(); - + var card = result.All().Value.FirstOrDefault(); if (card == null) { - await message.ModifyAsync(properties => properties.Content = ":red_circle: I couldn't find a card with that name..."); + await ReplyAsync("I couldn't find that card..."); return; } - var eb = new EmbedBuilder - { - Title = card.Name, - Description = card.Type - }; + var eb = new EmbedBuilder(); + eb.Title = card.Name; + eb.Description = card.Type; if (card.Colors != null) eb.WithColor(GetColor(card.Colors)); @@ -76,11 +64,7 @@ namespace Geekbot.Bot.Commands.Integrations 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(); - }); + await ReplyAsync("", false, eb.Build()); } catch (Exception e) { @@ -91,15 +75,21 @@ namespace Geekbot.Bot.Commands.Integrations private Color GetColor(IEnumerable colors) { var color = colors.FirstOrDefault(); - return color switch + switch (color) { - "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) - }; + case "Black": + return new Color(203, 194, 191); + case "White": + return new Color(255, 251, 213); + case "Blue": + return new Color(170, 224, 250); + case "Red": + return new Color(250, 170, 143); + case "Green": + return new Color(155, 211, 174); + default: + return new Color(204, 194, 212); + } } } } \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/Mal.cs b/Geekbot.net/Commands/Integrations/Mal.cs new file mode 100644 index 0000000..ace49f4 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Mal.cs @@ -0,0 +1,129 @@ +using System; +using System.Threading.Tasks; +using System.Web; +using System.Xml; +using Discord; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Clients; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; + +namespace Geekbot.net.Commands.Integrations +{ + public class Mal : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + private readonly IMalClient _malClient; + + public Mal(IMalClient malClient, IErrorHandler errorHandler) + { + _malClient = malClient; + _errorHandler = errorHandler; + } + + [Command("anime", RunMode = RunMode.Async)] + [Summary("Show Info about an Anime.")] + public async Task SearchAnime([Remainder] [Summary("anime-name")] 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("
", "") + .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 (XmlException e) + { + await _errorHandler.HandleCommandException(e, Context, "The MyAnimeList.net API refused to answer"); + } + 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 + { + if (_malClient.IsLoggedIn()) + { + var manga = await _malClient.GetManga(mangaName); + if (manga != null) + { + var eb = new EmbedBuilder(); + + var description = HttpUtility.HtmlDecode(manga.Synopsis) + .Replace("
", "") + .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 (XmlException e) + { + await _errorHandler.HandleCommandException(e, Context, "The MyAnimeList.net API refused to answer"); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictListItemDto.cs b/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictListItemDto.cs new file mode 100644 index 0000000..e98885b --- /dev/null +++ b/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictListItemDto.cs @@ -0,0 +1,12 @@ +namespace Geekbot.net.Commands.Integrations.UbranDictionary +{ + internal class UrbanListItemDto + { + public string Definition { get; set; } + public string Permalink { get; set; } + public string ThumbsUp { get; set; } + public string Word { get; set; } + public string Example { get; set; } + public string ThumbsDown { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictResponseDto.cs b/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictResponseDto.cs new file mode 100644 index 0000000..2c3e014 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictResponseDto.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Geekbot.net.Commands.Integrations.UbranDictionary +{ + internal class UrbanResponseDto + { + public string[] Tags { get; set; } + public List List { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictionary.cs b/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictionary.cs new file mode 100644 index 0000000..420d2b4 --- /dev/null +++ b/Geekbot.net/Commands/Integrations/UbranDictionary/UrbanDictionary.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Integrations.UbranDictionary +{ + public class UrbanDictionary : GeekbotBase + { + 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 + { + 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(stringResponse); + if (definitions.List.Count == 0) + { + await ReplyAsync("That word hasn't been defined..."); + return; + } + + var definition = definitions.List.First(e => !string.IsNullOrWhiteSpace(e.Example)); + + var eb = new EmbedBuilder(); + eb.WithAuthor(new EmbedAuthorBuilder + { + Name = definition.Word, + Url = definition.Permalink + }); + eb.WithColor(new Color(239, 255, 0)); + if (!string.IsNullOrEmpty(definition.Definition)) eb.Description = definition.Definition; + if (!string.IsNullOrEmpty(definition.Example)) eb.AddField("Example", definition.Example ?? "(no example given...)"); + if (!string.IsNullOrEmpty(definition.ThumbsUp)) eb.AddInlineField("Upvotes", definition.ThumbsUp); + if (!string.IsNullOrEmpty(definition.ThumbsDown)) eb.AddInlineField("Downvotes", definition.ThumbsDown); + if (definitions.Tags?.Length > 0) eb.AddField("Tags", string.Join(", ", definitions.Tags)); + + await ReplyAsync("", false, eb.Build()); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/Wikipedia.cs b/Geekbot.net/Commands/Integrations/Wikipedia.cs similarity index 91% rename from src/Bot/Commands/Integrations/Wikipedia.cs rename to Geekbot.net/Commands/Integrations/Wikipedia.cs index 82f42a0..1bc2390 100644 --- a/src/Bot/Commands/Integrations/Wikipedia.cs +++ b/Geekbot.net/Commands/Integrations/Wikipedia.cs @@ -5,17 +5,17 @@ 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 Geekbot.net.Database; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; using HtmlAgilityPack; +using WikipediaApi; +using WikipediaApi.Page; -namespace Geekbot.Bot.Commands.Integrations +namespace Geekbot.net.Commands.Integrations { - public class Wikipedia : TransactionModuleBase + public class Wikipedia : GeekbotBase { private readonly IErrorHandler _errorHandler; private readonly IWikipediaClient _wikipediaClient; diff --git a/Geekbot.net/Commands/Integrations/Youtube.cs b/Geekbot.net/Commands/Integrations/Youtube.cs new file mode 100644 index 0000000..26f0e9c --- /dev/null +++ b/Geekbot.net/Commands/Integrations/Youtube.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.GlobalSettings; +using Google.Apis.Services; +using Google.Apis.YouTube.v3; + +namespace Geekbot.net.Commands.Integrations +{ + public class Youtube : GeekbotBase + { + 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) + { + 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); + } + } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/BenedictCumberbatchNameGenerator.cs b/Geekbot.net/Commands/Randomness/BenedictCumberbatchNameGenerator.cs similarity index 92% rename from src/Bot/Commands/Randomness/BenedictCumberbatchNameGenerator.cs rename to Geekbot.net/Commands/Randomness/BenedictCumberbatchNameGenerator.cs index 23187bd..fa734fd 100644 --- a/src/Bot/Commands/Randomness/BenedictCumberbatchNameGenerator.cs +++ b/Geekbot.net/Commands/Randomness/BenedictCumberbatchNameGenerator.cs @@ -2,13 +2,13 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.RandomNumberGenerator; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.RandomNumberGenerator; -namespace Geekbot.Bot.Commands.Randomness +namespace Geekbot.net.Commands.Randomness { - public class BenedictCumberbatchNameGenerator : TransactionModuleBase + public class BenedictCumberbatchNameGenerator : GeekbotBase { private readonly IErrorHandler _errorHandler; private readonly IRandomNumberGenerator _randomNumberGenerator; @@ -19,7 +19,7 @@ namespace Geekbot.Bot.Commands.Randomness _randomNumberGenerator = randomNumberGenerator; } - [Command("bdcb", RunMode = RunMode.Async)] + [Command("bdcc", RunMode = RunMode.Async)] [Summary("Benedict Cumberbatch Name Generator")] public async Task GetQuote() { @@ -60,4 +60,4 @@ namespace Geekbot.Bot.Commands.Randomness } } } -} +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Cat/Cat.cs b/Geekbot.net/Commands/Randomness/Cat/Cat.cs new file mode 100644 index 0000000..b752955 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Cat/Cat.cs @@ -0,0 +1,53 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Randomness.Cat +{ + public class Cat : GeekbotBase + { + 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 + { + using (var client = new HttpClient()) + { + try + { + client.BaseAddress = new Uri("https://aws.random.cat"); + var response = await client.GetAsync("/meow"); + response.EnsureSuccessStatusCode(); + + var stringResponse = await response.Content.ReadAsStringAsync(); + var catFile = JsonConvert.DeserializeObject(stringResponse); + var eb = new EmbedBuilder(); + eb.ImageUrl = catFile.File; + await ReplyAsync("", false, eb.Build()); + } + catch + { + await ReplyAsync("Seems like the dog cought the cat (error occured)"); + } + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Cat/CatResponseDto.cs b/Geekbot.net/Commands/Randomness/Cat/CatResponseDto.cs new file mode 100644 index 0000000..05ebf2b --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Cat/CatResponseDto.cs @@ -0,0 +1,7 @@ +namespace Geekbot.net.Commands.Randomness.Cat +{ + internal class CatResponseDto + { + public string File { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/CheckEm.cs b/Geekbot.net/Commands/Randomness/CheckEm.cs new file mode 100644 index 0000000..790dce8 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/CheckEm.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Media; + +namespace Geekbot.net.Commands.Randomness +{ + public class CheckEm : GeekbotBase + { + private readonly IMediaProvider _checkEmImages; + private readonly IErrorHandler _errorHandler; + + public CheckEm(IMediaProvider mediaProvider, IErrorHandler errorHandler) + { + _checkEmImages = mediaProvider; + _errorHandler = errorHandler; + } + + [Command("checkem", RunMode = RunMode.Async)] + [Summary("Check for dubs")] + public async Task MuhDubs() + { + try + { + var number = new Random().Next(10000000, 99999999); + var dubtriqua = ""; + + var ns = GetIntArray(number); + if (ns[7] == ns[6]) + { + dubtriqua = "DUBS"; + if (ns[6] == ns[5]) + { + dubtriqua = "TRIPS"; + if (ns[5] == ns[4]) + dubtriqua = "QUADS"; + } + } + + var sb = new StringBuilder(); + sb.AppendLine($"Check em {Context.User.Mention}"); + sb.AppendLine($"**{number}**"); + if (!string.IsNullOrEmpty(dubtriqua)) + sb.AppendLine($":tada: {dubtriqua} :tada:"); + sb.AppendLine(_checkEmImages.GetCheckem()); + + await ReplyAsync(sb.ToString()); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + + private int[] GetIntArray(int num) + { + var listOfInts = new List(); + while (num > 0) + { + listOfInts.Add(num % 10); + num = num / 10; + } + + listOfInts.Reverse(); + return listOfInts.ToArray(); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Chuck/ChuckNorrisJokeResponseDto.cs b/Geekbot.net/Commands/Randomness/Chuck/ChuckNorrisJokeResponseDto.cs new file mode 100644 index 0000000..8d513b8 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Chuck/ChuckNorrisJokeResponseDto.cs @@ -0,0 +1,7 @@ +namespace Geekbot.net.Commands.Randomness.Chuck +{ + internal class ChuckNorrisJokeResponseDto + { + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Chuck/ChuckNorrisJokes.cs b/Geekbot.net/Commands/Randomness/Chuck/ChuckNorrisJokes.cs new file mode 100644 index 0000000..ceb559c --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Chuck/ChuckNorrisJokes.cs @@ -0,0 +1,52 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Randomness.Chuck +{ + public class ChuckNorrisJokes : GeekbotBase + { + 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 + { + using (var client = new HttpClient()) + { + try + { + client.DefaultRequestHeaders.Accept.Clear(); + client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + var response = await client.GetAsync("https://api.chucknorris.io/jokes/random"); + response.EnsureSuccessStatusCode(); + + var stringResponse = await response.Content.ReadAsStringAsync(); + var data = JsonConvert.DeserializeObject(stringResponse); + await ReplyAsync(data.Value); + } + catch (HttpRequestException) + { + await ReplyAsync("Api down..."); + } + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Dad/DadJokeResponseDto.cs b/Geekbot.net/Commands/Randomness/Dad/DadJokeResponseDto.cs new file mode 100644 index 0000000..262eee9 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Dad/DadJokeResponseDto.cs @@ -0,0 +1,7 @@ +namespace Geekbot.net.Commands.Randomness.Dad +{ + internal class DadJokeResponseDto + { + public string Joke { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Dad/DadJokes.cs b/Geekbot.net/Commands/Randomness/Dad/DadJokes.cs new file mode 100644 index 0000000..4bb204b --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Dad/DadJokes.cs @@ -0,0 +1,52 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Randomness.Dad +{ + public class DadJokes : GeekbotBase + { + 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 + { + using (var client = new HttpClient()) + { + try + { + client.DefaultRequestHeaders.Accept.Clear(); + client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + var response = await client.GetAsync("https://icanhazdadjoke.com/"); + response.EnsureSuccessStatusCode(); + + var stringResponse = await response.Content.ReadAsStringAsync(); + var data = JsonConvert.DeserializeObject(stringResponse); + await ReplyAsync(data.Joke); + } + catch (HttpRequestException) + { + await ReplyAsync("Api down..."); + } + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Dog/Dog.cs b/Geekbot.net/Commands/Randomness/Dog/Dog.cs new file mode 100644 index 0000000..6ea0d25 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Dog/Dog.cs @@ -0,0 +1,53 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Randomness.Dog +{ + public class Dog : GeekbotBase + { + 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 + { + 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(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) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Dog/DogResponseDto.cs b/Geekbot.net/Commands/Randomness/Dog/DogResponseDto.cs new file mode 100644 index 0000000..1fc1a82 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Dog/DogResponseDto.cs @@ -0,0 +1,7 @@ +namespace Geekbot.net.Commands.Randomness.Dog +{ + internal class DogResponseDto + { + public string Url { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/EightBall.cs b/Geekbot.net/Commands/Randomness/EightBall.cs new file mode 100644 index 0000000..e72f85e --- /dev/null +++ b/Geekbot.net/Commands/Randomness/EightBall.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; + +namespace Geekbot.net.Commands.Randomness +{ + public class EightBall : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + + public EightBall(IErrorHandler errorHandler) + { + _errorHandler = errorHandler; + } + + [Command("8ball", RunMode = RunMode.Async)] + [Summary("Ask 8Ball a Question.")] + public async Task Ball([Remainder] [Summary("question")] string echo) + { + try + { + var replies = new List + { + "It is certain", + "It is decidedly so", + "Without a doubt", + "Yes, definitely", + "You may rely on it", + "As I see it, yes", + "Most likely", + "Outlook good", + "Yes", + "Signs point to yes", + "Reply hazy try again", + "Ask again later", + "Better not tell you now", + "Cannot predict now", + "Concentrate and ask again", + "Don't count on it", + "My reply is no", + "My sources say no", + "Outlook not so good", + "Very doubtful" + }; + + var answer = new Random().Next(replies.Count); + await ReplyAsync(replies[answer]); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Fortune.cs b/Geekbot.net/Commands/Randomness/Fortune.cs similarity index 73% rename from src/Bot/Commands/Randomness/Fortune.cs rename to Geekbot.net/Commands/Randomness/Fortune.cs index 1157603..582cc75 100644 --- a/src/Bot/Commands/Randomness/Fortune.cs +++ b/Geekbot.net/Commands/Randomness/Fortune.cs @@ -1,11 +1,11 @@ using System.Threading.Tasks; using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.Media; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Media; -namespace Geekbot.Bot.Commands.Randomness +namespace Geekbot.net.Commands.Randomness { - public class Fortune : TransactionModuleBase + public class Fortune : GeekbotBase { private readonly IFortunesProvider _fortunes; diff --git a/Geekbot.net/Commands/Randomness/Gdq.cs b/Geekbot.net/Commands/Randomness/Gdq.cs new file mode 100644 index 0000000..3bed944 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Gdq.cs @@ -0,0 +1,39 @@ +using System; +using System.Net; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; + +namespace Geekbot.net.Commands.Randomness +{ + public class Gdq : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + + public Gdq(IErrorHandler errorHandler) + { + _errorHandler = errorHandler; + } + + [Command("gdq", RunMode = RunMode.Async)] + [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) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Kanye/Kanye.cs b/Geekbot.net/Commands/Randomness/Kanye/Kanye.cs new file mode 100644 index 0000000..a37fbc3 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Kanye/Kanye.cs @@ -0,0 +1,54 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Commands.Randomness.Dad; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Microsoft.AspNetCore.Hosting.Internal; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Randomness.Kanye +{ + public class Kanye : GeekbotBase + { + 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 + { + using (var client = new HttpClient()) + { + try + { + client.DefaultRequestHeaders.Accept.Clear(); + client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + var response = await client.GetAsync("https://api.kanye.rest/"); + response.EnsureSuccessStatusCode(); + + var stringResponse = await response.Content.ReadAsStringAsync(); + var data = JsonConvert.DeserializeObject(stringResponse); + await ReplyAsync(data.Quote); + } + catch (HttpRequestException) + { + await ReplyAsync("Api down..."); + } + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Randomness/Kanye/KanyeResponseDto.cs b/Geekbot.net/Commands/Randomness/Kanye/KanyeResponseDto.cs new file mode 100644 index 0000000..ff74f37 --- /dev/null +++ b/Geekbot.net/Commands/Randomness/Kanye/KanyeResponseDto.cs @@ -0,0 +1,8 @@ +namespace Geekbot.net.Commands.Randomness.Kanye +{ + public class KanyeResponseDto + { + public string Id { get; set; } + public string Quote { get; set; } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/RandomAnimals.cs b/Geekbot.net/Commands/Randomness/RandomAnimals.cs similarity index 60% rename from src/Bot/Commands/Randomness/RandomAnimals.cs rename to Geekbot.net/Commands/Randomness/RandomAnimals.cs index 5493485..8a15551 100644 --- a/src/Bot/Commands/Randomness/RandomAnimals.cs +++ b/Geekbot.net/Commands/Randomness/RandomAnimals.cs @@ -1,12 +1,12 @@ using System.Threading.Tasks; using Discord; using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.Media; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Media; -namespace Geekbot.Bot.Commands.Randomness +namespace Geekbot.net.Commands.Randomness { - public class RandomAnimals : TransactionModuleBase + public class RandomAnimals : GeekbotBase { private readonly IMediaProvider _mediaProvider; @@ -19,7 +19,7 @@ namespace Geekbot.Bot.Commands.Randomness [Summary("Get a random panda image")] public async Task Panda() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Panda))); + await ReplyAsync("", false, Eb(_mediaProvider.GetPanda())); } [Command("croissant", RunMode = RunMode.Async)] @@ -27,50 +27,50 @@ namespace Geekbot.Bot.Commands.Randomness [Summary("Get a random croissant image")] public async Task Croissant() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Croissant))); + await ReplyAsync("", false, Eb(_mediaProvider.GetCrossant())); } [Command("pumpkin", RunMode = RunMode.Async)] [Summary("Get a random pumpkin image")] public async Task Pumpkin() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Pumpkin))); + await ReplyAsync("", false, Eb(_mediaProvider.GetPumpkin())); } [Command("squirrel", RunMode = RunMode.Async)] [Summary("Get a random squirrel image")] public async Task Squirrel() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Squirrel))); + await ReplyAsync("", false, Eb(_mediaProvider.GetSquirrel())); } [Command("turtle", RunMode = RunMode.Async)] [Summary("Get a random turtle image")] public async Task Turtle() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Turtle))); + await ReplyAsync("", false, Eb(_mediaProvider.GetTurtle())); } - [Command("penguin", RunMode = RunMode.Async)] - [Alias("pengu")] - [Summary("Get a random penguin image")] - public async Task Penguin() + [Command("pinguin", RunMode = RunMode.Async)] + [Alias("pingu")] + [Summary("Get a random pinguin image")] + public async Task Pinguin() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Penguin))); + await ReplyAsync("", false, Eb(_mediaProvider.GetPinguin())); } [Command("fox", RunMode = RunMode.Async)] [Summary("Get a random fox image")] public async Task Fox() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Fox))); + await ReplyAsync("", false, Eb(_mediaProvider.GetFox())); } [Command("dab", RunMode = RunMode.Async)] [Summary("Get a random dab image")] public async Task Dab() { - await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Dab))); + await ReplyAsync("", false, Eb(_mediaProvider.GetDab())); } private static Embed Eb(string image) diff --git a/src/Bot/Commands/Randomness/Ship.cs b/Geekbot.net/Commands/Randomness/Ship.cs similarity index 59% rename from src/Bot/Commands/Randomness/Ship.cs rename to Geekbot.net/Commands/Randomness/Ship.cs index 55e55c5..3f43710 100644 --- a/src/Bot/Commands/Randomness/Ship.cs +++ b/Geekbot.net/Commands/Randomness/Ship.cs @@ -3,25 +3,25 @@ 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; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.RandomNumberGenerator; -namespace Geekbot.Bot.Commands.Randomness +namespace Geekbot.net.Commands.Randomness { - public class Ship : GeekbotCommandBase + public class Ship : GeekbotBase { + private readonly IErrorHandler _errorHandler; private readonly IRandomNumberGenerator _randomNumberGenerator; private readonly DatabaseContext _database; - public Ship(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager) + public Ship(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator) { _database = database; + _errorHandler = errorHandler; _randomNumberGenerator = randomNumberGenerator; } @@ -56,45 +56,45 @@ namespace Geekbot.Bot.Commands.Randomness 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)}"; + 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) { - await ErrorHandler.HandleCommandException(e, Context); + 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" - }; + if (rate < 20) + return "Not gonna happen"; + if (rate >= 20 && rate < 40) + return "Not such a good idea"; + if (rate >= 40 && rate < 60) + return "There might be a chance"; + if (rate >= 60 && rate < 80) + return "Almost a match, but could work"; + return rate >= 80 ? "It's a match" : "a"; } private string BlockCounter(int rate) { - var amount = rate / 10; + var amount = Math.Floor(decimal.Floor(rate / 10)); Console.WriteLine(amount); var blocks = ""; for (var i = 1; i <= 10; i++) if (i <= amount) { - blocks += ":white_medium_small_square:"; + blocks = blocks + ":white_medium_small_square:"; if (i == amount) - blocks += $" {rate}% "; + blocks = blocks + $" {rate}% "; } else { - blocks += ":black_medium_small_square:"; + blocks = blocks + ":black_medium_small_square:"; } return blocks; diff --git a/src/Bot/Commands/Randomness/Slap.cs b/Geekbot.net/Commands/Randomness/Slap.cs similarity index 81% rename from src/Bot/Commands/Randomness/Slap.cs rename to Geekbot.net/Commands/Randomness/Slap.cs index c99c325..3165581 100644 --- a/src/Bot/Commands/Randomness/Slap.cs +++ b/Geekbot.net/Commands/Randomness/Slap.cs @@ -4,15 +4,15 @@ 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.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; -namespace Geekbot.Bot.Commands.Randomness +namespace Geekbot.net.Commands.Randomness { - public class Slap : TransactionModuleBase + public class Slap : GeekbotBase { private readonly IErrorHandler _errorHandler; private readonly DatabaseContext _database; @@ -25,7 +25,7 @@ namespace Geekbot.Bot.Commands.Randomness [Command("slap", RunMode = RunMode.Async)] [Summary("slap someone")] - public async Task Slapper([Summary("@someone")] IUser user) + public async Task Slapper([Summary("@someone")] IGuildUser user) { try { @@ -77,14 +77,10 @@ namespace Geekbot.Bot.Commands.Randomness "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" + "powerless banhammer" }; - await ReplyAsync($"{Context.User.Username} slapped {user.Username} with a {things[new Random().Next(0, things.Count - 1)]}"); + await ReplyAsync($"{Context.GuildUser.Nickname} slapped {user.Nickname} with a {things[new Random().Next(things.Count - 1)]}"); await UpdateRecieved(user.Id); await UpdateGiven(Context.User.Id); @@ -132,4 +128,4 @@ namespace Geekbot.Bot.Commands.Randomness e.UserId.Equals(userId.AsLong())); } } -} +} \ No newline at end of file diff --git a/src/Bot/Commands/Rpg/Cookies.cs b/Geekbot.net/Commands/Rpg/Cookies.cs similarity index 59% rename from src/Bot/Commands/Rpg/Cookies.cs rename to Geekbot.net/Commands/Rpg/Cookies.cs index 66d845f..69e3d68 100644 --- a/src/Bot/Commands/Rpg/Cookies.cs +++ b/Geekbot.net/Commands/Rpg/Cookies.cs @@ -3,30 +3,29 @@ 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; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.RandomNumberGenerator; -namespace Geekbot.Bot.Commands.Rpg +namespace Geekbot.net.Commands.Rpg { [DisableInDirectMessage] [Group("cookies")] - [Alias("cookie")] - public class Cookies : GeekbotCommandBase + public class Cookies : GeekbotBase { private readonly DatabaseContext _database; + private readonly IErrorHandler _errorHandler; private readonly IRandomNumberGenerator _randomNumberGenerator; - public Cookies(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator, IGuildSettingsManager guildSettingsManager) - : base(errorHandler, guildSettingsManager) + public Cookies(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator) { _database = database; + _errorHandler = errorHandler; _randomNumberGenerator = randomNumberGenerator; } @@ -37,23 +36,21 @@ namespace Geekbot.Bot.Commands.Rpg try { var actor = await GetUser(Context.User.Id); - var timeoutDays = 1; - if (actor.LastPayout?.AddDays(timeoutDays) > DateTime.Now.ToUniversalTime()) + if (actor.LastPayout.Value.AddHours(24) > DateTimeOffset.Now) { - var remaining = actor.LastPayout.Value.AddDays(timeoutDays) - DateTimeOffset.Now.ToUniversalTime(); - var formattedWaitTime = DateLocalization.FormatDateTimeAsRemaining(remaining); - await ReplyAsync(string.Format(Localization.Cookies.WaitForMoreCookies, formattedWaitTime)); + var formatedWaitTime = Context.Translations.FormatDateTimeAsRemaining(actor.LastPayout.Value.AddHours(24)); + await ReplyAsync(Context.Translations.GetString("WaitForMoreCookies", formatedWaitTime)); return; } actor.Cookies += 10; - actor.LastPayout = DateTimeOffset.Now.ToUniversalTime(); + actor.LastPayout = DateTimeOffset.Now; await SetUser(actor); - await ReplyAsync(string.Format(Localization.Cookies.GetCookies, 10, actor.Cookies)); + await ReplyAsync(Context.Translations.GetString("GetCookies", 10, actor.Cookies)); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -64,11 +61,11 @@ namespace Geekbot.Bot.Commands.Rpg try { var actor = await GetUser(Context.User.Id); - await ReplyAsync(string.Format(Localization.Cookies.InYourJar, actor.Cookies)); + await ReplyAsync(Context.Translations.GetString("InYourJar", actor.Cookies)); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -80,15 +77,9 @@ namespace Geekbot.Bot.Commands.Rpg { 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); + await ReplyAsync(Context.Translations.GetString("NotEnoughToGive")); return; } @@ -100,11 +91,11 @@ namespace Geekbot.Bot.Commands.Rpg await SetUser(giver); await SetUser(taker); - await ReplyAsync(string.Format(Localization.Cookies.Given, amount, user.Username)); + await ReplyAsync(Context.Translations.GetString("Given", amount, user.Username)); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } @@ -118,7 +109,7 @@ namespace Geekbot.Bot.Commands.Rpg if (actor.Cookies < 5) { - await ReplyAsync(Localization.Cookies.NotEnoughCookiesToEat); + await ReplyAsync(Context.Translations.GetString("NotEnoughCookiesToEat")); return; } @@ -127,14 +118,14 @@ namespace Geekbot.Bot.Commands.Rpg await SetUser(actor); - await ReplyAsync(string.Format(Localization.Cookies.AteCookies, amount, actor.Cookies)); + await ReplyAsync(Context.Translations.GetString("AteCookies", amount, actor.Cookies)); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } - + private async Task GetUser(ulong userId) { var user = _database.Cookies.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId); @@ -154,7 +145,7 @@ namespace Geekbot.Bot.Commands.Rpg GuildId = Context.Guild.Id.AsLong(), UserId = userId.AsLong(), Cookies = 0, - LastPayout = DateTimeOffset.MinValue.ToUniversalTime() + LastPayout = DateTimeOffset.MinValue }; var newUser = _database.Cookies.Add(user).Entity; await _database.SaveChangesAsync(); diff --git a/src/Bot/Commands/User/GuildInfo.cs b/Geekbot.net/Commands/User/GuildInfo.cs similarity index 84% rename from src/Bot/Commands/User/GuildInfo.cs rename to Geekbot.net/Commands/User/GuildInfo.cs index c063d89..e55506c 100644 --- a/src/Bot/Commands/User/GuildInfo.cs +++ b/Geekbot.net/Commands/User/GuildInfo.cs @@ -3,16 +3,16 @@ 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.ErrorHandling; -using Geekbot.Core.Extensions; -using Geekbot.Core.Levels; +using Geekbot.net.Database; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Levels; -namespace Geekbot.Bot.Commands.User +namespace Geekbot.net.Commands.User { - public class GuildInfo : TransactionModuleBase + public class GuildInfo : GeekbotBase { private readonly IErrorHandler _errorHandler; private readonly DatabaseContext _database; diff --git a/Geekbot.net/Commands/User/Karma.cs b/Geekbot.net/Commands/User/Karma.cs new file mode 100644 index 0000000..fd4a82d --- /dev/null +++ b/Geekbot.net/Commands/User/Karma.cs @@ -0,0 +1,149 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; + +namespace Geekbot.net.Commands.User +{ + [DisableInDirectMessage] + public class Karma : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + private readonly DatabaseContext _database; + + public Karma(DatabaseContext database, IErrorHandler errorHandler) + { + _database = database; + _errorHandler = errorHandler; + } + + [Command("good", RunMode = RunMode.Async)] + [Summary("Increase Someones Karma")] + public async Task Good([Summary("@someone")] IUser user) + { + try + { + var actor = await GetUser(Context.User.Id); + if (user.Id == Context.User.Id) + { + await ReplyAsync(Context.Translations.GetString("CannotChangeOwn", Context.User.Username)); + } + else if (TimeoutFinished(actor.TimeOut)) + { + var formatedWaitTime = Context.Translations.FormatDateTimeAsRemaining(actor.TimeOut.AddMinutes(3)); + await ReplyAsync(Context.Translations.GetString("WaitUntill", Context.User.Username, formatedWaitTime)); + } + else + { + var target = await GetUser(user.Id); + target.Karma = target.Karma + 1; + SetUser(target); + + actor.TimeOut = DateTimeOffset.Now; + SetUser(actor); + + await _database.SaveChangesAsync(); + + var eb = new EmbedBuilder(); + eb.WithAuthor(new EmbedAuthorBuilder() + .WithIconUrl(user.GetAvatarUrl()) + .WithName(user.Username)); + + eb.WithColor(new Color(138, 219, 146)); + eb.Title = Context.Translations.GetString("Increased"); + eb.AddInlineField(Context.Translations.GetString("By"), Context.User.Username); + eb.AddInlineField(Context.Translations.GetString("Amount"), "+1"); + eb.AddInlineField(Context.Translations.GetString("Current"), target.Karma); + await ReplyAsync("", false, eb.Build()); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + + [Command("bad", RunMode = RunMode.Async)] + [Summary("Decrease Someones Karma")] + public async Task Bad([Summary("@someone")] IUser user) + { + try + { + var actor = await GetUser(Context.User.Id); + if (user.Id == Context.User.Id) + { + await ReplyAsync(Context.Translations.GetString("CannotChangeOwn", Context.User.Username)); + } + else if (TimeoutFinished(actor.TimeOut)) + { + var formatedWaitTime = Context.Translations.FormatDateTimeAsRemaining(actor.TimeOut.AddMinutes(3)); + await ReplyAsync(Context.Translations.GetString("WaitUntill", Context.User.Username, formatedWaitTime)); + } + else + { + var target = await GetUser(user.Id); + target.Karma = target.Karma - 1; + SetUser(target); + + actor.TimeOut = DateTimeOffset.Now; + SetUser(actor); + + await _database.SaveChangesAsync(); + + var eb = new EmbedBuilder(); + eb.WithAuthor(new EmbedAuthorBuilder() + .WithIconUrl(user.GetAvatarUrl()) + .WithName(user.Username)); + + eb.WithColor(new Color(138, 219, 146)); + eb.Title = Context.Translations.GetString("Decreased"); + eb.AddInlineField(Context.Translations.GetString("By"), Context.User.Username); + eb.AddInlineField(Context.Translations.GetString("Amount"), "-1"); + eb.AddInlineField(Context.Translations.GetString("Current"), target.Karma); + await ReplyAsync("", false, eb.Build()); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + + private bool TimeoutFinished(DateTimeOffset lastKarma) + { + return lastKarma.AddMinutes(3) > DateTimeOffset.Now; + } + + private async Task GetUser(ulong userId) + { + var user = _database.Karma.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId); + return user; + } + + private void SetUser(KarmaModel user) + { + _database.Karma.Update(user); + } + + private async Task CreateNewRow(ulong userId) + { + var user = new KarmaModel() + { + GuildId = Context.Guild.Id.AsLong(), + UserId = userId.AsLong(), + Karma = 0, + TimeOut = DateTimeOffset.MinValue + }; + var newUser = _database.Karma.Add(user).Entity; + await _database.SaveChangesAsync(); + return newUser; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/User/Ranking/Rank.cs b/Geekbot.net/Commands/User/Ranking/Rank.cs new file mode 100644 index 0000000..03d2e31 --- /dev/null +++ b/Geekbot.net/Commands/User/Ranking/Rank.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Database; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.Converters; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Highscores; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.UserRepository; + +namespace Geekbot.net.Commands.User.Ranking +{ + public class Rank : GeekbotBase + { + private readonly IEmojiConverter _emojiConverter; + private readonly IHighscoreManager _highscoreManager; + private readonly IErrorHandler _errorHandler; + private readonly DatabaseContext _database; + + public Rank(DatabaseContext database, IErrorHandler errorHandler, IEmojiConverter emojiConverter, IHighscoreManager highscoreManager) + { + _database = database; + _errorHandler = errorHandler; + _emojiConverter = emojiConverter; + _highscoreManager = highscoreManager; + } + + [Command("rank", RunMode = RunMode.Async)] + [Summary("get user top 10 in messages or karma")] + [DisableInDirectMessage] + public async Task RankCmd([Summary("type")] string typeUnformated = "messages", [Summary("amount")] int amount = 10) + { + try + { + HighscoreTypes type; + try + { + type = Enum.Parse(typeUnformated, true); + if (!Enum.IsDefined(typeof(HighscoreTypes), type)) throw new Exception(); + } + catch + { + await ReplyAsync(Context.Translations.GetString("InvalidType")); + return; + } + + var replyBuilder = new StringBuilder(); + if (amount > 20) + { + await ReplyAsync(Context.Translations.GetString("LimitingTo20Warning")); + amount = 20; + } + + var guildId = Context.Guild.Id; + Dictionary highscoreUsers; + try + { + highscoreUsers = _highscoreManager.GetHighscoresWithUserData(type, guildId, amount); + } + catch (HighscoreListEmptyException) + { + await ReplyAsync(Context.Translations.GetString("NoTypeFoundForServer", type)); + return; + } + + int guildMessages = 0; + if (type == HighscoreTypes.messages) + { + guildMessages = _database.Messages + .Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong())) + .Select(e => e.MessageCount) + .Sum(); + } + + var failedToRetrieveUser = highscoreUsers.Any(e => string.IsNullOrEmpty(e.Key.Username)); + + if (failedToRetrieveUser) replyBuilder.AppendLine(Context.Translations.GetString("FailedToResolveAllUsernames")); + replyBuilder.AppendLine(Context.Translations.GetString("HighscoresFor", type.ToString().CapitalizeFirst(), 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}**"); + + replyBuilder.Append(type == HighscoreTypes.messages + ? $" - {user.Value} {type} - {Math.Round((double) (100 * user.Value) / guildMessages, digits: 2)}%\n" + : $" - {user.Value} {type}\n"); + + highscorePlace++; + } + + await ReplyAsync(replyBuilder.ToString()); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/User/Stats.cs b/Geekbot.net/Commands/User/Stats.cs similarity index 62% rename from src/Bot/Commands/User/Stats.cs rename to Geekbot.net/Commands/User/Stats.cs index 61505c0..1ee85bf 100644 --- a/src/Bot/Commands/User/Stats.cs +++ b/Geekbot.net/Commands/User/Stats.cs @@ -3,25 +3,28 @@ 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.ErrorHandling; -using Geekbot.Core.Extensions; -using Geekbot.Core.GuildSettingsManager; -using Geekbot.Core.Levels; -using Localization = Geekbot.Core.Localization; +using Geekbot.net.Database; +using Geekbot.net.Lib; +using Geekbot.net.Lib.AlmostRedis; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Levels; -namespace Geekbot.Bot.Commands.User +namespace Geekbot.net.Commands.User { - public class Stats : GeekbotCommandBase + public class Stats : GeekbotBase { + private readonly IErrorHandler _errorHandler; private readonly ILevelCalc _levelCalc; + private readonly IAlmostRedis _redis; private readonly DatabaseContext _database; - public Stats(DatabaseContext database, IErrorHandler errorHandler, ILevelCalc levelCalc, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager) + public Stats(IAlmostRedis redis, DatabaseContext database, IErrorHandler errorHandler, ILevelCalc levelCalc) { + _redis = redis; _database = database; + _errorHandler = errorHandler; _levelCalc = levelCalc; } @@ -49,14 +52,12 @@ namespace Geekbot.Bot.Commands.User var level = _levelCalc.GetLevel(messages); - var percent = Math.Round((double) (100 * messages) / guildMessages, 2); + var percent = Math.Round((double) (100 * messages) / guildMessages, digits: 2); var cookies = _database.Cookies ?.FirstOrDefault(e => e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.UserId.Equals(userInfo.Id.AsLong())) ?.Cookies ?? 0; - var quotes = _database.Quotes.Count(e => e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.UserId.Equals(userInfo.Id.AsLong())); - var eb = new EmbedBuilder(); eb.WithAuthor(new EmbedAuthorBuilder() .WithIconUrl(userInfo.GetAvatarUrl()) @@ -70,24 +71,23 @@ namespace Geekbot.Bot.Commands.User e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.UserId.Equals(userInfo.Id.AsLong())); - eb.AddInlineField(Localization.Stats.OnDiscordSince, - $"{createdAt.Day}.{createdAt.Month}.{createdAt.Year} ({age} {Localization.Stats.Days})") - .AddInlineField(Localization.Stats.JoinedServer, - $"{joinedAt.Day}.{joinedAt.Month}.{joinedAt.Year} ({joinedDayAgo} {Localization.Stats.Days})") - .AddInlineField(Localization.Stats.Karma, karma?.Karma ?? 0) - .AddInlineField(Localization.Stats.Level, level) - .AddInlineField(Localization.Stats.MessagesSent, messages) - .AddInlineField(Localization.Stats.ServerTotal, $"{percent}%"); + 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?.Karma ?? 0) + .AddInlineField("Level", level) + .AddInlineField("Messages Sent", messages) + .AddInlineField("Server Total", $"{percent}%"); - if (correctRolls != null) eb.AddInlineField(Localization.Stats.GuessedRolls, correctRolls.Rolls); - if (cookies > 0) eb.AddInlineField(Localization.Stats.Cookies, cookies); - if (quotes > 0) eb.AddInlineField(Localization.Stats.Quotes, quotes); + if (correctRolls != null) eb.AddInlineField("Guessed Rolls", correctRolls.Rolls); + if (cookies > 0) eb.AddInlineField("Cookies", cookies); await ReplyAsync("", false, eb.Build()); } catch (Exception e) { - await ErrorHandler.HandleCommandException(e, Context); + await _errorHandler.HandleCommandException(e, Context); } } } diff --git a/src/Bot/Commands/Utils/AvatarGetter.cs b/Geekbot.net/Commands/Utils/AvatarGetter.cs similarity index 69% rename from src/Bot/Commands/Utils/AvatarGetter.cs rename to Geekbot.net/Commands/Utils/AvatarGetter.cs index 458eec8..2077fa5 100644 --- a/src/Bot/Commands/Utils/AvatarGetter.cs +++ b/Geekbot.net/Commands/Utils/AvatarGetter.cs @@ -2,12 +2,12 @@ using System.Threading.Tasks; using Discord; using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; -namespace Geekbot.Bot.Commands.Utils +namespace Geekbot.net.Commands.Utils { - public class AvatarGetter : TransactionModuleBase + public class AvatarGetter : GeekbotBase { private readonly IErrorHandler _errorHandler; @@ -22,8 +22,8 @@ namespace Geekbot.Bot.Commands.Utils { try { - user ??= Context.User; - var url = user.GetAvatarUrl(ImageFormat.Auto, 1024); + if (user == null) user = Context.User; + var url = user.GetAvatarUrl().Replace("128", "1024"); await ReplyAsync(url); } catch (Exception e) diff --git a/Geekbot.net/Commands/Utils/Changelog/Changelog.cs b/Geekbot.net/Commands/Utils/Changelog/Changelog.cs new file mode 100644 index 0000000..e86851a --- /dev/null +++ b/Geekbot.net/Commands/Utils/Changelog/Changelog.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Newtonsoft.Json; + +namespace Geekbot.net.Commands.Utils.Changelog +{ + public class Changelog : GeekbotBase + { + private readonly DiscordSocketClient _client; + private readonly IErrorHandler _errorHandler; + + public Changelog(IErrorHandler errorHandler, DiscordSocketClient client) + { + _errorHandler = errorHandler; + _client = client; + } + + [Command("changelog", RunMode = RunMode.Async)] + [Summary("Show the latest 10 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>(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) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Utils/Changelog/CommitAuthorDto.cs b/Geekbot.net/Commands/Utils/Changelog/CommitAuthorDto.cs new file mode 100644 index 0000000..8debd77 --- /dev/null +++ b/Geekbot.net/Commands/Utils/Changelog/CommitAuthorDto.cs @@ -0,0 +1,11 @@ +using System; + +namespace Geekbot.net.Commands.Utils.Changelog +{ + public class CommitAuthorDto + { + public string Name { get; set; } + public string Email { get; set; } + public DateTimeOffset Date { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Utils/Changelog/CommitDto.cs b/Geekbot.net/Commands/Utils/Changelog/CommitDto.cs new file mode 100644 index 0000000..3379697 --- /dev/null +++ b/Geekbot.net/Commands/Utils/Changelog/CommitDto.cs @@ -0,0 +1,7 @@ +namespace Geekbot.net.Commands.Utils.Changelog +{ + public class CommitDto + { + public CommitInfoDto Commit { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Utils/Changelog/CommitInfoDto.cs b/Geekbot.net/Commands/Utils/Changelog/CommitInfoDto.cs new file mode 100644 index 0000000..9008343 --- /dev/null +++ b/Geekbot.net/Commands/Utils/Changelog/CommitInfoDto.cs @@ -0,0 +1,8 @@ +namespace Geekbot.net.Commands.Utils.Changelog +{ + public class CommitInfoDto + { + public CommitAuthorDto Author { get; set; } + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Utils/Choose.cs b/Geekbot.net/Commands/Utils/Choose.cs new file mode 100644 index 0000000..84ba62a --- /dev/null +++ b/Geekbot.net/Commands/Utils/Choose.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Localization; + +namespace Geekbot.net.Commands.Utils +{ + public class Choose : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + + public Choose(IErrorHandler errorHandler) + { + _errorHandler = errorHandler; + } + + [Command("choose", RunMode = RunMode.Async)] + [Summary("Let the bot choose for you, seperate options with a semicolon.")] + public async Task Command([Remainder] [Summary("option1;option2")] + string choices) + { + try + { + var choicesArray = choices.Split(';'); + var choice = new Random().Next(choicesArray.Length); + await ReplyAsync(Context.Translations.GetString("Choice", choicesArray[choice].Trim())); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Utils/Dice/Dice.cs b/Geekbot.net/Commands/Utils/Dice/Dice.cs new file mode 100644 index 0000000..5cea32e --- /dev/null +++ b/Geekbot.net/Commands/Utils/Dice/Dice.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.RandomNumberGenerator; + +namespace Geekbot.net.Commands.Utils.Dice +{ + public class Dice : GeekbotBase + { + private readonly IRandomNumberGenerator _randomNumberGenerator; + + public Dice(IRandomNumberGenerator randomNumberGenerator) + { + _randomNumberGenerator = randomNumberGenerator; + } + + [Command("dice", RunMode = RunMode.Async)] + [Summary("Roll a dice.")] + public async Task RollCommand([Remainder] [Summary("dice-type")] string diceType = "1d20") + { + var splitedDices = diceType.Split("+"); + var dices = new List(); + var mod = 0; + foreach (var i in splitedDices) + { + var dice = ToDice(i); + if (dice.Sides != 0 && dice.Times != 0) + { + dices.Add(dice); + } + else if (dice.Mod != 0) + { + if (mod != 0) + { + await ReplyAsync("You can only have one mod"); + return; + } + + mod = dice.Mod; + } + } + + if (!dices.Any()) + { + await ReplyAsync( + "That is not a valid dice, examples are: 1d20, 1d6, 2d6, 1d6+2, 1d6+2d8+1d20+6, etc..."); + return; + } + + + if (dices.Any(d => d.Times > 20)) + { + await ReplyAsync("You can't throw more than 20 dices"); + return; + } + + if (dices.Any(d => d.Sides > 144)) + { + await ReplyAsync("A dice can't have more than 144 sides"); + return; + } + + var rep = new StringBuilder(); + rep.AppendLine($":game_die: {Context.User.Mention}"); + rep.Append("**Result:** "); + var resultStrings = new List(); + var total = 0; + var extraText = ""; + foreach (var dice in dices) + { + var results = new List(); + for (var i = 0; i < dice.Times; i++) + { + var roll = _randomNumberGenerator.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(); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Commands/Utils/Dice/DiceTypeDto.cs b/Geekbot.net/Commands/Utils/Dice/DiceTypeDto.cs new file mode 100644 index 0000000..5c54792 --- /dev/null +++ b/Geekbot.net/Commands/Utils/Dice/DiceTypeDto.cs @@ -0,0 +1,10 @@ +namespace Geekbot.net.Commands.Utils.Dice +{ + internal class DiceTypeDto + { + public string DiceType { get; set; } + public int Times { get; set; } + public int Sides { get; set; } + public int Mod { get; set; } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Emojify.cs b/Geekbot.net/Commands/Utils/Emojify.cs similarity index 56% rename from src/Bot/Commands/Utils/Emojify.cs rename to Geekbot.net/Commands/Utils/Emojify.cs index a513710..ddf1762 100644 --- a/src/Bot/Commands/Utils/Emojify.cs +++ b/Geekbot.net/Commands/Utils/Emojify.cs @@ -1,17 +1,21 @@ -using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.Converters; -using Geekbot.Core.ErrorHandling; +using System; +using System.Threading.Tasks; +using Discord.Commands; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Converters; +using Geekbot.net.Lib.ErrorHandling; -namespace Geekbot.Bot.Commands.Utils +namespace Geekbot.net.Commands.Utils { - public class Emojify : TransactionModuleBase + public class Emojify : GeekbotBase { + private readonly IEmojiConverter _emojiConverter; private readonly IErrorHandler _errorHandler; - public Emojify(IErrorHandler errorHandler) + public Emojify(IErrorHandler errorHandler, IEmojiConverter emojiConverter) { _errorHandler = errorHandler; + _emojiConverter = emojiConverter; } [Command("emojify", RunMode = RunMode.Async)] @@ -20,7 +24,7 @@ namespace Geekbot.Bot.Commands.Utils { try { - var emojis = EmojiConverter.TextToEmoji(text); + var emojis = _emojiConverter.TextToEmoji(text); if (emojis.Length > 1999) { await ReplyAsync("I can't take that much at once!"); @@ -29,14 +33,6 @@ namespace Geekbot.Bot.Commands.Utils await ReplyAsync($"{Context.User.Username}#{Context.User.Discriminator} said:"); await ReplyAsync(emojis); - try - { - await Context.Message.DeleteAsync(); - } - catch - { - // bot may not have enough permission, doesn't matter if it fails - } } catch (Exception e) { diff --git a/src/Bot/Commands/Utils/Help.cs b/Geekbot.net/Commands/Utils/Help.cs similarity index 80% rename from src/Bot/Commands/Utils/Help.cs rename to Geekbot.net/Commands/Utils/Help.cs index 7aa9aff..73f1956 100644 --- a/src/Bot/Commands/Utils/Help.cs +++ b/Geekbot.net/Commands/Utils/Help.cs @@ -3,12 +3,12 @@ using System.Text; using System.Threading.Tasks; using Discord; using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; -namespace Geekbot.Bot.Commands.Utils +namespace Geekbot.net.Commands.Utils { - public class Help : TransactionModuleBase + public class Help : GeekbotBase { private readonly IErrorHandler _errorHandler; @@ -27,7 +27,7 @@ namespace Geekbot.Bot.Commands.Utils sb.AppendLine("For a list of all commands, please visit the following page"); sb.AppendLine("https://geekbot.pizzaandcoffee.rocks/commands"); - var dm = await Context.User.CreateDMChannelAsync(RequestOptions.Default); + var dm = await Context.User.GetOrCreateDMChannelAsync(); await dm.SendMessageAsync(sb.ToString()); await Context.Message.AddReactionAsync(new Emoji("✅")); } diff --git a/src/Bot/Commands/Utils/Info.cs b/Geekbot.net/Commands/Utils/Info.cs similarity index 91% rename from src/Bot/Commands/Utils/Info.cs rename to Geekbot.net/Commands/Utils/Info.cs index 912528d..2ca8d8b 100644 --- a/src/Bot/Commands/Utils/Info.cs +++ b/Geekbot.net/Commands/Utils/Info.cs @@ -5,13 +5,13 @@ using System.Threading.Tasks; using Discord; using Discord.Commands; using Discord.WebSocket; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.Extensions; +using Geekbot.net.Lib; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; -namespace Geekbot.Bot.Commands.Utils +namespace Geekbot.net.Commands.Utils { - public class Info : TransactionModuleBase + public class Info : GeekbotBase { private readonly DiscordSocketClient _client; private readonly CommandService _commands; diff --git a/src/Bot/Commands/Utils/Ping.cs b/Geekbot.net/Commands/Utils/Ping.cs similarity index 73% rename from src/Bot/Commands/Utils/Ping.cs rename to Geekbot.net/Commands/Utils/Ping.cs index ee751cd..a794b3a 100644 --- a/src/Bot/Commands/Utils/Ping.cs +++ b/Geekbot.net/Commands/Utils/Ping.cs @@ -1,10 +1,10 @@ using System.Threading.Tasks; using Discord.Commands; -using Geekbot.Core; +using Geekbot.net.Lib; -namespace Geekbot.Bot.Commands.Utils +namespace Geekbot.net.Commands.Utils { - public class Ping : TransactionModuleBase + public class Ping : GeekbotBase { [Command("👀", RunMode = RunMode.Async)] [Summary("Look at the bot.")] diff --git a/Geekbot.net/Commands/Utils/Quote/Quote.cs b/Geekbot.net/Commands/Utils/Quote/Quote.cs new file mode 100644 index 0000000..6cfd3f6 --- /dev/null +++ b/Geekbot.net/Commands/Utils/Quote/Quote.cs @@ -0,0 +1,249 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib; +using Geekbot.net.Lib.CommandPreconditions; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.Polyfills; +using Geekbot.net.Lib.RandomNumberGenerator; + +namespace Geekbot.net.Commands.Utils.Quote +{ + [Group("quote")] + [DisableInDirectMessage] + public class Quote : GeekbotBase + { + private readonly IErrorHandler _errorHandler; + private readonly DatabaseContext _database; + private readonly IRandomNumberGenerator _randomNumberGenerator; + + public Quote(IErrorHandler errorHandler, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator) + { + _errorHandler = errorHandler; + _database = database; + _randomNumberGenerator = randomNumberGenerator; + } + + [Command] + [Summary("Return a random quoute from the database")] + public async Task GetRandomQuote() + { + try + { + var s = _database.Quotes.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong())).ToList(); + + if (!s.Any()) + { + await ReplyAsync(Context.Translations.GetString("NoQuotesFound")); + return; + } + + var random = _randomNumberGenerator.Next(0, s.Count()); + var quote = s[random]; + + var embed = QuoteBuilder(quote); + await ReplyAsync("", false, embed.Build()); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context, "Whoops, seems like the quote was to edgy to return"); + } + } + + [Command("save")] + [Summary("Save a quote from the last sent message by @user")] + public async Task SaveQuote([Summary("@someone")] IUser user) + { + try + { + if (user.Id == Context.Message.Author.Id) + { + await ReplyAsync(Context.Translations.GetString("CannotSaveOwnQuotes")); + return; + } + + if (user.IsBot) + { + await ReplyAsync(Context.Translations.GetString("CannotQuoteBots")); + return; + } + + var lastMessage = await GetLastMessageByUser(user); + if (lastMessage == null) return; + + var quote = CreateQuoteObject(lastMessage); + _database.Quotes.Add(quote); + await _database.SaveChangesAsync(); + + var embed = QuoteBuilder(quote); + await ReplyAsync(Context.Translations.GetString("QuoteAdded"), false, embed.Build()); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context, + "I counldn't find a quote from that user :disappointed:"); + } + } + + [Command("save")] + [Summary("Save a quote from a message id")] + public async Task SaveQuote([Summary("message-ID")] ulong messageId) + { + try + { + var message = await Context.Channel.GetMessageAsync(messageId); + if (message.Author.Id == Context.Message.Author.Id) + { + await ReplyAsync(Context.Translations.GetString("CannotSaveOwnQuotes")); + return; + } + + if (message.Author.IsBot) + { + await ReplyAsync(Context.Translations.GetString("CannotQuoteBots")); + return; + } + + var quote = CreateQuoteObject(message); + _database.Quotes.Add(quote); + await _database.SaveChangesAsync(); + + var embed = QuoteBuilder(quote); + await ReplyAsync(Context.Translations.GetString("QuoteAdded"), false, embed.Build()); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context, + "I couldn't find a message with that id :disappointed:"); + } + } + + [Command("make")] + [Summary("Create a quote from the last sent message by @user")] + public async Task ReturnSpecifiedQuote([Summary("@someone")] IUser user) + { + try + { + var lastMessage = await GetLastMessageByUser(user); + if (lastMessage == null) return; + var quote = CreateQuoteObject(lastMessage); + var embed = QuoteBuilder(quote); + await ReplyAsync("", false, embed.Build()); + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context, + "I counldn't find a quote from that user :disappointed:"); + } + } + + [Command("make")] + [Summary("Create a quote from a message id")] + public async Task ReturnSpecifiedQuote([Summary("message-ID")] 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) + { + await _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)] + [Summary("Remove a quote (required mod permissions)")] + public async Task RemoveQuote([Summary("quote-ID")] int id) + { + try + { + var quote = _database.Quotes.Where(e => e.GuildId == Context.Guild.Id.AsLong() && e.InternalId == id)?.FirstOrDefault(); + if (quote != null) + { + _database.Quotes.Remove(quote); + await _database.SaveChangesAsync(); + var embed = QuoteBuilder(quote); + await ReplyAsync(Context.Translations.GetString("Removed", id), false, embed.Build()); + } + else + { + await ReplyAsync(Context.Translations.GetString("NotFoundWithId")); + } + } + catch (Exception e) + { + await _errorHandler.HandleCommandException(e, Context, "I couldn't find a quote with that id :disappointed:"); + } + } + + private async Task GetLastMessageByUser(IUser user) + { + try + { + var list = Context.Channel.GetMessagesAsync().Flatten(); + return await list.FirstOrDefault(msg => + msg.Author.Id == user.Id && + msg.Embeds.Count == 0 && + msg.Id != Context.Message.Id && + !msg.Content.ToLower().StartsWith("!")); + } + catch + { + await ReplyAsync($"No quoteable message have been sent by {user.Username} in this channel"); + return null; + } + } + + private EmbedBuilder QuoteBuilder(QuoteModel quote) + { + var user = Context.Client.GetUserAsync(quote.UserId.AsUlong()).Result ?? new UserPolyfillDto { Username = "Unknown User" }; + var eb = new EmbedBuilder(); + eb.WithColor(new Color(143, 167, 232)); + eb.Title = $"#{quote.InternalId} | {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 QuoteModel CreateQuoteObject(IMessage message) + { + string image; + try + { + image = message.Attachments.First().Url; + } + catch (Exception) + { + image = null; + } + + var last = _database.Quotes.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong())) + .OrderByDescending(e => e.InternalId).FirstOrDefault(); + int internalId = 1; + if (last != null) internalId = last.InternalId + 1; + return new QuoteModel() + { + InternalId = internalId, + GuildId = Context.Guild.Id.AsLong(), + UserId = message.Author.Id.AsLong(), + Time = message.Timestamp.DateTime, + Quote = message.Content, + Image = image + }; + } + } +} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Quote/QuoteObjectDto.cs b/Geekbot.net/Commands/Utils/Quote/QuoteObjectDto.cs similarity index 81% rename from src/Bot/Commands/Utils/Quote/QuoteObjectDto.cs rename to Geekbot.net/Commands/Utils/Quote/QuoteObjectDto.cs index 32b65cf..a37ff76 100644 --- a/src/Bot/Commands/Utils/Quote/QuoteObjectDto.cs +++ b/Geekbot.net/Commands/Utils/Quote/QuoteObjectDto.cs @@ -1,6 +1,6 @@ using System; -namespace Geekbot.Bot.Commands.Utils.Quote +namespace Geekbot.net.Commands.Utils.Quote { internal class QuoteObjectDto { diff --git a/src/Core/Database/DatabaseContext.cs b/Geekbot.net/Database/DatabaseContext.cs similarity index 76% rename from src/Core/Database/DatabaseContext.cs rename to Geekbot.net/Database/DatabaseContext.cs index 14a1275..cbfa30d 100644 --- a/src/Core/Database/DatabaseContext.cs +++ b/Geekbot.net/Database/DatabaseContext.cs @@ -1,22 +1,22 @@ -using Geekbot.Core.Database.Models; +using Geekbot.net.Database.Models; using Microsoft.EntityFrameworkCore; -namespace Geekbot.Core.Database +namespace Geekbot.net.Database { public class DatabaseContext : DbContext { public DbSet Quotes { get; set; } public DbSet Users { get; set; } + public DbSet Guilds { get; set; } public DbSet GuildSettings { get; set; } public DbSet Karma { get; set; } public DbSet Ships { get; set; } public DbSet Rolls { get; set; } - public DbSet MessagesSeasons { get; set; } public DbSet Messages { get; set; } public DbSet Slaps { get; set; } public DbSet Globals { get; set; } public DbSet RoleSelfService { get; set; } + public DbSet Polls { get; set; } public DbSet Cookies { get; set; } - public DbSet ReactionListeners { get; set; } } } \ No newline at end of file diff --git a/src/Core/Database/DatabaseInitializer.cs b/Geekbot.net/Database/DatabaseInitializer.cs similarity index 80% rename from src/Core/Database/DatabaseInitializer.cs rename to Geekbot.net/Database/DatabaseInitializer.cs index 37f07bc..6adff44 100644 --- a/src/Core/Database/DatabaseInitializer.cs +++ b/Geekbot.net/Database/DatabaseInitializer.cs @@ -1,9 +1,11 @@ using System; -using Geekbot.Core.Database.LoggingAdapter; -using Geekbot.Core.Logger; +using Geekbot.net.Database.LoggingAdapter; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Logger; +using Microsoft.EntityFrameworkCore; using Npgsql.Logging; -namespace Geekbot.Core.Database +namespace Geekbot.net.Database { public class DatabaseInitializer { @@ -34,10 +36,7 @@ namespace Geekbot.Core.Database Port = _runParameters.DbPort, Database = _runParameters.DbDatabase, Username = _runParameters.DbUser, - Password = _runParameters.DbPassword, - RequireSsl = _runParameters.DbSsl, - TrustServerCertificate = _runParameters.DbTrustCert, - RedshiftCompatibility = _runParameters.DbRedshiftCompatibility + Password = _runParameters.DbPassword }); } } diff --git a/src/Core/Database/InMemoryDatabase.cs b/Geekbot.net/Database/InMemoryDatabase.cs similarity index 89% rename from src/Core/Database/InMemoryDatabase.cs rename to Geekbot.net/Database/InMemoryDatabase.cs index 1077aea..2b2cfd0 100644 --- a/src/Core/Database/InMemoryDatabase.cs +++ b/Geekbot.net/Database/InMemoryDatabase.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; -namespace Geekbot.Core.Database +namespace Geekbot.net.Database { public class InMemoryDatabase : DatabaseContext { diff --git a/src/Core/Database/LoggingAdapter/NpgsqlLoggingAdapter.cs b/Geekbot.net/Database/LoggingAdapter/NpgsqlLoggingAdapter.cs similarity index 72% rename from src/Core/Database/LoggingAdapter/NpgsqlLoggingAdapter.cs rename to Geekbot.net/Database/LoggingAdapter/NpgsqlLoggingAdapter.cs index 8a46c0d..e8850cd 100644 --- a/src/Core/Database/LoggingAdapter/NpgsqlLoggingAdapter.cs +++ b/Geekbot.net/Database/LoggingAdapter/NpgsqlLoggingAdapter.cs @@ -1,9 +1,10 @@ using System; -using Geekbot.Core.Logger; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Logger; using Npgsql.Logging; using LogLevel = NLog.LogLevel; -namespace Geekbot.Core.Database.LoggingAdapter +namespace Geekbot.net.Database.LoggingAdapter { public class NpgsqlLoggingAdapter : NpgsqlLogger { @@ -53,16 +54,23 @@ namespace Geekbot.Core.Database.LoggingAdapter private static LogLevel ToGeekbotLogLevel(NpgsqlLogLevel level) { - return level switch + switch (level) { - NpgsqlLogLevel.Trace => LogLevel.Trace, - NpgsqlLogLevel.Debug => LogLevel.Debug, - NpgsqlLogLevel.Info => LogLevel.Info, - NpgsqlLogLevel.Warn => LogLevel.Warn, - NpgsqlLogLevel.Error => LogLevel.Error, - NpgsqlLogLevel.Fatal => LogLevel.Fatal, - _ => throw new ArgumentOutOfRangeException(nameof(level)) - }; + case NpgsqlLogLevel.Trace: + return LogLevel.Trace; + case NpgsqlLogLevel.Debug: + return LogLevel.Debug; + case NpgsqlLogLevel.Info: + return LogLevel.Info; + case NpgsqlLogLevel.Warn: + return LogLevel.Warn; + case NpgsqlLogLevel.Error: + return LogLevel.Error; + case NpgsqlLogLevel.Fatal: + return LogLevel.Fatal; + default: + throw new ArgumentOutOfRangeException(nameof(level)); + } } } } \ No newline at end of file diff --git a/src/Core/Database/LoggingAdapter/NpgsqlLoggingProviderAdapter.cs b/Geekbot.net/Database/LoggingAdapter/NpgsqlLoggingProviderAdapter.cs similarity index 82% rename from src/Core/Database/LoggingAdapter/NpgsqlLoggingProviderAdapter.cs rename to Geekbot.net/Database/LoggingAdapter/NpgsqlLoggingProviderAdapter.cs index c4f76d7..efe82a3 100644 --- a/src/Core/Database/LoggingAdapter/NpgsqlLoggingProviderAdapter.cs +++ b/Geekbot.net/Database/LoggingAdapter/NpgsqlLoggingProviderAdapter.cs @@ -1,7 +1,8 @@ -using Geekbot.Core.Logger; +using Geekbot.net.Lib; +using Geekbot.net.Lib.Logger; using Npgsql.Logging; -namespace Geekbot.Core.Database.LoggingAdapter +namespace Geekbot.net.Database.LoggingAdapter { public class NpgsqlLoggingProviderAdapter : INpgsqlLoggingProvider { diff --git a/src/Core/Database/Models/CookiesModel.cs b/Geekbot.net/Database/Models/CookiesModel.cs similarity index 87% rename from src/Core/Database/Models/CookiesModel.cs rename to Geekbot.net/Database/Models/CookiesModel.cs index 6ec71f9..228f3b0 100644 --- a/src/Core/Database/Models/CookiesModel.cs +++ b/Geekbot.net/Database/Models/CookiesModel.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class CookiesModel { diff --git a/src/Core/Database/Models/GlobalsModel.cs b/Geekbot.net/Database/Models/GlobalsModel.cs similarity index 85% rename from src/Core/Database/Models/GlobalsModel.cs rename to Geekbot.net/Database/Models/GlobalsModel.cs index af084f9..63261fc 100644 --- a/src/Core/Database/Models/GlobalsModel.cs +++ b/Geekbot.net/Database/Models/GlobalsModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class GlobalsModel { diff --git a/src/Core/Database/Models/GuildSettingsModel.cs b/Geekbot.net/Database/Models/GuildSettingsModel.cs similarity index 90% rename from src/Core/Database/Models/GuildSettingsModel.cs rename to Geekbot.net/Database/Models/GuildSettingsModel.cs index 80655f7..ebb6dc4 100644 --- a/src/Core/Database/Models/GuildSettingsModel.cs +++ b/Geekbot.net/Database/Models/GuildSettingsModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class GuildSettingsModel { diff --git a/Geekbot.net/Database/Models/GuildsModel.cs b/Geekbot.net/Database/Models/GuildsModel.cs new file mode 100644 index 0000000..b6347a6 --- /dev/null +++ b/Geekbot.net/Database/Models/GuildsModel.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Geekbot.net.Database.Models +{ + public class GuildsModel + { + [Key] + public int Id { get; set; } + + [Required] + public long GuildId { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public long Owner { get; set; } + + public string IconUrl { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/Database/Models/KarmaModel.cs b/Geekbot.net/Database/Models/KarmaModel.cs similarity index 87% rename from src/Core/Database/Models/KarmaModel.cs rename to Geekbot.net/Database/Models/KarmaModel.cs index 2ec64f0..7d5f533 100644 --- a/src/Core/Database/Models/KarmaModel.cs +++ b/Geekbot.net/Database/Models/KarmaModel.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class KarmaModel { diff --git a/src/Core/Database/Models/MessagesModel.cs b/Geekbot.net/Database/Models/MessagesModel.cs similarity index 85% rename from src/Core/Database/Models/MessagesModel.cs rename to Geekbot.net/Database/Models/MessagesModel.cs index 87731d8..31332e8 100644 --- a/src/Core/Database/Models/MessagesModel.cs +++ b/Geekbot.net/Database/Models/MessagesModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class MessagesModel { diff --git a/Geekbot.net/Database/Models/PollModel.cs b/Geekbot.net/Database/Models/PollModel.cs new file mode 100644 index 0000000..3b12410 --- /dev/null +++ b/Geekbot.net/Database/Models/PollModel.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Geekbot.net.Database.Models +{ + public class PollModel + { + [Key] + public int Id { get; set; } + + [Required] + public long GuildId { get; set; } + + [Required] + public long ChannelId { get; set; } + + public string Question { get; set; } + + public long Creator { get; set; } + + public long MessageId { get; set; } + + public List Options { get; set; } + + public bool IsFinshed { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Database/Models/PollQuestionModel.cs b/Geekbot.net/Database/Models/PollQuestionModel.cs new file mode 100644 index 0000000..e251fe2 --- /dev/null +++ b/Geekbot.net/Database/Models/PollQuestionModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Geekbot.net.Database.Models +{ + public class PollQuestionModel + { + [Key] + public int Id { get; set; } + + public int OptionId { get; set; } + + public string OptionText { get; set; } + + public int Votes { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/Database/Models/QuoteModel.cs b/Geekbot.net/Database/Models/QuoteModel.cs similarity index 93% rename from src/Core/Database/Models/QuoteModel.cs rename to Geekbot.net/Database/Models/QuoteModel.cs index 3d356d2..98e8b21 100644 --- a/src/Core/Database/Models/QuoteModel.cs +++ b/Geekbot.net/Database/Models/QuoteModel.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class QuoteModel { diff --git a/src/Core/Database/Models/RoleSelfServiceModel.cs b/Geekbot.net/Database/Models/RoleSelfServiceModel.cs similarity index 85% rename from src/Core/Database/Models/RoleSelfServiceModel.cs rename to Geekbot.net/Database/Models/RoleSelfServiceModel.cs index 4aa92c4..de8c341 100644 --- a/src/Core/Database/Models/RoleSelfServiceModel.cs +++ b/Geekbot.net/Database/Models/RoleSelfServiceModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class RoleSelfServiceModel { diff --git a/src/Core/Database/Models/RollsModel.cs b/Geekbot.net/Database/Models/RollsModel.cs similarity index 85% rename from src/Core/Database/Models/RollsModel.cs rename to Geekbot.net/Database/Models/RollsModel.cs index aa6613d..de9b82e 100644 --- a/src/Core/Database/Models/RollsModel.cs +++ b/Geekbot.net/Database/Models/RollsModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class RollsModel { diff --git a/src/Core/Database/Models/ShipsModel.cs b/Geekbot.net/Database/Models/ShipsModel.cs similarity index 84% rename from src/Core/Database/Models/ShipsModel.cs rename to Geekbot.net/Database/Models/ShipsModel.cs index 23cf367..d8156b0 100644 --- a/src/Core/Database/Models/ShipsModel.cs +++ b/Geekbot.net/Database/Models/ShipsModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class ShipsModel { diff --git a/src/Core/Database/Models/SlapsModel.cs b/Geekbot.net/Database/Models/SlapsModel.cs similarity index 86% rename from src/Core/Database/Models/SlapsModel.cs rename to Geekbot.net/Database/Models/SlapsModel.cs index 09026c8..6c402aa 100644 --- a/src/Core/Database/Models/SlapsModel.cs +++ b/Geekbot.net/Database/Models/SlapsModel.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class SlapsModel { diff --git a/src/Core/Database/Models/UserModel.cs b/Geekbot.net/Database/Models/UserModel.cs similarity index 76% rename from src/Core/Database/Models/UserModel.cs rename to Geekbot.net/Database/Models/UserModel.cs index aa6281d..b9c60ca 100644 --- a/src/Core/Database/Models/UserModel.cs +++ b/Geekbot.net/Database/Models/UserModel.cs @@ -1,7 +1,8 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace Geekbot.Core.Database.Models +namespace Geekbot.net.Database.Models { public class UserModel { @@ -23,5 +24,7 @@ namespace Geekbot.Core.Database.Models public bool IsBot { get; set; } public DateTimeOffset Joined { get; set; } + + public List UsedNames { get; set; } } } \ No newline at end of file diff --git a/Geekbot.net/Database/Models/UserUsedNamesModel.cs b/Geekbot.net/Database/Models/UserUsedNamesModel.cs new file mode 100644 index 0000000..48ba873 --- /dev/null +++ b/Geekbot.net/Database/Models/UserUsedNamesModel.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Geekbot.net.Database.Models +{ + public class UserUsedNamesModel + { + [Key] + public int Id { get; set; } + + public string Name { get; set; } + + public DateTimeOffset FirstSeen { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Database/SqlConnectionString.cs b/Geekbot.net/Database/SqlConnectionString.cs new file mode 100644 index 0000000..3228a05 --- /dev/null +++ b/Geekbot.net/Database/SqlConnectionString.cs @@ -0,0 +1,16 @@ +namespace Geekbot.net.Database +{ + public class SqlConnectionString + { + public string Host { get; set; } + public string Port { get; set; } + public string Database { get; set; } + public string Username { get; set; } + public string Password { get; set; } + + public override string ToString() + { + return $"Server={Host};Port={Port};Database={Database};Uid={Username};Pwd={Password};"; + } + } +} \ No newline at end of file diff --git a/src/Core/Database/SqlDatabase.cs b/Geekbot.net/Database/SqlDatabase.cs similarity index 90% rename from src/Core/Database/SqlDatabase.cs rename to Geekbot.net/Database/SqlDatabase.cs index 8a4d195..e6d03d4 100644 --- a/src/Core/Database/SqlDatabase.cs +++ b/Geekbot.net/Database/SqlDatabase.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; -namespace Geekbot.Core.Database +namespace Geekbot.net.Database { public class SqlDatabase : DatabaseContext { diff --git a/Geekbot.net/Geekbot.net.csproj b/Geekbot.net/Geekbot.net.csproj new file mode 100755 index 0000000..4749374 --- /dev/null +++ b/Geekbot.net/Geekbot.net.csproj @@ -0,0 +1,104 @@ + + + Exe + netcoreapp2.2 + win-x64;linux-x64 + derp.ico + 4.1.0 + $(VersionSuffix) + $(Version)-$(VersionSuffix) + $(Version)-DEV + Pizza and Coffee Studios + Pizza and Coffee Studios + A Discord bot + https://github.com/pizzaandcoffee/Geekbot.net + NU1701 + git + https://geekbot.pizzaandcoffee.rocks + + + true + + + + + 2.1.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4.3.0 + + + 4.3.0 + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + PreserveNewest + + + Always + + + + + + \ No newline at end of file diff --git a/Geekbot.net/Handlers.cs b/Geekbot.net/Handlers.cs new file mode 100644 index 0000000..79390cc --- /dev/null +++ b/Geekbot.net/Handlers.cs @@ -0,0 +1,268 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.Rest; +using Discord.WebSocket; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib.AlmostRedis; +using Geekbot.net.Lib.Context; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.Logger; +using Geekbot.net.Lib.ReactionListener; +using Geekbot.net.Lib.UserRepository; +using Microsoft.EntityFrameworkCore; + +namespace Geekbot.net +{ + public class Handlers + { + private readonly DatabaseContext _database; + private readonly IDiscordClient _client; + private readonly IGeekbotLogger _logger; + private readonly IAlmostRedis _redis; + private readonly IServiceProvider _servicesProvider; + private readonly CommandService _commands; + private readonly IUserRepository _userRepository; + private readonly IReactionListener _reactionListener; + private readonly ITranslationHandler _translationHandler; + private readonly DatabaseContext _messageCounterDatabaseContext; + + public Handlers(DatabaseInitializer databaseInitializer, IDiscordClient client, IGeekbotLogger logger, IAlmostRedis redis, + IServiceProvider servicesProvider, CommandService commands, IUserRepository userRepository, + IReactionListener reactionListener, ITranslationHandler translationHandler) + { + _database = databaseInitializer.Initialize(); + _messageCounterDatabaseContext = databaseInitializer.Initialize(); + _client = client; + _logger = logger; + _redis = redis; + _servicesProvider = servicesProvider; + _commands = commands; + _userRepository = userRepository; + _reactionListener = reactionListener; + _translationHandler = translationHandler; + } + + // + // Incoming Messages + // + + public Task RunCommand(SocketMessage messageParam) + { + try + { + if (!(messageParam is SocketUserMessage message)) return Task.CompletedTask; + if (message.Author.IsBot) return Task.CompletedTask; + var argPos = 0; + + var lowCaseMsg = message.ToString().ToLower(); + if (lowCaseMsg.StartsWith("hui")) + { + var hasPing = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId.Equals(((SocketGuildChannel) message.Channel).Guild.Id.AsLong()))?.Hui ?? false; + if (hasPing) + { + message.Channel.SendMessageAsync("hui!!!"); + return Task.CompletedTask; + } + } + + if (lowCaseMsg.StartsWith("ping ") || lowCaseMsg.Equals("ping")) + { + var hasPing = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId.Equals(((SocketGuildChannel) message.Channel).Guild.Id.AsLong()))?.Ping ?? false; + if (hasPing) + { + message.Channel.SendMessageAsync("pong"); + return Task.CompletedTask; + } + } + + if (!(message.HasCharPrefix('!', ref argPos) || + message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return Task.CompletedTask; + var context = new GeekbotContext(_client, message, _translationHandler); + var commandExec = _commands.ExecuteAsync(context, argPos, _servicesProvider); + _logger.Information(LogSource.Command, + context.Message.Content.Split(" ")[0].Replace("!", ""), + SimpleConextConverter.ConvertContext(context)); + return Task.CompletedTask; + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Failed to Process Message", e); + return Task.CompletedTask; + } + } + + public async Task UpdateStats(SocketMessage message) + { + try + { + if (message == null) return; + if (message.Channel.Name.StartsWith('@')) + { + _logger.Information(LogSource.Message, $"[DM-Channel] {message.Content}", SimpleConextConverter.ConvertSocketMessage(message, true)); + return; + } + + var channel = (SocketGuildChannel) message.Channel; + + // just testing, redis will remain the source of truth for now + var rowId = await _messageCounterDatabaseContext.Database.ExecuteSqlCommandAsync( + "UPDATE \"Messages\" SET \"MessageCount\" = \"MessageCount\" + 1 WHERE \"GuildId\" = {0} AND \"UserId\" = {1}", + channel.Guild.Id.AsLong(), + message.Author.Id.AsLong() + ); + + if (rowId == 0) + { + _messageCounterDatabaseContext.Messages.Add(new MessagesModel + { + UserId = message.Author.Id.AsLong(), + GuildId = channel.Guild.Id.AsLong(), + MessageCount = 1 + }); + _messageCounterDatabaseContext.SaveChanges(); + } + + await _redis.Db.HashIncrementAsync($"{channel.Guild.Id}:Messages", message.Author.Id.ToString()); + await _redis.Db.HashIncrementAsync($"{channel.Guild.Id}:Messages", 0.ToString()); + + if (message.Author.IsBot) return; + _logger.Information(LogSource.Message, message.Content, SimpleConextConverter.ConvertSocketMessage(message)); + } + catch (Exception e) + { + _logger.Error(LogSource.Message, "Could not process message stats", e); + } + } + + // + // User Stuff + // + + public async Task UserJoined(SocketGuildUser user) + { + try + { + var userRepoUpdate = _userRepository.Update(user); + _logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})"); + + if (!user.IsBot) + { + var guildSettings = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId == user.Guild.Id.AsLong()); + var message = guildSettings?.WelcomeMessage; + if (string.IsNullOrEmpty(message)) return; + message = message.Replace("$user", user.Mention); + + var fallbackSender = new Func>(() => user.Guild.DefaultChannel.SendMessageAsync(message)); + if (guildSettings.WelcomeChannel != 0) + { + try + { + var target = await _client.GetChannelAsync(guildSettings.WelcomeChannel.AsUlong()); + var channel = target as ISocketMessageChannel; + await channel.SendMessageAsync(message); + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Failed to send welcome message to user defined welcome channel", e); + await fallbackSender(); + } + } + else + { + await fallbackSender(); + } + } + + await userRepoUpdate; + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Failed to send welcome message", e); + } + } + + public async Task UserUpdated(SocketUser oldUser, SocketUser newUser) + { + await _userRepository.Update(newUser); + } + + public async Task UserLeft(SocketGuildUser user) + { + try + { + var guild = _database.GuildSettings.FirstOrDefault(g => + g.GuildId.Equals(user.Guild.Id.AsLong())); + if (guild?.ShowLeave ?? false) + { + var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong()); + await modChannelSocket.SendMessageAsync($"{user.Username}#{user.Discriminator} left the server"); + } + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Failed to send leave message", e); + } + + _logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})"); + } + + // + // Message Stuff + // + + public async Task MessageDeleted(Cacheable message, ISocketMessageChannel channel) + { + try + { + var guildSocketData = ((IGuildChannel) channel).Guild; + var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildSocketData.Id.AsLong())); + if ((guild?.ShowDelete ?? false) && guild?.ModChannel != 0) + { + var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong()); + var sb = new StringBuilder(); + if (message.Value != null) + { + sb.AppendLine($"The following message from {message.Value.Author.Username}#{message.Value.Author.Discriminator} was deleted in <#{channel.Id}>"); + sb.AppendLine(message.Value.Content); + } + else + { + sb.AppendLine("Someone deleted a message, the message was not cached..."); + } + + await modChannelSocket.SendMessageAsync(sb.ToString()); + } + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Failed to send delete message...", e); + } + } + + // + // Reactions + // + + public Task ReactionAdded(Cacheable cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction) + { + if (reaction.User.Value.IsBot) return Task.CompletedTask; + if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask; + _reactionListener.GiveRole(socketMessageChannel, reaction); + return Task.CompletedTask; + } + + public Task ReactionRemoved(Cacheable cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction) + { + if (reaction.User.Value.IsBot) return Task.CompletedTask; + if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask; + _reactionListener.RemoveRole(socketMessageChannel, reaction); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/AlmostRedis/AlmostRedis.cs b/Geekbot.net/Lib/AlmostRedis/AlmostRedis.cs new file mode 100644 index 0000000..c6faf52 --- /dev/null +++ b/Geekbot.net/Lib/AlmostRedis/AlmostRedis.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Geekbot.net.Lib.Logger; +using StackExchange.Redis; + +namespace Geekbot.net.Lib.AlmostRedis +{ + // if anyone ever sees this, please come up with a better fucking name, i'd appriciate it + public class AlmostRedis : IAlmostRedis + { + private readonly GeekbotLogger _logger; + private readonly RunParameters _runParameters; + + public AlmostRedis(GeekbotLogger logger, RunParameters runParameters) + { + _logger = logger; + _runParameters = runParameters; + } + + public void Connect() + { + Connection = ConnectionMultiplexer.Connect($"{_runParameters.RedisHost}:{_runParameters.RedisPort}"); + Db = Connection.GetDatabase(int.Parse(_runParameters.RedisDatabase)); + _logger.Information(LogSource.Redis, $"Connected to Redis on {Connection.Configuration} at {Db.Database}"); + } + + public IDatabase Db { get; private set; } + + public ConnectionMultiplexer Connection { get; private set; } + + public IEnumerable GetAllKeys() + { + return Connection.GetServer($"{_runParameters.RedisHost}:{_runParameters.RedisPort}").Keys(int.Parse(_runParameters.RedisDatabase)); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/AlmostRedis/IAlmostRedis.cs b/Geekbot.net/Lib/AlmostRedis/IAlmostRedis.cs new file mode 100644 index 0000000..549c49e --- /dev/null +++ b/Geekbot.net/Lib/AlmostRedis/IAlmostRedis.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using StackExchange.Redis; + +namespace Geekbot.net.Lib.AlmostRedis +{ + public interface IAlmostRedis + { + void Connect(); + IDatabase Db { get; } + ConnectionMultiplexer Connection { get; } + IEnumerable GetAllKeys(); + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Clients/IMalClient.cs b/Geekbot.net/Lib/Clients/IMalClient.cs new file mode 100644 index 0000000..f59c511 --- /dev/null +++ b/Geekbot.net/Lib/Clients/IMalClient.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using MyAnimeListSharp.Core; + +namespace Geekbot.net.Lib.Clients +{ + public interface IMalClient + { + bool IsLoggedIn(); + Task GetAnime(string query); + Task GetManga(string query); + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Clients/MalClient.cs b/Geekbot.net/Lib/Clients/MalClient.cs new file mode 100644 index 0000000..a014cf1 --- /dev/null +++ b/Geekbot.net/Lib/Clients/MalClient.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Geekbot.net.Lib.GlobalSettings; +using Geekbot.net.Lib.Logger; +using MyAnimeListSharp.Auth; +using MyAnimeListSharp.Core; +using MyAnimeListSharp.Facade.Async; + +namespace Geekbot.net.Lib.Clients +{ + public class MalClient : IMalClient + { + private readonly IGlobalSettings _globalSettings; + private readonly IGeekbotLogger _logger; + private ICredentialContext _credentials; + private AnimeSearchMethodsAsync _animeSearch; + private MangaSearchMethodsAsync _mangaSearch; + + public MalClient(IGlobalSettings globalSettings, IGeekbotLogger logger) + { + _globalSettings = globalSettings; + _logger = logger; + ReloadClient(); + } + + public bool ReloadClient() + { + var malCredentials = _globalSettings.GetKey("MalCredentials"); + if (!string.IsNullOrEmpty(malCredentials)) + { + var credSplit = malCredentials.Split('|'); + _credentials = new CredentialContext() + { + UserName = credSplit[0], + Password = credSplit[1] + }; + _animeSearch = new AnimeSearchMethodsAsync(_credentials); + _mangaSearch = new MangaSearchMethodsAsync(_credentials); + _logger.Debug(LogSource.Geekbot, "Logged in to MAL"); + return true; + } + _logger.Debug(LogSource.Geekbot, "No MAL Credentials Set!"); + return false; + + } + + public bool IsLoggedIn() + { + return _credentials != null; + } + + public async Task GetAnime(string query) + { + var response = await _animeSearch.SearchDeserializedAsync(query); + return response.Entries.Count == 0 ? null : response.Entries[0]; + } + + public async Task GetManga(string query) + { + var response = await _mangaSearch.SearchDeserializedAsync(query); + return response.Entries.Count == 0 ? null : response.Entries[0]; + } + } +} \ No newline at end of file diff --git a/src/Bot/CommandPreconditions/DisableInDirectMessageAttribute.cs b/Geekbot.net/Lib/CommandPreconditions/DisableInDirectMessageAttribute.cs similarity index 86% rename from src/Bot/CommandPreconditions/DisableInDirectMessageAttribute.cs rename to Geekbot.net/Lib/CommandPreconditions/DisableInDirectMessageAttribute.cs index b75f4a1..110b69b 100644 --- a/src/Bot/CommandPreconditions/DisableInDirectMessageAttribute.cs +++ b/Geekbot.net/Lib/CommandPreconditions/DisableInDirectMessageAttribute.cs @@ -2,9 +2,9 @@ using System; using System.Threading.Tasks; using Discord.Commands; -namespace Geekbot.Bot.CommandPreconditions +namespace Geekbot.net.Lib.CommandPreconditions { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class DisableInDirectMessageAttribute : PreconditionAttribute { public override Task CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) diff --git a/src/Core/Constants.cs b/Geekbot.net/Lib/Constants.cs similarity index 72% rename from src/Core/Constants.cs rename to Geekbot.net/Lib/Constants.cs index 4dd08b9..4abee4e 100644 --- a/src/Core/Constants.cs +++ b/Geekbot.net/Lib/Constants.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace Geekbot.Core +namespace Geekbot.net.Lib { public static class Constants { @@ -8,7 +8,7 @@ namespace Geekbot.Core public static string BotVersion() { - return typeof(Constants).Assembly.GetCustomAttribute().InformationalVersion; + return typeof(Program).Assembly.GetCustomAttribute().InformationalVersion; } public static string LibraryVersion() diff --git a/Geekbot.net/Lib/Context/GeekbotContext.cs b/Geekbot.net/Lib/Context/GeekbotContext.cs new file mode 100644 index 0000000..dfc4281 --- /dev/null +++ b/Geekbot.net/Lib/Context/GeekbotContext.cs @@ -0,0 +1,56 @@ +using Discord; +using Geekbot.net.Lib.Localization; + +namespace Geekbot.net.Lib.Context +{ + /// The context of a command which may contain the client, user, guild, channel, and message. + public class GeekbotContext : IGeekbotContext + { + /// + public IDiscordClient Client { get; } + + /// + public IGuild Guild { get; } + + /// + public IMessageChannel Channel { get; } + + /// + public IUser User { get; } + + /// + public IUserMessage Message { get; } + + /// + public IGuildUser GuildUser { get; } + + /// + public TranslationGuildContext Translations { get; } + + /// Indicates whether the channel that the command is executed in is a private channel. + public bool IsPrivate + { + get + { + return this.Channel is IPrivateChannel; + } + } + + /// + /// Initializes a new class with the provided client and message. + /// + /// The underlying client. + /// The underlying message. + /// the translation handler + public GeekbotContext(IDiscordClient client, IUserMessage msg, ITranslationHandler translationHandler) + { + this.Client = client; + this.Guild = (msg.Channel as IGuildChannel)?.Guild; + this.Channel = msg.Channel; + this.User = msg.Author; + this.GuildUser = msg.Author as IGuildUser; + this.Message = msg; + this.Translations = translationHandler.GetGuildContext(this.Guild, this.Message).Result; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Context/IGeekbotContext.cs b/Geekbot.net/Lib/Context/IGeekbotContext.cs new file mode 100644 index 0000000..61884e1 --- /dev/null +++ b/Geekbot.net/Lib/Context/IGeekbotContext.cs @@ -0,0 +1,19 @@ +using Discord; +using Discord.Commands; +using Geekbot.net.Lib.Localization; + +namespace Geekbot.net.Lib.Context +{ + public interface IGeekbotContext : ICommandContext + { + /// + /// Gets the who executed the command. + /// + IGuildUser GuildUser { get; } + + /// + /// Gets the containing the necessary tools for command localization. + /// + TranslationGuildContext Translations { get; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Converters/EmojiConverter.cs b/Geekbot.net/Lib/Converters/EmojiConverter.cs new file mode 100644 index 0000000..06f1aa8 --- /dev/null +++ b/Geekbot.net/Lib/Converters/EmojiConverter.cs @@ -0,0 +1,93 @@ +using System.Collections; +using System.Text; + +namespace Geekbot.net.Lib.Converters +{ + public class EmojiConverter : IEmojiConverter + { + public string NumberToEmoji(int number) + { + if (number == 10) + { + return "🔟"; + } + var emojiMap = new[] + { + ":zero:", + ":one:", + ":two:", + ":three:", + ":four:", + ":five:", + ":six:", + ":seven:", + ":eight:", + ":nine:" + }; + var numbers = number.ToString().ToCharArray(); + var returnString = new StringBuilder(); + foreach (var n in numbers) + { + returnString.Append(emojiMap[int.Parse(n.ToString())]); + } + return returnString.ToString(); + } + + public string TextToEmoji(string text) + { + var emojiMap = new Hashtable + { + ['A'] = ":regional_indicator_a: ", + ['B'] = ":b: ", + ['C'] = ":regional_indicator_c: ", + ['D'] = ":regional_indicator_d: ", + ['E'] = ":regional_indicator_e: ", + ['F'] = ":regional_indicator_f: ", + ['G'] = ":regional_indicator_g: ", + ['H'] = ":regional_indicator_h: ", + ['I'] = ":regional_indicator_i: ", + ['J'] = ":regional_indicator_j: ", + ['K'] = ":regional_indicator_k: ", + ['L'] = ":regional_indicator_l: ", + ['M'] = ":regional_indicator_m: ", + ['N'] = ":regional_indicator_n: ", + ['O'] = ":regional_indicator_o: ", + ['P'] = ":regional_indicator_p: ", + ['Q'] = ":regional_indicator_q: ", + ['R'] = ":regional_indicator_r: ", + ['S'] = ":regional_indicator_s: ", + ['T'] = ":regional_indicator_t: ", + ['U'] = ":regional_indicator_u: ", + ['V'] = ":regional_indicator_v: ", + ['W'] = ":regional_indicator_w: ", + ['X'] = ":regional_indicator_x: ", + ['Y'] = ":regional_indicator_y: ", + ['Z'] = ":regional_indicator_z: ", + ['!'] = ":exclamation: ", + ['?'] = ":question: ", + ['#'] = ":hash: ", + ['*'] = ":star2: ", + ['+'] = ":heavy_plus_sign: ", + ['0'] = ":zero: ", + ['1'] = ":one: ", + ['2'] = ":two: ", + ['3'] = ":three: ", + ['4'] = ":four: ", + ['5'] = ":five: ", + ['6'] = ":six: ", + ['7'] = ":seven: ", + ['8'] = ":eight: ", + ['9'] = ":nine: ", + [' '] = " " + }; + var letters = text.ToUpper().ToCharArray(); + var returnString = new StringBuilder(); + foreach (var n in letters) + { + var emoji = emojiMap[n] ?? n; + returnString.Append(emoji); + } + return returnString.ToString(); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Converters/IEmojiConverter.cs b/Geekbot.net/Lib/Converters/IEmojiConverter.cs new file mode 100644 index 0000000..b0f666f --- /dev/null +++ b/Geekbot.net/Lib/Converters/IEmojiConverter.cs @@ -0,0 +1,8 @@ +namespace Geekbot.net.Lib.Converters +{ + public interface IEmojiConverter + { + string NumberToEmoji(int number); + string TextToEmoji(string text); + } +} \ No newline at end of file diff --git a/src/Core/Converters/IMtgManaConverter.cs b/Geekbot.net/Lib/Converters/IMtgManaConverter.cs similarity index 67% rename from src/Core/Converters/IMtgManaConverter.cs rename to Geekbot.net/Lib/Converters/IMtgManaConverter.cs index 0dd3034..d558f09 100644 --- a/src/Core/Converters/IMtgManaConverter.cs +++ b/Geekbot.net/Lib/Converters/IMtgManaConverter.cs @@ -1,4 +1,4 @@ -namespace Geekbot.Core.Converters +namespace Geekbot.net.Lib.Converters { public interface IMtgManaConverter { diff --git a/Geekbot.net/Lib/Converters/MtgManaConverter.cs b/Geekbot.net/Lib/Converters/MtgManaConverter.cs new file mode 100644 index 0000000..c29e1e0 --- /dev/null +++ b/Geekbot.net/Lib/Converters/MtgManaConverter.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using Utf8Json; + +namespace Geekbot.net.Lib.Converters +{ + public class MtgManaConverter : IMtgManaConverter + { + private Dictionary _manaDict; + + public MtgManaConverter() + { + // these emotes can be found at https://discord.gg/bz8HyA7 + var mtgEmojis = File.ReadAllText(Path.GetFullPath("./Lib/Converters/MtgManaEmojis.json")); + _manaDict = JsonSerializer.Deserialize>(mtgEmojis); + } + + public string ConvertMana(string mana) + { + var rgx = Regex.Matches(mana, @"(\{(.*?)\})"); + foreach (Match manaTypes in rgx) + { + var m = _manaDict.GetValueOrDefault(manaTypes.Value); + if (!string.IsNullOrEmpty(m)) + { + mana = mana.Replace(manaTypes.Value, m); + } + } + return mana; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Converters/MtgManaEmojis.json b/Geekbot.net/Lib/Converters/MtgManaEmojis.json new file mode 100644 index 0000000..8ebe75b --- /dev/null +++ b/Geekbot.net/Lib/Converters/MtgManaEmojis.json @@ -0,0 +1,50 @@ +{ + "{0}": "<:mtg_0:415216130043412482>", + "{1}": "<:mtg_1:415216130253389835>", + "{2}": "<:mtg_2:415216130031091713>", + "{3}": "<:mtg_3:415216130467037194>", + "{4}": "<:mtg_4:415216130026635295>", + "{5}": "<:mtg_5:415216130492203008>", + "{6}": "<:mtg_6:415216130458779658>", + "{7}": "<:mtg_7:415216130190475265>", + "{8}": "<:mtg_8:415216130517630986>", + "{9}": "<:mtg_9:415216130500722689>", + "{10": "<:mtg_10:415216130450391051>", + "{11}": "<:mtg_11:415216130811101185>", + "{12}": "<:mtg_12:415216130525888532>", + "{13}": "<:mtg_13:415216130517631000>", + "{14}": "<:mtg_14:415216130165178370>", + "{15}": "<:mtg_15:415216130576089108>", + "{16}": "<:mtg_16:415216130358247425>", + "{17}": "<:mtg_17:415216130601517056>", + "{18}": "<:mtg_18:415216130462842891>", + "{19}": "<:mtg_19:415216130614099988>", + "{20}": "<:mtg_20:415216130656043038>", + "{W}": "<:mtg_white:415216131515744256>", + "{U}": "<:mtg_blue:415216130521694209>", + "{B}": "<:mtg_black:415216130873884683>", + "{R}": "<:mtg_red:415216131322806272>", + "{G}": "<:mtg_green:415216131180331009>", + "{S}": "<:mtg_s:415216131293446144>", + "{T}": "<:mtg_tap:415258392727257088>", + "{C}": "<:mtg_colorless:415216130706374666>", + "{2/W}": "<:mtg_2w:415216130446065664>", + "{2/U}": "<:mtg_2u:415216130429550592>", + "{2/B}": "<:mtg_2b:415216130160984065>", + "{2/R}": "<:mtg_2r:415216130454716436>", + "{2/G}": "<:mtg_2g:415216130420899840>", + "{W/U}": "<:mtg_wu:415216130970484736>", + "{W/B}": "<:mtg_wb:415216131222011914>", + "{U/R}": "<:mtg_ur:415216130962096128>", + "{U/B}": "<:mtg_ub:415216130865758218>", + "{R/W}": "<:mtg_rw:415216130878210057>", + "{G/W}": "<:mtg_gw:415216130567962646>", + "{G/U}": "<:mtg_gu:415216130739666945>", + "{B/R}": "<:mtg_br:415216130580283394>", + "{B/G}": "<:mtg_bg:415216130781609994>", + "{U/P}": "<:mtg_up:415216130861432842>", + "{R/P}": "<:mtg_rp:415216130597322783>", + "{G/P}": "<:mtg_gp:415216130760769546>", + "{W/P}": "<:mtg_wp:415216131541041172>", + "{B/P}": "<:mtg_bp:415216130664169482>" +} \ No newline at end of file diff --git a/src/Core/ErrorHandling/ErrorHandler.cs b/Geekbot.net/Lib/ErrorHandling/ErrorHandler.cs similarity index 50% rename from src/Core/ErrorHandling/ErrorHandler.cs rename to Geekbot.net/Lib/ErrorHandling/ErrorHandler.cs index 0b55bd8..5323ee6 100644 --- a/src/Core/ErrorHandling/ErrorHandler.cs +++ b/Geekbot.net/Lib/ErrorHandling/ErrorHandler.cs @@ -1,32 +1,47 @@ using System; +using System.Net; using System.Threading.Tasks; using Discord.Commands; -using Geekbot.Core.Logger; -using Sentry; +using Discord.Net; +using Geekbot.net.Lib.GlobalSettings; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.Logger; +using SharpRaven; +using SharpRaven.Data; using Exception = System.Exception; -namespace Geekbot.Core.ErrorHandling +namespace Geekbot.net.Lib.ErrorHandling { public class ErrorHandler : IErrorHandler { private readonly IGeekbotLogger _logger; - private readonly Func _getDefaultErrorText; + private readonly ITranslationHandler _translation; + private readonly IRavenClient _raven; private readonly bool _errorsInChat; - public ErrorHandler(IGeekbotLogger logger, RunParameters runParameters, Func getDefaultErrorText) + public ErrorHandler(IGeekbotLogger logger, ITranslationHandler translation, IGlobalSettings globalSettings, bool errorsInChat) { _logger = logger; - _getDefaultErrorText = getDefaultErrorText; - _errorsInChat = runParameters.ExposeErrors; + _translation = translation; + _errorsInChat = errorsInChat; + + var sentryDsn = Environment.GetEnvironmentVariable("SENTRY"); + if (!string.IsNullOrEmpty(sentryDsn)) + { + _raven = new RavenClient(sentryDsn) { Release = Constants.BotVersion(), Environment = "Production" }; + _logger.Information(LogSource.Geekbot, $"Command Errors will be logged to Sentry: {sentryDsn}"); + } + else + { + _raven = null; + } } public async Task HandleCommandException(Exception e, ICommandContext context, string errorMessage = "def") { try { - var errorString = errorMessage == "def" - ? _getDefaultErrorText() - : errorMessage; + var errorString = errorMessage == "def" ? await _translation.GetString(context.Guild?.Id ?? 0, "errorHandler", "SomethingWentWrong") : errorMessage; var errorObj = SimpleConextConverter.ConvertContext(context); if (e.Message.Contains("50007")) return; if (e.Message.Contains("50013")) return; @@ -57,32 +72,36 @@ namespace Geekbot.Core.ErrorHandling } catch (Exception ex) { - try - { - await context.Channel.SendMessageAsync("Something went really really wrong here"); - } - finally - { - _logger.Error(LogSource.Geekbot, "Errorception", ex); - } + await context.Channel.SendMessageAsync("Something went really really wrong here"); + _logger.Error(LogSource.Geekbot, "Errorception", ex); + } + } + + public async Task HandleHttpException(HttpException e, ICommandContext context) + { + var errorStrings = await _translation.GetDict(context, "httpErrors"); + switch(e.HttpCode) + { + case HttpStatusCode.Forbidden: + await context.Channel.SendMessageAsync(errorStrings["403"]); + break; } } private void ReportExternal(Exception e, MessageDto errorObj) { - if (!SentrySdk.IsEnabled) return; - + 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 }; - sentryEvent.SetTag("discord_server", errorObj.Guild.Name); - sentryEvent.SetExtra("Channel", errorObj.Channel); - sentryEvent.SetExtra("Guild", errorObj.Guild); - sentryEvent.SetExtra("Message", errorObj.Message); - sentryEvent.SetExtra("User", errorObj.User); - - SentrySdk.CaptureEvent(sentryEvent); + _raven.Capture(sentryEvent); } } } \ No newline at end of file diff --git a/src/Core/ErrorHandling/IErrorHandler.cs b/Geekbot.net/Lib/ErrorHandling/IErrorHandler.cs similarity index 60% rename from src/Core/ErrorHandling/IErrorHandler.cs rename to Geekbot.net/Lib/ErrorHandling/IErrorHandler.cs index d0e1d20..05e0d8e 100644 --- a/src/Core/ErrorHandling/IErrorHandler.cs +++ b/Geekbot.net/Lib/ErrorHandling/IErrorHandler.cs @@ -1,11 +1,13 @@ using System; using System.Threading.Tasks; using Discord.Commands; +using Discord.Net; -namespace Geekbot.Core.ErrorHandling +namespace Geekbot.net.Lib.ErrorHandling { public interface IErrorHandler { Task HandleCommandException(Exception e, ICommandContext context, string errorMessage = "def"); + Task HandleHttpException(HttpException e, ICommandContext context); } } \ No newline at end of file diff --git a/Geekbot.net/Lib/Extensions/DbSetExtensions.cs b/Geekbot.net/Lib/Extensions/DbSetExtensions.cs new file mode 100644 index 0000000..c731fc0 --- /dev/null +++ b/Geekbot.net/Lib/Extensions/DbSetExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace Geekbot.net.Lib.Extensions +{ + public static class DbSetExtensions + { + public static EntityEntry AddIfNotExists(this DbSet dbSet, T entity, Expression> predicate = null) where T : class, new() + { + var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any(); + return !exists ? dbSet.Add(entity) : null; + } + } +} \ No newline at end of file diff --git a/src/Core/Extensions/EmbedBuilderExtensions.cs b/Geekbot.net/Lib/Extensions/EmbedBuilderExtensions.cs similarity index 86% rename from src/Core/Extensions/EmbedBuilderExtensions.cs rename to Geekbot.net/Lib/Extensions/EmbedBuilderExtensions.cs index c546306..246be44 100644 --- a/src/Core/Extensions/EmbedBuilderExtensions.cs +++ b/Geekbot.net/Lib/Extensions/EmbedBuilderExtensions.cs @@ -1,6 +1,6 @@ using Discord; -namespace Geekbot.Core.Extensions +namespace Geekbot.net.Lib.Extensions { public static class EmbedBuilderExtensions { diff --git a/src/Core/Extensions/LongExtensions.cs b/Geekbot.net/Lib/Extensions/LongExtensions.cs similarity index 79% rename from src/Core/Extensions/LongExtensions.cs rename to Geekbot.net/Lib/Extensions/LongExtensions.cs index 087910e..1bcdd9b 100644 --- a/src/Core/Extensions/LongExtensions.cs +++ b/Geekbot.net/Lib/Extensions/LongExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace Geekbot.Core.Extensions +namespace Geekbot.net.Lib.Extensions { public static class LongExtensions { diff --git a/src/Core/Extensions/StringExtensions.cs b/Geekbot.net/Lib/Extensions/StringExtensions.cs similarity index 83% rename from src/Core/Extensions/StringExtensions.cs rename to Geekbot.net/Lib/Extensions/StringExtensions.cs index 9affad1..9ec1dfe 100644 --- a/src/Core/Extensions/StringExtensions.cs +++ b/Geekbot.net/Lib/Extensions/StringExtensions.cs @@ -1,6 +1,6 @@ using System.Linq; -namespace Geekbot.Core.Extensions +namespace Geekbot.net.Lib.Extensions { public static class StringExtensions { diff --git a/src/Core/Extensions/UlongExtensions.cs b/Geekbot.net/Lib/Extensions/UlongExtensions.cs similarity index 79% rename from src/Core/Extensions/UlongExtensions.cs rename to Geekbot.net/Lib/Extensions/UlongExtensions.cs index 295f317..8fa2a67 100644 --- a/src/Core/Extensions/UlongExtensions.cs +++ b/Geekbot.net/Lib/Extensions/UlongExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace Geekbot.Core.Extensions +namespace Geekbot.net.Lib.Extensions { public static class UlongExtensions { diff --git a/Geekbot.net/Lib/GeekbotBase.cs b/Geekbot.net/Lib/GeekbotBase.cs new file mode 100644 index 0000000..e92f212 --- /dev/null +++ b/Geekbot.net/Lib/GeekbotBase.cs @@ -0,0 +1,9 @@ +using Discord.Commands; +using Geekbot.net.Lib.Context; + +namespace Geekbot.net.Lib +{ + public abstract class GeekbotBase : ModuleBase + { + } +} \ No newline at end of file diff --git a/src/Core/GeekbotExitCode.cs b/Geekbot.net/Lib/GeekbotExitCode.cs similarity index 66% rename from src/Core/GeekbotExitCode.cs rename to Geekbot.net/Lib/GeekbotExitCode.cs index 51006e1..d68aa4a 100644 --- a/src/Core/GeekbotExitCode.cs +++ b/Geekbot.net/Lib/GeekbotExitCode.cs @@ -1,6 +1,6 @@ -namespace Geekbot.Core +namespace Geekbot.net.Lib { - public enum GeekbotExitCode + public enum GeekbotExitCode : int { // General Clean = 0, @@ -8,10 +8,9 @@ // Geekbot Internals TranslationsFailed = 201, - KilledByApiCall = 210, // Dependent Services - /* 301 not in use anymore (redis) */ + RedisConnectionFailed = 301, DatabaseConnectionFailed = 302, // Discord Related diff --git a/src/Core/GlobalSettings/GlobalSettings.cs b/Geekbot.net/Lib/GlobalSettings/GlobalSettings.cs similarity index 91% rename from src/Core/GlobalSettings/GlobalSettings.cs rename to Geekbot.net/Lib/GlobalSettings/GlobalSettings.cs index a000f8b..685a1e5 100644 --- a/src/Core/GlobalSettings/GlobalSettings.cs +++ b/Geekbot.net/Lib/GlobalSettings/GlobalSettings.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; -namespace Geekbot.Core.GlobalSettings +namespace Geekbot.net.Lib.GlobalSettings { public class GlobalSettings : IGlobalSettings { diff --git a/src/Core/GlobalSettings/IGlobalSettings.cs b/Geekbot.net/Lib/GlobalSettings/IGlobalSettings.cs similarity index 72% rename from src/Core/GlobalSettings/IGlobalSettings.cs rename to Geekbot.net/Lib/GlobalSettings/IGlobalSettings.cs index 082b3dc..4833215 100644 --- a/src/Core/GlobalSettings/IGlobalSettings.cs +++ b/Geekbot.net/Lib/GlobalSettings/IGlobalSettings.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; -using Geekbot.Core.Database.Models; +using Geekbot.net.Database.Models; -namespace Geekbot.Core.GlobalSettings +namespace Geekbot.net.Lib.GlobalSettings { public interface IGlobalSettings { diff --git a/src/Core/Highscores/HighscoreListEmptyException.cs b/Geekbot.net/Lib/Highscores/HighscoreListEmptyException.cs similarity index 86% rename from src/Core/Highscores/HighscoreListEmptyException.cs rename to Geekbot.net/Lib/Highscores/HighscoreListEmptyException.cs index 5fc08ee..8c05ca9 100644 --- a/src/Core/Highscores/HighscoreListEmptyException.cs +++ b/Geekbot.net/Lib/Highscores/HighscoreListEmptyException.cs @@ -1,6 +1,6 @@ using System; -namespace Geekbot.Core.Highscores +namespace Geekbot.net.Lib.Highscores { public class HighscoreListEmptyException : Exception { diff --git a/src/Core/Highscores/HighscoreManager.cs b/Geekbot.net/Lib/Highscores/HighscoreManager.cs similarity index 61% rename from src/Core/Highscores/HighscoreManager.cs rename to Geekbot.net/Lib/Highscores/HighscoreManager.cs index c839b39..f3b3a18 100644 --- a/src/Core/Highscores/HighscoreManager.cs +++ b/Geekbot.net/Lib/Highscores/HighscoreManager.cs @@ -1,10 +1,12 @@ +using System; using System.Collections.Generic; using System.Linq; -using Geekbot.Core.Database; -using Geekbot.Core.Extensions; -using Geekbot.Core.UserRepository; +using Geekbot.net.Database; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Logger; +using Geekbot.net.Lib.UserRepository; -namespace Geekbot.Core.Highscores +namespace Geekbot.net.Lib.Highscores { public class HighscoreManager : IHighscoreManager { @@ -18,19 +20,28 @@ namespace Geekbot.Core.Highscores } - public Dictionary GetHighscoresWithUserData(HighscoreTypes type, ulong guildId, int amount, string season = null) + public Dictionary GetHighscoresWithUserData(HighscoreTypes type, ulong guildId, int amount) { - var list = type switch + Dictionary list; + switch (type) { - HighscoreTypes.messages => GetMessageList(guildId, amount), - HighscoreTypes.karma => GetKarmaList(guildId, amount), - HighscoreTypes.rolls => GetRollsList(guildId, amount), - HighscoreTypes.cookies => GetCookiesList(guildId, amount), - HighscoreTypes.seasons => GetMessageSeasonList(guildId, amount, season), - HighscoreTypes.quotes => GetQuotesList(guildId, amount), - _ => new Dictionary() - }; - + case HighscoreTypes.messages: + list = GetMessageList(guildId, amount); + break; + case HighscoreTypes.karma: + list = GetKarmaList(guildId, amount); + break; + case HighscoreTypes.rolls: + list = GetRollsList(guildId, amount); + break; + case HighscoreTypes.cookies: + list = GetCookiesList(guildId, amount); + break; + default: + list = new Dictionary(); + break; + } + if (!list.Any()) { throw new HighscoreListEmptyException($"No {type} found for guild {guildId}"); @@ -77,19 +88,6 @@ namespace Geekbot.Core.Highscores .ToDictionary(key => key.UserId.AsUlong(), key => key.MessageCount); } - public Dictionary GetMessageSeasonList(ulong guildId, int amount, string season) - { - if (string.IsNullOrEmpty(season)) - { - season = SeasonsUtils.GetCurrentSeason(); - } - return _database.MessagesSeasons - .Where(k => k.GuildId.Equals(guildId.AsLong()) && k.Season.Equals(season)) - .OrderByDescending(o => o.MessageCount) - .Take(amount) - .ToDictionary(key => key.UserId.AsUlong(), key => key.MessageCount); - } - public Dictionary GetKarmaList(ulong guildId, int amount) { return _database.Karma @@ -107,8 +105,8 @@ namespace Geekbot.Core.Highscores .Take(amount) .ToDictionary(key => key.UserId.AsUlong(), key => key.Rolls); } - - private Dictionary GetCookiesList(ulong guildId, int amount) + + public Dictionary GetCookiesList(ulong guildId, int amount) { return _database.Cookies .Where(k => k.GuildId.Equals(guildId.AsLong())) @@ -116,16 +114,5 @@ namespace Geekbot.Core.Highscores .Take(amount) .ToDictionary(key => key.UserId.AsUlong(), key => key.Cookies); } - - private Dictionary GetQuotesList(ulong guildId, int amount) - { - return _database.Quotes - .Where(row => row.GuildId == guildId.AsLong()) - .GroupBy(row => row.UserId) - .Select(row => new { userId = row.Key, amount = row.Count()}) - .OrderByDescending(row => row.amount) - .Take(amount) - .ToDictionary(key => key.userId.AsUlong(), key => key.amount); - } } } \ No newline at end of file diff --git a/Geekbot.net/Lib/Highscores/HighscoreTypes.cs b/Geekbot.net/Lib/Highscores/HighscoreTypes.cs new file mode 100644 index 0000000..adf5698 --- /dev/null +++ b/Geekbot.net/Lib/Highscores/HighscoreTypes.cs @@ -0,0 +1,10 @@ +namespace Geekbot.net.Lib.Highscores +{ + public enum HighscoreTypes + { + messages, + karma, + rolls, + cookies + } +} \ No newline at end of file diff --git a/src/Core/Highscores/HighscoreUserDto.cs b/Geekbot.net/Lib/Highscores/HighscoreUserDto.cs similarity index 82% rename from src/Core/Highscores/HighscoreUserDto.cs rename to Geekbot.net/Lib/Highscores/HighscoreUserDto.cs index 58e1897..7abb352 100644 --- a/src/Core/Highscores/HighscoreUserDto.cs +++ b/Geekbot.net/Lib/Highscores/HighscoreUserDto.cs @@ -1,4 +1,4 @@ -namespace Geekbot.Core.Highscores +namespace Geekbot.net.Lib.Highscores { public class HighscoreUserDto { diff --git a/src/Core/Highscores/IHighscoreManager.cs b/Geekbot.net/Lib/Highscores/IHighscoreManager.cs similarity index 63% rename from src/Core/Highscores/IHighscoreManager.cs rename to Geekbot.net/Lib/Highscores/IHighscoreManager.cs index 9c2d6f0..a09b07e 100644 --- a/src/Core/Highscores/IHighscoreManager.cs +++ b/Geekbot.net/Lib/Highscores/IHighscoreManager.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace Geekbot.Core.Highscores +namespace Geekbot.net.Lib.Highscores { public interface IHighscoreManager { - Dictionary GetHighscoresWithUserData(HighscoreTypes type, ulong guildId, int amount, string season = null); + Dictionary GetHighscoresWithUserData(HighscoreTypes type, ulong guildId, int amount); Dictionary GetMessageList(ulong guildId, int amount); - Dictionary GetMessageSeasonList(ulong guildId, int amount, string season); Dictionary GetKarmaList(ulong guildId, int amount); Dictionary GetRollsList(ulong guildId, int amount); } diff --git a/src/Core/Levels/ILevelCalc.cs b/Geekbot.net/Lib/Levels/ILevelCalc.cs similarity index 67% rename from src/Core/Levels/ILevelCalc.cs rename to Geekbot.net/Lib/Levels/ILevelCalc.cs index 17413e6..cc488e7 100644 --- a/src/Core/Levels/ILevelCalc.cs +++ b/Geekbot.net/Lib/Levels/ILevelCalc.cs @@ -1,4 +1,4 @@ -namespace Geekbot.Core.Levels +namespace Geekbot.net.Lib.Levels { public interface ILevelCalc { diff --git a/src/Core/Levels/LevelCalc.cs b/Geekbot.net/Lib/Levels/LevelCalc.cs similarity index 66% rename from src/Core/Levels/LevelCalc.cs rename to Geekbot.net/Lib/Levels/LevelCalc.cs index 8203f97..f5b6b80 100644 --- a/src/Core/Levels/LevelCalc.cs +++ b/Geekbot.net/Lib/Levels/LevelCalc.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; -using System.Linq; -namespace Geekbot.Core.Levels +namespace Geekbot.net.Lib.Levels { public class LevelCalc : ILevelCalc { - private readonly int[] _levels; + private int[] _levels; public LevelCalc() { @@ -22,7 +21,13 @@ namespace Geekbot.Core.Levels public int GetLevel(int? messages) { - return 1 + _levels.TakeWhile(level => !(level > messages)).Count(); + var returnVal = 1; + foreach (var level in _levels) + { + if (level > messages) break; + returnVal++; + } + return returnVal; } } } \ No newline at end of file diff --git a/Geekbot.net/Lib/Localization/ITranslationHandler.cs b/Geekbot.net/Lib/Localization/ITranslationHandler.cs new file mode 100644 index 0000000..3672c26 --- /dev/null +++ b/Geekbot.net/Lib/Localization/ITranslationHandler.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; + +namespace Geekbot.net.Lib.Localization +{ + public interface ITranslationHandler + { + Task GetString(ulong guildId, string command, string stringName); + string GetString(string language, string command, string stringName); + Task> GetDict(ICommandContext context, string command); + Task GetGuildContext(ICommandContext context); + Task GetGuildContext(IGuild guild, IUserMessage message); + Task SetLanguage(ulong guildId, string language); + List SupportedLanguages { get; } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Localization/TranslationGuildContext.cs b/Geekbot.net/Lib/Localization/TranslationGuildContext.cs new file mode 100644 index 0000000..6181c04 --- /dev/null +++ b/Geekbot.net/Lib/Localization/TranslationGuildContext.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Geekbot.net.Lib.Localization +{ + public class TranslationGuildContext + { + public ITranslationHandler TranslationHandler { get; } + public string Language { get; } + public Dictionary Dict { get; } + + public TranslationGuildContext(ITranslationHandler translationHandler, string language, Dictionary dict) + { + TranslationHandler = translationHandler; + Language = language; + Dict = dict; + } + + public string GetString(string stringToFormat, params object[] args) + { + return string.Format(Dict[stringToFormat] ?? "", args); + } + + public string FormatDateTimeAsRemaining(DateTimeOffset dateTime) + { + var remaining = dateTime - DateTimeOffset.Now; + const string formattable = "{0} {1}"; + var sb = new StringBuilder(); + + if (remaining.Days > 0) + { + var s = GetTimeString(TimeTypes.Days); + sb.AppendFormat(formattable, remaining.Days, GetSingOrPlur(remaining.Days, s)); + } + + if (remaining.Hours > 0) + { + if (sb.Length > 0) sb.Append(", "); + var s = GetTimeString(TimeTypes.Hours); + sb.AppendFormat(formattable, remaining.Hours, GetSingOrPlur(remaining.Hours, s)); + } + + if (remaining.Minutes > 0) + { + if (sb.Length > 0) sb.Append(", "); + var s = GetTimeString(TimeTypes.Minutes); + sb.AppendFormat(formattable, remaining.Minutes, GetSingOrPlur(remaining.Minutes, s)); + } + + if (remaining.Seconds > 0) + { + if (sb.Length > 0) + { + var and = TranslationHandler.GetString(Language, "dateTime", "And"); + sb.AppendFormat(" {0} ", and); + } + var s = GetTimeString(TimeTypes.Seconds); + sb.AppendFormat(formattable, remaining.Seconds, GetSingOrPlur(remaining.Seconds, s)); + } + + return sb.ToString().Trim(); + } + + public Task SetLanguage(ulong guildId, string language) + { + return TranslationHandler.SetLanguage(guildId, language); + } + + private string GetTimeString(TimeTypes type) + { + return TranslationHandler.GetString(Language, "dateTime", type.ToString()); + } + + private string GetSingOrPlur(int number, string rawString) + { + var versions = rawString.Split('|'); + return number == 1 ? versions[0] : versions[1]; + } + + private enum TimeTypes + { + Days, + Hours, + Minutes, + Seconds + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Localization/TranslationHandler.cs b/Geekbot.net/Lib/Localization/TranslationHandler.cs new file mode 100644 index 0000000..f1a30e5 --- /dev/null +++ b/Geekbot.net/Lib/Localization/TranslationHandler.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Logger; +using YamlDotNet.Serialization; + +namespace Geekbot.net.Lib.Localization +{ + public class TranslationHandler : ITranslationHandler + { + private readonly DatabaseContext _database; + private readonly IGeekbotLogger _logger; + private readonly Dictionary _serverLanguages; + private Dictionary>> _translations; + + public TranslationHandler(DatabaseContext database, IGeekbotLogger logger) + { + _database = database; + _logger = logger; + _logger.Information(LogSource.Geekbot, "Loading Translations"); + LoadTranslations(); + _serverLanguages = new Dictionary(); + } + + private void LoadTranslations() + { + try + { + // Read the file + var translationFile = File.ReadAllText(Path.GetFullPath("./Lib/Localization/Translations.yml")); + + // Deserialize + var input = new StringReader(translationFile); + var deserializer = new DeserializerBuilder().Build(); + var rawTranslations = deserializer.Deserialize>>>(input); + + // Sort + var sortedPerLanguage = new Dictionary>>(); + 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>(); + var strDict = new Dictionary + { + {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 + { + {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; + + // Find Languages + SupportedLanguages = new List(); + foreach (var lang in sortedPerLanguage) + { + SupportedLanguages.Add(lang.Key); + } + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Failed to load Translations", e); + Environment.Exit(GeekbotExitCode.TranslationsFailed.GetHashCode()); + } + } + + private async Task GetServerLanguage(ulong guildId) + { + try + { + string lang; + try + { + lang = _serverLanguages[guildId]; + if (!string.IsNullOrEmpty(lang)) + { + return lang; + } + throw new Exception(); + } + catch + { + lang = (await GetGuild(guildId)).Language ?? "EN"; + _serverLanguages[guildId] = lang; + return lang; + } + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Could not get guild language", e); + return "EN"; + } + } + + public async Task GetString(ulong guildId, string command, string stringName) + { + var serverLang = await GetServerLanguage(guildId); + return GetString(serverLang, command, stringName); + } + + public string GetString(string language, string command, string stringName) + { + var translation = _translations[language][command][stringName]; + if (!string.IsNullOrWhiteSpace(translation)) return translation; + translation = _translations[command][stringName]["EN"]; + if (string.IsNullOrWhiteSpace(translation)) + { + _logger.Warning(LogSource.Geekbot, $"No translation found for {command} - {stringName}"); + } + return translation; + } + + private Task> GetDict(ICommandContext context) + { + return GetDict(context.Guild, context.Message); + } + + private async Task> GetDict(IGuild guild, IUserMessage message) + { + try + { + var command = message.Content.Split(' ').First().TrimStart('!').ToLower(); + var serverLanguage = await GetServerLanguage(guild?.Id ?? 0); + return _translations[serverLanguage][command]; + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "No translations for command found", e); + return new Dictionary(); + } + } + + public async Task GetGuildContext(ICommandContext context) + { + var dict = await GetDict(context); + var language = await GetServerLanguage(context.Guild?.Id ?? 0); + return new TranslationGuildContext(this, language, dict); + } + + public async Task GetGuildContext(IGuild guild, IUserMessage message) + { + var dict = await GetDict(guild, message); + var language = await GetServerLanguage(guild?.Id ?? 0); + return new TranslationGuildContext(this, language, dict); + } + + public async Task> GetDict(ICommandContext context, string command) + { + try + { + var serverLanguage = await GetServerLanguage(context.Guild?.Id ?? 0); + return _translations[serverLanguage][command]; + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "No translations for command found", e); + return new Dictionary(); + } + } + + public async Task SetLanguage(ulong guildId, string language) + { + try + { + if (!SupportedLanguages.Contains(language)) return false; + var guild = await GetGuild(guildId); + guild.Language = language; + _database.GuildSettings.Update(guild); + _serverLanguages[guildId] = language; + return true; + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Error while changing language", e); + return false; + } + } + + public List SupportedLanguages { get; private set; } + + private async Task GetGuild(ulong guildId) + { + var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildId.AsLong())); + if (guild != null) return guild; + _database.GuildSettings.Add(new GuildSettingsModel + { + GuildId = guildId.AsLong() + }); + await _database.SaveChangesAsync(); + return _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildId.AsLong())); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Localization/Translations.yml b/Geekbot.net/Lib/Localization/Translations.yml new file mode 100644 index 0000000..04835a2 --- /dev/null +++ b/Geekbot.net/Lib/Localization/Translations.yml @@ -0,0 +1,175 @@ +--- +dateTime: + Days: + EN: "day|days" + CHDE: "tag|täg" + Hours: + EN: "hour|hours" + CHDE: "stund|stunde" + Minutes: + EN: "minute|minutes" + CHDE: "minute|minute" + Seconds: + EN: "second|seconds" + CHDE: "sekunde|sekunde" + And: + EN: "and" + CHDE: "und" +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" +cookies: + GetCookies: + EN: "You got {0} cookies, there are now {1} cookies in you cookie jar" + CHDE: "Du häsch {0} guetzli becho, du häsch jetzt {1} guetzli ih dr büchse" + WaitForMoreCookies: + EN: "You already got cookies in the last 24 hours, you can have more cookies in {0}" + CHDE: "Du hesch scho guetzli becho ih de letzti 24 stund, du chasch meh ha in {0}" + InYourJar: + EN: "There are {0} cookies in you cookie jar" + CHDE: "Es hät {0} guetzli ih dineri büchs" + Given: + EN: "You gave {0} cookies to {1}" + CHDE: "Du hesch {1} {0} guetzli geh" + NotEnoughToGive: + EN: "You don't have enough cookies" + CHDE: "Du hesch nid gnueg guetzli" + NotEnoughCookiesToEat: + EN: "Your cookie jar looks almost empty, you should probably not eat a cookie" + CHDE: "Du hesch chuum no guetzli ih dineri büchs, du sötsch warschinli keini esse" + AteCookies: + EN: "You ate {0} cookies, you've only got {1} cookies left" + CHDE: "Du hesch {0} guetzli gesse und hesch jezt no {1} übrig" +role: + NoRolesConfigured: + EN: "There are no roles configured for this server" + CHDE: "Es sind kei rolle für dä server konfiguriert" + ListHeader: + EN: "**Self Service Roles on {0}**" + CHDE: "**Self Service Rollene uf {0}**" + ListInstruction: + EN: "To get a role, use `!role [name]`" + CHDE: "Zum ä rolle becho, schriib `!role [name]`" + RoleNotFound: + EN: "That role doesn't exist or is not on the whitelist" + CHDE: "Die rolle gids nid or isch nid uf dr whitelist" + RemovedUserFromRole: + EN: "Removed you from {0}" + CHDE: "Han di entfernt vo {0}" + AddedUserFromRole: + EN: "Added you to {0}" + CHDE: "Han di hinzue gfüegt zu {0}" + CannotAddManagedRole: + EN: "You can't add a role that is managed by discord" + CHDE: "Du chasch kei rolle hinzuefüge wo verwalted wird vo discord" + CannotAddDangerousRole: + EN: "You cannot add that role to self service because it contains one or more dangerous permissions" + CHDE: "Du chasch die rolle nid hinzuefüge will er ein oder mehreri gföhrlichi berechtigunge het" + AddedRoleToWhitelist: + EN: "Added {0} to the whitelist" + CHDE: "{0} isch zur whitelist hinzuegfüegt" + RemovedRoleFromWhitelist: + EN: "Removed {0} from the whitelist" + CHDE: "{0} isch vo dr whitelist glöscht" +quote: + NoQuotesFound: + EN: "This server doesn't seem to have any quotes yet. You can add a quote with `!quote save @user` or `!quote save `" + CHDE: "Dä server het no kei quotes. Du chasch quotes hinzuefüege mit `!quote save @user` oder `!quote save `" + CannotSaveOwnQuotes: + EN: "You can't save your own quotes..." + CHDE: "Du chasch kei quotes vo dir selber speichere..." + CannotQuoteBots: + EN: "You can't save quotes by a bot..." + CHDE: "Du chasch kei quotes vomne bot speichere..." + QuoteAdded: + EN: "**Quote Added**" + CHDE: "**Quote hinzugfüegt**" + Removed: + EN: "**Removed #{0}**" + CHDE: "**#{0} glöscht**" + NotFoundWithId: + EN: "I couldn't find a quote with that ID :disappointed:" + CHDE: "Ich chan kei quote finde mit därri ID :disappointed:" +rank: + InvalidType: + EN: "Valid types are '`messages`' '`karma`', '`rolls`' and '`cookies`'" + CHDE: "Gültigi paramenter sind '`messages`' '`karma`', '`rolls`' und '`cookies`'" + LimitingTo20Warning: + EN: ":warning: Limiting to 20\n" + CHDE: ":warning: Limitiert uf 20\n" + NoTypeFoundForServer: + EN: "No {0} found on this server" + CHDE: "Kei {0} gfunde für dä server" + FailedToResolveAllUsernames: + EN: ":warning: I couldn't find all usernames. Maybe they left the server?\n" + CHDE: ":warning: Ich han nid alli benutzername gfunde. villiicht hend sie de server verlah?\n" + HighscoresFor: + EN: ":bar_chart: **{0} Highscore for {1}**" + CHDE: ":bar_chart: **{0} Highscore für {1}**" \ No newline at end of file diff --git a/src/Core/Logger/Adapters/DiscordLogger.cs b/Geekbot.net/Lib/Logger/DiscordLogger.cs similarity index 94% rename from src/Core/Logger/Adapters/DiscordLogger.cs rename to Geekbot.net/Lib/Logger/DiscordLogger.cs index e9bb02e..688e26b 100644 --- a/src/Core/Logger/Adapters/DiscordLogger.cs +++ b/Geekbot.net/Lib/Logger/DiscordLogger.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Discord; -namespace Geekbot.Core.Logger.Adapters +namespace Geekbot.net.Lib.Logger { public class DiscordLogger : IDiscordLogger { diff --git a/Geekbot.net/Lib/Logger/GeekbotLogger.cs b/Geekbot.net/Lib/Logger/GeekbotLogger.cs new file mode 100644 index 0000000..09a3ecb --- /dev/null +++ b/Geekbot.net/Lib/Logger/GeekbotLogger.cs @@ -0,0 +1,84 @@ +using System; +using Newtonsoft.Json; + +namespace Geekbot.net.Lib.Logger +{ + public class GeekbotLogger : IGeekbotLogger + { + private readonly bool _logAsJson; + private readonly NLog.Logger _logger; + private readonly JsonSerializerSettings _serializerSettings; + + public GeekbotLogger(RunParameters runParameters, bool sumologicActive) + { + _logAsJson = sumologicActive || runParameters.LogJson; + _logger = LoggerFactory.CreateNLog(runParameters, sumologicActive); + _serializerSettings = new JsonSerializerSettings + { + ReferenceLoopHandling = ReferenceLoopHandling.Serialize, + NullValueHandling = NullValueHandling.Include + }; + Information(LogSource.Geekbot, "Using GeekbotLogger"); + } + + public void Trace(LogSource source, string message, object extra = null) + { + _logger.Trace(CreateLogString("Trace", source, message, null, extra)); + } + + public void Debug(LogSource source, string message, object extra = null) + { + if (_logAsJson) _logger.Info(CreateLogString("Debug", source, message, null, extra)); + else _logger.Debug(CreateLogString("Debug", source, message, null, extra)); + } + + public void Information(LogSource source, string message, object extra = null) + { + _logger.Info(CreateLogString("Information", source, message, null, extra)); + } + + public void Warning(LogSource source, string message, Exception stackTrace = null, object extra = null) + { + if (_logAsJson) _logger.Info(CreateLogString("Warning", source, message, stackTrace, extra)); + else _logger.Warn(CreateLogString("Warning", source, message, stackTrace, extra)); + } + + public void Error(LogSource source, string message, Exception stackTrace, object extra = null) + { + if (_logAsJson) _logger.Info(CreateLogString("Error", source, message, stackTrace, extra)); + else _logger.Error(stackTrace, CreateLogString("Error", source, message, stackTrace, extra)); + } + + public NLog.Logger GetNLogger() + { + return _logger; + } + + public bool LogAsJson() + { + return _logAsJson; + } + + private string CreateLogString(string type, LogSource source, string message, Exception stackTrace = null, object extra = null) + { + if (_logAsJson) + { + var logObject = new GeekbotLoggerObject + { + Timestamp = DateTime.Now, + Type = type, + Source = source, + Message = message, + StackTrace = stackTrace, + Extra = extra + }; + return JsonConvert.SerializeObject(logObject, Formatting.None, _serializerSettings); + } + + if (source != LogSource.Message) return $"[{source}] - {message}"; + + var m = (MessageDto) extra; + return $"[{source}] - [{m?.Guild.Name} - {m?.Channel.Name}] {m?.User.Name}: {m?.Message.Content}"; + } + } +} \ No newline at end of file diff --git a/src/Core/Logger/IDiscordLogger.cs b/Geekbot.net/Lib/Logger/IDiscordLogger.cs similarity index 76% rename from src/Core/Logger/IDiscordLogger.cs rename to Geekbot.net/Lib/Logger/IDiscordLogger.cs index c127e0c..fdc8c7f 100644 --- a/src/Core/Logger/IDiscordLogger.cs +++ b/Geekbot.net/Lib/Logger/IDiscordLogger.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Discord; -namespace Geekbot.Core.Logger +namespace Geekbot.net.Lib.Logger { public interface IDiscordLogger { diff --git a/src/Core/Logger/IGeekbotLogger.cs b/Geekbot.net/Lib/Logger/IGeekbotLogger.cs similarity index 92% rename from src/Core/Logger/IGeekbotLogger.cs rename to Geekbot.net/Lib/Logger/IGeekbotLogger.cs index 1363629..944524a 100644 --- a/src/Core/Logger/IGeekbotLogger.cs +++ b/Geekbot.net/Lib/Logger/IGeekbotLogger.cs @@ -1,6 +1,6 @@ using System; -namespace Geekbot.Core.Logger +namespace Geekbot.net.Lib.Logger { public interface IGeekbotLogger { diff --git a/Geekbot.net/Lib/Logger/LogDto.cs b/Geekbot.net/Lib/Logger/LogDto.cs new file mode 100644 index 0000000..8ccfcdf --- /dev/null +++ b/Geekbot.net/Lib/Logger/LogDto.cs @@ -0,0 +1,14 @@ +using System; + +namespace Geekbot.net.Lib.Logger +{ + public class GeekbotLoggerObject + { + public DateTime Timestamp { get; set; } + public string Type { get; set; } + public LogSource Source { get; set; } + public string Message { get; set; } + public Exception StackTrace { get; set; } + public object Extra { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/Logger/LogSource.cs b/Geekbot.net/Lib/Logger/LogSource.cs similarity index 58% rename from src/Core/Logger/LogSource.cs rename to Geekbot.net/Lib/Logger/LogSource.cs index d2baff5..7cd92ff 100644 --- a/src/Core/Logger/LogSource.cs +++ b/Geekbot.net/Lib/Logger/LogSource.cs @@ -1,14 +1,16 @@ -using System.Text.Json.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; -namespace Geekbot.Core.Logger +namespace Geekbot.net.Lib.Logger { - [JsonConverter(typeof(JsonStringEnumConverter))] + [JsonConverter(typeof(StringEnumConverter))] public enum LogSource { Geekbot, Rest, Gateway, Discord, + Redis, Database, Message, UserRepository, @@ -16,7 +18,6 @@ namespace Geekbot.Core.Logger Api, Migration, HighscoreManager, - Interaction, Other } } \ No newline at end of file diff --git a/src/Core/Logger/LoggerFactory.cs b/Geekbot.net/Lib/Logger/LoggerFactory.cs similarity index 57% rename from src/Core/Logger/LoggerFactory.cs rename to Geekbot.net/Lib/Logger/LoggerFactory.cs index bf3d926..b07bcda 100644 --- a/src/Core/Logger/LoggerFactory.cs +++ b/Geekbot.net/Lib/Logger/LoggerFactory.cs @@ -5,46 +5,33 @@ using NLog.Config; using NLog.Targets; using SumoLogic.Logging.NLog; -namespace Geekbot.Core.Logger +namespace Geekbot.net.Lib.Logger { public class LoggerFactory { - public static NLog.Logger CreateNLog(RunParameters runParameters) + public static NLog.Logger CreateNLog(RunParameters runParameters, bool sumologicActive) { var config = new LoggingConfiguration(); - var minLevel = runParameters.Verbose ? LogLevel.Trace : LogLevel.Info; - if (!string.IsNullOrEmpty(runParameters.SumologicEndpoint)) + if (sumologicActive) { Console.WriteLine("Logging Geekbot Logs to Sumologic"); config.LoggingRules.Add( - new LoggingRule("*", minLevel, LogLevel.Fatal, + new LoggingRule("*", LogLevel.Debug, LogLevel.Fatal, new SumoLogicTarget() { - Url = runParameters.SumologicEndpoint, + Url = Environment.GetEnvironmentVariable("GEEKBOT_SUMO"), SourceName = "GeekbotLogger", Layout = "${message}", UseConsoleLog = false, OptimizeBufferReuse = true, Name = "Geekbot" }) - ); - } - else if (runParameters.LogJson) - { - config.LoggingRules.Add( - new LoggingRule("*", minLevel, LogLevel.Fatal, - new ConsoleTarget - { - Name = "Console", - Encoding = Encoding.UTF8, - Layout = "${message}" - } - ) - ); + ); } else { + var minLevel = runParameters.Verbose ? LogLevel.Trace : LogLevel.Info; config.LoggingRules.Add( new LoggingRule("*", minLevel, LogLevel.Fatal, new ColoredConsoleTarget @@ -52,12 +39,27 @@ namespace Geekbot.Core.Logger Name = "Console", Encoding = Encoding.UTF8, Layout = "[${longdate} ${level:format=FirstCharacter}] ${message} ${exception:format=toString}" - } - ) - ); + }) + ); + + config.LoggingRules.Add( + new LoggingRule("*", minLevel, LogLevel.Fatal, + new FileTarget + { + Name = "File", + Layout = "[${longdate} ${level}] ${message}", + Encoding = Encoding.UTF8, + LineEnding = LineEndingMode.Default, + MaxArchiveFiles = 30, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveFileName = "./Logs/Archive/{#####}.log", + FileName = "./Logs/Geekbot.log" + }) + ); } - - var loggerConfig = new LogFactory {Configuration = config}; + + var loggerConfig = new LogFactory { Configuration = config }; return loggerConfig.GetCurrentClassLogger(); } } diff --git a/src/Core/Logger/MessageDto.cs b/Geekbot.net/Lib/Logger/MessageDto.cs similarity index 92% rename from src/Core/Logger/MessageDto.cs rename to Geekbot.net/Lib/Logger/MessageDto.cs index 039024e..011acf3 100644 --- a/src/Core/Logger/MessageDto.cs +++ b/Geekbot.net/Lib/Logger/MessageDto.cs @@ -1,4 +1,4 @@ -namespace Geekbot.Core.Logger +namespace Geekbot.net.Lib.Logger { public class MessageDto { diff --git a/src/Core/Logger/SimpleConextConverter.cs b/Geekbot.net/Lib/Logger/SimpleConextConverter.cs similarity index 89% rename from src/Core/Logger/SimpleConextConverter.cs rename to Geekbot.net/Lib/Logger/SimpleConextConverter.cs index 23cee40..8b8b0ed 100644 --- a/src/Core/Logger/SimpleConextConverter.cs +++ b/Geekbot.net/Lib/Logger/SimpleConextConverter.cs @@ -1,7 +1,7 @@ using Discord.Commands; using Discord.WebSocket; -namespace Geekbot.Core.Logger +namespace Geekbot.net.Lib.Logger { public class SimpleConextConverter { @@ -11,7 +11,7 @@ namespace Geekbot.Core.Logger { Message = new MessageDto.MessageContent { - Content = context.Message.Content, // Only when an error occurs, including for diagnostic reason + Content = context.Message.Content, Id = context.Message.Id.ToString(), Attachments = context.Message.Attachments.Count, ChannelMentions = context.Message.MentionedChannelIds.Count, @@ -37,11 +37,12 @@ namespace Geekbot.Core.Logger } public static MessageDto ConvertSocketMessage(SocketMessage message, bool isPrivate = false) { - var channel = isPrivate ? null : (SocketGuildChannel) message.Channel; + SocketGuildChannel channel = isPrivate ? null : (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, diff --git a/src/Core/Media/FortunesProvider.cs b/Geekbot.net/Lib/Media/FortunesProvider.cs similarity index 85% rename from src/Core/Media/FortunesProvider.cs rename to Geekbot.net/Lib/Media/FortunesProvider.cs index 1b0d87a..56b5ed6 100644 --- a/src/Core/Media/FortunesProvider.cs +++ b/Geekbot.net/Lib/Media/FortunesProvider.cs @@ -1,10 +1,10 @@ using System; using System.IO; -using Geekbot.Core.Logger; +using Geekbot.net.Lib.Logger; -namespace Geekbot.Core.Media +namespace Geekbot.net.Lib.Media { - public class FortunesProvider : IFortunesProvider + internal class FortunesProvider : IFortunesProvider { private readonly string[] _fortuneArray; private readonly int _totalFortunes; diff --git a/src/Core/Media/IFortunesProvider.cs b/Geekbot.net/Lib/Media/IFortunesProvider.cs similarity index 68% rename from src/Core/Media/IFortunesProvider.cs rename to Geekbot.net/Lib/Media/IFortunesProvider.cs index a4ef0d9..c82e45c 100644 --- a/src/Core/Media/IFortunesProvider.cs +++ b/Geekbot.net/Lib/Media/IFortunesProvider.cs @@ -1,4 +1,4 @@ -namespace Geekbot.Core.Media +namespace Geekbot.net.Lib.Media { public interface IFortunesProvider { diff --git a/Geekbot.net/Lib/Media/IMediaProvider.cs b/Geekbot.net/Lib/Media/IMediaProvider.cs new file mode 100644 index 0000000..a646cea --- /dev/null +++ b/Geekbot.net/Lib/Media/IMediaProvider.cs @@ -0,0 +1,15 @@ +namespace Geekbot.net.Lib.Media +{ + public interface IMediaProvider + { + string GetCheckem(); + string GetPanda(); + string GetCrossant(); + string GetSquirrel(); + string GetPumpkin(); + string GetTurtle(); + string GetPinguin(); + string GetFox(); + string GetDab(); + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/Media/MediaProvider.cs b/Geekbot.net/Lib/Media/MediaProvider.cs new file mode 100644 index 0000000..c88b696 --- /dev/null +++ b/Geekbot.net/Lib/Media/MediaProvider.cs @@ -0,0 +1,147 @@ +using System; +using System.IO; +using Geekbot.net.Lib.Logger; + +namespace Geekbot.net.Lib.Media +{ + public class MediaProvider : IMediaProvider + { + private readonly Random _random; + private readonly IGeekbotLogger _logger; + private string[] _checkemImages; + private string[] _pandaImages; + private string[] _croissantImages; + private string[] _squirrelImages; + private string[] _pumpkinImages; + private string[] _turtlesImages; + private string[] _pinguinImages; + private string[] _foxImages; + private string[] _dabImages; + + public MediaProvider(IGeekbotLogger logger) + { + _random = new Random(); + _logger = logger; + + logger.Information(LogSource.Geekbot, "Loading Media Files"); + + LoadCheckem(); + LoadPandas(); + BakeCroissants(); + LoadSquirrels(); + LoadPumpkins(); + LoadTurtles(); + LoadPinguins(); + LoadFoxes(); + LoadDab(); + } + + private void LoadCheckem() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/checkEmPics")); + _checkemImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_checkemImages.Length} CheckEm Images"); + } + + private void LoadPandas() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pandas")); + _pandaImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_pandaImages.Length} Panda Images"); + } + + private void BakeCroissants() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/croissant")); + _croissantImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_croissantImages.Length} Croissant Images"); + } + + private void LoadSquirrels() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/squirrel")); + _squirrelImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_squirrelImages.Length} Squirrel Images"); + } + + private void LoadPumpkins() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pumpkin")); + _pumpkinImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_pumpkinImages.Length} Pumpkin Images"); + } + + private void LoadTurtles() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/turtles")); + _turtlesImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_turtlesImages.Length} Turtle Images"); + } + + private void LoadPinguins() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/pinguins")); + _pinguinImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_pinguinImages.Length} Pinguin Images"); + } + + private void LoadFoxes() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/foxes")); + _foxImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_foxImages.Length} Foxes Images"); + } + + private void LoadDab() + { + var rawLinks = File.ReadAllText(Path.GetFullPath("./Storage/dab")); + _dabImages = rawLinks.Split("\n"); + _logger.Trace(LogSource.Geekbot, $"Loaded {_dabImages.Length} Dab Images"); + } + + public string GetCheckem() + { + return _checkemImages[_random.Next(0, _checkemImages.Length)]; + } + + public string GetPanda() + { + return _pandaImages[_random.Next(0, _pandaImages.Length)]; + } + + public string GetCrossant() + { + return _croissantImages[_random.Next(0, _croissantImages.Length)]; + } + + public string GetSquirrel() + { + return _squirrelImages[_random.Next(0, _squirrelImages.Length)]; + } + + public string GetPumpkin() + { + return _pumpkinImages[_random.Next(0, _pumpkinImages.Length)]; + } + + public string GetTurtle() + { + return _turtlesImages[_random.Next(0, _turtlesImages.Length)]; + } + + public string GetPinguin() + { + return _pinguinImages[_random.Next(0, _pinguinImages.Length)]; + } + + public string GetFox() + { + return _foxImages[_random.Next(0, _foxImages.Length)]; + } + + public string GetDab() + { + return _dabImages[_random.Next(0, _dabImages.Length)]; + } + } +} \ No newline at end of file diff --git a/src/Core/Polyfills/UserPolyfillDto.cs b/Geekbot.net/Lib/Polyfills/UserPolyfillDto.cs similarity index 53% rename from src/Core/Polyfills/UserPolyfillDto.cs rename to Geekbot.net/Lib/Polyfills/UserPolyfillDto.cs index 3907e05..6d81581 100644 --- a/src/Core/Polyfills/UserPolyfillDto.cs +++ b/Geekbot.net/Lib/Polyfills/UserPolyfillDto.cs @@ -1,41 +1,26 @@ using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading.Tasks; using Discord; -namespace Geekbot.Core.Polyfills +namespace Geekbot.net.Lib.Polyfills { - public class UserPolyfillDto : IUser + internal class UserPolyfillDto : IUser { public ulong Id { get; set; } public DateTimeOffset CreatedAt { get; set; } public string Mention { get; set; } public IActivity Activity { get; } public UserStatus Status { get; set; } - IReadOnlyCollection IPresence.ActiveClients => ActiveClients; - - IReadOnlyCollection IPresence.Activities => Activities; - - public IImmutableSet ActiveClients { get; } - public IImmutableList Activities { get; } - public Task CreateDMChannelAsync(RequestOptions options = null) - { - throw new NotImplementedException(); - } - public string AvatarId { get; set; } - public string AvatarUrl { get; set; } public string Discriminator { get; set; } public ushort DiscriminatorValue { get; set; } public bool IsBot { get; set; } public bool IsWebhook { get; set; } public string Username { get; set; } - public UserProperties? PublicFlags { get; } - + public string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) { - return AvatarUrl ?? "https://discordapp.com/assets/6debd47ed13483642cf09e832ed0bc1b.png"; + return "https://discordapp.com/assets/6debd47ed13483642cf09e832ed0bc1b.png"; } public string GetDefaultAvatarUrl() diff --git a/src/Core/RandomNumberGenerator/IRandomNumberGenerator.cs b/Geekbot.net/Lib/RandomNumberGenerator/IRandomNumberGenerator.cs similarity index 67% rename from src/Core/RandomNumberGenerator/IRandomNumberGenerator.cs rename to Geekbot.net/Lib/RandomNumberGenerator/IRandomNumberGenerator.cs index f817248..32c6285 100644 --- a/src/Core/RandomNumberGenerator/IRandomNumberGenerator.cs +++ b/Geekbot.net/Lib/RandomNumberGenerator/IRandomNumberGenerator.cs @@ -1,4 +1,4 @@ -namespace Geekbot.Core.RandomNumberGenerator +namespace Geekbot.net.Lib.RandomNumberGenerator { public interface IRandomNumberGenerator { diff --git a/Geekbot.net/Lib/RandomNumberGenerator/RandomNumberGenerator.cs b/Geekbot.net/Lib/RandomNumberGenerator/RandomNumberGenerator.cs new file mode 100644 index 0000000..79b4620 --- /dev/null +++ b/Geekbot.net/Lib/RandomNumberGenerator/RandomNumberGenerator.cs @@ -0,0 +1,46 @@ +using System; +using System.Security.Cryptography; + +namespace Geekbot.net.Lib.RandomNumberGenerator +{ + public class RandomNumberGenerator : IRandomNumberGenerator + { + readonly RNGCryptoServiceProvider csp; + + public RandomNumberGenerator() + { + csp = new RNGCryptoServiceProvider(); + } + + public int Next(int minValue, int maxExclusiveValue) + { + if (minValue >= maxExclusiveValue) + { + throw new ArgumentOutOfRangeException("minValue must be lower than maxExclusiveValue"); + } + + var diff = (long)maxExclusiveValue - minValue; + var upperBound = uint.MaxValue / diff * diff; + + uint ui; + do + { + ui = GetRandomUInt(); + } while (ui >= upperBound); + return (int)(minValue + (ui % diff)); + } + + private uint GetRandomUInt() + { + var randomBytes = GenerateRandomBytes(sizeof(uint)); + return BitConverter.ToUInt32(randomBytes, 0); + } + + private byte[] GenerateRandomBytes(int bytesNumber) + { + var buffer = new byte[bytesNumber]; + csp.GetBytes(buffer); + return buffer; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/ReactionListener/IReactionListener.cs b/Geekbot.net/Lib/ReactionListener/IReactionListener.cs new file mode 100644 index 0000000..792a516 --- /dev/null +++ b/Geekbot.net/Lib/ReactionListener/IReactionListener.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Discord; +using Discord.WebSocket; + +namespace Geekbot.net.Lib.ReactionListener +{ + public interface IReactionListener + { + bool IsListener(ulong id); + Task AddRoleToListener(string messageId, IEmote emoji, IRole role); + void RemoveRole(ISocketMessageChannel channel, SocketReaction reaction); + void GiveRole(ISocketMessageChannel message, SocketReaction reaction); + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/ReactionListener/ReactionListener.cs b/Geekbot.net/Lib/ReactionListener/ReactionListener.cs new file mode 100644 index 0000000..18490db --- /dev/null +++ b/Geekbot.net/Lib/ReactionListener/ReactionListener.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Discord; +using Discord.WebSocket; +using StackExchange.Redis; + +namespace Geekbot.net.Lib.ReactionListener +{ + public class ReactionListener : IReactionListener + { + private readonly IDatabase _redis; + private Dictionary> _listener; + + public ReactionListener(IDatabase redis) + { + _redis = redis; + LoadListeners(); + } + + private Task LoadListeners() + { + var ids = _redis.SetMembers("MessageIds"); + _listener = new Dictionary>(); + foreach (var id in ids) + { + var reactions = _redis.HashGetAll($"Messages:{id}"); + var messageId = id; + var emojiDict = new Dictionary(); + foreach (var r in reactions) + { + IEmote emote; + if (!r.Name.ToString().StartsWith('<')) + { + var emo = new Emoji(r.Name); + emote = emo; + } + else + { + emote = Emote.Parse(r.Name); + } + emojiDict.Add(emote, ulong.Parse(r.Value)); + } + _listener.Add(messageId, emojiDict); + } + + return Task.CompletedTask; + } + + public bool IsListener(ulong id) + { + return _listener.ContainsKey(id.ToString()); + } + + public Task AddRoleToListener(string messageId, IEmote emoji, IRole role) + { + if (_redis.SetMembers("MessageIds").All(e => e.ToString() != messageId)) + { + _redis.SetAdd("MessageIds", messageId); + } + _redis.HashSet($"Messages:{messageId}", new[] {new HashEntry(emoji.ToString(), role.Id.ToString())}); + _redis.SetAdd("MessageIds", messageId); + if (_listener.ContainsKey(messageId)) + { + _listener[messageId].Add(emoji, role.Id); + return Task.CompletedTask; + } + var dict = new Dictionary(); + dict.Add(emoji, role.Id); + _listener.Add(messageId, dict); + return Task.CompletedTask; + } + + public async void RemoveRole(ISocketMessageChannel channel, SocketReaction reaction) + { + var roleId = _listener[reaction.MessageId.ToString()][reaction.Emote]; + var guild = (SocketGuildChannel) channel; + var role = guild.Guild.GetRole(roleId); + await ((IGuildUser) reaction.User.Value).RemoveRoleAsync(role); + } + + public async void GiveRole(ISocketMessageChannel channel, SocketReaction reaction) + { + var roleId = _listener[reaction.MessageId.ToString()][reaction.Emote]; + var guild = (SocketGuildChannel) channel; + var role = guild.Guild.GetRole(roleId); + await ((IGuildUser) reaction.User.Value).AddRoleAsync(role); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Lib/RunParameters.cs b/Geekbot.net/Lib/RunParameters.cs new file mode 100644 index 0000000..444a60f --- /dev/null +++ b/Geekbot.net/Lib/RunParameters.cs @@ -0,0 +1,76 @@ +using CommandLine; + +namespace Geekbot.net.Lib +{ + public class RunParameters + { + /************************************ + * General * + ************************************/ + + [Option('V', "verbose", Default = false, HelpText = "Logs everything.")] + public bool Verbose { get; set; } + + [Option('j', "log-json", Default = false, HelpText = "Logger outputs json")] + public bool LogJson { get; set; } + + [Option('a', "disable-api", Default = false, HelpText = "Disables the web api")] + public bool DisableApi { get; set; } + + [Option('e', "expose-errors", Default = false, HelpText = "Shows internal errors in the chat")] + public bool ExposeErrors { get; set; } + + [Option("token", Default = null, HelpText = "Set a new bot token")] + public string Token { get; set; } + + /************************************ + * Database * + ************************************/ + + [Option("in-memory", Default = false, HelpText = "Uses the in-memory database instead of postgresql")] + public bool InMemory { get; set; } + + // Postresql connection + [Option("database", Default = "geekbot", HelpText = "Select a postgresql database")] + public string DbDatabase { get; set; } + + [Option("db-host", Default = "localhost", HelpText = "Set a postgresql host (e.g. 127.0.0.1)")] + public string DbHost { get; set; } + + [Option("db-port", Default = "5432", HelpText = "Set a postgresql host (e.g. 5432)")] + public string DbPort { get; set; } + + [Option("db-user", Default = "geekbot", HelpText = "Set a postgresql user")] + public string DbUser { get; set; } + + [Option("db-password", Default = "", HelpText = "Set a posgresql password")] + public string DbPassword { get; set; } + + // Logging + [Option("db-logging", Default = false, HelpText = "Enable database logging")] + public bool DbLogging { get; set; } + + /************************************ + * Redis * + ************************************/ + + [Option("redis-host", Default = "127.0.0.1", HelpText = "Set a redis host")] + public string RedisHost { get; set; } + + [Option("redis-port", Default = "6379", HelpText = "Set a redis port")] + public string RedisPort { get; set; } + + [Option("redis-database", Default = "6", HelpText = "Select a redis database (1-15)")] + public string RedisDatabase { get; set; } + + /************************************ + * WebApi * + ************************************/ + + [Option("api-host", Default = "localhost", HelpText = "Host on which the WebApi listens")] + public string ApiHost { get; set; } + + [Option("api-port", Default = "12995", HelpText = "Port on which the WebApi listens")] + public string ApiPort { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/UserRepository/IUserRepository.cs b/Geekbot.net/Lib/UserRepository/IUserRepository.cs similarity index 68% rename from src/Core/UserRepository/IUserRepository.cs rename to Geekbot.net/Lib/UserRepository/IUserRepository.cs index 32598f6..b2f9a1f 100644 --- a/src/Core/UserRepository/IUserRepository.cs +++ b/Geekbot.net/Lib/UserRepository/IUserRepository.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; using Discord.WebSocket; -using Geekbot.Core.Database.Models; +using Geekbot.net.Database.Models; -namespace Geekbot.Core.UserRepository +namespace Geekbot.net.Lib.UserRepository { public interface IUserRepository { diff --git a/src/Core/UserRepository/UserRepository.cs b/Geekbot.net/Lib/UserRepository/UserRepository.cs similarity index 72% rename from src/Core/UserRepository/UserRepository.cs rename to Geekbot.net/Lib/UserRepository/UserRepository.cs index 6b436b1..b27a0a2 100644 --- a/src/Core/UserRepository/UserRepository.cs +++ b/Geekbot.net/Lib/UserRepository/UserRepository.cs @@ -1,13 +1,14 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Discord.WebSocket; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Core.Extensions; -using Geekbot.Core.Logger; +using Geekbot.net.Database; +using Geekbot.net.Database.Models; +using Geekbot.net.Lib.Extensions; +using Geekbot.net.Lib.Logger; -namespace Geekbot.Core.UserRepository +namespace Geekbot.net.Lib.UserRepository { public class UserRepository : IUserRepository { @@ -35,7 +36,12 @@ namespace Geekbot.Core.UserRepository savedUser.Discriminator = user.Discriminator; savedUser.AvatarUrl = user.GetAvatarUrl() ?? ""; savedUser.IsBot = user.IsBot; - savedUser.Joined = user.CreatedAt.ToUniversalTime(); + savedUser.Joined = user.CreatedAt; + if (savedUser.UsedNames == null) savedUser.UsedNames = new List(); + if (!savedUser.UsedNames.Any(e => e.Name.Equals(user.Username))) + { + savedUser.UsedNames.Add(new UserUsedNamesModel { Name = user.Username, FirstSeen = DateTimeOffset.Now }); + } if (isNew) { @@ -48,7 +54,7 @@ namespace Geekbot.Core.UserRepository await _database.SaveChangesAsync(); - _logger.Debug(LogSource.UserRepository, "Updated User", savedUser); + _logger.Information(LogSource.UserRepository, "Updated User", savedUser); await Task.Delay(500); return true; } diff --git a/Geekbot.net/Logs/.keep b/Geekbot.net/Logs/.keep new file mode 100755 index 0000000..e69de29 diff --git a/Geekbot.net/Program.cs b/Geekbot.net/Program.cs new file mode 100755 index 0000000..9833b3a --- /dev/null +++ b/Geekbot.net/Program.cs @@ -0,0 +1,213 @@ +using System; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using CommandLine; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using Geekbot.net.Database; +using Geekbot.net.Lib; +using Geekbot.net.Lib.AlmostRedis; +using Geekbot.net.Lib.Clients; +using Geekbot.net.Lib.Converters; +using Geekbot.net.Lib.ErrorHandling; +using Geekbot.net.Lib.GlobalSettings; +using Geekbot.net.Lib.Highscores; +using Geekbot.net.Lib.Levels; +using Geekbot.net.Lib.Localization; +using Geekbot.net.Lib.Logger; +using Geekbot.net.Lib.Media; +using Geekbot.net.Lib.RandomNumberGenerator; +using Geekbot.net.Lib.ReactionListener; +using Geekbot.net.Lib.UserRepository; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using WikipediaApi; + +namespace Geekbot.net +{ + internal class Program + { + private DiscordSocketClient _client; + private CommandService _commands; + private DatabaseInitializer _databaseInitializer; + private IGlobalSettings _globalSettings; + private IServiceCollection _services; + private IServiceProvider _servicesProvider; + private string _token; + private GeekbotLogger _logger; + private IUserRepository _userRepository; + private RunParameters _runParameters; + private IAlmostRedis _redis; + + private static void Main(string[] args) + { + RunParameters runParameters = null; + Parser.Default.ParseArguments(args) + .WithParsed(e => runParameters = e) + .WithNotParsed(_ => Environment.Exit(GeekbotExitCode.InvalidArguments.GetHashCode())); + + var logo = new StringBuilder(); + logo.AppendLine(@" ____ _____ _____ _ ______ ___ _____"); + logo.AppendLine(@" / ___| ____| ____| |/ / __ ) / _ \\_ _|"); + logo.AppendLine(@"| | _| _| | _| | ' /| _ \| | | || |"); + logo.AppendLine(@"| |_| | |___| |___| . \| |_) | |_| || |"); + logo.AppendLine(@" \____|_____|_____|_|\_\____/ \___/ |_|"); + logo.AppendLine($"Version {Constants.BotVersion()} ".PadRight(41, '=')); + Console.WriteLine(logo.ToString()); + var sumologicActive = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GEEKBOT_SUMO")); + var logger = new GeekbotLogger(runParameters, sumologicActive); + logger.Information(LogSource.Geekbot, "Starting..."); + try + { + new Program().MainAsync(runParameters, logger).GetAwaiter().GetResult(); + } + catch (Exception e) + { + logger.Error(LogSource.Geekbot, "RIP", e); + } + } + + private async Task MainAsync(RunParameters runParameters, GeekbotLogger logger) + { + _logger = logger; + _runParameters = runParameters; + logger.Information(LogSource.Geekbot, "Initing Stuff"); + var discordLogger = new DiscordLogger(logger); + + _client = new DiscordSocketClient(new DiscordSocketConfig + { + LogLevel = LogSeverity.Verbose, + MessageCacheSize = 1000, + ExclusiveBulkDelete = true + }); + _client.Log += discordLogger.Log; + _commands = new CommandService(); + + _databaseInitializer = new DatabaseInitializer(runParameters, logger); + var database = _databaseInitializer.Initialize(); + database.Database.EnsureCreated(); + if(!_runParameters.InMemory) database.Database.Migrate(); + + _globalSettings = new GlobalSettings(database); + + try + { + _redis = new AlmostRedis(logger, runParameters); + _redis.Connect(); + } + catch (Exception e) + { + logger.Error(LogSource.Redis, "Redis Connection Failed", e); + Environment.Exit(GeekbotExitCode.RedisConnectionFailed.GetHashCode()); + } + + _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; + } + + _services = new ServiceCollection(); + + _userRepository = new UserRepository(_databaseInitializer.Initialize(), logger); + var fortunes = new FortunesProvider(logger); + var mediaProvider = new MediaProvider(logger); + var malClient = new MalClient(_globalSettings, logger); + var levelCalc = new LevelCalc(); + var emojiConverter = new EmojiConverter(); + var mtgManaConverter = new MtgManaConverter(); + var wikipediaClient = new WikipediaClient(); + var randomNumberGenerator = new RandomNumberGenerator(); + + _services.AddSingleton(_redis); + _services.AddSingleton(_userRepository); + _services.AddSingleton(logger); + _services.AddSingleton(levelCalc); + _services.AddSingleton(emojiConverter); + _services.AddSingleton(fortunes); + _services.AddSingleton(mediaProvider); + _services.AddSingleton(malClient); + _services.AddSingleton(mtgManaConverter); + _services.AddSingleton(wikipediaClient); + _services.AddSingleton(randomNumberGenerator); + _services.AddSingleton(_globalSettings); + _services.AddTransient((e) => new HighscoreManager(_databaseInitializer.Initialize(), _userRepository)); + _services.AddTransient((e) => _databaseInitializer.Initialize()); + + logger.Information(LogSource.Geekbot, "Connecting to Discord"); + + await Login(); + + await Task.Delay(-1); + } + + private async Task Login() + { + try + { + await _client.LoginAsync(TokenType.Bot, _token); + await _client.StartAsync(); + var isConneted = await IsConnected(); + if (isConneted) + { + await _client.SetGameAsync(_globalSettings.GetKey("Game")); + _logger.Information(LogSource.Geekbot, $"Now Connected as {_client.CurrentUser.Username} to {_client.Guilds.Count} Servers"); + + _logger.Information(LogSource.Geekbot, "Registering Stuff"); + var translationHandler = new TranslationHandler(_databaseInitializer.Initialize(), _logger); + var errorHandler = new ErrorHandler(_logger, translationHandler, _globalSettings, _runParameters.ExposeErrors); + var reactionListener = new ReactionListener(_redis.Db); + _services.AddSingleton(errorHandler); + _services.AddSingleton(translationHandler); + _services.AddSingleton(_client); + _services.AddSingleton(reactionListener); + _servicesProvider = _services.BuildServiceProvider(); + await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _servicesProvider); + + var handlers = new Handlers(_databaseInitializer, _client, _logger, _redis, _servicesProvider, _commands, _userRepository, reactionListener, translationHandler); + + _client.MessageReceived += handlers.RunCommand; + _client.MessageDeleted += handlers.MessageDeleted; + _client.UserJoined += handlers.UserJoined; + _client.UserUpdated += handlers.UserUpdated; + _client.UserLeft += handlers.UserLeft; + _client.ReactionAdded += handlers.ReactionAdded; + _client.ReactionRemoved += handlers.ReactionRemoved; + if (!_runParameters.InMemory) _client.MessageReceived += handlers.UpdateStats; + + var webserver = _runParameters.DisableApi ? Task.Delay(10) : StartWebApi(); + + _logger.Information(LogSource.Geekbot, "Done and ready for use"); + + await webserver; + } + } + catch (Exception e) + { + _logger.Error(LogSource.Geekbot, "Could not connect to Discord", e); + Environment.Exit(GeekbotExitCode.CouldNotLogin.GetHashCode()); + } + } + + private async Task IsConnected() + { + while (!_client.ConnectionState.Equals(ConnectionState.Connected)) + await Task.Delay(25); + return true; + } + + private Task StartWebApi() + { + _logger.Information(LogSource.Api, "Starting Webserver"); + var highscoreManager = new HighscoreManager(_databaseInitializer.Initialize(), _userRepository); + WebApi.WebApiStartup.StartWebApi(_logger, _runParameters, _commands, _databaseInitializer.Initialize(), _client, _globalSettings, highscoreManager); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Geekbot.net/Storage/checkEmPics b/Geekbot.net/Storage/checkEmPics new file mode 100644 index 0000000..03be23e --- /dev/null +++ b/Geekbot.net/Storage/checkEmPics @@ -0,0 +1,122 @@ +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 \ No newline at end of file diff --git a/Geekbot.net/Storage/croissant b/Geekbot.net/Storage/croissant new file mode 100644 index 0000000..281b790 --- /dev/null +++ b/Geekbot.net/Storage/croissant @@ -0,0 +1,17 @@ +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 \ No newline at end of file diff --git a/src/Bot/Storage/dab b/Geekbot.net/Storage/dab similarity index 100% rename from src/Bot/Storage/dab rename to Geekbot.net/Storage/dab diff --git a/src/Bot/Storage/fortunes b/Geekbot.net/Storage/fortunes similarity index 100% rename from src/Bot/Storage/fortunes rename to Geekbot.net/Storage/fortunes diff --git a/Geekbot.net/Storage/foxes b/Geekbot.net/Storage/foxes new file mode 100644 index 0000000..020c1cf --- /dev/null +++ b/Geekbot.net/Storage/foxes @@ -0,0 +1,29 @@ +https://i.ytimg.com/vi/qF6OOGuT_hI/maxresdefault.jpg +https://www.hd-wallpapersdownload.com/script/bulk-upload/desktop-funny-fox-wallpaper.jpg +http://moziru.com/images/drawn-fox-funny-18.jpg +https://static.tumblr.com/bb34d8f163098ad1daafcffbdbb03975/rk23uap/Nwwp0rmi2/tumblr_static_tumblr_static__640.jpg +https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQoHUFOnZ3wJ2kT1skNdztFXXSvpU8bEoGS1alNZiuyLXvGJhcY +http://childrenstorytales.com/wp-content/uploads/2011/03/how-to-draw-a-red-fox-in-the-snow.jpg +https://www.popsci.com/sites/popsci.com/files/styles/1000_1x_/public/import/2013/images/2013/09/redfoxyawn.jpg?itok=yRkSVe8T +https://hdqwalls.com/wallpapers/wild-fox-art.jpg +https://ae01.alicdn.com/kf/HTB1Q9dpLpXXXXbhXpXXq6xXFXXXl/new-cute-fox-toy-lifelike-soft-long-yellow-fox-doll-gift-about-73cm.jpg_640x640.jpg +https://i.imgur.com/ktK9yXX.jpg +https://res.cloudinary.com/teepublic/image/private/s--yTx2ncFA--/t_Preview/b_rgb:c8e0ec,c_limit,f_auto,h_313,q_90,w_313/v1506478249/production/designs/1932607_0 +http://4.bp.blogspot.com/-Hz-o_KYj3Xk/Vlm2mwbztjI/AAAAAAAA8Ss/jbH5ovjmC9A/s1600/ScreenShot5502.jpg +https://i.pinimg.com/originals/1e/d5/2f/1ed52f70873a95ac02fa074e48edfb71.jpg +https://i.imgur.com/2vCrtap.jpg +https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSfukWGu_IBaDeJOMBqOhVAwsDfqEPw0BFpCn5_-Iyr_xjd7zi9 +https://cdn.pixabay.com/photo/2017/01/31/18/36/animal-2026297_960_720.png +https://i.pinimg.com/originals/e2/63/67/e26367a0844633b2a697b0a9d69e8cc9.jpg +https://i.ebayimg.com/images/g/BvkAAOSwqxdTqrip/s-l300.jpg +https://res.cloudinary.com/teepublic/image/private/s--1R53bger--/t_Preview/b_rgb:eae0c7,c_limit,f_jpg,h_630,q_90,w_630/v1481013120/production/designs/914528_1.jpg +https://i.pinimg.com/originals/97/fe/69/97fe698462afde7b4209ccefeecbce71.jpg +https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6G0ch6g-wG1TuDJ6BbkOFelMNnkgFXC6CxOw7qSNjoFkx-BCe +https://wallpaperscraft.com/image/fox_forest_grass_117190_540x960.jpg +https://image.freepik.com/free-vector/cartoon-flat-illustration-funny-cute-fox_6317-1174.jpg +https://orig00.deviantart.net/2feb/f/2013/137/a/f/fox_and_curious_squirrel_by_tamarar-d65ju8d.jpg +https://res.cloudinary.com/teepublic/image/private/s--dICeNmBx--/t_Preview/b_rgb:6e2229,c_limit,f_jpg,h_630,q_90,w_630/v1505243196/production/designs/1890493_1.jpg +https://vignette.wikia.nocookie.net/puppyinmypocketfanon/images/4/49/L-Baby-Fox.jpg/revision/latest?cb=20130421001806 +http://7-themes.com/data_images/out/69/7009194-fox-puppy.jpg +http://www.tehcute.com/pics/201401/little-fox-big.jpg +https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR6QXB1APLdUsyzO39kPvhnC9cOvcwzEtsxown9QjWilWppia2mwg \ No newline at end of file diff --git a/src/Bot/Storage/pandas b/Geekbot.net/Storage/pandas similarity index 78% rename from src/Bot/Storage/pandas rename to Geekbot.net/Storage/pandas index 6c6d725..9d5046c 100644 --- a/src/Bot/Storage/pandas +++ b/Geekbot.net/Storage/pandas @@ -4,9 +4,8 @@ https://nationalzoo.si.edu/sites/default/files/styles/slide_1400x700/public/supp https://media4.s-nbcnews.com/j/newscms/2016_36/1685951/ss-160826-twip-05_8cf6d4cb83758449fd400c7c3d71aa1f.nbcnews-ux-2880-1000.jpg https://ichef-1.bbci.co.uk/news/660/cpsprodpb/169F6/production/_91026629_gettyimages-519508400.jpg https://cdn.history.com/sites/2/2017/03/GettyImages-157278376.jpg +https://www.pandasinternational.org/wptemp/wp-content/uploads/2012/10/slider1.jpg https://tctechcrunch2011.files.wordpress.com/2015/11/panda.jpg http://www.nationalgeographic.com/content/dam/magazine/rights-exempt/2016/08/departments/panda-mania-12.jpg http://animals.sandiegozoo.org/sites/default/files/2016-09/panda1_10.jpg -http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Mammals/A-G/giant-panda-eating.adapt.945.1.jpg -https://static.independent.co.uk/s3fs-public/thumbnails/image/2015/10/08/15/Hong-Kong-pandas.jpg -https://3sn4dm1qd6i72l8a4r2ig7fl-wpengine.netdna-ssl.com/wp-content/uploads/2016/11/panda_lunlun_ZA_2083-b.jpg \ No newline at end of file +http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Mammals/A-G/giant-panda-eating.adapt.945.1.jpg \ No newline at end of file diff --git a/Geekbot.net/Storage/pinguins b/Geekbot.net/Storage/pinguins new file mode 100644 index 0000000..631f9d0 --- /dev/null +++ b/Geekbot.net/Storage/pinguins @@ -0,0 +1,13 @@ +https://i.ytimg.com/vi/Qr6sULJnu2o/maxresdefault.jpg +https://www.apex-expeditions.com/wp-content/uploads/2015/08/newzealandSlider_Macquarie_ElephantSealKingPenguins_GRiehle_1366x601.jpg +https://www.birdlife.org/sites/default/files/styles/1600/public/slide.jpg?itok=HRhQfA1S +http://experimentexchange.com/wp-content/uploads/2016/07/penguins-fact.jpg +http://images.mentalfloss.com/sites/default/files/styles/mf_image_16x9/public/istock-511366776.jpg?itok=cWhdWNZ8&resize=1100x619 +https://www.thevaporplace.ch/media/catalog/product/cache/1/thumbnail/800x800/9df78eab33525d08d6e5fb8d27136e95/a/t/atopack_penguin-15.jpg +https://www.superfastbusiness.com/wp-content/uploads/2015/10/real-time-penguin-algorithm-featured.jpg +http://www.antarctica.gov.au/__data/assets/image/0011/147737/varieties/antarctic.jpg +https://vignette.wikia.nocookie.net/robloxcreepypasta/images/1/11/AAEAAQAAAAAAAAdkAAAAJDc3YzkyYjJhLTYyZjctNDY2Mi04M2VjLTg4NjY4ZjgwYzRmNg.png/revision/latest?cb=20180207200526 +https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR3xV0lhpZuhT8Nmm6LaITsppZ7VfWcWXuyu2cPHrlv_dt_M92K5g +http://goboiano.com/wp-content/uploads/2017/04/Penguin-Kemeno-Friends-Waifu.jpg +https://cdn.yoast.com/app/uploads/2015/10/Penguins_1200x628.png +https://images.justwatch.com/backdrop/8611153/s1440/pingu \ No newline at end of file diff --git a/Geekbot.net/Storage/pumpkin b/Geekbot.net/Storage/pumpkin new file mode 100644 index 0000000..4b8e6f2 --- /dev/null +++ b/Geekbot.net/Storage/pumpkin @@ -0,0 +1,23 @@ +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 diff --git a/Geekbot.net/Storage/squirrel b/Geekbot.net/Storage/squirrel new file mode 100644 index 0000000..2216465 --- /dev/null +++ b/Geekbot.net/Storage/squirrel @@ -0,0 +1,45 @@ +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 diff --git a/src/Bot/Storage/turtles b/Geekbot.net/Storage/turtles similarity index 69% rename from src/Bot/Storage/turtles rename to Geekbot.net/Storage/turtles index aa0fbcf..9dbbf72 100644 --- a/src/Bot/Storage/turtles +++ b/Geekbot.net/Storage/turtles @@ -1,20 +1,21 @@ https://i.guim.co.uk/img/media/6b9be13031738e642f93f9271f3592044726a9b1/0_0_2863_1610/2863.jpg?w=640&h=360&q=55&auto=format&usm=12&fit=max&s=85f3b33cc158b5aa120c143dae1916ed http://cf.ltkcdn.net/small-pets/images/std/212089-676x450-Turtle-feeding-on-leaf.jpg +https://static1.squarespace.com/static/5369465be4b0507a1fd05af0/53767a6be4b0ad0822345e52/57e40ba4893fc031e05a018f/1498243318058/solvin.jpg?format=1500w https://c402277.ssl.cf1.rackcdn.com/photos/419/images/story_full_width/HI_287338Hero.jpg?1433950119 https://www.cdc.gov/salmonella/agbeni-08-17/images/turtle.jpg https://cdn.arstechnica.net/wp-content/uploads/2017/08/GettyImages-524757168.jpg http://pmdvod.nationalgeographic.com/NG_Video/595/319/4504517_098_05_TOS_thumbnail_640x360_636296259676.jpg +http://cdn1.arkive.org/media/7D/7D46329A-6ED2-4F08-909E-7B596417994A/Presentation.Large/Big-headed-turtle-close-up.jpg http://s7d2.scene7.com/is/image/PetSmart/ARTHMB-CleaningYourTortoiseOrTurtlesHabitat-20160818?$AR1104$ https://fthmb.tqn.com/9VGWzK_GWlvrjxtdFPX6EJxOq24=/960x0/filters:no_upscale()/133605352-56a2bce53df78cf7727960db.jpg +https://i.imgur.com/46QmzgF.jpg https://www.wildgratitude.com/wp-content/uploads/2015/07/turtle-spirit-animal1.jpg http://www.backwaterreptiles.com/images/turtles/red-eared-slider-turtle-for-sale.jpg +https://i.pinimg.com/736x/f1/f4/13/f1f413d6d07912be6080c08b186630ac--happy-turtle-funny-stuff.jpg +http://www.dupageforest.org/uploadedImages/Content/District_News/Nature_Stories/2016/Snapping%20Turtle%20Scott%20Plantier%20STP4793.jpg http://turtlebackzoo.com/wp-content/uploads/2016/07/exhibit-headers_0008_SOUTH-AMERICA-600x400.jpg +https://i.ytimg.com/vi/_YfYHFM3Das/maxresdefault.jpg https://i.pinimg.com/736x/dd/4e/7f/dd4e7f2f921ac28b1d5a59174d477131--cute-baby-sea-turtles-adorable-turtles.jpg http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Reptiles/A-G/green-sea-turtle-closeup-underwater.adapt.945.1.jpg +https://i.ytimg.com/vi/p4Jj9QZFJvw/hqdefault.jpg https://fthmb.tqn.com/nirxHkH3jBAe74ife6fJJu6k6q8=/2121x1414/filters:fill(auto,1)/Red-eared-sliders-GettyImages-617946009-58fae8835f9b581d59a5bab6.jpg -http://assets.worldwildlife.org/photos/167/images/original/MID_225023-circle-hawksbill-turtle.jpg?1345565600 -https://seaturtles.org/wp-content/uploads/2013/11/GRN-honuAnitaWintner2.jpg -https://images2.minutemediacdn.com/image/upload/c_crop,h_2549,w_4536,x_0,y_237/v1560186367/shape/mentalfloss/istock-687398754.jpg?itok=QsiF5yHP -https://c402277.ssl.cf1.rackcdn.com/photos/13028/images/story_full_width/seaturtle_spring2017.jpg?1485359391 -https://i2.wp.com/rangerrick.org/wp-content/uploads/2018/03/Turtle-Tale-RR-Jr-June-July-2017.jpg?fit=1156%2C650&ssl=1 -https://boyslifeorg.files.wordpress.com/2019/07/greenseaturtle.jpg \ No newline at end of file diff --git a/Geekbot.net/WebApi/ApiError.cs b/Geekbot.net/WebApi/ApiError.cs new file mode 100644 index 0000000..182518e --- /dev/null +++ b/Geekbot.net/WebApi/ApiError.cs @@ -0,0 +1,7 @@ +namespace Geekbot.net.WebApi +{ + public class ApiError + { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Callback/CallbackController.cs b/Geekbot.net/WebApi/Controllers/Callback/CallbackController.cs new file mode 100644 index 0000000..58bdb83 --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Callback/CallbackController.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Discord.WebSocket; +using Geekbot.net.Lib.GlobalSettings; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; + +namespace Geekbot.net.WebApi.Controllers.Callback +{ + public class CallbackController : Controller + { + private readonly DiscordSocketClient _client; + private readonly IGlobalSettings _globalSettings; + + public CallbackController(DiscordSocketClient client, IGlobalSettings globalSettings) + { + _client = client; + _globalSettings = globalSettings; + } + + [Route("/callback")] + public async Task DoCallback([FromQuery] string code) + { + var token = ""; + using (var client = new HttpClient()) + { + client.BaseAddress = new Uri("https://discordapp.com"); + var appInfo = await _client.GetApplicationInfoAsync(); + var accessToken = _globalSettings.GetKey("OAuthToken"); + var callbackUrl = _globalSettings.GetKey("OAuthCallbackUrl"); + + var form = new Dictionary(); + form.Add("client_id", appInfo.Id.ToString()); + form.Add("client_secret", accessToken); + form.Add("grant_type", "authorization_code"); + form.Add("code", code); + form.Add("scope", "identify email guilds"); + form.Add("redirect_uri", callbackUrl); + + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded")); + var result = await client.PostAsync("/api/oauth2/token", new FormUrlEncodedContent(form)); + result.EnsureSuccessStatusCode(); + + var stringResponse = await result.Content.ReadAsStringAsync(); + var responseData = JsonConvert.DeserializeObject(stringResponse); + token = responseData.access_token; + } + + return new RedirectResult($"https://geekbot.pizzaandcoffee.rocks/login?token={token}", false); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Callback/CallbackTokenResponseDto.cs b/Geekbot.net/WebApi/Controllers/Callback/CallbackTokenResponseDto.cs new file mode 100644 index 0000000..3c81592 --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Callback/CallbackTokenResponseDto.cs @@ -0,0 +1,11 @@ +namespace Geekbot.net.WebApi.Controllers.Callback +{ + public class CallbackTokenResponseDto + { + public string access_token { get; set; } + public string token_type { get; set; } + public int expires_in { get; set; } + public string refresh_token { get; set; } + public string scope { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Commands/CommandController.cs b/Geekbot.net/WebApi/Controllers/Commands/CommandController.cs new file mode 100644 index 0000000..74f6e3c --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Commands/CommandController.cs @@ -0,0 +1,41 @@ +using System.Linq; +using Discord.Commands; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; + +namespace Geekbot.net.WebApi.Controllers.Commands +{ + [EnableCors("AllowSpecificOrigin")] + public class CommandController : Controller + { + private readonly CommandService _commands; + + public CommandController(CommandService commands) + { + _commands = commands; + } + + [Route("/v1/commands")] + public IActionResult GetCommands() + { + var commandList = (from cmd in _commands.Commands + let cmdParamsObj = cmd.Parameters.Select(cmdParam => new CommandParamDto + { + Summary = cmdParam.Summary, + Default = cmdParam.DefaultValue?.ToString(), + Type = cmdParam.Type?.ToString() + }) + .ToList() + let param = string.Join(", !", cmd.Aliases) + select new CommandDto + { + Name = cmd.Name, + Summary = cmd.Summary, + IsAdminCommand = param.Contains("admin") || param.Contains("owner"), + Aliases = cmd.Aliases.ToList(), + Params = cmdParamsObj + }).ToList(); + return Ok(commandList.FindAll(e => !e.Aliases[0].StartsWith("owner"))); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Commands/CommandDto.cs b/Geekbot.net/WebApi/Controllers/Commands/CommandDto.cs new file mode 100644 index 0000000..68c4e74 --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Commands/CommandDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace Geekbot.net.WebApi.Controllers.Commands +{ + public class CommandDto + { + public string Name { get; set; } + public string Summary { get; set; } + public bool IsAdminCommand { get; set; } + public List Aliases { get; set; } + public List Params { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Commands/CommandParamDto.cs b/Geekbot.net/WebApi/Controllers/Commands/CommandParamDto.cs new file mode 100644 index 0000000..5f7519d --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Commands/CommandParamDto.cs @@ -0,0 +1,9 @@ +namespace Geekbot.net.WebApi.Controllers.Commands +{ + public class CommandParamDto + { + public string Summary { get; set; } + public string Default { get; set; } + public string Type { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Highscores/HighscoreController.cs b/Geekbot.net/WebApi/Controllers/Highscores/HighscoreController.cs new file mode 100644 index 0000000..3cccb0e --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Highscores/HighscoreController.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Geekbot.net.Lib.Highscores; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; + +namespace Geekbot.net.WebApi.Controllers.Highscores +{ + [EnableCors("AllowSpecificOrigin")] + public class HighscoreController : Controller + { + private readonly IHighscoreManager _highscoreManager; + + public HighscoreController(IHighscoreManager highscoreManager) + { + _highscoreManager = highscoreManager; + } + + [HttpPost] + [Route("/v1/highscore")] + public IActionResult GetHighscores([FromBody] HighscoreControllerPostBodyDto body) + { + if (!ModelState.IsValid || body == null) + { + var error = new SerializableError(ModelState); + return BadRequest(error); + } + + Dictionary list; + try + { + list = _highscoreManager.GetHighscoresWithUserData(body.Type, body.GuildId, body.Amount); + } + catch (HighscoreListEmptyException) + { + return NotFound(new ApiError + { + Message = $"No {body.Type} found on this server" + }); + } + + var response = new List(); + var counter = 1; + foreach (var item in list) + { + response.Add(new HighscoreControllerReponseBody + { + count = item.Value, + rank = counter, + user = item.Key + }); + counter++; + } + return Ok(response); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Highscores/HighscoreControllerPostBodyDto.cs b/Geekbot.net/WebApi/Controllers/Highscores/HighscoreControllerPostBodyDto.cs new file mode 100644 index 0000000..22da3c7 --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Highscores/HighscoreControllerPostBodyDto.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using Geekbot.net.Lib.Highscores; + +namespace Geekbot.net.WebApi.Controllers.Highscores +{ + public class HighscoreControllerPostBodyDto + { + [Required] + public ulong GuildId { get; set; } + + public HighscoreTypes Type { get; set; } = HighscoreTypes.messages; + + [Range(1, 150)] + public int Amount { get; set; } = 50; + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Highscores/HighscoreControllerReponseBody.cs b/Geekbot.net/WebApi/Controllers/Highscores/HighscoreControllerReponseBody.cs new file mode 100644 index 0000000..0e59880 --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Highscores/HighscoreControllerReponseBody.cs @@ -0,0 +1,11 @@ +using Geekbot.net.Lib.Highscores; + +namespace Geekbot.net.WebApi.Controllers.Highscores +{ + public class HighscoreControllerReponseBody + { + public int rank { get; set; } + public HighscoreUserDto user { get; set; } + public int count { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Status/ApiStatusDto.cs b/Geekbot.net/WebApi/Controllers/Status/ApiStatusDto.cs new file mode 100644 index 0000000..0d5e6ad --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Status/ApiStatusDto.cs @@ -0,0 +1,9 @@ +namespace Geekbot.net.WebApi.Controllers.Status +{ + public class ApiStatusDto + { + public string GeekbotVersion { get; set; } + public string ApiVersion { get; set; } + public string Status { get; set; } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Controllers/Status/StatusController.cs b/Geekbot.net/WebApi/Controllers/Status/StatusController.cs new file mode 100644 index 0000000..c437e64 --- /dev/null +++ b/Geekbot.net/WebApi/Controllers/Status/StatusController.cs @@ -0,0 +1,23 @@ +using System.Globalization; +using Geekbot.net.Lib; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; + +namespace Geekbot.net.WebApi.Controllers.Status +{ + [EnableCors("AllowSpecificOrigin")] + public class StatusController : Controller + { + [Route("/")] + public IActionResult GetCommands() + { + var responseBody = new ApiStatusDto + { + GeekbotVersion = Constants.BotVersion(), + ApiVersion = Constants.ApiVersion.ToString(CultureInfo.InvariantCulture), + Status = "Online" + }; + return Ok(responseBody); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Logging/AspLogProvider.cs b/Geekbot.net/WebApi/Logging/AspLogProvider.cs new file mode 100644 index 0000000..d35872d --- /dev/null +++ b/Geekbot.net/WebApi/Logging/AspLogProvider.cs @@ -0,0 +1,28 @@ +using System.Collections.Concurrent; +using Geekbot.net.Lib.Logger; +using Microsoft.Extensions.Logging; + +namespace Geekbot.net.WebApi.Logging +{ + public class AspLogProvider : ILoggerProvider + { + private readonly IGeekbotLogger _geekbotLogger; + + private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); + + public AspLogProvider(IGeekbotLogger geekbotLogger) + { + _geekbotLogger = geekbotLogger; + } + + public void Dispose() + { + _loggers.Clear(); + } + + public ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, name => new AspLogger(categoryName, _geekbotLogger)); + } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/Logging/AspLogger.cs b/Geekbot.net/WebApi/Logging/AspLogger.cs new file mode 100644 index 0000000..3316150 --- /dev/null +++ b/Geekbot.net/WebApi/Logging/AspLogger.cs @@ -0,0 +1,79 @@ +using System; +using Geekbot.net.Lib.Logger; +using Microsoft.Extensions.Logging; + +namespace Geekbot.net.WebApi.Logging +{ + public class AspLogger : ILogger + { + private readonly string _categoryName; + private readonly IGeekbotLogger _geekbotLogger; + + public AspLogger(string categoryName, IGeekbotLogger geekbotLogger) + { + geekbotLogger.Trace(LogSource.Api, $"Adding {categoryName}"); + _categoryName = categoryName; + _geekbotLogger = geekbotLogger; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + switch (logLevel) + { + case LogLevel.Trace: + _geekbotLogger.Trace(LogSource.Api, $"{eventId.Id} - {_categoryName} - {state}"); + break; + case LogLevel.Debug: + _geekbotLogger.Debug(LogSource.Api, $"{eventId.Id} - {_categoryName} - {state}"); + break; + case LogLevel.Information: + _geekbotLogger.Information(LogSource.Api, $"{eventId.Id} - {_categoryName} - {state}"); + break; + case LogLevel.Warning: + _geekbotLogger.Warning(LogSource.Api, $"{eventId.Id} - {_categoryName} - {state}", exception); + break; + case LogLevel.Error: + case LogLevel.Critical: + _geekbotLogger.Error(LogSource.Api, $"{eventId.Id} - {_categoryName} - {state}", exception); + break; + case LogLevel.None: + break; + default: + throw new ArgumentOutOfRangeException(nameof(logLevel)); + } + } + + public bool IsEnabled(LogLevel logLevel) + { + return !_geekbotLogger.LogAsJson() && _geekbotLogger.GetNLogger().IsEnabled(ToGeekbotLogLevel(logLevel)); + } + + public IDisposable BeginScope(TState state) + { + return null; + } + + private static NLog.LogLevel ToGeekbotLogLevel(LogLevel level) + { + switch (level) + { + case LogLevel.Trace: + return NLog.LogLevel.Trace; + case LogLevel.Debug: + return NLog.LogLevel.Debug; + case LogLevel.Information: + return NLog.LogLevel.Info; + case LogLevel.Warning: + return NLog.LogLevel.Warn; + case LogLevel.Error: + return NLog.LogLevel.Error; + case LogLevel.Critical: + return NLog.LogLevel.Fatal; + case LogLevel.None: + return NLog.LogLevel.Off; + default: + throw new ArgumentOutOfRangeException(nameof(level)); + } + } + } +} \ No newline at end of file diff --git a/Geekbot.net/WebApi/WebApiStartup.cs b/Geekbot.net/WebApi/WebApiStartup.cs new file mode 100644 index 0000000..2459362 --- /dev/null +++ b/Geekbot.net/WebApi/WebApiStartup.cs @@ -0,0 +1,58 @@ +using System.Net; +using System.Reflection; +using Discord.Commands; +using Discord.WebSocket; +using Geekbot.net.Database; +using Geekbot.net.Lib; +using Geekbot.net.Lib.GlobalSettings; +using Geekbot.net.Lib.Highscores; +using Geekbot.net.Lib.Logger; +using Geekbot.net.WebApi.Logging; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Geekbot.net.WebApi +{ + public static class WebApiStartup + { + public static void StartWebApi(IGeekbotLogger logger, RunParameters runParameters, CommandService commandService, + DatabaseContext databaseContext, DiscordSocketClient client, IGlobalSettings globalSettings, IHighscoreManager highscoreManager) + { + WebHost.CreateDefaultBuilder() + .UseKestrel(options => + { + options.Listen(IPAddress.Any, int.Parse(runParameters.ApiPort)); + }) + .ConfigureServices(services => + { + services.AddMvc(); + services.AddSingleton(commandService); + services.AddSingleton(databaseContext); + services.AddSingleton(client); + services.AddSingleton(globalSettings); + services.AddSingleton(highscoreManager); + services.AddCors(options => + { + options.AddPolicy("AllowSpecificOrigin", + builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); + }); + }) + .Configure(app => + { + app.UseMvc(); + app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().Build()); + }) + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.SetMinimumLevel(LogLevel.Debug); + logging.AddProvider(new AspLogProvider(logger)); + }) + .UseSetting(WebHostDefaults.ApplicationKey, typeof(Program).GetTypeInfo().Assembly.FullName) + .Build().Run(); + } + } +} \ No newline at end of file diff --git a/src/Startup/derp.ico b/Geekbot.net/derp.ico similarity index 100% rename from src/Startup/derp.ico rename to Geekbot.net/derp.ico diff --git a/tests/Core/Converters/EmojiConverter.test.cs b/Tests/Lib/Converters/EmojiConverter.test.cs similarity index 87% rename from tests/Core/Converters/EmojiConverter.test.cs rename to Tests/Lib/Converters/EmojiConverter.test.cs index 0bdc1f4..ad37824 100644 --- a/tests/Core/Converters/EmojiConverter.test.cs +++ b/Tests/Lib/Converters/EmojiConverter.test.cs @@ -1,7 +1,7 @@ -using Geekbot.Core.Converters; +using Geekbot.net.Lib.Converters; using Xunit; -namespace Tests.Core.Converters +namespace Tests.Lib.Converters { public class EmojiConverterTest { @@ -51,7 +51,8 @@ namespace Tests.Core.Converters [Theory, MemberData(nameof(NumberToEmojiTestData))] public void NumberToEmoji(string testName, NumberToEmojiTestDto testData) { - var result = EmojiConverter.NumberToEmoji(testData.Number); + var emojiConverter = new EmojiConverter(); + var result = emojiConverter.NumberToEmoji(testData.Number); Assert.Equal(result, testData.Expected); } @@ -85,7 +86,8 @@ namespace Tests.Core.Converters [Theory, MemberData(nameof(TextToEmojiTestData))] public void TextToEmoji(string testName, TextToEmojiTestDto testData) { - var result = EmojiConverter.TextToEmoji(testData.Text); + var emojiConverter = new EmojiConverter(); + var result = emojiConverter.TextToEmoji(testData.Text); Assert.Equal(result, testData.Expected); } } diff --git a/tests/Core/Levels/LevelCalc.test.cs b/Tests/Lib/Levels/LevelCalc.test.cs similarity index 91% rename from tests/Core/Levels/LevelCalc.test.cs rename to Tests/Lib/Levels/LevelCalc.test.cs index 02db6f5..9b4b97c 100644 --- a/tests/Core/Levels/LevelCalc.test.cs +++ b/Tests/Lib/Levels/LevelCalc.test.cs @@ -1,7 +1,8 @@ -using Geekbot.Core.Levels; +using System.Collections.Generic; +using Geekbot.net.Lib.Levels; using Xunit; -namespace Tests.Core.Levels +namespace Tests.Lib.Levels { public class LevelCalcTest { diff --git a/Tests/Lib/Localization/TranslationGuildContext.test.cs b/Tests/Lib/Localization/TranslationGuildContext.test.cs new file mode 100644 index 0000000..aeea0e1 --- /dev/null +++ b/Tests/Lib/Localization/TranslationGuildContext.test.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Geekbot.net.Lib.Localization; +using Moq; +using Xunit; + +namespace Tests.Lib.Localization +{ + public class TranslationGuildContext_test + { + public class FormatDateTimeAsRemainingTestDto + { + public DateTimeOffset DateTime { get; set; } + public string Expected { get; set; } + } + + public static TestData FormatDateTimeAsRemainingData => + new TestData + { + { + "Wait for days", + new FormatDateTimeAsRemainingTestDto + { + DateTime = DateTimeOffset.Now.AddDays(5), + Expected = "4 days, 23 hours, 59 minutes and 59 seconds" + } + }, + { + "Wait for minutes", + new FormatDateTimeAsRemainingTestDto + { + DateTime = DateTimeOffset.Now.AddMinutes(5), + Expected = "4 minutes and 59 seconds" + } + }, + { + "Wait for seconds", + new FormatDateTimeAsRemainingTestDto + { + DateTime = DateTimeOffset.Now.AddSeconds(5), + Expected = "4 seconds" + } + } + }; + + [Theory, MemberData(nameof(FormatDateTimeAsRemainingData))] + public void FormatDateTimeAsRemaining(string testName, FormatDateTimeAsRemainingTestDto testData) + { + var translationHandlerMock = new Mock(MockBehavior.Loose); + translationHandlerMock + .Setup(thm => thm.GetString("EN", "dateTime", "Days")) + .Returns("day|days"); + translationHandlerMock + .Setup(thm => thm.GetString("EN", "dateTime", "Hours")) + .Returns("hour|hours"); + translationHandlerMock + .Setup(thm => thm.GetString("EN", "dateTime", "Minutes")) + .Returns("minute|minutes"); + translationHandlerMock + .Setup(thm => thm.GetString("EN", "dateTime", "Seconds")) + .Returns("second|seconds"); + translationHandlerMock + .Setup(thm => thm.GetString("EN", "dateTime", "And")) + .Returns("and"); + + var context = new TranslationGuildContext(translationHandlerMock.Object, "EN", new Dictionary()); + var result = context.FormatDateTimeAsRemaining(testData.DateTime); + Assert.Equal(result, testData.Expected); + } + } +} \ No newline at end of file diff --git a/Tests/Lib/Localization/Translations.test.cs b/Tests/Lib/Localization/Translations.test.cs new file mode 100644 index 0000000..fc43091 --- /dev/null +++ b/Tests/Lib/Localization/Translations.test.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FluentAssertions; +using Xunit; +using YamlDotNet.Serialization; + +namespace Tests.Lib.Localization +{ + public class Translations_test + { + [Fact] + public void TranslationsYamlIsValid() + { + // Read the file + var translationFile = File.ReadAllText(Path.GetFullPath("./../../../../Geekbot.net/Lib/Localization/Translations.yml")); + + // Deserialize + var input = new StringReader(translationFile); + var deserializer = new DeserializerBuilder().Build(); + var rawTranslations = deserializer.Deserialize>>>(input); + + // These languages must be supported + var supportedLanguages = new List + { + "EN", + "CHDE" + }; + + // Iterate every single key to make sure it's populated + foreach (var command in rawTranslations) + { + foreach (var str in command.Value) + { + str.Value.Select(e => e.Key).ToList().Should().BeEquivalentTo(supportedLanguages, str.Key); + foreach (var lang in str.Value) + { + lang.Value.Should().NotBeNullOrEmpty($"{command.Key} / {str.Key} / {lang.Key}"); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/TestData.cs b/Tests/TestData.cs similarity index 100% rename from tests/TestData.cs rename to Tests/TestData.cs diff --git a/tests/Tests.csproj b/Tests/Tests.csproj similarity index 55% rename from tests/Tests.csproj rename to Tests/Tests.csproj index 786164e..1c07b9c 100644 --- a/tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,21 +1,20 @@  - net6.0 - win10-x64;linux-x64;linux-musl-x64 - false + netcoreapp2.2 false NU1701 xUnit1026 - - - + + + + - + \ No newline at end of file diff --git a/src/Core/WikipediaClient/IWikipediaClient.cs b/WikipediaApi/IWikipediaClient.cs similarity index 65% rename from src/Core/WikipediaClient/IWikipediaClient.cs rename to WikipediaApi/IWikipediaClient.cs index fdb1ae1..4d1dae9 100644 --- a/src/Core/WikipediaClient/IWikipediaClient.cs +++ b/WikipediaApi/IWikipediaClient.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; -using Geekbot.Core.WikipediaClient.Page; +using WikipediaApi.Page; -namespace Geekbot.Core.WikipediaClient +namespace WikipediaApi { public interface IWikipediaClient { diff --git a/WikipediaApi/Page/PageApiUrls.cs b/WikipediaApi/Page/PageApiUrls.cs new file mode 100644 index 0000000..8a121be --- /dev/null +++ b/WikipediaApi/Page/PageApiUrls.cs @@ -0,0 +1,14 @@ +using System; + +namespace WikipediaApi.Page +{ + public class PageApiUrls + { + public Uri Summary { get; set; } + public Uri Metadata { get; set; } + public Uri References { get; set; } + public Uri Media { get; set; } + public Uri EditHtml { get; set; } + public Uri TalkPageHtml { get; set; } + } +} \ No newline at end of file diff --git a/WikipediaApi/Page/PageContentUrlCollection.cs b/WikipediaApi/Page/PageContentUrlCollection.cs new file mode 100644 index 0000000..f6c680b --- /dev/null +++ b/WikipediaApi/Page/PageContentUrlCollection.cs @@ -0,0 +1,8 @@ +namespace WikipediaApi.Page +{ + public class PageContentUrlCollection + { + public PageContentUrls Desktop { get; set; } + public PageContentUrls Mobile { get; set; } + } +} \ No newline at end of file diff --git a/WikipediaApi/Page/PageContentUrls.cs b/WikipediaApi/Page/PageContentUrls.cs new file mode 100644 index 0000000..64a80dc --- /dev/null +++ b/WikipediaApi/Page/PageContentUrls.cs @@ -0,0 +1,12 @@ +using System; + +namespace WikipediaApi.Page +{ + public class PageContentUrls + { + public Uri Page { get; set; } + public Uri Revisions { get; set; } + public Uri Edit { get; set; } + public Uri Talk { get; set; } + } +} \ No newline at end of file diff --git a/WikipediaApi/Page/PageCoordinates.cs b/WikipediaApi/Page/PageCoordinates.cs new file mode 100644 index 0000000..7fa42ba --- /dev/null +++ b/WikipediaApi/Page/PageCoordinates.cs @@ -0,0 +1,8 @@ +namespace WikipediaApi.Page +{ + public class PageCoordinates + { + public float Lat { get; set; } + public float Lon { get; set; } + } +} \ No newline at end of file diff --git a/WikipediaApi/Page/PageImage.cs b/WikipediaApi/Page/PageImage.cs new file mode 100644 index 0000000..4a8b28d --- /dev/null +++ b/WikipediaApi/Page/PageImage.cs @@ -0,0 +1,12 @@ +using System; + +namespace WikipediaApi.Page +{ + public class PageImage + { + public Uri Source { get; set; } + public int Width { get; set; } + public int Height { get; set; } + + } +} \ No newline at end of file diff --git a/WikipediaApi/Page/PageNamespace.cs b/WikipediaApi/Page/PageNamespace.cs new file mode 100644 index 0000000..66600b6 --- /dev/null +++ b/WikipediaApi/Page/PageNamespace.cs @@ -0,0 +1,8 @@ +namespace WikipediaApi.Page +{ + public class PageNamespace + { + public ulong Id { get; set; } + public string Text { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PagePreview.cs b/WikipediaApi/Page/PagePreview.cs similarity index 53% rename from src/Core/WikipediaClient/Page/PagePreview.cs rename to WikipediaApi/Page/PagePreview.cs index b87a05e..8db9dad 100644 --- a/src/Core/WikipediaClient/Page/PagePreview.cs +++ b/WikipediaApi/Page/PagePreview.cs @@ -1,65 +1,67 @@ using System; -using System.Text.Json.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; -namespace Geekbot.Core.WikipediaClient.Page +namespace WikipediaApi.Page { public class PagePreview { - [JsonPropertyName("type")] + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] public PageTypes Type { get; set; } - [JsonPropertyName("title")] + [JsonProperty("title")] public string Title { get; set; } - [JsonPropertyName("displaytitle")] + [JsonProperty("displaytitle")] public string Displaytitle { get; set; } - [JsonPropertyName("namespace")] + [JsonProperty("namespace")] public PageNamespace Namespace { get; set; } - [JsonPropertyName("titles")] + [JsonProperty("titles")] public PageTitles Titles { get; set; } - [JsonPropertyName("pageid")] + [JsonProperty("pageid")] public ulong Pageid { get; set; } - [JsonPropertyName("thumbnail")] + [JsonProperty("thumbnail")] public PageImage Thumbnail { get; set; } - [JsonPropertyName("originalimage")] + [JsonProperty("originalimage")] public PageImage Originalimage { get; set; } - [JsonPropertyName("lang")] + [JsonProperty("lang")] public string Lang { get; set; } - [JsonPropertyName("dir")] + [JsonProperty("dir")] public string Dir { get; set; } - [JsonPropertyName("revision")] - public string Revision { get; set; } + [JsonProperty("revision")] + public ulong Revision { get; set; } - [JsonPropertyName("tid")] + [JsonProperty("tid")] public string Tid { get; set; } - [JsonPropertyName("timestamp")] + [JsonProperty("timestamp")] public DateTimeOffset Timestamp { get; set; } - [JsonPropertyName("description")] + [JsonProperty("description")] public string Description { get; set; } - [JsonPropertyName("coordinates")] + [JsonProperty("coordinates")] public PageCoordinates Coordinates { get; set; } - [JsonPropertyName("content_urls")] + [JsonProperty("content_urls")] public PageContentUrlCollection ContentUrls { get; set; } - [JsonPropertyName("api_urls")] + [JsonProperty("api_urls")] public PageApiUrls ApiUrls { get; set; } - [JsonPropertyName("extract")] + [JsonProperty("extract")] public string Extract { get; set; } - [JsonPropertyName("extract_html")] + [JsonProperty("extract_html")] public string ExtractHtml { get; set; } } } \ No newline at end of file diff --git a/WikipediaApi/Page/PageTitles.cs b/WikipediaApi/Page/PageTitles.cs new file mode 100644 index 0000000..31a55b9 --- /dev/null +++ b/WikipediaApi/Page/PageTitles.cs @@ -0,0 +1,10 @@ +namespace WikipediaApi.Page +{ + public class PageTitles + { + public string Canonical { get; set; } + public string Normalized { get; set; } + public string Display { get; set; } + + } +} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageTypes.cs b/WikipediaApi/Page/PageTypes.cs similarity index 70% rename from src/Core/WikipediaClient/Page/PageTypes.cs rename to WikipediaApi/Page/PageTypes.cs index 9ad748a..a415d75 100644 --- a/src/Core/WikipediaClient/Page/PageTypes.cs +++ b/WikipediaApi/Page/PageTypes.cs @@ -1,9 +1,7 @@ using System.Runtime.Serialization; -using System.Text.Json.Serialization; -namespace Geekbot.Core.WikipediaClient.Page +namespace WikipediaApi.Page { - [JsonConverter(typeof(JsonStringEnumConverter))] public enum PageTypes { [EnumMember(Value = "standard")] diff --git a/WikipediaApi/WikipediaApi.csproj b/WikipediaApi/WikipediaApi.csproj new file mode 100644 index 0000000..3e82c68 --- /dev/null +++ b/WikipediaApi/WikipediaApi.csproj @@ -0,0 +1,8 @@ + + + netcoreapp2.2 + + + + + \ No newline at end of file diff --git a/src/Core/WikipediaClient/WikipediaClient.cs b/WikipediaApi/WikipediaClient.cs similarity index 75% rename from src/Core/WikipediaClient/WikipediaClient.cs rename to WikipediaApi/WikipediaClient.cs index cf13277..6576f3d 100644 --- a/src/Core/WikipediaClient/WikipediaClient.cs +++ b/WikipediaApi/WikipediaClient.cs @@ -1,9 +1,9 @@ using System.Net.Http; -using System.Text.Json; using System.Threading.Tasks; -using Geekbot.Core.WikipediaClient.Page; +using Newtonsoft.Json; +using WikipediaApi.Page; -namespace Geekbot.Core.WikipediaClient +namespace WikipediaApi { public class WikipediaClient : IWikipediaClient { @@ -19,7 +19,7 @@ namespace Geekbot.Core.WikipediaClient response.EnsureSuccessStatusCode(); var stringResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(stringResponse); + return JsonConvert.DeserializeObject(stringResponse); } } } \ No newline at end of file diff --git a/ansible-requirements.yml b/ansible-requirements.yml deleted file mode 100644 index 90d8fb4..0000000 --- a/ansible-requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -collections: - - name: community.docker - version: 2.7.0 \ No newline at end of file diff --git a/readme.md b/readme.md index 883e553..7d8544f 100644 --- a/readme.md +++ b/readme.md @@ -1,28 +1,29 @@ -[![pipeline status](https://gitlab.com/dbgit/open/geekbot/badges/master/pipeline.svg)](https://gitlab.com/dbgit/open/geekbot/commits/master) +[![pipeline status](https://git.boerlage.me/open/Geekbot.net/badges/master/pipeline.svg)](https://git.boerlage.me/open/Geekbot.net/commits/master) # [Geekbot.net](https://geekbot.pizzaandcoffee.rocks/) -A General Purpose Discord Bot written in C# +A General Purpose Discord Bot written in DotNet Core. You can invite Geekbot to your server with [this link](https://discordapp.com/oauth2/authorize?client_id=171249478546882561&scope=bot&permissions=1416834054) ## Technologies -* .NET 5 -* PostgreSQL +* DotNet Core 2 +* Redis * Discord.net ## Running -You can start geekbot with: `dotnet run` +Make sure redis is running -On your first run geekbot will ask for your bot token. +Run these commands -You might need to pass some additional configuration (e.g. database credentials), these can be passed as commandline arguments or environment variables. +* `dotnet restore` +* `dotnet run` -For a list of commandline arguments and environment variables use `dotnet run -- -h` +On your first run geekbot will ask for your bot token, everything else is taken care of. -All Environment Variables must be prefixed with `GEEKBOT_` +For a list of launch options use `dotnet run -h` ## Contributing diff --git a/src/Bot/Bot.csproj b/src/Bot/Bot.csproj deleted file mode 100644 index 2219b32..0000000 --- a/src/Bot/Bot.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - net6.0 - $(VersionSuffix) - Geekbot.Bot - Geekbot.Bot - $(VersionSuffix) - 0.0.0-DEV - NU1701 - enable - True - Library - - - - - - - - - - - - - - PreserveNewest - - - - - - - diff --git a/src/Bot/BotStartup.cs b/src/Bot/BotStartup.cs deleted file mode 100644 index afb1a8a..0000000 --- a/src/Bot/BotStartup.cs +++ /dev/null @@ -1,127 +0,0 @@ -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 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(_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()); - var userHandler = new UserHandler(serviceProvider.GetService(), _logger, serviceProvider.GetService(), _client); - var reactionHandler = new ReactionHandler(serviceProvider.GetService()); - var statsHandler = new StatsHandler(_logger, serviceProvider.GetService()); - var messageDeletedHandler = new MessageDeletedHandler(serviceProvider.GetService(), _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; - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Admin/Admin.cs b/src/Bot/Commands/Admin/Admin.cs deleted file mode 100644 index 43fb3c4..0000000 --- a/src/Bot/Commands/Admin/Admin.cs +++ /dev/null @@ -1,253 +0,0 @@ -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(); - 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 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 GetAvailableCultures() - { - var result = new List(); - 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; - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Games/Roll/Roll.cs b/src/Bot/Commands/Games/Roll/Roll.cs deleted file mode 100644 index e9ed9e9..0000000 --- a/src/Bot/Commands/Games/Roll/Roll.cs +++ /dev/null @@ -1,50 +0,0 @@ -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; - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/LolMmr/LolMmr.cs b/src/Bot/Commands/Integrations/LolMmr/LolMmr.cs deleted file mode 100644 index 511d7b8..0000000 --- a/src/Bot/Commands/Integrations/LolMmr/LolMmr.cs +++ /dev/null @@ -1,61 +0,0 @@ -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(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); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/LolMmr/LolMmrDto.cs b/src/Bot/Commands/Integrations/LolMmr/LolMmrDto.cs deleted file mode 100644 index 233bcfc..0000000 --- a/src/Bot/Commands/Integrations/LolMmr/LolMmrDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -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; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/LolMmr/LolMrrInfoDto.cs b/src/Bot/Commands/Integrations/LolMmr/LolMrrInfoDto.cs deleted file mode 100644 index fbcc49a..0000000 --- a/src/Bot/Commands/Integrations/LolMmr/LolMrrInfoDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Integrations.LolMmr -{ - public class LolMrrInfoDto - { - [JsonPropertyName("avg")] - public decimal? Avg { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/Mal.cs b/src/Bot/Commands/Integrations/Mal.cs deleted file mode 100644 index ffc8dd7..0000000 --- a/src/Bot/Commands/Integrations/Mal.cs +++ /dev/null @@ -1,108 +0,0 @@ -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); - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/UrbanDictionary.cs b/src/Bot/Commands/Integrations/UrbanDictionary.cs deleted file mode 100644 index 44fe868..0000000 --- a/src/Bot/Commands/Integrations/UrbanDictionary.cs +++ /dev/null @@ -1,39 +0,0 @@ -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); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Integrations/Youtube.cs b/src/Bot/Commands/Integrations/Youtube.cs deleted file mode 100644 index 50e9519..0000000 --- a/src/Bot/Commands/Integrations/Youtube.cs +++ /dev/null @@ -1,59 +0,0 @@ -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); - // } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Cat/Cat.cs b/src/Bot/Commands/Randomness/Cat/Cat.cs deleted file mode 100644 index 1198113..0000000 --- a/src/Bot/Commands/Randomness/Cat/Cat.cs +++ /dev/null @@ -1,38 +0,0 @@ -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(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); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Cat/CatResponseDto.cs b/src/Bot/Commands/Randomness/Cat/CatResponseDto.cs deleted file mode 100644 index 523613b..0000000 --- a/src/Bot/Commands/Randomness/Cat/CatResponseDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Randomness.Cat -{ - internal class CatResponseDto - { - [JsonPropertyName("file")] - public string File { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokeResponseDto.cs b/src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokeResponseDto.cs deleted file mode 100644 index 7c0aefa..0000000 --- a/src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokeResponseDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Randomness.Chuck -{ - internal class ChuckNorrisJokeResponseDto - { - [JsonPropertyName("value")] - public string Value { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokes.cs b/src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokes.cs deleted file mode 100644 index e2b1f16..0000000 --- a/src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokes.cs +++ /dev/null @@ -1,41 +0,0 @@ -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(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); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Dad/DadJokeResponseDto.cs b/src/Bot/Commands/Randomness/Dad/DadJokeResponseDto.cs deleted file mode 100644 index 012f9e9..0000000 --- a/src/Bot/Commands/Randomness/Dad/DadJokeResponseDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Randomness.Dad -{ - internal class DadJokeResponseDto - { - [JsonPropertyName("joke")] - public string Joke { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Dad/DadJokes.cs b/src/Bot/Commands/Randomness/Dad/DadJokes.cs deleted file mode 100644 index 67f9679..0000000 --- a/src/Bot/Commands/Randomness/Dad/DadJokes.cs +++ /dev/null @@ -1,33 +0,0 @@ -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(new Uri("https://icanhazdadjoke.com/")); - await ReplyAsync(response.Joke); - } - catch (Exception e) - { - await _errorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Dog/Dog.cs b/src/Bot/Commands/Randomness/Dog/Dog.cs deleted file mode 100644 index 39e57c7..0000000 --- a/src/Bot/Commands/Randomness/Dog/Dog.cs +++ /dev/null @@ -1,34 +0,0 @@ -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(new Uri("http://random.dog/woof.json")); - await ReplyAsync(response.Url); - } - catch (Exception e) - { - await _errorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Dog/DogResponseDto.cs b/src/Bot/Commands/Randomness/Dog/DogResponseDto.cs deleted file mode 100644 index 9f0dfce..0000000 --- a/src/Bot/Commands/Randomness/Dog/DogResponseDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Randomness.Dog -{ - internal class DogResponseDto - { - [JsonPropertyName("url")] - public string Url { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/EightBall.cs b/src/Bot/Commands/Randomness/EightBall.cs deleted file mode 100644 index b5f0c3a..0000000 --- a/src/Bot/Commands/Randomness/EightBall.cs +++ /dev/null @@ -1,41 +0,0 @@ -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(); - 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); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Kanye/Kanye.cs b/src/Bot/Commands/Randomness/Kanye/Kanye.cs deleted file mode 100644 index e5d2e95..0000000 --- a/src/Bot/Commands/Randomness/Kanye/Kanye.cs +++ /dev/null @@ -1,33 +0,0 @@ -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(new Uri("https://api.kanye.rest/")); - await ReplyAsync(response.Quote); - } - catch (Exception e) - { - await _errorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Randomness/Kanye/KanyeResponseDto.cs b/src/Bot/Commands/Randomness/Kanye/KanyeResponseDto.cs deleted file mode 100644 index ab8c06f..0000000 --- a/src/Bot/Commands/Randomness/Kanye/KanyeResponseDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Randomness.Kanye -{ - public class KanyeResponseDto - { - [JsonPropertyName("quote")] - public string Quote { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/User/Karma.cs b/src/Bot/Commands/User/Karma.cs deleted file mode 100644 index 469c371..0000000 --- a/src/Bot/Commands/User/Karma.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Threading.Tasks; -using Discord; -using Discord.Commands; -using Geekbot.Bot.CommandPreconditions; -using Geekbot.Commands.Karma; -using Geekbot.Core; -using Geekbot.Core.Database; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.Extensions; -using Geekbot.Core.GuildSettingsManager; - -namespace Geekbot.Bot.Commands.User -{ - [DisableInDirectMessage] - public class Karma : GeekbotCommandBase - { - private readonly DatabaseContext _database; - - public Karma(DatabaseContext database, IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager) - { - _database = database; - } - - [Command("good", RunMode = RunMode.Async)] - [Summary("Increase Someones Karma")] - public async Task Good([Summary("@someone")] IUser user) - { - await ChangeKarma(user, KarmaChange.Up); - } - - [Command("bad", RunMode = RunMode.Async)] - [Summary("Decrease Someones Karma")] - public async Task Bad([Summary("@someone")] IUser user) - { - await ChangeKarma(user, KarmaChange.Down); - } - - [Command("neutral", RunMode = RunMode.Async)] - [Summary("Do nothing to someones Karma")] - public async Task Neutral([Summary("@someone")] IUser user) - { - await ChangeKarma(user, KarmaChange.Same); - } - - private async Task ChangeKarma(IUser user, KarmaChange change) - { - try - { - var author = new Interactions.Resolved.User() - { - Id = Context.User.Id.ToString(), - Username = Context.User.Username, - Discriminator = Context.User.Discriminator, - Avatar = Context.User.AvatarId, - }; - var targetUser = new Interactions.Resolved.User() - { - Id = user.Id.ToString(), - Username = user.Username, - Discriminator = user.Discriminator, - Avatar = user.AvatarId, - }; - - var karma = new Geekbot.Commands.Karma.Karma(_database, Context.Guild.Id.AsLong()); - var res = await karma.ChangeKarma(author, targetUser, change); - - await ReplyAsync(string.Empty, false, res.ToDiscordNetEmbed().Build()); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/User/Rank.cs b/src/Bot/Commands/User/Rank.cs deleted file mode 100644 index 639469f..0000000 --- a/src/Bot/Commands/User/Rank.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Discord.Commands; -using Geekbot.Bot.CommandPreconditions; -using Geekbot.Core; -using Geekbot.Core.Database; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.GuildSettingsManager; -using Geekbot.Core.Highscores; - -namespace Geekbot.Bot.Commands.User -{ - public class Rank : GeekbotCommandBase - { - private readonly IHighscoreManager _highscoreManager; - private readonly DatabaseContext _database; - - public Rank(DatabaseContext database, IErrorHandler errorHandler, IHighscoreManager highscoreManager, IGuildSettingsManager guildSettingsManager) - : base(errorHandler, guildSettingsManager) - { - _database = database; - _highscoreManager = highscoreManager; - } - - [Command("rank", RunMode = RunMode.Async)] - [Summary("Get the highscore for various stats like message count, karma, correctly guessed roles, etc...")] - [DisableInDirectMessage] - public async Task RankCmd( - [Summary("type")] string typeUnformated = "messages", - [Summary("amount")] int amount = 10, - [Summary("season")] string season = null) - { - try - { - var res = new Geekbot.Commands.Rank(_database, _highscoreManager) - .Run(typeUnformated, amount, season, Context.Guild.Id, Context.Guild.Name); - await ReplyAsync(res); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Changelog/Changelog.cs b/src/Bot/Commands/Utils/Changelog/Changelog.cs deleted file mode 100644 index 989ac0d..0000000 --- a/src/Bot/Commands/Utils/Changelog/Changelog.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Discord; -using Discord.Commands; -using Discord.WebSocket; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; - -namespace Geekbot.Bot.Commands.Utils.Changelog -{ - public class Changelog : TransactionModuleBase - { - private readonly DiscordSocketClient _client; - private readonly IErrorHandler _errorHandler; - - public Changelog(IErrorHandler errorHandler, DiscordSocketClient client) - { - _errorHandler = errorHandler; - _client = client; - } - - [Command("changelog", RunMode = RunMode.Async)] - [Summary("Show the latest 10 updates")] - public async Task GetChangelog() - { - try - { - var commits = await HttpAbstractions.Get>(new Uri("https://api.github.com/repos/pizzaandcoffee/geekbot.net/commits")); - - 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) - { - await _errorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Changelog/CommitAuthorDto.cs b/src/Bot/Commands/Utils/Changelog/CommitAuthorDto.cs deleted file mode 100644 index 19d93eb..0000000 --- a/src/Bot/Commands/Utils/Changelog/CommitAuthorDto.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Utils.Changelog -{ - public class CommitAuthorDto - { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("email")] - public string Email { get; set; } - - [JsonPropertyName("date")] - public DateTimeOffset Date { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Changelog/CommitDto.cs b/src/Bot/Commands/Utils/Changelog/CommitDto.cs deleted file mode 100644 index e67d08c..0000000 --- a/src/Bot/Commands/Utils/Changelog/CommitDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Utils.Changelog -{ - public class CommitDto - { - [JsonPropertyName("commit")] - public CommitInfoDto Commit { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Changelog/CommitInfoDto.cs b/src/Bot/Commands/Utils/Changelog/CommitInfoDto.cs deleted file mode 100644 index 592da9e..0000000 --- a/src/Bot/Commands/Utils/Changelog/CommitInfoDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Bot.Commands.Utils.Changelog -{ - public class CommitInfoDto - { - [JsonPropertyName("author")] - public CommitAuthorDto Author { get; set; } - - [JsonPropertyName("message")] - public string Message { get; set; } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Choose.cs b/src/Bot/Commands/Utils/Choose.cs deleted file mode 100644 index 450433d..0000000 --- a/src/Bot/Commands/Utils/Choose.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.GuildSettingsManager; -using Localization = Geekbot.Core.Localization; - -namespace Geekbot.Bot.Commands.Utils -{ - public class Choose : GeekbotCommandBase - { - public Choose(IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager) : base(errorHandler, guildSettingsManager) - { - } - - [Command("choose", RunMode = RunMode.Async)] - [Summary("Let the bot choose for you, separate options with a semicolon.")] - public async Task Command([Remainder] [Summary("option1;option2")] - string choices) - { - try - { - var choicesArray = choices.Split(';'); - var choice = new Random().Next(choicesArray.Length); - await ReplyAsync(string.Format(Localization.Choose.Choice, choicesArray[choice].Trim())); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Dice.cs b/src/Bot/Commands/Utils/Dice.cs deleted file mode 100644 index c57001f..0000000 --- a/src/Bot/Commands/Utils/Dice.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.DiceParser; -using Geekbot.Core.ErrorHandling; - -namespace Geekbot.Bot.Commands.Utils -{ - public class Dice : TransactionModuleBase - { - private readonly IErrorHandler _errorHandler; - private readonly IDiceParser _diceParser; - - public Dice(IErrorHandler errorHandler, IDiceParser diceParser) - { - _errorHandler = errorHandler; - _diceParser = diceParser; - } - - // ToDo: Separate representation and logic - // ToDo: Translate - [Command("dice", RunMode = RunMode.Async)] - [Summary("Roll a dice. (use '!dice help' for a manual)")] - public async Task RollCommand([Remainder] [Summary("input")] string diceInput = "1d20") - { - try - { - if (diceInput == "help") - { - await ShowDiceHelp(); - return; - } - - var parsed = _diceParser.Parse(diceInput); - - var sb = new StringBuilder(); - sb.AppendLine($"{Context.User.Mention} :game_die:"); - foreach (var die in parsed.Dice) - { - sb.AppendLine($"**{die.DiceName}**"); - var diceResultList = new List(); - var total = 0; - - foreach (var roll in die.Roll()) - { - diceResultList.Add(roll.ToString()); - total += roll.Result; - } - - sb.AppendLine(string.Join(" | ", diceResultList)); - - if (parsed.SkillModifier != 0) - { - sb.AppendLine($"Skill: {parsed.SkillModifier}"); - } - - if (parsed.Options.ShowTotal) - { - var totalLine = $"Total: {total}"; - if (parsed.SkillModifier > 0) - { - totalLine += ($" (+{parsed.SkillModifier} = {total + parsed.SkillModifier})"); - } - - if (parsed.SkillModifier < 0) - { - totalLine += ($" ({parsed.SkillModifier} = {total - parsed.SkillModifier})"); - } - - sb.AppendLine(totalLine); - } - } - - await Context.Channel.SendMessageAsync(sb.ToString()); - } - catch (DiceException e) - { - await Context.Channel.SendMessageAsync($"**:warning: {e.DiceName} is invalid:** {e.Message}"); - } - catch (Exception e) - { - await _errorHandler.HandleCommandException(e, Context); - } - } - - private async Task ShowDiceHelp() - { - var sb = new StringBuilder(); - sb.AppendLine("**__Examples__**"); - sb.AppendLine("```"); - sb.AppendLine("!dice - throw a 1d20"); - sb.AppendLine("!dice 1d12 - throw a 1d12"); - sb.AppendLine("!dice +1d20 - throw with advantage"); - sb.AppendLine("!dice -1d20 - throw with disadvantage"); - sb.AppendLine("!dice 1d20 +2 - throw with a +2 skill bonus"); - sb.AppendLine("!dice 1d20 -2 - throw with a -2 skill bonus"); - sb.AppendLine("!dice 8d6 - throw a fireball 🔥"); - sb.AppendLine("!dice 8d6 total - calculate the total"); - sb.AppendLine("!dice 2d20 6d6 2d12 - drop your dice pouch"); - sb.AppendLine("```"); - - await Context.Channel.SendMessageAsync(sb.ToString()); - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Lmgtfy.cs b/src/Bot/Commands/Utils/Lmgtfy.cs deleted file mode 100644 index 76fa6fa..0000000 --- a/src/Bot/Commands/Utils/Lmgtfy.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Web; -using Discord.Commands; -using Geekbot.Core; -using Geekbot.Core.ErrorHandling; - -namespace Geekbot.Bot.Commands.Utils -{ - public class Lmgtfy : TransactionModuleBase - { - private readonly IErrorHandler _errorHandler; - - public Lmgtfy(IErrorHandler errorHandler) - { - _errorHandler = errorHandler; - } - - [Command("lmgtfy", RunMode = RunMode.Async)] - [Summary("Get a 'Let me google that for you' link")] - public async Task GetUrl([Remainder] [Summary("question")] string question) - { - try - { - var encoded = HttpUtility.UrlEncode(question).Replace("%20", "+"); - await Context.Channel.SendMessageAsync($""); - } - catch (Exception e) - { - await _errorHandler.HandleCommandException(e, Context); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Quote/MessageLink.cs b/src/Bot/Commands/Utils/Quote/MessageLink.cs deleted file mode 100644 index dff1273..0000000 --- a/src/Bot/Commands/Utils/Quote/MessageLink.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Data; -using System.Text.RegularExpressions; - -namespace Geekbot.Bot.Commands.Utils.Quote -{ - public class MessageLink - { - public readonly static Regex re = new Regex( - @"https:\/\/((canary|ptb)\.)?discord(app)?.com\/channels\/(?\d{16,20})\/(?\d{16,20})\/(?\d{16,20})", - RegexOptions.Compiled | RegexOptions.IgnoreCase, - new TimeSpan(0, 0, 2)); - - public ulong GuildId { get; set; } - public ulong ChannelId { get; set; } - public ulong MessageId { get; set; } - - public MessageLink(string url) - { - var matches = re.Matches(url); - - foreach (Match match in matches) - { - foreach (Group matchGroup in match.Groups) - { - switch (matchGroup.Name) - { - case "GuildId": - GuildId = ulong.Parse(matchGroup.Value); - break; - case "ChannelId": - ChannelId = ulong.Parse(matchGroup.Value); - break; - case "MessageId": - MessageId = ulong.Parse(matchGroup.Value); - break; - } - } - } - } - - public static bool IsValid(string link) - { - return re.IsMatch(link); - } - } -} \ No newline at end of file diff --git a/src/Bot/Commands/Utils/Quote/Quote.cs b/src/Bot/Commands/Utils/Quote/Quote.cs deleted file mode 100644 index 243e5dd..0000000 --- a/src/Bot/Commands/Utils/Quote/Quote.cs +++ /dev/null @@ -1,322 +0,0 @@ -using System; -using System.Linq; -using System.Text; -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.Polyfills; -using Geekbot.Core.RandomNumberGenerator; -using Geekbot.Core.UserRepository; -using Microsoft.EntityFrameworkCore; -using Sentry; -using Constants = Geekbot.Core.Constants; -using Localization = Geekbot.Core.Localization; - -namespace Geekbot.Bot.Commands.Utils.Quote -{ - [Group("quote")] - [DisableInDirectMessage] - public class Quote : GeekbotCommandBase - { - private readonly DatabaseContext _database; - private readonly IRandomNumberGenerator _randomNumberGenerator; - private readonly IUserRepository _userRepository; - private readonly bool _isDev; - - public Quote(IErrorHandler errorHandler, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator, IGuildSettingsManager guildSettingsManager, IUserRepository userRepository) - : base(errorHandler, guildSettingsManager) - { - _database = database; - _randomNumberGenerator = randomNumberGenerator; - _userRepository = userRepository; - // to remove restrictions when developing - _isDev = Constants.BotVersion() == "0.0.0-DEV"; - } - - [Command] - [Summary("Return a random quote from the database")] - public async Task GetRandomQuote() - { - try - { - var getQuoteFromDbSpan = Transaction.StartChild("GetQuoteFromDB"); - var quote = _database.Quotes.FromSqlInterpolated($"select * from \"Quotes\" where \"GuildId\" = {Context.Guild.Id} order by random() limit 1"); - getQuoteFromDbSpan.Finish(); - - if (!quote.Any()) - { - await ReplyAsync(Localization.Quote.NoQuotesFound); - Transaction.Status = SpanStatus.NotFound; - return; - } - - var buildQuoteEmbedSpan = Transaction.StartChild("BuildQuoteEmbed"); - var embed = QuoteBuilder(quote.FirstOrDefault()); - buildQuoteEmbedSpan.Finish(); - await ReplyAsync("", false, embed.Build()); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context, "Whoops, seems like the quote was to edgy to return"); - Transaction.Status = SpanStatus.InternalError; - } - } - - [Command("add")] - [Alias("save")] - [Summary("Add a quote from the last sent message by @user")] - public async Task AddQuote([Summary("@someone")] IUser user) - { - await QuoteFromMention(user, true); - } - - [Command("make")] - [Alias("preview")] - [Summary("Preview a quote from the last sent message by @user")] - public async Task ReturnSpecifiedQuote([Summary("@someone")] IUser user) - { - await QuoteFromMention(user, false); - } - - [Command("add")] - [Alias("save")] - [Summary("Add a quote from a message link")] - public async Task AddQuote([Summary("message-link")] string messageLink) - { - await QuoteFromMessageLink(messageLink, true); - } - - [Command("make")] - [Alias("preview")] - [Summary("Preview a quote from a message link")] - public async Task ReturnSpecifiedQuote([Summary("message-link")] string messageLink) - { - await QuoteFromMessageLink(messageLink, false); - } - - [Command("remove")] - [RequireUserPermission(GuildPermission.ManageMessages)] - [Summary("Remove a quote (user needs the 'ManageMessages' permission)")] - public async Task RemoveQuote([Summary("quote-ID")] int id) - { - try - { - var quote = _database.Quotes.Where(e => e.GuildId == Context.Guild.Id.AsLong() && e.InternalId == id)?.FirstOrDefault(); - if (quote != null) - { - _database.Quotes.Remove(quote); - await _database.SaveChangesAsync(); - var embed = QuoteBuilder(quote); - await ReplyAsync(string.Format(Localization.Quote.Removed, id), false, embed.Build()); - } - else - { - await ReplyAsync(Localization.Quote.NotFoundWithId); - } - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context, "I couldn't find a quote with that id :disappointed:"); - } - } - - [Command("stats")] - [Summary("Show quote stats for this server")] - public async Task GetQuoteStatsForServer() - { - try - { - // setup - var eb = new EmbedBuilder(); - eb.Author = new EmbedAuthorBuilder() - { - IconUrl = Context.Guild.IconUrl, - Name = $"{Context.Guild.Name} - {Localization.Quote.QuoteStats}" - }; - - // gather data - var totalQuotes = _database.Quotes.Count(row => row.GuildId == Context.Guild.Id.AsLong()); - if (totalQuotes == 0) - { - // no quotes, no stats, end of the road - await ReplyAsync(Localization.Quote.NoQuotesFound); - return; - } - - var mostQuotedPerson = _database.Quotes - .Where(row => row.GuildId == Context.Guild.Id.AsLong()) - .GroupBy(row => row.UserId) - .Select(row => new { userId = row.Key, amount = row.Count()}) - .OrderByDescending(row => row.amount) - .First(); - var mostQuotedPersonUser = Context.Client.GetUserAsync(mostQuotedPerson.userId.AsUlong()).Result ?? new UserPolyfillDto {Username = "Unknown User"}; - - var quotesByYear = _database.Quotes - .Where(row => row.GuildId == Context.Guild.Id.AsLong()) - .GroupBy(row => row.Time.Year) - .Select(row => new { year = row.Key, amount = row.Count()}) - .OrderBy(row => row.year); - - // add data to the embed - eb.AddField(Localization.Quote.MostQuotesPerson, $"{mostQuotedPersonUser.Username} ({mostQuotedPerson.amount})"); - eb.AddInlineField(Localization.Quote.TotalQuotes, totalQuotes); - - foreach (var year in quotesByYear) - { - eb.AddInlineField(year.year.ToString(), year.amount); - } - - await ReplyAsync("", false, eb.Build()); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context); - } - } - - private async Task QuoteFromMention(IUser user, bool saveToDb) - { - try - { - var list = Context.Channel.GetMessagesAsync().Flatten(); - var message = await list.FirstOrDefaultAsync(msg => - msg.Author.Id == user.Id && - msg.Embeds.Count == 0 && - msg.Id != Context.Message.Id && - !msg.Content.ToLower().StartsWith("!")); - if (message == null) return; - - await ProcessQuote(message, saveToDb); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context, $"No quoteable messages have been sent by {user.Username} in this channel"); - } - - } - - private async Task QuoteFromMessageLink(string messageLink, bool saveToDb) - { - try - { - if (!MessageLink.IsValid(messageLink)) - { - await ReplyAsync(Localization.Quote.NotAValidMessageLink); - return; - } - - var link = new MessageLink(messageLink); - if (link.GuildId != Context.Guild.Id) - { - await ReplyAsync(Localization.Quote.OnlyQuoteFromSameServer); - return; - } - - var channel = link.ChannelId == Context.Channel.Id - ? Context.Channel - : await Context.Guild.GetTextChannelAsync(link.ChannelId); - - var message = await channel.GetMessageAsync(link.MessageId); - - await ProcessQuote(message, saveToDb); - } - catch (Exception e) - { - await ErrorHandler.HandleCommandException(e, Context, "I couldn't find that message :disappointed:"); - } - } - - private async Task ProcessQuote(IMessage message, bool saveToDb) - { - if (message.Author.Id == Context.Message.Author.Id && saveToDb && !_isDev) - { - await ReplyAsync(Localization.Quote.CannotSaveOwnQuotes); - return; - } - - if (message.Author.IsBot && saveToDb && !_isDev) - { - await ReplyAsync(Localization.Quote.CannotQuoteBots); - return; - } - - var quote = CreateQuoteObject(message); - if (saveToDb) - { - await _database.Quotes.AddAsync(quote); - await _database.SaveChangesAsync(); - } - - var embed = QuoteBuilder(quote); - - var sb = new StringBuilder(); - if (saveToDb) sb.AppendLine(Localization.Quote.QuoteAdded); - - await ReplyAsync(sb.ToString(), false, embed.Build()); - } - - private EmbedBuilder QuoteBuilder(QuoteModel quote) - { - var getEmbedUserSpan = Transaction.StartChild("GetEmbedUser"); - var user = Context.Client.GetUserAsync(quote.UserId.AsUlong()).Result; - if (user == null) - { - var getEmbedUserFromRepoSpan = Transaction.StartChild("GetEmbedUserFromRepo"); - var fallbackUserFromRepo = _userRepository.Get(quote.UserId.AsUlong()); - user = new UserPolyfillDto() - { - Username = fallbackUserFromRepo?.Username ?? "Unknown User", - AvatarUrl = fallbackUserFromRepo?.AvatarUrl - }; - getEmbedUserFromRepoSpan.Finish(); - } - getEmbedUserSpan.Finish(); - - var embedBuilderSpan = Transaction.StartChild("EmbedBuilder"); - var eb = new EmbedBuilder(); - eb.WithColor(new Color(143, 167, 232)); - eb.Title = quote.InternalId == 0 - ? $"{user.Username} @ {quote.Time.Day}.{quote.Time.Month}.{quote.Time.Year}" - : $"#{quote.InternalId} | {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; - embedBuilderSpan.Finish(); - - return eb; - } - - private QuoteModel CreateQuoteObject(IMessage message) - { - string image; - try - { - image = message.Attachments.First().Url; - } - catch (Exception) - { - image = null; - } - - var last = _database.Quotes.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong())).OrderByDescending(e => e.InternalId).FirstOrDefault(); - var internalId = 0; - if (last != null) internalId = last.InternalId + 1; - return new QuoteModel() - { - InternalId = internalId, - GuildId = Context.Guild.Id.AsLong(), - UserId = message.Author.Id.AsLong(), - Time = message.Timestamp.DateTime.ToUniversalTime(), - Quote = message.Content, - Image = image - }; - } - } -} \ No newline at end of file diff --git a/src/Bot/Handlers/CommandHandler.cs b/src/Bot/Handlers/CommandHandler.cs deleted file mode 100644 index 19d4edd..0000000 --- a/src/Bot/Handlers/CommandHandler.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Discord; -using Discord.Commands; -using Discord.Rest; -using Discord.WebSocket; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Core.GuildSettingsManager; -using Geekbot.Core.Logger; - -namespace Geekbot.Bot.Handlers -{ - public class CommandHandler - { - private readonly IDiscordClient _client; - private readonly IGeekbotLogger _logger; - private readonly IServiceProvider _servicesProvider; - private readonly CommandService _commands; - private readonly RestApplication _applicationInfo; - private readonly IGuildSettingsManager _guildSettingsManager; - private readonly List _ignoredServers; - - public CommandHandler(IDiscordClient client, IGeekbotLogger logger, IServiceProvider servicesProvider, CommandService commands, RestApplication applicationInfo, - IGuildSettingsManager guildSettingsManager) - { - _client = client; - _logger = logger; - _servicesProvider = servicesProvider; - _commands = commands; - _applicationInfo = applicationInfo; - _guildSettingsManager = guildSettingsManager; - - // Some guilds only want very specific functionally without any of the commands, a quick hack that solves that "short term" - // ToDo: create a clean solution for this... - _ignoredServers = new List - { - 228623803201224704, // SwitzerLAN - // 169844523181015040, // EEvent - 248531441548263425, // MYI - 110373943822540800 // Discord Bots - }; - } - - public Task RunCommand(SocketMessage messageParam) - { - try - { - if (!(messageParam is SocketUserMessage message)) return Task.CompletedTask; - if (message.Author.IsBot) return Task.CompletedTask; - - ulong guildId = message.Author switch - { - SocketGuildUser user => user.Guild.Id, - _ => 0 // DM Channel - }; - - if (IsIgnoredGuild(guildId, message.Author.Id)) return Task.CompletedTask; - - var lowCaseMsg = message.ToString().ToLower(); - if (ShouldHui(lowCaseMsg, guildId)) - { - message.Channel.SendMessageAsync("hui!!!"); - return Task.CompletedTask; - } - - if (ShouldPong(lowCaseMsg, guildId)) - { - message.Channel.SendMessageAsync("pong"); - return Task.CompletedTask; - } - - var argPos = 0; - if (!IsCommand(message, ref argPos)) return Task.CompletedTask; - - ExecuteCommand(message, argPos); - } - catch (Exception e) - { - _logger.Error(LogSource.Geekbot, "Failed to Process Message", e); - } - - return Task.CompletedTask; - } - - private void ExecuteCommand(IUserMessage message, int argPos) - { - var context = new CommandContext(_client, message); - _commands.ExecuteAsync(context, argPos, _servicesProvider); - _logger.Information(LogSource.Command, context.Message.Content.Split(" ")[0].Replace("!", ""), SimpleConextConverter.ConvertContext(context)); - } - - private bool IsIgnoredGuild(ulong guildId, ulong authorId) - { - if (!_ignoredServers.Contains(guildId)) return false; - return authorId == _applicationInfo.Owner.Id; - } - - private bool IsCommand(IUserMessage message, ref int argPos) - { - return message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos); - } - - private bool ShouldPong(string lowerCaseMessage, ulong guildId) - { - if (!lowerCaseMessage.StartsWith("ping ") && !lowerCaseMessage.Equals("ping")) return false; - if (guildId == 0) return true; - return GetGuildSettings(guildId)?.Ping ?? false; - } - - private bool ShouldHui(string lowerCaseMessage, ulong guildId) - { - if (!lowerCaseMessage.StartsWith("hui")) return false; - if (guildId == 0) return true; - return GetGuildSettings(guildId)?.Hui ?? false; - } - - private GuildSettingsModel GetGuildSettings(ulong guildId) - { - return _guildSettingsManager.GetSettings(guildId, false); - } - } -} \ No newline at end of file diff --git a/src/Bot/Handlers/MessageDeletedHandler.cs b/src/Bot/Handlers/MessageDeletedHandler.cs deleted file mode 100644 index b8ffe5c..0000000 --- a/src/Bot/Handlers/MessageDeletedHandler.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Discord; -using Discord.WebSocket; -using Geekbot.Core.Database; -using Geekbot.Core.Extensions; -using Geekbot.Core.Logger; - -namespace Geekbot.Bot.Handlers -{ - public class MessageDeletedHandler - { - private readonly DatabaseContext _database; - private readonly IGeekbotLogger _logger; - private readonly IDiscordClient _client; - - public MessageDeletedHandler(DatabaseContext database, IGeekbotLogger logger, IDiscordClient client) - { - _database = database; - _logger = logger; - _client = client; - } - - public async Task HandleMessageDeleted(Cacheable message, Cacheable cacheableMessageChannel) - { - try - { - var guildSocketData = ((IGuildChannel) cacheableMessageChannel.Value).Guild; - var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildSocketData.Id.AsLong())); - if ((guild?.ShowDelete ?? false) && guild?.ModChannel != 0) - { - var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong()); - 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 <#{cacheableMessageChannel.Id}>"); - sb.AppendLine(message.Value.Content); - } - else - { - sb.AppendLine("Someone deleted a message, the message was not cached..."); - } - - await modChannelSocket.SendMessageAsync(sb.ToString()); - } - } - catch (Exception e) - { - _logger.Error(LogSource.Geekbot, "Failed to send delete message...", e); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Handlers/ReactionHandler.cs b/src/Bot/Handlers/ReactionHandler.cs deleted file mode 100644 index 816e125..0000000 --- a/src/Bot/Handlers/ReactionHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Threading.Tasks; -using Discord; -using Discord.WebSocket; -using Geekbot.Core.ReactionListener; - -namespace Geekbot.Bot.Handlers -{ - public class ReactionHandler - { - private readonly IReactionListener _reactionListener; - - public ReactionHandler(IReactionListener reactionListener) - { - _reactionListener = reactionListener; - } - - public Task Added(Cacheable cacheableUserMessage, Cacheable cacheableMessageChannel, SocketReaction reaction) - { - if (reaction.User.Value.IsBot) return Task.CompletedTask; - if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask; - _reactionListener.GiveRole(cacheableMessageChannel.Value, reaction); - return Task.CompletedTask; - } - - public Task Removed(Cacheable cacheableUserMessage, Cacheable cacheableMessageChannel, SocketReaction reaction) - { - if (reaction.User.Value.IsBot) return Task.CompletedTask; - if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask; - _reactionListener.RemoveRole(cacheableMessageChannel.Value, reaction); - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/src/Bot/Handlers/StatsHandler.cs b/src/Bot/Handlers/StatsHandler.cs deleted file mode 100644 index b089515..0000000 --- a/src/Bot/Handlers/StatsHandler.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Threading.Tasks; -using Discord.WebSocket; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Core.Extensions; -using Geekbot.Core.Highscores; -using Geekbot.Core.Logger; -using Microsoft.EntityFrameworkCore; - -namespace Geekbot.Bot.Handlers -{ - public class StatsHandler - { - private readonly IGeekbotLogger _logger; - private readonly DatabaseContext _database; - private string _season; - - public StatsHandler(IGeekbotLogger logger, DatabaseContext database) - { - _logger = logger; - _database = database; - _season = SeasonsUtils.GetCurrentSeason(); - - var timer = new System.Timers.Timer() - { - Enabled = true, - AutoReset = true, - Interval = TimeSpan.FromMinutes(5).TotalMilliseconds - }; - timer.Elapsed += (sender, args) => - { - var current = SeasonsUtils.GetCurrentSeason(); - if (current == _season) return; - _season = SeasonsUtils.GetCurrentSeason(); - }; - } - - public async Task UpdateStats(SocketMessage message) - { - try - { - if (message == null) return; - if (message.Channel.Name.StartsWith('@')) - { - _logger.Information(LogSource.Message, $"[DM-Channel] {message.Content}", SimpleConextConverter.ConvertSocketMessage(message, true)); - return; - } - - var channel = (SocketGuildChannel) message.Channel; - - // ignore the discord bots server - // ToDo: create a clean solution for this... - if (channel.Guild.Id == 110373943822540800) - { - return; - } - - await UpdateTotalTable(message, channel); - await UpdateSeasonsTable(message, channel); - - - if (message.Author.IsBot) return; - _logger.Information(LogSource.Message, message.Content, SimpleConextConverter.ConvertSocketMessage(message)); - } - catch (Exception e) - { - _logger.Error(LogSource.Message, "Could not process message stats", e); - } - } - - private async Task UpdateTotalTable(SocketMessage message, SocketGuildChannel channel) - { - var rowId = await _database.Database.ExecuteSqlRawAsync( - "UPDATE \"Messages\" SET \"MessageCount\" = \"MessageCount\" + 1 WHERE \"GuildId\" = {0} AND \"UserId\" = {1}", - channel.Guild.Id.AsLong(), - message.Author.Id.AsLong() - ); - - if (rowId == 0) - { - await _database.Messages.AddAsync(new MessagesModel - { - UserId = message.Author.Id.AsLong(), - GuildId = channel.Guild.Id.AsLong(), - MessageCount = 1 - }); - await _database.SaveChangesAsync(); - } - } - - private async Task UpdateSeasonsTable(SocketMessage message, SocketGuildChannel channel) - { - var rowId = await _database.Database.ExecuteSqlRawAsync( - "UPDATE \"MessagesSeasons\" SET \"MessageCount\" = \"MessageCount\" + 1 WHERE \"GuildId\" = {0} AND \"UserId\" = {1} AND \"Season\" = {2}", - channel.Guild.Id.AsLong(), - message.Author.Id.AsLong(), - _season - ); - - if (rowId == 0) - { - await _database.MessagesSeasons.AddAsync(new MessageSeasonsModel() - { - UserId = message.Author.Id.AsLong(), - GuildId = channel.Guild.Id.AsLong(), - Season = _season, - MessageCount = 1 - }); - await _database.SaveChangesAsync(); - } - } - } -} \ No newline at end of file diff --git a/src/Bot/Handlers/UserHandler.cs b/src/Bot/Handlers/UserHandler.cs deleted file mode 100644 index 1f58131..0000000 --- a/src/Bot/Handlers/UserHandler.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Discord; -using Discord.Rest; -using Discord.WebSocket; -using Geekbot.Core.Database; -using Geekbot.Core.Extensions; -using Geekbot.Core.Logger; -using Geekbot.Core.UserRepository; - -namespace Geekbot.Bot.Handlers -{ - public class UserHandler - { - private readonly IUserRepository _userRepository; - private readonly IGeekbotLogger _logger; - private readonly DatabaseContext _database; - private readonly IDiscordClient _client; - - public UserHandler(IUserRepository userRepository, IGeekbotLogger logger, DatabaseContext database, IDiscordClient client) - { - _userRepository = userRepository; - _logger = logger; - _database = database; - _client = client; - } - - public async Task Joined(SocketGuildUser user) - { - try - { - var userRepoUpdate = _userRepository.Update(user); - _logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})"); - - if (!user.IsBot) - { - var guildSettings = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId == user.Guild.Id.AsLong()); - var message = guildSettings?.WelcomeMessage; - if (string.IsNullOrEmpty(message)) return; - message = message.Replace("$user", user.Mention); - - var fallbackSender = new Func>(() => user.Guild.DefaultChannel.SendMessageAsync(message)); - if (guildSettings.WelcomeChannel != 0) - { - try - { - var target = await _client.GetChannelAsync(guildSettings.WelcomeChannel.AsUlong()); - var channel = target as ISocketMessageChannel; - await channel.SendMessageAsync(message); - } - catch (Exception e) - { - _logger.Error(LogSource.Geekbot, "Failed to send welcome message to user defined welcome channel", e); - await fallbackSender(); - } - } - else - { - await fallbackSender(); - } - } - - await userRepoUpdate; - } - catch (Exception e) - { - _logger.Error(LogSource.Geekbot, "Failed to send welcome message", e); - } - } - - public async Task Updated(SocketUser oldUser, SocketUser newUser) - { - await _userRepository.Update(newUser); - } - - public async Task Left(SocketGuild socketGuild, SocketUser socketUser) - { - try - { - var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(socketGuild.Id.AsLong())); - if (guild?.ShowLeave ?? false) - { - var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong()); - await modChannelSocket.SendMessageAsync($"{socketUser.Username}#{socketUser.Discriminator} left the server"); - } - } - catch (Exception e) - { - _logger.Error(LogSource.Geekbot, "Failed to send leave message", e); - } - - _logger.Information(LogSource.Geekbot, $"{socketUser.Username} ({socketUser.Id}) joined {socketGuild.Name} ({socketGuild.Id})"); - } - } -} \ No newline at end of file diff --git a/src/Bot/Storage/croissant b/src/Bot/Storage/croissant deleted file mode 100644 index 6b4897f..0000000 --- a/src/Bot/Storage/croissant +++ /dev/null @@ -1,10 +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 -https://storage.cpstatic.ch/storage/og_image/laugengipfel--425319.jpg -https://c1.staticflickr.com/3/2835/10874180753_2b2916e3ce_b.jpg -https://www.spatz-dessert.ch/image_upload/Laugengipfel.jpg -http://www.baeckerei-meier.ch/images/p005_1_03.png -http://i.huffpost.com/gen/1278175/thumbs/o-CROISSANT-facebook.jpg \ No newline at end of file diff --git a/src/Bot/Storage/foxes b/src/Bot/Storage/foxes deleted file mode 100644 index 52fd9d2..0000000 --- a/src/Bot/Storage/foxes +++ /dev/null @@ -1,20 +0,0 @@ -https://i.ytimg.com/vi/qF6OOGuT_hI/maxresdefault.jpg -https://static.tumblr.com/bb34d8f163098ad1daafcffbdbb03975/rk23uap/Nwwp0rmi2/tumblr_static_tumblr_static__640.jpg -https://www.popsci.com/sites/popsci.com/files/styles/1000_1x_/public/import/2013/images/2013/09/redfoxyawn.jpg?itok=yRkSVe8T -https://hdqwalls.com/wallpapers/wild-fox-art.jpg -https://i.imgur.com/ktK9yXX.jpg -http://4.bp.blogspot.com/-Hz-o_KYj3Xk/Vlm2mwbztjI/AAAAAAAA8Ss/jbH5ovjmC9A/s1600/ScreenShot5502.jpg -https://wallpaperscraft.com/image/fox_forest_grass_117190_540x960.jpg -https://orig00.deviantart.net/2feb/f/2013/137/a/f/fox_and_curious_squirrel_by_tamarar-d65ju8d.jpg -http://www.tehcute.com/pics/201401/little-fox-big.jpg -https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR6QXB1APLdUsyzO39kPvhnC9cOvcwzEtsxown9QjWilWppia2mwg -https://www.wildlifeaid.org.uk/wp-content/uploads/2016/03/FP9_July09.jpg -https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Vulpes_vulpes_ssp_fulvus_6568085.jpg/1200px-Vulpes_vulpes_ssp_fulvus_6568085.jpg -http://images.hellogiggles.com/uploads/2017/06/10023347/fox1.jpg -https://i.ytimg.com/vi/mtroFou8Xb4/maxresdefault.jpg -http://wallpapers-best.com/uploads/posts/2015-09/20_fox.jpg -https://www.whats-your-sign.com/wp-content/uploads/2018/02/RedFoxSymbolism4.jpg -https://cdn.zmescience.com/wp-content/uploads/2016/09/8505162700_11394c3f6a_b.jpg -http://wallpapers-best.com/uploads/posts/2015-09/18_fox.jpg -https://s.abcnews.com/images/General/red-fox-new-jersey-gty-jt-191119_hpMain_16x9_992.jpg -https://i.ytimg.com/vi/ClNRWL_9L8s/maxresdefault.jpg \ No newline at end of file diff --git a/src/Bot/Storage/penguins b/src/Bot/Storage/penguins deleted file mode 100644 index 95aa70d..0000000 --- a/src/Bot/Storage/penguins +++ /dev/null @@ -1,15 +0,0 @@ -https://www.birdlife.org/sites/default/files/styles/1600/public/slide.jpg?itok=HRhQfA1S -http://experimentexchange.com/wp-content/uploads/2016/07/penguins-fact.jpg -http://www.antarctica.gov.au/__data/assets/image/0011/147737/varieties/antarctic.jpg -https://images.justwatch.com/backdrop/8611153/s1440/pingu -http://4.bp.blogspot.com/-VhmPPCcZnwA/TWR303DSAuI/AAAAAAAAABU/eSSokmd376s/s1600/2-Penguins-penguins-4234010-1280-1024.jpg -https://media.glamour.com/photos/56959e35d9dab9ff41b308a0/master/pass/inspired-2015-02-gentoo-penguin-main.jpg -https://indiansciencejournal.files.wordpress.com/2012/04/emperor-penguin-credit-british-antarctic-survey.jpg -https://fthmb.tqn.com/7cd9Q3LSapEShHq2mKvQgSPr_tc=/2250x1500/filters:fill(auto,1)/149267744-56a008755f9b58eba4ae8f46.jpg -https://blogs.voanews.com/science-world/files/2014/07/11240219084_941dfbf66e_b.jpg -https://blogs.biomedcentral.com/bmcseriesblog/wp-content/uploads/sites/9/2015/11/IMG_5391-2.jpg -https://i2-prod.mirror.co.uk/incoming/article11682518.ece/ALTERNATES/s615/Emperor-penguins-on-ice.jpg -https://www.gannett-cdn.com/presto/2019/04/15/PPHX/8dfe0433-c22c-4458-9ba8-3aab866774f8-Penguins5cad7bfd107a4.jpg?crop=5759,3224,x0,y0&width=3200&height=1680&fit=bounds -http://4.bp.blogspot.com/_FNQgkfCwYxs/S82jBAxMVEI/AAAAAAAAAns/_3lAuJhUfcs/s1600/311785583_af8f2d1ea7_o.jpg -http://wallsdesk.com/wp-content/uploads/2017/01/Pictures-of-Penguin-.jpg -http://2.bp.blogspot.com/_W90V87w3sr8/TP3RPYwrrjI/AAAAAAAAAXk/riN0GwRwhFM/s1600/leap-of-faith-adelie-penguin-pictures.jpg \ No newline at end of file diff --git a/src/Bot/Storage/pumpkin b/src/Bot/Storage/pumpkin deleted file mode 100644 index 3652397..0000000 --- a/src/Bot/Storage/pumpkin +++ /dev/null @@ -1,14 +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 -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 -https://www.digsdigs.com/photos/2009/10/100-halloween-pumpkin-carving-ideas-12.jpg -https://i.pinimg.com/736x/59/8a/0f/598a0fbf789631b76c1ffd4443194d8e--halloween-pumpkins-fall-halloween.jpg -http://i.huffpost.com/gen/1405530/images/o-PUMPKINS-facebook.jpg -https://www.reviewjournal.com/wp-content/uploads/2016/10/web1_thinkstockphotos-491684958_7239666.jpg -https://img.sunset02.com/sites/default/files/1494265591/pumpkins-growing-on-farm-getty-sun-0517.jpg -https://toronto.citynews.ca/wp-content/blogs.dir/sites/10/2015/10/06/pumpkin-patch.jpg -http://i.huffpost.com/gen/3494726/images/o-PUMPKIN-facebook.jpg -https://servingjoy.com/wp-content/uploads/2014/12/Beautiful-autumn-halloween-pumpkins.jpg -https://www.history.com/.image/t_share/MTU3ODc5MDg2NDI4OTg4NzQ1/still-life-of-a-jack-o-lantern.jpg \ No newline at end of file diff --git a/src/Bot/Storage/squirrel b/src/Bot/Storage/squirrel deleted file mode 100644 index 91fd240..0000000 --- a/src/Bot/Storage/squirrel +++ /dev/null @@ -1,25 +0,0 @@ -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 -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://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 -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 -https://i.ytimg.com/vi/pzUs0DdzK3Y/hqdefault.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/1c/7d/4f/1c7d4f067a10066aad802ce5ac468d71--group-boards-a-squirrel.jpg -https://i.pinimg.com/736x/d6/42/12/d64212cc6221916db4173962bf6c131a--cute-squirrel-baby-squirrel.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://img15.deviantart.net/9c50/i/2011/213/c/9/just_taking_it_easy_by_lou_in_canada-d42do3d.jpg -https://insider.si.edu/wp-content/uploads/2018/01/Chipmunk-Photo-Mark-Rounds.jpg -https://media.mnn.com/assets/images/2014/12/gray-squirrel-uc-berkeley.jpg.1080x0_q100_crop-scale.jpg -https://citywildlife.org/wp-content/uploads/Juvenile-squirrel.jpg \ No newline at end of file diff --git a/src/Commands/Commands.csproj b/src/Commands/Commands.csproj deleted file mode 100644 index 7f0bd8f..0000000 --- a/src/Commands/Commands.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net6.0 - $(VersionSuffix) - $(VersionSuffix) - 0.0.0-DEV - Geekbot.Commands - Geekbot.Commands - NU1701 - CS8618 - enable - enable - True - Library - - - - - - - - diff --git a/src/Commands/Karma/Karma.cs b/src/Commands/Karma/Karma.cs deleted file mode 100644 index 026af2f..0000000 --- a/src/Commands/Karma/Karma.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Drawing; -using Geekbot.Core; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Interactions.Embed; -using Geekbot.Interactions.Resolved; -using Localization = Geekbot.Core.Localization; - -namespace Geekbot.Commands.Karma; - -public class Karma -{ - private readonly DatabaseContext _database; - private readonly long _guildId; - - public Karma(DatabaseContext database, long guildId) - { - _database = database; - _guildId = guildId; - } - - public async Task ChangeKarma(User author, User targetUser, KarmaChange change) - { - // Get the user - var authorRecord = await GetUser(long.Parse(author.Id)); - - // Check if the user can change karma - if (targetUser.Id == author.Id) - { - var message = change switch - { - KarmaChange.Up => Localization.Karma.CannotChangeOwnUp, - KarmaChange.Same => Localization.Karma.CannotChangeOwnSame, - KarmaChange.Down => Localization.Karma.CannotChangeOwnDown, - _ => throw new ArgumentOutOfRangeException(nameof(change), change, null) - }; - return Embed.ErrorEmbed(string.Format(message, author.Username)); - } - - var timeoutMinutes = 3; - if (authorRecord.TimeOut.AddMinutes(timeoutMinutes) > DateTimeOffset.Now.ToUniversalTime()) - { - var remaining = authorRecord.TimeOut.AddMinutes(timeoutMinutes) - DateTimeOffset.Now.ToUniversalTime(); - var formatedWaitTime = DateLocalization.FormatDateTimeAsRemaining(remaining); - return Embed.ErrorEmbed(string.Format(Localization.Karma.WaitUntill, author.Username, formatedWaitTime)); - } - - // Get the values for the change direction - var (title, amount) = change switch - { - KarmaChange.Up => (Localization.Karma.Increased, 1), - KarmaChange.Same => (Localization.Karma.Neutral, 0), - KarmaChange.Down => (Localization.Karma.Decreased, -1), - _ => throw new ArgumentOutOfRangeException(nameof(change), change, null) - }; - - // Change it - var targetUserRecord = await GetUser(long.Parse(targetUser.Id)); - targetUserRecord.Karma += amount; - _database.Karma.Update(targetUserRecord); - - authorRecord.TimeOut = DateTimeOffset.Now.ToUniversalTime(); - _database.Karma.Update(authorRecord); - - await _database.SaveChangesAsync(); - - // Respond - var eb = new Embed() - { - Author = new () - { - Name = targetUser.Username, - IconUrl = targetUser.GetAvatarUrl() - }, - Title = title, - }; - eb.SetColor(Color.PaleGreen); - eb.AddInlineField(Localization.Karma.By, author.Username); - eb.AddInlineField(Localization.Karma.Amount, amount.ToString()); - eb.AddInlineField(Localization.Karma.Current, targetUserRecord.Karma.ToString()); - return eb; - } - - private async Task GetUser(long userId) - { - var user = _database.Karma.FirstOrDefault(u => u.GuildId.Equals(_guildId) && u.UserId.Equals(userId)) ?? await CreateNewRow(userId); - return user; - } - - private async Task CreateNewRow(long userId) - { - var user = new KarmaModel() - { - GuildId = _guildId, - UserId = userId, - Karma = 0, - TimeOut = DateTimeOffset.MinValue.ToUniversalTime() - }; - var newUser = _database.Karma.Add(user).Entity; - await _database.SaveChangesAsync(); - return newUser; - } -} \ No newline at end of file diff --git a/src/Commands/Karma/KarmaChange.cs b/src/Commands/Karma/KarmaChange.cs deleted file mode 100644 index 96cbe88..0000000 --- a/src/Commands/Karma/KarmaChange.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Geekbot.Commands.Karma; - -public enum KarmaChange -{ - Up, - Same, - Down -} diff --git a/src/Commands/Rank.cs b/src/Commands/Rank.cs deleted file mode 100644 index 6de2d83..0000000 --- a/src/Commands/Rank.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Text; -using Geekbot.Core.Converters; -using Geekbot.Core.Database; -using Geekbot.Core.Extensions; -using Geekbot.Core.Highscores; -using Localization = Geekbot.Core.Localization; - -namespace Geekbot.Commands -{ - public class Rank - { - private readonly DatabaseContext _database; - private readonly IHighscoreManager _highscoreManager; - - public Rank(DatabaseContext database, IHighscoreManager highscoreManager) - { - _database = database; - _highscoreManager = highscoreManager; - } - - public string Run(string typeUnformated, int amount, string season, ulong guildId, string guildName) - { - HighscoreTypes type; - try - { - type = Enum.Parse(typeUnformated, true); - if (!Enum.IsDefined(typeof(HighscoreTypes), type)) throw new Exception(); - } - catch - { - return Localization.Rank.InvalidType; - } - - var replyBuilder = new StringBuilder(); - if (amount > 20) - { - replyBuilder.AppendLine(Localization.Rank.LimitingTo20Warning); - amount = 20; - } - - Dictionary highscoreUsers; - try - { - highscoreUsers = _highscoreManager.GetHighscoresWithUserData(type, guildId, amount, season); - } - catch (HighscoreListEmptyException) - { - return string.Format(Core.Localization.Rank.NoTypeFoundForServer, type); - } - - var guildMessages = 0; - if (type == HighscoreTypes.messages) - { - guildMessages = _database.Messages - .Where(e => e.GuildId.Equals(guildId.AsLong())) - .Select(e => e.MessageCount) - .Sum(); - } - - var failedToRetrieveUser = highscoreUsers.Any(e => string.IsNullOrEmpty(e.Key.Username)); - - if (failedToRetrieveUser) replyBuilder.AppendLine(Core.Localization.Rank.FailedToResolveAllUsernames).AppendLine(); - - if (type == HighscoreTypes.seasons) - { - if (string.IsNullOrEmpty(season)) - { - season = SeasonsUtils.GetCurrentSeason(); - } - - replyBuilder.AppendLine(string.Format(Core.Localization.Rank.HighscoresFor, $"{type.ToString().CapitalizeFirst()} ({season})", guildName)); - } - else - { - replyBuilder.AppendLine(string.Format(Core.Localization.Rank.HighscoresFor, type.ToString().CapitalizeFirst(), guildName)); - } - - var highscorePlace = 1; - foreach (var (user, value) in highscoreUsers) - { - replyBuilder.Append(highscorePlace < 11 - ? $"{EmojiConverter.NumberToEmoji(highscorePlace)} " - : $"`{highscorePlace}.` "); - - replyBuilder.Append(user.Username != null - ? $"**{user.Username}#{user.Discriminator}**" - : $"**{user.Id}**"); - - replyBuilder.Append(type switch - { - HighscoreTypes.messages => $" - {value} {HighscoreTypes.messages} - {Math.Round((double)(100 * value) / guildMessages, 2)}%\n", - HighscoreTypes.seasons => $" - {value} {HighscoreTypes.messages}\n", - _ => $" - {value} {type}\n" - }); - - highscorePlace++; - } - - return replyBuilder.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Commands/Roll/Roll.cs b/src/Commands/Roll/Roll.cs deleted file mode 100644 index a431bd9..0000000 --- a/src/Commands/Roll/Roll.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Geekbot.Core; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Core.Extensions; -using Geekbot.Core.KvInMemoryStore; -using Geekbot.Core.RandomNumberGenerator; - -namespace Geekbot.Commands.Roll -{ - public class Roll - { - private readonly IKvInMemoryStore _kvInMemoryStore; - private readonly DatabaseContext _database; - private readonly IRandomNumberGenerator _randomNumberGenerator; - - public Roll(IKvInMemoryStore kvInMemoryStore, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator) - { - _kvInMemoryStore = kvInMemoryStore; - _database = database; - _randomNumberGenerator = randomNumberGenerator; - } - - public async Task RunFromGateway(ulong guildId, ulong userId, string userName, string unparsedGuess) - { - int.TryParse(unparsedGuess, out var guess); - return await this.Run(guildId.AsLong(), userId.AsLong(), userName, guess); - } - - public async Task RunFromInteraction(string guildId, string userId, string userName, int guess) - { - return await this.Run(long.Parse(guildId), long.Parse(userId), userName, guess); - } - - private async Task Run(long guildId, long userId, string userName, int guess) - { - var number = _randomNumberGenerator.Next(1, 100); - - if (guess <= 100 && guess > 0) - { - var kvKey = $"{guildId}:{userId}:RollsPrevious"; - var prevRoll = _kvInMemoryStore.Get(kvKey); - - if (prevRoll?.LastGuess == guess && prevRoll?.GuessedOn.AddDays(1) > DateTime.Now) - { - return string.Format( - Core.Localization.Roll.NoPrevGuess, - $"<@{userId}>", - DateLocalization.FormatDateTimeAsRemaining(prevRoll.GuessedOn.AddDays(1))); - } - - _kvInMemoryStore.Set(kvKey, new RollTimeout { LastGuess = guess, GuessedOn = DateTime.Now }); - - var answer = string.Format(Core.Localization.Roll.Rolled, $"<@{userId}>", number, guess); - - if (guess == number) - { - var user = await GetUser(guildId, userId); - user.Rolls += 1; - _database.Rolls.Update(user); - await _database.SaveChangesAsync(); - answer += string.Format(($"\n{Core.Localization.Roll.Gratz}"), userName); - } - - return answer; - } - else - { - return string.Format(Core.Localization.Roll.RolledNoGuess, $"<@{userId}>", number); - } - } - - private async Task GetUser(long guildId, long userId) - { - var user = _database.Rolls.FirstOrDefault(u => u.GuildId.Equals(guildId) && u.UserId.Equals(userId)) ?? await CreateNewRow(guildId, userId); - return user; - } - - private async Task CreateNewRow(long guildId, long userId) - { - var user = new RollsModel() - { - GuildId = guildId, - UserId = userId, - Rolls = 0 - }; - var newUser = _database.Rolls.Add(user).Entity; - await _database.SaveChangesAsync(); - return newUser; - } - } -} \ No newline at end of file diff --git a/src/Commands/Roll/RollTimeout.cs b/src/Commands/Roll/RollTimeout.cs deleted file mode 100644 index d296c45..0000000 --- a/src/Commands/Roll/RollTimeout.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Geekbot.Commands.Roll -{ - public record RollTimeout - { - public int LastGuess { get; set; } - public DateTime GuessedOn { get; set; } - } -} \ No newline at end of file diff --git a/src/Commands/UrbanDictionary/UrbanDictionary.cs b/src/Commands/UrbanDictionary/UrbanDictionary.cs deleted file mode 100644 index 9862463..0000000 --- a/src/Commands/UrbanDictionary/UrbanDictionary.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Drawing; -using Geekbot.Core; -using Geekbot.Interactions.Embed; - -namespace Geekbot.Commands.UrbanDictionary; - -public class UrbanDictionary -{ - public static async Task Run(string term) - { - var definitions = await HttpAbstractions.Get(new Uri($"https://api.urbandictionary.com/v0/define?term={term}")); - - if (definitions.List.Count == 0) - { - return null; - } - - var definition = definitions.List.First(e => !string.IsNullOrWhiteSpace(e.Example)); - - static string ShortenIfToLong(string str, int maxLength) => str.Length > maxLength ? $"{str[..(maxLength - 5)]}[...]" : str; - - var eb = new Embed(); - eb.Author = new() - { - Name = definition.Word, - Url = definition.Permalink - }; - eb.SetColor(Color.Gold); - - if (!string.IsNullOrEmpty(definition.Definition)) eb.Description = ShortenIfToLong(definition.Definition, 1800); - if (!string.IsNullOrEmpty(definition.Example)) eb.AddField("Example", ShortenIfToLong(definition.Example, 1024)); - if (definition.ThumbsUp != 0) eb.AddInlineField("Upvotes", definition.ThumbsUp.ToString()); - if (definition.ThumbsDown != 0) eb.AddInlineField("Downvotes", definition.ThumbsDown.ToString()); - - return eb; - } -} \ No newline at end of file diff --git a/src/Commands/UrbanDictionary/UrbanDictionaryListItem.cs b/src/Commands/UrbanDictionary/UrbanDictionaryListItem.cs deleted file mode 100644 index 822ca8a..0000000 --- a/src/Commands/UrbanDictionary/UrbanDictionaryListItem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Commands.UrbanDictionary; - -public record UrbanDictionaryListItem -{ - [JsonPropertyName("definition")] - public string Definition { get; set; } - - [JsonPropertyName("permalink")] - public string Permalink { get; set; } - - [JsonPropertyName("thumbs_up")] - public int ThumbsUp { get; set; } - - [JsonPropertyName("word")] - public string Word { get; set; } - - [JsonPropertyName("example")] - public string Example { get; set; } - - [JsonPropertyName("thumbs_down")] - public int ThumbsDown { get; set; } -} diff --git a/src/Commands/UrbanDictionary/UrbanDictionaryResponse.cs b/src/Commands/UrbanDictionary/UrbanDictionaryResponse.cs deleted file mode 100644 index 42861c3..0000000 --- a/src/Commands/UrbanDictionary/UrbanDictionaryResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Commands.UrbanDictionary; - -public struct UrbanDictionaryResponse -{ - [JsonPropertyName("tags")] - public string[] Tags { get; set; } - - [JsonPropertyName("list")] - public List List { get; set; } -} \ No newline at end of file diff --git a/src/Core/BotCommandLookup/CommandInfo.cs b/src/Core/BotCommandLookup/CommandInfo.cs deleted file mode 100644 index c5793ac..0000000 --- a/src/Core/BotCommandLookup/CommandInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Geekbot.Core.BotCommandLookup; - -public struct CommandInfo -{ - public string Name { get; set; } - public Dictionary Parameters { get; set; } - public List Aliases { get; set; } - public string Summary { get; set; } -} \ No newline at end of file diff --git a/src/Core/BotCommandLookup/CommandLookup.cs b/src/Core/BotCommandLookup/CommandLookup.cs deleted file mode 100644 index 62369c6..0000000 --- a/src/Core/BotCommandLookup/CommandLookup.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Discord.Commands; - -namespace Geekbot.Core.BotCommandLookup; - -public class CommandLookup -{ - private readonly Assembly _assembly; - - public CommandLookup(Assembly assembly) - { - _assembly = assembly; - } - - public List GetCommands() - { - var commands = SearchCommands(_assembly); - var result = new List(); - commands.ForEach(x => GetCommandDefinition(ref result, x)); - - return result; - } - - private List SearchCommands(Assembly assembly) - { - bool IsLoadableModule(TypeInfo info) => info.DeclaredMethods.Any(x => x.GetCustomAttribute() != null || x.GetCustomAttribute() != null); - return assembly - .DefinedTypes - .Where(typeInfo => typeInfo.IsPublic || typeInfo.IsNestedPublic) - .Where(IsLoadableModule) - .ToList(); - } - - private void GetCommandDefinition(ref List commandInfos, TypeInfo commandType) - { - var methods = commandType - .GetMethods() - .Where(x => x.GetCustomAttribute() != null) - .ToList(); - - var commandGroup = (commandType.GetCustomAttributes().FirstOrDefault(attr => attr is GroupAttribute) as GroupAttribute)?.Prefix; - - foreach (var command in methods) - { - var commandInfo = new CommandInfo() - { - Parameters = new Dictionary(), - }; - - foreach (var attr in command.GetCustomAttributes()) - { - - switch (attr) - { - case SummaryAttribute name: - commandInfo.Summary = name.Text; - break; - case CommandAttribute name: - commandInfo.Name = string.IsNullOrEmpty(commandGroup) ? name.Text : $"{commandGroup} {name.Text}"; - break; - case AliasAttribute name: - commandInfo.Aliases = name.Aliases.ToList() ?? new List(); - break; - } - } - - foreach (var param in command.GetParameters()) - { - var paramName = param.Name ?? string.Empty; - var paramInfo = new ParameterInfo() - { - Summary = param.GetCustomAttribute()?.Text ?? string.Empty, - Type = param.ParameterType.Name, - DefaultValue = param.DefaultValue?.ToString() - }; - commandInfo.Parameters.Add(paramName, paramInfo); - } - - if (!string.IsNullOrEmpty(commandInfo.Name)) - { - commandInfos.Add(commandInfo); - } - } - } -} \ No newline at end of file diff --git a/src/Core/BotCommandLookup/ParameterInfo.cs b/src/Core/BotCommandLookup/ParameterInfo.cs deleted file mode 100644 index afdfd50..0000000 --- a/src/Core/BotCommandLookup/ParameterInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Geekbot.Core.BotCommandLookup; - -public struct ParameterInfo -{ - public string Summary { get; set; } - public string Type { get; set; } - public string DefaultValue { get; set; } -} \ No newline at end of file diff --git a/src/Core/Converters/EmojiConverter.cs b/src/Core/Converters/EmojiConverter.cs deleted file mode 100644 index d25415f..0000000 --- a/src/Core/Converters/EmojiConverter.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Collections; -using System.Text; - -namespace Geekbot.Core.Converters -{ - public static class EmojiConverter - { - private static readonly string[] NumberEmojiMap = - { - ":zero:", - ":one:", - ":two:", - ":three:", - ":four:", - ":five:", - ":six:", - ":seven:", - ":eight:", - ":nine:" - }; - - public static string NumberToEmoji(int number) - { - if (number == 10) - { - return "🔟"; - } - - var numbers = number.ToString().ToCharArray(); - var returnString = new StringBuilder(); - foreach (var n in numbers) - { - returnString.Append(NumberEmojiMap[int.Parse(n.ToString())]); - } - return returnString.ToString(); - } - - private static readonly Hashtable TextEmojiMap = 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: ", - [' '] = " " - }; - - public static string TextToEmoji(string text) - { - var letters = text.ToUpper().ToCharArray(); - var returnString = new StringBuilder(); - foreach (var n in letters) - { - var emoji = TextEmojiMap[n] ?? n; - returnString.Append(emoji); - } - return returnString.ToString(); - } - - private static readonly Hashtable RegionalIndicatorMap = new Hashtable() - { - ['A'] = new Rune(0x1F1E6), - ['B'] = new Rune(0x1F1E7), - ['C'] = new Rune(0x1F1E8), - ['D'] = new Rune(0x1F1E9), - ['E'] = new Rune(0x1F1EA), - ['F'] = new Rune(0x1F1EB), - ['G'] = new Rune(0x1F1EC), - ['H'] = new Rune(0x1F1ED), - ['I'] = new Rune(0x1F1EE), - ['J'] = new Rune(0x1F1EF), - ['K'] = new Rune(0x1F1F0), - ['L'] = new Rune(0x1F1F1), - ['M'] = new Rune(0x1F1F2), - ['N'] = new Rune(0x1F1F3), - ['O'] = new Rune(0x1F1F4), - ['P'] = new Rune(0x1F1F5), - ['Q'] = new Rune(0x1F1F6), - ['R'] = new Rune(0x1F1F7), - ['S'] = new Rune(0x1F1F8), - ['T'] = new Rune(0x1F1F9), - ['U'] = new Rune(0x1F1FA), - ['V'] = new Rune(0x1F1FB), - ['W'] = new Rune(0x1F1FC), - ['X'] = new Rune(0x1F1FD), - ['Y'] = new Rune(0x1F1FE), - ['Z'] = new Rune(0x1F1FF) - }; - - public static string CountryCodeToEmoji(string countryCode) - { - var letters = countryCode.ToUpper().ToCharArray(); - var returnString = new StringBuilder(); - foreach (var n in letters) - { - var emoji = RegionalIndicatorMap[n]; - returnString.Append(emoji); - } - return returnString.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Core/Converters/MtgManaConverter.cs b/src/Core/Converters/MtgManaConverter.cs deleted file mode 100644 index aa6b74c..0000000 --- a/src/Core/Converters/MtgManaConverter.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace Geekbot.Core.Converters -{ - public class MtgManaConverter : IMtgManaConverter - { - private readonly Dictionary _manaDict; - - public MtgManaConverter() - { - // these emotes can be found at https://discord.gg/bz8HyA7 - _manaDict = new Dictionary - { - {"{0}", "<:mtg_0:415216130043412482>"}, - {"{1}", "<:mtg_1:415216130253389835>"}, - {"{2}", "<:mtg_2:415216130031091713>"}, - {"{3}", "<:mtg_3:415216130467037194>"}, - {"{4}", "<:mtg_4:415216130026635295>"}, - {"{5}", "<:mtg_5:415216130492203008>"}, - {"{6}", "<:mtg_6:415216130458779658>"}, - {"{7}", "<:mtg_7:415216130190475265>"}, - {"{8}", "<:mtg_8:415216130517630986>"}, - {"{9}", "<:mtg_9:415216130500722689>"}, - {"{10", "<:mtg_10:415216130450391051>"}, - {"{11}", "<:mtg_11:415216130811101185>"}, - {"{12}", "<:mtg_12:415216130525888532>"}, - {"{13}", "<:mtg_13:415216130517631000>"}, - {"{14}", "<:mtg_14:415216130165178370>"}, - {"{15}", "<:mtg_15:415216130576089108>"}, - {"{16}", "<:mtg_16:415216130358247425>"}, - {"{17}", "<:mtg_17:415216130601517056>"}, - {"{18}", "<:mtg_18:415216130462842891>"}, - {"{19}", "<:mtg_19:415216130614099988>"}, - {"{20}", "<:mtg_20:415216130656043038>"}, - {"{W}", "<:mtg_white:415216131515744256>"}, - {"{U}", "<:mtg_blue:415216130521694209>"}, - {"{B}", "<:mtg_black:415216130873884683>"}, - {"{R}", "<:mtg_red:415216131322806272>"}, - {"{G}", "<:mtg_green:415216131180331009>"}, - {"{S}", "<:mtg_s:415216131293446144>"}, - {"{T}", "<:mtg_tap:415258392727257088>"}, - {"{C}", "<:mtg_colorless:415216130706374666>"}, - {"{2/W}", "<:mtg_2w:415216130446065664>"}, - {"{2/U}", "<:mtg_2u:415216130429550592>"}, - {"{2/B}", "<:mtg_2b:415216130160984065>"}, - {"{2/R}", "<:mtg_2r:415216130454716436>"}, - {"{2/G}", "<:mtg_2g:415216130420899840>"}, - {"{W/U}", "<:mtg_wu:415216130970484736>"}, - {"{W/B}", "<:mtg_wb:415216131222011914>"}, - {"{U/R}", "<:mtg_ur:415216130962096128>"}, - {"{U/B}", "<:mtg_ub:415216130865758218>"}, - {"{R/W}", "<:mtg_rw:415216130878210057>"}, - {"{G/W}", "<:mtg_gw:415216130567962646>"}, - {"{G/U}", "<:mtg_gu:415216130739666945>"}, - {"{B/R}", "<:mtg_br:415216130580283394>"}, - {"{B/G}", "<:mtg_bg:415216130781609994>"}, - {"{U/P}", "<:mtg_up:415216130861432842>"}, - {"{R/P}", "<:mtg_rp:415216130597322783>"}, - {"{G/P}", "<:mtg_gp:415216130760769546>"}, - {"{W/P}", "<:mtg_wp:415216131541041172>"}, - {"{B/P}", "<:mtg_bp:415216130664169482>"} - }; - } - - public string ConvertMana(string mana) - { - var rgx = Regex.Matches(mana, @"(\{(.*?)\})"); - foreach (Match manaTypes in rgx) - { - var m = _manaDict.GetValueOrDefault(manaTypes.Value); - if (!string.IsNullOrEmpty(m)) - { - mana = mana.Replace(manaTypes.Value, m); - } - } - - return mana; - } - } -} \ No newline at end of file diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj deleted file mode 100644 index 2cd8dbc..0000000 --- a/src/Core/Core.csproj +++ /dev/null @@ -1,94 +0,0 @@ - - - - net6.0 - $(VersionSuffix) - $(VersionSuffix) - 0.0.0-DEV - Geekbot.Core - Geekbot.Core - NU1701 - True - Library - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - ResXFileCodeGenerator - Admin.Designer.cs - - - ResXFileCodeGenerator - Choose.Designer.cs - - - ResXFileCodeGenerator - Cookies.Designer.cs - - - ResXFileCodeGenerator - Corona.Designer.cs - - - ResXFileCodeGenerator - EightBall.Designer.cs - - - ResXFileCodeGenerator - Internal.Designer.cs - - - ResXFileCodeGenerator - Karma.Designer.cs - - - ResXFileCodeGenerator - Quote.Designer.cs - - - ResXFileCodeGenerator - Rank.Designer.cs - - - ResXFileCodeGenerator - Role.Designer.cs - - - ResXFileCodeGenerator - Roll.Designer.cs - - - ResXFileCodeGenerator - Ship.Designer.cs - - - ResXFileCodeGenerator - Stats.Designer.cs - - - - diff --git a/src/Core/Database/Models/MessageSeasonsModel.cs b/src/Core/Database/Models/MessageSeasonsModel.cs deleted file mode 100644 index 5a4252c..0000000 --- a/src/Core/Database/Models/MessageSeasonsModel.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Geekbot.Core.Database.Models -{ - public class MessageSeasonsModel - { - [Key] - public int Id { get; set; } - - [Required] - public long GuildId { get; set; } - - [Required] - public long UserId { get; set; } - - [Required] - public string Season { get; set; } - - public int MessageCount { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Database/Models/ReactionListenerModel.cs b/src/Core/Database/Models/ReactionListenerModel.cs deleted file mode 100644 index 5ec28be..0000000 --- a/src/Core/Database/Models/ReactionListenerModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Geekbot.Core.Database.Models -{ - public class ReactionListenerModel - { - [Key] - public int Id { get; set; } - - [Required] - public long GuildId { get; set; } - - [Required] - public long MessageId { get; set; } - - [Required] - public long RoleId { get; set; } - - [Required] - public string Reaction { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/Database/SqlConnectionString.cs b/src/Core/Database/SqlConnectionString.cs deleted file mode 100644 index ca2858d..0000000 --- a/src/Core/Database/SqlConnectionString.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text; - -namespace Geekbot.Core.Database -{ - public class SqlConnectionString - { - public string Host { get; set; } - public string Port { get; set; } - public string Database { get; set; } - public string Username { get; set; } - public string Password { get; set; } - public bool RequireSsl { get; set; } - public bool TrustServerCertificate { get; set; } - public bool RedshiftCompatibility { get; set; } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append("Application Name=Geekbot;"); - - sb.Append($"Host={Host};"); - sb.Append($"Port={Port};"); - sb.Append($"Database={Database};"); - sb.Append($"Username={Username};"); - sb.Append($"Password={Password};"); - - var sslMode = RequireSsl ? "Require" : "Prefer"; - sb.Append($"SSL Mode={sslMode};"); - sb.Append($"Trust Server Certificate={TrustServerCertificate.ToString()};"); - - if (RedshiftCompatibility) - { - sb.Append("Server Compatibility Mode=Redshift"); - } - - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Core/DateLocalization.cs b/src/Core/DateLocalization.cs deleted file mode 100644 index 39b447f..0000000 --- a/src/Core/DateLocalization.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Text; - -namespace Geekbot.Core -{ - public class DateLocalization - { - public static string FormatDateTimeAsRemaining(DateTimeOffset dateTime) - { - return FormatDateTimeAsRemaining(dateTime - DateTimeOffset.Now); - } - - public static string FormatDateTimeAsRemaining(TimeSpan remaining) - { - const string formattable = "{0} {1}"; - var sb = new StringBuilder(); - - if (remaining.Days > 0) - { - sb.AppendFormat(formattable, remaining.Days, GetSingularOrPlural(remaining.Days, Localization.Internal.Days)); - } - - if (remaining.Hours > 0) - { - if (sb.Length > 0) sb.Append(", "); - sb.AppendFormat(formattable, remaining.Hours, GetSingularOrPlural(remaining.Hours, Localization.Internal.Hours)); - } - - if (remaining.Minutes > 0) - { - if (sb.Length > 0) sb.Append(", "); - sb.AppendFormat(formattable, remaining.Minutes, GetSingularOrPlural(remaining.Minutes, Localization.Internal.Minutes)); - } - - if (remaining.Seconds > 0) - { - if (sb.Length > 0) - { - sb.AppendFormat(" {0} ", Localization.Internal.And); - } - sb.AppendFormat(formattable, remaining.Seconds, GetSingularOrPlural(remaining.Seconds, Localization.Internal.Seconds)); - } - - return sb.ToString().Trim(); - } - - private static string GetSingularOrPlural(int number, string rawString) - { - var versions = rawString.Split('|'); - return number == 1 ? versions[0] : versions[1]; - } - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/DiceException.cs b/src/Core/DiceParser/DiceException.cs deleted file mode 100644 index 6c6c29c..0000000 --- a/src/Core/DiceParser/DiceException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Geekbot.Core.DiceParser -{ - public class DiceException : Exception - { - public DiceException(string message) : base(message) - { - } - - public string DiceName { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/DiceInput.cs b/src/Core/DiceParser/DiceInput.cs deleted file mode 100644 index 60f0bd2..0000000 --- a/src/Core/DiceParser/DiceInput.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Geekbot.Core.DiceParser -{ - public class DiceInput - { - public List Dice { get; set; } = new List(); - public DiceInputOptions Options { get; set; } = new DiceInputOptions(); - public int SkillModifier { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/DiceInputOptions.cs b/src/Core/DiceParser/DiceInputOptions.cs deleted file mode 100644 index 5606a5e..0000000 --- a/src/Core/DiceParser/DiceInputOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Geekbot.Core.DiceParser -{ - public struct DiceInputOptions - { - public bool ShowTotal { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/DiceParser.cs b/src/Core/DiceParser/DiceParser.cs deleted file mode 100644 index b0f6a88..0000000 --- a/src/Core/DiceParser/DiceParser.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using Geekbot.Core.RandomNumberGenerator; - -namespace Geekbot.Core.DiceParser -{ - public class DiceParser : IDiceParser - { - private readonly IRandomNumberGenerator _randomNumberGenerator; - private readonly Regex _inputRegex; - private readonly Regex _singleDieRegex; - - public DiceParser(IRandomNumberGenerator randomNumberGenerator) - { - _randomNumberGenerator = randomNumberGenerator; - _inputRegex = new Regex( - @"((?\+\d+d\d+)|(?\-\d+d\d+)|(?\d+d\d+)|(?(total))|(?(\+|\-)\d+))\s", - RegexOptions.Compiled | RegexOptions.IgnoreCase, - new TimeSpan(0, 0, 2)); - _singleDieRegex = new Regex( - @"\d+d\d+", - RegexOptions.Compiled | RegexOptions.IgnoreCase, - new TimeSpan(0, 0, 0, 0, 500)); - } - - public DiceInput Parse(string input) - { - // adding a whitespace at the end, otherwise the parser might pickup on false items - var inputWithExtraWhitespace = $"{input} "; - - var matches = _inputRegex.Matches(inputWithExtraWhitespace); - var result = new DiceInput(); - var resultOptions = new DiceInputOptions(); - - foreach (Match match in matches) - { - foreach (Group matchGroup in match.Groups) - { - if (matchGroup.Success) - { - switch (matchGroup.Name) - { - case "DieNormal": - result.Dice.Add(Die(matchGroup.Value, DieAdvantageType.None)); - break; - case "DieAdvantage": - result.Dice.Add(Die(matchGroup.Value, DieAdvantageType.Advantage)); - break; - case "DieDisadvantage": - result.Dice.Add(Die(matchGroup.Value, DieAdvantageType.Disadvantage)); - break; - case "Keywords": - Keywords(matchGroup.Value, ref resultOptions); - break; - case "SkillModifer": - result.SkillModifier = SkillModifer(matchGroup.Value); - break; - } - } - } - } - - if (!result.Dice.Any()) - { - result.Dice.Add(new SingleDie(_randomNumberGenerator)); - } - - result.Options = resultOptions; - - return result; - } - - private SingleDie Die(string match, DieAdvantageType advantageType) - { - var x = _singleDieRegex.Match(match).Value.Split('d'); - var die = new SingleDie(_randomNumberGenerator) - { - Amount = int.Parse(x[0]), - Sides = int.Parse(x[1]), - AdvantageType = advantageType - }; - die.ValidateDie(); - return die; - } - - private int SkillModifer(string match) - { - return int.Parse(match); - } - - private void Keywords(string match, ref DiceInputOptions options) - { - switch (match) - { - case "total": - options.ShowTotal = true; - break; - } - } - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/DieAdvantageType.cs b/src/Core/DiceParser/DieAdvantageType.cs deleted file mode 100644 index 9bd7486..0000000 --- a/src/Core/DiceParser/DieAdvantageType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Geekbot.Core.DiceParser -{ - public enum DieAdvantageType - { - Advantage, - Disadvantage, - None - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/DieResult.cs b/src/Core/DiceParser/DieResult.cs deleted file mode 100644 index aa309c0..0000000 --- a/src/Core/DiceParser/DieResult.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace Geekbot.Core.DiceParser -{ - public class DieResult - { - // public int Result { get; set; } - public int Roll1 { get; set; } - public int Roll2 { get; set; } - public DieAdvantageType AdvantageType { get; set; } - - public override string ToString() - { - return AdvantageType switch - { - DieAdvantageType.Advantage => Roll1 > Roll2 ? $"(**{Roll1}**, {Roll2})" : $"({Roll1}, **{Roll2}**)", - DieAdvantageType.Disadvantage => Roll1 < Roll2 ? $"(**{Roll1}**, {Roll2})" : $"({Roll1}, **{Roll2}**)", - _ => Result.ToString() - }; - } - - public int Result => AdvantageType switch - { - DieAdvantageType.None => Roll1, - DieAdvantageType.Advantage => Math.Max(Roll1, Roll2), - DieAdvantageType.Disadvantage => Math.Min(Roll1, Roll2), - _ => 0 - }; - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/IDiceParser.cs b/src/Core/DiceParser/IDiceParser.cs deleted file mode 100644 index ab1ebd3..0000000 --- a/src/Core/DiceParser/IDiceParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Geekbot.Core.DiceParser -{ - public interface IDiceParser - { - DiceInput Parse(string input); - } -} \ No newline at end of file diff --git a/src/Core/DiceParser/SingleDie.cs b/src/Core/DiceParser/SingleDie.cs deleted file mode 100644 index 7c1ae41..0000000 --- a/src/Core/DiceParser/SingleDie.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using Geekbot.Core.Extensions; -using Geekbot.Core.RandomNumberGenerator; - -namespace Geekbot.Core.DiceParser -{ - public class SingleDie - { - private readonly IRandomNumberGenerator _random; - - public SingleDie(IRandomNumberGenerator random) - { - _random = random; - } - - public int Sides { get; set; } = 20; - public int Amount { get; set; } = 1; - public DieAdvantageType AdvantageType { get; set; } = DieAdvantageType.None; - - public string DiceName => AdvantageType switch - { - DieAdvantageType.Advantage => $"{Amount}d{Sides} (with advantage)", - DieAdvantageType.Disadvantage => $"{Amount}d{Sides} (with disadvantage)", - _ => $"{Amount}d{Sides}" - }; - - public List Roll() - { - var results = new List(); - - Amount.Times(() => - { - var result = new DieResult - { - Roll1 = _random.Next(1, Sides), - AdvantageType = AdvantageType - }; - - if (AdvantageType == DieAdvantageType.Advantage || AdvantageType == DieAdvantageType.Disadvantage) - { - result.Roll2 = _random.Next(1, Sides); - } - - results.Add(result); - }); - - return results; - } - - public void ValidateDie() - { - if (Amount < 1) - { - throw new DiceException("To few dice, must be a minimum of 1"); - } - if (Amount > 24) - { - throw new DiceException("To many dice, maximum allowed is 24") { DiceName = DiceName }; - } - - if (Sides < 2) - { - throw new DiceException("Die must have at least 2 sides") { DiceName = DiceName }; - } - - if (Sides > 145) - { - throw new DiceException("Die can not have more than 145 sides") { DiceName = DiceName }; - } - } - } -} \ No newline at end of file diff --git a/src/Core/Extensions/DbSetExtensions.cs b/src/Core/Extensions/DbSetExtensions.cs deleted file mode 100644 index 3ccffe9..0000000 --- a/src/Core/Extensions/DbSetExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; - -namespace Geekbot.Core.Extensions -{ - public static class DbSetExtensions - { - public static EntityEntry AddIfNotExists(this DbSet dbSet, T entity, Expression> predicate = null) where T : class, new() - { - var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any(); - return !exists ? dbSet.Add(entity) : null; - } - - // https://github.com/dotnet/efcore/issues/18124 - public static IAsyncEnumerable AsAsyncEnumerable(this Microsoft.EntityFrameworkCore.DbSet obj) where TEntity : class - { - return Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsAsyncEnumerable(obj); - } - public static IQueryable Where(this Microsoft.EntityFrameworkCore.DbSet obj, System.Linq.Expressions.Expression> predicate) where TEntity : class - { - return System.Linq.Queryable.Where(obj, predicate); - } - } -} \ No newline at end of file diff --git a/src/Core/Extensions/IntExtensions.cs b/src/Core/Extensions/IntExtensions.cs deleted file mode 100644 index dac03e7..0000000 --- a/src/Core/Extensions/IntExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Geekbot.Core.Extensions -{ - public static class IntExtensions - { - public static void Times(this int count, Action action) - { - for (var i = 0; i < count; i++) - { - action(); - } - } - } -} \ No newline at end of file diff --git a/src/Core/GeekbotCommandBase.cs b/src/Core/GeekbotCommandBase.cs deleted file mode 100644 index 801c53c..0000000 --- a/src/Core/GeekbotCommandBase.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Globalization; -using System.Threading; -using Discord.Commands; -using Geekbot.Core.Database.Models; -using Geekbot.Core.ErrorHandling; -using Geekbot.Core.GuildSettingsManager; - -namespace Geekbot.Core -{ - public class GeekbotCommandBase : TransactionModuleBase - { - protected readonly IGuildSettingsManager GuildSettingsManager; - protected GuildSettingsModel GuildSettings; - protected readonly IErrorHandler ErrorHandler; - - protected GeekbotCommandBase(IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager) - { - GuildSettingsManager = guildSettingsManager; - ErrorHandler = errorHandler; - } - - protected override void BeforeExecute(CommandInfo command) - { - base.BeforeExecute(command); - - var setupSpan = Transaction.StartChild("Setup"); - - GuildSettings = GuildSettingsManager.GetSettings(Context?.Guild?.Id ?? 0); - var language = GuildSettings.Language; - Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(language); - - setupSpan.Finish(); - } - } -} \ No newline at end of file diff --git a/src/Core/GuildSettingsManager/GuildSettingsManager.cs b/src/Core/GuildSettingsManager/GuildSettingsManager.cs deleted file mode 100644 index 565563f..0000000 --- a/src/Core/GuildSettingsManager/GuildSettingsManager.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Core.Extensions; - -namespace Geekbot.Core.GuildSettingsManager -{ - public class GuildSettingsManager : IGuildSettingsManager - { - private readonly DatabaseContext _database; - private readonly Dictionary _settings; - - public GuildSettingsManager(DatabaseContext database) - { - _database = database; - _settings = new Dictionary(); - } - - public GuildSettingsModel GetSettings(ulong guildId, bool createIfNonExist = true) - { - return _settings.ContainsKey(guildId) ? _settings[guildId] : GetFromDatabase(guildId, createIfNonExist); - } - - public async Task UpdateSettings(GuildSettingsModel settings) - { - _database.GuildSettings.Update(settings); - if (_settings.ContainsKey(settings.GuildId.AsUlong())) - { - _settings[settings.GuildId.AsUlong()] = settings; - } - else - { - _settings.Add(settings.GuildId.AsUlong(), settings); - } - await _database.SaveChangesAsync(); - } - - private GuildSettingsModel GetFromDatabase(ulong guildId, bool createIfNonExist) - { - var settings = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId.Equals(guildId.AsLong())); - if (createIfNonExist && settings == null) - { - settings = CreateSettings(guildId); - } - - _settings.Add(guildId, settings); - return settings; - } - - private GuildSettingsModel CreateSettings(ulong guildId) - { - _database.GuildSettings.Add(new GuildSettingsModel - { - GuildId = guildId.AsLong(), - Hui = false, - Ping = false, - Language = "EN", - ShowDelete = false, - ShowLeave = false, - WikiLang = "en" - }); - _database.SaveChanges(); - return _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildId.AsLong())); - } - } -} \ No newline at end of file diff --git a/src/Core/GuildSettingsManager/IGuildSettingsManager.cs b/src/Core/GuildSettingsManager/IGuildSettingsManager.cs deleted file mode 100644 index d34eb73..0000000 --- a/src/Core/GuildSettingsManager/IGuildSettingsManager.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; -using Geekbot.Core.Database.Models; - -namespace Geekbot.Core.GuildSettingsManager -{ - public interface IGuildSettingsManager - { - GuildSettingsModel GetSettings(ulong guildId, bool createIfNonExist = true); - Task UpdateSettings(GuildSettingsModel settings); - } -} \ No newline at end of file diff --git a/src/Core/Highscores/HighscoreTypes.cs b/src/Core/Highscores/HighscoreTypes.cs deleted file mode 100644 index 3aa396e..0000000 --- a/src/Core/Highscores/HighscoreTypes.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Geekbot.Core.Highscores -{ - public enum HighscoreTypes - { - messages, - karma, - rolls, - cookies, - seasons, - quotes - } -} \ No newline at end of file diff --git a/src/Core/Highscores/SeasonsUtils.cs b/src/Core/Highscores/SeasonsUtils.cs deleted file mode 100644 index d2a8e3e..0000000 --- a/src/Core/Highscores/SeasonsUtils.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Globalization; - -namespace Geekbot.Core.Highscores -{ - public class SeasonsUtils - { - public static string GetCurrentSeason() - { - var now = DateTime.Now; - var year = (now.Year - 2000).ToString(CultureInfo.InvariantCulture); - var quarter = Math.Ceiling(now.Month / 3.0).ToString(CultureInfo.InvariantCulture); - return $"{year}Q{quarter}"; - } - } -} \ No newline at end of file diff --git a/src/Core/HttpAbstractions.cs b/src/Core/HttpAbstractions.cs deleted file mode 100644 index 629de74..0000000 --- a/src/Core/HttpAbstractions.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace Geekbot.Core -{ - public static class HttpAbstractions - { - public static HttpClient CreateDefaultClient() - { - var client = new HttpClient - { - DefaultRequestHeaders = - { - Accept = {MediaTypeWithQualityHeaderValue.Parse("application/json")}, - } - }; - client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Geekbot/v0.0.0 (+https://geekbot.pizzaandcoffee.rocks/)"); - - return client; - } - - public static async Task Get(Uri location, HttpClient httpClient = null, bool disposeClient = true, int maxRetries = 3) - { - httpClient ??= CreateDefaultClient(); - httpClient.BaseAddress = location; - - HttpResponseMessage response; - try - { - response = await Execute(() => httpClient.GetAsync(location.PathAndQuery), maxRetries); - } - finally - { - if (disposeClient) - { - httpClient.Dispose(); - } - } - - var stringResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(stringResponse); - } - - public static async Task Post(Uri location, object data, HttpClient httpClient = null, bool disposeClient = true, int maxRetries = 3) - { - httpClient ??= CreateDefaultClient(); - httpClient.BaseAddress = location; - - var content = new StringContent( - JsonSerializer.Serialize(data, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), - Encoding.UTF8, - "application/json" - ); - - HttpResponseMessage response; - try - { - response = await Execute(() => httpClient.PostAsync(location, content), maxRetries); - } - finally - { - if (disposeClient) - { - httpClient.Dispose(); - } - } - - var stringResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(stringResponse); - } - - public static async Task Post(Uri location, object data, HttpClient httpClient = null, bool disposeClient = true, int maxRetries = 3) - { - httpClient ??= CreateDefaultClient(); - httpClient.BaseAddress = location; - - var content = new StringContent( - JsonSerializer.Serialize(data, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), - Encoding.UTF8, - "application/json" - ); - - try - { - await Execute(() => httpClient.PostAsync(location, content), maxRetries); - } - finally - { - if (disposeClient) - { - httpClient.Dispose(); - } - } - } - - public static async Task Patch(Uri location, object data, HttpClient httpClient = null, bool disposeClient = true, int maxRetries = 3) - { - httpClient ??= CreateDefaultClient(); - httpClient.BaseAddress = location; - - var content = new StringContent( - JsonSerializer.Serialize(data, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }), - Encoding.UTF8, - "application/json" - ); - - try - { - await Execute(() => httpClient.PatchAsync(location, content), maxRetries); - } - finally - { - if (disposeClient) - { - httpClient.Dispose(); - } - } - } - - public static async Task Delete(Uri location, HttpClient httpClient = null, bool disposeClient = true, int maxRetries = 3) - { - httpClient ??= CreateDefaultClient(); - httpClient.BaseAddress = location; - - try - { - await Execute(() => httpClient.DeleteAsync(location), maxRetries); - } - finally - { - if (disposeClient) - { - httpClient.Dispose(); - } - } - } - - private static async Task Execute(Func> request, int maxRetries) - { - var attempt = 0; - while (true) - { - var response = await request(); - if (!response.IsSuccessStatusCode) - { - if (attempt >= maxRetries) - { - throw new HttpRequestException($"Request failed after {attempt} attempts"); - } - - if (response.Headers.Contains("Retry-After")) - { - var retryAfter = response.Headers.GetValues("Retry-After").First(); - if (retryAfter.Contains(':')) - { - var duration = DateTimeOffset.Parse(retryAfter).ToUniversalTime() - DateTimeOffset.Now.ToUniversalTime(); - await Task.Delay(duration); - } - else - { - await Task.Delay(int.Parse(retryAfter) * 1000); - } - } - else if (response.StatusCode is HttpStatusCode.BadGateway or HttpStatusCode.ServiceUnavailable or HttpStatusCode.GatewayTimeout) - { - await Task.Delay(TimeSpan.FromSeconds(Math.Ceiling(attempt * 1.5))); - } - else - { - response.EnsureSuccessStatusCode(); - } - - attempt++; - } - else - { - return response; - } - } - } - } -} \ No newline at end of file diff --git a/src/Core/KvInMemoryStore/IKvInMemoryStore.cs b/src/Core/KvInMemoryStore/IKvInMemoryStore.cs deleted file mode 100644 index 6281f4b..0000000 --- a/src/Core/KvInMemoryStore/IKvInMemoryStore.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Geekbot.Core.KvInMemoryStore -{ - public interface IKvInMemoryStore - { - public T Get(string key); - public void Set(string key, T value); - public void Remove(string key); - } -} \ No newline at end of file diff --git a/src/Core/KvInMemoryStore/KvInMemoryStore.cs b/src/Core/KvInMemoryStore/KvInMemoryStore.cs deleted file mode 100644 index 4a50546..0000000 --- a/src/Core/KvInMemoryStore/KvInMemoryStore.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; - -namespace Geekbot.Core.KvInMemoryStore -{ - public class KvInInMemoryStore : IKvInMemoryStore - { - private readonly Dictionary _storage = new Dictionary(); - - public T Get(string key) - { - try - { - return (T) _storage[key]; - } - catch - { - return default; - } - } - - public void Set(string key, T value) - { - _storage.Remove(key); - _storage.Add(key, value); - } - - public void Remove(string key) - { - _storage.Remove(key); - } - } -} \ No newline at end of file diff --git a/src/Core/Localization/Admin.Designer.cs b/src/Core/Localization/Admin.Designer.cs deleted file mode 100644 index 460d471..0000000 --- a/src/Core/Localization/Admin.Designer.cs +++ /dev/null @@ -1,78 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Admin { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Admin() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Admin", typeof(Admin).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to I'm talking english. - /// - public static string GetLanguage { - get { - return ResourceManager.GetString("GetLanguage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to I will reply in english from now on. - /// - public static string NewLanguageSet { - get { - return ResourceManager.GetString("NewLanguageSet", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Admin.de-ch.resx b/src/Core/Localization/Admin.de-ch.resx deleted file mode 100644 index b89939b..0000000 --- a/src/Core/Localization/Admin.de-ch.resx +++ /dev/null @@ -1,20 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - I werd ab jetzt uf schwiizerdüütsch antworte, äuuä - - - I red schwiizerdüütsch - - \ No newline at end of file diff --git a/src/Core/Localization/Admin.resx b/src/Core/Localization/Admin.resx deleted file mode 100644 index 6fd2c62..0000000 --- a/src/Core/Localization/Admin.resx +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - I will reply in english from now on - - - I'm talking english - - \ No newline at end of file diff --git a/src/Core/Localization/Choose.Designer.cs b/src/Core/Localization/Choose.Designer.cs deleted file mode 100644 index 91ef136..0000000 --- a/src/Core/Localization/Choose.Designer.cs +++ /dev/null @@ -1,69 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Choose { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Choose() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Choose", typeof(Choose).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to I Choose **{0}**. - /// - public static string Choice { - get { - return ResourceManager.GetString("Choice", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Choose.de-ch.resx b/src/Core/Localization/Choose.de-ch.resx deleted file mode 100644 index eedc39b..0000000 --- a/src/Core/Localization/Choose.de-ch.resx +++ /dev/null @@ -1,17 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - I nimme **{0}** - - \ No newline at end of file diff --git a/src/Core/Localization/Choose.resx b/src/Core/Localization/Choose.resx deleted file mode 100644 index 052177b..0000000 --- a/src/Core/Localization/Choose.resx +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - I Choose **{0}** - - \ No newline at end of file diff --git a/src/Core/Localization/Cookies.Designer.cs b/src/Core/Localization/Cookies.Designer.cs deleted file mode 100644 index 7a3442c..0000000 --- a/src/Core/Localization/Cookies.Designer.cs +++ /dev/null @@ -1,96 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - using System; - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Cookies { - - private static System.Resources.ResourceManager resourceMan; - - private static System.Globalization.CultureInfo resourceCulture; - - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Cookies() { - } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - public static System.Resources.ResourceManager ResourceManager { - get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Geekbot.Core.Localization.Cookies", typeof(Cookies).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - public static System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - public static string GetCookies { - get { - return ResourceManager.GetString("GetCookies", resourceCulture); - } - } - - public static string WaitForMoreCookies { - get { - return ResourceManager.GetString("WaitForMoreCookies", resourceCulture); - } - } - - public static string InYourJar { - get { - return ResourceManager.GetString("InYourJar", resourceCulture); - } - } - - public static string Given { - get { - return ResourceManager.GetString("Given", resourceCulture); - } - } - - public static string NotEnoughToGive { - get { - return ResourceManager.GetString("NotEnoughToGive", resourceCulture); - } - } - - public static string NotEnoughCookiesToEat { - get { - return ResourceManager.GetString("NotEnoughCookiesToEat", resourceCulture); - } - } - - public static string AteCookies { - get { - return ResourceManager.GetString("AteCookies", resourceCulture); - } - } - - public static string CantTakeCookies { - get { - return ResourceManager.GetString("CantTakeCookies", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Cookies.de-ch.resx b/src/Core/Localization/Cookies.de-ch.resx deleted file mode 100644 index 3652224..0000000 --- a/src/Core/Localization/Cookies.de-ch.resx +++ /dev/null @@ -1,38 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Du häsch {0} guetzli becho, du häsch jetzt {1} guetzli ih dr büchse - - - Du hesch scho guetzli becho hüt, du chasch meh ha in {0} - - - Es hät {0} guetzli ih dineri büchs - - - Du hesch {1} {0} guetzli geh - - - Du hesch nid gnueg guetzli - - - Du hesch chuum no guetzli ih dineri büchs, du sötsch warschinli keini esse - - - Du hesch {0} guetzli gesse und hesch jezt no {1} übrig - - - :police_officer: Du chasch nid guetzli vo anderne chlaue... - - \ No newline at end of file diff --git a/src/Core/Localization/Cookies.resx b/src/Core/Localization/Cookies.resx deleted file mode 100644 index a359c5c..0000000 --- a/src/Core/Localization/Cookies.resx +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - You got {0} cookies, there are now {1} cookies in you cookie jar - - - You already got cookies today, you can have more cookies in {0} - - - There are {0} cookies in you cookie jar - - - You gave {0} cookies to {1} - - - You don't have enough cookies - - - Your cookie jar looks almost empty, you should probably not eat a cookie - - - You ate {0} cookies, you've only got {1} cookies left - - - You can't take someone else's cookies - - \ No newline at end of file diff --git a/src/Core/Localization/Corona.Designer.cs b/src/Core/Localization/Corona.Designer.cs deleted file mode 100644 index a19bbcd..0000000 --- a/src/Core/Localization/Corona.Designer.cs +++ /dev/null @@ -1,114 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Corona { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Corona() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Corona", typeof(Corona).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Active. - /// - public static string Active { - get { - return ResourceManager.GetString("Active", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Confirmed Corona Cases. - /// - public static string ConfirmedCases { - get { - return ResourceManager.GetString("ConfirmedCases", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Deaths. - /// - public static string Deaths { - get { - return ResourceManager.GetString("Deaths", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Recovered. - /// - public static string Recovered { - get { - return ResourceManager.GetString("Recovered", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Source. - /// - public static string Source { - get { - return ResourceManager.GetString("Source", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Total. - /// - public static string Total { - get { - return ResourceManager.GetString("Total", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Corona.de-ch.resx b/src/Core/Localization/Corona.de-ch.resx deleted file mode 100644 index 3c4180c..0000000 --- a/src/Core/Localization/Corona.de-ch.resx +++ /dev/null @@ -1,32 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Bstätigti Corona Fallzahle - - - Total - - - Aktiv - - - Erholt - - - Gstorbe - - - Quelle - - \ No newline at end of file diff --git a/src/Core/Localization/Corona.resx b/src/Core/Localization/Corona.resx deleted file mode 100644 index 44bf85e..0000000 --- a/src/Core/Localization/Corona.resx +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Confirmed Corona Cases - - - Total - - - Active - - - Recovered - - - Deaths - - - Source - - \ No newline at end of file diff --git a/src/Core/Localization/EightBall.Designer.cs b/src/Core/Localization/EightBall.Designer.cs deleted file mode 100644 index eee2bc5..0000000 --- a/src/Core/Localization/EightBall.Designer.cs +++ /dev/null @@ -1,240 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class EightBall { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public EightBall() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.EightBall", typeof(EightBall).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to As I see it, yes. - /// - public static string AsISeeItYes { - get { - return ResourceManager.GetString("AsISeeItYes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ask again later. - /// - public static string AskAgainLater { - get { - return ResourceManager.GetString("AskAgainLater", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Better not tell you now. - /// - public static string BetterNotTellYouNow { - get { - return ResourceManager.GetString("BetterNotTellYouNow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot predict now. - /// - public static string CannotPredictNow { - get { - return ResourceManager.GetString("CannotPredictNow", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Concentrate and ask again. - /// - public static string ConcentrateAndAskAgain { - get { - return ResourceManager.GetString("ConcentrateAndAskAgain", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Don't count on it. - /// - public static string DontCountOnIt { - get { - return ResourceManager.GetString("DontCountOnIt", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It is certain. - /// - public static string ItIsCertain { - get { - return ResourceManager.GetString("ItIsCertain", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It is decidedly so. - /// - public static string ItIsDecidedlySo { - get { - return ResourceManager.GetString("ItIsDecidedlySo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Most likely. - /// - public static string MostLikely { - get { - return ResourceManager.GetString("MostLikely", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to My reply is no. - /// - public static string MyReplyIsNo { - get { - return ResourceManager.GetString("MyReplyIsNo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to My sources say no. - /// - public static string MySourcesSayNo { - get { - return ResourceManager.GetString("MySourcesSayNo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Outlook good. - /// - public static string OutlookGood { - get { - return ResourceManager.GetString("OutlookGood", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Outlook not so good. - /// - public static string OutlookNotSoGood { - get { - return ResourceManager.GetString("OutlookNotSoGood", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reply hazy try again. - /// - public static string ReplyHazyTryAgain { - get { - return ResourceManager.GetString("ReplyHazyTryAgain", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Signs point to yes. - /// - public static string SignsPointToYes { - get { - return ResourceManager.GetString("SignsPointToYes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Very doubtful. - /// - public static string VeryDoubtful { - get { - return ResourceManager.GetString("VeryDoubtful", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Without a doubt. - /// - public static string WithoutADoubt { - get { - return ResourceManager.GetString("WithoutADoubt", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yes. - /// - public static string Yes { - get { - return ResourceManager.GetString("Yes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yes, definitely. - /// - public static string YesDefinitely { - get { - return ResourceManager.GetString("YesDefinitely", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You may rely on it. - /// - public static string YouMayRelyOnIt { - get { - return ResourceManager.GetString("YouMayRelyOnIt", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/EightBall.de-ch.resx b/src/Core/Localization/EightBall.de-ch.resx deleted file mode 100644 index 89d7a60..0000000 --- a/src/Core/Localization/EightBall.de-ch.resx +++ /dev/null @@ -1,94 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Es isch sicher - - - - So isch es entschiede worde - - - - Ohni zwifel - - - - Ja, absolut - - - - Chasch davo usgoh - - - - Wie ich es gsehn, ja - - - - Sehr waschinli - - - - Ussicht isch guet - - - - Ja - - - - Ahzeiche zeigend uf ja - - - - Antwort isch verschwumme, versuechs nomol - - - - Frög spöter nomol - - - - Segs dir jetzt besser nid - - - - Im mommnet chani das nid vorussege - - - - Konzentrier di und frog nomol - - - - Zähl nid druf - - - - Mini antwort isch nei - - - - Mini quellene seged nei - - - - Ussicht isch ned so guet - - - - Sehr froglich - - - \ No newline at end of file diff --git a/src/Core/Localization/EightBall.resx b/src/Core/Localization/EightBall.resx deleted file mode 100644 index 6eba369..0000000 --- a/src/Core/Localization/EightBall.resx +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 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 - - - \ No newline at end of file diff --git a/src/Core/Localization/Internal.Designer.cs b/src/Core/Localization/Internal.Designer.cs deleted file mode 100644 index 967b26e..0000000 --- a/src/Core/Localization/Internal.Designer.cs +++ /dev/null @@ -1,123 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Internal { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Internal() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Internal", typeof(Internal).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to and. - /// - public static string And { - get { - return ResourceManager.GetString("And", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to day|days. - /// - public static string Days { - get { - return ResourceManager.GetString("Days", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to hour|hours. - /// - public static string Hours { - get { - return ResourceManager.GetString("Hours", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Seems like i don't have enough permission to that :confused:. - /// - public static string Http403 { - get { - return ResourceManager.GetString("Http403", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to minute|minutes. - /// - public static string Minutes { - get { - return ResourceManager.GetString("Minutes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to second|seconds. - /// - public static string Seconds { - get { - return ResourceManager.GetString("Seconds", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Something went wrong :confused:. - /// - public static string SomethingWentWrong { - get { - return ResourceManager.GetString("SomethingWentWrong", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Internal.de-ch.resx b/src/Core/Localization/Internal.de-ch.resx deleted file mode 100644 index 568bb1f..0000000 --- a/src/Core/Localization/Internal.de-ch.resx +++ /dev/null @@ -1,35 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Öppis isch schief gange :confused: - - - Gseht danach us das ich nid gnueg recht han zum das mache :confused: - - - tag|täg - - - stund|stunde - - - minute|minute - - - sekunde|sekunde - - - und - - \ No newline at end of file diff --git a/src/Core/Localization/Internal.resx b/src/Core/Localization/Internal.resx deleted file mode 100644 index a771d6b..0000000 --- a/src/Core/Localization/Internal.resx +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Something went wrong :confused: - - - Seems like i don't have enough permission to that :confused: - - - day|days - - - hour|hours - - - minute|minutes - - - second|seconds - - - and - - \ No newline at end of file diff --git a/src/Core/Localization/Karma.Designer.cs b/src/Core/Localization/Karma.Designer.cs deleted file mode 100644 index b3e8325..0000000 --- a/src/Core/Localization/Karma.Designer.cs +++ /dev/null @@ -1,150 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Karma { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Karma() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Karma", typeof(Karma).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Amount. - /// - public static string Amount { - get { - return ResourceManager.GetString("Amount", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to By. - /// - public static string By { - get { - return ResourceManager.GetString("By", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sorry {0}, but you can't lower your own karma. - /// - public static string CannotChangeOwnDown { - get { - return ResourceManager.GetString("CannotChangeOwnDown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sorry {0}, but you can't give yourself neutral karma. - /// - public static string CannotChangeOwnSame { - get { - return ResourceManager.GetString("CannotChangeOwnSame", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sorry {0}, but you can't give yourself karma. - /// - public static string CannotChangeOwnUp { - get { - return ResourceManager.GetString("CannotChangeOwnUp", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Current. - /// - public static string Current { - get { - return ResourceManager.GetString("Current", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Karma lowered. - /// - public static string Decreased { - get { - return ResourceManager.GetString("Decreased", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gained Karma. - /// - public static string Increased { - get { - return ResourceManager.GetString("Increased", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Neutral Karma. - /// - public static string Neutral { - get { - return ResourceManager.GetString("Neutral", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sorry {0}, but you have to wait {1} before you can give karma again.... - /// - public static string WaitUntill { - get { - return ResourceManager.GetString("WaitUntill", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Karma.de-ch.resx b/src/Core/Localization/Karma.de-ch.resx deleted file mode 100644 index 5805a27..0000000 --- a/src/Core/Localization/Karma.de-ch.resx +++ /dev/null @@ -1,44 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Sorry {0}, aber du chasch dr selber kei karma geh - - - Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch geh... - - - Karma becho - - - Vo - - - Mengi - - - Jetzt - - - Sorry {0}, aber du chasch dis eigete karma nid senke - - - Karma gsenkt - - - Neutral Karma - - - Sorry {0}, aber du chasch dr selber kei neutrals karma geh - - \ No newline at end of file diff --git a/src/Core/Localization/Karma.resx b/src/Core/Localization/Karma.resx deleted file mode 100644 index a16e14a..0000000 --- a/src/Core/Localization/Karma.resx +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Sorry {0}, but you can't give yourself karma - - - Sorry {0}, but you have to wait {1} before you can give karma again... - - - Gained Karma - - - By - - - Amount - - - Current - - - Sorry {0}, but you can't lower your own karma - - - Karma lowered - - - Neutral Karma - - - Sorry {0}, but you can't give yourself neutral karma - - \ No newline at end of file diff --git a/src/Core/Localization/Quote.Designer.cs b/src/Core/Localization/Quote.Designer.cs deleted file mode 100644 index 9146971..0000000 --- a/src/Core/Localization/Quote.Designer.cs +++ /dev/null @@ -1,159 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Quote { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Quote() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Quote", typeof(Quote).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to You can't save quotes by a bot.... - /// - public static string CannotQuoteBots { - get { - return ResourceManager.GetString("CannotQuoteBots", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You can't save your own quotes.... - /// - public static string CannotSaveOwnQuotes { - get { - return ResourceManager.GetString("CannotSaveOwnQuotes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Most quoted person. - /// - public static string MostQuotesPerson { - get { - return ResourceManager.GetString("MostQuotesPerson", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This server doesn't seem to have any quotes yet. You can add a quote with `!quote save @user` or `!quote save <messageId>`. - /// - public static string NoQuotesFound { - get { - return ResourceManager.GetString("NoQuotesFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to That is not a valid message link. - /// - public static string NotAValidMessageLink { - get { - return ResourceManager.GetString("NotAValidMessageLink", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to I couldn't find a quote with that ID :disappointed:. - /// - public static string NotFoundWithId { - get { - return ResourceManager.GetString("NotFoundWithId", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You can only quote messages from the same server. - /// - public static string OnlyQuoteFromSameServer { - get { - return ResourceManager.GetString("OnlyQuoteFromSameServer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to **Quote Added**. - /// - public static string QuoteAdded { - get { - return ResourceManager.GetString("QuoteAdded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quote Stats. - /// - public static string QuoteStats { - get { - return ResourceManager.GetString("QuoteStats", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to **Removed #{0}**. - /// - public static string Removed { - get { - return ResourceManager.GetString("Removed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Total. - /// - public static string TotalQuotes { - get { - return ResourceManager.GetString("TotalQuotes", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Quote.de-ch.resx b/src/Core/Localization/Quote.de-ch.resx deleted file mode 100644 index 99fd959..0000000 --- a/src/Core/Localization/Quote.de-ch.resx +++ /dev/null @@ -1,47 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Dä server het no kei quotes. Du chasch quotes hinzuefüege mit `!quote save @user` oder `!quote save <messageId>` - - - Du chasch kei quotes vo dir selber speichere... - - - Du chasch kei quotes vomne bot speichere... - - - **Quote hinzugfüegt** - - - **#{0} glöscht** - - - Ich chan kei quote finde mit därri ID :disappointed: - - - Quote statistike - - - Total - - - Meist quoteti person - - - Das isch kei korrete nachrichtelink - - - Du chasch numme nachrichte vom gliche server quote - - \ No newline at end of file diff --git a/src/Core/Localization/Quote.resx b/src/Core/Localization/Quote.resx deleted file mode 100644 index b51d79c..0000000 --- a/src/Core/Localization/Quote.resx +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - This server doesn't seem to have any quotes yet. You can add a quote with `!quote save @user` or `!quote save <messageId>` - - - You can't save your own quotes... - - - You can't save quotes by a bot... - - - **Quote Added** - - - **Removed #{0}** - - - I couldn't find a quote with that ID :disappointed: - - - Quote Stats - - - Total - - - Most quoted person - - - That is not a valid message link - - - You can only quote messages from the same server - - \ No newline at end of file diff --git a/src/Core/Localization/Rank.Designer.cs b/src/Core/Localization/Rank.Designer.cs deleted file mode 100644 index 1439a8c..0000000 --- a/src/Core/Localization/Rank.Designer.cs +++ /dev/null @@ -1,105 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Rank { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Rank() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Rank", typeof(Rank).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to :warning: I couldn't find all usernames. Maybe they left the server?. - /// - public static string FailedToResolveAllUsernames { - get { - return ResourceManager.GetString("FailedToResolveAllUsernames", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to :bar_chart: **{0} Highscore for {1}**. - /// - public static string HighscoresFor { - get { - return ResourceManager.GetString("HighscoresFor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Valid types are '`messages`' '`karma`', '`rolls`', '`cookies`', '`seasons`' and '`quotes`'. - /// - public static string InvalidType { - get { - return ResourceManager.GetString("InvalidType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to :warning: Limiting to 20. - /// - public static string LimitingTo20Warning { - get { - return ResourceManager.GetString("LimitingTo20Warning", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No {0} found on this server. - /// - public static string NoTypeFoundForServer { - get { - return ResourceManager.GetString("NoTypeFoundForServer", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Rank.de-ch.resx b/src/Core/Localization/Rank.de-ch.resx deleted file mode 100644 index 743b8cd..0000000 --- a/src/Core/Localization/Rank.de-ch.resx +++ /dev/null @@ -1,29 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - :warning: Limitiert uf 20 - - - Kei {0} gfunde für dä server - - - :warning: Ich han nid alli benutzername gfunde. villiicht hend sie de server verlah? - - - :bar_chart: **{0} Highscore für {1}** - - - Gültigi paramenter sind '`messages`' '`karma`', '`rolls`', '`cookies`', '`seasons`' und '`quotes`' - - \ No newline at end of file diff --git a/src/Core/Localization/Rank.resx b/src/Core/Localization/Rank.resx deleted file mode 100644 index 606a34e..0000000 --- a/src/Core/Localization/Rank.resx +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Valid types are '`messages`' '`karma`', '`rolls`', '`cookies`', '`seasons`' and '`quotes`' - - - :warning: Limiting to 20 - - - No {0} found on this server - - - :warning: I couldn't find all usernames. Maybe they left the server? - - - :bar_chart: **{0} Highscore for {1}** - - \ No newline at end of file diff --git a/src/Core/Localization/Role.Designer.cs b/src/Core/Localization/Role.Designer.cs deleted file mode 100644 index fc48852..0000000 --- a/src/Core/Localization/Role.Designer.cs +++ /dev/null @@ -1,150 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Role { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Role() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Role", typeof(Role).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Added {0} to the whitelist. - /// - public static string AddedRoleToWhitelist { - get { - return ResourceManager.GetString("AddedRoleToWhitelist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Added you to {0}. - /// - public static string AddedUserFromRole { - get { - return ResourceManager.GetString("AddedUserFromRole", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You cannot add that role to self service because it contains one or more dangerous permissions. - /// - public static string CannotAddDangerousRole { - get { - return ResourceManager.GetString("CannotAddDangerousRole", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You can't add a role that is managed by discord. - /// - public static string CannotAddManagedRole { - get { - return ResourceManager.GetString("CannotAddManagedRole", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to **Self Service Roles on {0}**. - /// - public static string ListHeader { - get { - return ResourceManager.GetString("ListHeader", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to To get a role, use `!role [name]`. - /// - public static string ListInstruction { - get { - return ResourceManager.GetString("ListInstruction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There are no roles configured for this server. - /// - public static string NoRolesConfigured { - get { - return ResourceManager.GetString("NoRolesConfigured", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Removed {0} from the whitelist. - /// - public static string RemovedRoleFromWhitelist { - get { - return ResourceManager.GetString("RemovedRoleFromWhitelist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Removed you from {0}. - /// - public static string RemovedUserFromRole { - get { - return ResourceManager.GetString("RemovedUserFromRole", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to That role doesn't exist or is not on the whitelist. - /// - public static string RoleNotFound { - get { - return ResourceManager.GetString("RoleNotFound", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Role.de-ch.resx b/src/Core/Localization/Role.de-ch.resx deleted file mode 100644 index b0b3259..0000000 --- a/src/Core/Localization/Role.de-ch.resx +++ /dev/null @@ -1,44 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Es sind kei rolle für dä server konfiguriert - - - **Self Service Rollene uf {0}** - - - Zum ä rolle becho, schriib `!role [name]` - - - Die rolle gids nid or isch nid uf dr whitelist - - - Han di entfernt vo {0} - - - Han di hinzue gfüegt zu {0} - - - Du chasch kei rolle hinzuefüge wo verwalted wird vo discord - - - Du chasch die rolle nid hinzuefüge will er ein oder mehreri gföhrlichi berechtigunge het - - - {0} isch zur whitelist hinzuegfüegt - - - {0} isch vo dr whitelist glöscht - - \ No newline at end of file diff --git a/src/Core/Localization/Role.resx b/src/Core/Localization/Role.resx deleted file mode 100644 index 63b70d0..0000000 --- a/src/Core/Localization/Role.resx +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - There are no roles configured for this server - - - **Self Service Roles on {0}** - - - To get a role, use `!role [name]` - - - That role doesn't exist or is not on the whitelist - - - Removed you from {0} - - - Added you to {0} - - - You can't add a role that is managed by discord - - - You cannot add that role to self service because it contains one or more dangerous permissions - - - Added {0} to the whitelist - - - Removed {0} from the whitelist - - \ No newline at end of file diff --git a/src/Core/Localization/Roll.Designer.cs b/src/Core/Localization/Roll.Designer.cs deleted file mode 100644 index bceb55d..0000000 --- a/src/Core/Localization/Roll.Designer.cs +++ /dev/null @@ -1,96 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Roll { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Roll() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Roll", typeof(Roll).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Congratulations {0}, your guess was correct!. - /// - public static string Gratz { - get { - return ResourceManager.GetString("Gratz", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to :red_circle: {0}, you can't guess the same number again, guess another number or wait {1}. - /// - public static string NoPrevGuess { - get { - return ResourceManager.GetString("NoPrevGuess", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}, you rolled {1}, your guess was {2}. - /// - public static string Rolled { - get { - return ResourceManager.GetString("Rolled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0}, you rolled {1}. - /// - public static string RolledNoGuess { - get { - return ResourceManager.GetString("RolledNoGuess", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Roll.de-ch.resx b/src/Core/Localization/Roll.de-ch.resx deleted file mode 100644 index ba73316..0000000 --- a/src/Core/Localization/Roll.de-ch.resx +++ /dev/null @@ -1,26 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - {0}, du hesch {1} grollt und hesch {2} grate - - - Gratuliere {0}, du hesch richtig grate! - - - {0}, du hesch {1} grollt - - - :red_circle: {0}, du chasch nid nomol es gliche rate, rate öppis anders oder warte {1} - - \ No newline at end of file diff --git a/src/Core/Localization/Roll.resx b/src/Core/Localization/Roll.resx deleted file mode 100644 index 822abb6..0000000 --- a/src/Core/Localization/Roll.resx +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - {0}, you rolled {1}, your guess was {2} - - - Congratulations {0}, your guess was correct! - - - {0}, you rolled {1} - - - :red_circle: {0}, you can't guess the same number again, guess another number or wait {1} - - \ No newline at end of file diff --git a/src/Core/Localization/Ship.Designer.cs b/src/Core/Localization/Ship.Designer.cs deleted file mode 100644 index 2d917b6..0000000 --- a/src/Core/Localization/Ship.Designer.cs +++ /dev/null @@ -1,114 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Ship { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Ship() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Ship", typeof(Ship).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Almost a match. - /// - public static string CouldWork { - get { - return ResourceManager.GetString("CouldWork", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It's a match. - /// - public static string ItsAMatch { - get { - return ResourceManager.GetString("ItsAMatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Matchmaking. - /// - public static string Matchmaking { - get { - return ResourceManager.GetString("Matchmaking", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Not going happen. - /// - public static string NotGoingToHappen { - get { - return ResourceManager.GetString("NotGoingToHappen", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Not such a good idea. - /// - public static string NotSuchAGoodIdea { - get { - return ResourceManager.GetString("NotSuchAGoodIdea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to There might be a chance. - /// - public static string ThereMightBeAChance { - get { - return ResourceManager.GetString("ThereMightBeAChance", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Ship.de-ch.resx b/src/Core/Localization/Ship.de-ch.resx deleted file mode 100644 index ec2880b..0000000 --- a/src/Core/Localization/Ship.de-ch.resx +++ /dev/null @@ -1,32 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Verkupple - - - Wird nöd klappe - - - Nöd so ä gueti idee - - - Es gid eventuel ä chance - - - Fasch en match - - - Es isch es traumpaar - - \ No newline at end of file diff --git a/src/Core/Localization/Ship.resx b/src/Core/Localization/Ship.resx deleted file mode 100644 index 611040f..0000000 --- a/src/Core/Localization/Ship.resx +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Matchmaking - - - Not going happen - - - Not such a good idea - - - There might be a chance - - - Almost a match - - - It's a match - - \ No newline at end of file diff --git a/src/Core/Localization/Stats.Designer.cs b/src/Core/Localization/Stats.Designer.cs deleted file mode 100644 index 5f9b96c..0000000 --- a/src/Core/Localization/Stats.Designer.cs +++ /dev/null @@ -1,150 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Geekbot.Core.Localization { - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class Stats { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - public Stats() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Geekbot.Core.Localization.Stats", typeof(Stats).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Cookies. - /// - public static string Cookies { - get { - return ResourceManager.GetString("Cookies", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Days. - /// - public static string Days { - get { - return ResourceManager.GetString("Days", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guessed Rolls. - /// - public static string GuessedRolls { - get { - return ResourceManager.GetString("GuessedRolls", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Joined Server. - /// - public static string JoinedServer { - get { - return ResourceManager.GetString("JoinedServer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Karma. - /// - public static string Karma { - get { - return ResourceManager.GetString("Karma", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Level. - /// - public static string Level { - get { - return ResourceManager.GetString("Level", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Messages Sent. - /// - public static string MessagesSent { - get { - return ResourceManager.GetString("MessagesSent", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to On Discord Since. - /// - public static string OnDiscordSince { - get { - return ResourceManager.GetString("OnDiscordSince", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quotes. - /// - public static string Quotes { - get { - return ResourceManager.GetString("Quotes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Server Total. - /// - public static string ServerTotal { - get { - return ResourceManager.GetString("ServerTotal", resourceCulture); - } - } - } -} diff --git a/src/Core/Localization/Stats.de-ch.resx b/src/Core/Localization/Stats.de-ch.resx deleted file mode 100644 index 0af0477..0000000 --- a/src/Core/Localization/Stats.de-ch.resx +++ /dev/null @@ -1,44 +0,0 @@ - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Server Total - - - Uf Discord siit - - - Nachrichte versendet - - - Level - - - Karma - - - Server Bitrette - - - Grateni Rolls - - - Guetzli - - - Täg - - - Quotes - - \ No newline at end of file diff --git a/src/Core/Localization/Stats.resx b/src/Core/Localization/Stats.resx deleted file mode 100644 index 6eb3a92..0000000 --- a/src/Core/Localization/Stats.resx +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - On Discord Since - - - Joined Server - - - Karma - - - Level - - - Messages Sent - - - Server Total - - - Guessed Rolls - - - Cookies - - - Days - - - Quotes - - \ No newline at end of file diff --git a/src/Core/Logger/Adapters/ILoggerAdapter.cs b/src/Core/Logger/Adapters/ILoggerAdapter.cs deleted file mode 100644 index 90e7bf3..0000000 --- a/src/Core/Logger/Adapters/ILoggerAdapter.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; - -namespace Geekbot.Core.Logger.Adapters; - -public class ILoggerAdapter : ILogger -{ - private readonly string _categoryName; - private readonly LogSource _logSource; - private readonly IGeekbotLogger _geekbotLogger; - public ILoggerAdapter(string categoryName, LogSource logSource, IGeekbotLogger geekbotLogger) - { - _categoryName = categoryName; - _logSource = logSource; - _geekbotLogger = geekbotLogger; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - switch (logLevel) - { - case LogLevel.Trace: - _geekbotLogger.Trace(_logSource, $"{eventId.Id} - {_categoryName} - {state}"); - break; - case LogLevel.Debug: - _geekbotLogger.Debug(_logSource, $"{eventId.Id} - {_categoryName} - {state}"); - break; - case LogLevel.Information: - _geekbotLogger.Information(_logSource, $"{eventId.Id} - {_categoryName} - {state}"); - break; - case LogLevel.Warning: - _geekbotLogger.Warning(_logSource, $"{eventId.Id} - {_categoryName} - {state}", exception); - break; - case LogLevel.Error: - case LogLevel.Critical: - _geekbotLogger.Error(_logSource, $"{eventId.Id} - {_categoryName} - {state}", exception); - break; - case LogLevel.None: - break; - default: - throw new ArgumentOutOfRangeException(nameof(logLevel)); - } - } - - public bool IsEnabled(LogLevel logLevel) - { - return _geekbotLogger.GetNLogger().IsEnabled(ToGeekbotLogLevel(logLevel)); - // return !_geekbotLogger.LogAsJson() && _geekbotLogger.GetNLogger().IsEnabled(ToGeekbotLogLevel(logLevel)); - } - - public IDisposable BeginScope(TState state) - { - return null; - } - - private static NLog.LogLevel ToGeekbotLogLevel(LogLevel level) - { - return level switch - { - LogLevel.Trace => NLog.LogLevel.Trace, - LogLevel.Debug => NLog.LogLevel.Debug, - LogLevel.Information => NLog.LogLevel.Info, - LogLevel.Warning => NLog.LogLevel.Warn, - LogLevel.Error => NLog.LogLevel.Error, - LogLevel.Critical => NLog.LogLevel.Fatal, - LogLevel.None => NLog.LogLevel.Off, - _ => throw new ArgumentOutOfRangeException(nameof(level)) - }; - } -} \ No newline at end of file diff --git a/src/Core/Logger/Adapters/ILoggerProviderProvider.cs b/src/Core/Logger/Adapters/ILoggerProviderProvider.cs deleted file mode 100644 index bc31a24..0000000 --- a/src/Core/Logger/Adapters/ILoggerProviderProvider.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; - -namespace Geekbot.Core.Logger.Adapters; - -public class ILoggerProviderProvider : ILoggerProvider, ILoggerFactory -{ - private readonly IGeekbotLogger _geekbotLogger; - private readonly LogSource _logSource; - - private readonly ConcurrentDictionary _loggers = new(); - - public ILoggerProviderProvider(IGeekbotLogger geekbotLogger, LogSource logSource) - { - _geekbotLogger = geekbotLogger; - _logSource = logSource; - } - - public void Dispose() - { - _loggers.Clear(); - } - - public ILogger CreateLogger(string categoryName) - { - return _loggers.GetOrAdd(categoryName, name => new ILoggerAdapter(categoryName, _logSource, _geekbotLogger)); - } - - public void AddProvider(ILoggerProvider provider) - { - throw new System.NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/Core/Logger/ExceptionDto.cs b/src/Core/Logger/ExceptionDto.cs deleted file mode 100644 index bd67d90..0000000 --- a/src/Core/Logger/ExceptionDto.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Geekbot.Core.Logger; - -public struct ExceptionDto -{ - public string Message { get; init; } - - public string InnerException { get; init; } - - public string Source { get; init; } - - public ExceptionDto(Exception exception) - { - Message = exception.Message; - InnerException = string.IsNullOrEmpty(exception?.InnerException?.ToString()) ? exception?.StackTrace : exception?.InnerException?.ToString(); - Source = exception.Source; - } -}; \ No newline at end of file diff --git a/src/Core/Logger/GeekbotLogger.cs b/src/Core/Logger/GeekbotLogger.cs deleted file mode 100644 index 83a6d49..0000000 --- a/src/Core/Logger/GeekbotLogger.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Geekbot.Core.Logger -{ - public class GeekbotLogger : IGeekbotLogger - { - private readonly bool _logAsJson; - private readonly NLog.Logger _logger; - private readonly JsonSerializerOptions _serializerSettings; - - public GeekbotLogger(RunParameters runParameters) - { - _logAsJson = !string.IsNullOrEmpty(runParameters.SumologicEndpoint) || runParameters.LogJson; - _logger = LoggerFactory.CreateNLog(runParameters); - _serializerSettings = new JsonSerializerOptions - { - ReferenceHandler = ReferenceHandler.IgnoreCycles, - DefaultIgnoreCondition = JsonIgnoreCondition.Never, - }; - Information(LogSource.Geekbot, "Using GeekbotLogger"); - } - - public void Trace(LogSource source, string message, object extra = null) - => _logger.Trace(CreateLogString("Trace", source, message, null, extra)); - - public void Debug(LogSource source, string message, object extra = null) - => _logger.Debug(CreateLogString("Debug", source, message, null, extra)); - - public void Information(LogSource source, string message, object extra = null) - => _logger.Info(CreateLogString("Information", source, message, null, extra)); - - public void Warning(LogSource source, string message, Exception stackTrace = null, object extra = null) - => _logger.Warn(CreateLogString("Warning", source, message, stackTrace, extra)); - - public void Error(LogSource source, string message, Exception stackTrace, object extra = null) - => _logger.Error(stackTrace, CreateLogString("Error", source, message, stackTrace, extra)); - - public NLog.Logger GetNLogger() => _logger; - - public bool LogAsJson() => _logAsJson; - - private string CreateLogString(string type, LogSource source, string message, Exception exception = null, object extra = null) - { - if (_logAsJson) - { - var logObject = new GeekbotLoggerObject - { - Timestamp = DateTime.Now, - Type = type, - Source = source, - Message = message, - StackTrace = exception != null ? new ExceptionDto(exception) : null, - Extra = extra - }; - return JsonSerializer.Serialize(logObject, _serializerSettings); - } - - if (source != LogSource.Message) return $"[{source}] - {message}"; - - var m = (MessageDto) extra; - return $"[{source}] - [{m?.Guild.Name} - {m?.Channel.Name}] {m?.User.Name}: {m?.Message.Content}"; - } - } -} \ No newline at end of file diff --git a/src/Core/Logger/LogDto.cs b/src/Core/Logger/LogDto.cs deleted file mode 100644 index ee6a3e5..0000000 --- a/src/Core/Logger/LogDto.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Geekbot.Core.Logger; - -public struct GeekbotLoggerObject -{ - public DateTime Timestamp { get; set; } - - public string Type { get; set; } - - public LogSource Source { get; set; } - - public string Message { get; set; } - - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public ExceptionDto? StackTrace { get; set; } - - public object Extra { get; set; } -} diff --git a/src/Core/Media/IMediaProvider.cs b/src/Core/Media/IMediaProvider.cs deleted file mode 100644 index 03e32cf..0000000 --- a/src/Core/Media/IMediaProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Geekbot.Core.Media -{ - public interface IMediaProvider - { - string GetMedia(MediaType type); - } -} \ No newline at end of file diff --git a/src/Core/Media/MediaProvider.cs b/src/Core/Media/MediaProvider.cs deleted file mode 100644 index 4c72ee0..0000000 --- a/src/Core/Media/MediaProvider.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.IO; -using Geekbot.Core.Logger; -using Geekbot.Core.RandomNumberGenerator; - -namespace Geekbot.Core.Media -{ - public class MediaProvider : IMediaProvider - { - private readonly IRandomNumberGenerator _random; - private readonly IGeekbotLogger _logger; - private readonly string[] _pandaImages; - private readonly string[] _croissantImages; - private readonly string[] _squirrelImages; - private readonly string[] _pumpkinImages; - private readonly string[] _turtlesImages; - private readonly string[] _penguinImages; - private readonly string[] _foxImages; - private readonly string[] _dabImages; - - public MediaProvider(IGeekbotLogger logger, IRandomNumberGenerator random) - { - _random = random; - _logger = logger; - - logger.Information(LogSource.Geekbot, "Loading Media Files"); - LoadMedia("./Storage/pandas", ref _pandaImages); - LoadMedia("./Storage/croissant", ref _croissantImages); - LoadMedia("./Storage/squirrel", ref _squirrelImages); - LoadMedia("./Storage/pumpkin", ref _pumpkinImages); - LoadMedia("./Storage/turtles", ref _turtlesImages); - LoadMedia("./Storage/penguins", ref _penguinImages); - LoadMedia("./Storage/foxes", ref _foxImages); - LoadMedia("./Storage/dab", ref _dabImages); - } - - private void LoadMedia(string path, ref string[] storage) - { - var rawLinks = File.ReadAllText(Path.GetFullPath(path)); - storage = rawLinks.Split("\n"); - _logger.Trace(LogSource.Geekbot, $"Loaded {storage.Length} Images from ${path}"); - } - - public string GetMedia(MediaType type) - { - var collection = type switch - { - MediaType.Panda => _pandaImages, - MediaType.Croissant => _croissantImages, - MediaType.Squirrel => _squirrelImages, - MediaType.Pumpkin => _pumpkinImages, - MediaType.Turtle => _turtlesImages, - MediaType.Penguin => _penguinImages, - MediaType.Fox => _foxImages, - MediaType.Dab => _dabImages, - _ => new string[0] - }; - - return collection[_random.Next(0, collection.Length)]; - } - } -} \ No newline at end of file diff --git a/src/Core/Media/MediaType.cs b/src/Core/Media/MediaType.cs deleted file mode 100644 index fa30164..0000000 --- a/src/Core/Media/MediaType.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Geekbot.Core.Media -{ - public enum MediaType - { - Panda, - Croissant, - Squirrel, - Pumpkin, - Turtle, - Penguin, - Fox, - Dab - } -} \ No newline at end of file diff --git a/src/Core/RandomNumberGenerator/RandomNumberGenerator.cs b/src/Core/RandomNumberGenerator/RandomNumberGenerator.cs deleted file mode 100644 index 7460677..0000000 --- a/src/Core/RandomNumberGenerator/RandomNumberGenerator.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; - -namespace Geekbot.Core.RandomNumberGenerator -{ - public class RandomNumberGenerator : IRandomNumberGenerator - { - private readonly System.Security.Cryptography.RandomNumberGenerator rng; - - public RandomNumberGenerator() - { - rng = System.Security.Cryptography.RandomNumberGenerator.Create(); - } - - public int Next(int minValue, int maxInclusiveValue) - { - if (minValue == maxInclusiveValue) - { - return maxInclusiveValue; - } - - if (minValue >= maxInclusiveValue) - { - throw new ArgumentOutOfRangeException("minValue", "must be lower than maxExclusiveValue"); - } - - return GetFromCrypto(minValue, maxInclusiveValue); - } - - private int GetFromCrypto(int minValue, int maxInclusiveValue) - { - var maxExclusiveValue = maxInclusiveValue + 1; - - var diff = (long) maxExclusiveValue - minValue; - var upperBound = uint.MaxValue / diff * diff; - - uint ui; - do - { - ui = GetRandomUInt(); - } while (ui >= upperBound); - - return (int) (minValue + (ui % diff)); - } - - private uint GetRandomUInt() - { - var randomBytes = GenerateRandomBytes(sizeof(uint)); - return BitConverter.ToUInt32(randomBytes, 0); - } - - private byte[] GenerateRandomBytes(int bytesNumber) - { - var buffer = new byte[bytesNumber]; - rng.GetBytes(buffer); - return buffer; - } - } -} \ No newline at end of file diff --git a/src/Core/ReactionListener/IReactionListener.cs b/src/Core/ReactionListener/IReactionListener.cs deleted file mode 100644 index c22d792..0000000 --- a/src/Core/ReactionListener/IReactionListener.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; -using Discord; -using Discord.WebSocket; - -namespace Geekbot.Core.ReactionListener -{ - public interface IReactionListener - { - bool IsListener(ulong id); - Task AddRoleToListener(ulong messageId, ulong guildId, string emoji, IRole role); - void RemoveRole(IMessageChannel channel, SocketReaction reaction); - void GiveRole(IMessageChannel message, SocketReaction reaction); - IEmote ConvertStringToEmote(string emoji); - } -} \ No newline at end of file diff --git a/src/Core/ReactionListener/ReactionListener.cs b/src/Core/ReactionListener/ReactionListener.cs deleted file mode 100644 index dcb0d5b..0000000 --- a/src/Core/ReactionListener/ReactionListener.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Discord; -using Discord.WebSocket; -using Geekbot.Core.Database; -using Geekbot.Core.Database.Models; -using Geekbot.Core.Extensions; -using Geekbot.Core.Logger; -using Sentry; - -namespace Geekbot.Core.ReactionListener -{ - public class ReactionListener : IReactionListener - { - private readonly DatabaseContext _database; - - private readonly IGeekbotLogger _logger; - - // - private Dictionary> _listener; - - public ReactionListener(DatabaseContext database, IGeekbotLogger logger) - { - _database = database; - _logger = logger; - LoadListeners(); - } - - private void LoadListeners() - { - _listener = new Dictionary>(); - foreach (var row in _database.ReactionListeners) - { - var messageId = row.MessageId.AsUlong(); - if (!_listener.ContainsKey(messageId)) - { - _listener.Add(messageId, new Dictionary()); - } - - _listener[messageId].Add(ConvertStringToEmote(row.Reaction), row.RoleId.AsUlong()); - } - } - - public bool IsListener(ulong id) - { - return _listener.ContainsKey(id); - } - - public async Task AddRoleToListener(ulong messageId, ulong guildId, string emoji, IRole role) - { - var emote = ConvertStringToEmote(emoji); - - await _database.ReactionListeners.AddAsync(new ReactionListenerModel() - { - GuildId = guildId.AsLong(), - MessageId = messageId.AsLong(), - RoleId = role.Id.AsLong(), - Reaction = emoji - }); - await _database.SaveChangesAsync(); - - if (!_listener.ContainsKey(messageId)) - { - _listener.Add(messageId, new Dictionary()); - } - _listener[messageId].Add(emote, role.Id); - } - - public async void RemoveRole(IMessageChannel channel, SocketReaction reaction) - { - _listener.TryGetValue(reaction.MessageId, out var registeredReactions); - if (registeredReactions == null) return; - if (!registeredReactions.ContainsKey(reaction.Emote)) return; - var roleId = registeredReactions[reaction.Emote]; - var guild = (SocketGuildChannel) channel; - - try - { - var role = guild.Guild.GetRole(roleId); - await ((IGuildUser) reaction.User.Value).RemoveRoleAsync(role); - } - catch (Exception error) - { - HandleDeletedRole(error, guild, reaction, roleId); - } - } - - public async void GiveRole(IMessageChannel channel, SocketReaction reaction) - { - _listener.TryGetValue(reaction.MessageId, out var registeredReactions); - if (registeredReactions == null) return; - if (!registeredReactions.ContainsKey(reaction.Emote)) return; - var roleId = registeredReactions[reaction.Emote]; - var guild = (SocketGuildChannel) channel; - - try - { - - var role = guild.Guild.GetRole(roleId); - await ((IGuildUser) reaction.User.Value).AddRoleAsync(role); - } - catch (Exception error) - { - HandleDeletedRole(error, guild, reaction, roleId); - } - } - - private void HandleDeletedRole(Exception error, SocketGuildChannel guild, SocketReaction reaction, ulong roleId) - { - _logger.Warning(LogSource.Interaction, "Failed to get or assign role in reaction listener", error); - - if (!SentrySdk.IsEnabled) return; - var sentryEvent = new SentryEvent(error) - { - Message = "Failed to get or assign role in reaction listener" - }; - sentryEvent.SetTag("discord_server", guild.Id.ToString()); - sentryEvent.SetExtra("Message", reaction.MessageId.ToString()); - sentryEvent.SetExtra("User", roleId.ToString()); - - SentrySdk.CaptureEvent(sentryEvent); - } - - public IEmote ConvertStringToEmote(string emoji) - { - if (!emoji.StartsWith('<')) - { - return new Emoji(emoji); - } - return Emote.Parse(emoji); - } - } -} \ No newline at end of file diff --git a/src/Core/RunParameters.cs b/src/Core/RunParameters.cs deleted file mode 100644 index a886a98..0000000 --- a/src/Core/RunParameters.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using CommandLine; - -namespace Geekbot.Core -{ - public class RunParameters - { - /************************************ - * General * - ************************************/ - - [Option("token", HelpText = "Set a new bot token. By default it will use your previous bot token which was stored in the database (default: null) (env: TOKEN)")] - public string Token { get; set; } = ParamFallback("TOKEN"); - - [Option('V', "verbose", HelpText = "Logs everything. (default: false) (env: LOG_VERBOSE)")] - public bool Verbose { get; set; } = ParamFallback("LOG_VERBOSE", false); - - [Option('j', "log-json", HelpText = "Logger outputs json (default: false ) (env: LOG_JSON)")] - public bool LogJson { get; set; } = ParamFallback("LOG_JSON", false); - - [Option('e', "expose-errors", HelpText = "Shows internal errors in the chat (default: false) (env: EXPOSE_ERRORS)")] - public bool ExposeErrors { get; set; } = ParamFallback("EXPOSE_ERRORS", false); - - [Option("disable-gateway", HelpText = "Disables the Discord Gateway (default: false) (env: GATEWAY_DISABLE)")] - public bool DisableGateway { get; set; } = ParamFallback("GATEWAY_DISABLE", false); - - /************************************ - * Database * - ************************************/ - - [Option("in-memory", HelpText = "Uses the in-memory database instead of postgresql (default: false) (env: DB_INMEMORY)")] - public bool InMemory { get; set; } = ParamFallback("DB_INMEMORY", false); - - // Postresql connection - [Option("database", HelpText = "Select a postgresql database (default: geekbot) (env: DB_DATABASE)")] - public string DbDatabase { get; set; } = ParamFallback("DB_DATABASE", "geekbot"); - - [Option("db-host", HelpText = "Set a postgresql host (default: localhost) (env: DB_HOST)")] - public string DbHost { get; set; } = ParamFallback("DB_HOST", "localhost"); - - [Option("db-port", HelpText = "Set a postgresql host (default: 5432) (env: DB_PORT)")] - public string DbPort { get; set; } = ParamFallback("DB_PORT", "5432"); - - [Option("db-user", HelpText = "Set a postgresql user (default: geekbot) (env: DB_USER)")] - public string DbUser { get; set; } = ParamFallback("DB_USER", "geekbot"); - - [Option("db-password", HelpText = "Set a posgresql password (default: empty) (env: DB_PASSWORD)")] - public string DbPassword { get; set; } = ParamFallback("DB_PASSWORD", ""); - - [Option("db-require-ssl", HelpText = "Require SSL to connect to the database (default: false) (env: DB_REQUIRE_SSL)")] - public bool DbSsl { get; set; } = ParamFallback("DB_REQUIRE_SSL", false); - - [Option("db-trust-cert", HelpText = "Trust the database certificate, regardless if it is valid (default: false) (env: DB_TRUST_CERT)")] - public bool DbTrustCert { get; set; } = ParamFallback("DB_TRUST_CERT", false); - - [Option("db-redshift-compat", HelpText = "Enable compatibility for AWS Redshift and DigitalOcean Managed Database (default: false) (env: DB_REDSHIFT_COMPAT)")] - public bool DbRedshiftCompatibility { get; set; } = ParamFallback("DB_REDSHIFT_COMPAT", false); - - // Logging - [Option("db-logging", HelpText = "Enable database logging (default: false) (env: DB_LOGGING)")] - public bool DbLogging { get; set; } = ParamFallback("DB_LOGGING", false); - - /************************************ - * WebApi * - ************************************/ - - [Option('a', "disable-api", HelpText = "Disables the WebApi (default: false) (env: API_DISABLE)")] - public bool DisableApi { get; set; } = ParamFallback("API_DISABLE", false); - - [Option("api-host", HelpText = "Host on which the WebApi listens (default: localhost) (env: API_HOST)")] - public string ApiHost { get; set; } = ParamFallback("API_HOST", "localhost"); - - [Option("api-port", HelpText = "Port on which the WebApi listens (default: 12995) (env: API_PORT)")] - public string ApiPort { get; set; } = ParamFallback("API_PORT", "12995"); - - /************************************ - * Intergrations * - ************************************/ - - [Option("sumologic", HelpText = "Sumologic endpoint for logging (default: null) (env: SUMOLOGIC)")] - public string SumologicEndpoint { get; set; } = ParamFallback("SUMOLOGIC"); - - [Option("sentry", HelpText = "Sentry endpoint for error reporting (default: null) (env: SENTRY)")] - public string SentryEndpoint { get; set; } = ParamFallback("SENTRY"); - - /************************************ - * Helper Functions * - ************************************/ - - private static string ParamFallback(string key, string defaultValue = null) - { - var envVar = GetEnvironmentVariable(key); - return !string.IsNullOrEmpty(envVar) ? envVar : defaultValue; - } - - private static bool ParamFallback(string key, bool defaultValue) - { - var envVar = GetEnvironmentVariable(key); - if (!string.IsNullOrEmpty(envVar)) - { - return envVar.ToLower() switch - { - "true" => true, - "1" => true, - "false" => false, - "0" => false, - _ => defaultValue - }; - } - - return defaultValue; - } - - private static string GetEnvironmentVariable(string name) - { - return Environment.GetEnvironmentVariable($"GEEKBOT_{name}"); - } - } -} \ No newline at end of file diff --git a/src/Core/TransactionModuleBase.cs b/src/Core/TransactionModuleBase.cs deleted file mode 100644 index cbe5206..0000000 --- a/src/Core/TransactionModuleBase.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Discord; -using Discord.Commands; -using Sentry; - -namespace Geekbot.Core -{ - public class TransactionModuleBase : ModuleBase - { - protected ITransaction Transaction; - - protected override void BeforeExecute(CommandInfo command) - { - base.BeforeExecute(command); - - // Transaction Setup - Transaction = SentrySdk.StartTransaction(new Transaction(command.Name, "Exec")); - Transaction.SetTags(new [] - { - new KeyValuePair("Guild", Context.Guild.Name), - }); - Transaction.User = new User() - { - Id = Context.User.Id.ToString(), - Username = Context.User.Username, - }; - Transaction.Status = SpanStatus.Ok; - } - - protected override void AfterExecute(CommandInfo command) - { - base.AfterExecute(command); - Transaction.Finish(); - } - - protected Task ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null) - { - var replySpan = Transaction.StartChild("Reply"); - var msg = base.ReplyAsync(message, isTTS, embed, options, allowedMentions, messageReference); - replySpan.Finish(); - return msg; - } - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageApiUrls.cs b/src/Core/WikipediaClient/Page/PageApiUrls.cs deleted file mode 100644 index 59d3258..0000000 --- a/src/Core/WikipediaClient/Page/PageApiUrls.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageApiUrls - { - [JsonPropertyName("summary")] - public Uri Summary { get; set; } - - [JsonPropertyName("metadata")] - public Uri Metadata { get; set; } - - [JsonPropertyName("references")] - public Uri References { get; set; } - - [JsonPropertyName("media")] - public Uri Media { get; set; } - - [JsonPropertyName("edit_html")] - public Uri EditHtml { get; set; } - - [JsonPropertyName("talk_page_html")] - public Uri TalkPageHtml { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageContentUrlCollection.cs b/src/Core/WikipediaClient/Page/PageContentUrlCollection.cs deleted file mode 100644 index 39bbc0c..0000000 --- a/src/Core/WikipediaClient/Page/PageContentUrlCollection.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageContentUrlCollection - { - [JsonPropertyName("desktop")] - public PageContentUrls Desktop { get; set; } - - [JsonPropertyName("mobile")] - public PageContentUrls Mobile { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageContentUrls.cs b/src/Core/WikipediaClient/Page/PageContentUrls.cs deleted file mode 100644 index 8da17c4..0000000 --- a/src/Core/WikipediaClient/Page/PageContentUrls.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageContentUrls - { - [JsonPropertyName("page")] - public Uri Page { get; set; } - - [JsonPropertyName("revisions")] - public Uri Revisions { get; set; } - - [JsonPropertyName("edit")] - public Uri Edit { get; set; } - - [JsonPropertyName("talk")] - public Uri Talk { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageCoordinates.cs b/src/Core/WikipediaClient/Page/PageCoordinates.cs deleted file mode 100644 index 8537325..0000000 --- a/src/Core/WikipediaClient/Page/PageCoordinates.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageCoordinates - { - [JsonPropertyName("lat")] - public float Lat { get; set; } - - [JsonPropertyName("lon")] - public float Lon { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageImage.cs b/src/Core/WikipediaClient/Page/PageImage.cs deleted file mode 100644 index 0d0429a..0000000 --- a/src/Core/WikipediaClient/Page/PageImage.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageImage - { - [JsonPropertyName("source")] - public Uri Source { get; set; } - - [JsonPropertyName("width")] - public int Width { get; set; } - - [JsonPropertyName("height")] - public int Height { get; set; } - - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageNamespace.cs b/src/Core/WikipediaClient/Page/PageNamespace.cs deleted file mode 100644 index 29eaba8..0000000 --- a/src/Core/WikipediaClient/Page/PageNamespace.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageNamespace - { - [JsonPropertyName("id")] - public ulong Id { get; set; } - - [JsonPropertyName("text")] - public string Text { get; set; } - } -} \ No newline at end of file diff --git a/src/Core/WikipediaClient/Page/PageTitles.cs b/src/Core/WikipediaClient/Page/PageTitles.cs deleted file mode 100644 index f550c38..0000000 --- a/src/Core/WikipediaClient/Page/PageTitles.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Core.WikipediaClient.Page -{ - public class PageTitles - { - [JsonPropertyName("Canonical")] - public string Canonical { get; set; } - - [JsonPropertyName("Normalized")] - public string Normalized { get; set; } - - [JsonPropertyName("Display")] - public string Display { get; set; } - } -} \ No newline at end of file diff --git a/src/Interactions/ApplicationCommand/Command.cs b/src/Interactions/ApplicationCommand/Command.cs deleted file mode 100644 index 37a0ffd..0000000 --- a/src/Interactions/ApplicationCommand/Command.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Geekbot.Interactions.ApplicationCommand -{ - /// - public record Command - { - /// - /// unique id of the command - /// - [JsonPropertyName("id")] - public string? Id { get; set; } - - /// - /// the type of command, defaults 1 if not set - /// - [JsonPropertyName("type")] - public CommandType Type { get; set; } - - /// - /// unique id of the parent application - /// - [JsonPropertyName("application_id")] - public string? ApplicationId { get; set; } - - /// - /// guild id of the command, if not global - /// - [JsonPropertyName("guild_id")] - public string? GuildId { get; set; } - - /// - /// 1-32 character name - /// - /// - /// CHAT_INPUT command names and command option names must match the following regex ^[\w-]{1,32}$ with the unicode flag set. If there is a lowercase variant of any letters used, you must use those. - /// Characters with no lowercase variants and/or uncased letters are still allowed. USER and MESSAGE commands may be mixed case and can include spaces. - /// - [JsonPropertyName("name")] - public string Name { get; set; } - - /// - /// 1-100 character description for CHAT_INPUT commands, empty string for USER and MESSAGE commands - /// - /// - /// Exclusive: CHAT_INPUT - /// - [JsonPropertyName("description")] - public string? Description { get; set; } - - /// - /// the parameters for the command, max 25 - /// - /// - /// Exclusive: CHAT_INPUT - /// - [JsonPropertyName("options")] - public List