- // ==UserScript==
- // @name Show ctime of retweets
- // @name:ja リツイート自体の日時を表示
- // @name:ko 리트윗 날짜와 시간을 표시합니다.
- // @name:zh-CN 显示转发的日期和时间。
- // @name:zh-TW 顯示轉發的日期和時間。
- // @namespace https://gf.qytechs.cn/en/scripts/462070-show-ctime-of-retweets
- // @version 1.2.0
- // @description Also shows Twitter Blue badges and tweet source labels.
- // @description:ja Twitter Blue バッジ、ツイートソースラベルも表示できます。
- // @description:ko Twitter Blue 배지, 트윗 소스 라벨도 표시할 수 있습니다.
- // @description:zh-CN 您还可以显示推特蓝徽章、推文来源标签。
- // @description:zh-TW 您還可以顯示推特藍徽章、推文來源標籤。
- // @author AeamaN
- // @contributionURL bitcoin:1DC6uWJWzzwU3iRJDXhUquv6QAYaRvtfFJ
- // @match https://twitter.com/*
- // @match https://mobile.twitter.com/*
- // @match https://twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/*
- // @match https://mobile.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/*
- // @require https://gf.qytechs.cn/scripts/467232-legacy-verified-12/code/Legacy%20verified%2012.js
- // @require https://gf.qytechs.cn/scripts/467238-legacy-verified-15/code/Legacy%20verified%2015.js
- // @require https://gf.qytechs.cn/scripts/467255-legacy-verified-19/code/Legacy%20verified%2019.js
- // @require https://gf.qytechs.cn/scripts/467256-legacy-verified-24/code/Legacy%20verified%2024.js
- // @require https://gf.qytechs.cn/scripts/467257-legacy-verified-29/code/Legacy%20verified%2029.js
- // @require https://gf.qytechs.cn/scripts/467258-legacy-verified-3/code/Legacy%20verified%203.js
- // @require https://gf.qytechs.cn/scripts/467259-legacy-verified-4/code/Legacy%20verified%204.js
- // @require https://gf.qytechs.cn/scripts/467260-legacy-verified-5/code/Legacy%20verified%205.js
- // @require https://gf.qytechs.cn/scripts/467262-legacy-verified-6/code/Legacy%20verified%206.js
- // @require https://gf.qytechs.cn/scripts/467263-legacy-verified-7/code/Legacy%20verified%207.js
- // @require https://gf.qytechs.cn/scripts/467264-legacy-verified-8/code/Legacy%20verified%208.js
- // @require https://gf.qytechs.cn/scripts/467265-legacy-verified-9/code/Legacy%20verified%209.js
- // @grant GM.getValue
- // @grant GM.registerMenuCommand
- // @grant GM.setValue
- // @run-at document-idle
- // ==/UserScript==
- //
- // ES2017(ES8) or later.
- //
- // ES2017(ES8) 以降が必要です。
-
-
- (async function() { /* START */
-
-
- 'use strict';
-
-
- // //////////// Settings //////////// //
- // No GUI Settings
- // Default values are used.
- const NOGUI = false;
- // ////////////////////////////////// //
-
- // ///////// Default valuse ///////// //
- // Date formats
- // 1. 31.12.70 23:59
- // 2. 31.12.70(Th) 23:59
- // 3. 31.12.70 23:59:59
- // 4. 31.12.70(Th) 23:59:59
- //
- // 5. 70-12/31 23:59
- // 6. 70-12/31(Th) 23:59
- // 7. 70-12/31 23:59'59
- // 8. 70-12/31(Th) 23:59'59
- //
- // 9. 70/12/31 23:59
- // 10. 70/12/31(Th) 23:59
- // 11. 70/12/31 23:59:59
- // 12. 70/12/31(Th) 23:59:59 [ye/mo/da(we) ho:mi:se]
- //
- // 0. Not Shown
- const FMT = 10;
-
- // Substitute TB badge
- // when TB without legacy verification
- const TB = false;
-
- // Show source labels
- // 1. Tweet only
- // 2. Tweet and (Retweet)
- // 0. Not shown
- const SSL = 2;
-
- // Loop interval(ms)
- // const INTL = 1000;
- const INTL = 800;
- // ////////////////////////////////// //
-
- const MYNAME = 'sctrt120';
- const BTKN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs'
- + '=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
- const EBTKN = encodeURIComponent(BTKN);
-
- let top_sr = '1';
- let json_sr = [];
- let json_sr_new = [];
- let timeout_sr = false;
- let s_mus = true;
- let observer = new MutationObserver(function(mutations) {
- s_mus = mutations;
- });
- let fmt, tb, ssl, intl;
- let dalg;
-
- /*
- let cookie
-
- async function main_track()
-
- async function touid(sn)
- async function getuid(s_name)
-
- async function statsrt(uid, rtto)
- async function statsrt_new(uid, rtto)
- async function statsrt_old(uid, rtto)
- async function gettl(id, cs)
-
- async function subsall()
- function chklv(id)
-
- async function addsl()
- async function getdetl(tid)
-
- function mkreq(url, eq, ebt, controller)
-
- function datef(date, f)
-
- function makeDialog()
- function makeFunc(dalg)
- async function initgui()
- */
-
-
- let cookie = { // "https://qiita.com/aqril_1132/items/925a7cb04276d9f916d7"
- getObj: function() {
- let cookie = document.cookie;
- let cookieObj = {};
- if (!!cookie) {
- Array.prototype.forEach.call(cookie.split(';'), function(c) {
- let array = [c][0].split('=').map(function(a) {return a.trim()});
- let key = ~c.indexOf('=') ? unescape(array[0]) : '';
- let val = ~c.indexOf('=') ? unescape(array[1]) : unescape(array[0]);
- if (!cookieObj.hasOwnProperty(key)) {
- cookieObj[key] = [val];
- } else {
- cookieObj[key].push(val);
- }
- });
- }
- return cookieObj;
- },
- getByName: function(name) {
- let ret = [];
- let cookieObj = this.getObj();
- if (cookieObj.hasOwnProperty(name)) {
- ret = cookieObj[name];
- }
- return ret;
- },
- deleteByName : function(name, path) {
- var str = escape(name)
- + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'
- + (path ? '; path=' + path : '');
- document.cookie = str;
- }
- };
-
-
- async function main_track() {
- s_mus = null; // 不要?
-
- const SEL_END = 'main div[data-testid="primaryColumn"] section article span.css-cens5h.r-b88u0q:not([aria-label])';
- const SEL_RTTO = `div[data-testid^="User-Name"] a.css-901oao.r-qvutc0:not(.us-${MYNAME})`; // UTL, HTL
- const SEL_RTTO_2 = 'main div[data-testid="primaryColumn"] section article '
- + `div.css-1dbjc4n.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2 a.css-901oao.r-qvutc0:not(.us-${MYNAME})`;
- // Retweet def-ja, def-en, ble-ja, ble-en
- const SEL_ADD = `a.us-${MYNAME}`;
-
- let elms;
- let pe, fpe, xpe, elm2, old, span, span2, a;
- let sn, rtto, date;
- let id, stats;
-
- elms = document.querySelectorAll(SEL_END);
-
- for(let elm of elms) {
- pe = elm.parentNode;
- fpe = elm.parentNode.parentNode.parentNode.parentNode;
- sn = pe.getAttribute('href').slice(1);
-
- xpe = elm.closest('article');
- elm2 = xpe.querySelector(SEL_RTTO);
- if(!elm2) elm2 = xpe.querySelector(SEL_RTTO_2);
-
- rtto = elm2.getAttribute('href').split('/')[3];
-
- id = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
- if(!id) continue;
-
- stats = await statsrt(id[0], rtto); // id, rtto -> tid, tca
-
- if(!fmt) continue;
-
- old = fpe.querySelector(SEL_ADD)
-
- if(!old) {
- span = document.createElement('span');
- span.className = `us-${MYNAME}`;
- span.style.margin = '0px 3px 0px 3px';
- span.textContent = '·';
- span.style.color = getComputedStyle(elm, null).color;
- span.style.font = getComputedStyle(elm, null).font;
- span.style.lineHeight = getComputedStyle(pe, null).lineHeight;
-
- span2 = document.createElement('span');
- span2.className = `us-${MYNAME}`;
- span2.style.lineHeight = getComputedStyle(pe, null).lineHeight;
- span2.style.display = 'inline-block';
-
- a = document.createElement('a');
- a.className = `us-${MYNAME}`;
- a.setAttribute('dir', 'ltr');
- a.setAttribute('role', 'link');
- a.setAttribute('href', `/${sn}/status/${stats[0]}`);
- a.setAttribute('target', '_blank');
- a.setAttribute('rel', 'noopener noreferrer');
- date = datef(new Date(stats[1]), fmt);
- a.textContent = date;
- a.style.color = getComputedStyle(elm, null).color;
- a.style.font = getComputedStyle(elm, null).font;
- a.style.textDecoration = getComputedStyle(elm, null).textDecoration;
-
- fpe.appendChild(span);
- fpe.appendChild(span2);
- span2.appendChild(a);
-
- s_mus = null;
- } else {
- date = datef(new Date(stats[1]), fmt);
- if(old.textContent != date) old.textContent = date; // TZ change
- }
- }
- }
-
-
- async function touid(sn) {
- if(localStorage.getItem(`${MYNAME}_idl`) === null) { // 無い時
- let r = await getuid(sn); // screen name -> id, blue, created_at, screen_name, verified
- if(r) {
- localStorage.setItem(`${MYNAME}_idl`, JSON.stringify(r));
- } else {
- console.log(`${MYNAME}: touid:error.`);
- return null; // エラー
- }
- }
-
- let str = localStorage.getItem(`${MYNAME}_idl`); // ある時はココから
- let json = JSON.parse(str);
-
- for(let e of json) {
- if(e[3] == sn) return e;
- }
-
- json = json.concat(await getuid(sn)); // あるけど、無い時
- str = JSON.stringify(json);
- localStorage.setItem(`${MYNAME}_idl`, str);
-
- for(let e of json) {
- if(e[3] == sn) return e;
- }
-
- console.log(`${MYNAME}: touid:error:end.`);
- return null; // エラー
- }
-
-
- async function getuid(s_name) {
- let url = 'https://api.twitter.com/graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName';
- if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
- url = 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/'
- + 'graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName';
- }
- let q = '?variables={'
- + `"screen_name":"${s_name}",`
- + '"withSafetyModeUserFields":true,'
- + '"withSuperFollowsUserFields":true'
- + '}'
- + '&features={'
- + '"responsive_web_twitter_blue_verified_badge_is_enabled":true,'
- + '"responsive_web_graphql_exclude_directive_enabled":false,' // false
- + '"verified_phone_label_enabled":true,' // false
- + '"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,'
- + '"responsive_web_graphql_timeline_navigation_enabled":true'
- + '}';
- let eq = encodeURI(q);
-
- let controller = new AbortController();
- let req = mkreq(url, eq, EBTKN, controller);
- let res = {};
-
- try {
- setTimeout(function() {controller.abort()}, 60000);
- res = await fetch(req);
- if(!res.ok) {
- console.log(`${MYNAME}: getuid:notok:` + res.ok + '.');
- return null;
- } // 失敗なら空で終わり
- } catch(err) {
- console.log(`${MYNAME}: getuid:error:` + err + '.');
- return null; // 失敗なら空で終わり
- }
-
- let json = JSON.parse(await res.text());
-
- let id, bl, ca, sn, vf;
- id = json.data.user.result.rest_id;
- bl = json.data.user.result.is_blue_verified;
- ca = json.data.user.result.legacy.created_at;
- sn = json.data.user.result.legacy.screen_name;
- vf = json.data.user.result.legacy.verified;
-
- return [[id, bl, ca, sn, vf]];
- }
-
-
- async function statsrt(uid, rtto) { // 連続している事、entries[]の順序が正しい事
- let num = localStorage.length;
- let keys = [];
- let str = '';
- let time = null;
-
- top_sr = '1';
- json_sr = [];
- json_sr_new = [];
- timeout_sr = false; // 初期化
-
- for(let i = 0; i < num; i++) {
- keys.push(localStorage.key(i));
- }
-
- if(!keys.includes(`${MYNAME}_tl_` + uid)) { // 無い時
- let r = await gettl(uid, null); // id, cursor -> tid, tca, s, rtid, rtca
- if(r) {
- localStorage.setItem(`${MYNAME}_tl_` + uid, JSON.stringify(r));
- } else {
- console.log(`${MYNAME}: statsrt:error.`);
- return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970']; // エラー
- }
- }
-
- if(localStorage.getItem(`${MYNAME}_timerl`) === null) {
- time = Date.now();
- localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify([[uid, time]]));
- } else {
- let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
- for(let e of j) if(e[0] == uid) time = e[1];
- if(!time) { // 項目が無い時
- time = Date.now(); // この時点の保存値にセットされる
- j = j.concat([[uid, time]]);
- localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
- }
- }
-
- str = localStorage.getItem(`${MYNAME}_tl_` + uid); // ある時はココから
- json_sr = JSON.parse(str);
-
- for(let e of json_sr) {
- if(e[3] == rtto) return [e[0], e[1]]; // あれば終わり
- }
-
- for(let e of json_sr) {
- if(/^[0-9]/i.test(e[0])) {
- top_sr = e[0];
- break;
- }
- } // 上作成
-
- await statsrt_new(uid, rtto); // json_sr、json_sr_new更新
-
- json_sr = json_sr_new.concat(json_sr); // json_sr更新
- str = JSON.stringify(json_sr); // str更新
- localStorage.setItem(`${MYNAME}_tl_` + uid, str); // とりあえず、ストレージに保存
-
- for(let e of json_sr) {
- if(e[3] == rtto) return [e[0], e[1]]; // あれば終わり
- }
-
- let ret = await statsrt_old(uid, rtto); // json_sr更新、保存
- if(ret) return ret;
-
- let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
- for(let e of j) if(e[0] == uid) time = e[1]; // time更新
-
- if(!timeout_sr && Date.now() - time > 600000) {
- localStorage.removeItem(`${MYNAME}_tl_${uid}`); // センシティブ
- console.log(`${MYNAME}: statsrt:remove:${MYNAME}_tl_${uid}.`);
-
- j = j.filter(function(e) {
- return e[0] != uid;
- });
- localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
- time = null;
- }
-
- console.log(`${MYNAME}: statsrt:end.`);
- return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970'];
- // TLがとても古いか、センシティブか、エラー
- }
-
-
- async function statsrt_new(uid, rtto) { // json_sr、json_sr_new更新
- let btmtmp = '';
-
- outer_block:
- for(const start = Date.now(); 1;) { // 新しい方、top_srを見る
- let ret = await gettl(uid, btmtmp);
- json_sr_new = json_sr_new.concat(ret); // json_sr_new更新
-
- for(let e of json_sr_new.slice().reverse()) { // 不要
- if(/^[0-9]/i.test(e[0])) {
- btmtmp = e[0];
- break;
- }
- } // 下更新
-
- if(ret.length < 2) {
- json_sr = []; // 非連続
-
- let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
- j = j.filter(function(e) {
- return e[0] != uid;
- });
- j = j.concat([[uid, Date.now()]]);
- localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
-
- break;
- } // 空の時は終了、一回目は無い(一個の時以外)
-
- for(let e of json_sr_new) {
- if(e[0] == top_sr) break outer_block; // あれば終了
- }
-
- if(Date.now() - start > 150000) { // 時間経過時打ち切り
- json_sr = []; // 非連続かも
-
- let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
- j = j.filter(function(e) {
- return e[0] != uid;
- });
- j = j.concat([[uid, Date.now()]]);
- localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
-
- break;
- }
-
- await new Promise(resolve => setTimeout(resolve, 200));
- }
- }
-
-
- async function statsrt_old(uid, rtto) {
- let btm = '';
-
- for(const start = Date.now(); 1;) { // 古い方、rttoを見る
- for(let e of json_sr.slice().reverse()) { // 不要
- if(/^[0-9]/i.test(e[0])) {
- btm = e[0];
- break;
- }
- } // 下更新
-
- let ret = await gettl(uid, btm);
- json_sr = json_sr.concat(ret); // json_sr更新
- let s = JSON.stringify(json_sr);
- localStorage.setItem(`${MYNAME}_tl_` + uid, s); // とりあえず、ストレージに保存
-
- if(ret.length < 2) {
- break;
- } // 空の時は終了
-
- for(let e of json_sr) {
- if(e[3] == rtto) return [e[0], e[1]]; // あれば終わり
- }
-
- if(Date.now() - start > 300000) {
- timeout_sr = true;
- break;
- } // 時間経過時打ち切り
-
- await new Promise(resolve => setTimeout(resolve, 200));
- }
- }
-
-
- async function gettl(id, cs) {
- let url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
- if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
- url = 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/'
- + '1.1/statuses/user_timeline.json';
- }
- let kav = '';
- if(cs) kav = `&max_id=${cs}`;
- let q = `?user_id=${id}&count=50${kav}`;
- let eq = encodeURI(q);
-
- let controller = new AbortController();
- let req = mkreq(url, eq, EBTKN, controller);
- let res = {};
-
- let ret = [];
-
- try {
- setTimeout(function() {controller.abort()}, 60000);
- res = await fetch(req);
- if(!res.ok) {
- console.log(`${MYNAME}: gettl:notok:` + res.ok + '.');
- return null;
- } // 失敗なら空で終わり
- } catch(err) {
- console.log(`${MYNAME}: gettl:error:` + err + '.');
- return null; // 失敗なら空で終わり
- }
-
- let json = JSON.parse(await res.text());
-
- let num = Object.keys(json).length;
- let tid, tca, s, rtid, rtca;
-
- for(let i = 0; i < num; i++) {
- tid = json[i].id_str;
- tca = json[i].created_at;
- s = json[i].source;
- if(typeof(json[i].retweeted_status) !== 'undefined') {
- rtid = json[i].retweeted_status.id_str;
- rtca = json[i].retweeted_status.created_at;
- } else {
- rtid = 'none';
- rtca = 'none';
- }
-
- ret.push([tid, tca, s, rtid, rtca]);
- }
-
- return ret;
- }
-
-
- async function subsall() {
- const SEL_V = 'path[d^="M20.396 11c-.018-.646-.215-1.275-.57-1"]';
- const SEL_B = 'path[d^="M16.5 3H2v18h15c3.038 0 5.5-2.46 5.5-5"]';
- const SEL_E = 'svg.r-4qtqp9.r-yyyyoo.r-lwhw9o.r-dnmrzs.r-bnwqim.r-1plcrui.r-lrvibr.r-cnnz9e';
- // def-ja, def-en, ble-ja, ble-en
- const SEL_E2 = 'svg.r-4qtqp9.r-yyyyoo.r-1xvli5t.r-dnmrzs.r-bnwqim.r-1plcrui.r-f5ekn1.r-17h40o6.r-lrvibr';
- // def-ja, def-en, ble-ja, ble-en
- const D_V = 'M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-'
- + '.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-'
- + '.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 '
- + '1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1'
- + '.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 '
- + '1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02'
- + '.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 '
- + '1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 '
- + '1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 '
- + '1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 '
- + '1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 '
- + '1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 '
- + '1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z';
- const D_B = 'M16.5 3H2v18h15c3.038 0 5.5-2.46 5.5-5.5 0-1.4-.524-2.68-1.385-3.65-.08-.09-.089-'
- + '.22-.023-.32.574-.87.908-1.91.908-3.03C22 5.46 19.538 3 16.5 3zm-.796 5.99c.457-.05'
- + '.892-.17 1.296-.35-.302.45-.684.84-1.125 1.15.004.1.006.19.006.29 0 2.94-2.269 '
- + '6.32-6.421 6.32-1.274 0-2.46-.37-3.459-1 .177.02.357.03.539.03 1.057 0 2.03-.35 '
- + '2.803-.95-.988-.02-1.821-.66-2.109-1.54.138.03.28.04.425.04.206 0 '
- + '.405-.03.595-.08-1.033-.2-1.811-1.1-1.811-2.18v-.03c.305.17.652.27 '
- + '1.023.28-.606-.4-1.004-1.08-1.004-1.85 0-.4.111-.78.305-1.11 1.113 1.34 2.775 2.22 '
- + '4.652 2.32-.038-.17-.058-.33-.058-.51 0-1.23 1.01-2.22 2.256-2.22.649 0 '
- + '1.235.27 1.647.7.514-.1.997-.28 1.433-.54-.168.52-.526.96-.992 1.23z';
- const SEL_P_KS = 'form div[data-testid="TypeaheadUser"]'; // キーワード検索
- const SEL_P_KSR = 'form div[data-testid="typeaheadRecentSearchesItem"]'; // キーワード検索(最新)
- const SEL_P_DS = 'div[aria-labelledby="modal-header"] article div[data-testid^="User-Name"]'; // 表示をカスタマイズする
- const SEL_P_VRRT = 'main section article div[data-testid="card.layoutLarge.detail"]'; // ビデオ引用RT
- const SEL_P_T2 = 'main div[data-testid="primaryColumn"] div[data-testid="UserName"]'; // トップ2
- const SEL_P_RRT = 'main section article div[data-testid^="User-Name"]'; // 引用RT
- const SEL_KSD = 'div.css-1dbjc4n.r-1awozwy.r-18u37iz.r-1wbh5a2 div.css-1hf3ou5.r-14j79pv span'; // バグ?対策 def-ja, def-en
- const SEL_KSB = 'div.css-1dbjc4n.r-1awozwy.r-18u37iz.r-1wbh5a2 div.css-1hf3ou5.r-115tad6 span'; // バグ?対策 ble-ja, ble-en
- const SEL = 'div.css-1dbjc4n.r-18u37iz.r-1wbh5a2.r-13hce6t span.css-901oao.css-16my406.r-bcqeeo.r-qvutc0'; // def-ja, def-en, ble-ja, ble-en
- const SEL_2 = 'div.css-1dbjc4n.r-1awozwy.r-18u37iz.r-1wbh5a2 span.css-901oao.css-16my406.r-bcqeeo.r-qvutc0'; // def-ja, def-en, ble-ja, ble-en
-
- let elms = document.querySelectorAll(SEL_V + ', ' + SEL_B);
-
- for(let e of elms) {
- let spe = e.parentNode.parentNode;
- let tpe = e.parentNode.parentNode.parentNode;
- let sn;
-
- if(tpe.querySelector(SEL_E + ', ' + SEL_E2)) continue;
-
- if(spe.style.color == 'rgb(232, 134, 143)') {
- spe.style.color = 'rgb(29, 155, 240)';
- s_mus = null;
- }
-
- let xpe = e.closest('main div[data-testid="primaryColumn"] h2[role="heading"]'); // トップ
- if(xpe) {
- sn = document.URL.split('/')[3];
- }
-
- if(!sn && spe.getAttribute('data-testid') == 'verificationBadge') sn = document.URL.split('/')[3]; // 認証済アカウントポップアップ
-
- if(!sn) {
- let xpe = e.closest(SEL_P_KS)
- let sne;
- if(xpe) sne = xpe.querySelector(SEL_KSD);
- if(xpe && !sne) sne = xpe.querySelector(SEL_KSB);
- if(sne) sn = sne.textContent.split('@')[1];
- }
- if(!sn) {
- let xpe = e.closest(SEL_P_KSR)
- let sne;
- if(xpe) sne = xpe.querySelector(SEL_KSD);
- if(xpe && !sne) sne = xpe.querySelector(SEL_KSB);
- if(sne) sn = sne.textContent.split('@')[1];
- }
- if(!sn) {
- let xpe = e.closest(SEL_P_DS)
- let sne;
- if(xpe) sne = xpe.querySelector(SEL);
- if(sne) sn = sne.textContent.split('@')[1];
- }
- if(!sn) {
- let xpe = e.closest(SEL_P_VRRT)
- let sne;
- if(xpe) sne = xpe.querySelector(SEL);
- if(sne) sn = sne.textContent.split('@')[1];
- }
- if(!sn) {
- let xpe = e.closest(SEL_P_T2)
- let sne;
- if(xpe) sne = xpe.querySelector(SEL_2);
- if(sne) sn = sne.textContent.split('@')[1];
- }
- if(!sn) {
- let xpe = e.closest(SEL_P_RRT)
- let sne;
- if(xpe) sne = xpe.querySelector(SEL);
- if(sne) sn = sne.textContent.split('@')[1];
- }
-
- if(!sn) {
- let xpe = e.closest('a'); // TL等
- if(xpe) {
- sn = xpe.getAttribute('href').split('/')[1];
- }
- }
-
- if(!sn) {
- spe.style.color = 'rgb(232, 134, 143)';
- s_mus = null;
- continue;
- }
-
- let a = [
- '', 'compose', 'explore', 'home', 'i', 'login', 'messages',
- 'notifications', 'search', 'search-advanced','settings'
- ];
-
- if(a.indexOf(sn) > -1) {
- spe.style.color = 'rgb(232, 134, 143)';
- s_mus = null;
- continue;
- }
-
- let pr = await touid(sn);
- let r = chklv(pr[0]);
-
- if(r === true) {
- e.setAttribute('d', D_V);
- s_mus = null;
- } else if(r === false) {
- e.setAttribute('d', D_B);
- s_mus = null;
- } else {
- spe.style.color = 'rgb(232, 134, 143)';
- s_mus = null;
- }
- }
- }
-
-
- function chklv(id) {
- let ud = +id.substring(0, 2);
- let l;
-
- ud < 13 ? l = LVL_12
- : ud < 16 ? l = LVL_15
- : ud < 20 ? l = LVL_19
- : ud < 25 ? l = LVL_24
- : ud < 30 ? l = LVL_29
- : ud < 40 ? l = LVL_3
- : ud < 50 ? l = LVL_4
- : ud < 60 ? l = LVL_5
- : ud < 70 ? l = LVL_6
- : ud < 80 ? l = LVL_7
- : ud < 90 ? l = LVL_8
- : l = LVL_9;
-
- return l.includes(id);
- }
-
-
- async function addsl() {
- s_mus = null; // 不要?
-
- const SEL_END = 'main div[data-testid="primaryColumn"] section article div.css-1dbjc4n.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2';
- const SEL_END_RT = 'main div[data-testid="primaryColumn"] section article span.css-cens5h.r-b88u0q:not([aria-label])';
- const SEL_ADD = `span.us-${MYNAME}`;
-
- let old;
- let ca, span, span2, a, a2;
-
- let elms_end = document.querySelectorAll(SEL_END);
-
- for(let elm of elms_end) {
- let uid;
- let tid;
- let tsl = '?';
- let rtsl = '(?)';
- let thref = 'https://help.twitter.com/using-twitter/how-to-tweet#source-labels';
- if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
- thref = 'https://help.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/using-twitter/how-to-tweet#source-labels';
- }
- let rthref = thref;
-
- ca = elm.querySelector('a');
- tid = ca.getAttribute('href').split('/')[3];
-
- let detl = await getdetl(tid); // tid -> tca, s, uid, sn, uca
-
- if(detl) {
- tsl = detl[1].split('<')[1].split('>')[1];
- thref = detl[1].split('"')[1];
- }
-
- let elm_end_rt = document.querySelector(SEL_END_RT);
- if(elm_end_rt) {
- let pe = elm_end_rt.parentNode;
- let sn = pe.getAttribute('href').slice(1);
-
- uid = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
- // リツイートした人のuid
- }
-
- if(ssl == 1 || !elm_end_rt) {
- ;
- } else if(uid && localStorage.getItem(`${MYNAME}_tl_${uid[0]}`) !== null) {
- let str = localStorage.getItem(`${MYNAME}_tl_${uid[0]}`);
- let json = JSON.parse(str);
-
- for(let e of json) {
- if(e[3] == tid) {
- rtsl = '(' + e[2].split('<')[1].split('>')[1] + ')';
- rthref = e[2].split('"')[1];
- }
- }
- }
-
- old = elm.querySelectorAll(SEL_ADD)
-
- if(!old.length) {
- span = document.createElement('span');
- span.className = `us-${MYNAME}`;
- span.style.margin = '0px 3px 0px 3px';
- span.textContent = '·';
- span.style.color = getComputedStyle(ca, null).color;
- span.style.font = getComputedStyle(ca, null).font;
- span.style.lineHeight = getComputedStyle(elm, null).lineHeight;
-
- span2 = document.createElement('span');
- span2.className = `us-${MYNAME}`;
- span2.style.lineHeight = getComputedStyle(elm, null).lineHeight;
- span2.style.display = 'inline-block';
-
- a = document.createElement('a');
- a.className = `us-${MYNAME}`;
- a.setAttribute('role', 'link');
- a.setAttribute('href', thref);
- a.setAttribute('target', '_blank');
- a.setAttribute('rel', 'nofollow noopener noreferrer');
- a.textContent = tsl;
- a.style.color = getComputedStyle(ca, null).color;
- a.style.font = getComputedStyle(ca, null).font;
- a.style.textDecoration = getComputedStyle(ca, null).textDecoration;
-
- a2 = document.createElement('a');
- a2.className = `us-${MYNAME}`;
- a2.setAttribute('role', 'link');
- a2.setAttribute('href', rthref);
- a2.setAttribute('target', '_blank');
- a2.setAttribute('rel', 'nofollow noopener noreferrer');
- a2.textContent = rtsl;
- a2.style.color = getComputedStyle(ca, null).color;
- a2.style.font = getComputedStyle(ca, null).font;
- a2.style.textDecoration = getComputedStyle(ca, null).textDecoration;
-
- elm.appendChild(span);
- elm.appendChild(span2);
- span2.appendChild(a);
- if(ssl == 2 && elm_end_rt) span2.appendChild(a2);
-
- s_mus = null;
- }
- }
- }
-
-
- async function getdetl(tid) {
- let url = 'https://api.twitter.com/1.1/statuses/show.json';
- if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
- url = 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/'
- + '1.1/statuses/show.json';
- }
- let q = `?id=${tid}`;
- let eq = encodeURI(q);
-
- let controller = new AbortController();
- let req = mkreq(url, eq, EBTKN, controller);
- let res = {};
-
- try {
- setTimeout(function() {controller.abort()}, 60000);
- res = await fetch(req);
- if(!res.ok) {
- console.log(`${MYNAME}: getdtl:notok:` + res.ok + '.');
- return null;
- } // 失敗なら空で終わり
- } catch(err) {
- console.log(`${MYNAME}: getdtl:error:` + err + '.');
- return null; // 失敗なら空で終わり
- }
-
- let json = JSON.parse(await res.text());
-
- let num = 1; // 一個しかない
- let tca, s, uid, sn, uca;
-
- for(let i = 0; i < num; i++) {
- tca = json.created_at;
- s = json.source;
- if(typeof(json.user) !== 'undefined') {
- uid = json.user.id_str;
- sn = json.user.screen_name;
- uca = json.user.created_at;
- } else {
- uid = 'none';
- sn = 'none';
- uca = 'none';
- }
- }
-
- return [tca, s, uid, sn, uca];
- }
-
-
- function mkreq(url, eq, ebt, controller) {
- let req;
-
- if(cookie.getByName('gt').length && !cookie.getByName('twid').length) {
- req = new Request(`${url}${eq}`,{
- headers: {
- 'authorization': `Bearer ${ebt}`,
- 'x-csrf-token': cookie.getByName('ct0')[0],
- 'x-guest-token': cookie.getByName('gt')[0]
- },
- cache: 'force-cache',
- redirect: 'follow',
- signal: controller.signal
- });
- } else {
- req = new Request(`${url}${eq}`,{
- headers: {
- 'authorization': `Bearer ${ebt}`,
- 'x-csrf-token': cookie.getByName('ct0')[0],
- 'x-twitter-auth-type': 'OAuth2Session'
- },
- cache: 'force-cache',
- redirect: 'follow',
- mode: 'cors',
- credentials: 'include',
- signal: controller.signal
- });
- }
-
- return req;
- }
-
-
- function datef(date, f) {
- let week_l;
- let l = document.documentElement.getAttribute('lang');
- l == 'ja' ? week_l = ['日', '月', '火', '水', '木', '金', '土']
- : l == 'ko' ? week_l = ['일', '월', '화', '수', '목', '금', '토']
- : l == 'zh-Hant' ? week_l = ['日', '一', '二', '三', '四', '五', '六']
- : l == 'zh' ? week_l = ['日', '一', '二', '三', '四', '五', '六']
- : l == 'ru' ? week_l = ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб']
- : l == 'de' ? week_l = ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam']
- : l == 'it' ? week_l = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab']
- : l == 'fr' ? week_l = ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']
- : l == 'pt' ? week_l = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'] // Add your language
- : week_l = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
-
- let ye, mo, da, we, ho, mi, se;
- ye = date.getFullYear().toString().slice(-2);
- mo = ('0' + (date.getMonth() + 1)).slice(-2);
- da = ('0' + date.getDate()).slice(-2);
- we = week_l[date.getDay()];
- ho = ('0' + date.getHours()).slice(-2);
- mi = ('0' + date.getMinutes()).slice(-2);
- se = ('0' + date.getSeconds()).slice(-2);
-
- return f == 1 ? `${da}.${mo}.${ye} ${ho}:${mi}`
- : f == 2 ? `${da}.${mo}.${ye}(${we}) ${ho}:${mi}`
- : f == 3 ? `${da}.${mo}.${ye} ${ho}:${mi}:${se}`
- : f == 4 ? `${da}.${mo}.${ye}(${we}) ${ho}:${mi}:${se}`
- : f == 5 ? `${ye}-${mo}/${da} ${ho}:${mi}`
- : f == 6 ? `${ye}-${mo}/${da}(${we}) ${ho}:${mi}`
- : f == 7 ? `${ye}-${mo}/${da} ${ho}:${mi}'${se}`
- : f == 8 ? `${ye}-${mo}/${da}(${we}) ${ho}:${mi}'${se}`
- : f == 9 ? `${ye}/${mo}/${da} ${ho}:${mi}`
- : f == 10 ? `${ye}/${mo}/${da}(${we}) ${ho}:${mi}`
- : f == 11 ? `${ye}/${mo}/${da} ${ho}:${mi}:${se}`
- : f == 12 ? `${ye}/${mo}/${da}(${we}) ${ho}:${mi}:${se}`
- : `${ye}/${mo}/${da}(${we}) ${ho}:${mi}:${se}`;
- }
-
-
- function makeDialog() {
- let dalg = document.createElement('div');
-
- dalg.className = `us-${MYNAME}`;
-
- dalg.style.all = 'initial';
- dalg.style.backgroundColor = 'rgb(235, 235, 235)';
- dalg.style.border = '3px outset';
- dalg.style.borderRadius = '1%';
- dalg.style.display = 'none';
- dalg.style.fontFamily = 'monospace';
- dalg.style.fontSize = '12px';
- dalg.style.height = '340px';
- dalg.style.width = '400px';
- dalg.style.paddingLeft = '2px';
- dalg.style.paddingRight = '2px';
- dalg.style.position = 'fixed';
- dalg.style.right = '8px';
- dalg.style.top = '8px';
- dalg.style.zIndex = '2147483647';
-
- let html = '<span style="all: initial; font-size: 120%; line-height: 140%">'
- + `${GM.info.script.name} ${GM.info.script.version} Settings`
- + '</span><br />\n'
-
- + `<input type="radio" name="fmt" value="1" class="top_r" />31.12.70 23:59`
- + ` `
- + `<input type="radio" name="fmt" value="9" class="top_r" />70/12/31 23:59<br />\n`
- + `<input type="radio" name="fmt" value="2" class="mid_r" />31.12.70(Th) 23:59`
- + ` `
- + `<input type="radio" name="fmt" value="10" class="mid_r" />70/12/31(Th) 23:59<br />\n`
- + `<input type="radio" name="fmt" value="3" class="mid_r" />31.12.70 23:59:59`
- + ` `
- + `<input type="radio" name="fmt" value="11" class="mid_r" />70/12/31 23:59:59<br />\n`
- + `<input type="radio" name="fmt" value="4" class="mid_r" />31.12.70(Th) 23:59:59`
- + ` `
- + `<input type="radio" name="fmt" value="12" class="mid_r" />70/12/31(Th) 23:59:59<br />\n`
- + `<input type="radio" name="fmt" value="5" class="mid_r" />70-12/31 23:59`
- + ` `
- + `<input type="radio" name="fmt" value="0" class="mid_r" />Not Shown<br />\n`
- + `<input type="radio" name="fmt" value="6" class="mid_r" />70-12/31(Th) 23:59<br />\n`
- + `<input type="radio" name="fmt" value="7" class="mid_r" />70-12/31 23:59'59<br />\n`
- + `<input type="radio" name="fmt" value="8" class="btm_r" />70-12/31(Th) 23:59'59<br />\n`
-
- + `<input type="checkbox" name="tb" class="top_c" />Substitute TB badge<br />\n`
-
- + `<input type="radio" name="ssl" value="1" class="top_r" />Tweet source labels only`
- + ` `
- + `<input type="radio" name="ssl" value="0" class="top_r" />Not shown<br />\n`
- + `<input type="radio" name="ssl" value="2" class="btm_r" />Tweet and (Retweet)<br />\n`
-
- + '<span style="all: initial; font-size: 100%">'
- + 'Loop interval(ms) '
- + '</span><input type="text" name="intl" size="10" class="top_t" /><br />\n'
-
- + '<input type="button" class="top_b" value="Cancel" />\n'
- + '<input type="button" class="top_b" value="Set default" />\n'
- + '<input type="button" class="top_b" value="Save & Close" />\n';
-
- dalg.innerHTML = html;
-
- for(let e of dalg.querySelectorAll('input.top_r')) {
- e.style.all = 'initial';
- e.style.appearance = 'auto';
- e.style.marginRight = '1px';
- e.style.marginTop = '1px';
- }
- for(let e of dalg.querySelectorAll('input.mid_r, input.btm_r')) {
- e.style.all = 'initial';
- e.style.appearance = 'auto';
- e.style.marginRight = '1px';
- e.style.marginTop = '1px';
- }
- for(let e of dalg.querySelectorAll('input.top_c')) {
- e.style.all = 'initial';
- e.style.appearance = 'auto';
- e.style.marginRight = '1px';
- e.style.marginTop = '1px';
- }
- for(let e of dalg.querySelectorAll('input.top_t')) {
- e.style.all = 'initial';
- e.style.backgroundColor = 'rgb(255, 255, 255)';
- e.style.fontFamily = 'monospace';
- e.style.fontSize = '100%';
- e.style.marginLeft = '1px';
- e.style.marginRight = '1px';
- e.style.marginTop = '8px';
- e.style.marginBottom = '0px';
- e.style.paddingLeft = '1px';
- e.style.paddingRight = '1px';
- e.style.paddingTop = '1px';
- e.style.paddingBottom = '1px';
- }
- for(let e of dalg.querySelectorAll('input.top_b')) {
- e.style.all = 'initial';
- e.style.backgroundColor = 'rgb(190, 190, 190)';
- e.style.borderRadius = '10%';
- e.style.cursor = 'default';
- e.style.fontSize = '110%';
- e.style.marginTop = '10px';
- e.style.marginBottom = '0px';
- e.style.paddingTop = '6px';
- e.style.paddingBottom = '6px';
- e.style.textAlign = 'center';
- e.style.width = '90px';
- }
-
- return dalg;
- }
-
-
- function makeFunc(dalg) {
- dalg.addEventListener('click', function(event) {
- event.stopPropagation();
- }, false);
-
- dalg.querySelector('input[value="Cancel"]').addEventListener('click', function() {
- dalg.style.display = 'none';
- }, false);
- dalg.querySelector('input[value="Cancel"]').addEventListener('mouseenter', function(event) {
- event.target.style.backgroundColor = 'rgb(170, 170, 170)';
- }, false);
- dalg.querySelector('input[value="Cancel"]').addEventListener('mouseleave', function(event) {
- event.target.style.backgroundColor = 'rgb(190, 190, 190)'
- }, false);
-
- dalg.querySelector('input[value="Set default"]').addEventListener('click', function() {
- dalg.querySelector(`input[name="fmt"][value="${FMT}"]`).checked = true;
- dalg.querySelector('input[name="tb"]').checked = TB;
- dalg.querySelector(`input[name="ssl"][value="${SSL}"]`).checked = true;
- dalg.querySelector('input[name="intl"]').value = INTL;
- }, false);
- dalg.querySelector('input[value="Set default"]').addEventListener('mouseenter', function(event) {
- event.target.style.backgroundColor = 'rgb(170, 170, 170)';
- }, false);
- dalg.querySelector('input[value="Set default"]').addEventListener('mouseleave', function(event) {
- event.target.style.backgroundColor = 'rgb(190, 190, 190)'
- }, false);
-
- dalg.querySelector('input[value="Save & Close"]').addEventListener('click', async function() {
- for(let e of dalg.querySelectorAll('input[name="fmt"]')) {
- if(e.checked) {
- fmt = +e.value;
- break;
- }
- }
- tb = dalg.querySelector('input[name="tb"]').checked;
- for(let e of dalg.querySelectorAll('input[name="ssl"]')) {
- if(e.checked) {
- ssl = +e.value;
- break;
- }
- }
- intl = +dalg.querySelector('input[name="intl"]').value;
-
- await GM.setValue('fmt', fmt);
- await GM.setValue('tb', tb);
- await GM.setValue('ssl', ssl);
- await GM.setValue('intl', intl);
-
- dalg.style.display = 'none';
- }, false);
- dalg.querySelector('input[value="Save & Close"]').addEventListener('mouseenter', function(event) {
- event.target.style.backgroundColor = 'rgb(170, 170, 170)';
- }, false);
- dalg.querySelector('input[value="Save & Close"]').addEventListener('mouseleave', function(event) {
- event.target.style.backgroundColor = 'rgb(190, 190, 190)'
- }, false);
- }
-
-
- async function initgui() {
- if(await GM.getValue('fmt') === undefined) {
- await GM.setValue('fmt', FMT);
- } else {
- fmt = await GM.getValue('fmt');
- }
- if(await GM.getValue('tb') === undefined) {
- await GM.setValue('tb', TB);
- } else {
- tb = await GM.getValue('tb');
- }
- if(await GM.getValue('ssl') === undefined) {
- await GM.setValue('ssl', SSL);
- } else {
- ssl = await GM.getValue('ssl');
- }
- if(await GM.getValue('intl') === undefined) {
- await GM.setValue('intl', INTL);
- } else {
- intl = await GM.getValue('intl');
- }
-
- dalg = makeDialog();
- makeFunc(dalg);
- document.body.appendChild(dalg);
-
- GM.registerMenuCommand('Settings', function() {
- if(dalg.style.display == 'none') {
- dalg.querySelector(`input[name="fmt"][value="${fmt}"]`).checked = true;
- dalg.querySelector('input[name="tb"]').checked = tb;
- dalg.querySelector(`input[name="ssl"][value="${ssl}"]`).checked = true;
- dalg.querySelector('input[name="intl"]').value = intl;
-
- dalg.style.display = 'block';
- }
- });
- }
-
-
- console.log(`${MYNAME}: start.`);
-
- fmt = FMT;
- tb = TB;
- ssl = SSL;
- intl = INTL;
-
- if(!NOGUI) await initgui();
- observer.observe(document.documentElement, {childList:true, subtree:true});
-
- while(1) {
- if(s_mus) {
- if(fmt || ssl == 2) await main_track();
- if(tb) await subsall();
- if(ssl) await addsl();
- s_mus = null; // 初期値がtrue、非同期処理用
- }
-
- await new Promise(resolve => setTimeout(resolve, intl)); // intl は外から非同期に変更する
- }
-
-
- })(); /* END */