scripter-x 1.0.26 → 1.0.28

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scripter-x",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "ScripterX — local Flipkart session extractor (runs on your residential IP, syncs to marthunt)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,9 +3,9 @@ import { extractOtp } from '../flipkart.js';
3
3
 
4
4
  const BASE = 'https://api.tempotp.online';
5
5
  // serviceId -> cost (₹). Keep ids as strings (they go straight into the GET params).
6
- export const SERVICES = { '1040': 10.0, '940': 16.0, '2451': 12.5, '2484': 18.0 };
6
+ export const SERVICES = { '1040': 10.0, '940': 16.0, '2451': 12.5, '2452': 12.5, '2453': 12.0, '2484': 18.0 };
7
7
  // optional friendly names shown in the service picker
8
- export const SERVICE_NAMES = { '1040': 'Flipkart', '940': 'Flipkart', '2451': 'Shopsy/Flipkart 1 (SERVER 16)', '2484': 'ALL 6 DIGIT (SERVER 17 [ALL])' };
8
+ export const SERVICE_NAMES = { '1040': 'Flipkart', '940': 'Flipkart', '2451': 'Shopsy/Flipkart 1 (SERVER 16)', '2452': 'Shopsy/Flipkart 2 (SERVER 16)', '2453': 'Shopsy/Flipkart 3 (SERVER 16)', '2484': 'ALL 6 DIGIT (SERVER 17 [ALL])' };
9
9
  export const DEFAULT_SERVICE = '940';
10
10
  const COUNTRY = '22';
11
11
 
package/src/ui/RunView.js CHANGED
@@ -30,14 +30,35 @@ function SlotRow({ slot, mobile, phase, detail, wait }) {
30
30
  );
31
31
  }
32
32
 
