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 +1 -1
- package/src/providers/tempotp.js +2 -2
- package/src/ui/RunView.js +27 -6
- package/src/util.js +29 -14
- package/src/worker.js +27 -5
package/package.json
CHANGED
package/src/providers/tempotp.js
CHANGED
|
@@ -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
|
-
|
|
34
|
-
|
|
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:
|
|
40
|
-
h(Box, { flexGrow: 1 }, h(Text, { color: 'gray' },
|
|
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,
|
|
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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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 (
|
|
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 && (
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
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
|
|
90
|
-
if (
|
|
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 = [...
|
|
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) {
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|