Skip to content

Instantly share code, notes, and snippets.

@fa0311
Last active November 10, 2025 04:41
Show Gist options
  • Save fa0311/f36b00d36d6c4cf9e73c0dd5aefe3516 to your computer and use it in GitHub Desktop.
Save fa0311/f36b00d36d6c4cf9e73c0dd5aefe3516 to your computer and use it in GitHub Desktop.
twitter-inject-request

WebDriverで使えるhookスクリプト

// 初期化
const __origApply = Function.prototype.apply;
const client = await new Promise((resolve) => {
    Function.prototype.apply = function (thisArg, argsArray) {
        if (thisArg && typeof thisArg === 'object' && thisArg.dispatch === this) {
            __origApply.call(console.log, console, ['[dispatch.apply]', thisArg, argsArray]);
            resolve(thisArg);
        }
        return __origApply.call(this, thisArg, argsArray);
    };
});
Function.prototype.apply = __origApply
const request = (query) => client.dispatch.apply(client, [query]);

Function.prototype.apply に hook して thisArg を取得してhookをもとに戻す

実際にリクエストにhookするため、初期化はリクエストが送られるまで待機する

(スクロールなどをして初期化させる)

// 使い方
const res = await request(
    {
        "data": {
            "variables": {
                "tweet_text": `Hello, world! It's ${new Date().toLocaleString()}. @faa0311 is a god.`,
                "dark_request": false,
                "media": {
                    "media_entities": [],
                    "possibly_sensitive": false
                },
                "semantic_annotation_ids": [],
                "disallowed_reply_options": null
            },
            "features": {
                "premium_content_api_read_enabled": false,
                "communities_web_enable_tweet_community_results_fetch": true,
                "c9s_tweet_anatomy_moderator_badge_enabled": true,
                "responsive_web_grok_analyze_button_fetch_trends_enabled": false,
                "responsive_web_grok_analyze_post_followups_enabled": true,
                "responsive_web_jetfuel_frame": true,
                "responsive_web_grok_share_attachment_enabled": true,
                "responsive_web_edit_tweet_api_enabled": true,
                "graphql_is_translatable_rweb_tweet_is_translatable_enabled": true,
                "view_counts_everywhere_api_enabled": true,
                "longform_notetweets_consumption_enabled": true,
                "responsive_web_twitter_article_tweet_consumption_enabled": true,
                "tweet_awards_web_tipping_enabled": false,
                "responsive_web_grok_show_grok_translated_post": false,
                "responsive_web_grok_analysis_button_from_backend": true,
                "creator_subscriptions_quote_tweet_preview_enabled": false,
                "longform_notetweets_rich_text_read_enabled": true,
                "longform_notetweets_inline_media_enabled": true,
                "payments_enabled": false,
                "profile_label_improvements_pcf_label_in_post_enabled": true,
                "responsive_web_profile_redirect_enabled": false,
                "rweb_tipjar_consumption_enabled": true,
                "verified_phone_label_enabled": false,
                "articles_preview_enabled": true,
                "responsive_web_grok_community_note_auto_translation_is_enabled": false,
                "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false,
                "freedom_of_speech_not_reach_fetch_enabled": true,
                "standardized_nudges_misinfo": true,
                "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true,
                "responsive_web_grok_image_annotation_enabled": true,
                "responsive_web_grok_imagine_annotation_enabled": true,
                "responsive_web_graphql_timeline_navigation_enabled": true,
                "responsive_web_enhance_cards_enabled": false
            },
            "queryId": "MtyT_TbO2PpwaFHNPK2qoQ"
        },
        "headers": {
            "content-type": "application/json"
        },
        "method": "POST",
        "path": "/graphql/MtyT_TbO2PpwaFHNPK2qoQ/CreateTweet"
    }
);
console.log(res);
@fa0311
Copy link
Author

fa0311 commented Nov 9, 2025

main.js の 2万行辺りにあるこれにhookします

var u = P({
  client: this.client,
  dispatch: function() {
    return d.dispatch.apply(d, arguments)
  },
  featureSwitches: n
})

検索するときは空白を抜いた return d.dispatch.apply(d,arguments) とかで出てくると思います

@fa0311
Copy link
Author

fa0311 commented Nov 9, 2025

@fa0311
Copy link
Author

fa0311 commented Nov 9, 2025

Webpackのモジュール読み込みにhookする
ロードが実行される前に挿入する必要がある

(async () => {
  const result = [];
  const origCall = Function.prototype.call;
  Function.prototype.call = function (thisArg, ...args) {
    const module = args[0];
    const ret = origCall.apply(this, [thisArg, ...args]);
    try {
      const exp = module.exports;
      if (exp.operationName) {
        console.log(exp.operationName);
        result.push(exp);
      }
    } catch (_) {}
    return ret;
  };
  await new Promise((resolve) => setTimeout(resolve, 5000));
  Function.prototype.call = origCall;
  console.log(result);
})();

Webpackの性質上使われるタイミングにならないとロードされない
適当に5秒待つ

@fa0311
Copy link
Author

fa0311 commented Nov 9, 2025

FutureFlagは初期化時に消されるのでそれよりも前に参照する

window.__INITIAL_STATE__["featureSwitch"]["defaultConfig"];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment