using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Google.Apis.YouTube.v3; using Google.Apis.Services; using System.Text.RegularExpressions; using Google.Apis.Urlshortener.v1; using Google.Apis.Urlshortener.v1.Data; using NLog; using Google.Apis.Customsearch.v1; using System.Net.Http; using System.Net; using Newtonsoft.Json.Linq; using NadekoBot.Extensions; namespace NadekoBot.Core.Services.Impl { public class GoogleApiService : IGoogleApiService { const string search_engine_id = "018084019232060951019:hs5piey28-e"; private YouTubeService yt; private UrlshortenerService sh; private CustomsearchService cs; private Logger _log { get; } public GoogleApiService(IBotCredentials creds) { _creds = creds; var bcs = new BaseClientService.Initializer { ApplicationName = "Nadeko Bot", ApiKey = _creds.GoogleApiKey, }; _log = LogManager.GetCurrentClassLogger(); yt = new YouTubeService(bcs); sh = new UrlshortenerService(bcs); cs = new CustomsearchService(bcs); } private static readonly Regex plRegex = new Regex("(?:youtu\\.be\\/|list=)(?[\\da-zA-Z\\-_]*)", RegexOptions.Compiled); public async Task> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) { await Task.Yield(); if (string.IsNullOrWhiteSpace(keywords)) throw new ArgumentNullException(nameof(keywords)); if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count)); var match = plRegex.Match(keywords); if (match.Length > 1) { return new[] { match.Groups["id"].Value.ToString() }; } var query = yt.Search.List("snippet"); query.MaxResults = count; query.Type = "playlist"; query.Q = keywords; return (await query.ExecuteAsync()).Items.Select(i => i.Id.PlaylistId); } //private readonly Regex YtVideoIdRegex = new Regex(@"(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)(?[a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled); private readonly IBotCredentials _creds; public async Task> GetRelatedVideosAsync(string id, int count = 1) { await Task.Yield(); if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count)); var query = yt.Search.List("snippet"); query.MaxResults = count; query.RelatedToVideoId = id; query.Type = "video"; return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId); } public async Task> GetVideoLinksByKeywordAsync(string keywords, int count = 1) { await Task.Yield(); 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 => "http://www.youtube.com/watch?v=" + i.Id.VideoId); } public async Task> GetVideoInfosByKeywordAsync(string keywords, int count = 1) { await Task.Yield(); 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) { await Task.Yield(); if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); if (string.IsNullOrWhiteSpace(_creds.GoogleApiKey)) return url; try { var response = await sh.Url.Insert(new Url { LongUrl = url }).ExecuteAsync(); return response.Id; } catch (Exception ex) { _log.Warn(ex); return url; } } public async Task> GetPlaylistTracksAsync(string playlistId, int count = 50) { await Task.Yield(); 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 = toGet; 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; } public async Task> GetVideoDurationsAsync(IEnumerable videoIds) { await Task.Yield(); var videoIdsList = videoIds as List ?? videoIds.ToList(); Dictionary toReturn = new Dictionary(); if (!videoIdsList.Any()) return toReturn; var toGet = 0; var remaining = videoIdsList.Count; do { toGet = remaining > 50 ? 50 : remaining; remaining -= toGet; var q = yt.Videos.List("contentDetails"); q.Id = string.Join(",", videoIdsList.Take(toGet)); videoIdsList = videoIdsList.Skip(toGet).ToList(); var items = (await q.ExecuteAsync().ConfigureAwait(false)).Items; foreach (var i in items) { toReturn.Add(i.Id, System.Xml.XmlConvert.ToTimeSpan(i.ContentDetails.Duration)); } } while (remaining > 0); return toReturn; } public async Task GetImageAsync(string query, int start = 1) { await Task.Yield(); if (string.IsNullOrWhiteSpace(query)) throw new ArgumentNullException(nameof(query)); var req = cs.Cse.List(query); req.Cx = search_engine_id; req.Num = 1; req.Fields = "items(image(contextLink,thumbnailLink),link)"; req.SearchType = CseResource.ListRequest.SearchTypeEnum.Image; req.Start = start; var search = await req.ExecuteAsync().ConfigureAwait(false); return new ImageResult(search.Items[0].Image, search.Items[0].Link); } public IEnumerable Languages => _languageDictionary.Keys.OrderBy(x => x); private readonly Dictionary _languageDictionary = new Dictionary() { { "afrikaans", "af"}, { "albanian", "sq"}, { "arabic", "ar"}, { "armenian", "hy"}, { "azerbaijani", "az"}, { "basque", "eu"}, { "belarusian", "be"}, { "bengali", "bn"}, { "bulgarian", "bg"}, { "catalan", "ca"}, { "chinese-traditional", "zh-TW"}, { "chinese-simplified", "zh-CN"}, { "chinese", "zh-CN"}, { "croatian", "hr"}, { "czech", "cs"}, { "danish", "da"}, { "dutch", "nl"}, { "english", "en"}, { "esperanto", "eo"}, { "estonian", "et"}, { "filipino", "tl"}, { "finnish", "fi"}, { "french", "fr"}, { "galician", "gl"}, { "german", "de"}, { "georgian", "ka"}, { "greek", "el"}, { "haitian Creole", "ht"}, { "hebrew", "iw"}, { "hindi", "hi"}, { "hungarian", "hu"}, { "icelandic", "is"}, { "indonesian", "id"}, { "irish", "ga"}, { "italian", "it"}, { "japanese", "ja"}, { "korean", "ko"}, { "lao", "lo"}, { "latin", "la"}, { "latvian", "lv"}, { "lithuanian", "lt"}, { "macedonian", "mk"}, { "malay", "ms"}, { "maltese", "mt"}, { "norwegian", "no"}, { "persian", "fa"}, { "polish", "pl"}, { "portuguese", "pt"}, { "romanian", "ro"}, { "russian", "ru"}, { "serbian", "sr"}, { "slovak", "sk"}, { "slovenian", "sl"}, { "spanish", "es"}, { "swahili", "sw"}, { "swedish", "sv"}, { "tamil", "ta"}, { "telugu", "te"}, { "thai", "th"}, { "turkish", "tr"}, { "ukrainian", "uk"}, { "urdu", "ur"}, { "vietnamese", "vi"}, { "welsh", "cy"}, { "yiddish", "yi"}, { "af", "af"}, { "sq", "sq"}, { "ar", "ar"}, { "hy", "hy"}, { "az", "az"}, { "eu", "eu"}, { "be", "be"}, { "bn", "bn"}, { "bg", "bg"}, { "ca", "ca"}, { "zh-tw", "zh-TW"}, { "zh-cn", "zh-CN"}, { "hr", "hr"}, { "cs", "cs"}, { "da", "da"}, { "nl", "nl"}, { "en", "en"}, { "eo", "eo"}, { "et", "et"}, { "tl", "tl"}, { "fi", "fi"}, { "fr", "fr"}, { "gl", "gl"}, { "de", "de"}, { "ka", "ka"}, { "el", "el"}, { "ht", "ht"}, { "iw", "iw"}, { "hi", "hi"}, { "hu", "hu"}, { "is", "is"}, { "id", "id"}, { "ga", "ga"}, { "it", "it"}, { "ja", "ja"}, { "ko", "ko"}, { "lo", "lo"}, { "la", "la"}, { "lv", "lv"}, { "lt", "lt"}, { "mk", "mk"}, { "ms", "ms"}, { "mt", "mt"}, { "no", "no"}, { "fa", "fa"}, { "pl", "pl"}, { "pt", "pt"}, { "ro", "ro"}, { "ru", "ru"}, { "sr", "sr"}, { "sk", "sk"}, { "sl", "sl"}, { "es", "es"}, { "sw", "sw"}, { "sv", "sv"}, { "ta", "ta"}, { "te", "te"}, { "th", "th"}, { "tr", "tr"}, { "uk", "uk"}, { "ur", "ur"}, { "vi", "vi"}, { "cy", "cy"}, { "yi", "yi"}, }; public async Task Translate(string sourceText, string sourceLanguage, string targetLanguage) { await Task.Yield(); string text; if (!_languageDictionary.ContainsKey(sourceLanguage) || !_languageDictionary.ContainsKey(targetLanguage)) throw new ArgumentException(); var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", ConvertToLanguageCode(sourceLanguage), ConvertToLanguageCode(targetLanguage), WebUtility.UrlEncode(sourceText)); using (var http = new HttpClient()) { http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); text = await http.GetStringAsync(url).ConfigureAwait(false); } return (string.Concat(JArray.Parse(text)[0].Select(x => x[0]))); } private string ConvertToLanguageCode(string language) { _languageDictionary.TryGetValue(language, out var mode); return mode; } } }