domain setup

This commit is contained in:
adrapier03
2026-07-05 19:49:37 +07:00
parent 1274bd5a03
commit 620a3de21c
8 changed files with 313 additions and 42 deletions
+16 -8
View File
@@ -18,7 +18,8 @@ Edit `config.json`:
```json ```json
{ {
"tempmail": { "tempmail": {
"apiUrl": "https://tempmail.adrnode.com/api" "apiUrl": "https://tempmail.adrnode.com/api",
"domain": "shitoors.fun"
}, },
"captcha": { "captcha": {
"provider": "2captcha", "provider": "2captcha",
@@ -44,6 +45,7 @@ Yang **wajib** diisi:
- `xiaomi.referralLink` — URL referral, format `?ref=XXXXXX` (sama dengan inviteCode) - `xiaomi.referralLink` — URL referral, format `?ref=XXXXXX` (sama dengan inviteCode)
Yang opsional: Yang opsional:
- `tempmail.domain` — domain kustom untuk pendaftaran email (misal: `"shitoors.fun"`)
- `xiaomi.password` — password untuk semua akun baru (default OK) - `xiaomi.password` — password untuk semua akun baru (default OK)
- `browser.headless` — `true` tanpa UI, `false` (default) buat lihat browser - `browser.headless` — `true` tanpa UI, `false` (default) buat lihat browser
@@ -61,6 +63,7 @@ node chain-loop.js --count 5
| `--count N` | 1 | jumlah akun yang dibuat | | `--count N` | 1 | jumlah akun yang dibuat |
| `--seed XXXXXX` | `config.xiaomi.inviteCode` | invite code untuk akun pertama | | `--seed XXXXXX` | `config.xiaomi.inviteCode` | invite code untuk akun pertama |
| `--output file.txt` | `chain-result.txt` | file output | | `--output file.txt` | `chain-result.txt` | file output |
| `--proxy [arg]` | - | Menentukan proxy. Jika diisi string url (`http://user:pass@host:port`), akan memakai proxy tersebut. Jika bendera `--proxy` ditulis tanpa nilai (atau `--proxy proxy.txt`), program akan otomatis memuat list proxy dari file `proxy.txt` di workspace dan merotasinya per iterasi. |
```bash ```bash
# 5 akun, seed dari config # 5 akun, seed dari config
@@ -84,6 +87,7 @@ node regis-loop.js --count 5
|---|---|---| |---|---|---|
| `--count N` | 1 | jumlah akun yang dibuat | | `--count N` | 1 | jumlah akun yang dibuat |
| `--output file.txt` | `regis-result.txt` | file output (format: email:password:refCode:apiKey) | | `--output file.txt` | `regis-result.txt` | file output (format: email:password:refCode:apiKey) |
| `--proxy [arg]` | - | Sama seperti di `chain-loop.js`. Berguna untuk merotasi proxy per iterasi agar setiap akun terdaftar menggunakan IP berbeda. |
```bash ```bash
# Membuat 5 akun baru secara bergantian tanpa referral link / redeem code sama sekali # Membuat 5 akun baru secara bergantian tanpa referral link / redeem code sama sekali
@@ -94,18 +98,18 @@ node regis-loop.js --count 5
## Per-akun flow ## Per-akun flow
``` ```
[1] Bikin temp email [1] Bikin temp email (menggunakan domain kustom jika diatur)
[2] Launch browser dengan random fingerprint (UA, viewport, locale, GPU, dst) [2] Launch browser dengan random fingerprint (dan proxy yang dirotasi jika diaktifkan)
[3] Buka referral link (?ref=<currentSeed>) [3] Buka referral link (?ref=<currentSeed>)
[4] Sign up form (email + password + confirm + terms checkbox) [4] Sign up form (email + password + confirm + terms checkbox)
[5] Solve captcha (reCAPTCHA via 2Captcha + image captcha) [5] Solve captcha (reCAPTCHA via 2Captcha + image captcha)
[6] Verify email (kode 6 digit dari tempmail) [6] Verify email (kode 6 digit dari tempmail)
[7] Klik Console → Balance → "Enter invite code" → isi 6-box → Redeem [7] Pengecekan Balance & Terms:
→ cek restriction (kalau kena, STOP) - **Regis Loop**: Klik Console → Balance → deteksi & setujui Terms modal → cek balance (baca saldo).
→ reload halaman, verify balance bertambah ~$2 - **Chain Loop**: Klik Console → Balance → deteksi & setujui Terms modal → "Enter invite code" → isi 6-box → Redeem → cek restriction (kalau kena, STOP) → reload halaman → verify balance bertambah ~$2.
[8] Create API key (atau ambil existing kalau sudah ada) [8] Create API key (atau ambil existing kalau sudah ada)
[9] Fill Ultraspeed beta application form [9] Fill Ultraspeed beta application form
[10] Klik "Refer & earn" → ambil ref code akun ini → seed untuk akun berikut [10] Klik "Refer & earn" → ambil ref code akun ini
``` ```
## Output ## Output
@@ -136,9 +140,13 @@ akan di-skip otomatis, aman untuk re-run.
**Verifikasi balance** — Setelah redeem invite code, halaman di-reload dan balance **Verifikasi balance** — Setelah redeem invite code, halaman di-reload dan balance
dibaca ulang. Harus bertambah ~$2.00. Kalau tidak, iterasi dianggap gagal. dibaca ulang. Harus bertambah ~$2.00. Kalau tidak, iterasi dianggap gagal.
**Restriction detection** — Kalau setelah redeem muncul notif "risk control restrictions", **Restriction detection** — Kalau setelah redeem atau saat membuka halaman balance muncul notif "risk control restrictions",
loop berhenti total (akun kena cap, gak bisa lanjut). loop berhenti total (akun kena cap, gak bisa lanjut).
**Proxy Auto-Rotation & Sanitized Logs** — Mendukung rotasi proxy otomatis per iterasi dengan membaca file `proxy.txt`. Username/password autentikasi disembunyikan dari logs untuk menjaga kerahasiaan.
**Custom Email Domain** — Mendukung domain email kustom via properti `tempmail.domain` di `config.json`.
## Stop conditions ## Stop conditions
Loop berhenti kalau: Loop berhenti kalau:
+23 -2
View File
@@ -18,7 +18,7 @@ import { readFileSync, appendFileSync, existsSync } from 'fs';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname, join } from 'path'; import { dirname, join } from 'path';
import { MimoRegistration } from './index.js'; import { MimoRegistration, loadProxyList } from './index.js';
import './extras.js'; // attach getReferralCode import './extras.js'; // attach getReferralCode
import { generateFingerprint, buildInitScript, buildExtraHeaders } from './fingerprint.js'; import { generateFingerprint, buildInitScript, buildExtraHeaders } from './fingerprint.js';
@@ -50,6 +50,27 @@ const outputFile = outputIdx !== -1
const failLog = join(outputDir, 'chain-fail.log'); const failLog = join(outputDir, 'chain-fail.log');
const proxyIdx = args.indexOf('--proxy');
let proxyList = [];
if (proxyIdx !== -1) {
const nextArg = args[proxyIdx + 1];
let proxyPath = 'proxy.txt';
if (nextArg && !nextArg.startsWith('-')) {
proxyPath = nextArg;
}
const resolvedPath = join(__dirname, proxyPath);
if (existsSync(resolvedPath)) {
proxyList = loadProxyList(resolvedPath);
console.log(`Loaded ${proxyList.length} proxies from ${proxyPath}`);
} else if (nextArg && !nextArg.startsWith('-')) {
proxyList = [nextArg];
} else {
console.log(`Proxy option enabled but ${proxyPath} not found.`);
}
} else if (config.proxy) {
proxyList = [config.proxy];
}
// ---- File helpers ----------------------------------------------------- // ---- File helpers -----------------------------------------------------
function saveResult(row) { function saveResult(row) {
@@ -75,4 +96,4 @@ function logFail(email, error) {
appendFileSync(failLog, line, 'utf8'); appendFileSync(failLog, line, 'utf8');
} }
export { config, saveResult, logFail, count, seedRef, outputFile, failLog, __dirname }; export { config, saveResult, logFail, count, seedRef, outputFile, failLog, __dirname, proxyList };
+9 -2
View File
@@ -17,12 +17,12 @@
import { chromium } from 'playwright'; import { chromium } from 'playwright';
import { join } from 'path'; import { join } from 'path';
import { MimoRegistration } from './index.js'; import { MimoRegistration, parseProxy } from './index.js';
import './extras.js'; // attach getReferralCode import './extras.js'; // attach getReferralCode
import { generateFingerprint, buildInitScript, buildExtraHeaders } from './fingerprint.js'; import { generateFingerprint, buildInitScript, buildExtraHeaders } from './fingerprint.js';
import { import {
config, saveResult, logFail, count, seedRef, outputFile, failLog, __dirname, config, saveResult, logFail, count, seedRef, outputFile, failLog, __dirname, proxyList,
} from './chain-loop-config.js'; } from './chain-loop-config.js';
// ---- One-iteration runner -------------------------------------------- // ---- One-iteration runner --------------------------------------------
@@ -34,6 +34,12 @@ async function runIteration(idx, total, currentRef) {
// Per-iter: clone config + override invite code & referral link // Per-iter: clone config + override invite code & referral link
const iterConfig = JSON.parse(JSON.stringify(config)); const iterConfig = JSON.parse(JSON.stringify(config));
if (proxyList && proxyList.length > 0) {
const selectedProxy = proxyList[idx % proxyList.length];
iterConfig.proxy = selectedProxy;
const cleanProxyName = selectedProxy.split('@').pop();
console.log(` ↳ Using proxy [${(idx % proxyList.length) + 1}/${proxyList.length}]: ${cleanProxyName}`);
}
iterConfig.xiaomi.inviteCode = currentRef; iterConfig.xiaomi.inviteCode = currentRef;
iterConfig.xiaomi.referralLink = iterConfig.xiaomi.referralLink =
`https://platform.xiaomimimo.com/?ref=${encodeURIComponent(currentRef)}`; `https://platform.xiaomimimo.com/?ref=${encodeURIComponent(currentRef)}`;
@@ -67,6 +73,7 @@ async function runIteration(idx, total, currentRef) {
reg.browser = await chromium.launch({ reg.browser = await chromium.launch({
headless: iterConfig.browser.headless, headless: iterConfig.browser.headless,
channel: 'chrome', channel: 'chrome',
proxy: parseProxy(iterConfig.proxy),
args: [ args: [
`--window-size=${fp.viewport.width},${fp.viewport.height}`, `--window-size=${fp.viewport.width},${fp.viewport.height}`,
'--disable-blink-features=AutomationControlled', '--disable-blink-features=AutomationControlled',
+3 -2
View File
@@ -1,10 +1,11 @@
{ {
"tempmail": { "tempmail": {
"apiUrl": "https://tempmail.adrnode.com/api" "apiUrl": "https://tempmail.adrnode.com/api",
"domain": "foxsight.xyz"
}, },
"captcha": { "captcha": {
"provider": "2captcha", "provider": "2captcha",
"apiKey": "YOUR_2CAPTCHA_API_KEY" "apiKey": "82d7c3c7dfca21006510df3c6993b807"
}, },
"xiaomi": { "xiaomi": {
"referralLink": "https://platform.xiaomimimo.com/?ref=XXXXXX", "referralLink": "https://platform.xiaomimimo.com/?ref=XXXXXX",
+8 -8
View File
@@ -34,10 +34,10 @@ const VIEWPORTS = [
{ width: 1280, height: 800 }, { width: 1280, height: 800 },
]; ];
const LOCALES = ['en-US', 'en-GB', 'id-ID', 'en-AU', 'en-CA', 'en-SG']; const LOCALES = ['en-US', 'en-GB', 'en-AU', 'en-CA', 'en-SG'];
const TIMEZONES = [ const TIMEZONES = [
'Asia/Jakarta', 'Asia/Singapore', 'Asia/Bangkok', 'Asia/Singapore', 'Asia/Bangkok',
'Asia/Kuala_Lumpur', 'Asia/Manila', 'Asia/Ho_Chi_Minh', 'Asia/Kuala_Lumpur', 'Asia/Manila', 'Asia/Ho_Chi_Minh',
]; ];
@@ -52,11 +52,11 @@ const WEBGL_RENDERERS = [
{ vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (NVIDIA)', renderer: 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) HD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (Intel)', renderer: 'ANGLE (Intel, Intel(R) HD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (AMD)', renderer: 'ANGLE (AMD, AMD Radeon RX 6600 XT Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (AMD)', renderer: 'ANGLE (AMD, AMD Radeon RX 6600 XT Direct3D11 vs_5_0 ps_5_0, D3D11)' },
{ vendor: 'Google Inc. (AMD)', renderer: 'ANGLE (AMD, AMD Radeon Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' }, { vendor: 'Google Inc. (AMD)', renderer: 'ANGLE (AMD, AMD Radeon Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)' },
]; ];
// ---- Helpers ------------------------------------------------------------ // ---- Helpers ------------------------------------------------------------
@@ -249,7 +249,7 @@ export function buildInitScript(fp) {
// CLI test: `node fingerprint.js` — print 3 contoh fingerprint // CLI test: `node fingerprint.js` — print 3 contoh fingerprint
if (import.meta.url === `file:///${process.argv[1].replace(/\\/g, '/')}` || if (import.meta.url === `file:///${process.argv[1].replace(/\\/g, '/')}` ||
(process.argv[1] && process.argv[1].endsWith('fingerprint.js'))) { (process.argv[1] && process.argv[1].endsWith('fingerprint.js'))) {
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
const fp = generateFingerprint(); const fp = generateFingerprint();
console.log(`\n--- Sample ${i + 1} ---`); console.log(`\n--- Sample ${i + 1} ---`);
+95 -10
View File
@@ -23,8 +23,9 @@ const config = JSON.parse(readFileSync(join(__dirname, 'config.json'), 'utf8'));
// ============================================================================ // ============================================================================
class TempmailClient { class TempmailClient {
constructor(apiUrl) { constructor(apiUrl, domain) {
this.apiUrl = apiUrl; this.apiUrl = apiUrl;
this.domain = domain;
this.sessionId = null; this.sessionId = null;
} }
@@ -48,13 +49,19 @@ class TempmailClient {
async createInbox() { async createInbox() {
await this.initSession(); await this.initSession();
const response = await fetch(`${this.apiUrl}/inboxes`, { const headers = {
'Content-Type': 'application/json',
'x-session-id': this.sessionId
};
const options = {
method: 'POST', method: 'POST',
headers: { headers
'Content-Type': 'application/json', };
'x-session-id': this.sessionId if (this.domain) {
} options.body = JSON.stringify({ domain: this.domain });
}); }
const response = await fetch(`${this.apiUrl}/inboxes`, options);
if (!response.ok) { if (!response.ok) {
throw new Error(`Tempmail API error: ${response.status}`); throw new Error(`Tempmail API error: ${response.status}`);
@@ -219,7 +226,7 @@ class CaptchaSolver {
class MimoRegistration { class MimoRegistration {
constructor(config) { constructor(config) {
this.config = config; this.config = config;
this.tempmail = new TempmailClient(config.tempmail.apiUrl); this.tempmail = new TempmailClient(config.tempmail.apiUrl, config.tempmail.domain);
this.captcha = new CaptchaSolver(config.captcha.apiKey); this.captcha = new CaptchaSolver(config.captcha.apiKey);
this.browser = null; this.browser = null;
this.page = null; this.page = null;
@@ -249,9 +256,15 @@ class MimoRegistration {
console.log(` ↳ CPU/Mem : ${fp.hardwareConcurrency} cores / ${fp.deviceMemory} GB`); console.log(` ↳ CPU/Mem : ${fp.hardwareConcurrency} cores / ${fp.deviceMemory} GB`);
console.log(` ↳ GPU : ${fp.webgl.renderer.substring(0, 70)}…`); console.log(` ↳ GPU : ${fp.webgl.renderer.substring(0, 70)}…`);
const proxyConfig = parseProxy(config.proxy);
if (proxyConfig) {
console.log(` ↳ Proxy : ${proxyConfig.server}`);
}
this.browser = await chromium.launch({ this.browser = await chromium.launch({
headless: config.browser.headless, headless: config.browser.headless,
channel: 'chrome', channel: 'chrome',
proxy: proxyConfig,
args: [ args: [
// Hindari flag --headless yg kelihatan, dan window-size konsisten dgn viewport // Hindari flag --headless yg kelihatan, dan window-size konsisten dgn viewport
`--window-size=${fp.viewport.width},${fp.viewport.height}`, `--window-size=${fp.viewport.width},${fp.viewport.height}`,
@@ -2008,6 +2021,33 @@ async function main() {
count = parseInt(config.registration.count, 10) || 1; count = parseInt(config.registration.count, 10) || 1;
} }
const proxyArgIndex = process.argv.indexOf('--proxy');
let proxyList = [];
if (proxyArgIndex !== -1) {
const nextArg = process.argv[proxyArgIndex + 1];
// if nextArg is another flag or empty, assume default proxy.txt
let proxyPath = 'proxy.txt';
if (nextArg && !nextArg.startsWith('-')) {
proxyPath = nextArg;
}
// Check if the parameter is a file path that exists, or default proxy.txt exists
const { existsSync } = await import('fs');
const resolvedPath = join(__dirname, proxyPath);
if (existsSync(resolvedPath)) {
proxyList = loadProxyList(resolvedPath);
console.log(`Loaded ${proxyList.length} proxies from ${proxyPath}`);
} else if (nextArg && !nextArg.startsWith('-')) {
// Direct proxy string instead of file
proxyList = [nextArg];
} else {
// Default file doesn't exist either
console.log(`Proxy option enabled but ${proxyPath} not found.`);
}
} else if (config.proxy) {
proxyList = [config.proxy];
}
console.log(`Starting loop to create ${count} account(s)...`); console.log(`Starting loop to create ${count} account(s)...`);
let successCount = 0; let successCount = 0;
@@ -2018,7 +2058,12 @@ async function main() {
console.log(` [Loop ${i + 1}/${count}] Starting Auto-Registration`); console.log(` [Loop ${i + 1}/${count}] Starting Auto-Registration`);
console.log('='.repeat(70)); console.log('='.repeat(70));
const registration = new MimoRegistration(config); const iterConfig = JSON.parse(JSON.stringify(config));
if (proxyList.length > 0) {
iterConfig.proxy = proxyList[i % proxyList.length];
}
const registration = new MimoRegistration(iterConfig);
try { try {
const result = await registration.run(); const result = await registration.run();
@@ -2082,4 +2127,44 @@ if (import.meta.url === `file:///${process.argv[1].replace(/\\/g, '/')}` ||
main(); main();
} }
export { MimoRegistration, TempmailClient, CaptchaSolver }; function loadProxyList(filePath) {
try {
const { readFileSync } = require('fs'); // or use fs import
} catch (e) {}
// Since we already import readFileSync from 'fs' at the top of this file, we can use it directly
try {
const content = readFileSync(filePath, 'utf8');
return content
.split(/\r?\n/)
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
} catch (e) {
console.error(`Error loading proxy list: ${e.message}`);
return [];
}
}
function parseProxy(proxyStr) {
if (!proxyStr) return undefined;
let formatted = proxyStr.trim();
if (!/^https?:\/\//i.test(formatted) && !/^socks[45]:\/\//i.test(formatted)) {
formatted = 'http://' + formatted;
}
try {
const url = new URL(formatted);
const proxyObj = {
server: `${url.protocol}//${url.host}`,
};
if (url.username) {
proxyObj.username = decodeURIComponent(url.username);
}
if (url.password) {
proxyObj.password = decodeURIComponent(url.password);
}
return proxyObj;
} catch (e) {
return { server: proxyStr };
}
}
export { MimoRegistration, TempmailClient, CaptchaSolver, parseProxy, loadProxyList };
+154 -10
View File
@@ -15,7 +15,7 @@ import { readFileSync, appendFileSync, existsSync } from 'fs';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname, join } from 'path'; import { dirname, join } from 'path';
import { MimoRegistration } from './index.js'; import { MimoRegistration, parseProxy, loadProxyList } from './index.js';
import './extras.js'; // attach getReferralCode import './extras.js'; // attach getReferralCode
import { generateFingerprint, buildInitScript, buildExtraHeaders } from './fingerprint.js'; import { generateFingerprint, buildInitScript, buildExtraHeaders } from './fingerprint.js';
@@ -43,6 +43,27 @@ const outputFile = outputIdx !== -1
const failLog = join(outputDir, 'regis-fail.log'); const failLog = join(outputDir, 'regis-fail.log');
const proxyIdx = args.indexOf('--proxy');
let proxyList = [];
if (proxyIdx !== -1) {
const nextArg = args[proxyIdx + 1];
let proxyPath = 'proxy.txt';
if (nextArg && !nextArg.startsWith('-')) {
proxyPath = nextArg;
}
const resolvedPath = join(__dirname, proxyPath);
if (existsSync(resolvedPath)) {
proxyList = loadProxyList(resolvedPath);
console.log(`Loaded ${proxyList.length} proxies from ${proxyPath}`);
} else if (nextArg && !nextArg.startsWith('-')) {
proxyList = [nextArg];
} else {
console.log(`Proxy option enabled but ${proxyPath} not found.`);
}
} else if (config.proxy) {
proxyList = [config.proxy];
}
// ---- File helpers ----------------------------------------------------- // ---- File helpers -----------------------------------------------------
function saveResult(row) { function saveResult(row) {
@@ -76,6 +97,12 @@ async function runIteration(idx, total) {
// Per-iter: clone config + set referral link to direct URL (no referral parameter) // Per-iter: clone config + set referral link to direct URL (no referral parameter)
const iterConfig = JSON.parse(JSON.stringify(config)); const iterConfig = JSON.parse(JSON.stringify(config));
if (proxyList && proxyList.length > 0) {
const selectedProxy = proxyList[idx % proxyList.length];
iterConfig.proxy = selectedProxy;
const cleanProxyName = selectedProxy.split('@').pop();
console.log(` ↳ Using proxy [${(idx % proxyList.length) + 1}/${proxyList.length}]: ${cleanProxyName}`);
}
iterConfig.xiaomi.inviteCode = ''; iterConfig.xiaomi.inviteCode = '';
iterConfig.xiaomi.referralLink = 'https://platform.xiaomimimo.com/'; iterConfig.xiaomi.referralLink = 'https://platform.xiaomimimo.com/';
@@ -95,12 +122,12 @@ async function runIteration(idx, total) {
try { try {
// 1. Tempmail // 1. Tempmail
console.log('[1/7] Creating temporary email...'); console.log('[1/8] Creating temporary email...');
email = await reg.tempmail.createInbox(); email = await reg.tempmail.createInbox();
console.log(`✓ Email: ${email}`); console.log(`✓ Email: ${email}`);
// 2. Browser // 2. Browser
console.log('[2/7] Launching browser...'); console.log('[2/8] Launching browser...');
const fp = generateFingerprint(); const fp = generateFingerprint();
reg.fingerprint = fp; reg.fingerprint = fp;
console.log(` ↳ Chrome ${fp.chromeMajor}, ${fp.viewport.width}x${fp.viewport.height}, ${fp.locale}/${fp.timezone}`); console.log(` ↳ Chrome ${fp.chromeMajor}, ${fp.viewport.width}x${fp.viewport.height}, ${fp.locale}/${fp.timezone}`);
@@ -108,6 +135,7 @@ async function runIteration(idx, total) {
reg.browser = await chromium.launch({ reg.browser = await chromium.launch({
headless: iterConfig.browser.headless, headless: iterConfig.browser.headless,
channel: 'chrome', channel: 'chrome',
proxy: parseProxy(iterConfig.proxy),
args: [ args: [
`--window-size=${fp.viewport.width},${fp.viewport.height}`, `--window-size=${fp.viewport.width},${fp.viewport.height}`,
'--disable-blink-features=AutomationControlled', '--disable-blink-features=AutomationControlled',
@@ -134,31 +162,135 @@ async function runIteration(idx, total) {
}; };
// 3. Open registration page directly // 3. Open registration page directly
console.log('[3/7] Opening registration page...'); console.log('[3/8] Opening registration page...');
await reg.page.goto(iterConfig.xiaomi.referralLink, { await reg.page.goto(iterConfig.xiaomi.referralLink, {
waitUntil: 'networkidle', waitUntil: 'networkidle',
timeout: iterConfig.browser.timeout, timeout: iterConfig.browser.timeout,
}); });
// 4. Sign up form // 4. Sign up form
console.log('[4/7] Filling Sign up form...'); console.log('[4/8] Filling Sign up form...');
await reg.fillRegistrationForm(email); await reg.fillRegistrationForm(email);
await reg.submitRegistration(); await reg.submitRegistration();
// 5. Captcha // 5. Captcha
console.log('[5/7] Handling captcha...'); console.log('[5/8] Handling captcha...');
await reg.handleXiaomiCaptcha(); await reg.handleXiaomiCaptcha();
await reg.handleImageCaptcha(); await reg.handleImageCaptcha();
// 6. Verify email // 6. Verify email
console.log('[6/7] Verifying email...'); console.log('[6/8] Verifying email...');
await reg.verifyEmail(email); await reg.verifyEmail(email);
// 7. Skip Redeem // 7. Check Balance (Terms modal will be handled here if opened)
console.log('[Info] Skipping redeem/enter invite code step.'); console.log('[7/8] Checking balance...');
try {
// 1. Klik menu "Console" dulu — biar navigasi-nya mirip user beneran
await reg.clickConsoleMenu();
// 2. Navigasi ke balance page jika belum
let url = reg.page.url();
if (!url.includes('/console/balance')) {
console.log(` Not on balance page yet (current: ${url}), navigating...`);
const balanceClicked = await reg.page.evaluate(() => {
const links = Array.from(document.querySelectorAll('a, [role="link"], li, [role="menuitem"]'));
const target = links.find(el => {
const txt = (el.textContent || '').trim();
return /^Balance$/i.test(txt) && el.offsetHeight > 0;
});
if (target) {
target.scrollIntoView({ block: 'center' });
target.click();
return true;
}
return false;
});
if (balanceClicked) {
console.log(' ✓ Clicked Balance link in console sidebar');
await reg.page.waitForURL(/\/console\/balance/, { timeout: 8000 }).catch(() => {});
await reg.page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => {});
} else {
console.log(' ! Balance link not found, falling back to direct goto...');
await reg.page.goto('https://platform.xiaomimimo.com/console/balance', {
waitUntil: 'networkidle',
timeout: iterConfig.browser.timeout,
});
}
}
await reg.page.waitForTimeout(2500);
await reg.handleOAuthRedirect();
await reg.page.waitForTimeout(2000);
// Close cookies banner if any
console.log(' Checking for cookies banner...');
const acceptCookiesBtn = await reg.page.waitForSelector('button:has-text("Accept All"), button:has-text("Accept"), button:has-text("Allow All")', { timeout: 4000 }).catch(() => null);
if (acceptCookiesBtn) {
await acceptCookiesBtn.click({ force: true }).catch(() => {});
console.log(' ✓ Accepted cookies banner');
await reg.page.waitForTimeout(2000);
}
// Terms modal
await reg.handleTermsModal();
// Overlays
await reg.waitForOverlaysGone();
// Take screenshot
await reg.page.screenshot({ path: 'screenshot-balance.png', fullPage: false });
console.log(' ✓ Captured screenshot-balance.png');
// Cek apakah ada notifikasi risk control / block/ restricted
const restrictionMsg = await reg.page.evaluate(() => {
const text = document.body.innerText || '';
const patterns = [
/risk\s*control\s*restriction/i,
/account\s+has\s+risk\s+control/i,
/contact\s+customer\s+service/i,
/account\s+(is\s+)?restricted/i,
];
for (const re of patterns) {
const m = text.match(new RegExp('([^\\n]{0,200}' + re.source + '[^\\n]{0,200})', re.flags));
if (m) return m[1].trim();
}
return null;
}).catch(() => null);
if (restrictionMsg) {
console.log(' ❌ ACCOUNT RESTRICTED:');
console.log(` ${restrictionMsg}`);
try {
await reg.page.screenshot({
path: `error-restriction-${Date.now()}.png`,
fullPage: true,
});
} catch (e) {}
const err = new Error(`ACCOUNT_RESTRICTED: ${restrictionMsg}`);
err.code = 'ACCOUNT_RESTRICTED';
err.restrictionMsg = restrictionMsg;
throw err;
}
// Read balance
const balance = await reg.readBalance();
console.log(` 💰 Balance: $${balance !== null ? balance.toFixed(2) : 'unknown'}`);
if (balance === 0) {
const err = new Error('Balance is $0.00');
err.code = 'BALANCE_ZERO';
throw err;
}
} catch (e) {
if (e.code === 'ACCOUNT_RESTRICTED' || e.code === 'BALANCE_ZERO') {
throw e;
}
console.log(` ! check balance error (lanjut): ${e.message}`);
}
// 8. Create API key (atau ambil existing) // 8. Create API key (atau ambil existing)
console.log('[7/7] Creating API key...'); console.log('[8/8] Creating API key...');
try { try {
apiKey = await reg.createApiKey(); apiKey = await reg.createApiKey();
console.log(`✓ API key: ${apiKey || '(none)'}`); console.log(`✓ API key: ${apiKey || '(none)'}`);
@@ -201,6 +333,18 @@ async function runIteration(idx, total) {
return { ok: true, email, refCode, apiKey }; return { ok: true, email, refCode, apiKey };
} catch (err) { } catch (err) {
if (err.code === 'BALANCE_ZERO') {
console.log(`\n⚠ Regis Iteration ${idx + 1} skipped: ${err.message}`);
logFail(email, err.message);
return {
ok: false,
email,
error: err.message,
restricted: false,
stopReason: err.code,
};
}
console.error(`\n❌ Regis Iteration ${idx + 1} failed: ${err.message}`); console.error(`\n❌ Regis Iteration ${idx + 1} failed: ${err.message}`);
if (reg.page) { if (reg.page) {
try { try {
+5
View File
@@ -1,2 +1,7 @@
# Regis loop results. Format: email:password:refCode:apiKey # Regis loop results. Format: email:password:refCode:apiKey
[email protected]:MimoAuto2024!:DPWQPR:sk-spot7dmzwv05ijj960sz2vab17h3ii42x01zcyg31834cpun [email protected]:MimoAuto2024!:DPWQPR:sk-spot7dmzwv05ijj960sz2vab17h3ii42x01zcyg31834cpun
[email protected]:MimoAuto2024!::
[email protected]:MimoAuto2024!:T58EXJ:sk-snu9xvlbnir5ofgne07liaa0juagrr5a9l3939wii7oz8lo7
[email protected]:MimoAuto2024!::
[email protected]:MimoAuto2024!::
[email protected]:MimoAuto2024!:XAFA8U:sk-scb9vvdzsv5h3wirlrkto59v4e11in0gubz36kdnijk0r5xo