diff --git a/wp-tools/index.js b/wp-tools/index.js new file mode 100644 index 0000000..186537b --- /dev/null +++ b/wp-tools/index.js @@ -0,0 +1,323 @@ +/** + * WordPress Tools - OpenClaw 插件 + * 提供 WordPress 发布和 AI 生图工具 + * + * 修复说明: + * 1. register 改为同步方法(解决 "register must be synchronous" 错误) + * 2. wp_publish 新增 images 参数(支持传入图片路径上传到媒体库) + * 3. 新增 wp_publish_with_image 工具(一步完成生图+发布) + */ + +import { spawn } from 'child_process'; + +// WordPress 发布脚本路径 +const WP_PUBLISH_SCRIPT = '/www/wwwroot/wp-publish/scripts/wp_publish_text.py'; +const WP_GENERATE_SCRIPT = '/www/wwwroot/wp-publish/scripts/wp_generate_image.py'; +const PYTHON_PATH = '/usr/bin/python3'; + +/** + * 执行 Python 脚本 + */ +function executePythonScript(scriptPath, args) { + return new Promise((resolve, reject) => { + const process = spawn(PYTHON_PATH, [scriptPath, ...args], { + env: { ...process.env }, + stdio: ['pipe', 'pipe', 'pipe'] + }); + + let stdout = ''; + let stderr = ''; + + process.stdout.on('data', (data) => { + stdout += data.toString(); + }); + + process.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + process.on('close', (code) => { + if (code === 0) { + resolve(stdout); + } else { + reject(new Error(`脚本执行失败: ${stderr}`)); + } + }); + + process.on('error', (error) => { + reject(error); + }); + }); +} + +/** + * 定义插件入口 + */ +export default { + id: 'wp-tools', + name: 'WordPress Tools', + description: 'WordPress 发布和 AI 生图工具', + + // 修复1:去掉 async,改为同步注册 + register(api) { + + // ========== 工具 1:WordPress 发布 ========== + api.registerTool({ + name: 'wp_publish', + description: '发布文章到 WordPress 网站。支持文字内容和图片上传。', + parameters: { + type: 'object', + properties: { + text: { + type: 'string', + description: '文章内容(必填)' + }, + title: { + type: 'string', + description: '文章标题(可选,默认从内容提取)' + }, + category: { + type: 'string', + description: '分类名称(如:ai, ai-kepu, jishu 等)' + }, + tags: { + type: 'string', + description: '标签(可选)' + }, + status: { + type: 'string', + description: '发布状态(publish/draft)', + enum: ['publish', 'draft'] + }, + images: { + type: 'string', + description: '图片文件路径(可选),多个路径用逗号分隔。AI 生图完成后,将返回的图片路径传入此参数即可自动上传到 WordPress 媒体库。' + } + }, + required: ['text'] + }, + async execute(_id, params) { + try { + const args = [ + params.text, + '--title', params.title || '', + '--instruction', params.category ? `#分类 ${params.category}` : '', + '--status', params.status || 'publish' + ]; + + // 修复2:支持传入图片路径 + if (params.images) { + const imagePaths = params.images.split(',').map(p => p.trim()).filter(Boolean); + for (const imgPath of imagePaths) { + args.push('--images', imgPath); + } + } + + const result = await executePythonScript(WP_PUBLISH_SCRIPT, args); + + return { + content: [{ + type: 'text', + text: `✅ 文章发布成功!\n\n${result}` + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: `❌ 发布失败:${error.message}` + }] + }; + } + } + }); + + // ========== 工具 2:AI 生图 ========== + api.registerTool({ + name: 'wp_generate_image', + description: '使用 AI 生成图片。支持通义万相模型,可指定尺寸、格式和数量。生成后的图片可通过 wp_publish 的 images 参数上传到 WordPress 媒体库。', + parameters: { + type: 'object', + properties: { + prompt: { + type: 'string', + description: '图片描述(必填,如:一只可爱的猫咪在草地上晒太阳)' + }, + model: { + type: 'string', + description: '模型名称(默认:wanx-v1)', + enum: ['wanx-v1', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus'] + }, + size: { + type: 'string', + description: '图片尺寸(默认:1024*1024)', + enum: ['1024*1024', '720*1280', '1280*720', '512*512'] + }, + format: { + type: 'string', + description: '图片格式(默认:webp)', + enum: ['webp', 'jpg', 'png'] + }, + count: { + type: 'number', + description: '生成数量(1-4,默认:1)', + minimum: 1, + maximum: 4 + }, + style: { + type: 'string', + description: '图片风格(如:写实、动漫、水彩等)' + } + }, + required: ['prompt'] + }, + async execute(_id, params) { + try { + const args = [ + params.prompt, + '--model', params.model || 'wanx-v1', + '--size', params.size || '1024*1024', + '--format', params.format || 'webp', + '--count', params.count || 1 + ]; + + if (params.style) { + args.push('--style', params.style); + } + + const result = await executePythonScript(WP_GENERATE_SCRIPT, args); + + return { + content: [{ + type: 'text', + text: `✅ AI 生图完成!\n\n${result}` + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: `❌ AI 生图失败:${error.message}` + }] + }; + } + } + }); + + // ========== 工具 3:生图并发布(新增) ========== + api.registerTool({ + name: 'wp_publish_with_image', + description: '一步完成 AI 生图并发布到 WordPress。自动执行:生图 → 上传到媒体库 → 发布文章。', + parameters: { + type: 'object', + properties: { + title: { + type: 'string', + description: '文章标题(必填)' + }, + text: { + type: 'string', + description: '文章内容(必填)' + }, + image_prompt: { + type: 'string', + description: '图片描述(必填,用于 AI 生图)' + }, + image_model: { + type: 'string', + description: '生图模型(默认:wanx-v1)', + enum: ['wanx-v1', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus'] + }, + image_size: { + type: 'string', + description: '图片尺寸(默认:1024*1024)', + enum: ['1024*1024', '720*1280', '1280*720', '512*512'] + }, + category: { + type: 'string', + description: '分类名称' + }, + tags: { + type: 'string', + description: '标签' + }, + status: { + type: 'string', + description: '发布状态(publish/draft)', + enum: ['publish', 'draft'] + } + }, + required: ['title', 'text', 'image_prompt'] + }, + async execute(_id, params) { + try { + // 步骤1:先调用生图 + const genArgs = [ + params.image_prompt, + '--model', params.image_model || 'wanx-v1', + '--size', params.image_size || '1024*1024', + '--format', 'jpg', + '--count', '1' + ]; + + const genResult = await executePythonScript(WP_GENERATE_SCRIPT, genArgs); + + // 从生图结果中提取图片路径 + let imagePath = ''; + try { + // 尝试从输出中提取 JSON 中的 paths + const jsonMatch = genResult.match(/\{[\s\S]*"paths"[\s\S]*\}/); + if (jsonMatch) { + const genJson = JSON.parse(jsonMatch[0]); + if (genJson.paths && genJson.paths.length > 0) { + imagePath = genJson.paths[0]; + } + } + } catch (e) { + // 如果解析失败,尝试从文本中提取路径 + const pathMatch = genResult.match(/\/www\/wwwroot\/wp-publish\/temp\/[\w.]+/); + if (pathMatch) { + imagePath = pathMatch[0]; + } + } + + if (!imagePath) { + return { + content: [{ + type: 'text', + text: `⚠️ 图片生成完成但未找到路径,直接发布文字内容。\n\n生图结果:${genResult}` + }] + }; + } + + // 步骤2:发布文章并上传图片 + const pubArgs = [ + params.text, + '--title', params.title, + '--instruction', params.category ? `#分类 ${params.category}` : '', + '--status', params.status || 'publish', + '--images', imagePath + ]; + + const pubResult = await executePythonScript(WP_PUBLISH_SCRIPT, pubArgs); + + return { + content: [{ + type: 'text', + text: `✅ 生图并发布成功!\n\n生图结果:${genResult}\n\n发布结果:${pubResult}` + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: `❌ 操作失败:${error.message}` + }] + }; + } + } + }); + + console.log('[wp-tools] WordPress 工具已注册'); + } +}; diff --git a/wp-tools/index_extensions.js b/wp-tools/index_extensions.js new file mode 100644 index 0000000..186537b --- /dev/null +++ b/wp-tools/index_extensions.js @@ -0,0 +1,323 @@ +/** + * WordPress Tools - OpenClaw 插件 + * 提供 WordPress 发布和 AI 生图工具 + * + * 修复说明: + * 1. register 改为同步方法(解决 "register must be synchronous" 错误) + * 2. wp_publish 新增 images 参数(支持传入图片路径上传到媒体库) + * 3. 新增 wp_publish_with_image 工具(一步完成生图+发布) + */ + +import { spawn } from 'child_process'; + +// WordPress 发布脚本路径 +const WP_PUBLISH_SCRIPT = '/www/wwwroot/wp-publish/scripts/wp_publish_text.py'; +const WP_GENERATE_SCRIPT = '/www/wwwroot/wp-publish/scripts/wp_generate_image.py'; +const PYTHON_PATH = '/usr/bin/python3'; + +/** + * 执行 Python 脚本 + */ +function executePythonScript(scriptPath, args) { + return new Promise((resolve, reject) => { + const process = spawn(PYTHON_PATH, [scriptPath, ...args], { + env: { ...process.env }, + stdio: ['pipe', 'pipe', 'pipe'] + }); + + let stdout = ''; + let stderr = ''; + + process.stdout.on('data', (data) => { + stdout += data.toString(); + }); + + process.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + process.on('close', (code) => { + if (code === 0) { + resolve(stdout); + } else { + reject(new Error(`脚本执行失败: ${stderr}`)); + } + }); + + process.on('error', (error) => { + reject(error); + }); + }); +} + +/** + * 定义插件入口 + */ +export default { + id: 'wp-tools', + name: 'WordPress Tools', + description: 'WordPress 发布和 AI 生图工具', + + // 修复1:去掉 async,改为同步注册 + register(api) { + + // ========== 工具 1:WordPress 发布 ========== + api.registerTool({ + name: 'wp_publish', + description: '发布文章到 WordPress 网站。支持文字内容和图片上传。', + parameters: { + type: 'object', + properties: { + text: { + type: 'string', + description: '文章内容(必填)' + }, + title: { + type: 'string', + description: '文章标题(可选,默认从内容提取)' + }, + category: { + type: 'string', + description: '分类名称(如:ai, ai-kepu, jishu 等)' + }, + tags: { + type: 'string', + description: '标签(可选)' + }, + status: { + type: 'string', + description: '发布状态(publish/draft)', + enum: ['publish', 'draft'] + }, + images: { + type: 'string', + description: '图片文件路径(可选),多个路径用逗号分隔。AI 生图完成后,将返回的图片路径传入此参数即可自动上传到 WordPress 媒体库。' + } + }, + required: ['text'] + }, + async execute(_id, params) { + try { + const args = [ + params.text, + '--title', params.title || '', + '--instruction', params.category ? `#分类 ${params.category}` : '', + '--status', params.status || 'publish' + ]; + + // 修复2:支持传入图片路径 + if (params.images) { + const imagePaths = params.images.split(',').map(p => p.trim()).filter(Boolean); + for (const imgPath of imagePaths) { + args.push('--images', imgPath); + } + } + + const result = await executePythonScript(WP_PUBLISH_SCRIPT, args); + + return { + content: [{ + type: 'text', + text: `✅ 文章发布成功!\n\n${result}` + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: `❌ 发布失败:${error.message}` + }] + }; + } + } + }); + + // ========== 工具 2:AI 生图 ========== + api.registerTool({ + name: 'wp_generate_image', + description: '使用 AI 生成图片。支持通义万相模型,可指定尺寸、格式和数量。生成后的图片可通过 wp_publish 的 images 参数上传到 WordPress 媒体库。', + parameters: { + type: 'object', + properties: { + prompt: { + type: 'string', + description: '图片描述(必填,如:一只可爱的猫咪在草地上晒太阳)' + }, + model: { + type: 'string', + description: '模型名称(默认:wanx-v1)', + enum: ['wanx-v1', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus'] + }, + size: { + type: 'string', + description: '图片尺寸(默认:1024*1024)', + enum: ['1024*1024', '720*1280', '1280*720', '512*512'] + }, + format: { + type: 'string', + description: '图片格式(默认:webp)', + enum: ['webp', 'jpg', 'png'] + }, + count: { + type: 'number', + description: '生成数量(1-4,默认:1)', + minimum: 1, + maximum: 4 + }, + style: { + type: 'string', + description: '图片风格(如:写实、动漫、水彩等)' + } + }, + required: ['prompt'] + }, + async execute(_id, params) { + try { + const args = [ + params.prompt, + '--model', params.model || 'wanx-v1', + '--size', params.size || '1024*1024', + '--format', params.format || 'webp', + '--count', params.count || 1 + ]; + + if (params.style) { + args.push('--style', params.style); + } + + const result = await executePythonScript(WP_GENERATE_SCRIPT, args); + + return { + content: [{ + type: 'text', + text: `✅ AI 生图完成!\n\n${result}` + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: `❌ AI 生图失败:${error.message}` + }] + }; + } + } + }); + + // ========== 工具 3:生图并发布(新增) ========== + api.registerTool({ + name: 'wp_publish_with_image', + description: '一步完成 AI 生图并发布到 WordPress。自动执行:生图 → 上传到媒体库 → 发布文章。', + parameters: { + type: 'object', + properties: { + title: { + type: 'string', + description: '文章标题(必填)' + }, + text: { + type: 'string', + description: '文章内容(必填)' + }, + image_prompt: { + type: 'string', + description: '图片描述(必填,用于 AI 生图)' + }, + image_model: { + type: 'string', + description: '生图模型(默认:wanx-v1)', + enum: ['wanx-v1', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus'] + }, + image_size: { + type: 'string', + description: '图片尺寸(默认:1024*1024)', + enum: ['1024*1024', '720*1280', '1280*720', '512*512'] + }, + category: { + type: 'string', + description: '分类名称' + }, + tags: { + type: 'string', + description: '标签' + }, + status: { + type: 'string', + description: '发布状态(publish/draft)', + enum: ['publish', 'draft'] + } + }, + required: ['title', 'text', 'image_prompt'] + }, + async execute(_id, params) { + try { + // 步骤1:先调用生图 + const genArgs = [ + params.image_prompt, + '--model', params.image_model || 'wanx-v1', + '--size', params.image_size || '1024*1024', + '--format', 'jpg', + '--count', '1' + ]; + + const genResult = await executePythonScript(WP_GENERATE_SCRIPT, genArgs); + + // 从生图结果中提取图片路径 + let imagePath = ''; + try { + // 尝试从输出中提取 JSON 中的 paths + const jsonMatch = genResult.match(/\{[\s\S]*"paths"[\s\S]*\}/); + if (jsonMatch) { + const genJson = JSON.parse(jsonMatch[0]); + if (genJson.paths && genJson.paths.length > 0) { + imagePath = genJson.paths[0]; + } + } + } catch (e) { + // 如果解析失败,尝试从文本中提取路径 + const pathMatch = genResult.match(/\/www\/wwwroot\/wp-publish\/temp\/[\w.]+/); + if (pathMatch) { + imagePath = pathMatch[0]; + } + } + + if (!imagePath) { + return { + content: [{ + type: 'text', + text: `⚠️ 图片生成完成但未找到路径,直接发布文字内容。\n\n生图结果:${genResult}` + }] + }; + } + + // 步骤2:发布文章并上传图片 + const pubArgs = [ + params.text, + '--title', params.title, + '--instruction', params.category ? `#分类 ${params.category}` : '', + '--status', params.status || 'publish', + '--images', imagePath + ]; + + const pubResult = await executePythonScript(WP_PUBLISH_SCRIPT, pubArgs); + + return { + content: [{ + type: 'text', + text: `✅ 生图并发布成功!\n\n生图结果:${genResult}\n\n发布结果:${pubResult}` + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: `❌ 操作失败:${error.message}` + }] + }; + } + } + }); + + console.log('[wp-tools] WordPress 工具已注册'); + } +}; diff --git a/wp-tools/openclaw.plugin.json b/wp-tools/openclaw.plugin.json new file mode 100644 index 0000000..95ca62f --- /dev/null +++ b/wp-tools/openclaw.plugin.json @@ -0,0 +1,15 @@ +{ + "id": "wp-tools", + "name": "WordPress Tools", + "description": "WordPress 发布和 AI 生图工具", + "contracts": { + "tools": ["wp_publish", "wp_generate_image", "wp_publish_with_image"] + }, + "activation": { + "onStartup": true + }, + "configSchema": { + "type": "object", + "additionalProperties": false + } +}