This commit is contained in:
kirigaya 2025-05-13 11:12:24 +08:00
parent 72182b32f5
commit dfe436d178
41 changed files with 4163 additions and 112 deletions

View File

@ -4,28 +4,19 @@
> 简体中文 < | <a href="https://www.google.com/search?q=best+website+to+learn+chinese&newwindow=1&sca_esv=a76695392a9980a7&rlz=1C1CHBD_zh-HKHK1072HK1072&sxsrf=ADLYWIKib09skMzGw8JqpTv2AWB6Xk8uZQ%3A1716986674152&ei=MiNXZsP3COrk2roPt-SrgA0&oq=best+website+to+learn+chin&gs_lp=Egxnd3Mtd2l6LXNlcnAiGmJlc3Qgd2Vic2l0ZSB0byBsZWFybiBjaGluKgIIADIIEAAYgAQYywEyCBAAGIAEGMsBMggQABiABBiiBDIIEAAYgAQYogQyCBAAGIAEGKIEMggQABiABBiiBDIIEAAYgAQYogRItDNQtgNY3C5wAXgAkAEFmAHMBKABlzaqAQwwLjE4LjUuNC4wLjK4AQPIAQD4AQGYAhigAosmwgINECMYgAQYsAMYJxiKBcICDBAAGIAEGLADGAoYDMICERAuGIAEGLADGMcBGMsBGK8BwgIHEAAYsAMYHsICCRAAGLADGAgYHsICBBAjGCfCAhAQLhiABBjRAxhDGMcBGIoFwgIFEAAYgATCAgsQABiABBixAxiDAcICExAuGIAEGNEDGMcBGMkDGAoYywHCAgsQABiABBiSAxiKBcICBhAAGAgYHsICIhAuGIAEGNEDGMcBGMkDGAoYywEYlwUY3AQY3gQY4ATYAQHCAhcQLhiABBjHARiYBRiZBRjLARieBRivAcICERAuGIAEGNEDGMcBGMkDGMsBwgImEC4YgAQYxwEYmAUYmQUYywEYngUYrwEYlwUY3AQY3gQY4ATYAQHCAgYQABgeGA-YAwCIBgGQBgq6BgYIARABGBSSBwoxLjE2LjUuMS4xoAf2rwE&sclient=gws-wiz-serp" target="_blank">English</a>
</div>
## 结构
## 环境搭建
每一个服务都放在 ./node 中,根据各自的 readme 分别配置。
你需要准备的环境变量:
```bash
git clone xxxxxx
npm install pm2 -g
yarn
pip install -r requirements.txt
export DEEPSEEK_API_TOKEN=xxx
export OMCP_DISCORD_SERVER_IP=xxx
export OMCP_DISCORD_SERVER_PORT=xxx
export OMCP_DISCORD_TOKEN=xxx
```
下载最新版本的 Lagrange.core ,并完成初始化。完成后根目录为
```
📁bot
📁rag
📁app
├─📁publish
│ ├─🏷️...
│ ├─🏷appsettings.json
│ └─🪛Lagrange.OneBot
└─🎗️...
```
---

View File

