Greasy Fork

Greasy Fork is available in English.

显示转发的日期和时间。

您还可以显示推特蓝徽章、推文来源标签。

当前为 2024-02-21 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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.8.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://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(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;

// Loop interval(ms)
const INTL = 800;
////////////////////////////////////////

const MYNAME = 'sctrt180';
const BTKN =
	'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=' +
	'1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
const EBTKN = encodeURIComponent(BTKN);
const originalXHROpen = XMLHttpRequest.prototype.open;

let s_mus = true;
let observer = new MutationObserver(function (mutations) {
	s_mus = mutations;
});
let tb, ssl, fmt, intl;
let dalg;

function getcv(name) {
	let cookie = document.cookie;
	let obj = {};
	if (!cookie) return [];

	cookie.split(';').forEach(function (rec) {
		let kav = rec.split('=');
		let key, val;

		if (rec.includes('=')) {
			[key, val] = [unescape(kav[0].trim()), unescape(kav[1].trim())];
		} else {
			[key, val] = ['', unescape(kav[0].trim())];
		}

		if (!obj[key]) {
			obj[key] = [val];
		} else {
			obj[key].push(val);
		}
	});

	return obj[name] ?? [];
}

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' +
		'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
		'&nbsp;' +
		'<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' +
		'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
		'<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' +
		'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
		'<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' +
		'&nbsp;&nbsp;&nbsp;&nbsp;' +
		'<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' +
		'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
		'&nbsp;' +
		'<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' +
		'&nbsp;&nbsp;&nbsp;&nbsp;' +
		'<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)&nbsp;' +
		'</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';
		}
	});
}

