autoreg-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok60915d52e8425
2026-10-20 02:16:32 +03:00
commit f58eafeb48
565 changed files with 60064 additions and 0 deletions

View File

@@ -0,0 +1,287 @@
# Misskey/Sharkey (Universe Federation) Registration Pattern
## Instance: dc.hhhl.cc
**Software**: Sharkey fork "Universe Federation" v2025.5.2-dev
**Captcha**: Cloudflare Turnstile (sitekey: `0x4AAAAAADUhQ0UI_7UTXChP`)
**Email restriction**: Only `gmail.com` and `qq.com` accepted
**Email verification**: Required for signup. Verification emails land in Gmail SPAM.
## API Endpoints
```bash
# Instance meta (POST required)
POST https://dc.hhhl.cc/api/meta
Body: {}
# Signup (requires Turnstile token)
POST https://dc.hhhl.cc/api/signup
Body: {"username":"...","password":"...","emailAddress":"...","turnstileResponse":"..."}
# List users (public)
POST https://dc.hhhl.cc/api/users
Body: {}
# Check user exists (ActivityPub)
GET https://dc.hhhl.cc/@username
Accept: application/activity+json
# 404 = not found, 200 = exists
```
## Signup Form Selectors (Misskey/Sharkey SPA)
The form uses NO `name` attributes on inputs. Use type-based selectors:
```
Username: input[type="text"]
Email: input[type="email"]
Password: [REDACTED]"password"] (first)
Confirm: input[type="password"] (second)
```
The form is inside a modal with hash `#mkmodal` or `#/signup`. The SPA uses Vue/React — native DOM events may not trigger framework state updates. Use `Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set.call(el, value)` + `dispatchEvent(new Event('input', {bubbles: true}))`.
## Turnstile Behavior
1. **Local Chrome (CDP)**: Turnstile loads in a separate Cloudflare challenge page/target. The widget may auto-solve after the checkbox is clicked.
2. **Browserbase (cloud browser)**: Turnstile iframe appears but has empty `src` — widget does NOT load. Cloudflare blocks cloud browser IPs.
3. **nodriver**: Works but may fail to connect to Chrome on Windows. Use explicit `browser_executable_path`.
4. **CDP direct (recommended)**: Most reliable. Launch Chrome with `--remote-debugging-port`, connect via WebSocket, use `Page.navigate`, `Runtime.evaluate`.
## Registration Flow
1. Navigate to `https://dc.hhhl.cc/`
2. Click "Присоединяйтесь к этому серверу" button
3. Check two checkboxes (terms agreement)
4. Click "Продолжить" (disabled until checkboxes checked)
5. Handle "Подтвердить" confirmation dialogs (up to 2)
6. Fill signup form: username, email, password ×2
7. Solve Turnstile (manual or auto)
8. Click "Начать" submit button
9. Check response: if URL changes from `#mkmodal` → success
## Silent Registration Failure
**Critical**: The SPA may close the dialog and return to the main page WITHOUT an error message. This looks like success but the account is never created. Always verify:
```bash
# Check if user exists (ActivityPub)
curl -s -H "Accept: application/activity+json" "https://dc.hhhl.cc/@username"
# 404 = registration failed silently
# 200 = account exists
# Better: Misskey API (JSON, more reliable)
curl -s -X POST "https://dc.hhhl.cc/api/users/show" \
-H "Content-Type: application/json" \
-d '{"username":"username_here"}'
# 200 + {"id":"..."} = account exists
# 404 + {"error":{"code":"NO_SUCH_USER"}} = failed
```
**Pitfall: `#mkmodal` URL is NOT a success indicator** — The modal URL appears even when registration silently fails (Turnstile token rejected). The CDP script `autoreg_v4.py` used `#mkmodal` as a success check — all 10 accounts were fake (`NO_SUCH_USER`). Always verify via API, never trust the URL alone.
**Pitfall: `#/signup` in URL = definitely failed** — if the page stays on the signup URL after submit, registration was rejected.
## Python 3.14 Incompatibility
**`greenlet` C extension is broken on Python 3.14** — This affects `nodriver`, `patchright`, and `playwright`. All three fail with `ModuleNotFoundError: No module named 'greenlet._greenlet'`.
**Fix**: Use Python 3.11 at `/c/Users/User/AppData/Local/Python/Python311/python.exe`. Working versions:
- Python 3.11.9 ✅ (greenlet, patchright, playwright all work)
- Python 3.13 ✅ (available but untested for Turnstile)
- Python 3.14 ❌ (greenlet broken)
## Turnstile Bypass Attempts — All Failed on dc.hhhl.cc
Cloudflare Turnstile on dc.hhhl.cc (sitekey `0x4AAAAAADUhQ0UI_7UTXChP`) blocks ALL automated approaches tested:
| Approach | Result | Notes |
|----------|--------|-------|
| **CDP (Chrome DevTools Protocol)** | ❌ Blocked | Turnstile never auto-solves, submit button stays disabled |
| **Browserbase (cloud browser)** | ❌ Blocked | Turnstile iframe has empty `src` — widget doesn't load |
| **patchright (Playwright anti-detection)** | ❌ Blocked | Turnstile checkbox clickable but never auto-solves |
| **Theyka Turnstile-Solver** | ❌ Domain mismatch | Solver generates token on localhost — token invalid for dc.hhhl.cc |
**Only working path**: manual Turnstile solve in a real browser, then extract the `cf-turnstile-response` token from the DOM. Or use a paid service (capsolver, 2captcha) that can generate domain-valid tokens.
**Turnstile-Solver domain-binding note**: The Theyka solver (`github.com/Theyka/Turnstile-Solver`) uses `page.route()` to intercept the target URL and serve a local page with the Turnstile widget. The token is generated for `localhost`, not the target domain. Cloudflare rejects it. This pattern is NOT suitable for actual site registration — only for testing the solver itself.
## Vue/Sharkey Form Interaction Pattern
The Sharkey (Misskey fork) SPA uses Vue.js. Standard Playwright/CDP clicks on checkboxes and buttons are silently ignored by the Vue reactivity system. **JS evaluation + dispatchEvent is required**:
```javascript
// Check checkboxes and click Continue (all in one JS call)
(() => {
const cbs = document.querySelectorAll('input[type="checkbox"]');
cbs.forEach(cb => {
cb.checked = true;
cb.dispatchEvent(new Event('change', {bubbles: true}));
cb.dispatchEvent(new Event('input', {bubbles: true}));
});
const btns = document.querySelectorAll('button');
for (const btn of btns) {
if (btn.textContent.includes('Продолжить')) {
btn.disabled = false;
btn.click();
return 'clicked';
}
}
})()
```
**Form filling also needs JS bypass**:
```javascript
const el = document.querySelector('input[name="username"]');
el.focus();
el.value = 'my_username';
el.dispatchEvent(new Event('input', {bubbles: true}));
```
**Turnstile checkbox click** (inside cross-origin iframe):
```javascript
const frames = document.querySelectorAll('iframe');
for (const frame of frames) {
try {
const doc = frame.contentDocument || frame.contentWindow.document;
const cb = doc.querySelector('input[type="checkbox"]');
if (cb) { cb.click(); break; }
} catch(e) {}
}
```
## Verification Email Problem
Short-term Gmail via AnyMessage cannot read spam folder. Verification emails from Misskey go to Gmail spam. Solutions:
- Use long-term email with IMAP (but AnyMessage long-term has no Gmail)
- Use external Gmail PVA service with IMAP/app-password
- Check spam via `[REDACTED-NAME]@gmail.com` IMAP (if forwarded)
## CDP Launch Pattern
```python
CHROME = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
CDP_PORT = 9225
# Kill existing Chrome on port first
subprocess.run(f'for /f "tokens=5" %a in (\'netstat -ano ^| findstr :{CDP_PORT}\') do taskkill /F /PID %a',
shell=True, capture_output=True, timeout=5)
subprocess.run('taskkill /F /IM chrome.exe 2>nul', shell=True, capture_output=True, timeout=5)
time.sleep(2)
cmd = [
CHROME,
f"--remote-debugging-port={CDP_PORT}",
f"--user-data-dir={profile_dir}",
"--no-sandbox", "--disable-gpu",
"--no-first-run", "--no-default-browser-check",
"--no-restore-last-session", # CRITICAL: prevents Chrome from restoring previous tabs
"--disable-blink-features=AutomationControlled",
"--window-size=1280,900",
"--new-window",
"about:blank",
]
proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(4)
```
### CRITICAL: Browser-level vs Page-level WebSocket
When connecting to CDP, there are TWO WebSocket endpoints:
- **Browser-level** (`/json/version` → `webSocketDebuggerUrl`): Only supports `Target.*` methods (createTarget, closeTarget, etc.)
- **Page-level** (`/json` → page's `webSocketDebuggerUrl`): Supports `Page.*`, `Runtime.*`, `DOM.*`
**Calling `Page.enable` on the browser-level WS returns `"'Page.enable' wasn't found"` (error -32601).**
Correct pattern:
```python
# 1. Connect to browser-level WS
browser_ws = GET /json/version → data["webSocketDebuggerUrl"]
browser_cdp = connect(browser_ws)
# 2. Create a new tab (browser-level)
result = await browser_cdp.cmd("Target.createTarget", {"url": "about:blank"})
target_id = result["targetId"]
# 3. Close browser connection, open page connection
await browser_cdp.close()
# 4. Find the new page's WS URL
pages = GET /json
page_ws = next(p["webSocketDebuggerUrl"] for p in pages if p["id"] == target_id)
# 5. Connect to page-level WS — NOW you can call Page.enable, Runtime.enable
page_cdp = connect(page_ws)
await page_cdp.cmd("Page.enable")
await page_cdp.cmd("Runtime.enable")
```
### Chrome `--no-restore-last-session` Flag
Without this flag, Chrome restores the previous session's tabs and ignores the URL argument. The browser opens on a chrome-extension page or previous session's tabs instead of the intended URL. This caused `manual_turnstile.py` to hang on `chrome-extension://nkeimhogjdpnpccoofpliimaahmaaome/background` for 180 seconds.
### Turnstile Token Polling + form.submit() Pattern
After the user solves Turnstile manually, the token appears in `input[name="cf-turnstile-response"]`. Poll at 1-second intervals (not 2s — faster detection reduces token expiry risk):
```python
token = None
t0 = time.time()
while time.time() - t0 < 180:
token = await cdp.js("""
(function(){
var el=document.querySelector('input[name="cf-turnstile-response"]');
if(el&&el.value&&el.value.length>20)return el.value;
return '';
})()
""")
if isinstance(token, str) and len(token) > 20:
break
await asyncio.sleep(1) # 1s polling
# IMMEDIATE submit via form.submit() — faster than button.click()
await cdp.js("""
(function(){
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
if (forms[i].querySelector('input[name="username"]')) {
forms[i].submit();
return 'submitted';
}
}
// Fallback
var btns = document.querySelectorAll('button');
for (var i = 0; i < btns.length; i++) {
if (btns[i].textContent.includes('Начать')) {
btns[i].click();
return 'clicked';
}
}
return 'not found';
})()
""")
```
**Why `form.submit()` over `button.click()`**: The button click triggers Vue/React event handlers which may add delays. `form.submit()` sends the form data directly, reducing the window between token detection and server submission. This matters because the Turnstile token may have a short validity window.
### API Verification with Retries
Misskey is slow to index new users. `POST /api/users/show` may return `NO_SUCH_USER` for 10-30 seconds after successful registration. Retry pattern:
```python
for attempt in range(8):
ok, info = verify_api(username)
if ok:
break
await asyncio.sleep(3) # 3s between retries
```
## Gmail IMAP UTF-7 Folder Names (Russian)
Russian Gmail accounts use UTF-7 encoded folder names:
- Spam: `[Gmail]/&BCEEPwQwBDw-` (= "Спам")
- All Mail: `[Gmail]/&BBIEQQRP- &BD8EPgRHBEIEMA-` (= "Вся почта")
- Sent: `[Gmail]/&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-` (= "Отправленные")
- Trash: `[Gmail]/&BBoEPgRABDcEOAQ9BDA-` (= "Корзина")
Use `mail.list()` to discover actual folder names, then use the encoded names in `mail.select()`.