@fudrouter/fsrouter 0.6.91 → 0.6.93
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/dist/open-sse/handlers/chatCore.js +16 -0
- package/dist/routes/oauth/[provider]/[action]/route.js +6 -0
- package/package.json +10 -2
- package/public/.vite/manifest.json +89 -89
- package/public/assets/EndpointPageClient.kiCCiZeY.js +6 -0
- package/public/assets/Loading.BUhrzmZs.js +1 -0
- package/public/assets/NoAuthProxyCard.BI22GO9q.js +89 -0
- package/public/assets/index.DZiGd5-F.js +89 -0
- package/public/assets/page.1oqivCfq.js +1 -0
- package/public/assets/page.B5R9sdV7.js +1 -0
- package/public/assets/page.B5x2tCz5.js +1 -0
- package/public/assets/page.BFjNdqva.js +1 -0
- package/public/assets/page.BGs8R5av.js +1 -0
- package/public/assets/page.BLO61tLD.js +2 -0
- package/public/assets/page.BLWAx8lB.js +1 -0
- package/public/assets/page.BMcCQehz.js +6 -0
- package/public/assets/page.BS1sQouT.js +1 -0
- package/public/assets/page.BUWLUkXc.js +1 -0
- package/public/assets/page.BWaWJ_9t.js +1 -0
- package/public/assets/page.BYhmIkhw.js +1 -0
- package/public/assets/page.Buhl7l9y.js +1 -0
- package/public/assets/page.CBlkUp7t.js +1 -0
- package/public/assets/page.CG2arj7j.js +4 -0
- package/public/assets/page.CLgmM6Q_.js +5 -0
- package/public/assets/page.CLu_guvG.js +1 -0
- package/public/assets/page.COCdcdEM.js +1 -0
- package/public/assets/page.C_zhg1wq.js +1 -0
- package/public/assets/page.CtaqVBbT.js +6 -0
- package/public/assets/page.Cx0GMnA6.js +5 -0
- package/public/assets/page.D47UJLch.js +2 -0
- package/public/assets/page.D9ZFXJ3P.js +1 -0
- package/public/assets/page.D9kdvCh4.js +1 -0
- package/public/assets/page.DO1WoRkx.js +23 -0
- package/public/assets/page.DU201OUE.js +1 -0
- package/public/assets/page.DXxD-0Vl.js +31 -0
- package/public/assets/page.DaJXSZm4.js +1 -0
- package/public/assets/page.Dkeyd3nc.js +1 -0
- package/public/assets/page.DwJ35Irb.js +1 -0
- package/public/assets/page.DzLEPjRK.js +64 -0
- package/public/assets/page.VAojhnMr.js +1 -0
- package/public/assets/page.WBkwLufg.js +1 -0
- package/public/assets/page.laQQR26l.js +1 -0
- package/public/assets/page.nzF601n5.js +1 -0
- package/public/assets/page.tzSMuTXr.js +1 -0
- package/public/assets/page.xCigoHGB.js +1 -0
- package/public/index.html +4 -4
- package/scripts/postinstall.cjs +90 -0
- package/src/automation/cf_token_via_session.py +0 -158
- package/src/automation/cloudflare_signup.py +0 -3689
- package/src/automation/codebuddy_signup.py +0 -636
- package/src/automation/flashloop_signup.py +0 -338
- package/src/automation/kimi_signup.py +0 -401
- package/src/automation/leonardo_signup.py +0 -902
- package/src/automation/openvecta_signup.py +0 -652
- package/src/automation/qoder_signup.py +0 -323
- package/src/automation/test_proxy.py +0 -86
- package/src/automation/weavy_signup.py +0 -1964
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Flashloop account auto-signup via Camoufox (anti-fingerprint) + Fsmail email verification.
|
|
3
|
-
|
|
4
|
-
Referral code: UWZKVP (hardcoded in URL)
|
|
5
|
-
|
|
6
|
-
Outputs JSON lines to stdout:
|
|
7
|
-
{"step": "..."} — progress update
|
|
8
|
-
{"status": "success", "email": "...", "password": "..."} — final result
|
|
9
|
-
{"status": "error", "error": "..."} — failure
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import sys
|
|
13
|
-
import json
|
|
14
|
-
import argparse
|
|
15
|
-
import time
|
|
16
|
-
import random
|
|
17
|
-
import string
|
|
18
|
-
import re
|
|
19
|
-
import urllib.request
|
|
20
|
-
import urllib.parse
|
|
21
|
-
import urllib.error
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
|
|
24
|
-
REFERRAL_CODE = "UWZKVP"
|
|
25
|
-
SIGNUP_URL = "https://www.flashloop.app/onboarding"
|
|
26
|
-
|
|
27
|
-
# ── Stdout JSON helpers ────────────────────────────────────────────────────────
|
|
28
|
-
def emit(obj):
|
|
29
|
-
print(json.dumps(obj), flush=True)
|
|
30
|
-
|
|
31
|
-
def log_step(msg):
|
|
32
|
-
emit({"step": msg})
|
|
33
|
-
|
|
34
|
-
def success(email, password):
|
|
35
|
-
emit({"status": "success", "email": email, "password": password})
|
|
36
|
-
|
|
37
|
-
def die(msg):
|
|
38
|
-
emit({"status": "error", "error": msg})
|
|
39
|
-
sys.exit(1)
|
|
40
|
-
|
|
41
|
-
# ── Fsmail helpers ─────────────────────────────────────────────────────────────
|
|
42
|
-
def fsmail_request(base_url, api_key, path, method="GET", data=None):
|
|
43
|
-
url = base_url.rstrip("/") + "/api" + path
|
|
44
|
-
req = urllib.request.Request(url, method=method)
|
|
45
|
-
req.add_header("Authorization", f"Bearer {api_key}")
|
|
46
|
-
req.add_header("Content-Type", "application/json")
|
|
47
|
-
req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
|
48
|
-
if data:
|
|
49
|
-
req.data = json.dumps(data).encode()
|
|
50
|
-
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
51
|
-
return json.loads(resp.read())
|
|
52
|
-
|
|
53
|
-
def create_fsmail_inbox(base_url, api_key):
|
|
54
|
-
"""Create a random inbox and return (email, alias)."""
|
|
55
|
-
rand = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
|
|
56
|
-
alias = f"fl{rand}"
|
|
57
|
-
domain = None
|
|
58
|
-
|
|
59
|
-
# Get available domains
|
|
60
|
-
try:
|
|
61
|
-
domains_data = fsmail_request(base_url, api_key, "/domains")
|
|
62
|
-
domains = domains_data.get("domains", [])
|
|
63
|
-
if domains:
|
|
64
|
-
domain = domains[0].get("name", domains[0].get("domain", ""))
|
|
65
|
-
except Exception:
|
|
66
|
-
pass
|
|
67
|
-
|
|
68
|
-
if not domain:
|
|
69
|
-
die("Tidak ada domain tersedia di Fsmail")
|
|
70
|
-
|
|
71
|
-
email = f"{alias}@{domain}"
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
fsmail_request(base_url, api_key, "/inboxes", method="POST",
|
|
75
|
-
data={"alias": alias, "domain": domain})
|
|
76
|
-
except Exception as e:
|
|
77
|
-
log_step(f"Warning: inbox creation: {e}")
|
|
78
|
-
|
|
79
|
-
return email, alias
|
|
80
|
-
|
|
81
|
-
def wait_for_flashloop_email():
|
|
82
|
-
"""Wait for Flashloop verification email and extract OTP code."""
|
|
83
|
-
log_step(f"Menunggu email verifikasi Flashloop...")
|
|
84
|
-
deadline = time.time() + timeout
|
|
85
|
-
seen_ids = set()
|
|
86
|
-
|
|
87
|
-
while time.time() < deadline:
|
|
88
|
-
try:
|
|
89
|
-
data = fsmail_request(base_url, api_key, f"/inboxes/{urllib.parse.quote(alias)}/messages")
|
|
90
|
-
messages = data.get("messages", [])
|
|
91
|
-
for msg in messages:
|
|
92
|
-
msg_id = msg.get("id", "")
|
|
93
|
-
subject = msg.get("subject", "")
|
|
94
|
-
if msg_id in seen_ids:
|
|
95
|
-
continue
|
|
96
|
-
seen_ids.add(msg_id)
|
|
97
|
-
|
|
98
|
-
subj_lower = subject.lower()
|
|
99
|
-
is_flashloop = (
|
|
100
|
-
"flashloop" in subj_lower or
|
|
101
|
-
"verify" in subj_lower or
|
|
102
|
-
"confirm" in subj_lower or
|
|
103
|
-
"code" in subj_lower or
|
|
104
|
-
"otp" in subj_lower
|
|
105
|
-
)
|
|
106
|
-
if is_flashloop:
|
|
107
|
-
# Fetch full message
|
|
108
|
-
try:
|
|
109
|
-
full = fsmail_request(base_url, api_key,
|
|
110
|
-
f"/inboxes/{urllib.parse.quote(alias)}/messages/{urllib.parse.quote(msg_id)}")
|
|
111
|
-
body = full.get("body", "") or full.get("text", "") or full.get("html", "")
|
|
112
|
-
|
|
113
|
-
# Extract OTP code (usually 4-8 digits)
|
|
114
|
-
otp_match = re.search(r'\b(\d{4,8})\b', body)
|
|
115
|
-
if otp_match:
|
|
116
|
-
code = otp_match.group(1)
|
|
117
|
-
log_step(f"Kode verifikasi ditemukan: {code}")
|
|
118
|
-
return code
|
|
119
|
-
|
|
120
|
-
# Try extracting from subject
|
|
121
|
-
otp_subj = re.search(r'\b(\d{4,8})\b', subject)
|
|
122
|
-
if otp_subj:
|
|
123
|
-
code = otp_subj.group(1)
|
|
124
|
-
log_step(f"Kode verifikasi dari subject: {code}")
|
|
125
|
-
return code
|
|
126
|
-
|
|
127
|
-
# Try extracting verification link
|
|
128
|
-
link_match = re.search(r'https?://[^\s"<>]+verify[^\s"<>]*', body)
|
|
129
|
-
if not link_match:
|
|
130
|
-
link_match = re.search(r'https?://[^\s"<>]+confirm[^\s"<>]*', body)
|
|
131
|
-
if link_match:
|
|
132
|
-
link = link_match.group(0)
|
|
133
|
-
log_step(f"Link verifikasi ditemukan")
|
|
134
|
-
return {"type": "link", "url": link}
|
|
135
|
-
|
|
136
|
-
log_step(f"Email ditemukan tapi tidak bisa extract kode/link")
|
|
137
|
-
return body # Return raw body for manual parsing
|
|
138
|
-
except Exception as e:
|
|
139
|
-
log_step(f"Error fetching message: {e}")
|
|
140
|
-
except Exception:
|
|
141
|
-
pass
|
|
142
|
-
|
|
143
|
-
time.sleep(3)
|
|
144
|
-
|
|
145
|
-
die("Timeout menunggu email verifikasi Flashloop")
|
|
146
|
-
|
|
147
|
-
# ── Password generator ─────────────────────────────────────────────────────────
|
|
148
|
-
def gen_password(length=14):
|
|
149
|
-
chars = string.ascii_letters + string.digits + "!@#$%"
|
|
150
|
-
while True:
|
|
151
|
-
pwd = ''.join(random.choices(chars, k=length))
|
|
152
|
-
if (any(c.isupper() for c in pwd) and
|
|
153
|
-
any(c.islower() for c in pwd) and
|
|
154
|
-
any(c.isdigit() for c in pwd)):
|
|
155
|
-
return pwd
|
|
156
|
-
|
|
157
|
-
# ── Main ───────────────────────────────────────────────────────────────────────
|
|
158
|
-
def main():
|
|
159
|
-
parser = argparse.ArgumentParser()
|
|
160
|
-
parser.add_argument("--email", required=True)
|
|
161
|
-
parser.add_argument("--password", required=True)
|
|
162
|
-
parser.add_argument("--fsmail-url", required=True)
|
|
163
|
-
parser.add_argument("--fsmail-key", required=True)
|
|
164
|
-
parser.add_argument("--profiles-dir", required=True)
|
|
165
|
-
parser.add_argument("--headless", action="store_true", default=True)
|
|
166
|
-
parser.add_argument("--delay", type=float, default=0)
|
|
167
|
-
args = parser.parse_args()
|
|
168
|
-
|
|
169
|
-
if args.delay > 0:
|
|
170
|
-
log_step(f"Delay {args.delay}s...")
|
|
171
|
-
time.sleep(args.delay)
|
|
172
|
-
|
|
173
|
-
email = args.email
|
|
174
|
-
password = args.password
|
|
175
|
-
alias = email.split("@")[0]
|
|
176
|
-
log_step(f"Email: {email}")
|
|
177
|
-
|
|
178
|
-
# Launch browser
|
|
179
|
-
log_step("Meluncurkan browser...")
|
|
180
|
-
try:
|
|
181
|
-
from camoufox.sync_api import Camoufox
|
|
182
|
-
except ImportError:
|
|
183
|
-
die("Camoufox tidak terinstall. Jalankan: pip install camoufox && python -m camoufox fetch")
|
|
184
|
-
|
|
185
|
-
launch_kwargs = dict(headless=args.headless, os="windows", locale="en-US")
|
|
186
|
-
|
|
187
|
-
def _make_camoufox(kw):
|
|
188
|
-
try:
|
|
189
|
-
return Camoufox(**kw)
|
|
190
|
-
except TypeError:
|
|
191
|
-
kw.pop("os", None)
|
|
192
|
-
try:
|
|
193
|
-
return Camoufox(**kw)
|
|
194
|
-
except TypeError:
|
|
195
|
-
kw.pop("locale", None)
|
|
196
|
-
return Camoufox(**kw)
|
|
197
|
-
|
|
198
|
-
browser_ctx = _make_camoufox(dict(launch_kwargs))
|
|
199
|
-
|
|
200
|
-
with browser_ctx as browser:
|
|
201
|
-
page = browser.new_page()
|
|
202
|
-
|
|
203
|
-
# Step 1: Go to signup page with referral code
|
|
204
|
-
log_step("Membuka halaman signup Flashloop...")
|
|
205
|
-
page.goto(SIGNUP_URL, wait_until="networkidle", timeout=30000)
|
|
206
|
-
time.sleep(5)
|
|
207
|
-
|
|
208
|
-
# Step 2: Click "Continue with Email"
|
|
209
|
-
log_step("Klik 'Continue with Email'...")
|
|
210
|
-
try:
|
|
211
|
-
email_btn = page.locator('a:has-text("Continue with Email"), button:has-text("Continue with Email")').first
|
|
212
|
-
email_btn.wait_for(timeout=15000)
|
|
213
|
-
email_btn.click()
|
|
214
|
-
time.sleep(2)
|
|
215
|
-
except Exception as e:
|
|
216
|
-
die(f"Tidak bisa klik 'Continue with Email': {e}")
|
|
217
|
-
|
|
218
|
-
# Step 3: Fill signup form
|
|
219
|
-
log_step("Mengisi form signup...")
|
|
220
|
-
try:
|
|
221
|
-
# Email field
|
|
222
|
-
email_input = page.locator('input[placeholder*="Email"], input[type="email"], input[name="email"]').first
|
|
223
|
-
email_input.wait_for(timeout=10000)
|
|
224
|
-
email_input.fill(email)
|
|
225
|
-
time.sleep(0.5)
|
|
226
|
-
|
|
227
|
-
# Password field
|
|
228
|
-
pwd_input = page.locator('input[placeholder*="Password"], input[type="password"], input[name="password"]').first
|
|
229
|
-
pwd_input.fill(password)
|
|
230
|
-
time.sleep(0.5)
|
|
231
|
-
except Exception as e:
|
|
232
|
-
die(f"Gagal mengisi form: {e}")
|
|
233
|
-
|
|
234
|
-
# Step 4: Click "Create Account"
|
|
235
|
-
log_step("Membuat akun...")
|
|
236
|
-
try:
|
|
237
|
-
create_btn = page.locator('button:has-text("Create Account"), button:has-text("Sign Up"), button:has-text("Register")').first
|
|
238
|
-
create_btn.click()
|
|
239
|
-
time.sleep(3)
|
|
240
|
-
except Exception as e:
|
|
241
|
-
die(f"Gagal klik 'Create Account': {e}")
|
|
242
|
-
|
|
243
|
-
# Step 5: Handle post-signup (referral code + optional verification)
|
|
244
|
-
log_step("Menunggu halaman setelah signup...")
|
|
245
|
-
time.sleep(3)
|
|
246
|
-
|
|
247
|
-
# Check for referral code input: "GOT A REFERRAL CODE?"
|
|
248
|
-
try:
|
|
249
|
-
page_text_lower = page.content().lower()
|
|
250
|
-
if "referral" in page_text_lower or "bonus credits" in page_text_lower:
|
|
251
|
-
log_step("Halaman referral terdeteksi, memasukkan kode UWZKVP...")
|
|
252
|
-
# Try multiple locator strategies
|
|
253
|
-
ref_input = None
|
|
254
|
-
for selector in [
|
|
255
|
-
'input[placeholder*="referral" i]',
|
|
256
|
-
'input[placeholder*="code" i]',
|
|
257
|
-
'input[name*="referral" i]',
|
|
258
|
-
'input[name*="code" i]',
|
|
259
|
-
'input[type="text"]',
|
|
260
|
-
'input:not([type="password"]):not([type="hidden"]):not([type="email"])',
|
|
261
|
-
]:
|
|
262
|
-
try:
|
|
263
|
-
el = page.locator(selector).first
|
|
264
|
-
if el.is_visible(timeout=1000):
|
|
265
|
-
ref_input = el
|
|
266
|
-
break
|
|
267
|
-
except Exception:
|
|
268
|
-
continue
|
|
269
|
-
|
|
270
|
-
if ref_input:
|
|
271
|
-
ref_input.fill(REFERRAL_CODE)
|
|
272
|
-
time.sleep(0.5)
|
|
273
|
-
# Try to find and click Apply Code button
|
|
274
|
-
for btn_text in ["Apply Code", "Apply", "Submit", "Continue"]:
|
|
275
|
-
try:
|
|
276
|
-
btn = page.locator(f'button:has-text("{btn_text}")').first
|
|
277
|
-
if btn.is_visible(timeout=1000):
|
|
278
|
-
btn.click()
|
|
279
|
-
log_step(f"Klik '{btn_text}' - kode referral diterapkan!")
|
|
280
|
-
break
|
|
281
|
-
except Exception:
|
|
282
|
-
continue
|
|
283
|
-
time.sleep(3)
|
|
284
|
-
else:
|
|
285
|
-
log_step("Input referral tidak ditemukan, lanjut...")
|
|
286
|
-
except Exception as e:
|
|
287
|
-
log_step(f"Referral step tidak ditemukan atau sudah di-skip: {e}")
|
|
288
|
-
|
|
289
|
-
# Check if already logged in (pricing page or dashboard)
|
|
290
|
-
current_url = page.url
|
|
291
|
-
if "pricing" in current_url or "dashboard" in current_url or "app" in current_url:
|
|
292
|
-
log_step(f"Sudah masuk! URL: {current_url}")
|
|
293
|
-
success(email, password)
|
|
294
|
-
else:
|
|
295
|
-
# Check for OTP input
|
|
296
|
-
otp_inputs = page.locator('input[maxlength="1"], input[placeholder*="code"], input[placeholder*="Code"], input[type="tel"], input[name*="code"], input[name*="otp"]')
|
|
297
|
-
|
|
298
|
-
if otp_inputs.count() > 0:
|
|
299
|
-
log_step("Halaman OTP terdeteksi, menunggu kode dari email...")
|
|
300
|
-
result = wait_for_flashloop_email()
|
|
301
|
-
|
|
302
|
-
if isinstance(result, dict) and result.get("type") == "link":
|
|
303
|
-
log_step("Membuka link verifikasi...")
|
|
304
|
-
page.goto(result["url"], wait_until="domcontentloaded", timeout=30000)
|
|
305
|
-
time.sleep(5)
|
|
306
|
-
elif isinstance(result, str) and len(result) <= 8 and result.isdigit():
|
|
307
|
-
log_step(f"Memasukkan kode OTP: {result}")
|
|
308
|
-
inputs = page.locator('input[maxlength="1"], input[placeholder*="code"], input[type="tel"]')
|
|
309
|
-
for i, digit in enumerate(result):
|
|
310
|
-
if i < inputs.count():
|
|
311
|
-
inputs.nth(i).fill(digit)
|
|
312
|
-
time.sleep(0.2)
|
|
313
|
-
time.sleep(2)
|
|
314
|
-
try:
|
|
315
|
-
verify_btn = page.locator('button:has-text("Verify"), button:has-text("Confirm"), button:has-text("Submit")').first
|
|
316
|
-
verify_btn.click(timeout=5000)
|
|
317
|
-
time.sleep(3)
|
|
318
|
-
except Exception:
|
|
319
|
-
pass
|
|
320
|
-
else:
|
|
321
|
-
log_step("Tidak ada kode verifikasi, lanjut...")
|
|
322
|
-
else:
|
|
323
|
-
log_step("Tidak ada OTP, menunggu sebentar...")
|
|
324
|
-
time.sleep(5)
|
|
325
|
-
|
|
326
|
-
# Final check
|
|
327
|
-
log_step("Menunggu halaman akhir...")
|
|
328
|
-
time.sleep(5)
|
|
329
|
-
current_url = page.url
|
|
330
|
-
log_step(f"URL akhir: {current_url}")
|
|
331
|
-
|
|
332
|
-
if "onboarding" not in current_url or "login" not in current_url:
|
|
333
|
-
success(email, password)
|
|
334
|
-
else:
|
|
335
|
-
die(f"Signup gagal. URL: {current_url}")
|
|
336
|
-
|
|
337
|
-
if __name__ == "__main__":
|
|
338
|
-
main()
|