function datef(date, f) {
	let l = document.documentElement.getAttribute('lang');
	let week_l =
		l == 'ja' ? ['日', '月', '火', '水', '木', '金', '土'] :
		l == 'ko' ? ['일', '월', '화', '수', '목', '금', '토'] :
		l == 'zh-Hant' ? ['日', '一', '二', '三', '四', '五', '六'] :
		l == 'zh' ? ['日', '一', '二', '三', '四', '五', '六'] :
		l == 'ru' ? ['ВС', 'ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ'] :
		l == 'de' ? ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'] :
		l == 'it' ? ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'] :
		l == 'fr' ? ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'] :
		l == 'pt' ? ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'] : // Add your language
			['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 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.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 unique(n) {
	// 要${MYNAME}_idl、${MYNAME}_twts
	let str = await GM.getValue(`${MYNAME}_cntr`);
	if (str === undefined) str = JSON.stringify([0, 0]); // 何も無い時
	let cntr = JSON.parse(str);
	let key, limit;

	if (n == 0) {
		key = `${MYNAME}_idl`;
		limit = 0;
		cntr = [cntr[0] + 1, cntr[1]];
	} else {
		key = `${MYNAME}_twts`;
		limit = 500;
		cntr = [cntr[0], cntr[1] + 1];
	}

	if (cntr[n] > limit) {
		let array = JSON.parse(await GM.getValue(key));
		array = [...new Set(array.map(JSON.stringify))].map(JSON.parse);
		await GM.setValue(key, JSON.stringify(array));

		cntr = n == 0 ? [0, cntr[1]] : [cntr[0], 0];
	}

	await GM.setValue(`${MYNAME}_cntr`, JSON.stringify(cntr));
}

async function touid(sn) {
	let str; // Temp
	str = await GM.getValue(`${MYNAME}_idl`);
	if (str === undefined) {
		// 何も無い時
		str = JSON.stringify([]);
		await GM.setValue(`${MYNAME}_idl`, str);
	}
	let array = JSON.parse(str);
	for (let e of array) {
		if (e[3].toLowerCase() == sn.toLowerCase()) return e;
	}

	let r = await getuid(sn); // 無い時
	if (r) {
		array = array.concat([r]);
		await GM.setValue(`${MYNAME}_idl`, JSON.stringify(array));

		return r.includes(sn) ? r : null;
	}
	console.log(`${MYNAME}: touid:error.`);
	return null; // エラー
}

async function getdetl(twtid) {
	let url =
		'https://twitter.com/i/api/graphql/3HC_X_wzxnMmUBRIn3MWpQ/TweetResultByRestId';

	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 tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
	let path_result = json.data.tweetResult.result;
	let tid = path_result.rest_id;
	if (!tid) {
		path_result = json.data.tweetResult.result.tweet;
		tid = path_result.rest_id;
	}
	let ts = path_result.source;

	if (typeof path_result.legacy !== 'undefined') {
		tuid = path_result.legacy.user_id_str;
		tsn = path_result.core.user_results.result.legacy.screen_name;
		tca = path_result.legacy.created_at;
		if (typeof path_result.legacy.retweeted_status_result !== 'undefined') {
			if (
				typeof path_result.legacy.retweeted_status_result.result.legacy !==
				'undefined'
			) {
				rtuid =
					path_result.legacy.retweeted_status_result.result.legacy.user_id_str;
				rtsn =
					path_result.legacy.retweeted_status_result.result.core.user_results
						.result.legacy.screen_name;
				rtid = path_result.legacy.retweeted_status_result.result.rest_id;
				rtca =
					path_result.legacy.retweeted_status_result.result.legacy.created_at;
				rts = path_result.legacy.retweeted_status_result.result.source;
			} else {
				rtuid =
					path_result.legacy.retweeted_status_result.result.tweet.legacy
						.user_id_str;
				rtsn =
					path_result.legacy.retweeted_status_result.result.tweet.core
						.user_results.result.legacy.screen_name;
				rtid = path_result.legacy.retweeted_status_result.result.tweet.rest_id;
				rtca =
					path_result.legacy.retweeted_status_result.result.tweet.legacy
						.created_at;
				rts = path_result.legacy.retweeted_status_result.result.tweet.source;
			}
		} else {
			rtuid = 'none';
			rtsn = 'none'; // 未確認
			rtid = 'none';
			rtca = 'none';
			rts = 'none';
		}
	} else {
		tuid = 'none';
		tsn = 'none'; // 未確認
		tca = 'none';
		rtuid = 'none';
		rtsn = 'none';
		rtid = 'none';
		rtca = 'none';
		rts = 'none';
	}
	let ret = [tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts];

	if (!tid || !ts) {
		console.log(`${MYNAME}: getdetl:error.`);
		return null; // エラー
	}

	let str = await GM.getValue(`${MYNAME}_twts`);

	if (str === undefined) {
		// 無い時
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify([ret]));
	} else {
		// ある時
		let array = JSON.parse(str);
		array = array.concat([ret]);
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));

		await unique(1);
	}

	return ret;
}

