顯示轉發的日期和時間。

您還可以顯示推特藍徽章、推文來源標籤。

目前為 2024-05-17 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Show ctime of retweets
  3. // @name:ja リツイート自体の日時を表示
  4. // @name:ko 리트윗 날짜와 시간을 표시합니다.
  5. // @name:zh-CN 显示转发的日期和时间。
  6. // @name:zh-TW 顯示轉發的日期和時間。
  7. // @namespace https://gf.qytechs.cn/en/scripts/462070-show-ctime-of-retweets
  8. // @version 1.9.0
  9. // @description Also shows Twitter Blue badges and tweet source labels.
  10. // @description:ja Twitter Blue バッジ、ツイートソースラベルも表示できます。
  11. // @description:ko Twitter Blue 배지, 트윗 소스 라벨도 표시할 수 있습니다.
  12. // @description:zh-CN 您还可以显示推特蓝徽章、推文来源标签。
  13. // @description:zh-TW 您還可以顯示推特藍徽章、推文來源標籤。
  14. // @author AeamaN
  15. // @contributionURL bitcoin:1DC6uWJWzzwU3iRJDXhUquv6QAYaRvtfFJ
  16. // @match https://twitter.com/*
  17. // @match https://mobile.twitter.com/*
  18. // @match https://twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/*
  19. // @match https://mobile.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/*
  20. // @match https://x.com/*
  21. // @match https://mobile.x.com/*
  22. // @require https://update.gf.qytechs.cn/scripts/467232/1197541/Legacy%20verified%2012.js
  23. // @require https://update.gf.qytechs.cn/scripts/467238/1197542/Legacy%20verified%2015.js
  24. // @require https://update.gf.qytechs.cn/scripts/467255/1197539/Legacy%20verified%2019.js
  25. // @require https://update.gf.qytechs.cn/scripts/467256/1197544/Legacy%20verified%2024.js
  26. // @require https://update.gf.qytechs.cn/scripts/467257/1197545/Legacy%20verified%2029.js
  27. // @require https://update.gf.qytechs.cn/scripts/467258/1197546/Legacy%20verified%203.js
  28. // @require https://update.gf.qytechs.cn/scripts/467259/1197547/Legacy%20verified%204.js
  29. // @require https://update.gf.qytechs.cn/scripts/467260/1197548/Legacy%20verified%205.js
  30. // @require https://update.gf.qytechs.cn/scripts/467262/1197549/Legacy%20verified%206.js
  31. // @require https://update.gf.qytechs.cn/scripts/467263/1197550/Legacy%20verified%207.js
  32. // @require https://update.gf.qytechs.cn/scripts/467264/1197553/Legacy%20verified%208.js
  33. // @require https://update.gf.qytechs.cn/scripts/467265/1197554/Legacy%20verified%209.js
  34. // @grant GM.getValue
  35. // @grant GM.registerMenuCommand
  36. // @grant GM.setValue
  37. // @run-at document-body
  38. // ==/UserScript==
  39.  
  40. // ES2017(ES8) or later.
  41. // ES2017(ES8) 以降が必要です。
  42.  
  43. (async function() { /* START */
  44.  
  45.  
  46. 'use strict';
  47.  
  48. /////////////// Settings ///////////////
  49. // No GUI Settings
  50. // Default values are used
  51. const NOGUI = false;
  52. ////////////////////////////////////////
  53.  
  54. //////////// Default valuse ////////////
  55. // Substitute TB badge
  56. // when TB without legacy verification
  57. const TB = false;
  58.  
  59. // Show source labels
  60. // 1. Tweet only
  61. // 2. Tweet and (Retweet)
  62. // 0. Not shown
  63. const SSL = 2;
  64.  
  65. // Date formats
  66. // 1. 31.12.70 23:59
  67. // 2. 31.12.70 23:59:59
  68. // 3. 31.12.70(Th) 23:59
  69. // 4. 31.12.70(Th) 23:59:59
  70. //
  71. // 5. 70/12/31 23:59
  72. // 6. 70/12/31 23:59:59
  73. // 7. 70/12/31(Th) 23:59
  74. // 8. 70/12/31(Th) 23:59:59 [ye/mo/da(we) ho:mi:se]
  75. //
  76. // 9. 70-12/31 23:59
  77. // 10. 70-12/31 23:59'59
  78. // 11. 70-12/31(Th) 23:59
  79. // 12. 70-12/31(Th) 23:59'59
  80. //
  81. // 13. M59-12-31 23:59
  82. // 14. M59-12-31 23:59:59
  83. // 15. M59-12-31(Th) 23:59
  84. // 16. M59-12-31(Th) 23:59:59
  85. //
  86. // 0. Not Shown
  87. const FMT = 7;
  88.  
  89. // Loop interval(ms)
  90. const INTL = 800;
  91. ////////////////////////////////////////
  92.  
  93. const MYNAME = 'sctrt190';
  94. const BTKN =
  95. 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=' +
  96. '1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
  97. const EBTKN = encodeURIComponent(BTKN);
  98. const originalXHROpen = XMLHttpRequest.prototype.open;
  99.  
  100. let s_mus = true;
  101. let observer = new MutationObserver(function (mutations) {
  102. s_mus = mutations;
  103. });
  104. let tb, ssl, fmt, intl;
  105. let dalg;
  106.  
  107. function getcv(name) {
  108. let cookie = document.cookie;
  109. let obj = {};
  110. if (!cookie) return [];
  111.  
  112. cookie.split(';').forEach(function (rec) {
  113. let kav = rec.split('=');
  114. let key, val;
  115.  
  116. if (rec.includes('=')) {
  117. [key, val] = [unescape(kav[0].trim()), unescape(kav[1].trim())];
  118. } else {
  119. [key, val] = ['', unescape(kav[0].trim())];
  120. }
  121.  
  122. if (!obj[key]) {
  123. obj[key] = [val];
  124. } else {
  125. obj[key].push(val);
  126. }
  127. });
  128.  
  129. return obj[name] ?? [];
  130. }
  131.  
  132. function makeDialog() {
  133. let dalg = document.createElement('div');
  134.  
  135. dalg.className = `us-${MYNAME}`;
  136.  
  137. dalg.style.all = 'initial';
  138. dalg.style.backgroundColor = 'rgb(235, 235, 235)';
  139. dalg.style.border = '3px outset';
  140. dalg.style.borderRadius = '1%';
  141. dalg.style.display = 'none';
  142. dalg.style.fontFamily = 'monospace';
  143. dalg.style.fontSize = '12px';
  144. dalg.style.height = '360px';
  145. dalg.style.width = '400px';
  146. dalg.style.paddingLeft = '2px';
  147. dalg.style.paddingRight = '2px';
  148. dalg.style.position = 'fixed';
  149. dalg.style.right = '8px';
  150. dalg.style.top = '8px';
  151. dalg.style.zIndex = '2147483647';
  152.  
  153. let html =
  154. '<span style="all: initial; font-size: 120%; line-height: 210%">' +
  155. `${GM.info.script.name} ${GM.info.script.version} Settings` +
  156. '</span><br />\n' +
  157.  
  158. '<input type="radio" name="fmt" value="1" class="top_r" />31.12.70 23:59' +
  159. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  160. '<input type="radio" name="fmt" value="2" class="top_r" />31.12.70 23:59:59<br />\n' +
  161. '<input type="radio" name="fmt" value="3" class="mid_r" />31.12.70(Th) 23:59' +
  162. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  163. '<input type="radio" name="fmt" value="4" class="mid_r" />31.12.70(Th) 23:59:59<br />\n' +
  164. '<input type="radio" name="fmt" value="5" class="mid_r" />70/12/31 23:59' +
  165. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  166. '<input type="radio" name="fmt" value="6" class="mid_r" />70/12/31 23:59:59<br />\n' +
  167. '<input type="radio" name="fmt" value="7" class="mid_r" />70/12/31(Th) 23:59' +
  168. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  169. '<input type="radio" name="fmt" value="8" class="mid_r" />70/12/31(Th) 23:59:59<br />\n' +
  170. '<input type="radio" name="fmt" value="9" class="mid_r" />70-12/31 23:59' +
  171. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  172. '<input type="radio" name="fmt" value="10" class="mid_r" />70-12/31 23:59\'59<br />\n' +
  173. '<input type="radio" name="fmt" value="11" class="mid_r" />70-12/31(Th) 23:59' +
  174. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  175. '<input type="radio" name="fmt" value="12" class="mid_r" />70-12/31(Th) 23:59\'59<br />\n' +
  176. '<input type="radio" name="fmt" value="13" class="mid_r" />M59-12-31 23:59' +
  177. '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
  178. '<input type="radio" name="fmt" value="14" class="mid_r" />M59-12-31 23:59:59<br />\n' +
  179. '<input type="radio" name="fmt" value="15" class="mid_r" />M59-12-31(Th) 23:59' +
  180. '&nbsp;&nbsp;&nbsp;&nbsp;' +
  181. '<input type="radio" name="fmt" value="16" class="mid_r" />M59-12-31(Th) 23:59:59<br />\n' +
  182. '<input type="radio" name="fmt" value="0" class="btm_r" />Not Shown<br />\n' +
  183.  
  184. '<input type="checkbox" name="tb" class="top_c" />Substitute TB badge<br />\n' +
  185.  
  186. '<input type="radio" name="ssl" value="1" class="top_r" />Tweet source labels only' +
  187. '&nbsp;&nbsp;&nbsp;&nbsp;' +
  188. '<input type="radio" name="ssl" value="2" class="top_r" />Tweet and (Retweet)<br />\n' +
  189. '<input type="radio" name="ssl" value="0" class="btm_r" />Not shown<br />\n' +
  190.  
  191. '<span style="all: initial; font-size: 100%">' +
  192. 'Loop interval(ms)&nbsp;' +
  193. '</span><input type="text" name="intl" size="10" class="top_t" /><br />\n' +
  194.  
  195. '<input type="button" class="top_b" value="Cancel" />\n' +
  196. '<input type="button" class="top_b" value="Set default" />\n' +
  197. '<input type="button" class="top_b" value="Save & Close" />\n';
  198.  
  199. dalg.innerHTML = html;
  200.  
  201. for (let e of dalg.querySelectorAll('input.top_r')) {
  202. e.style.all = 'initial';
  203. e.style.appearance = 'auto';
  204. e.style.marginRight = '1px';
  205. e.style.marginTop = '1px';
  206. }
  207. for (let e of dalg.querySelectorAll('input.mid_r, input.btm_r')) {
  208. e.style.all = 'initial';
  209. e.style.appearance = 'auto';
  210. e.style.marginRight = '1px';
  211. e.style.marginTop = '1px';
  212. }
  213. for (let e of dalg.querySelectorAll('input.top_c')) {
  214. e.style.all = 'initial';
  215. e.style.appearance = 'auto';
  216. e.style.marginRight = '1px';
  217. e.style.marginTop = '1px';
  218. }
  219. for (let e of dalg.querySelectorAll('input.top_t')) {
  220. e.style.all = 'initial';
  221. e.style.backgroundColor = 'rgb(255, 255, 255)';
  222. e.style.fontFamily = 'monospace';
  223. e.style.fontSize = '100%';
  224. e.style.marginLeft = '1px';
  225. e.style.marginRight = '1px';
  226. e.style.marginTop = '8px';
  227. e.style.marginBottom = '0px';
  228. e.style.paddingLeft = '1px';
  229. e.style.paddingRight = '1px';
  230. e.style.paddingTop = '1px';
  231. e.style.paddingBottom = '1px';
  232. }
  233. for (let e of dalg.querySelectorAll('input.top_b')) {
  234. e.style.all = 'initial';
  235. e.style.backgroundColor = 'rgb(190, 190, 190)';
  236. e.style.borderRadius = '10%';
  237. e.style.cursor = 'default';
  238. e.style.fontSize = '110%';
  239. e.style.marginTop = '10px';
  240. e.style.marginBottom = '0px';
  241. e.style.paddingTop = '6px';
  242. e.style.paddingBottom = '6px';
  243. e.style.textAlign = 'center';
  244. e.style.width = '90px';
  245. }
  246.  
  247. return dalg;
  248. }
  249.  
  250. function makeFunc(dalg) {
  251. dalg.addEventListener(
  252. 'click',
  253. function (event) {
  254. event.stopPropagation();
  255. },
  256. false,
  257. );
  258.  
  259. dalg.querySelector('input[value="Cancel"]').addEventListener(
  260. 'click',
  261. function () {
  262. dalg.style.display = 'none';
  263. },
  264. false,
  265. );
  266. dalg.querySelector('input[value="Cancel"]').addEventListener(
  267. 'mouseenter',
  268. function (event) {
  269. event.target.style.backgroundColor = 'rgb(170, 170, 170)';
  270. },
  271. false,
  272. );
  273. dalg.querySelector('input[value="Cancel"]').addEventListener(
  274. 'mouseleave',
  275. function (event) {
  276. event.target.style.backgroundColor = 'rgb(190, 190, 190)';
  277. },
  278. false,
  279. );
  280.  
  281. dalg.querySelector('input[value="Set default"]').addEventListener(
  282. 'click',
  283. function () {
  284. dalg.querySelector(`input[name="fmt"][value="${FMT}"]`).checked = true;
  285. dalg.querySelector('input[name="tb"]').checked = TB;
  286. dalg.querySelector(`input[name="ssl"][value="${SSL}"]`).checked = true;
  287. dalg.querySelector('input[name="intl"]').value = INTL;
  288. },
  289. false,
  290. );
  291. dalg.querySelector('input[value="Set default"]').addEventListener(
  292. 'mouseenter',
  293. function (event) {
  294. event.target.style.backgroundColor = 'rgb(170, 170, 170)';
  295. },
  296. false,
  297. );
  298. dalg.querySelector('input[value="Set default"]').addEventListener(
  299. 'mouseleave',
  300. function (event) {
  301. event.target.style.backgroundColor = 'rgb(190, 190, 190)';
  302. },
  303. false,
  304. );
  305.  
  306. dalg.querySelector('input[value="Save & Close"]').addEventListener(
  307. 'click',
  308. async function () {
  309. for (let e of dalg.querySelectorAll('input[name="fmt"]')) {
  310. if (e.checked) {
  311. fmt = +e.value;
  312. break;
  313. }
  314. }
  315. tb = dalg.querySelector('input[name="tb"]').checked;
  316. for (let e of dalg.querySelectorAll('input[name="ssl"]')) {
  317. if (e.checked) {
  318. ssl = +e.value;
  319. break;
  320. }
  321. }
  322. intl = +dalg.querySelector('input[name="intl"]').value;
  323.  
  324. await GM.setValue('fmt', fmt);
  325. await GM.setValue('tb', tb);
  326. await GM.setValue('ssl', ssl);
  327. await GM.setValue('intl', intl);
  328.  
  329. dalg.style.display = 'none';
  330. },
  331. false,
  332. );
  333. dalg.querySelector('input[value="Save & Close"]').addEventListener(
  334. 'mouseenter',
  335. function (event) {
  336. event.target.style.backgroundColor = 'rgb(170, 170, 170)';
  337. },
  338. false,
  339. );
  340. dalg.querySelector('input[value="Save & Close"]').addEventListener(
  341. 'mouseleave',
  342. function (event) {
  343. event.target.style.backgroundColor = 'rgb(190, 190, 190)';
  344. },
  345. false,
  346. );
  347. }
  348.  
  349. async function initgui() {
  350. if ((await GM.getValue('fmt')) === undefined) {
  351. await GM.setValue('fmt', FMT);
  352. } else {
  353. fmt = await GM.getValue('fmt');
  354. }
  355. if ((await GM.getValue('tb')) === undefined) {
  356. await GM.setValue('tb', TB);
  357. } else {
  358. tb = await GM.getValue('tb');
  359. }
  360. if ((await GM.getValue('ssl')) === undefined) {
  361. await GM.setValue('ssl', SSL);
  362. } else {
  363. ssl = await GM.getValue('ssl');
  364. }
  365. if ((await GM.getValue('intl')) === undefined) {
  366. await GM.setValue('intl', INTL);
  367. } else {
  368. intl = await GM.getValue('intl');
  369. }
  370.  
  371. dalg = makeDialog();
  372. makeFunc(dalg);
  373. document.body.appendChild(dalg);
  374.  
  375. GM.registerMenuCommand('Settings', function () {
  376. if (dalg.style.display == 'none') {
  377. dalg.querySelector(`input[name="fmt"][value="${fmt}"]`).checked = true;
  378. dalg.querySelector('input[name="tb"]').checked = tb;
  379. dalg.querySelector(`input[name="ssl"][value="${ssl}"]`).checked = true;
  380. dalg.querySelector('input[name="intl"]').value = intl;
  381.  
  382. dalg.style.display = 'block';
  383. }
  384. });
  385. }
  386.  
  387. function datef(date, f) {
  388. let l = document.documentElement.getAttribute('lang');
  389. let week_l =
  390. l == 'ja' ? ['日', '月', '火', '水', '木', '金', '土'] :
  391. l == 'ko' ? ['일', '월', '화', '수', '목', '금', '토'] :
  392. l == 'zh-Hant' ? ['日', '一', '二', '三', '四', '五', '六'] :
  393. l == 'zh' ? ['日', '一', '二', '三', '四', '五', '六'] :
  394. l == 'ru' ? ['ВС', 'ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ'] :
  395. l == 'de' ? ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'] :
  396. l == 'it' ? ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'] :
  397. l == 'fr' ? ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'] :
  398. l == 'pt' ? ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'] : // Add your language
  399. ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
  400.  
  401. let ye, ym, mo, da, we, ho, mi, se;
  402. ye = date.getFullYear().toString().slice(-2);
  403. ym = date.getFullYear() - 1911;
  404. mo = ('0' + (date.getMonth() + 1)).slice(-2);
  405. da = ('0' + date.getDate()).slice(-2);
  406. we = week_l[date.getDay()];
  407. ho = ('0' + date.getHours()).slice(-2);
  408. mi = ('0' + date.getMinutes()).slice(-2);
  409. se = ('0' + date.getSeconds()).slice(-2);
  410.  
  411. return f == 1 ? `${da}.${mo}.${ye} ${ho}:${mi}` :
  412. f == 2 ? `${da}.${mo}.${ye} ${ho}:${mi}:${se}` :
  413. f == 3 ? `${da}.${mo}.${ye}(${we}) ${ho}:${mi}` :
  414. f == 4 ? `${da}.${mo}.${ye}(${we}) ${ho}:${mi}:${se}` :
  415. f == 5 ? `${ye}/${mo}/${da} ${ho}:${mi}` :
  416. f == 6 ? `${ye}/${mo}/${da} ${ho}:${mi}:${se}` :
  417. f == 7 ? `${ye}/${mo}/${da}(${we}) ${ho}:${mi}` :
  418. f == 8 ? `${ye}/${mo}/${da}(${we}) ${ho}:${mi}:${se}` :
  419. f == 9 ? `${ye}-${mo}/${da} ${ho}:${mi}` :
  420. f == 10 ? `${ye}-${mo}/${da} ${ho}:${mi}'${se}` :
  421. f == 11 ? `${ye}-${mo}/${da}(${we}) ${ho}:${mi}` :
  422. f == 12 ? `${ye}-${mo}/${da}(${we}) ${ho}:${mi}'${se}` :
  423. f == 13 ? `M${ym}-${mo}-${da} ${ho}:${mi}` :
  424. f == 14 ? `M${ym}-${mo}-${da} ${ho}:${mi}:${se}` :
  425. f == 15 ? `M${ym}-${mo}-${da}(${we}) ${ho}:${mi}` :
  426. f == 16 ? `M${ym}-${mo}-${da}(${we}) ${ho}:${mi}:${se}` :
  427. `${ye}/${mo}/${da}(${we}) ${ho}:${mi}:${se}`;
  428. }
  429.  
  430. function mkreq(url, eq, ebt, controller) {
  431. let req;
  432.  
  433. if (getcv('gt').length && !getcv('twid').length) {
  434. req = new Request(`${url}${eq}`, {
  435. headers: {
  436. authorization: `Bearer ${ebt}`,
  437. 'x-csrf-token': getcv('ct0')[0],
  438. 'x-guest-token': getcv('gt')[0],
  439. },
  440. cache: 'force-cache',
  441. redirect: 'follow',
  442. signal: controller.signal,
  443. });
  444. } else {
  445. req = new Request(`${url}${eq}`, {
  446. headers: {
  447. authorization: `Bearer ${ebt}`,
  448. 'x-csrf-token': getcv('ct0')[0],
  449. 'x-twitter-auth-type': 'OAuth2Session',
  450. },
  451. cache: 'force-cache',
  452. redirect: 'follow',
  453. mode: 'cors',
  454. credentials: 'include',
  455. signal: controller.signal,
  456. });
  457. }
  458.  
  459. return req;
  460. }
  461.  
  462. async function getuid(s_name) {
  463. let url = 'https://api.x.com/graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName';
  464. if (/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
  465. url =
  466. 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/' +
  467. 'graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName';
  468. }
  469. let q =
  470. '?variables={' +
  471. `"screen_name":"${s_name}",` +
  472. '"withSafetyModeUserFields":true,' +
  473. '"withSuperFollowsUserFields":true' +
  474. '}' +
  475. '&features={' +
  476. '"responsive_web_twitter_blue_verified_badge_is_enabled":true,' +
  477. '"responsive_web_graphql_exclude_directive_enabled":false,' + // false
  478. '"verified_phone_label_enabled":true,' + // false
  479. '"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,' +
  480. '"responsive_web_graphql_timeline_navigation_enabled":true' +
  481. '}';
  482. let eq = encodeURI(q);
  483. let controller = new AbortController();
  484. let req = mkreq(url, eq, EBTKN, controller);
  485. let res = {};
  486.  
  487. try {
  488. setTimeout(function () {
  489. controller.abort();
  490. }, 60000);
  491. res = await fetch(req);
  492. if (!res.ok) {
  493. console.log(`${MYNAME}: getuid:notok:${res.ok}.`);
  494. return null;
  495. } // 失敗なら空で終わり
  496. } catch (err) {
  497. console.log(`${MYNAME}: getuid:error:${err}.`);
  498. return null; // 失敗なら空で終わり
  499. }
  500.  
  501. let json = JSON.parse(await res.text());
  502.  
  503. let id, bl, ca, sn, vf;
  504. id = json.data.user.result.rest_id;
  505. bl = json.data.user.result.is_blue_verified;
  506. ca = json.data.user.result.legacy.created_at;
  507. sn = json.data.user.result.legacy.screen_name;
  508. vf = json.data.user.result.legacy.verified;
  509.  
  510. return [id, bl, ca, sn, vf];
  511. }
  512.  
  513. async function unique(n) {
  514. // 要${MYNAME}_idl、${MYNAME}_twts
  515. let str = await GM.getValue(`${MYNAME}_cntr`);
  516. if (str === undefined) str = JSON.stringify([0, 0]); // 何も無い時
  517. let cntr = JSON.parse(str);
  518. let key, limit;
  519.  
  520. if (n == 0) {
  521. key = `${MYNAME}_idl`;
  522. limit = 0;
  523. cntr = [cntr[0] + 1, cntr[1]];
  524. } else {
  525. key = `${MYNAME}_twts`;
  526. limit = 500;
  527. cntr = [cntr[0], cntr[1] + 1];
  528. }
  529.  
  530. if (cntr[n] > limit) {
  531. let array = JSON.parse(await GM.getValue(key));
  532. array = [...new Set(array.map(JSON.stringify))].map(JSON.parse);
  533. await GM.setValue(key, JSON.stringify(array));
  534.  
  535. cntr = n == 0 ? [0, cntr[1]] : [cntr[0], 0];
  536. }
  537.  
  538. await GM.setValue(`${MYNAME}_cntr`, JSON.stringify(cntr));
  539. }
  540.  
  541. async function touid(sn) {
  542. let str; // Temp
  543. str = await GM.getValue(`${MYNAME}_idl`);
  544. if (str === undefined) {
  545. // 何も無い時
  546. str = JSON.stringify([]);
  547. await GM.setValue(`${MYNAME}_idl`, str);
  548. }
  549. let array = JSON.parse(str);
  550. for (let e of array) {
  551. if (e[3].toLowerCase() == sn.toLowerCase()) return e;
  552. }
  553.  
  554. let r = await getuid(sn); // 無い時
  555. if (r) {
  556. array = array.concat([r]);
  557. await GM.setValue(`${MYNAME}_idl`, JSON.stringify(array));
  558.  
  559. return r.includes(sn) ? r : null;
  560. }
  561. console.log(`${MYNAME}: touid:error.`);
  562. return null; // エラー
  563. }
  564.  
  565. async function getdetl(twtid) {
  566. let url =
  567. 'https://x.com/i/api/graphql/3HC_X_wzxnMmUBRIn3MWpQ/TweetResultByRestId';
  568.  
  569. let q =
  570. '?variables={' +
  571. `"tweetId":"${twtid}",` +
  572. '"withCommunity":false,' +
  573. '"includePromotedContent":false,' +
  574. '"withVoice":false' +
  575. '}' +
  576. '&features={' +
  577. '"creator_subscriptions_tweet_preview_api_enabled":true,' +
  578. '"tweetypie_unmention_optimization_enabled":true,' +
  579. '"responsive_web_edit_tweet_api_enabled":true,' +
  580. '"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,' +
  581. '"view_counts_everywhere_api_enabled":true,' +
  582. '"longform_notetweets_consumption_enabled":true,' +
  583. '"responsive_web_twitter_article_tweet_consumption_enabled":false,' +
  584. '"tweet_awards_web_tipping_enabled":false,' +
  585. '"freedom_of_speech_not_reach_fetch_enabled":true,' +
  586. '"standardized_nudges_misinfo":true,' +
  587. '"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,' +
  588. '"longform_notetweets_rich_text_read_enabled":true,' +
  589. '"longform_notetweets_inline_media_enabled":true,' +
  590. '"responsive_web_graphql_exclude_directive_enabled":true,' +
  591. '"verified_phone_label_enabled":false,' +
  592. '"responsive_web_media_download_video_enabled":false,' +
  593. '"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,' +
  594. '"responsive_web_graphql_timeline_navigation_enabled":true,' +
  595. '"responsive_web_enhance_cards_enabled":false' +
  596. '}' +
  597. '&fieldToggles={' +
  598. '"withArticleRichContentState":false,' +
  599. '"withAuxiliaryUserLabels":false' +
  600. '}';
  601. let eq = encodeURI(q);
  602.  
  603. let controller = new AbortController();
  604. let req = mkreq(url, eq, EBTKN, controller);
  605. let res = {};
  606.  
  607. try {
  608. setTimeout(function () {
  609. controller.abort();
  610. }, 60000);
  611. res = await fetch(req);
  612. if (!res.ok) {
  613. console.log(`${MYNAME}: getdtl:notok:${res.ok}.`);
  614. return null;
  615. } // 失敗なら空で終わり
  616. } catch (err) {
  617. console.log(`${MYNAME}: getdtl:error:${err}.`);
  618. return null; // 失敗なら空で終わり
  619. }
  620.  
  621. let json = JSON.parse(await res.text());
  622.  
  623. let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
  624. let path_result = json.data.tweetResult.result;
  625. let tid = path_result.rest_id;
  626. if (!tid) {
  627. path_result = json.data.tweetResult.result.tweet;
  628. tid = path_result.rest_id;
  629. }
  630. let ts = path_result.source;
  631.  
  632. if (typeof path_result.legacy !== 'undefined') {
  633. tuid = path_result.legacy.user_id_str;
  634. tsn = path_result.core.user_results.result.legacy.screen_name;
  635. tca = path_result.legacy.created_at;
  636. if (typeof path_result.legacy.retweeted_status_result !== 'undefined') {
  637. if (
  638. typeof path_result.legacy.retweeted_status_result.result.legacy !==
  639. 'undefined'
  640. ) {
  641. rtuid =
  642. path_result.legacy.retweeted_status_result.result.legacy.user_id_str;
  643. rtsn =
  644. path_result.legacy.retweeted_status_result.result.core.user_results
  645. .result.legacy.screen_name;
  646. rtid = path_result.legacy.retweeted_status_result.result.rest_id;
  647. rtca =
  648. path_result.legacy.retweeted_status_result.result.legacy.created_at;
  649. rts = path_result.legacy.retweeted_status_result.result.source;
  650. } else {
  651. rtuid =
  652. path_result.legacy.retweeted_status_result.result.tweet.legacy
  653. .user_id_str;
  654. rtsn =
  655. path_result.legacy.retweeted_status_result.result.tweet.core
  656. .user_results.result.legacy.screen_name;
  657. rtid = path_result.legacy.retweeted_status_result.result.tweet.rest_id;
  658. rtca =
  659. path_result.legacy.retweeted_status_result.result.tweet.legacy
  660. .created_at;
  661. rts = path_result.legacy.retweeted_status_result.result.tweet.source;
  662. }
  663. } else {
  664. rtuid = 'none';
  665. rtsn = 'none'; // 未確認
  666. rtid = 'none';
  667. rtca = 'none';
  668. rts = 'none';
  669. }
  670. } else {
  671. tuid = 'none';
  672. tsn = 'none'; // 未確認
  673. tca = 'none';
  674. rtuid = 'none';
  675. rtsn = 'none';
  676. rtid = 'none';
  677. rtca = 'none';
  678. rts = 'none';
  679. }
  680. let ret = [tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts];
  681.  
  682. if (!tid || !ts) {
  683. console.log(`${MYNAME}: getdetl:error.`);
  684. return null; // エラー
  685. }
  686.  
  687. let str = await GM.getValue(`${MYNAME}_twts`);
  688.  
  689. if (str === undefined) {
  690. // 無い時
  691. await GM.setValue(`${MYNAME}_twts`, JSON.stringify([ret]));
  692. } else {
  693. // ある時
  694. let array = JSON.parse(str);
  695. array = array.concat([ret]);
  696. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));
  697.  
  698. await unique(1);
  699. }
  700.  
  701. return ret;
  702. }
  703.  
  704. async function gettwts(stat, text, path) {
  705. if (stat < 200 || stat > 299) {
  706. console.log(`${MYNAME}: gettwts:error:${stat}.`);
  707. return null; // エラー
  708. }
  709.  
  710. let n = 0;
  711. let ret = [];
  712. let json = JSON.parse(text);
  713. let path_instructions =
  714. path.length == 4
  715. ? json['data'][path[0]][path[1]][path[2]][path[3]]['instructions']
  716. : path.length == 3
  717. ? json['data'][path[0]][path[1]][path[2]]['instructions']
  718. : json['data'][path[0]][path[1]]['instructions'];
  719.  
  720. while (path_instructions[n].type != 'TimelineAddEntries')
  721. if (typeof path_instructions[++n] === 'undefined') return null;
  722.  
  723. let path_entries = path_instructions[n].entries;
  724. let num = Object.keys(path_entries).length;
  725.  
  726. for (let i = 0; i < num; i++) {
  727. if (!/^tweet-/i.test(path_entries[i].entryId)) continue;
  728.  
  729. let path_tid = path_entries[i].content.itemContent.tweet_results.result;
  730. if (typeof path_tid.tweet !== 'undefined') {
  731. path_tid = path_tid.tweet;
  732. }
  733. let tid = path_tid.rest_id;
  734. let ts = path_tid.source;
  735. let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
  736.  
  737. if (typeof path_tid.legacy === 'undefined') {
  738. // 何も無い
  739. tuid = 'none';
  740. tsn = 'none'; // 未確認
  741. tca = 'none';
  742. rtuid = 'none';
  743. rtsn = 'none'; // 未確認
  744. rtid = 'none';
  745. rtca = 'none';
  746. rts = 'none';
  747. ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
  748. continue;
  749. }
  750.  
  751. tuid = path_tid.legacy.user_id_str;
  752. tsn = path_tid.core.user_results.result.legacy.screen_name;
  753. tca = path_tid.legacy.created_at;
  754.  
  755. if (typeof path_tid.legacy.retweeted_status_result === 'undefined') {
  756. // リツイートでは無い
  757. rtuid = 'none';
  758. rtsn = 'none';
  759. rtid = 'none';
  760. rtca = 'none';
  761. rts = 'none';
  762. ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
  763. continue;
  764. }
  765.  
  766. let path_rtuid = path_tid.legacy.retweeted_status_result.result;
  767. if (typeof path_rtuid.tweet !== 'undefined') {
  768. path_rtuid = path_rtuid.tweet;
  769. }
  770.  
  771. rtuid = path_rtuid.legacy.user_id_str;
  772. rtsn = path_rtuid.core.user_results.result.legacy.screen_name;
  773. rtca = path_rtuid.legacy.created_at;
  774. rts = path_rtuid.source;
  775.  
  776. let path_rtid = path_rtuid.edit_control;
  777. if (typeof path_rtid.edit_control_initial !== 'undefined') {
  778. path_rtid = path_rtid.edit_control_initial;
  779. }
  780.  
  781. rtid = path_rtid.edit_tweet_ids[5 - path_rtid.edits_remaining];
  782.  
  783. ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
  784. }
  785.  
  786. if (!ret) {
  787. console.log(`${MYNAME}: gettwts:error.`);
  788. return null; // エラー
  789. }
  790.  
  791. let str = await GM.getValue(`${MYNAME}_twts`);
  792.  
  793. if (str === undefined) {
  794. // 無い時
  795. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(ret));
  796. } else {
  797. // ある時
  798. let array = JSON.parse(str);
  799. array = array.concat(ret);
  800. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));
  801.  
  802. await unique(1);
  803. }
  804. }
  805.  
  806. async function gettwts_d(stat, text) {
  807. if (stat < 200 || stat > 299) {
  808. console.log(`${MYNAME}: gettwts_d:error:${stat}.`);
  809. return null; // エラー
  810. }
  811.  
  812. let json = JSON.parse(text);
  813. let n = 0;
  814. let ret = [];
  815.  
  816. if (json.errors) {
  817. console.log(`${MYNAME}: gettwts_d:error:errors.`);
  818. return null; // エラー
  819. }
  820.  
  821. for (
  822. ;
  823. json.data.threaded_conversation_with_injections_v2.instructions[n].type !=
  824. 'TimelineAddEntries' &&
  825. json.data.threaded_conversation_with_injections_v2.instructions[n].type !=
  826. 'TimelineAddToModule';
  827. ++n
  828. );
  829. if (
  830. json.data.threaded_conversation_with_injections_v2.instructions[n].type ==
  831. 'TimelineAddToModule'
  832. ) {
  833. console.log(`${MYNAME}: gettwts_d:error:ne.`);
  834. return null; // エラー
  835. }
  836.  
  837. let path_ents =
  838. json.data.threaded_conversation_with_injections_v2.instructions[n].entries;
  839. let num = Object.keys(path_ents).length; // 一個しかない?
  840.  
  841. for (let i = 0; i < num; i++) {
  842. if (!/^tweet-/i.test(path_ents[i].entryId)) continue;
  843.  
  844. let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
  845. let path_result = path_ents[i].content.itemContent.tweet_results.result;
  846. let tid = path_result.rest_id;
  847. if (!tid) {
  848. path_result = path_ents[i].content.itemContent.tweet_results.result.tweet;
  849. tid = path_result.rest_id;
  850. }
  851. let ts = path_result.source;
  852.  
  853. if (typeof path_result.legacy !== 'undefined') {
  854. tuid = path_result.legacy.user_id_str;
  855. tsn = path_result.core.user_results.result.legacy.screen_name;
  856. tca = path_result.legacy.created_at;
  857. if (typeof path_result.legacy.retweeted_status_result !== 'undefined') {
  858. if (
  859. typeof path_result.legacy.retweeted_status_result.result.legacy !==
  860. 'undefined'
  861. ) {
  862. rtuid =
  863. path_result.legacy.retweeted_status_result.result.legacy
  864. .user_id_str;
  865. rtsn =
  866. path_result.legacy.retweeted_status_result.result.core
  867. .user_results.result.legacy.screen_name;
  868. rtid = path_result.legacy.retweeted_status_result.result.rest_id;
  869. rtca =
  870. path_result.legacy.retweeted_status_result.result.legacy
  871. .created_at;
  872. rts = path_result.legacy.retweeted_status_result.result.source;
  873. } else {
  874. rtuid =
  875. path_result.legacy.retweeted_status_result.result.tweet.legacy
  876. .user_id_str;
  877. rtsn =
  878. path_result.legacy.retweeted_status_result.result.tweet.core
  879. .user_results.result.legacy.screen_name;
  880. rtid =
  881. path_result.legacy.retweeted_status_result.result.tweet.rest_id;
  882. rtca =
  883. path_result.legacy.retweeted_status_result.result.tweet.legacy
  884. .created_at;
  885. rts = path_result.legacy.retweeted_status_result.result.tweet.source;
  886. }
  887. } else {
  888. rtuid = 'none';
  889. rtsn = 'none'; // 未確認
  890. rtid = 'none';
  891. rtca = 'none';
  892. rts = 'none';
  893. }
  894. } else {
  895. tuid = 'none';
  896. tsn = 'none'; // 未確認
  897. tca = 'none';
  898. rtuid = 'none';
  899. rtsn = 'none';
  900. rtid = 'none';
  901. rtca = 'none';
  902. rts = 'none';
  903. }
  904.  
  905. ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
  906. }
  907.  
  908. if (!ret) {
  909. console.log(`${MYNAME}: gettwts_d:error.`);
  910. return null; // エラー
  911. }
  912.  
  913. let str = await GM.getValue(`${MYNAME}_twts`);
  914.  
  915. if (str === undefined) {
  916. // 無い時
  917. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(ret));
  918. } else {
  919. // ある時
  920. let array = JSON.parse(str);
  921. array = array.concat(ret);
  922. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));
  923.  
  924. await unique(1);
  925. }
  926. }
  927.  
  928. async function gettwts_r(stat, text) {
  929. if (stat < 200 || stat > 299) {
  930. console.log(`${MYNAME}: gettwts_r:error:${stat}.`);
  931. return null; // エラー
  932. }
  933.  
  934. let json = JSON.parse(text);
  935. let path_ents = json.data;
  936. let num = Object.keys(path_ents).length;
  937. let ret = [];
  938.  
  939. for (let i = 0; i < num; i++) {
  940. // 一個しかない?
  941. let tuid, tsn, tca, rtuid, rtsn, rtid, rtca, rts;
  942. let path_result = path_ents.tweetResult.result;
  943. let tid = path_result.rest_id;
  944. if (!tid) {
  945. path_result = path_ents.tweetResult.result.tweet;
  946. tid = path_result.rest_id;
  947. }
  948. let ts = path_result.source;
  949.  
  950. if (typeof path_result.legacy !== 'undefined') {
  951. tuid = path_result.legacy.user_id_str;
  952. tsn = path_result.core.user_results.result.legacy.screen_name;
  953. tca = path_result.legacy.created_at;
  954. if (typeof path_result.legacy.retweeted_status_result !== 'undefined') {
  955. if (
  956. typeof path_result.legacy.retweeted_status_result.result.legacy !==
  957. 'undefined'
  958. ) {
  959. rtuid =
  960. path_result.legacy.retweeted_status_result.result.legacy
  961. .user_id_str;
  962. rtsn =
  963. path_result.legacy.retweeted_status_result.result.core
  964. .user_results.result.legacy.screen_name;
  965. rtid = path_result.legacy.retweeted_status_result.result.rest_id;
  966. rtca =
  967. path_result.legacy.retweeted_status_result.result.legacy
  968. .created_at;
  969. rts = path_result.legacy.retweeted_status_result.result.source;
  970. } else {
  971. rtuid =
  972. path_result.legacy.retweeted_status_result.result.tweet.legacy
  973. .user_id_str;
  974. rtsn =
  975. path_result.legacy.retweeted_status_result.result.tweet.core
  976. .user_results.result.legacy.screen_name;
  977. rtid =
  978. path_result.legacy.retweeted_status_result.result.tweet.rest_id;
  979. rtca =
  980. path_result.legacy.retweeted_status_result.result.tweet.legacy
  981. .created_at;
  982. rts = path_result.legacy.retweeted_status_result.result.tweet.source;
  983. }
  984. } else {
  985. rtuid = 'none';
  986. rtsn = 'none'; // 未確認
  987. rtid = 'none';
  988. rtca = 'none';
  989. rts = 'none';
  990. }
  991. } else {
  992. tuid = 'none';
  993. tsn = 'none'; // 未確認
  994. tca = 'none';
  995. rtuid = 'none';
  996. rtsn = 'none';
  997. rtid = 'none';
  998. rtca = 'none';
  999. rts = 'none';
  1000. }
  1001.  
  1002. ret.push([tuid, tsn, tid, tca, ts, rtuid, rtsn, rtid, rtca, rts]);
  1003. }
  1004.  
  1005. if (!ret) {
  1006. console.log(`${MYNAME}: gettwts_r:error.`);
  1007. return null; // エラー
  1008. }
  1009.  
  1010. let str = await GM.getValue(`${MYNAME}_twts`);
  1011.  
  1012. if (str === undefined) {
  1013. // 無い時
  1014. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(ret));
  1015. } else {
  1016. // ある時
  1017. let array = JSON.parse(str);
  1018. array = array.concat(ret);
  1019. await GM.setValue(`${MYNAME}_twts`, JSON.stringify(array));
  1020.  
  1021. await unique(1);
  1022. }
  1023. }
  1024.  
  1025. async function statssn(sn) {
  1026. let str = await GM.getValue(`${MYNAME}_twts`);
  1027. if (!str) return null;
  1028.  
  1029. let snl = sn.toLowerCase();
  1030. let array = JSON.parse(str);
  1031.  
  1032. for (let e of array) {
  1033. let t = e[1].toLowerCase() == snl;
  1034. let rt;
  1035. if (!t) rt = e[6].toLowerCase() == snl;
  1036.  
  1037. if (t || rt) return t ? [e[0], e[1]] : [e[5], e[6]];
  1038. }
  1039.  
  1040. return null;
  1041. }
  1042.  
  1043. function chklv(id) {
  1044. let ud = +id.substring(0, 2);
  1045.  
  1046. let l =
  1047. ud < 13 ? LVL_12 :
  1048. ud < 16 ? LVL_15 :
  1049. ud < 20 ? LVL_19 :
  1050. ud < 25 ? LVL_24 :
  1051. ud < 30 ? LVL_29 :
  1052. ud < 40 ? LVL_3 :
  1053. ud < 50 ? LVL_4 :
  1054. ud < 60 ? LVL_5 :
  1055. ud < 70 ? LVL_6 :
  1056. ud < 80 ? LVL_7 :
  1057. ud < 90 ? LVL_8 :
  1058. LVL_9;
  1059.  
  1060. return l.includes(id);
  1061. }
  1062.  
  1063. async function statsrt(uid, rtid) {
  1064. let str = await GM.getValue(`${MYNAME}_twts`);
  1065. if (!str) return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970'];
  1066.  
  1067. let array = JSON.parse(str);
  1068.  
  1069. for (let e of array) {
  1070. if (e[0] == uid && e[7] == rtid) return [e[2], e[3]]; // あれば終わり
  1071. }
  1072.  
  1073. console.log(`${MYNAME}: statsrt:end.`);
  1074. return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970'];
  1075. // エラー
  1076. }
  1077.  
  1078. function twXHRStateHandler({ target: xhr }, res) {
  1079. if (xhr.readyState === 4) {
  1080. const stat = xhr.status;
  1081. const rawText = xhr.responseText;
  1082. if (res == 1)
  1083. gettwts(stat, rawText, ['user', 'result', 'timeline_v2', 'timeline']);
  1084. else if (res == 2) gettwts(stat, rawText, ['home', 'home_timeline_urt']);
  1085. else if (res == 3) gettwts(stat, rawText, ['home', 'home_timeline_urt']);
  1086. else if (res == 4)
  1087. gettwts(stat, rawText, ['list', 'tweets_timeline', 'timeline']);
  1088. else if (res == 5)
  1089. gettwts(stat, rawText, [
  1090. 'search_by_raw_query',
  1091. 'search_timeline',
  1092. 'timeline',
  1093. ]);
  1094. else if (res == 6) gettwts_d(stat, rawText);
  1095. else if (res == 7) gettwts_r(stat, rawText);
  1096. }
  1097. }
  1098.  
  1099. function isTwtsURL(url) {
  1100. if (!url.includes('/graphql/')) return 0;
  1101. else if (url.includes('/UserTweets?')) return 1;
  1102. else if (url.includes('/HomeTimeline')) return 2;
  1103. else if (url.includes('/HomeLatestTimeline')) return 3;
  1104. else if (url.includes('/ListLatestTweetsTimeline?')) return 4;
  1105. else if (url.includes('/SearchTimeline?')) return 5;
  1106. else if (url.includes('/TweetDetail?')) return 6;
  1107. else if (url.includes('/TweetResultByRestId?')) return 7;
  1108. else return 0;
  1109. }
  1110.  
  1111. function overrideXHROpen() {
  1112. XMLHttpRequest.prototype.open = function () {
  1113. let res = isTwtsURL(arguments[1]);
  1114. if (res) {
  1115. this.addEventListener('readystatechange', function (evt) {
  1116. twXHRStateHandler(evt, res);
  1117. });
  1118. }
  1119. return originalXHROpen.apply(this, arguments);
  1120. };
  1121.  
  1122. console.log(`${MYNAME}: XMLHttpRequest.open overriden.`);
  1123. }
  1124.  
  1125. async function subsbadge() {
  1126. const SEL_V = 'path[d^="M20.396 11c-.018-.646-.215-1.275-.57-1"]';
  1127. const SEL_B = 'path[d^="M16.5 3H2v18h15c3.038 0 5.5-2.46 5.5-5"]';
  1128.  
  1129. let elms = document.querySelectorAll(`${SEL_V}, ${SEL_B}`);
  1130.  
  1131. for (let e of elms) {
  1132. const SEL_E =
  1133. 'svg.r-4qtqp9.r-yyyyoo.r-lwhw9o.r-dnmrzs.r-bnwqim.' +
  1134. 'r-1plcrui.r-lrvibr.r-cnnz9e';
  1135. // def-ja, def-en, ble-ja, ble-en
  1136. const SEL_E2 =
  1137. 'svg.r-4qtqp9.r-yyyyoo.r-1xvli5t.r-dnmrzs.r-bnwqim.' +
  1138. 'r-1plcrui.r-f5ekn1.r-17h40o6.r-lrvibr';
  1139. // def-ja, def-en, ble-ja, ble-en
  1140. const D_V =
  1141. 'M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.6' +
  1142. '07.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.8' +
  1143. '82-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.' +
  1144. '604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.90' +
  1145. '2-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.1' +
  1146. '44 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 ' +
  1147. '1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634' +
  1148. '.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.' +
  1149. '586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.5' +
  1150. '67s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.90' +
  1151. '7.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.' +
  1152. '246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072' +
  1153. ' 4.4-4.794 1.347 1.246z';
  1154. const D_B =
  1155. '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' +
  1156. '-.22-.023-.32.574-.87.908-1.91.908-3.03C22 5.46 19.538 3 16.5 3zm-.796 5.99c.457' +
  1157. '-.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' +
  1158. '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-.' +
  1159. '35 2.803-.95-.988-.02-1.821-.66-2.109-1.54.138.03.28.04.425.04.206 0 .405-.03.59' +
  1160. '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' +
  1161. '.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-.' +
  1162. '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-.' +
  1163. '28 1.433-.54-.168.52-.526.96-.992 1.23z';
  1164. const SEL_P_KS = 'form div[data-testid="TypeaheadUser"]'; // キーワード検索
  1165. const SEL_P_KSR = 'form div[data-testid="typeaheadRecentSearchesItem"]'; // キーワード検索(最新)
  1166. const SEL_P_DS =
  1167. 'div[aria-labelledby="modal-header"] div.css-175oi2r.r-1wbh5a2.r-dnmrzs.r-1ny4l3l>' +
  1168. 'div[data-testid^="User-Name"]'; // 表示をカスタマイズする、ノート
  1169. const SEL_P_VRRT =
  1170. 'main section article div[data-testid="card.layoutLarge.detail"]'; // ビデオ引用RT
  1171. const SEL_P_T2 =
  1172. 'main div[data-testid="primaryColumn"] div[data-testid="UserName"]'; // トップ2
  1173. const SEL_P_RRT = 'main section article div[data-testid^="User-Name"]'; // 引用RT
  1174. const SEL_KS =
  1175. 'div.css-175oi2r.r-1awozwy.r-18u37iz.r-1wbh5a2 div.css-1rynq56.r-1wvb978 span'; // バグ?対策 def-ja, def-en
  1176. const SEL =
  1177. 'div.css-175oi2r.r-18u37iz.r-1wbh5a2.r-13hce6t span.css-1qaijid.r-bcqeeo.r-qvutc0';
  1178. // def-ja, def-en, ble-ja, ble-en
  1179. const SEL_2 =
  1180. 'div.css-175oi2r.r-1awozwy.r-18u37iz.r-1wbh5a2 span.css-1qaijid.r-bcqeeo.r-qvutc0';
  1181. // def-ja, def-en, ble-ja, ble-en
  1182.  
  1183. let spe = e.parentNode.parentNode;
  1184. let tpe = e.parentNode.parentNode.parentNode;
  1185. let sn;
  1186. let ss; // Temp.
  1187.  
  1188. if (tpe.querySelector(`${SEL_E}, ${SEL_E2}`)) continue;
  1189.  
  1190. if (spe.style.color == 'rgb(232, 134, 143)') {
  1191. ss = s_mus;
  1192. spe.style.color = 'rgb(29, 155, 240)';
  1193. s_mus = ss;
  1194. }
  1195.  
  1196. let xpe = e.closest('main div[data-testid="primaryColumn"] h2[role="heading"]'); // トップ
  1197. if (xpe) {
  1198. sn = document.URL.split('/')[3];
  1199. }
  1200.  
  1201. if (!sn && spe.getAttribute('data-testid') == 'verificationBadge')
  1202. sn = document.URL.split('/')[3];
  1203. // 認証済アカウントポップアップ
  1204.  
  1205. if (!sn) {
  1206. let xpe = e.closest(SEL_P_KS);
  1207. let sne;
  1208. if (xpe) sne = xpe.querySelector(SEL_KS);
  1209. if (sne) sn = sne.textContent.split('@')[1];
  1210. }
  1211. if (!sn) {
  1212. let xpe = e.closest(SEL_P_KSR);
  1213. let sne;
  1214. if (xpe) sne = xpe.querySelector(SEL_KS);
  1215. if (sne) sn = sne.textContent.split('@')[1];
  1216. }
  1217. if (!sn) {
  1218. let xpe = e.closest(SEL_P_DS);
  1219. let sne;
  1220. if (xpe) sne = xpe.querySelector(SEL);
  1221. if (sne) sn = sne.textContent.split('@')[1];
  1222. }
  1223. if (!sn) {
  1224. let xpe = e.closest(SEL_P_VRRT);
  1225. let sne;
  1226. if (xpe) sne = xpe.querySelector(SEL);
  1227. if (sne) sn = sne.textContent.split('@')[1];
  1228. }
  1229. if (!sn) {
  1230. let xpe = e.closest(SEL_P_T2);
  1231. let sne;
  1232. if (xpe) sne = xpe.querySelector(SEL_2);
  1233. if (sne) sn = sne.textContent.split('@')[1];
  1234. }
  1235. if (!sn) {
  1236. let xpe = e.closest(SEL_P_RRT);
  1237. let sne;
  1238. if (xpe) sne = xpe.querySelector(SEL);
  1239. if (sne) sn = sne.textContent.split('@')[1];
  1240. }
  1241.  
  1242. if (!sn) {
  1243. let xpe = e.closest('a'); // ビデオ引用TW、TL等
  1244. if (xpe) {
  1245. let a = xpe.getAttribute('href').split('/')[1];
  1246. let b = xpe.getAttribute('href').split('/')[2];
  1247. let c = xpe.getAttribute('href').split('/')[3];
  1248.  
  1249. if (a == 'i' && b == 'status') {
  1250. let str = await GM.getValue(`${MYNAME}_twts`);
  1251. let array = [];
  1252. if (str) array = JSON.parse(str);
  1253.  
  1254. for (let e of array) {
  1255. if (e[2] == c) {
  1256. sn = e[1];
  1257. break;
  1258. } else if (e[7] == c) {
  1259. sn = e[6];
  1260. break;
  1261. }
  1262. }
  1263.  
  1264. if (!sn) {
  1265. let detl = await getdetl(c);
  1266. if (detl) sn = detl[1];
  1267. }
  1268. } else {
  1269. sn = a;
  1270. }
  1271. }
  1272. }
  1273.  
  1274. if (!sn) {
  1275. ss = s_mus;
  1276. spe.style.color = 'rgb(232, 134, 143)';
  1277. s_mus = ss;
  1278. continue;
  1279. }
  1280.  
  1281. let a = [
  1282. '',
  1283. 'compose',
  1284. 'explore',
  1285. 'home',
  1286. 'i',
  1287. 'login',
  1288. 'messages',
  1289. 'notifications',
  1290. 'search',
  1291. 'search-advanced',
  1292. 'settings',
  1293. ];
  1294.  
  1295. if (a.includes(sn.toLowerCase())) {
  1296. ss = s_mus;
  1297. spe.style.color = 'rgb(232, 134, 143)';
  1298. s_mus = ss;
  1299. continue;
  1300. }
  1301.  
  1302. let pr = await statssn(sn); // screen name -> id, screen_name
  1303. if (!pr) pr = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
  1304. let r = pr ? chklv(pr[0]) : pr;
  1305.  
  1306. ss = s_mus;
  1307. if (r === true) {
  1308. e.setAttribute('d', D_V);
  1309. } else if (r === false) {
  1310. e.setAttribute('d', D_B);
  1311. } else {
  1312. spe.style.color = 'rgb(232, 134, 143)';
  1313. }
  1314. s_mus = ss;
  1315. }
  1316. }
  1317.  
  1318. async function addsl() {
  1319. const SEL_END =
  1320. 'main div[data-testid="primaryColumn"] section article ' +
  1321. 'div.css-175oi2r.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2';
  1322. const SEL_END_RT =
  1323. 'main div[data-testid="primaryColumn"] section article ' +
  1324. 'span[id].r-8akbws.r-krxsd3.r-n6v787.r-1cwl3u0.r-b88u0q';
  1325. const SEL_ADD = `span.us-${MYNAME}`;
  1326.  
  1327. let uid, tid, tsl, rtsl, thref, rthref, detl;
  1328. let ca, span, span2, a, a2;
  1329.  
  1330. let elm_end = document.querySelector(SEL_END);
  1331. let elm_end_rt = document.querySelector(SEL_END_RT);
  1332. if (!elm_end) return;
  1333.  
  1334. let old = elm_end.querySelectorAll(SEL_ADD);
  1335. if (old.length) return;
  1336.  
  1337. tsl = '?';
  1338. rtsl = '(?)';
  1339. thref = 'https://help.twitter.com/using-twitter/how-to-tweet#source-labels';
  1340. if (/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
  1341. thref =
  1342. 'https://help.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion' +
  1343. '/using-twitter/how-to-tweet#source-labels';
  1344. }
  1345. rthref = thref;
  1346.  
  1347. ca = elm_end.querySelector('a');
  1348. tid = ca.getAttribute('href').split('/')[3];
  1349.  
  1350. if (elm_end_rt) {
  1351. let pe = elm_end_rt.parentNode;
  1352. let sn = pe.getAttribute('href').slice(1);
  1353.  
  1354. uid = await statssn(sn); // screen name -> id, screen_name
  1355. if (!uid) uid = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
  1356. // リツイートした人のuid
  1357. }
  1358.  
  1359. let str = await GM.getValue(`${MYNAME}_twts`); // 出来るだけ遅らせる?
  1360. let array = [];
  1361. if (str) array = JSON.parse(str);
  1362.  
  1363. let i = 2;
  1364. if (document.URL.split('/')[5] != tid) i = 7;
  1365.  
  1366. for (let e of array) {
  1367. if (e[i] == tid) {
  1368. detl = e;
  1369. break;
  1370. }
  1371. }
  1372.  
  1373. if (!detl) detl = await getdetl(tid); // ツイートはtid、rtidどちらにもなり得る
  1374.  
  1375. if (detl) {
  1376. tsl = detl[4].split('<')[1].split('>')[1];
  1377. thref = detl[4].split('"')[1];
  1378. } else {
  1379. console.log(`${MYNAME}: addsl:error.`); // エラー
  1380. }
  1381.  
  1382. if (ssl == 1 || !elm_end_rt) {
  1383. } else if (uid) {
  1384. for (let e of array) {
  1385. if (e[0] == uid[0] && e[7] == tid) {
  1386. // リツイートは一つしか無い
  1387. rtsl = `(${e[4].split('<')[1].split('>')[1]})`;
  1388. rthref = e[4].split('"')[1];
  1389. break;
  1390. }
  1391. }
  1392. }
  1393.  
  1394. span = document.createElement('span');
  1395. span.className = `us-${MYNAME}`;
  1396. span.style.margin = '0px 3px 0px 3px';
  1397. span.textContent = '·';
  1398. span.style.color = getComputedStyle(ca, null).color;
  1399. span.style.font = getComputedStyle(ca, null).font;
  1400. span.style.lineHeight = getComputedStyle(elm_end, null).lineHeight;
  1401.  
  1402. span2 = document.createElement('span');
  1403. span2.className = `us-${MYNAME}`;
  1404. span2.style.lineHeight = getComputedStyle(elm_end, null).lineHeight;
  1405. span2.style.display = 'inline-block';
  1406.  
  1407. a = document.createElement('a');
  1408. a.className = `us-${MYNAME}`;
  1409. a.setAttribute('role', 'link');
  1410. a.setAttribute('href', thref);
  1411. a.setAttribute('target', '_blank');
  1412. a.setAttribute('rel', 'nofollow noopener noreferrer');
  1413. a.textContent = tsl;
  1414. a.style.color = getComputedStyle(ca, null).color;
  1415. a.style.font = getComputedStyle(ca, null).font;
  1416. a.style.textDecoration = getComputedStyle(ca, null).textDecoration;
  1417.  
  1418. a2 = document.createElement('a');
  1419. a2.className = `us-${MYNAME}`;
  1420. a2.setAttribute('role', 'link');
  1421. a2.setAttribute('href', rthref);
  1422. a2.setAttribute('target', '_blank');
  1423. a2.setAttribute('rel', 'nofollow noopener noreferrer');
  1424. a2.textContent = rtsl;
  1425. a2.style.color = getComputedStyle(ca, null).color;
  1426. a2.style.font = getComputedStyle(ca, null).font;
  1427. a2.style.textDecoration = getComputedStyle(ca, null).textDecoration;
  1428.  
  1429. let ss = s_mus;
  1430.  
  1431. elm_end.appendChild(span);
  1432. elm_end.appendChild(span2);
  1433. span2.appendChild(a);
  1434. if (ssl == 2 && elm_end_rt) span2.appendChild(a2);
  1435.  
  1436. s_mus = ss;
  1437. }
  1438.  
  1439. async function main_track() {
  1440. const SEL_END_RT =
  1441. 'main div[data-testid="primaryColumn"] section article ' +
  1442. 'span[id].r-8akbws.r-krxsd3.r-n6v787.r-1cwl3u0.r-b88u0q';
  1443.  
  1444. let elms = document.querySelectorAll(SEL_END_RT);
  1445.  
  1446. for (let elm of elms) {
  1447. const SEL_RTTO = `div[data-testid^="User-Name"] a.css-146c3p1.r-qvutc0:not(.us-${MYNAME})`; // UTL, HTL
  1448. const SEL_RTTO_2 =
  1449. 'main div[data-testid="primaryColumn"] section article ' +
  1450. `div.css-175oi2r.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2 a.css-1jxf684.r-qvutc0:not(.us-${MYNAME})`;
  1451. // Retweet def-ja, def-en, ble-ja, ble-en
  1452. const SEL_ADD = `a.us-${MYNAME}`;
  1453.  
  1454. let pe = elm.parentNode;
  1455. let fpe = elm.parentNode.parentNode.parentNode.parentNode;
  1456. let xpe = elm.closest('article');
  1457. let elm2 = xpe.querySelector(SEL_RTTO);
  1458. if (!elm2) elm2 = xpe.querySelector(SEL_RTTO_2);
  1459. let old = fpe.querySelector(SEL_ADD);
  1460.  
  1461. let sn = pe.getAttribute('href').slice(1);
  1462. if (sn.includes('/')) continue;
  1463.  
  1464. let rtid = elm2.getAttribute('href').split('/')[3];
  1465.  
  1466. let id = await statssn(sn); // screen name -> id, screen_name
  1467. if (!id) id = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
  1468. if (!id) continue;
  1469.  
  1470. let stats = await statsrt(id[0], rtid); // id, rtid -> tid, tca
  1471.  
  1472. let span, span2, a;
  1473. let date;
  1474. let ss; // Temp.
  1475.  
  1476. if (!fmt) continue;
  1477.  
  1478. if (!old) {
  1479. span = document.createElement('span');
  1480. span.className = `us-${MYNAME}`;
  1481. span.style.margin = '0px 3px 0px 3px';
  1482. span.textContent = '·';
  1483. span.style.color = getComputedStyle(elm, null).color;
  1484. span.style.font = getComputedStyle(elm, null).font;
  1485. span.style.lineHeight = getComputedStyle(pe, null).lineHeight;
  1486.  
  1487. span2 = document.createElement('span');
  1488. span2.className = `us-${MYNAME}`;
  1489. span2.style.lineHeight = getComputedStyle(pe, null).lineHeight;
  1490. span2.style.display = 'inline-block';
  1491.  
  1492. a = document.createElement('a');
  1493. a.className = `us-${MYNAME}`;
  1494. a.setAttribute('dir', 'ltr');
  1495. a.setAttribute('role', 'link');
  1496. a.setAttribute('href', `/${sn}/status/${stats[0]}`);
  1497. a.setAttribute('target', '_blank');
  1498. a.setAttribute('rel', 'noopener noreferrer');
  1499. date = datef(new Date(stats[1]), fmt);
  1500. a.textContent = date;
  1501. a.style.color = getComputedStyle(elm, null).color;
  1502. a.style.font = getComputedStyle(elm, null).font;
  1503. a.style.textDecoration = getComputedStyle(elm, null).textDecoration;
  1504.  
  1505. ss = s_mus;
  1506.  
  1507. fpe.appendChild(span);
  1508. fpe.appendChild(span2);
  1509. span2.appendChild(a);
  1510.  
  1511. s_mus = ss;
  1512. } else {
  1513. ss = s_mus;
  1514.  
  1515. date = datef(new Date(stats[1]), fmt);
  1516. if (old.textContent != date) old.textContent = date; // TZ change
  1517.  
  1518. s_mus = ss;
  1519. }
  1520. }
  1521. }
  1522.  
  1523. console.log(`${MYNAME}: start.`);
  1524.  
  1525. tb = TB;
  1526. ssl = SSL;
  1527. fmt = FMT;
  1528. intl = INTL;
  1529.  
  1530. if (!NOGUI) await initgui();
  1531. overrideXHROpen();
  1532. observer.observe(document.documentElement, { childList: true, subtree: true });
  1533.  
  1534. while (1) {
  1535. if (s_mus) {
  1536. s_mus = null; // 初期値がtrue、変更もしない
  1537. if (tb) await subsbadge();
  1538. if (ssl) await addsl();
  1539. if (fmt || ssl == 2) await main_track();
  1540. }
  1541.  
  1542. await new Promise((resolve) => setTimeout(resolve, intl)); // intl は外から非同期に変更する
  1543. }
  1544.  
  1545.  
  1546. })(); /* END */

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址