@ -1,4 +1,47 @@
mcp-server:
port: 25565
bot:
port: 25564
towardsdatascience:
route: /get-news-from-towards-science
prompt:
请通过 headless 的方式帮我搜集 https://towardsdatascience.com/tag/editors-pick/ 最热门的前三条信息,这些信息大概率在 main 元素的 ul li 下面。
请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。
然后帮我进入这些网站后总结相关信息,文章通常也在 <main> 中,返回翻译好的简体中文,并且整理成一个简单的资讯。
按以下格式输出(严格禁止添加任何额外引导语或总结句):
[用一句吸引人的话开头,例如:"⌨️ 今日份 AI & CS 技术文章分享"]
📌 [翻译成简体中文的文章标题]
摘要:[文章的核心观点100字以内]
作者:[若存在]
发布时间:[若存在]
链接:[文章URL]
mcp:
command: node ~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js
settings:
systemPrompt: 'you are a clever bot'
hacknews:
route: /get-news-from-hack-news
prompt:
请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。
请一步步寻找,先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。
然后帮我进入这些网站后总结相关信息,返回翻译好的简体中文,并且整理成一个简单的资讯。
按以下格式输出(严格禁止添加任何额外引导语或总结句):
[用一句吸引人的话开头,例如:"⌨️ 今日份 AI & CS 技术文章分享"]
📌 [翻译成简体中文的文章标题]
摘要:[文章的核心观点100字以内]
作者:[若存在]
发布时间:[若存在]
链接:[文章URL]`.
mcp:
command: node ~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js
settings:
systemPrompt: 'you are a clever bot'

0
node/.gitignore vendored Normal file
View File

View File

@ -1,4 +1,5 @@
*.json
*.db
*.png
*.OneBot
*.OneBot
lagrange-0-db

View File

@ -12,12 +12,15 @@
"@types/node": "^20.12.12",
"axios": "^1.7.2",
"fs": "^0.0.1-security",
"lagrange.onebot": "^1.0.2",
"json-bigint": "^1.0.0",
"lagrange.onebot": "^1.0.8",
"node-schedule": "^2.1.1",
"ws": "^8.17.0",
"yaml": "^2.4.2"
},
"devDependencies": {
"@sinonjs/referee-sinon": "^12.0.0",
"@types/node-schedule": "^2.1.7",
"@types/ws": "^8.5.10",
"chai": "4.3.4",
"jsverify": "^0.8.4",
@ -439,6 +442,16 @@
"undici-types": "~6.19.2"
}
},
"node_modules/@types/node-schedule": {
"version": "2.1.7",
"resolved": "https://registry.npmmirror.com/@types/node-schedule/-/node-schedule-2.1.7.tgz",
"integrity": "sha512-G7Z3R9H7r3TowoH6D2pkzUHPhcJrDF4Jz1JOQ80AX0K2DWTHoN9VC94XzFAPNMdbW9TBzMZ3LjpFi7RYdbxtXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
@ -706,6 +719,15 @@
"node": ">=10.0.0"
}
},
"node_modules/bignumber.js": {
"version": "9.3.0",
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.0.tgz",
"integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
@ -1211,6 +1233,18 @@
"node": ">= 0.6"
}
},
"node_modules/cron-parser": {
"version": "4.9.0",
"resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-4.9.0.tgz",
"integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
"license": "MIT",
"dependencies": {
"luxon": "^3.2.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/croner": {
"version": "4.1.97",
"resolved": "https://registry.npmmirror.com/croner/-/croner-4.1.97.tgz",
@ -2400,6 +2434,15 @@
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
"license": "MIT"
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"license": "MIT",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
@ -2457,18 +2500,34 @@
"license": "(MIT OR Apache-2.0)"
},
"node_modules/lagrange.onebot": {
"version": "1.0.4",
"resolved": "https://registry.npmmirror.com/lagrange.onebot/-/lagrange.onebot-1.0.4.tgz",
"integrity": "sha512-Ll/8d7H3Sea/Ajy5lj4ReBf8JmfR3ptgCWbF/7UYZ/lTtmA8Lms18p1XDR8El90AsARRIqYDmlk92vfOGcNewg==",
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/lagrange.onebot/-/lagrange.onebot-1.0.8.tgz",
"integrity": "sha512-x2IdveKDW5GLm6NJRYjV+/6lAds94AQN3Ow00bFBqFTtZDAED6AKBCnIrrkyfns1OXpXJrZXbdGfny11zgtdmA==",
"dependencies": {
"@ptkdev/logger": "^1.8.0",
"@types/node": "^20.12.12",
"axios": "^1.7.2",
"@types/node": "22.15.16",
"axios": "^1.9.0",
"node-schedule": "^2.1.1",
"pm2": "^5.4.0",
"ws": "^8.17.0",
"yaml": "^2.4.2"
}
},
"node_modules/lagrange.onebot/node_modules/@types/node": {
"version": "22.15.16",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-22.15.16.tgz",
"integrity": "sha512-3pr+KjwpVujqWqOKT8mNR+rd09FqhBLwg+5L/4t0cNYBzm/yEiYGCxWttjaPBsLtAo+WFNoXzGJfolM1JuRXoA==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/lagrange.onebot/node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/lazy": {
"version": "1.0.11",
"resolved": "https://registry.npmmirror.com/lazy/-/lazy-1.0.11.tgz",
@ -2542,6 +2601,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/long-timeout": {
"version": "0.1.1",
"resolved": "https://registry.npmmirror.com/long-timeout/-/long-timeout-0.1.1.tgz",
"integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==",
"license": "MIT"
},
"node_modules/lowdb": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/lowdb/-/lowdb-1.0.0.tgz",
@ -2567,6 +2632,15 @@
"node": ">=12"
}
},
"node_modules/luxon": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.6.1.tgz",
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -2805,6 +2879,20 @@
"node": ">=16"
}
},
"node_modules/node-schedule": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/node-schedule/-/node-schedule-2.1.1.tgz",
"integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==",
"license": "MIT",
"dependencies": {
"cron-parser": "^4.2.0",
"long-timeout": "0.1.1",
"sorted-array-functions": "^1.3.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
@ -3872,6 +3960,12 @@
"node": ">= 14"
}
},
"node_modules/sorted-array-functions": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz",
"integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==",
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",

View File

@ -18,12 +18,15 @@
"@types/node": "^20.12.12",
"axios": "^1.7.2",
"fs": "^0.0.1-security",
"lagrange.onebot": "^1.0.2",
"json-bigint": "^1.0.0",
"lagrange.onebot": "^1.0.8",
"node-schedule": "^2.1.1",
"ws": "^8.17.0",
"yaml": "^2.4.2"
},
"devDependencies": {
"@sinonjs/referee-sinon": "^12.0.0",
"@types/node-schedule": "^2.1.7",
"@types/ws": "^8.5.10",
"chai": "4.3.4",
"jsverify": "^0.8.4",

18
node/bot/src/global.ts Normal file
View File

@ -0,0 +1,18 @@
export const qq_groups = {
/**
* OpenMCP正式级技术组
*/
OPENMCP_DEV: 782833642,
/**
* Tiphereth
*/
TEST_CHANNEL: 1046693162
};
export const qq_users = {
/**
* dayo !
*/
JIN_HUI: 1193466151
};

View File

@ -1,14 +1,23 @@
import * as fs from 'fs';
import { server } from 'lagrange.onebot';
import './services/test';
import { TestChannel } from './test-channel/test-channel.controller';
import { OpenMcpChannel } from './openmcp-dev/openmcp-dev.controller';
import { qq_users } from './global';
// 注册的模块
export const InstallChannels = [
TestChannel,
OpenMcpChannel
];
server.onMounted(c => {
c.sendPrivateMsg(1193466151, '成功上线');
c.sendPrivateMsg(qq_users.JIN_HUI, '成功上线');
});
server.onUnmounted(c => {
c.sendPrivateMsg(1193466151, '成功下线');
c.sendPrivateMsg(qq_users.JIN_HUI, '成功下线');
});
@ -21,4 +30,8 @@ server.run({
port: impl.Port,
path: impl.Suffix,
qq: 1542544558
});
InstallChannels.forEach(channel => {
console.log('上线模块:' + channel.name);
});

View File

@ -0,0 +1,97 @@
import '../plugins/image';
import * as path from 'path';
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger, ApproveMessage, Message } from 'lagrange.onebot';
import { getNews, publishOpenMCP } from './openmcp-dev.service';
import { qq_groups, qq_users } from '../global';
import { parseCommand, sendMessageToDiscord } from '../util';
import axios from 'axios';
console.log('activate ' + path.basename(__filename));
const visitCache = new Map<string, number>();
export class OpenMcpChannel {
@mapper.onGroup(qq_groups.OPENMCP_DEV, { onlyAdmin: true })
async handleTestChannel(c: LagrangeContext<GroupMessage>) {
const text = c.getRawText();
const commandResult = parseCommand(text);
if (commandResult !== undefined) {
// 校验身份
if (c.message.user_id === qq_users.JIN_HUI) {
const now = Date.now();
const lastVisit = visitCache.get(c.message.user_id.toString());
const info = await c.getGroupMemberInfo(c.message.group_id, c.message.user_id);
const role = info['data'].role;
const name = info['data'].nickname;
if (!lastVisit || (now - lastVisit) > 10 * 60 * 1000) {
c.sendMessage('检测到超级管理员TIP 系统允许访问,正在执行 ' + JSON.stringify(commandResult));
visitCache.set(c.message.user_id.toString(), now);
}
}
const { command, args } = commandResult;
switch (command) {
case 'news':
await getNews(c);
break;
case 'ping':
c.sendMessage('ping');
break;
case 'file':
if (args.length === 0) {
c.sendMessage('usage\n:file filename');
} else {
const filename = args[0];
const file = path.join('/home/kirigaya/download/', filename);
c.uploadGroupFile(c.message.group_id, file, filename);
}
break;
case 'notice':
c.sendGroupNotice(c.message.group_id, 'hello world');
break;
case 'get-notice':
const notice = await c.getGroupNotice(c.message.group_id);
c.sendMessage('当前的公告内容为:' + JSON.stringify(notice, null, 2));
break;
case 'pub':
await publishOpenMCP(c);
break;
case 'discord':
await sendMessageToDiscord('hello from qq');
break;
default:
break;
}
}
}
@mapper.onGroupIncrease(qq_groups.OPENMCP_DEV)
async handleTestChannelIncrease(c: LagrangeContext<ApproveMessage>) {
console.log(c.message);
// c.setGroupAddRequest('', c.message.sub_type, true, '');
}
@mapper.createTimeSchedule('0 0 10 * * *')
async handleTestChannelSchedule(c: LagrangeContext<Message>) {
const res = await axios.post('http://localhost:3000/get-news-from-hack-news');
const data = res.data;
const message = data.msg;
c.sendGroupMsg(qq_groups.OPENMCP_DEV, message);
}
}

View File

@ -0,0 +1,32 @@
import '../plugins/image';
import axios from 'axios';
import { LagrangeContext, PrivateMessage, GroupMessage } from 'lagrange.onebot'
import path from 'path';
import { sendMessageToDiscord, wait } from '../util';
export async function getNews(c: LagrangeContext<PrivateMessage | GroupMessage>) {
const res = await axios.post('http://localhost:3000/get-news-from-hack-news');
const data = res.data;
const message = data.msg;
console.log('message', message);
c.sendMessage(message);
}
export async function publishOpenMCP(c: LagrangeContext<GroupMessage>) {
const res = await axios.post('http://localhost:3000/publish-openmcp-client');
const data = res.data;
const message = data.msg;
console.log('message', message);
const { vsix, content } = message;
await c.uploadGroupFile(c.message.group_id, vsix, path.basename(vsix));
await wait(2000);
await c.sendGroupNotice(c.message.group_id, content);
await sendMessageToDiscord(content);
}

View File

@ -24,4 +24,5 @@ export class Impl {
}
}
}
}

View File

@ -0,0 +1,38 @@
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger } from 'lagrange.onebot';
console.log('activate ' + __filename);
export class Impl {
@mapper.onGroup(782833642)
@plugins.use('echo')
@plugins.use('pm')
@plugins.use('wget-image')
async handleOpenMCP(c: LagrangeContext<GroupMessage>) {
const text = c.getRawText();
console.log('[receive] ' + text);
if (c.qq) {
}
if (text.startsWith(':')) {
const command = text.substring(1);
switch (command) {
case 'news':
break;
default:
break;
}
}
// c.sendMessage([{
// type: 'image',
// data: {
// file: 'file:///data/zhelonghuang/project/rag-llm/images/bird.png',
// timeout: 10000
// }
// }])
c.finishSession();
}
}

View File

@ -3,10 +3,6 @@ import axios from 'axios';
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger } from 'lagrange.onebot'
import { apiGetIntentRecogition, apiQueryVecdb } from '../api/vecdb';
import { handleGroupIntent } from './intent';
console.log('activate ' + __filename);
export class Impl {
@ -84,4 +80,27 @@ async function getNews(c: LagrangeContext<PrivateMessage>) {
console.log('message', message);
c.sendMessage(message);
}
}
async function getFile(c: LagrangeContext<PrivateMessage>) {
c.sendMessage([
// {
// type: 'text',
// data: {
// file: 'file:///data/zhelonghuang/project/rag-llm/images/bird.png',
// timeout: 10000
// }
// }
])
}
async function publishOpenMCP(c: LagrangeContext<PrivateMessage>) {
const res = await axios.post('http://localhost:3000/get-news-from-towards-science');
const data = res.data;
const message = data.msg;
console.log('message', message);
c.sendMessage(message);
}

View File

@ -0,0 +1,121 @@
import '../plugins/image';
import * as path from 'path';
import { mapper, plugins, LagrangeContext, PrivateMessage, GroupMessage, Send, logger, ApproveMessage, Message } from 'lagrange.onebot';
import { getNews, publishOpenMCP } from './test-channel.service';
import { qq_groups, qq_users } from '../global';
import { parseCommand, sendMessageToDiscord } from '../util';
console.log('activate ' + path.basename(__filename));
const visitCache = new Map<string, number>();
export class TestChannel {
@mapper.onPrivateUser(qq_users.JIN_HUI)
@plugins.use('echo')
@plugins.use('pm')
@plugins.use('wget-image')
async handleJinhui(c: LagrangeContext<PrivateMessage>) {
const text = c.getRawText();
console.log('[receive] ' + text);
if (text.startsWith(':')) {
const command = text.substring(1);
switch (command) {
case 'news':
await getNews(c);
break;
case 'ping':
c.sendMessage('ping');
break;
default:
break;
}
}
c.finishSession();
}
@mapper.onGroup(qq_groups.TEST_CHANNEL, { onlyAdmin: true })
async handleTestChannel(c: LagrangeContext<GroupMessage>) {
const text = c.getRawText();
const commandResult = parseCommand(text);
if (commandResult !== undefined) {
// 校验身份
if (c.message.user_id === qq_users.JIN_HUI) {
const now = Date.now();
const lastVisit = visitCache.get(c.message.user_id.toString());
const info = await c.getGroupMemberInfo(c.message.group_id, c.message.user_id);
const role = info['data'].role;
const name = info['data'].nickname;
if (!lastVisit || (now - lastVisit) > 10 * 60 * 1000) {
c.sendMessage('检测到超级管理员TIP 系统允许访问,正在执行 ' + JSON.stringify(commandResult));
visitCache.set(c.message.user_id.toString(), now);
}
}
const { command, args } = commandResult;
switch (command) {
case 'news':
await getNews(c);
break;
case 'ping':
c.sendMessage('ping');
break;
case 'file':
if (args.length === 0) {
c.sendMessage('usage\n:file filename');
} else {
const filename = args[0];
const file = path.join('/home/kirigaya/download/', filename);
c.uploadGroupFile(c.message.group_id, file, filename);
}
break;
case 'notice':
c.sendGroupNotice(c.message.group_id, 'hello world');
break;
case 'get-notice':
const notice = await c.getGroupNotice(c.message.group_id);
c.sendMessage('当前的公告内容为:' + JSON.stringify(notice, null, 2));
break;
case 'pub':
await publishOpenMCP(c);
break;
case 'discord':
await sendMessageToDiscord('hello from qq');
break;
default:
break;
}
}
}
@mapper.onGroupIncrease(qq_groups.TEST_CHANNEL)
async handleTestChannelIncrease(c: LagrangeContext<ApproveMessage>) {
console.log(c.message);
c.setGroupAddRequest('', c.message.sub_type, true, '');
}
@mapper.createTimeSchedule('0 11 20 * * *')
async handleTestChannelSchedule(c: LagrangeContext<Message>) {
c.sendPrivateMsg(qq_users.JIN_HUI, 'hello');
}
}

View File

@ -0,0 +1,32 @@
import '../plugins/image';
import axios from 'axios';
import { LagrangeContext, PrivateMessage, GroupMessage } from 'lagrange.onebot'
import path from 'path';
import { sendMessageToDiscord, wait } from '../util';
export async function getNews(c: LagrangeContext<PrivateMessage | GroupMessage>) {
const res = await axios.post('http://localhost:3000/get-news-from-hack-news');
const data = res.data;
const message = data.msg;
console.log('message', message);
c.sendMessage(message);
}
export async function publishOpenMCP(c: LagrangeContext<GroupMessage>) {
const res = await axios.post('http://localhost:3000/publish-openmcp-client');
const data = res.data;
const message = data.msg;
console.log('message', message);
const { vsix, content } = message;
await c.uploadGroupFile(c.message.group_id, vsix, path.basename(vsix));
await wait(2000);
await c.sendGroupNotice(c.message.group_id, content);
await sendMessageToDiscord(content);
}

53
node/bot/src/util.ts Normal file
View File

@ -0,0 +1,53 @@
import axios from "axios";
export function parseCommand(text: string) {
if (text.startsWith(':')) {
text = text.substring(1);
} else {
return;
}
const command = text.split(' ')[0];
const args = text.split(' ').slice(1);
return {
command,
args
}
}
export function wait(timeout: number) {
return new Promise(resolve => {
setTimeout(() => {
resolve(void 0);
}, timeout);
})
}
export async function sendMessageToDiscord(message: string) {
const ip = process.env['OMCP_DISCORD_SERVER_IP'];
const port = process.env['OMCP_DISCORD_SERVER_PORT'];
const token = process.env['OMCP_DISCORD_TOKEN'];
try {
const response = await axios({
method: 'post',
url: `http://${ip}:${port}/api/message`,
headers: {
'Authorization': token,
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
'Content-Type': 'application/json',
'Accept': '*/*',
'Host': `${ip}:${port}`,
'Connection': 'keep-alive'
},
data: {
content: message,
channel: '1368185363549651107'
}
});
console.log('Response:', response.data);
} catch (error) {
console.error('Error:', error.response ? error.response.data : error.message);
}
}

