mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-19 22:10:51 +00:00
Plugins: move message tool schemas into channel plugins
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
createDiscordMessageToolComponentsSchema,
|
||||
createUnionActionGate,
|
||||
listTokenSourcedAccounts,
|
||||
} from "openclaw/plugin-sdk/channel-runtime";
|
||||
@@ -11,6 +10,7 @@ import type {
|
||||
import type { DiscordActionConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { createDiscordActionGate, listEnabledDiscordAccounts } from "./accounts.js";
|
||||
import { handleDiscordMessageAction } from "./actions/handle-action.js";
|
||||
import { createDiscordMessageToolComponentsSchema } from "./message-tool-schema.js";
|
||||
|
||||
function resolveDiscordActionDiscovery(cfg: Parameters<typeof listEnabledDiscordAccounts>[0]) {
|
||||
const accounts = listTokenSourcedAccounts(listEnabledDiscordAccounts(cfg));
|
||||
|
||||
114
extensions/discord/src/message-tool-schema.ts
Normal file
114
extensions/discord/src/message-tool-schema.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { stringEnum } from "openclaw/plugin-sdk/core";
|
||||
|
||||
const discordComponentEmojiSchema = Type.Object({
|
||||
name: Type.String(),
|
||||
id: Type.Optional(Type.String()),
|
||||
animated: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
const discordComponentOptionSchema = Type.Object({
|
||||
label: Type.String(),
|
||||
value: Type.String(),
|
||||
description: Type.Optional(Type.String()),
|
||||
emoji: Type.Optional(discordComponentEmojiSchema),
|
||||
default: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
const discordComponentButtonSchema = Type.Object({
|
||||
label: Type.String(),
|
||||
style: Type.Optional(stringEnum(["primary", "secondary", "success", "danger", "link"])),
|
||||
url: Type.Optional(Type.String()),
|
||||
emoji: Type.Optional(discordComponentEmojiSchema),
|
||||
disabled: Type.Optional(Type.Boolean()),
|
||||
allowedUsers: Type.Optional(
|
||||
Type.Array(
|
||||
Type.String({
|
||||
description: "Discord user ids or names allowed to interact with this button.",
|
||||
}),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
const discordComponentSelectSchema = Type.Object({
|
||||
type: Type.Optional(stringEnum(["string", "user", "role", "mentionable", "channel"])),
|
||||
placeholder: Type.Optional(Type.String()),
|
||||
minValues: Type.Optional(Type.Number()),
|
||||
maxValues: Type.Optional(Type.Number()),
|
||||
options: Type.Optional(Type.Array(discordComponentOptionSchema)),
|
||||
});
|
||||
|
||||
const discordComponentBlockSchema = Type.Object({
|
||||
type: Type.String(),
|
||||
text: Type.Optional(Type.String()),
|
||||
texts: Type.Optional(Type.Array(Type.String())),
|
||||
accessory: Type.Optional(
|
||||
Type.Object({
|
||||
type: Type.String(),
|
||||
url: Type.Optional(Type.String()),
|
||||
button: Type.Optional(discordComponentButtonSchema),
|
||||
}),
|
||||
),
|
||||
spacing: Type.Optional(stringEnum(["small", "large"])),
|
||||
divider: Type.Optional(Type.Boolean()),
|
||||
buttons: Type.Optional(Type.Array(discordComponentButtonSchema)),
|
||||
select: Type.Optional(discordComponentSelectSchema),
|
||||
items: Type.Optional(
|
||||
Type.Array(
|
||||
Type.Object({
|
||||
url: Type.String(),
|
||||
description: Type.Optional(Type.String()),
|
||||
spoiler: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
),
|
||||
),
|
||||
file: Type.Optional(Type.String()),
|
||||
spoiler: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
const discordComponentModalFieldSchema = Type.Object({
|
||||
type: Type.String(),
|
||||
name: Type.Optional(Type.String()),
|
||||
label: Type.String(),
|
||||
description: Type.Optional(Type.String()),
|
||||
placeholder: Type.Optional(Type.String()),
|
||||
required: Type.Optional(Type.Boolean()),
|
||||
options: Type.Optional(Type.Array(discordComponentOptionSchema)),
|
||||
minValues: Type.Optional(Type.Number()),
|
||||
maxValues: Type.Optional(Type.Number()),
|
||||
minLength: Type.Optional(Type.Number()),
|
||||
maxLength: Type.Optional(Type.Number()),
|
||||
style: Type.Optional(stringEnum(["short", "paragraph"])),
|
||||
});
|
||||
|
||||
const discordComponentModalSchema = Type.Object({
|
||||
title: Type.String(),
|
||||
triggerLabel: Type.Optional(Type.String()),
|
||||
triggerStyle: Type.Optional(stringEnum(["primary", "secondary", "success", "danger", "link"])),
|
||||
fields: Type.Array(discordComponentModalFieldSchema),
|
||||
});
|
||||
|
||||
export function createDiscordMessageToolComponentsSchema() {
|
||||
return Type.Object(
|
||||
{
|
||||
text: Type.Optional(Type.String()),
|
||||
reusable: Type.Optional(
|
||||
Type.Boolean({
|
||||
description: "Allow components to be used multiple times until they expire.",
|
||||
}),
|
||||
),
|
||||
container: Type.Optional(
|
||||
Type.Object({
|
||||
accentColor: Type.Optional(Type.String()),
|
||||
spoiler: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
),
|
||||
blocks: Type.Optional(Type.Array(discordComponentBlockSchema)),
|
||||
modal: Type.Optional(discordComponentModalSchema),
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Discord components v2 payload. Set reusable=true to keep buttons, selects, and forms active until expiry.",
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import {
|
||||
createSlackMessageToolBlocksSchema,
|
||||
type ChannelMessageActionAdapter,
|
||||
type ChannelMessageToolDiscovery,
|
||||
} from "openclaw/plugin-sdk/channel-runtime";
|
||||
@@ -8,6 +7,7 @@ import type { SlackActionContext } from "./action-runtime.js";
|
||||
import { handleSlackAction } from "./action-runtime.js";
|
||||
import { handleSlackMessageAction } from "./message-action-dispatch.js";
|
||||
import { extractSlackToolSend, listSlackMessageActions } from "./message-actions.js";
|
||||
import { createSlackMessageToolBlocksSchema } from "./message-tool-schema.js";
|
||||
import { isSlackInteractiveRepliesEnabled } from "./runtime-api.js";
|
||||
import { resolveSlackChannelId } from "./targets.js";
|
||||
|
||||
|
||||
13
extensions/slack/src/message-tool-schema.ts
Normal file
13
extensions/slack/src/message-tool-schema.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
||||
export function createSlackMessageToolBlocksSchema() {
|
||||
return Type.Array(
|
||||
Type.Object(
|
||||
{},
|
||||
{
|
||||
additionalProperties: true,
|
||||
description: "Slack Block Kit payload blocks (Slack only).",
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
createMessageToolButtonsSchema,
|
||||
createTelegramPollExtraToolSchemas,
|
||||
createUnionActionGate,
|
||||
listTokenSourcedAccounts,
|
||||
resolveReactionMessageId,
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
} from "./accounts.js";
|
||||
import { handleTelegramAction } from "./action-runtime.js";
|
||||
import { isTelegramInlineButtonsEnabled } from "./inline-buttons.js";
|
||||
import { createTelegramPollExtraToolSchemas } from "./message-tool-schema.js";
|
||||
|
||||
export const telegramMessageActionRuntime = {
|
||||
handleTelegramAction,
|
||||
|
||||
9
extensions/telegram/src/message-tool-schema.ts
Normal file
9
extensions/telegram/src/message-tool-schema.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
||||
export function createTelegramPollExtraToolSchemas() {
|
||||
return {
|
||||
pollDurationSeconds: Type.Optional(Type.Number()),
|
||||
pollAnonymous: Type.Optional(Type.Boolean()),
|
||||
pollPublic: Type.Optional(Type.Boolean()),
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ChannelMessageCapability } from "../../channels/plugins/message-capabilities.js";
|
||||
import {
|
||||
createDiscordMessageToolComponentsSchema,
|
||||
createMessageToolButtonsSchema,
|
||||
createSlackMessageToolBlocksSchema,
|
||||
createTelegramPollExtraToolSchemas,
|
||||
} from "../../channels/plugins/message-tool-schema.js";
|
||||
import { createMessageToolButtonsSchema } from "../../channels/plugins/message-tool-schema.js";
|
||||
import type { ChannelMessageActionName, ChannelPlugin } from "../../channels/plugins/types.js";
|
||||
import type { MessageActionRunResult } from "../../infra/outbound/message-action-runner.js";
|
||||
type CreateMessageTool = typeof import("./message-tool.js").createMessageTool;
|
||||
@@ -22,6 +18,22 @@ type DescribeMessageTool = NonNullable<
|
||||
type MessageToolDiscoveryContext = Parameters<DescribeMessageTool>[0];
|
||||
type MessageToolSchema = NonNullable<ReturnType<DescribeMessageTool>>["schema"];
|
||||
|
||||
function createDiscordMessageToolComponentsSchema() {
|
||||
return Type.Object({ type: Type.Literal("discord-components") });
|
||||
}
|
||||
|
||||
function createSlackMessageToolBlocksSchema() {
|
||||
return Type.Array(Type.Object({}, { additionalProperties: true }));
|
||||
}
|
||||
|
||||
function createTelegramPollExtraToolSchemas() {
|
||||
return {
|
||||
pollDurationSeconds: Type.Optional(Type.Number()),
|
||||
pollAnonymous: Type.Optional(Type.Boolean()),
|
||||
pollPublic: Type.Optional(Type.Boolean()),
|
||||
};
|
||||
}
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
runMessageAction: vi.fn(),
|
||||
loadConfig: vi.fn(() => ({})),
|
||||
|
||||
@@ -2,93 +2,6 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import { stringEnum } from "../../agents/schema/typebox.js";
|
||||
|
||||
const discordComponentEmojiSchema = Type.Object({
|
||||
name: Type.String(),
|
||||
id: Type.Optional(Type.String()),
|
||||
animated: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
const discordComponentOptionSchema = Type.Object({
|
||||
label: Type.String(),
|
||||
value: Type.String(),
|
||||
description: Type.Optional(Type.String()),
|
||||
emoji: Type.Optional(discordComponentEmojiSchema),
|
||||
default: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
const discordComponentButtonSchema = Type.Object({
|
||||
label: Type.String(),
|
||||
style: Type.Optional(stringEnum(["primary", "secondary", "success", "danger", "link"])),
|
||||
url: Type.Optional(Type.String()),
|
||||
emoji: Type.Optional(discordComponentEmojiSchema),
|
||||
disabled: Type.Optional(Type.Boolean()),
|
||||
allowedUsers: Type.Optional(
|
||||
Type.Array(
|
||||
Type.String({
|
||||
description: "Discord user ids or names allowed to interact with this button.",
|
||||
}),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
const discordComponentSelectSchema = Type.Object({
|
||||
type: Type.Optional(stringEnum(["string", "user", "role", "mentionable", "channel"])),
|
||||
placeholder: Type.Optional(Type.String()),
|
||||
minValues: Type.Optional(Type.Number()),
|
||||
maxValues: Type.Optional(Type.Number()),
|
||||
options: Type.Optional(Type.Array(discordComponentOptionSchema)),
|
||||
});
|
||||
|
||||
const discordComponentBlockSchema = Type.Object({
|
||||
type: Type.String(),
|
||||
text: Type.Optional(Type.String()),
|
||||
texts: Type.Optional(Type.Array(Type.String())),
|
||||
accessory: Type.Optional(
|
||||
Type.Object({
|
||||
type: Type.String(),
|
||||
url: Type.Optional(Type.String()),
|
||||
button: Type.Optional(discordComponentButtonSchema),
|
||||
}),
|
||||
),
|
||||
spacing: Type.Optional(stringEnum(["small", "large"])),
|
||||
divider: Type.Optional(Type.Boolean()),
|
||||
buttons: Type.Optional(Type.Array(discordComponentButtonSchema)),
|
||||
select: Type.Optional(discordComponentSelectSchema),
|
||||
items: Type.Optional(
|
||||
Type.Array(
|
||||
Type.Object({
|
||||
url: Type.String(),
|
||||
description: Type.Optional(Type.String()),
|
||||
spoiler: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
),
|
||||
),
|
||||
file: Type.Optional(Type.String()),
|
||||
spoiler: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
const discordComponentModalFieldSchema = Type.Object({
|
||||
type: Type.String(),
|
||||
name: Type.Optional(Type.String()),
|
||||
label: Type.String(),
|
||||
description: Type.Optional(Type.String()),
|
||||
placeholder: Type.Optional(Type.String()),
|
||||
required: Type.Optional(Type.Boolean()),
|
||||
options: Type.Optional(Type.Array(discordComponentOptionSchema)),
|
||||
minValues: Type.Optional(Type.Number()),
|
||||
maxValues: Type.Optional(Type.Number()),
|
||||
minLength: Type.Optional(Type.Number()),
|
||||
maxLength: Type.Optional(Type.Number()),
|
||||
style: Type.Optional(stringEnum(["short", "paragraph"])),
|
||||
});
|
||||
|
||||
const discordComponentModalSchema = Type.Object({
|
||||
title: Type.String(),
|
||||
triggerLabel: Type.Optional(Type.String()),
|
||||
triggerStyle: Type.Optional(stringEnum(["primary", "secondary", "success", "danger", "link"])),
|
||||
fields: Type.Array(discordComponentModalFieldSchema),
|
||||
});
|
||||
|
||||
export function createMessageToolButtonsSchema(): TSchema {
|
||||
return Type.Array(
|
||||
Type.Array(
|
||||
@@ -113,48 +26,3 @@ export function createMessageToolCardSchema(): TSchema {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function createDiscordMessageToolComponentsSchema(): TSchema {
|
||||
return Type.Object(
|
||||
{
|
||||
text: Type.Optional(Type.String()),
|
||||
reusable: Type.Optional(
|
||||
Type.Boolean({
|
||||
description: "Allow components to be used multiple times until they expire.",
|
||||
}),
|
||||
),
|
||||
container: Type.Optional(
|
||||
Type.Object({
|
||||
accentColor: Type.Optional(Type.String()),
|
||||
spoiler: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
),
|
||||
blocks: Type.Optional(Type.Array(discordComponentBlockSchema)),
|
||||
modal: Type.Optional(discordComponentModalSchema),
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Discord components v2 payload. Set reusable=true to keep buttons, selects, and forms active until expiry.",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function createSlackMessageToolBlocksSchema(): TSchema {
|
||||
return Type.Array(
|
||||
Type.Object(
|
||||
{},
|
||||
{
|
||||
additionalProperties: true,
|
||||
description: "Slack Block Kit payload blocks (Slack only).",
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function createTelegramPollExtraToolSchemas(): Record<string, TSchema> {
|
||||
return {
|
||||
pollDurationSeconds: Type.Optional(Type.Number()),
|
||||
pollAnonymous: Type.Optional(Type.Boolean()),
|
||||
pollPublic: Type.Optional(Type.Boolean()),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user