async function gettwts(stat, text, path) {
	if (stat < 200 || stat > 299) {
		console.log(`${MYNAME}: gettwts:error:${stat}.`);
		return null; // エラー
	}

	let n = 0;
	let ret = [];
	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'];

	if(typeof path_instructions[0] === 'undefined') return null;
	for (; path_instructions[n].type != 'TimelineAddEntries'; ++n) if (n > 8) return null;

	let path_entries = path_instructions[n].entries;
	let num = Object.keys(path_entries).length;

	for (let i = 0; i < num; i++) {
		if (!/^tweet-/i.test(path_entries[i].entryId)) continue;

		let path_tid = path_entries[i].content.itemContent.tweet_results.result;
		if (typeof path_tid.tweet !== 'undefined') {
			path_tid = path_tid.tweet;
		}
		let tid = path_tid.rest_id;
		let ts = path_tid.source;
		let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;

		if (typeof path_tid.legacy === 'undefined') {
			// 何も無い
			tuid = 'none';
			tsn = 'none'; // 未確認
			tca = 'none';
			rtuid = 'none';
			rtsn = 'none'; // 未確認
			rtid = 'none';
			rtca = 'none';
			rts = 'none';
			ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
			continue;
		}

		tuid = path_tid.legacy.user_id_str;
		tsn = path_tid.core.user_results.result.legacy.screen_name;
		tca = path_tid.legacy.created_at;

		if (typeof path_tid.legacy.retweeted_status_result === 'undefined') {
			// リツイートでは無い
			rtuid = 'none';
			rtsn = 'none';
			rtid = 'none';
			rtca = 'none';
			rts = 'none';
			ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
			continue;
		}

		let path_rtuid = path_tid.legacy.retweeted_status_result.result;
		if (typeof path_rtuid.tweet !== 'undefined') {
			path_rtuid = path_rtuid.tweet;
		}

		rtuid = path_rtuid.legacy.user_id_str;
		rtsn = path_rtuid.core.user_results.result.legacy.screen_name;
		rtca = path_rtuid.legacy.created_at;
		rts = path_rtuid.source;

		let path_rtid = path_rtuid.edit_control;
		if (typeof path_rtid.edit_control_initial !== 'undefined') {
			path_rtid = path_rtid.edit_control_initial;
		}

		rtid = path_rtid.edit_tweet_ids[5 - path_rtid.edits_remaining];

		ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
	}

	if (!ret) {
		console.log(`${MYNAME}: gettwts:error.`);
		return null; // エラー
	}

	let str = await GM.getValue(`${MYNAME}_twts`);

	if (str === undefined) {
		// 無い時
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(ret));
	} else {
		// ある時
		let array = JSON.parse(str);
		array = array.concat(ret);
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));

		await unique(1);
	}
}

async 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 n = 0;
	let ret = [];

	if (json.errors) {
		console.log(`${MYNAME}: gettwts_d:error:errors.`);
		return null; // エラー
	}

	if(typeof json.data.threaded_conversation_with_injections_v2.instructions[0] === 'undefined')
		return null;
	for (
		;
		json.data.threaded_conversation_with_injections_v2.instructions[n].type !=
			'TimelineAddEntries' &&
		json.data.threaded_conversation_with_injections_v2.instructions[n].type !=
			'TimelineAddToModule';
		++n
	);
	if (
		json.data.threaded_conversation_with_injections_v2.instructions[n].type ==
		'TimelineAddToModule'
	) {
		console.log(`${MYNAME}: gettwts_d:error:ne.`);
		return null; // エラー
	}

	let path_ents =
		json.data.threaded_conversation_with_injections_v2.instructions[n].entries;
	let num = Object.keys(path_ents).length; // 一個しかない?

	for (let i = 0; i < num; i++) {
		if (!/^tweet-/i.test(path_ents[i].entryId)) continue;

		let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
		let path_result = path_ents[i].content.itemContent.tweet_results.result;
		let tid = path_result.rest_id;
		if (!tid) {
			path_result = path_ents[i].content.itemContent.tweet_results.result.tweet;
			tid = path_result.rest_id;
		}
		let ts = path_result.source;

		if (typeof path_result.legacy !== 'undefined') {
			tuid = path_result.legacy.user_id_str;
			tsn = path_result.core.user_results.result.legacy.screen_name;
			tca = path_result.legacy.created_at;
			if (typeof path_result.legacy.retweeted_status_result !== 'undefined') {
				if (
					typeof path_result.legacy.retweeted_status_result.result.legacy !==
					'undefined'
				) {
					rtuid =
						path_result.legacy.retweeted_status_result.result.legacy
							.user_id_str;
					rtsn =
						path_result.legacy.retweeted_status_result.result.core
							.user_results.result.legacy.screen_name;
					rtid = path_result.legacy.retweeted_status_result.result.rest_id;
					rtca =
						path_result.legacy.retweeted_status_result.result.legacy
							.created_at;
					rts = path_result.legacy.retweeted_status_result.result.source;
				} else {
					rtuid =
						path_result.legacy.retweeted_status_result.result.tweet.legacy
							.user_id_str;
					rtsn =
						path_result.legacy.retweeted_status_result.result.tweet.core
							.user_results.result.legacy.screen_name;
					rtid =
						path_result.legacy.retweeted_status_result.result.tweet.rest_id;
					rtca =
						path_result.legacy.retweeted_status_result.result.tweet.legacy
							.created_at;
					rts = path_result.legacy.retweeted_status_result.result.tweet.source;
				}
			} else {
				rtuid = 'none';
				rtsn = 'none'; // 未確認
				rtid = 'none';
				rtca = 'none';
				rts = 'none';
			}
		} else {
			tuid = 'none';
			tsn = 'none'; // 未確認
			tca = 'none';
			rtuid = 'none';
			rtsn = 'none';
			rtid = 'none';
			rtca = 'none';
			rts = 'none';
		}

		ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
	}

	if (!ret) {
		console.log(`${MYNAME}: gettwts_d:error.`);
		return null; // エラー
	}

	let str = await GM.getValue(`${MYNAME}_twts`);

	if (str === undefined) {
		// 無い時
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(ret));
	} else {
		// ある時
		let array = JSON.parse(str);
		array = array.concat(ret);
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));

		await unique(1);
	}
}

