scripter-x 1.0.25 → 1.0.26

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/package.json +1 -1
  2. package/src/flipkart.js +61 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scripter-x",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "ScripterX — local Flipkart session extractor (runs on your residential IP, syncs to marthunt)",
5
5
  "type": "module",
6
6
  "bin": {
package/src/flipkart.js CHANGED
@@ -88,34 +88,68 @@ export class FlipkartLogin {
88
88
  this.isNewUser = false; // set by sendOtp: new number → verify as SIGNUP, not LOGIN
89
89
  }
90
90
 
91
- async sendOtp(mobile) {
92
- await throttle.wait();
91
+ // Send-OTP variants. The NATIVE app flow (okhttp UA, .net host, app body with
92
+ // addAppHash/phoneNumberFormat) is far less throttled than the browser/web flow —
93
+ // it mirrors exactly what the Flipkart app sends (captured). We try native first,
94
+ // and fall back to the web flow only if native yields no requestId.
95
+ async _sendNative(mobile) {
96
+ const body = { actionRequestContext: {
97
+ type: 'LOGIN_IDENTITY_VERIFY', loginId: mobile.slice(-10), loginIdPrefix: '+91',
98
+ phoneNumberFormat: 'E164', addAppHash: true, loginType: 'MOBILE',
99
+ verificationType: 'OTP', sourceContext: 'DEFAULT',
100
+ correlationId: null, snaFlowId: null, clientQueryParamMap: null } };
101
+ const res = await fetch(`${NATIVE_HOST}/1/action/view`, {
102
+ method: 'POST', body: JSON.stringify(body),
103
+ headers: {
104
+ 'User-Agent': 'okhttp/4.9.2', 'X-User-Agent': nativeUa(this.vid),
105
+ 'Content-Type': 'application/json; charset=UTF-8',
106
+ 'x-request-metaInfo': '{"actionType":"LOGIN_IDENTITY_VERIFY","pageUri":"questContext"}',
107
+ },
108
+ });
109
+ return res;
110
+ }
111
+
112
+ async _sendWeb(mobile) {
93
113
  const body = { actionRequestContext: {
94
114
  type: 'LOGIN_IDENTITY_VERIFY', loginIdPrefix: '+91', loginId: mobile.slice(-10),
95
115
  clientQueryParamMap: { ret: '/my-account', entryPage: 'DEFAULT' },
96
116
  loginType: 'MOBILE', verificationType: 'OTP', screenName: 'LOGIN_V4_MOBILE',
97
117
  triggerSna: false, sourceContext: 'DEFAULT' } };
98
- let res;
99
- try {
100
- res = await fetch(`${WEB_HOST}/1/action/view`, {
101
- method: 'POST', body: JSON.stringify(body),
102
- headers: {
103
- Accept: '*/*', 'Content-Type': 'application/json',
104
- 'X-User-Agent': FKUA, 'User-Agent': FKUA,
105
- Origin: 'https://www.flipkart.com', Referer: 'https://www.flipkart.com/',
106
- 'sec-ch-ua-mobile': '?1', 'sec-ch-ua-platform': '"iOS"', 'sec-fetch-site': 'same-site',
107
- 'x-request-metaInfo': '{"actionType":"LOGIN_IDENTITY_VERIFY","pageUri":"questContext"}',
108
- },
109
- });
110
- } catch (e) { throw new TransientError(`send network error: ${e.message}`); }
111
- // 529/5xx = Flipkart overloaded / blocking this IP — retryable.
112
- if (res.status === 429 || res.status === 529 || res.status >= 500) {
113
- throw new TransientError(`Flipkart busy on send (HTTP ${res.status})`);
118
+ return fetch(`${WEB_HOST}/1/action/view`, {
119
+ method: 'POST', body: JSON.stringify(body),
120
+ headers: {
121
+ Accept: '*/*', 'Content-Type': 'application/json',
122
+ 'X-User-Agent': FKUA, 'User-Agent': FKUA,
123
+ Origin: 'https://www.flipkart.com', Referer: 'https://www.flipkart.com/',
124
+ 'sec-ch-ua-mobile': '?1', 'sec-ch-ua-platform': '"iOS"', 'sec-fetch-site': 'same-site',
125
+ 'x-request-metaInfo': '{"actionType":"LOGIN_IDENTITY_VERIFY","pageUri":"questContext"}',
126
+ },
127
+ });
128
+ }
129
+
130
+ async sendOtp(mobile) {
131
+ await throttle.wait();
132
+ // The WEB flow establishes the SN session cookie that VERIFY needs, so it runs
133
+ // FIRST. If it throttles (200 but no requestId), retry on the NATIVE app flow —
134
+ // which is much less throttled and now reuses the SN web just set. Either flow
135
+ // that yields a requestId wins.
136
+ let res, data, rid;
137
+ for (const send of [this._sendWeb.bind(this), this._sendNative.bind(this)]) {
138
+ try {
139
+ res = await send(mobile);
140
+ } catch (e) { throw new TransientError(`send network error: ${e.message}`); }
141
+ // 529/5xx = Flipkart overloaded / blocking this IP — retryable.
142
+ if (res.status === 429 || res.status === 529 || res.status >= 500) {
143
+ throw new TransientError(`Flipkart busy on send (HTTP ${res.status})`);
144
+ }
145
+ parseCookies(res, this.jar);
146
+ // capture SN as soon as any flow provides it (web normally does)
147
+ if (this.jar.SN) { this.sn = this.jar.SN; this.vid = this.sn.split('.')[0]; }
148
+ data = await res.json().catch(() => ({}));
149
+ rid = data?.RESPONSE?.actionResponseContext?.requestId;
150
+ if (rid) break; // got it — done
114
151
  }
115
- parseCookies(res, this.jar);
116
- const data = await res.json().catch(() => ({}));
117
- const rid = data?.RESPONSE?.actionResponseContext?.requestId;
118
- // no requestId on a 200 = soft anti-bot throttle — also retryable (often clears on retry).
152
+ // no requestId after both flows = soft anti-bot throttle — retryable (often clears on retry).
119
153
  if (!rid) throw new TransientError('blocked while sending OTP (no requestId — throttled)');
120
154
  this.reqId = rid;
121
155
  // NEW vs EXISTING account: for an UNREGISTERED number FK still sends the OTP but
@@ -205,11 +239,15 @@ export class FlipkartLogin {
205
239
  parseCookies(res, fresh);
206
240
  const ud = fresh.ud || this.jar.ud || '';
207
241
  const vd = fresh.vd || '';
242
+ // secureToken: prefer the value FK returns in the SESSION (vid:vid); fall back to
243
+ // computing it from vid. (Was a free var in the old single-method verify — the
244
+ // SIGNUP/LOGIN split moved its definition into _verifyWith, hence recompute here.)
245
+ const secureToken = env.secureToken || (this.vid ? `${this.vid}:${this.vid}` : '');
208
246
  return {
209
247
  accountId: env.accountId || '', at: env.at || '', rt: env.rt || '', sn: env.sn || '',
210
248
  secureToken, secureCookie: this.jar.S || '', ud, vd,
211
249
  cookie_T: fresh.T || this.jar.T || '', // T cookie — needed for the email-attach calls
212
- visitId: (env.sn || '').split('.')[0], nsid: env.nsid || '',
250
+ visitId: env.vid || (env.sn || '').split('.')[0], nsid: env.nsid || '',
213
251
  mobileNo: mobile.slice(-10), isLoggedIn: true, rt_expires_at: rtExpiry(env.rt || ''),
214
252
  };
215
253
  }