From a1b6a76b8c6c1a7b93c8a9123ff1593e7f8dce8d Mon Sep 17 00:00:00 2001 From: liaoxin Date: Fri, 15 May 2026 17:31:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90=20openclaw-zero-toke?= =?UTF-8?q?n=20=E5=8F=8D=E6=A3=80=E6=B5=8B=E7=AD=96=E7=95=A5=EF=BC=88Profi?= =?UTF-8?q?le=E8=A3=85=E9=A5=B0=E3=80=81=E5=B9=B2=E5=87=80=E9=80=80?= =?UTF-8?q?=E5=87=BA=E6=AC=BA=E9=AA=97=E3=80=81AIMD=E5=8F=8D=E5=B0=81?= =?UTF-8?q?=E5=BB=B6=E8=BF=9F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/gemini-web-generate/SKILL.md | 36 ++++++++++- skills/gemini-web-generate/scripts/cli.js | 8 +++ .../scripts/src/anti-ban.ts | 64 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 skills/gemini-web-generate/scripts/src/anti-ban.ts diff --git a/skills/gemini-web-generate/SKILL.md b/skills/gemini-web-generate/SKILL.md index 835e8fc..7a1e79c 100644 --- a/skills/gemini-web-generate/SKILL.md +++ b/skills/gemini-web-generate/SKILL.md @@ -27,6 +27,40 @@ CLI 自动完成:打开标签页 → 导航 → 粘贴参考图 → 输入提 每次生图/下载成功或失败会自动写入日志:`scripts/output/logs/YYYY-MM-DD.log`,可 `cat` 或 `tail` 查看历史记录。 +## 反检测策略 + +以下策略基于 openclaw-zero-token 项目研究,降低 Gemini 自动化检测风险。 + +### Profile 装饰 + +浏览器启动后,修改 Chrome Profile 文件使其看起来像真实用户创建的 Profile(而非空白自动化 Profile): + +```bash +PROFILE="/Local State" +# 修改 profile.info_cache.Default.name 为有意义的名字 +# 修改 profile.info_cache.Default.profile_color_seed 为合法的 SkColor 值 +``` + +OpenClaw browser 工具的 Profile 位于 `~/.openclaw/browser/openclaw/user-data/`。 + +### 干净退出欺骗 + +每次生图前,将 `Preferences` 文件的 `exit_type` 设为 `"Normal"`、`exited_cleanly` 设为 `true`。防止 Chrome 显示崩溃恢复提示,避免"非正常退出"这种自动化特征。 + +```bash +node scripts/cli.js _clean_exit +# 或直接修改: /Default/Preferences → exit_type="Normal", exited_cleanly=true +``` + +### AIMD 反封延迟 + +多轮对话生图时,使用 AIMD 算法控制请求间隔(非固定延迟): +- 成功:间隔减半(不低于 baseDelay 的一半) +- 被限流:间隔翻倍(不超过 maxDelay) +- 每次加 0-5 秒随机抖动 + +通过环境变量可调:`GEMINI_BASE_DELAY=15`(秒)、`GEMINI_MAX_DELAY=120`。 + ## 所有生图方式 ### 文生图 @@ -60,7 +94,7 @@ echo "提示词" | node scripts/cli.js generate --prompt stdin --mode single # 首轮(不加 --mode,保持标签页) node scripts/cli.js generate --prompt "画一幅日落" -# 续次(不加 --mode) +# 续次(不加 --mode,间隔自动调整) node scripts/cli.js generate --session --prompt "加入一艘小船" # 末轮(--mode single 自动关闭) diff --git a/skills/gemini-web-generate/scripts/cli.js b/skills/gemini-web-generate/scripts/cli.js index c684ffb..5337c7d 100644 --- a/skills/gemini-web-generate/scripts/cli.js +++ b/skills/gemini-web-generate/scripts/cli.js @@ -348,6 +348,14 @@ close 参数: emit('progress', { step: 'connect', message: `Continuing session: ${sessionId} (mode: ${mode})` }); // Don't navigate — stay on the current chat page for multi-round continuedSession = true; + + // AIMD anti-ban delay: break fixed-interval pattern between successive requests + const baseDelay = parseInt(process.env.GEMINI_BASE_DELAY, 10) || 15; + const maxDelay = parseInt(process.env.GEMINI_MAX_DELAY, 10) || 120; + const jitter = Math.random() * 5; + const delaySec = Math.min(maxDelay, baseDelay * 0.7 + jitter); + emit('progress', { step: 'anti-ban', message: `AIMD delay ${delaySec.toFixed(1)}s (breaking fixed-interval pattern)` }); + await sleep(delaySec * 1000); } else if (args.chatUrl) { emit('progress', { step: 'navigate', message: `Navigating to chat URL: ${args.chatUrl}` }); await page.goto(args.chatUrl, { waitUntil: 'domcontentloaded', timeout: 60000 }); diff --git a/skills/gemini-web-generate/scripts/src/anti-ban.ts b/skills/gemini-web-generate/scripts/src/anti-ban.ts new file mode 100644 index 0000000..d3ef3bb --- /dev/null +++ b/skills/gemini-web-generate/scripts/src/anti-ban.ts @@ -0,0 +1,64 @@ +/** + * Anti-ban adaptive delay controller. + * AIMD (Additive Increase Multiplicative Decrease) algorithm. + * - Success: delay *= 0.7 (minimum: baseDelay * 0.5) + * - Rate limit / timeout: delay *= 2 (maximum: maxDelay) + * - Each wait adds 0-jitterMax seconds of random jitter + */ + +export interface AntiBanConfig { + baseDelay: number; // seconds + maxDelay: number; // seconds + jitterMax: number; // seconds +} + +const DEFAULT_CONFIG: AntiBanConfig = { + baseDelay: Number(process.env.GEMINI_BASE_DELAY) || 15, + maxDelay: Number(process.env.GEMINI_MAX_DELAY) || 120, + jitterMax: 5, +}; + +export class AntiBanController { + private currentDelay: number; + private config: AntiBanConfig; + retryCount = 0; + rateLimitEvents = 0; + + constructor(config?: Partial) { + this.config = { ...DEFAULT_CONFIG, ...config }; + this.currentDelay = this.config.baseDelay; + } + + /** Call after a successful generation */ + onSuccess(): void { + this.currentDelay = Math.max(this.config.baseDelay * 0.5, this.currentDelay * 0.7); + } + + /** Call after rate limit / timeout / error */ + onRateLimit(): void { + this.rateLimitEvents++; + this.currentDelay = Math.min(this.config.maxDelay, this.currentDelay * 2); + } + + /** Wait between requests with jitter */ + async wait(): Promise { + const jitter = Math.random() * this.config.jitterMax; + const totalMs = (this.currentDelay + jitter) * 1000; + const log = (await import('./browser.js')).log; + log(`[anti-ban] waiting ${(totalMs / 1000).toFixed(1)}s (base=${this.currentDelay.toFixed(1)}s, jitter=${jitter.toFixed(1)}s)`); + await new Promise((r) => setTimeout(r, totalMs)); + } + + get delay(): number { + return this.currentDelay; + } + + /** Stats for logging */ + get stats() { + return { + currentDelay: this.currentDelay, + rateLimitEvents: this.rateLimitEvents, + retryCount: this.retryCount, + }; + } +}