diff --git a/README.md b/README.md index c921bfc..1d62e79 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,12 @@ Check out the desklet configuration settings, and choose the data refresh period ## Release Notes +### 0.8.5 - May 25, 2023 + +Bugfixes: + +- adapt to recent changes in Yahoo Finance Quotes API + ### 0.8.4 - May 8, 2023 Features: diff --git a/files/yfquotes@thegli/desklet.js b/files/yfquotes@thegli/desklet.js index ee05ae3..1b085e9 100644 --- a/files/yfquotes@thegli/desklet.js +++ b/files/yfquotes@thegli/desklet.js @@ -31,6 +31,8 @@ const UUID = "yfquotes@thegli"; const DESKLET_DIR = imports.ui.deskletManager.deskletMeta[UUID].path; const ABSENT = "N/A"; const YF_PAGE = "https://finance.yahoo.com/quote/"; +const ERROR_RESPONSE_BEGIN="{\"quoteResponse\":{\"result\":[],\"error\":\""; +const ERROR_RESPONSE_END = "\"}}"; Gettext.bindtextdomain(UUID, GLib.get_home_dir() + "/.local/share/locale"); @@ -41,6 +43,11 @@ if (Soup.MAJOR_VERSION == 2) { } else { //version 3 _httpSession = new Soup.Session(); } +_httpSession.timeout = 10; +_httpSession.idle_timeout = 10; + +let _cookieStore = null; +let _crumb = null; function _(str) { return Gettext.dgettext(UUID, str); @@ -70,18 +77,87 @@ let YahooFinanceQuoteReader = function () {}; YahooFinanceQuoteReader.prototype = { constructor : YahooFinanceQuoteReader, - getAsyncResponse : function(quoteSymbols, apiVersion, callback) { + getCookie : function (callback) { + let here = this; + let message = Soup.Message.new("GET", "https://finance.yahoo.com/quote/%5EGSPC/options"); + if (Soup.MAJOR_VERSION === 2) { + message.request_headers.append("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + message.request_headers.append("Accept-Encoding", "gzip, deflate"); + _httpSession.queue_message(message, function (session, message) { + try { + callback.call(here, message); + } catch(e) { + global.logError(e); + } + }); + } else { //version 3, untested! + message.get_request_headers().append("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + message.get_request_headers().append("Accept-Encoding", "gzip, deflate"); + _httpSession.send_and_read_async(message, Soup.MessagePriority.NORMAL, null, function (session, result) { + try { + const bytes = _httpSession.send_and_read_finish(result); + callback.call(here, ByteArray.toString(bytes.get_data())); + } catch(e) { + global.logError(e); + } + }); + } + }, + + getCrumb : function (callback) { + let here = this; + let message = Soup.Message.new("GET", "https://query2.finance.yahoo.com/v1/test/getcrumb"); + if (Soup.MAJOR_VERSION === 2) { + message.request_headers.append("Accept", "*/*"); + message.request_headers.append("Accept-Encoding", "gzip, deflate"); + if (_cookieStore != null) { + Soup.cookies_to_request(_cookieStore, message); + } + _httpSession.queue_message(message, function (session, message) { + if (message.status_code === Soup.KnownStatusCode.OK) { + try { + callback.call(here, message.response_body); + } catch(e) { + global.logError(e); + } + } else { + global.logWarning("Error retrieving crumb! Status: " + message.status_code + ": " + message.reason_phrase); + callback.call(here, null); + } + }); + } else { //version 3, untested! + message.get_request_headers().append("Accept", "*/*"); + message.get_request_headers().append("Accept-Encoding", "gzip, deflate"); + if (_cookieStore != null) { + Soup.cookies_to_request(_cookieStore, message); + } + _httpSession.send_and_read_async(message, Soup.MessagePriority.NORMAL, null, function (session, result) { + if( message.get_status() === Soup.Status.OK) { + try { + const bytes = _httpSession.send_and_read_finish(result); + callback.call(here, ByteArray.toString(bytes.get_data())); + } catch(e) { + global.logError(e); + } + } else { + global.logWarning("Error retrieving crumb! Status: " + message.get_status() + ": " + message.get_reason_phrase()); + callback.call(here, null); + } + }); + } + }, + + getFinanceData : function (quoteSymbols, apiVersion, callback) { const requestUrl = this.createYahooQueryUrl(apiVersion, quoteSymbols); - const errorBegin="{\"quoteResponse\":{\"result\":[],\"error\":\""; - const errorEnd = "\"}}"; - let here = this; let message = Soup.Message.new("GET", requestUrl); - _httpSession.timeout = 10; - _httpSession.idle_timeout = 10; if (Soup.MAJOR_VERSION === 2) { + if (apiVersion !== "6" && _cookieStore != null) { + Soup.cookies_to_request(_cookieStore, message); + } + _httpSession.queue_message(message, function (session, message) { - if( message.status_code === 200) { + if( message.status_code === Soup.KnownStatusCode.OK) { try { callback.call(here, message.response_body.data.toString()); } catch(e) { @@ -89,12 +165,16 @@ YahooFinanceQuoteReader.prototype = { } } else { global.logWarning("Error retrieving url " + requestUrl + ". Status: " + message.status_code + ": " + message.reason_phrase); - callback.call(here, errorBegin + _("Yahoo Finance service not available!") + errorEnd); + callback.call(here, here.buildErrorResponse(_("Yahoo Finance service not available!"))); } }); - } else { //version 3 + } else { //version 3, untested! + if (apiVersion !== "6" && _cookieStore != null) { + Soup.cookies_to_request(_cookieStore, message); + } + _httpSession.send_and_read_async(message, Soup.MessagePriority.NORMAL, null, function (session, result) { - if( message.get_status() === 200) { + if( message.get_status() === Soup.Status.OK) { try { const bytes = _httpSession.send_and_read_finish(result); callback.call(here, ByteArray.toString(bytes.get_data())); @@ -103,14 +183,22 @@ YahooFinanceQuoteReader.prototype = { } } else { global.logWarning("Error retrieving url " + requestUrl + ". Status: " + message.get_status() + ": " + message.get_reason_phrase()); - callback.call(here, errorBegin + _("Yahoo Finance service not available!") + errorEnd); + callback.call(here, here.buildErrorResponse(_("Yahoo Finance service not available!"))); } }); } }, createYahooQueryUrl : function (apiVersion, quoteSymbols) { - return "https://query1.finance.yahoo.com/v" + apiVersion + "/finance/quote?symbols=" + quoteSymbols.join(","); + if (apiVersion === "6") { + return "https://query1.finance.yahoo.com/v6/finance/quote?symbols=" + quoteSymbols.join(","); + } else { + return "https://query1.finance.yahoo.com/v7/finance/quote?symbols=" + quoteSymbols.join(",") + "&crumb=" + _crumb; + } + }, + + buildErrorResponse : function (errorMsg) { + return ERROR_RESPONSE_BEGIN + errorMsg + ERROR_RESPONSE_END; } }; @@ -469,18 +557,37 @@ StockQuoteDesklet.prototype = { onUpdate : function () { const quoteSymbols = this.quoteSymbolsText.split("\n"); - const yfApiVersion = this.apiVersion ? this.apiVersion : "6"; + const yfApiVersion = this.apiVersion ? this.apiVersion : "7"; try { const _that = this; - this.quoteReader.getAsyncResponse(quoteSymbols, yfApiVersion, function(response) { - let parsedResponse = JSON.parse(response); - _that.render([parsedResponse.quoteResponse.result, parsedResponse.quoteResponse.error]); - _that.setUpdateTimer(); - }); + + if (yfApiVersion !== "6" && (_cookieStore == null || _crumb == null)) { + this.quoteReader.getCookie(function(responseMessage) { + _cookieStore = Soup.cookies_from_response(responseMessage); + + _that.quoteReader.getCrumb(function(responseBody) { + if (responseBody) { + _crumb = responseBody.data.toString(); + } + _that.renderFinanceData(quoteSymbols, yfApiVersion); + }); + }); + } else { + _that.renderFinanceData(quoteSymbols, yfApiVersion); + } } catch (err) { this.onError(quoteSymbols, err); } }, + + renderFinanceData : function (quoteSymbols, yfApiVersion) { + const _that = this; + this.quoteReader.getFinanceData(quoteSymbols, yfApiVersion, function(response) { + let parsedResponse = JSON.parse(response); + _that.render([parsedResponse.quoteResponse.result, parsedResponse.quoteResponse.error]); + _that.setUpdateTimer(); + }); + }, setUpdateTimer : function () { this.updateLoop = Mainloop.timeout_add(this.delayMinutes * 60 * 1000, Lang.bind(this, this.onUpdate)); diff --git a/files/yfquotes@thegli/metadata.json b/files/yfquotes@thegli/metadata.json index f76b1fd..7296f38 100644 --- a/files/yfquotes@thegli/metadata.json +++ b/files/yfquotes@thegli/metadata.json @@ -3,6 +3,6 @@ "name": "Yahoo Finance Quotes", "prevent-decorations": true, "max-instances": "10", - "version": "0.8.4", + "version": "0.8.5", "uuid": "yfquotes@thegli" } diff --git a/files/yfquotes@thegli/po/yfquotes@thegli.pot b/files/yfquotes@thegli/po/yfquotes@thegli.pot index c0ac283..59dddd3 100644 --- a/files/yfquotes@thegli/po/yfquotes@thegli.pot +++ b/files/yfquotes@thegli/po/yfquotes@thegli.pot @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-08 21:58+0200\n" +"POT-Creation-Date: 2023-05-25 23:25+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,23 +16,23 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: desklet.js:92 desklet.js:106 +#: desklet.js:168 desklet.js:186 msgid "Yahoo Finance service not available!" msgstr "" -#: desklet.js:438 +#: desklet.js:526 msgid "Updated at " msgstr "" -#: desklet.js:445 +#: desklet.js:533 msgid "Error: " msgstr "" -#: desklet.js:490 +#: desklet.js:597 msgid "Cannot display quotes information for symbols: " msgstr "" -#: desklet.js:491 +#: desklet.js:598 msgid "The following error occurred: " msgstr "" diff --git a/files/yfquotes@thegli/settings-schema.json b/files/yfquotes@thegli/settings-schema.json index 0bfee12..c808fa1 100644 --- a/files/yfquotes@thegli/settings-schema.json +++ b/files/yfquotes@thegli/settings-schema.json @@ -114,7 +114,7 @@ }, "apiVersion": { "type": "radiogroup", - "default": "6", + "default": "7", "options": { "V6": "6", "V7": "7"