Compare commits

..

3 commits

401 changed files with 5619 additions and 11740 deletions

View file

@ -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

18
.gitignore vendored
View file

@ -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/

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<GuildSettingsModel> 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<ISocketMessageChannel> GetModChannel(ulong channelId)
{
try
{
if(channelId == ulong.MinValue) throw new Exception();
var modChannel = (ISocketMessageChannel) _client.GetChannel(channelId);
if(modChannel == null) throw new Exception();
return modChannel;
}
catch
{
await ReplyAsync(
"Modchannel doesn't seem to exist, please set one with `!admin modchannel [channelId]`");
return null;
}
}
}
}

View file

@ -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");
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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<RollsModel> 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<RollsModel> 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;
}
}
}

View file

@ -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<GoogleKgApiResponseDto>(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);
}
}
}
}

View file

@ -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; }
}
}

View file

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

View file

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

View file

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

View file

@ -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; }
}
}

View file

@ -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<string> 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);
}
}
}
}

View file

@ -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("<br />", "")
.Replace("[i]", "*")
.Replace("[/i]", "*");
eb.Title = anime.Title;
eb.Description = description;
eb.ImageUrl = anime.Image;
eb.AddInlineField("Premiered", $"{anime.StartDate}");
eb.AddInlineField("Ended", anime.EndDate == "0000-00-00" ? "???" : anime.EndDate);
eb.AddInlineField("Status", anime.Status);
eb.AddInlineField("Episodes", anime.Episodes);
eb.AddInlineField("MAL Score", anime.Score);
eb.AddInlineField("Type", anime.Type);
eb.AddField("MAL Link", $"https://myanimelist.net/anime/{anime.Id}");
await ReplyAsync("", false, eb.Build());
}
else
{
await ReplyAsync("No anime found with that name...");
}
}
else
{
await ReplyAsync(
"Unfortunally i'm not connected to MyAnimeList.net, please tell my senpai to connect me");
}
}
catch (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("<br />", "")
.Replace("[i]", "*")
.Replace("[/i]", "*");
eb.Title = manga.Title;
eb.Description = description;
eb.ImageUrl = manga.Image;
eb.AddInlineField("Premiered", $"{manga.StartDate}");
eb.AddInlineField("Ended", manga.EndDate == "0000-00-00" ? "???" : manga.EndDate);
eb.AddInlineField("Status", manga.Status);
eb.AddInlineField("Volumes", manga.Volumes);
eb.AddInlineField("Chapters", manga.Chapters);
eb.AddInlineField("MAL Score", manga.Score);
eb.AddField("MAL Link", $"https://myanimelist.net/manga/{manga.Id}");
await ReplyAsync("", false, eb.Build());
}
else
{
await ReplyAsync("No manga found with that name...");
}
}
else
{
await ReplyAsync(
"Unfortunally i'm not connected to MyAnimeList.net, please tell my senpai to connect me");
}
}
catch (XmlException e)
{
await _errorHandler.HandleCommandException(e, Context, "The MyAnimeList.net API refused to answer");
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -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; }
}
}

View file

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

View file

@ -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<UrbanResponseDto>(stringResponse);
if (definitions.List.Count == 0)
{
await ReplyAsync("That word hasn't been defined...");
return;
}
var definition = definitions.List.First(e => !string.IsNullOrWhiteSpace(e.Example));
var eb = new EmbedBuilder();
eb.WithAuthor(new EmbedAuthorBuilder
{
Name = definition.Word,
Url = definition.Permalink
});
eb.WithColor(new Color(239, 255, 0));
if (!string.IsNullOrEmpty(definition.Definition)) eb.Description = definition.Definition;
if (!string.IsNullOrEmpty(definition.Example)) eb.AddField("Example", definition.Example ?? "(no example given...)");
if (!string.IsNullOrEmpty(definition.ThumbsUp)) eb.AddInlineField("Upvotes", definition.ThumbsUp);
if (!string.IsNullOrEmpty(definition.ThumbsDown)) eb.AddInlineField("Downvotes", definition.ThumbsDown);
if (definitions.Tags?.Length > 0) eb.AddField("Tags", string.Join(", ", definitions.Tags));
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -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;

View file

@ -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);
}
}
}
}

View file

@ -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
}
}
}
}
}

View file

@ -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<CatResponseDto>(stringResponse);
var eb = new EmbedBuilder();
eb.ImageUrl = catFile.File;
await ReplyAsync("", false, eb.Build());
}
catch
{
await ReplyAsync("Seems like the dog cought the cat (error occured)");
}
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

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