async 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_ents = json.data;
	let num = Object.keys(path_ents).length;
	let ret = [];

	for (let i = 0; i < num; i++) {
		// 一個しかない?
		let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
		let path_result = path_ents.tweetResult.result;
		let tid = path_result.rest_id;
		if (!tid) {
			path_result = path_ents.tweetResult.result.tweet;
			tid = path_result.rest_id;
		}
		let ts = path_result.source;

		if (typeof path_result.legacy !== 'undefined') {
			tuid = path_result.legacy.user_id_str;
			tsn = path_result.core.user_results.result.legacy.screen_name;
			tca = path_result.legacy.created_at;
			if (typeof path_result.legacy.retweeted_status_result !== 'undefined') {
				if (
					typeof path_result.legacy.retweeted_status_result.result.legacy !==
					'undefined'
				) {
					rtuid =
						path_result.legacy.retweeted_status_result.result.legacy
							.user_id_str;
					rtsn =
						path_result.legacy.retweeted_status_result.result.core
							.user_results.result.legacy.screen_name;
					rtid = path_result.legacy.retweeted_status_result.result.rest_id;
					rtca =
						path_result.legacy.retweeted_status_result.result.legacy
							.created_at;
					rts = path_result.legacy.retweeted_status_result.result.source;
				} else {
					rtuid =
						path_result.legacy.retweeted_status_result.result.tweet.legacy
							.user_id_str;
					rtsn =
						path_result.legacy.retweeted_status_result.result.tweet.core
							.user_results.result.legacy.screen_name;
					rtid =
						path_result.legacy.retweeted_status_result.result.tweet.rest_id;
					rtca =
						path_result.legacy.retweeted_status_result.result.tweet.legacy
							.created_at;
					rts = path_result.legacy.retweeted_status_result.result.tweet.source;
				}
			} else {
				rtuid = 'none';
				rtsn = 'none'; // 未確認
				rtid = 'none';
				rtca = 'none';
				rts = 'none';
			}
		} else {
			tuid = 'none';
			tsn = 'none'; // 未確認
			tca = 'none';
			rtuid = 'none';
			rtsn = 'none';
			rtid = 'none';
			rtca = 'none';
			rts = 'none';
		}

		ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
	}

	if (!ret) {
		console.log(`${MYNAME}: gettwts_r:error.`);
		return null; // エラー
	}

	let str = await GM.getValue(`${MYNAME}_twts`);

	if (str === undefined) {
		// 無い時
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(ret));
	} else {
		// ある時
		let array = JSON.parse(str);
		array = array.concat(ret);
		await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));

		await unique(1);
	}
}

