Files
autoreg-skills-max/skills/bulk-account-registration/references/misskey-sharkey-registration.md

287 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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()`.