View file

@ -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<int>();
while (num > 0)
{
listOfInts.Add(num % 10);
num = num / 10;
}
listOfInts.Reverse();
return listOfInts.ToArray();
}
}
}

View file

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

View file

@ -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<ChuckNorrisJokeResponseDto>(stringResponse);
await ReplyAsync(data.Value);
}
catch (HttpRequestException)
{
await ReplyAsync("Api down...");
}
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

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

View file

@ -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<DadJokeResponseDto>(stringResponse);
await ReplyAsync(data.Joke);
}
catch (HttpRequestException)
{
await ReplyAsync("Api down...");
}
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -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<DogResponseDto>(stringResponse);
var eb = new EmbedBuilder();
eb.ImageUrl = dogFile.Url;
await ReplyAsync("", false, eb.Build());
}
catch (HttpRequestException e)
{
await ReplyAsync($"Seems like the dog got lost (error occured)\r\n{e.Message}");
}
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

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

View file

@ -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<string>
{
"It is certain",
"It is decidedly so",
"Without a doubt",
"Yes, definitely",
"You may rely on it",
"As I see it, yes",
"Most likely",
"Outlook good",
"Yes",
"Signs point to yes",
"Reply hazy try again",
"Ask again later",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
"Don't count on it",
"My reply is no",
"My sources say no",
"Outlook not so good",
"Very doubtful"
};
var answer = new Random().Next(replies.Count);
await ReplyAsync(replies[answer]);
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -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;

View file

@ -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);
}
}
}
}

View file

@ -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<KanyeResponseDto>(stringResponse);
await ReplyAsync(data.Quote);
}
catch (HttpRequestException)
{
await ReplyAsync("Api down...");
}
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -0,0 +1,8 @@
namespace Geekbot.net.Commands.Randomness.Kanye
{
public class KanyeResponseDto
{
public string Id { get; set; }
public string Quote { get; set; }
}
}

View file

@ -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)

View file

@ -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;

View file

@ -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()));
}
}
}
}

View file

@ -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<CookiesModel> GetUser(ulong userId)
{
var user = _database.Cookies.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId);
@ -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();

View file

@ -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;

View file

@ -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<KarmaModel> 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<KarmaModel> 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;
}
}
}

View file

@ -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<HighscoreTypes>(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<HighscoreUserDto, int> 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);
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -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)

View file

@ -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<List<CommitDto>>(stringResponse);
var eb = new EmbedBuilder();
eb.WithColor(new Color(143, 165, 102));
eb.WithAuthor(new EmbedAuthorBuilder
{
IconUrl = _client.CurrentUser.GetAvatarUrl(),
Name = "Latest Updates",
Url = "https://geekbot.pizzaandcoffee.rocks/updates"
});
var sb = new StringBuilder();
foreach (var commit in commits.Take(10))
sb.AppendLine($"- {commit.Commit.Message} ({commit.Commit.Author.Date:yyyy-MM-dd})");
eb.Description = sb.ToString();
eb.WithFooter(new EmbedFooterBuilder
{
Text = $"List generated from github commits on {DateTime.Now:yyyy-MM-dd}"
});
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

@ -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; }
}
}

View file

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

View file

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

View file

@ -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);
}
}
}
}

View file

@ -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<DiceTypeDto>();
var mod = 0;
foreach (var i in splitedDices)
{
var dice = ToDice(i);
if (dice.Sides != 0 && dice.Times != 0)
{
dices.Add(dice);
}
else if (dice.Mod != 0)
{
if (mod != 0)
{
await ReplyAsync("You can only have one mod");
return;
}
mod = dice.Mod;
}
}
if (!dices.Any())
{
await ReplyAsync(
"That is not a valid dice, examples are: 1d20, 1d6, 2d6, 1d6+2, 1d6+2d8+1d20+6, etc...");
return;
}
if (dices.Any(d => d.Times > 20))
{
await ReplyAsync("You can't throw more than 20 dices");
return;
}
if (dices.Any(d => d.Sides > 144))
{
await ReplyAsync("A dice can't have more than 144 sides");
return;
}
var rep = new StringBuilder();
rep.AppendLine($":game_die: {Context.User.Mention}");
rep.Append("**Result:** ");
var resultStrings = new List<string>();
var total = 0;
var extraText = "";
foreach (var dice in dices)
{
var results = new List<int>();
for (var i = 0; i < dice.Times; i++)
{
var roll = _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();
}
}
}