View File

@ -1,11 +1,11 @@
{
"name": "mcp-server",
"name": "faas",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mcp-server",
"name": "faas",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
@ -13,12 +13,14 @@
"bson": "^6.10.3",
"cors": "^2.8.5",
"express": "^5.1.0",
"js-yaml": "^4.1.0",
"morgan": "^1.10.0",
"openmcp-sdk": "^0.0.4"
},
"devDependencies": {
"@types/cors": "^2.8.18",
"@types/express": "^5.0.1",
"@types/js-yaml": "^4.0.9",
"@types/morgan": "^1.9.9",
"@types/node": "^22.15.17",
"ts-node": "^10.9.2",
@ -239,6 +241,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/js-yaml": {
"version": "4.0.9",
"resolved": "https://registry.npmmirror.com/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
@ -486,6 +495,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
@ -1370,6 +1385,18 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/lie": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/lie/-/lie-3.1.1.tgz",

View File

@ -1,5 +1,5 @@
{
"name": "mcp-server",
"name": "faas",
"version": "1.0.0",
"description": "执行 mcp 部分的模块",
"main": "index.js",
@ -13,12 +13,14 @@
"bson": "^6.10.3",
"cors": "^2.8.5",
"express": "^5.1.0",
"js-yaml": "^4.1.0",
"morgan": "^1.10.0",
"openmcp-sdk": "^0.0.4"
},
"devDependencies": {
"@types/cors": "^2.8.18",
"@types/express": "^5.0.1",
"@types/js-yaml": "^4.0.9",
"@types/morgan": "^1.9.9",
"@types/node": "^22.15.17",
"ts-node": "^10.9.2",

View File

@ -0,0 +1,287 @@
{
"messages": [
{
"role": "user",
"content": "请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。\n\n请一步步寻找先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。\n\n然后帮我进入这些网站后总结相关信息返回翻译好的简体中文并且整理成一个简单的资讯。\n按以下格式输出严格禁止添加任何额外引导语或总结句\n\n[用一句吸引人的话开头,例如:\"⌨️ 今日份 AI & CS 技术文章分享\"]\n\n📌 [翻译成简体中文的文章标题]\n摘要[文章的核心观点100字以内]\n作者[若存在]\n发布时间[若存在]\n链接[文章URL]",
"extraInfo": {
"created": 1747073959697,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_a0f34b9b-d93a-437d-af28-bf96d68555f3",
"index": 0,
"type": "function",
"function": {
"name": "k_navigate",
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true}}"
}
}
],
"extraInfo": {
"created": 1747073967399,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_a0f34b9b-d93a-437d-af28-bf96d68555f3",
"content": [
{
"type": "error",
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
}
],
"extraInfo": {
"created": 1747073997731,
"state": "tool call failed",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1071,
"completion_tokens": 36,
"total_tokens": 1107,
"prompt_tokens_details": {
"cached_tokens": 1024
},
"prompt_cache_hit_tokens": 1024,
"prompt_cache_miss_tokens": 47
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_0e73ade3-e100-4f12-b3cd-505cf31a3e26",
"index": 0,
"type": "function",
"function": {
"name": "k_navigate",
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
}
}
],
"extraInfo": {
"created": 1747074003265,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_0e73ade3-e100-4f12-b3cd-505cf31a3e26",
"content": [
{
"type": "error",
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
}
],
"extraInfo": {
"created": 1747074034143,
"state": "tool call failed",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1141,
"completion_tokens": 54,
"total_tokens": 1195,
"prompt_tokens_details": {
"cached_tokens": 1088
},
"prompt_cache_hit_tokens": 1088,
"prompt_cache_miss_tokens": 53
}
}
},
{
"role": "assistant",
"content": "很抱歉,我无法直接访问该网站以获取信息。您可以尝试手动访问 [Hacker News](https://news.ycombinator.com/) 并查看最热门的前三条信息,然后我可以帮助您整理和翻译相关内容。",
"extraInfo": {
"created": 1747074039413,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1229,
"completion_tokens": 49,
"total_tokens": 1278,
"prompt_tokens_details": {
"cached_tokens": 1216
},
"prompt_cache_hit_tokens": 1216,
"prompt_cache_miss_tokens": 13
}
}
}
],
"settings": {
"temperature": 0.7,
"enableTools": [
{
"name": "k_navigate",
"description": "Navigate to a URL",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to navigate to"
},
"launchOptions": {
"type": "object",
"description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }"
},
"allowDangerous": {
"type": "boolean",
"description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false."
}
},
"required": [
"url"
]
},
"enabled": true
},
{
"name": "k_screenshot",
"description": "Take a screenshot of the current page or a specific element",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name for the screenshot"
},
"selector": {
"type": "string",
"description": "CSS selector for element to screenshot"
},
"width": {
"type": "number",
"description": "Width in pixels (default: 800)"
},
"height": {
"type": "number",
"description": "Height in pixels (default: 600)"
}
},
"required": [
"name"
]
},
"enabled": true
},
{
"name": "k_click",
"description": "Click an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to click"
}
},
"required": [
"selector"
]
},
"enabled": true
},
{
"name": "k_fill",
"description": "Fill out an input field",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for input field"
},
"value": {
"type": "string",
"description": "Value to fill"
}
},
"required": [
"selector",
"value"
]
},
"enabled": true
},
{
"name": "k_select",
"description": "Select an element on the page with Select tag",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to select"
},
"value": {
"type": "string",
"description": "Value to select"
}
},
"required": [
"selector",
"value"
]
},
"enabled": true
},
{
"name": "k_hover",
"description": "Hover an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to hover"
}
},
"required": [
"selector"
]
},
"enabled": true
},
{
"name": "k_evaluate",
"description": "Execute JavaScript in the browser console",
"inputSchema": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "JavaScript code to execute"
}
},
"required": [
"script"
]
},
"enabled": true
}
],
"systemPrompt": "",
"contextLength": 20
},
"mcp": {
"connectionType": "STDIO",
"command": "node",
"args": [
"~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js"
]
}
}

