mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
feat(heartbeat): default delivery to the configured owner, never groups (#121988)
Unset heartbeat.target now resolves "owner": elected heartbeat notifications deliver to the operator's DM resolved from commands.ownerAllowFrom or the channel allowFrom (first concrete entry; wildcards and channel-scoped wildcards excluded; configured owners exhausted across channels before any channel-local fallback). Delivery requires the channel's own classifier to positively prove a direct destination — every bundled messaging plugin now ships an inferTargetChatType contract — and unproven or group-shaped destinations fail closed to the visible no-route state. The first implicitly-routed delivery carries a one-line self-explanation naming the target: "none" opt-out. Explicit target "last" remains as the follow-the-conversation opt-in. Refines the unreleased #121892 default before it ships; refs #121880. Co-authored-by: Ayaan Zaidi <hi@obviy.us> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,13 @@ import type { OpenClawConfig } from "../runtime-api.js";
|
||||
import { feishuPlugin } from "./channel.js";
|
||||
import { looksLikeFeishuId, normalizeFeishuTarget, resolveReceiveIdType } from "./targets.js";
|
||||
|
||||
describe("feishu target classification", () => {
|
||||
it("distinguishes users from chats", () => {
|
||||
expect(feishuPlugin.messaging?.inferTargetChatType?.({ to: "ou_owner" })).toBe("direct");
|
||||
expect(feishuPlugin.messaging?.inferTargetChatType?.({ to: "oc_group" })).toBe("group");
|
||||
});
|
||||
});
|
||||
|
||||
const probeFeishuMock = vi.hoisted(() => vi.fn());
|
||||
const createFeishuClientMock = vi.hoisted(() => vi.fn());
|
||||
const addReactionFeishuMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
@@ -110,7 +110,7 @@ import { resolveFeishuSessionConversation } from "./session-conversation.js";
|
||||
import { resolveFeishuOutboundSessionRoute } from "./session-route.js";
|
||||
import { feishuSetupContract } from "./setup-core.js";
|
||||
import { feishuSetupWizard, runFeishuLogin } from "./setup-surface.js";
|
||||
import { looksLikeFeishuId, normalizeFeishuTarget } from "./targets.js";
|
||||
import { looksLikeFeishuId, normalizeFeishuTarget, resolveReceiveIdType } from "./targets.js";
|
||||
import type { FeishuConfig, FeishuProbeResult, ResolvedFeishuAccount } from "./types.js";
|
||||
|
||||
function readFeishuMediaParam(params: Record<string, unknown>): string | undefined {
|
||||
@@ -1693,6 +1693,8 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount, FeishuProbeResul
|
||||
messaging: {
|
||||
targetPrefixes: ["feishu", "lark"],
|
||||
normalizeTarget: (raw) => normalizeFeishuTarget(raw) ?? undefined,
|
||||
inferTargetChatType: ({ to }) =>
|
||||
resolveReceiveIdType(to) === "chat_id" ? "group" : "direct",
|
||||
resolveDeliveryTarget: ({ conversationId, parentConversationId }) => {
|
||||
const directId = parseFeishuDirectConversationId(conversationId);
|
||||
if (directId) {
|
||||
|
||||
Reference in New Issue
Block a user