显示转发的日期和时间。

还可以显示鸣叫来源标签。

目前为 2023-05-14 提交的版本。查看 最新版本

  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.1.0
  9. // @description And shows tweet source labels.
  10. // @description:ja ツイートソースラベルも表示できます。
  11. // @description:ko 트윗 소스 라벨도 표시할 수 있습니다.
  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. // @grant GM.getValue
  21. // @grant GM.registerMenuCommand
  22. // @grant GM.setValue
  23. // @run-at document-idle
  24. // ==/UserScript==
  25. //
  26. // ES2017(ES8) or later.
  27. //
  28. // ES2017(ES8) 以降が必要です。
  29. //
  30. // ES2017(ES8) 이상이 필요합니다.
  31. //
  32. // 需要 ES2017(ES8) 或更高版本。
  33. //
  34. // 需要 ES2017(ES8) 或更高版本。
  35.  
  36.  
  37. (async function() { /* START */
  38.  
  39.  
  40. 'use strict';
  41.  
  42.  
  43. // //////////// Settings //////////// //
  44. // No GUI Settings
  45. // Default values are used.
  46. const NOGUI = false;
  47. // ////////////////////////////////// //
  48.  
  49. // ///////// Default valuse ///////// //
  50. // Date formats
  51. // 1. 31.12.70 23:59
  52. // 2. 31.12.70(Th) 23:59
  53. // 3. 31.12.70 23:59:59
  54. // 4. 31.12.70(Th) 23:59:59
  55. //
  56. // 5. 70-12/31 23:59
  57. // 6. 70-12/31(Th) 23:59
  58. // 7. 70-12/31 23:59'59
  59. // 8. 70-12/31(Th) 23:59'59
  60. //
  61. // 9. 70/12/31 23:59
  62. // 10. 70/12/31(Th) 23:59
  63. // 11. 70/12/31 23:59:59
  64. // 12. 70/12/31(Th) 23:59:59 [ye/mo/da(we) ho:mi:se]
  65. //
  66. // 0. Not Shown
  67. const FMT = 10;
  68.  
  69. // Show source labels
  70. // 1. Tweet only
  71. // 2. Tweet and (Retweet)
  72. // 0. Not shown
  73. const SSL = 2;
  74.  
  75. // Loop interval(ms)
  76. // const INTL = 1000;
  77. const INTL = 800;
  78. // ////////////////////////////////// //
  79.  
  80. const MYNAME = 'sctrt110';
  81. const BTKN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs'
  82. + '=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
  83. const EBTKN = encodeURIComponent(BTKN);
  84.  
  85. let top_sr = '1';
  86. let json_sr = [];
  87. let json_sr_new = [];
  88. let timeout_sr = false;
  89. let s_mus = true;
  90. let observer = new MutationObserver(function(mutations) {
  91. s_mus = mutations;
  92. });
  93. let fmt, ssl, intl;
  94. let dalg;
  95.  
  96.  
  97. let cookie = { // "https://qiita.com/aqril_1132/items/925a7cb04276d9f916d7"
  98. getObj: function() {
  99. let cookie = document.cookie;
  100. let cookieObj = {};
  101. if (!!cookie) {
  102. Array.prototype.forEach.call(cookie.split(';'), function(c) {
  103. let array = [c][0].split('=').map(function(a) {return a.trim()});
  104. let key = ~c.indexOf('=') ? unescape(array[0]) : '';
  105. let val = ~c.indexOf('=') ? unescape(array[1]) : unescape(array[0]);
  106. if (!cookieObj.hasOwnProperty(key)) {
  107. cookieObj[key] = [val];
  108. } else {
  109. cookieObj[key].push(val);
  110. }
  111. });
  112. }
  113. return cookieObj;
  114. },
  115. getByName: function(name) {
  116. let ret = [];
  117. let cookieObj = this.getObj();
  118. if (cookieObj.hasOwnProperty(name)) {
  119. ret = cookieObj[name];
  120. }
  121. return ret;
  122. },
  123. deleteByName : function(name, path) {
  124. var str = escape(name)
  125. + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'
  126. + (path ? '; path=' + path : '');
  127. document.cookie = str;
  128. }
  129. };
  130.  
  131.  
  132. async function main_track() {
  133. s_mus = null; // 不要?
  134. const SEL_END = 'main div[data-testid="primaryColumn"] section article span.css-cens5h.r-b88u0q:not([aria-label])';
  135. const SEL_RTTO = `div[data-testid^="User-Name"] a.css-901oao.r-qvutc0:not(.us-${MYNAME})`; // UTL, HTL
  136. const SEL_RTTO_2 = 'main div[data-testid="primaryColumn"] section article '
  137. + `div.css-1dbjc4n.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2 a.css-901oao.r-qvutc0:not(.us-${MYNAME})`;
  138. // Retweet def-ja, def-en, ble-ja, ble-en
  139. const SEL_ADD = `a.us-${MYNAME}`;
  140. let elms;
  141. let pe, fpe, xpe, elm2, old, span, span2, a;
  142. let sn, rtto, date;
  143. let id, stats;
  144. elms = document.querySelectorAll(SEL_END);
  145. for(let elm of elms) {
  146. pe = elm.parentNode;
  147. fpe = elm.parentNode.parentNode.parentNode.parentNode;
  148. sn = pe.getAttribute('href').slice(1);
  149. xpe = elm.closest('article');
  150. elm2 = xpe.querySelector(SEL_RTTO);
  151. if(!elm2) elm2 = xpe.querySelector(SEL_RTTO_2);
  152. rtto = elm2.getAttribute('href').split('/')[3];
  153. id = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
  154. if(!id) continue;
  155. stats = await statsrt(id[0], rtto); // id, rtto -> tid, tca
  156. if(!fmt) continue;
  157. old = fpe.querySelector(SEL_ADD)
  158. if(!old) {
  159. span = document.createElement('span');
  160. span.className = `us-${MYNAME}`;
  161. span.style.margin = '0px 3px 0px 3px';
  162. span.textContent = '·';
  163. span.style.color = getComputedStyle(elm, null).color;
  164. span.style.font = getComputedStyle(elm, null).font;
  165. span.style.lineHeight = getComputedStyle(pe, null).lineHeight;
  166. span2 = document.createElement('span');
  167. span2.className = `us-${MYNAME}`;
  168. span2.style.lineHeight = getComputedStyle(pe, null).lineHeight;
  169. span2.style.display = 'inline-block';
  170. a = document.createElement('a');
  171. a.className = `us-${MYNAME}`;
  172. a.setAttribute('dir', 'ltr');
  173. a.setAttribute('role', 'link');
  174. a.setAttribute('href', `/${sn}/status/${stats[0]}`);
  175. a.setAttribute('target', '_blank');
  176. a.setAttribute('rel', 'noopener noreferrer');
  177. date = datef(new Date(stats[1]), fmt);
  178. a.textContent = date;
  179. a.style.color = getComputedStyle(elm, null).color;
  180. a.style.font = getComputedStyle(elm, null).font;
  181. a.style.textDecoration = getComputedStyle(elm, null).textDecoration;
  182. fpe.appendChild(span);
  183. fpe.appendChild(span2);
  184. span2.appendChild(a);
  185. s_mus = null;
  186. } else {
  187. date = datef(new Date(stats[1]), fmt);
  188. if(old.textContent != date) old.textContent = date; // TZ change
  189. }
  190. }
  191. }
  192.  
  193.  
  194. async function touid(sn) {
  195. if(localStorage.getItem(`${MYNAME}_idl`) === null) { // 無い時
  196. let s = JSON.stringify(await getuid(sn)); // screen name -> id, blue, created_at, screen_name, verified
  197. if(s) {
  198. localStorage.setItem(`${MYNAME}_idl`, s);
  199. } else {
  200. console.log(`${MYNAME}: touid:error.`);
  201. return null; // エラー
  202. }
  203. }
  204. let str = localStorage.getItem(`${MYNAME}_idl`); // ある時はココから
  205. let json = JSON.parse(str);
  206. for(let e of json) {
  207. if(e[3] == sn) return e;
  208. }
  209. json = json.concat(await getuid(sn)); // あるけど、無い時
  210. str = JSON.stringify(json);
  211. localStorage.setItem(`${MYNAME}_idl`, str);
  212. for(let e of json) {
  213. if(e[3] == sn) return e;
  214. }
  215. console.log(`${MYNAME}: touid:error:end.`);
  216. return null; // エラー
  217. }
  218.  
  219.  
  220. async function getuid(s_name) {
  221. let url = 'https://api.twitter.com/graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName';
  222. if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
  223. url = 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/'
  224. + 'graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName';
  225. }
  226. let q = '?variables={'
  227. + `"screen_name":"${s_name}",`
  228. + '"withSafetyModeUserFields":true,'
  229. + '"withSuperFollowsUserFields":true'
  230. + '}'
  231. + '&features={'
  232. + '"responsive_web_twitter_blue_verified_badge_is_enabled":true,'
  233. + '"responsive_web_graphql_exclude_directive_enabled":false,' // false
  234. + '"verified_phone_label_enabled":true,' // false
  235. + '"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,'
  236. + '"responsive_web_graphql_timeline_navigation_enabled":true'
  237. + '}';
  238. let eq = encodeURI(q);
  239. let controller = new AbortController();
  240. let req = mkreq(url, eq, EBTKN, controller);
  241. let res = {};
  242. try {
  243. setTimeout(function() {controller.abort()}, 60000);
  244. res = await fetch(req);
  245. if(!res.ok) {
  246. console.log(`${MYNAME}: getuid:error:` + res.ok + '.');
  247. return null;
  248. } // 失敗なら空で終わり
  249. } catch(err) {
  250. console.log(`${MYNAME}: getuid:error:` + err + '.');
  251. return null; // 失敗なら空で終わり
  252. }
  253. let json = JSON.parse(await res.text());
  254. let id, bl, ca, sn, vf;
  255. id = json.data.user.result.rest_id;
  256. bl = json.data.user.result.is_blue_verified;
  257. ca = json.data.user.result.legacy.created_at;
  258. sn = json.data.user.result.legacy.screen_name;
  259. vf = json.data.user.result.legacy.verified;
  260. return [[id, bl, ca, sn, vf]];
  261. }
  262.  
  263.  
  264. async function statsrt(uid, rtto) { // 連続している事、entries[]の順序が正しい事
  265. let num = localStorage.length;
  266. let keys = [];
  267. let str = '';
  268. let time = null;
  269. top_sr = '1';
  270. json_sr = [];
  271. json_sr_new = [];
  272. timeout_sr = false; // 初期化
  273. for(let i = 0; i < num; i++) {
  274. keys.push(localStorage.key(i));
  275. }
  276. if(!keys.includes(`${MYNAME}_tl_` + uid)) { // 無い時
  277. let s = JSON.stringify(await gettl(uid, null)); // id, cursor -> tid, tca, s, rtid, rtca
  278. if(s) {
  279. localStorage.setItem(`${MYNAME}_tl_` + uid, s);
  280. } else {
  281. console.log(`${MYNAME}: statsrt:error.`);
  282. return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970']; // エラー
  283. }
  284. }
  285. if(localStorage.getItem(`${MYNAME}_timerl`) === null) {
  286. time = Date.now();
  287. localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify([[uid, time]]));
  288. } else {
  289. let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
  290. for(let e of j) if(e[0] == uid) time = e[1];
  291. if(!time) { // 項目が無い時
  292. time = Date.now(); // この時点の保存値にセットされる
  293. j = j.concat([[uid, time]]);
  294. localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
  295. }
  296. }
  297. str = localStorage.getItem(`${MYNAME}_tl_` + uid); // ある時はココから
  298. json_sr = JSON.parse(str);
  299. for(let e of json_sr) {
  300. if(e[3] == rtto) return [e[0], e[1]]; // あれば終わり
  301. }
  302. for(let e of json_sr) {
  303. if(/^[0-9]/i.test(e[0])) {
  304. top_sr = e[0];
  305. break;
  306. }
  307. } // 上作成
  308. await statsrt_new(uid, rtto); // json_sr、json_sr_new更新
  309. json_sr = json_sr_new.concat(json_sr); // json_sr更新
  310. str = JSON.stringify(json_sr); // str更新
  311. localStorage.setItem(`${MYNAME}_tl_` + uid, str); // とりあえず、ストレージに保存
  312. for(let e of json_sr) {
  313. if(e[3] == rtto) return [e[0], e[1]]; // あれば終わり
  314. }
  315. let ret = await statsrt_old(uid, rtto); // json_sr更新、保存
  316. if(ret) return ret;
  317. let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
  318. for(let e of j) if(e[0] == uid) time = e[1]; // time更新
  319. if(!timeout_sr && Date.now() - time > 600000) {
  320. localStorage.removeItem(`${MYNAME}_tl_${uid}`); // センシティブ
  321. console.log(`${MYNAME}: statsrt:remove:${MYNAME}_tl_${uid}.`);
  322. j = j.filter(function(e) {
  323. return e[0] != uid;
  324. });
  325. localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
  326. time = null;
  327. }
  328. console.log(`${MYNAME}: statsrt:end.`);
  329. return ['0000000000000000000', 'Thu Jan 01 00:00:00 +0000 1970'];
  330. // TLがとても古いか、センシティブか、エラー
  331. }
  332.  
  333.  
  334. async function statsrt_new(uid, rtto) { // json_sr、json_sr_new更新
  335. let btmtmp = '';
  336. outer_block:
  337. for(const start = Date.now(); 1;) { // 新しい方、top_srを見る
  338. let ret = await gettl(uid, btmtmp);
  339. json_sr_new = json_sr_new.concat(ret); // json_sr_new更新
  340. for(let e of json_sr_new.slice().reverse()) { // 不要
  341. if(/^[0-9]/i.test(e[0])) {
  342. btmtmp = e[0];
  343. break;
  344. }
  345. } // 下更新
  346. if(ret.length < 2) {
  347. json_sr = []; // 非連続
  348. let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
  349. j = j.filter(function(e) {
  350. return e[0] != uid;
  351. });
  352. j = j.concat([[uid, Date.now()]]);
  353. localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
  354. break;
  355. } // 空の時は終了、一回目は無い(一個の時以外)
  356. for(let e of json_sr_new) {
  357. if(e[0] == top_sr) break outer_block; // あれば終了
  358. }
  359. if(Date.now() - start > 150000) { // 時間経過時打ち切り
  360. json_sr = []; // 非連続かも
  361. let j = JSON.parse(localStorage.getItem(`${MYNAME}_timerl`));
  362. j = j.filter(function(e) {
  363. return e[0] != uid;
  364. });
  365. j = j.concat([[uid, Date.now()]]);
  366. localStorage.setItem(`${MYNAME}_timerl`, JSON.stringify(j));
  367. break;
  368. }
  369. await new Promise(resolve => setTimeout(resolve, 200));
  370. }
  371. }
  372.  
  373.  
  374. async function statsrt_old(uid, rtto) {
  375. let btm = '';
  376. for(const start = Date.now(); 1;) { // 古い方、rttoを見る
  377. for(let e of json_sr.slice().reverse()) { // 不要
  378. if(/^[0-9]/i.test(e[0])) {
  379. btm = e[0];
  380. break;
  381. }
  382. } // 下更新
  383. let ret = await gettl(uid, btm);
  384. json_sr = json_sr.concat(ret); // json_sr更新
  385. let s = JSON.stringify(json_sr);
  386. localStorage.setItem(`${MYNAME}_tl_` + uid, s); // とりあえず、ストレージに保存
  387. if(ret.length < 2) {
  388. break;
  389. } // 空の時は終了
  390. for(let e of json_sr) {
  391. if(e[3] == rtto) return [e[0], e[1]]; // あれば終わり
  392. }
  393. if(Date.now() - start > 300000) {
  394. timeout_sr = true;
  395. break;
  396. } // 時間経過時打ち切り
  397. await new Promise(resolve => setTimeout(resolve, 200));
  398. }
  399. }
  400.  
  401.  
  402. async function gettl(id, cs) {
  403. let url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
  404. if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
  405. url = 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/'
  406. + '1.1/statuses/user_timeline.json';
  407. }
  408. let kav = '';
  409. if(cs) kav = `&max_id=${cs}`;
  410. let q = `?user_id=${id}&count=50${kav}`;
  411. let eq = encodeURI(q);
  412. let controller = new AbortController();
  413. let req = mkreq(url, eq, EBTKN, controller);
  414. let res = {};
  415. let ret = [];
  416. try {
  417. setTimeout(function() {controller.abort()}, 60000);
  418. res = await fetch(req);
  419. if(!res.ok) {
  420. console.log(`${MYNAME}: gettl:error:` + res.ok + '.');
  421. return null;
  422. } // 失敗なら空で終わり
  423. } catch(err) {
  424. console.log(`${MYNAME}: gettl:error:` + err + '.');
  425. return null; // 失敗なら空で終わり
  426. }
  427. let json = JSON.parse(await res.text());
  428. let num = Object.keys(json).length;
  429. let tid, tca, s, rtid, rtca;
  430. for(let i = 0; i < num; i++) {
  431. tid = json[i].id_str;
  432. tca = json[i].created_at;
  433. s = json[i].source;
  434. if(typeof(json[i].retweeted_status) !== 'undefined') {
  435. rtid = json[i].retweeted_status.id_str;
  436. rtca = json[i].retweeted_status.created_at;
  437. } else {
  438. rtid = 'none';
  439. rtca = 'none';
  440. }
  441. ret.push([tid, tca, s, rtid, rtca]);
  442. }
  443. return ret;
  444. }
  445.  
  446.  
  447. async function addsl() {
  448. s_mus = null; // 不要?
  449. const SEL_END = 'main div[data-testid="primaryColumn"] section article div.css-1dbjc4n.r-1d09ksm.r-1471scf.r-18u37iz.r-1wbh5a2';
  450. const SEL_END_RT = 'main div[data-testid="primaryColumn"] section article span.css-cens5h.r-b88u0q:not([aria-label])';
  451. const SEL_ADD = `span.us-${MYNAME}`;
  452. let old;
  453. let ca, span, span2, a, a2;
  454. let elms_end = document.querySelectorAll(SEL_END);
  455. for(let elm of elms_end) {
  456. let uid;
  457. let tid;
  458. let tsl = '?';
  459. let rtsl = '(?)';
  460. let thref = 'https://help.twitter.com/using-twitter/how-to-tweet#source-labels';
  461. if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
  462. thref = 'https://help.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/using-twitter/how-to-tweet#source-labels';
  463. }
  464. let rthref = thref;
  465. ca = elm.querySelector('a');
  466. tid = ca.getAttribute('href').split('/')[3];
  467. let detl = await getdetl(tid); // tid -> tca, s, uid, sn, uca
  468. if(detl) {
  469. tsl = detl[1].split('<')[1].split('>')[1];
  470. thref = detl[1].split('"')[1];
  471. }
  472. let elm_end_rt = document.querySelector(SEL_END_RT);
  473. if(elm_end_rt) {
  474. let pe = elm_end_rt.parentNode;
  475. let sn = pe.getAttribute('href').slice(1);
  476. uid = await touid(sn); // screen name -> id, blue, created_at, screen_name, verified
  477. // リツイートした人のuid
  478. }
  479. if(ssl == 1 || !elm_end_rt) {
  480. ;
  481. } else if(uid && localStorage.getItem(`${MYNAME}_tl_${uid[0]}`) !== null) {
  482. let str = localStorage.getItem(`${MYNAME}_tl_${uid[0]}`);
  483. let json = JSON.parse(str);
  484. for(let e of json) {
  485. if(e[3] == tid) {
  486. rtsl = '(' + e[2].split('<')[1].split('>')[1] + ')';
  487. rthref = e[2].split('"')[1];
  488. }
  489. }
  490. }
  491. old = elm.querySelectorAll(SEL_ADD)
  492. if(!old.length) {
  493. span = document.createElement('span');
  494. span.className = `us-${MYNAME}`;
  495. span.style.margin = '0px 3px 0px 3px';
  496. span.textContent = '·';
  497. span.style.color = getComputedStyle(ca, null).color;
  498. span.style.font = getComputedStyle(ca, null).font;
  499. span.style.lineHeight = getComputedStyle(elm, null).lineHeight;
  500. span2 = document.createElement('span');
  501. span2.className = `us-${MYNAME}`;
  502. span2.style.lineHeight = getComputedStyle(elm, null).lineHeight;
  503. span2.style.display = 'inline-block';
  504. a = document.createElement('a');
  505. a.className = `us-${MYNAME}`;
  506. a.setAttribute('role', 'link');
  507. a.setAttribute('href', thref);
  508. a.setAttribute('target', '_blank');
  509. a.setAttribute('rel', 'nofollow noopener noreferrer');
  510. a.textContent = tsl;
  511. a.style.color = getComputedStyle(ca, null).color;
  512. a.style.font = getComputedStyle(ca, null).font;
  513. a.style.textDecoration = getComputedStyle(ca, null).textDecoration;
  514. a2 = document.createElement('a');
  515. a2.className = `us-${MYNAME}`;
  516. a2.setAttribute('role', 'link');
  517. a2.setAttribute('href', rthref);
  518. a2.setAttribute('target', '_blank');
  519. a2.setAttribute('rel', 'nofollow noopener noreferrer');
  520. a2.textContent = rtsl;
  521. a2.style.color = getComputedStyle(ca, null).color;
  522. a2.style.font = getComputedStyle(ca, null).font;
  523. a2.style.textDecoration = getComputedStyle(ca, null).textDecoration;
  524. elm.appendChild(span);
  525. elm.appendChild(span2);
  526. span2.appendChild(a);
  527. if(ssl == 2 && elm_end_rt) span2.appendChild(a2);
  528. s_mus = null;
  529. }
  530. }
  531. }
  532.  
  533.  
  534. async function getdetl(tid) {
  535. let url = 'https://api.twitter.com/1.1/statuses/show.json';
  536. if(/^[^:]+:\/\/[^/]+\.onion\//i.test(document.URL)) {
  537. url = 'https://api.twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/'
  538. + '1.1/statuses/show.json';
  539. }
  540. let q = `?id=${tid}`;
  541. let eq = encodeURI(q);
  542. let controller = new AbortController();
  543. let req = mkreq(url, eq, EBTKN, controller);
  544. let res = {};
  545. try {
  546. setTimeout(function() {controller.abort()}, 60000);
  547. res = await fetch(req);
  548. if(!res.ok) {
  549. console.log(`${MYNAME}: getdtl:error:` + res.ok + '.');
  550. return null;
  551. } // 失敗なら空で終わり
  552. } catch(err) {
  553. console.log(`${MYNAME}: getdtl:error:` + err + '.');
  554. return null; // 失敗なら空で終わり
  555. }
  556. let json = JSON.parse(await res.text());
  557. let num = 1; // 一個しかない
  558. let tca, s, uid, sn, uca;
  559. for(let i = 0; i < num; i++) {
  560. tca = json.created_at;
  561. s = json.source;
  562. if(typeof(json.user) !== 'undefined') {
  563. uid = json.user.id_str;
  564. sn = json.user.screen_name;
  565. uca = json.user.created_at;
  566. } else {
  567. uid = 'none';
  568. sn = 'none';
  569. uca = 'none';
  570. }
  571. }
  572. return [tca, s, uid, sn, uca];
  573. }
  574.  
  575.  
  576. function mkreq(url, eq, ebt, controller) {
  577. let req;
  578. if(cookie.getByName('gt').length && !cookie.getByName('twid').length) {
  579. req = new Request(`${url}${eq}`,{
  580. headers: {
  581. 'authorization': `Bearer ${ebt}`,
  582. 'x-csrf-token': cookie.getByName('ct0')[0],
  583. 'x-guest-token': cookie.getByName('gt')[0]
  584. },
  585. cache: 'force-cache',
  586. redirect: 'follow',
  587. signal: controller.signal
  588. });
  589. } else {
  590. req = new Request(`${url}${eq}`,{
  591. headers: {
  592. 'authorization': `Bearer ${ebt}`,
  593. 'x-csrf-token': cookie.getByName('ct0')[0],
  594. 'x-twitter-auth-type': 'OAuth2Session'
  595. },
  596. cache: 'force-cache',
  597. redirect: 'follow',
  598. mode: 'cors',
  599. credentials: 'include',
  600. signal: controller.signal
  601. });
  602. }
  603. return req;
  604. }
  605.  
  606.  
  607. function datef(date, f) {
  608. let week_l;
  609. let l = document.documentElement.getAttribute('lang');
  610. l == 'ja' ? week_l = ['日', '月', '火', '水', '木', '金', '土']
  611. : l == 'ko' ? week_l = ['일', '월', '화', '수', '목', '금', '토']
  612. : l == 'zh-Hant' ? week_l = ['日', '一', '二', '三', '四', '五', '六']
  613. : l == 'zh' ? week_l = ['日', '一', '二', '三', '四', '五', '六']
  614. : l == 'ru' ? week_l = ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб']
  615. : l == 'de' ? week_l = ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam']
  616. : l == 'it' ? week_l = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab']
  617. : l == 'fr' ? week_l = ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']
  618. : l == 'pt' ? week_l = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'] // Add your language
  619. : week_l = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
  620. let ye, mo, da, we, ho, mi, se;
  621. ye = date.getFullYear().toString().slice(-2);
  622. mo = ('0' + (date.getMonth() + 1)).slice(-2);
  623. da = ('0' + date.getDate()).slice(-2);
  624. we = week_l[date.getDay()];
  625. ho = ('0' + date.getHours()).slice(-2);
  626. mi = ('0' + date.getMinutes()).slice(-2);
  627. se = ('0' + date.getSeconds()).slice(-2);
  628. return f == 1 ? `${da}.${mo}.${ye} ${ho}:${mi}`
  629. : f == 2 ? `${da}.${mo}.${ye}(${we}) ${ho}:${mi}`
  630. : f == 3 ? `${da}.${mo}.${ye} ${ho}:${mi}:${se}`
  631. : f == 4 ? `${da}.${mo}.${ye}(${we}) ${ho}:${mi}:${se}`
  632. : f == 5 ? `${ye}-${mo}/${da} ${ho}:${mi}`
  633. : f == 6 ? `${ye}-${mo}/${da}(${we}) ${ho}:${mi}`
  634. : f == 7 ? `${ye}-${mo}/${da} ${ho}:${mi}'${se}`
  635. : f == 8 ? `${ye}-${mo}/${da}(${we}) ${ho}:${mi}'${se}`
  636. : f == 9 ? `${ye}/${mo}/${da} ${ho}:${mi}`
  637. : f == 10 ? `${ye}/${mo}/${da}(${we}) ${ho}:${mi}`
  638. : f == 11 ? `${ye}/${mo}/${da} ${ho}:${mi}:${se}`
  639. : f == 12 ? `${ye}/${mo}/${da}(${we}) ${ho}:${mi}:${se}`
  640. : `${ye}/${mo}/${da}(${we}) ${ho}:${mi}:${se}`;
  641. }
  642.  
  643.  
  644. function makeDialog() {
  645. let dalg = document.createElement('div');
  646. dalg.className = `us-${MYNAME}`;
  647. dalg.style.all = 'initial';
  648. dalg.style.backgroundColor = 'rgb(235, 235, 235)';
  649. dalg.style.border = '3px outset';
  650. dalg.style.borderRadius = '1%';
  651. dalg.style.display = 'none';
  652. dalg.style.fontFamily = 'monospace';
  653. dalg.style.fontSize = '12px';
  654. dalg.style.height = '340px';
  655. dalg.style.width = '400px';
  656. dalg.style.paddingLeft = '2px';
  657. dalg.style.paddingRight = '2px';
  658. dalg.style.position = 'fixed';
  659. dalg.style.right = '8px';
  660. dalg.style.top = '8px';
  661. dalg.style.zIndex = '2147483647';
  662. let html = '<span style="all: initial; font-size: 120%; line-height: 140%">'
  663. + `${GM.info.script.name} ${GM.info.script.version} Settings`
  664. + '</span><br />\n'
  665. + `<input type="radio" name="fmt" value="1" class="top_r" />31.12.70 23:59`
  666. + `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`
  667. + `<input type="radio" name="fmt" value="9" class="top_r" />70/12/31 23:59<br />\n`
  668. + `<input type="radio" name="fmt" value="2" class="mid_r" />31.12.70(Th) 23:59`
  669. + `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`
  670. + `<input type="radio" name="fmt" value="10" class="mid_r" />70/12/31(Th) 23:59<br />\n`
  671. + `<input type="radio" name="fmt" value="3" class="mid_r" />31.12.70 23:59:59`
  672. + `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`
  673. + `<input type="radio" name="fmt" value="11" class="mid_r" />70/12/31 23:59:59<br />\n`
  674. + `<input type="radio" name="fmt" value="4" class="mid_r" />31.12.70(Th) 23:59:59`
  675. + `&nbsp;&nbsp;&nbsp;&nbsp;`
  676. + `<input type="radio" name="fmt" value="12" class="mid_r" />70/12/31(Th) 23:59:59<br />\n`
  677. + `<input type="radio" name="fmt" value="5" class="mid_r" />70-12/31 23:59`
  678. + `&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`
  679. + `<input type="radio" name="fmt" value="0" class="mid_r" />Not Shown<br />\n`
  680. + `<input type="radio" name="fmt" value="6" class="mid_r" />70-12/31(Th) 23:59<br />\n`
  681. + `<input type="radio" name="fmt" value="7" class="mid_r" />70-12/31 23:59'59<br />\n`
  682. + `<input type="radio" name="fmt" value="8" class="btm_r" />70-12/31(Th) 23:59'59<br />\n`
  683. + `<input type="radio" name="ssl" value="1" class="top_r" />Tweet source labels only`
  684. + `&nbsp;&nbsp;&nbsp;&nbsp;`
  685. + `<input type="radio" name="ssl" value="0" class="top_r" />Not shown<br />\n`
  686. + `<input type="radio" name="ssl" value="2" class="btm_r" />Tweet and (Retweet)<br />\n`
  687. + '<span style="all: initial; font-size: 100%">'
  688. + 'Loop interval(ms)&nbsp;'
  689. + '</span><input type="text" name="intl" size="10" class="top_t" /><br />\n'
  690. + '<input type="button" class="top_b" value="Cancel" />\n'
  691. + '<input type="button" class="top_b" value="Set default" />\n'
  692. + '<input type="button" class="top_b" value="Save & Close" />\n';
  693. dalg.innerHTML = html;
  694. for(let e of dalg.querySelectorAll('input.top_r')) {
  695. e.style.all = 'initial';
  696. e.style.appearance = 'auto';
  697. e.style.marginRight = '1px';
  698. e.style.marginTop = '8px';
  699. }
  700. for(let e of dalg.querySelectorAll('input.mid_r, input.btm_r')) {
  701. e.style.all = 'initial';
  702. e.style.appearance = 'auto';
  703. e.style.marginRight = '1px';
  704. e.style.marginTop = '1px';
  705. }
  706. for(let e of dalg.querySelectorAll('input.top_t')) {
  707. e.style.all = 'initial';
  708. e.style.backgroundColor = 'rgb(255, 255, 255)';
  709. e.style.fontFamily = 'monospace';
  710. e.style.fontSize = '100%';
  711. e.style.marginLeft = '1px';
  712. e.style.marginRight = '1px';
  713. e.style.marginTop = '8px';
  714. e.style.marginBottom = '0px';
  715. e.style.paddingLeft = '1px';
  716. e.style.paddingRight = '1px';
  717. e.style.paddingTop = '1px';
  718. e.style.paddingBottom = '1px';
  719. }
  720. for(let e of dalg.querySelectorAll('input.top_b')) {
  721. e.style.all = 'initial';
  722. e.style.backgroundColor = 'rgb(190, 190, 190)';
  723. e.style.borderRadius = '10%';
  724. e.style.cursor = 'default';
  725. e.style.fontSize = '110%';
  726. e.style.marginTop = '10px';
  727. e.style.marginBottom = '0px';
  728. e.style.paddingTop = '6px';
  729. e.style.paddingBottom = '6px';
  730. e.style.textAlign = 'center';
  731. e.style.width = '90px';
  732. }
  733. return dalg;
  734. }
  735.  
  736.  
  737. function makeFunc(dalg) {
  738. dalg.addEventListener('click', function(event) {
  739. event.stopPropagation();
  740. }, false);
  741. dalg.querySelector('input[value="Cancel"]').addEventListener('click', function() {
  742. dalg.style.display = 'none';
  743. }, false);
  744. dalg.querySelector('input[value="Cancel"]').addEventListener('mouseenter', function(event) {
  745. event.target.style.backgroundColor = 'rgb(170, 170, 170)';
  746. }, false);
  747. dalg.querySelector('input[value="Cancel"]').addEventListener('mouseleave', function(event) {
  748. event.target.style.backgroundColor = 'rgb(190, 190, 190)'
  749. }, false);
  750. dalg.querySelector('input[value="Set default"]').addEventListener('click', function() {
  751. dalg.querySelector(`input[name="fmt"][value="${FMT}"]`).checked = true;
  752. dalg.querySelector(`input[name="ssl"][value="${SSL}"]`).checked = true;
  753. dalg.querySelector('input[name="intl"]').value = INTL;
  754. }, false);
  755. dalg.querySelector('input[value="Set default"]').addEventListener('mouseenter', function(event) {
  756. event.target.style.backgroundColor = 'rgb(170, 170, 170)';
  757. }, false);
  758. dalg.querySelector('input[value="Set default"]').addEventListener('mouseleave', function(event) {
  759. event.target.style.backgroundColor = 'rgb(190, 190, 190)'
  760. }, false);
  761. dalg.querySelector('input[value="Save & Close"]').addEventListener('click', async function() {
  762. for(let e of dalg.querySelectorAll('input[name="fmt"]')) {
  763. if(e.checked) {
  764. fmt = +e.value;
  765. break;
  766. }
  767. }
  768. for(let e of dalg.querySelectorAll('input[name="ssl"]')) {
  769. if(e.checked) {
  770. ssl = +e.value;
  771. break;
  772. }
  773. }
  774. intl = +dalg.querySelector('input[name="intl"]').value;
  775. await GM.setValue('fmt', fmt);
  776. await GM.setValue('ssl', ssl);
  777. await GM.setValue('intl', intl);
  778. dalg.style.display = 'none';
  779. }, false);
  780. dalg.querySelector('input[value="Save & Close"]').addEventListener('mouseenter', function(event) {
  781. event.target.style.backgroundColor = 'rgb(170, 170, 170)';
  782. }, false);
  783. dalg.querySelector('input[value="Save & Close"]').addEventListener('mouseleave', function(event) {
  784. event.target.style.backgroundColor = 'rgb(190, 190, 190)'
  785. }, false);
  786. }
  787.  
  788.  
  789. async function initgui() {
  790. if(await GM.getValue('fmt') === undefined) {
  791. await GM.setValue('fmt', FMT);
  792. } else {
  793. fmt = await GM.getValue('fmt');
  794. }
  795. if(await GM.getValue('ssl') === undefined) {
  796. await GM.setValue('ssl', SSL);
  797. } else {
  798. ssl = await GM.getValue('ssl');
  799. }
  800. if(await GM.getValue('intl') === undefined) {
  801. await GM.setValue('intl', INTL);
  802. } else {
  803. intl = await GM.getValue('intl');
  804. }
  805. dalg = makeDialog();
  806. makeFunc(dalg);
  807. document.body.appendChild(dalg);
  808. GM.registerMenuCommand('Settings', function() {
  809. if(dalg.style.display == 'none') {
  810. dalg.querySelector(`input[name="fmt"][value="${fmt}"]`).checked = true;
  811. dalg.querySelector(`input[name="ssl"][value="${ssl}"]`).checked = true;
  812. dalg.querySelector('input[name="intl"]').value = intl;
  813. dalg.style.display = 'block';
  814. }
  815. });
  816. }
  817.  
  818.  
  819. console.log(`${MYNAME}: start.`);
  820.  
  821. fmt = FMT;
  822. ssl = SSL;
  823. intl = INTL;
  824.  
  825. if(!NOGUI) await initgui();
  826.  
  827. observer.observe(document.documentElement, {childList:true, subtree:true});
  828. while(1) {
  829. if(s_mus) {
  830. if(fmt || ssl == 2) await main_track();
  831. if(ssl) await addsl();
  832. s_mus = null; // 初期値がtrue、非同期処理用
  833. }
  834. await new Promise(resolve => setTimeout(resolve, intl)); // intl は外から非同期に変更する
  835. }
  836.  
  837.  
  838. })(); /* END */

QingJ © 2025

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