import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk/bluebubbles"; import { z } from "zod"; import { buildSecretInputSchema, hasConfiguredSecretInput } from "./secret-input.js"; const allowFromEntry = z.union([z.string(), z.number()]); const bluebubblesActionSchema = z .object({ reactions: z.boolean().default(true), edit: z.boolean().default(true), unsend: z.boolean().default(true), reply: z.boolean().default(true), sendWithEffect: z.boolean().default(true), renameGroup: z.boolean().default(true), setGroupIcon: z.boolean().default(true), addParticipant: z.boolean().default(true), removeParticipant: z.boolean().default(true), leaveGroup: z.boolean().default(true), sendAttachment: z.boolean().default(true), }) .optional(); const bluebubblesGroupConfigSchema = z.object({ requireMention: z.boolean().optional(), tools: ToolPolicySchema, }); const bluebubblesAccountSchema = z .object({ name: z.string().optional(), enabled: z.boolean().optional(), markdown: MarkdownConfigSchema, serverUrl: z.string().optional(), password: buildSecretInputSchema().optional(), webhookPath: z.string().optional(), dmPolicy: z.enum(["pairing", "allowlist", "open", "disabled"]).optional(), allowFrom: z.array(allowFromEntry).optional(), groupAllowFrom: z.array(allowFromEntry).optional(), groupPolicy: z.enum(["open", "disabled", "allowlist"]).optional(), historyLimit: z.number().int().min(0).optional(), dmHistoryLimit: z.number().int().min(0).optional(), textChunkLimit: z.number().int().positive().optional(), chunkMode: z.enum(["length", "newline"]).optional(), mediaMaxMb: z.number().int().positive().optional(), mediaLocalRoots: z.array(z.string()).optional(), sendReadReceipts: z.boolean().optional(), allowPrivateNetwork: z.boolean().optional(), blockStreaming: z.boolean().optional(), groups: z.object({}).catchall(bluebubblesGroupConfigSchema).optional(), }) .superRefine((value, ctx) => { const serverUrl = value.serverUrl?.trim() ?? ""; const passwordConfigured = hasConfiguredSecretInput(value.password); if (serverUrl && !passwordConfigured) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["password"], message: "password is required when serverUrl is configured", }); } }); export const BlueBubblesConfigSchema = bluebubblesAccountSchema.extend({ accounts: z.object({}).catchall(bluebubblesAccountSchema).optional(), defaultAccount: z.string().optional(), actions: bluebubblesActionSchema, });