From 9a2ffc1d7c507f73437801c958fd2271c7873313 Mon Sep 17 00:00:00 2001 From: Yeechan Lu Date: Thu, 27 Mar 2014 16:56:50 +0800 Subject: [PATCH] Add Google suggestions for web searches Fix issues with push results --- Wox.Infrastructure/HttpRequest.cs | 137 ++++++++++++++++++ Wox.Infrastructure/Wox.Infrastructure.csproj | 1 + Wox.Plugin.System/CMD/CMD.cs | 4 +- Wox.Plugin.System/SuggestionSources/Google.cs | 42 ++++++ .../SuggestionSources/ISuggestionSource.cs | 18 +++ Wox.Plugin.System/WebSearchPlugin.cs | 49 +++++-- Wox.Plugin.System/Wox.Plugin.System.csproj | 2 + Wox.Plugin/PluginInitContext.cs | 2 +- Wox/Commands/PluginCommand.cs | 6 +- Wox/Commands/SystemCommand.cs | 6 +- 10 files changed, 249 insertions(+), 18 deletions(-) create mode 100644 Wox.Infrastructure/HttpRequest.cs create mode 100644 Wox.Plugin.System/SuggestionSources/Google.cs create mode 100644 Wox.Plugin.System/SuggestionSources/ISuggestionSource.cs diff --git a/Wox.Infrastructure/HttpRequest.cs b/Wox.Infrastructure/HttpRequest.cs new file mode 100644 index 0000000000..1e53db6d0b --- /dev/null +++ b/Wox.Infrastructure/HttpRequest.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +//From:http://blog.csdn.net/zhoufoxcn/article/details/6404236 +namespace Wox.Infrastructure +{ + /// + /// 有关HTTP请求的辅助类 + /// + public class HttpRequest + { + private static readonly string DefaultUserAgent = "Wox/" + Assembly.GetEntryAssembly().GetName().Version.ToString() + " (+https://github.com/qianlifeng/Wox)"; + + /// + /// 创建GET方式的HTTP请求 + /// + /// 请求的URL + /// 请求的超时时间 + /// 请求的客户端浏览器信息,可以为空 + /// 随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空 + /// + public static HttpWebResponse CreateGetHttpResponse(string url, int? timeout, string userAgent, CookieCollection cookies) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException("url"); + } + HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; + request.Method = "GET"; + request.UserAgent = DefaultUserAgent; + if (!string.IsNullOrEmpty(userAgent)) + { + request.UserAgent = userAgent; + } + if (timeout.HasValue) + { + request.Timeout = timeout.Value; + } + if (cookies != null) + { + request.CookieContainer = new CookieContainer(); + request.CookieContainer.Add(cookies); + } + return request.GetResponse() as HttpWebResponse; + } + /// + /// 创建POST方式的HTTP请求 + /// + /// 请求的URL + /// 随同请求POST的参数名称及参数值字典 + /// 请求的超时时间 + /// 请求的客户端浏览器信息,可以为空 + /// 发送HTTP请求时所用的编码 + /// 随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空 + /// + public static HttpWebResponse CreatePostHttpResponse(string url, IDictionary parameters, int? timeout, string userAgent, Encoding requestEncoding, CookieCollection cookies) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException("url"); + } + if (requestEncoding == null) + { + throw new ArgumentNullException("requestEncoding"); + } + HttpWebRequest request = null; + //如果是发送HTTPS请求 + if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) + { + ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); + request = WebRequest.Create(url) as HttpWebRequest; + request.ProtocolVersion = HttpVersion.Version10; + } + else + { + request = WebRequest.Create(url) as HttpWebRequest; + } + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + if (!string.IsNullOrEmpty(userAgent)) + { + request.UserAgent = userAgent; + } + else + { + request.UserAgent = DefaultUserAgent; + } + + if (timeout.HasValue) + { + request.Timeout = timeout.Value; + } + if (cookies != null) + { + request.CookieContainer = new CookieContainer(); + request.CookieContainer.Add(cookies); + } + //如果需要POST数据 + if (!(parameters == null || parameters.Count == 0)) + { + StringBuilder buffer = new StringBuilder(); + int i = 0; + foreach (string key in parameters.Keys) + { + if (i > 0) + { + buffer.AppendFormat("&{0}={1}", key, parameters[key]); + } + else + { + buffer.AppendFormat("{0}={1}", key, parameters[key]); + } + i++; + } + byte[] data = requestEncoding.GetBytes(buffer.ToString()); + using (Stream stream = request.GetRequestStream()) + { + stream.Write(data, 0, data.Length); + } + } + return request.GetResponse() as HttpWebResponse; + } + + private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) + { + return true; //总是接受 + } + } +} diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index e3c8ae0600..c1dc32ec1c 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -49,6 +49,7 @@ + diff --git a/Wox.Plugin.System/CMD/CMD.cs b/Wox.Plugin.System/CMD/CMD.cs index f2d5b1dd35..279e012881 100644 --- a/Wox.Plugin.System/CMD/CMD.cs +++ b/Wox.Plugin.System/CMD/CMD.cs @@ -57,7 +57,7 @@ namespace Wox.Plugin.System.CMD } catch (Exception) { } - context.PushResults(new List() { result }); + context.PushResults(query, new List() { result }); IEnumerable history = CMDStorage.Instance.CMDHistory.Where(o => o.Key.Contains(cmd)) .OrderByDescending(o => o.Value) @@ -92,7 +92,7 @@ namespace Wox.Plugin.System.CMD return ret; }).Where(o => o != null).Take(4); - context.PushResults(history.ToList()); + context.PushResults(query, history.ToList()); try { diff --git a/Wox.Plugin.System/SuggestionSources/Google.cs b/Wox.Plugin.System/SuggestionSources/Google.cs new file mode 100644 index 0000000000..4ae376a8b4 --- /dev/null +++ b/Wox.Plugin.System/SuggestionSources/Google.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Wox.Infrastructure; +using YAMP.Numerics; + +namespace Wox.Plugin.System.SuggestionSources +{ + public class Google : AbstractSuggestionSource + { + public override List GetSuggestions(string query) + { + var response = + HttpRequest.CreateGetHttpResponse("https://www.google.com/complete/search?output=chrome&q=" + Uri.EscapeUriString(query), null, + null, null); + var stream = response.GetResponseStream(); + + if (stream != null) + { + var body = new StreamReader(stream).ReadToEnd(); + var json = JsonConvert.DeserializeObject(body) as JContainer; + if (json != null) + { + var results = json[1] as JContainer; + if (results != null) + { + var j = results.OfType().Select(o => o.Value); + return results.OfType().Select(o => o.Value).OfType().ToList(); + } + } + } + + return null; + } + } +} diff --git a/Wox.Plugin.System/SuggestionSources/ISuggestionSource.cs b/Wox.Plugin.System/SuggestionSources/ISuggestionSource.cs new file mode 100644 index 0000000000..4683f3cab2 --- /dev/null +++ b/Wox.Plugin.System/SuggestionSources/ISuggestionSource.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Wox.Plugin.System.SuggestionSources +{ + public interface ISuggestionSource + { + List GetSuggestions(string query); + } + + public abstract class AbstractSuggestionSource : ISuggestionSource + { + public abstract List GetSuggestions(string query); + } +} diff --git a/Wox.Plugin.System/WebSearchPlugin.cs b/Wox.Plugin.System/WebSearchPlugin.cs index ca35b46f6a..07f62f2057 100644 --- a/Wox.Plugin.System/WebSearchPlugin.cs +++ b/Wox.Plugin.System/WebSearchPlugin.cs @@ -7,11 +7,14 @@ using Newtonsoft.Json; using Wox.Infrastructure; using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage.UserSettings; +using Wox.Plugin.System.SuggestionSources; namespace Wox.Plugin.System { public class WebSearchPlugin : BaseSystemPlugin { + private PluginInitContext context; + protected override List QueryInternal(Query query) { List results = new List(); @@ -23,22 +26,49 @@ namespace Wox.Plugin.System if (webSearch != null) { string keyword = query.ActionParameters.Count > 0 ? query.GetAllRemainingParameter() : ""; - string title = string.Format("Search {0} for \"{1}\"", webSearch.Title, keyword); + string title = keyword; + string subtitle = "Search " + webSearch.Title; if (string.IsNullOrEmpty(keyword)) { - title = "Search " + webSearch.Title; + title = subtitle; + subtitle = null; } - results.Add(new Result() + context.PushResults(query, new List() { - Title = title, - Score = 6, - IcoPath = webSearch.IconPath, - Action = (c) => + new Result() { - Process.Start(webSearch.Url.Replace("{q}", keyword)); - return true; + Title = title, + SubTitle = subtitle, + Score = 6, + IcoPath = webSearch.IconPath, + Action = (c) => + { + Process.Start(webSearch.Url.Replace("{q}", keyword)); + return true; + } } }); + + if (!string.IsNullOrEmpty(keyword)) + { + ISuggestionSource sugg = new Google(); + var result = sugg.GetSuggestions(keyword); + if (result != null) + { + context.PushResults(query, result.Select(o => new Result() + { + Title = o, + SubTitle = subtitle, + Score = 5, + IcoPath = webSearch.IconPath, + Action = (c) => + { + Process.Start(webSearch.Url.Replace("{q}", o)); + return true; + } + }).ToList()); + } + } } return results; @@ -46,6 +76,7 @@ namespace Wox.Plugin.System protected override void InitInternal(PluginInitContext context) { + this.context = context; } } } diff --git a/Wox.Plugin.System/Wox.Plugin.System.csproj b/Wox.Plugin.System/Wox.Plugin.System.csproj index d44cae02a4..685d2ca9e0 100644 --- a/Wox.Plugin.System/Wox.Plugin.System.csproj +++ b/Wox.Plugin.System/Wox.Plugin.System.csproj @@ -70,6 +70,8 @@ + + diff --git a/Wox.Plugin/PluginInitContext.cs b/Wox.Plugin/PluginInitContext.cs index 7169132dee..68be7a58f1 100644 --- a/Wox.Plugin/PluginInitContext.cs +++ b/Wox.Plugin/PluginInitContext.cs @@ -32,6 +32,6 @@ namespace Wox.Plugin public Func ShellRun { get; set; } - public Action> PushResults { get; set; } + public Action> PushResults { get; set; } } } diff --git a/Wox/Commands/PluginCommand.cs b/Wox/Commands/PluginCommand.cs index 8a29e4a7e8..5770188ad4 100644 --- a/Wox/Commands/PluginCommand.cs +++ b/Wox/Commands/PluginCommand.cs @@ -27,17 +27,17 @@ namespace Wox.Commands { try { - thirdPlugin.InitContext.PushResults = r => + thirdPlugin.InitContext.PushResults = (qu, r) => { r.ForEach(o => { o.PluginDirectory = thirdPlugin.Metadata.PluginDirecotry; - o.OriginQuery = q; + o.OriginQuery = qu; }); UpdateResultView(r); }; List results = thirdPlugin.Plugin.Query(q) ?? new List(); - thirdPlugin.InitContext.PushResults(results); + thirdPlugin.InitContext.PushResults(q, results); } catch (Exception queryException) { diff --git a/Wox/Commands/SystemCommand.cs b/Wox/Commands/SystemCommand.cs index 0d90c0a90c..30816701bf 100644 --- a/Wox/Commands/SystemCommand.cs +++ b/Wox/Commands/SystemCommand.cs @@ -17,20 +17,20 @@ namespace Wox.Commands PluginPair pair1 = pair; ThreadPool.QueueUserWorkItem(state => { - pair1.InitContext.PushResults = r => + pair1.InitContext.PushResults = (q, r) => { if (r == null || r.Count == 0) return; foreach (Result result in r) { result.PluginDirectory = pair1.Metadata.PluginDirecotry; - result.OriginQuery = query; + result.OriginQuery = q; result.AutoAjustScore = true; } UpdateResultView(r); }; List results = pair1.Plugin.Query(query); - pair1.InitContext.PushResults(results); + pair1.InitContext.PushResults(query, results); }); } }