// ==UserScript==
// @name Show ctime of retweets
// @name:ja リツイート自体の日時を表示
// @name:ko 리트윗 날짜와 시간을 표시합니다.
// @name:zh-CN 显示转发的日期和时间。
// @name:zh-TW 顯示轉發的日期和時間。
// @namespace https://greasyfork.org/en/scripts/462070-show-ctime-of-retweets
// @version 1.11.6
// @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.org/scripts/467232/1197541/Legacy%20verified%2012.js
// @require https://update.greasyfork.org/scripts/467238/1197542/Legacy%20verified%2015.js
// @require https://update.greasyfork.org/scripts/467255/1197539/Legacy%20verified%2019.js
// @require https://update.greasyfork.org/scripts/467256/1197544/Legacy%20verified%2024.js
// @require https://update.greasyfork.org/scripts/467257/1197545/Legacy%20verified%2029.js
// @require https://update.greasyfork.org/scripts/467258/1197546/Legacy%20verified%203.js
// @require https://update.greasyfork.org/scripts/467259/1197547/Legacy%20verified%204.js
// @require https://update.greasyfork.org/scripts/467260/1197548/Legacy%20verified%205.js
// @require https://update.greasyfork.org/scripts/467262/1197549/Legacy%20verified%206.js
// @require https://update.greasyfork.org/scripts/467263/1197550/Legacy%20verified%207.js
// @require https://update.greasyfork.org/scripts/467264/1197553/Legacy%20verified%208.js
// @require https://update.greasyfork.org/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 NO_GUI = 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 show
const SHOW_SL = 2;
// Date formats
// 1. Show
// 0. Not show
const FMT = 1;
// YE(70), YF(1970), YEU(UTC), YFU(UTC), YM(59),
// MO(12), DA(31), WE(Th), HO(12), MI(59) and SE(59)
const FMTS = 'YE/MO/DA(WE) HO:MI:SE';
// Week label
const SUN = '😊';
const MON = '😭';
const TUE = 'Tu';
const WED = 'We';
const THU = 'Th';
const FRI = 'Fr';
const SAT = 'Sa';
// Loop interval(ms)
const INTL = 800;
// ////////////////////////////////// //
const MYNAME = 'sctrt1116';
const BTKN =
'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=' +
'1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
const EBTKN = encodeURIComponent(BTKN);
const SEL_SB_V = 'path[d^="M20.396 11c-.018-.646-.215-1.275-.57-1"]';
const SEL_SB_B = 'path[d^="M16.5 3H2v18h15c3.038 0 5.5-2.46 5.5-5"]';
const SEL_SB_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_SB_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 DATA_SB_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 DATA_SB_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_SB_P_KS = 'form button[data-testid="TypeaheadUser"]'; // キーワード検索
const SEL_SB_P_KSR = 'form div[data-testid="typeaheadRecentSearchesItem"]';
// キーワード検索(最新)
const SEL_SB_P_DS =
'div[aria-labelledby="modal-header"] div.css-175oi2r.r-1wbh5a2.r-dnmrzs.r-1ny4l3l>' +
'div[data-testid^="User-Name"]'; // 表示をカスタマイズする、ノート
const SEL_SB_P_VRRT = 'main section article div[data-testid="card.layoutLarge.detail"]'; // ビデオ引用RT
const SEL_SB_P_T2 = 'main div[data-testid="primaryColumn"] div[data-testid="UserName"]'; // トップ2
const SEL_SB_P_RRT = 'main section article div[data-testid^="User-Name"]'; // 引用RT
const SEL_SB_KS = 'div.css-175oi2r.r-1awozwy.r-18u37iz.r-1wbh5a2 div.css-146c3p1.r-1wvb978 span';
// バグ?対策 def-ja, def-en
const SEL_SB = '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_SB_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
const SEL_AS_END =
'main div[data-testid="primaryColumn"] section article ' +
'div.css-175oi2r.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2';
const SEL_AS_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 = `span.us-${MYNAME}`;
const SEL_MT_END_RT =
'main div[data-testid="primaryColumn"] section article ' +
'span[id].r-8akbws.r-krxsd3.r-n6v787.r-1cwl3u0.r-b88u0q';
const SEL_MT_RTTO = `div[data-testid^="User-Name"] a.css-146c3p1.r-qvutc0:not(.us-${MYNAME})`;
// UTL, HTL
const SEL_MT_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 = `a.us-${MYNAME}`;
let dtbs = {};
let time_ref = {};
let muts = true;
let observer = new MutationObserver(function (mutations) {
muts = mutations;
});
let orig_xhr_open;
let tb, show_sl, fmt, fmts, sun, mon, tue, wed, thu, fri, sat, intl;
async function loadDB(key) {
let str = await GM.getValue(key);
if (str === undefined && key.indexOf('cntr') == 0) { // 何も無い時
return [0, false];
} else if (str === undefined) {
return [];
} else { // ある時
return JSON.parse(str);
}
}
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 makeDalg() {
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 = '420px';
dalg.style.width = '480px';
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';
const HTML =
'<span style="all: initial; font-size: 130%; font-weight: bold; line-height: 210%">' +
`Settings - ${GM.info.script.name} ${GM.info.script.version}` +
'</span><br />\n' +
'<input type="radio" name="fmt" value="1" class="top_r" />' +
'Format:<br />\n' +
' ' +
'<input type="text" name="fmts" size="28" class="top_t" />(*1)<br />\n' +
' ' +
'WE in the format:<br />\n' +
' ' +
'<input type="text" name="sun" size="5" class="btm_t" />' +
'<input type="text" name="mon" size="5" class="btm_t" />' +
'<input type="text" name="tue" size="5" class="btm_t" />' +
'<input type="text" name="wed" size="5" class="btm_t" />' +
'<input type="text" name="thu" size="5" class="btm_t" />' +
'<input type="text" name="fri" size="5" class="btm_t" />' +
'<input type="text" name="sat" size="5" class="btm_t" />(*2)<br />\n' +
'<input type="radio" name="fmt" value="0" class="btm_r" />Not show<br /><br />\n' +
'<input type="checkbox" name="tb" class="top_c" />Substitute TB badge<br /><br />\n' +
'<input type="radio" name="show_sl" value="1" class="top_r" />Tweet source labels only' +
' ' +
'<input type="radio" name="show_sl" value="2" class="top_r" />Tweet and (Retweet)<br />\n' +
'<input type="radio" name="show_sl" value="0" class="btm_r" />Not show<br /><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' +
'<br />\n' +
'<span style="all: initial; font-size: 100%; line-height: 140%">' +
'(*1)Symbols YE, YF, YEU(UTC), YFU(UTC), YM,<br />\n' +
' ' +
'MO, DA, WE, HO, MI and SE are available<br />\n' +
'(*1)Example:<br />\n' +
' ' +
'YE/MO/DA(WE) HO:MI:SE >> 70/12/31(Th) 23:59:59<br />\n' +
' ' +
'WE, MO/DA-YF HO:MI:SE >> Th, 12/31-1970 23:59:59<br />\n' +
' ' +
'YFU/MO/DA HO:MI(UTC) >> 1970/12/31 23:59(UTC)<br />\n' +
' ' +
'MYM/MO/DA HO:MI >> M60/01/01 07:59<br />\n' +
'(*2)Sunday origin' +
'</span><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" /><br /><br />\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 = '4px';
e.style.marginRight = '1px';
e.style.marginTop = '2px';
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.btm_t')) {
e.style.all = 'initial';
e.style.backgroundColor = 'rgb(255, 255, 255)';
e.style.fontFamily = 'monospace';
e.style.fontSize = '100%';
e.style.marginLeft = '4px';
e.style.marginRight = '1px';
e.style.marginTop = '2px';
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 = '26px';
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="fmts"]').value = FMTS;
dalg.querySelector('input[name="sun"]').value = SUN;
dalg.querySelector('input[name="mon"]').value = MON;
dalg.querySelector('input[name="tue"]').value = TUE;
dalg.querySelector('input[name="wed"]').value = WED;
dalg.querySelector('input[name="thu"]').value = THU;
dalg.querySelector('input[name="fri"]').value = FRI;
dalg.querySelector('input[name="sat"]').value = SAT;
dalg.querySelector('input[name="tb"]').checked = TB;
dalg.querySelector(`input[name="show_sl"][value="${SHOW_SL}"]`).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;
}
}
fmts = dalg.querySelector('input[name="fmts"]').value;
sun = dalg.querySelector('input[name="sun"]').value;
mon = dalg.querySelector('input[name="mon"]').value;
tue = dalg.querySelector('input[name="tue"]').value;
wed = dalg.querySelector('input[name="wed"]').value;
thu = dalg.querySelector('input[name="thu"]').value;
fri = dalg.querySelector('input[name="fri"]').value;
sat = dalg.querySelector('input[name="sat"]').value;
tb = dalg.querySelector('input[name="tb"]').checked;
for (let e of dalg.querySelectorAll('input[name="show_sl"]')) {
if (e.checked) {
show_sl = +e.value;
break;
}
}
intl = +dalg.querySelector('input[name="intl"]').value;
await GM.setValue('fmt', fmt);
await GM.setValue('fmts', fmts);
await GM.setValue('sun', sun);
await GM.setValue('mon', mon);
await GM.setValue('tue', tue);
await GM.setValue('wed', wed);
await GM.setValue('thu', thu);
await GM.setValue('fri', fri);
await GM.setValue('sat', sat);
await GM.setValue('tb', tb);
await GM.setValue('show_sl', show_sl);
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('fmts')) === undefined) {
await GM.setValue('fmts', FMTS);
} else {
fmts = await GM.getValue('fmts');
}
if ((await GM.getValue('sun')) === undefined) {
await GM.setValue('sun', SUN);
} else {
sun = await GM.getValue('sun');
}
if ((await GM.getValue('mon')) === undefined) {
await GM.setValue('mon', MON);
} else {
mon = await GM.getValue('mon');
}
if ((await GM.getValue('tue')) === undefined) {
await GM.setValue('tue', TUE);
} else {
tue = await GM.getValue('tue');
}
if ((await GM.getValue('wed')) === undefined) {
await GM.setValue('wed', WED);
} else {
wed = await GM.getValue('wed');
}
if ((await GM.getValue('thu')) === undefined) {
await GM.setValue('thu', THU);
} else {
thu = await GM.getValue('thu');
}
if ((await GM.getValue('fri')) === undefined) {
await GM.setValue('fri', FRI);
} else {
fri = await GM.getValue('fri');
}
if ((await GM.getValue('sat')) === undefined) {
await GM.setValue('sat', SAT);
} else {
sat = await GM.getValue('sat');
}
if ((await GM.getValue('tb')) === undefined) {
await GM.setValue('tb', TB);
} else {
tb = await GM.getValue('tb');
}
if ((await GM.getValue('show_sl')) === undefined) {
await GM.setValue('show_sl', SHOW_SL);
} else {
show_sl = await GM.getValue('show_sl');
}
if ((await GM.getValue('intl')) === undefined) {
await GM.setValue('intl', INTL);
} else {
intl = await GM.getValue('intl');
}
let dalg = makeDalg();
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="fmts"]').value = fmts;
dalg.querySelector('input[name="sun"]').value = sun;
dalg.querySelector('input[name="mon"]').value = mon;
dalg.querySelector('input[name="tue"]').value = tue;
dalg.querySelector('input[name="wed"]').value = wed;
dalg.querySelector('input[name="thu"]').value = thu;
dalg.querySelector('input[name="fri"]').value = fri;
dalg.querySelector('input[name="sat"]').value = sat;
dalg.querySelector('input[name="tb"]').checked = tb;
dalg.querySelector(`input[name="show_sl"][value="${show_sl}"]`).checked = true;
dalg.querySelector('input[name="intl"]').value = intl;
dalg.style.display = 'block';
}
});
}
function fmtDate(fmt, date) {
let datetime;
if (fmts.indexOf('YEU') >= 0 || fmts.indexOf('YFU') >= 0) {
const WN = date.getUTCDay();
datetime = {
'YEU': date.getUTCFullYear().toString().slice(-2),
'YFU': date.getUTCFullYear(),
'MO': ('0' + (date.getUTCMonth() + 1)).slice(-2),
'DA': ('0' + date.getUTCDate()).slice(-2),
'WE':
WN == 0 ? sun :
WN == 1 ? mon :
WN == 2 ? tue :
WN == 3 ? wed :
WN == 4 ? thu :
WN == 5 ? fri :
WN == 6 ? sat :
'?',
'HO': ('0' + date.getUTCHours()).slice(-2),
'MI': ('0' + date.getUTCMinutes()).slice(-2),
'SE': ('0' + date.getUTCSeconds()).slice(-2)
};
} else if (fmts.indexOf('YM') >= 0) {
date.setUTCHours(date.getUTCHours() + 8);
const WN = date.getUTCDay();
datetime = {
'YM': date.getUTCFullYear() - 1911,
'MO': ('0' + (date.getUTCMonth() + 1)).slice(-2),
'DA': ('0' + date.getUTCDate()).slice(-2),
'WE':
WN == 0 ? sun :
WN == 1 ? mon :
WN == 2 ? tue :
WN == 3 ? wed :
WN == 4 ? thu :
WN == 5 ? fri :
WN == 6 ? sat :
'?',
'HO': ('0' + date.getUTCHours()).slice(-2),
'MI': ('0' + date.getUTCMinutes()).slice(-2),
'SE': ('0' + date.getUTCSeconds()).slice(-2)
};
} else {
const WN = date.getDay();
datetime = {
'YE': date.getFullYear().toString().slice(-2),
'YF': date.getFullYear(),
'MO': ('0' + (date.getMonth() + 1)).slice(-2),
'DA': ('0' + date.getDate()).slice(-2),
'WE':
WN == 0 ? sun :
WN == 1 ? mon :
WN == 2 ? tue :
WN == 3 ? wed :
WN == 4 ? thu :
WN == 5 ? fri :
WN == 6 ? sat :
'?',
'HO': ('0' + date.getHours()).slice(-2),
'MI': ('0' + date.getMinutes()).slice(-2),
'SE': ('0' + date.getSeconds()).slice(-2)
};
}
let ret = fmts;
for (let d in datetime) {
ret = ret.replace(d, datetime[d]);
}
return ret;
}
function makeReq(url, eqs, ebtkn, controller) {
let req;
if (getCV('gt').length && !getCV('twid').length) {
req = new Request(`${url}${eqs}`, {
headers: {
'authorization': `Bearer ${ebtkn}`,
'x-guest-token': getCV('gt')[0]
},
cache: 'force-cache',
redirect: 'follow',
signal: controller.signal
});
} else {
req = new Request(`${url}${eqs}`, {
headers: {
'authorization': `Bearer ${ebtkn}`,
'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/Yka-W8dz7RaEuQNkroPkYw/UserByScreenName';
if (/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
url =
'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/' +
'graphql/Yka-W8dz7RaEuQNkroPkYw/UserByScreenName';
}
const QS =
'?variables={' +
`"screen_name":"${s_name}",` +
'"withSafetyModeUserFields":true' +
'}' +
'&features={' +
'"hidden_profile_subscriptions_enabled":true,' +
'"rweb_tipjar_consumption_enabled":true,' +
'"responsive_web_graphql_exclude_directive_enabled":false,' + // true
'"verified_phone_label_enabled":true,' + // false
'"subscriptions_verification_info_is_identity_verified_enabled":true,' +
'"subscriptions_verification_info_verified_since_enabled":true,' +
'"highlights_tweets_tab_ui_enabled":true,' +
'"responsive_web_twitter_article_notes_tab_enabled":true,' +
'"subscriptions_feature_can_gift_premium":true,' +
'"creator_subscriptions_tweet_preview_api_enabled":true,' +
'"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,' +
'"responsive_web_graphql_timeline_navigation_enabled":true' +
'}' +
'&fieldToggles={' +
'"withAuxiliaryUserLabels":false' +
'}';
const EQS = encodeURI(QS);
let controller = new AbortController();
let req = makeReq(url, EQS, 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 saveDtbs(key) {
if (dtbs[`cntr_${key}`][1] && Date.now() - time_ref[key] > 8000) {
dtbs[`cntr_${key}`][1] = false;
time_ref[key] = Date.now();
let limit;
limit = key == 'idl' ? 100 : 50;
if (dtbs[`cntr_${key}`][0] >= limit) {
dtbs[key] = [...new Set(dtbs[key].map(JSON.stringify))].map(JSON.parse);
dtbs[`cntr_${key}`][0] = 0;
}
dtbs[key].push([Date.now()]);
await GM.setValue(key, JSON.stringify(dtbs[key]));
await GM.setValue(`cntr_${key}`, JSON.stringify(dtbs[`cntr_${key}`]));
}
}
async function toUID(s_name) {
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() == s_name.toLowerCase())
return dtbs.idl[i - 1];
}
}
let r = await getUID(s_name);
// 無い時
// screen name -> id, blue, created_at, screen_name, verified
if (r) {
dtbs.idl = dtbs.idl.concat([r]);
dtbs.cntr_idl[0]++;
dtbs.cntr_idl[1] = true;
return r.includes(s_name) ? 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(twt_id) {
const URL = 'https://api.x.com/graphql/sCU6ckfHY0CyJ4HFjPhjtg/TweetResultByRestId';
// 一つだけ、これが基本、自身でfetch
const QS =
'?variables={' +
`"tweetId":"${twt_id}",` +
'"withCommunity":false,' +
'"includePromotedContent":false,' +
'"withVoice":false' +
'}' +
'&features={' +
'"creator_subscriptions_tweet_preview_api_enabled":true,' +
'"communities_web_enable_tweet_community_results_fetch":true,' +
'"c9s_tweet_anatomy_moderator_badge_enabled":true,' +
'"articles_preview_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":true,' +
'"tweet_awards_web_tipping_enabled":false,' +
'"creator_subscriptions_quote_tweet_preview_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,' +
'"rweb_video_timestamps_enabled":true,' +
'"longform_notetweets_rich_text_read_enabled":true,' +
'"longform_notetweets_inline_media_enabled":true,' +
'"rweb_tipjar_consumption_enabled":true,' +
'"responsive_web_graphql_exclude_directive_enabled":false,' + // true
'"verified_phone_label_enabled":true,' + // 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":true,' +
'"withArticlePlainText":false,' +
'"withGrokAnalyze":false,' +
'"withDisallowedReplyControls":false' +
'}';
const EQS = encodeURI(QS);
let controller = new AbortController();
let req = makeReq(URL, EQS, EBTKN, controller);
let res = {};
try {
setTimeout(function () { controller.abort(); }, 60000);
res = await fetch(req);
if (!res.ok) {
console.log(`${MYNAME}: getDetl:notok:${res.ok}.`);
return null;
} // 失敗なら空で終わり
} catch (err) {
console.log(`${MYNAME}: getDetl: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);
dtbs.cntr_twts[0]++;
dtbs.cntr_twts[1] = true;
}
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 getTwtsR(stat, text) {
if (stat < 200 || stat > 299) {
console.log(`${MYNAME}: getTwtsR: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);
dtbs.cntr_twts[0]++;
dtbs.cntr_twts[1] = true;
}
}
function getTwtsD(stat, text) {
if (stat < 200 || stat > 299) {
console.log(`${MYNAME}: getTwtsD:error:${stat}.`);
return null; // エラー
}
let json = JSON.parse(text);
let ret = [];
if (json.errors) {
console.log(`${MYNAME}: getTwtsD: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);
dtbs.cntr_twts[0]++;
dtbs.cntr_twts[1] = true;
}
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);
dtbs.cntr_twts[0]++;
dtbs.cntr_twts[1] = true;
}
function statsSN(s_name) {
let sn_l = s_name.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() == sn_l;
let rt;
if (!t) rt = dtbs.twts[i - 1][6].toLowerCase() == sn_l;
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(u_id, rt_id) {
for (let i = dtbs.twts.length; i > 0; i--) { // 下から
if (typeof dtbs.twts[i - 1][1] === 'undefined') continue;
if (dtbs.twts[i - 1][0] == u_id && dtbs.twts[i - 1][7] == rt_id)
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 twtXHRStateHandler({ target: xhr }, res) {
if (xhr.readyState === 4) {
const STAT = xhr.status;
const RAW_TEXT = xhr.responseText;
if (res == 1)
getTwtsR(STAT, RAW_TEXT);
else if (res == 2)
getTwtsD(STAT, RAW_TEXT);
else if (res == 3)
getTwts(STAT, RAW_TEXT, ['user', 'result', 'timeline_v2', 'timeline']);
else if (res == 4)
getTwts(STAT, RAW_TEXT, ['home', 'home_timeline_urt']);
else if (res == 5)
getTwts(STAT, RAW_TEXT, ['home', 'home_timeline_urt']);
else if (res == 6)
getTwts(STAT, RAW_TEXT, ['list', 'tweets_timeline', 'timeline']);
else if (res == 7)
getTwts(STAT, RAW_TEXT, ['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() {
orig_xhr_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
const RES = isTwtsURL(arguments[1]);
if (RES) {
this.addEventListener(
'readystatechange',
function (evt) { twtXHRStateHandler(evt, RES); }
);
}
return orig_xhr_open.apply(this, arguments);
};
console.log(`${MYNAME}: XMLHttpRequest.open overriden.`);
}
async function subsBadge() {
let elms = document.querySelectorAll(`${SEL_SB_V}, ${SEL_SB_B}`);
for (let e of elms) {
let spe = e.parentNode.parentNode;
let tpe = e.parentNode.parentNode.parentNode;
let sn;
let ss; // Temp.
if (tpe.querySelector(`${SEL_SB_E}, ${SEL_SB_E2}`)) continue;
if (spe.style.color == 'rgb(232, 134, 143)') {
ss = muts;
spe.style.color = 'rgb(29, 155, 240)';
muts = 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_SB_P_KS);
let sne;
if (xpe) sne = xpe.querySelector(SEL_SB_KS);
if (sne) sn = sne.textContent.split('@')[1];
}
if (!sn) {
let xpe = e.closest(SEL_SB_P_KSR);
let sne;
if (xpe) sne = xpe.querySelector(SEL_SB_KS);
if (sne) sn = sne.textContent.split('@')[1];
}
if (!sn) {
let xpe = e.closest(SEL_SB_P_DS);
let sne;
if (xpe) sne = xpe.querySelector(SEL_SB);
if (sne) sn = sne.textContent.split('@')[1];
}
if (!sn) {
let xpe = e.closest(SEL_SB_P_VRRT);
let sne;
if (xpe) sne = xpe.querySelector(SEL_SB);
if (sne) sn = sne.textContent.split('@')[1];
}
if (!sn) {
let xpe = e.closest(SEL_SB_P_T2);
let sne;
if (xpe) sne = xpe.querySelector(SEL_SB_2);
if (sne) sn = sne.textContent.split('@')[1];
}
if (!sn) {
let xpe = e.closest(SEL_SB_P_RRT);
let sne;
if (xpe) sne = xpe.querySelector(SEL_SB);
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 = muts;
spe.style.color = 'rgb(232, 134, 143)';
muts = ss;
continue;
}
let sp = [
'', 'compose', 'explore', 'home', 'i',
'login', 'messages', 'notifications', 'search', 'search-advanced',
'settings'
];
if (sp.includes(sn.toLowerCase())) {
ss = muts;
spe.style.color = 'rgb(232, 134, 143)';
muts = 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 = muts;
if (r === true) {
e.setAttribute('d', DATA_SB_V);
} else if (r === false) {
e.setAttribute('d', DATA_SB_B);
} else {
spe.style.color = 'rgb(232, 134, 143)';
}
muts = ss;
}
}
async function addSL() {
let uid, tid, tsl, rtsl, thref, rthref, detl;
let ca, span, span2, a, a2;
let elm_end = document.querySelector(SEL_AS_END);
let elm_end_rt = document.querySelector(SEL_AS_END_RT);
if (!elm_end) return;
let old = elm_end.querySelectorAll(SEL_ADD_SPAN);
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 (show_sl != 1 && elm_end_rt && 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 = muts;
elm_end.appendChild(span);
elm_end.appendChild(span2);
span2.appendChild(a);
if (show_sl == 2 && elm_end_rt) span2.appendChild(a2);
muts = ss;
}
async function mainTrack() {
let elms = document.querySelectorAll(SEL_MT_END_RT);
for (let elm of elms) {
let pe = elm.parentNode;
let fpe = elm.parentNode.parentNode.parentNode.parentNode;
let xpe = elm.closest('article');
let elm2 = xpe.querySelector(SEL_MT_RTTO);
if (!elm2) elm2 = xpe.querySelector(SEL_MT_RTTO_2);
let old = fpe.querySelector(SEL_ADD_A);
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 = fmtDate(fmt, new Date(stats[1]));
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 = muts;
fpe.appendChild(span);
fpe.appendChild(a);
muts = ss;
} else {
ss = muts;
date = fmtDate(fmt, new Date(stats[1]));
if (old.textContent != date) old.textContent = date; // TZ change
muts = ss;
}
}
}
console.log(`${MYNAME}: start.`);
dtbs.idl = await loadDB('idl');
dtbs.twts = await loadDB('twts');
dtbs.cntr_idl = await loadDB('cntr_idl');
dtbs.cntr_twts = await loadDB('cntr_twts');
time_ref.idl = time_ref.twts = Date.now();
tb = TB;
show_sl = SHOW_SL;
fmt = FMT;
fmts = FMTS;
sun = SUN;
mon = MON;
tue = TUE;
wed = WED;
thu = THU;
fri = FRI;
sat = SAT;
intl = INTL;
if (!NO_GUI) await initGUI();
overrideXHROpen();
observer.observe(document.documentElement, { childList: true, subtree: true });
while (true) {
if (muts) {
muts = null; // 初期値がtrue、変更もしない
if (tb) await subsBadge();
if (show_sl) await addSL();
if (fmt || show_sl == 2) await mainTrack();
}
await saveDtbs('idl');
await saveDtbs('twts');
await new Promise(function (resv) { setTimeout(resv, intl); });
// intl は外から非同期に変更する
}
})(); /* END */