Compare commits

..

1 commit

Author SHA1 Message Date
70d92706e2
Add (broken) Context handler 2018-01-21 23:42:27 +01:00
360 changed files with 5393 additions and 14204 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

16
.gitignore vendored
View file

@ -1,10 +1,12 @@
/*/**/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.sln.DotSettings.user
app
Geekbot.net/Logs/*
!/Geekbot.net/Logs/.keep

View file

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

View file

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

View file

@ -3,19 +3,9 @@ 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}"
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}") = "Tests", "Tests\Tests.csproj", "{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -23,34 +13,14 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDCB3D92-E7B5-47BB-A9B5-CFAEFA57CDB4}.Release|Any CPU.Build.0 = Release|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CAF5F02-EFFE-4FDA-BD44-EEADDBA9600E}.Release|Any CPU.Build.0 = Release|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47671723-52A9-4668-BBC5-2BA76AE3B288}.Release|Any CPU.Build.0 = Release|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A63D5DC-6325-4F53-8ED2-9843239B76CC}.Release|Any CPU.Build.0 = Release|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBF79896-9F7F-443D-B336-155E276DFF16}.Release|Any CPU.Build.0 = Release|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C771DFE-912A-4276-B0A6-047E09603F1E}.Release|Any CPU.Build.0 = Release|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF6859D9-C539-4910-BE1E-9ECFED2F46FA}.Release|Any CPU.Build.0 = Release|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A691B018-4B19-4A7A-A0F6-DBB17641254F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

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

View file

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

View file

@ -2,12 +2,11 @@
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.net.Lib;
namespace Geekbot.Bot.Commands.Utils
namespace Geekbot.net.Commands
{
public class AvatarGetter : TransactionModuleBase
public class AvatarGetter : ModuleBase
{
private readonly IErrorHandler _errorHandler;
@ -17,18 +16,19 @@ namespace Geekbot.Bot.Commands.Utils
}
[Command("avatar", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get someones avatar")]
public async Task GetAvatar([Remainder, Summary("@someone")] IUser user = null)
public async Task getAvatar([Remainder] [Summary("user")] IUser user = null)
{
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)
{
await _errorHandler.HandleCommandException(e, Context);
_errorHandler.HandleCommandException(e, Context);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
namespace Geekbot.net.Commands
{
public class ContextHandler : IContextHandler
{
private Dictionary<ulong, ContextStore> _store;
private readonly IDiscordClient _client;
private readonly CommandService _commandService;
private readonly IServiceProvider _servicesProvider;
public ContextHandler(IDiscordClient client, CommandService commandService, IServiceProvider servicesProvider)
{
_store = new Dictionary<ulong, ContextStore>();
_commandService = commandService;
_servicesProvider = servicesProvider;
_client = client;
}
public ContextReference HasContext(IUserMessage message)
{
if (_store.ContainsKey(message.Author.Id)) return ContextReference.User;
if (_store.ContainsKey(message.Channel.Id)) return ContextReference.Channel;
return ContextReference.None;
}
public void SaveContext(ContextReference type, ICommandContext context, string commandName)
{
var contextStore = new ContextStore()
{
CommandName = commandName
};
var id = GetId(type, context.Message);
_store.Add(id, contextStore);
}
public async void ExecuteOnContext(ContextReference type, IUserMessage message)
{
var id = GetId(type, message);
var obj = _store[id];
var context = new CommandContext(_client, message);
var rest = await _commandService.ExecuteAsync(context, $"{obj.CommandSearch} {context.Message?.Content}", _servicesProvider);
if (!rest.IsSuccess)
{
await context.Channel.SendMessageAsync(rest.ErrorReason);
}
}
public void ClearContext(ulong id)
{
_store.Remove(id);
}
private ulong GetId(ContextReference type, IUserMessage message)
{
if (type == ContextReference.Channel)
{
return message.Author.Id;
}
if (type == ContextReference.User)
{
return message.Author.Id;
}
throw new Exception("No Context Object Found");
}
private class ContextStore
{
public string CommandName { get; set; }
public string CommandSearch => $"{CommandName} ctx";
}
}
public enum ContextReference
{
User,
Channel,
None
}
public interface IContextHandler
{
ContextReference HasContext(IUserMessage message);
void SaveContext(ContextReference type, ICommandContext context, string commandName);
void ExecuteOnContext(ContextReference type, IUserMessage message);
void ClearContext(ulong id);
}
}

View file

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

View file

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

View file

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

View file

@ -1,26 +1,29 @@
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;
namespace Geekbot.Bot.Commands.Utils
namespace Geekbot.net.Commands
{
public class Emojify : TransactionModuleBase
public class Emojify : ModuleBase
{
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)]
[Remarks(CommandCategories.Helpers)]
[Summary("Emojify text")]
public async Task Dflt([Remainder] [Summary("text")] string text)
{
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,18 +32,10 @@ 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)
{
await _errorHandler.HandleCommandException(e, Context);
_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
{
public class Fortune : TransactionModuleBase
public class Fortune : ModuleBase
{
private readonly IFortunesProvider _fortunes;
@ -15,6 +15,7 @@ namespace Geekbot.Bot.Commands.Randomness
}
[Command("fortune", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Randomness)]
[Summary("Get a random fortune")]
public async Task GetAFortune()
{

View file

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

View file

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Net;
using Geekbot.net.Lib;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
public class Google : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Google(IErrorHandler errorHandler, IDatabase redis)
{
_errorHandler = errorHandler;
_redis = redis;
}
[Command("google", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Google Something.")]
public async Task askGoogle([Remainder, Summary("SearchText")] string searchText)
{
try
{
using (var client = new WebClient())
{
var apiKey = _redis.StringGet("googleGraphKey");
if (!apiKey.HasValue)
{
await ReplyAsync("No Google API key has been set, please contact my owner");
return;
}
var url = new Uri($"https://kgsearch.googleapis.com/v1/entities:search?languages=en&limit=1&query={searchText}&key={apiKey}");
var responseString = client.DownloadString(url);
var response = Utf8Json.JsonSerializer.Deserialize<GoogleKGApiResponse>(responseString);
if (!response.itemListElement.Any())
{
await ReplyAsync("No results were found...");
return;
}
var data = response.itemListElement.First().result;
var eb = new EmbedBuilder();
eb.Title = data.name;
if(!string.IsNullOrEmpty(data.description)) eb.WithDescription(data.description);
if(!string.IsNullOrEmpty(data.detailedDescription?.url)) eb.WithUrl(data.detailedDescription.url);
if(!string.IsNullOrEmpty(data.detailedDescription?.articleBody)) eb.AddField("Details", data.detailedDescription.articleBody);
if(!string.IsNullOrEmpty(data.image?.contentUrl)) eb.WithThumbnailUrl(data.image.contentUrl);
await ReplyAsync("", false, eb.Build());
}
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
public class GoogleKGApiResponse
{
public List<GoogleKGApiElement> itemListElement { get; set; }
public class GoogleKGApiElement
{
public GoogleKGApiResult result { get; set; }
public double resultScore { get; set; }
}
public class GoogleKGApiResult
{
public string name { get; set; }
public string description { get; set; }
public GoogleKGApiImage image { get; set; }
public GoogleKGApiDetailed detailedDescription { get; set; }
}
public class GoogleKGApiImage
{
public string contentUrl { get; set; }
public string url { get; set; }
}
public class GoogleKGApiDetailed
{
public string articleBody { get; set; }
public string url { get; set; }
public string license { get; set; }
}
}
}
}

View file

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

View file

@ -1,14 +1,12 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
using Geekbot.net.Lib;
namespace Geekbot.Bot.Commands.Utils
namespace Geekbot.net.Commands
{
public class Help : TransactionModuleBase
public class Help : ModuleBase
{
private readonly IErrorHandler _errorHandler;
@ -18,6 +16,7 @@ namespace Geekbot.Bot.Commands.Utils
}
[Command("help", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("List all Commands")]
public async Task GetHelp()
{
@ -27,13 +26,12 @@ 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("✅"));
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
_errorHandler.HandleCommandException(e, Context);
}
}
}

View file

@ -5,26 +5,28 @@ 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 StackExchange.Redis;
namespace Geekbot.Bot.Commands.Utils
namespace Geekbot.net.Commands
{
public class Info : TransactionModuleBase
public class Info : ModuleBase
{
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Info(IErrorHandler errorHandler, DiscordSocketClient client, CommandService commands)
public Info(IDatabase redis, IErrorHandler errorHandler, DiscordSocketClient client, CommandService commands)
{
_redis = redis;
_errorHandler = errorHandler;
_client = client;
_commands = commands;
}
[Command("info", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get Information about the bot")]
public async Task BotInfo()
{
@ -32,30 +34,31 @@ namespace Geekbot.Bot.Commands.Utils
{
var eb = new EmbedBuilder();
var appInfo = await _client.GetApplicationInfoAsync();
eb.WithAuthor(new EmbedAuthorBuilder()
.WithIconUrl(appInfo.IconUrl)
.WithName($"{Constants.Name} V{Constants.BotVersion()}"));
.WithIconUrl(_client.CurrentUser.GetAvatarUrl())
.WithName($"{Constants.Name} V{Constants.BotVersion}"));
var botOwner = await Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner")));
var uptime = DateTime.Now.Subtract(Process.GetCurrentProcess().StartTime);
eb.AddInlineField("Bot Name", _client.CurrentUser.Username);
eb.AddInlineField("Bot Owner", $"{appInfo.Owner.Username}#{appInfo.Owner.Discriminator}");
eb.AddInlineField("Library", $"Discord.NET {Constants.LibraryVersion()}");
eb.AddInlineField("Bot Owner", $"{botOwner.Username}#{botOwner.Discriminator}");
eb.AddInlineField("Library", "Discord.NET V1.0.2");
eb.AddInlineField("Uptime", $"{uptime.Days}D {uptime.Hours}H {uptime.Minutes}M {uptime.Seconds}S");
eb.AddInlineField("Servers", Context.Client.GetGuildsAsync().Result.Count);
eb.AddInlineField("Total Commands", _commands.Commands.Count());
eb.AddField("Website", "https://geekbot.pizzaandcoffee.rocks/");
await ReplyAsync("", false, eb.Build());
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
_errorHandler.HandleCommandException(e, Context);
}
}
[Command("uptime", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get the Bot Uptime")]
public async Task BotUptime()
{
@ -66,7 +69,7 @@ namespace Geekbot.Bot.Commands.Utils
}
catch (Exception e)
{
await _errorHandler.HandleCommandException(e, Context);
_errorHandler.HandleCommandException(e, Context);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,117 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Geekbot.net.Lib;
using Serilog;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[Group("owner")]
[RequireUserPermission(GuildPermission.Administrator)]
public class Owner : ModuleBase
{
private readonly DiscordSocketClient _client;
private readonly IErrorHandler _errorHandler;
private readonly IGeekbotLogger _logger;
private readonly IDatabase _redis;
private readonly IUserRepository _userRepository;
public Owner(IDatabase redis, DiscordSocketClient client, IGeekbotLogger logger, IUserRepository userRepositry,
IErrorHandler errorHandler)
{
_redis = redis;
_client = client;
_logger = logger;
_userRepository = userRepositry;
_errorHandler = errorHandler;
}
[Command("youtubekey", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Set the youtube api key")]
public async Task SetYoutubeKey([Summary("API Key")] string key)
{
var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result;
if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString()))
{
await ReplyAsync(
$"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})");
return;
}
_redis.StringSet("youtubeKey", key);
await ReplyAsync("Apikey has been set");
}
[Command("game", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Set the game that the bot is playing")]
public async Task SetGame([Remainder] [Summary("Game")] string key)
{
var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result;
if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString()))
{
await ReplyAsync(
$"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})");
return;
}
_redis.StringSet("Game", key);
await _client.SetGameAsync(key);
_logger.Information("Geekbot", $"Changed game to {key}");
await ReplyAsync($"Now Playing {key}");
}
[Command("popuserrepo", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Populate user cache")]
public async Task popUserRepoCommand()
{
try
{
var botOwner = Context.Guild.GetUserAsync(ulong.Parse(_redis.StringGet("botOwner"))).Result;
if (!Context.User.Id.ToString().Equals(botOwner.Id.ToString()))
{
await ReplyAsync(
$"Sorry, only the botowner can do this ({botOwner.Username}#{botOwner.Discriminator})");
return;
}
}
catch (Exception)
{
await ReplyAsync(
$"Sorry, only the botowner can do this");
return;
}
var success = 0;
var failed = 0;
try
{
_logger.Warning("UserRepository", "Populating User Repositry");
await ReplyAsync("Starting Population of User Repository");
foreach (var guild in _client.Guilds)
{
_logger.Information("UserRepository", $"Populating users from {guild.Name}");
foreach (var user in guild.Users)
{
var succeded = await _userRepository.Update(user);
var inc = succeded ? success++ : failed++;
}
}
_logger.Warning("UserRepository", "Finished Updating User Repositry");
await ReplyAsync(
$"Successfully Populated User Repository with {success} Users in {_client.Guilds.Count} Guilds (Failed: {failed})");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context,
"Couldn't complete User Repository, see console for more info");
}
}
}
}

View file

@ -1,13 +1,14 @@
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.net.Lib;
namespace Geekbot.Bot.Commands.Utils
namespace Geekbot.net.Commands
{
public class Ping : TransactionModuleBase
public class Ping : ModuleBase
{
[Command("👀", RunMode = RunMode.Async)]
[Summary("Look at the bot.")]
[Remarks(CommandCategories.Fun)]
public async Task Eyes()
{
await ReplyAsync("S... Stop looking at me... baka!");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,146 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Net;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
[Group("role")]
public class Role : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IDatabase _redis;
public Role(IErrorHandler errorHandler, IDatabase redis)
{
_errorHandler = errorHandler;
_redis = redis;
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get a list of all available roles.")]
public async Task getAllRoles()
{
try
{
var roles = _redis.HashGetAll($"{Context.Guild.Id}:RoleWhitelist");
if (roles.Length == 0)
{
await ReplyAsync("There are no roles configured for this server");
return;
}
var sb = new StringBuilder();
sb.AppendLine($"**Self Service Roles on {Context.Guild.Name}**");
sb.AppendLine("To get a role, use `!role name`");
foreach (var role in roles) sb.AppendLine($"- {role.Name}");
await ReplyAsync(sb.ToString());
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[Command(RunMode = RunMode.Async)]
[Remarks(CommandCategories.Helpers)]
[Summary("Get a role by mentioning it.")]
public async Task giveRole([Summary("roleNickname")] string roleNameRaw)
{
try
{
var roleName = roleNameRaw.ToLower();
if (_redis.HashExists($"{Context.Guild.Id}:RoleWhitelist", roleName))
{
var guildUser = (IGuildUser) Context.User;
var roleId = ulong.Parse(_redis.HashGet($"{Context.Guild.Id}:RoleWhitelist", roleName));
var role = Context.Guild.Roles.First(r => r.Id == roleId);
if (role == null)
{
await ReplyAsync("That role doesn't seem to exist");
return;
}
if (guildUser.RoleIds.Contains(roleId))
{
await guildUser.RemoveRoleAsync(role);
await ReplyAsync($"Removed you from {role.Name}");
return;
}
await guildUser.AddRoleAsync(role);
await ReplyAsync($"Added you to {role.Name}");
return;
}
await ReplyAsync("That role doesn't seem to exist");
}
catch (HttpException e)
{
_errorHandler.HandleHttpException(e, Context);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.Administrator)]
[Command("add", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Add a role to the whitelist.")]
public async Task addRole([Summary("@role")] IRole role, [Summary("alias")] string roleName)
{
try
{
if (role.IsManaged)
{
await ReplyAsync("You can't add a role that is managed by discord");
return;
}
if (role.Permissions.ManageRoles
|| role.Permissions.Administrator
|| role.Permissions.ManageGuild
|| role.Permissions.BanMembers
|| role.Permissions.KickMembers)
{
await ReplyAsync(
"Woah, i don't think you want to add that role to self service as it contains some dangerous permissions");
return;
}
_redis.HashSet($"{Context.Guild.Id}:RoleWhitelist",
new[] {new HashEntry(roleName.ToLower(), role.Id.ToString())});
await ReplyAsync($"Added {role.Name} to the whitelist");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.Administrator)]
[Command("remove", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Remove a role from the whitelist.")]
public async Task removeRole([Summary("roleNickname")] string roleName)
{
try
{
_redis.HashDelete($"{Context.Guild.Id}:RoleWhitelist", roleName);
await ReplyAsync($"Removed {roleName} from the whitelist");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

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

View file

@ -0,0 +1,66 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
namespace Geekbot.net.Commands
{
[Group("say")]
public class Say : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly IContextHandler _contextHandler;
public Say(IErrorHandler errorHandler, IContextHandler contextHandler)
{
_errorHandler = errorHandler;
_contextHandler = contextHandler;
}
[RequireUserPermission(GuildPermission.Administrator)]
[Command("record", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Say Something.")]
public async Task recordEcho()
{
try
{
_contextHandler.SaveContext(ContextReference.User, Context, "say");
await ReplyAsync("Recording...");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
[RequireUserPermission(GuildPermission.Administrator)]
[Command("ctx", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Say Something.")]
public async Task recordEcho([Remainder] [Summary("What?")] string echo)
{
Console.WriteLine("actually got here...");
_contextHandler.ClearContext(Context.User.Id);
await Context.Channel.SendMessageAsync(echo);
}
[RequireUserPermission(GuildPermission.Administrator)]
[Command("ss", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Admin)]
[Summary("Say Something.")]
public async Task Echo([Remainder] [Summary("What?")] string echo)
{
try
{
await Context.Message.DeleteAsync();
await ReplyAsync(echo);
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

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

View file

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.net.Lib;
using StackExchange.Redis;
namespace Geekbot.net.Commands
{
public class Slap : ModuleBase
{
private readonly IErrorHandler _errorHandler;
private readonly Random _random;
private readonly IDatabase _redis;
public Slap(IErrorHandler errorHandler, Random random, IDatabase redis)
{
_errorHandler = errorHandler;
_random = random;
_redis = redis;
}
[Command("slap", RunMode = RunMode.Async)]
[Remarks(CommandCategories.Fun)]
[Summary("slap someone")]
public async Task Slapper([Summary("@user")] IUser user)
{
try
{
if (user.Id == Context.User.Id)
{
await ReplyAsync("Why would you slap yourself?");
return;
}
var things = new List<string>()
{
"thing",
"rubber chicken",
"leek stick",
"large trout",
"flat hand",
"strip of bacon",
"feather",
"piece of pizza",
"moldy banana",
"sharp retort",
"printed version of wikipedia",
"panda paw",
"spiked sledgehammer",
"monstertruck",
"dirty toilet brush",
"sleeping seagull",
"sunflower",
"mousepad",
"lolipop",
"bottle of rum"
};
_redis.HashIncrement($"{Context.Guild.Id}:SlapsRecieved", user.Id.ToString());
_redis.HashIncrement($"{Context.Guild.Id}:SlapsGiven", Context.User.Id.ToString());
await ReplyAsync($"{Context.User.Username} slapped {user.Username} with a {things[_random.Next(things.Count - 1)]}");
}
catch (Exception e)
{
_errorHandler.HandleCommandException(e, Context);
}
}
}
}

View file

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

View file

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

View file

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

View file

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

119
Geekbot.net/Commands/mal.cs Normal file
View file

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

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

@ -0,0 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<ApplicationIcon>derp.ico</ApplicationIcon>
<Version>1.1.0</Version>
<Company>Pizza and Coffee Studios</Company>
<Authors>Pizza and Coffee Studios</Authors>
<Description>A Discord bot</Description>
<RepositoryUrl>https://github.com/pizzaandcoffee/Geekbot.net</RepositoryUrl>
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net">
<Version>1.0.2</Version>
</PackageReference>
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.29.1.991" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
<PackageReference Include="MtgApiManager.Lib" Version="1.0.0.2" />
<PackageReference Include="MyAnimeListSharp" Version="1.3.4" />
<PackageReference Include="Nancy" Version="2.0.0-clinteastwood" />
<PackageReference Include="Nancy.Hosting.Self" Version="2.0.0-clinteastwood" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Overwatch.Net" Version="3.0.0" />
<PackageReference Include="PokeApi.NET" Version="1.1.0" />
<PackageReference Include="Serilog" Version="2.6.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1-dev-00757" />
<PackageReference Include="Serilog.Sinks.Literate" Version="3.0.1-dev-00044" />
<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.1-dev-00771" />
<PackageReference Include="Serilog.Sinks.SumoLogic" Version="2.3.0" />
<PackageReference Include="SharpRaven" Version="2.2.0" />
<PackageReference Include="StackExchange.Redis">
<Version>1.2.6</Version>
</PackageReference>
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Json">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime.Serialization.Primitives">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="Utf8Json" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
<None Update="Storage\checkEmPics">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\croissant">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\fortunes">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pandas">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\pumpkin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\squirrel">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\Translations.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Storage\turtles">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

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

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
namespace Geekbot.net.Lib
{
public class Constants
{
public const string Name = "Geekbot";
public const double BotVersion = 3.5;
public const double ApiVersion = 1;
}
}

View file

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

View file

@ -0,0 +1,92 @@
using System;
using System.Net;
using System.Runtime.InteropServices.ComTypes;
using System.Security.Principal;
using Discord.Commands;
using Discord.Net;
using Nancy.Extensions;
using Serilog;
using SharpRaven;
using SharpRaven.Data;
using SharpRaven.Utilities;
using Utf8Json;
namespace Geekbot.net.Lib
{
public class ErrorHandler : IErrorHandler
{
private readonly IGeekbotLogger _logger;
private readonly ITranslationHandler _translation;
private readonly IRavenClient _raven;
public ErrorHandler(IGeekbotLogger logger, ITranslationHandler translation)
{
_logger = logger;
_translation = translation;
var sentryDsn = Environment.GetEnvironmentVariable("SENTRY");
if (!string.IsNullOrEmpty(sentryDsn))
{
_raven = new RavenClient(sentryDsn);
_logger.Information("Geekbot", $"Command Errors will be logged to Sentry: {sentryDsn}");
}
else
{
_raven = null;
}
}
public void HandleCommandException(Exception e, ICommandContext Context, string errorMessage = "def")
{
try
{
var errorString = errorMessage == "def" ? _translation.GetString(Context.Guild.Id, "errorHandler", "SomethingWentWrong") : errorMessage;
var errorObj = SimpleConextConverter.ConvertContext(Context);
_logger.Error("Geekbot", "An error ocured", e, errorObj);
if (!string.IsNullOrEmpty(errorMessage))
{
Context.Channel.SendMessageAsync(errorString);
}
if (e.Message.Contains("50013")) return;
if (e.Message.Contains("50007")) 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
};
_raven.Capture(sentryEvent);
}
catch (Exception ex)
{
_logger.Error("Geekbot", "Errorception", ex);
}
}
public async void HandleHttpException(HttpException e, ICommandContext Context)
{
var errorStrings = _translation.GetDict(Context, "httpErrors");
switch(e.HttpCode)
{
case HttpStatusCode.Forbidden:
await Context.Channel.SendMessageAsync(errorStrings["403"]);
break;
}
}
}
public interface IErrorHandler
{
void HandleCommandException(Exception e, ICommandContext Context, string errorMessage = "def");
void HandleHttpException(HttpException e, ICommandContext Context);
}
}

View file

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

View file

@ -2,11 +2,11 @@
using System.Collections.Generic;
using System.Linq;
namespace Geekbot.Core.Levels
namespace Geekbot.net.Lib
{
public class LevelCalc : ILevelCalc
{
private readonly int[] _levels;
private int[] _levels;
public LevelCalc()
{
@ -20,9 +20,20 @@ namespace Geekbot.Core.Levels
_levels = levels.ToArray();
}
public int GetLevel(int? messages)
public int GetLevel(int messages)
{
return 1 + _levels.TakeWhile(level => !(level > messages)).Count();
var returnVal = 1;
foreach (var level in _levels)
{
if (level > messages) break;
returnVal++;
}
return returnVal;
}
}
public interface ILevelCalc
{
int GetLevel(int experience);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

0
Geekbot.net/Logs/.keep Executable file
View file

243
Geekbot.net/Program.cs Executable file
View file

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

View file

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

View file

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

View file

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

View file

@ -4,9 +4,8 @@ https://nationalzoo.si.edu/sites/default/files/styles/slide_1400x700/public/supp
https://media4.s-nbcnews.com/j/newscms/2016_36/1685951/ss-160826-twip-05_8cf6d4cb83758449fd400c7c3d71aa1f.nbcnews-ux-2880-1000.jpg
https://ichef-1.bbci.co.uk/news/660/cpsprodpb/169F6/production/_91026629_gettyimages-519508400.jpg
https://cdn.history.com/sites/2/2017/03/GettyImages-157278376.jpg
https://www.pandasinternational.org/wptemp/wp-content/uploads/2012/10/slider1.jpg
https://tctechcrunch2011.files.wordpress.com/2015/11/panda.jpg
http://www.nationalgeographic.com/content/dam/magazine/rights-exempt/2016/08/departments/panda-mania-12.jpg
http://animals.sandiegozoo.org/sites/default/files/2016-09/panda1_10.jpg
http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Mammals/A-G/giant-panda-eating.adapt.945.1.jpg
https://static.independent.co.uk/s3fs-public/thumbnails/image/2015/10/08/15/Hong-Kong-pandas.jpg
https://3sn4dm1qd6i72l8a4r2ig7fl-wpengine.netdna-ssl.com/wp-content/uploads/2016/11/panda_lunlun_ZA_2083-b.jpg
http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Mammals/A-G/giant-panda-eating.adapt.945.1.jpg

View file

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

View file

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

View file

@ -1,20 +1,21 @@
https://i.guim.co.uk/img/media/6b9be13031738e642f93f9271f3592044726a9b1/0_0_2863_1610/2863.jpg?w=640&h=360&q=55&auto=format&usm=12&fit=max&s=85f3b33cc158b5aa120c143dae1916ed
http://cf.ltkcdn.net/small-pets/images/std/212089-676x450-Turtle-feeding-on-leaf.jpg
https://static1.squarespace.com/static/5369465be4b0507a1fd05af0/53767a6be4b0ad0822345e52/57e40ba4893fc031e05a018f/1498243318058/solvin.jpg?format=1500w
https://c402277.ssl.cf1.rackcdn.com/photos/419/images/story_full_width/HI_287338Hero.jpg?1433950119
https://www.cdc.gov/salmonella/agbeni-08-17/images/turtle.jpg
https://cdn.arstechnica.net/wp-content/uploads/2017/08/GettyImages-524757168.jpg
http://pmdvod.nationalgeographic.com/NG_Video/595/319/4504517_098_05_TOS_thumbnail_640x360_636296259676.jpg
http://cdn1.arkive.org/media/7D/7D46329A-6ED2-4F08-909E-7B596417994A/Presentation.Large/Big-headed-turtle-close-up.jpg
http://s7d2.scene7.com/is/image/PetSmart/ARTHMB-CleaningYourTortoiseOrTurtlesHabitat-20160818?$AR1104$
https://fthmb.tqn.com/9VGWzK_GWlvrjxtdFPX6EJxOq24=/960x0/filters:no_upscale()/133605352-56a2bce53df78cf7727960db.jpg
https://i.imgur.com/46QmzgF.jpg
https://www.wildgratitude.com/wp-content/uploads/2015/07/turtle-spirit-animal1.jpg
http://www.backwaterreptiles.com/images/turtles/red-eared-slider-turtle-for-sale.jpg
https://i.pinimg.com/736x/f1/f4/13/f1f413d6d07912be6080c08b186630ac--happy-turtle-funny-stuff.jpg
http://www.dupageforest.org/uploadedImages/Content/District_News/Nature_Stories/2016/Snapping%20Turtle%20Scott%20Plantier%20STP4793.jpg
http://turtlebackzoo.com/wp-content/uploads/2016/07/exhibit-headers_0008_SOUTH-AMERICA-600x400.jpg
https://i.ytimg.com/vi/_YfYHFM3Das/maxresdefault.jpg
https://i.pinimg.com/736x/dd/4e/7f/dd4e7f2f921ac28b1d5a59174d477131--cute-baby-sea-turtles-adorable-turtles.jpg
http://kids.nationalgeographic.com/content/dam/kids/photos/animals/Reptiles/A-G/green-sea-turtle-closeup-underwater.adapt.945.1.jpg
https://i.ytimg.com/vi/p4Jj9QZFJvw/hqdefault.jpg
https://fthmb.tqn.com/nirxHkH3jBAe74ife6fJJu6k6q8=/2121x1414/filters:fill(auto,1)/Red-eared-sliders-GettyImages-617946009-58fae8835f9b581d59a5bab6.jpg
http://assets.worldwildlife.org/photos/167/images/original/MID_225023-circle-hawksbill-turtle.jpg?1345565600
https://seaturtles.org/wp-content/uploads/2013/11/GRN-honuAnitaWintner2.jpg
https://images2.minutemediacdn.com/image/upload/c_crop,h_2549,w_4536,x_0,y_237/v1560186367/shape/mentalfloss/istock-687398754.jpg?itok=QsiF5yHP
https://c402277.ssl.cf1.rackcdn.com/photos/13028/images/story_full_width/seaturtle_spring2017.jpg?1485359391
https://i2.wp.com/rangerrick.org/wp-content/uploads/2018/03/Turtle-Tale-RR-Jr-June-July-2017.jpg?fit=1156%2C650&ssl=1
https://boyslifeorg.files.wordpress.com/2019/07/greenseaturtle.jpg

View file

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

View file

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

View file

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

View file

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 361 KiB

View file

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

View file

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

16
Tests/Tests.csproj Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,59 +0,0 @@
using Discord.Commands;
using Geekbot.Core;
// using Geekbot.Core.ErrorHandling;
// using Geekbot.Core.GlobalSettings;
// using Google.Apis.Services;
// using Google.Apis.YouTube.v3;
namespace Geekbot.Bot.Commands.Integrations
{
public class Youtube : TransactionModuleBase
{
// private readonly IGlobalSettings _globalSettings;
// private readonly IErrorHandler _errorHandler;
// public Youtube(IGlobalSettings globalSettings, IErrorHandler errorHandler)
// {
// _globalSettings = globalSettings;
// _errorHandler = errorHandler;
// }
[Command("yt", RunMode = RunMode.Async)]
[Summary("Search for something on youtube.")]
public async Task Yt([Remainder] [Summary("title")] string searchQuery)
{
await ReplyAsync("The youtube command is temporarily disabled");
// var key = _globalSettings.GetKey("YoutubeKey");
// if (string.IsNullOrEmpty(key))
// {
// await ReplyAsync("No youtube key set, please tell my senpai to set one");
// return;
// }
//
// try
// {
// var youtubeService = new YouTubeService(new BaseClientService.Initializer
// {
// ApiKey = key,
// ApplicationName = GetType().ToString()
// });
//
// var searchListRequest = youtubeService.Search.List("snippet");
// searchListRequest.Q = searchQuery;
// searchListRequest.MaxResults = 2;
//
// var searchListResponse = await searchListRequest.ExecuteAsync();
//
// var result = searchListResponse.Items[0];
//
// await ReplyAsync(
// $"\"{result.Snippet.Title}\" from \"{result.Snippet.ChannelTitle}\" https://youtu.be/{result.Id.VideoId}");
// }
// catch (Exception e)
// {
// await _errorHandler.HandleCommandException(e, Context);
// }
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

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