Caputchin
自定义游戏开发

用手动模式自己驱动验证

到这篇教程结束时,你将从你自己的代码、而非内置 UI 来驱动 Caputchin 验证生命周期,在两个元素中适合你情况的那一个上。手动模式trigger="manual")存在于两个不同的元素上,而它们干的活儿确实不同:

元素手动模式给你什么你驱动
<caputchin-widget>内置复选框被隐藏;你从你 自己的触发(一次表单提交、一个自定义按钮)触发 proof-of-work 求解。无游戏。start()(然后求解通常自行结清)
<caputchin-game>无 iframe;你把 你自己的游戏标记 嵌进组件的布局外壳,并从玩法决定结果。pass() / fail()(无 start();回合一挂载即上线)

按你要回答的问题来选:

  • “我想要正常的隐形/复选框验证,但由我自己的手势触发,且不带默认复选框。” → 下面的 复选框组件
  • “我想要我自己的交互式挑战渲染在我自己的 DOM 里,而非一个 iframe 游戏,且由我决定通过/失败。” → 下面的 游戏宿主

游戏路径适配在哪里,见 跑你自己的游戏。你的页面上需要已经有组件;如果还没有,就先去 添加组件

两者共有的一个局限:一个手动模式的回合 无法满足一个 游戏关卡。复选框元素根本从不给一个密钥设关卡(只有 <caputchin-game> 能),而一个手动的 <caputchin-game> 回合没有可回放的轨迹,所以一个设了关卡的密钥拒绝它。手动模式是给未设关卡的密钥(proof-of-work 验证)或纯游戏用途的。

路径 A:复选框组件,你自己的触发

当你想要标准验证、但想自己触发它时用这个,例如只在访客提交一个表单时跑求解,或者放在你自己的按钮后面而非内置复选框。

1. 把元素标为手动

<caputchin-widget id="cap" sitekey="cpt_pub_..." trigger="manual"></caputchin-widget>
<button id="go" type="button">Verify and continue</button>

有了 trigger="manual",内置复选框 UI 被隐藏;元素等你来开始求解。

2. 从你的手势开始求解

手动模式下的复选框元素暴露单一一个方法,start()

  • start() 踢动 proof-of-work 求解,取代复选框点击。在 trigger="manual" 下有效。

复选框组件没有 pass() / fail();你一旦调用 start(),求解就自行结清并发出 pass 事件。(释放/中止把手在游戏宿主上,在路径 B 里讲到。)

const widget = document.getElementById("cap");

document.getElementById("go").addEventListener("click", () => {
  widget.start(); // begin verification now
});

widget.addEventListener("pass", () => {
  // token issued; submit your form / continue
});

当求解结清时,元素完成验证,并(在一个表单里)像任何其他组件那样注入 caputchin-token 字段。你的后端 核验那个令牌 一如往常;手动模式在服务端什么都不改。

路径 B:游戏宿主,你自己的 DOM

当你想把你自己的交互式挑战渲染在你自己的标记里、而非加载一个 iframe 游戏,并自己决定结果时用这个。

1. 把元素标为手动并嵌入你的标记

<caputchin-game> 上放 trigger="manual",并把你自己的标记放进它里面。在其他每个模式里组件都忽略 light-DOM 子元素;手动模式是它通过一个 <slot> 把它们投影进它布局外壳的那唯一一个模式。

<caputchin-game id="cap" sitekey="cpt_pub_..." trigger="manual">
  <!-- Your own markup. The widget renders it inside its shell. -->
  <div id="my-game">
    <p>Tap the banana three times.</p>
    <button id="banana" type="button">🍌</button>
  </div>
</caputchin-game>

在有一个 sitekey 存在、且密钥未设关卡时,组件在后台跑它的 proof-of-work 检查;你的交互是看得见的那部分。在没有 sitekey(或 no-verify)时,它是纯游戏:你的交互跑起来,没有什么可验证。

2. 从你的代码驱动结果

手动模式下的游戏宿主暴露 pass()fail()没有 start():回合在元素一挂载时就上线(工厂被挂载就是那个开始信号)。

  • pass(payload?) 释放验证:访客成功了。调用它一次;之后的调用被忽略(裁定已锁定)。
  • fail(payload?) 中止回合。可选;一个未完成的回合无论如何都被当作非通过。

那些载荷:

  • pass({ trace })trace 是你的交互产出的那条不透明的回合记录。在一个设了关卡的回合上,服务器重放它以得裁定;在一个未设关卡或纯游戏的密钥上,它被原样接受,所以当没有什么可回放时传一个空字符串(或一个简单的标记)。这里没有客户端提供的分数。
  • fail({ code?, message? }):一个可选的诊断 codemessage,两者都是客户端元数据,绝非一个信任信号。
const widget = document.getElementById("cap");
let taps = 0;

document.getElementById("banana").addEventListener("click", () => {
  taps += 1;
  if (taps >= 3) {
    widget.pass({ trace: `banana:${taps}` }); // visitor won
  }
});

// Optional: give up explicitly.
// widget.fail({ code: "gave-up", message: "closed the prompt" });

和复选框路径一样,pass() 完成验证并注入令牌;你的后端以同样的方式核验它。

对结果作出反应(两条路径)

监听组件的事件来更新你自己的 UI:

widget.addEventListener("pass", () => {
  // verification released
});
widget.addEventListener("error", (e) => {
  console.warn("verification error", e.detail);
});

那个信任决定始终是你的后端确认令牌,而非那个事件;事件是给 UX 的。

手动模式做不到什么

  • 给一个站点密钥设关卡。 一个打开了 要求一个游戏 的密钥根本无法被复选框元素满足,而且拒绝一个手动的 <caputchin-game>(它在搭建时抛出一个配置错误),因为没有可回放的轨迹。手动模式是给未设关卡或纯游戏的密钥的。
  • 重放一个自定义游戏的回合。 如果你需要一个设关卡的游戏,就把它渲染成一个 自托管 iframe 游戏 并产出一条轨迹,而不是手动模式。

另见

本页内容