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