- 创建 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 开发进度
217 lines
5.7 KiB
JavaScript
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
|
|
});
|
|
} |