Some cleanup and many music improvements
This commit is contained in:
		@@ -11,6 +11,7 @@ using Discord.WebSocket;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.ClashOfClans
 | 
			
		||||
{
 | 
			
		||||
@@ -19,6 +20,8 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
			
		||||
    {
 | 
			
		||||
        public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
 | 
			
		||||
 | 
			
		||||
        private static Timer checkWarTimer { get; }
 | 
			
		||||
 | 
			
		||||
        static ClashOfClans()
 | 
			
		||||
        {
 | 
			
		||||
            using (var uow = DbHandler.UnitOfWork())
 | 
			
		||||
@@ -32,13 +35,21 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
			
		||||
                                                         ?.GetTextChannel(cw.ChannelId);
 | 
			
		||||
                            return cw;
 | 
			
		||||
                        })
 | 
			
		||||
                        .Where(cw => cw?.Channel != null)
 | 
			
		||||
                        .Where(cw => cw.Channel != null)
 | 
			
		||||
                        .GroupBy(cw => cw.GuildId)
 | 
			
		||||
                        .ToDictionary(g => g.Key, g => g.ToList()));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public ClashOfClans() : base()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            checkWarTimer = new Timer(async _ =>
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var kvp in ClashWars)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var war in kvp.Value)
 | 
			
		||||
                    {
 | 
			
		||||
                        try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
 | 
			
		||||
@@ -46,12 +57,21 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
			
		||||
            var Bases = war.Bases;
 | 
			
		||||
            for (var i = 0; i < Bases.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                if (Bases[i].CallUser == null) continue;
 | 
			
		||||
                if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
 | 
			
		||||
                var callUser = Bases[i].CallUser;
 | 
			
		||||
                if (callUser == null) continue;
 | 
			
		||||
                if ((!Bases[i].BaseDestroyed) && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
 | 
			
		||||
                {
 | 
			
		||||
                    Bases[i] = null;
 | 
			
		||||
                    try { await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); } catch { }
 | 
			
		||||
            }
 | 
			
		||||
                    if (Bases[i].Stars != 3)
 | 
			
		||||
                        Bases[i].BaseDestroyed = true;
 | 
			
		||||
                    else
 | 
			
		||||
                        Bases[i] = null;
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        SaveWar(war);
 | 
			
		||||
                        await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch { }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -216,7 +236,7 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
			
		||||
        {
 | 
			
		||||
            var channel = (ITextChannel)umsg.Channel;
 | 
			
		||||
 | 
			
		||||
            var warsInfo = GetWarInfo(umsg,number);
 | 
			
		||||
            var warsInfo = GetWarInfo(umsg, number);
 | 
			
		||||
            if (warsInfo == null)
 | 
			
		||||
            {
 | 
			
		||||
                await channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false);
 | 
			
		||||
@@ -359,4 +379,4 @@ namespace NadekoBot.Modules.ClashOfClans
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -115,7 +115,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
			
		||||
                reactions.Add(cr);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await imsg.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
            await imsg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                .WithTitle("New Custom Reaction")
 | 
			
		||||
                .WithDescription($"#{cr.Id}")
 | 
			
		||||
                .AddField(efb => efb.WithName("Trigger").WithValue(key))
 | 
			
		||||
@@ -225,7 +225,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
			
		||||
                await imsg.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await imsg.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                await imsg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                    .WithDescription($"#{id}")
 | 
			
		||||
                    .AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
 | 
			
		||||
                    .AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
 | 
			
		||||
@@ -304,7 +304,7 @@ namespace NadekoBot.Modules.CustomReactions
 | 
			
		||||
            await imsg.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value)
 | 
			
		||||
                                               .Skip((page - 1)*9)
 | 
			
		||||
                                               .Take(9)
 | 
			
		||||
                                               .Aggregate(new EmbedBuilder().WithColor(NadekoBot.OkColor).WithTitle($"Custom Reaction stats page #{page}"),
 | 
			
		||||
                                               .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"),
 | 
			
		||||
                                                         (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true)))
 | 
			
		||||
                                                         .Build())
 | 
			
		||||
                              .ConfigureAwait(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
 | 
			
		||||
            if (Errors >= MaxErrors)
 | 
			
		||||
                await GameChannel.EmbedAsync(embed.WithColor(NadekoBot.ErrorColor).Build()).ConfigureAwait(false);
 | 
			
		||||
            else
 | 
			
		||||
                await GameChannel.EmbedAsync(embed.WithColor(NadekoBot.OkColor).Build()).ConfigureAwait(false);
 | 
			
		||||
                await GameChannel.EmbedAsync(embed.WithOkColor().Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Task PotentialGuess(IMessage msg)
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Games.Trivia
 | 
			
		||||
            Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                      .WithTitle("Leaderboard")
 | 
			
		||||
                      .WithDescription(GetLeaderboard())
 | 
			
		||||
                      .Build(), "Trivia game ended.").ConfigureAwait(false);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Games
 | 
			
		||||
                return;
 | 
			
		||||
                var rng = new NadekoRandom();
 | 
			
		||||
 | 
			
		||||
            await channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
            await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                               .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
 | 
			
		||||
                               .AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses.Shuffle().FirstOrDefault()).WithIsInline(false))
 | 
			
		||||
                               .Build());
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Help
 | 
			
		||||
                var embed = new EmbedBuilder()
 | 
			
		||||
                .AddField(fb => fb.WithIndex(1).WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Prefix)} { GetCommandRequirements(com)}").WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithIndex(2).WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Prefix)}").WithIsInline(false))
 | 
			
		||||
                .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,9 +41,9 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
 | 
			
		||||
        public float Volume { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public event EventHandler<Song> OnCompleted = delegate { };
 | 
			
		||||
        public event EventHandler<Song> OnStarted = delegate { };
 | 
			
		||||
        public event Action<bool> OnPauseChanged = delegate { };
 | 
			
		||||
        public event Func<MusicPlayer, Song, Task> OnCompleted = delegate { return Task.CompletedTask; };
 | 
			
		||||
        public event Func<MusicPlayer, Song, Task> OnStarted = delegate { return Task.CompletedTask; };
 | 
			
		||||
        public event Func<bool, Task> OnPauseChanged = delegate { return Task.CompletedTask; };
 | 
			
		||||
 | 
			
		||||
        public IVoiceChannel PlaybackVoiceChannel { get; private set; }
 | 
			
		||||
 | 
			
		||||
@@ -250,11 +250,11 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
        {
 | 
			
		||||
            var curSong = CurrentSong;
 | 
			
		||||
            var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
 | 
			
		||||
                                                          s.TotalLength == TimeSpan.Zero);
 | 
			
		||||
                                                            s.TotalTime == TimeSpan.Zero);
 | 
			
		||||
            if (curSong != null)
 | 
			
		||||
                toUpdate = toUpdate.Append(curSong);
 | 
			
		||||
            var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3))
 | 
			
		||||
                              .Distinct();
 | 
			
		||||
                                .Distinct();
 | 
			
		||||
 | 
			
		||||
            var durations = await NadekoBot.Google.GetVideoDurationsAsync(ids);
 | 
			
		||||
 | 
			
		||||
