Split Geekbot.net into src/Bot, src/Core, and src/Web
This commit is contained in:
parent
7b6dd2d2f9
commit
fc0af492ad
197 changed files with 542 additions and 498 deletions
43
src/Bot/Bot.csproj
Normal file
43
src/Bot/Bot.csproj
Normal file
|
@ -0,0 +1,43 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||
<ApplicationIcon>derp.ico</ApplicationIcon>
|
||||
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
|
||||
<RootNamespace>Geekbot.Bot</RootNamespace>
|
||||
<AssemblyName>Geekbot</AssemblyName>
|
||||
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>
|
||||
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.0-DEV</Version>
|
||||
<Company>Pizza and Coffee Studios</Company>
|
||||
<Authors>Pizza and Coffee Studios</Authors>
|
||||
<Description>A Discord bot</Description>
|
||||
<RepositoryUrl>https://github.com/pizzaandcoffee/Geekbot.net</RepositoryUrl>
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://geekbot.pizzaandcoffee.rocks</PackageProjectUrl>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="Discord.Net" Version="2.2.0" />
|
||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.45.0.1929" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.24" />
|
||||
<PackageReference Include="MtgApiManager.Lib" Version="1.2.2" />
|
||||
<PackageReference Include="MyAnimeListSharp" Version="1.3.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="PokeApi.NET" Version="1.1.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Storage\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\Web\Web.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
234
src/Bot/Commands/Admin/Admin.cs
Normal file
234
src/Bot/Commands/Admin/Admin.cs
Normal file
|
@ -0,0 +1,234 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.GuildSettingsManager;
|
||||
using Geekbot.Core.Localization;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Admin
|
||||
{
|
||||
[Group("admin")]
|
||||
[RequireUserPermission(GuildPermission.Administrator)]
|
||||
[DisableInDirectMessage]
|
||||
public class Admin : ModuleBase
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IGuildSettingsManager _guildSettingsManager;
|
||||
private readonly ITranslationHandler _translation;
|
||||
|
||||
public Admin(DiscordSocketClient client, IErrorHandler errorHandler, IGuildSettingsManager guildSettingsManager, ITranslationHandler translationHandler)
|
||||
{
|
||||
_client = client;
|
||||
_errorHandler = errorHandler;
|
||||
_guildSettingsManager = guildSettingsManager;
|
||||
_translation = translationHandler;
|
||||
}
|
||||
|
||||
[Command("welcome", RunMode = RunMode.Async)]
|
||||
[Summary("Set a Welcome Message (use '$user' to mention the new joined user).")]
|
||||
public async Task SetWelcomeMessage([Remainder, Summary("message")] string welcomeMessage)
|
||||
{
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
guild.WelcomeMessage = welcomeMessage;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
|
||||
var formatedMessage = welcomeMessage.Replace("$user", Context.User.Mention);
|
||||
await ReplyAsync($"Welcome message has been changed\r\nHere is an example of how it would look:\r\n{formatedMessage}");
|
||||
}
|
||||
|
||||
[Command("welcomechannel", RunMode = RunMode.Async)]
|
||||
[Summary("Set a channel for the welcome messages (by default it uses the top most channel)")]
|
||||
public async Task SelectWelcomeChannel([Summary("#Channel")] ISocketMessageChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = await channel.SendMessageAsync("...");
|
||||
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
guild.WelcomeChannel = channel.Id.AsLong();
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
|
||||
await m.DeleteAsync();
|
||||
|
||||
await ReplyAsync("Successfully saved the welcome channel");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context, "That channel doesn't seem to exist or i don't have write permissions");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("modchannel", RunMode = RunMode.Async)]
|
||||
[Summary("Set a channel for moderation purposes")]
|
||||
public async Task SelectModChannel([Summary("#Channel")] ISocketMessageChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = await channel.SendMessageAsync("verifying...");
|
||||
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
guild.ModChannel = channel.Id.AsLong();
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Successfully saved mod channel, you can now do the following");
|
||||
sb.AppendLine("- `!admin showleave` - send message to mod channel when someone leaves");
|
||||
sb.AppendLine("- `!admin showdel` - send message to mod channel when someone deletes a message");
|
||||
await m.ModifyAsync(e => e.Content = sb.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context, "That channel doesn't seem to exist or i don't have write permissions");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("showleave", RunMode = RunMode.Async)]
|
||||
[Summary("Toggle - notify modchannel when someone leaves")]
|
||||
public async Task ShowLeave()
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
var modChannel = await GetModChannel(guild.ModChannel.AsUlong());
|
||||
if (modChannel == null) return;
|
||||
|
||||
guild.ShowLeave = !guild.ShowLeave;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
await modChannel.SendMessageAsync(guild.ShowLeave
|
||||
? "Saved - now sending messages here when someone leaves"
|
||||
: "Saved - stopping sending messages here when someone leaves"
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("showdel", RunMode = RunMode.Async)]
|
||||
[Summary("Toggle - notify modchannel when someone deletes a message")]
|
||||
public async Task ShowDelete()
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
var modChannel = await GetModChannel(guild.ModChannel.AsUlong());
|
||||
if (modChannel == null) return;
|
||||
|
||||
guild.ShowDelete = !guild.ShowDelete;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
await modChannel.SendMessageAsync(guild.ShowDelete
|
||||
? "Saved - now sending messages here when someone deletes a message"
|
||||
: "Saved - stopping sending messages here when someone deletes a message"
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("setlang", RunMode = RunMode.Async)]
|
||||
[Summary("Change the bots language")]
|
||||
public async Task SetLanguage([Summary("language")] string languageRaw)
|
||||
{
|
||||
try
|
||||
{
|
||||
var language = languageRaw.ToUpper();
|
||||
var success = await _translation.SetLanguage(Context.Guild.Id, language);
|
||||
if (success)
|
||||
{
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
guild.Language = language;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
await ReplyAsync(transContext.GetString("NewLanguageSet"));
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyAsync(
|
||||
$"That doesn't seem to be a supported language\r\nSupported Languages are {string.Join(", ", _translation.SupportedLanguages)}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("wiki", RunMode = RunMode.Async)]
|
||||
[Summary("Change the wikipedia instance (use lang code in xx.wikipedia.org)")]
|
||||
public async Task SetWikiLanguage([Summary("language")] string languageRaw)
|
||||
{
|
||||
try
|
||||
{
|
||||
var language = languageRaw.ToLower();
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
guild.WikiLang = language;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
|
||||
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);
|
||||
guild.Ping = !guild.Ping;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
await ReplyAsync(guild.Ping ? "i will reply to ping now" : "No more pongs...");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("hui", RunMode = RunMode.Async)]
|
||||
[Summary("Enable the ping reply.")]
|
||||
public async Task ToggleHui()
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = _guildSettingsManager.GetSettings(Context.Guild.Id);
|
||||
guild.Hui = !guild.Hui;
|
||||
await _guildSettingsManager.UpdateSettings(guild);
|
||||
await ReplyAsync(guild.Hui ? "i will reply to hui now" : "No more hui's...");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
src/Bot/Commands/Admin/Mod.cs
Normal file
38
src/Bot/Commands/Admin/Mod.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Admin
|
||||
{
|
||||
[Group("mod")]
|
||||
[RequireUserPermission(GuildPermission.KickMembers)]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
[DisableInDirectMessage]
|
||||
public class Mod : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Mod(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("namehistory", RunMode = RunMode.Async)]
|
||||
[Summary("See past usernames of an user")]
|
||||
public async Task UsernameHistory([Summary("@someone")] IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("This command has been removed due to low usage and excessively high database usage");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
src/Bot/Commands/Admin/Owner/Owner.cs
Normal file
126
src/Bot/Commands/Admin/Owner/Owner.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.GlobalSettings;
|
||||
using Geekbot.Core.Logger;
|
||||
using Geekbot.Core.UserRepository;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Admin.Owner
|
||||
{
|
||||
[Group("owner")]
|
||||
[RequireOwner]
|
||||
public class Owner : ModuleBase
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IGeekbotLogger _logger;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public Owner(DiscordSocketClient client, IGeekbotLogger logger, IUserRepository userRepositry, IErrorHandler errorHandler, IGlobalSettings globalSettings)
|
||||
{
|
||||
_client = client;
|
||||
_logger = logger;
|
||||
_userRepository = userRepositry;
|
||||
_errorHandler = errorHandler;
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
196
src/Bot/Commands/Admin/Role.cs
Normal file
196
src/Bot/Commands/Admin/Role.cs
Normal file
|
@ -0,0 +1,196 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Net;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.ReactionListener;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Admin
|
||||
{
|
||||
[Group("role")]
|
||||
[DisableInDirectMessage]
|
||||
public class Role : ModuleBase
|
||||
{
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IReactionListener _reactionListener;
|
||||
private readonly ITranslationHandler _translationHandler;
|
||||
|
||||
public Role(DatabaseContext database, IErrorHandler errorHandler, IReactionListener reactionListener, ITranslationHandler translationHandler)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_reactionListener = reactionListener;
|
||||
_translationHandler = translationHandler;
|
||||
}
|
||||
|
||||
[Command(RunMode = RunMode.Async)]
|
||||
[Summary("Get a list of all available roles.")]
|
||||
public async Task GetAllRoles()
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
var roles = _database.RoleSelfService.Where(g => g.GuildId.Equals(Context.Guild.Id.AsLong())).ToList();
|
||||
if (roles.Count == 0)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("NoRolesConfigured"));
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(transContext.GetString("ListHeader", Context.Guild.Name));
|
||||
sb.AppendLine(transContext.GetString("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 transContext = await _translationHandler.GetGuildContext(Context);
|
||||
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(transContext.GetString("RoleNotFound"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (guildUser.RoleIds.Contains(role.Id))
|
||||
{
|
||||
await guildUser.RemoveRoleAsync(role);
|
||||
await ReplyAsync(transContext.GetString("RemovedUserFromRole", role.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
await guildUser.AddRoleAsync(role);
|
||||
await ReplyAsync(transContext.GetString("AddedUserFromRole", role.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyAsync(transContext.GetString("RoleNotFound"));
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
await _errorHandler.HandleHttpException(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
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
if (role.IsManaged)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotAddManagedRole"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (role.Permissions.ManageRoles
|
||||
|| role.Permissions.Administrator
|
||||
|| role.Permissions.ManageGuild
|
||||
|| role.Permissions.BanMembers
|
||||
|| role.Permissions.KickMembers)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotAddDangerousRole"));
|
||||
return;
|
||||
}
|
||||
|
||||
_database.RoleSelfService.Add(new RoleSelfServiceModel
|
||||
{
|
||||
GuildId = Context.Guild.Id.AsLong(),
|
||||
RoleId = role.Id.AsLong(),
|
||||
WhiteListName = roleName
|
||||
});
|
||||
await _database.SaveChangesAsync();
|
||||
await ReplyAsync(transContext.GetString("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 transContext = await _translationHandler.GetGuildContext(Context);
|
||||
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(transContext.GetString("RemovedRoleFromWhitelist", roleName));
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyAsync(transContext.GetString("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 e)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync("Custom emojis from other servers are not supported");
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
76
src/Bot/Commands/Games/Pokedex.cs
Normal file
76
src/Bot/Commands/Games/Pokedex.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using PokeAPI;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Games
|
||||
{
|
||||
public class Pokedex : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Pokedex(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("pokedex", RunMode = RunMode.Async)]
|
||||
[Summary("A Pokedex Tool")]
|
||||
public async Task GetPokemon([Summary("pokemon-name")] string pokemonName)
|
||||
{
|
||||
try
|
||||
{
|
||||
DataFetcher.ShouldCacheData = true;
|
||||
Pokemon pokemon;
|
||||
try
|
||||
{
|
||||
pokemon = await DataFetcher.GetNamedApiObject<Pokemon>(pokemonName.ToLower());
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyAsync("I couldn't find that pokemon :confused:");
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = await PokemonEmbedBuilder(pokemon);
|
||||
await ReplyAsync("", false, embed.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
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.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("Height", pokemon.Height);
|
||||
eb.AddInlineField("Weight", pokemon.Mass);
|
||||
return eb;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return string.Empty;
|
||||
return char.ToUpper(s[0]) + s.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
98
src/Bot/Commands/Games/Roll/Roll.cs
Normal file
98
src/Bot/Commands/Games/Roll/Roll.cs
Normal file
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.KvInMemoryStore;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Games.Roll
|
||||
{
|
||||
public class Roll : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IKvInMemoryStore _kvInMemoryStore;
|
||||
private readonly ITranslationHandler _translation;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IRandomNumberGenerator _randomNumberGenerator;
|
||||
|
||||
public Roll(IKvInMemoryStore kvInMemoryStore,IErrorHandler errorHandler, ITranslationHandler translation, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
_kvInMemoryStore = kvInMemoryStore;
|
||||
_translation = translation;
|
||||
_database = database;
|
||||
_randomNumberGenerator = randomNumberGenerator;
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("roll", RunMode = RunMode.Async)]
|
||||
[Summary("Guess which number the bot will roll (1-100")]
|
||||
public async Task RollCommand([Remainder] [Summary("guess")] string stuff = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var number = _randomNumberGenerator.Next(1, 100);
|
||||
int.TryParse(stuff, out var guess);
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
if (guess <= 100 && guess > 0)
|
||||
{
|
||||
var kvKey = $"{Context.Guild.Id}:{Context.User.Id}:RollsPrevious";
|
||||
|
||||
var prevRoll = _kvInMemoryStore.Get<RollTimeout>(kvKey);
|
||||
|
||||
if (prevRoll?.LastGuess == guess && prevRoll?.GuessedOn.AddDays(1) > DateTime.Now)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString(
|
||||
"NoPrevGuess",
|
||||
Context.Message.Author.Mention,
|
||||
transContext.FormatDateTimeAsRemaining(prevRoll.GuessedOn.AddDays(1))));
|
||||
return;
|
||||
}
|
||||
|
||||
_kvInMemoryStore.Set(kvKey, new RollTimeout { LastGuess = guess, GuessedOn = DateTime.Now });
|
||||
|
||||
await ReplyAsync(transContext.GetString("Rolled", Context.Message.Author.Mention, number, guess));
|
||||
if (guess == number)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("Gratz", Context.Message.Author));
|
||||
var user = await GetUser(Context.User.Id);
|
||||
user.Rolls += 1;
|
||||
_database.Rolls.Update(user);
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("RolledNoGuess", Context.Message.Author.Mention, number));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<RollsModel> GetUser(ulong userId)
|
||||
{
|
||||
var user = _database.Rolls.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId);
|
||||
return user;
|
||||
}
|
||||
|
||||
private async Task<RollsModel> CreateNewRow(ulong userId)
|
||||
{
|
||||
var user = new RollsModel()
|
||||
{
|
||||
GuildId = Context.Guild.Id.AsLong(),
|
||||
UserId = userId.AsLong(),
|
||||
Rolls = 0
|
||||
};
|
||||
var newUser = _database.Rolls.Add(user).Entity;
|
||||
await _database.SaveChangesAsync();
|
||||
return newUser;
|
||||
}
|
||||
}
|
||||
}
|
10
src/Bot/Commands/Games/Roll/RollTimeout.cs
Normal file
10
src/Bot/Commands/Games/Roll/RollTimeout.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Games.Roll
|
||||
{
|
||||
public class RollTimeout
|
||||
{
|
||||
public int LastGuess { get; set; }
|
||||
public DateTime GuessedOn { get; set; }
|
||||
}
|
||||
}
|
61
src/Bot/Commands/Integrations/LolMmr/LolMmr.cs
Normal file
61
src/Bot/Commands/Integrations/LolMmr/LolMmr.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Integrations.LolMmr
|
||||
{
|
||||
public class LolMmr : ModuleBase
|
||||
{
|
||||
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}");
|
||||
sb.AppendLine($"Ranked: {data.Ranked.Avg}");
|
||||
sb.AppendLine($"ARAM: {data.ARAM.Avg}");
|
||||
|
||||
await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/Bot/Commands/Integrations/LolMmr/LolMmrDto.cs
Normal file
9
src/Bot/Commands/Integrations/LolMmr/LolMmrDto.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Geekbot.Bot.Commands.Integrations.LolMmr
|
||||
{
|
||||
public class LolMmrDto
|
||||
{
|
||||
public LolMrrInfoDto Ranked { get; set; }
|
||||
public LolMrrInfoDto Normal { get; set; }
|
||||
public LolMrrInfoDto ARAM { get; set; }
|
||||
}
|
||||
}
|
10
src/Bot/Commands/Integrations/LolMmr/LolMrrInfoDto.cs
Normal file
10
src/Bot/Commands/Integrations/LolMmr/LolMrrInfoDto.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Integrations.LolMmr
|
||||
{
|
||||
public class LolMrrInfoDto
|
||||
{
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public decimal Avg { get; set; } = 0;
|
||||
}
|
||||
}
|
96
src/Bot/Commands/Integrations/MagicTheGathering.cs
Normal file
96
src/Bot/Commands/Integrations/MagicTheGathering.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Converters;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using MtgApiManager.Lib.Service;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Integrations
|
||||
{
|
||||
public class MagicTheGathering : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IMtgManaConverter _manaConverter;
|
||||
|
||||
public MagicTheGathering(IErrorHandler errorHandler, IMtgManaConverter manaConverter)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_manaConverter = manaConverter;
|
||||
}
|
||||
|
||||
[Command("mtg", RunMode = RunMode.Async)]
|
||||
[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 card = result.All().Value.FirstOrDefault();
|
||||
if (card == null)
|
||||
{
|
||||
await message.ModifyAsync(properties => properties.Content = ":red_circle: I couldn't find a card with that name...");
|
||||
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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
128
src/Bot/Commands/Integrations/Mal.cs
Normal file
128
src/Bot/Commands/Integrations/Mal.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.MalClient;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Integrations
|
||||
{
|
||||
public class Mal : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IMalClient _malClient;
|
||||
|
||||
public Mal(IMalClient malClient, IErrorHandler errorHandler)
|
||||
{
|
||||
_malClient = malClient;
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("anime", RunMode = RunMode.Async)]
|
||||
[Summary("Show Info about an Anime.")]
|
||||
public async Task SearchAnime([Remainder] [Summary("anime-name")] string animeName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_malClient.IsLoggedIn())
|
||||
{
|
||||
var anime = await _malClient.GetAnime(animeName);
|
||||
if (anime != null)
|
||||
{
|
||||
var eb = new EmbedBuilder();
|
||||
|
||||
var description = HttpUtility.HtmlDecode(anime.Synopsis)
|
||||
.Replace("<br />", "")
|
||||
.Replace("[i]", "*")
|
||||
.Replace("[/i]", "*");
|
||||
|
||||
eb.Title = anime.Title;
|
||||
eb.Description = description;
|
||||
eb.ImageUrl = anime.Image;
|
||||
eb.AddInlineField("Premiered", $"{anime.StartDate}");
|
||||
eb.AddInlineField("Ended", anime.EndDate == "0000-00-00" ? "???" : anime.EndDate);
|
||||
eb.AddInlineField("Status", anime.Status);
|
||||
eb.AddInlineField("Episodes", anime.Episodes);
|
||||
eb.AddInlineField("MAL Score", anime.Score);
|
||||
eb.AddInlineField("Type", anime.Type);
|
||||
eb.AddField("MAL Link", $"https://myanimelist.net/anime/{anime.Id}");
|
||||
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyAsync("No anime found with that name...");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyAsync(
|
||||
"Unfortunally i'm not connected to MyAnimeList.net, please tell my senpai to connect me");
|
||||
}
|
||||
}
|
||||
catch (XmlException e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context, "The MyAnimeList.net API refused to answer");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("manga", RunMode = RunMode.Async)]
|
||||
[Summary("Show Info about a Manga.")]
|
||||
public async Task SearchManga([Remainder] [Summary("manga-name")] string mangaName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_malClient.IsLoggedIn())
|
||||
{
|
||||
var manga = await _malClient.GetManga(mangaName);
|
||||
if (manga != null)
|
||||
{
|
||||
var eb = new EmbedBuilder();
|
||||
|
||||
var description = HttpUtility.HtmlDecode(manga.Synopsis)
|
||||
.Replace("<br />", "")
|
||||
.Replace("[i]", "*")
|
||||
.Replace("[/i]", "*");
|
||||
|
||||
eb.Title = manga.Title;
|
||||
eb.Description = description;
|
||||
eb.ImageUrl = manga.Image;
|
||||
eb.AddInlineField("Premiered", $"{manga.StartDate}");
|
||||
eb.AddInlineField("Ended", manga.EndDate == "0000-00-00" ? "???" : manga.EndDate);
|
||||
eb.AddInlineField("Status", manga.Status);
|
||||
eb.AddInlineField("Volumes", manga.Volumes);
|
||||
eb.AddInlineField("Chapters", manga.Chapters);
|
||||
eb.AddInlineField("MAL Score", manga.Score);
|
||||
eb.AddField("MAL Link", $"https://myanimelist.net/manga/{manga.Id}");
|
||||
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyAsync("No manga found with that name...");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyAsync(
|
||||
"Unfortunally i'm not connected to MyAnimeList.net, please tell my senpai to connect me");
|
||||
}
|
||||
}
|
||||
catch (XmlException e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context, "The MyAnimeList.net API refused to answer");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace Geekbot.Bot.Commands.Integrations.UbranDictionary
|
||||
{
|
||||
internal class UrbanListItemDto
|
||||
{
|
||||
public string Definition { get; set; }
|
||||
public string Permalink { get; set; }
|
||||
public string ThumbsUp { get; set; }
|
||||
public string Word { get; set; }
|
||||
public string Example { get; set; }
|
||||
public string ThumbsDown { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Integrations.UbranDictionary
|
||||
{
|
||||
internal class UrbanResponseDto
|
||||
{
|
||||
public string[] Tags { get; set; }
|
||||
public List<UrbanListItemDto> List { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Integrations.UbranDictionary
|
||||
{
|
||||
public class UrbanDictionary : ModuleBase
|
||||
{
|
||||
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 definitions = await HttpAbstractions.Get<UrbanResponseDto>(new Uri($"https://api.urbandictionary.com/v0/define?term={word}"));
|
||||
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 description = definition.Definition;
|
||||
if (description.Length > 1801)
|
||||
{
|
||||
description = description.Substring(0, 1800) + " [...]";
|
||||
}
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithAuthor(new EmbedAuthorBuilder
|
||||
{
|
||||
Name = definition.Word,
|
||||
Url = definition.Permalink
|
||||
});
|
||||
eb.WithColor(new Color(239, 255, 0));
|
||||
if (!string.IsNullOrEmpty(definition.Definition)) eb.Description = description;
|
||||
if (!string.IsNullOrEmpty(definition.Example)) eb.AddField("Example", definition.Example ?? "(no example given...)");
|
||||
if (!string.IsNullOrEmpty(definition.ThumbsUp)) eb.AddInlineField("Upvotes", definition.ThumbsUp);
|
||||
if (!string.IsNullOrEmpty(definition.ThumbsDown)) eb.AddInlineField("Downvotes", definition.ThumbsDown);
|
||||
if (definitions.Tags?.Length > 0) eb.AddField("Tags", string.Join(", ", definitions.Tags));
|
||||
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
112
src/Bot/Commands/Integrations/Wikipedia.cs
Normal file
112
src/Bot/Commands/Integrations/Wikipedia.cs
Normal file
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
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 : ModuleBase
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
58
src/Bot/Commands/Integrations/Youtube.cs
Normal file
58
src/Bot/Commands/Integrations/Youtube.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
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 : ModuleBase
|
||||
{
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Youtube(IGlobalSettings globalSettings, IErrorHandler errorHandler)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("yt", RunMode = RunMode.Async)]
|
||||
[Summary("Search for something on youtube.")]
|
||||
public async Task Yt([Remainder] [Summary("title")] string searchQuery)
|
||||
{
|
||||
var key = _globalSettings.GetKey("YoutubeKey");
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
await ReplyAsync("No youtube key set, please tell my senpai to set one");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var youtubeService = new YouTubeService(new BaseClientService.Initializer
|
||||
{
|
||||
ApiKey = key,
|
||||
ApplicationName = GetType().ToString()
|
||||
});
|
||||
|
||||
var searchListRequest = youtubeService.Search.List("snippet");
|
||||
searchListRequest.Q = searchQuery;
|
||||
searchListRequest.MaxResults = 2;
|
||||
|
||||
var searchListResponse = await searchListRequest.ExecuteAsync();
|
||||
|
||||
var result = searchListResponse.Items[0];
|
||||
|
||||
await ReplyAsync(
|
||||
$"\"{result.Snippet.Title}\" from \"{result.Snippet.ChannelTitle}\" https://youtu.be/{result.Id.VideoId}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class BenedictCumberbatchNameGenerator : ModuleBase
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
src/Bot/Commands/Randomness/Cat/Cat.cs
Normal file
38
src/Bot/Commands/Randomness/Cat/Cat.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness.Cat
|
||||
{
|
||||
public class Cat : ModuleBase
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
src/Bot/Commands/Randomness/Cat/CatResponseDto.cs
Normal file
7
src/Bot/Commands/Randomness/Cat/CatResponseDto.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Cat
|
||||
{
|
||||
internal class CatResponseDto
|
||||
{
|
||||
public string File { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Chuck
|
||||
{
|
||||
internal class ChuckNorrisJokeResponseDto
|
||||
{
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
41
src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokes.cs
Normal file
41
src/Bot/Commands/Randomness/Chuck/ChuckNorrisJokes.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness.Chuck
|
||||
{
|
||||
public class ChuckNorrisJokes : ModuleBase
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
src/Bot/Commands/Randomness/Dad/DadJokeResponseDto.cs
Normal file
7
src/Bot/Commands/Randomness/Dad/DadJokeResponseDto.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Dad
|
||||
{
|
||||
internal class DadJokeResponseDto
|
||||
{
|
||||
public string Joke { get; set; }
|
||||
}
|
||||
}
|
33
src/Bot/Commands/Randomness/Dad/DadJokes.cs
Normal file
33
src/Bot/Commands/Randomness/Dad/DadJokes.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness.Dad
|
||||
{
|
||||
public class DadJokes : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public DadJokes(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("dad", RunMode = RunMode.Async)]
|
||||
[Summary("A random dad joke")]
|
||||
public async Task Say()
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await HttpAbstractions.Get<DadJokeResponseDto>(new Uri("https://icanhazdadjoke.com/"));
|
||||
await ReplyAsync(response.Joke);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
src/Bot/Commands/Randomness/Dog/Dog.cs
Normal file
38
src/Bot/Commands/Randomness/Dog/Dog.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness.Dog
|
||||
{
|
||||
public class Dog : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Dog(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("dog", RunMode = RunMode.Async)]
|
||||
[Summary("Return a random image of a dog.")]
|
||||
public async Task Say()
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await HttpAbstractions.Get<DogResponseDto>(new Uri("http://random.dog/woof.json"));
|
||||
var eb = new EmbedBuilder
|
||||
{
|
||||
ImageUrl = response.Url
|
||||
};
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
src/Bot/Commands/Randomness/Dog/DogResponseDto.cs
Normal file
7
src/Bot/Commands/Randomness/Dog/DogResponseDto.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Dog
|
||||
{
|
||||
internal class DogResponseDto
|
||||
{
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
57
src/Bot/Commands/Randomness/EightBall.cs
Normal file
57
src/Bot/Commands/Randomness/EightBall.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class EightBall : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public EightBall(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("8ball", RunMode = RunMode.Async)]
|
||||
[Summary("Ask 8Ball a Question.")]
|
||||
public async Task Ball([Remainder] [Summary("question")] string echo)
|
||||
{
|
||||
try
|
||||
{
|
||||
var replies = new List<string>
|
||||
{
|
||||
"It is certain",
|
||||
"It is decidedly so",
|
||||
"Without a doubt",
|
||||
"Yes, definitely",
|
||||
"You may rely on it",
|
||||
"As I see it, yes",
|
||||
"Most likely",
|
||||
"Outlook good",
|
||||
"Yes",
|
||||
"Signs point to yes",
|
||||
"Reply hazy try again",
|
||||
"Ask again later",
|
||||
"Better not tell you now",
|
||||
"Cannot predict now",
|
||||
"Concentrate and ask again",
|
||||
"Don't count on it",
|
||||
"My reply is no",
|
||||
"My sources say no",
|
||||
"Outlook not so good",
|
||||
"Very doubtful"
|
||||
};
|
||||
|
||||
var answer = new Random().Next(replies.Count);
|
||||
await ReplyAsync(replies[answer]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
src/Bot/Commands/Randomness/Fortune.cs
Normal file
23
src/Bot/Commands/Randomness/Fortune.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Media;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class Fortune : ModuleBase
|
||||
{
|
||||
private readonly IFortunesProvider _fortunes;
|
||||
|
||||
public Fortune(IFortunesProvider fortunes)
|
||||
{
|
||||
_fortunes = fortunes;
|
||||
}
|
||||
|
||||
[Command("fortune", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random fortune")]
|
||||
public async Task GetAFortune()
|
||||
{
|
||||
await ReplyAsync(_fortunes.GetRandomFortune());
|
||||
}
|
||||
}
|
||||
}
|
36
src/Bot/Commands/Randomness/Gdq.cs
Normal file
36
src/Bot/Commands/Randomness/Gdq.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class Gdq : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Gdq(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("gdq", RunMode = RunMode.Async)]
|
||||
[Summary("Get a quote from the GDQ donation generator.")]
|
||||
public async Task GetQuote()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new WebClient();
|
||||
var url = new Uri("http://taskinoz.com/gdq/api/");
|
||||
var response = client.DownloadString(url);
|
||||
|
||||
await ReplyAsync(response);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
src/Bot/Commands/Randomness/Greetings/GreetingBaseDto.cs
Normal file
11
src/Bot/Commands/Randomness/Greetings/GreetingBaseDto.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Greetings
|
||||
{
|
||||
public class GreetingBaseDto
|
||||
{
|
||||
public string Language { get; set; }
|
||||
public string LanguageNative { get; set; }
|
||||
public string LanguageCode { get; set; }
|
||||
public string Script { get; set; }
|
||||
public GreetingDto Primary { get; set; }
|
||||
}
|
||||
}
|
10
src/Bot/Commands/Randomness/Greetings/GreetingDto.cs
Normal file
10
src/Bot/Commands/Randomness/Greetings/GreetingDto.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Greetings
|
||||
{
|
||||
public class GreetingDto
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public string Dialect { get; set; }
|
||||
public string Romanization { get; set; }
|
||||
public string[] Use { get; set; }
|
||||
}
|
||||
}
|
51
src/Bot/Commands/Randomness/Greetings/Greetings.cs
Normal file
51
src/Bot/Commands/Randomness/Greetings/Greetings.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness.Greetings
|
||||
{
|
||||
public class Greetings : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Greetings(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("hello", RunMode = RunMode.Async)]
|
||||
[Alias("greeting", "hi", "hallo")]
|
||||
[Summary("Say hello to the bot and get a reply in a random language")]
|
||||
public async Task GetGreeting()
|
||||
{
|
||||
try
|
||||
{
|
||||
var greeting = await HttpAbstractions.Get<GreetingBaseDto>(new Uri("https://api.greetings.dev/v1/greeting"));
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.Title = greeting.Primary.Text;
|
||||
eb.AddInlineField("Language", greeting.Language);
|
||||
|
||||
if (greeting.Primary.Dialect != null)
|
||||
{
|
||||
eb.AddInlineField("Dialect", greeting.Primary.Dialect);
|
||||
}
|
||||
|
||||
if (greeting.Primary.Romanization != null)
|
||||
{
|
||||
eb.AddInlineField("Roman", greeting.Primary.Romanization);
|
||||
}
|
||||
|
||||
await ReplyAsync(string.Empty, false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
src/Bot/Commands/Randomness/Kanye/Kanye.cs
Normal file
33
src/Bot/Commands/Randomness/Kanye/Kanye.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness.Kanye
|
||||
{
|
||||
public class Kanye : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Kanye(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("kanye", RunMode = RunMode.Async)]
|
||||
[Summary("A random kayne west quote")]
|
||||
public async Task Say()
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await HttpAbstractions.Get<KanyeResponseDto>(new Uri("https://api.kanye.rest/"));
|
||||
await ReplyAsync(response.Quote);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/Bot/Commands/Randomness/Kanye/KanyeResponseDto.cs
Normal file
8
src/Bot/Commands/Randomness/Kanye/KanyeResponseDto.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Geekbot.Bot.Commands.Randomness.Kanye
|
||||
{
|
||||
public class KanyeResponseDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Quote { get; set; }
|
||||
}
|
||||
}
|
80
src/Bot/Commands/Randomness/RandomAnimals.cs
Normal file
80
src/Bot/Commands/Randomness/RandomAnimals.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Media;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class RandomAnimals : ModuleBase
|
||||
{
|
||||
private readonly IMediaProvider _mediaProvider;
|
||||
|
||||
public RandomAnimals(IMediaProvider mediaProvider)
|
||||
{
|
||||
_mediaProvider = mediaProvider;
|
||||
}
|
||||
|
||||
[Command("panda", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random panda image")]
|
||||
public async Task Panda()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Panda)));
|
||||
}
|
||||
|
||||
[Command("croissant", RunMode = RunMode.Async)]
|
||||
[Alias("gipfeli")]
|
||||
[Summary("Get a random croissant image")]
|
||||
public async Task Croissant()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Croissant)));
|
||||
}
|
||||
|
||||
[Command("pumpkin", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random pumpkin image")]
|
||||
public async Task Pumpkin()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Pumpkin)));
|
||||
}
|
||||
|
||||
[Command("squirrel", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random squirrel image")]
|
||||
public async Task Squirrel()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Squirrel)));
|
||||
}
|
||||
|
||||
[Command("turtle", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random turtle image")]
|
||||
public async Task Turtle()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Turtle)));
|
||||
}
|
||||
|
||||
[Command("penguin", RunMode = RunMode.Async)]
|
||||
[Alias("pengu")]
|
||||
[Summary("Get a random penguin image")]
|
||||
public async Task Penguin()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Penguin)));
|
||||
}
|
||||
|
||||
[Command("fox", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random fox image")]
|
||||
public async Task Fox()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Fox)));
|
||||
}
|
||||
|
||||
[Command("dab", RunMode = RunMode.Async)]
|
||||
[Summary("Get a random dab image")]
|
||||
public async Task Dab()
|
||||
{
|
||||
await ReplyAsync("", false, Eb(_mediaProvider.GetMedia(MediaType.Dab)));
|
||||
}
|
||||
|
||||
private static Embed Eb(string image)
|
||||
{
|
||||
return new EmbedBuilder {ImageUrl = image}.Build();
|
||||
}
|
||||
}
|
||||
}
|
107
src/Bot/Commands/Randomness/Ship.cs
Normal file
107
src/Bot/Commands/Randomness/Ship.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class Ship : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IRandomNumberGenerator _randomNumberGenerator;
|
||||
private readonly ITranslationHandler _translation;
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
public Ship(DatabaseContext database, IErrorHandler errorHandler, IRandomNumberGenerator randomNumberGenerator, ITranslationHandler translation)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_randomNumberGenerator = randomNumberGenerator;
|
||||
_translation = translation;
|
||||
}
|
||||
|
||||
[Command("Ship", RunMode = RunMode.Async)]
|
||||
[Summary("Ask the Shipping meter")]
|
||||
public async Task Command([Summary("@user1")] IUser user1, [Summary("@user2")] IUser user2)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userKeys = user1.Id < user2.Id
|
||||
? new Tuple<long, long>(user1.Id.AsLong(), user2.Id.AsLong())
|
||||
: new Tuple<long, long>(user2.Id.AsLong(), user1.Id.AsLong());
|
||||
|
||||
var dbval = _database.Ships.FirstOrDefault(s =>
|
||||
s.FirstUserId.Equals(userKeys.Item1) &&
|
||||
s.SecondUserId.Equals(userKeys.Item2));
|
||||
|
||||
var shippingRate = 0;
|
||||
if (dbval == null)
|
||||
{
|
||||
shippingRate = _randomNumberGenerator.Next(1, 100);
|
||||
_database.Ships.Add(new ShipsModel()
|
||||
{
|
||||
FirstUserId = userKeys.Item1,
|
||||
SecondUserId = userKeys.Item2,
|
||||
Strength = shippingRate
|
||||
});
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
shippingRate = dbval.Strength;
|
||||
}
|
||||
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
|
||||
var reply = $":heartpulse: **{transContext.GetString("Matchmaking")}** :heartpulse:\r\n";
|
||||
reply += $":two_hearts: {user1.Mention} :heart: {user2.Mention} :two_hearts:\r\n";
|
||||
reply += $"0% [{BlockCounter(shippingRate)}] 100% - {DeterminateSuccess(shippingRate, transContext)}";
|
||||
await ReplyAsync(reply);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private string DeterminateSuccess(int rate, TranslationGuildContext transContext)
|
||||
{
|
||||
return (rate / 20) switch
|
||||
{
|
||||
0 => transContext.GetString("NotGonnaToHappen"),
|
||||
1 => transContext.GetString("NotSuchAGoodIdea"),
|
||||
2 => transContext.GetString("ThereMightBeAChance"),
|
||||
3 => transContext.GetString("CouldWork"),
|
||||
4 => transContext.GetString("ItsAMatch"),
|
||||
_ => "nope"
|
||||
};
|
||||
}
|
||||
|
||||
private string BlockCounter(int rate)
|
||||
{
|
||||
var amount = rate / 10;
|
||||
Console.WriteLine(amount);
|
||||
var blocks = "";
|
||||
for (var i = 1; i <= 10; i++)
|
||||
if (i <= amount)
|
||||
{
|
||||
blocks += ":white_medium_small_square:";
|
||||
if (i == amount)
|
||||
blocks += $" {rate}% ";
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks += ":black_medium_small_square:";
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
}
|
||||
}
|
130
src/Bot/Commands/Randomness/Slap.cs
Normal file
130
src/Bot/Commands/Randomness/Slap.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Randomness
|
||||
{
|
||||
public class Slap : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
public Slap(IErrorHandler errorHandler, DatabaseContext database)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_database = database;
|
||||
}
|
||||
|
||||
[Command("slap", RunMode = RunMode.Async)]
|
||||
[Summary("slap someone")]
|
||||
public async Task Slapper([Summary("@someone")] IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (user.Id == Context.User.Id)
|
||||
{
|
||||
await ReplyAsync("Why would you slap yourself?");
|
||||
return;
|
||||
}
|
||||
|
||||
var things = new List<string>
|
||||
{
|
||||
"thing",
|
||||
"rubber chicken",
|
||||
"leek stick",
|
||||
"large trout",
|
||||
"flat hand",
|
||||
"strip of bacon",
|
||||
"feather",
|
||||
"piece of pizza",
|
||||
"moldy banana",
|
||||
"sharp retort",
|
||||
"printed version of wikipedia",
|
||||
"panda paw",
|
||||
"spiked sledgehammer",
|
||||
"monstertruck",
|
||||
"dirty toilet brush",
|
||||
"sleeping seagull",
|
||||
"sunflower",
|
||||
"mousepad",
|
||||
"lolipop",
|
||||
"bottle of rum",
|
||||
"cheese slice",
|
||||
"critical 1",
|
||||
"natural 20",
|
||||
"mjölnir (aka mewmew)",
|
||||
"kamehameha",
|
||||
"copy of Twilight",
|
||||
"med pack (get ready for the end boss)",
|
||||
"derp",
|
||||
"condom (used)",
|
||||
"gremlin fed after midnight",
|
||||
"wet baguette",
|
||||
"exploding kitten",
|
||||
"shiny piece of shit",
|
||||
"mismatched pair of socks",
|
||||
"horcrux",
|
||||
"tuna",
|
||||
"suggestion",
|
||||
"teapot",
|
||||
"candle",
|
||||
"dictionary",
|
||||
"powerless banhammer"
|
||||
};
|
||||
|
||||
await ReplyAsync($"{Context.User.Username} slapped {user.Username} with a {things[new Random().Next(things.Count - 1)]}");
|
||||
|
||||
await UpdateRecieved(user.Id);
|
||||
await UpdateGiven(Context.User.Id);
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateGiven(ulong userId)
|
||||
{
|
||||
var user = await GetUser(userId);
|
||||
user.Given++;
|
||||
_database.Slaps.Update(user);
|
||||
}
|
||||
|
||||
private async Task UpdateRecieved(ulong userId)
|
||||
{
|
||||
var user = await GetUser(userId);
|
||||
user.Recieved++;
|
||||
_database.Slaps.Update(user);
|
||||
}
|
||||
|
||||
private async Task<SlapsModel> GetUser(ulong userId)
|
||||
{
|
||||
var user = _database.Slaps.FirstOrDefault(e =>
|
||||
e.GuildId.Equals(Context.Guild.Id.AsLong()) &&
|
||||
e.UserId.Equals(userId.AsLong())
|
||||
);
|
||||
|
||||
if (user != null) return user;
|
||||
|
||||
_database.Slaps.Add(new SlapsModel
|
||||
{
|
||||
GuildId = Context.Guild.Id.AsLong(),
|
||||
UserId = userId.AsLong(),
|
||||
Recieved = 0,
|
||||
Given = 0
|
||||
});
|
||||
await _database.SaveChangesAsync();
|
||||
return _database.Slaps.FirstOrDefault(e =>
|
||||
e.GuildId.Equals(Context.Guild.Id.AsLong()) &&
|
||||
e.UserId.Equals(userId.AsLong()));
|
||||
}
|
||||
}
|
||||
}
|
161
src/Bot/Commands/Rpg/Cookies.cs
Normal file
161
src/Bot/Commands/Rpg/Cookies.cs
Normal file
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Rpg
|
||||
{
|
||||
[DisableInDirectMessage]
|
||||
[Group("cookies")]
|
||||
[Alias("cookie")]
|
||||
public class Cookies : ModuleBase
|
||||
{
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly ITranslationHandler _translation;
|
||||
private readonly IRandomNumberGenerator _randomNumberGenerator;
|
||||
|
||||
public Cookies(DatabaseContext database, IErrorHandler errorHandler, ITranslationHandler translation , IRandomNumberGenerator randomNumberGenerator)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_translation = translation;
|
||||
_randomNumberGenerator = randomNumberGenerator;
|
||||
}
|
||||
|
||||
[Command("get", RunMode = RunMode.Async)]
|
||||
[Summary("Get a cookie every 24 hours")]
|
||||
public async Task GetCookies()
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var actor = await GetUser(Context.User.Id);
|
||||
if (actor.LastPayout.Value.AddDays(1).Date > DateTime.Now.Date)
|
||||
{
|
||||
var formatedWaitTime = transContext.FormatDateTimeAsRemaining(DateTimeOffset.Now.AddDays(1).Date);
|
||||
await ReplyAsync(transContext.GetString("WaitForMoreCookies", formatedWaitTime));
|
||||
return;
|
||||
}
|
||||
actor.Cookies += 10;
|
||||
actor.LastPayout = DateTimeOffset.Now;
|
||||
await SetUser(actor);
|
||||
await ReplyAsync(transContext.GetString("GetCookies", 10, actor.Cookies));
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("jar", RunMode = RunMode.Async)]
|
||||
[Summary("Look at your cookie jar")]
|
||||
public async Task PeekIntoCookieJar()
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var actor = await GetUser(Context.User.Id);
|
||||
await ReplyAsync(transContext.GetString("InYourJar", actor.Cookies));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("give", RunMode = RunMode.Async)]
|
||||
[Summary("Give cookies to someone")]
|
||||
public async Task GiveACookie([Summary("@someone")] IUser user, [Summary("amount")] int amount = 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var giver = await GetUser(Context.User.Id);
|
||||
|
||||
if (giver.Cookies < amount)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("NotEnoughToGive"));
|
||||
return;
|
||||
}
|
||||
|
||||
var taker = await GetUser(user.Id);
|
||||
|
||||
giver.Cookies -= amount;
|
||||
taker.Cookies += amount;
|
||||
|
||||
await SetUser(giver);
|
||||
await SetUser(taker);
|
||||
|
||||
await ReplyAsync(transContext.GetString("Given", amount, user.Username));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("eat", RunMode = RunMode.Async)]
|
||||
[Summary("Eat a cookie")]
|
||||
public async Task EatACookie()
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var actor = await GetUser(Context.User.Id);
|
||||
|
||||
if (actor.Cookies < 5)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("NotEnoughCookiesToEat"));
|
||||
return;
|
||||
}
|
||||
|
||||
var amount = _randomNumberGenerator.Next(1, 5);
|
||||
actor.Cookies -= amount;
|
||||
|
||||
await SetUser(actor);
|
||||
|
||||
await ReplyAsync(transContext.GetString("AteCookies", amount, actor.Cookies));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<CookiesModel> GetUser(ulong userId)
|
||||
{
|
||||
var user = _database.Cookies.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId);
|
||||
return user;
|
||||
}
|
||||
|
||||
private async Task SetUser(CookiesModel user)
|
||||
{
|
||||
_database.Cookies.Update(user);
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task<CookiesModel> CreateNewRow(ulong userId)
|
||||
{
|
||||
var user = new CookiesModel()
|
||||
{
|
||||
GuildId = Context.Guild.Id.AsLong(),
|
||||
UserId = userId.AsLong(),
|
||||
Cookies = 0,
|
||||
LastPayout = DateTimeOffset.MinValue
|
||||
};
|
||||
var newUser = _database.Cookies.Add(user).Entity;
|
||||
await _database.SaveChangesAsync();
|
||||
return newUser;
|
||||
}
|
||||
}
|
||||
}
|
60
src/Bot/Commands/User/GuildInfo.cs
Normal file
60
src/Bot/Commands/User/GuildInfo.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Levels;
|
||||
|
||||
namespace Geekbot.Bot.Commands.User
|
||||
{
|
||||
public class GuildInfo : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly ILevelCalc _levelCalc;
|
||||
|
||||
public GuildInfo(DatabaseContext database, ILevelCalc levelCalc, IErrorHandler errorHandler)
|
||||
{
|
||||
_database = database;
|
||||
_levelCalc = levelCalc;
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("serverstats", RunMode = RunMode.Async)]
|
||||
[Summary("Show some info about the bot.")]
|
||||
[DisableInDirectMessage]
|
||||
public async Task GetInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithAuthor(new EmbedAuthorBuilder()
|
||||
.WithIconUrl(Context.Guild.IconUrl)
|
||||
.WithName(Context.Guild.Name));
|
||||
eb.WithColor(new Color(110, 204, 147));
|
||||
|
||||
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);
|
||||
|
||||
eb.AddField("Server Age", $"{created.Day}/{created.Month}/{created.Year} ({age} days)");
|
||||
eb.AddInlineField("Level", level)
|
||||
.AddInlineField("Messages", messages);
|
||||
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
153
src/Bot/Commands/User/Karma.cs
Normal file
153
src/Bot/Commands/User/Karma.cs
Normal file
|
@ -0,0 +1,153 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Localization;
|
||||
|
||||
namespace Geekbot.Bot.Commands.User
|
||||
{
|
||||
[DisableInDirectMessage]
|
||||
public class Karma : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly ITranslationHandler _translation;
|
||||
|
||||
public Karma(DatabaseContext database, IErrorHandler errorHandler, ITranslationHandler translation)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_translation = translation;
|
||||
}
|
||||
|
||||
[Command("good", RunMode = RunMode.Async)]
|
||||
[Summary("Increase Someones Karma")]
|
||||
public async Task Good([Summary("@someone")] IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var actor = await GetUser(Context.User.Id);
|
||||
if (user.Id == Context.User.Id)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotChangeOwn", Context.User.Username));
|
||||
}
|
||||
else if (TimeoutFinished(actor.TimeOut))
|
||||
{
|
||||
var formatedWaitTime = transContext.FormatDateTimeAsRemaining(actor.TimeOut.AddMinutes(3));
|
||||
await ReplyAsync(transContext.GetString("WaitUntill", Context.User.Username, formatedWaitTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = await GetUser(user.Id);
|
||||
target.Karma += 1;
|
||||
SetUser(target);
|
||||
|
||||
actor.TimeOut = DateTimeOffset.Now;
|
||||
SetUser(actor);
|
||||
|
||||
await _database.SaveChangesAsync();
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithAuthor(new EmbedAuthorBuilder()
|
||||
.WithIconUrl(user.GetAvatarUrl())
|
||||
.WithName(user.Username));
|
||||
|
||||
eb.WithColor(new Color(138, 219, 146));
|
||||
eb.Title = transContext.GetString("Increased");
|
||||
eb.AddInlineField(transContext.GetString("By"), Context.User.Username);
|
||||
eb.AddInlineField(transContext.GetString("Amount"), "+1");
|
||||
eb.AddInlineField(transContext.GetString("Current"), target.Karma);
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("bad", RunMode = RunMode.Async)]
|
||||
[Summary("Decrease Someones Karma")]
|
||||
public async Task Bad([Summary("@someone")] IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var actor = await GetUser(Context.User.Id);
|
||||
if (user.Id == Context.User.Id)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotChangeOwn", Context.User.Username));
|
||||
}
|
||||
else if (TimeoutFinished(actor.TimeOut))
|
||||
{
|
||||
var formatedWaitTime = transContext.FormatDateTimeAsRemaining(actor.TimeOut.AddMinutes(3));
|
||||
await ReplyAsync(transContext.GetString("WaitUntill", Context.User.Username, formatedWaitTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = await GetUser(user.Id);
|
||||
target.Karma -= 1;
|
||||
SetUser(target);
|
||||
|
||||
actor.TimeOut = DateTimeOffset.Now;
|
||||
SetUser(actor);
|
||||
|
||||
await _database.SaveChangesAsync();
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithAuthor(new EmbedAuthorBuilder()
|
||||
.WithIconUrl(user.GetAvatarUrl())
|
||||
.WithName(user.Username));
|
||||
|
||||
eb.WithColor(new Color(138, 219, 146));
|
||||
eb.Title = transContext.GetString("Decreased");
|
||||
eb.AddInlineField(transContext.GetString("By"), Context.User.Username);
|
||||
eb.AddInlineField(transContext.GetString("Amount"), "-1");
|
||||
eb.AddInlineField(transContext.GetString("Current"), target.Karma);
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TimeoutFinished(DateTimeOffset lastKarma)
|
||||
{
|
||||
return lastKarma.AddMinutes(3) > DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
private async Task<KarmaModel> GetUser(ulong userId)
|
||||
{
|
||||
var user = _database.Karma.FirstOrDefault(u =>u.GuildId.Equals(Context.Guild.Id.AsLong()) && u.UserId.Equals(userId.AsLong())) ?? await CreateNewRow(userId);
|
||||
return user;
|
||||
}
|
||||
|
||||
private void SetUser(KarmaModel user)
|
||||
{
|
||||
_database.Karma.Update(user);
|
||||
}
|
||||
|
||||
private async Task<KarmaModel> CreateNewRow(ulong userId)
|
||||
{
|
||||
var user = new KarmaModel()
|
||||
{
|
||||
GuildId = Context.Guild.Id.AsLong(),
|
||||
UserId = userId.AsLong(),
|
||||
Karma = 0,
|
||||
TimeOut = DateTimeOffset.MinValue
|
||||
};
|
||||
var newUser = _database.Karma.Add(user).Entity;
|
||||
await _database.SaveChangesAsync();
|
||||
return newUser;
|
||||
}
|
||||
}
|
||||
}
|
116
src/Bot/Commands/User/Ranking/Rank.cs
Normal file
116
src/Bot/Commands/User/Ranking/Rank.cs
Normal file
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Converters;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Highscores;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.UserRepository;
|
||||
|
||||
namespace Geekbot.Bot.Commands.User.Ranking
|
||||
{
|
||||
public class Rank : ModuleBase
|
||||
{
|
||||
private readonly IEmojiConverter _emojiConverter;
|
||||
private readonly IHighscoreManager _highscoreManager;
|
||||
private readonly ITranslationHandler _translationHandler;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public Rank(DatabaseContext database, IErrorHandler errorHandler, IUserRepository userRepository,
|
||||
IEmojiConverter emojiConverter, IHighscoreManager highscoreManager, ITranslationHandler translationHandler)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_userRepository = userRepository;
|
||||
_emojiConverter = emojiConverter;
|
||||
_highscoreManager = highscoreManager;
|
||||
_translationHandler = translationHandler;
|
||||
}
|
||||
|
||||
[Command("rank", RunMode = RunMode.Async)]
|
||||
[Summary("get user top 10 in messages or karma")]
|
||||
[DisableInDirectMessage]
|
||||
public async Task RankCmd([Summary("type")] string typeUnformated = "messages", [Summary("amount")] int amount = 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
HighscoreTypes type;
|
||||
try
|
||||
{
|
||||
type = Enum.Parse<HighscoreTypes>(typeUnformated, true);
|
||||
if (!Enum.IsDefined(typeof(HighscoreTypes), type)) throw new Exception();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("InvalidType"));
|
||||
return;
|
||||
}
|
||||
|
||||
var replyBuilder = new StringBuilder();
|
||||
if (amount > 20)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("LimitingTo20Warning"));
|
||||
amount = 20;
|
||||
}
|
||||
|
||||
var guildId = Context.Guild.Id;
|
||||
Dictionary<HighscoreUserDto, int> highscoreUsers;
|
||||
try
|
||||
{
|
||||
highscoreUsers = _highscoreManager.GetHighscoresWithUserData(type, guildId, amount);
|
||||
}
|
||||
catch (HighscoreListEmptyException)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("NoTypeFoundForServer", type));
|
||||
return;
|
||||
}
|
||||
|
||||
var guildMessages = 0;
|
||||
if (type == HighscoreTypes.messages)
|
||||
{
|
||||
guildMessages = _database.Messages
|
||||
.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
.Select(e => e.MessageCount)
|
||||
.Sum();
|
||||
}
|
||||
|
||||
var failedToRetrieveUser = highscoreUsers.Any(e => string.IsNullOrEmpty(e.Key.Username));
|
||||
|
||||
if (failedToRetrieveUser) replyBuilder.AppendLine(transContext.GetString("FailedToResolveAllUsernames"));
|
||||
replyBuilder.AppendLine(transContext.GetString("HighscoresFor", type.ToString().CapitalizeFirst(), Context.Guild.Name));
|
||||
var highscorePlace = 1;
|
||||
foreach (var user in highscoreUsers)
|
||||
{
|
||||
replyBuilder.Append(highscorePlace < 11
|
||||
? $"{_emojiConverter.NumberToEmoji(highscorePlace)} "
|
||||
: $"`{highscorePlace}.` ");
|
||||
|
||||
replyBuilder.Append(user.Key.Username != null
|
||||
? $"**{user.Key.Username}#{user.Key.Discriminator}**"
|
||||
: $"**{user.Key.Id}**");
|
||||
|
||||
replyBuilder.Append(type == HighscoreTypes.messages
|
||||
? $" - {user.Value} {type} - {Math.Round((double) (100 * user.Value) / guildMessages, 2)}%\n"
|
||||
: $" - {user.Value} {type}\n");
|
||||
|
||||
highscorePlace++;
|
||||
}
|
||||
|
||||
await ReplyAsync(replyBuilder.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
src/Bot/Commands/User/Stats.cs
Normal file
90
src/Bot/Commands/User/Stats.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Levels;
|
||||
|
||||
namespace Geekbot.Bot.Commands.User
|
||||
{
|
||||
public class Stats : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly ILevelCalc _levelCalc;
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
public Stats(DatabaseContext database, IErrorHandler errorHandler, ILevelCalc levelCalc)
|
||||
{
|
||||
_database = database;
|
||||
_errorHandler = errorHandler;
|
||||
_levelCalc = levelCalc;
|
||||
}
|
||||
|
||||
[Command("stats", RunMode = RunMode.Async)]
|
||||
[Summary("Get information about this user")]
|
||||
[DisableInDirectMessage]
|
||||
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 = _database.Messages
|
||||
?.FirstOrDefault(e => e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.UserId.Equals(userInfo.Id.AsLong()))
|
||||
?.MessageCount ?? 0;
|
||||
var guildMessages = _database.Messages
|
||||
?.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong()))
|
||||
.Select(e => e.MessageCount)
|
||||
.Sum() ?? 0;
|
||||
|
||||
var level = _levelCalc.GetLevel(messages);
|
||||
|
||||
var percent = Math.Round((double) (100 * messages) / guildMessages, 2);
|
||||
|
||||
var cookies = _database.Cookies
|
||||
?.FirstOrDefault(e => e.GuildId.Equals(Context.Guild.Id.AsLong()) && e.UserId.Equals(userInfo.Id.AsLong()))
|
||||
?.Cookies ?? 0;
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithAuthor(new EmbedAuthorBuilder()
|
||||
.WithIconUrl(userInfo.GetAvatarUrl())
|
||||
.WithName(userInfo.Username));
|
||||
eb.WithColor(new Color(221, 255, 119));
|
||||
|
||||
var karma = _database.Karma.FirstOrDefault(e =>
|
||||
e.GuildId.Equals(Context.Guild.Id.AsLong()) &&
|
||||
e.UserId.Equals(userInfo.Id.AsLong()));
|
||||
var correctRolls = _database.Rolls.FirstOrDefault(e =>
|
||||
e.GuildId.Equals(Context.Guild.Id.AsLong()) &&
|
||||
e.UserId.Equals(userInfo.Id.AsLong()));
|
||||
|
||||
eb.AddInlineField("Discordian Since",
|
||||
$"{createdAt.Day}.{createdAt.Month}.{createdAt.Year} ({age} days)")
|
||||
.AddInlineField("Joined Server",
|
||||
$"{joinedAt.Day}.{joinedAt.Month}.{joinedAt.Year} ({joinedDayAgo} days)")
|
||||
.AddInlineField("Karma", karma?.Karma ?? 0)
|
||||
.AddInlineField("Level", level)
|
||||
.AddInlineField("Messages Sent", messages)
|
||||
.AddInlineField("Server Total", $"{percent}%");
|
||||
|
||||
if (correctRolls != null) eb.AddInlineField("Guessed Rolls", correctRolls.Rolls);
|
||||
if (cookies > 0) eb.AddInlineField("Cookies", cookies);
|
||||
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
src/Bot/Commands/Utils/AvatarGetter.cs
Normal file
34
src/Bot/Commands/Utils/AvatarGetter.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class AvatarGetter : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public AvatarGetter(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("avatar", RunMode = RunMode.Async)]
|
||||
[Summary("Get someones avatar")]
|
||||
public async Task GetAvatar([Remainder, Summary("@someone")] IUser user = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (user == null) user = Context.User;
|
||||
var url = user.GetAvatarUrl().Replace("128", "1024");
|
||||
await ReplyAsync(url);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
src/Bot/Commands/Utils/Changelog/Changelog.cs
Normal file
57
src/Bot/Commands/Utils/Changelog/Changelog.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils.Changelog
|
||||
{
|
||||
public class Changelog : ModuleBase
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Changelog(IErrorHandler errorHandler, DiscordSocketClient client)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
[Command("changelog", RunMode = RunMode.Async)]
|
||||
[Summary("Show the latest 10 updates")]
|
||||
public async Task GetChangelog()
|
||||
{
|
||||
try
|
||||
{
|
||||
var commits = await HttpAbstractions.Get<List<CommitDto>>(new Uri("https://api.github.com/repos/pizzaandcoffee/geekbot.net/commits"));
|
||||
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithColor(new Color(143, 165, 102));
|
||||
eb.WithAuthor(new EmbedAuthorBuilder
|
||||
{
|
||||
IconUrl = _client.CurrentUser.GetAvatarUrl(),
|
||||
Name = "Latest Updates",
|
||||
Url = "https://geekbot.pizzaandcoffee.rocks/updates"
|
||||
});
|
||||
var sb = new StringBuilder();
|
||||
foreach (var commit in commits.Take(10))
|
||||
sb.AppendLine($"- {commit.Commit.Message} ({commit.Commit.Author.Date:yyyy-MM-dd})");
|
||||
eb.Description = sb.ToString();
|
||||
eb.WithFooter(new EmbedFooterBuilder
|
||||
{
|
||||
Text = $"List generated from github commits on {DateTime.Now:yyyy-MM-dd}"
|
||||
});
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
src/Bot/Commands/Utils/Changelog/CommitAuthorDto.cs
Normal file
11
src/Bot/Commands/Utils/Changelog/CommitAuthorDto.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils.Changelog
|
||||
{
|
||||
public class CommitAuthorDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public DateTimeOffset Date { get; set; }
|
||||
}
|
||||
}
|
7
src/Bot/Commands/Utils/Changelog/CommitDto.cs
Normal file
7
src/Bot/Commands/Utils/Changelog/CommitDto.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Geekbot.Bot.Commands.Utils.Changelog
|
||||
{
|
||||
public class CommitDto
|
||||
{
|
||||
public CommitInfoDto Commit { get; set; }
|
||||
}
|
||||
}
|
8
src/Bot/Commands/Utils/Changelog/CommitInfoDto.cs
Normal file
8
src/Bot/Commands/Utils/Changelog/CommitInfoDto.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Geekbot.Bot.Commands.Utils.Changelog
|
||||
{
|
||||
public class CommitInfoDto
|
||||
{
|
||||
public CommitAuthorDto Author { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
38
src/Bot/Commands/Utils/Choose.cs
Normal file
38
src/Bot/Commands/Utils/Choose.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Localization;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Choose : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly ITranslationHandler _translation;
|
||||
|
||||
public Choose(IErrorHandler errorHandler, ITranslationHandler translation)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_translation = translation;
|
||||
}
|
||||
|
||||
[Command("choose", RunMode = RunMode.Async)]
|
||||
[Summary("Let the bot choose for you, seperate options with a semicolon.")]
|
||||
public async Task Command([Remainder] [Summary("option1;option2")]
|
||||
string choices)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translation.GetGuildContext(Context);
|
||||
var choicesArray = choices.Split(';');
|
||||
var choice = new Random().Next(choicesArray.Length);
|
||||
await ReplyAsync(transContext.GetString("Choice", choicesArray[choice].Trim()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
66
src/Bot/Commands/Utils/Corona/CoronaStats.cs
Normal file
66
src/Bot/Commands/Utils/Corona/CoronaStats.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils.Corona
|
||||
{
|
||||
public class CoronaStats : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public CoronaStats(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("corona", RunMode = RunMode.Async)]
|
||||
[Summary("Get the latest worldwide corona statistics")]
|
||||
public async Task Summary()
|
||||
{
|
||||
try
|
||||
{
|
||||
var summary = await HttpAbstractions.Get<CoronaSummaryDto>(new Uri("https://api.covid19api.com/world/total"));
|
||||
var activeCases = summary.TotalConfirmed - (summary.TotalRecovered + summary.TotalDeaths);
|
||||
|
||||
string CalculatePercentage(decimal i) => (i / summary.TotalConfirmed).ToString("#0.##%");
|
||||
var activePercent = CalculatePercentage(activeCases);
|
||||
var recoveredPercentage = CalculatePercentage(summary.TotalRecovered);
|
||||
var deathsPercentage = CalculatePercentage(summary.TotalDeaths);
|
||||
|
||||
var numberFormat = "#,#";
|
||||
var totalFormatted = summary.TotalConfirmed.ToString(numberFormat);
|
||||
var activeFormatted = activeCases.ToString(numberFormat);
|
||||
var recoveredFormatted = summary.TotalRecovered.ToString(numberFormat);
|
||||
var deathsFormatted = summary.TotalDeaths.ToString(numberFormat);
|
||||
|
||||
var eb = new EmbedBuilder
|
||||
{
|
||||
Author = new EmbedAuthorBuilder
|
||||
{
|
||||
Name = "Confirmed Corona Cases",
|
||||
IconUrl = "https://www.redcross.org/content/dam/icons/disasters/virus/Virus-1000x1000-R-Pl.png"
|
||||
},
|
||||
Footer = new EmbedFooterBuilder
|
||||
{
|
||||
Text = "Source: covid19api.com",
|
||||
},
|
||||
Color = Color.Red
|
||||
};
|
||||
eb.AddField("Total", totalFormatted);
|
||||
eb.AddInlineField("Active", $"{activeFormatted} ({activePercent})");
|
||||
eb.AddInlineField("Recovered", $"{recoveredFormatted} ({recoveredPercentage})");
|
||||
eb.AddInlineField("Deaths", $"{deathsFormatted} ({deathsPercentage})");
|
||||
|
||||
await Context.Channel.SendMessageAsync(String.Empty, false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/Bot/Commands/Utils/Corona/CoronaSummaryDto.cs
Normal file
9
src/Bot/Commands/Utils/Corona/CoronaSummaryDto.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Geekbot.Bot.Commands.Utils.Corona
|
||||
{
|
||||
public class CoronaSummaryDto
|
||||
{
|
||||
public decimal TotalConfirmed { get; set; }
|
||||
public decimal TotalDeaths { get; set; }
|
||||
public decimal TotalRecovered { get; set; }
|
||||
}
|
||||
}
|
107
src/Bot/Commands/Utils/Dice.cs
Normal file
107
src/Bot/Commands/Utils/Dice.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.DiceParser;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Dice : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly IDiceParser _diceParser;
|
||||
|
||||
public Dice(IErrorHandler errorHandler, IDiceParser diceParser)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_diceParser = diceParser;
|
||||
}
|
||||
|
||||
// ToDo: Separate representation and logic
|
||||
// ToDo: Translate
|
||||
[Command("dice", RunMode = RunMode.Async)]
|
||||
[Summary("Roll a dice. (use '!dice help' for a manual)")]
|
||||
public async Task RollCommand([Remainder] [Summary("input")] string diceInput = "1d20")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (diceInput == "help")
|
||||
{
|
||||
await ShowDiceHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
var parsed = _diceParser.Parse(diceInput);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"{Context.User.Mention} :game_die:");
|
||||
foreach (var die in parsed.Dice)
|
||||
{
|
||||
sb.AppendLine($"**{die.DiceName}**");
|
||||
var diceResultList = new List<string>();
|
||||
var total = 0;
|
||||
|
||||
foreach (var roll in die.Roll())
|
||||
{
|
||||
diceResultList.Add(roll.ToString());
|
||||
total += roll.Result;
|
||||
}
|
||||
|
||||
sb.AppendLine(string.Join(" | ", diceResultList));
|
||||
|
||||
if (parsed.SkillModifier != 0)
|
||||
{
|
||||
sb.AppendLine($"Skill: {parsed.SkillModifier}");
|
||||
}
|
||||
|
||||
if (parsed.Options.ShowTotal)
|
||||
{
|
||||
var totalLine = $"Total: {total}";
|
||||
if (parsed.SkillModifier > 0)
|
||||
{
|
||||
totalLine += ($" (+{parsed.SkillModifier} = {total + parsed.SkillModifier})");
|
||||
}
|
||||
|
||||
if (parsed.SkillModifier < 0)
|
||||
{
|
||||
totalLine += ($" ({parsed.SkillModifier} = {total - parsed.SkillModifier})");
|
||||
}
|
||||
|
||||
sb.AppendLine(totalLine);
|
||||
}
|
||||
}
|
||||
|
||||
await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
}
|
||||
catch (DiceException e)
|
||||
{
|
||||
await Context.Channel.SendMessageAsync($"**:warning: {e.DiceName} is invalid:** {e.Message}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowDiceHelp()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("**__Examples__**");
|
||||
sb.AppendLine("```");
|
||||
sb.AppendLine("!dice - throw a 1d20");
|
||||
sb.AppendLine("!dice 1d12 - throw a 1d12");
|
||||
sb.AppendLine("!dice +1d20 - throw with advantage");
|
||||
sb.AppendLine("!dice -1d20 - throw with disadvantage");
|
||||
sb.AppendLine("!dice 1d20 +2 - throw with a +2 skill bonus");
|
||||
sb.AppendLine("!dice 1d20 -2 - throw with a -2 skill bonus");
|
||||
sb.AppendLine("!dice 8d6 - throw a fireball 🔥");
|
||||
sb.AppendLine("!dice 8d6 total - calculate the total");
|
||||
sb.AppendLine("!dice 2d20 6d6 2d12 - drop your dice pouch");
|
||||
sb.AppendLine("```");
|
||||
|
||||
await Context.Channel.SendMessageAsync(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
42
src/Bot/Commands/Utils/Emojify.cs
Normal file
42
src/Bot/Commands/Utils/Emojify.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.Converters;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Emojify : ModuleBase
|
||||
{
|
||||
private readonly IEmojiConverter _emojiConverter;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Emojify(IErrorHandler errorHandler, IEmojiConverter emojiConverter)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_emojiConverter = emojiConverter;
|
||||
}
|
||||
|
||||
[Command("emojify", RunMode = RunMode.Async)]
|
||||
[Summary("Emojify text")]
|
||||
public async Task Dflt([Remainder] [Summary("text")] string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
var emojis = _emojiConverter.TextToEmoji(text);
|
||||
if (emojis.Length > 1999)
|
||||
{
|
||||
await ReplyAsync("I can't take that much at once!");
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyAsync($"{Context.User.Username}#{Context.User.Discriminator} said:");
|
||||
await ReplyAsync(emojis);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
src/Bot/Commands/Utils/Help.cs
Normal file
39
src/Bot/Commands/Utils/Help.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Help : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Help(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("help", RunMode = RunMode.Async)]
|
||||
[Summary("List all Commands")]
|
||||
public async Task GetHelp()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
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.GetOrCreateDMChannelAsync();
|
||||
await dm.SendMessageAsync(sb.ToString());
|
||||
await Context.Message.AddReactionAsync(new Emoji("✅"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
73
src/Bot/Commands/Utils/Info.cs
Normal file
73
src/Bot/Commands/Utils/Info.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Info : ModuleBase
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly CommandService _commands;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Info(IErrorHandler errorHandler, DiscordSocketClient client, CommandService commands)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_client = client;
|
||||
_commands = commands;
|
||||
}
|
||||
|
||||
[Command("info", RunMode = RunMode.Async)]
|
||||
[Summary("Get Information about the bot")]
|
||||
public async Task BotInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var eb = new EmbedBuilder();
|
||||
|
||||
var appInfo = await _client.GetApplicationInfoAsync();
|
||||
|
||||
eb.WithAuthor(new EmbedAuthorBuilder()
|
||||
.WithIconUrl(appInfo.IconUrl)
|
||||
.WithName($"{Constants.Name} V{Constants.BotVersion()}"));
|
||||
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("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);
|
||||
}
|
||||
}
|
||||
|
||||
[Command("uptime", RunMode = RunMode.Async)]
|
||||
[Summary("Get the Bot Uptime")]
|
||||
public async Task BotUptime()
|
||||
{
|
||||
try
|
||||
{
|
||||
var uptime = DateTime.Now.Subtract(Process.GetCurrentProcess().StartTime);
|
||||
await ReplyAsync($"{uptime.Days}D {uptime.Hours}H {uptime.Minutes}M {uptime.Seconds}S");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
src/Bot/Commands/Utils/Lmgtfy.cs
Normal file
33
src/Bot/Commands/Utils/Lmgtfy.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Lmgtfy : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public Lmgtfy(IErrorHandler errorHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
[Command("lmgtfy", RunMode = RunMode.Async)]
|
||||
[Summary("Get a 'Let me google that for you' link")]
|
||||
public async Task GetUrl([Remainder] [Summary("question")] string question)
|
||||
{
|
||||
try
|
||||
{
|
||||
var encoded = HttpUtility.UrlEncode(question).Replace("%20", "+");
|
||||
await Context.Channel.SendMessageAsync($"<https://lmgtfy.com/?q={encoded}>");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/Bot/Commands/Utils/Ping.cs
Normal file
15
src/Bot/Commands/Utils/Ping.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.Threading.Tasks;
|
||||
using Discord.Commands;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils
|
||||
{
|
||||
public class Ping : ModuleBase
|
||||
{
|
||||
[Command("👀", RunMode = RunMode.Async)]
|
||||
[Summary("Look at the bot.")]
|
||||
public async Task Eyes()
|
||||
{
|
||||
await ReplyAsync("S... Stop looking at me... baka!");
|
||||
}
|
||||
}
|
||||
}
|
313
src/Bot/Commands/Utils/Quote/Quote.cs
Normal file
313
src/Bot/Commands/Utils/Quote/Quote.cs
Normal file
|
@ -0,0 +1,313 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Geekbot.Core.CommandPreconditions;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.Polyfills;
|
||||
using Geekbot.Core.RandomNumberGenerator;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils.Quote
|
||||
{
|
||||
[Group("quote")]
|
||||
[DisableInDirectMessage]
|
||||
public class Quote : ModuleBase
|
||||
{
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IRandomNumberGenerator _randomNumberGenerator;
|
||||
private readonly ITranslationHandler _translationHandler;
|
||||
|
||||
public Quote(IErrorHandler errorHandler, DatabaseContext database, IRandomNumberGenerator randomNumberGenerator, ITranslationHandler translationHandler)
|
||||
{
|
||||
_errorHandler = errorHandler;
|
||||
_database = database;
|
||||
_randomNumberGenerator = randomNumberGenerator;
|
||||
_translationHandler = translationHandler;
|
||||
}
|
||||
|
||||
[Command]
|
||||
[Summary("Return a random quoute from the database")]
|
||||
public async Task GetRandomQuote()
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = _database.Quotes.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong())).ToList();
|
||||
|
||||
if (!s.Any())
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
await ReplyAsync(transContext.GetString("NoQuotesFound"));
|
||||
return;
|
||||
}
|
||||
|
||||
var random = _randomNumberGenerator.Next(0, s.Count);
|
||||
var quote = s[random];
|
||||
|
||||
var embed = QuoteBuilder(quote);
|
||||
await ReplyAsync("", false, embed.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context, "Whoops, seems like the quote was to edgy to return");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("save")]
|
||||
[Summary("Save a quote from the last sent message by @user")]
|
||||
public async Task SaveQuote([Summary("@someone")] IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
if (user.Id == Context.Message.Author.Id)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotSaveOwnQuotes"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.IsBot)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotQuoteBots"));
|
||||
return;
|
||||
}
|
||||
|
||||
var lastMessage = await GetLastMessageByUser(user);
|
||||
if (lastMessage == null) return;
|
||||
|
||||
var quote = CreateQuoteObject(lastMessage);
|
||||
_database.Quotes.Add(quote);
|
||||
await _database.SaveChangesAsync();
|
||||
|
||||
var embed = QuoteBuilder(quote);
|
||||
await ReplyAsync(transContext.GetString("QuoteAdded"), false, embed.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context,
|
||||
"I counldn't find a quote from that user :disappointed:");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("save")]
|
||||
[Summary("Save a quote from a message id")]
|
||||
public async Task SaveQuote([Summary("message-ID")] ulong messageId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
var message = await Context.Channel.GetMessageAsync(messageId);
|
||||
if (message.Author.Id == Context.Message.Author.Id)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotSaveOwnQuotes"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Author.IsBot)
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("CannotQuoteBots"));
|
||||
return;
|
||||
}
|
||||
|
||||
var quote = CreateQuoteObject(message);
|
||||
_database.Quotes.Add(quote);
|
||||
await _database.SaveChangesAsync();
|
||||
|
||||
var embed = QuoteBuilder(quote);
|
||||
await ReplyAsync(transContext.GetString("QuoteAdded"), false, embed.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context,
|
||||
"I couldn't find a message with that id :disappointed:");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("make")]
|
||||
[Summary("Create a quote from the last sent message by @user")]
|
||||
public async Task ReturnSpecifiedQuote([Summary("@someone")] IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lastMessage = await GetLastMessageByUser(user);
|
||||
if (lastMessage == null) return;
|
||||
var quote = CreateQuoteObject(lastMessage);
|
||||
var embed = QuoteBuilder(quote);
|
||||
await ReplyAsync("", false, embed.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context,
|
||||
"I counldn't find a quote from that user :disappointed:");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("make")]
|
||||
[Summary("Create a quote from a message id")]
|
||||
public async Task ReturnSpecifiedQuote([Summary("message-ID")] ulong messageId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var message = await Context.Channel.GetMessageAsync(messageId);
|
||||
var quote = CreateQuoteObject(message);
|
||||
var embed = QuoteBuilder(quote);
|
||||
await ReplyAsync("", false, embed.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context,
|
||||
"I couldn't find a message with that id :disappointed:");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("remove")]
|
||||
[RequireUserPermission(GuildPermission.ManageMessages)]
|
||||
[Summary("Remove a quote (user needs the 'ManageMessages' permission)")]
|
||||
public async Task RemoveQuote([Summary("quote-ID")] int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
var quote = _database.Quotes.Where(e => e.GuildId == Context.Guild.Id.AsLong() && e.InternalId == id)?.FirstOrDefault();
|
||||
if (quote != null)
|
||||
{
|
||||
_database.Quotes.Remove(quote);
|
||||
await _database.SaveChangesAsync();
|
||||
var embed = QuoteBuilder(quote);
|
||||
await ReplyAsync(transContext.GetString("Removed", id), false, embed.Build());
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyAsync(transContext.GetString("NotFoundWithId"));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context, "I couldn't find a quote with that id :disappointed:");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("stats")]
|
||||
[Summary("Show quote stats for this server")]
|
||||
public async Task GetQuoteStatsForServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
// setup
|
||||
var transContext = await _translationHandler.GetGuildContext(Context);
|
||||
var eb = new EmbedBuilder();
|
||||
eb.Author = new EmbedAuthorBuilder()
|
||||
{
|
||||
IconUrl = Context.Guild.IconUrl,
|
||||
Name = $"{Context.Guild.Name} - {transContext.GetString("QuoteStats")}"
|
||||
};
|
||||
|
||||
// gather data
|
||||
var totalQuotes = _database.Quotes.Count(row => row.GuildId == Context.Guild.Id.AsLong());
|
||||
if (totalQuotes == 0)
|
||||
{
|
||||
// no quotes, no stats, end of the road
|
||||
await ReplyAsync(transContext.GetString("NoQuotesFound"));
|
||||
return;
|
||||
}
|
||||
|
||||
var mostQuotedPerson = _database.Quotes
|
||||
.Where(row => row.GuildId == Context.Guild.Id.AsLong())
|
||||
.GroupBy(row => row.UserId)
|
||||
.Select(row => new { userId = row.Key, amount = row.Count()})
|
||||
.OrderBy(row => row.amount)
|
||||
.Last();
|
||||
var mostQuotedPersonUser = Context.Client.GetUserAsync(mostQuotedPerson.userId.AsUlong()).Result ?? new UserPolyfillDto {Username = "Unknown User"};
|
||||
|
||||
var quotesByYear = _database.Quotes
|
||||
.Where(row => row.GuildId == Context.Guild.Id.AsLong())
|
||||
.GroupBy(row => row.Time.Year)
|
||||
.Select(row => new { year = row.Key, amount = row.Count()})
|
||||
.OrderBy(row => row.year);
|
||||
|
||||
// add data to the embed
|
||||
eb.AddField(transContext.GetString("MostQuotesPerson"), $"{mostQuotedPersonUser.Username} ({mostQuotedPerson.amount})");
|
||||
eb.AddInlineField(transContext.GetString("TotalQuotes"), totalQuotes);
|
||||
|
||||
foreach (var year in quotesByYear)
|
||||
{
|
||||
eb.AddInlineField(year.year.ToString(), year.amount);
|
||||
}
|
||||
|
||||
await ReplyAsync("", false, eb.Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _errorHandler.HandleCommandException(e, Context);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IMessage> GetLastMessageByUser(IUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var list = Context.Channel.GetMessagesAsync().Flatten();
|
||||
return await list.FirstOrDefaultAsync(msg =>
|
||||
msg.Author.Id == user.Id &&
|
||||
msg.Embeds.Count == 0 &&
|
||||
msg.Id != Context.Message.Id &&
|
||||
!msg.Content.ToLower().StartsWith("!"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
await ReplyAsync($"No quoteable message have been sent by {user.Username} in this channel");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private EmbedBuilder QuoteBuilder(QuoteModel quote)
|
||||
{
|
||||
var user = Context.Client.GetUserAsync(quote.UserId.AsUlong()).Result ?? new UserPolyfillDto { Username = "Unknown User" };
|
||||
var eb = new EmbedBuilder();
|
||||
eb.WithColor(new Color(143, 167, 232));
|
||||
if (quote.InternalId == 0)
|
||||
{
|
||||
eb.Title = $"{user.Username} @ {quote.Time.Day}.{quote.Time.Month}.{quote.Time.Year}";
|
||||
}
|
||||
else
|
||||
{
|
||||
eb.Title = $"#{quote.InternalId} | {user.Username} @ {quote.Time.Day}.{quote.Time.Month}.{quote.Time.Year}";
|
||||
}
|
||||
eb.Description = quote.Quote;
|
||||
eb.ThumbnailUrl = user.GetAvatarUrl();
|
||||
if (quote.Image != null) eb.ImageUrl = quote.Image;
|
||||
return eb;
|
||||
}
|
||||
|
||||
private QuoteModel CreateQuoteObject(IMessage message)
|
||||
{
|
||||
string image;
|
||||
try
|
||||
{
|
||||
image = message.Attachments.First().Url;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
image = null;
|
||||
}
|
||||
|
||||
var last = _database.Quotes.Where(e => e.GuildId.Equals(Context.Guild.Id.AsLong())).OrderByDescending(e => e.InternalId).FirstOrDefault();
|
||||
var internalId = 0;
|
||||
if (last != null) internalId = last.InternalId + 1;
|
||||
return new QuoteModel()
|
||||
{
|
||||
InternalId = internalId,
|
||||
GuildId = Context.Guild.Id.AsLong(),
|
||||
UserId = message.Author.Id.AsLong(),
|
||||
Time = message.Timestamp.DateTime,
|
||||
Quote = message.Content,
|
||||
Image = image
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
12
src/Bot/Commands/Utils/Quote/QuoteObjectDto.cs
Normal file
12
src/Bot/Commands/Utils/Quote/QuoteObjectDto.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace Geekbot.Bot.Commands.Utils.Quote
|
||||
{
|
||||
internal class QuoteObjectDto
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public string Quote { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
public string Image { get; set; }
|
||||
}
|
||||
}
|
126
src/Bot/Handlers/CommandHandler.cs
Normal file
126
src/Bot/Handlers/CommandHandler.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Rest;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.GuildSettingsManager;
|
||||
using Geekbot.Core.Logger;
|
||||
|
||||
namespace Geekbot.Bot.Handlers
|
||||
{
|
||||
public class CommandHandler
|
||||
{
|
||||
private readonly IDiscordClient _client;
|
||||
private readonly IGeekbotLogger _logger;
|
||||
private readonly IServiceProvider _servicesProvider;
|
||||
private readonly CommandService _commands;
|
||||
private readonly RestApplication _applicationInfo;
|
||||
private readonly IGuildSettingsManager _guildSettingsManager;
|
||||
private readonly List<ulong> _ignoredServers;
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
public CommandHandler(DatabaseContext database, IDiscordClient client, IGeekbotLogger logger, IServiceProvider servicesProvider, CommandService commands, RestApplication applicationInfo,
|
||||
IGuildSettingsManager guildSettingsManager)
|
||||
{
|
||||
_database = database;
|
||||
_client = client;
|
||||
_logger = logger;
|
||||
_servicesProvider = servicesProvider;
|
||||
_commands = commands;
|
||||
_applicationInfo = applicationInfo;
|
||||
_guildSettingsManager = guildSettingsManager;
|
||||
|
||||
// Some guilds only want very specific functionally without any of the commands, a quick hack that solves that "short term"
|
||||
// ToDo: create a clean solution for this...
|
||||
_ignoredServers = new List<ulong>
|
||||
{
|
||||
228623803201224704, // SwitzerLAN
|
||||
169844523181015040, // EEvent
|
||||
248531441548263425, // MYI
|
||||
110373943822540800 // Discord Bots
|
||||
};
|
||||
}
|
||||
|
||||
public Task RunCommand(SocketMessage messageParam)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!(messageParam is SocketUserMessage message)) return Task.CompletedTask;
|
||||
if (message.Author.IsBot) return Task.CompletedTask;
|
||||
|
||||
ulong guildId = message.Author switch
|
||||
{
|
||||
SocketGuildUser user => user.Guild.Id,
|
||||
_ => 0 // DM Channel
|
||||
};
|
||||
|
||||
if (IsIgnoredGuild(guildId, message.Author.Id)) return Task.CompletedTask;
|
||||
|
||||
var lowCaseMsg = message.ToString().ToLower();
|
||||
if (ShouldHui(lowCaseMsg, guildId))
|
||||
{
|
||||
message.Channel.SendMessageAsync("hui!!!");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (ShouldPong(lowCaseMsg, guildId))
|
||||
{
|
||||
message.Channel.SendMessageAsync("pong");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var argPos = 0;
|
||||
if (!IsCommand(message, ref argPos)) return Task.CompletedTask;
|
||||
|
||||
ExecuteCommand(message, argPos);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(LogSource.Geekbot, "Failed to Process Message", e);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ExecuteCommand(IUserMessage message, int argPos)
|
||||
{
|
||||
var context = new CommandContext(_client, message);
|
||||
_commands.ExecuteAsync(context, argPos, _servicesProvider);
|
||||
_logger.Information(LogSource.Command, context.Message.Content.Split(" ")[0].Replace("!", ""), SimpleConextConverter.ConvertContext(context));
|
||||
}
|
||||
|
||||
private bool IsIgnoredGuild(ulong guildId, ulong authorId)
|
||||
{
|
||||
if (!_ignoredServers.Contains(guildId)) return false;
|
||||
return authorId == _applicationInfo.Owner.Id;
|
||||
}
|
||||
|
||||
private bool IsCommand(IUserMessage message, ref int argPos)
|
||||
{
|
||||
return message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos);
|
||||
}
|
||||
|
||||
private bool ShouldPong(string lowerCaseMessage, ulong guildId)
|
||||
{
|
||||
if (!lowerCaseMessage.StartsWith("ping ") && !lowerCaseMessage.Equals("ping")) return false;
|
||||
if (guildId == 0) return true;
|
||||
return GetGuildSettings(guildId)?.Ping ?? false;
|
||||
}
|
||||
|
||||
private bool ShouldHui(string lowerCaseMessage, ulong guildId)
|
||||
{
|
||||
if (!lowerCaseMessage.StartsWith("hui")) return false;
|
||||
if (guildId == 0) return true;
|
||||
return GetGuildSettings(guildId)?.Hui ?? false;
|
||||
}
|
||||
|
||||
private GuildSettingsModel GetGuildSettings(ulong guildId)
|
||||
{
|
||||
return _guildSettingsManager.GetSettings(guildId, false);
|
||||
}
|
||||
}
|
||||
}
|
55
src/Bot/Handlers/MessageDeletedHandler.cs
Normal file
55
src/Bot/Handlers/MessageDeletedHandler.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Logger;
|
||||
|
||||
namespace Geekbot.Bot.Handlers
|
||||
{
|
||||
public class MessageDeletedHandler
|
||||
{
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IGeekbotLogger _logger;
|
||||
private readonly IDiscordClient _client;
|
||||
|
||||
public MessageDeletedHandler(DatabaseContext database, IGeekbotLogger logger, IDiscordClient client)
|
||||
{
|
||||
_database = database;
|
||||
_logger = logger;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task HandleMessageDeleted(Cacheable<IMessage, ulong> message, ISocketMessageChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var guildSocketData = ((IGuildChannel) channel).Guild;
|
||||
var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(guildSocketData.Id.AsLong()));
|
||||
if ((guild?.ShowDelete ?? false) && guild?.ModChannel != 0)
|
||||
{
|
||||
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong());
|
||||
var sb = new StringBuilder();
|
||||
if (message.Value != null)
|
||||
{
|
||||
sb.AppendLine($"The following message from {message.Value.Author.Username}#{message.Value.Author.Discriminator} was deleted in <#{channel.Id}>");
|
||||
sb.AppendLine(message.Value.Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine("Someone deleted a message, the message was not cached...");
|
||||
}
|
||||
|
||||
await modChannelSocket.SendMessageAsync(sb.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(LogSource.Geekbot, "Failed to send delete message...", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
src/Bot/Handlers/ReactionHandler.cs
Normal file
33
src/Bot/Handlers/ReactionHandler.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.ReactionListener;
|
||||
|
||||
namespace Geekbot.Bot.Handlers
|
||||
{
|
||||
public class ReactionHandler
|
||||
{
|
||||
private readonly IReactionListener _reactionListener;
|
||||
|
||||
public ReactionHandler(IReactionListener reactionListener)
|
||||
{
|
||||
_reactionListener = reactionListener;
|
||||
}
|
||||
|
||||
public Task Added(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
|
||||
{
|
||||
if (reaction.User.Value.IsBot) return Task.CompletedTask;
|
||||
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
|
||||
_reactionListener.GiveRole(socketMessageChannel, reaction);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Removed(Cacheable<IUserMessage, ulong> cacheable, ISocketMessageChannel socketMessageChannel, SocketReaction reaction)
|
||||
{
|
||||
if (reaction.User.Value.IsBot) return Task.CompletedTask;
|
||||
if (!_reactionListener.IsListener(reaction.MessageId)) return Task.CompletedTask;
|
||||
_reactionListener.RemoveRole(socketMessageChannel, reaction);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
62
src/Bot/Handlers/StatsHandler.cs
Normal file
62
src/Bot/Handlers/StatsHandler.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Database.Models;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Logger;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Geekbot.Bot.Handlers
|
||||
{
|
||||
public class StatsHandler
|
||||
{
|
||||
private readonly IGeekbotLogger _logger;
|
||||
private readonly DatabaseContext _database;
|
||||
|
||||
public StatsHandler(IGeekbotLogger logger, DatabaseContext database)
|
||||
{
|
||||
_logger = logger;
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public async Task UpdateStats(SocketMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (message == null) return;
|
||||
if (message.Channel.Name.StartsWith('@'))
|
||||
{
|
||||
_logger.Information(LogSource.Message, $"[DM-Channel] {message.Content}", SimpleConextConverter.ConvertSocketMessage(message, true));
|
||||
return;
|
||||
}
|
||||
|
||||
var channel = (SocketGuildChannel) message.Channel;
|
||||
|
||||
var rowId = await _database.Database.ExecuteSqlRawAsync(
|
||||
"UPDATE \"Messages\" SET \"MessageCount\" = \"MessageCount\" + 1 WHERE \"GuildId\" = {0} AND \"UserId\" = {1}",
|
||||
channel.Guild.Id.AsLong(),
|
||||
message.Author.Id.AsLong()
|
||||
);
|
||||
|
||||
if (rowId == 0)
|
||||
{
|
||||
await _database.Messages.AddAsync(new MessagesModel
|
||||
{
|
||||
UserId = message.Author.Id.AsLong(),
|
||||
GuildId = channel.Guild.Id.AsLong(),
|
||||
MessageCount = 1
|
||||
});
|
||||
await _database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (message.Author.IsBot) return;
|
||||
_logger.Information(LogSource.Message, message.Content, SimpleConextConverter.ConvertSocketMessage(message));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(LogSource.Message, "Could not process message stats", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
96
src/Bot/Handlers/UserHandler.cs
Normal file
96
src/Bot/Handlers/UserHandler.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using Discord.Rest;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.Extensions;
|
||||
using Geekbot.Core.Logger;
|
||||
using Geekbot.Core.UserRepository;
|
||||
|
||||
namespace Geekbot.Bot.Handlers
|
||||
{
|
||||
public class UserHandler
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IGeekbotLogger _logger;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IDiscordClient _client;
|
||||
|
||||
public UserHandler(IUserRepository userRepository, IGeekbotLogger logger, DatabaseContext database, IDiscordClient client)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_logger = logger;
|
||||
_database = database;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task Joined(SocketGuildUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userRepoUpdate = _userRepository.Update(user);
|
||||
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
|
||||
|
||||
if (!user.IsBot)
|
||||
{
|
||||
var guildSettings = _database.GuildSettings.FirstOrDefault(guild => guild.GuildId == user.Guild.Id.AsLong());
|
||||
var message = guildSettings?.WelcomeMessage;
|
||||
if (string.IsNullOrEmpty(message)) return;
|
||||
message = message.Replace("$user", user.Mention);
|
||||
|
||||
var fallbackSender = new Func<Task<RestUserMessage>>(() => user.Guild.DefaultChannel.SendMessageAsync(message));
|
||||
if (guildSettings.WelcomeChannel != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var target = await _client.GetChannelAsync(guildSettings.WelcomeChannel.AsUlong());
|
||||
var channel = target as ISocketMessageChannel;
|
||||
await channel.SendMessageAsync(message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(LogSource.Geekbot, "Failed to send welcome message to user defined welcome channel", e);
|
||||
await fallbackSender();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await fallbackSender();
|
||||
}
|
||||
}
|
||||
|
||||
await userRepoUpdate;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(LogSource.Geekbot, "Failed to send welcome message", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Updated(SocketUser oldUser, SocketUser newUser)
|
||||
{
|
||||
await _userRepository.Update(newUser);
|
||||
}
|
||||
|
||||
public async Task Left(SocketGuildUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
var guild = _database.GuildSettings.FirstOrDefault(g => g.GuildId.Equals(user.Guild.Id.AsLong()));
|
||||
if (guild?.ShowLeave ?? false)
|
||||
{
|
||||
var modChannelSocket = (ISocketMessageChannel) await _client.GetChannelAsync(guild.ModChannel.AsUlong());
|
||||
await modChannelSocket.SendMessageAsync($"{user.Username}#{user.Discriminator} left the server");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(LogSource.Geekbot, "Failed to send leave message", e);
|
||||
}
|
||||
|
||||
_logger.Information(LogSource.Geekbot, $"{user.Username} ({user.Id}) joined {user.Guild.Name} ({user.Guild.Id})");
|
||||
}
|
||||
}
|
||||
}
|
0
src/Bot/Logs/.keep
Normal file
0
src/Bot/Logs/.keep
Normal file
234
src/Bot/Program.cs
Normal file
234
src/Bot/Program.cs
Normal file
|
@ -0,0 +1,234 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CommandLine;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Geekbot.Bot.Handlers;
|
||||
using Geekbot.Core;
|
||||
using Geekbot.Core.Converters;
|
||||
using Geekbot.Core.Database;
|
||||
using Geekbot.Core.DiceParser;
|
||||
using Geekbot.Core.ErrorHandling;
|
||||
using Geekbot.Core.GlobalSettings;
|
||||
using Geekbot.Core.GuildSettingsManager;
|
||||
using Geekbot.Core.Highscores;
|
||||
using Geekbot.Core.KvInMemoryStore;
|
||||
using Geekbot.Core.Levels;
|
||||
using Geekbot.Core.Localization;
|
||||
using Geekbot.Core.Logger;
|
||||
using Geekbot.Core.MalClient;
|
||||
using Geekbot.Core.Media;
|
||||
using Geekbot.Core.RandomNumberGenerator;
|
||||
using Geekbot.Core.ReactionListener;
|
||||
using Geekbot.Core.UserRepository;
|
||||
using Geekbot.Core.WikipediaClient;
|
||||
using Geekbot.Web;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Geekbot.Bot
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private DiscordSocketClient _client;
|
||||
private CommandService _commands;
|
||||
private DatabaseInitializer _databaseInitializer;
|
||||
private IGlobalSettings _globalSettings;
|
||||
private IServiceProvider _servicesProvider;
|
||||
private GeekbotLogger _logger;
|
||||
private IUserRepository _userRepository;
|
||||
private RunParameters _runParameters;
|
||||
private IReactionListener _reactionListener;
|
||||
private IGuildSettingsManager _guildSettingsManager;
|
||||
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
RunParameters runParameters = null;
|
||||
Parser.Default.ParseArguments<RunParameters>(args)
|
||||
.WithParsed(e => runParameters = e)
|
||||
.WithNotParsed(_ => Environment.Exit(GeekbotExitCode.InvalidArguments.GetHashCode()));
|
||||
|
||||
var logo = new StringBuilder();
|
||||
logo.AppendLine(@" ____ _____ _____ _ ______ ___ _____");
|
||||
logo.AppendLine(@" / ___| ____| ____| |/ / __ ) / _ \\_ _|");
|
||||
logo.AppendLine(@"| | _| _| | _| | ' /| _ \| | | || |");
|
||||
logo.AppendLine(@"| |_| | |___| |___| . \| |_) | |_| || |");
|
||||
logo.AppendLine(@" \____|_____|_____|_|\_\____/ \___/ |_|");
|
||||
logo.AppendLine($"Version {Constants.BotVersion()} ".PadRight(41, '='));
|
||||
Console.WriteLine(logo.ToString());
|
||||
var logger = new GeekbotLogger(runParameters);
|
||||
logger.Information(LogSource.Geekbot, "Starting...");
|
||||
try
|
||||
{
|
||||
await new Program().Start(runParameters, logger);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error(LogSource.Geekbot, "RIP", e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Start(RunParameters runParameters, GeekbotLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_runParameters = runParameters;
|
||||
|
||||
logger.Information(LogSource.Geekbot, "Connecting to Database");
|
||||
var database = ConnectToDatabase();
|
||||
_globalSettings = new GlobalSettings(database);
|
||||
|
||||
logger.Information(LogSource.Geekbot, "Connecting to Discord");
|
||||
SetupDiscordClient();
|
||||
await Login();
|
||||
_logger.Information(LogSource.Geekbot, $"Now Connected as {_client.CurrentUser.Username} to {_client.Guilds.Count} Servers");
|
||||
await _client.SetGameAsync(_globalSettings.GetKey("Game"));
|
||||
|
||||
_logger.Information(LogSource.Geekbot, "Loading Dependencies and Handlers");
|
||||
RegisterDependencies();
|
||||
await RegisterHandlers();
|
||||
|
||||
_logger.Information(LogSource.Api, "Starting Web API");
|
||||
StartWebApi();
|
||||
|
||||
_logger.Information(LogSource.Geekbot, "Done and ready for use");
|
||||
|
||||
await Task.Delay(-1);
|
||||
}
|
||||
|
||||
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 DatabaseContext ConnectToDatabase()
|
||||
{
|
||||
_databaseInitializer = new DatabaseInitializer(_runParameters, _logger);
|
||||
var database = _databaseInitializer.Initialize();
|
||||
database.Database.EnsureCreated();
|
||||
if(!_runParameters.InMemory) database.Database.Migrate();
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
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 void SetupDiscordClient()
|
||||
{
|
||||
_client = new DiscordSocketClient(new DiscordSocketConfig
|
||||
{
|
||||
LogLevel = LogSeverity.Verbose,
|
||||
MessageCacheSize = 1000,
|
||||
ExclusiveBulkDelete = true
|
||||
});
|
||||
|
||||
var discordLogger = new DiscordLogger(_logger);
|
||||
_client.Log += discordLogger.Log;
|
||||
}
|
||||
|
||||
private void RegisterDependencies()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
_userRepository = new UserRepository(_databaseInitializer.Initialize(), _logger);
|
||||
_reactionListener = new ReactionListener(_databaseInitializer.Initialize());
|
||||
_guildSettingsManager = new GuildSettingsManager(_databaseInitializer.Initialize());
|
||||
var fortunes = new FortunesProvider(_logger);
|
||||
var malClient = new MalClient(_globalSettings, _logger);
|
||||
var levelCalc = new LevelCalc();
|
||||
var emojiConverter = new EmojiConverter();
|
||||
var mtgManaConverter = new MtgManaConverter();
|
||||
var wikipediaClient = new WikipediaClient();
|
||||
var randomNumberGenerator = new RandomNumberGenerator();
|
||||
var mediaProvider = new MediaProvider(_logger, randomNumberGenerator);
|
||||
var kvMemoryStore = new KvInInMemoryStore();
|
||||
var translationHandler = new TranslationHandler(_logger, _guildSettingsManager);
|
||||
var errorHandler = new ErrorHandler(_logger, translationHandler, _runParameters);
|
||||
var diceParser = new DiceParser(randomNumberGenerator);
|
||||
|
||||
services.AddSingleton(_userRepository);
|
||||
services.AddSingleton<IGeekbotLogger>(_logger);
|
||||
services.AddSingleton<ILevelCalc>(levelCalc);
|
||||
services.AddSingleton<IEmojiConverter>(emojiConverter);
|
||||
services.AddSingleton<IFortunesProvider>(fortunes);
|
||||
services.AddSingleton<IMediaProvider>(mediaProvider);
|
||||
services.AddSingleton<IMalClient>(malClient);
|
||||
services.AddSingleton<IMtgManaConverter>(mtgManaConverter);
|
||||
services.AddSingleton<IWikipediaClient>(wikipediaClient);
|
||||
services.AddSingleton<IRandomNumberGenerator>(randomNumberGenerator);
|
||||
services.AddSingleton<IKvInMemoryStore>(kvMemoryStore);
|
||||
services.AddSingleton<IGlobalSettings>(_globalSettings);
|
||||
services.AddSingleton<IErrorHandler>(errorHandler);
|
||||
services.AddSingleton<IDiceParser>(diceParser);
|
||||
services.AddSingleton<ITranslationHandler>(translationHandler);
|
||||
services.AddSingleton<IReactionListener>(_reactionListener);
|
||||
services.AddSingleton<IGuildSettingsManager>(_guildSettingsManager);
|
||||
services.AddSingleton(_client);
|
||||
services.AddTransient<IHighscoreManager>(e => new HighscoreManager(_databaseInitializer.Initialize(), _userRepository));
|
||||
services.AddTransient(e => _databaseInitializer.Initialize());
|
||||
|
||||
_servicesProvider = services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
private async Task RegisterHandlers()
|
||||
{
|
||||
var applicationInfo = await _client.GetApplicationInfoAsync();
|
||||
|
||||
_commands = new CommandService();
|
||||
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _servicesProvider);
|
||||
|
||||
var commandHandler = new CommandHandler(_databaseInitializer.Initialize(), _client, _logger, _servicesProvider, _commands, applicationInfo, _guildSettingsManager);
|
||||
var userHandler = new UserHandler(_userRepository, _logger, _databaseInitializer.Initialize(), _client);
|
||||
var reactionHandler = new ReactionHandler(_reactionListener);
|
||||
var statsHandler = new StatsHandler(_logger, _databaseInitializer.Initialize());
|
||||
var messageDeletedHandler = new MessageDeletedHandler(_databaseInitializer.Initialize(), _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;
|
||||
}
|
||||
|
||||
private void StartWebApi()
|
||||
{
|
||||
if (_runParameters.DisableApi)
|
||||
{
|
||||
_logger.Warning(LogSource.Api, "Web API is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
var highscoreManager = new HighscoreManager(_databaseInitializer.Initialize(), _userRepository);
|
||||
WebApiStartup.StartWebApi(_logger, _runParameters, _commands, _databaseInitializer.Initialize(), _client, _globalSettings, highscoreManager);
|
||||
}
|
||||
}
|
||||
}
|
17
src/Bot/Storage/croissant
Normal file
17
src/Bot/Storage/croissant
Normal 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
|
8
src/Bot/Storage/dab
Normal file
8
src/Bot/Storage/dab
Normal file
|
@ -0,0 +1,8 @@
|
|||
https://pre00.deviantart.net/dcde/th/pre/i/2018/247/8/1/dabbing_pug_cute_dab_dance_by_manekibb-dcm2lvd.png
|
||||
https://banner2.kisspng.com/20180625/xfv/kisspng-squidward-tentacles-dab-desktop-wallpaper-dab-emoji-5b31a97a839bf2.6353972915299813065391.jpg
|
||||
https://djbooth.net/.image/t_share/MTUzNDg2MDIzOTU4NzM0NzA1/life-death-of-dab-dancejpg.jpg
|
||||
https://res.cloudinary.com/teepublic/image/private/s--gfsWHvaH--/t_Preview/b_rgb:262c3a,c_limit,f_jpg,h_630,q_90,w_630/v1493209189/production/designs/1524888_1.jpg
|
||||
https://i1.wp.com/fortniteskins.net/wp-content/uploads/2018/04/dab-skin-1.png?quality=90&strip=all&ssl=1
|
||||
https://i.pinimg.com/originals/12/d4/0a/12d40a8fc66b7a7ea8b9044ee0303974.png
|
||||
https://images.bewakoof.com/t540/dab-penguin-boyfriend-t-shirt-women-s-printed-boyfriend-t-shirts-196918-1538034349.jpg
|
||||
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSKH1FLh5L3pOR2tamx-5OBqm_W2FFl8F7gteTAs2vMowwJ1Y32
|
9760
src/Bot/Storage/fortunes
Normal file
9760
src/Bot/Storage/fortunes
Normal file
File diff suppressed because it is too large
Load diff
29
src/Bot/Storage/foxes
Normal file
29
src/Bot/Storage/foxes
Normal file
|
@ -0,0 +1,29 @@
|
|||
https://i.ytimg.com/vi/qF6OOGuT_hI/maxresdefault.jpg
|
||||
https://www.hd-wallpapersdownload.com/script/bulk-upload/desktop-funny-fox-wallpaper.jpg
|
||||
http://moziru.com/images/drawn-fox-funny-18.jpg
|
||||
https://static.tumblr.com/bb34d8f163098ad1daafcffbdbb03975/rk23uap/Nwwp0rmi2/tumblr_static_tumblr_static__640.jpg
|
||||
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQoHUFOnZ3wJ2kT1skNdztFXXSvpU8bEoGS1alNZiuyLXvGJhcY
|
||||
http://childrenstorytales.com/wp-content/uploads/2011/03/how-to-draw-a-red-fox-in-the-snow.jpg
|
||||
https://www.popsci.com/sites/popsci.com/files/styles/1000_1x_/public/import/2013/images/2013/09/redfoxyawn.jpg?itok=yRkSVe8T
|
||||
https://hdqwalls.com/wallpapers/wild-fox-art.jpg
|
||||
https://ae01.alicdn.com/kf/HTB1Q9dpLpXXXXbhXpXXq6xXFXXXl/new-cute-fox-toy-lifelike-soft-long-yellow-fox-doll-gift-about-73cm.jpg_640x640.jpg
|
||||
https://i.imgur.com/ktK9yXX.jpg
|
||||
https://res.cloudinary.com/teepublic/image/private/s--yTx2ncFA--/t_Preview/b_rgb:c8e0ec,c_limit,f_auto,h_313,q_90,w_313/v1506478249/production/designs/1932607_0
|
||||
http://4.bp.blogspot.com/-Hz-o_KYj3Xk/Vlm2mwbztjI/AAAAAAAA8Ss/jbH5ovjmC9A/s1600/ScreenShot5502.jpg
|
||||
https://i.pinimg.com/originals/1e/d5/2f/1ed52f70873a95ac02fa074e48edfb71.jpg
|
||||
https://i.imgur.com/2vCrtap.jpg
|
||||
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSfukWGu_IBaDeJOMBqOhVAwsDfqEPw0BFpCn5_-Iyr_xjd7zi9
|
||||
https://cdn.pixabay.com/photo/2017/01/31/18/36/animal-2026297_960_720.png
|
||||
https://i.pinimg.com/originals/e2/63/67/e26367a0844633b2a697b0a9d69e8cc9.jpg
|
||||
https://i.ebayimg.com/images/g/BvkAAOSwqxdTqrip/s-l300.jpg
|
||||
https://res.cloudinary.com/teepublic/image/private/s--1R53bger--/t_Preview/b_rgb:eae0c7,c_limit,f_jpg,h_630,q_90,w_630/v1481013120/production/designs/914528_1.jpg
|
||||
https://i.pinimg.com/originals/97/fe/69/97fe698462afde7b4209ccefeecbce71.jpg
|
||||
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6G0ch6g-wG1TuDJ6BbkOFelMNnkgFXC6CxOw7qSNjoFkx-BCe
|
||||
https://wallpaperscraft.com/image/fox_forest_grass_117190_540x960.jpg
|
||||
https://image.freepik.com/free-vector/cartoon-flat-illustration-funny-cute-fox_6317-1174.jpg
|
||||
https://orig00.deviantart.net/2feb/f/2013/137/a/f/fox_and_curious_squirrel_by_tamarar-d65ju8d.jpg
|
||||
https://res.cloudinary.com/teepublic/image/private/s--dICeNmBx--/t_Preview/b_rgb:6e2229,c_limit,f_jpg,h_630,q_90,w_630/v1505243196/production/designs/1890493_1.jpg
|
||||
https://vignette.wikia.nocookie.net/puppyinmypocketfanon/images/4/49/L-Baby-Fox.jpg/revision/latest?cb=20130421001806
|
||||
http://7-themes.com/data_images/out/69/7009194-fox-puppy.jpg
|
||||
http://www.tehcute.com/pics/201401/little-fox-big.jpg
|
||||
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR6QXB1APLdUsyzO39kPvhnC9cOvcwzEtsxown9QjWilWppia2mwg
|
11
src/Bot/Storage/pandas
Normal file
11
src/Bot/Storage/pandas
Normal file
|
@ -0,0 +1,11 @@
|
|||
https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Grosser_Panda.JPG/1200px-Grosser_Panda.JPG
|
||||
https://c402277.ssl.cf1.rackcdn.com/photos/13100/images/featured_story/BIC_128.png?1485963152
|
||||
https://nationalzoo.si.edu/sites/default/files/styles/slide_1400x700/public/support/adopt/giantpanda-03.jpg?itok=3EdEO0Vi
|
||||
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
|
13
src/Bot/Storage/penguins
Normal file
13
src/Bot/Storage/penguins
Normal file
|
@ -0,0 +1,13 @@
|
|||
https://i.ytimg.com/vi/Qr6sULJnu2o/maxresdefault.jpg
|
||||
https://www.apex-expeditions.com/wp-content/uploads/2015/08/newzealandSlider_Macquarie_ElephantSealKingPenguins_GRiehle_1366x601.jpg
|
||||
https://www.birdlife.org/sites/default/files/styles/1600/public/slide.jpg?itok=HRhQfA1S
|
||||
http://experimentexchange.com/wp-content/uploads/2016/07/penguins-fact.jpg
|
||||
http://images.mentalfloss.com/sites/default/files/styles/mf_image_16x9/public/istock-511366776.jpg?itok=cWhdWNZ8&resize=1100x619
|
||||
https://www.thevaporplace.ch/media/catalog/product/cache/1/thumbnail/800x800/9df78eab33525d08d6e5fb8d27136e95/a/t/atopack_penguin-15.jpg
|
||||
https://www.superfastbusiness.com/wp-content/uploads/2015/10/real-time-penguin-algorithm-featured.jpg
|
||||
http://www.antarctica.gov.au/__data/assets/image/0011/147737/varieties/antarctic.jpg
|
||||
https://vignette.wikia.nocookie.net/robloxcreepypasta/images/1/11/AAEAAQAAAAAAAAdkAAAAJDc3YzkyYjJhLTYyZjctNDY2Mi04M2VjLTg4NjY4ZjgwYzRmNg.png/revision/latest?cb=20180207200526
|
||||
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR3xV0lhpZuhT8Nmm6LaITsppZ7VfWcWXuyu2cPHrlv_dt_M92K5g
|
||||
http://goboiano.com/wp-content/uploads/2017/04/Penguin-Kemeno-Friends-Waifu.jpg
|
||||
https://cdn.yoast.com/app/uploads/2015/10/Penguins_1200x628.png
|
||||
https://images.justwatch.com/backdrop/8611153/s1440/pingu
|
23
src/Bot/Storage/pumpkin
Normal file
23
src/Bot/Storage/pumpkin
Normal 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
|
45
src/Bot/Storage/squirrel
Normal file
45
src/Bot/Storage/squirrel
Normal 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
|
21
src/Bot/Storage/turtles
Normal file
21
src/Bot/Storage/turtles
Normal file
|
@ -0,0 +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
|
BIN
src/Bot/derp.ico
Normal file
BIN
src/Bot/derp.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
Loading…
Add table
Add a link
Reference in a new issue