async function statssn(sn) {
	let str = await GM.getValue(`${MYNAME}_twts`);
	if (!str) return null;

	let snl = sn.toLowerCase();
	let array = JSON.parse(str);

	for (let e of array) {
		let t = e[1].toLowerCase() == snl;
		let rt;
		if (!t) rt = e[6].toLowerCase() == snl;

		if (t || rt) return t ? [e[0], e[1]] : [e[5], e[6]];
	}

	return null;
}

function chklv(id) {
	let ud = +id.substring(0, 2);

	let 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);
}

async function statsrt(uid, rtid) {
	let str = await GM.getValue(`${MYNAME}_twts`);
	if (!str) return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970'];

	let array = JSON.parse(str);

	for (let e of array) {
		if (e[0] == uid && e[7] == rtid) return [e[2], e[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(stat, rawText, ['user', 'result', 'timeline_v2', 'timeline']);
		else if (res == 2) gettwts(stat, rawText, ['home', 'home_timeline_urt']);
		else if (res == 3) gettwts(stat, rawText, ['home', 'home_timeline_urt']);
		else if (res == 4)
			gettwts(stat, rawText, ['list', 'tweets_timeline', 'timeline']);
		else if (res == 5)
			gettwts(stat, rawText, [
				'search_by_raw_query',
				'search_timeline',
				'timeline',
			]);
		else if (res == 6) gettwts_d(stat, rawText);
		else if (res == 7) gettwts_r(stat, rawText);
	}
}

function isTwtsURL(url) {
	if (!url.includes('/graphql/')) return 0;
	else if (url.includes('/UserTweets?')) return 1;
	else if (url.includes('/HomeTimeline')) return 2;
	else if (url.includes('/HomeLatestTimeline')) return 3;
	else if (url.includes('/ListLatestTweetsTimeline?')) return 4;
	else if (url.includes('/SearchTimeline?')) return 5;
	else if (url.includes('/TweetDetail?')) return 6;
	else if (url.includes('/TweetResultByRestId?')) return 7;
	else return 0;
}

function overrideXHROpen() {
	XMLHttpRequest.prototype.open = function () {
		let 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 div[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-1rynq56.r-1wvb978 span'; // バグ?対策 def-ja, def-en
		const SEL =
			'div.css-175oi2r.r-18u37iz.r-1wbh5a2.r-13hce6t span.css-1qaijid.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-1qaijid.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') {
					let str = await GM.getValue(`${MYNAME}_twts`);
					let array = [];
					if (str) array = JSON.parse(str);

					for (let e of array) {
						if (e[2] == c) {
							sn = e[1];
							break;
						} else if (e[7] == c) {
							sn = e[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 = await 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 = await statssn(sn); // screen name -> id, screen_name
		if (!uid) uid = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
		// リツイートした人のuid
	}

	let str = await GM.getValue(`${MYNAME}_twts`); // 出来るだけ遅らせる?
	let array = [];
	if (str) array = JSON.parse(str);

	let i = 2;
	if (document.URL.split('/')[5] != tid) i = 7;

	for (let e of array) {
		if (e[i] == tid) {
			detl = e;
			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 e of array) {
			if (e[0] == uid[0] && e[7] == tid) {
				// リツイートは一つしか無い
				rtsl = `(${e[4].split('<')[1].split('>')[1]})`;
				rthref = e[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-1rynq56.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-1qaijid.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 = await 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 = await 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.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;

			ss = s_mus;

			fpe.appendChild(span);
			fpe.appendChild(span2);
			span2.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.`);

tb = TB;
ssl = SSL;
fmt = FMT;
intl = INTL;

if (!NOGUI) await initgui();
overrideXHROpen();
observer.observe(document.documentElement, { childList: true, subtree: true });

while (1) {
	if (s_mus) {
		s_mus = null; // 初期値がtrue、変更もしない
		if (tb) await subsbadge();
		if (ssl) await addsl();
		if (fmt || ssl == 2) await main_track();
	}

	await new Promise((resolve) => setTimeout(resolve, intl)); // intl は外から非同期に変更する
}


})(); /*  END  */