@@ -264,11 +264,12 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
                {
 | 
			
		||||
                    if (s.SongInfo.Query.EndsWith(kvp.Key))
 | 
			
		||||
                    {
 | 
			
		||||
                        s.TotalLength = kvp.Value;
 | 
			
		||||
                        s.TotalTime = kvp.Value;
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Destroy()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/NadekoBot/Modules/Music/Classes/MusicExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/NadekoBot/Modules/Music/Classes/MusicExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
{
 | 
			
		||||
    public static class MusicExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static EmbedAuthorBuilder WithMusicIcon(this EmbedAuthorBuilder eab) =>
 | 
			
		||||
            eab.WithIconUrl("http://i.imgur.com/nhKS3PT.png");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -18,60 +18,54 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
    {
 | 
			
		||||
        public string Provider { get; set; }
 | 
			
		||||
        public MusicType ProviderType { get; set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Will be set only if the providertype is normal
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Query { get; set; }
 | 
			
		||||
        public string Title { get; set; }
 | 
			
		||||
        public string Uri { get; set; }
 | 
			
		||||
    public string AlbumArt { get; set; }
 | 
			
		||||
        public string AlbumArt { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class Song
 | 
			
		||||
    {
 | 
			
		||||
    public StreamState State { get; set; }
 | 
			
		||||
        public string PrettyName =>
 | 
			
		||||
            $"**{SongInfo.Title.TrimTo(55)} `{(SongInfo.Provider ?? "-")} by {QueuerName}`**";
 | 
			
		||||
        //$"{SongInfo.Title.TrimTo(70)}";
 | 
			
		||||
        public SongInfo SongInfo { get; }
 | 
			
		||||
        public MusicPlayer MusicPlayer { get; set; }
 | 
			
		||||
        
 | 
			
		||||
        public string PrettyUser =>
 | 
			
		||||
            $"{QueuerName}";
 | 
			
		||||
        public string QueuerName { get; set; }
 | 
			
		||||
        
 | 
			
		||||
        public string PrettyProvider =>
 | 
			
		||||
            $"{(SongInfo.Provider ?? "No Provider")}";
 | 
			
		||||
 | 
			
		||||
        public string PrettyCurrentTime()
 | 
			
		||||
        {
 | 
			
		||||
            var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
 | 
			
		||||
            //var str = $"{(int)time.TotalMinutes}m {time.Seconds}s / ";
 | 
			
		||||
			var str = $"";
 | 
			
		||||
            if (TotalLength == TimeSpan.Zero)
 | 
			
		||||
                str += "(?)";
 | 
			
		||||
            else if (TotalLength == TimeSpan.MaxValue)
 | 
			
		||||
                str += "**∞**";
 | 
			
		||||
            else
 | 
			
		||||
                str += $"{(int)TotalLength.TotalMinutes}m {TotalLength.Seconds}s";
 | 
			
		||||
            return str;
 | 
			
		||||
        }
 | 
			
		||||
		public string PrettyMusicPlayTime()
 | 
			
		||||
		{
 | 
			
		||||
		var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
 | 
			
		||||
        var str = $"{(int)time.TotalMinutes}m {time.Seconds}s";
 | 
			
		||||
		return str;
 | 
			
		||||
		}
 | 
			
		||||
        public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
 | 
			
		||||
        public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / frameBytes / 1000 / milliseconds);
 | 
			
		||||
 | 
			
		||||
        const int milliseconds = 20;
 | 
			
		||||
        const int samplesPerFrame = (48000 / 1000) * milliseconds;
 | 
			
		||||
        const int frameBytes = 3840; //16-bit, 2 channels
 | 
			
		||||
 | 
			
		||||
        private ulong bytesSent { get; set; } = 0;
 | 
			
		||||
 | 
			
		||||
        public bool PrintStatusMessage { get; set; } = true;
 | 
			
		||||
        //pwetty
 | 
			
		||||
 | 
			
		||||
        public string PrettyProvider =>
 | 
			
		||||
            $"{(SongInfo.Provider ?? "No Provider")}";
 | 
			
		||||
 | 
			
		||||
        public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime;
 | 
			
		||||
 | 
			
		||||
        public string PrettyName => $"**[{SongInfo.Title.TrimTo(70)}]({SongInfo.Query})**";
 | 
			
		||||
 | 
			
		||||
        public string PrettyInfo => $"{PrettyTotalTime} | {PrettyProvider} | {QueuerName}";
 | 
			
		||||
 | 
			
		||||
        public string PrettyFullName => $"{PrettyName}\n\t\t*{PrettyInfo}*";
 | 
			
		||||
 | 
			
		||||
        public string PrettyCurrentTime => TotalTime.ToString(@"mm\:ss");
 | 
			
		||||
 | 
			
		||||
        private string PrettyTotalTime {
 | 
			
		||||
            get {
 | 
			
		||||
                if (TotalTime == TimeSpan.Zero)
 | 
			
		||||
                    return "(?)";
 | 
			
		||||
                else if (TotalTime == TimeSpan.MaxValue)
 | 
			
		||||
                    return "**∞**";
 | 
			
		||||
                else
 | 
			
		||||
                    return TotalTime.ToString(@"mm\:ss");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private int skipTo = 0;
 | 
			
		||||
        private Logger _log;
 | 
			
		||||
 | 
			
		||||
        public int SkipTo {
 | 
			
		||||
            get { return skipTo; }
 | 
			
		||||
            set {
 | 
			
		||||
@@ -80,7 +74,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TimeSpan TotalLength { get; set; } = TimeSpan.Zero;
 | 
			
		||||
        private readonly Logger _log;
 | 
			
		||||
 | 
			
		||||
        public Song(SongInfo songInfo)
 | 
			
		||||
        {
 | 
			
		||||
@@ -92,16 +86,9 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
        {
 | 
			
		||||
            var s = new Song(SongInfo);
 | 
			
		||||
            s.MusicPlayer = MusicPlayer;
 | 
			
		||||
            s.State = StreamState.Queued;
 | 
			
		||||
            return s;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Song SetMusicPlayer(MusicPlayer mp)
 | 
			
		||||
        {
 | 
			
		||||
            this.MusicPlayer = mp;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
 | 
			
		||||
        {
 | 
			
		||||
            var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
 | 
			
		||||
@@ -111,7 +98,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var attempt = 0;             
 | 
			
		||||
                var attempt = 0;
 | 
			
		||||
 | 
			
		||||
                var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken, 1.MiB()); //Fast connection can do this easy
 | 
			
		||||
                var finished = false;
 | 
			
		||||
@@ -132,7 +119,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
                            _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.");
 | 
			
		||||
@@ -142,7 +129,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
                        {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                     }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (prebufferingTask.IsCanceled)
 | 
			
		||||
                    {
 | 
			
		||||
                        _log.Debug("Prebuffering canceled. Cannot get any data from the stream.");
 | 
			
		||||
@@ -151,7 +138,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
                    finished = true;
 | 
			
		||||
                }
 | 
			
		||||
                sw.Stop();
 | 
			
		||||
                _log.Debug("Prebuffering successfully completed in "+ sw.Elapsed);
 | 
			
		||||
                _log.Debug("Prebuffering successfully completed in " + sw.Elapsed);
 | 
			
		||||
 | 
			
		||||
                var outStream = voiceClient.CreatePCMStream(960);
 | 
			
		||||
 | 
			
		||||
@@ -163,7 +150,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
                    //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
 | 
			
		||||
                    var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
 | 
			
		||||
                    //await inStream.CopyToAsync(voiceClient.OutputStream);
 | 
			
		||||
                    if(read < frameBytes)
 | 
			
		||||
                    if (read < frameBytes)
 | 
			
		||||
                        _log.Debug("read {0}", read);
 | 
			
		||||
                    unchecked
 | 
			
		||||
                    {
 | 
			
		||||
@@ -210,7 +197,7 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                await bufferTask;
 | 
			
		||||
                if(inStream != null)
 | 
			
		||||
                if (inStream != null)
 | 
			
		||||
                    inStream.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -224,35 +211,6 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
            _log.Debug("Buffering successfull");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        //stackoverflow ftw
 | 
			
		||||
        private static byte[] AdjustVolume(byte[] audioSamples, float volume)
 | 
			
		||||
        {
 | 
			
		||||
            if (Math.Abs(volume - 1.0f) < 0.01f)
 | 
			
		||||
                return audioSamples;
 | 
			
		||||
            var array = new byte[audioSamples.Length];
 | 
			
		||||
            for (var i = 0; i < array.Length; i += 2)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                // convert byte pair to int
 | 
			
		||||
                short buf1 = audioSamples[i + 1];
 | 
			
		||||
                short buf2 = audioSamples[i];
 | 
			
		||||
 | 
			
		||||
                buf1 = (short)((buf1 & 0xff) << 8);
 | 
			
		||||
                buf2 = (short)(buf2 & 0xff);
 | 
			
		||||
 | 
			
		||||
                var res = (short)(buf1 | buf2);
 | 
			
		||||
                res = (short)(res * volume);
 | 
			
		||||
 | 
			
		||||
                // convert back
 | 
			
		||||
                array[i] = (byte)res;
 | 
			
		||||
                array[i + 1] = (byte)(res >> 8);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            return array;
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        //aidiakapi ftw
 | 
			
		||||
        public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
 | 
			
		||||
        {
 | 
			
		||||
@@ -278,202 +236,5 @@ namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
 | 
			
		||||
            return audioSamples;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(query))
 | 
			
		||||
                throw new ArgumentNullException(nameof(query));
 | 
			
		||||
 | 
			
		||||
            if (musicType != MusicType.Local && IsRadioLink(query))
 | 
			
		||||
            {
 | 
			
		||||
                musicType = MusicType.Radio;
 | 
			
		||||
                query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                switch (musicType)
 | 
			
		||||
                {
 | 
			
		||||
                    case MusicType.Local:
 | 
			
		||||
                        return new Song(new SongInfo
 | 
			
		||||
                        {
 | 
			
		||||
                            Uri = "\"" + Path.GetFullPath(query) + "\"",
 | 
			
		||||
                            Title = Path.GetFileNameWithoutExtension(query),
 | 
			
		||||
                            Provider = "Local File",
 | 
			
		||||
                            ProviderType = musicType,
 | 
			
		||||
                            Query = query,
 | 
			
		||||
                        });
 | 
			
		||||
                    case MusicType.Radio:
 | 
			
		||||
                        return new Song(new SongInfo
 | 
			
		||||
                        {
 | 
			
		||||
                            Uri = query,
 | 
			
		||||
                            Title = $"{query}",
 | 
			
		||||
                            Provider = "Radio Stream",
 | 
			
		||||
                            ProviderType = musicType,
 | 
			
		||||
                            Query = query
 | 
			
		||||
                        })
 | 
			
		||||
                        { TotalLength = TimeSpan.MaxValue };
 | 
			
		||||
                }
 | 
			
		||||
                if (SoundCloud.Default.IsSoundCloudLink(query))
 | 
			
		||||
                {
 | 
			
		||||
                    var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
 | 
			
		||||
                    return new Song(new SongInfo
 | 
			
		||||
                    {
 | 
			
		||||
                        Title = svideo.FullName,
 | 
			
		||||
                        Provider = "SoundCloud",
 | 
			
		||||
                        Uri = svideo.StreamLink,
 | 
			
		||||
                        ProviderType = musicType,
 | 
			
		||||
                        Query = svideo.TrackLink,
 | 
			
		||||
            AlbumArt = svideo.artwork_url,
 | 
			
		||||
                    })
 | 
			
		||||
                    { TotalLength = TimeSpan.FromMilliseconds(svideo.Duration) };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (musicType == MusicType.Soundcloud)
 | 
			
		||||
                {
 | 
			
		||||
                    var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
 | 
			
		||||
                    return new Song(new SongInfo
 | 
			
		||||
                    {
 | 
			
		||||
                        Title = svideo.FullName,
 | 
			
		||||
                        Provider = "SoundCloud",
 | 
			
		||||
                        Uri = svideo.StreamLink,
 | 
			
		||||
                        ProviderType = MusicType.Normal,
 | 
			
		||||
                        Query = svideo.TrackLink,
 | 
			
		||||
            AlbumArt = svideo.artwork_url,
 | 
			
		||||
                    })
 | 
			
		||||
                    { TotalLength = TimeSpan.FromMilliseconds(svideo.Duration) };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
 | 
			
		||||
                if (string.IsNullOrWhiteSpace(link))
 | 
			
		||||
                    throw new OperationCanceledException("Not a valid youtube query.");
 | 
			
		||||
                var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
 | 
			
		||||
                var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
 | 
			
		||||
                var video = videos
 | 
			
		||||
                    .Where(v => v.AudioBitrate < 256)
 | 
			
		||||
                    .OrderByDescending(v => v.AudioBitrate)
 | 
			
		||||
                    .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
                if (video == null) // do something with this error
 | 
			
		||||
                    throw new Exception("Could not load any video elements based on the query.");
 | 
			
		||||
                var m = Regex.Match(query, @"\?t=(?<t>\d*)");
 | 
			
		||||
                int gotoTime = 0;
 | 
			
		||||
                if (m.Captures.Count > 0)
 | 
			
		||||
                    int.TryParse(m.Groups["t"].ToString(), out gotoTime);
 | 
			
		||||
                var song = new Song(new SongInfo
 | 
			
		||||
                {
 | 
			
		||||
                    Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
 | 
			
		||||
                    Provider = "YouTube",
 | 
			
		||||
                    Uri = video.Uri,
 | 
			
		||||
                    Query = link,
 | 
			
		||||
                    ProviderType = musicType,
 | 
			
		||||
                });
 | 
			
		||||
                song.SkipTo = gotoTime;
 | 
			
		||||
                return song;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine($"Failed resolving the link.{ex.Message}");
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static async Task<string> HandleStreamContainers(string query)
 | 
			
		||||
        {
 | 
			
		||||
            string file = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                using (var http = new HttpClient())
 | 
			
		||||
                {
 | 
			
		||||
                    file = await http.GetStringAsync(query).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return query;
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".pls"))
 | 
			
		||||
            {
 | 
			
		||||
                //File1=http://armitunes.com:8000/
 | 
			
		||||
                //Regex.Match(query)
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .pls:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".m3u"))
 | 
			
		||||
            {
 | 
			
		||||
                /* 
 | 
			
		||||
# This is a comment
 | 
			
		||||
                   C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
 | 
			
		||||
                   C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
 | 
			
		||||
                */
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .m3u:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".asx"))
 | 
			
		||||
            {
 | 
			
		||||
                //<ref href="http://armitunes.com:8000"/>
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .asx:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".xspf"))
 | 
			
		||||
            {
 | 
			
		||||
                /*
 | 
			
		||||
                <?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
                    <playlist version="1" xmlns="http://xspf.org/ns/0/">
 | 
			
		||||
                        <trackList>
 | 
			
		||||
                            <track><location>file:///mp3s/song_1.mp3</location></track>
 | 
			
		||||
                */
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .xspf:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return query;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static bool IsRadioLink(string query) =>
 | 
			
		||||
            (query.StartsWith("http") ||
 | 
			
		||||
            query.StartsWith("ww"))
 | 
			
		||||
            &&
 | 
			
		||||
            (query.Contains(".pls") ||
 | 
			
		||||
            query.Contains(".m3u") ||
 | 
			
		||||
            query.Contains(".asx") ||
 | 
			
		||||
            query.Contains(".xspf"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										212
									
								
								src/NadekoBot/Modules/Music/Classes/SongHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								src/NadekoBot/Modules/Music/Classes/SongHandler.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,212 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using VideoLibrary;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Music.Classes
 | 
			
		||||
{
 | 
			
		||||
    public static class SongHandler
 | 
			
		||||
    {
 | 
			
		||||
        public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(query))
 | 
			
		||||
                throw new ArgumentNullException(nameof(query));
 | 
			
		||||
 | 
			
		||||
            if (musicType != MusicType.Local && IsRadioLink(query))
 | 
			
		||||
            {
 | 
			
		||||
                musicType = MusicType.Radio;
 | 
			
		||||
                query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                switch (musicType)
 | 
			
		||||
                {
 | 
			
		||||
                    case MusicType.Local:
 | 
			
		||||
                        return new Song(new SongInfo
 | 
			
		||||
                        {
 | 
			
		||||
                            Uri = "\"" + Path.GetFullPath(query) + "\"",
 | 
			
		||||
                            Title = Path.GetFileNameWithoutExtension(query),
 | 
			
		||||
                            Provider = "Local File",
 | 
			
		||||
                            ProviderType = musicType,
 | 
			
		||||
                            Query = query,
 | 
			
		||||
                        });
 | 
			
		||||
                    case MusicType.Radio:
 | 
			
		||||
                        return new Song(new SongInfo
 | 
			
		||||
                        {
 | 
			
		||||
                            Uri = query,
 | 
			
		||||
                            Title = $"{query}",
 | 
			
		||||
                            Provider = "Radio Stream",
 | 
			
		||||
                            ProviderType = musicType,
 | 
			
		||||
                            Query = query
 | 
			
		||||
                        })
 | 
			
		||||
                        { TotalTime = TimeSpan.MaxValue };
 | 
			
		||||
                }
 | 
			
		||||
                if (SoundCloud.Default.IsSoundCloudLink(query))
 | 
			
		||||
                {
 | 
			
		||||
                    var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
 | 
			
		||||
                    return new Song(new SongInfo
 | 
			
		||||
                    {
 | 
			
		||||
                        Title = svideo.FullName,
 | 
			
		||||
                        Provider = "SoundCloud",
 | 
			
		||||
                        Uri = svideo.StreamLink,
 | 
			
		||||
                        ProviderType = musicType,
 | 
			
		||||
                        Query = svideo.TrackLink,
 | 
			
		||||
                        AlbumArt = svideo.artwork_url,
 | 
			
		||||
                    })
 | 
			
		||||
                    { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (musicType == MusicType.Soundcloud)
 | 
			
		||||
                {
 | 
			
		||||
                    var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
 | 
			
		||||
                    return new Song(new SongInfo
 | 
			
		||||
                    {
 | 
			
		||||
                        Title = svideo.FullName,
 | 
			
		||||
                        Provider = "SoundCloud",
 | 
			
		||||
                        Uri = svideo.StreamLink,
 | 
			
		||||
                        ProviderType = MusicType.Normal,
 | 
			
		||||
                        Query = svideo.TrackLink,
 | 
			
		||||
                        AlbumArt = svideo.artwork_url,
 | 
			
		||||
                    })
 | 
			
		||||
                    { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
 | 
			
		||||
                if (string.IsNullOrWhiteSpace(link))
 | 
			
		||||
                    throw new OperationCanceledException("Not a valid youtube query.");
 | 
			
		||||
                var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
 | 
			
		||||
                var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
 | 
			
		||||
                var video = videos
 | 
			
		||||
                    .Where(v => v.AudioBitrate < 256)
 | 
			
		||||
                    .OrderByDescending(v => v.AudioBitrate)
 | 
			
		||||
                    .FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
                if (video == null) // do something with this error
 | 
			
		||||
                    throw new Exception("Could not load any video elements based on the query.");
 | 
			
		||||
                var m = Regex.Match(query, @"\?t=(?<t>\d*)");
 | 
			
		||||
                int gotoTime = 0;
 | 
			
		||||
                if (m.Captures.Count > 0)
 | 
			
		||||
                    int.TryParse(m.Groups["t"].ToString(), out gotoTime);
 | 
			
		||||
                var song = new Song(new SongInfo
 | 
			
		||||
                {
 | 
			
		||||
                    Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
 | 
			
		||||
                    Provider = "YouTube",
 | 
			
		||||
                    Uri = video.Uri,
 | 
			
		||||
                    Query = link,
 | 
			
		||||
                    ProviderType = musicType,
 | 
			
		||||
                });
 | 
			
		||||
                song.SkipTo = gotoTime;
 | 
			
		||||
                return song;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine($"Failed resolving the link.{ex.Message}");
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static async Task<string> HandleStreamContainers(string query)
 | 
			
		||||
        {
 | 
			
		||||
            string file = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                using (var http = new HttpClient())
 | 
			
		||||
                {
 | 
			
		||||
                    file = await http.GetStringAsync(query).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
                return query;
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".pls"))
 | 
			
		||||
            {
 | 
			
		||||
                //File1=http://armitunes.com:8000/
 | 
			
		||||
                //Regex.Match(query)
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .pls:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".m3u"))
 | 
			
		||||
            {
 | 
			
		||||
                /* 
 | 
			
		||||
# This is a comment
 | 
			
		||||
                   C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
 | 
			
		||||
                   C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
 | 
			
		||||
                */
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .m3u:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".asx"))
 | 
			
		||||
            {
 | 
			
		||||
                //<ref href="http://armitunes.com:8000"/>
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .asx:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (query.Contains(".xspf"))
 | 
			
		||||
            {
 | 
			
		||||
                /*
 | 
			
		||||
                <?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
                    <playlist version="1" xmlns="http://xspf.org/ns/0/">
 | 
			
		||||
                        <trackList>
 | 
			
		||||
                            <track><location>file:///mp3s/song_1.mp3</location></track>
 | 
			
		||||
                */
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
 | 
			
		||||
                    var res = m.Groups["url"]?.ToString();
 | 
			
		||||
                    return res?.Trim();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    Console.WriteLine($"Failed reading .xspf:\n{file}");
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return query;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static bool IsRadioLink(string query) =>
 | 
			
		||||
            (query.StartsWith("http") ||
 | 
			
		||||
            query.StartsWith("ww"))
 | 
			
		||||
            &&
 | 
			
		||||
            (query.Contains(".pls") ||
 | 
			
		||||
            query.Contains(".m3u") ||
 | 
			
		||||
            query.Contains(".asx") ||
 | 
			
		||||
            query.Contains(".xspf"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -29,13 +29,13 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
        {
 | 
			
		||||
            //it can fail if its currenctly opened or doesn't exist. Either way i don't care
 | 
			
		||||
            try { Directory.Delete(MusicDataPath, true); } catch { }
 | 
			
		||||
	    
 | 
			
		||||
	    NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
 | 
			
		||||
 | 
			
		||||
            NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
 | 
			
		||||
 | 
			
		||||
            Directory.CreateDirectory(MusicDataPath);
 | 
			
		||||
        }
 | 
			
		||||
	
 | 
			
		||||
	private Task Client_UserVoiceStateUpdated(IUser iusr, IVoiceState oldState, IVoiceState newState)
 | 
			
		||||
 | 
			
		||||
        private Task Client_UserVoiceStateUpdated(IUser iusr, IVoiceState oldState, IVoiceState newState)
 | 
			
		||||
        {
 | 
			
		||||
            var usr = iusr as IGuildUser;
 | 
			
		||||
            if (usr == null ||
 | 
			
		||||
@@ -98,12 +98,12 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
 | 
			
		||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
        [RequireContext(ContextType.Guild)]
 | 
			
		||||
		public async Task Destroy(IUserMessage umsg)
 | 
			
		||||
        public async Task Destroy(IUserMessage umsg)
 | 
			
		||||
        //public Task Destroy(IUserMessage umsg)
 | 
			
		||||
        {
 | 
			
		||||
            var channel = (ITextChannel)umsg.Channel;
 | 
			
		||||
			await channel.SendErrorAsync("Command is temporarily disabled.").ConfigureAwait(false);
 | 
			
		||||
			
 | 
			
		||||
            await channel.SendErrorAsync("This command is temporarily disabled.").ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
            /*MusicPlayer musicPlayer;
 | 
			
		||||
            if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask;
 | 
			
		||||
            if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
 | 
			
		||||
@@ -175,55 +175,35 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (currentSong.TotalLength == TimeSpan.Zero)
 | 
			
		||||
            {
 | 
			
		||||
                await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
			
 | 
			
		||||
            try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
 | 
			
		||||
 | 
			
		||||
            //var toSend = $"🎵 Currently Playing {currentSong.PrettyName} " + $"`{currentSong.PrettyCurrentTime()}`\n";
 | 
			
		||||
	    //var toSend = $"🎵 Currently Playing {currentSong.PrettyName}\n";
 | 
			
		||||
		
 | 
			
		||||
		//I did that ^ because current song, bugs when a youtube playlist is queued with more than 50 song, it more like a bug with youtube page token I believe.
 | 
			
		||||
			
 | 
			
		||||
			const int itemsPerPage = 10;
 | 
			
		||||
            const int itemsPerPage = 10;
 | 
			
		||||
            int startAt = itemsPerPage * (page - 1);
 | 
			
		||||
            var number = 1 + startAt;
 | 
			
		||||
            var number = 0 + startAt;
 | 
			
		||||
 | 
			
		||||
		var embed = new EmbedBuilder()
 | 
			
		||||
		    .WithAuthor(eab => eab.WithName($"Track List: Page {page}").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
			.WithDescription(string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(10).Select(v => $"`{number++}.` **[{v.SongInfo.Title.TrimTo(70)}]({v.SongInfo.Query})**\n\t\t*{v.PrettyCurrentTime()}* **|** *{v.PrettyProvider}* **|** *{v.QueuerName}*")))
 | 
			
		||||
            .WithFooter(ef => ef.WithText($"{musicPlayer.Playlist.Count} tracks currently queued."))
 | 
			
		||||
			.WithColor(NadekoBot.OkColor);
 | 
			
		||||
		if (musicPlayer.RepeatSong)
 | 
			
		||||
		{	 
 | 
			
		||||
			embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyMusicPlayTime()} / {currentSong.PrettyCurrentTime()}");
 | 
			
		||||
		}
 | 
			
		||||
		else if (musicPlayer.RepeatPlaylist)
 | 
			
		||||
		{	 
 | 
			
		||||
			embed.WithTitle("🔁 Repeating Playlist");
 | 
			
		||||
		}
 | 
			
		||||
		if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
 | 
			
		||||
		{	 
 | 
			
		||||
			embed.WithTitle("🎵 Song queue is full!");
 | 
			
		||||
		}
 | 
			
		||||
        await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		//var toSend = $"test"; //this was for testing
 | 
			
		||||
            //if (musicPlayer.RepeatSong)
 | 
			
		||||
                //toSend += "🔂";
 | 
			
		||||
            //else if (musicPlayer.RepeatPlaylist)
 | 
			
		||||
                //toSend += "🔁";
 | 
			
		||||
            //toSend += $" `{musicPlayer.Playlist.Count} tracks currently queued. Showing page {page}:` ";
 | 
			
		||||
            //if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
 | 
			
		||||
                //toSend += "**Song queue is full!**\n";
 | 
			
		||||
            //else
 | 
			
		||||
                //toSend += "\n";
 | 
			
		||||
            //const int itemsPerPage = 15;
 | 
			
		||||
            //int startAt = itemsPerPage * (page - 1);
 | 
			
		||||
            //var number = 1 + startAt;
 | 
			
		||||
            //await channel.SendConfirmAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false);
 | 
			
		||||
            var embed = new EmbedBuilder()
 | 
			
		||||
                .WithAuthor(eab => eab.WithName($"Player Queue: Page {page}")
 | 
			
		||||
                                      .WithMusicIcon())
 | 
			
		||||
                .WithDescription(string.Join("\n", musicPlayer.Playlist
 | 
			
		||||
                    .Skip(startAt)
 | 
			
		||||
                    .Take(10)
 | 
			
		||||
                    .Select(v => $"`{++number}.` {v.PrettyFullName}")))
 | 
			
		||||
                .WithFooter(ef => ef.WithText($"{musicPlayer.Playlist.Count} tracks currently queued."))
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
 | 
			
		||||
            if (musicPlayer.RepeatSong)
 | 
			
		||||
            {
 | 
			
		||||
                embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}");
 | 
			
		||||
            }
 | 
			
		||||
            else if (musicPlayer.RepeatPlaylist)
 | 
			
		||||
            {
 | 
			
		||||
                embed.WithTitle("🔁 Repeating Playlist");
 | 
			
		||||
            }
 | 
			
		||||
            if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
 | 
			
		||||
            {
 | 
			
		||||
                embed.WithTitle("🎵 Song queue is full!");
 | 
			
		||||
            }
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -231,53 +211,23 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
        public async Task NowPlaying(IUserMessage umsg)
 | 
			
		||||
        {
 | 
			
		||||
            var channel = (ITextChannel)umsg.Channel;
 | 
			
		||||
	    
 | 
			
		||||
 | 
			
		||||
            MusicPlayer musicPlayer;
 | 
			
		||||
            if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
 | 
			
		||||
                return;
 | 
			
		||||
            var currentSong = musicPlayer.CurrentSong;
 | 
			
		||||
            if (currentSong == null)
 | 
			
		||||
                return;
 | 
			
		||||
		var videoid = Regex.Match(currentSong.SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
 | 
			
		||||
				
 | 
			
		||||
		var embed = new EmbedBuilder()
 | 
			
		||||
			    .WithAuthor(eab => eab.WithName("Now Playing").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
				.WithTitle($"{currentSong.SongInfo.Title}")
 | 
			
		||||
				//.WithDescription($"{currentSong.PrettyCurrentTime()}")
 | 
			
		||||
				.WithFooter(ef => ef.WithText($"{currentSong.PrettyProvider} | {currentSong.QueuerName}"))
 | 
			
		||||
                .WithColor(NadekoBot.OkColor);
 | 
			
		||||
		if (currentSong.SongInfo.Provider.Equals("YouTube", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
		{
 | 
			
		||||
				embed.WithThumbnail(tn => tn.Url = $"https://img.youtube.com/vi/{videoid}/0.jpg");
 | 
			
		||||
				embed.WithUrl($"{currentSong.SongInfo.Query}");
 | 
			
		||||
				//if (musicPlayer.Playlist.Count < 50)
 | 
			
		||||
				//{
 | 
			
		||||
					if (currentSong.TotalLength == TimeSpan.Zero)
 | 
			
		||||
					{
 | 
			
		||||
						await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false);
 | 
			
		||||
					}
 | 
			
		||||
						embed.WithDescription($"{currentSong.PrettyMusicPlayTime()} / {currentSong.PrettyCurrentTime()}");
 | 
			
		||||
				//}
 | 
			
		||||
				//else if (musicPlayer.Playlist.Count > 50)
 | 
			
		||||
				//{
 | 
			
		||||
					//embed.WithDescription($"{currentSong.PrettyMusicPlayTime()}");
 | 
			
		||||
				//}
 | 
			
		||||
		}
 | 
			
		||||
		else if (currentSong.SongInfo.Provider.Equals("SoundCloud", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
		{
 | 
			
		||||
				embed.WithThumbnail(tn => tn.Url = $"{currentSong.SongInfo.AlbumArt}");
 | 
			
		||||
				embed.WithUrl($"{currentSong.SongInfo.Query}");
 | 
			
		||||
				if (currentSong.TotalLength == TimeSpan.Zero)
 | 
			
		||||
				{
 | 
			
		||||
					await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false);
 | 
			
		||||
				}
 | 
			
		||||
				embed.WithDescription($"{currentSong.PrettyMusicPlayTime()} / {currentSong.PrettyCurrentTime()}");
 | 
			
		||||
		}
 | 
			
		||||
		else if (currentSong.SongInfo.Provider.Equals("Local File", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
		{
 | 
			
		||||
				embed.WithDescription($"{currentSong.PrettyMusicPlayTime()}");
 | 
			
		||||
		}
 | 
			
		||||
            	await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
            var videoid = Regex.Match(currentSong.SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
 | 
			
		||||
 | 
			
		||||
            var embed = new EmbedBuilder()
 | 
			
		||||
                    .WithAuthor(eab => eab.WithName("Now Playing")
 | 
			
		||||
                        .WithMusicIcon())
 | 
			
		||||
                    .WithDescription(currentSong.PrettyName)
 | 
			
		||||
                    .WithFooter(ef => ef.WithText(currentSong.PrettyCurrentTime + "/" + currentSong.PrettyInfo))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -365,19 +315,18 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
 | 
			
		||||
            var msg =
 | 
			
		||||
                await channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
            foreach (var id in idArray)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await QueueSong(((IGuildUser)umsg.Author), channel, ((IGuildUser)umsg.Author).VoiceChannel, id, true).ConfigureAwait(false);
 | 
			
		||||
					//await Task.Delay(2000).ConfigureAwait(false); //fixes google api error for few songs on playlist.//
 | 
			
		||||
                }
 | 
			
		||||
                catch (SongNotFoundException) { }
 | 
			
		||||
                catch { break; }
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
            await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -511,12 +460,12 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
            var song = (musicPlayer.Playlist as List<Song>)?[num - 1];
 | 
			
		||||
            musicPlayer.RemoveSongAt(num - 1);
 | 
			
		||||
            //await channel.SendConfirmAsync($"🎵 Track {song.PrettyName} at position `#{num}` has been **removed**.").ConfigureAwait(false);
 | 
			
		||||
			var embed = new EmbedBuilder()
 | 
			
		||||
				.WithAuthor(eab => eab.WithName("Song Removed!").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
				.AddField(fb => fb.WithName("**Song Position**").WithValue($"#{num}").WithIsInline(true))
 | 
			
		||||
				.AddField(fb => fb.WithName("**Song Name**").WithValue($"**[{song.SongInfo.Title.TrimTo(70)}]({song.SongInfo.Query})** `{song.PrettyProvider} | {song.QueuerName.TrimTo(15)}`").WithIsInline(true))
 | 
			
		||||
				.WithColor(NadekoBot.ErrorColor);
 | 
			
		||||
			await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
            var embed = new EmbedBuilder()
 | 
			
		||||
                .WithAuthor(eab => eab.WithName("Song Removed!").WithMusicIcon())
 | 
			
		||||
                .AddField(fb => fb.WithName("**Song Position**").WithValue($"#{num}").WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("**Song Name**").WithValue($"**[{song.SongInfo.Title.TrimTo(70)}]({song.SongInfo.Query})** `{song.PrettyProvider} | {song.QueuerName.TrimTo(15)}`").WithIsInline(true))
 | 
			
		||||
                .WithColor(NadekoBot.ErrorColor);
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -565,19 +514,15 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
            playlist.Insert(n2 - 1, s);
 | 
			
		||||
            var nn1 = n2 < n1 ? n1 : n1 - 1;
 | 
			
		||||
            playlist.RemoveAt(nn1);
 | 
			
		||||
	    
 | 
			
		||||
	    var embed = new EmbedBuilder()
 | 
			
		||||
	    	.WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
 | 
			
		||||
		.WithUrl($"{s.SongInfo.Query}")
 | 
			
		||||
		.WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
		.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
 | 
			
		||||
		.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
 | 
			
		||||
		.WithColor(NadekoBot.OkColor);
 | 
			
		||||
 | 
			
		||||
            var embed = new EmbedBuilder()
 | 
			
		||||
                .WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
 | 
			
		||||
            .WithUrl($"{s.SongInfo.Query}")
 | 
			
		||||
            .WithAuthor(eab => eab.WithName("Song Moved").WithMusicIcon())
 | 
			
		||||
            .AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
 | 
			
		||||
            .AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
 | 
			
		||||
            .WithOkColor();
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
            //await channel.SendConfirmAsync($"🎵Moved {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [NadekoCommand, Usage, Description, Aliases]
 | 
			
		||||
@@ -606,9 +551,15 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
            if (currentSong == null)
 | 
			
		||||
                return;
 | 
			
		||||
            var currentValue = musicPlayer.ToggleRepeatSong();
 | 
			
		||||
            await channel.SendConfirmAsync(currentValue ?
 | 
			
		||||
                                        $"🔂 Repeating track: {currentSong.PrettyName}" :
 | 
			
		||||
                                        $"🔂 Current track repeat stopped.")
 | 
			
		||||
 | 
			
		||||
            if (currentValue)
 | 
			
		||||
                await channel.EmbedAsync(new EmbedBuilder()
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
 | 
			
		||||
                    .WithDescription(currentSong.PrettyFullName)
 | 
			
		||||
                    .Build()).ConfigureAwait(false);
 | 
			
		||||
            else
 | 
			
		||||
                await channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
 | 
			
		||||
                                            .ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -635,7 +586,8 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
 | 
			
		||||
            var curSong = musicPlayer.CurrentSong;
 | 
			
		||||
            var songs = musicPlayer.Playlist.Append(curSong)
 | 
			
		||||
                                .Select(s=> new PlaylistSong() {
 | 
			
		||||
                                .Select(s => new PlaylistSong()
 | 
			
		||||
                                {
 | 
			
		||||
                                    Provider = s.SongInfo.Provider,
 | 
			
		||||
                                    ProviderType = s.SongInfo.ProviderType,
 | 
			
		||||
                                    Title = s.SongInfo.Title,
 | 
			
		||||
@@ -711,14 +663,14 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
 | 
			
		||||
            //await channel.SendConfirmAsync($@"🎶 **Page {num} of saved playlists:**
 | 
			
		||||
 | 
			
		||||
//" + string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by __{r.Author}__ ({r.Songs.Count} songs)"))).ConfigureAwait(false);
 | 
			
		||||
            //" + string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by __{r.Author}__ ({r.Songs.Count} songs)"))).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
			var embed = new EmbedBuilder()
 | 
			
		||||
				.WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
				.WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}**\t by **`{r.Author}`**\t ({r.Songs.Count} songs)")))
 | 
			
		||||
				.WithColor(NadekoBot.OkColor);
 | 
			
		||||
            var embed = new EmbedBuilder()
 | 
			
		||||
                .WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon())
 | 
			
		||||
                .WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}**\t by **`{r.Author}`**\t ({r.Songs.Count} songs)")))
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //todo only author or owner
 | 
			
		||||
@@ -866,116 +818,63 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
                    vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
 | 
			
		||||
                }
 | 
			
		||||
                var mp = new MusicPlayer(voiceCh, vol);
 | 
			
		||||
                IUserMessage playingMessage = null;
 | 
			
		||||
		IUserMessage lastFinishedMessage = null;
 | 
			
		||||
 | 
			
		||||
                IUserMessage finishedMessage = null;
 | 
			
		||||
                mp.OnCompleted += async (s, song) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (song.PrintStatusMessage)
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            if (lastFinishedMessage != null)
 | 
			
		||||
							{
 | 
			
		||||
								await lastFinishedMessage.DeleteAsync().ConfigureAwait(false);
 | 
			
		||||
								//try { lastFinishedMessage = await textCh.SendConfirmAsync($"🎵 Finished {song.PrettyName}").ConfigureAwait(false); } catch { }
 | 
			
		||||
								try { lastFinishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
															.WithAuthor(eab => eab.WithName("Finished Song").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
															.WithTitle($"{song.SongInfo.Title}")
 | 
			
		||||
															.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
 | 
			
		||||
                                                            .Build())
 | 
			
		||||
                                                            .ConfigureAwait(false); } catch { }
 | 
			
		||||
							}
 | 
			
		||||
                            else
 | 
			
		||||
							{
 | 
			
		||||
								try { lastFinishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
															.WithAuthor(eab => eab.WithName("Finished Song").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
															.WithTitle($"{song.SongInfo.Title}")
 | 
			
		||||
															.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
 | 
			
		||||
                                                            .Build())
 | 
			
		||||
                                                            .ConfigureAwait(false); } catch { }
 | 
			
		||||
								//try { lastFinishedMessage = await textCh.SendConfirmAsync($"🎵 Finished {song.PrettyName}").ConfigureAwait(false); } catch { }
 | 
			
		||||
							}
 | 
			
		||||
                            if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
 | 
			
		||||
                            {
 | 
			
		||||
                                await QueueSong(queuer.Guild.GetCurrentUser(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        catch { }
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                mp.OnStarted += async (s, song) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (song.PrintStatusMessage)
 | 
			
		||||
                    {
 | 
			
		||||
                        var sender = s as MusicPlayer;
 | 
			
		||||
						//var msgTxt = $"🎵 Playing {song.PrettyName}\t `Vol: {(int)(sender.Volume * 100)}%`";
 | 
			
		||||
                        if (sender == null)
 | 
			
		||||
                            return;
 | 
			
		||||
						if (playingMessage != null)
 | 
			
		||||
						{
 | 
			
		||||
							await playingMessage.DeleteAsync().ConfigureAwait(false);
 | 
			
		||||
							//try { playingMessage = await textCh.SendConfirmAsync(msgTxt).ConfigureAwait(false); } catch { }
 | 
			
		||||
							try { playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
															.WithAuthor(eab => eab.WithName("Playing Song").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
															.WithTitle($"{song.SongInfo.Title}")
 | 
			
		||||
															.WithDescription($"Volume: {(int)(sender.Volume * 100)}%")
 | 
			
		||||
															.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
 | 
			
		||||
                                                            .Build())
 | 
			
		||||
                                                            .ConfigureAwait(false); } catch { }
 | 
			
		||||
						}
 | 
			
		||||
						else
 | 
			
		||||
						{
 | 
			
		||||
							//try { playingMessage = await textCh.SendConfirmAsync(msgTxt).ConfigureAwait(false); } catch { }
 | 
			
		||||
							try { playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
															.WithAuthor(eab => eab.WithName("Playing Song").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
															.WithTitle($"{song.SongInfo.Title}")
 | 
			
		||||
															.WithDescription($"Volume: {(int)(sender.Volume * 100)}%")
 | 
			
		||||
															.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
 | 
			
		||||
                                                            .Build())
 | 
			
		||||
                                                            .ConfigureAwait(false); } catch { }
 | 
			
		||||
						}
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
				IUserMessage resumemsg = null;
 | 
			
		||||
				IUserMessage pausemsg = null;
 | 
			
		||||
		mp.OnPauseChanged += async (paused) =>
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        if (paused)
 | 
			
		||||
						{
 | 
			
		||||
							if (pausemsg != null)
 | 
			
		||||
							{
 | 
			
		||||
								await pausemsg.DeleteAsync().ConfigureAwait(false);
 | 
			
		||||
								try { pausemsg = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); } catch { }
 | 
			
		||||
							}
 | 
			
		||||
							else
 | 
			
		||||
							{
 | 
			
		||||
								try { pausemsg = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); } catch { }
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
                        else
 | 
			
		||||
						{
 | 
			
		||||
							if (resumemsg != null)
 | 
			
		||||
							{
 | 
			
		||||
								await resumemsg.DeleteAsync().ConfigureAwait(false);
 | 
			
		||||
								try { resumemsg = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); } catch { }
 | 
			
		||||
							}
 | 
			
		||||
							else
 | 
			
		||||
							{
 | 
			
		||||
								try { resumemsg = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); } catch { }
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
                        if (finishedMessage != null)
 | 
			
		||||
                            finishedMessage.DeleteAfter(0);
 | 
			
		||||
                        finishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                                  .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
 | 
			
		||||
                                                  .WithDescription(song.PrettyName)
 | 
			
		||||
                                                  .WithFooter(ef => ef.WithText(song.PrettyInfo))
 | 
			
		||||
                                                  .Build())
 | 
			
		||||
                                                  .ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                        if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
 | 
			
		||||
                        {
 | 
			
		||||
                            await QueueSong(queuer.Guild.GetCurrentUser(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch { }
 | 
			
		||||
                };
 | 
			
		||||
                IUserMessage playingMessage = null;
 | 
			
		||||
                mp.OnStarted += async (player, song) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (playingMessage != null)
 | 
			
		||||
                        playingMessage.DeleteAfter(0);
 | 
			
		||||
 | 
			
		||||
                    playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                                .WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
 | 
			
		||||
                                                .WithDescription(song.PrettyName)
 | 
			
		||||
                                                .WithFooter(ef => ef.WithText(song.PrettyInfo))
 | 
			
		||||
                                                .Build())
 | 
			
		||||
                                                .ConfigureAwait(false);
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mp.OnPauseChanged += async (paused) =>
 | 
			
		||||
                {
 | 
			
		||||
                    IUserMessage pauseMessage = null;
 | 
			
		||||
                    if (paused)
 | 
			
		||||
                    {
 | 
			
		||||
                        pauseMessage = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        pauseMessage = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (pauseMessage != null)
 | 
			
		||||
                        pauseMessage.DeleteAfter(15);
 | 
			
		||||
                };
 | 
			
		||||
                return mp;
 | 
			
		||||
            });
 | 
			
		||||
            Song resolvedSong;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                musicPlayer.ThrowIfQueueFull();
 | 
			
		||||
                resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false);
 | 
			
		||||
                resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                if (resolvedSong == null)
 | 
			
		||||
                    throw new SongNotFoundException();
 | 
			
		||||
@@ -992,19 +891,19 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    //var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
 | 
			
		||||
                    var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
															.WithAuthor(eab => eab.WithName("Queued Song").WithIconUrl("http://i.imgur.com/nhKS3PT.png"))
 | 
			
		||||
															.WithTitle($"{resolvedSong.SongInfo.Title}")
 | 
			
		||||
															.WithDescription($"Queue #{musicPlayer.Playlist.Count + 1}")
 | 
			
		||||
															.WithFooter(ef => ef.WithText($"{resolvedSong.PrettyProvider}"))
 | 
			
		||||
                    var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                                            .WithAuthor(eab => eab.WithName("Queued Song").WithMusicIcon())
 | 
			
		||||
                                                            .WithTitle($"{resolvedSong.SongInfo.Title}")
 | 
			
		||||
                                                            .WithDescription($"Queue #{musicPlayer.Playlist.Count + 1}")
 | 
			
		||||
                                                            .WithFooter(ef => ef.WithText($"{resolvedSong.PrettyProvider}"))
 | 
			
		||||
                                                            .Build())
 | 
			
		||||
                                                            .ConfigureAwait(false);
 | 
			
		||||
					var t = Task.Run(async () =>
 | 
			
		||||
                    var t = Task.Run(async () =>
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            await Task.Delay(10000).ConfigureAwait(false);
 | 
			
		||||
                        
 | 
			
		||||
 | 
			
		||||
                            await queuedMessage.DeleteAsync().ConfigureAwait(false);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch { }
 | 
			
		||||
@@ -1014,4 +913,4 @@ namespace NadekoBot.Modules.Music
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -316,7 +316,7 @@ namespace NadekoBot.Modules.Pokemon
 | 
			
		||||
            var targetType = StringToPokemonType(typeTargeted);
 | 
			
		||||
            if (targetType == null)
 | 
			
		||||
            {
 | 
			
		||||
                await channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name).WithValue(pt.Icon).WithIsInline(true))).WithColor(NadekoBot.OkColor).Build()).ConfigureAwait(false);
 | 
			
		||||
                await channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name).WithValue(pt.Icon).WithIsInline(true))).WithOkColor().Build()).ConfigureAwait(false);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (targetType == GetPokeType(user.Id))
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                                                    $"limit={showCount}")
 | 
			
		||||
                                                    .ConfigureAwait(false))["data"] as JArray;
 | 
			
		||||
                    var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
 | 
			
		||||
                    var eb = new EmbedBuilder().WithColor(NadekoBot.OkColor).WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
 | 
			
		||||
                    var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
 | 
			
		||||
                    for (var i = 0; i < dataList.Count; i++)
 | 
			
		||||
                    {
 | 
			
		||||
                        var champ = dataList[i];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.API;
 | 
			
		||||
using NadekoBot.Extensions;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
@@ -35,7 +36,7 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB
 | 
			
		||||
        public string Poster { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Embed GetEmbed() =>
 | 
			
		||||
            new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
            new EmbedBuilder().WithOkColor()
 | 
			
		||||
                              .WithTitle(Title)
 | 
			
		||||
                              .WithUrl($"http://www.imdb.com/title/{ImdbId}/")
 | 
			
		||||
                              .WithDescription(Plot)
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true))
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
 | 
			
		||||
                            .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                            .WithOkColor();
 | 
			
		||||
                        await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true))
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
 | 
			
		||||
                            .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                            .WithOkColor();
 | 
			
		||||
                        await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
					}
 | 
			
		||||
					if (string.IsNullOrWhiteSpace(competitiveplay))
 | 
			
		||||
@@ -85,7 +85,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"0 hour").WithIsInline(true))
 | 
			
		||||
                            .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
 | 
			
		||||
                            .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                            .WithOkColor();
 | 
			
		||||
                        await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
					}
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                    if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
 | 
			
		||||
                    {
 | 
			
		||||
                        var p = kvp.Value;
 | 
			
		||||
                        await channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                        await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                            .WithTitle(kvp.Key.ToTitleCase())
 | 
			
		||||
                            .WithDescription(p.BaseStats.ToString())
 | 
			
		||||
                            .AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
 | 
			
		||||
@@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                {
 | 
			
		||||
                    if (kvp.Key.ToUpperInvariant() == ability)
 | 
			
		||||
                    {
 | 
			
		||||
                        await channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                        await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                            .WithTitle(kvp.Value.Name)
 | 
			
		||||
                            .WithDescription(kvp.Value.Desc)
 | 
			
		||||
                            .AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true))
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                    var res = await http.GetStringAsync($"{xkcdUrl}/{num}/info.0.json").ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
                    var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
 | 
			
		||||
                    var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                    var embed = new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                                  .WithImage(eib => eib.WithUrl(comic.ImageLink))
 | 
			
		||||
                                                  .WithAuthor(eab => eab.WithName(comic.Title).WithUrl($"{xkcdUrl}/{num}").WithIconUrl("http://xkcd.com/s/919f27.ico"))
 | 
			
		||||
                                                  .AddField(efb => efb.WithName("Comic#").WithValue(comic.Num.ToString()).WithIsInline(true))
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                .AddField(fb => fb.WithName("🔆 **Feels like**").WithValue($"{obj["feelscentigrade"]}°C ({obj["feelsfahrenheit"]}°F)").WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("🌄 **Sunrise**").WithValue($"{obj["sunrise"]}").WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("🌇 **Sunset**").WithValue($"{obj["sunset"]}").WithIsInline(true))
 | 
			
		||||
                .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
            await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -202,7 +202,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                await msg.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await msg.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
            await msg.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                                           .AddField(efb => efb.WithName("Original Url")
 | 
			
		||||
                                                                               .WithValue($"<{arg}>"))
 | 
			
		||||
                                                            .AddField(efb => efb.WithName("Short Url")
 | 
			
		||||
@@ -255,7 +255,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                    var desc = item["text"].ToString();
 | 
			
		||||
                    var types = String.Join(",\n", item["types"].ToObject<string[]>());
 | 
			
		||||
                    var img = item["editions"][0]["image_url"].ToString();
 | 
			
		||||
                    var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                    var embed = new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                    .WithTitle(item["name"].ToString())
 | 
			
		||||
                                    .WithDescription(desc)
 | 
			
		||||
                                    .WithImage(eib => eib.WithUrl(img))
 | 
			
		||||
@@ -364,7 +364,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                        .WithUrl("http://www.yodaspeak.co.uk/")
 | 
			
		||||
                        .WithAuthor(au => au.WithName("Yoda").WithIconUrl("http://www.yodaspeak.co.uk/yoda-small1.gif"))
 | 
			
		||||
                        .WithDescription(res)
 | 
			
		||||
                        .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                        .WithOkColor();
 | 
			
		||||
                    await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
@@ -405,7 +405,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                    var word = item["word"].ToString();
 | 
			
		||||
                    var def = item["definition"].ToString();
 | 
			
		||||
                    var link = item["permalink"].ToString();
 | 
			
		||||
                    var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                    var embed = new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                     .WithUrl(link)
 | 
			
		||||
                                     .WithAuthor(eab => eab.WithIconUrl("http://i.imgur.com/nwERwQE.jpg").WithName(word))
 | 
			
		||||
                                     .WithDescription(def);
 | 
			
		||||
@@ -452,7 +452,7 @@ namespace NadekoBot.Modules.Searches
 | 
			
		||||
                var hashtag = item["hashtag"].ToString();
 | 
			
		||||
                var link = item["uri"].ToString();
 | 
			
		||||
                var desc = item["text"].ToString();
 | 
			
		||||
                await channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
 | 
			
		||||
                await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
 | 
			
		||||
                                                                 .WithAuthor(eab => eab.WithUrl(link)
 | 
			
		||||
                                                                                       .WithIconUrl("http://res.cloudinary.com/urbandictionary/image/upload/a_exif,c_fit,h_200,w_200/v1394975045/b8oszuu3tbq7ebyo7vo1.jpg")
 | 
			
		||||
                                                                                       .WithName(query))
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Utility
 | 
			
		||||
                .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("**Roles**").WithValue(guild.Roles.Count().ToString()).WithIsInline(true))
 | 
			
		||||
                .WithImage(tn => tn.WithUrl(guild.IconUrl))
 | 
			
		||||
                .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
            if (guild.Emojis.Count() > 0)
 | 
			
		||||
            {
 | 
			
		||||
                embed.AddField(fb => fb.WithName("**Custom Emojis**").WithValue(Format.Italics(string.Join(", ", guild.Emojis))).WithIsInline(true));
 | 
			
		||||
@@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Utility
 | 
			
		||||
                .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
 | 
			
		||||
                .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
            await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Utility
 | 
			
		||||
                .AddField(fb => fb.WithName("**Current Game**").WithValue($"{(user.Game?.Name == null ? "-" : user.Game.Name)}").WithIsInline(true))
 | 
			
		||||
                .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.Roles.Count()})** - {string.Join(", ", user.Roles.Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
 | 
			
		||||
                .WithThumbnail(tn => tn.WithUrl(user.AvatarUrl))
 | 
			
		||||
                .WithColor(NadekoBot.OkColor);
 | 
			
		||||
                .WithOkColor();
 | 
			
		||||
            await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Utility
 | 
			
		||||
            {
 | 
			
		||||
                var res = Units.GroupBy(x => x.UnitType)
 | 
			
		||||
                               .Aggregate(new EmbedBuilder().WithTitle("__Units which can be used by the converter__")
 | 
			
		||||
                                                            .WithColor(NadekoBot.OkColor),
 | 
			
		||||
                                                            .WithOkColor(),
 | 
			
		||||
                                          (embed, g) => embed.AddField(efb => 
 | 
			
		||||
                                                                         efb.WithName(g.Key.ToTitleCase())
 | 
			
		||||
                                                                         .WithValue(String.Join(", ", g.Select(x => x.Triggers.FirstOrDefault())
 | 
			
		||||
 
 | 
			
		||||
@@ -263,7 +263,7 @@ namespace NadekoBot.Modules.Utility
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithColor(NadekoBot.OkColor),
 | 
			
		||||
            await channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(),
 | 
			
		||||
                                     (embed, g) => embed.AddField(efb => efb.WithName(g.Name)
 | 
			
		||||
                                                                           .WithValue($"```css\nID: {g.Id}\nMembers: {g.GetUsers().Count}\nOwnerID: {g.OwnerId} ```")
 | 
			
		||||
                                                                           .WithIsInline(false)))
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,20 @@ namespace NadekoBot.Extensions
 | 
			
		||||
            http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static EmbedBuilder WithOkColor(this EmbedBuilder eb) =>
 | 
			
		||||
            eb.WithColor(NadekoBot.OkColor);
 | 
			
		||||
 | 
			
		||||
        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 async Task<IMessage> SendMessageToOwnerAsync(this IGuild guild, string message)
 | 
			
		||||
        {
 | 
			
		||||
            var ownerPrivate = await (await guild.GetOwnerAsync().ConfigureAwait(false)).CreateDMChannelAsync()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user