View File

@ -0,0 +1,287 @@
{
"messages": [
{
"role": "user",
"content": "请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。\n\n请一步步寻找先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。\n\n然后帮我进入这些网站后总结相关信息返回翻译好的简体中文并且整理成一个简单的资讯。\n按以下格式输出严格禁止添加任何额外引导语或总结句\n\n[用一句吸引人的话开头,例如:\"⌨️ 今日份 AI & CS 技术文章分享\"]\n\n📌 [翻译成简体中文的文章标题]\n摘要[文章的核心观点100字以内]\n作者[若存在]\n发布时间[若存在]\n链接[文章URL]",
"extraInfo": {
"created": 1747074265643,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_27a3e2af-e707-4eaf-909c-2fa9f1075667",
"index": 0,
"type": "function",
"function": {
"name": "k_navigate",
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true}}"
}
}
],
"extraInfo": {
"created": 1747074272702,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_27a3e2af-e707-4eaf-909c-2fa9f1075667",
"content": [
{
"type": "error",
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
}
],
"extraInfo": {
"created": 1747074303017,
"state": "tool call failed",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1071,
"completion_tokens": 36,
"total_tokens": 1107,
"prompt_tokens_details": {
"cached_tokens": 1024
},
"prompt_cache_hit_tokens": 1024,
"prompt_cache_miss_tokens": 47
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_273aedd5-6187-403d-bb53-404b293360b7",
"index": 0,
"type": "function",
"function": {
"name": "k_navigate",
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
}
}
],
"extraInfo": {
"created": 1747074308989,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_273aedd5-6187-403d-bb53-404b293360b7",
"content": [
{
"type": "error",
"text": "McpError: MCP error -32603: Navigation timeout of 30000 ms exceeded"
}
],
"extraInfo": {
"created": 1747074339889,
"state": "tool call failed",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1141,
"completion_tokens": 54,
"total_tokens": 1195,
"prompt_tokens_details": {
"cached_tokens": 1088
},
"prompt_cache_hit_tokens": 1088,
"prompt_cache_miss_tokens": 53
}
}
},
{
"role": "assistant",
"content": "很抱歉,我无法访问该网站以获取所需的信息。可能是由于网站的反爬机制或其他技术限制导致无法加载页面。如果您有其他需求或可以尝试其他方法,请告诉我!",
"extraInfo": {
"created": 1747074347931,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1229,
"completion_tokens": 39,
"total_tokens": 1268,
"prompt_tokens_details": {
"cached_tokens": 1216
},
"prompt_cache_hit_tokens": 1216,
"prompt_cache_miss_tokens": 13
}
}
}
],
"settings": {
"temperature": 0.7,
"enableTools": [
{
"name": "k_navigate",
"description": "Navigate to a URL",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to navigate to"
},
"launchOptions": {
"type": "object",
"description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }"
},
"allowDangerous": {
"type": "boolean",
"description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false."
}
},
"required": [
"url"
]
},
"enabled": true
},
{
"name": "k_screenshot",
"description": "Take a screenshot of the current page or a specific element",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name for the screenshot"
},
"selector": {
"type": "string",
"description": "CSS selector for element to screenshot"
},
"width": {
"type": "number",
"description": "Width in pixels (default: 800)"
},
"height": {
"type": "number",
"description": "Height in pixels (default: 600)"
}
},
"required": [
"name"
]
},
"enabled": true
},
{
"name": "k_click",
"description": "Click an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to click"
}
},
"required": [
"selector"
]
},
"enabled": true
},
{
"name": "k_fill",
"description": "Fill out an input field",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for input field"
},
"value": {
"type": "string",
"description": "Value to fill"
}
},
"required": [
"selector",
"value"
]
},
"enabled": true
},
{
"name": "k_select",
"description": "Select an element on the page with Select tag",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to select"
},
"value": {
"type": "string",
"description": "Value to select"
}
},
"required": [
"selector",
"value"
]
},
"enabled": true
},
{
"name": "k_hover",
"description": "Hover an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to hover"
}
},
"required": [
"selector"
]
},
"enabled": true
},
{
"name": "k_evaluate",
"description": "Execute JavaScript in the browser console",
"inputSchema": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "JavaScript code to execute"
}
},
"required": [
"script"
]
},
"enabled": true
}
],
"systemPrompt": "",
"contextLength": 20
},
"mcp": {
"connectionType": "STDIO",
"command": "node",
"args": [
"~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js"
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,563 @@
{
"messages": [
{
"role": "user",
"content": "请通过 headless 的方式帮我搜集 https://news.ycombinator.com/ 最热门的前三条信息。\n\n请一步步寻找先罗列所有元素剔除了 css class 后的 html 源代码,过滤可能是广告的元素后然后再从元素里面找到符合要求数量的有效元素。不要在第一步的时候就盲目获取元素的属性。\n\n然后帮我进入这些网站后总结相关信息返回翻译好的简体中文并且整理成一个简单的资讯。\n按以下格式输出严格禁止添加任何额外引导语或总结句\n\n[用一句吸引人的话开头,例如:\"⌨️ 今日份 AI & CS 技术文章分享\"]\n\n📌 [翻译成简体中文的文章标题]\n摘要[文章的核心观点100字以内]\n作者[若存在]\n发布时间[若存在]\n链接[文章URL]",
"extraInfo": {
"created": 1747101600320,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_d1b875c1-5a16-4136-a2f4-53648127051f",
"index": 0,
"type": "function",
"function": {
"name": "k_navigate",
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true}}"
}
}
],
"extraInfo": {
"created": 1747101606670,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_d1b875c1-5a16-4136-a2f4-53648127051f",
"content": [
{
"type": "error",
"text": "McpError: MCP error -32603: net::ERR_CONNECTION_RESET at https://news.ycombinator.com/"
}
],
"extraInfo": {
"created": 1747101616953,
"state": "tool call failed",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1071,
"completion_tokens": 36,
"total_tokens": 1107,
"prompt_tokens_details": {
"cached_tokens": 1024
},
"prompt_cache_hit_tokens": 1024,
"prompt_cache_miss_tokens": 47
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_16d05805-8a72-4fb2-b002-6acc27237b94",
"index": 0,
"type": "function",
"function": {
"name": "k_navigate",
"arguments": "{\"url\":\"https://news.ycombinator.com/\",\"launchOptions\":{\"headless\":true,\"args\":[\"--no-sandbox\"]},\"allowDangerous\":true}"
}
}
],
"extraInfo": {
"created": 1747101623731,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_16d05805-8a72-4fb2-b002-6acc27237b94",
"content": [
{
"type": "text",
"text": "Navigated to https://news.ycombinator.com/"
}
],
"extraInfo": {
"created": 1747101628493,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1150,
"completion_tokens": 54,
"total_tokens": 1204,
"prompt_tokens_details": {
"cached_tokens": 1088
},
"prompt_cache_hit_tokens": 1088,
"prompt_cache_miss_tokens": 62
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_c1b1ae98-17cd-4ef6-b4a7-5209e80c7695",
"index": 0,
"type": "function",
"function": {
"name": "k_evaluate",
"arguments": "{\"script\":\"document.documentElement.outerHTML\"}"
}
}
],
"extraInfo": {
"created": 1747101633965,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_c1b1ae98-17cd-4ef6-b4a7-5209e80c7695",
"content": [
{
"type": "text",
"text": "Execution result:\n\"<html><head></head><body></body></html>\"\n\nConsole output:\n"
}
],
"extraInfo": {
"created": 1747101634989,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1230,
"completion_tokens": 25,
"total_tokens": 1255,
"prompt_tokens_details": {
"cached_tokens": 1152
},
"prompt_cache_hit_tokens": 1152,
"prompt_cache_miss_tokens": 78
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_4d783557-5cfc-428b-ae7c-8535ce181d57",
"index": 0,
"type": "function",
"function": {
"name": "k_evaluate",
"arguments": "{\"script\":\"document.body.outerHTML\"}"
}
}
],
"extraInfo": {
"created": 1747101640718,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_4d783557-5cfc-428b-ae7c-8535ce181d57",
"content": [
{
"type": "text",
"text": "Execution result:\n\"<body></body>\"\n\nConsole output:\n"
}
],
"extraInfo": {
"created": 1747101640724,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1296,
"completion_tokens": 23,
"total_tokens": 1319,
"prompt_tokens_details": {
"cached_tokens": 1216
},
"prompt_cache_hit_tokens": 1216,
"prompt_cache_miss_tokens": 80
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_53fcd540-014c-4bc3-9ba6-15a9d5b65b3b",
"index": 0,
"type": "function",
"function": {
"name": "k_evaluate",
"arguments": "{\"script\":\"document.querySelectorAll('a.storylink').length\"}"
}
}
],
"extraInfo": {
"created": 1747101646038,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_53fcd540-014c-4bc3-9ba6-15a9d5b65b3b",
"content": [
{
"type": "text",
"text": "Execution result:\n0\n\nConsole output:\n"
}
],
"extraInfo": {
"created": 1747101646042,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1352,
"completion_tokens": 28,
"total_tokens": 1380,
"prompt_tokens_details": {
"cached_tokens": 1280
},
"prompt_cache_hit_tokens": 1280,
"prompt_cache_miss_tokens": 72
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_7ec588aa-9a81-41ad-9d5e-1cab03e5e7ef",
"index": 0,
"type": "function",
"function": {
"name": "k_evaluate",
"arguments": "{\"script\":\"document.querySelectorAll('a').length\"}"
}
}
],
"extraInfo": {
"created": 1747101651231,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_7ec588aa-9a81-41ad-9d5e-1cab03e5e7ef",
"content": [
{
"type": "text",
"text": "Execution result:\n0\n\nConsole output:\n"
}
],
"extraInfo": {
"created": 1747101651236,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1406,
"completion_tokens": 25,
"total_tokens": 1431,
"prompt_tokens_details": {
"cached_tokens": 1344
},
"prompt_cache_hit_tokens": 1344,
"prompt_cache_miss_tokens": 62
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_77caa460-fd9a-41a9-9db6-98c7d65bd59f",
"index": 0,
"type": "function",
"function": {
"name": "k_evaluate",
"arguments": "{\"script\":\"document.querySelectorAll('*').length\"}"
}
}
],
"extraInfo": {
"created": 1747101656646,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_77caa460-fd9a-41a9-9db6-98c7d65bd59f",
"content": [
{
"type": "text",
"text": "Execution result:\n3\n\nConsole output:\n"
}
],
"extraInfo": {
"created": 1747101656651,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1457,
"completion_tokens": 25,
"total_tokens": 1482,
"prompt_tokens_details": {
"cached_tokens": 1408
},
"prompt_cache_hit_tokens": 1408,
"prompt_cache_miss_tokens": 49
}
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_379c9e3f-c525-4cde-965c-dea7635f116b",
"index": 0,
"type": "function",
"function": {
"name": "k_evaluate",
"arguments": "{\"script\":\"Array.from(document.querySelectorAll('*')).map(el => el.outerHTML).join('\\\\n')\"}"
}
}
],
"extraInfo": {
"created": 1747101663316,
"state": "success",
"serverName": "deepseek"
}
},
{
"role": "tool",
"index": 0,
"tool_call_id": "call_0_379c9e3f-c525-4cde-965c-dea7635f116b",
"content": [
{
"type": "text",
"text": "Execution result:\n\"<html><head></head><body></body></html>\\n<head></head>\\n<body></body>\"\n\nConsole output:\n"
}
],
"extraInfo": {
"created": 1747101663320,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1508,
"completion_tokens": 39,
"total_tokens": 1547,
"prompt_tokens_details": {
"cached_tokens": 1472
},
"prompt_cache_hit_tokens": 1472,
"prompt_cache_miss_tokens": 36
}
}
},
{
"role": "assistant",
"content": "由于页面加载失败或内容未被正确渲染,无法获取 Hacker News 的热门信息。建议稍后重试或检查网络设置。",
"extraInfo": {
"created": 1747101669791,
"state": "success",
"serverName": "deepseek",
"usage": {
"prompt_tokens": 1600,
"completion_tokens": 30,
"total_tokens": 1630,
"prompt_tokens_details": {
"cached_tokens": 1536
},
"prompt_cache_hit_tokens": 1536,
"prompt_cache_miss_tokens": 64
}
}
}
],
"settings": {
"temperature": 0.7,
"enableTools": [
{
"name": "k_navigate",
"description": "Navigate to a URL",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "URL to navigate to"
},
"launchOptions": {
"type": "object",
"description": "PuppeteerJS LaunchOptions. Default null. If changed and not null, browser restarts. Example: { headless: true, args: ['--no-sandbox'] }"
},
"allowDangerous": {
"type": "boolean",
"description": "Allow dangerous LaunchOptions that reduce security. When false, dangerous args like --no-sandbox will throw errors. Default false."
}
},
"required": [
"url"
]
},
"enabled": true
},
{
"name": "k_screenshot",
"description": "Take a screenshot of the current page or a specific element",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name for the screenshot"
},
"selector": {
"type": "string",
"description": "CSS selector for element to screenshot"
},
"width": {
"type": "number",
"description": "Width in pixels (default: 800)"
},
"height": {
"type": "number",
"description": "Height in pixels (default: 600)"
}
},
"required": [
"name"
]
},
"enabled": true
},
{
"name": "k_click",
"description": "Click an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to click"
}
},
"required": [
"selector"
]
},
"enabled": true
},
{
"name": "k_fill",
"description": "Fill out an input field",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for input field"
},
"value": {
"type": "string",
"description": "Value to fill"
}
},
"required": [
"selector",
"value"
]
},
"enabled": true
},
{
"name": "k_select",
"description": "Select an element on the page with Select tag",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to select"
},
"value": {
"type": "string",
"description": "Value to select"
}
},
"required": [
"selector",
"value"
]
},
"enabled": true
},
{
"name": "k_hover",
"description": "Hover an element on the page",
"inputSchema": {
"type": "object",
"properties": {
"selector": {
"type": "string",
"description": "CSS selector for element to hover"
}
},
"required": [
"selector"
]
},
"enabled": true
},
{
"name": "k_evaluate",
"description": "Execute JavaScript in the browser console",
"inputSchema": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "JavaScript code to execute"
}
},
"required": [
"script"
]
},
"enabled": true
}
],
"systemPrompt": "",
"contextLength": 20
},
"mcp": {
"connectionType": "STDIO",
"command": "node",
"args": [
"~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js"
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,9 @@ const corsOptions = {
};
export const app = express();
export const testRouter = express.Router();
app.use(express.json());
app.use(cors(corsOptions));
app.use(morgan('dev'));
app.use(morgan('dev'));
app.use('/test', testRouter);

View File

@ -1,23 +1,14 @@
import { Request, Response } from 'express';
import { app } from './common';
import express, { Request, Response } from 'express';
import { app, testRouter } from './common';
import "./service/news";
import "./openmcp/openmcp";
// 路由为 localhost:3000
app.get('/', (req: Request, res: Response) => {
res.send('<h1>Hello, World!</h1><br><img src="https://picx.zhimg.com/v2-b4251de7d2499e942c7ebf447a90d2eb_l.jpg"/>');
});
app.post('/hello', async (req: Request, res: Response) => {
try {
res.send('message');
} catch (error) {
console.log('error happen in /save-view, ' + error);
res.send('error')
}
})
// 运行在 3000 端口
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {

View File

@ -0,0 +1,54 @@
import { Request, Response } from "express";
import { app } from "../common";
import { executeCommand } from "../util";
import * as fs from 'fs';
import * as path from 'path';
import { execSync, spawnSync } from 'child_process';
import os from 'os';
export const OPENMCP_CLIENT = os.homedir() + '/project/openmcp-client';
async function publishOpenMCP() {
const updateResult = execSync('git pull origin main', { cwd: OPENMCP_CLIENT, env: process.env });
console.log(updateResult.toString());
const rmResult = execSync('rm *.vsix', { cwd: OPENMCP_CLIENT });
console.log(rmResult.toString());
const packageResult = execSync('vsce package', { cwd: OPENMCP_CLIENT });
console.log(packageResult.toString());
// 找到 OPENMCP_CLIENT 下的第一个 vsix 文件,返回绝对路径
const vsixFile = fs.readdirSync(OPENMCP_CLIENT).find(file => file.endsWith('.vsix'));
const vsixPath = path.join(OPENMCP_CLIENT, vsixFile);
return vsixPath;
}
function getLastChangeLog() {
const changelog = fs.readFileSync(path.join(OPENMCP_CLIENT, 'CHANGELOG.md'), { encoding: 'utf-8' });
const newContent = changelog.split('## [main]')[1];
const version = newContent.split('\n')[0];
const updateContent = newContent.split('\n').slice(1).join('\n');
const content = `✴️ openmcp client ${version} 更新内容\n\n` + updateContent.trim() + '\n\n在 vscode/trae/cursor 等编辑器的插件商城搜索【openmcp】就可以下载最新的版本了';
return content
}
app.post('/publish-openmcp-client', async (req: Request, res: Response) => {
try {
const filePath = await publishOpenMCP();
const updateContent = getLastChangeLog();
res.send({
code: 200,
msg: {
vsix: filePath,
content: updateContent
}
})
} catch (error) {
console.log(error);
res.send(error.toString());
}
})

View File

@ -0,0 +1,32 @@
import * as fs from 'fs';
import * as yaml from 'js-yaml';
import * as path from 'path';
const CONFIG_PATH = path.join(__dirname, '../../../config.yaml');
interface McpConfig {
prompt: string;
mcp: {
command?: string;
url?: string;
oauth?: string;
};
settings: {
systemPrompt?: string;
};
}
export function getConfig() {
try {
const fileContent = fs.readFileSync(CONFIG_PATH, 'utf8');
return yaml.load(fileContent);
} catch (error) {
console.error('Error reading config file:', error);
throw error;
}
}
// 使用示例
// const config = getConfig();
// console.log(config.towardsdatascience.prompt);

View File

@ -0,0 +1,195 @@
import { Request, Response } from 'express';
import { app, testRouter } from "../common";
import { createTaskContext } from "./common";
import * as fs from 'fs';
import * as path from 'path';
import { markdownToPlainText, getFormatedTime } from '../util';
export async function getNewsFromTowardsScience() {
const { taskLoop, adapter } = await createTaskContext();
const tools = await adapter.listTools();
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置
const storage = {
messages: [],
settings: {
temperature: 0.7,
enableTools: tools,
systemPrompt: '',
contextLength: 20
}
};
// 本次发出的问题
const message = `
headless https://towardsdatascience.com/tag/editors-pick/ 最热门的前三条信息,这些信息大概率在 main 元素的 ul li 下面。
css class html 广
<main>
["⌨️ 今日份 AI & CS 技术文章分享"]
📌 []
[100]
[]
[]
[URL]`.trim();
// 开启事件循环
await taskLoop.start(storage, message);
// 保存数据到本地
const filename = getFormatedTime();
const savePath = path.join(__dirname, '..', '..', 'runtime-data', 'get-news-from-towards-science', `${filename}.json`);
if (!fs.existsSync(path.dirname(savePath))) {
fs.mkdirSync(path.dirname(savePath));
}
fs.writeFileSync(savePath, JSON.stringify(
{
messages: storage.messages,
settings: storage.settings,
mcp: {
connectionType: 'STDIO',
command: 'node',
args: ['~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js']
}
},
null, 2
));
const lastMessage = storage.messages.at(-1);
console.log('lastMessage', lastMessage);
if (lastMessage?.role === 'assistant') {
return markdownToPlainText(lastMessage.content);
}
return '';
}
export async function getNewsFromHackNews() {
const { taskLoop, adapter } = await createTaskContext();
const tools = await adapter.listTools();
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置
const storage = {
messages: [],
settings: {
temperature: 0.7,
enableTools: tools,
systemPrompt: '',
contextLength: 20
}
};
// 本次发出的问题
const message = `
headless https://news.ycombinator.com/ 最热门的前三条信息。
css class html 广
["⌨️ 今日份 AI & CS 技术文章分享"]
📌 []
[100]
[]
[]
[URL]`.trim();
// 开启事件循环
await taskLoop.start(storage, message);
// 保存数据到本地
const filename = getFormatedTime();
const savePath = path.join(__dirname, '..', '..', 'runtime-data', 'get-news-from-towards-science', `${filename}.json`);
console.log('savePath', savePath);
if (!fs.existsSync(path.dirname(savePath))) {
fs.mkdirSync(path.dirname(savePath), { recursive: true });
}
fs.writeFileSync(savePath, JSON.stringify(
{
messages: storage.messages,
settings: storage.settings,
mcp: {
connectionType: 'STDIO',
command: 'node',
args: ['~/project/Lagrange.RagBot/node/servers/my-browser/dist/browser.js']
}
},
null, 2
));
console.log('write data to ' + savePath);
const lastMessage = storage.messages.at(-1);
console.log('lastMessage', lastMessage);
if (lastMessage?.role === 'assistant') {
return markdownToPlainText(lastMessage.content);
}
return '';
}
app.post('/get-news-from-towards-science', async (req: Request, res: Response) => {
try {
const news = await getNewsFromTowardsScience();
res.send({
code: 200,
msg: news
});
} catch (error) {
res.send(error);
}
});
app.post('/get-news-from-hack-news', async (req: Request, res: Response) => {
try {
const news = await getNewsFromHackNews();
res.send({
code: 200,
msg: news
});
} catch (error) {
res.send(error);
}
});
testRouter.post('/get-news-from-towards-science', async (req: Request, res: Response) => {
try {
res.send({
code: 200,
msg: `以下是整理好的咨询信息,并已翻译为简体中文:
1Uh-Uh, Not Guilty
Uh-Uh, Not Guilty
2Regression Discontinuity Design: How It Works and When to Use It
RDD使
Regression Discontinuity Design
`
});
} catch (error) {
res.send(error);
}
});

94
node/faas/src/util.ts Normal file
View File

@ -0,0 +1,94 @@
import { spawn, SpawnOptions, execSync } from 'child_process';
export function executeCommand(
command: string,
cwd?: string
): Promise<{ stdout: string; stderr: string; code: number | null }> {
return new Promise((resolve, reject) => {
// 自动拆分命令和参数(支持带引号的参数)
const [cmd, ...args] = command.match(/(?:[^\s"']+|['"][^'"]*['"])+/g) || [];
if (!cmd) {
reject(new Error('Empty command'));
return;
}
// 移除参数中的引号(如果存在)
const processedArgs = args.map(arg =>
arg.replace(/^['"](.*)['"]$/, '$1')
);
// 将 cwd 的 ~ 替换为用户的 home 目录
if (cwd && cwd.startsWith('~')) {
cwd = cwd.replace('~', process.env.HOME || '');
}
const options: SpawnOptions = {
cwd,
stdio: 'pipe',
shell: process.platform === 'win32',
env: {
...process.env, // 继承父进程所有环境变量
}
};
const child = spawn(cmd, processedArgs, options);
let stdout = '';
let stderr = '';
child.stdout?.on('data', (data) => {
stdout += data.toString();
});
child.stderr?.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
resolve({
stdout: stdout.trim(),
stderr: stderr.trim(),
code,
});
});
child.on('error', (error) => {
reject(error);
});
});
}
export function markdownToPlainText(markdown: string): string {
// 移除 Markdown 标题符号
let plainText = markdown.replace(/^#+\s*/gm, '');
// 移除 Markdown 链接格式
plainText = plainText.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
// 移除 Markdown 加粗和斜体符号
plainText = plainText.replace(/\*\*([^*]+)\*\*/g, '$1');
plainText = plainText.replace(/\*([^*]+)\*/g, '$1');
// 移除 Markdown 代码块符号
plainText = plainText.replace(/`([^`]+)`/g, '$1');
// 移除 Markdown 分割线
plainText = plainText.replace(/^-{3,}/gm, '');
// 移除多余的空行
plainText = plainText.replace(/\n{3,}/g, '\n\n');
return plainText.trim();
}
export function getFormatedTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
return `${year}_${month}_${day}_${hours}_${minutes}_${seconds}`;
}

View File

@ -1,57 +0,0 @@
import { Request, Response } from 'express';
import { app } from "../common";
import { createTaskContext } from "./common";
export async function getNewsFromTowardsScience() {
const { taskLoop, adapter } = await createTaskContext();
const tools = await adapter.listTools();
// 创建当前事件循环对应的上下文,并且配置当前上下文的设置
const storage = {
messages: [],
settings: {
temperature: 0.7,
enableTools: tools,
systemPrompt: 'you are a clever bot',
contextLength: 20
}
};
// 本次发出的问题
const message = `
headless https://towardsdatascience.com/tag/editors-pick/ 最热门的前三条信息,这些信息大概率在 <main> 元素的 ul li 下面。然后帮我进入这些网站后总结相关信息,文章通常也在 <main> 中,并且整理成一个简单的咨询,返回翻译好的简体中文。比如
K1
{}
{}
广`.trim();
// 开启事件循环
await taskLoop.start(storage, message);
// 打印上下文,最终的回答在 messages.at(-1) 中
console.log(storage.messages);
const lastMessage = storage.messages.at(-1);
if (lastMessage?.role === 'assistant') {
return lastMessage.content;
}
return '';
}
app.post('/get-news-from-towards-science', async (req: Request, res: Response) => {
try {
const news = await getNewsFromTowardsScience();
res.send({
code: 200,
msg: news
});
} catch (error) {
res.send(error);
}
});

View File

@ -161,7 +161,10 @@ async function ensureBrowser({ launchOptions, allowDangerous }: any) {
previousLaunchOptions = launchOptions;
if (!browser) {
const npx_args = { headless: false }
const npx_args = {
headless: false,
args: [ '--proxy-server=127.0.0.1:7890' ]
}
const docker_args = { headless: true, args: ["--no-sandbox", "--single-process", "--no-zygote"] }
browser = await puppeteer.launch(deepMerge(
process.env.DOCKER_CONTAINER ? docker_args : npx_args,
@ -221,7 +224,9 @@ export async function handleToolCall(name: string, args: any): Promise<CallToolR
switch (name) {
case "k_navigate":
await page.goto(args.url);
await page.goto(args.url, {
waitUntil: 'networkidle2'
});
return {
content: [{
type: "text",

6
package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "Lagrange.RagBot",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

70
scripts/qq-text-format.js Normal file
View File

@ -0,0 +1,70 @@
function markdownToPlainText(markdown) {
// 移除 Markdown 标题符号
let plainText = markdown.replace(/^#+\s*/gm, '');
// 移除 Markdown 链接格式
plainText = plainText.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
// 移除 Markdown 加粗和斜体符号
plainText = plainText.replace(/\*\*([^*]+)\*\*/g, '$1');
plainText = plainText.replace(/\*([^*]+)\*/g, '$1');
// 移除 Markdown 代码块符号
plainText = plainText.replace(/`([^`]+)`/g, '$1');
// 移除 Markdown 分割线
plainText = plainText.replace(/^-{3,}/gm, '');
// 移除多余的空行
plainText = plainText.replace(/\n{3,}/g, '\n\n');
return plainText.trim();
}
// 示例用法
const markdown = `以下是整理好的热门文章资讯,已翻译为简体中文:
---
### 1. **我的GPT造型师教会我如何更好地提示**
**文章介绍**
本文探讨了大型语言模型LLM的奇怪行为并通过作者与GPT造型师的互动揭示了如何通过优化提示来更好地利用这些模型文章深入分析了LLM的工作原理和局限性
**文章链接**
\`https://towardsdatascience.com/what-my-gpt-stylist-taught-me-about-prompting-better-inside-the-strange-behavior-of-llms/\`
**作者**
Arielle Caron
**发布时间**
2025年5月9日
---
### 2. **AccentFold论文回顾非洲ASR领域最重要的研究之一**
**文章介绍**
文章回顾了AccentFold论文该研究解决了当前自动语音识别ASR系统在处理非洲口音时的局限性作者详细分析了论文的创新点和实际应用价值
**文章链接**
\`https://towardsdatascience.com/a-review-of-accentfold-one-of-the-most-important-papers-on-african-asr/\`
**作者**
Zaynab Awofeso
**发布时间**
2025年5月9日
---
### 3. **欺骗性数据的危险第二部分基础比例与糟糕的统计**
**文章介绍**
本文是作者关于欺骗性数据系列的第二部分重点讨论了相关性基础比例摘要统计和不确定性如何误导人们文章通过生动的例子和图表帮助读者理解统计陷阱
**文章链接**
\`https://towardsdatascience.com/the-dangers-of-deceptive-data-part-2-base-proportions-and-bad-statistics/\`
**作者**
Murtaza Ali
**发布时间**
2025年5月8日
---
希望这些资讯对您有帮助如果需要进一步了解某篇文章请随时告诉我`;
console.log(markdownToPlainText(markdown));