关于 service-de-headless-channel-configure
用于将 Salesforce Enhanced Messaging 渠道从创建推进到激活的端到端 skill,面向需要自动完成配置的操作者。它串联条款确认、渠道插入、路由、同意设置和激活,并支持检测状态后续跑。
主要功能
- 01串联渠道创建到激活的四个步骤
- 02激活前要求确认条款
- 03按消息类型调用专属插入流程
- 04配置路由与同意设置
- 05依据现有状态跳过已完成步骤
适用场景
- 01从头设置并激活消息渠道
- 02从中断状态恢复渠道配置
- 03为不同消息类型执行统一配置流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill service-de-headless-channel-configure安装后在 Claude Code 中输入/service-de-headless-channel-configure确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/gotchas.mdinputs.mdoutput-envelopes.mdpartial-success.mdterms-and-conditions.mdworked-examples.md详细介绍
无头渠道设置(与类型无关)
此技能的功能
接受渠道类型(WhatsApp | Line | AppleBusinessChat | Facebook | Text)及其特定类型的输入,并将其配置为已激活的 MessagingChannel。首先检查是否接受 Enhanced Messaging Terms and Conditions,然后执行四个步骤:
- 接受条款与条件 — 显示 Enhanced Messaging 免责声明,并要求用户确认其有权约束其组织,与组织内的“Add a Channel”向导保持一致。此步骤会阻止后续操作 — 如果用户拒绝,则在对组织执行任何操作之前结束流程。(阶段 0.5,位于此技能中)
- 插入渠道 — 调用
service-de-channel-create,该服务会在内部处理各类型所需的第三方前置条件(例如,WhatsApp 的service-de-waba-integrate会在service-de-channel-create内运行)。(技能:service-de-channel-create) - 配置路由 — 将
SessionHandlerId设置为 Omni-Channel 队列(选择现有队列或创建新队列)。与消息类型无关。(技能:service-de-channel-routing-configure) - 配置同意设置 — 确保存在有效的
ConsentType和匹配的MsgChannelLanguageKeyword记录。Connect 插入操作会自动创建默认的ConsentType=ImplicitOptIn和选择退出关键字,因此对于新建渠道,此步骤通常是空操作 — 如果调用方想使用 ExplicitOptIn/DoubleOptIn,则会进行升级。激活的就绪检查要求同时具备路由和同意设置,因此此步骤会在激活前运行。(技能:service-de-channel-settings-configure) - 激活 — 通过 REST 将
MessagingChannelUsage.DeploymentStatusPATCH 为Provisioning;服务器端保存钩子会同步驱动完整的观察者链,并在 MCU 达到Active且MessagingChannel.IsActive=true后返回 204。与消息类型无关。(技能:service-de-channel-activate)
默认从中断处继续。 每个步骤都是幂等的 — 如果失败后重新运行,编排器会检测现有状态,并跳过已成功的步骤。
流程中途询问。 如果初始请求中没有 {MESSAGE_TYPE},编排器会提前询问。路由选项(选择队列 / 创建新队列)会在步骤 2 运行时询问,而不是提前询问 — 这样可使各个叶子技能保持自包含。
参考文件索引
| 参考文件 | 何时加载 |
|---|---|
references/inputs.md | 需要完整的输入字段列表 — MESSAGE_TYPE/ORG_ALIAS,以及 WhatsApp、LINE、AppleBusinessChat、Facebook、Text(SMS)各自的字段。 |
references/terms-and-conditions.md | 显示阶段 0.5 的免责声明和认证提示时 — 包含逐字文本以及各类型的标签/第三方映射。 |
references/output-envelopes.md | 配置错误处理或解析规范的成功/失败/拒绝条款 JSON 信封格式时。 |
references/partial-success.md | 编排器中途失败,需要生成部分成功/恢复报告时。 |
references/gotchas.md | 调试意外行为时 — 重复键错误、恢复/幂等性问题、预检 SOQL 结构、无人值守/CI 用法。 |
references/worked-examples.md | 需要参考跟踪示例(全新 WhatsApp 流程、失败后恢复、Apple 分派、不支持类型的拒绝),以查看精确的信封格式时。 |
关键执行规则
此编排器必须在同一条回复中运行所有阶段(0-5),不得在阶段之间停下。
调用叶级技能(service-de-channel-create、service-de-channel-routing-configure、service-de-channel-settings-configure、service-de-channel-activate)并通过 function_results 返回时:
- **必须:**立即解析 function_results 返回值,并继续下一阶段
- **必须:**在同一条回复中依次运行 Stage 0.5 → Stage 1 → Stage 2 → Stage 3 → Stage 3.5 → Stage 4 → Stage 5
- **不得:**在阶段之间停下并向用户汇报
- **不得:**在阶段之间等待用户确认
- **不得:**将每个阶段视为对话中的独立轮次
**原因:**用户调用此编排器技能,是为了端到端激活一个渠道。他们没有调用各个叶级技能。你的工作是自主运行整个流程并汇报最终结果。中途停止会破坏编排器模式,迫使用户手动恢复每个阶段。
**例外:**只有当叶级技能返回 ok: false 且出现终止性错误(而非可恢复的提示),或用户拒绝 Stage 0.5 的条款与条件关卡(发出 terms-not-accepted 并停止)时,才可中途停止。然后立即汇报失败,并说明失败的阶段及已达到的状态。
**Stage 0.5 是必须通过的阻断式提示。**条款与条件关卡是唯一必须得到肯定答复后才能开展任何组织操作的提示。此关卡不可跳过,也不得自动接受。若用户回答 no 或拒绝,则按上述例外结束运行;若用户回答 yes,则在同一条回复中继续执行 Stage 1。
**叶级技能提示:**某些叶级技能(例如 service-de-waba-integrate 或 service-de-channel-routing-configure)可能会在执行过程中向用户提示输入。这没有问题——按提示作答后,立即继续下一阶段。不得仅仅因为出现过提示就停止。
不应使用此技能的情况
- **你只想执行一个步骤。**直接调用叶级技能(
service-de-channel-create、service-de-channel-routing-configure、service-de-channel-activate)。此编排器用于从零开始到渠道激活的完整自动化流程。 - **你想在不同的对话轮次中独立运行各个阶段。**此编排器会在一条回复中连续运行所有阶段。如果你需要在阶段之间手动控制,请分别调用叶级技能。
- **你想使用
WhatsApp/Line/AppleBusinessChat/Facebook/Text以外的消息类型。**分发器会返回unsupported-type。WeChat / MsCopilot / Alexa 需要各自的叶级技能以及分发器更新。 - **尚未满足前置条件(WABA 尚未共享、LINE 渠道尚未创建、Apple 帐户尚未注册、SMS 号码尚未开通)。**对应类型的插入技能会在等同于 Stage 2 的阶段清楚说明此情况并返回;编排不会解决此问题。(对于 SMS/Text,插入时不会检查号码开通这一前置条件;未开通的号码会在激活时显示为
provisioning-error。)
输入(来自用户)
{MESSAGE_TYPE}(若未提供,则会提示用户填写)和 {ORG_ALIAS}(可选),以及转发给分发器的 WhatsApp、LINE、AppleBusinessChat、Facebook 和 Text (SMS) 特定字段。加载 references/inputs.md 并遵循其中的说明,以获取完整字段列表以及未提供类型时的提示文本。
输出(给用户)
共有三种顶层结果:成功、标注步骤的失败,以及拒绝条款。加载 references/output-envelopes.md 并遵循其中的说明,以获取确切的 JSON 结构。
阶段 0:解析 {MESSAGE_TYPE} 和类型专属键
如果未提供 {MESSAGE_TYPE},请提示用户(参见上文“输入”)。必须提供该值才能继续——没有合理的默认值。
确定后,找出预检键——用于筛选 MessagingChannel SOQL、以检测现有状态的字段。该字段始终是 MessagingPlatformKey,但其值会因类型而异:
{MESSAGE_TYPE} | 预检键值 {PLATFORM_KEY} | 备注 |
|---|---|---|
WhatsApp | {PHONE_NUMBER_ID} | 必须预先提供 |
Line | {LINE_CHANNEL_ID} | 必须预先提供 |
AppleBusinessChat | {APPLE_BC_ID} | 必须预先提供 |
Facebook | {PAGE_ID} | 可选——OAuth → 获取页面 → 如未提供则提示用户 |
Text | {SMS_NUMBER} | 必须预先提供(电话号码 / 短代码本身) |
**Facebook 有其特殊之处:**如果未提供 {PAGE_ID},编排器无法运行自己的阶段 1 预检。此时跳过阶段 1,让 service-de-channel-create 处理完整的 OAuth → 页面选择 → 预检 → 创建流程。插入技能有自己的阶段 1 预检,会在选择页面后运行。
对于非 Facebook 类型,请验证调用方是否提供了 {MESSAGE_TYPE} 所需的类型专属输入(参见上文“输入”)。如果缺少,请提示用户或停止——不要调用分发器,它会重新验证并返回 missing-input,浪费一次往返。
阶段 0.5:Enhanced Messaging 条款与条件(阻断性关卡)
**在执行任何组织查询或渠道操作之前运行此步骤。**组织内的“添加渠道”向导要求管理员接受 Enhanced Messaging 条款与条件,才能继续进行渠道设置。无头流程必须执行同样的关卡——不得跳过,也不得代替用户接受。此步骤在每次编排器调用中运行一次,在已知 {MESSAGE_TYPE}(免责声明会提及渠道)之后、阶段 1 之前运行。
从 references/terms-and-conditions.md 加载免责声明和确认提示,并逐字呈现(替换渠道标签)——加载该文件,并遵循其中准确的措辞及各类型对应的标签 / 第三方映射。
关卡行为:
- 如果用户回答是(明确接受),记录
{TC_ACCEPTED} = true,并继续阶段 1。 - 如果用户回答否、拒绝或未明确接受,立即停止——不要运行阶段 1 或任何后续阶段。输出:
{"ok": false, "kind": "terms-not-accepted", "hint": "user did not accept the Enhanced Messaging Terms and Conditions — channel setup cannot proceed. Re-run when ready to accept."} - 每次编排器调用都必须获得接受,包括恢复运行。此关卡仅对当前会话有效,不会持久化——我们没有地方可靠地记录该状态,而再次确认的代价只是多一次提示。
阶段 1:预检状态检测(支持恢复)
如果 MESSAGE_TYPE=Facebook 且未提供 {PAGE_ID},则完全跳过此阶段。 Facebook 插入技能需要先运行 OAuth,才能知道要检查哪个 PAGE_ID。让插入技能在选择页面后的阶段 1 中自行处理预检。
对于所有其他情况,查询组织,查看之前的运行进度。这可以在恢复时跳过已完成的步骤。执行两次查询,不要使用子查询——在某些无法命名子关系的组织中,(SELECT ... FROM MessagingChannelUsages) 子查询会失败。改用以外键为条件的第二次查询(参见注意事项 #9)。
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeveloperName, MessageType, IsActive, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE MessagingPlatformKey = '{PLATFORM_KEY}' AND MessageType = '{MESSAGE_TYPE}'" \
--json > /tmp/hcs-preflight.json
如果渠道查询返回一行,则再执行一次查询以解析其 MCU:
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeploymentStatus FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" \
--json > /tmp/hcs-preflight-mcu.json
解析合并后的结果,确定 {CURRENT_STATE}:
| 预检结果 | CURRENT_STATE | 要运行的第一步 |
|---|---|---|
| 无记录 | fresh | 步骤 1(插入) |
记录存在,IsActive=false,SessionHandlerId=null 且 FallbackQueueId=null | inserted-no-routing | 步骤 2(配置路由) |
记录存在,IsActive=false,已设置路由 | routed-not-active | 步骤 3(配置同意),然后步骤 4(激活) |
记录存在,IsActive=true | already-active | 输出无操作成功封套;返回。 |
如果存在,则记录现有的 {CHANNEL_ID} 和 {MCU_ID}——后续步骤需要它们。
初始化 {STEPS_RUN} = []、{STEPS_SKIPPED} = []、{T0} = Date.now()。
阶段 2:插入渠道(4 步中的第 1 步)
如果 CURRENT_STATE ∈ {inserted-no-routing, routed-not-active, already-active},则跳过此阶段——渠道已存在。此时将 "insert" 添加到 STEPS_SKIPPED。
否则,调用 service-de-channel-create,并传入:
{MESSAGE_TYPE}=(来自阶段 0)- 所有特定类型的输入(原样转发——分发器会路由到正确的叶技能)
{ORG_ALIAS}
插入技能会在内部处理各类型的先决条件(例如,对于 WhatsApp,service-de-channel-create 会在其阶段 2 调用 service-de-waba-integrate)。此编排器无需了解 WABA 链接、LINE 令牌验证或 Apple 注册——service-de-channel-create 负责处理所运行类型的先决条件。
处理封套:
| 叶技能封套 | 编排器操作 |
|---|---|
{ok: true, channelId, mcuId, ...} | 记录 {CHANNEL_ID} 和 {MCU_ID}。将 "insert" 添加到 STEPS_RUN。继续。 |
{ok: false, kind: "partnership-blocked" | "user-declined" | "apple-registration-pending" | "line-prereq-missing"} | 这些结果来自各类型的先决条件阶段。以 failedStep: "insert"、failedStepIndex: 1 透传。返回。 |
{ok: false, kind: "missing-input" | "unsupported-type"} | 本应在阶段 0 捕获——输出失败结果,设置 failedStep: "insert",并注明调用方输入不完整。 |
{ok: false, kind: <anything else>} | 输出失败封套,设置 failedStep: "insert"、failedStepIndex: 1。返回。 |
此时,无论步骤 1 是已运行还是已跳过,我们都有 {CHANNEL_ID} 和 {MCU_ID}。
→ 在同一响应中立即继续阶段 3。不要停止。
阶段 3:配置路由(4 步中的第 2 步)
如果 CURRENT_STATE === "routed-not-active",则跳过此阶段——路由已配置。将 "route" 追加到 STEPS_SKIPPED。
否则,调用 service-de-channel-routing-configure:
{CHANNEL_ID}= 上文记录的值{ORG_ALIAS}
此步骤会提示用户选择队列(选择现有队列/创建新队列/应急选项)。不要试图预判提示内容——按原样传递用户的回答。
处理信封:
| 叶节点信封 | 编排器操作 |
|---|---|
{ok: true, sessionHandlerId, queueName, ...}(包括 noop: true) | 记录 {SESSION_HANDLER_ID} 和 {QUEUE_NAME}。将 "route" 追加到 STEPS_RUN。继续。 |
{ok: false, kind: "unsupported-routing-type", ...} | 用户选择了 flow/user/asa/aea。发出部分成功结果,设置 failedStep: "route",并提供指向 UI 的恢复提示。返回。 |
{ok: false, kind: "metadata-deploy-failed" | "patch-failed" | "verify-failed"} | 发出失败信封,设置 failedStep: "route"。返回。 |
→ 在同一响应中立即继续阶段 3.5。不要停止。
阶段 3.5:配置同意(激活之前)
激活的就绪检查要求同意和路由都已配置——即使渠道已完成路由,如果尚未配置同意,激活仍会失败。调用 service-de-channel-settings-configure:
{CHANNEL_ID}= 上文记录的值{CONSENT_TYPE}、{LANGUAGE}以及关键字/提示词值——传递用户提供的值;未提供的值则省略{ORG_ALIAS}
此阶段会确保同意配置满足激活要求——不一定会写入数据。Connect 插入操作会自动预置 ConsentType=ImplicitOptIn 以及默认的 opt-out MsgChannelLanguageKeyword,这已经满足就绪检查。因此,在新建的 ImplicitOptIn 渠道上,叶节点的阶段 1 通常会发现渠道已经就绪,并返回 noop: true——这是预期结果,不是错误。只有在升级到 ExplicitOptIn/DoubleOptIn,或覆盖预置的关键字/提示词时,才会实际写入。当用户预先提供了升级所需的输入时,应预期会执行写入,并让叶节点提示用户补充缺失的值——不要抑制这些提示。
处理信封:
| 叶节点信封 | 编排器操作 |
|---|---|
{ok: true, ...}(包括 noop: true) | 将 "consent" 追加到 STEPS_RUN(如果为 noop,则追加到 STEPS_SKIPPED)。继续。 |
{ok: false, kind: "not-enhanced"} | 不应发生——这些技能只会创建 Enhanced 渠道。发出失败结果,设置 failedStep: "consent"。返回。 |
{ok: false, kind: "missing-consent-input", hint} | 用户未提供所选 ConsentType 所需的关键字/提示词。发出失败结果,设置 failedStep: "consent",并传递 hint。返回。 |
{ok: false, ...}(任何其他类型——channel-patch-failed、keyword-record-failed、verify-failed,或无法识别的信封) | 发出失败结果,设置 failedStep: "consent",并逐字传递叶节点的 kind 和消息。返回。 |
→ 在同一响应中立即继续阶段 4。不要停止。
阶段 4:激活(第 4 步,共 4 步)
始终运行(除非 CURRENT_STATE === "already-active",这种情况下阶段 1 会提前返回)。
调用 service-de-channel-activate:
{CHANNEL_ID}= 上文记录的值{ORG_ALIAS}
不设置超时参数——激活是一次同步 PATCH。如果服务端返回时仍处于转换中,叶节点会进行防御性轮询;但预期只需一次往返。
处理信封:
| 叶节点信封 | 编排器操作 |
|---|---|
{ok: true, isActive: true, ...}(包括 noop: true) | 记录结果。将 "activate" 追加到 STEPS_RUN。继续执行阶段 5。 |
{ok: false, kind: "no-routing"} | 不寻常(我们刚设置过)——可能是竞争条件或权限问题。输出失败信息,并提示检查权限。返回。 |
{ok: false, kind: "no-mcu" | "channel-missing"} | 成功插入后不应发生。输出失败信息,并附上叶节点信封。返回。 |
{ok: false, kind: "readiness-failed", errorMessage} | validateChannelReadinessOnProvisioning 拒绝了 PATCH——缺少同意记录(STOP/HELP 关键字记录)。阶段 3.5 本应捕获此情况;如果再次出现,请为该频道重新运行 service-de-channel-settings-configure。输出失败信息,并传递恢复提示。返回。 |
{ok: false, kind: "provisioning-error", errorReason, errorDetails} | 第三方拒绝了请求(Meta /register 失败等)。MCU 现在处于 Error 状态。输出失败信息,并传递 errorReason/errorDetails。返回。 |
{ok: false, kind: "auth" | "transport"} | REST 调用在 HTTP 层失败(sf 身份验证返回 401,或实例返回 5xx)。传递错误信息。返回。 |
阶段 5:最终报告
构建成功信封:
{
"ok": true,
"messageType": "{MESSAGE_TYPE}",
"channelId": "{CHANNEL_ID}",
"mcuId": "{MCU_ID}",
"channelName": "{CHANNEL_NAME or inferred}",
"sessionHandlerId": "{SESSION_HANDLER_ID}",
"queueName": "{QUEUE_NAME}",
"isActive": true,
"stepsRun": [...],
"stepsSkipped": [...],
"totalDurationMs": Date.now() - T0
}
向用户呈现:
Success — {MESSAGE_TYPE} channel '{CHANNEL_NAME}' is live on {ORG_ALIAS}.
Channel ID: {CHANNEL_ID}
Routed to: {queueName} ({SESSION_HANDLER_ID})
Steps: ran {STEPS_RUN.join(", ")}{, skipped " + STEPS_SKIPPED.join(", ") if any}
Total time: {formatted from durationMs}
如果因恢复操作跳过了任何步骤,报告应明确说明。
部分成功与恢复报告
如果编排器中途失败,信封始终会包含描述已完成事项的 stateSoFar。stateSoFar 必须始终同时包含 stepsRun 和 stepsSkipped(均为数组;如果尚无已完成或已跳过的步骤,则为空数组),并附上已完成步骤对应的字段——部分成功渲染器会读取这两个字段。渲染失败信息时,请加载 references/partial-success.md 并遵循其中的说明——该文件包含准确的模板,用于展示哪些步骤已完成以及如何重试。
示例演练
参考跟踪示例(全新 WhatsApp 运行、路由失败后恢复、Apple 分发、不支持类型的拒绝),请参阅 references/worked-examples.md。
注意事项
共有九个已知注意事项,涉及先决条件的归属、MessagingPlatformKey 唯一性、恢复语义、仅支持 CSOT、错误分类边界、提示词放置位置、平台密钥映射、挂钟时长,以及双查询预检变通方案。调试意外行为时,请加载 references/gotchas.md 并遵循其中的说明。