From b3243eb0e912f13df458c7fb8ea49a00a427dfa1 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sat, 15 Jul 2017 15:08:34 +0200 Subject: [PATCH] More cleanup --- .../Commands/ResetPermissionsCommands.cs | 29 +- .../Searches/Commands/AnimeSearchCommands.cs | 17 +- .../Administration/GuildTimezoneService.cs | 2 +- src/NadekoBot/Services/CommandHandler.cs | 2 - src/NadekoBot/Services/Music/Song.cs | 245 ------------ .../Permissions/ResetPermissionsService.cs | 44 +++ src/NadekoBot/_Extensions/Extensions.cs | 356 ++---------------- .../_Extensions/IMessageChannelExtensions.cs | 119 ++++++ src/NadekoBot/_Extensions/IUserExtensions.cs | 43 +++ src/NadekoBot/_Extensions/NumberExtensions.cs | 29 ++ src/NadekoBot/_Extensions/StringExtensions.cs | 136 +++++++ 11 files changed, 412 insertions(+), 610 deletions(-) delete mode 100644 src/NadekoBot/Services/Music/Song.cs create mode 100644 src/NadekoBot/Services/Permissions/ResetPermissionsService.cs create mode 100644 src/NadekoBot/_Extensions/IMessageChannelExtensions.cs create mode 100644 src/NadekoBot/_Extensions/IUserExtensions.cs create mode 100644 src/NadekoBot/_Extensions/NumberExtensions.cs create mode 100644 src/NadekoBot/_Extensions/StringExtensions.cs diff --git a/src/NadekoBot/Modules/Permissions/Commands/ResetPermissionsCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/ResetPermissionsCommands.cs index 049b4782..2e6ea132 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/ResetPermissionsCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/ResetPermissionsCommands.cs @@ -13,15 +13,11 @@ namespace NadekoBot.Modules.Permissions [Group] public class ResetPermissionsCommands : NadekoSubmodule { - private readonly PermissionService _service; - private readonly DbService _db; - private readonly GlobalPermissionService _globalPerms; + private readonly ResetPermissionsService _service; - public ResetPermissionsCommands(PermissionService service, GlobalPermissionService globalPerms, DbService db) + public ResetPermissionsCommands(ResetPermissionsService service) { _service = service; - _db = db; - _globalPerms = globalPerms; } [NadekoCommand, Usage, Description, Aliases] @@ -29,14 +25,7 @@ namespace NadekoBot.Modules.Permissions [RequireUserPermission(GuildPermission.Administrator)] public async Task ResetPermissions() { - //todo 50 move to service - using (var uow = _db.UnitOfWork) - { - var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id); - config.Permissions = Permissionv2.GetDefaultPermlist; - await uow.CompleteAsync(); - _service.UpdateCache(config); - } + await _service.ResetPermissions(Context.Guild.Id).ConfigureAwait(false); await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false); } @@ -44,17 +33,7 @@ namespace NadekoBot.Modules.Permissions [OwnerOnly] public async Task ResetGlobalPermissions() { - //todo 50 move to service - using (var uow = _db.UnitOfWork) - { - var gc = uow.BotConfig.GetOrCreate(); - gc.BlockedCommands.Clear(); - gc.BlockedModules.Clear(); - - _globalPerms.BlockedCommands.Clear(); - _globalPerms.BlockedModules.Clear(); - await uow.CompleteAsync(); - } + await _service.ResetGlobalPermissions().ConfigureAwait(false); await ReplyConfirmLocalized("global_perms_reset").ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index 1c32b091..ff41e0ca 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -54,16 +54,6 @@ namespace NadekoBot.Modules.Searches return $"[{elem.InnerHtml}]({elem.Href})"; })); - //var favManga = "No favorite manga yet."; - //if (favorites[1].QuerySelector("p") == null) - // favManga = string.Join("\n", favorites[1].QuerySelectorAll("ul > li > div.di-tc.va-t > a") - // .Take(3) - // .Select(x => - // { - // var elem = (IHtmlAnchorElement)x; - // return $"[{elem.InnerHtml}]({elem.Href})"; - // })); - var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li.clearfix") .Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml)) .ToList(); @@ -113,7 +103,8 @@ namespace NadekoBot.Modules.Searches await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } - private static string MalInfoToEmoji(string info) { + private static string MalInfoToEmoji(string info) + { info = info.Trim().ToLowerInvariant(); switch (info) { @@ -156,7 +147,7 @@ namespace NadekoBot.Modules.Searches .WithImageUrl(animeData.image_url_lge) .AddField(efb => efb.WithName(GetText("episodes")).WithValue(animeData.total_episodes.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("status")).WithValue(animeData.AiringStatus.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", animeData.Genres)).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", animeData.Genres.Any() ? animeData.Genres : new[] { "none" })).WithIsInline(true)) .WithFooter(efb => efb.WithText(GetText("score") + " " + animeData.average_score + " / 100")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -183,7 +174,7 @@ namespace NadekoBot.Modules.Searches .WithImageUrl(mangaData.image_url_lge) .AddField(efb => efb.WithName(GetText("chapters")).WithValue(mangaData.total_chapters.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("status")).WithValue(mangaData.publishing_status.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", mangaData.Genres)).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : new[] { "none" })).WithIsInline(true)) .WithFooter(efb => efb.WithText(GetText("score") + " " + mangaData.average_score + " / 100")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); diff --git a/src/NadekoBot/Services/Administration/GuildTimezoneService.cs b/src/NadekoBot/Services/Administration/GuildTimezoneService.cs index c15eb534..caa9340d 100644 --- a/src/NadekoBot/Services/Administration/GuildTimezoneService.cs +++ b/src/NadekoBot/Services/Administration/GuildTimezoneService.cs @@ -11,7 +11,7 @@ namespace NadekoBot.Services.Administration { public class GuildTimezoneService : INService { - //hack >.> + // todo 70 this is a hack >.< public static ConcurrentDictionary AllServices { get; } = new ConcurrentDictionary(); private ConcurrentDictionary _timezones; private readonly DbService _db; diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 81ecb073..12740fa0 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -274,8 +274,6 @@ namespace NadekoBot.Services } else if (result.Error != null) { - //todo 80 should have log levels and it should return some kind of result, - // instead of tuple with the type of thing that went wrong, like before LogErroredExecution(result.Error, usrMsg, channel as ITextChannel, exec2, exec3, execTime); if (guild != null) await CommandErrored(result.Info, channel as ITextChannel, result.Error); diff --git a/src/NadekoBot/Services/Music/Song.cs b/src/NadekoBot/Services/Music/Song.cs deleted file mode 100644 index a9567f9c..00000000 --- a/src/NadekoBot/Services/Music/Song.cs +++ /dev/null @@ -1,245 +0,0 @@ -using NadekoBot.Extensions; -using System.Net; -using Discord; -using NadekoBot.Services.Database.Models; -using System; - -namespace NadekoBot.Services.Music -{ - //public class Song - //{ - // public SongInfo SongInfo { get; } - // public MusicPlayer MusicPlayer { get; set; } - - // private string _queuerName; - // public string QueuerName { get{ - // return Format.Sanitize(_queuerName); - // } set { _queuerName = value; } } - - // public TimeSpan TotalTime { get; set; } = TimeSpan.Zero; - // public TimeSpan CurrentTime => TimeSpan.FromSeconds(BytesSent / (float)_frameBytes / (1000 / (float)_milliseconds)); - - // private const int _milliseconds = 20; - // private const int _samplesPerFrame = (48000 / 1000) * _milliseconds; - // private const int _frameBytes = 3840; //16-bit, 2 channels - - // private ulong BytesSent { get; set; } - - // //pwetty - - // public string PrettyProvider => - // $"{(SongInfo.Provider ?? "???")}"; - - // public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime; - - // public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({SongUrl})**"; - - // public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}"; - - // public string PrettyFullName => $"{PrettyName}\n\t\t`{PrettyTotalTime} | {PrettyProvider} | {QueuerName}`"; - - // public string PrettyCurrentTime { - // get { - // var time = CurrentTime.ToString(@"mm\:ss"); - // var hrs = (int)CurrentTime.TotalHours; - - // if (hrs > 0) - // return hrs + ":" + time; - // else - // return time; - // } - // } - - // public string PrettyTotalTime { - // get - // { - // if (TotalTime == TimeSpan.Zero) - // return "(?)"; - // if (TotalTime == TimeSpan.MaxValue) - // return "∞"; - // var time = TotalTime.ToString(@"mm\:ss"); - // var hrs = (int)TotalTime.TotalHours; - - // if (hrs > 0) - // return hrs + ":" + time; - // return time; - // } - // } - - // public string Thumbnail { - // get { - // switch (SongInfo.ProviderType) - // { - // case MusicType.Radio: - // return "https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links - // case MusicType.Normal: - // //todo 50 have videoid in songinfo from the start - // var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+"); - // return $"https://img.youtube.com/vi/{ videoId }/0.jpg"; - // case MusicType.Local: - // return "https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links - // case MusicType.Soundcloud: - // return SongInfo.AlbumArt; - // default: - // return ""; - // } - // } - // } - - // public string SongUrl { - // get { - // switch (SongInfo.ProviderType) - // { - // case MusicType.Normal: - // return SongInfo.Query; - // case MusicType.Soundcloud: - // return SongInfo.Query; - // case MusicType.Local: - // return $"https://google.com/search?q={ WebUtility.UrlEncode(SongInfo.Title).Replace(' ', '+') }"; - // case MusicType.Radio: - // return $"https://google.com/search?q={SongInfo.Title}"; - // default: - // return ""; - // } - // } - // } - - // private readonly Logger _log; - - // public Song(SongInfo songInfo) - // { - // SongInfo = songInfo; - // _log = LogManager.GetCurrentClassLogger(); - // } - - // public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) - // { - // BytesSent = (ulong) SkipTo * 3840 * 50; - // var filename = Path.Combine(MusicService.MusicDataPath, DateTime.UtcNow.UnixTimestamp().ToString()); - - // var inStream = new SongBuffer(MusicPlayer, filename, SongInfo, SkipTo, _frameBytes * 100); - // var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false); - - // try - // { - // var attempt = 0; - - // var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken, 1.MiB()); //Fast connection can do this easy - // var finished = false; - // var count = 0; - // var sw = new Stopwatch(); - // var slowconnection = false; - // sw.Start(); - // while (!finished) - // { - // var t = await Task.WhenAny(prebufferingTask, Task.Delay(2000, cancelToken)); - // if (t != prebufferingTask) - // { - // count++; - // if (count == 10) - // { - // slowconnection = true; - // prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken, 20.MiB()); - // _log.Warn("Slow connection buffering more to ensure no disruption, consider hosting in cloud"); - // continue; - // } - - // if (inStream.BufferingCompleted && count == 1) - // { - // _log.Debug("Prebuffering canceled. Cannot get any data from the stream."); - // return; - // } - // else - // { - // continue; - // } - // } - // else if (prebufferingTask.IsCanceled) - // { - // _log.Debug("Prebuffering canceled. Cannot get any data from the stream."); - // return; - // } - // finished = true; - // } - // sw.Stop(); - // _log.Debug("Prebuffering successfully completed in " + sw.Elapsed); - - // var outStream = voiceClient.CreatePCMStream(AudioApplication.Music); - - // int nextTime = Environment.TickCount + _milliseconds; - - // byte[] buffer = new byte[_frameBytes]; - // while (!cancelToken.IsCancellationRequested && //song canceled for whatever reason - // !(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime - // { - // var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); - // //await inStream.CopyToAsync(voiceClient.OutputStream); - // if (read < _frameBytes) - // _log.Debug("read {0}", read); - // unchecked - // { - // BytesSent += (ulong)read; - // } - // if (read < _frameBytes) - // { - // if (read == 0) - // { - // if (inStream.BufferingCompleted) - // break; - // if (attempt++ == 20) - // { - // MusicPlayer.SongCancelSource.Cancel(); - // break; - // } - // if (slowconnection) - // { - // _log.Warn("Slow connection has disrupted music, waiting a bit for buffer"); - - // await Task.Delay(1000, cancelToken).ConfigureAwait(false); - // nextTime = Environment.TickCount + _milliseconds; - // } - // else - // { - // await Task.Delay(100, cancelToken).ConfigureAwait(false); - // nextTime = Environment.TickCount + _milliseconds; - // } - // } - // else - // attempt = 0; - // } - // else - // attempt = 0; - - // while (MusicPlayer.Paused) - // { - // await Task.Delay(200, cancelToken).ConfigureAwait(false); - // nextTime = Environment.TickCount + _milliseconds; - // } - - - // buffer = AdjustVolume(buffer, MusicPlayer.Volume); - // if (read != _frameBytes) continue; - // nextTime = unchecked(nextTime + _milliseconds); - // int delayMillis = unchecked(nextTime - Environment.TickCount); - // if (delayMillis > 0) - // await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false); - // await outStream.WriteAsync(buffer, 0, read).ConfigureAwait(false); - // } - // } - // finally - // { - // await bufferTask; - // inStream.Dispose(); - // } - // } - - // private async Task CheckPrebufferingAsync(SongBuffer inStream, CancellationToken cancelToken, long size) - // { - // while (!inStream.BufferingCompleted && inStream.Length < size) - // { - // await Task.Delay(100, cancelToken); - // } - // _log.Debug("Buffering successfull"); - // } - //} -} \ No newline at end of file diff --git a/src/NadekoBot/Services/Permissions/ResetPermissionsService.cs b/src/NadekoBot/Services/Permissions/ResetPermissionsService.cs new file mode 100644 index 00000000..7816aab3 --- /dev/null +++ b/src/NadekoBot/Services/Permissions/ResetPermissionsService.cs @@ -0,0 +1,44 @@ +using NadekoBot.Services.Database.Models; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Permissions +{ + public class ResetPermissionsService : INService + { + private readonly PermissionService _perms; + private readonly GlobalPermissionService _globalPerms; + private readonly DbService _db; + + public ResetPermissionsService(PermissionService perms, GlobalPermissionService globalPerms, DbService db) + { + _perms = perms; + _globalPerms = globalPerms; + _db = db; + } + + public async Task ResetPermissions(ulong guildId) + { + using (var uow = _db.UnitOfWork) + { + var config = uow.GuildConfigs.GcWithPermissionsv2For(guildId); + config.Permissions = Permissionv2.GetDefaultPermlist; + await uow.CompleteAsync().ConfigureAwait(false); + _perms.UpdateCache(config); + } + } + + public async Task ResetGlobalPermissions() + { + using (var uow = _db.UnitOfWork) + { + var gc = uow.BotConfig.GetOrCreate(); + gc.BlockedCommands.Clear(); + gc.BlockedModules.Clear(); + + _globalPerms.BlockedCommands.Clear(); + _globalPerms.BlockedModules.Clear(); + await uow.CompleteAsync().ConfigureAwait(false); + } + } + } +} diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 5622b936..e2d26adc 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -48,86 +48,10 @@ namespace NadekoBot.Extensions public static bool IsAuthor(this IMessage msg, IDiscordClient client) => msg.Author?.Id == client.CurrentUser.Id; - private static readonly IEmote arrow_left = new Emoji("⬅"); - private static readonly IEmote arrow_right = new Emoji("➡"); - - public static string ToBase64(this string plainText) - { - var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); - return Convert.ToBase64String(plainTextBytes); - } - 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 Stream ToStream(this IEnumerable bytes, bool canWrite = false) - { - var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); - ms.Seek(0, SeekOrigin.Begin); - return ms; - } - public static Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordSocketClient client, int currentPage, Func pageFunc, int? lastPage = null, bool addPaginatedFooter = true) => - channel.SendPaginatedConfirmAsync(client, currentPage, (x) => Task.FromResult(pageFunc(x)), lastPage, addPaginatedFooter); - /// - /// danny kamisama - /// - public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordSocketClient client, int currentPage, Func> 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 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); - } - - private static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage) + public static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage) { if (lastPage != null) return embed.WithFooter(efb => efb.WithText($"{curPage + 1} / {lastPage + 1}")); @@ -135,6 +59,12 @@ namespace NadekoBot.Extensions 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 reactionAdded, Action reactionRemoved = null) { if (reactionRemoved == null) @@ -153,17 +83,6 @@ namespace NadekoBot.Extensions http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); } - public static string GetInitials(this string txt, string glue = "") => - string.Join(glue, txt.Split(' ').Select(x => x.FirstOrDefault())); - - public static DateTime ToUnixTimestamp(this double number) => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(number); - - public static EmbedBuilder WithOkColor(this EmbedBuilder eb) => - eb.WithColor(NadekoBot.OkColor); - - public static EmbedBuilder WithErrorColor(this EmbedBuilder eb) => - eb.WithColor(NadekoBot.ErrorColor); - public static IMessage DeleteAfter(this IUserMessage msg, int seconds) { Task.Run(async () => @@ -184,29 +103,9 @@ namespace NadekoBot.Extensions return module; } - public static async Task 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 async Task> MentionedUsers(this IUserMessage msg) => - public static IEnumerable GetRoles(this IGuildUser user) => - user.RoleIds.Select(r => user.Guild.GetRole(r)).Where(r => r != null); - - public static IEnumerable ForEach(this IEnumerable elems, Action exec) - { - foreach (var elem in elems) - { - exec(elem); - } - return elems; - } - public static void AddRange(this HashSet target, IEnumerable elements) where T : class { foreach (var item in elements) @@ -223,90 +122,22 @@ namespace NadekoBot.Extensions } } - public static bool IsInteger(this decimal number) => number == Math.Truncate(number); - - public static string SanitizeMentions(this string str) => - str.Replace("@everyone", "@everyοne").Replace("@here", "@һere"); - public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds; - //public static async Task SendMessageAsync(this IUser user, string message, bool isTTS = false) => - // await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(message, isTTS).ConfigureAwait(false); - - public static async Task SendConfirmAsync(this IUser user, string text) - => await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text)); - - public static async Task 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); - } - - public static async Task 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); - } - - public static async Task SendErrorAsync(this IUser user, string error) - => await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error)); - - public static async Task 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 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 IEnumerable Members(this IRole role) => role.Guild.GetUsersAsync().GetAwaiter().GetResult().Where(u => u.RoleIds.Contains(role.Id)) ?? Enumerable.Empty(); + + public static string ToJson(this T any, Formatting formatting = Formatting.Indented) => + JsonConvert.SerializeObject(any, formatting); - public static Task EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "") - => ch.SendMessageAsync(msg, embed: embed); - - public static Task SendErrorAsync(this IMessageChannel ch, string title, string error, string url = null, string footer = null) + public static Stream ToStream(this ImageSharp.Image img) { - 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); + var imageStream = new MemoryStream(); + img.Save(imageStream); + imageStream.Position = 0; + return imageStream; } - public static Task SendErrorAsync(this IMessageChannel ch, string error) - => ch.SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error)); - - public static Task 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); - } - - public static Task SendConfirmAsync(this IMessageChannel ch, string text) - => ch.SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text)); - - public static Task SendTableAsync(this IMessageChannel ch, string seed, IEnumerable items, Func 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 SendTableAsync(this IMessageChannel ch, IEnumerable items, Func howToPrint, int columns = 3) => - ch.SendTableAsync("", items, howToPrint, columns); - /// /// returns an IEnumerable with randomized element order /// @@ -339,135 +170,32 @@ namespace NadekoBot.Extensions } } - /// - /// Easy use of fast, efficient case-insensitive Contains check with StringComparison Member Types - /// CurrentCulture, CurrentCultureIgnoreCase, InvariantCulture, InvariantCultureIgnoreCase, Ordinal, OrdinalIgnoreCase - /// - public static bool ContainsNoCase(this string str, string contains, StringComparison compare) + public static IEnumerable ForEach(this IEnumerable elems, Action exec) { - return str.IndexOf(contains, compare) >= 0; + foreach (var elem in elems) + { + exec(elem); + } + return elems; } - public static string TrimTo(this string str, int maxLength, bool hideDots = false) + public static Stream ToStream(this IEnumerable bytes, bool canWrite = 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); - } - - /// - /// Removes trailing S or ES (if specified) on the given string if the num is 1 - /// - /// - /// - /// - /// String with the correct singular/plural form - 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 ToStream(this string str) - { - var ms = new MemoryStream(); - var sw = new StreamWriter(ms); - await sw.WriteAsync(str); - await sw.FlushAsync(); - ms.Position = 0; + var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); + ms.Seek(0, SeekOrigin.Begin); return ms; - } - public static string ToJson(this T any, Formatting formatting = Formatting.Indented) => - JsonConvert.SerializeObject(any, formatting); + public static IEnumerable GetRoles(this IGuildUser user) => + user.RoleIds.Select(r => user.Guild.GetRole(r)).Where(r => r != null); - public static int KiB(this int value) => value * 1024; - public static int KB(this int value) => value * 1000; + public static async Task SendMessageToOwnerAsync(this IGuild guild, string message) + { + var ownerPrivate = await (await guild.GetOwnerAsync().ConfigureAwait(false)).GetOrCreateDMChannelAsync() + .ConfigureAwait(false); - 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 string Unmention(this string str) => str.Replace("@", "ම"); + return await ownerPrivate.SendMessageAsync(message).ConfigureAwait(false); + } public static ImageSharp.Image Merge(this IEnumerable images) { @@ -484,25 +212,5 @@ namespace NadekoBot.Extensions return canvas; } - - public static Stream ToStream(this ImageSharp.Image img) - { - var imageStream = new MemoryStream(); - img.Save(imageStream); - imageStream.Position = 0; - return imageStream; - } - - private static readonly Regex filterRegex = new Regex(@"(?:discord(?:\.gg|.me|app\.com\/invite)\/(?([\w]{16}|(?:[\w]+-?){3})))", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - public static bool IsDiscordInvite(this string str) - => filterRegex.IsMatch(str); - - public static string RealAvatarUrl(this IUser usr) - { - return usr.AvatarId.StartsWith("a_") - ? $"{DiscordConfig.CDNUrl}avatars/{usr.Id}/{usr.AvatarId}.gif" - : usr.GetAvatarUrl(ImageFormat.Auto); - } } } \ No newline at end of file diff --git a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs new file mode 100644 index 00000000..806ec904 --- /dev/null +++ b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs @@ -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 EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "") + => ch.SendMessageAsync(msg, embed: embed); + + public static Task 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); + } + + public static Task SendErrorAsync(this IMessageChannel ch, string error) + => ch.SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error)); + + public static Task 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); + } + + public static Task SendConfirmAsync(this IMessageChannel ch, string text) + => ch.SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text)); + + public static Task SendTableAsync(this IMessageChannel ch, string seed, IEnumerable items, Func 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 SendTableAsync(this IMessageChannel ch, IEnumerable items, Func 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 pageFunc, int? lastPage = null, bool addPaginatedFooter = true) => + channel.SendPaginatedConfirmAsync(client, currentPage, (x) => Task.FromResult(pageFunc(x)), lastPage, addPaginatedFooter); + /// + /// danny kamisama + /// + public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, DiscordSocketClient client, int currentPage, Func> 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 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); + } + } +} diff --git a/src/NadekoBot/_Extensions/IUserExtensions.cs b/src/NadekoBot/_Extensions/IUserExtensions.cs new file mode 100644 index 00000000..d1183e19 --- /dev/null +++ b/src/NadekoBot/_Extensions/IUserExtensions.cs @@ -0,0 +1,43 @@ +using Discord; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace NadekoBot.Extensions +{ + public static class IUserExtensions + { + public static async Task SendConfirmAsync(this IUser user, string text) + => await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithOkColor().WithDescription(text)); + + public static async Task 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); + } + + public static async Task 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); + } + + public static async Task SendErrorAsync(this IUser user, string error) + => await (await user.GetOrCreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithErrorColor().WithDescription(error)); + + public static async Task 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 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); + } +} diff --git a/src/NadekoBot/_Extensions/NumberExtensions.cs b/src/NadekoBot/_Extensions/NumberExtensions.cs new file mode 100644 index 00000000..b708841f --- /dev/null +++ b/src/NadekoBot/_Extensions/NumberExtensions.cs @@ -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); + } +} diff --git a/src/NadekoBot/_Extensions/StringExtensions.cs b/src/NadekoBot/_Extensions/StringExtensions.cs new file mode 100644 index 00000000..fb083882 --- /dev/null +++ b/src/NadekoBot/_Extensions/StringExtensions.cs @@ -0,0 +1,136 @@ +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 + { + /// + /// Easy use of fast, efficient case-insensitive Contains check with StringComparison Member Types + /// CurrentCulture, CurrentCultureIgnoreCase, InvariantCulture, InvariantCultureIgnoreCase, Ordinal, OrdinalIgnoreCase + /// + 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); + } + + /// + /// Removes trailing S or ES (if specified) on the given string if the num is 1 + /// + /// + /// + /// + /// String with the correct singular/plural form + 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 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)\/(?([\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())); + } +}