summaryrefslogtreecommitdiff
path: root/packages/tweetdeck/tests/python.ts
blob: 73a174bb8655fc83d520db3a591c221248d9d500 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { TransactionIdGenerator as TransactionIdGenerator } from "../src/lib/fetching/python";

export const cookie = `auth_token=651d779cb99b54e7ce81bf8f8050e597c452b50b;gt=1868678280205029867;guest_id=v1%3A176150648682600359;__cf_bm=mfj12NkVbU0uPRz84om3BecRhAwv.8CCo.w0B4Gw45o-1761513085.2856057-1.0.1.1-gHPgX0w_GRneFL_GLZ5kPSXd7VCe4EuZnxN9FeKlpRg5PR5CCFrHazEjrE_EM53YxTbd1hDJoaWNXSU.Abr7FmBF5aCso.Jqn6M1ycwSXLhvQ3nTdZXRfiGpDrQugR6q;twid=u%3D1710606417324015616;g_state={"i_p":1733335699025,"i_l":1};external_referer=8e8t2xd8A2w%3D|0|F8C7rVpldvH1T6C9tW%2FBRRUSYlUEkKIshjiEn%2Br%2F70sqqRYvQQ%2FYUQ%3D%3D;auth_multi="1761426801275097088:f10fe04281e0bdf50e9dba703e2cba86adb84847";lang=en;night_mode=2;__cf_bm=koa1LKRskRZYwhHNG6YcIcSFN87JPc24Tb3umQqxxxU-1761507653-1.0.1.1-9OQutUp3hCAW8o5Sr8S0h.uE0eTWkxGRYiiESZu6WW9Q_Y_out9M6G9FqS5n9BVn1svlAUT97_FyLZosboQ45Q0M_HZ4F_rYtd3ojsAm6n8;__cuid=b4c86e91126647b5b7e07dadeac8ec2b;ct0=678e86afdb647f32835e25d1204c99af50b95d0e9ebcaf9db2d837fb9c4e48759a0e218c4845337f429c10e8d95f8a97cb9a7cee84c1bc5a7b522f440d5b6824466b6f4b62c328cc32d980a22fb00745;dnt=1;guest_id_ads=v1%3A176150648682600359;guest_id_marketing=v1%3A176150648682600359;kdt=camHTtNmw0pQUK0ZHaYd6jJnJRRYihbuZ0Ii7yN2;personalization_id="v1_KKOJ4dZoYTZuKLY9eadfIg=="`;

async function initTransactionIdGenerator(): Promise<TransactionIdGenerator> {
  const MAX_REDIRECTS = 5;
  let currentUrl = "https://x.com";
  let html = "";
  const headers: Record<string, string> = {
    "User-Agent":
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
    "Accept-Language": "en-US,en;q=0.9",
    Cookie: cookie,
  };

  try {
    for (
      let redirectCount = 0;
      redirectCount < MAX_REDIRECTS;
      redirectCount++
    ) {
      const response = await fetch(currentUrl, { headers });
      headers["Cookie"] = cookie;

      if (!response.ok) {
        throw new Error(
          `Failed fetching ${currentUrl}: ${response.status} ${response.statusText}`,
        );
      }

      html = await response.text();

      // Check for meta refresh redirect
      const metaRefreshMatch = html.match(
        /<meta\s+http-equiv=["']refresh["']\s+content=["'][^;]+;\s*url=([^"']+)/i,
      );
      const migrationRedirectionRegex =
        /(http(?:s)?:\/\/(?:www\.)?(?:twitter|x)\.com(?:\/x)?\/migrate(?:[\/?])?tok=[a-zA-Z0-9%\-_]+)/i;
      const bodyMatch = html.match(migrationRedirectionRegex);
      let migrationUrl = metaRefreshMatch?.[1] || bodyMatch?.[0];

      if (migrationUrl) {
        currentUrl = migrationUrl;
        continue;
      }

      // Handle migration form
      const formMatch = html.match(
        /<form[^>]*?(?:name=["']f["']|action=["']https:\/\/x\.com\/x\/migrate["'])[^>]*>([\s\S]*?)<\/form>/i,
      );
      if (formMatch) {
        const formContent = formMatch[1];
        const actionMatch = formMatch[0].match(/action=["']([^"']+)["']/i);
        const methodMatch = formMatch[0].match(/method=["']([^"']+)["']/i);

        const formAction = actionMatch?.[1] || "https://x.com/x/migrate";
        const formMethod = methodMatch?.[1]?.toUpperCase() || "POST";
        const formUrl = new URL(formAction, currentUrl);
        if (!formUrl.searchParams.has("mx")) {
          formUrl.searchParams.set("mx", "2");
        }

        const payload = new URLSearchParams();
        const inputRegex =
          /<input[^>]*?name=["']([^"']+)["'][^>]*?(?:value=["']([^"']*)["'])?[^>]*?>/gi;
        let inputMatch;
        while ((inputMatch = inputRegex.exec(formContent!)) !== null) {
          payload.append(inputMatch[1]!, inputMatch[2] || "");
        }

        const formHeaders = {
          ...headers,
          "Content-Type": "application/x-www-form-urlencoded",
          Referer: currentUrl,
        };

        const response = await fetch(formUrl.toString(), {
          method: formMethod,
          headers: formHeaders,
          body: payload,
          redirect: "manual",
        });
        headers["Cookie"] = cookie;

        if (
          response.status >= 300 &&
          response.status < 400 &&
          response.headers.has("location")
        ) {
          currentUrl = new URL(
            response.headers.get("location")!,
            currentUrl,
          ).toString();
          continue;
        }

        if (!response.ok) {
          throw new Error(
            `Migration form submission failed: ${response.status} ${response.statusText}`,
          );
        }

        html = await response.text();

        const subsequentMetaRefresh = html.match(
          /<meta\s+http-equiv=["']refresh["']\s+content=["'][^;]+;\s*url=([^"']+)/i,
        );
        const subsequentBodyMatch = html.match(migrationRedirectionRegex);

        if (subsequentMetaRefresh?.[1] || subsequentBodyMatch?.[0]) {
          currentUrl = subsequentMetaRefresh?.[1] || subsequentBodyMatch![0];
          if (!currentUrl.startsWith("http")) {
            currentUrl = new URL(currentUrl, response.url).toString();
          }
          continue;
        }
      }

      break;
    }

    if (!html) {
      throw new Error("Failed to retrieve HTML after potential migrations.");
    }

    const transactionIdGenerator = new TransactionIdGenerator(html);
    return transactionIdGenerator;
  } catch (error: any) {
    throw new Error(
      `Transaction ID initialization failed: ${error.message || error}`,
    );
  }
}

async function generateTransactionId(
  method: string,
  path: string,
): Promise<string> {
  const gen = new TransactionIdGenerator("");
  await gen.init();
  // const gen = await initTransactionIdGenerator();

  return gen.getTransactionId(method, path);
}

const res = await generateTransactionId(
  "GET",
  "i/api/graphql/2AtIgw7Kz26sV6sEBrQjSQ/UsersByRestIds",
);
console.log({ res });