From f71801cbe656def7bd0f0b4d0f7062ed8005f373 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 13 Jun 2017 14:21:24 +0200 Subject: [PATCH] .qs command added --- src/NadekoBot/Modules/Music/Music.cs | 38 ++++++++++++++++++ src/NadekoBot/Modules/NadekoModule.cs | 40 +++++++++++++++++++ src/NadekoBot/Modules/Searches/Searches.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 9 +++++ src/NadekoBot/Services/IGoogleApiService.cs | 3 +- .../Services/Impl/GoogleApiService.cs | 18 ++++++++- src/NadekoBot/Services/Music/MusicService.cs | 2 +- .../Services/Utility/PatreonRewardsService.cs | 2 +- 8 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 89ddfde5..2210dd9b 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -182,6 +182,44 @@ namespace NadekoBot.Modules.Music } } + [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 SoundCloudQueue([Remainder] string query) diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 248e8441..1282fd5a 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -5,6 +5,8 @@ using NadekoBot.Services; using NLog; using System.Globalization; using System.Threading.Tasks; +using System; +using Discord.WebSocket; namespace NadekoBot.Modules { @@ -84,6 +86,44 @@ namespace NadekoBot.Modules var text = GetText(textKey, replacements); return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text); } + + // todo maybe make this generic and use + // TypeConverter typeConverter = TypeDescriptor.GetConverter(propType); + public async Task GetUserInputAsync(ulong userId, ulong channelId) + { + var userInputTask = new TaskCompletionSource(); + var dsc = (DiscordShardedClient)Context.Client; + try + { + dsc.MessageReceived += MessageReceived; + + if ((await Task.WhenAny(userInputTask.Task, Task.Delay(10000))) != userInputTask.Task) + { + return null; + } + + return await userInputTask.Task; + } + finally + { + dsc.MessageReceived -= MessageReceived; + } + + Task MessageReceived(SocketMessage arg) + { + if (!(arg is SocketUserMessage userMsg) || + !(userMsg.Channel is ITextChannel chan) || + userMsg.Author.Id != userId || + userMsg.Channel.Id != channelId) + { + return Task.CompletedTask; + } + + userInputTask.SetResult(arg.Content); + userMsg.DeleteAfter(1); + return Task.CompletedTask; + } + } } public abstract class NadekoSubmodule : NadekoTopLevelModule diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 0780d365..6f05d846 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -89,7 +89,7 @@ namespace NadekoBot.Modules.Searches public async Task Youtube([Remainder] string query = null) { if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return; - var result = (await _google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault(); + var result = (await _google.GetVideoLinksByKeywordAsync(query, 1)).FirstOrDefault(); if (string.IsNullOrWhiteSpace(result)) { await ReplyErrorLocalized("no_results").ConfigureAwait(false); diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index ae6ae74a..b3705bd0 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1512,6 +1512,15 @@ `{0}q Dream Of Venice` + + queuesearch qs yqs + + + Search for top 5 youtube song result using keywords, and type the index of the song to play that song. Bot will join your voice channel. **You must be in a voice channel**. + + + `{0}qs Dream Of Venice` + soundcloudqueue sq diff --git a/src/NadekoBot/Services/IGoogleApiService.cs b/src/NadekoBot/Services/IGoogleApiService.cs index 2ecf1405..f2e26f11 100644 --- a/src/NadekoBot/Services/IGoogleApiService.cs +++ b/src/NadekoBot/Services/IGoogleApiService.cs @@ -9,7 +9,8 @@ namespace NadekoBot.Services { IEnumerable Languages { get; } - Task> GetVideosByKeywordsAsync(string keywords, int count = 1); + Task> GetVideoLinksByKeywordAsync(string keywords, int count = 1); + Task> GetVideoInfosByKeywordAsync(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); diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index edc0df7a..d7d93f4c 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -12,6 +12,7 @@ using Google.Apis.Customsearch.v1; using System.Net.Http; using System.Net; using Newtonsoft.Json.Linq; +using NadekoBot.Extensions; namespace NadekoBot.Services.Impl { @@ -86,7 +87,7 @@ namespace NadekoBot.Services.Impl return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId); } - public async Task> GetVideosByKeywordsAsync(string keywords, int count = 1) + public async Task> GetVideoLinksByKeywordAsync(string keywords, int count = 1) { if (string.IsNullOrWhiteSpace(keywords)) throw new ArgumentNullException(nameof(keywords)); @@ -111,6 +112,21 @@ namespace NadekoBot.Services.Impl return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId); } + public async Task> GetVideoInfosByKeywordAsync(string keywords, int count = 1) + { + if (string.IsNullOrWhiteSpace(keywords)) + throw new ArgumentNullException(nameof(keywords)); + + if (count <= 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + var query = yt.Search.List("snippet"); + query.MaxResults = count; + query.Q = keywords; + query.Type = "video"; + return (await query.ExecuteAsync()).Items.Select(i => (i.Snippet.Title.TrimTo(50), i.Id.VideoId, "http://www.youtube.com/watch?v=" + i.Id.VideoId)); + } + public async Task ShortenUrl(string url) { if (string.IsNullOrWhiteSpace(url)) diff --git a/src/NadekoBot/Services/Music/MusicService.cs b/src/NadekoBot/Services/Music/MusicService.cs index 6bb411bf..06748412 100644 --- a/src/NadekoBot/Services/Music/MusicService.cs +++ b/src/NadekoBot/Services/Music/MusicService.cs @@ -304,7 +304,7 @@ namespace NadekoBot.Services.Music { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) }; } - var link = (await _google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault(); + var link = (await _google.GetVideoLinksByKeywordAsync(query).ConfigureAwait(false)).FirstOrDefault(); if (string.IsNullOrWhiteSpace(link)) throw new OperationCanceledException("Not a valid youtube query."); var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); diff --git a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs index bb420964..e8202d84 100644 --- a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs +++ b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Services.Utility private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1); private readonly Logger _log; - public readonly TimeSpan Interval = TimeSpan.FromHours(1); + public readonly TimeSpan Interval = TimeSpan.FromMinutes(15); private IBotCredentials _creds; private readonly DbService _db; private readonly CurrencyService _currency;