diff options
-rwxr-xr-x | twittperator.js | 391 |
1 files changed, 215 insertions, 176 deletions
diff --git a/twittperator.js b/twittperator.js index 922d7a7..ad4873b 100755 --- a/twittperator.js +++ b/twittperator.js @@ -25,10 +25,10 @@ let PLUGIN_INFO = <VimperatorPlugin> - <name>twittperator</name> + <name>Twittperator</name> <description>Twitter Client using ChirpStream</description> <description lang="ja">OAuth対応Twitterクライアント</description> - <version>1.4.2</version> + <version>1.4.2.3.4.5.6.7.8.9.10.11.12.13</version> <minVersion>2.3</minVersion> <maxVersion>2.4</maxVersion> <author mail="teramako@gmail.com" homepage="http://d.hatena.ne.jp/teramako/">teramako</author> @@ -40,12 +40,12 @@ let PLUGIN_INFO = == Command == - Use completion for comfort. :tw[ittperator] -getPIN - Opens the page to authorize twittperator and get your PIN from Twitter + Opens the page to authorize Twittperator and get your PIN from Twitter. :tw[ittperator] -setPIN {PINcode} Allows Twittperator to access Twitter by signifying your PIN. :tw[ittperator] - Shows recent your timeline. ( The timeline will be cashed and expired 90 seconds after Twittperator get from Twitter. ) + Shows recent your timeline. (The timeline will be cashed and expired 90 seconds after Twittperator get from Twitter.) :tw[ittperator]! Gets recent your timeline from Twitter and shows it. :tw[ittperator]!@user @@ -72,7 +72,7 @@ let PLUGIN_INFO = :tw -getPIN ||< and you will get the page to authorize Twittperator to access Twitter in a new tab. - If you allow and you will get the PIN ( 7 digits numbers ), then yank it. + If you allow and you will get the PIN (7 digit numbers), then yank it. Secondarily, authorize Twittperator with your PIN. >|| @@ -279,7 +279,7 @@ let PLUGIN_INFO = } } return into; - } + }; OAuth.setProperties(OAuth, // utility functions { @@ -306,8 +306,7 @@ let PLUGIN_INFO = s = s.replace(/\(/g, "%28"); s = s.replace(/\)/g, "%29"); return s; - } - , + }, decodePercent: function decodePercent(s) { if (s != null) { // Handle application/x-www-form-urlencoded, which is defined by @@ -315,8 +314,7 @@ let PLUGIN_INFO = s = s.replace(/\+/g, " "); } return decodeURIComponent(s); - } - , + }, /** Convert the given parameters to an Array of name-value pairs. */ getParameterList: function getParameterList(parameters) { if (parameters == null) { @@ -333,8 +331,7 @@ let PLUGIN_INFO = list.push([p, parameters[p]]); } return list; - } - , + }, /** Convert the given parameters to a map from name to value. */ getParameterMap: function getParameterMap(parameters) { if (parameters == null) { @@ -354,8 +351,7 @@ let PLUGIN_INFO = return map; } return parameters; - } - , + }, getParameter: function getParameter(parameters, name) { if (!parameters instanceof Array) { return OAuth.getParameterMap(parameters)[name]; @@ -366,8 +362,7 @@ let PLUGIN_INFO = } } return null; - } - , + }, formEncode: function formEncode(parameters) { var form = ""; var list = OAuth.getParameterList(parameters); @@ -379,8 +374,7 @@ let PLUGIN_INFO = +"="+ OAuth.percentEncode(value); } return form; - } - , + }, decodeForm: function decodeForm(form) { var list = []; var nvps = form.split("&"); @@ -402,8 +396,7 @@ let PLUGIN_INFO = list.push([name, value]); } return list; - } - , + }, setParameter: function setParameter(message, name, value) { var parameters = message.parameters; if (parameters instanceof Array) { @@ -425,19 +418,18 @@ let PLUGIN_INFO = parameters[name] = value; message.parameters = parameters; } - } - , + }, setParameters: function setParameters(message, parameters) { var list = OAuth.getParameterList(parameters); for (var i = 0; i < list.length; ++i) { OAuth.setParameter(message, list[i][0], list[i][1]); } - } - , + }, /** Fill in parameters to help construct a request message. This function doesn't fill in every parameter. The accessor object should be like: - {consumerKey:"foo", consumerSecret:"bar", accessorSecret:"nurn", token:"krelm", tokenSecret:"blah"} + { consumerKey: "foo", consumerSecret: "bar", accessorSecret: "nurn", + token: "krelm", tokenSecret: "blah" } The accessorSecret property is optional. */ completeRequest: function completeRequest(message, accessor) { @@ -461,13 +453,11 @@ let PLUGIN_INFO = OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); } OAuth.SignatureMethod.sign(message, accessor); - } - , + }, setTimestampAndNonce: function setTimestampAndNonce(message) { OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); - } - , + }, addToURL: function addToURL(url, parameters) { newURL = url; if (parameters != null) { @@ -480,8 +470,7 @@ let PLUGIN_INFO = } } return newURL; - } - , + }, /** Construct the value of the Authorization header for an HTTP request. */ getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) { var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"'; @@ -494,8 +483,7 @@ let PLUGIN_INFO = } } return header; - } - , + }, /** Correct the time using a parameter from the URL from which the last script was loaded. */ correctTimestampFromSrc: function correctTimestampFromSrc(parameterName) { parameterName = parameterName || "oauth_timestamp"; @@ -509,21 +497,17 @@ let PLUGIN_INFO = var t = parameters[parameterName]; if (t == null) return; OAuth.correctTimestamp(t); - } - , + }, /** Generate timestamps starting with the given value. */ correctTimestamp: function correctTimestamp(timestamp) { OAuth.timeCorrectionMsec = (timestamp * 1000) - (new Date()).getTime(); - } - , + }, /** The difference between the correct time and my clock. */ - timeCorrectionMsec: 0 - , + timeCorrectionMsec: 0, timestamp: function timestamp() { var t = (new Date()).getTime() + OAuth.timeCorrectionMsec; return Math.floor(t / 1000); - } - , + }, nonce: function nonce(length) { var chars = OAuth.nonce.CHARS; var result = ""; @@ -566,8 +550,7 @@ let PLUGIN_INFO = var signature = this.getSignature(baseString); OAuth.setParameter(message, "oauth_signature", signature); return signature; // just in case someone's interested - } - , + }, /** Set the key string for signing. */ initialize: function initialize(name, accessor) { var consumerSecret; @@ -585,7 +568,8 @@ let PLUGIN_INFO = }); /* SignatureMethod expects an accessor object to be like this: - {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."} + { tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", + accessorSecret: "xcmvzc..." } The accessorSecret property is optional. */ // Class members: @@ -598,8 +582,7 @@ let PLUGIN_INFO = OAuth.setParameter(message, "oauth_signature_method", name); } OAuth.SignatureMethod.newMethod(name, accessor).sign(message); - } - , + }, /** Instantiate a SignatureMethod for the given method name. */ newMethod: function newMethod(name, accessor) { var impl = OAuth.SignatureMethod.REGISTERED[name]; @@ -616,11 +599,9 @@ let PLUGIN_INFO = } err.oauth_acceptable_signature_methods = acceptable; throw err; - } - , + }, /** A map from signature method name to constructor. */ - REGISTERED : {} - , + REGISTERED: {}, /** Subsequently, the given constructor will be used for the named methods. The constructor will be called with no parameters. The resulting object should usually implement getSignature(baseString). @@ -630,8 +611,7 @@ let PLUGIN_INFO = for (var n = 0; n < names.length; ++n) { OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor; } - } - , + }, /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */ makeSubclass: function makeSubclass(getSignatureFunction) { var superClass = OAuth.SignatureMethod; @@ -644,8 +624,7 @@ let PLUGIN_INFO = subClass.prototype.getSignature = getSignatureFunction; subClass.prototype.constructor = subClass; return subClass; - } - , + }, getBaseString: function getBaseString(message) { var URL = message.action; var q = URL.indexOf("?"); @@ -663,8 +642,7 @@ let PLUGIN_INFO = return OAuth.percentEncode(message.method.toUpperCase()) +"&"+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL)) +"&"+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters)); - } - , + }, normalizeUrl: function normalizeUrl(url) { var uri = OAuth.SignatureMethod.parseUri(url); var scheme = uri.protocol.toLowerCase(); @@ -672,7 +650,7 @@ let PLUGIN_INFO = var dropPort = (scheme == "http" && uri.port == 80) || (scheme == "https" && uri.port == 443); if (dropPort) { - // find the last : in the authority + // find the last ":" in the authority var index = authority.lastIndexOf(":"); if (index >= 0) { authority = authority.substring(0, index); @@ -684,21 +662,23 @@ let PLUGIN_INFO = } // we know that there is no query and no fragment here. return scheme + "://" + authority + path; - } - , + }, parseUri: function parseUri(str) { /* This function was adapted from parseUri 1.2.1 http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js */ - var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], - parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }}; + var o = { + key: ["source", "protocol", "authority", "userInfo", "user", + "password", "host", "port", "relative", "path", "directory", + "file", "query", "anchor"], + parser: { strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ } + }; var m = o.parser.strict.exec(str); var uri = {}; var i = 14; while (i--) uri[o.key[i]] = m[i] || ""; return uri; - } - , + }, normalizeParameters: function normalizeParameters(parameters) { if (parameters == null) { return ""; @@ -710,11 +690,11 @@ let PLUGIN_INFO = if (nvp[0] != "oauth_signature") { sortable.push([ OAuth.percentEncode(nvp[0]) + " " // because it comes before any character that can appear in a percentEncoded string. - + OAuth.percentEncode(nvp[1]) - , nvp]); + + OAuth.percentEncode(nvp[1]), + nvp]); } } - sortable.sort(function(a,b) { + sortable.sort(function(a, b) { if (a[0] < b[0]) return -1; if (a[0] > b[0]) return 1; return 0; @@ -728,20 +708,14 @@ let PLUGIN_INFO = }); OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"], - OAuth.SignatureMethod.makeSubclass( - function getSignature(baseString) { - return this.key; - } - )); + OAuth.SignatureMethod.makeSubclass(function getSignature(baseString) this.key)); OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"], - OAuth.SignatureMethod.makeSubclass( - function getSignature(baseString) { - b64pad = "="; - var signature = b64_hmac_sha1(this.key, baseString); - return signature; - } - )); + OAuth.SignatureMethod.makeSubclass(function getSignature(baseString) { + b64pad = "="; + var signature = b64_hmac_sha1(this.key, baseString); + return signature; + })); // oauth.js 2}}} // {{{2 sha1.js @@ -960,12 +934,12 @@ let PLUGIN_INFO = } }, // temp for request - request : { - token :"",// response oauth_token + request: { + token :"", // response oauth_token tokenSecret: ""// response oauth_token_secret }, // トークンが取得済みかの真偽値を返す - isAuthorize : function() { + isAuthorize: function() { let accessor = this.getAccessor(); if (accessor.consumerKey && accessor.consumerSecret && accessor.token && accessor.tokenSecret) { @@ -975,17 +949,17 @@ let PLUGIN_INFO = }, getAccessor: function() { return { - consumerKey: this.accessor.get("consumerKey",""), - consumerSecret: this.accessor.get("consumerSecret",""), - token: this.accessor.get("token",""), - tokenSecret: this.accessor.get("tokenSecret","") + consumerKey: this.accessor.get("consumerKey", ""), + consumerSecret: this.accessor.get("consumerSecret", ""), + token: this.accessor.get("token", ""), + tokenSecret: this.accessor.get("tokenSecret", "") }; }, - deleteAccessor : function() { + deleteAccessor: function() { var clientInfo = { clientName: this.accessor.get("clientName", ""), - consumerKey: this.accessor.get("consumerKey",""), - consumerSecret: this.accessor.get("consumerSecret",""), + consumerKey: this.accessor.get("consumerKey", ""), + consumerSecret: this.accessor.get("consumerSecret", ""), }; this.accessor.clear(); this.accessor.set("clientName", clientInfo.clientName); @@ -993,13 +967,13 @@ let PLUGIN_INFO = this.accessor.set("consumerSecret", clientInfo.consumerSecret); }, // 認証ページのURLを取得 - getRequestToken : function(callback) { + getRequestToken: function(callback) { let message = { method: "GET", action: "https://twitter.com/oauth/request_token", parameters: { oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey","") + oauth_consumer_key: this.accessor.get("consumerKey", "") } }; OAuth.setTimestampAndNonce(message); @@ -1034,14 +1008,14 @@ let PLUGIN_INFO = self.initialize(); }); }, - // pinを元にAccess Tokenを取得して保存、callbackにはaccessorオブジェクトを渡す + // PINを元にaccess tokenを取得して保存、callbackにはaccessorオブジェクトを渡す getAccessToken: function(pin, callback) { var message = { method: "GET", action: "https://twitter.com/oauth/access_token", parameters: { oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""), + oauth_consumer_key: this.accessor.get("consumerKey", ""), oauth_token: this.request.token, // Request Token oauth_verifier: pin } @@ -1055,7 +1029,7 @@ let PLUGIN_INFO = url: target, onload: function(d) { if (d.status == 200) { - /* 返り値からAccess Token/Access Token Secretを取り出す */ + /* 返り値からaccess token/access token secretを取り出す */ var res = d.responseText; var parameter = self.getParameter(res); self.accessor.set("token", parameter["oauth_token"]); @@ -1075,14 +1049,14 @@ let PLUGIN_INFO = }, // api+?+query にアクセスした結果をcallbackに渡す get: function(api, query, callback) { - var btquery = (query)? "?"+this.buildQuery(query) : ""; + var btquery = query ? "?" + this.buildQuery(query) : ""; var message = { method: "GET", - action: api + btquery, + action: setting.apiURLBase + api + btquery, parameters: { oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""),// queryの構築 - oauth_token: this.accessor.get("token","") // Access Token + oauth_consumer_key: this.accessor.get("consumerKey", ""), // Queryの構築 + oauth_token: this.accessor.get("token", "") // Access token } }; OAuth.setTimestampAndNonce(message); @@ -1106,15 +1080,15 @@ let PLUGIN_INFO = post: function(api, content, callback) { var message = { method: "POST", - action: api, + action: setting.apiURLBase + api, parameters: { oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""), - oauth_token: this.accessor.get("token","") // Access Token + oauth_consumer_key: this.accessor.get("consumerKey", ""), + oauth_token: this.accessor.get("token", "") // Access token } }; // 送信するデータをパラメータに追加する - for ( var key in content ) { + for (var key in content) { message.parameters[key] = content[key]; } OAuth.setTimestampAndNonce(message); @@ -1136,14 +1110,75 @@ let PLUGIN_INFO = }; Utils.xmlhttpRequest(options); // 送信 }, + put: function(api, content, callback) { + var message = { + method: "PUT", + action: setting.apiURLBase + api, + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey", ""), + oauth_token: this.accessor.get("token", "") // Access token + } + }; + // 送信するデータをパラメータに追加する + for (var key in content) { + message.parameters[key] = content[key]; + } + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.getAccessor()); + var target = OAuth.addToURL(message.action, message.parameters); + var options = { + method: message.method, + url: target, + onload: function(d) { + if (d.status == 200) { + if (callback) { + callback(d.responseText); + } + } else { + callback(d); + } + } + }; + Utils.xmlhttpRequest(options); // 送信 + }, + delete: function(api, content, callback) { + var btquery = query ? "?" + this.buildQuery(query) : ""; + var message = { + method: "DELETE", + action: setting.apiURLBase + api + btquery, + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey", ""), // Queryの構築 + oauth_token: this.accessor.get("token", "") // Access token + } + }; + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.getAccessor()); + var target = OAuth.addToURL(message.action, message.parameters); + var options = { + method: message.method, + url: target, + onload: function(d) { + if (d.status == 200) { + if (callback) { + callback(d.responseText); + } + }else{ + callback(d.statusText); + } + }, + }; + Utils.xmlhttpRequest(options); // 送信 + }, getAuthorizationHeader: function(api) { var message = { method: "GET", action: api, parameters: { oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""),// queryの構築 - oauth_token: this.accessor.get("token",""), // Access Token + oauth_consumer_key: this.accessor.get("consumerKey", ""), // Queryの構築 + oauth_token: this.accessor.get("token", ""), // Access token oauth_version: "1.0" } }; @@ -1151,19 +1186,19 @@ let PLUGIN_INFO = OAuth.SignatureMethod.sign(message, this.getAccessor()); return OAuth.getAuthorizationHeader("Twitter", message.parameters); }, - // utility関数 + // Utility関数 // http://kevin.vanzonneveld.net - urlEncode : function(str) { + urlEncode: function(str) { str = (str+"").toString(); return encodeURIComponent(str).replace(/!/g, "%21").replace(/"/g, "%27").replace(/\(/g, "%28") .replace(/\)/g, "%29").replace(/\*/g, "%2A").replace(/%20/g, "+"); }, // オブジェクトからクエリを生成 - buildQuery : function(formdata, numeric_prefix, arg_separator) { - // * example 1: http_build_query({foo: "bar", php: "hypertext processor", baz: "boom", cow: "milk"}, "", "&"); - // * returns 1: "foo=bar&php=hypertext+processor&baz=boom&cow=milk" - // * example 2: http_build_query({"php": "hypertext processor", 0: "foo", 1: "bar", 2: "baz", 3: "boom", "cow": "milk"}, "myvar_"); - // * returns 2: "php=hypertext+processor&myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&cow=milk" + buildQuery: function(formdata, numeric_prefix, arg_separator) { + // * example 1: http_build_query({ foo: "bar", php: "Hypertext Processor", baz: "boom", cow: "milk" }, "", "&"); + // * returns 1: "foo=bar&php=Hypertext+Processor&baz=boom&cow=milk" + // * example 2: http_build_query({ "PHP": "Hypertext Processor", 0: "foo", 1: "bar", 2: "baz", 3: "boom", "cow": "milk" }, "myvar_"); + // * returns 2: "PHP=Hypertext+Processor&myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&cow=milk" var value, key, tmp = []; var self = this; var _http_build_query_helper = function(key, val, arg_separator) { @@ -1200,28 +1235,27 @@ let PLUGIN_INFO = return tmp.join(arg_separator); }, - // Query String から 連想配列を返す + // Query string から 連想配列を返す getParameter: function(str) { - var dec = decodeURIComponent; - var par = {}, itm; - if (typeof str == "undefined") return par; - if (str.indexOf("?", 0) > -1) str = str.split("?")[1]; - str = str.split("&"); - for (var i = 0; str.length > i; i++) { - itm = str[i].split("="); - if (itm[0] != "") { - par[itm[0]] = typeof itm[1] == "undefined" ? true : dec(itm[1]); + if (typeof str == "undefined") return {}; + var itm; + if (str.indexOf("?", 0) > -1) str = str.split("?", 2)[1]; + var regex = str.indexOf("&") > str.indexOf(";") ? /;+/ : /&+/; + return str.split(regex).reduce(function(r, v) { + var kv = v.split("=", 2); + if (kv[0] != "") { + r[kv[0]] = typeof kv[1] == "undefined" ? true : decodeURIComponent(kv[1]); } - } - return par; - } - }; - return p; + return r; + }, {}); + } + }; + return p; })(); // }}} // Twittperator - function Stream({host, path}) { // {{{ + function Stream({ host, path }) { // {{{ function extractURL(s) let (m = s.match(/https?:\/\/\S+/)) (m && m[0]); @@ -1234,9 +1268,9 @@ let PLUGIN_INFO = stop(); if (restartCount > 13) - return liberator.echoerr('Twittperator: Gave up to connect to ChirpUserStream...'); + return liberator.echoerr("Twittperator: Gave up to connect to ChirpUserStream..."); - liberator.echoerr('Twittperator: ChirpUserStream will be restared...'); + liberator.echoerr("Twittperator: ChirpUserStream will be restared..."); // 試行済み回数^2 秒後にリトライ setTimeout(start, Math.pow(2, restartCount) * 1000); @@ -1289,7 +1323,7 @@ let PLUGIN_INFO = socketService.createTransport( null, 0, useProxy ? setting.proxyHost : host, - useProxy ? parseInt(setting.proxyPort || '3128', 10) : 80, + useProxy ? parseInt(setting.proxyPort || "3128", 10) : 80, null); let os = transport.openOutputStream(0, 0, 0); let is = transport.openInputStream(0, 0, 0); @@ -1319,7 +1353,7 @@ let PLUGIN_INFO = lines[0] = buf + lines[0]; for (let [, line] in Iterator(lines.slice(0, -1))) { try { - if (/^\s*\{/(line)) + if (/^[ \n\r\t]*\{/(line)) onMsg(Utils.fixStatusObject(JSON.parse(line)), line); } catch (e) { liberator.log(e); } } @@ -1327,11 +1361,11 @@ let PLUGIN_INFO = } else { buf += data; } - } catch (e if /^(?:NS_ERROR_NET_RESET|NS_BASE_STREAM_CLOSED)$/(e)) { - liberator.echoerr('Twittperator: ChirpStream was stopped by ' + e.name + '.'); + } catch (e if /^NS_(?:ERROR_NET_RESET|BASE_STREAM_CLOSED)$/(e)) { + liberator.echoerr("Twittperator: ChirpStream was stopped by " + e.name + "."); restart(); } catch (e) { - liberator.echoerr('Twittperator: Unknown error on ChirpStream connection: ' + e.name); + liberator.echoerr("Twittperator: Unknown error on ChirpStream connection: " + e.name); restart(); } }, 500); @@ -1367,16 +1401,16 @@ let PLUGIN_INFO = clearPluginData: clearPluginData }; }; // }}} - let ChirpUserStream = Stream({host: "chirpstream.twitter.com", path: "/2b/user.json"}); + let ChirpUserStream = Stream({ host: "chirpstream.twitter.com", path: "/2b/user.json" }); let Twitter = { // {{{ destroy: function(id) { // {{{ - tw.post("http://api.twitter.com/1/statuses/destroy/" + id + ".json", null, function(text) { + tw.delete("statuses/destroy/" + id + ".json", null, function(text) { let res = Utils.fixStatusObject(JSON.parse(text)); Twittperator.echo("delete: " + res.user.name + " " + res.text) }); }, // }}} favorite: function(id) { // {{{ - tw.post("http://api.twitter.com/1/favorites/create/" + id + ".json", null, function(text) { + tw.post("favorites/create/" + id + ".json", null, function(text) { let res = Utils.fixStatusObject(JSON.parse(text)); Twittperator.echo("fav: " + res.user.name + " " + res.text) }); @@ -1392,10 +1426,10 @@ let PLUGIN_INFO = if (!force && !expiredStatus && history.length > 0) { onload(history); } else { - let api = "http://api.twitter.com/1/statuses/home_timeline.json", query = {}; + let api = "statuses/home_timeline.json", query = {}; if (target) { - api = "http://api.twitter.com/1/statuses/user_timeline.json"; + api = "statuses/user_timeline.json"; query.screen_name = target; } else { query = null; @@ -1429,13 +1463,13 @@ let PLUGIN_INFO = say: function(stat) { // {{{ let sendData = {}; let prefix, replyUser, replyID, postfix; - if (stat.match(/^(.*)@([^\s#]+)(?:#(\d+))(.*)$/)) { + if (stat.match(/^(.*)@(\w{1,15})#(\d+)(.*)$/)) { [prefix, replyUser, replyID, postfix] = [RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$4]; if (stat.indexOf("RT @" + replyUser + "#" + replyID) == 0) { Twittperator.withProtectedUserConfirmation( - {screenName: replyUser, statusId: replyID}, - 'retweet', - function() Twitter.reTweet(replyID) + { screenName: replyUser, statusId: replyID }, + "retweet", + function() Twitter.retweet(replyID) ); return; } @@ -1444,27 +1478,27 @@ let PLUGIN_INFO = sendData.in_reply_to_status_id = replyID; } Twittperator.withProtectedUserConfirmation( - {screenName: replyUser, statusId: replyID}, - 'reply', + { screenName: replyUser, statusId: replyID }, + "reply", function() { sendData.status = stat; sendData.source = "Twittperator"; - tw.post("http://api.twitter.com/1/statuses/update.json", sendData, function(text) { + tw.post("statuses/update.json", sendData, function(text) { let t = Utils.fixStatusObject(JSON.parse(text || "{}")).text; Twittperator.echo("Your post " + '"' + t + '" (' + t.length + " characters) was sent."); }); } ); }, // }}} - reTweet: function(id) { // {{{ - let url = "http://api.twitter.com/1/statuses/retweet/" + id + ".json"; - tw.post(url, null, function(text) { + retweet: function(id) { // {{{ + let url = "statuses/retweet/" + id + ".json"; + tw.put(url, null, function(text) { let res = Utils.fixStatusObject(JSON.parse(text)); - Twittperator.echo("ReTweet: " + res.retweeted_status.text); + Twittperator.echo("Retweet: " + res.retweeted_status.text); }); }, // }}} unfavorite: function(id) { // {{{ - tw.post("http://api.twitter.com/1/favorites/destroy/" + id + ".json", null, function(text) { + tw.delete("favorites/destroy/" + id + ".json", null, function(text) { let res = Utils.fixStatusObject(JSON.parse(text)); Twittperator.echo("unfav: " + res.user.name + " " + res.text, true); }); @@ -1479,27 +1513,30 @@ let PLUGIN_INFO = let center = m[0]; let [head, tail] = [center[0], center.slice(1)]; let right = str.substring(m.index + m[0].length); - let content = head === "@" ? <a highlight="URL" href={"http://twitter.com/" + tail}> {center} </a> - : <a highlight="URL" href={center}> {center} </a> + let content = head === "@" ? <a highlight="URL" href={setting.showTLURLScheme + "://twitter.com/" + tail}> {center} </a> + : <a highlight="URL" href={center}> {center} </a>; return <>{Utils.anchorLink(left)}{content}{Utils.anchorLink(right)}</>; } return str; }, // }}} fixStatusObject: function(st) { // {{{ - const Amps = { + const ENTITIY_PAIRS = { lt: "<", gt: ">", - quot: "\"", + quot: '"' hellip: "\u2026", }; - function unescapeAmps(str) - str.replace(/&([^;]+);/g, function(m, n) Amps[n] || m); + function unescapeSpecificEntities(str) + str.replace(/&([a-z\d]+);/g, function(m, n) ENTITIY_PAIRS[n] || m); + + function unescapeBrakets(str) + str.replace(/</g, "<").replace(/>/g, ">"); let result = {}; for (let [n, v] in Iterator(st)) { - result[n] = v && typeof v === 'object' ? Utils.fixStatusObject(v) : - n === 'text' ? unescapeAmps(v) : + result[n] = v && typeof v === "object" ? Utils.fixStatusObject(v) : + n === "text" ? unescapeBrakets(v) : v; } return result; @@ -1519,13 +1556,7 @@ let PLUGIN_INFO = echo: function(msg) { // {{{ liberator.echo("[Twittperator] " + msg); }, // }}} - isProtected: function({statusId, userId, screenName}) { // {{{ - function isp(f) { - let r; - history.some(function(st) f(st) && (r = st)); - return r && r.user.protected && r.user.screen_name; - } - + isProtected: function({ statusId, userId, screenName }) { // {{{ if (screenName && isp(function(st) st.user && st.user.screen_name == screenName)) return true; @@ -1536,6 +1567,12 @@ let PLUGIN_INFO = return true; return false; + + function isp(f) { + let r; + history.some(function(st) f(st) && (r = st)); + return (r && r.user.protected && r.user.screen_name) ? true : false; + } }, // }}} loadPlugins: function() { // {{{ function isEnabled(file) @@ -1636,7 +1673,7 @@ let PLUGIN_INFO = liberator.echo(html, true); }, // }}} showTwitterMentions: function(arg) { // {{{ - tw.get("http://api.twitter.com/1/statuses/mentions.json", null, function(text) { + tw.get("statuses/mentions.json", null, function(text) { Twittperator.showTL(JSON.parse(text).map(Utils.fixStatusObject)); }); }, // }}} @@ -1678,13 +1715,13 @@ let PLUGIN_INFO = }, // }}} withProtectedUserConfirmation: function(check, actionName, action) { // {{{ function canceled() - Twittperator.echo('Canceled.'); + Twittperator.echo("Canceled."); let protectedUserName = Twittperator.isProtected(check); if (protectedUserName) { commandline.input( - protectedUserName + ' is protected user! Do you really want to ' + actionName + '? Input "yes" if you want. => ', - function(s) (s === 'yes' ? action : canceled)(), + protectedUserName + " is protected user! Do you really want to " + actionName + '? Input "yes" if you want. => ', + function(s) (s === "yes" ? action : canceled)(), { onCancel: canceled } @@ -1733,15 +1770,15 @@ let PLUGIN_INFO = __proto__: init, get expr() { return RegExp( - '^' + + "^" + this.command.map(function(c) let (r = util.escapeRegex(c)) - (/^\W$/(c) ? r : r + ' ') - ).join("|" /* /|/ */) + (/^\W$/(c) ? r : r + " ") + ).join("|") ); }, match: function(s) s.match(this.expr), - action: function(args) init.action(args.literalArg.replace(this.expr, '').trim()) + action: function(args) init.action(args.literalArg.replace(this.expr, "").trim()) }; }; @@ -1770,7 +1807,7 @@ let PLUGIN_INFO = command: ["@"], description: "Show mentions or follower tweets", action: function(arg) { - if (arg.match(/^.+/)) { + if (arg.length > 0) { Twittperator.showFollowersStatus(arg, true); } else { Twittperator.showTwitterMentions(); @@ -1903,7 +1940,7 @@ let PLUGIN_INFO = if (!args.bang || args.literalArg.match(/^(\W|\S+\s)/)) return; context.title = ["Sub command", "Description"]; - context.completions = SubCommands.map(function({command, description}) [command[0], description]); + context.completions = SubCommands.map(function({ command, description }) [command[0], description]); } // }}} commands.addUserCommand(["tw[ittperator]"], "Twittperator command", // {{{ @@ -1986,7 +2023,9 @@ let PLUGIN_INFO = showTLURLScheme: let (v = gv.twittperator_show_tl_with_https_url) ("http" + (v === false ? "" : "s")), proxyHost: gv.twittperator_proxy_host, proxyPort: gv.twittperator_proxy_port, - screenName: gv.twittperator_screen_name + screenName: gv.twittperator_screen_name, + apiURLBase: "http" + (!!gv.twittperator_use_ssl_connection_for_api_ep ? "s" : "") + + "://api.twitter.com/" + (gv.twittperator_twitter_api_version || 1) + "/", }); let statusRefreshTimer; |