View file

@ -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; }
}
}

View file

@ -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)
{

View file

@ -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("✅"));
}

View file

@ -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;

View file

@ -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.")]

View file

@ -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<IMessage> 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
};
}
}
}

View file

@ -1,6 +1,6 @@
using System;
namespace Geekbot.Bot.Commands.Utils.Quote
namespace Geekbot.net.Commands.Utils.Quote
{
internal class QuoteObjectDto
{

View file

@ -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<QuoteModel> Quotes { get; set; }
public DbSet<UserModel> Users { get; set; }
public DbSet<GuildsModel> Guilds { get; set; }
public DbSet<GuildSettingsModel> GuildSettings { get; set; }
public DbSet<KarmaModel> Karma { get; set; }
public DbSet<ShipsModel> Ships { get; set; }
public DbSet<RollsModel> Rolls { get; set; }
public DbSet<MessageSeasonsModel> MessagesSeasons { get; set; }
public DbSet<MessagesModel> Messages { get; set; }
public DbSet<SlapsModel> Slaps { get; set; }
public DbSet<GlobalsModel> Globals { get; set; }
public DbSet<RoleSelfServiceModel> RoleSelfService { get; set; }
public DbSet<PollModel> Polls { get; set; }
public DbSet<CookiesModel> Cookies { get; set; }
public DbSet<ReactionListenerModel> ReactionListeners { get; set; }
}
}

View file

@ -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
});
}
}

View file

@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore;
namespace Geekbot.Core.Database
namespace Geekbot.net.Database
{
public class InMemoryDatabase : DatabaseContext
{

View file

@ -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));
}
}
}
}

View file

@ -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
{

View file

@ -1,7 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class CookiesModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class GlobalsModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class GuildSettingsModel
{

View file

@ -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; }
}
}

View file

@ -1,7 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class KarmaModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class MessagesModel
{

View file

@ -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<PollQuestionModel> Options { get; set; }
public bool IsFinshed { get; set; }
}
}

View file

@ -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; }
}
}

View file

@ -1,7 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class QuoteModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class RoleSelfServiceModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class RollsModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class ShipsModel
{

View file

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Geekbot.Core.Database.Models
namespace Geekbot.net.Database.Models
{
public class SlapsModel
{

View file

@ -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<UserUsedNamesModel> UsedNames { get; set; }
}
}

View file

@ -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; }
}
}

View file

@ -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};";
}
}
}

View file

@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore;
namespace Geekbot.Core.Database
namespace Geekbot.net.Database
{
public class SqlDatabase : DatabaseContext
{

104
Geekbot.net/Geekbot.net.csproj Executable file
View file

@ -0,0 +1,104 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<ApplicationIcon>derp.ico</ApplicationIcon>
<Version>4.1.0</Version>
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
<Version Condition=" '$(VersionSuffix)' != '' ">$(Version)-$(VersionSuffix)</Version>
<Version Condition=" '$(VersionSuffix)' == '' ">$(Version)-DEV</Version>
<Company>Pizza and Coffee Studios</Company>
<Authors>Pizza and Coffee Studios</Authors>
<Description>A Discord bot</Description>
<RepositoryUrl>https://github.com/pizzaandcoffee/Geekbot.net</RepositoryUrl>
<NoWarn>NU1701</NoWarn>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://geekbot.pizzaandcoffee.rocks</PackageProjectUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.4.3" />
<PackageReference Include="Discord.Net">
<Version>2.1.0</Version>
</PackageReference>
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.38.0.1488" />
<PackageReference Include="HtmlAgilityPack" Version="1.9.1" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Cors" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="MtgApiManager.Lib" Version="1.2.1" />
<PackageReference Include="MyAnimeListSharp" Version="1.3.4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NLog" Version="4.5.11" />
<PackageReference Include="NLog.Config" Version="4.5.11" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="2.0.0-preview1" />
<PackageReference Include="PokeApi.NET" Version="1.1.1" />
<PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="SumoLogic.Logging.NLog" Version="1.0.1.1" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Json">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime.Serialization.Primitives">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="Utf8Json" Version="1.3.7" />
<PackageReference Include="YamlDotNet" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="Storage\checkEmPics">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\croissant">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\fortunes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pandas">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pumpkin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\squirrel">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\turtles">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pinguins">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\foxes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Lib\Converters\MtgManaEmojis.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Storage\dab">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Lib\Localization\Translations.yml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WikipediaApi\WikipediaApi.csproj" />
</ItemGroup>
</Project>

268
Geekbot.net/Handlers.cs Normal file
View file

@ -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<Task<RestUserMessage>>(() => 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<IMessage, ulong> 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<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.GiveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
public Task ReactionRemoved(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return Task.CompletedTask;
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
_reactionListener.RemoveRole(socketMessageChannel, reaction);
return Task.CompletedTask;
}
}
}

View file

@ -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<RedisKey> GetAllKeys()
{
return Connection.GetServer($"{_runParameters.RedisHost}:{_runParameters.RedisPort}").Keys(int.Parse(_runParameters.RedisDatabase));
}
}
}

