mirror of https://github.com/openclaw/openclaw.git
Discord: support direct plugin conversation binds
This commit is contained in:
parent
9d55374088
commit
38394ab3a8
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from "./thread-bindings.types.js";
|
||||
|
||||
function buildThreadTarget(threadId: string): string {
|
||||
return `channel:${threadId}`;
|
||||
return /^(channel:|user:)/i.test(threadId) ? threadId : `channel:${threadId}`;
|
||||
}
|
||||
|
||||
export function isThreadArchived(raw: unknown): boolean {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
setRuntimeConfigSnapshot,
|
||||
type OpenClawConfig,
|
||||
} from "../../../../src/config/config.js";
|
||||
import { getSessionBindingService } from "../../../../src/infra/outbound/session-binding-service.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
const sendMessageDiscord = vi.fn(async (_to: string, _text: string, _opts?: unknown) => ({}));
|
||||
|
|
@ -788,6 +789,57 @@ describe("thread binding lifecycle", () => {
|
|||
expect(usedTokenNew).toBe(true);
|
||||
});
|
||||
|
||||
it("binds current Discord DMs as direct conversation bindings", async () => {
|
||||
createThreadBindingManager({
|
||||
accountId: "default",
|
||||
persist: false,
|
||||
enableSweeper: false,
|
||||
idleTimeoutMs: 24 * 60 * 60 * 1000,
|
||||
maxAgeMs: 0,
|
||||
});
|
||||
|
||||
hoisted.restGet.mockClear();
|
||||
hoisted.restPost.mockClear();
|
||||
|
||||
const bound = await getSessionBindingService().bind({
|
||||
targetSessionKey: "plugin-binding:openclaw-codex-app-server:dm",
|
||||
targetKind: "session",
|
||||
conversation: {
|
||||
channel: "discord",
|
||||
accountId: "default",
|
||||
conversationId: "user:1177378744822943744",
|
||||
},
|
||||
placement: "current",
|
||||
metadata: {
|
||||
pluginBindingOwner: "plugin",
|
||||
pluginId: "openclaw-codex-app-server",
|
||||
pluginRoot: "/Users/huntharo/github/openclaw-app-server",
|
||||
},
|
||||
});
|
||||
|
||||
expect(bound).toMatchObject({
|
||||
conversation: {
|
||||
channel: "discord",
|
||||
accountId: "default",
|
||||
conversationId: "user:1177378744822943744",
|
||||
parentConversationId: "user:1177378744822943744",
|
||||
},
|
||||
});
|
||||
expect(
|
||||
getSessionBindingService().resolveByConversation({
|
||||
channel: "discord",
|
||||
accountId: "default",
|
||||
conversationId: "user:1177378744822943744",
|
||||
}),
|
||||
).toMatchObject({
|
||||
conversation: {
|
||||
conversationId: "user:1177378744822943744",
|
||||
},
|
||||
});
|
||||
expect(hoisted.restGet).not.toHaveBeenCalled();
|
||||
expect(hoisted.restPost).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps overlapping thread ids isolated per account", async () => {
|
||||
const a = createThreadBindingManager({
|
||||
accountId: "a",
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ function toThreadBindingTargetKind(raw: BindingTargetKind): "subagent" | "acp" {
|
|||
return raw === "subagent" ? "subagent" : "acp";
|
||||
}
|
||||
|
||||
function isDirectConversationBindingId(value?: string | null): boolean {
|
||||
const trimmed = value?.trim();
|
||||
return Boolean(trimmed && /^(user:|channel:)/i.test(trimmed));
|
||||
}
|
||||
|
||||
function toSessionBindingRecord(
|
||||
record: ThreadBindingRecord,
|
||||
defaults: { idleTimeoutMs: number; maxAgeMs: number },
|
||||
|
|
@ -265,6 +270,8 @@ export function createThreadBindingManager(
|
|||
const cfg = resolveCurrentCfg();
|
||||
let threadId = normalizeThreadId(bindParams.threadId);
|
||||
let channelId = bindParams.channelId?.trim() || "";
|
||||
const directConversationBinding =
|
||||
isDirectConversationBindingId(threadId) || isDirectConversationBindingId(channelId);
|
||||
|
||||
if (!threadId && bindParams.createThread) {
|
||||
if (!channelId) {
|
||||
|
|
@ -288,6 +295,10 @@ export function createThreadBindingManager(
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!channelId && directConversationBinding) {
|
||||
channelId = threadId;
|
||||
}
|
||||
|
||||
if (!channelId) {
|
||||
channelId =
|
||||
(await resolveChannelIdForBinding({
|
||||
|
|
@ -310,12 +321,12 @@ export function createThreadBindingManager(
|
|||
const targetKind = normalizeTargetKind(bindParams.targetKind, targetSessionKey);
|
||||
let webhookId = bindParams.webhookId?.trim() || "";
|
||||
let webhookToken = bindParams.webhookToken?.trim() || "";
|
||||
if (!webhookId || !webhookToken) {
|
||||
if (!directConversationBinding && (!webhookId || !webhookToken)) {
|
||||
const cachedWebhook = findReusableWebhook({ accountId, channelId });
|
||||
webhookId = cachedWebhook.webhookId ?? "";
|
||||
webhookToken = cachedWebhook.webhookToken ?? "";
|
||||
}
|
||||
if (!webhookId || !webhookToken) {
|
||||
if (!directConversationBinding && (!webhookId || !webhookToken)) {
|
||||
const createdWebhook = await createWebhookForChannel({
|
||||
cfg,
|
||||
accountId,
|
||||
|
|
@ -513,6 +524,9 @@ export function createThreadBindingManager(
|
|||
});
|
||||
continue;
|
||||
}
|
||||
if (isDirectConversationBindingId(binding.threadId)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const channel = await rest.get(Routes.channel(binding.threadId));
|
||||
if (!channel || typeof channel !== "object") {
|
||||
|
|
|
|||
Loading…
Reference in New Issue