Initial split of the modules

This commit is contained in:
Master Kwoth
2017-09-30 00:46:33 +02:00
parent cdc2c43913
commit 599245b1ca
499 changed files with 469 additions and 256 deletions

View File

@ -0,0 +1,218 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using ImageSharp;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using NadekoBot.Common.Collections;
using SixLabors.Primitives;
using NadekoBot.Common;
namespace NadekoBot.Extensions
{
public static class Extensions
{
public static async Task<string> ReplaceAsync(this Regex regex, string input, Func<Match, Task<string>> replacementFn)
{
var sb = new StringBuilder();
var lastIndex = 0;
foreach (Match match in regex.Matches(input))
{
sb.Append(input, lastIndex, match.Index - lastIndex)
.Append(await replacementFn(match).ConfigureAwait(false));
lastIndex = match.Index + match.Length;
}
sb.Append(input, lastIndex, input.Length - lastIndex);
return sb.ToString();
}
public static void ThrowIfNull<T>(this T obj, string name) where T : class
{
if (obj == null)
throw new ArgumentNullException(nameof(name));
}
public static ConcurrentDictionary<TKey, TValue> ToConcurrent<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> dict)
=> new ConcurrentDictionary<TKey, TValue>(dict);
public static bool IsAuthor(this IMessage msg, IDiscordClient client) =>
msg.Author?.Id == client.CurrentUser.Id;
public static string RealSummary(this CommandInfo cmd, string prefix) => string.Format(cmd.Summary, prefix);
public static string RealRemarks(this CommandInfo cmd, string prefix) => string.Format(cmd.Remarks, prefix);
public static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage)
{
if (lastPage != null)
return embed.WithFooter(efb => efb.WithText($"{curPage + 1} / {lastPage + 1}"));
else
return embed.WithFooter(efb => efb.WithText(curPage.ToString()));
}
public static EmbedBuilder WithOkColor(this EmbedBuilder eb) =>
eb.WithColor(NadekoBot.OkColor);
public static EmbedBuilder WithErrorColor(this EmbedBuilder eb) =>
eb.WithColor(NadekoBot.ErrorColor);
public static ReactionEventWrapper OnReaction(this IUserMessage msg, DiscordSocketClient client, Action<SocketReaction> reactionAdded, Action<SocketReaction> reactionRemoved = null)
{
if (reactionRemoved == null)
reactionRemoved = delegate { };
var wrap = new ReactionEventWrapper(client, msg);
wrap.OnReactionAdded += (r) => { var _ = Task.Run(() => reactionAdded(r)); };
wrap.OnReactionRemoved += (r) => { var _ = Task.Run(() => reactionRemoved(r)); };
return wrap;
}
public static void AddFakeHeaders(this HttpClient http)
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1");
http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
}
public static IMessage DeleteAfter(this IUserMessage msg, int seconds)
{
Task.Run(async () =>
{
await Task.Delay(seconds * 1000);
try { await msg.DeleteAsync().ConfigureAwait(false); }
catch { }
});
return msg;
}
public static ModuleInfo GetTopLevelModule(this ModuleInfo module)
{
while (module.Parent != null)
{
module = module.Parent;
}
return module;
}
//public static async Task<IEnumerable<IGuildUser>> MentionedUsers(this IUserMessage msg) =>
public static void AddRange<T>(this HashSet<T> target, IEnumerable<T> elements) where T : class
{
foreach (var item in elements)
{
target.Add(item);
}
}
public static void AddRange<T>(this ConcurrentHashSet<T> target, IEnumerable<T> elements) where T : class
{
foreach (var item in elements)
{
target.Add(item);
}
}
public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
public static async Task<IEnumerable<IGuildUser>> GetMembersAsync(this IRole role) =>
(await role.Guild.GetUsersAsync(CacheMode.CacheOnly)).Where(u => u.RoleIds.Contains(role.Id)) ?? Enumerable.Empty<IGuildUser>();
public static string ToJson<T>(this T any, Formatting formatting = Formatting.Indented) =>
JsonConvert.SerializeObject(any, formatting);
public static MemoryStream ToStream(this ImageSharp.Image<Rgba32> img)
{
var imageStream = new MemoryStream();
img.SaveAsPng(imageStream, new ImageSharp.Formats.PngEncoder() { CompressionLevel = 9});
imageStream.Position = 0;
return imageStream;
}
/// <summary>
/// returns an IEnumerable with randomized element order
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
// Thanks to @Joe4Evr for finding a bug in the old version of the shuffle
using (var provider = RandomNumberGenerator.Create())
{
var list = items.ToList();
var n = list.Count;
while (n > 1)
{
var box = new byte[(n / Byte.MaxValue) + 1];
int boxSum;
do
{
provider.GetBytes(box);
boxSum = box.Sum(b => b);
}
while (!(boxSum < n * ((Byte.MaxValue * box.Length) / n)));
var k = (boxSum % n);
n--;
var value = list[k];
list[k] = list[n];
list[n] = value;
}
return list;
}
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> elems, Action<T> exec)
{
foreach (var elem in elems)
{
exec(elem);
}
return elems;
}
public static Stream ToStream(this IEnumerable<byte> bytes, bool canWrite = false)
{
var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
public static IEnumerable<IRole> GetRoles(this IGuildUser user) =>
user.RoleIds.Select(r => user.Guild.GetRole(r)).Where(r => r != null);
public static async Task<IMessage> SendMessageToOwnerAsync(this IGuild guild, string message)
{
var ownerPrivate = await (await guild.GetOwnerAsync().ConfigureAwait(false)).GetOrCreateDMChannelAsync()
.ConfigureAwait(false);
return await ownerPrivate.SendMessageAsync(message).ConfigureAwait(false);
}
public static Image<Rgba32> Merge(this IEnumerable<Image<Rgba32>> images)
{
var imgs = images.ToArray();
var canvas = new Image<Rgba32>(imgs.Sum(img => img.Width), imgs.Max(img => img.Height));
var xOffset = 0;
for (int i = 0; i < imgs.Length; i++)
{
canvas.DrawImage(imgs[i], 100, default(Size), new Point(xOffset, 0));
xOffset += imgs[i].Bounds.Width;
}
return canvas;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace NadekoBot.Extensions
{
public static class IEnumerableExtensions
{
public static IEnumerable<T> Distinct<T, U>(this IEnumerable<T> data, Func<T, U> getKey) =>
data.GroupBy(x => getKey(x))
.Select(x => x.First());
}
}

View File

@ -0,0 +1,119 @@
using Discord;
using Discord.WebSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Extensions
{
public static class IMessageChannelExtensions
{
public static Task<IUserMessage> EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "")
=> ch.SendMessageAsync(msg, embed: embed.Build());
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string title, string error, string url = null, string footer = null)
{
var eb = new EmbedBuilder().WithErrorColor().WithDescription(error)
.WithTitle(title);
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
eb.WithUrl(url);
if (!string.IsNullOrWhiteSpace(footer))
eb.WithFooter(efb => efb.WithText(footer));
return ch.SendMessageAsync("", embed: eb.Build());
}
public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, string error)
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error).Build());
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string title, string text, string url = null, string footer = null)
{
var eb = new EmbedBuilder().WithOkColor().WithDescription(text)
.WithTitle(title);
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
eb.WithUrl(url);
if (!string.IsNullOrWhiteSpace(footer))
eb.WithFooter(efb => efb.WithText(footer));
return ch.SendMessageAsync("", embed: eb.Build());
}
public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, string text)
=> ch.SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text).Build());
public static Task<IUserMessage> SendTableAsync<T>(this IMessageChannel ch, string seed, IEnumerable<T> items, Func<T, string> howToPrint, int columns = 3)
{
var i = 0;
return ch.SendMessageAsync($@"{seed}```css
{string.Join("\n", items.GroupBy(item => (i++) / columns)
.Select(ig => string.Concat(ig.Select(el => howToPrint(el)))))}
```");
}
public static Task<IUserMessage> SendTableAsync<T>(this IMessageChannel ch, IEnumerable<T> items, Func<T, string> howToPrint, int columns = 3) =>
ch.SendTableAsync("", items, howToPrint, columns);
private static readonly IEmote arrow_left = new Emoji("⬅");
private static readonly IEmote arrow_right = new Emoji("➡");
public static Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordSocketClient client, int currentPage, Func<int, EmbedBuilder> pageFunc, int? lastPage = null, bool addPaginatedFooter = true) =>
channel.SendPaginatedConfirmAsync(client, currentPage, (x) => Task.FromResult(pageFunc(x)), lastPage, addPaginatedFooter);
/// <summary>
/// danny kamisama
/// </summary>
public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordSocketClient client, int currentPage, Func<int, Task<EmbedBuilder>> pageFunc, int? lastPage = null, bool addPaginatedFooter = true)
{
var embed = await pageFunc(currentPage).ConfigureAwait(false);
if (addPaginatedFooter)
embed.AddPaginatedFooter(currentPage, lastPage);
var msg = await channel.EmbedAsync(embed) as IUserMessage;
if (lastPage == 0)
return;
await msg.AddReactionAsync(arrow_left).ConfigureAwait(false);
await msg.AddReactionAsync(arrow_right).ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false);
Action<SocketReaction> changePage = async r =>
{
try
{
if (r.Emote.Name == arrow_left.Name)
{
if (currentPage == 0)
return;
var toSend = await pageFunc(--currentPage).ConfigureAwait(false);
if (addPaginatedFooter)
toSend.AddPaginatedFooter(currentPage, lastPage);
await msg.ModifyAsync(x => x.Embed = toSend.Build()).ConfigureAwait(false);
}
else if (r.Emote.Name == arrow_right.Name)
{
if (lastPage == null || lastPage > currentPage)
{
var toSend = await pageFunc(++currentPage).ConfigureAwait(false);
if (addPaginatedFooter)
toSend.AddPaginatedFooter(currentPage, lastPage);
await msg.ModifyAsync(x => x.Embed = toSend.Build()).ConfigureAwait(false);
}
}
}
catch (Exception)
{
//ignored
}
};
using (msg.OnReaction(client, changePage, changePage))
{
await Task.Delay(30000).ConfigureAwait(false);
}
await msg.RemoveAllReactionsAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,49 @@
using Discord;
using NadekoBot.Services.Database.Models;
using System;
using System.IO;
using System.Threading.Tasks;
namespace NadekoBot.Extensions
{
public static class IUserExtensions
{
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, string text)
=> await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text).Build());
public static async Task<IUserMessage> SendConfirmAsync(this IUser user, string title, string text, string url = null)
{
var eb = new EmbedBuilder().WithOkColor().WithDescription(text);
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
eb.WithUrl(url);
return await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: eb.Build());
}
public static async Task<IUserMessage> SendErrorAsync(this IUser user, string title, string error, string url = null)
{
var eb = new EmbedBuilder().WithErrorColor().WithDescription(error);
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
eb.WithUrl(url);
return await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: eb.Build());
}
public static async Task<IUserMessage> SendErrorAsync(this IUser user, string error)
=> await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error).Build());
public static async Task<IUserMessage> SendFileAsync(this IUser user, string filePath, string caption = null, string text = null, bool isTTS = false) =>
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(File.Open(filePath, FileMode.Open), caption ?? "x", text, isTTS).ConfigureAwait(false);
public static async Task<IUserMessage> SendFileAsync(this IUser user, Stream fileStream, string fileName, string caption = null, bool isTTS = false) =>
await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(fileStream, fileName, caption, isTTS).ConfigureAwait(false);
public static string RealAvatarUrl(this IUser usr) =>
usr.AvatarId.StartsWith("a_")
? $"{DiscordConfig.CDNUrl}avatars/{usr.Id}/{usr.AvatarId}.gif"
: usr.GetAvatarUrl(ImageFormat.Auto);
public static string RealAvatarUrl(this DiscordUser usr) =>
usr.AvatarId.StartsWith("a_")
? $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.gif"
: $"{DiscordConfig.CDNUrl}avatars/{usr.UserId}/{usr.AvatarId}.png";
}
}

View File

@ -0,0 +1,10 @@
using Discord;
namespace NadekoBot.Extensions
{
public static class MusicExtensions
{
public static EmbedAuthorBuilder WithMusicIcon(this EmbedAuthorBuilder eab) =>
eab.WithIconUrl("http://i.imgur.com/nhKS3PT.png");
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace NadekoBot.Extensions
{
public static class NumberExtensions
{
public static int KiB(this int value) => value * 1024;
public static int KB(this int value) => value * 1000;
public static int MiB(this int value) => value.KiB() * 1024;
public static int MB(this int value) => value.KB() * 1000;
public static int GiB(this int value) => value.MiB() * 1024;
public static int GB(this int value) => value.MB() * 1000;
public static ulong KiB(this ulong value) => value * 1024;
public static ulong KB(this ulong value) => value * 1000;
public static ulong MiB(this ulong value) => value.KiB() * 1024;
public static ulong MB(this ulong value) => value.KB() * 1000;
public static ulong GiB(this ulong value) => value.MiB() * 1024;
public static ulong GB(this ulong value) => value.MB() * 1000;
public static bool IsInteger(this decimal number) => number == Math.Truncate(number);
public static DateTime ToUnixTimestamp(this double number) => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(number);
}
}

View File

@ -0,0 +1,141 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NadekoBot.Extensions
{
public static class StringExtensions
{
public static string StripHTML(this string input)
{
return Regex.Replace(input, "<.*?>", String.Empty);
}
/// <summary>
/// Easy use of fast, efficient case-insensitive Contains check with StringComparison Member Types
/// CurrentCulture, CurrentCultureIgnoreCase, InvariantCulture, InvariantCultureIgnoreCase, Ordinal, OrdinalIgnoreCase
/// </summary>
public static bool ContainsNoCase(this string str, string contains, StringComparison compare)
{
return str.IndexOf(contains, compare) >= 0;
}
public static string TrimTo(this string str, int maxLength, bool hideDots = false)
{
if (maxLength < 0)
throw new ArgumentOutOfRangeException(nameof(maxLength), $"Argument {nameof(maxLength)} can't be negative.");
if (maxLength == 0)
return string.Empty;
if (maxLength <= 3)
return string.Concat(str.Select(c => '.'));
if (str.Length < maxLength)
return str;
return string.Concat(str.Take(maxLength - 3)) + (hideDots ? "" : "...");
}
public static string ToTitleCase(this string str)
{
var tokens = str.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < tokens.Length; i++)
{
var token = tokens[i];
tokens[i] = token.Substring(0, 1).ToUpper() + token.Substring(1);
}
return string.Join(" ", tokens);
}
/// <summary>
/// Removes trailing S or ES (if specified) on the given string if the num is 1
/// </summary>
/// <param name="str"></param>
/// <param name="num"></param>
/// <param name="es"></param>
/// <returns>String with the correct singular/plural form</returns>
public static string SnPl(this string str, int? num, bool es = false)
{
if (str == null)
throw new ArgumentNullException(nameof(str));
if (num == null)
throw new ArgumentNullException(nameof(num));
return num == 1 ? str.Remove(str.Length - 1, es ? 2 : 1) : str;
}
//http://www.dotnetperls.com/levenshtein
public static int LevenshteinDistance(this string s, string t)
{
var n = s.Length;
var m = t.Length;
var d = new int[n + 1, m + 1];
// Step 1
if (n == 0)
{
return m;
}
if (m == 0)
{
return n;
}
// Step 2
for (var i = 0; i <= n; d[i, 0] = i++)
{
}
for (var j = 0; j <= m; d[0, j] = j++)
{
}
// Step 3
for (var i = 1; i <= n; i++)
{
//Step 4
for (var j = 1; j <= m; j++)
{
// Step 5
var cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
// Step 6
d[i, j] = Math.Min(
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
d[i - 1, j - 1] + cost);
}
}
// Step 7
return d[n, m];
}
public static async Task<Stream> ToStream(this string str)
{
var ms = new MemoryStream();
var sw = new StreamWriter(ms);
await sw.WriteAsync(str);
await sw.FlushAsync();
ms.Position = 0;
return ms;
}
private static readonly Regex filterRegex = new Regex(@"(?:discord(?:\.gg|.me|app\.com\/invite)\/(?<id>([\w]{16}|(?:[\w]+-?){3})))", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static bool IsDiscordInvite(this string str)
=> filterRegex.IsMatch(str);
public static string Unmention(this string str) => str.Replace("@", "ම");
public static string SanitizeMentions(this string str) =>
str.Replace("@everyone", "@everyοne").Replace("@here", "@һere");
public static string ToBase64(this string plainText)
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
public static string GetInitials(this string txt, string glue = "") =>
string.Join(glue, txt.Split(' ').Select(x => x.FirstOrDefault()));
}
}