View file

@ -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<RedisKey> GetAllKeys();
}
}

View file

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

View file

@ -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<AnimeEntry> GetAnime(string query)
{
var response = await _animeSearch.SearchDeserializedAsync(query);
return response.Entries.Count == 0 ? null : response.Entries[0];
}
public async Task<MangaEntry> GetManga(string query)
{
var response = await _mangaSearch.SearchDeserializedAsync(query);
return response.Entries.Count == 0 ? null : response.Entries[0];
}
}
}

View file

@ -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<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)

View file

@ -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<AssemblyInformationalVersionAttribute>().InformationalVersion;
return typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
}
public static string LibraryVersion()

View file

@ -0,0 +1,56 @@
using Discord;
using Geekbot.net.Lib.Localization;
namespace Geekbot.net.Lib.Context
{
/// <summary> The context of a command which may contain the client, user, guild, channel, and message. </summary>
public class GeekbotContext : IGeekbotContext
{
/// <inheritdoc />
public IDiscordClient Client { get; }
/// <inheritdoc />
public IGuild Guild { get; }
/// <inheritdoc />
public IMessageChannel Channel { get; }
/// <inheritdoc />
public IUser User { get; }
/// <inheritdoc />
public IUserMessage Message { get; }
/// <inheritdoc />
public IGuildUser GuildUser { get; }
/// <inheritdoc />
public TranslationGuildContext Translations { get; }
/// <summary> Indicates whether the channel that the command is executed in is a private channel. </summary>
public bool IsPrivate
{
get
{
return this.Channel is IPrivateChannel;
}
}
/// <summary>
/// Initializes a new <see cref="T:Discord.Commands.CommandContext" /> class with the provided client and message.
/// </summary>
/// <param name="client">The underlying client.</param>
/// <param name="msg">The underlying message.</param>
/// <param name="translationHandler">the translation handler</param>
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;
}
}
}

View file

@ -0,0 +1,19 @@
using Discord;
using Discord.Commands;
using Geekbot.net.Lib.Localization;
namespace Geekbot.net.Lib.Context
{
public interface IGeekbotContext : ICommandContext
{
/// <summary>
/// Gets the <see cref="T:Discord:IGuildUser"/> who executed the command.
/// </summary>
IGuildUser GuildUser { get; }
/// <summary>
/// Gets the <see cref="T:Geekbot:net:Lib:Localization:TranslationGuildContext"/> containing the necessary tools for command localization.
/// </summary>
TranslationGuildContext Translations { get; }
}
}

View file

@ -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();
}
}
}

View file

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

View file

@ -1,4 +1,4 @@
namespace Geekbot.Core.Converters
namespace Geekbot.net.Lib.Converters
{
public interface IMtgManaConverter
{

View file

@ -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<string, string> _manaDict;
public MtgManaConverter()
{
// these emotes can be found at https://discord.gg/bz8HyA7
var mtgEmojis = File.ReadAllText(Path.GetFullPath("./Lib/Converters/MtgManaEmojis.json"));
_manaDict = JsonSerializer.Deserialize<Dictionary<string, string>>(mtgEmojis);
}
public string ConvertMana(string mana)
{
var rgx = Regex.Matches(mana, @"(\{(.*?)\})");
foreach (Match manaTypes in rgx)
{
var m = _manaDict.GetValueOrDefault(manaTypes.Value);
if (!string.IsNullOrEmpty(m))
{
mana = mana.Replace(manaTypes.Value, m);
}
}
return mana;
}
}
}

View file

@ -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>"
}

View file

@ -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<string> _getDefaultErrorText;
private readonly ITranslationHandler _translation;
private readonly IRavenClient _raven;
private readonly bool _errorsInChat;
public ErrorHandler(IGeekbotLogger logger, RunParameters runParameters, Func<string> 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);
}
}
}

View file

@ -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);
}
}

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