using Discord.Commands; using Discord.WebSocket; using NadekoBot.Core.Services; using Discord; using System.Threading.Tasks; using System; using System.Linq; using NadekoBot.Extensions; using System.Collections.Generic; using NadekoBot.Core.Services.Database.Models; using System.IO; using System.Net.Http; using NadekoBot.Common; using NadekoBot.Common.Attributes; using NadekoBot.Common.Collections; using Newtonsoft.Json.Linq; using NadekoBot.Core.Services.Impl; using NadekoBot.Modules.Music.Services; using NadekoBot.Modules.Music.Common.Exceptions; using NadekoBot.Modules.Music.Common; using NadekoBot.Modules.Music.Extensions; namespace NadekoBot.Modules.Music { [NoPublicBot] public class Music : NadekoTopLevelModule { private readonly DiscordSocketClient _client; private readonly IBotCredentials _creds; private readonly IGoogleApiService _google; private readonly DbService _db; public Music(DiscordSocketClient client, IBotCredentials creds, IGoogleApiService google, DbService db) { _client = client; _creds = creds; _google = google; _db = db; } //todo 50 changing server region is bugged again //private Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState) //{ // var t = Task.Run(() => // { // var usr = iusr as SocketGuildUser; // if (usr == null || // oldState.VoiceChannel == newState.VoiceChannel) // return; // var player = _music.GetPlayerOrDefault(usr.Guild.Id); // if (player == null) // return; // try // { // //if bot moved // if ((player.VoiceChannel == oldState.VoiceChannel) && // usr.Id == _client.CurrentUser.Id) // { // //if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel // // player.TogglePause(); // //else if (!player.Paused && newState.VoiceChannel.Users.Count <= 1) // pause if there are no users in the new channel // // player.TogglePause(); // // player.SetVoiceChannel(newState.VoiceChannel); // return; // } // ////if some other user moved // //if ((player.VoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause // // player.Paused && // // newState.VoiceChannel.Users.Count >= 2) || // keep in mind bot is in the channel (+1) // // (player.VoiceChannel == oldState.VoiceChannel && // if left last, and player unpaused, pause // // !player.Paused && // // oldState.VoiceChannel.Users.Count == 1)) // //{ // // player.TogglePause(); // // return; // //} // } // catch // { // // ignored // } // }); // return Task.CompletedTask; //} private async Task InternalQueue(MusicPlayer mp, SongInfo songInfo, bool silent, bool queueFirst = false) { if (songInfo == null) { if(!silent) await ReplyErrorLocalized("song_not_found").ConfigureAwait(false); return; } int index; try { index = queueFirst ? mp.EnqueueNext(songInfo) : mp.Enqueue(songInfo); } catch (QueueFullException) { await ReplyErrorLocalized("queue_full", mp.MaxQueueSize).ConfigureAwait(false); throw; } if (index != -1) { if (!silent) { try { var embed = new EmbedBuilder().WithOkColor() .WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index + 1)).WithMusicIcon()) .WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ") .WithFooter(ef => ef.WithText(songInfo.PrettyProvider)); if (Uri.IsWellFormedUriString(songInfo.Thumbnail, UriKind.Absolute)) embed.WithThumbnailUrl(songInfo.Thumbnail); var queuedMessage = await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false); if (mp.Stopped) { (await ReplyErrorLocalized("queue_stopped", Format.Code(Prefix + "play")).ConfigureAwait(false)).DeleteAfter(10); } queuedMessage?.DeleteAfter(10); } catch { // ignored } } } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Play([Remainder] string query = null) { var mp = await _service.GetOrCreatePlayer(Context); if (string.IsNullOrWhiteSpace(query)) { await Next(); } else if (int.TryParse(query, out var index)) if (index >= 1) mp.SetIndex(index - 1); else return; else { try { await Queue(query); } catch { } } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Queue([Remainder] string query) { var mp = await _service.GetOrCreatePlayer(Context); var songInfo = await _service.ResolveSong(query, Context.User.ToString()); try { await InternalQueue(mp, songInfo, false); } catch (QueueFullException) { return; } if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages) { Context.Message.DeleteAfter(10); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task QueueNext([Remainder] string query) { var mp = await _service.GetOrCreatePlayer(Context); var songInfo = await _service.ResolveSong(query, Context.User.ToString()); try { await InternalQueue(mp, songInfo, false, true); } catch (QueueFullException) { return; } if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages) { Context.Message.DeleteAfter(10); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task QueueSearch([Remainder] string query) { var videos = (await _google.GetVideoInfosByKeywordAsync(query, 5)) .ToArray(); if (!videos.Any()) { await ReplyErrorLocalized("song_not_found").ConfigureAwait(false); return; } var msg = await Context.Channel.SendConfirmAsync(string.Join("\n", videos.Select((x, i) => $"`{i + 1}.`\n\t{Format.Bold(x.Name)}\n\t{x.Url}"))); try { var input = await GetUserInputAsync(Context.User.Id, Context.Channel.Id); if (input == null || !int.TryParse(input, out var index) || (index -= 1) < 0 || index >= videos.Length) { try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } return; } query = videos[index].Url; await Queue(query).ConfigureAwait(false); } finally { try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task ListQueue(int page = 0) { var mp = await _service.GetOrCreatePlayer(Context); var (current, songs) = mp.QueueArray(); if (!songs.Any()) { await ReplyErrorLocalized("no_player").ConfigureAwait(false); return; } if (--page < -1) return; try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } const int itemsPerPage = 10; if (page == -1) page = current / itemsPerPage; //if page is 0 (-1 after this decrement) that means default to the page current song is playing from var total = mp.TotalPlaytime; var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format", (int)total.TotalHours, total.Minutes, total.Seconds); var maxPlaytime = mp.MaxPlaytimeSeconds; Func printAction = curPage => { var startAt = itemsPerPage * curPage; var number = 0 + startAt; var desc = string.Join("\n", songs .Skip(startAt) .Take(itemsPerPage) .Select(v => { if(number++ == current) return $"**⇒**`{number}.` {v.PrettyFullName}"; else return $"`{number}.` {v.PrettyFullName}"; })); desc = $"`🔊` {songs[current].PrettyFullName}\n\n" + desc; var add = ""; if (mp.Stopped) add += Format.Bold(GetText("queue_stopped", Format.Code(Prefix + "play"))) + "\n"; var mps = mp.MaxPlaytimeSeconds; if (mps > 0) add += Format.Bold(GetText("song_skips_after", TimeSpan.FromSeconds(mps).ToString("HH\\:mm\\:ss"))) + "\n"; if (mp.RepeatCurrentSong) add += "🔂 " + GetText("repeating_cur_song") + "\n"; else if (mp.Shuffle) add += "🔀 " + GetText("shuffling_playlist") + "\n"; else { if (mp.Autoplay) add += "↪ " + GetText("autoplaying") + "\n"; if (mp.FairPlay && !mp.Autoplay) add += " " + GetText("fairplay") + "\n"; else if (mp.RepeatPlaylist) add += "🔁 " + GetText("repeating_playlist") + "\n"; } if (!string.IsNullOrWhiteSpace(add)) desc = add + "\n" + desc; var embed = new EmbedBuilder() .WithAuthor(eab => eab.WithName(GetText("player_queue", curPage + 1, (songs.Length / itemsPerPage) + 1)) .WithMusicIcon()) .WithDescription(desc) .WithFooter(ef => ef.WithText($"{mp.PrettyVolume} | {songs.Length} " + $"{("tracks".SnPl(songs.Length))} | {totalStr}")) .WithOkColor(); return embed; }; await Context.Channel.SendPaginatedConfirmAsync(_client, page, printAction, songs.Length, itemsPerPage, false).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Next(int skipCount = 1) { if (skipCount < 1) return; var mp = await _service.GetOrCreatePlayer(Context); mp.Next(skipCount); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Stop() { var mp = await _service.GetOrCreatePlayer(Context); mp.Stop(); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task AutoDisconnect() { var newVal = _service.ToggleAutoDc(Context.Guild.Id); if(newVal) await ReplyConfirmLocalized("autodc_enable").ConfigureAwait(false); else await ReplyConfirmLocalized("autodc_disable").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Destroy() { await _service.DestroyPlayer(Context.Guild.Id); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Pause() { var mp = await _service.GetOrCreatePlayer(Context); mp.TogglePause(); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Volume(int val) { var mp = await _service.GetOrCreatePlayer(Context); if (val < 0 || val > 100) { await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false); return; } mp.SetVolume(val); await ReplyConfirmLocalized("volume_set", val).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Defvol([Remainder] int val) { if (val < 0 || val > 100) { await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false); return; } using (var uow = _db.UnitOfWork) { uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f; uow.Complete(); } await ReplyConfirmLocalized("defvol_set", val).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [Priority(1)] public async Task SongRemove(int index) { if (index < 1) { await ReplyErrorLocalized("removed_song_error").ConfigureAwait(false); return; } var mp = await _service.GetOrCreatePlayer(Context); try { var song = mp.RemoveAt(index - 1); var embed = new EmbedBuilder() .WithAuthor(eab => eab.WithName(GetText("removed_song") + " #" + (index)).WithMusicIcon()) .WithDescription(song.PrettyName) .WithFooter(ef => ef.WithText(song.PrettyInfo)) .WithErrorColor(); await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false); } catch (ArgumentOutOfRangeException) { await ReplyErrorLocalized("removed_song_error").ConfigureAwait(false); } } public enum All { All } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [Priority(0)] public async Task SongRemove(All all) { var mp = _service.GetPlayerOrDefault(Context.Guild.Id); if (mp == null) return; mp.Stop(true); await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Playlists([Remainder] int num = 1) { if (num <= 0) return; List playlists; using (var uow = _db.UnitOfWork) { playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num); } var embed = new EmbedBuilder() .WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon()) .WithDescription(string.Join("\n", playlists.Select(r => GetText("playlists", r.Id, r.Name, r.Author, r.Songs.Count)))) .WithOkColor(); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task DeletePlaylist([Remainder] int id) { var success = false; try { using (var uow = _db.UnitOfWork) { var pl = uow.MusicPlaylists.Get(id); if (pl != null) { if (_creds.IsOwner(Context.User) || pl.AuthorId == Context.User.Id) { uow.MusicPlaylists.Remove(pl); await uow.CompleteAsync().ConfigureAwait(false); success = true; } } } if (!success) await ReplyErrorLocalized("playlist_delete_fail").ConfigureAwait(false); else await ReplyConfirmLocalized("playlist_deleted").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Save([Remainder] string name) { var mp = await _service.GetOrCreatePlayer(Context); var songs = mp.QueueArray().Songs .Select(s => new PlaylistSong() { Provider = s.Provider, ProviderType = s.ProviderType, Title = s.Title, Query = s.Query, }).ToList(); MusicPlaylist playlist; using (var uow = _db.UnitOfWork) { playlist = new MusicPlaylist { Name = name, Author = Context.User.Username, AuthorId = Context.User.Id, Songs = songs.ToList(), }; uow.MusicPlaylists.Add(playlist); await uow.CompleteAsync().ConfigureAwait(false); } await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithTitle(GetText("playlist_saved")) .AddField(efb => efb.WithName(GetText("name")).WithValue(name)) .AddField(efb => efb.WithName(GetText("id")).WithValue(playlist.Id.ToString()))); } private static readonly ConcurrentHashSet PlaylistLoadBlacklist = new ConcurrentHashSet(); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Load([Remainder] int id) { if (!PlaylistLoadBlacklist.Add(Context.Guild.Id)) return; try { var mp = await _service.GetOrCreatePlayer(Context); MusicPlaylist mpl; using (var uow = _db.UnitOfWork) { mpl = uow.MusicPlaylists.GetWithSongs(id); } if (mpl == null) { await ReplyErrorLocalized("playlist_id_not_found").ConfigureAwait(false); return; } IUserMessage msg = null; try { msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue", Format.Bold(mpl.Songs.Count.ToString()))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } foreach (var item in mpl.Songs) { try { await Task.Yield(); await Task.WhenAll(Task.Delay(1000), InternalQueue(mp, await _service.ResolveSong(item.Query, Context.User.ToString(), item.ProviderType), true)).ConfigureAwait(false); } catch (SongNotFoundException) { } catch { break; } } if (msg != null) await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false); } finally { PlaylistLoadBlacklist.TryRemove(Context.Guild.Id); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Fairplay() { var mp = await _service.GetOrCreatePlayer(Context); var val = mp.FairPlay = !mp.FairPlay; if (val) { await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false); } else { await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task SongAutoDelete() { var mp = await _service.GetOrCreatePlayer(Context); var val = mp.AutoDelete = !mp.AutoDelete; if (val) { await ReplyConfirmLocalized("sad_enabled").ConfigureAwait(false); } else { await ReplyConfirmLocalized("sad_disabled").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task SoundCloudQueue([Remainder] string query) { var mp = await _service.GetOrCreatePlayer(Context); var song = await _service.ResolveSong(query, Context.User.ToString(), MusicType.Soundcloud); await InternalQueue(mp, song, false).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task SoundCloudPl([Remainder] string pl) { pl = pl?.Trim(); if (string.IsNullOrWhiteSpace(pl)) return; var mp = await _service.GetOrCreatePlayer(Context); using (var http = new HttpClient()) { var scvids = JObject.Parse(await http.GetStringAsync($"https://scapi.nadekobot.me/resolve?url={pl}").ConfigureAwait(false))["tracks"].ToObject(); IUserMessage msg = null; try { msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue", Format.Bold(scvids.Length.ToString()))).ConfigureAwait(false); } catch { } foreach (var svideo in scvids) { try { await Task.Yield(); var sinfo = await svideo.GetSongInfo(); sinfo.QueuerName = Context.User.ToString(); await InternalQueue(mp, sinfo, true); } catch (Exception ex) { _log.Warn(ex); break; } } if (msg != null) await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task NowPlaying() { var mp = await _service.GetOrCreatePlayer(Context); var (_, currentSong) = mp.Current; if (currentSong == null) return; try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } var embed = new EmbedBuilder().WithOkColor() .WithAuthor(eab => eab.WithName(GetText("now_playing")).WithMusicIcon()) .WithDescription(currentSong.PrettyName) .WithThumbnailUrl(currentSong.Thumbnail) .WithFooter(ef => ef.WithText(mp.PrettyVolume + " | " + mp.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task ShufflePlaylist() { var mp = await _service.GetOrCreatePlayer(Context); var val = mp.ToggleShuffle(); if(val) await ReplyConfirmLocalized("songs_shuffle_enable").ConfigureAwait(false); else await ReplyConfirmLocalized("songs_shuffle_disable").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Playlist([Remainder] string playlist) { if (string.IsNullOrWhiteSpace(playlist)) return; var mp = await _service.GetOrCreatePlayer(Context); var plId = (await _google.GetPlaylistIdsByKeywordsAsync(playlist).ConfigureAwait(false)).FirstOrDefault(); if (plId == null) { await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); return; } var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); if (!ids.Any()) { await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); return; } var count = ids.Count(); var msg = await Context.Channel.SendMessageAsync("🎵 " + GetText("attempting_to_queue", Format.Bold(count.ToString()))).ConfigureAwait(false); foreach (var song in ids) { try { if (mp.Exited) return; await Task.WhenAll(Task.Delay(150), InternalQueue(mp, await _service.ResolveSong(song, Context.User.ToString(), MusicType.YouTube), true)); } catch (SongNotFoundException) { } catch { break; } } await msg.ModifyAsync(m => m.Content = "✅ " + Format.Bold(GetText("playlist_queue_complete"))).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Radio(string radioLink) { var mp = await _service.GetOrCreatePlayer(Context); var song = await _service.ResolveSong(radioLink, Context.User.ToString(), MusicType.Radio); await InternalQueue(mp, song, false).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [OwnerOnly] public async Task Local([Remainder] string path) { var mp = await _service.GetOrCreatePlayer(Context); var song = await _service.ResolveSong(path, Context.User.ToString(), MusicType.Local); await InternalQueue(mp, song, false).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [OwnerOnly] public async Task LocalPl([Remainder] string dirPath) { if (string.IsNullOrWhiteSpace(dirPath)) return; var mp = await _service.GetOrCreatePlayer(Context); DirectoryInfo dir; try { dir = new DirectoryInfo(dirPath); } catch { return; } var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories) .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System) && x.Extension != ".jpg" && x.Extension != ".png"); foreach (var file in fileEnum) { try { await Task.Yield(); var song = await _service.ResolveSong(file.FullName, Context.User.ToString(), MusicType.Local); await InternalQueue(mp, song, true).ConfigureAwait(false); } catch (QueueFullException) { break; } catch (Exception ex) { _log.Warn(ex); break; } } await ReplyConfirmLocalized("dir_queue_complete").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Move() { var vch = ((IGuildUser)Context.User).VoiceChannel; if (vch == null) return; var mp = _service.GetPlayerOrDefault(Context.Guild.Id); if (mp == null) return; await mp.SetVoiceChannel(vch); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task MoveSong([Remainder] string fromto) { if (string.IsNullOrWhiteSpace(fromto)) return; MusicPlayer mp = _service.GetPlayerOrDefault(Context.Guild.Id); if (mp == null) return; fromto = fromto?.Trim(); var fromtoArr = fromto.Split('>'); SongInfo s; if (fromtoArr.Length != 2 || !int.TryParse(fromtoArr[0], out var n1) || !int.TryParse(fromtoArr[1], out var n2) || n1 < 1 || n2 < 1 || n1 == n2 || (s = mp.MoveSong(--n1, --n2)) == null) { await ReplyConfirmLocalized("invalid_input").ConfigureAwait(false); return; } var embed = new EmbedBuilder() .WithTitle(s.Title.TrimTo(65)) .WithUrl(s.SongUrl) .WithAuthor(eab => eab.WithName(GetText("song_moved")).WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png")) .AddField(fb => fb.WithName(GetText("from_position")).WithValue($"#{n1 + 1}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("to_position")).WithValue($"#{n2 + 1}").WithIsInline(true)) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task SetMaxQueue(uint size = 0) { if (size < 0) return; var mp = await _service.GetOrCreatePlayer(Context); mp.MaxQueueSize = size; if (size == 0) await ReplyConfirmLocalized("max_queue_unlimited").ConfigureAwait(false); else await ReplyConfirmLocalized("max_queue_x", size).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task SetMaxPlaytime(uint seconds) { if (seconds < 15 && seconds != 0) return; var mp = await _service.GetOrCreatePlayer(Context); mp.MaxPlaytimeSeconds = seconds; if (seconds == 0) await ReplyConfirmLocalized("max_playtime_none").ConfigureAwait(false); else await ReplyConfirmLocalized("max_playtime_set", seconds).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task ReptCurSong() { var mp = await _service.GetOrCreatePlayer(Context); var (_, currentSong) = mp.Current; if (currentSong == null) return; var currentValue = mp.ToggleRepeatSong(); if (currentValue) await Context.Channel.EmbedAsync(new EmbedBuilder() .WithOkColor() .WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 " + GetText("repeating_track"))) .WithDescription(currentSong.PrettyName) .WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false); else await Context.Channel.SendConfirmAsync("🔂 " + GetText("repeating_track_stopped")) .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task RepeatPl() { var mp = await _service.GetOrCreatePlayer(Context); var currentValue = mp.ToggleRepeatPlaylist(); if (currentValue) await ReplyConfirmLocalized("rpl_enabled").ConfigureAwait(false); else await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Autoplay() { var mp = await _service.GetOrCreatePlayer(Context); if (!mp.ToggleAutoplay()) await ReplyConfirmLocalized("autoplay_disabled").ConfigureAwait(false); else await ReplyConfirmLocalized("autoplay_enabled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] public async Task SetMusicChannel() { var mp = await _service.GetOrCreatePlayer(Context); mp.OutputTextChannel = (ITextChannel)Context.Channel; await ReplyConfirmLocalized("set_music_channel").ConfigureAwait(false); } } }