diff --git a/src/NadekoBot/Modules/Administration/AdministrationModule.cs b/src/NadekoBot/Modules/Administration/AdministrationModule.cs index 387d3d78..1d849a3b 100644 --- a/src/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/src/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Restart(IMessage imsg) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // await channel.SendMessageAsync("`Restarting in 2 seconds...`"); // await Task.Delay(2000); @@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Administration //[RequirePermission(GuildPermission.ManageGuild)] //public async Task Delmsgoncmd(IMessage imsg) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var conf = SpecificConfigurations.Default.Of(channel.Guild.Id); // conf.AutoDeleteMessagesOnCommand = !conf.AutoDeleteMessagesOnCommand; @@ -58,7 +58,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageRoles)] public async Task Setrole(IMessage imsg, IGuildUser usr, [Remainder] IRole role) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { await usr.AddRolesAsync(role).ConfigureAwait(false); @@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageRoles)] public async Task Removerole(IMessage imsg, IGuildUser usr, [Remainder] IRole role) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { await usr.RemoveRolesAsync(role).ConfigureAwait(false); @@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageRoles)] public async Task RenameRole(IMessage imsg, IRole roleToEdit, string newname) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { if (roleToEdit.Position > (await channel.Guild.GetCurrentUserAsync().ConfigureAwait(false)).Roles.Max(r => r.Position)) @@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageRoles)] public async Task RemoveAllRoles(IMessage imsg, [Remainder] IGuildUser user) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { @@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageRoles)] public async Task CreateRole(IMessage imsg, [Remainder] string roleName = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(roleName)) @@ -154,7 +154,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageRoles)] public async Task RoleColor(IMessage imsg, params string[] args) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (args.Count() != 2 && args.Count() != 4) { @@ -192,7 +192,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.BanMembers)] public async Task Ban(IMessage imsg, IGuildUser user) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var msg = ""; @@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.BanMembers)] public async Task Softban(IMessage imsg, IGuildUser user, [Remainder] string msg = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!string.IsNullOrWhiteSpace(msg)) { @@ -244,7 +244,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] public async Task Kick(IMessage imsg, IGuildUser user, [Remainder] string msg = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (user == null) { @@ -273,7 +273,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.MuteMembers)] public async Task Mute(IMessage imsg, params IGuildUser[] users) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!users.Any()) return; @@ -296,7 +296,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.MuteMembers)] public async Task Unmute(IMessage imsg, params IGuildUser[] users) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!users.Any()) return; @@ -319,7 +319,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.DeafenMembers)] public async Task Deafen(IMessage imsg, params IGuildUser[] users) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!users.Any()) return; @@ -342,7 +342,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.DeafenMembers)] public async Task UnDeafen(IMessage imsg, params IGuildUser[] users) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!users.Any()) return; @@ -374,7 +374,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageChannels)] public async Task CreatVoiChanl(IMessage imsg, [Remainder] string channelName) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; //todo actually print info about created channel await channel.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false); await channel.SendMessageAsync($"Created voice channel **{channelName}**.").ConfigureAwait(false); @@ -394,7 +394,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageChannels)] public async Task CreaTxtChanl(IMessage imsg, [Remainder] string channelName) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; //todo actually print info about created channel var txtCh = await channel.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false); await channel.SendMessageAsync($"Added text channel **{channelName}**.").ConfigureAwait(false); @@ -405,7 +405,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageChannels)] public async Task SetTopic(IMessage imsg, [Remainder] string topic = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; topic = topic ?? ""; await (channel as ITextChannel).ModifyAsync(c => c.Topic = topic); //await (channel).ModifyAsync(c => c).ConfigureAwait(false); @@ -417,7 +417,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.ManageChannels)] public async Task SetChanlName(IMessage imsg, [Remainder] string name) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false); await channel.SendMessageAsync(":ok: **New channel name set.**").ConfigureAwait(false); @@ -429,7 +429,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] public async Task Prune(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var user = await channel.Guild.GetCurrentUserAsync(); @@ -471,7 +471,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Die(IMessage imsg) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // await channel.SendMessageAsync("`Shutting down.`").ConfigureAwait(false); // await Task.Delay(2000).ConfigureAwait(false); @@ -483,7 +483,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Setname(IMessage imsg, [Remainder] string newName = null) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; //} @@ -492,7 +492,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task NewAvatar(IMessage imsg, [Remainder] string img = null) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // if (string.IsNullOrWhiteSpace(img)) // return; @@ -511,7 +511,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task SetGame(IMessage imsg, [Remainder] string game = null) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // game = game ?? ""; @@ -523,7 +523,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Send(IMessage imsg, string where, [Remainder] string msg = null) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // if (string.IsNullOrWhiteSpace(msg)) // return; @@ -569,7 +569,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Donadd(IMessage imsg, IUser donator, int amount) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var donator = channel.Guild.FindUsers(donator).FirstOrDefault(); // var amount = int.Parse(amount); // if (donator == null) return; @@ -592,7 +592,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Announce(IMessage imsg, [Remainder] string message) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // foreach (var ch in (await _client.GetGuildsAsync().ConfigureAwait(false)).Select(async g => await g.GetDefaultChannelAsync().ConfigureAwait(false))) // { @@ -607,7 +607,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task SaveChat(IMessage imsg, int cnt) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // ulong? lastmsgId = null; // var sb = new StringBuilder(); @@ -640,7 +640,7 @@ namespace NadekoBot.Modules.Administration [RequirePermission(GuildPermission.MentionEveryone)] public async Task MentionRole(IMessage imsg, params IRole[] roles) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; string send = $"--{imsg.Author.Mention} has invoked a mention on the following roles--"; foreach (var role in roles) @@ -665,7 +665,7 @@ namespace NadekoBot.Modules.Administration //[RequireContext(ContextType.Guild)] //public async Task Donators(IMessage imsg) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var rows = DbHandler.Instance.GetAllRows(); // var donatorsOrdered = rows.OrderByDescending(d => d.Amount); diff --git a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs index 75a12f99..b171b632 100644 --- a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs +++ b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs @@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Administration //[RequirePermission(GuildPermission.ManageRoles)] //public async Task AutoAssignRole(IMessage imsg, IRole role) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var config = SpecificConfigurations.Default.Of(e.Server.Id); diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index c7864d46..5e3a55ed 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Administration _client.MessageReceived += async (imsg) => { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (channel == null || await imsg.IsAuthor()) return; @@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] public async Task Slowmode(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; ConcurrentDictionary throwaway; if (RatelimitingChannels.TryRemove(channel.Id, out throwaway)) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 79ace8a1..e7cb5744 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -24,7 +24,7 @@ // [RequireContext(ContextType.Guild)] // public async Task Leave(IMessage imsg, [Remainder] string guildStr) // { -// var channel = imsg.Channel as ITextChannel; +// var channel = (ITextChannel)imsg.Channel; // guildStr = guildStr.ToUpperInvariant(); // var server = _client.GetGuilds().FirstOrDefault(g => g.Id.ToString() == guildStr) ?? _client.GetGuilds().FirstOrDefault(g => g.Name.ToUpperInvariant() == guildStr); diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 413f672b..3ec97d5d 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task CreateWar(IMessage imsg, int size, [Remainder] string enemyClan = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels) return; @@ -73,7 +73,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task StartWar(IMessage imsg, [Remainder] string number = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; int num = 0; int.TryParse(number, out num); @@ -100,7 +100,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task ListWar(IMessage imsg, [Remainder] string number = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; // if number is null, print all wars in a short way if (string.IsNullOrWhiteSpace(number)) @@ -143,7 +143,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task Claim(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var warsInfo = GetWarInfo(imsg, number); if (warsInfo == null || warsInfo.Item1.Count == 0) { @@ -170,7 +170,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task ClaimFinish1(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await FinishClaim(imsg, number, baseNumber, other_name, 1); } @@ -178,7 +178,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task ClaimFinish2(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await FinishClaim(imsg, number, baseNumber, other_name, 2); } @@ -186,7 +186,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task ClaimFinish(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await FinishClaim(imsg, number, baseNumber, other_name); } @@ -194,7 +194,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task EndWar(IMessage imsg, int number) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var warsInfo = GetWarInfo(imsg,number); if (warsInfo == null) @@ -213,7 +213,7 @@ namespace NadekoBot.Modules.ClashOfClans [RequireContext(ContextType.Guild)] public async Task Unclaim(IMessage imsg, int number, [Remainder] string otherName = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var warsInfo = GetWarInfo(imsg, number); if (warsInfo == null || warsInfo.Item1.Count == 0) @@ -239,7 +239,7 @@ namespace NadekoBot.Modules.ClashOfClans private async Task FinishClaim(IMessage imsg, int number, int baseNumber, [Remainder] string other_name, int stars = 3) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var warInfo = GetWarInfo(imsg, number); if (warInfo == null || warInfo.Item1.Count == 0) { @@ -265,7 +265,7 @@ namespace NadekoBot.Modules.ClashOfClans private static Tuple, int> GetWarInfo(IMessage imsg, int num) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; //check if there are any wars List wars = null; ClashWars.TryGetValue(channel.Guild.Id, out wars); diff --git a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index b5e36bb7..380b11c2 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Gambling [RequireContext(ContextType.Guild)] public async Task Race(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var ar = new AnimalRace(channel.Guild.Id, channel); @@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Gambling [RequireContext(ContextType.Guild)] public async Task JoinRace(IMessage imsg, int amount = 0) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (amount < 0) amount = 0; diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 6f613c94..e281ed7a 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -26,7 +26,7 @@ namespace NadekoBot.Modules.Gambling // InternalRoll(imsg, arg, false); //private async Task InternalRoll(IMessage imsg, string arg, bool ordered) { - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var r = new Random(); // if (string.IsNullOrWhiteSpace(arg)) // { @@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Gambling [RequireContext(ContextType.Guild)] public async Task NRoll(IMessage imsg, [Remainder] string range) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 59e8833f..72d72fb4 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Gambling [RequireContext(ContextType.Guild)] public async Task Raffle(IMessage imsg, [Remainder] IRole role = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; role = role ?? channel.Guild.EveryoneRole; @@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Gambling //[RequireContext(ContextType.Guild)] //public async Task Cash(IMessage imsg, [Remainder] string arg) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var usr = e.Message.MentionedUsers.FirstOrDefault() ?? imsg.Author; // var pts = GetUserFlowers(usr.Id); @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Gambling //[RequireContext(ContextType.Guild)] //public async Task Give(IMessage imsg, long amount, [Remainder] IUser receiver) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // if (amount <= 0) // return; // var userFlowers = GetUserFlowers(imsg.Author.Id); @@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Gambling //[RequireContext(ContextType.Guild)] //public async Task Award(IMessage imsg, long amount, [Remainder] ulong usrId) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // if (amount <= 0) // return; @@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Gambling //[RequireContext(ContextType.Guild)] //public async Task Take(IMessage imsg, long amount, [Remainder] ulong usrId) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // if (amount <= 0) // return; @@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Gambling //[RequireContext(ContextType.Guild)] //public async Task BetRoll(IMessage imsg, int amount) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // if (amount < 1) // return; @@ -160,7 +160,7 @@ namespace NadekoBot.Modules.Gambling // [RequireContext(ContextType.Guild)] // public async Task Leaderboard(IMessage imsg) // { -// var channel = imsg.Channel as ITextChannel; +// var channel = (ITextChannel)imsg.Channel; // var richestTemp = DbHandler.Instance.GetTopRichest(); // var richest = richestTemp as CurrencyState[] ?? richestTemp.ToArray(); diff --git a/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs b/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs index dd660876..a17bc3d3 100644 --- a/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/LeetCommands.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Leet(IMessage imsg, int level, [Remainder] string text = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; text = text.Trim(); if (string.IsNullOrWhiteSpace(text)) diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index 0f3d1721..95900dcc 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Poll(IMessage imsg, [Remainder] string arg = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels) return; @@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Pollend(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels) return; diff --git a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs index 956b40fa..e2c7c347 100644 --- a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Trivia(IMessage imsg, string[] args) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; TriviaGame trivia; if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia)) @@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Tl(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; TriviaGame trivia; if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia)) @@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Tq(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; TriviaGame trivia; if (RunningTrivias.TryRemove(channel.Guild.Id, out trivia)) diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index bf9a3413..861d4697 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Choose(IMessage imsg, [Remainder] string list = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(list)) return; var listArr = list.Split(';'); @@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task _8Ball(IMessage imsg, [Remainder] string question = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(question)) return; @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Rps(IMessage imsg, string input) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; Func GetRPSPick = (p) => { @@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Linux(IMessage imsg, string guhnoo, string loonix) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendMessageAsync( $@"I'd just like to interject for moment. What you're refering to as {loonix}, is in fact, {guhnoo}/{loonix}, or as I've recently taken to calling it, {guhnoo} plus {loonix}. {loonix} is not an operating system unto itself, but rather another free component of a fully functioning {guhnoo} system made useful by the {guhnoo} corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX. diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 2d38e4dd..68e9cca4 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Help [RequireContext(ContextType.Guild)] public async Task Modules(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendMessageAsync("`List of modules:` \nβ€’ " + string.Join("\nβ€’ ", _commands.Modules.Select(m => m.Name)) + $"\n`Type \"-commands module_name\" to get a list of commands in that module.`") .ConfigureAwait(false); @@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Help [RequireContext(ContextType.Guild)] public async Task Commands(IMessage imsg, [Remainder] string module = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; module = module?.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(module)) @@ -69,7 +69,7 @@ namespace NadekoBot.Modules.Help [RequireContext(ContextType.Guild)] public async Task H(IMessage imsg, [Remainder] string comToFind = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; comToFind = comToFind?.ToLowerInvariant(); if (string.IsNullOrWhiteSpace(comToFind)) @@ -117,7 +117,7 @@ namespace NadekoBot.Modules.Help [RequireContext(ContextType.Guild)] public async Task Guide(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendMessageAsync( @"**LIST OF COMMANDS**: @@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Help [RequireContext(ContextType.Guild)] public async Task Donate(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendMessageAsync( $@"You can support the project on patreon. or diff --git a/src/NadekoBot/_Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs similarity index 92% rename from src/NadekoBot/_Modules/Music/Classes/MusicControls.cs rename to src/NadekoBot/Modules/Music/Classes/MusicControls.cs index e13239cf..03aaccef 100644 --- a/src/NadekoBot/_Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Music.Classes public event EventHandler OnCompleted = delegate { }; public event EventHandler OnStarted = delegate { }; - public Channel PlaybackVoiceChannel { get; private set; } + public IVoiceChannel PlaybackVoiceChannel { get; private set; } private bool Destroyed { get; set; } = false; public bool RepeatSong { get; private set; } = false; @@ -54,12 +54,10 @@ namespace NadekoBot.Modules.Music.Classes private ConcurrentQueue actionQueue { get; set; } = new ConcurrentQueue(); - public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) + public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume) { if (startingVoiceChannel == null) throw new ArgumentNullException(nameof(startingVoiceChannel)); - if (startingVoiceChannel.Type != ChannelType.Voice) - throw new ArgumentException("Channel must be of type voice"); Volume = defaultVolume ?? 1.0f; PlaybackVoiceChannel = startingVoiceChannel; @@ -101,9 +99,9 @@ namespace NadekoBot.Modules.Music.Classes { try { - if (audioClient?.State != ConnectionState.Connected) + if (audioClient?.ConnectionState != ConnectionState.Connected) { - audioClient = await PlaybackVoiceChannel.JoinAudio(); + audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); continue; } @@ -246,7 +244,7 @@ namespace NadekoBot.Modules.Music.Classes public void Destroy() { - actionQueue.Enqueue(() => + actionQueue.Enqueue(async () => { RepeatPlaylist = false; RepeatSong = false; @@ -254,16 +252,16 @@ namespace NadekoBot.Modules.Music.Classes playlist.Clear(); if (!SongCancelSource.IsCancellationRequested) SongCancelSource.Cancel(); - audioClient.Disconnect(); + await audioClient.DisconnectAsync(); }); } - internal Task MoveToVoiceChannel(Channel voiceChannel) + internal Task MoveToVoiceChannel(IVoiceChannel voiceChannel) { - if (audioClient?.State != ConnectionState.Connected) + if (audioClient?.ConnectionState != ConnectionState.Connected) throw new InvalidOperationException("Can't move while bot is not connected to voice channel."); PlaybackVoiceChannel = voiceChannel; - return PlaybackVoiceChannel.JoinAudio(); + return PlaybackVoiceChannel.ConnectAsync(); } internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; diff --git a/src/NadekoBot/_Modules/Music/Classes/PlaylistFullException.cs b/src/NadekoBot/Modules/Music/Classes/PlaylistFullException.cs similarity index 100% rename from src/NadekoBot/_Modules/Music/Classes/PlaylistFullException.cs rename to src/NadekoBot/Modules/Music/Classes/PlaylistFullException.cs diff --git a/src/NadekoBot/_Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs similarity index 95% rename from src/NadekoBot/_Modules/Music/Classes/Song.cs rename to src/NadekoBot/Modules/Music/Classes/Song.cs index 2d808736..4e0d0184 100644 --- a/src/NadekoBot/_Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; using System.Linq; +using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -104,6 +105,9 @@ namespace NadekoBot.Modules.Music.Classes sw.Stop(); Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed); + + var outStream = voiceClient.CreatePCMStream(3840); + const int blockSize = 3840; byte[] buffer = new byte[blockSize]; while (!cancelToken.IsCancellationRequested) @@ -130,7 +134,6 @@ namespace NadekoBot.Modules.Music.Classes break; if (attempt++ == 20) { - voiceClient.Wait(); MusicPlayer.SongCancelSource.Cancel(); break; } @@ -147,13 +150,12 @@ namespace NadekoBot.Modules.Music.Classes await Task.Delay(200, cancelToken).ConfigureAwait(false); buffer = AdjustVolume(buffer, MusicPlayer.Volume); - voiceClient.Send(buffer, 0, read); + await outStream.WriteAsync(buffer, 0, read); } } finally { await bufferTask; - await Task.Run(() => voiceClient.Clear()); if(inStream != null) inStream.Dispose(); Console.WriteLine("l"); @@ -285,10 +287,10 @@ namespace NadekoBot.Modules.Music.Classes }); } - var link = await SearchHelper.FindYoutubeUrlByKeywords(query).ConfigureAwait(false); + 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.Factory.StartNew(async () => await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false)).Unwrap().ConfigureAwait(false); + var allVideos = await Task.Run(async () => await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false)).ConfigureAwait(false); var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); var video = videos .Where(v => v.AudioBitrate < 192) @@ -324,7 +326,10 @@ namespace NadekoBot.Modules.Music.Classes string file = null; try { - file = await http.GetStringAsync(query).ConfigureAwait(false); + using (var http = new HttpClient()) + { + file = await http.GetStringAsync(query).ConfigureAwait(false); + } } catch { diff --git a/src/NadekoBot/_Modules/Music/Classes/SongBuffer.cs b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs similarity index 100% rename from src/NadekoBot/_Modules/Music/Classes/SongBuffer.cs rename to src/NadekoBot/Modules/Music/Classes/SongBuffer.cs diff --git a/src/NadekoBot/_Modules/Music/Classes/SoundCloud.cs b/src/NadekoBot/Modules/Music/Classes/SoundCloud.cs similarity index 86% rename from src/NadekoBot/_Modules/Music/Classes/SoundCloud.cs rename to src/NadekoBot/Modules/Music/Classes/SoundCloud.cs index 9cab3fcf..88c5bdbe 100644 --- a/src/NadekoBot/_Modules/Music/Classes/SoundCloud.cs +++ b/src/NadekoBot/Modules/Music/Classes/SoundCloud.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using System; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; namespace NadekoBot.Modules.Music.Classes @@ -18,10 +19,17 @@ namespace NadekoBot.Modules.Music.Classes { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.SoundCloudClientID)) - throw new ArgumentNullException(nameof(NadekoBot.Credentials.SoundCloudClientID)); + if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.SoundCloudClientId)) + throw new ArgumentNullException(nameof(NadekoBot.Credentials.SoundCloudClientId)); - var response = await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={url}&client_id={NadekoBot.Credentials.SoundCloudClientID}").ConfigureAwait(false); + string response = ""; + + using (var http = new HttpClient()) + { + response = await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={url}&client_id={NadekoBot.Credentials.SoundCloudClientId}").ConfigureAwait(false); + + } + var responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject(response); if (responseObj?.Kind != "track") @@ -37,10 +45,14 @@ namespace NadekoBot.Modules.Music.Classes { if (string.IsNullOrWhiteSpace(query)) throw new ArgumentNullException(nameof(query)); - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.SoundCloudClientID)) - throw new ArgumentNullException(nameof(NadekoBot.Credentials.SoundCloudClientID)); + if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.SoundCloudClientId)) + throw new ArgumentNullException(nameof(NadekoBot.Credentials.SoundCloudClientId)); - var response = await http.GetStringAsync($"http://api.soundcloud.com/tracks?q={Uri.EscapeDataString(query)}&client_id={NadekoBot.Credentials.SoundCloudClientID}").ConfigureAwait(false); + var response = ""; + using (var http = new HttpClient()) + { + await http.GetStringAsync($"http://api.soundcloud.com/tracks?q={Uri.EscapeDataString(query)}&client_id={NadekoBot.Credentials.SoundCloudClientId}").ConfigureAwait(false); + } var responseObj = JsonConvert.DeserializeObject(response).Where(s => s.Streamable).FirstOrDefault(); if (responseObj?.Kind != "track") @@ -62,7 +74,7 @@ namespace NadekoBot.Modules.Music.Classes [JsonProperty("permalink_url")] public string TrackLink { get; set; } = ""; [JsonIgnore] - public string StreamLink => $"https://api.soundcloud.com/tracks/{Id}/stream?client_id={NadekoBot.Credentials.SoundCloudClientID}"; + public string StreamLink => $"https://api.soundcloud.com/tracks/{Id}/stream?client_id={NadekoBot.Credentials.SoundCloudClientId}"; } public class SoundCloudUser { diff --git a/src/NadekoBot/Modules/Music/MusicModule.cs b/src/NadekoBot/Modules/Music/MusicModule.cs new file mode 100644 index 00000000..48ae23c2 --- /dev/null +++ b/src/NadekoBot/Modules/Music/MusicModule.cs @@ -0,0 +1,724 @@ +ο»Ώusing Discord.Commands; +using NadekoBot.Modules.Music.Classes; +using System.Collections.Concurrent; +using Discord.WebSocket; +using NadekoBot.Services; +using System.IO; +using Discord; +using System.Threading.Tasks; +using NadekoBot.Attributes; +using System; +using System.Linq; +using NadekoBot.Extensions; +using System.Net.Http; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace NadekoBot.Modules.Music +{ + [Module("!!", AppendSpace = false)] + public partial class MusicModule : DiscordModule + { + public static ConcurrentDictionary MusicPlayers = new ConcurrentDictionary(); + + public const string MusicDataPath = "data/musicdata"; + private IGoogleApiService _google; + + public MusicModule(ILocalization loc, CommandService cmds, IBotConfiguration config, DiscordSocketClient client, IGoogleApiService google) : base(loc, cmds, config, client) + { + //it can fail if its currenctly opened or doesn't exist. Either way i don't care + try { Directory.Delete(MusicDataPath, true); } catch { } + + Directory.CreateDirectory(MusicDataPath); + + _google = google; + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Next(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; + if (musicPlayer.PlaybackVoiceChannel == ((IGuildUser)imsg.Author).VoiceChannel) + musicPlayer.Next(); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Stop(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; + if (((IGuildUser)imsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel) + { + musicPlayer.Autoplay = false; + musicPlayer.Stop(); + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Destroy(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + + MusicPlayer musicPlayer; + if (!MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer)) return; + if (((IGuildUser)imsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel) + musicPlayer.Destroy(); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Pause(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + musicPlayer.TogglePause(); + if (musicPlayer.Paused) + await channel.SendMessageAsync("🎡`Music Player paused.`").ConfigureAwait(false); + else + await channel.SendMessageAsync("🎡`Music Player unpaused.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Queue(IMessage imsg, [Remainder] string query) + { + var channel = (ITextChannel)imsg.Channel; + + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, query).ConfigureAwait(false); + if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) + { + await Task.Delay(10000).ConfigureAwait(false); + await imsg.DeleteAsync().ConfigureAwait(false); + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task SoundCloudQueue(IMessage imsg, [Remainder] string query) + { + var channel = (ITextChannel)imsg.Channel; + + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false); + if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) + { + await Task.Delay(10000).ConfigureAwait(false); + await imsg.DeleteAsync().ConfigureAwait(false); + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task ListQueue(IMessage imsg, int page = 1) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + { + await channel.SendMessageAsync("🎡 No active music player.").ConfigureAwait(false); + return; + } + if (page <= 0) + return; + + var currentSong = musicPlayer.CurrentSong; + if (currentSong == null) + return; + var toSend = $"🎡`Now Playing` {currentSong.PrettyName} " + $"{currentSong.PrettyCurrentTime()}\n"; + 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.SendMessageAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task NowPlaying(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + var currentSong = musicPlayer.CurrentSong; + if (currentSong == null) + return; + await channel.SendMessageAsync($"🎡`Now Playing` {currentSong.PrettyName} " + + $"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Volume(IMessage imsg, int val) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + if (val < 0) + return; + var volume = musicPlayer.SetVolume(val); + await channel.SendMessageAsync($"🎡 `Volume set to {volume}%`").ConfigureAwait(false); + } + ////todo DB + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task Defvol(IMessage imsg, [Remainder] int val) + //{ + // var channel = (ITextChannel)imsg.Channel; + // var arg = val; + // float volume; + // if (!float.TryParse(arg, out volume) || volume < 0 || volume > 100) + // { + // await channel.SendMessageAsync("Volume number invalid.").ConfigureAwait(false); + // return; + // } + // var conf = SpecificConfigurations.Default.Of(channel.Guild.Id); + // conf.DefaultMusicVolume = volume / 100; + // await channel.SendMessageAsync($"🎡 `Default volume set to {volume}%`").ConfigureAwait(false); + //} + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Mute(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + musicPlayer.SetVolume(0); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Max(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + musicPlayer.SetVolume(100); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Shuffle(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + if (musicPlayer.Playlist.Count < 2) + { + await channel.SendMessageAsync("πŸ’’ Not enough songs in order to perform the shuffle.").ConfigureAwait(false); + return; + } + + musicPlayer.Shuffle(); + await channel.SendMessageAsync("🎡 `Songs shuffled.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Playlist(IMessage imsg, [Remainder] string playlist) + { + var channel = (ITextChannel)imsg.Channel; + var arg = playlist; + if (string.IsNullOrWhiteSpace(arg)) + return; + if (((IGuildUser)imsg.Author).VoiceChannel?.Guild != channel.Guild) + { + await channel.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); + return; + } + var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault(); + if (plId == null) + { + await channel.SendMessageAsync("No search results for that query."); + return; + } + var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); + if (!ids.Any()) + { + await channel.SendMessageAsync($"🎡 `Failed to find any songs.`").ConfigureAwait(false); + return; + } + var idArray = ids as string[] ?? ids.ToArray(); + var count = idArray.Length; + var msg = + await channel.SendMessageAsync($"🎡 `Attempting to queue {count} songs".SnPl(count) + "...`").ConfigureAwait(false); + foreach (var id in idArray) + { + try + { + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, id, true).ConfigureAwait(false); + } + catch (PlaylistFullException) + { break; } + catch { } + } + await msg.ModifyAsync(m => m.Content = "🎡 `Playlist queue complete.`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task SoundCloudPl(IMessage imsg, [Remainder] string pl) + { + var channel = (ITextChannel)imsg.Channel; + pl = pl?.Trim(); + + if (string.IsNullOrWhiteSpace(pl)) + return; + + using (var http = new HttpClient()) + { + var scvids = JObject.Parse(await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Credentials.SoundCloudClientId}").ConfigureAwait(false))["tracks"].ToObject(); + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false); + + MusicPlayer mp; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out mp)) + return; + + foreach (var svideo in scvids.Skip(1)) + { + try + { + mp.AddSong(new Song(new Classes.SongInfo + { + Title = svideo.FullName, + Provider = "SoundCloud", + Uri = svideo.StreamLink, + ProviderType = MusicType.Normal, + Query = svideo.TrackLink, + }), ((IGuildUser)imsg.Author).Username); + } + catch (PlaylistFullException) { break; } + } + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task LocalPl(IMessage imsg, [Remainder] string directory) + { + var channel = (ITextChannel)imsg.Channel; + var arg = directory; + if (string.IsNullOrWhiteSpace(arg)) + return; + try + { + var fileEnum = new DirectoryInfo(arg).GetFiles() + .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); + foreach (var file in fileEnum) + { + try + { + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); + } + catch (PlaylistFullException) + { + break; + } + catch { } + } + await channel.SendMessageAsync("🎡 `Directory queue complete.`").ConfigureAwait(false); + } + catch { } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Radio(IMessage imsg, string radio_link) + { + var channel = (ITextChannel)imsg.Channel; + if (((IGuildUser)imsg.Author).VoiceChannel?.Guild != channel.Guild) + { + await channel.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); + return; + } + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false); + if (channel.Guild.GetCurrentUser().GetPermissions(channel).ManageMessages) + { + await Task.Delay(10000).ConfigureAwait(false); + await imsg.DeleteAsync().ConfigureAwait(false); + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Local(IMessage imsg, [Remainder] string path) + { + var channel = (ITextChannel)imsg.Channel; + var arg = path; + if (string.IsNullOrWhiteSpace(arg)) + return; + await QueueSong(((IGuildUser)imsg.Author), channel, ((IGuildUser)imsg.Author).VoiceChannel, path, musicType: MusicType.Local).ConfigureAwait(false); + + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Move(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + var voiceChannel = ((IGuildUser)imsg.Author).VoiceChannel; + if (voiceChannel == null || voiceChannel.Guild != channel.Guild || !MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + musicPlayer.MoveToVoiceChannel(voiceChannel); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Remove(IMessage imsg, int num) + { + var channel = (ITextChannel)imsg.Channel; + + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + { + return; + } + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + if (num <= 0 || num > musicPlayer.Playlist.Count) + return; + var song = (musicPlayer.Playlist as List)?[num - 1]; + musicPlayer.RemoveSongAt(num - 1); + await channel.SendMessageAsync($"🎡**Track {song.PrettyName} at position `#{num}` has been removed.**").ConfigureAwait(false); + } + //todo fix + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Remove(IMessage imsg, string all) + { + var channel = (ITextChannel)imsg.Channel; + + if (all.Trim().ToUpperInvariant() != "ALL") + return; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return; + musicPlayer.ClearQueue(); + await channel.SendMessageAsync($"🎡`Queue cleared!`").ConfigureAwait(false); + return; + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task MoveSong(IMessage imsg, [Remainder] string fromto) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + { + return; + } + fromto = fromto?.Trim(); + var fromtoArr = fromto.Split('>'); + + int n1; + int n2; + + var playlist = musicPlayer.Playlist as List ?? musicPlayer.Playlist.ToList(); + + if (fromtoArr.Length != 2 || !int.TryParse(fromtoArr[0], out n1) || + !int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 || + n1 > playlist.Count || n2 > playlist.Count) + { + await channel.SendMessageAsync("`Invalid input.`").ConfigureAwait(false); + return; + } + + var s = playlist[n1 - 1]; + playlist.Insert(n2 - 1, s); + var nn1 = n2 < n1 ? n1 : n1 - 1; + playlist.RemoveAt(nn1); + + await channel.SendMessageAsync($"🎡`Moved` {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false); + + + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task SetMaxQueue(IMessage imsg, uint size) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + { + return; + } + musicPlayer.MaxQueueSize = size; + await channel.SendMessageAsync($"🎡 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task ReptCurSong(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + var currentSong = musicPlayer.CurrentSong; + if (currentSong == null) + return; + var currentValue = musicPlayer.ToggleRepeatSong(); + await channel.SendMessageAsync(currentValue ? + $"πŸŽ΅πŸ”‚`Repeating track:`{currentSong.PrettyName}" : + $"πŸŽ΅πŸ”‚`Current track repeat stopped.`") + .ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task RepeatPl(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + var currentValue = musicPlayer.ToggleRepeatPlaylist(); + await channel.SendMessageAsync($"πŸŽ΅πŸ”`Repeat playlist {(currentValue ? "enabled" : "disabled")}`").ConfigureAwait(false); + } + + /// + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task Save(IMessage imsg, [Remainder] string name) + //{ + // var channel = (ITextChannel)imsg.Channel; + + //} + + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task Load(IMessage imsg, [Remainder] string name) + //{ + // var channel = (ITextChannel)imsg.Channel; + + //} + + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task Playlists(IMessage imsg, [Remainder] string num) + //{ + // var channel = (ITextChannel)imsg.Channel; + + //} + + //[LocalizedCommand, LocalizedDescription, LocalizedSummary] + //[RequireContext(ContextType.Guild)] + //public async Task DeletePlaylist(IMessage imsg, [Remainder] string pl) + //{ + // var channel = (ITextChannel)imsg.Channel; + + //} + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Goto(IMessage imsg, int time) + { + var channel = (ITextChannel)imsg.Channel; + + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + if (((IGuildUser)imsg.Author).VoiceChannel != musicPlayer.PlaybackVoiceChannel) + return; + + if (time < 0) + return; + + var currentSong = musicPlayer.CurrentSong; + + if (currentSong == null) + return; + + //currentSong.PrintStatusMessage = false; + var gotoSong = currentSong.Clone(); + gotoSong.SkipTo = time; + musicPlayer.AddSong(gotoSong, 0); + musicPlayer.Next(); + + var minutes = (time / 60).ToString(); + var seconds = (time % 60).ToString(); + + if (minutes.Length == 1) + minutes = "0" + minutes; + if (seconds.Length == 1) + seconds = "0" + seconds; + + await channel.SendMessageAsync($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false); + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task GetLink(IMessage imsg, int index = 0) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + + if (index < 0) + return; + + if (index > 0) + { + + var selSong = musicPlayer.Playlist.DefaultIfEmpty(null).ElementAtOrDefault(index - 1); + if (selSong == null) + { + await channel.SendMessageAsync("Could not select song, likely wrong index"); + + } + else + { + await channel.SendMessageAsync($"🎢`Selected song {selSong.SongInfo.Title}:` <{selSong.SongInfo.Query}>").ConfigureAwait(false); + } + } + else + { + var curSong = musicPlayer.CurrentSong; + if (curSong == null) + return; + await channel.SendMessageAsync($"🎢`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false); + } + } + + [LocalizedCommand, LocalizedDescription, LocalizedSummary] + [RequireContext(ContextType.Guild)] + public async Task Autoplay(IMessage imsg) + { + var channel = (ITextChannel)imsg.Channel; + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) + return; + + if (!musicPlayer.ToggleAutoplay()) + await channel.SendMessageAsync("🎢`Autoplay disabled.`").ConfigureAwait(false); + else + await channel.SendMessageAsync("🎢`Autoplay enabled.`").ConfigureAwait(false); + } + + public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) + { + if (voiceCh == null || voiceCh.Guild != textCh.Guild) + { + if (!silent) + await textCh.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining.").ConfigureAwait(false); + throw new ArgumentNullException(nameof(voiceCh)); + } + if (string.IsNullOrWhiteSpace(query) || query.Length < 3) + throw new ArgumentException("πŸ’’ Invalid query for queue song.", nameof(query)); + + var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server => + { + //todo DB + float vol = 100;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; + var mp = new MusicPlayer(voiceCh, vol); + + + IMessage playingMessage = null; + IMessage lastFinishedMessage = null; + mp.OnCompleted += async (s, song) => + { + if (song.PrintStatusMessage) + { + try + { + if (lastFinishedMessage != null) + await lastFinishedMessage.DeleteAsync().ConfigureAwait(false); + if (playingMessage != null) + await playingMessage.DeleteAsync().ConfigureAwait(false); + lastFinishedMessage = await textCh.SendMessageAsync($"🎡`Finished`{song.PrettyName}").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 (Exception e) + { + Console.WriteLine(e); + } + } + }; + mp.OnStarted += async (s, song) => + { + if (song.PrintStatusMessage) + { + var sender = s as MusicPlayer; + if (sender == null) + return; + + try + { + + var msgTxt = $"🎡`Playing`{song.PrettyName} `Vol: {(int)(sender.Volume * 100)}%`"; + playingMessage = await textCh.SendMessageAsync(msgTxt).ConfigureAwait(false); + } + catch { } + } + }; + return mp; + }); + Song resolvedSong; + try + { + musicPlayer.ThrowIfQueueFull(); + resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false); + + musicPlayer.AddSong(resolvedSong, queuer.Username); + } + catch (PlaylistFullException) + { + await textCh.SendMessageAsync($"🎡 `Queue is full at {musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}.` "); + throw; + } + if (!silent) + { + var queuedMessage = await textCh.SendMessageAsync($"🎡`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + Task.Run(async () => + { + await Task.Delay(10000).ConfigureAwait(false); + try + { + await queuedMessage.DeleteAsync().ConfigureAwait(false); + } + catch { } + }).ConfigureAwait(false); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index b3977f5a..3fb08633 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -26,7 +26,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Hentai(IMessage imsg, [Remainder] string tag = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; tag = tag?.Trim() ?? ""; @@ -45,7 +45,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Danbooru(IMessage imsg, [Remainder] string tag = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; tag = tag?.Trim() ?? ""; var link = await GetDanbooruImageLink(tag).ConfigureAwait(false); @@ -59,7 +59,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Gelbooru(IMessage imsg, [Remainder] string tag = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; tag = tag?.Trim() ?? ""; var link = await GetRule34ImageLink(tag).ConfigureAwait(false); @@ -73,7 +73,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Rule34(IMessage imsg, [Remainder] string tag = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; tag = tag?.Trim() ?? ""; var link = await GetGelbooruImageLink(tag).ConfigureAwait(false); @@ -87,7 +87,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task E621(IMessage imsg, [Remainder] string tag = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; tag = tag?.Trim() ?? ""; var link = await GetE621ImageLink(tag).ConfigureAwait(false); @@ -101,7 +101,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Cp(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendMessageAsync("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false); } @@ -110,7 +110,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Boobs(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { JToken obj; @@ -130,7 +130,7 @@ namespace NadekoBot.Modules.NSFW [RequireContext(ContextType.Guild)] public async Task Butts(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index d5624c15..fef39c40 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Poke(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; } diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index f9b309d8..de6bf340 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -15,7 +15,7 @@ // [RequireContext(ContextType.Guild)] // public async Task Anime(IMessage imsg, [Remainder] string query = null) // { -// var channel = imsg.Channel as ITextChannel; +// var channel = (ITextChannel)imsg.Channel; // if (!(await ValidateQuery(imsg.Channel as ITextChannel, query).ConfigureAwait(false))) return; // string result; @@ -36,7 +36,7 @@ // [RequireContext(ContextType.Guild)] // public async Task Manga(IMessage imsg, [Remainder] string query = null) // { -// var channel = imsg.Channel as ITextChannel; +// var channel = (ITextChannel)imsg.Channel; // if (!(await ValidateQuery(imsg.Channel as ITextChannel, query).ConfigureAwait(false))) return; // string result; diff --git a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs index c9c89d33..450c126b 100644 --- a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs @@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Yomama(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { var response = await http.GetStringAsync("http://api.yomomma.info/").ConfigureAwait(false); @@ -58,7 +58,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Randjoke(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { var response = await http.GetStringAsync("http://tambal.azurewebsites.net/joke/random").ConfigureAwait(false); @@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task ChuckNorris(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { var response = await http.GetStringAsync("http://api.icndb.com/jokes/random/").ConfigureAwait(false); @@ -82,7 +82,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task WowJoke(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!wowJokes.Any()) { @@ -94,7 +94,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task MagicItem(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var rng = new Random(); var item = magicItems[rng.Next(0, magicItems.Count)].ToString(); diff --git a/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs b/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs index 1a4a5de9..7ff589a3 100644 --- a/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs @@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Lolban(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; diff --git a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs index d551655c..b6ce9b79 100644 --- a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Memelist(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { var data = JsonConvert.DeserializeObject>(await http.GetStringAsync("http://memegen.link/templates/")) @@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Memegen(IMessage imsg, string meme, string topText, string botText) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var top = Uri.EscapeDataString(topText.Replace(' ', '-')); var bot = Uri.EscapeDataString(botText.Replace(' ', '-')); diff --git a/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs b/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs index 19479696..1f77ba04 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs @@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Osu(IMessage imsg, string usr, string mode) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(usr)) return; @@ -63,7 +63,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Osub(IMessage imsg, [Remainder] string map) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) { @@ -100,7 +100,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Osu5(IMessage imsg, string user, [Remainder] string mode) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) { await channel.SendMessageAsync("πŸ’’ An osu! API key is required.").ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index 48789faf..9a5e0d3a 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Pokemon(IMessage imsg, [Remainder] string pokemon = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; pokemon = pokemon?.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(pokemon)) @@ -64,7 +64,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task PokemonAbility(IMessage imsg, [Remainder] string ability = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; ability = ability?.Trim().ToUpperInvariant().Replace(" ", ""); if (string.IsNullOrWhiteSpace(ability)) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index ea571a23..1f587161 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Searches [RequireContext(ContextType.Guild)] public async Task Weather(IMessage imsg, string city, string country) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; city = city.Replace(" ", ""); country = city.Replace(" ", ""); string response; @@ -51,9 +51,9 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Youtube(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!(await ValidateQuery(imsg.Channel as ITextChannel, query).ConfigureAwait(false))) return; - var result = (await _google.FindVideosByKeywordsAsync(query, 1)).FirstOrDefault(); + var result = (await _google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault(); if (string.IsNullOrWhiteSpace(result)) { await channel.SendMessageAsync("No results found for that query."); @@ -66,7 +66,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Imdb(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (!(await ValidateQuery(imsg.Channel as ITextChannel, query).ConfigureAwait(false))) return; await imsg.Channel.TriggerTypingAsync().ConfigureAwait(false); @@ -90,7 +90,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task RandomCat(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { await channel.SendMessageAsync(JObject.Parse( @@ -103,7 +103,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task RandomDog(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { await channel.SendMessageAsync("http://random.dog/" + await http.GetStringAsync("http://random.dog/woof").ConfigureAwait(false)).ConfigureAwait(false); @@ -114,7 +114,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task I(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(query)) return; @@ -144,7 +144,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Ir(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(query)) return; @@ -176,7 +176,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Lmgtfy(IMessage imsg, [Remainder] string ffs = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; if (string.IsNullOrWhiteSpace(ffs)) @@ -190,7 +190,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Google(IMessage imsg, [Remainder] string terms = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; terms = terms?.Trim(); @@ -204,7 +204,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 //[RequireContext(ContextType.Guild)] //public async Task Hearthstone(IMessage imsg, [Remainder] string name = null) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var arg = name; // if (string.IsNullOrWhiteSpace(arg)) // { @@ -249,7 +249,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Ud(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var arg = query; if (string.IsNullOrWhiteSpace(arg)) @@ -283,7 +283,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Hashtag(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var arg = query; if (string.IsNullOrWhiteSpace(arg)) @@ -318,7 +318,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 //[RequireContext(ContextType.Guild)] //public async Task Quote(IMessage imsg) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // var quote = NadekoBot.Config.Quotes[rng.Next(0, NadekoBot.Config.Quotes.Count)].ToString(); // await channel.SendMessageAsync(quote).ConfigureAwait(false); @@ -328,7 +328,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Catfact(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; using (var http = new HttpClient()) { var response = await http.GetStringAsync("http://catfacts-api.appspot.com/api/facts").ConfigureAwait(false); @@ -342,7 +342,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Revav(IMessage imsg, [Remainder] string arg = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var usrStr = arg?.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(usrStr)) @@ -359,7 +359,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Revimg(IMessage imsg, [Remainder] string imageLink = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; imageLink = imageLink?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(imageLink)) @@ -371,7 +371,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Safebooru(IMessage imsg, [Remainder] string tag = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; tag = tag?.Trim() ?? ""; var link = await GetSafebooruImageLink(tag).ConfigureAwait(false); @@ -385,7 +385,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Wiki(IMessage imsg, [Remainder] string query = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; query = query?.Trim(); if (string.IsNullOrWhiteSpace(query)) @@ -406,7 +406,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 //[RequireContext(ContextType.Guild)] //public async Task Clr(IMessage imsg, [Remainder] string color = null) //{ - // var channel = imsg.Channel as ITextChannel; + // var channel = (ITextChannel)imsg.Channel; // color = color?.Trim().Replace("#", ""); // if (string.IsNullOrWhiteSpace((string)color)) @@ -431,7 +431,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Videocall(IMessage imsg, [Remainder] string arg = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { @@ -454,7 +454,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 [RequireContext(ContextType.Guild)] public async Task Avatar(IMessage imsg, [Remainder] string mention = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var usr = imsg.MentionedUsers.FirstOrDefault(); if (usr == null) diff --git a/src/NadekoBot/Modules/Translator/Translator.cs b/src/NadekoBot/Modules/Translator/Translator.cs index c6833b4c..a53e0eb6 100644 --- a/src/NadekoBot/Modules/Translator/Translator.cs +++ b/src/NadekoBot/Modules/Translator/Translator.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Translator [RequireContext(ContextType.Guild)] public async Task Translate(IMessage imsg, string langs, [Remainder] string text = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; try { @@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Translator [RequireContext(ContextType.Guild)] public async Task Translangs(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => str, columns: 4); } diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 587debef..a9f9ecbb 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -27,7 +27,7 @@ namespace NadekoBot.Modules.Utility [RequireContext(ContextType.Guild)] public async Task WhoPlays(IMessage imsg, [Remainder] string game = null) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; game = game.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(game)) return; @@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Utility { if (string.IsNullOrWhiteSpace(roles)) return; - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant()); string send = _l["`Here is a list of users in a specfic role:`"]; foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@EVERYONE" && str != "EVERYONE")) @@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Utility [RequireContext(ContextType.Guild)] public async Task Stats(IMessage imsg) { - var channel = imsg.Channel as ITextChannel; + var channel = (ITextChannel)imsg.Channel; await channel.SendMessageAsync(await NadekoBot.Stats.Print()); } diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index c20aa1fb..d71c5364 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -23,7 +23,7 @@ namespace NadekoBot public static Localization Localizer { get; private set; } public static BotCredentials Credentials { get; private set; } - private static GoogleApiService Youtube { get; set; } + public static GoogleApiService Google { get; set; } public static StatsService Stats { get; private set; } public async Task RunAsync(string[] args) @@ -43,7 +43,7 @@ namespace NadekoBot Commands = new CommandService(); Config = new BotConfiguration(); Localizer = new Localization(); - Youtube = new GoogleApiService(); + Google = new GoogleApiService(); Stats = new StatsService(Client); _log = LogManager.GetCurrentClassLogger(); @@ -53,7 +53,7 @@ namespace NadekoBot depMap.Add(Config); depMap.Add(Client); depMap.Add(Commands); - depMap.Add(Youtube); + depMap.Add(Google); //connect await Client.LoginAsync(TokenType.Bot, Credentials.Token); diff --git a/src/NadekoBot/Services/IGoogleApiService.cs b/src/NadekoBot/Services/IGoogleApiService.cs index c872b96e..43d0f4da 100644 --- a/src/NadekoBot/Services/IGoogleApiService.cs +++ b/src/NadekoBot/Services/IGoogleApiService.cs @@ -5,9 +5,10 @@ namespace NadekoBot.Services { public interface IGoogleApiService { - Task> FindVideosByKeywordsAsync(string keywords, int count = 1); - Task> FindPlaylistIdsByKeywordsAsync(string keywords, int count = 1); - Task> FindRelatedVideosAsync(string url, int count = 1); + Task> GetVideosByKeywordsAsync(string keywords, int count = 1); + Task> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1); + Task> GetRelatedVideosAsync(string url, int count = 1); + Task> GetPlaylistTracksAsync(string playlistId, int count = 50); Task ShortenUrl(string url); } diff --git a/src/NadekoBot/Services/Impl/BotCredentials.cs b/src/NadekoBot/Services/Impl/BotCredentials.cs index d06a0cbb..f2515391 100644 --- a/src/NadekoBot/Services/Impl/BotCredentials.cs +++ b/src/NadekoBot/Services/Impl/BotCredentials.cs @@ -24,6 +24,8 @@ namespace NadekoBot.Services.Impl public ulong[] OwnerIds { get; } public string LoLApiKey { get; } + public string OsuApiKey { get; } + public string SoundCloudClientId { get; } public BotCredentials() { @@ -36,17 +38,22 @@ namespace NadekoBot.Services.Impl LoLApiKey = cm.LoLApiKey; GoogleApiKey = cm.GoogleApiKey; MashapeKey = cm.MashapeKey; + OsuApiKey = cm.OsuApiKey; + SoundCloudClientId = cm.SoundCloudClientId; } else _log.Fatal("credentials.json is missing. Failed to start."); } - private class CredentialsModel { + private class CredentialsModel + { public string Token { get; set; } public ulong[] OwnerIds { get; set; } public string LoLApiKey { get; set; } public string GoogleApiKey { get; set; } public string MashapeKey { get; set; } + public string OsuApiKey { get; set; } + public string SoundCloudClientId { get; set; } } public bool IsOwner(IUser u) => OwnerIds.Contains(u.Id); diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index 7a27c785..4d74e290 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -26,7 +26,7 @@ namespace NadekoBot.Services.Impl yt = new YouTubeService(bcs); sh = new UrlshortenerService(bcs); } - public async Task> FindPlaylistIdsByKeywordsAsync(string keywords, int count = 1) + public async Task> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) { if (string.IsNullOrWhiteSpace(keywords)) throw new ArgumentNullException(nameof(keywords)); @@ -46,7 +46,7 @@ namespace NadekoBot.Services.Impl return (await query.ExecuteAsync()).Items.Select(i => i.Id.PlaylistId); } - public async Task> FindRelatedVideosAsync(string id, int count = 1) + public async Task> GetRelatedVideosAsync(string id, int count = 1) { if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); @@ -66,7 +66,7 @@ namespace NadekoBot.Services.Impl return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId); } - public async Task> FindVideosByKeywordsAsync(string keywords, int count = 1) + public async Task> GetVideosByKeywordsAsync(string keywords, int count = 1) { if (string.IsNullOrWhiteSpace(keywords)) throw new ArgumentNullException(nameof(keywords)); @@ -89,5 +89,37 @@ namespace NadekoBot.Services.Impl var response = await sh.Url.Insert(new Url { LongUrl = url }).ExecuteAsync(); return response.Id; } + + public async Task> GetPlaylistTracksAsync(string playlistId, int count = 50) + { + if (string.IsNullOrWhiteSpace(playlistId)) + throw new ArgumentNullException(nameof(playlistId)); + + if (count <= 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + string nextPageToken = null; + + List toReturn = new List(count); + + do + { + var toGet = count > 50 ? 50 : count; + count -= toGet; + + var query = yt.PlaylistItems.List("contentDetails"); + query.MaxResults = count; + query.PlaylistId = playlistId; + query.PageToken = nextPageToken; + + var data = await query.ExecuteAsync(); + + toReturn.AddRange(data.Items.Select(i => i.ContentDetails.VideoId)); + nextPageToken = data.NextPageToken; + } + while (count > 0 && !string.IsNullOrWhiteSpace(nextPageToken)); + + return toReturn; + } } } diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index d9265ce3..5e574c07 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -22,6 +22,8 @@ namespace NadekoBot.Extensions http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); } + public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + public static async Task SendMessageAsync(this IGuildUser user, string message, bool isTTS = false) => await (await user.CreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(message, isTTS).ConfigureAwait(false); diff --git a/src/NadekoBot/_Modules/Music/Classes/PoopyBuffer.cs b/src/NadekoBot/_Modules/Music/Classes/PoopyBuffer.cs deleted file mode 100644 index c4a95daa..00000000 --- a/src/NadekoBot/_Modules/Music/Classes/PoopyBuffer.cs +++ /dev/null @@ -1,131 +0,0 @@ -ο»Ώusing System; -using System.Threading; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Music.Classes -{ - - /// - /// πŸ’© - /// - public class PoopyBuffer - { - - private readonly byte[] ringBuffer; - - public int WritePosition { get; private set; } = 0; - public int ReadPosition { get; private set; } = 0; - - public int ContentLength => (WritePosition >= ReadPosition ? - WritePosition - ReadPosition : - (BufferSize - ReadPosition) + WritePosition); - - public int BufferSize { get; } - - private readonly SemaphoreSlim readWriteLock = new SemaphoreSlim(1, 1); - - public PoopyBuffer(int size) - { - if (size <= 0) - throw new ArgumentException(); - BufferSize = size; - ringBuffer = new byte[size]; - } - - public Task ReadAsync(byte[] buffer, int count) - { - return Task.Run(async () => - { - if (buffer.Length < count) - throw new ArgumentException(); - //Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***"); - await readWriteLock.WaitAsync().ConfigureAwait(false); - try - { - //read as much as you can if you're reading too much - if (count > ContentLength) - count = ContentLength; - //if nothing to read, return 0 - if (WritePosition == ReadPosition) - return 0; - // if buffer is in the "normal" state, just read - if (WritePosition > ReadPosition) - { - Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count); - ReadPosition += count; - //Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]"); - return count; - } - //else ReadPos buffer.Length) - throw new ArgumentException(); - while (ContentLength + count > BufferSize) - { - await Task.Delay(20, cancelToken).ConfigureAwait(false); - if (cancelToken.IsCancellationRequested) - return; - } - await Task.Run(async () => - { - //the while above assures that i cannot write past readposition with my write, so i don't have to check - // *unless its multithreaded or task is not awaited - await readWriteLock.WaitAsync().ConfigureAwait(false); - try - { - // if i can just write without hitting buffer.length, do it - if (WritePosition + count < BufferSize) - { - Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count); - WritePosition += count; - //Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]"); - return; - } - // otherwise, i have to write to the end, then write the rest from the start - - var wroteNormaly = BufferSize - WritePosition; - Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly); - - //Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]"); - - var wroteFromStart = count - wroteNormaly; - Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart); - - //Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}"); - - WritePosition = wroteFromStart; - } - finally { readWriteLock.Release(); } - }); - } - } -} diff --git a/src/NadekoBot/_Modules/Music/MusicModule.cs b/src/NadekoBot/_Modules/Music/MusicModule.cs deleted file mode 100644 index 49308c94..00000000 --- a/src/NadekoBot/_Modules/Music/MusicModule.cs +++ /dev/null @@ -1,900 +0,0 @@ -ο»Ώusing Discord; -using Discord.Commands; -using Discord.Modules; -using NadekoBot.Classes; -using NadekoBot.DataModels; -using NadekoBot.Extensions; -using NadekoBot.Modules.Music.Classes; -using NadekoBot.Modules.Permissions.Classes; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Music -{ - internal class MusicModule : DiscordModule - { - public static ConcurrentDictionary MusicPlayers = new ConcurrentDictionary(); - - public const string MusicDataPath = "data/musicdata"; - - public MusicModule() - { - //it can fail if its currenctly opened or doesn't exist. Either way i don't care - try { Directory.Delete(MusicDataPath, true); } catch { } - - Directory.CreateDirectory(MusicDataPath); - } - - public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Music; - - public override void Install(ModuleManager manager) - { - var client = NadekoBot.Client; - - manager.CreateCommands("", cgb => - { - - cgb.AddCheck(PermissionChecker.Instance); - - commands.ForEach(cmd => cmd.Init(cgb)); - - cgb.CreateCommand(Prefix + "next") - .Alias(Prefix + "n") - .Alias(Prefix + "skip") - .Description($"Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `{Prefix}n`") - .Do(e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) return; - if (musicPlayer.PlaybackVoiceChannel == imsg.Author.VoiceChannel) - musicPlayer.Next(); - }); - - cgb.CreateCommand(Prefix + "stop") - .Alias(Prefix + "s") - .Description($"Stops the music and clears the playlist. Stays in the channel. | `{Prefix}s`") - .Do(e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) return; - if (imsg.Author.VoiceChannel == musicPlayer.PlaybackVoiceChannel) - { - musicPlayer.Autoplay = false; - musicPlayer.Stop(); - } - }); - - cgb.CreateCommand(Prefix + "destroy") - .Alias(Prefix + "d") - .Description("Completely stops the music and unbinds the bot from the channel. " + - $"(may cause weird behaviour) | `{Prefix}d`") - .Do(e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryRemove(e.Server, out musicPlayer)) return; - if (imsg.Author.VoiceChannel == musicPlayer.PlaybackVoiceChannel) - musicPlayer.Destroy(); - }); - - cgb.CreateCommand(Prefix + "pause") - .Alias(Prefix + "p") - .Description($"Pauses or Unpauses the song. | `{Prefix}p`") - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - musicPlayer.TogglePause(); - if (musicPlayer.Paused) - await channel.SendMessageAsync("🎡`Music Player paused.`").ConfigureAwait(false); - else - await channel.SendMessageAsync("🎡`Music Player unpaused.`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "queue") - .Alias(Prefix + "q") - .Alias(Prefix + "yq") - .Description("Queue a song using keywords or a link. Bot will join your voice channel." + - $"**You must be in a voice channel**. | `{Prefix}q Dream Of Venice`") - .Parameter("query", ParameterType.Unparsed) - .Do(async e => - { - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, query).ConfigureAwait(false); - if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) - { - await Task.Delay(10000).ConfigureAwait(false); - await e.Message.Delete().ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "soundcloudqueue") - .Alias(Prefix + "sq") - .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + - $"**You must be in a voice channel**. | `{Prefix}sq Dream Of Venice`") - .Parameter("query", ParameterType.Unparsed) - .Do(async e => - { - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false); - if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) - { - await Task.Delay(10000).ConfigureAwait(false); - await e.Message.Delete().ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "listqueue") - .Alias(Prefix + "lq") - .Description($"Lists 15 currently queued songs per page. Default page is 1. | `{Prefix}lq` or `{Prefix}lq 2`") - .Parameter("page", ParameterType.Optional) - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - { - await channel.SendMessageAsync("🎡 No active music player.").ConfigureAwait(false); - return; - } - - int page; - if (!int.TryParse(page, out page) || page <= 0) - { - page = 1; - } - - var currentSong = musicPlayer.CurrentSong; - if (currentSong == null) - return; - var toSend = $"🎡`Now Playing` {currentSong.PrettyName} " + $"{currentSong.PrettyCurrentTime()}\n"; - 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.SendMessageAsync(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "nowplaying") - .Alias(Prefix + "np") - .Description($"Shows the song currently playing. | `{Prefix}np`") - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - var currentSong = musicPlayer.CurrentSong; - if (currentSong == null) - return; - await channel.SendMessageAsync($"🎡`Now Playing` {currentSong.PrettyName} " + - $"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "volume") - .Alias(Prefix + "vol") - .Description($"Sets the music volume 0-100% | `{Prefix}vol 50`") - .Parameter("val", ParameterType.Required) - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - var arg = val; - int volume; - if (!int.TryParse(arg, out volume)) - { - await channel.SendMessageAsync("Volume number invalid.").ConfigureAwait(false); - return; - } - volume = musicPlayer.SetVolume(volume); - await channel.SendMessageAsync($"🎡 `Volume set to {volume}%`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "defvol") - .Alias(Prefix + "dv") - .Description("Sets the default music volume when music playback is started (0-100)." + - $" Persists through restarts. | `{Prefix}dv 80`") - .Parameter("val", ParameterType.Required) - .Do(async e => - { - var arg = val; - float volume; - if (!float.TryParse(arg, out volume) || volume < 0 || volume > 100) - { - await channel.SendMessageAsync("Volume number invalid.").ConfigureAwait(false); - return; - } - var conf = SpecificConfigurations.Default.Of(e.Server.Id); - conf.DefaultMusicVolume = volume / 100; - await channel.SendMessageAsync($"🎡 `Default volume set to {volume}%`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "mute") - .Alias(Prefix + "min") - .Description($"Sets the music volume to 0% | `{Prefix}min`") - .Do(e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - musicPlayer.SetVolume(0); - }); - - cgb.CreateCommand(Prefix + "max") - .Description($"Sets the music volume to 100%. | `{Prefix}max`") - .Do(e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - musicPlayer.SetVolume(100); - }); - - cgb.CreateCommand(Prefix + "half") - .Description($"Sets the music volume to 50%. | `{Prefix}half`") - .Do(e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - musicPlayer.SetVolume(50); - }); - - cgb.CreateCommand(Prefix + "shuffle") - .Alias(Prefix + "sh") - .Description($"Shuffles the current playlist. | `{Prefix}sh`") - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - if (musicPlayer.Playlist.Count < 2) - { - await channel.SendMessageAsync("πŸ’’ Not enough songs in order to perform the shuffle.").ConfigureAwait(false); - return; - } - - musicPlayer.Shuffle(); - await channel.SendMessageAsync("🎡 `Songs shuffled.`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "playlist") - .Alias(Prefix + "pl") - .Description($"Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `{Prefix}pl playlist link or name`") - .Parameter("playlist", ParameterType.Unparsed) - .Do(async e => - { - var arg = playlist; - if (string.IsNullOrWhiteSpace(arg)) - return; - if (imsg.Author.VoiceChannel?.Server != e.Server) - { - await channel.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); - return; - } - var plId = await SearchHelper.GetPlaylistIdByKeyword(arg).ConfigureAwait(false); - if (plId == null) - { - await channel.SendMessageAsync("No search results for that query."); - return; - } - var ids = await SearchHelper.GetVideoIDs(plId, 500).ConfigureAwait(false); - if (ids == null || ids.Count == 0) - { - await channel.SendMessageAsync($"🎡 `Failed to find any songs.`").ConfigureAwait(false); - return; - } - var idArray = ids as string[] ?? ids.ToArray(); - var count = idArray.Length; - var msg = - await channel.SendMessageAsync($"🎡 `Attempting to queue {count} songs".SnPl(count) + "...`").ConfigureAwait(false); - foreach (var id in idArray) - { - try - { - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, id, true).ConfigureAwait(false); - } - catch (PlaylistFullException) - { break; } - catch { } - } - await msg.Edit("🎡 `Playlist queue complete.`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "soundcloudpl") - .Alias(Prefix + "scpl") - .Description($"Queue a soundcloud playlist using a link. | `{Prefix}scpl https://soundcloud.com/saratology/sets/symphony`") - .Parameter("pl", ParameterType.Unparsed) - .Do(async e => - { - var pl = pl?.Trim(); - - if (string.IsNullOrWhiteSpace(pl)) - return; - - var scvids = JObject.Parse(await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Credentials.SoundCloudClientID}").ConfigureAwait(false))["tracks"].ToObject(); - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false); - - MusicPlayer mp; - if (!MusicPlayers.TryGetValue(e.Server, out mp)) - return; - - foreach (var svideo in scvids.Skip(1)) - { - try - { - mp.AddSong(new Song(new Classes.SongInfo - { - Title = svideo.FullName, - Provider = "SoundCloud", - Uri = svideo.StreamLink, - ProviderType = MusicType.Normal, - Query = svideo.TrackLink, - }), imsg.Author.Username); - } - catch (PlaylistFullException) { break; } - } - }); - - cgb.CreateCommand(Prefix + "localplaylst") - .Alias(Prefix + "lopl") - .Description($"Queues all songs from a directory. **Bot Owner Only!** | `{Prefix}lopl C:/music/classical`") - .Parameter("directory", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - var arg = directory; - if (string.IsNullOrWhiteSpace(arg)) - return; - try - { - var fileEnum = new DirectoryInfo(arg).GetFiles() - .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); - foreach (var file in fileEnum) - { - try - { - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); - } - catch (PlaylistFullException) - { - break; - } - catch { } - } - await channel.SendMessageAsync("🎡 `Directory queue complete.`").ConfigureAwait(false); - } - catch { } - }); - - cgb.CreateCommand(Prefix + "radio").Alias(Prefix + "ra") - .Description($"Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `{Prefix}ra radio link here`") - .Parameter("radio_link", ParameterType.Required) - .Do(async e => - { - if (imsg.Author.VoiceChannel?.Server != e.Server) - { - await channel.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); - return; - } - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false); - if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) - { - await Task.Delay(10000).ConfigureAwait(false); - await e.Message.Delete().ConfigureAwait(false); - } - }); - - cgb.CreateCommand(Prefix + "local") - .Alias(Prefix + "lo") - .Description($"Queues a local file by specifying a full path. **Bot Owner Only!** | `{Prefix}lo C:/music/mysong.mp3`") - .Parameter("path", ParameterType.Unparsed) - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - var arg = path; - if (string.IsNullOrWhiteSpace(arg)) - return; - await QueueSong(imsg.Author, e.Channel, imsg.Author.VoiceChannel, path, musicType: MusicType.Local).ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "move") - .Alias(Prefix + "mv") - .Description($"Moves the bot to your voice channel. (works only if music is already playing) | `{Prefix}mv`") - .Do(e => - { - MusicPlayer musicPlayer; - var voiceChannel = imsg.Author.VoiceChannel; - if (voiceChannel == null || voiceChannel.Server != e.Server || !MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - musicPlayer.MoveToVoiceChannel(voiceChannel); - }); - - cgb.CreateCommand(Prefix + "remove") - .Alias(Prefix + "rm") - .Description($"Remove a song by its # in the queue, or 'all' to remove whole queue. | `{Prefix}rm 5`") - .Parameter("num", ParameterType.Required) - .Do(async e => - { - var arg = num; - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - { - return; - } - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - if (arg?.ToLower() == "all") - { - musicPlayer.ClearQueue(); - await channel.SendMessageAsync($"🎡`Queue cleared!`").ConfigureAwait(false); - return; - } - int num; - if (!int.TryParse(arg, out num)) - { - return; - } - if (num <= 0 || num > musicPlayer.Playlist.Count) - return; - var song = (musicPlayer.Playlist as List)?[num - 1]; - musicPlayer.RemoveSongAt(num - 1); - await channel.SendMessageAsync($"🎡**Track {song.PrettyName} at position `#{num}` has been removed.**").ConfigureAwait(false); - }); - - //var msRegex = new Regex(@"(?\d+)>(?\d+)", RegexOptions.Compiled); - cgb.CreateCommand(Prefix + "movesong") - .Alias(Prefix + "ms") - .Description($"Moves a song from one position to another. | `{Prefix} ms` 5>3") - .Parameter("fromto") - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - { - return; - } - var fromto = fromto.Trim(); - var fromtoArr = fromto.Split('>'); - - int n1; - int n2; - - var playlist = musicPlayer.Playlist as List ?? musicPlayer.Playlist.ToList(); - - if (fromtoArr.Length != 2 || !int.TryParse(fromtoArr[0], out n1) || - !int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 || - n1 > playlist.Count || n2 > playlist.Count) - { - await channel.SendMessageAsync("`Invalid input.`").ConfigureAwait(false); - return; - } - - var s = playlist[n1 - 1]; - playlist.Insert(n2 - 1, s); - var nn1 = n2 < n1 ? n1 : n1 - 1; - playlist.RemoveAt(nn1); - - await channel.SendMessageAsync($"🎡`Moved` {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false); - - }); - - cgb.CreateCommand(Prefix + "setmaxqueue") - .Alias(Prefix + "smq") - .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. | `{Prefix}smq` 50 or `{Prefix}smq`") - .Parameter("size", ParameterType.Unparsed) - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - { - return; - } - - var sizeStr = size?.Trim(); - uint size = 0; - if (string.IsNullOrWhiteSpace(sizeStr) || !uint.TryParse(sizeStr, out size)) - { - size = 0; - } - - musicPlayer.MaxQueueSize = size; - await channel.SendMessageAsync($"🎡 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); - }); - - cgb.CreateCommand(Prefix + "cleanup") - .Description($"Cleans up hanging voice connections. **Bot Owner Only!** | `{Prefix}cleanup`") - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(e => - { - foreach (var kvp in MusicPlayers) - { - var songs = kvp.Value.Playlist; - var currentSong = kvp.Value.CurrentSong; - if (songs.Count == 0 && currentSong == null) - { - MusicPlayer throwaway; - MusicPlayers.TryRemove(kvp.Key, out throwaway); - throwaway.Destroy(); - } - } - }); - - cgb.CreateCommand(Prefix + "reptcursong") - .Alias(Prefix + "rcs") - .Description($"Toggles repeat of current song. | `{Prefix}rcs`") - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - var currentSong = musicPlayer.CurrentSong; - if (currentSong == null) - return; - var currentValue = musicPlayer.ToggleRepeatSong(); - await channel.SendMessageAsync(currentValue ? - $"πŸŽ΅πŸ”‚`Repeating track:`{currentSong.PrettyName}" : - $"πŸŽ΅πŸ”‚`Current track repeat stopped.`") - .ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "rpeatplaylst") - .Alias(Prefix + "rpl") - .Description($"Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `{Prefix}rpl`") - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - var currentValue = musicPlayer.ToggleRepeatPlaylist(); - await channel.SendMessageAsync($"πŸŽ΅πŸ”`Repeat playlist {(currentValue ? "enabled" : "disabled")}`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "save") - .Description($"Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `{Prefix}save classical1`") - .Parameter("name", ParameterType.Unparsed) - .Do(async e => - { - var name = name?.Trim(); - - if (string.IsNullOrWhiteSpace(name) || - name.Length > 20 || - name.Contains("-")) - return; - - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - - //to avoid concurrency issues - var currentPlaylist = new List(musicPlayer.Playlist); - var curSong = musicPlayer.CurrentSong; - if (curSong != null) - currentPlaylist.Insert(0, curSong); - - if (!currentPlaylist.Any()) - return; - - - var songInfos = currentPlaylist.Select(s => new DataModels.SongInfo - { - Provider = s.SongInfo.Provider, - ProviderType = (int)s.SongInfo.ProviderType, - Title = s.SongInfo.Title, - Uri = s.SongInfo.Uri, - Query = s.SongInfo.Query, - }).ToList(); - - var playlist = new MusicPlaylist - { - CreatorId = (long)imsg.Author.Id, - CreatorName = imsg.Author.Username, - Name = name.ToLowerInvariant(), - }; - DbHandler.Instance.SaveAll(songInfos); - DbHandler.Instance.Save(playlist); - DbHandler.Instance.Connection.InsertAll(songInfos.Select(s => new PlaylistSongInfo - { - PlaylistId = playlist.Id.Value, - SongInfoId = s.Id.Value - }), typeof(PlaylistSongInfo)); - - await channel.SendMessageAsync($"🎡 `Saved playlist as {name}-{playlist.Id}`").ConfigureAwait(false); - - }); - - cgb.CreateCommand(Prefix + "load") - .Description($"Loads a playlist under a certain name. | `{Prefix}load classical-1`") - .Parameter("name", ParameterType.Unparsed) - .Do(async e => - { - var voiceCh = imsg.Author.VoiceChannel; - var textCh = e.Channel; - if (voiceCh == null || voiceCh.Server != textCh.Server) - { - await textCh.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining.").ConfigureAwait(false); - return; - } - var name = name?.Trim().ToLowerInvariant(); - - if (string.IsNullOrWhiteSpace(name)) - return; - - var parts = name.Split('-'); - if (parts.Length != 2) - return; - var playlistName = parts[0]; - - int playlistNumber; - if (!int.TryParse(parts[1], out playlistNumber)) - return; - - var playlist = DbHandler.Instance.FindOne( - p => p.Id == playlistNumber); - - if (playlist == null) - { - await channel.SendMessageAsync("Can't find playlist under that name.").ConfigureAwait(false); - return; - } - - var psis = DbHandler.Instance.FindAll(psi => - psi.PlaylistId == playlist.Id); - - var songInfos = psis.Select(psi => DbHandler.Instance - .FindOne(si => si.Id == psi.SongInfoId)); - - await channel.SendMessageAsync($"`Attempting to load {songInfos.Count()} songs`").ConfigureAwait(false); - foreach (var si in songInfos) - { - try - { - await QueueSong(imsg.Author, textCh, voiceCh, si.Query, true, (MusicType)si.ProviderType).ConfigureAwait(false); - } - catch (PlaylistFullException) - { - break; - } - catch (Exception ex) - { - Console.WriteLine($"Failed QueueSong in load playlist. {ex}"); - } - } - }); - - cgb.CreateCommand(Prefix + "playlists") - .Alias(Prefix + "pls") - .Description($"Lists all playlists. Paginated. 20 per page. Default page is 0. |`{Prefix}pls 1`") - .Parameter("num", ParameterType.Optional) - .Do(e => - { - int num = 0; - int.TryParse(num, out num); - if (num < 0) - return; - var result = DbHandler.Instance.GetPlaylistData(num); - if (result.Count == 0) - channel.SendMessageAsync($"`No saved playlists found on page {num}`").ConfigureAwait(false); - else - channel.SendMessageAsync($"```js\n--- List of saved playlists ---\n\n" + string.Join("\n", result.Select(r => $"'{r.Name}-{r.Id}' by {r.Creator} ({r.SongCnt} songs)")) + $"\n\n --- Page {num} ---```").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "deleteplaylist") - .Alias(Prefix + "delpls") - .Description($"Deletes a saved playlist. Only if you made it or if you are the bot owner. | `{Prefix}delpls animu-5`") - .Parameter("pl", ParameterType.Required) - .Do(async e => - { - var pl = pl.Trim().Split('-')[1]; - if (string.IsNullOrWhiteSpace(pl)) - return; - var plnum = int.Parse(pl); - if (NadekoBot.IsOwner(imsg.Author.Id)) - DbHandler.Instance.Delete(plnum); - else - DbHandler.Instance.DeleteWhere(mp => mp.Id == plnum && (long)imsg.Author.Id == mp.CreatorId); - await channel.SendMessageAsync("`Ok.` :ok:").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "goto") - .Description($"Goes to a specific time in seconds in a song. | `{Prefix}goto 30`") - .Parameter("time") - .Do(async e => - { - var skipToStr = time?.Trim(); - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - if (imsg.Author.VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; - int skipTo; - if (!int.TryParse(skipToStr, out skipTo) || skipTo < 0) - return; - - var currentSong = musicPlayer.CurrentSong; - - if (currentSong == null) - return; - - //currentSong.PrintStatusMessage = false; - var gotoSong = currentSong.Clone(); - gotoSong.SkipTo = skipTo; - musicPlayer.AddSong(gotoSong, 0); - musicPlayer.Next(); - - var minutes = (skipTo / 60).ToString(); - var seconds = (skipTo % 60).ToString(); - - if (minutes.Length == 1) - minutes = "0" + minutes; - if (seconds.Length == 1) - seconds = "0" + seconds; - - await channel.SendMessageAsync($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false); - }); - - cgb.CreateCommand(Prefix + "getlink") - .Alias(Prefix + "gl") - .Description($"Shows a link to the song in the queue by index, or the currently playing song by default. | `{Prefix}gl`") - .Parameter("index", ParameterType.Optional) - .Do(async e => - { - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - int index; - string arg = index?.Trim(); - if (!string.IsNullOrEmpty(arg) && int.TryParse(arg, out index)) - { - - var selSong = musicPlayer.Playlist.DefaultIfEmpty(null).ElementAtOrDefault(index - 1); - if (selSong == null) - { - await channel.SendMessageAsync("Could not select song, likely wrong index"); - - } - else - { - await channel.SendMessageAsync($"🎢`Selected song {selSong.SongInfo.Title}:` <{selSong.SongInfo.Query}>").ConfigureAwait(false); - } - } - else - { - var curSong = musicPlayer.CurrentSong; - if (curSong == null) - return; - await channel.SendMessageAsync($"🎢`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false); - } - - }); - - cgb.CreateCommand(Prefix + "autoplay") - .Alias(Prefix + "ap") - .Description($"Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `{Prefix}ap`") - .Do(async e => - { - - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) - return; - - if (!musicPlayer.ToggleAutoplay()) - await channel.SendMessageAsync("🎢`Autoplay disabled.`").ConfigureAwait(false); - else - await channel.SendMessageAsync("🎢`Autoplay enabled.`").ConfigureAwait(false); - }); - }); - } - - public static async Task QueueSong(User queuer, Channel textCh, Channel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) - { - if (voiceCh == null || voiceCh.Server != textCh.Server) - { - if (!silent) - await textCh.SendMessageAsync("πŸ’’ You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining.").ConfigureAwait(false); - throw new ArgumentNullException(nameof(voiceCh)); - } - if (string.IsNullOrWhiteSpace(query) || query.Length < 3) - throw new ArgumentException("πŸ’’ Invalid query for queue song.", nameof(query)); - - var musicPlayer = MusicPlayers.GetOrAdd(textCh.Server, server => - { - float vol = SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; - var mp = new MusicPlayer(voiceCh, vol); - - - Message playingMessage = null; - Message lastFinishedMessage = null; - mp.OnCompleted += async (s, song) => - { - if (song.PrintStatusMessage) - { - try - { - if (lastFinishedMessage != null) - await lastFinishedMessage.Delete().ConfigureAwait(false); - if (playingMessage != null) - await playingMessage.Delete().ConfigureAwait(false); - lastFinishedMessage = await textCh.SendMessageAsync($"🎡`Finished`{song.PrettyName}").ConfigureAwait(false); - if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube") - { - await QueueSong(queuer.Server.CurrentUser, textCh, voiceCh, (await SearchHelper.GetRelatedVideoIds(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false); - } - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - }; - mp.OnStarted += async (s, song) => - { - if (song.PrintStatusMessage) - { - var sender = s as MusicPlayer; - if (sender == null) - return; - - try - { - - var msgTxt = $"🎡`Playing`{song.PrettyName} `Vol: {(int)(sender.Volume * 100)}%`"; - playingMessage = await textCh.SendMessageAsync(msgTxt).ConfigureAwait(false); - } - catch { } - } - }; - return mp; - }); - Song resolvedSong; - try - { - musicPlayer.ThrowIfQueueFull(); - resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false); - - musicPlayer.AddSong(resolvedSong, queuer.Name); - } - catch (PlaylistFullException) - { - await textCh.SendMessageAsync($"🎡 `Queue is full at {musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}.` "); - throw; - } - if (!silent) - { - var queuedMessage = await textCh.SendMessageAsync($"🎡`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - Task.Run(async () => - { - await Task.Delay(10000).ConfigureAwait(false); - try - { - await queuedMessage.Delete().ConfigureAwait(false); - } - catch { } - }).ConfigureAwait(false); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - } -} diff --git a/src/NadekoBot/project.json b/src/NadekoBot/project.json index 75b7a326..329311cc 100644 --- a/src/NadekoBot/project.json +++ b/src/NadekoBot/project.json @@ -5,9 +5,11 @@ "copyright": "Kwoth", "buildOptions": { "emitEntryPoint": true, + "allowUnsafe": true, "compile": { "exclude": [ "_Models", "Classes", "_Modules" ] - } + }, + "define": [] }, "dependencies": { "Microsoft.NETCore.App": { @@ -24,12 +26,16 @@ "Google.Apis.YouTube.v3": "1.15.0.582", "Google.Apis.Urlshortener.v1": "1.15.0.138", "System.Diagnostics.Contracts": "4.0.1", - "NLog": "4.4.0-betaV15" + "NLog": "4.4.0-betaV15", + "VideoLibrary": "1.3.4" }, "frameworks": { "netcoreapp1.0": { - "imports": "dnxcore50" + "imports": [ + "dnxcore50", + "portable-net45+win8+wpa81" + ] } } } diff --git a/src/NadekoBot/project.lock.json b/src/NadekoBot/project.lock.json index 430c2ca3..4fb84727 100644 --- a/src/NadekoBot/project.lock.json +++ b/src/NadekoBot/project.lock.json @@ -680,6 +680,9 @@ }, "compile": { "ref/netstandard1.3/System.Collections.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Collections.Concurrent/4.0.12": { @@ -884,6 +887,9 @@ }, "compile": { "ref/netstandard1.3/System.Diagnostics.Debug.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Diagnostics.DiagnosticSource/4.0.0": { @@ -994,6 +1000,9 @@ }, "compile": { "ref/netstandard1.0/System.Diagnostics.Tools.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Diagnostics.TraceSource/4.0.0": { @@ -1032,6 +1041,9 @@ }, "compile": { "ref/netstandard1.5/System.Diagnostics.Tracing.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wpa81/_._": {} } }, "System.Dynamic.Runtime/4.0.11": { @@ -1069,6 +1081,9 @@ }, "compile": { "ref/netstandard1.3/System.Globalization.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Globalization.Calendars/4.0.1": { @@ -1118,6 +1133,9 @@ }, "compile": { "ref/netstandard1.5/System.IO.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.IO.Compression/4.1.0": { @@ -1141,6 +1159,9 @@ "compile": { "ref/netstandard1.3/System.IO.Compression.dll": {} }, + "runtime": { + "lib/portable-net45+win8+wpa81/_._": {} + }, "runtimeTargets": { "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll": { "assetType": "runtime", @@ -1400,6 +1421,9 @@ "compile": { "ref/netstandard1.3/System.Net.Http.dll": {} }, + "runtime": { + "lib/portable-net45+win8+wpa81/_._": {} + }, "runtimeTargets": { "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll": { "assetType": "runtime", @@ -1453,6 +1477,9 @@ }, "compile": { "ref/netstandard1.3/System.Net.Primitives.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Net.Requests/4.0.11": { @@ -1475,6 +1502,9 @@ "compile": { "ref/netstandard1.3/System.Net.Requests.dll": {} }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} + }, "runtimeTargets": { "runtimes/unix/lib/netstandard1.3/System.Net.Requests.dll": { "assetType": "runtime", @@ -1650,6 +1680,9 @@ }, "compile": { "ref/netstandard1.5/System.Reflection.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Reflection.DispatchProxy/4.0.1": { @@ -1728,6 +1761,9 @@ }, "compile": { "ref/netstandard1.0/System.Reflection.Extensions.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Reflection.Metadata/1.3.0": { @@ -1765,6 +1801,9 @@ }, "compile": { "ref/netstandard1.0/System.Reflection.Primitives.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Reflection.TypeExtensions/4.1.0": { @@ -1807,6 +1846,9 @@ }, "compile": { "ref/netstandard1.0/System.Resources.ResourceManager.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Resources.ResourceWriter/4.0.0-beta-22816": { @@ -1830,6 +1872,9 @@ }, "compile": { "ref/netstandard1.5/System.Runtime.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp80+wpa81/_._": {} } }, "System.Runtime.Extensions/4.1.0": { @@ -1841,6 +1886,9 @@ }, "compile": { "ref/netstandard1.5/System.Runtime.Extensions.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Runtime.Handles/4.0.1": { @@ -1866,6 +1914,9 @@ }, "compile": { "ref/netstandard1.5/System.Runtime.InteropServices.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wpa81/_._": {} } }, "System.Runtime.InteropServices.RuntimeInformation/4.0.0": { @@ -2218,6 +2269,9 @@ }, "compile": { "ref/netstandard1.3/System.Text.Encoding.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Text.Encoding.CodePages/4.0.1": { @@ -2256,6 +2310,9 @@ }, "compile": { "ref/netstandard1.3/System.Text.Encoding.Extensions.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Text.RegularExpressions/4.1.0": { @@ -2319,6 +2376,9 @@ }, "compile": { "ref/netstandard1.3/System.Threading.Tasks.dll": {} + }, + "runtime": { + "lib/portable-net45+win8+wp8+wpa81/_._": {} } }, "System.Threading.Tasks.Dataflow/4.6.0": { @@ -2522,6 +2582,15 @@ "lib/netstandard1.3/_._": {} } }, + "VideoLibrary/1.3.4": { + "type": "package", + "compile": { + "lib/portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10/libvideo.dll": {} + }, + "runtime": { + "lib/portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10/libvideo.dll": {} + } + }, "Discord.Net/1.0.0-dev": { "type": "project", "framework": ".NETStandard,Version=v1.3", @@ -7871,6 +7940,16 @@ "ref/xamarinwatchos10/_._" ] }, + "VideoLibrary/1.3.4": { + "sha512": "HZ2RAE9xx/sjJGnwm8etawoJXYluaYGas4bAFpE14S62NFodNKzUf7Cm9TQ+JFJxAdY+1g1FEKk1b6FPSv9aMg==", + "type": "package", + "path": "VideoLibrary/1.3.4", + "files": [ + "VideoLibrary.1.3.4.nupkg.sha512", + "VideoLibrary.nuspec", + "lib/portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10/libvideo.dll" + ] + }, "Discord.Net/1.0.0-dev": { "type": "project", "path": "../../discord.net/src/Discord.Net/project.json", @@ -7895,7 +7974,8 @@ "NLog >= 4.4.0-betaV15", "Newtonsoft.Json >= 9.0.1", "System.Diagnostics.Contracts >= 4.0.1", - "System.Resources.ResourceWriter >= 4.0.0-beta-22816" + "System.Resources.ResourceWriter >= 4.0.0-beta-22816", + "VideoLibrary >= 1.3.4" ], ".NETCoreApp,Version=v1.0": [] },