Did a full pass on stuff done so far

This commit is contained in:
Kwoth 2016-08-20 16:02:06 +02:00
parent e8ffcbf7f6
commit 8e79e4e877
17 changed files with 172 additions and 399 deletions

View File

@ -1,72 +1,53 @@
//using Discord.Commands;
//using NadekoBot.Classes;
//using NadekoBot.Modules.Permissions.Classes;
//using System;
//using System.Linq;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using System.Threading.Tasks;
//namespace NadekoBot.Modules.Administration.Commands
//{
// class AutoAssignRole : DiscordCommand
// {
// public AutoAssignRole(DiscordModule module) : base(module)
// {
// NadekoBot.Client.UserJoined += (s, e) =>
// {
// try
// {
// var config = SpecificConfigurations.Default.Of(e.Server.Id);
namespace NadekoBot.Modules.Administration.Commands
{
//todo DB
public partial class Administration
{
[Group]
public class AutoAssignRole
{
public AutoAssignRole(DiscordSocketClient _client)
{
_client.UserJoined += (user) =>
{
//var config = SpecificConfigurations.Default.Of(e.Server.Id);
// var role = e.Server.Roles.Where(r => r.Id == config.AutoAssignedRole).FirstOrDefault();
//var role = e.Server.Roles.Where(r => r.Id == config.AutoAssignedRole).FirstOrDefault();
// if (role == null)
// return;
//if (role == null)
// return;
// e.User.AddRoles(role);
// }
// catch (Exception ex)
// {
// Console.WriteLine($"aar exception. {ex}");
// }
// };
// }
//e.User.AddRoles(role);
return Task.CompletedTask;
};
}
// internal override void Init(CommandGroupBuilder cgb)
// {
// cgb.CreateCommand(Module.Prefix + "autoassignrole")
// .Alias(Module.Prefix + "aar")
// .Description($"Automaticaly assigns a specified role to every user who joins the server. **Needs Manage Roles Permissions.** |`{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable")
// .Parameter("role", ParameterType.Unparsed)
// .AddCheck(new SimpleCheckers.ManageRoles())
// .Do(async e =>
// {
// if (!e.Server.CurrentUser.ServerPermissions.ManageRoles)
// {
// await imsg.Channel.SendMessageAsync("I do not have the permission to manage roles.").ConfigureAwait(false);
// return;
// }
// var r = e.GetArg("role")?.Trim();
//[LocalizedCommand, LocalizedDescription, LocalizedSummary]
//[RequireContext(ContextType.Guild)]
//[RequirePermission(GuildPermission.ManageRoles)]
//public async Task AutoAssignRole(IMessage imsg, IRole role)
//{
// var channel = imsg.Channel as ITextChannel;
// var config = SpecificConfigurations.Default.Of(e.Server.Id);
// var config = SpecificConfigurations.Default.Of(e.Server.Id);
// if (string.IsNullOrWhiteSpace(r)) //if role is not specified, disable
// {
// config.AutoAssignedRole = 0;
// if (string.IsNullOrWhiteSpace(r)) //if role is not specified, disable
// {
// config.AutoAssignedRole = 0;
// await imsg.Channel.SendMessageAsync("`Auto assign role on user join is now disabled.`").ConfigureAwait(false);
// return;
// }
// var role = e.Server.FindRoles(r).FirstOrDefault();
// await imsg.Channel.SendMessageAsync("`Auto assign role on user join is now disabled.`").ConfigureAwait(false);
// return;
// }
// if (role == null)
// {
// await imsg.Channel.SendMessageAsync("💢 `Role not found.`").ConfigureAwait(false);
// return;
// }
// config.AutoAssignedRole = role.Id;
// await imsg.Channel.SendMessageAsync("`Auto assigned role is set.`").ConfigureAwait(false);
// });
// }
// }
//}
// config.AutoAssignedRole = role.Id;
// await imsg.Channel.SendMessageAsync("`Auto assigned role is set.`").ConfigureAwait(false);
//}
}
}
}

