Twitter(現X)の前後ポストを表示する

    @@ -1,7 +1,7 @@ /* * @title Twitter(現X)の前後ポストを表示する * @description 個別 Tweet の URL から前後 1 時間の投稿を RT を含めて表示する - * @include https://twitter.com/* + * @include https://x.com/* * @license MIT License * @javascript_url */ @@ -38,16 +38,16 @@ } const main = () => { - const match = location.href.match(/https:\/\/twitter\.com\/(?<name>[^/]+)\/status\/(?<snowflake>\d+)/); + const match = location.href.match(/https:\/\/(twitter|x)\.com\/(?<name>[^/]+)\/status\/(?<snowflake>\d+)/); if (!match) { alert('このページは Twitter の個別 Tweet ではありません'); return; } const range = getSearchRange(match.groups.snowflake); const query = `from:${match.groups.name} since:${range.since} until:${range.until} include:nativeretweets`; - location.href = `https://twitter.com/search?f=live&q=${encodeURIComponent(query)}`; + location.href = `https://x.com/search?f=live&q=${encodeURIComponent(query)}`; } main(); -})(); +})();
  • /*
     * @title Twitter(現X)の前後ポストを表示する
     * @description 個別 Tweet の URL から前後 1 時間の投稿を RT を含めて表示する
     * @include https://x.com/*
     * @license MIT License
     * @javascript_url
     */
    
    (() => {
    const TWITTER_EPOCH = 1288834974657;
    
    // 'YYYY-MM-DD_HH:mm:ss_JST'形式で日付をフォーマット
    const formatTwitterDate = (date) => {
      const pad2 = (n) => n.toString().padStart(2, '0');
    
      const year = date.getFullYear();
      const month = pad2(date.getMonth() + 1);
      const day = pad2(date.getDate());
      const hour = pad2(date.getHours());
      const minute = pad2(date.getMinutes());
      const second = pad2(date.getSeconds());
      return `${year}-${month}-${day}_${hour}:${minute}:${second}_JST`;
    }
    
    // Snowflake ID 文字列を受け取り、前後 60 分の since, until を返す
    const getSearchRange = (snowflakeStr) => {
      const snowflake = BigInt(snowflakeStr);
      // 41 bits: millisec from TWITTER_EPOCH
      // 10 bits: worker_id
      // 12 bits: sequence number
      // なので 22 bit 右シフトする
      const timestamp = Number(snowflake >> 22n) + TWITTER_EPOCH;
    
      return {
        since: formatTwitterDate(new Date(timestamp - 60 * 60 * 1000)), // 60 分前
        until: formatTwitterDate(new Date(timestamp + 60 * 60 * 1000)), // 60 分後
      };
    }
    
    const main = () => {
      const match = location.href.match(/https:\/\/(twitter|x)\.com\/(?<name>[^/]+)\/status\/(?<snowflake>\d+)/);
      if (!match) {
        alert('このページは Twitter の個別 Tweet ではありません');
        return;
      }
      const range = getSearchRange(match.groups.snowflake);
      const query = `from:${match.groups.name} since:${range.since} until:${range.until} include:nativeretweets`;
      location.href = `https://x.com/search?f=live&q=${encodeURIComponent(query)}`;
    }
    
    main();
    
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。