您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
您还可以显示推特蓝徽章、推文来源标签。
当前为
// ==UserScript== // @name Show ctime of retweets // @name:ja リツイート自体の日時を表示 // @name:ko 리트윗 날짜와 시간을 표시합니다. // @name:zh-CN 显示转发的日期和时间。 // @name:zh-TW 顯示轉發的日期和時間。 // @namespace http://greasyfork.icu/en/scripts/462070-show-ctime-of-retweets // @version 1.11.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/* // @match https://x.com/* // @match https://mobile.x.com/* // @require https://update.greasyfork.icu/scripts/467232/1197541/Legacy%20verified%2012.js // @require https://update.greasyfork.icu/scripts/467238/1197542/Legacy%20verified%2015.js // @require https://update.greasyfork.icu/scripts/467255/1197539/Legacy%20verified%2019.js // @require https://update.greasyfork.icu/scripts/467256/1197544/Legacy%20verified%2024.js // @require https://update.greasyfork.icu/scripts/467257/1197545/Legacy%20verified%2029.js // @require https://update.greasyfork.icu/scripts/467258/1197546/Legacy%20verified%203.js // @require https://update.greasyfork.icu/scripts/467259/1197547/Legacy%20verified%204.js // @require https://update.greasyfork.icu/scripts/467260/1197548/Legacy%20verified%205.js // @require https://update.greasyfork.icu/scripts/467262/1197549/Legacy%20verified%206.js // @require https://update.greasyfork.icu/scripts/467263/1197550/Legacy%20verified%207.js // @require https://update.greasyfork.icu/scripts/467264/1197553/Legacy%20verified%208.js // @require https://update.greasyfork.icu/scripts/467265/1197554/Legacy%20verified%209.js // @grant GM.getValue // @grant GM.registerMenuCommand // @grant GM.setValue // @run-at document-body // ==/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 ///////// // // 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; // Date formats // 1. 31.12.70 23:59 // 2. 31.12.70 23:59:59 // 3. 31.12.70(Th) 23:59 // 4. 31.12.70(Th) 23:59:59 // // 5. 70/12/31 23:59 // 6. 70/12/31 23:59:59 // 7. 70/12/31(Th) 23:59 // 8. 70/12/31(Th) 23:59:59 [ye/mo/da(we) ho:mi:se] // // 9. 70-12/31 23:59 // 10. 70-12/31 23:59'59 // 11. 70-12/31(Th) 23:59 // 12. 70-12/31(Th) 23:59'59 // // 13. M59-12-31 23:59 // 14. M59-12-31 23:59:59 // 15. M59-12-31(Th) 23:59 // 16. M59-12-31(Th) 23:59:59 // // 0. Not Shown const FMT = 7; // Loop interval(ms) const INTL = 800; // ////////////////////////////////// // const MYNAME = 'sctrt1110'; const BTKN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=' + '1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA'; const EBTKN = encodeURIComponent(BTKN); let cntr_i = [0, 0]; let dtbs = {}; let time_r = {}; let s_mus = true; let observer = new MutationObserver(function (mutations) { s_mus = mutations; }); let tb, ssl, fmt, intl; let originalXHROPEN; async function load_dtbs(key) { let str = await GM.getValue(key); let array = []; if (str === undefined) { // 何も無い時 await GM.setValue(key, JSON.stringify(array)); } else { // ある時 array = JSON.parse(str); } return array; } function getcv(name) { let cookie = document.cookie; if (!cookie) return []; let obj = {}; cookie.split(';').forEach(function (rec) { let kav = rec.split('='); let key, val; [key, val] = rec.includes('=') ? [unescape(kav[0].trim()), unescape(kav[1].trim())] : ['', unescape(kav[0].trim())]; if (!obj[key]) obj[key] = [val]; else obj[key].push(val); }); return obj[name] ?? []; } function pathexists(obj, path) { for (let key of path) { if (!(obj = obj[key])) return false; } return true; } 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 = '360px'; dalg.style.width = '420px'; dalg.style.paddingLeft = '2px'; dalg.style.paddingRight = '2px'; dalg.style.position = 'fixed'; dalg.style.right = '8px'; dalg.style.top = '8px'; dalg.style.zIndex = '2147483647'; dalg.style.overflow = 'auto'; let html = '<span style="all: initial; font-size: 120%; line-height: 210%">' + `${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="2" class="top_r" />31.12.70 23:59:59<br />\n' + '<input type="radio" name="fmt" value="3" class="mid_r" />31.12.70(Th) 23:59' + ' ' + '<input type="radio" name="fmt" value="4" class="mid_r" />31.12.70(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="6" class="mid_r" />70/12/31 23:59:59<br />\n' + '<input type="radio" name="fmt" value="7" class="mid_r" />70/12/31(Th) 23:59' + ' ' + '<input type="radio" name="fmt" value="8" class="mid_r" />70/12/31(Th) 23:59:59<br />\n' + '<input type="radio" name="fmt" value="9" class="mid_r" />70-12/31 23:59' + ' ' + '<input type="radio" name="fmt" value="10" class="mid_r" />70-12/31 23:59\'59<br />\n' + '<input type="radio" name="fmt" value="11" class="mid_r" />70-12/31(Th) 23: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="13" class="mid_r" />M59-12-31 23:59' + ' ' + '<input type="radio" name="fmt" value="14" class="mid_r" />M59-12-31 23:59:59<br />\n' + '<input type="radio" name="fmt" value="15" class="mid_r" />M59-12-31(Th) 23:59' + ' ' + '<input type="radio" name="fmt" value="16" class="mid_r" />M59-12-31(Th) 23:59:59<br />\n' + '<input type="radio" name="fmt" value="0" class="btm_r" />Not Shown<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="2" class="top_r" />Tweet and (Retweet)<br />\n' + '<input type="radio" name="ssl" value="0" class="btm_r" />Not shown<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'); } let 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'; } }); } function datef(date, f) { const LANG = document.documentElement.getAttribute('lang'); const WEEK = { 'ja': ['日', '月', '火', '水', '木', '金', '土'], 'ko': ['일', '월', '화', '수', '목', '금', '토'], 'zh-Hant': ['日', '一', '二', '三', '四', '五', '六'], 'zh': ['日', '一', '二', '三', '四', '五', '六'], 'ru': ['ВС', 'ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ'], 'de': ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'], 'it': ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], 'fr': ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'], 'pt': ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'], 'en': ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] // Add your language }; const WEEK_L = WEEK[LANG] ?? WEEK['en']; const YE = date.getFullYear().toString().slice(-2); const YM = date.getFullYear() - 1911; const MO = ('0' + (date.getMonth() + 1)).slice(-2); const DA = ('0' + date.getDate()).slice(-2); const WE = WEEK_L[date.getDay()]; const HO = ('0' + date.getHours()).slice(-2); const MI = ('0' + date.getMinutes()).slice(-2); const SE = ('0' + date.getSeconds()).slice(-2); const F = [ `${DA}.${MO}.${YE} ${HO}:${MI}`, // 0=1 `${DA}.${MO}.${YE} ${HO}:${MI}:${SE}`, `${DA}.${MO}.${YE}(${WE}) ${HO}:${MI}`, `${DA}.${MO}.${YE}(${WE}) ${HO}:${MI}:${SE}`, `${YE}/${MO}/${DA} ${HO}:${MI}`, `${YE}/${MO}/${DA} ${HO}:${MI}:${SE}`, `${YE}/${MO}/${DA}(${WE}) ${HO}:${MI}`, `${YE}/${MO}/${DA}(${WE}) ${HO}:${MI}:${SE}`, `${YE}-${MO}/${DA} ${HO}:${MI}`, `${YE}-${MO}/${DA} ${HO}:${MI}'${SE}`, `${YE}-${MO}/${DA}(${WE}) ${HO}:${MI}`, `${YE}-${MO}/${DA}(${WE}) ${HO}:${MI}'${SE}`, `M${YM}-${MO}-${DA} ${HO}:${MI}`, `M${YM}-${MO}-${DA} ${HO}:${MI}:${SE}`, `M${YM}-${MO}-${DA}(${WE}) ${HO}:${MI}`, `M${YM}-${MO}-${DA}(${WE}) ${HO}:${MI}:${SE}`, `${YE}/${MO}/${DA}(${WE}) ${HO}:${MI}:${SE}` ]; return F[f - 1] ?? F[16]; } function mkreq(url, eq, ebt, controller) { let req; if (getcv('gt').length && !getcv('twid').length) { req = new Request(`${url}${eq}`, { headers: { 'authorization': `Bearer ${ebt}`, 'x-csrf-token': getcv('ct0')[0], 'x-guest-token': getcv('gt')[0] }, cache: 'force-cache', redirect: 'follow', signal: controller.signal }); } else { req = new Request(`${url}${eq}`, { headers: { 'authorization': `Bearer ${ebt}`, 'x-csrf-token': getcv('ct0')[0], 'x-twitter-auth-type': 'OAuth2Session' }, cache: 'force-cache', redirect: 'follow', mode: 'cors', credentials: 'include', signal: controller.signal }); } return req; } async function getuid(s_name) { let url = 'https://api.x.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 unique(key) { // 要${MYNAME}_idl、${MYNAME}_twts let n = key == 'idl' ? 0 : 1; if (!cntr_i[n]) return; if (Date.now() - time_r[key] > 8000) { let i = cntr_i[n]; cntr_i[n] = 0; time_r[key] = Date.now(); let str = await GM.getValue('cntr'); if (str === undefined) str = JSON.stringify([0, 0]); // 何も無い時 let cntr = JSON.parse(str); let limit; if (key == 'idl') { cntr = [cntr[0] + i, cntr[1]]; limit = 100; } else { cntr = [cntr[0], cntr[1] + i]; limit = 50; } if (cntr[n] > limit) { dtbs[key] = [...new Set(dtbs[key].map(JSON.stringify))].map(JSON.parse); cntr = n == 0 ? [0, cntr[1]] : [cntr[0], 0]; } dtbs[key].push([Date.now()]); await GM.setValue(key, JSON.stringify(dtbs[key])); await GM.setValue('cntr', JSON.stringify(cntr)); } } async function touid(sn) { let n = dtbs.idl.findIndex(function (a) { const NOW = Date.now(); return typeof a[0] === 'number' && a[0] > NOW - 3600000; }); if (n >= 0) { for (let i = dtbs.idl.length; i > n; i--) { // 下から if (typeof dtbs.idl[i - 1][1] === 'undefined') continue; if (dtbs.idl[i - 1][3].toLowerCase() == sn.toLowerCase()) return dtbs.idl[i - 1]; } } let r = await getuid(sn); // 無い時 // screen name -> id, blue, created_at, screen_name, verified if (r) { dtbs.idl = dtbs.idl.concat([r]); cntr_i[0]++; return r.includes(sn) ? r : null; } console.log(`${MYNAME}: touid:error.`); return null; // エラー } function get10xn(path_result, tl) { let path_t = path_result.rest_id ? path_result : path_result.tweet; // ある前提 let path_t_1 = null; let path_t_2 = null; let path_qt = null; if (pathexists(path_t, ['legacy', 'retweeted_status_result', 'result', 'legacy'])) { path_t_1 = path_t.legacy.retweeted_status_result.result; } else if(pathexists(path_t, ['legacy', 'retweeted_status_result', 'result', 'tweet'])) { path_t_1 = path_t.legacy.retweeted_status_result.result.tweet; } if (path_t_1) { if (pathexists(path_t_1, ['edit_control', 'edit_tweet_ids'])) { path_t_2 = path_t_1.edit_control; } else { // 他も必ずある? path_t_2 = path_t_1.edit_control.edit_control_initial; } } if (pathexists(path_result, ['quoted_status_result', 'result', 'rest_id'])) { path_qt = path_result.quoted_status_result.result; } else if(pathexists(path_result, ['quoted_status_result', 'result', 'tweet', 'rest_id'])) { path_qt = path_result.quoted_status_result.result.tweet; } let tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts; let ret = []; tid = path_t.rest_id; ts = path_t.source; if (!path_t.legacy) { // 何も無い tuid = 'none'; tsn = 'none'; // 未確認 tca = 'none'; rtuid = 'none'; rtsn = 'none'; rtid = 'none'; rtca = 'none'; rts = 'none'; } else if (!path_t_1) { // リツイートでは無い tuid = path_t.legacy.user_id_str; tsn = path_t.core.user_results.result.legacy.screen_name; tca = path_t.legacy.created_at; rtuid = 'none'; rtsn = 'none'; // 未確認 rtid = 'none'; rtca = 'none'; rts = 'none'; } else { tuid = path_t.legacy.user_id_str; tsn = path_t.core.user_results.result.legacy.screen_name; tca = path_t.legacy.created_at; rtuid = path_t_1.legacy.user_id_str; rtsn = path_t_1.core.user_results.result.legacy.screen_name; rtid = tl ? path_t_2.edit_tweet_ids[5 - path_t_2.edits_remaining] : path_t_1.rest_id; rtca = path_t_1.legacy.created_at; rts = path_t_1.source; } ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]); if (!ret.length) { console.log(`${MYNAME}: get10xn:error:t.`); return null; // エラー } // tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts再利用 if (path_qt) { tid = path_qt.rest_id; ts = path_qt.source; if (!path_qt.legacy) { // 何も無い tuid = 'none'; tsn = 'none'; tca = 'none'; rtuid = 'none'; rtsn = 'none'; rtid = 'none'; rtca = 'none'; rts = 'none'; } else { tuid = path_qt.legacy.user_id_str; tsn = path_qt.core.user_results.result.legacy.screen_name; tca = path_qt.legacy.created_at; rtuid = 'none'; rtsn = 'none'; rtid = 'none'; rtca = 'none'; rts = 'none'; } if (!tid || !ts) { console.log(`${MYNAME}: get10xn:error:qt.`); return null; // エラー } ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]); } return ret; // 最大2 } async function getdetl(twtid) { let url = 'https://x.com/i/api/graphql/3HC_X_wzxnMmUBRIn3MWpQ/TweetResultByRestId'; // 一つだけ、これが基本、自身でfetch let q = '?variables={' + `"tweetId":"${twtid}",` + '"withCommunity":false,' + '"includePromotedContent":false,' + '"withVoice":false' + '}' + '&features={' + '"creator_subscriptions_tweet_preview_api_enabled":true,' + '"tweetypie_unmention_optimization_enabled":true,' + '"responsive_web_edit_tweet_api_enabled":true,' + '"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,' + '"view_counts_everywhere_api_enabled":true,' + '"longform_notetweets_consumption_enabled":true,' + '"responsive_web_twitter_article_tweet_consumption_enabled":false,' + '"tweet_awards_web_tipping_enabled":false,' + '"freedom_of_speech_not_reach_fetch_enabled":true,' + '"standardized_nudges_misinfo":true,' + '"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,' + '"longform_notetweets_rich_text_read_enabled":true,' + '"longform_notetweets_inline_media_enabled":true,' + '"responsive_web_graphql_exclude_directive_enabled":true,' + '"verified_phone_label_enabled":false,' + '"responsive_web_media_download_video_enabled":false,' + '"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,' + '"responsive_web_graphql_timeline_navigation_enabled":true,' + '"responsive_web_enhance_cards_enabled":false' + '}' + '&fieldToggles={' + '"withArticleRichContentState":false,' + '"withAuxiliaryUserLabels":false' + '}'; 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 path_result = json.data.tweetResult.result; let ret = null; if (path_result) ret = get10xn(path_result, false); if (ret) { dtbs.twts = dtbs.twts.concat(ret); cntr_i[1]++; } return ( ret ? [ ret[0][0], ret[0][1], ret[0][2], ret[0][3], ret[0][4], ret[0][5], ret[0][6], ret[0][7], ret[0][8], ret[0][9] ] : ret ); } function gettwts_r(stat, text) { if (stat < 200 || stat > 299) { console.log(`${MYNAME}: gettwts_r:error:${stat}.`); return null; // エラー } let json = JSON.parse(text); let path_result = json.data.tweetResult.result; let ret = null; if (path_result) ret = get10xn(path_result, false); if (ret) { dtbs.twts = dtbs.twts.concat(ret); cntr_i[1]++; } } function gettwts_d(stat, text) { if (stat < 200 || stat > 299) { console.log(`${MYNAME}: gettwts_d:error:${stat}.`); return null; // エラー } let json = JSON.parse(text); let ret = []; if (json.errors) { console.log(`${MYNAME}: gettwts_d:error:errors.`); return null; // エラー } let path_instructions = json.data.threaded_conversation_with_injections_v2.instructions; for (let n in path_instructions) { if (path_instructions[+n].type == 'TimelineAddEntries') { let path_entries = path_instructions[+n].entries; for (let n in path_entries) { let path_result = null; if (/^tweet-/i.test(path_entries[+n].entryId)) { if ( pathexists(path_entries[+n], [ 'content', 'itemContent', 'tweet_results', 'result', 'rest_id' ]) ) { path_result = path_entries[+n].content.itemContent.tweet_results.result; } else if( pathexists(path_entries[+n], [ 'content', 'itemContent', 'tweet_results', 'result', 'tweet' ]) ) { path_result = path_entries[+n].content.itemContent.tweet_results.result.tweet; } } else if (/^conversationthread-/i.test(path_entries[+n].entryId)) { if ( pathexists(path_entries[+n], [ 'content', 'items', 0, 'item', 'itemContent', 'tweet_results', 'result', 'rest_id' ]) ) { path_result = path_entries[+n].content.items[0].item.itemContent.tweet_results.result; // 0だけ } else if ( pathexists(path_entries[+n], [ 'content', 'items', 0, 'item', 'itemContent', 'tweet_results', 'result', 'tweet' ]) ) { path_result = path_entries[+n].content.items[0].item.itemContent.tweet_results.result.tweet; } } else { continue; } if(path_result) ret = ret.concat(get10xn(path_result, false)); } } else if (path_instructions[+n].type == 'TimelineAddToModule') { let path_moduleitems = path_instructions[+n].moduleItems; for (let n in path_moduleitems) { let path_result = null; if (/^conversationthread-/i.test(path_moduleitems[+n].entryId)) { if ( pathexists(path_moduleitems[+n], [ 'item', 'itemContent', 'tweet_results', 'result', 'rest_id' ]) ) { path_result = path_moduleitems[+n].item.itemContent.tweet_results.result; } else if ( pathexists(path_moduleitems[+n], [ 'item', 'itemContent', 'tweet_results', 'result', 'tweet' ]) ) { path_result = path_moduleitems[+n].item.itemContent.tweet_results.result.tweet; } } else { continue; } if (path_result) ret = ret.concat(get10xn(path_result, false)); } } } if (ret.length) dtbs.twts = dtbs.twts.concat(ret); cntr_i[1]++; } function gettwts(stat, text, path) { if (stat < 200 || stat > 299) { console.log(`${MYNAME}: gettwts:error:${stat}.`); return null; // エラー } let json = JSON.parse(text); let path_instructions = path.length == 4 ? json['data'][path[0]][path[1]][path[2]][path[3]]['instructions'] : path.length == 3 ? json['data'][path[0]][path[1]][path[2]]['instructions'] : json['data'][path[0]][path[1]]['instructions']; let n = 0; let ret = []; while (path_instructions[n].type != 'TimelineAddEntries') if (typeof path_instructions[++n] === 'undefined') return null; let path_entries = path_instructions[n].entries; let num = Object.keys(path_entries).length; for (let n in path_entries) { let path_result = null; if (/^tweet-/i.test(path_entries[+n].entryId)) { if (pathexists( path_entries[+n], ['content', 'itemContent', 'tweet_results', 'result', 'rest_id'] )) { path_result = path_entries[+n].content.itemContent.tweet_results.result; } else if (pathexists( path_entries[+n], ['content', 'itemContent', 'tweet_results', 'result', 'tweet'] )) { path_result = path_entries[+n].content.itemContent.tweet_results.result.tweet; } } else if (/^conversationthread-/i.test(path_entries[+n].entryId)) { if (pathexists( path_entries[+n], ['content', 'items', 0, 'item','itemContent', 'tweet_results', 'result', 'rest_id'] )) { path_result = path_entries[+n].content.items[0].item.itemContent.tweet_results.result; // 0だけ } else if (pathexists( path_entries[+n], ['content', 'items', 0, 'item','itemContent', 'tweet_results', 'result', 'tweet'] )) { path_result = path_entries[+n].content.items[0].item.itemContent.tweet_results.result.tweet; } } else { continue; } if (path_result) ret = ret.concat(get10xn(path_result, false)); } if (ret.length) dtbs.twts = dtbs.twts.concat(ret); cntr_i[1]++; } function statssn(sn) { let snl = sn.toLowerCase(); let n = dtbs.twts.findIndex(function (a) { const NOW = Date.now(); return typeof a[0] === 'number' && a[0] > NOW - 3600000; }); if (n >= 0) { for (let i = dtbs.twts.length; i > n; i--) { // 下から if (typeof dtbs.twts[i - 1][1] === 'undefined') continue; let t = dtbs.twts[i - 1][1].toLowerCase() == snl; let rt; if (!t) rt = dtbs.twts[i - 1][6].toLowerCase() == snl; if (t || rt) return t ? [dtbs.twts[i - 1][0], dtbs.twts[i - 1][1]] : [dtbs.twts[i - 1][5], dtbs.twts[i - 1][6]]; } } return null; } function chklv(id) { const UD = +id.substring(0, 2); const L = UD < 13 ? LVL_12 : UD < 16 ? LVL_15 : UD < 20 ? LVL_19 : UD < 25 ? LVL_24 : UD < 30 ? LVL_29 : UD < 40 ? LVL_3 : UD < 50 ? LVL_4 : UD < 60 ? LVL_5 : UD < 70 ? LVL_6 : UD < 80 ? LVL_7 : UD < 90 ? LVL_8 : LVL_9; return L.includes(id); } function statsrt(uid, rtid) { for (let i = dtbs.twts.length; i > 0; i--) { // 下から if (typeof dtbs.twts[i - 1][1] === 'undefined') continue; if (dtbs.twts[i - 1][0] == uid && dtbs.twts[i - 1][7] == rtid) return [dtbs.twts[i - 1][2], dtbs.twts[i - 1][3]]; // あれば終わり } console.log(`${MYNAME}: statsrt:end.`); return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970']; // エラー } function twXHRStateHandler({ target: xhr }, res) { if (xhr.readyState === 4) { const STAT = xhr.status; const rawTEXT = xhr.responseText; if (res == 1) gettwts_r(STAT, rawTEXT); else if (res == 2) gettwts_d(STAT, rawTEXT); else if (res == 3) gettwts(STAT, rawTEXT, ['user', 'result', 'timeline_v2', 'timeline']); else if (res == 4) gettwts(STAT, rawTEXT, ['home', 'home_timeline_urt']); else if (res == 5) gettwts(STAT, rawTEXT, ['home', 'home_timeline_urt']); else if (res == 6) gettwts(STAT, rawTEXT, ['list', 'tweets_timeline', 'timeline']); else if (res == 7) gettwts(STAT, rawTEXT, ['search_by_raw_query', 'search_timeline', 'timeline']); } } function isTwtsURL(url) { if (!url.includes('/graphql/')) return 0; else if (url.includes('/TweetResultByRestId?')) return 1; else if (url.includes('/TweetDetail?')) return 2; else if (url.includes('/UserTweets?')) return 3; else if (url.includes('/HomeTimeline')) return 4; else if (url.includes('/HomeLatestTimeline')) return 5; else if (url.includes('/ListLatestTweetsTimeline?')) return 6; else if (url.includes('/SearchTimeline?')) return 7; else return 0; } function overrideXHROpen() { originalXHROPEN = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function () { const RES = isTwtsURL(arguments[1]); if (RES) { this.addEventListener( 'readystatechange', function (evt) { twXHRStateHandler(evt, RES); } ); } return originalXHROPEN.apply(this, arguments); }; console.log(`${MYNAME}: XMLHttpRequest.open overriden.`); } async function subsbadge() { 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"]'; let elms = document.querySelectorAll(`${SEL_V}, ${SEL_B}`); for (let e of elms) { 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-.6' + '07.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.8' + '82-.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.90' + '2-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.1' + '44 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-.5' + '67s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.90' + '7.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.2' + '69 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.59' + '5-.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 button[data-testid="TypeaheadUser"]'; // キーワード検索 const SEL_P_KSR = 'form div[data-testid="typeaheadRecentSearchesItem"]'; // キーワード検索(最新) const SEL_P_DS = 'div[aria-labelledby="modal-header"] div.css-175oi2r.r-1wbh5a2.r-dnmrzs.r-1ny4l3l>' + '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_KS = 'div.css-175oi2r.r-1awozwy.r-18u37iz.r-1wbh5a2 div.css-146c3p1.r-1wvb978 span'; // バグ?対策 def-ja, def-en const SEL = 'div.css-175oi2r.r-18u37iz.r-1wbh5a2.r-1ez5h0i span.css-1jxf684.r-bcqeeo.r-qvutc0'; // def-ja, def-en, ble-ja, ble-en const SEL_2 = 'div.css-175oi2r.r-1awozwy.r-18u37iz.r-1wbh5a2 span.css-1jxf684.r-bcqeeo.r-qvutc0'; // def-ja, def-en, ble-ja, ble-en let spe = e.parentNode.parentNode; let tpe = e.parentNode.parentNode.parentNode; let sn; let ss; // Temp. if (tpe.querySelector(`${SEL_E}, ${SEL_E2}`)) continue; if (spe.style.color == 'rgb(232, 134, 143)') { ss = s_mus; spe.style.color = 'rgb(29, 155, 240)'; s_mus = ss; } 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_KS); if (sne) sn = sne.textContent.split('@')[1]; } if (!sn) { let xpe = e.closest(SEL_P_KSR); let sne; if (xpe) sne = xpe.querySelector(SEL_KS); 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'); // ビデオ引用TW、TL等 if (xpe) { let a = xpe.getAttribute('href').split('/')[1]; let b = xpe.getAttribute('href').split('/')[2]; let c = xpe.getAttribute('href').split('/')[3]; if (a == 'i' && b == 'status') { for (let i = dtbs.twts.length; i > 0; i--) { // 下から if (typeof dtbs.twts[i - 1][1] === 'undefined') continue; if (dtbs.twts[i - 1][2] == c) { sn = dtbs.twts[i - 1][1]; break; } else if (dtbs.twts[i - 1][7] == c) { sn = dtbs.twts[i - 1][6]; break; } } if (!sn) { let detl = await getdetl(c); if (detl) sn = detl[1]; } } else { sn = a; } } } if (!sn) { ss = s_mus; spe.style.color = 'rgb(232, 134, 143)'; s_mus = ss; continue; } let a = [ '', 'compose', 'explore', 'home', 'i', 'login', 'messages', 'notifications', 'search', 'search-advanced', 'settings' ]; if (a.includes(sn.toLowerCase())) { ss = s_mus; spe.style.color = 'rgb(232, 134, 143)'; s_mus = ss; continue; } let pr = statssn(sn); // screen name -> id, screen_name if (!pr) pr = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified let r = pr ? chklv(pr[0]) : pr; ss = s_mus; if (r === true) { e.setAttribute('d', D_V); } else if (r === false) { e.setAttribute('d', D_B); } else { spe.style.color = 'rgb(232, 134, 143)'; } s_mus = ss; } } async function addsl() { const SEL_END = 'main div[data-testid="primaryColumn"] section article ' + 'div.css-175oi2r.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2'; const SEL_END_RT = 'main div[data-testid="primaryColumn"] section article ' + 'span[id].r-8akbws.r-krxsd3.r-n6v787.r-1cwl3u0.r-b88u0q'; const SEL_ADD = `span.us-${MYNAME}`; let uid, tid, tsl, rtsl, thref, rthref, detl; let ca, span, span2, a, a2; let elm_end = document.querySelector(SEL_END); let elm_end_rt = document.querySelector(SEL_END_RT); if (!elm_end) return; let old = elm_end.querySelectorAll(SEL_ADD); if (old.length) return; tsl = '?'; rtsl = '(?)'; 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'; } rthref = thref; ca = elm_end.querySelector('a'); tid = ca.getAttribute('href').split('/')[3]; if (elm_end_rt) { let pe = elm_end_rt.parentNode; let sn = pe.getAttribute('href').slice(1); uid = statssn(sn); // screen name -> id, screen_name if (!uid) uid = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified // リツイートした人のuid } let i = 2; if (document.URL.split('/')[5] != tid) i = 7; for (let j = dtbs.twts.length; j > 0; j--) { // 下から if (typeof dtbs.twts[j - 1][1] === 'undefined') continue; if (dtbs.twts[j - 1][i] == tid) { detl = dtbs.twts[j - 1]; break; } } if (!detl) detl = await getdetl(tid); // ツイートはtid、rtidどちらにもなり得る if (detl) { tsl = detl[4].split('<')[1].split('>')[1]; thref = detl[4].split('"')[1]; } else { console.log(`${MYNAME}: addsl:error.`); // エラー } if (ssl == 1 || !elm_end_rt) { ; } else if (uid) { for (let j = dtbs.twts.length; j > 0; j--) { // 下から if (typeof dtbs.twts[j - 1][1] === 'undefined') continue; if (dtbs.twts[j - 1][0] == uid[0] && dtbs.twts[j - 1][7] == tid) { // リツイートは一つしか無い rtsl = `(${dtbs.twts[j - 1][4].split('<')[1].split('>')[1]})`; rthref = dtbs.twts[j - 1][4].split('"')[1]; break; } } } 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_end, null).lineHeight; span2 = document.createElement('span'); span2.className = `us-${MYNAME}`; span2.style.lineHeight = getComputedStyle(elm_end, 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; let ss = s_mus; elm_end.appendChild(span); elm_end.appendChild(span2); span2.appendChild(a); if (ssl == 2 && elm_end_rt) span2.appendChild(a2); s_mus = ss; } async function main_track() { const SEL_END_RT = 'main div[data-testid="primaryColumn"] section article ' + 'span[id].r-8akbws.r-krxsd3.r-n6v787.r-1cwl3u0.r-b88u0q'; let elms = document.querySelectorAll(SEL_END_RT); for (let elm of elms) { const SEL_RTTO = `div[data-testid^="User-Name"] a.css-146c3p1.r-qvutc0:not(.us-${MYNAME})`; // UTL, HTL const SEL_RTTO_2 = 'main div[data-testid="primaryColumn"] section article ' + `div.css-175oi2r.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2 a.css-1jxf684.r-qvutc0:not(.us-${MYNAME})`; // Retweet def-ja, def-en, ble-ja, ble-en const SEL_ADD = `a.us-${MYNAME}`; let pe = elm.parentNode; let fpe = elm.parentNode.parentNode.parentNode.parentNode; let xpe = elm.closest('article'); let elm2 = xpe.querySelector(SEL_RTTO); if (!elm2) elm2 = xpe.querySelector(SEL_RTTO_2); let old = fpe.querySelector(SEL_ADD); let sn = pe.getAttribute('href').slice(1); if (sn.includes('/')) continue; let rtid = elm2.getAttribute('href').split('/')[3]; let id = statssn(sn); // screen name -> id, screen_name if (!id) id = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified if (!id) continue; let stats = statsrt(id[0], rtid); // id, rtid -> tid, tca let span, span2, a; let date; let ss; // Temp. if (!fmt) continue; 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.setProperty( 'line-height', getComputedStyle(elm, null).lineHeight, 'important' ); span2 = document.createElement('span'); span2.className = `us-${MYNAME}`; 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; ss = s_mus; fpe.appendChild(span); fpe.appendChild(a); s_mus = ss; } else { ss = s_mus; date = datef(new Date(stats[1]), fmt); if (old.textContent != date) old.textContent = date; // TZ change s_mus = ss; } } } console.log(`${MYNAME}: start.`); dtbs.idl = await load_dtbs('idl'); dtbs.twts = await load_dtbs('twts'); time_r.idl = time_r.twts = Date.now(); tb = TB; ssl = SSL; fmt = FMT; intl = INTL; if (!NOGUI) await initgui(); overrideXHROpen(); observer.observe(document.documentElement, { childList: true, subtree: true }); while (true) { if (s_mus) { s_mus = null; // 初期値がtrue、変更もしない if (tb) await subsbadge(); if (ssl) await addsl(); if (fmt || ssl == 2) await main_track(); } await unique('idl'); await unique('twts'); await new Promise((resolve) => setTimeout(resolve, intl)); // intl は外から非同期に変更する } })(); /* END */