Files
insightflow/chrome-extension/background.js
OpenClaw Bot 797ca58e8e Phase 7 Task 7: 插件与集成系统
- 创建 plugin_manager.py 模块
  - PluginManager: 插件管理主类
  - ChromeExtensionHandler: Chrome 插件处理
  - BotHandler: 飞书/钉钉/Slack 机器人处理
  - WebhookIntegration: Zapier/Make Webhook 集成
  - WebDAVSync: WebDAV 同步管理

- 创建完整的 Chrome 扩展代码
  - manifest.json, background.js, content.js, content.css
  - popup.html/js: 弹出窗口界面
  - options.html/js: 设置页面
  - 支持网页剪藏、选中文本保存、项目选择

- 更新 schema.sql 添加插件相关数据库表
  - plugins: 插件配置表
  - bot_sessions: 机器人会话表
  - webhook_endpoints: Webhook 端点表
  - webdav_syncs: WebDAV 同步配置表
  - plugin_activity_logs: 插件活动日志表

- 更新 main.py 添加插件相关 API 端点
  - GET/POST /api/v1/plugins - 插件管理
  - POST /api/v1/plugins/chrome/clip - Chrome 插件保存网页
  - POST /api/v1/bots/webhook/{platform} - 接收机器人消息
  - GET /api/v1/bots/sessions - 机器人会话列表
  - POST /api/v1/webhook-endpoints - 创建 Webhook 端点
  - POST /webhook/{type}/{token} - 接收外部 Webhook
  - POST /api/v1/webdav-syncs - WebDAV 同步配置
  - POST /api/v1/webdav-syncs/{id}/test - 测试 WebDAV 连接
  - POST /api/v1/webdav-syncs/{id}/sync - 触发 WebDAV 同步

- 更新 requirements.txt 添加插件依赖
  - beautifulsoup4: HTML 解析
  - webdavclient3: WebDAV 客户端

- 更新 STATUS.md 和 README.md 开发进度
2026-02-23 12:09:15 +08:00

217 lines
5.7 KiB
JavaScript

// InsightFlow Chrome Extension - Background Script
// 处理后台任务、右键菜单、消息传递
// 默认配置
const DEFAULT_CONFIG = {
serverUrl: 'http://122.51.127.111:18000',
apiKey: '',
defaultProjectId: ''
};
// 初始化
chrome.runtime.onInstalled.addListener(() => {
// 创建右键菜单
chrome.contextMenus.create({
id: 'clipSelection',
title: '保存到 InsightFlow',
contexts: ['selection', 'page']
});
// 初始化存储
chrome.storage.sync.get(['insightflowConfig'], (result) => {
if (!result.insightflowConfig) {
chrome.storage.sync.set({ insightflowConfig: DEFAULT_CONFIG });
}
});
});
// 处理右键菜单点击
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === 'clipSelection') {
clipPage(tab, info.selectionText);
}
});
// 处理扩展图标点击
chrome.action.onClicked.addListener((tab) => {
clipPage(tab);
});
// 监听来自内容脚本的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'clipPage') {
clipPage(sender.tab, request.selectionText);
sendResponse({ success: true });
} else if (request.action === 'getConfig') {
chrome.storage.sync.get(['insightflowConfig'], (result) => {
sendResponse(result.insightflowConfig || DEFAULT_CONFIG);
});
return true; // 保持消息通道开放
} else if (request.action === 'saveConfig') {
chrome.storage.sync.set({ insightflowConfig: request.config }, () => {
sendResponse({ success: true });
});
return true;
} else if (request.action === 'fetchProjects') {
fetchProjects().then(projects => {
sendResponse({ success: true, projects });
}).catch(error => {
sendResponse({ success: false, error: error.message });
});
return true;
}
});
// 剪藏页面
async function clipPage(tab, selectionText = null) {
try {
// 获取配置
const config = await getConfig();
if (!config.apiKey) {
showNotification('请先配置 API Key', '点击扩展图标打开设置');
chrome.runtime.openOptionsPage();
return;
}
// 获取页面内容
const [{ result }] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: extractPageContent,
args: [selectionText]
});
// 发送到 InsightFlow
const response = await sendToInsightFlow(config, result);
if (response.success) {
showNotification('保存成功', '内容已导入 InsightFlow');
} else {
showNotification('保存失败', response.error || '未知错误');
}
} catch (error) {
console.error('Clip error:', error);
showNotification('保存失败', error.message);
}
}
// 提取页面内容
function extractPageContent(selectionText) {
const data = {
url: window.location.href,
title: document.title,
selection: selectionText,
timestamp: new Date().toISOString()
};
if (selectionText) {
// 只保存选中的文本
data.content = selectionText;
data.contentType = 'selection';
} else {
// 保存整个页面
// 获取主要内容
const article = document.querySelector('article') ||
document.querySelector('main') ||
document.querySelector('.content') ||
document.querySelector('#content');
if (article) {
data.content = article.innerText;
data.contentType = 'article';
} else {
// 获取 body 文本,但移除脚本和样式
const bodyClone = document.body.cloneNode(true);
const scripts = bodyClone.querySelectorAll('script, style, nav, header, footer, aside');
scripts.forEach(el => el.remove());
data.content = bodyClone.innerText;
data.contentType = 'page';
}
// 限制内容长度
if (data.content.length > 50000) {
data.content = data.content.substring(0, 50000) + '...';
data.truncated = true;
}
}
// 获取元数据
data.meta = {
description: document.querySelector('meta[name="description"]')?.content || '',
keywords: document.querySelector('meta[name="keywords"]')?.content || '',
author: document.querySelector('meta[name="author"]')?.content || ''
};
return data;
}
// 发送到 InsightFlow
async function sendToInsightFlow(config, data) {
const url = `${config.serverUrl}/api/v1/plugins/chrome/clip`;
const payload = {
url: data.url,
title: data.title,
content: data.content,
content_type: data.contentType,
meta: data.meta,
project_id: config.defaultProjectId || null
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': config.apiKey
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const error = await response.text();
throw new Error(error);
}
return await response.json();
}
// 获取配置
function getConfig() {
return new Promise((resolve) => {
chrome.storage.sync.get(['insightflowConfig'], (result) => {
resolve(result.insightflowConfig || DEFAULT_CONFIG);
});
});
}
// 获取项目列表
async function fetchProjects() {
const config = await getConfig();
if (!config.apiKey) {
throw new Error('请先配置 API Key');
}
const response = await fetch(`${config.serverUrl}/api/v1/projects`, {
headers: {
'X-API-Key': config.apiKey
}
});
if (!response.ok) {
throw new Error('获取项目列表失败');
}
const data = await response.json();
return data.projects || [];
}
// 显示通知
function showNotification(title, message) {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon128.png',
title,
message
});
}