显示转发的日期和时间。

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

目前为 2024-12-18 提交的版本。查看 最新版本

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

QingJ © 2025

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