xnotif 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +73 -62
  2. package/package.json +2 -5
package/dist/index.mjs CHANGED
@@ -1,6 +1,4 @@
1
1
  import { EventEmitter } from "events";
2
- import { TwitterOpenApi } from "twitter-openapi-typescript";
3
- import { BaseAPI } from "twitter-openapi-typescript-generated";
4
2
 
5
3
  //#region src/utils.ts
6
4
  function base64urlToBuffer(b64url) {
@@ -274,70 +272,83 @@ var AutopushClient = class {
274
272
 
275
273
  //#endregion
276
274
  //#region src/twitter.ts
277
- const API_HEADER_NAMES = [
278
- "Accept",
279
- "x-twitter-client-language",
280
- "Priority",
281
- "Referer",
282
- "Sec-Fetch-Dest",
283
- "Sec-Ch-Ua-Platform",
284
- "Sec-Fetch-Mode",
285
- "x-csrf-token",
286
- "x-client-uuid",
287
- "x-guest-token",
288
- "Sec-Ch-Ua",
289
- "x-twitter-active-user",
290
- "user-agent",
291
- "Accept-Language",
292
- "Sec-Fetch-Site",
293
- "x-twitter-auth-type",
294
- "Sec-Ch-Ua-Mobile",
295
- "Accept-Encoding"
296
- ];
297
- var NotificationApi = class extends BaseAPI {
298
- constructor(config) {
299
- super(config);
300
- }
301
- async post(path, body, initOverride) {
302
- const headers = {};
303
- const apiKey = this.configuration.apiKey;
304
- if (apiKey) for (const name of API_HEADER_NAMES) {
305
- const value = await apiKey(name);
306
- if (value) headers[name] = value;
307
- }
308
- const accessToken = this.configuration.accessToken;
309
- if (accessToken) headers["Authorization"] = `Bearer ${await accessToken()}`;
310
- headers["Content-Type"] = "application/json";
311
- return this.request({
312
- path,
313
- method: "POST",
314
- headers,
315
- body
316
- }, initOverride);
317
- }
318
- };
319
- async function createClient$1(cookies) {
320
- return new TwitterOpenApi().getClientFromCookies(cookies);
275
+ const BEARER_TOKEN = "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA";
276
+ const HEADER_URL = "https://raw.githubusercontent.com/fa0311/latest-user-agent/refs/heads/main/header.json";
277
+ const PAIR_URL = "https://raw.githubusercontent.com/fa0311/x-client-transaction-pair-dict/refs/heads/main/pair.json";
278
+ async function generateTransactionId(method, path, key, animationKey) {
279
+ const DEFAULT_KEYWORD = "obfiowerehiring";
280
+ const ADDITIONAL_RANDOM_NUMBER = 3;
281
+ const timeNow = Math.floor((Date.now() - 1682924400 * 1e3) / 1e3);
282
+ const timeNowBytes = [
283
+ timeNow & 255,
284
+ timeNow >> 8 & 255,
285
+ timeNow >> 16 & 255,
286
+ timeNow >> 24 & 255
287
+ ];
288
+ const data = `${method}!${path}!${timeNow}${DEFAULT_KEYWORD}${animationKey}`;
289
+ const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(data));
290
+ const hashBytes = Array.from(new Uint8Array(hashBuffer));
291
+ const keyBytes = Array.from(Buffer.from(key, "base64"));
292
+ const randomNum = Math.floor(Math.random() * 256);
293
+ const bytesArr = [
294
+ ...keyBytes,
295
+ ...timeNowBytes,
296
+ ...hashBytes.slice(0, 16),
297
+ ADDITIONAL_RANDOM_NUMBER
298
+ ];
299
+ const out = new Uint8Array([randomNum, ...bytesArr.map((b) => b ^ randomNum)]);
300
+ return Buffer.from(out).toString("base64").replace(/=/g, "");
321
301
  }
322
- function makeInitOverride(client, path) {
323
- return client.initOverrides({
324
- "@method": "POST",
325
- "@path": path
326
- });
302
+ function encodeCookies(cookies) {
303
+ return Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join("; ");
304
+ }
305
+ async function createClient$1(cookies) {
306
+ const [headerJson, pairs] = await Promise.all([fetch(HEADER_URL).then((r) => r.json()), fetch(PAIR_URL).then((r) => r.json())]);
307
+ const ignore = new Set(["host", "connection"]);
308
+ const headers = {
309
+ ...Object.fromEntries(Object.entries(headerJson["chrome-fetch"]).filter(([k]) => !ignore.has(k))),
310
+ "accept-encoding": "identity",
311
+ pragma: "no-cache",
312
+ referer: "https://x.com",
313
+ priority: "u=1, i",
314
+ "x-twitter-client-language": "en",
315
+ "x-twitter-active-user": "yes",
316
+ authorization: `Bearer ${BEARER_TOKEN}`
317
+ };
318
+ if (cookies["ct0"]) {
319
+ headers["x-twitter-auth-type"] = "OAuth2Session";
320
+ headers["x-csrf-token"] = cookies["ct0"];
321
+ }
322
+ if (cookies["gt"]) headers["x-guest-token"] = cookies["gt"];
323
+ return {
324
+ headers,
325
+ cookies,
326
+ pairs
327
+ };
327
328
  }
328
329
  async function registerPush(client, subscription) {
329
- const api = new NotificationApi(client.config);
330
330
  const path = "/1.1/notifications/settings/login.json";
331
- const res = await api.post(path, { push_device_info: {
332
- os_version: "Web/Chrome",
333
- udid: "Web/Chrome",
334
- env: 3,
335
- locale: "en",
336
- protocol_version: 1,
337
- token: subscription.endpoint,
338
- encryption_key1: subscription.p256dh,
339
- encryption_key2: subscription.auth
340
- } }, makeInitOverride(client, path));
331
+ const pair = client.pairs[Math.floor(Math.random() * client.pairs.length)];
332
+ const tid = await generateTransactionId("POST", path, pair.verification, pair.animationKey);
333
+ const res = await fetch(`https://x.com/i/api${path}`, {
334
+ method: "POST",
335
+ headers: {
336
+ ...client.headers,
337
+ cookie: encodeCookies(client.cookies),
338
+ "content-type": "application/json",
339
+ "x-client-transaction-id": tid
340
+ },
341
+ body: JSON.stringify({ push_device_info: {
342
+ os_version: "Web/Chrome",
343
+ udid: "Web/Chrome",
344
+ env: 3,
345
+ locale: "en",
346
+ protocol_version: 1,
347
+ token: subscription.endpoint,
348
+ encryption_key1: subscription.p256dh,
349
+ encryption_key2: subscription.auth
350
+ } })
351
+ });
341
352
  if (!res.ok) {
342
353
  const text = await res.text();
343
354
  throw new Error(`login.json failed (${res.status}): ${text}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xnotif",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Receive Twitter/X push notifications programmatically via Mozilla Autopush",
5
5
  "keywords": [
6
6
  "autopush",
@@ -30,10 +30,7 @@
30
30
  "import": "./dist/index.mjs"
31
31
  }
32
32
  },
33
- "dependencies": {
34
- "twitter-openapi-typescript": "^0.0.55",
35
- "twitter-openapi-typescript-generated": "^0.0.38"
36
- },
33
+ "dependencies": {},
37
34
  "devDependencies": {
38
35
  "@types/node": "^22.0.0",
39
36
  "@vitest/coverage-v8": "^4.0.18",