auto-account-machine/browser-automation-ts/configs/sites/windsurf.yaml
2025-11-21 17:59:49 +08:00

336 lines
11 KiB
YAML
Raw 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.

# Windsurf 注册自动化配置
site:
name: Windsurf
url: https://windsurf.com/refer?referral_code=55424ec434
# 工作流定义
workflow:
# ==================== 步骤 0: 处理邀请链接 ====================
- action: navigate
name: "打开邀请链接"
url: "https://windsurf.com/refer?referral_code=55424ec434"
options:
waitUntil: 'networkidle2'
timeout: 30000
- action: click
name: "点击接受邀请"
selector:
- text: 'Sign up to accept referral'
selector: 'button'
- css: 'button.bg-sk-aqua'
- css: 'button:has-text("Sign up to accept referral")'
timeout: 30000
waitForNavigation: true
# 验证跳转到注册页面带referral_code
- action: verify
name: "验证跳转到注册页面"
conditions:
success:
- urlContains: "/account/register"
- urlContains: "referral_code=55424ec434"
timeout: 10000
# ==================== 步骤 1: 打开注册页面并填写信息(带重试) ====================
- action: retryBlock
name: "打开注册页面并填写基本信息"
maxRetries: 3
retryDelay: 3000
steps:
# 1.1 验证注册页面元素已加载
- action: wait
name: "等待注册表单加载"
type: element
selector: '#firstName'
timeout: 15000
# 1.2 填写基本信息
- action: fillForm
name: "填写基本信息"
fields:
firstName: "{{account.firstName}}"
lastName: "{{account.lastName}}"
email: "{{account.email}}"
# 1.3 勾选同意条款
- action: click
name: "勾选同意条款"
selector:
- css: 'input[type="checkbox"]'
optional: true
# 1.4 点击 Continue
- action: click
name: "点击 Continue (基本信息)"
selector:
- text: 'Continue'
selector: 'button, a' # 明确指定查找范围:按钮或链接
timeout: 30000
# 验证点击后密码页面出现
verifyAfter:
appears:
- '#password'
- 'input[type="password"]'
# ==================== 步骤 2: 设置密码 ====================
- action: fillForm
name: "设置密码"
fields:
password: "{{account.password}}"
passwordConfirmation: "{{account.password}}"
- action: click
name: "提交密码"
selector:
- text: 'Continue'
selector: 'button, a'
timeout: 30000
# ==================== 步骤 2.5: Cloudflare Turnstile 验证 ====================
- action: custom
name: "Cloudflare Turnstile 验证"
handler: "handleTurnstile"
params:
timeout: 30000
maxRetries: 3
# 重试策略:
# - 'refresh': 刷新页面(适用于保持状态的网站)
# - 'restart': 刷新后重新填写(适用于刷新=重置的网站,如 Windsurf
# - 'wait': 只等待不刷新
retryStrategy: 'restart' # Windsurf 刷新会回到第一步
optional: true
# ==================== 步骤 3: 邮箱验证 ====================
# 需要自定义处理:获取邮件验证码 + 填写6个输入框
- action: custom
name: "邮箱验证"
handler: "handleEmailVerification"
params:
timeout: 120000
# ==================== 步骤 4: 跳过问卷调查 ====================
- action: click
name: "跳过问卷"
selector:
- text: 'Skip this step'
selector: 'button, a'
- text: 'skip'
selector: 'button, a'
exact: false
# ==================== 步骤 5: 选择计划 ====================
- action: click
name: "选择计划"
selector:
- text: 'Select plan'
selector: 'button, a'
timeout: 30000
# 等待跳转到 Stripe 支付页面(通用 URL 等待)
- action: wait
name: "等待跳转到 Stripe"
type: url
urlContains: "stripe.com"
timeout: 20000
# ==================== 步骤 6: 填写支付信息(带重试) ====================
- action: retryBlock
name: "提交支付并验证"
maxRetries: 9999 # 无限重试,直到成功
retryDelay: 15000 # 增加到15秒避免触发Stripe风控
onRetryBefore:
# 生成银行卡(第一次或重试都生成新卡)
- action: custom
handler: "generateCard"
steps:
# 6.1 选择银行卡支付方式(每次重试都重新选择)
- action: click
name: "选择银行卡支付"
selector:
- css: 'input[type="radio"][value="card"]'
verifyAfter:
checked: true # 验证 radio button 是否选中
# 填写支付信息(银行卡+国家+地址)
- action: fillForm
name: "填写支付信息"
fields:
cardNumber: "{{card.number}}"
cardExpiry: "{{card.month}}{{card.year}}"
cardCvc: "{{card.cvv}}"
billingName: "{{account.firstName}} {{account.lastName}}"
billingCountry:
find:
- css: '#billingCountry'
value: "MO"
type: "select"
addressLine1:
find:
- css: '#billingAddressLine1' # 使用 id 选择器,兼容所有语言
- css: 'input[name="billingAddressLine1"]'
- css: 'input[placeholder*="地址"]'
- css: 'input[placeholder*="Address"]'
- css: 'input[placeholder*="Alamat"]' # 印尼语
value: "kowloon"
# 滚动到页面底部(确保订阅按钮可见)
- action: scroll
name: "滚动到订阅按钮"
type: bottom
# 提交支付(内部会等待按钮变为可点击状态)
- action: click
name: "点击提交支付"
selector:
- css: 'button[data-testid="hosted-payment-submit-button"]' # Stripe 官方 testid
- css: 'button.SubmitButton' # Stripe 按钮类名
- css: 'button[type="submit"]' # 通用 submit 按钮
- css: 'button:has(.SubmitButton-IconContainer)' # 包含 icon 容器的按钮
timeout: 30000 # 给足够时间等待按钮从 disabled 变为可点击
waitForEnabled: true # 循环等待按钮激活(不是等待出现,而是等待可点击)
# 验证点击是否生效(检查按钮状态变化)
- action: custom
name: "验证订阅按钮点击生效"
handler: "verifySubmitClick"
# 处理 hCaptcha等待60秒让用户手动完成
- action: custom
name: "hCaptcha 验证"
handler: "handleHCaptcha"
# 验证支付结果(轮询检测成功或失败)
- action: verify
name: "验证支付结果"
conditions:
success:
- urlNotContains: "stripe.com"
- urlNotContains: "checkout.stripe.com"
failure:
# 英文
- textContains: "card was declined"
- textContains: "Your card was declined"
- textContains: "declined"
- textContains: "We are unable to authenticate your payment method"
# 中文
- textContains: "卡片被拒绝"
- textContains: "我们无法验证您的支付方式"
- textContains: "我们未能验证您的支付方式"
- textContains: "请选择另一支付方式并重试"
# 马来语
- textContains: "Kad anda telah ditolak" # 您的卡已被拒绝
- textContains: "Kami tidak dapat mensahihkan kaedah pembayaran"
- textContains: "Sila pilih kaedah pembayaran yang berbeza"
# 印尼语
- textContains: "Kami tidak dapat memverifikasi metode pembayaran"
- textContains: "Silakan pilih metode pembayaran"
- textContains: "kartu ditolak"
# 泰语
- textContains: "บัตรของคุณถูกปฏิเสธ" # 您的卡被拒绝
- textContains: "ไม่สามารถยืนยัน" # 无法验证
- textContains: "เราตรวจสอบสิทธิ์วิธีการชำระเงินของคุณไม่ได้" # 我们无法验证您的支付方式
- textContains: "โปรดเลือกวิธีการชำระเงินอื่น" # 请选择另一个支付方式
# 通用错误元素
- elementExists: ".error-message"
timeout: 15000
pollInterval: 500
onFailure: "throw"
# ==================== 步骤 7: 获取订阅信息 ====================
# 7.1 跳转到订阅使用页面
- action: navigate
name: "跳转订阅页面"
url: "https://windsurf.com/subscription/usage"
# 7.2 提取配额信息
- action: extract
name: "提取配额信息"
selector: "p.caption1.font-medium.text-sk-black\\/80 span.caption3 span"
multiple: true
contextKey: "quotaRaw"
required: false
# 7.3 提取账单周期信息
- action: extract
name: "提取账单周期"
selector: "p.caption1"
extractType: "text"
filter:
contains: "Next billing cycle"
regex: "(\\d+)\\s+days?.*on\\s+([A-Za-z]+\\s+\\d+,\\s+\\d{4})"
saveTo:
days: "$1"
date: "$2"
contextKey: "billingInfo"
required: false
# 7.4 处理提取的数据(自定义)
- action: custom
name: "处理订阅数据"
handler: "processSubscriptionData"
optional: true
# ==================== 步骤 8: 保存到数据库 ====================
- action: custom
name: "保存到数据库"
handler: "saveToDatabase"
optional: true
# ==================== 步骤 9: 退出登录 ====================
# 9.1 滚动到页面底部
- action: scroll
name: "滚动到页面底部"
direction: "bottom"
# 9.2 等待滚动完成
- action: wait
name: "等待滚动完成"
duration: 1000
# 9.3 点击退出登录
- action: click
name: "点击退出登录"
selector:
- css: 'div.body3.cursor-pointer:has-text("Log out")'
- css: 'div.body3:has-text("Log out")'
- xpath: '//div[contains(@class, "body3") and contains(text(), "Log out")]'
- text: "Log out"
timeout: 15000
waitForNavigation: true
# 9.4 验证页面已变化(离开订阅页面)
- action: verify
name: "验证页面已变化"
conditions:
success:
- or:
- urlContains: "/account/login"
- urlContains: "/login"
- urlContains: "/"
failure:
- urlContains: "/subscription/usage"
timeout: 10000
optional: true
# 9.5 验证跳转到登录页
- action: verify
name: "验证跳转到登录页"
conditions:
success:
- or:
- urlContains: "/account/login"
- urlContains: "/login"
timeout: 5000
optional: true
# 错误处理配置
errorHandling:
screenshot: true
retry:
enabled: true
maxAttempts: 3
delay: 2000