diff --git a/GoogleAlertAPI.cs b/GoogleAlertAPI.cs index 0523463..45c6bc8 100644 --- a/GoogleAlertAPI.cs +++ b/GoogleAlertAPI.cs @@ -15,62 +15,48 @@ namespace Google { public partial class GoogleAlertAPI { - private string email = string.Empty; - private string password = string.Empty; - private string auth_id = string.Empty; - private string auth_token = string.Empty; - private Hashtable AlertTable = new Hashtable(); - private HttpClient client; - private HttpClientHandler httpClientHandler; - private CookieContainer cookieContainer = new CookieContainer(); + private string _auth_id = string.Empty; + private string _auth_token = string.Empty; + private readonly Hashtable _alertTable = new Hashtable(); + private readonly HttpClient _client; + private readonly HttpClientHandler _httpClientHandler; + private readonly CookieContainer _cookieContainer = new CookieContainer(); - private string SCRIPT_JSON_TAG = "window.STATE = "; - private string GOOGLE_LOGIN_URL = "https://accounts.google.com/ServiceLoginAuth"; + private const string SCRIPT_JSON_TAG = "window.STATE"; + private const string SCRIPT_JSON_TAG_REGEX = SCRIPT_JSON_TAG + "=(.*]]])"; - public GoogleAlertAPI(string email, string password) + public GoogleAlertAPI(string SID, string HSID, string SSID) { - this.email = email; - this.password = password; - httpClientHandler = new HttpClientHandler() { - CookieContainer = cookieContainer, + _httpClientHandler = new HttpClientHandler() { + CookieContainer = _cookieContainer, UseCookies = true, UseDefaultCredentials = false }; - client = new HttpClient(httpClientHandler); - client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/xhtml+xml,application/xml"); + _client = new HttpClient(_httpClientHandler); + _client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/xhtml+xml,application/xml"); //client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate"); - client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.8"); - client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0"); - client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Charset", "ISO-8859-1"); + _client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.8"); + _client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0"); + _client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Charset", "ISO-8859-1"); - TryLogin(); + _cookieContainer.Add(new Cookie("SID", SID, "/", ".google.com")); + _cookieContainer.Add(new Cookie("HSID", HSID, "/", ".google.com")); + _cookieContainer.Add(new Cookie("SSID", SSID, "/", ".google.com")); + + Login(); } - private void TryLogin() + private void Login() { - Task loginTask = getUrlString(GOOGLE_LOGIN_URL); - Task.WaitAll(loginTask); - string body = loginTask.Result; - List> postData = getLoginFormParams(body, email, password); - - loginTask = postUrlString(GOOGLE_LOGIN_URL, postData); - Task.WaitAll(loginTask); - body = loginTask.Result; - - if (hasElement(body, "//span[@id[starts-with(.,'errormsg')]]")) - { - throw new Exception("Login error."); - } - - Task alertTask = postUrlString("https://www.google.com/alerts?hl=en&", null); + Task alertTask = PostUrlString("https://www.google.com/alerts?hl=en&", null); Task.WaitAll(alertTask); - body = alertTask.Result; - List script_list = getElements(body,"//script"); + var body = alertTask.Result; + List script_list = GetElements(body,"//script"); foreach (string script in script_list) { - if (script.StartsWith(SCRIPT_JSON_TAG)) + if (script.Contains(SCRIPT_JSON_TAG)) { - string json_str = script.Substring(script.IndexOf(SCRIPT_JSON_TAG) + SCRIPT_JSON_TAG.Length, script.Length - SCRIPT_JSON_TAG.Length -1); + string json_str = Regex.Match(script, SCRIPT_JSON_TAG_REGEX).Groups[1].Value; JArray json = JArray.Parse(json_str); if (json[1] == null) { @@ -81,73 +67,66 @@ private void TryLogin() { foreach (var alert_item in alert_item_token[1]) { - AlertTable[alert_item[1].ToString()] = alert_item[2][6][0][11].ToString(); + _alertTable[alert_item[1].ToString()] = alert_item[1][5][0][10].ToString(); } } - auth_id = json[3].ToString(); - auth_token = json[2][6][0][14].ToString(); + _auth_id = json[2].ToString(); + _auth_token = json[1][5][0][13].ToString(); } } - if (string.IsNullOrEmpty(auth_id) || string.IsNullOrEmpty(auth_token)) + if (string.IsNullOrEmpty(_auth_id) || string.IsNullOrEmpty(_auth_token)) { throw new Exception("Login error."); } } - public List getAlerts() + public List GetAlerts() { List alert_list = new List(); - if (!string.IsNullOrEmpty(auth_id)) + if (!string.IsNullOrEmpty(_auth_id)) { - Task alertTask = postUrlString("https://www.google.com/alerts?hl=en&", null); + Task alertTask = PostUrlString("https://www.google.com/alerts?hl=en&", null); Task.WaitAll(alertTask); string body = alertTask.Result; - List script_list = getElements(body, "//script"); + List script_list = GetElements(body, "//script"); foreach (string script in script_list) { - if (script.StartsWith(SCRIPT_JSON_TAG)) + if (script.Contains(SCRIPT_JSON_TAG)) { - string json_str = script.Substring(script.IndexOf(SCRIPT_JSON_TAG) + SCRIPT_JSON_TAG.Length, script.Length - SCRIPT_JSON_TAG.Length - 1); + string json_str = Regex.Match(script, SCRIPT_JSON_TAG_REGEX).Groups[1].Value; JArray json = JArray.Parse(json_str); JToken alert_item_token = json[1]; - if (IsNullOrEmpty(alert_item_token) || json[1] == null) - { - break; - } - if (IsNullOrEmpty(alert_item_token) || json[1][1] == null || json[1][1].Count() <= 0) - { + + if (IsNullOrEmpty(alert_item_token) || json[1] == null || json[1][1] == null || json[1][1].Count() <= 0) break; - } + foreach (var alert_item in json[1][1]) { - Alert alert = new Alert(); - alert.feedUrl = "https://www.google.com/alerts/feeds/" + alert_item[3]+ "/" +alert_item[2][6][0][11]; - alert.id = alert_item[1].ToString(); - alert.query = alert_item[2][3][1].ToString(); - Enum.TryParse(alert_item[2][5].ToString(), out alert.howMany); - Enum.TryParse(alert_item[2][6][0][4].ToString(), out alert.howOften); + Alert alert = new Alert + { + feedUrl = $"https://www.google.com/alerts/feeds/{alert_item[3]}/{alert_item[2][6][0][11]}", + id = alert_item[1].ToString(), + query = alert_item[2][3][1].ToString() + }; + + Enum.TryParse(alert_item[2][5].ToString(), out alert.howMany); + Enum.TryParse(alert_item[2][6][0][4].ToString(), out alert.howOften); alert.language = GetEnumValueFromDescription(alert_item[2][3][3][1].ToString()); alert.region = GetEnumValueFromDescription(alert_item[2][3][3][2].ToString()); if (alert_item[2][4] != null) - { for (int i = 0; i < alert_item[2][4].Count(); i++) - { alert.sources.Add(GetEnumValueFromDescription(alert_item[2][4][i].ToString())); - } - } + if (alert_item[2][6][0][1].ToString() == "1") - { alert.deliveryTo = DeliveryTo.Email; - } else - { alert.deliveryTo = DeliveryTo.Feed; - } + alert_list.Add(alert); } - auth_id = json[3].ToString(); + _auth_id = json[3].ToString(); break; } } @@ -155,14 +134,14 @@ public List getAlerts() return alert_list; } - public string createAlert(Alert alert) + public string CreateAlert(Alert alert) { string id = string.Empty; - if (!string.IsNullOrEmpty(auth_id) && alert != null) + if (!string.IsNullOrEmpty(_auth_id) && alert != null) { List> postData = new List>(); - postData.Add(new KeyValuePair("params",createPostParameter(alert,false))); - Task createTask = postUrlString("https://www.google.com/alerts/create?hl=en&", postData); + postData.Add(new KeyValuePair("params",CreatePostParameter(alert,false))); + Task createTask = PostUrlString("https://www.google.com/alerts/create?hl=en&", postData); Task.WaitAll(createTask); string body = createTask.Result; JArray json = JArray.Parse(body); @@ -173,16 +152,15 @@ public string createAlert(Alert alert) else { id = json[4][0][1].ToString(); - AlertTable[id] = json[4][0][3][6][0][11].ToString(); + _alertTable[id] = json[4][0][3][6][0][11].ToString(); if (alert.deliveryTo == DeliveryTo.Feed) { var link_text = json[4][0][2].ToString(); Match match = Regex.Match(link_text, @"/alerts/feeds/[\w^/]+/[\w^/]+",RegexOptions.Compiled); + if (match.Success) - { alert.feedUrl = "https://www.google.com" + match.Value; - } } } @@ -190,37 +168,39 @@ public string createAlert(Alert alert) return id; } - public string modifyAlert(Alert alert) + public bool DeleteAlert(string alert_id) + { + if (!string.IsNullOrEmpty(_auth_id) && !string.IsNullOrEmpty(alert_id)) + { + List> postData = new List>(); + postData.Add(new KeyValuePair("params", "[null,\"" + alert_id + "\"]")); + Task deleteTask = PostUrlString("https://www.google.com/alerts/delete?hl=en&", postData); + Task.WaitAll(deleteTask); + return true; + } + return false; + } + + public string ModifyAlert(Alert alert) { string id = string.Empty; - if (!string.IsNullOrEmpty(auth_id) && (alert != null) && (!string.IsNullOrEmpty(alert.id))) + if (!string.IsNullOrEmpty(_auth_id) && (alert != null) && (!string.IsNullOrEmpty(alert.id))) { List> postData = new List>(); - postData.Add(new KeyValuePair("params", createPostParameter(alert, true))); - Task createTask = postUrlString("https://www.google.com/alerts/modify?hl=en&", postData); + postData.Add(new KeyValuePair("params", CreatePostParameter(alert, true))); + Task createTask = PostUrlString("https://www.google.com/alerts/modify?hl=en&", postData); Task.WaitAll(createTask); string body = createTask.Result; JArray json = JArray.Parse(body); id = json[4][0][1].ToString(); - AlertTable[id] = json[4][0][3][6][0][11].ToString(); + _alertTable[id] = json[4][0][3][6][0][11].ToString(); } return id; } - public bool deleteAlert(string alert_id) - { - if (!string.IsNullOrEmpty(auth_id) && !string.IsNullOrEmpty(alert_id)) - { - List> postData = new List>(); - postData.Add(new KeyValuePair("params", "[null,\"" + alert_id + "\"]")); - Task deleteTask = postUrlString("https://www.google.com/alerts/delete?hl=en&", postData); - Task.WaitAll(deleteTask); - return true; - } - return false; - } + #region Private Methods - private string createPostParameter(Alert alert, bool isEdit) + private string CreatePostParameter(Alert alert, bool isEdit, string email = "") { string domainExt = "com"; string feedKey = "1"; @@ -232,27 +212,23 @@ private string createPostParameter(Alert alert, bool isEdit) _howOften = HowOften.AsItHappens; _email = string.Empty; } + if (!string.IsNullOrEmpty(_email)) - { domainExt = _email.Substring(_email.LastIndexOf(".") + 1); - } + string _sources = string.Empty; foreach (var source in alert.sources) { if (string.IsNullOrEmpty(_sources)) - { _sources += GetDescriptionFromEnumValue(source); - } else - { _sources += "," + GetDescriptionFromEnumValue(source); - } } _sources = "[" + _sources +"]"; + if (_sources == "[]") - { _sources = "null"; - } + string howOften = string.Empty; switch(_howOften){ case HowOften.AsItHappens: @@ -265,11 +241,11 @@ private string createPostParameter(Alert alert, bool isEdit) howOften = "[null,null,3,3]," + (int)_howOften; break; } + string update = string.Empty; if (isEdit) - { update = "\"" + alert.id + "\","; - } + string flagLanguage = "1"; Language _language = alert.language; if (alert.language == Language.AnyLanguage) @@ -277,6 +253,7 @@ private string createPostParameter(Alert alert, bool isEdit) _language = Language.English; flagLanguage = "0"; } + string flagRegion = "1"; Region _region = alert.region; if (alert.region == Region.AnyRegion) @@ -284,88 +261,40 @@ private string createPostParameter(Alert alert, bool isEdit) _region = Region.UnitedStates; flagRegion = "0"; } - string query = alert.query; - return "[null," + update + "[null,null,null,[null,\"" + query + "\",\"" + domainExt + "\",[null,\"" + GetDescriptionFromEnumValue(_language) + "\",\"" + GetDescriptionFromEnumValue(_region) + "\"],null,null,null," + flagRegion + "," + flagLanguage + "]," + _sources + "," + (int)alert.howMany + ",[[null," + feedKey + ",\"" + _email + "\"," + howOften + ",\"en-US\"," + (isEdit ? "1" : "null") + ",null,null,null,null,\"" + (isEdit ? (string)this.AlertTable[alert.id] : "0") + "\",null,null,\"" + this.auth_token + "\"]]]]"; - } + string query = alert.query.Replace("\"", "\\\""); - private async Task getUrlString(string url) - { - HttpResponseMessage response = await client.GetAsync(url); - response.EnsureSuccessStatusCode(); - Task body = response.Content.ReadAsStringAsync(); - return await Task.Run(() => body.Result); + return "[null," + update + "[null,null,null,[null,\"" + query + "\",\"" + domainExt + "\",[null,\"" + GetDescriptionFromEnumValue(_language) + "\",\"" + GetDescriptionFromEnumValue(_region) + "\"],null,null,null," + flagRegion + "," + flagLanguage + "]," + _sources + "," + (int)alert.howMany + ",[[null," + feedKey + ",\"" + _email + "\"," + howOften + ",\"en-US\"," + (isEdit ? "1" : "null") + ",null,null,null,null,\"" + (isEdit ? (string)this._alertTable[alert.id] : "0") + "\",null,null,\"" + this._auth_token + "\"]]]]"; } - private async Task postUrlString(string url, List> postData) + private async Task PostUrlString(string url, List> postData) { if (postData == null) - { postData = new List>(); - } + HttpContent postContent = new FormUrlEncodedContent(postData); if ((postData != null) && (postData.Count == 1) && postData[0].Key == "params") - { - url = url + "x=" + this.auth_id; - } - HttpResponseMessage response = await client.PostAsync(url, postContent); + url = url + "x=" + this._auth_id; + + HttpResponseMessage response = await _client.PostAsync(url, postContent); response.EnsureSuccessStatusCode(); Task body = response.Content.ReadAsStringAsync(); return await Task.Run(() => body.Result); } - private bool hasElement(string body, string xpath) - { - HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); - doc.LoadHtml(body); - HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes(xpath); - return nodes != null && nodes.Count > 0; - } - - private List getElements(string body, string xpath) + private List GetElements(string body, string xpath) { List list = new List(); HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(body); HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes(xpath); + foreach (var node in nodes) - { list.Add(node.InnerHtml); - } + return list; } - private List> getLoginFormParams(string body, string username, string password) - { - HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); - doc.LoadHtml(body); - HtmlAgilityPack.HtmlNode loginform = doc.GetElementbyId("gaia_loginform"); - HtmlAgilityPack.HtmlNodeCollection inputElements = loginform.SelectNodes("//input"); - List> paramList = new List>(); - bool hasPassword = false; - foreach (HtmlAgilityPack.HtmlNode input in inputElements) - { - string name = input.GetAttributeValue("name", string.Empty); - string value = input.GetAttributeValue("value", string.Empty); - if (name == "Email") - { - value = username; - } - else if (name == "Passwd") - { - hasPassword = true; - value = password; - } - paramList.Add(new KeyValuePair(name, value)); - } - if (!hasPassword) - { - paramList.Add(new KeyValuePair("Passwd", password)); - } - return paramList; - } - - private static string GetDescriptionFromEnumValue(Enum value) { DescriptionAttribute attribute = value.GetType() @@ -398,5 +327,7 @@ public static bool IsNullOrEmpty(JToken token) (token.Type == JTokenType.String && token.ToString() == String.Empty) || (token.Type == JTokenType.Null); } + + #endregion } } diff --git a/GoogleAlertAPI.csproj b/GoogleAlertAPI.csproj index 97b402f..715a484 100644 --- a/GoogleAlertAPI.csproj +++ b/GoogleAlertAPI.csproj @@ -31,12 +31,11 @@ 4 - - ..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll + + packages\HtmlAgilityPack.1.11.16\lib\Net45\HtmlAgilityPack.dll - - False - ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + + packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll diff --git a/GoogleAlertAPI.sln b/GoogleAlertAPI.sln new file mode 100644 index 0000000..403c74a --- /dev/null +++ b/GoogleAlertAPI.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29411.108 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoogleAlertAPI", "GoogleAlertAPI.csproj", "{C84F5E1E-F821-4F42-BBDE-DBB45FAB43EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C84F5E1E-F821-4F42-BBDE-DBB45FAB43EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C84F5E1E-F821-4F42-BBDE-DBB45FAB43EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C84F5E1E-F821-4F42-BBDE-DBB45FAB43EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C84F5E1E-F821-4F42-BBDE-DBB45FAB43EF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D2FE58EE-1721-46A1-872F-F7E32BEB0B22} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 39b9a7d..217bfbb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,18 @@ # GoogleAlertAPI +### Update +Since the password authentification no longer exist, we should use Cookies identifiers. + + +How to get Cookie IDs: +1. Open Chrome Dev Tools (F12) +2. Open "Network" tab +3. Select Checkbox "Preserve log" +4. Open Google SignIn page https://accounts.google.com/ServiceLogin +5. Enter email, press next +6. Press "Clear" in "Network" tab +7. Enter password and press "Next" +8. In "Network" tab find a row with "challenge?hl=" and select it +9. Open "Cookies" tab and copy SID, SSID, HSID values ### Usage Create Alert diff --git a/packages.config b/packages.config index 6ac3620..eaa7426 100644 --- a/packages.config +++ b/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file