View File

@ -7,6 +7,7 @@
//using System.Collections.Generic;
//using System.Linq;
////todo DB
//namespace NadekoBot.Modules.Administration.Commands
//{
// class CrossServerTextChannel : DiscordCommand

View File

@ -1,225 +0,0 @@
//using Discord;
//using Discord.Commands;
//using NadekoBot.Classes;
//using NadekoBot.Modules.Permissions.Classes;
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//namespace NadekoBot.Modules.Administration.Commands
//{
// class CustomReactionsCommands : DiscordCommand
// {
// public CustomReactionsCommands(DiscordModule module) : base(module)
// {
// }
// internal override void Init(CommandGroupBuilder cgb)
// {
// var Prefix = Module.Prefix;
// cgb.CreateCommand(Prefix + "addcustreact")
// .Alias(Prefix + "acr")
// .Description($"Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!** | `{Prefix}acr \"hello\" I love saying hello to %user%`")
// .AddCheck(SimpleCheckers.OwnerOnly())
// .Parameter("name", ParameterType.Required)
// .Parameter("message", ParameterType.Unparsed)
// .Do(async e =>
// {
// var name = e.GetArg("name");
// var message = e.GetArg("message")?.Trim();
// if (string.IsNullOrWhiteSpace(message))
// {
// await imsg.Channel.SendMessageAsync($"Incorrect command usage. See -h {Prefix}acr for correct formatting").ConfigureAwait(false);
// return;
// }
// if (NadekoBot.Config.CustomReactions.ContainsKey(name))
// NadekoBot.Config.CustomReactions[name].Add(message);
// else
// NadekoBot.Config.CustomReactions.Add(name, new System.Collections.Generic.List<string>() { message });
// await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
// await imsg.Channel.SendMessageAsync($"Added {name} : {message}").ConfigureAwait(false);
// });
// cgb.CreateCommand(Prefix + "listcustreact")
// .Alias(Prefix + "lcr")
// .Description($"Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. |`{Prefix}lcr 1`")
// .Parameter("num", ParameterType.Required)
// .Do(async e =>
// {
// var numStr = e.GetArg("num");
// if (numStr.ToUpperInvariant() == "ALL")
// {
// var fullstr = String.Join("\n", NadekoBot.Config.CustomReactions.Select(kvp => kvp.Key));
// do
// {
// var str = string.Concat(fullstr.Take(1900));
// fullstr = new string(fullstr.Skip(1900).ToArray());
// await e.User.SendMessage("```xl\n" + str + "```");
// } while (fullstr.Length != 0);
// return;
// }
// int num;
// if (!int.TryParse(numStr, out num) || num <= 0) num = 1;
// var cmds = GetCustomsOnPage(num - 1);
// if (!cmds.Any())
// {
// await imsg.Channel.SendMessageAsync("`There are no custom reactions.`");
// }
// else
// {
// string result = SearchHelper.ShowInPrettyCode<string>(cmds, s => $"{s,-25}"); //People prefer starting with 1
// await imsg.Channel.SendMessageAsync($"`Showing page {num}:`\n" + result).ConfigureAwait(false);
// }
// });
// cgb.CreateCommand(Prefix + "showcustreact")
// .Alias(Prefix + "scr")
// .Description($"Shows all possible responses from a single custom reaction. |`{Prefix}scr %mention% bb`")
// .Parameter("name", ParameterType.Unparsed)
// .Do(async e =>
// {
// var name = e.GetArg("name")?.Trim();
// if (string.IsNullOrWhiteSpace(name))
// return;
// if (!NadekoBot.Config.CustomReactions.ContainsKey(name))
// {
// await imsg.Channel.SendMessageAsync("`Can't find that custom reaction.`").ConfigureAwait(false);
// return;
// }
// var items = NadekoBot.Config.CustomReactions[name];
// var message = new StringBuilder($"Responses for {Format.Bold(name)}:\n");
// var last = items.Last();
// int i = 1;
// foreach (var reaction in items)
// {
// message.AppendLine($"[{i++}] " + Format.Code(Format.Escape(reaction)));
// }
// await imsg.Channel.SendMessageAsync(message.ToString());
// });
// cgb.CreateCommand(Prefix + "editcustreact")
// .Alias(Prefix + "ecr")
// .Description($"Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `{Prefix}ecr \"%mention% disguise\" 2 Test 123`")
// .Parameter("name", ParameterType.Required)
// .Parameter("index", ParameterType.Required)
// .Parameter("message", ParameterType.Unparsed)
// .AddCheck(SimpleCheckers.OwnerOnly())
// .Do(async e =>
// {
// var name = e.GetArg("name")?.Trim();
// if (string.IsNullOrWhiteSpace(name))
// return;
// var indexstr = e.GetArg("index")?.Trim();
// if (string.IsNullOrWhiteSpace(indexstr))
// return;
// var msg = e.GetArg("message")?.Trim();
// if (string.IsNullOrWhiteSpace(msg))
// return;
// if (!NadekoBot.Config.CustomReactions.ContainsKey(name))
// {
// await imsg.Channel.SendMessageAsync("`Could not find given commandname`").ConfigureAwait(false);
// return;
// }
// int index;
// if (!int.TryParse(indexstr, out index) || index < 1 || index > NadekoBot.Config.CustomReactions[name].Count)
// {
// await imsg.Channel.SendMessageAsync("`Invalid index.`").ConfigureAwait(false);
// return;
// }
// index = index - 1;
// NadekoBot.Config.CustomReactions[name][index] = msg;
// await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
// await imsg.Channel.SendMessageAsync($"Edited response #{index + 1} from `{name}`").ConfigureAwait(false);
// });
// cgb.CreateCommand(Prefix + "delcustreact")
// .Alias(Prefix + "dcr")
// .Description($"Deletes a custom reaction with given name (and index). **Bot Owner Only.**| `{Prefix}dcr index`")
// .Parameter("name", ParameterType.Required)
// .Parameter("index", ParameterType.Optional)
// .AddCheck(SimpleCheckers.OwnerOnly())
// .Do(async e =>
// {
// var name = e.GetArg("name")?.Trim();
// if (string.IsNullOrWhiteSpace(name))
// return;
// if (!NadekoBot.Config.CustomReactions.ContainsKey(name))
// {
// await imsg.Channel.SendMessageAsync("Could not find given commandname").ConfigureAwait(false);
// return;
// }
// string message = "";
// int index;
// if (int.TryParse(e.GetArg("index")?.Trim() ?? "", out index))
// {
// index = index - 1;
// if (index < 0 || index > NadekoBot.Config.CustomReactions[name].Count)
// {
// await imsg.Channel.SendMessageAsync("Given index was out of range").ConfigureAwait(false);
// return;
// }
// NadekoBot.Config.CustomReactions[name].RemoveAt(index);
// if (!NadekoBot.Config.CustomReactions[name].Any())
// {
// NadekoBot.Config.CustomReactions.Remove(name);
// }
// message = $"Deleted response #{index + 1} from `{name}`";
// }
// else
// {
// NadekoBot.Config.CustomReactions.Remove(name);
// message = $"Deleted custom reaction: `{name}`";
// }
// await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
// await imsg.Channel.SendMessageAsync(message).ConfigureAwait(false);
// });
// }
// private readonly int ItemsPerPage = 30;
// private IEnumerable<string> GetCustomsOnPage(int page)
// {
// var items = NadekoBot.Config.CustomReactions.Skip(page * ItemsPerPage).Take(ItemsPerPage);
// if (!items.Any())
// {
// return Enumerable.Empty<string>();
// }
// return items.Select(kvp => kvp.Key);
// /*
// var message = new StringBuilder($"--- Custom reactions - page {page + 1} ---\n");
// foreach (var cr in items)
// {
// message.Append($"{Format.Code(cr.Key)}\n");
// int i = 1;
// var last = cr.Value.Last();
// foreach (var reaction in cr.Value)
// {
// if (last != reaction)
// message.AppendLine(" `├" + i++ + "─`" + Format.Bold(reaction));
// else
// message.AppendLine(" `└" + i++ + "─`" + Format.Bold(reaction));
// }
// }
// return message.ToString() + "\n";
// */
// }
// }
//}
//// zeta is a god
////├
////─
////│
////└