33
- function ResultRow({ status, mobile, detail, coupon, cost }) {
34
- const st = STATUS[status] || STATUS.pending;
33
+ // Map a result to a clean { glyph, label, color, note } for display. Driven by the
34
+ // worker's `category` so every outcome reads consistently (no "successed"/"success" mix).
35
+ function resultDisplay({ status, category, detail }) {
36
+ const C = COLORS;
37
+ switch (category) {
38
+ case 'fresh': return { glyph: '🆕', label: 'fresh account', color: C.accent, note: 'new account created' };
39
+ case 'coupon': return { glyph: '✓', label: 'success', color: C.success, note: '₹100 coupon available' };
40
+ case 'no_coupon': return { glyph: '✓', label: 'success', color: C.success, note: 'no ₹100 coupon' };
41
+ case 'extracted': return { glyph: '✓', label: 'success', color: C.success, note: 'extracted' };
42
+ case 'timeout': return { glyph: '⏱', label: 'timeout', color: C.warn, note: 'no OTP in 90s' };
43
+ case 'cancelled_user': return { glyph: '⊘', label: 'cancelled', color: C.warn, note: 'cancelled by user' };
44
+ case 'wrong_otp': return { glyph: '✗', label: 'failed', color: C.danger, note: 'wrong OTP code' };
45
+ case 'goal_reached': return { glyph: '•', label: 'skipped', color: C.muted, note: 'goal reached' };
46
+ case 'failed': return { glyph: '✗', label: 'failed', color: C.danger, note: detail || 'failed' };
47
+ default: {
48
+ const st = STATUS[status] || STATUS.pending;
49
+ const note = status === 'success' ? 'extracted' : (detail || '');
50
+ return { glyph: st.glyph, label: status, color: st.color, note };
51
+ }
52
+ }
53
+ }
54
+
55
+ function ResultRow({ status, category, mobile, detail, cost }) {
56
+ const d = resultDisplay({ status, category, detail });
35
57
  const mob = (mobile || '—').slice(-10) || '—';
36
- const text = status === 'success' ? (coupon ? '₹100 coupon' : 'extracted') : (detail || '');
37
58
  return h(Box, null,
38
59
  h(Box, { width: 13 }, h(Text, null, mob)),
39
- h(Box, { width: 13 }, h(Text, { color: st.color }, `${st.glyph} ${status}`)),
40
- h(Box, { flexGrow: 1 }, h(Text, { color: 'gray' }, text)),
60
+ h(Box, { width: 14 }, h(Text, { color: d.color }, `${d.glyph} ${d.label}`)),
61
+ h(Box, { flexGrow: 1 }, h(Text, { color: 'gray' }, d.note)),
41
62
  h(Box, { width: 7, justifyContent: 'flex-end' }, h(Text, { color: 'gray' }, cost ? `₹${cost}` : '')),
42
63
  );
43
64
  }
@@ -76,7 +97,7 @@ export function RunView({ controller }) {
76
97
  slotList.length ? slotList.map((sl) => h(SlotRow, { key: sl.slot, ...sl }))
77
98
  : h(Text, { color: 'gray' }, 'starting…')),
78
99
  h(Panel, { title: 'results' },
79
- lastRows.length ? lastRows.map((r, i) => h(ResultRow, { key: i, status: r.status, mobile: r.mobile, detail: r.detail, coupon: r.coupon_eligible, cost: r.cost }))
100
+ lastRows.length ? lastRows.map((r, i) => h(ResultRow, { key: i, status: r.status, category: r.category, mobile: r.mobile, detail: r.detail, cost: r.cost }))
80
101
  : h(Text, { color: 'gray' }, 'waiting for first result…')),
81
102
  h(Box, { borderStyle: 'round', borderColor: COLORS.accent, paddingX: 1 },
82
103
  h(Text, { color: COLORS.success }, `✓ ${stats.succeeded}`),
package/src/util.js CHANGED
@@ -61,36 +61,51 @@ function writeAccounts(accounts, { name, out, checkMinutes } = {}) {
61
61
  return dest;
62
62
  }
63
63
 
64
- // Partition by coupon_eligible when the Minutes check was requested.
65
- const minutesFree = []; // coupon_eligible=true no Minutes order, ₹100 coupon available
66
- const minutesUsed = []; // coupon_eligible=false already placed a Minutes order
67
- const unchecked = []; // coupon_eligible absent check wasn't done
64
+ // Partition into the THREE buckets the user wants:
65
+ // fresh brand-new account just created (signup)
66
+ // coupon → existing account with the ₹100 Minutes coupon available
67
+ // noCoupon existing account, coupon already used
68
+ // plus `unchecked` (Minutes wasn't checked). Categorisation prefers the explicit
69
+ // `category` set by the worker; falls back to is_new_account / coupon_eligible.
70
+ const fresh = [];
71
+ const coupon = [];
72
+ const noCoupon = [];
73
+ const unchecked = [];
68
74
 
69
75
  let hasChecked = false;
70
76
  for (const a of accounts) {
71
77
  const sess = a.session;
72
78
  if (!sess) continue;
79
+ const cat = a.category;
73
80
  const eligible = a.coupon_eligible;
74
- if (eligible !== undefined && eligible !== null) {
81
+ if (cat === 'fresh' || a.is_new_account) {
82
+ fresh.push(sess);
83
+ hasChecked = true;
84
+ } else if (cat === 'coupon' || eligible === true) {
85
+ coupon.push(sess);
86
+ hasChecked = true;
87
+ } else if (cat === 'no_coupon' || eligible === false) {
88
+ noCoupon.push(sess);
75
89
  hasChecked = true;
76
- (eligible ? minutesFree : minutesUsed).push(sess);
77
90
  } else {
78
91
  unchecked.push(sess);
79
92
  }
80
93
  }
81
94
 
82
95
  const results = [];
83
- if (hasChecked || (checkMinutes && (minutesFree.length || minutesUsed.length))) {
84
- const destFree = write('-minutes-free', minutesFree);
85
- if (destFree) results.push({ label: '🟢 minutes-free (₹100 coupon)', path: destFree, count: minutesFree.length });
86
- const destUsed = write('-minutes-used', minutesUsed);
87
- if (destUsed) results.push({ label: '🔴 minutes-used (coupon gone)', path: destUsed, count: minutesUsed.length });
96
+ if (hasChecked || (checkMinutes && (fresh.length || coupon.length || noCoupon.length))) {
97
+ const dFresh = write('-fresh-new-account', fresh);
98
+ if (dFresh) results.push({ label: '🆕 fresh new account', path: dFresh, count: fresh.length });
99
+ const dCoupon = write('-with-100-coupon', coupon);
100
+ if (dCoupon) results.push({ label: '🟢 ₹100 coupon available', path: dCoupon, count: coupon.length });
101
+ const dNo = write('-no-100-coupon', noCoupon);
102
+ if (dNo) results.push({ label: '🔴 no ₹100 coupon', path: dNo, count: noCoupon.length });
88
103
  if (unchecked.length) {
89
- const destUnk = write('-unchecked', unchecked);
90
- if (destUnk) results.push({ label: '⚪ unchecked', path: destUnk, count: unchecked.length });
104
+ const dUnk = write('-unchecked', unchecked);
105
+ if (dUnk) results.push({ label: '⚪ unchecked', path: dUnk, count: unchecked.length });
91
106
  }
92
107
  } else {
93
- const allSessions = [...minutesFree, ...minutesUsed, ...unchecked];
108
+ const allSessions = [...fresh, ...coupon, ...noCoupon, ...unchecked];
94
109
  const dest = write('', allSessions);
95
110
  if (dest) results.push({ label: 'combined', path: dest, count: allSessions.length });
96
111
  }
package/src/worker.js CHANGED
@@ -78,6 +78,8 @@ export class Worker {
78
78
  if (res.status === 'success' && res.session) {
79
79
  this.results.push({
80
80
  id_no: res.id_no, mobile: res.mobile || '', session: res.session,
81
+ category: res.category, // 'fresh' | 'coupon' | 'no_coupon' | 'extracted'
82
+ is_new_account: !!res.is_new_account,
81
83
  minutes_checked: !!res.minutes_checked,
82
84
  coupon_eligible: res.coupon_eligible ?? undefined,
83
85
  has_minutes_order: res.has_minutes_order ?? undefined,
@@ -152,18 +154,25 @@ export class Worker {
152
154
  this._emit(slot, { phase: 'done', detail: 'goal reached' });
153
155
  res.status = 'cancelled';
154
156
  res.detail = 'goal reached';
157
+ res.category = 'goal_reached';
155
158
  return res;
156
159
  }
157
160
 
158
161
  this._emit(slot, { mobile: '', phase: 'renting', detail: 'requesting a number', wait: 0 });
159
162
  const number = await this._rent(slot);
160
- if (!number) { res.status = 'cancelled'; res.detail = 'stopped while renting'; return res; }
163
+ if (!number) {
164
+ res.status = 'cancelled';
165
+ res.detail = this.stopped ? 'cancelled by user' : 'stopped while renting';
166
+ res.category = this.stopped ? 'cancelled_user' : 'goal_reached';
167
+ return res;
168
+ }
161
169
  if (this.stats.succeeded >= this.requested) {
162
170
  this.log(`goal reached by another slot; releasing ${number.mobile} immediately`);
163
171
  this._release(number, Date.now(), slot);
164
172
  this._emit(slot, { phase: 'done', detail: 'goal reached' });
165
173
  res.status = 'cancelled';
166
174
  res.detail = 'goal reached';
175
+ res.category = 'goal_reached';
167
176
  return res;
168
177
  }
169
178
  const rentedAt = Date.now();
@@ -194,11 +203,13 @@ export class Worker {
194
203
  this._release(number, rentedAt, slot);
195
204
  };
196
205
 
197
- const fail = (detail) => {
206
+ const fail = (detail, category) => {
198
207
  const charged = smsArrived.v;
199
208
  res.status = charged ? 'failed' : 'cancelled';
200
209
  res.cost = charged ? number.cost : 0;
201
210
  res.detail = detail;
211
+ // category: timeout (90s, no OTP), wrong_otp, send_blocked, or generic failed/cancelled
212
+ res.category = category || (res.status === 'failed' ? 'failed' : 'cancelled');
202
213
  this.log(`FAILED ${number.mobile}: ${detail} (charged: ${charged})`);
203
214
  releaseOnce(charged, 'failed');
204
215
  return res;
@@ -224,13 +235,14 @@ export class Worker {
224
235
  const deadline = this.provider.name === 'tempotp' ? TEMPOTP_DEADLINE : OTPCART_DEADLINE;
225
236
  const doResend = this.provider.name === 'otpcart';
226
237
  const otp = await this._awaitOtp(slot, stream, fk, number, deadline, doResend, smsArrived);
227
- if (!otp) return fail(`no OTP within ${deadline / 1000}s abandoned`);
238
+ if (!otp) return fail(`no OTP in ${deadline / 1000}s (timeout)`, 'timeout');
228
239
 
229
240
  // 3. VERIFY (already retries transients internally via _verify; re-verify on blockage)
230
241
  this._emit(slot, { phase: 'verify', detail: 'verifying OTP' });
231
242
  const { session, error: verifyErr } = await this._verify(slot, fk, stream, number, otp, smsArrived);
232
243
  if (!session) {
233
- const failed = fail(verifyErr || 'verify failed');
244
+ const isWrongOtp = /incorrect|wrong|invalid|expired|code/i.test(verifyErr || '');
245
+ const failed = fail(verifyErr || 'verify failed', isWrongOtp ? 'wrong_otp' : 'failed');
234
246
  if (this.onFailure && !this.stopped && !this._asking) {
235
247
  this._asking = true;
236
248
  try {
@@ -282,7 +294,17 @@ export class Worker {
282
294
  res.status = 'success';
283
295
  res.cost = number.cost;
284
296
  res.session = session;
285
- const doneDetail = res.email_linked ? `extracted + ${res.linked_email}` : 'extracted';
297
+ res.is_new_account = !!fk.isNewUser; // a freshly-created (signup) account
298
+ // Derive the result CATEGORY for display + file routing:
299
+ // fresh → brand-new account just created
300
+ // coupon → existing account, ₹100 Minutes coupon available
301
+ // no_coupon → existing account, coupon already used
302
+ // extracted → success but Minutes wasn't checked
303
+ res.category = res.is_new_account ? 'fresh'
304
+ : (res.minutes_checked ? (res.coupon_eligible ? 'coupon' : 'no_coupon') : 'extracted');
305
+ const doneDetail = res.is_new_account ? 'fresh account'
306
+ : res.minutes_checked ? (res.coupon_eligible ? '₹100 coupon' : 'no coupon')
307
+ : 'extracted';
286
308
  this._emit(slot, { phase: 'done', detail: doneDetail });
287
309
  releaseOnce(true, doneDetail, true); // success: OTP consumed → no cancel
288
310
  return res;