Split Geekbot.net into src/Bot, src/Core, and src/Web

This commit is contained in:
runebaas 2020-08-08 22:24:01 +02:00
parent 7b6dd2d2f9
commit fc0af492ad
No known key found for this signature in database
GPG key ID: 2677AF508D0300D6
197 changed files with 542 additions and 498 deletions

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

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

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

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

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

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

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

View file

@ -0,0 +1,61 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Integrations.LolMmr
{
public class LolMmr : 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);
}
}
}
}

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

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

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

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

@ -0,0 +1,38 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Cat
{
public class Cat : 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);
}
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,41 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Chuck
{
public class ChuckNorrisJokes : 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);
}
}
}
}

View file

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

View file

@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Dad
{
public class DadJokes : 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);
}
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.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);
}
}
}
}

View file

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

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

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

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

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

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

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

View file

@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Discord.Commands;
using Geekbot.Core;
using Geekbot.Core.ErrorHandling;
namespace Geekbot.Bot.Commands.Randomness.Kanye
{
public class Kanye : 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);
}
}
}
}

View file

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

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

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

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

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

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

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

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

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

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

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

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

View file

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

View file

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

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

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

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

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

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

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

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

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

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

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

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