View File

@ -5,6 +5,7 @@
//using System.IO;
//using System.Linq;
////todo DB
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class IncidentsCommands : DiscordCommand

View File

@ -9,6 +9,8 @@
//using System.Linq;
//using System.Threading.Tasks;
////todo DB
////todo Add flags for every event
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class LogCommand : DiscordCommand

View File

@ -6,7 +6,8 @@
//using System.Collections.Concurrent;
//using System.Threading.Tasks;
//using System.Timers;
////todo DB
////todo persist restarts
//namespace NadekoBot.Modules.Administration.Commands
//{
// class MessageRepeater : DiscordCommand

View File

@ -12,6 +12,7 @@
//using System.Timers;
//using Timer = System.Timers.Timer;
////todo DB
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class PlayingRotate : DiscordCommand

View File

@ -1,63 +1,75 @@
//using Discord.Commands;
//using NadekoBot.Classes;
//using NadekoBot.Modules.Permissions.Classes;
//using System;
//using System.Collections.Concurrent;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Classes;
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class RatelimitCommand : DiscordCommand
// {
//todo rewrite to accept msg/sec (for example 1/5 - 1 message every 5 seconds)
namespace NadekoBot.Modules.Administration.Commands
{
public partial class Administration
{
[Group]
public class RatelimitCommand
{
public static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, DateTime>> RatelimitingChannels = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, DateTime>>();
// public static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, DateTime>> RatelimitingChannels = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, DateTime>>();
private static readonly TimeSpan ratelimitTime = new TimeSpan(0, 0, 0, 5);
private DiscordSocketClient _client { get; }
// private static readonly TimeSpan ratelimitTime = new TimeSpan(0, 0, 0, 5);
public RatelimitCommand(DiscordSocketClient client)
{
// public RatelimitCommand(DiscordModule module) : base(module)
// {
// NadekoBot.Client.MessageReceived += async (s, e) =>
// {
// if (e.Channel.IsPrivate || e.User.Id == NadekoBot.Client.CurrentUser.Id)
// return;
// ConcurrentDictionary<ulong, DateTime> userTimePair;
// if (!RatelimitingChannels.TryGetValue(e.Channel.Id, out userTimePair)) return;
// DateTime lastMessageTime;
// if (userTimePair.TryGetValue(e.User.Id, out lastMessageTime))
// {
// if (DateTime.Now - lastMessageTime < ratelimitTime)
// {
// try
// {
// await e.Message.Delete().ConfigureAwait(false);
// }
// catch { }
// return;
// }
// }
// userTimePair.AddOrUpdate(e.User.Id, id => DateTime.Now, (id, dt) => DateTime.Now);
// };
// }
this._client = client;
// internal override void Init(CommandGroupBuilder cgb)
// {
// cgb.CreateCommand(Module.Prefix + "slowmode")
// .Description($"Toggles slow mode. When ON, users will be able to send only 1 message every 5 seconds. **Needs Manage Messages Permissions.**| `{Prefix}slowmode`")
// .AddCheck(SimpleCheckers.ManageMessages())
// .Do(async e =>
// {
// ConcurrentDictionary<ulong, DateTime> throwaway;
// if (RatelimitingChannels.TryRemove(e.Channel.Id, out throwaway))
// {
// await imsg.Channel.SendMessageAsync("Slow mode disabled.").ConfigureAwait(false);
// return;
// }
// if (RatelimitingChannels.TryAdd(e.Channel.Id, new ConcurrentDictionary<ulong, DateTime>()))
// {
// await imsg.Channel.SendMessageAsync("Slow mode initiated. " +
// "Users can't send more than 1 message every 5 seconds.")
// .ConfigureAwait(false);
// }
// });
// }
// }
//}
_client.MessageReceived += async (imsg) =>
{
var channel = imsg.Channel as ITextChannel;
if (channel == null || await imsg.IsAuthor(client))
return;
ConcurrentDictionary<ulong, DateTime> userTimePair;
if (!RatelimitingChannels.TryGetValue(channel.Id, out userTimePair)) return;
DateTime lastMessageTime;
if (userTimePair.TryGetValue(imsg.Author.Id, out lastMessageTime))
{
if (DateTime.Now - lastMessageTime < ratelimitTime)
{
try
{
await imsg.DeleteAsync().ConfigureAwait(false);
}
catch { }
return;
}
}
userTimePair.AddOrUpdate(imsg.Author.Id, id => DateTime.Now, (id, dt) => DateTime.Now);
};
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Slowmode(IMessage imsg)
{
var channel = imsg.Channel as ITextChannel;
ConcurrentDictionary<ulong, DateTime> throwaway;
if (RatelimitingChannels.TryRemove(channel.Id, out throwaway))
{
await imsg.Channel.SendMessageAsync("Slow mode disabled.").ConfigureAwait(false);
return;
}
if (RatelimitingChannels.TryAdd(channel.Id, new ConcurrentDictionary<ulong, DateTime>()))
{
await imsg.Channel.SendMessageAsync("Slow mode initiated. " +
"Users can't send more than 1 message every 5 seconds.")
.ConfigureAwait(false);
}
}
}
}
}

View File

@ -7,7 +7,7 @@
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
////todo DB
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class SelfAssignedRolesCommand : DiscordCommand

View File

@ -1,42 +1,50 @@
//using Discord.Commands;
//using NadekoBot.Classes;
//using NadekoBot.Modules.Permissions.Classes;
//using Discord;
//using Discord.Commands;
//using Discord.WebSocket;
//using NadekoBot.Attributes;
//using System.Linq;
//using System.Threading.Tasks;
////todo owner only
//namespace NadekoBot.Modules.Administration.Commands
//{
// class SelfCommands : DiscordCommand
// public partial class Administration
// {
// public SelfCommands(DiscordModule module) : base(module)
// [Group]
// class SelfCommands
// {
// private DiscordSocketClient _client;
// public SelfCommands(DiscordSocketClient client)
// {
// this._client = client;
// }
// internal override void Init(CommandGroupBuilder cgb)
// [LocalizedCommand, LocalizedDescription, LocalizedSummary]
// [RequireContext(ContextType.Guild)]
// public async Task Leave(IMessage imsg, [Remainder] string guildStr)
// {
// cgb.CreateCommand(Module.Prefix + "leave")
// .Description($"Makes Nadeko leave the server. Either name or id required. **Bot Owner Only!**| `{Prefix}leave 123123123331`")
// .Parameter("arg", ParameterType.Required)
// .AddCheck(SimpleCheckers.OwnerOnly())
// .Do(async e =>
// {
// var arg = e.GetArg("arg").Trim();
// var server = NadekoBot.Client.Servers.FirstOrDefault(s => s.Id.ToString() == arg) ??
// NadekoBot.Client.FindServers(arg).FirstOrDefault();
// var channel = imsg.Channel as ITextChannel;
// guildStr = guildStr.ToUpperInvariant();
// var server = _client.GetGuilds().FirstOrDefault(g => g.Id.ToString() == guildStr) ?? _client.GetGuilds().FirstOrDefault(g => g.Name.ToUpperInvariant() == guildStr);
// if (server == null)
// {
// await imsg.Channel.SendMessageAsync("Cannot find that server").ConfigureAwait(false);
// return;
// }
// if (!server.IsOwner)
// if (server.OwnerId != _client.GetCurrentUser().Id)
// {
// await server.Leave().ConfigureAwait(false);
// await server.LeaveAsync().ConfigureAwait(false);
// await channel.SendMessageAsync("Left server " + server.Name).ConfigureAwait(false);
// }
// else
// {
// await server.Delete().ConfigureAwait(false);
// await server.DeleteAsync().ConfigureAwait(false);
// await channel.SendMessageAsync("Deleted server " + server.Name).ConfigureAwait(false);
// }
// }
// await NadekoBot.SendMessageToOwner("Left server " + server.Name).ConfigureAwait(false);
// });
// }
// }
//}

View File

@ -5,19 +5,8 @@
//using System.Linq;
//using System.Threading.Tasks;
///* Voltana's legacy
//public class AsyncLazy<T> : Lazy<Task<T>>
//{
// public AsyncLazy(Func<T> valueFactory) :
// base(() => Task.Factory.StartNew(valueFactory)) { }
// public AsyncLazy(Func<Task<T>> taskFactory) :
// base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap()) { }
// public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
//}
//*/
//// todo DB
//// todo rewrite
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class ServerGreetCommand : DiscordCommand

View File

@ -9,6 +9,8 @@
//using System.Threading.Tasks;
//using ChPermOverride = Discord.ChannelPermissionOverrides;
////todo DB
////todo rewrite
//namespace NadekoBot.Modules.Administration.Commands
//{
// internal class VoicePlusTextCommand : DiscordCommand

View File

@ -12,7 +12,6 @@ namespace NadekoBot.Modules.Games.Commands
{
public partial class GamesModule
{
//todo DB in the future
public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>();

View File

@ -14,7 +14,6 @@
////todo Rewrite?
//namespace NadekoBot.Modules.Games.Commands
//{
// public static class SentencesProvider
// {
// internal static string GetRandomSentence()

View File

@ -7,7 +7,7 @@
//using System.Threading;
//using System.Threading.Tasks;
//todo Unit Conversion lib
////todo Unit Conversion lib
//namespace NadekoBot.Modules.Searches.Commands
//{
// class ConverterCommand : DiscordCommand

View File

@ -19,13 +19,10 @@ namespace NadekoBot.Modules.Searches
[Module("~", AppendSpace = false)]
public class Searches : DiscordModule
{
private readonly Random rng;
private IYoutubeService _yt { get; }
public Searches(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client, IYoutubeService youtube) : base(loc, cmds, config, client)
{
rng = new Random();
_yt = youtube;
}
@ -156,6 +153,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
{
using (var http = new HttpClient())
{
var rng = new Random();
var reqString = $"https://www.googleapis.com/customsearch/v1?q={Uri.EscapeDataString(query)}&cx=018084019232060951019%3Ahs5piey28-e&num=1&searchType=image&start={ rng.Next(1, 50) }&fields=items%2Flink&key={NadekoBot.Credentials.GoogleApiKey}";
var obj = JObject.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false));
var items = obj["items"] as JArray;

View File

@ -1,4 +1,5 @@
using Discord;
using Discord.WebSocket;
using System;
using System.Collections.Generic;
using System.IO;
@ -25,6 +26,9 @@ namespace NadekoBot.Extensions
public static async Task<IMessage> Reply(this IMessage msg, string content) =>
await msg.Channel.SendMessageAsync(content).ConfigureAwait(false);
public static Task<bool> IsAuthor(this IMessage msg, DiscordSocketClient client) =>
Task.FromResult(client.GetCurrentUser().Id == msg.Author.Id);
public static async Task<IEnumerable<IUser>> Members(this IRole role) =>
await role.Members();