顯示轉發的日期和時間。

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

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

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

QingJ © 2025

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