Files
feishu-agent-binding/index.js
openclaw 21ba680afc rename: feishu-bot-manager → feishu-agent-binding
- 更新 SKILL.md 中的技能名称和命令示例
- 更新 index.js 中的技能名称注释
- 更新 package.json 中的 name 字段
- 添加 name 和 description frontmatter 字段
2026-03-17 14:19:38 +08:00

253 lines
7.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* feishu-agent-binding
* 飞书多账户机器人配置管理 Skill
*
* 支持的路由绑定方案:
* 1. 账户级绑定 - 该飞书账户的所有消息路由到指定 Agent
* 2. 群聊级绑定 - 特定群聊的消息路由到指定 Agent
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// 配置路径
const CONFIG_PATH = path.join(process.env.HOME, '.openclaw', 'openclaw.json');
const BACKUP_DIR = path.join(process.env.HOME, '.openclaw', 'backups');
// 颜色输出
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
cyan: '\x1b[36m',
gray: '\x1b[90m',
bold: '\x1b[1m'
};
const log = {
info: (msg) => console.log(`${colors.cyan}${colors.reset} ${msg}`),
success: (msg) => console.log(`${colors.green}${colors.reset} ${msg}`),
warning: (msg) => console.log(`${colors.yellow}${colors.reset} ${msg}`),
error: (msg) => console.log(`${colors.red}${colors.reset} ${msg}`),
step: (num, total, msg) => console.log(`\n${colors.cyan}[${num}/${total}]${colors.reset} ${msg}`),
preview: (msg) => console.log(`${colors.gray}${msg}${colors.reset}`),
bold: (msg) => console.log(`${colors.bold}${msg}${colors.reset}`)
};
// 读取配置
function loadConfig() {
try {
const content = fs.readFileSync(CONFIG_PATH, 'utf8');
return JSON.parse(content);
} catch (err) {
log.error(`读取配置失败: ${err.message}`);
process.exit(1);
}
}
// 保存配置
function saveConfig(config) {
try {
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
return true;
} catch (err) {
log.error(`保存配置失败: ${err.message}`);
return false;
}
}
// 创建备份
function createBackup() {
try {
if (!fs.existsSync(BACKUP_DIR)) {
fs.mkdirSync(BACKUP_DIR, { recursive: true });
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupPath = path.join(BACKUP_DIR, `openclaw.json.${timestamp}`);
fs.copyFileSync(CONFIG_PATH, backupPath);
return backupPath;
} catch (err) {
log.error(`创建备份失败: ${err.message}`);
return null;
}
}
// 显示路由方案说明
function showRoutingOptions() {
console.log(`
${colors.bold}📋 路由绑定方案${colors.reset}
${colors.bold}方案 1账户级绑定${colors.reset}
该飞书账户的所有消息 → 指定 Agent
适用:一个机器人专门服务一个 Agent
${colors.bold}方案 2群聊级绑定${colors.reset}
特定群聊的消息 → 指定 Agent
适用:把 Agent 绑定到特定群聊
${colors.yellow}提示:群聊级绑定优先级更高,会覆盖账户级绑定!${colors.reset}
`);
}
// 快速模式
async function quickMode(options) {
console.log(`\n${colors.cyan}🤖 飞书机器人配置助手${colors.reset}\n`);
const {
appid,
appsecret,
accountid,
botname,
agentid,
chatid,
routingmode
} = options;
if (!appid || !appsecret) {
log.error('需要提供 --app-id 和 --app-secret');
process.exit(1);
}
const config = loadConfig();
const accountId = accountid || `bot-${Date.now()}`;
// 创建备份
const backupPath = createBackup();
log.success(`配置已备份: ${path.basename(backupPath)}`);
// 确保 channels.feishu.accounts 存在
if (!config.channels) config.channels = {};
if (!config.channels.feishu) config.channels.feishu = { enabled: true };
if (!config.channels.feishu.accounts) config.channels.feishu.accounts = {};
// 添加账户
config.channels.feishu.accounts[accountId] = {
appId: appid,
appSecret: appsecret,
botName: botname || 'Feishu Bot',
dmPolicy: options.dmpolicy || 'open',
allowFrom: ['*'],
enabled: true
};
// 添加绑定
if (!config.bindings) config.bindings = [];
const mode = routingmode || 'account';
if (mode === 'account' && agentid) {
// 账户级绑定:该账户所有消息路由到指定 Agent
config.bindings.push({
agentId: agentid,
match: {
channel: 'feishu',
accountId: accountId
}
});
log.success(`已添加账户级绑定: ${agentid}${accountId}`);
} else if (mode === 'group' && agentid && chatid) {
// 群聊级绑定:特定群聊消息路由到指定 Agent
config.bindings.push({
agentId: agentid,
match: {
channel: 'feishu',
peer: { kind: 'group', id: chatid }
}
});
log.success(`已添加群聊级绑定: ${agentid}${chatid}`);
}
saveConfig(config);
log.success('配置已更新');
// 设置会话绑定颗粒度
log.info('设置会话绑定颗粒度...');
try {
execSync('openclaw config set session.dmScope "per-account-channel-peer"', { stdio: 'pipe' });
log.success('会话绑定颗粒度已设置');
} catch (err) {
log.warning('设置 dmScope 失败,请手动执行:');
console.log(' openclaw config set session.dmScope "per-account-channel-peer"');
}
// 重启
log.warning('正在重启 Gateway...');
try {
execSync('openclaw gateway restart', { stdio: 'inherit' });
log.success('Gateway 重启完成');
} catch (err) {
log.error(`重启失败: ${err.message}`);
log.info('请手动执行: openclaw gateway restart');
}
// 完成提示
console.log('\n' + '─'.repeat(50));
log.success('配置完成!');
console.log('\n配置摘要:');
console.log(` 账户 ID: ${accountId}`);
console.log(` 路由模式: ${mode}`);
if (agentid) console.log(` Agent: ${agentid}`);
if (chatid) console.log(` 群聊: ${chatid}`);
console.log('\n如配置有误可从备份恢复:');
console.log(` cp ${backupPath} ${CONFIG_PATH}`);
console.log('─'.repeat(50) + '\n');
}
// 处理命令行参数
function parseArgs() {
const args = process.argv.slice(2);
const options = {};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg.startsWith('--')) {
const key = arg.slice(2).replace(/-/g, '');
const value = args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : 'true';
options[key] = value;
if (value !== 'true') i++;
}
}
return options;
}
// 主入口
const options = parseArgs();
if (options.help || options.h) {
showRoutingOptions();
console.log(`
${colors.bold}用法:${colors.reset}
node index.js [选项]
${colors.bold}选项:${colors.reset}
--app-id <id> 飞书 App ID (必填)
--app-secret <secret> 飞书 App Secret (必填)
--account-id <id> 账户标识 (可选, 默认自动生成)
--bot-name <name> 机器人名称 (可选)
--dm-policy <policy> DM 策略: open/pairing/allowlist (默认: open)
--agent-id <id> 要绑定的 Agent ID (可选)
--chat-id <id> 群聊 ID oc_xxx (群聊绑定时需要)
--routing-mode <mode> 路由模式: account/group (默认: account)
--help, -h 显示帮助
${colors.bold}示例:${colors.reset}
# 账户级绑定 - 该机器人所有消息都由指定 Agent 处理
node index.js --app-id cli_xxx --app-secret yyy --agent-id recruiter --routing-mode account
# 群聊级绑定 - 特定群聊的消息由指定 Agent 处理
node index.js --app-id cli_xxx --app-secret yyy --agent-id recruiter --chat-id oc_xxx --routing-mode group
`);
process.exit(0);
}
if (options.appid) {
quickMode(options);
} else {
log.error('请提供 --app-id 和 --app-secret或使用 --help 查看帮助');
process.exit(1);
}