generated from tailored/router-template
Compare commits
26 Commits
8891b196ba
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 52d37d0679 | |||
| 6a9e847ff1 | |||
| 143cbc877c | |||
| 42da851c37 | |||
| e2d0720698 | |||
| 204165bf73 | |||
| bce94f52a0 | |||
| b807cc9f38 | |||
| a25f7c7eb4 | |||
| e982ddb001 | |||
| 7bc9a06b7c | |||
| d4d6960a6c | |||
| 1220076257 | |||
| 530a4f21f5 | |||
| 71359fba88 | |||
| 29725a8614 | |||
| 32b6e04d6c | |||
| da6d4041ad | |||
| a76b506327 | |||
| 2c6c3dd60d | |||
| d5c2964e0e | |||
| ebe7c9afce | |||
| 75f1d75cae | |||
| 0b6b2fe730 | |||
| ce7cd03cb3 | |||
| a150cbc533 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,3 +15,5 @@ logs
|
||||
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
.turbo
|
||||
17
README.md
17
README.md
@@ -1 +1,16 @@
|
||||
# router app template
|
||||
# 社交路由功能模块
|
||||
|
||||
## 小红书
|
||||
|
||||
自动获取和上传
|
||||
|
||||
[代理浏览器](https://git.xiongxiao.me/media/social-xhs-api-server)
|
||||
|
||||
### 功能
|
||||
|
||||
#### 获取评论
|
||||
|
||||
#### 获取笔记信息
|
||||
|
||||
#### 返回笔记信息
|
||||
|
||||
|
||||
29
package.json
29
package.json
@@ -15,9 +15,11 @@
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 bun --watch src/dev.ts ",
|
||||
"worker": "bun --watch src/task/worker.ts",
|
||||
"worker:server": "bun src/task/worker.ts",
|
||||
"build": "rimraf dist && bun run bun.config.mjs",
|
||||
"test": "tsx test/**/*.ts",
|
||||
"clean": "rm -rf dist",
|
||||
"turbo:build": "turbo run build",
|
||||
"pub": "npm run build && envision pack -p -u",
|
||||
"cmd": "tsx cmd/index.ts "
|
||||
},
|
||||
@@ -34,9 +36,11 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kevisual/code-center-module": "0.0.18",
|
||||
"@kevisual/router": "0.0.13",
|
||||
"@kevisual/use-config": "^1.0.12",
|
||||
"@kevisual/ai": "^0.0.8",
|
||||
"@kevisual/code-center-module": "0.0.23",
|
||||
"@kevisual/context": "^0.0.3",
|
||||
"@kevisual/router": "0.0.23",
|
||||
"@kevisual/use-config": "^1.0.19",
|
||||
"cookie": "^1.0.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"formidable": "^3.5.4",
|
||||
@@ -44,30 +48,31 @@
|
||||
"nanoid": "^5.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/ai-center": "^0.0.3",
|
||||
"@kevisual/app-assistant": "workspace:*",
|
||||
"@kevisual/logger": "^0.0.4",
|
||||
"@kevisual/social-prompts": "workspace:*",
|
||||
"@kevisual/types": "^0.0.9",
|
||||
"@kevisual/use-config": "^1.0.12",
|
||||
"@kevisual/types": "^0.0.10",
|
||||
"@kevisual/use-config": "^1.0.19",
|
||||
"@kevisual/xhs": "workspace:*",
|
||||
"@types/bun": "^1.2.11",
|
||||
"@types/bun": "^1.2.16",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/formidable": "^3.4.5",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.15.3",
|
||||
"bullmq": "^5.51.1",
|
||||
"commander": "^13.1.0",
|
||||
"@types/node": "^24.0.3",
|
||||
"bullmq": "^5.55.0",
|
||||
"commander": "^14.0.0",
|
||||
"concurrently": "^9.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.5.0",
|
||||
"inquire": "^0.4.8",
|
||||
"ioredis": "^5.6.1",
|
||||
"nodemon": "^3.1.10",
|
||||
"pg": "^8.15.6",
|
||||
"openai": "^5.6.0",
|
||||
"pg": "^8.16.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"sequelize": "^6.37.7",
|
||||
"tape": "^5.9.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0"
|
||||
"packageManager": "pnpm@10.12.1"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const unreadUrl = 'https://edith.xiaohongshu.com/api/sns/web/unread_count';
|
||||
const cookie =
|
||||
'a1=19686f83a65uiloc0y7wv79une457hsjh5lt00dpe40000147626;abRequestId=0a794332-4561-5f49-93f7-780b8b028e1f;access-token-creator.xiaohongshu.com=customer.creator.AT-68c517498636784561614544frjvxzj7yu8iewie;agora_session=6a0031373435393132333637323735343733393437313634000000000000;customerClientId=536706778174172;galaxy_creator_session_id=OhpHDDSoADhNEhnH5LLnQpletFLApu1fd91f;galaxy.creator.beaker.session.id=1745912429847011598150;gid=yjKqYfK0qDyYyj2DDSqd4ujxyW9kvxIuT62ddkMWhElyuxq8yDd6hl888q2WYy88j8i80yYD;loadts=1746020512562;sec_poison_id=441c932e-a6ac-4d8d-97ae-beb14adb1929;unread={%22ub%22:%2267eaf1fe000000001202c3ea%22%2C%22ue%22:%226803aa37000000001c0319d8%22%2C%22uc%22:35};web_session=040069b2e9c511ca302086ca253a4bde8b1cd1;webBuild=4.62.3;webId=97e5f097499594cad49aa0bd1a8ed83f;websectiga=3633fe24d49c7dd0eb923edc8205740f10fdb18b25d424d2a2322c6196d2a4ad;x-user-id-creator.xiaohongshu.com=639d86590000000026006076;xsecappid=xhs-pc-web;acw_tc=0a00df6217460205042195762e721fba339a0dbe8e4738b961a5ff15e74619;';
|
||||
'a1=xxxx;abRequestId=0a794332-4561-5f49-93f7-780b8b028e1f;access-token-creator.xiaohongshu.com=customer.creator.AT-68c517498636784561614544frjvxzj7yu8iewie;agora_session=6a0031373435393132333637323735343733393437313634000000000000;customerClientId=536706778174172;galaxy_creator_session_id=OhpHDDSoADhNEhnH5LLnQpletFLApu1fd91f;galaxy.creator.beaker.session.id=1745912429847011598150;gid=yjKqYfK0qDyYyj2DDSqd4ujxyW9kvxIuT62ddkMWhElyuxq8yDd6hl888q2WYy88j8i80yYD;loadts=1746020512562;sec_poison_id=441c932e-a6ac-4d8d-97ae-beb14adb1929;unread={%22ub%22:%2267eaf1fe000000001202c3ea%22%2C%22ue%22:%226803aa37000000001c0319d8%22%2C%22uc%22:35};web_session=040069b2e9c511ca302086ca253a4bde8b1cd1;webBuild=4.62.3;webId=97e5f097499594cad49aa0bd1a8ed83f;websectiga=3633fe24d49c7dd0eb923edc8205740f10fdb18b25d424d2a2322c6196d2a4ad;x-user-id-creator.xiaohongshu.com=639d86590000000026006076;xsecappid=xhs-pc-web;acw_tc=0a00df6217460205042195762e721fba339a0dbe8e4738b961a5ff15e74619;';
|
||||
|
||||
const cookieObj = cookie.split('; ').reduce((acc, item) => {
|
||||
const [key, value] = item.split('=');
|
||||
@@ -17,7 +17,7 @@ const headers = {
|
||||
|
||||
const meUri = '/api/sns/web/v2/user/me';
|
||||
const getSign = async (uri: string, data: any, a1: string, web_session?: string) => {
|
||||
const signs = await fetch('http://light.xiongxiao.me:5006/sign', {
|
||||
const signs = await fetch('http://localhost:5006/sign', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -7,6 +7,7 @@ import path from 'path';
|
||||
const rp = (resolvePath) => {
|
||||
return path.resolve(process.cwd(), resolvePath);
|
||||
};
|
||||
const external = ['jsdom']
|
||||
// bun run src/index.ts --
|
||||
await Bun.build({
|
||||
target: 'node',
|
||||
@@ -16,6 +17,7 @@ await Bun.build({
|
||||
naming: {
|
||||
entry: 'app.mjs',
|
||||
},
|
||||
external: external,
|
||||
|
||||
define: {
|
||||
VERSION: JSON.stringify(pkg.version),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kevisual/xhs-core",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"description": "",
|
||||
"main": "dist/app.mjs",
|
||||
"types": "dist/app.d.ts",
|
||||
|
||||
20
packages/xhs/.gitignore
vendored
20
packages/xhs/.gitignore
vendored
@@ -1 +1,21 @@
|
||||
db-sqlite
|
||||
|
||||
node_modules
|
||||
|
||||
dist
|
||||
|
||||
app.config.json5
|
||||
|
||||
apps.config.json
|
||||
|
||||
deploy.tar.gz
|
||||
cache-file
|
||||
|
||||
/apps
|
||||
|
||||
logs
|
||||
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
.turbo
|
||||
@@ -6,7 +6,7 @@
|
||||
"types": "app.d.ts",
|
||||
"scripts": {
|
||||
"build": "bun run bun.config.mjs",
|
||||
"postbuild": "dts -i src/index.ts -o app.d.ts",
|
||||
"postbuild2": "dts -i src/index.ts -o app.d.ts",
|
||||
"cmd": "tsx src/test/command.ts ",
|
||||
"dts": "dts -i src/index.js -o app.d.ts"
|
||||
},
|
||||
@@ -19,12 +19,12 @@
|
||||
"keywords": [],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.10.0",
|
||||
"packageManager": "pnpm@10.12.1",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@kevisual/use-config": "^1.0.14",
|
||||
"@kevisual/xhs-core": "workspace:*",
|
||||
"@types/node": "^22.15.3"
|
||||
"@kevisual/use-config": "^1.0.19",
|
||||
"@kevisual/xhs-core": "0.0.4",
|
||||
"@types/node": "^24.0.3"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
@@ -33,6 +33,10 @@
|
||||
},
|
||||
"./index": {
|
||||
"import": "./src/index.ts"
|
||||
},
|
||||
"./index.ts": {
|
||||
"import": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
0
packages/xhs/readme.md
Normal file
0
packages/xhs/readme.md
Normal file
@@ -1,15 +1,15 @@
|
||||
import { QueryRouterServer } from '@kevisual/router/browser';
|
||||
import { XhsServices } from '@kevisual/xhs/services/xhs-services.ts';
|
||||
|
||||
import { XhsServices, XhsClient } from '@kevisual/xhs/services/xhs-services.ts';
|
||||
export { XhsServices };
|
||||
export const app = new QueryRouterServer();
|
||||
export const xhsServices = new XhsServices();
|
||||
const cookie =
|
||||
'a1=1969a2df762vy6p46vet3jjpwfvnoce52hge24v0430000640615;abRequestId=48bccb63-a540-5533-8215-546916a6386f;gid=yjKj0JdyjKJ4yjKj0JfiWx4hKJhvKU4Khd9qk84VVUEihdq8IlSd2J888K48Ky28SSqJKYSK;loadts=1746343425888;sec_poison_id=32d8febc-7543-41e7-8a1f-c652d32a1e1a;unread={%22ub%22:%2267f73e21000000001b0384ea%22%2C%22ue%22:%226812f08300000000090166d7%22%2C%22uc%22:32};web_session=040069b6528dbc23c355705e223a4b27b6660a;webBuild=4.62.3;webId=05b45ad626037308d58668196c6af47d;websectiga=8886be45f388a1ee7bf611a69f3e174cae48f1ea02c0f8ec3256031b8be9c7ee;xsecappid=xhs-pc-web;acw_tc=0a00d14717463434250061733e8b2fcd3804e9b06020e1f339ebd8b7e80fc4;';
|
||||
export const xhsServices = new XhsServices(); // Semicolon separated Cookie File
|
||||
|
||||
xhsServices.createRoot({
|
||||
const cookie =
|
||||
'a1=****;abRequestId=f98f27d6-cceb-53d9-a9ef-dbd68786231b;access-token-creator.xiaohongshu.com=customer.creator.AT-68c517518075239703025713copo06lnusenqic1;customer-sso-sid=68c517518075239698553486mipyx4lwk2ac5jaj;customerClientId=716985104518687;galaxy_creator_session_id=wda55FJhoiAWZ8cekT2aGiM9fuztft3mOdRM;galaxy.creator.beaker.session.id=1750438297110093146037;gid=yjWYf8Si4SWjyjWYf8SfSxdCDWU2Id0SWSdMITUki6jv0dq898D40M888JJ88KK8fdKS8jiy;loadts=1750444792784;sec_poison_id=2f592adb-ec34-48cd-a6ce-22b90bd67c3b;unread={%22ub%22:%226852a4100000000022030063%22%2C%22ue%22:%226854e0db00000000100251f6%22%2C%22uc%22:28};web_session=040069b6528dbc23c355980a603a4b3e03bb6a;webBuild=4.68.0;webId=1dbb23b746393db622165a22357897d5;websectiga=10f9a40ba454a07755a08f27ef8194c53637eba4551cf9751c009d9afb564467;x-user-id-creator.xiaohongshu.com=6726cef4000000001c019303;xsecappid=xhs-pc-web;acw_tc=0a00d41117504447915317110e41effee63c597aa77f2c9f0dc1b0a10248f8;';
|
||||
|
||||
export const xhsRootClient: XhsClient = xhsServices.createRoot({
|
||||
cookie,
|
||||
signConfig: {
|
||||
signUrl: 'http://light.xiongxiao.me:5006/sign',
|
||||
// signUrl: 'http://localhost:5006/sign',
|
||||
signUrl: 'http://localhost:5006/sign',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { XhsClient } from './libs/xhs.ts';
|
||||
import { app, xhsServices } from './app.ts';
|
||||
import { app, xhsServices, xhsRootClient, XhsServices } from './app.ts';
|
||||
import './routes/index.ts';
|
||||
|
||||
export { XhsClient, app, xhsServices };
|
||||
export { XhsClient, app, xhsServices, xhsRootClient, XhsServices };
|
||||
|
||||
@@ -10,6 +10,9 @@ const parseComment = (comment: CommonentInfo) => {
|
||||
content: comment.content,
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 格式化评论信息
|
||||
*/
|
||||
export class Parse {
|
||||
static getComment(mention: Mention) {
|
||||
const typeList = ['comment/item', 'mention/comment', 'comment/comment'];
|
||||
|
||||
@@ -38,6 +38,7 @@ export const getSign = async (signInfo: SignInfo, options?: SignOptions): Promis
|
||||
// signUrl = 'http://localhost:5005/sign';
|
||||
// const urlA1 = ''http://light.xiongxiao.me:5006/a1';
|
||||
// const urlA1 = 'http://localhost:5005/a1';
|
||||
// console.log('sign', signUrl);
|
||||
const signs = await fetch(signUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -66,6 +67,9 @@ export class XhsClient extends XhsClientBase {
|
||||
constructor(opts: XhsOptions) {
|
||||
super(opts as any);
|
||||
}
|
||||
setCookie(cookie: string) {
|
||||
this.cookie = cookie;
|
||||
}
|
||||
getApiInfo = getApiInfo;
|
||||
printResult(msg: string, data: any) {
|
||||
if (msg === 'response') {
|
||||
@@ -212,6 +216,7 @@ export class XhsClient extends XhsClientBase {
|
||||
|
||||
try {
|
||||
const response = await this.post(uri, data, { sign: this.sign.bind(this) });
|
||||
console.log('getNoteById response', response, typeof response);
|
||||
// return response['items'][0]['node_card'];
|
||||
return response;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import './mentions/index.ts'
|
||||
import './notes/index.ts'
|
||||
@@ -4,6 +4,14 @@ import { Mention } from '@kevisual/xhs/libs/xhs-type/mention.ts';
|
||||
const sleep = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
export const splitContent = (content: string, maxLength: number = 270) => {
|
||||
const parts: string[] = [];
|
||||
for (let i = 0; i < content.length; i += maxLength) {
|
||||
parts.push(content.slice(i, i + maxLength));
|
||||
}
|
||||
return parts;
|
||||
};
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'mention',
|
||||
@@ -18,6 +26,7 @@ app
|
||||
} else {
|
||||
ctx.body = {
|
||||
unread_count: 0,
|
||||
likes: 0,
|
||||
};
|
||||
}
|
||||
})
|
||||
@@ -75,17 +84,24 @@ app
|
||||
.define(async (ctx) => {
|
||||
const { note_id, comment_id, content } = ctx.query;
|
||||
const client = xhsServices.getClient();
|
||||
// content 小红书最大 300个字内,超过则分割, 分比300小,比如270
|
||||
const textArr: string[] = splitContent(content);
|
||||
const resArr: any[] = [];
|
||||
for (let text of textArr) {
|
||||
const res = await client.postComment({
|
||||
note_id: note_id,
|
||||
comment_id: comment_id,
|
||||
content,
|
||||
content: text,
|
||||
});
|
||||
if (res.code === 0) {
|
||||
ctx.body = res.data;
|
||||
resArr.push(res.data);
|
||||
} else {
|
||||
console.log('添加评论失败', res.code);
|
||||
ctx.throw(res.code, '添加评论失败');
|
||||
}
|
||||
await sleep(2000);
|
||||
}
|
||||
ctx.body = resArr;
|
||||
})
|
||||
.addTo(app);
|
||||
app
|
||||
@@ -109,22 +125,26 @@ app
|
||||
const handleMention: any[] = [];
|
||||
for (const mention of mentionList) {
|
||||
const mention_id = mention.id;
|
||||
const note_id = mention.item_info.id;
|
||||
const note_id = mention.item_info.id; // item_info 是笔记信息
|
||||
const note_userid = mention.item_info.user_info?.userid || '';
|
||||
const note_username = mention.item_info.user_info?.nickname || '';
|
||||
const xsec_token = mention.item_info.xsec_token;
|
||||
let comment: any = Parse.getComment(mention);
|
||||
// console.log('note_id', note_id, 'xsec_token', xsec_token, comment);
|
||||
handleMention.push({
|
||||
mention_id,
|
||||
note_id,
|
||||
note_userid,
|
||||
note_username,
|
||||
xsec_token,
|
||||
comment,
|
||||
mention,
|
||||
});
|
||||
}
|
||||
console.log('获取提及列表成功', res.code, res.data?.message_list?.length);
|
||||
console.log('获取提及列表成功', '[小红书code]', res.code, '提及数量', res.data?.message_list?.length);
|
||||
ctx.body = handleMention;
|
||||
} else {
|
||||
console.log('获取提及列表失败', res.code);
|
||||
console.log('获取提及列表失败', '[小红书code]', res.code);
|
||||
ctx.throw(res.code, '获取提及列表失败');
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,13 +14,16 @@ app
|
||||
key: 'getUnread',
|
||||
});
|
||||
console.log('unredRes', unredRes.body, unredRes.code);
|
||||
|
||||
if (unredRes.code === 200) {
|
||||
const unread_count = unredRes.body.unread_count;
|
||||
const likes = unredRes.body.likes;
|
||||
const unread = unread_count - likes;
|
||||
const mentionRes = await app.call({
|
||||
path: 'mention',
|
||||
key: 'getMention',
|
||||
payload: {
|
||||
num: unread_count,
|
||||
num: unread,
|
||||
},
|
||||
});
|
||||
if (mentionRes.code === 200) {
|
||||
|
||||
14
packages/xhs/src/routes/notes/create-note.ts
Normal file
14
packages/xhs/src/routes/notes/create-note.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { app, xhsServices } from '@kevisual/xhs/app.ts';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'note',
|
||||
key: 'create',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const client = xhsServices.getClient();
|
||||
// const res = await client.getNote({});
|
||||
// if (res.code === 0) {
|
||||
// }
|
||||
})
|
||||
.addTo(app);
|
||||
18
packages/xhs/src/routes/notes/get-note.ts
Normal file
18
packages/xhs/src/routes/notes/get-note.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { app, xhsServices } from '@kevisual/xhs/app.ts';
|
||||
app
|
||||
.route({
|
||||
path: 'note',
|
||||
key: 'getNote',
|
||||
description: '获取笔记详情',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { node_id, xsec_token } = ctx.query;
|
||||
const client = xhsServices.getClient();
|
||||
const res = await client.getNote(node_id, xsec_token);
|
||||
if (res.code === 200) {
|
||||
ctx.body = res.data || {};
|
||||
} else {
|
||||
ctx.throw(`获取笔记失败: ${node_id}`);
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
2
packages/xhs/src/routes/notes/index.ts
Normal file
2
packages/xhs/src/routes/notes/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './create-note.ts'
|
||||
import './get-note.ts'
|
||||
@@ -3,10 +3,12 @@ import { Sequelize } from 'sequelize';
|
||||
// import { createSequelize } from '@kevisual/xhs/services/xhs-db/db.ts';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
|
||||
export { XhsClient };
|
||||
type XhsClientOptions = {
|
||||
key: string;
|
||||
cookie: string;
|
||||
userid?: string;
|
||||
username?: string;
|
||||
signConfig?: {
|
||||
signUrl: string;
|
||||
};
|
||||
@@ -69,7 +71,7 @@ export class XhsServices {
|
||||
|
||||
createRoot(options: Partial<XhsClientOptions>) {
|
||||
options.key = options.key || this.root;
|
||||
return this.createClient(options as XhsClientOptions);
|
||||
return this.createClient(options as XhsClientOptions) as XhsClient;
|
||||
}
|
||||
getKey(key?: string) {
|
||||
if (!key) key = this.root;
|
||||
@@ -88,4 +90,60 @@ export class XhsServices {
|
||||
const xhsClient = this.map.get(this.getKey(key));
|
||||
return xhsClient;
|
||||
}
|
||||
getXhsUserInfo(key?: string) {
|
||||
const xhsClient = this.map.get(this.getKey(key));
|
||||
return {
|
||||
userid: xhsClient?.options?.userid || '',
|
||||
username: xhsClient?.options?.username || '',
|
||||
};
|
||||
}
|
||||
isOwner(user: { username: string; userid: string }, key?: string) {
|
||||
const xhsUserInfo = this.getXhsUserInfo(key);
|
||||
if (!xhsUserInfo.userid || !xhsUserInfo.username) {
|
||||
return false;
|
||||
}
|
||||
return user.userid === xhsUserInfo.userid;
|
||||
}
|
||||
isReplayAi(data: any, key?: string) {
|
||||
const mention = data?.mention || {};
|
||||
const user_info = mention?.comment_info?.target_comment?.user_info || {};
|
||||
if (user_info?.userid) {
|
||||
const xhsUserInfo = this.getXhsUserInfo(key);
|
||||
// 处理用户信息
|
||||
return user_info.userid === xhsUserInfo.userid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
setCookie(cookie: string, key?: string) {
|
||||
const xhsClient = this.map.get(this.getKey(key));
|
||||
if (xhsClient) {
|
||||
xhsClient.options.cookie = cookie;
|
||||
xhsClient.client.setCookie(cookie);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置用户信息
|
||||
* @param user
|
||||
* @param key
|
||||
*/
|
||||
setUserInfo(user: { userid: string; username: string }, key?: string) {
|
||||
const xhsClient = this.map.get(this.getKey(key));
|
||||
if (xhsClient) {
|
||||
xhsClient.options.userid = user.userid;
|
||||
xhsClient.options.username = user.username;
|
||||
}
|
||||
}
|
||||
|
||||
setSignConfig(signConfig: { signUrl: string }, key?: string) {
|
||||
const xhsClient = this.map.get(this.getKey(key));
|
||||
if (xhsClient) {
|
||||
xhsClient.options.signConfig = signConfig;
|
||||
xhsClient.client.signConfig = signConfig;
|
||||
}
|
||||
console.log('setSignConfig', xhsClient?.options?.signConfig);
|
||||
}
|
||||
getSignConfig(key?: string) {
|
||||
const xhsClient = this.map.get(this.getKey(key));
|
||||
return xhsClient?.options?.signConfig || {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { xhsServices, app } from '../index.ts';
|
||||
import { xhsServices, app, xhsRootClient } from '../index.ts';
|
||||
import { useConfig } from '@kevisual/use-config/env';
|
||||
const config = useConfig();
|
||||
import { program } from 'commander';
|
||||
|
||||
xhsRootClient.setCookie(config.XHS_ROOT_COOKIE || '');
|
||||
export { program, xhsServices, app };
|
||||
|
||||
@@ -8,17 +8,27 @@ import util from 'node:util';
|
||||
// });
|
||||
const getNoteById = async () => {
|
||||
const client = xhsServices.getClient();
|
||||
client.getNoteById('68136dab0000000007034c46', 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM').then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
// client.getNoteById('68136dab0000000007034c46', 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM').then((res) => {
|
||||
// console.log(res);
|
||||
// });
|
||||
|
||||
const res = await client.getNoteById('68136dab0000000007034c46', 'LB6fmNfsd0keAQNjh3zOejDC2TVQLGY3zlTZjeRazBZdI=');
|
||||
// console.log(res);
|
||||
};
|
||||
const getNote = async () => {
|
||||
// const id = '68136dab0000000007034c46';
|
||||
// const x = 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM=';
|
||||
const id = '68136dab0000000007034c46';
|
||||
const x = 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM=';
|
||||
const x = 'LB6fmNfsd0keAQNjh3zOejDC2TVQLGY3zlTZjeRazBZdI=';
|
||||
const client = xhsServices.getClient();
|
||||
client.getNote(id, x).then((res) => {
|
||||
const res = await client.getNote(id, x).then((res) => {
|
||||
console.log(util.inspect(res, { depth: null }));
|
||||
return res;
|
||||
});
|
||||
console.log('type res', typeof res);
|
||||
if (res.code === 0) {
|
||||
console.log('desc', res.data.desc);
|
||||
}
|
||||
};
|
||||
program
|
||||
.command('get-note')
|
||||
|
||||
8
packages/xhs/src/test/split-text.ts
Normal file
8
packages/xhs/src/test/split-text.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { splitContent } from '../routes/mentions/mention.ts';
|
||||
|
||||
const content = `这是一个测试内容,长度超过300个字符。34`.repeat(43); // 模拟一个超过300个字符的内容
|
||||
const maxLength = 270; // 设置分割长度为270个字符
|
||||
const parts = splitContent(content, maxLength);
|
||||
console.log('分割后的内容:', parts);
|
||||
// 输出分割后的内容
|
||||
console.log('分割后的内容长度:', parts.map(part => part.length));
|
||||
395
pnpm-lock.yaml
generated
395
pnpm-lock.yaml
generated
@@ -8,15 +8,21 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@kevisual/ai':
|
||||
specifier: ^0.0.8
|
||||
version: 0.0.8
|
||||
'@kevisual/code-center-module':
|
||||
specifier: 0.0.18
|
||||
version: 0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.13)(@kevisual/use-config@1.0.14(dotenv@16.5.0))(ioredis@5.6.1)(pg@8.15.6)(sequelize@6.37.7(pg@8.15.6))
|
||||
specifier: 0.0.23
|
||||
version: 0.0.23(dotenv@16.5.0)
|
||||
'@kevisual/context':
|
||||
specifier: ^0.0.3
|
||||
version: 0.0.3
|
||||
'@kevisual/router':
|
||||
specifier: 0.0.13
|
||||
version: 0.0.13
|
||||
specifier: 0.0.23
|
||||
version: 0.0.23
|
||||
'@kevisual/use-config':
|
||||
specifier: ^1.0.12
|
||||
version: 1.0.14(dotenv@16.5.0)
|
||||
specifier: ^1.0.19
|
||||
version: 1.0.19(dotenv@16.5.0)
|
||||
cookie:
|
||||
specifier: ^1.0.2
|
||||
version: 1.0.2
|
||||
@@ -33,24 +39,24 @@ importers:
|
||||
specifier: ^5.1.5
|
||||
version: 5.1.5
|
||||
devDependencies:
|
||||
'@kevisual/ai-center':
|
||||
specifier: ^0.0.3
|
||||
version: 0.0.3
|
||||
'@kevisual/app-assistant':
|
||||
specifier: workspace:*
|
||||
version: link:packages/app-assistant
|
||||
'@kevisual/logger':
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@kevisual/social-prompts':
|
||||
specifier: workspace:*
|
||||
version: link:packages/social-prompts
|
||||
'@kevisual/types':
|
||||
specifier: ^0.0.9
|
||||
version: 0.0.9
|
||||
specifier: ^0.0.10
|
||||
version: 0.0.10
|
||||
'@kevisual/xhs':
|
||||
specifier: workspace:*
|
||||
version: link:packages/xhs
|
||||
'@types/bun':
|
||||
specifier: ^1.2.11
|
||||
version: 1.2.11
|
||||
specifier: ^1.2.16
|
||||
version: 1.2.16
|
||||
'@types/crypto-js':
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
@@ -61,14 +67,14 @@ importers:
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
'@types/node':
|
||||
specifier: ^22.15.3
|
||||
version: 22.15.3
|
||||
specifier: ^24.0.3
|
||||
version: 24.0.3
|
||||
bullmq:
|
||||
specifier: ^5.51.1
|
||||
version: 5.51.1
|
||||
specifier: ^5.55.0
|
||||
version: 5.55.0
|
||||
commander:
|
||||
specifier: ^13.1.0
|
||||
version: 13.1.0
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0
|
||||
concurrently:
|
||||
specifier: ^9.1.2
|
||||
version: 9.1.2
|
||||
@@ -87,15 +93,18 @@ importers:
|
||||
nodemon:
|
||||
specifier: ^3.1.10
|
||||
version: 3.1.10
|
||||
openai:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.0(ws@8.18.1)(zod@3.25.67)
|
||||
pg:
|
||||
specifier: ^8.15.6
|
||||
version: 8.15.6
|
||||
specifier: ^8.16.2
|
||||
version: 8.16.2
|
||||
rimraf:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
sequelize:
|
||||
specifier: ^6.37.7
|
||||
version: 6.37.7(pg@8.15.6)(sqlite3@5.1.7)
|
||||
version: 6.37.7(pg@8.16.2)(sqlite3@5.1.7)
|
||||
tape:
|
||||
specifier: ^5.9.0
|
||||
version: 5.9.0
|
||||
@@ -128,7 +137,7 @@ importers:
|
||||
version: 5.1.5
|
||||
sequelize:
|
||||
specifier: ^6.37.7
|
||||
version: 6.37.7(pg@8.15.6)(sqlite3@5.1.7)
|
||||
version: 6.37.7(pg@8.16.2)(sqlite3@5.1.7)
|
||||
sqlite3:
|
||||
specifier: ^5.1.7
|
||||
version: 5.1.7
|
||||
@@ -137,8 +146,8 @@ importers:
|
||||
specifier: ^1.0.14
|
||||
version: 1.0.14(dotenv@16.5.0)
|
||||
'@kevisual/xhs-core':
|
||||
specifier: workspace:*
|
||||
version: link:../xhs-core
|
||||
specifier: 0.0.3
|
||||
version: 0.0.3
|
||||
'@types/node':
|
||||
specifier: ^22.15.3
|
||||
version: 22.15.3
|
||||
@@ -212,21 +221,17 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||
|
||||
'@kevisual/ai-center@0.0.3':
|
||||
resolution: {integrity: sha512-GYY8PyOxh8G9VYkm5rBCmOabrdIdN5b5kyx15OaX0fZEi1rG5bzDTL4BTqx5KhurG01HHbXiq32BbCv4ZaSMVA==}
|
||||
'@kevisual/ai@0.0.8':
|
||||
resolution: {integrity: sha512-MvK4U1iWf8hz7lj/+YBQV3qWRRDy42VH8fInKFVxjpEGPGaxfXOMP73C85T4Cf82OGU/fxOayiR0xLi2SyBTLw==}
|
||||
|
||||
'@kevisual/auth@1.0.5':
|
||||
resolution: {integrity: sha512-GwsLj7unKXi7lmMiIIgdig4LwwLiDJnOy15HHZR5gMbyK6s5/uJiMY5RXPB2+onGzTNDqFo/hXjsD2wkerHPVg==}
|
||||
|
||||
'@kevisual/code-center-module@0.0.18':
|
||||
resolution: {integrity: sha512-BfANmxLEO1AwVmqpa6VDgxk//YN8asf1r5jIPpyKDQm12kyyrYgHND9AgGCDRH8lvq6rYVe0svCZXD5b06UPWQ==}
|
||||
peerDependencies:
|
||||
'@kevisual/auth': ^1.0.5
|
||||
'@kevisual/router': ^0.0.7
|
||||
'@kevisual/use-config': ^1.0.8
|
||||
ioredis: ^5.5.0
|
||||
pg: ^8.13.3
|
||||
sequelize: ^6.37.5
|
||||
'@kevisual/code-center-module@0.0.23':
|
||||
resolution: {integrity: sha512-qusUc+BdheJAKFSCOfRJATxKOgE7uqhwMtB8fEI+N9NdbGAX7mshn3Pcq082RFFYrWLMNICsWq/6WczEFamgYw==}
|
||||
|
||||
'@kevisual/context@0.0.3':
|
||||
resolution: {integrity: sha512-cYpqVmoHVjv3NrGMU1e6iyiPDKRtNnK2wOWgbBU5d+8IHuC8VYHTg2GY5bnD/272GK7EYdr9x/c3QgsCIqR2BQ==}
|
||||
|
||||
'@kevisual/db@0.0.1':
|
||||
resolution: {integrity: sha512-FgDS5FzcU5Um2UV6EYQN4+D4R24M6/A7DANMPpvTpRioS94apKsnthZQWNL65e6gPc4ca3ys8zygrF6rd3ZA7Q==}
|
||||
@@ -238,17 +243,31 @@ packages:
|
||||
'@kevisual/load@0.0.6':
|
||||
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
|
||||
|
||||
'@kevisual/router@0.0.13':
|
||||
resolution: {integrity: sha512-raji8aKXr0jigmJVOKBXb5gpstiAuyoIDy9m6SyPf4lRjCU3pspVI1bpscOUCBlaPICo6TLzPQxXhyTvvvtdWw==}
|
||||
'@kevisual/logger@0.0.4':
|
||||
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
|
||||
|
||||
'@kevisual/types@0.0.9':
|
||||
resolution: {integrity: sha512-SDJ7GMbOx7Ghz2kreHqym56ccAJS3t93y+NS0+afTLxcq2+cKcoEy2F8WXEv0mnJ6EsDp5AbA7Jv5TZA1Jbc3A==}
|
||||
'@kevisual/router@0.0.22':
|
||||
resolution: {integrity: sha512-Cqv2vV+hPBHrMMfvWlfDIuNrQcmd260oQZ4S5QR/R4tV35XtMKiseqhnC9uR09oVBJUh+d5rW3YucDDddheeDQ==}
|
||||
|
||||
'@kevisual/router@0.0.23':
|
||||
resolution: {integrity: sha512-W6ehlhAzNe58vq4QeQt2XFoO84Qaw34A0PVOByJsJ2ICj4YKBTclAt+rOAoISCvUeSbeNOIuhUE3sLyPfplzUw==}
|
||||
|
||||
'@kevisual/types@0.0.10':
|
||||
resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==}
|
||||
|
||||
'@kevisual/use-config@1.0.14':
|
||||
resolution: {integrity: sha512-U4fmPFJre+Ph+hdg+EKVaRGxdpD4T4ZoOz5QLEyC6o3ekg8NC4n1i7Myo1ektqATyoG9Y0q/bJdOhiJwnLEt4g==}
|
||||
peerDependencies:
|
||||
dotenv: ^16.4.7
|
||||
|
||||
'@kevisual/use-config@1.0.19':
|
||||
resolution: {integrity: sha512-Q1IH4eMqUe5w6Bq8etoqOSls9FPIy0xwwD3wHf26EsQLZadhccI9qkDuFzP/rFWDa57mwFPEfwbGE5UlqWOCkw==}
|
||||
peerDependencies:
|
||||
dotenv: ^16.4.7
|
||||
|
||||
'@kevisual/xhs-core@0.0.3':
|
||||
resolution: {integrity: sha512-i1prOqtRSqsj4Nkcgw9GNNJXXLMeNnA2oAYX6Xrx/uskwTLoJfSwN41Z9k1XFyZfJ4oOu4ByFKeQLxVi4UkrqA==}
|
||||
|
||||
'@ljharb/resumer@0.1.3':
|
||||
resolution: {integrity: sha512-d+tsDgfkj9X5QTriqM4lKesCkMMJC3IrbPKHvayP00ELx2axdXvDfWkqjxrLXIzGcQzmj7VAUT1wopqARTvafw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -409,8 +428,8 @@ packages:
|
||||
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
'@types/bun@1.2.11':
|
||||
resolution: {integrity: sha512-ZLbbI91EmmGwlWTRWuV6J19IUiUC5YQ3TCEuSHI3usIP75kuoA8/0PVF+LTrbEnVc8JIhpElWOxv1ocI1fJBbw==}
|
||||
'@types/bun@1.2.16':
|
||||
resolution: {integrity: sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ==}
|
||||
|
||||
'@types/cors@2.8.17':
|
||||
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
|
||||
@@ -442,6 +461,9 @@ packages:
|
||||
'@types/node@22.15.3':
|
||||
resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==}
|
||||
|
||||
'@types/node@24.0.3':
|
||||
resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==}
|
||||
|
||||
'@types/validator@13.12.2':
|
||||
resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
|
||||
|
||||
@@ -561,8 +583,11 @@ packages:
|
||||
bullmq@5.51.1:
|
||||
resolution: {integrity: sha512-JEZokH5Sb6p66HRjbfQjPNYuSilDRcB8UREmJzOBqTTaJFza8I92vsBF3J/zmtzd7KVv3dxhZyH9CYSLOJALRA==}
|
||||
|
||||
bun-types@1.2.11:
|
||||
resolution: {integrity: sha512-dbkp5Lo8HDrXkLrONm6bk+yiiYQSntvFUzQp0v3pzTAsXk6FtgVMjdQ+lzFNVAmQFUkPQZ3WMZqH5tTo+Dp/IA==}
|
||||
bullmq@5.55.0:
|
||||
resolution: {integrity: sha512-LKaQZroyXBYSQd/SNP9EcmCZgiZjIImtQHBlnupUvhX1GmmJfIXjn0bf8lek3bvajMUbvVf8FrYdFD0ajAuy0g==}
|
||||
|
||||
bun-types@1.2.16:
|
||||
resolution: {integrity: sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A==}
|
||||
|
||||
cacache@15.3.0:
|
||||
resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==}
|
||||
@@ -622,9 +647,9 @@ packages:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
commander@13.1.0:
|
||||
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
|
||||
engines: {node: '>=18'}
|
||||
commander@14.0.0:
|
||||
resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
@@ -743,6 +768,10 @@ packages:
|
||||
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
detect-libc@2.0.4:
|
||||
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -768,12 +797,19 @@ packages:
|
||||
eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
|
||||
ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
||||
encodeurl@2.0.0:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
encoding@0.1.13:
|
||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||
|
||||
@@ -830,6 +866,13 @@ packages:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
escape-html@1.0.3:
|
||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||
|
||||
etag@1.8.1:
|
||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
|
||||
@@ -869,6 +912,10 @@ packages:
|
||||
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
fresh@2.0.0:
|
||||
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
|
||||
@@ -991,6 +1038,10 @@ packages:
|
||||
http-cache-semantics@4.1.1:
|
||||
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
|
||||
|
||||
http-errors@2.0.0:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-proxy-agent@4.0.1:
|
||||
resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -1242,10 +1293,18 @@ packages:
|
||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-db@1.54.0:
|
||||
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@2.1.35:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mime-types@3.0.1:
|
||||
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1401,9 +1460,25 @@ packages:
|
||||
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
on-finished@2.4.1:
|
||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
openai@5.6.0:
|
||||
resolution: {integrity: sha512-jNH5z+hYAdOMZXyEt0yZ7246s+UZjg2AwFQqkAhZIPPjxNtHHO5mykOefau6FkOqj16aC94MOdJl/rZBcKj/cQ==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
ws: ^8.18.0
|
||||
zod: ^3.23.8
|
||||
peerDependenciesMeta:
|
||||
ws:
|
||||
optional: true
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
own-keys@1.0.1:
|
||||
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1437,31 +1512,34 @@ packages:
|
||||
resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
pg-cloudflare@1.2.5:
|
||||
resolution: {integrity: sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==}
|
||||
pg-cloudflare@1.2.6:
|
||||
resolution: {integrity: sha512-uxmJAnmIgmYgnSFzgOf2cqGQBzwnRYcrEgXuFjJNEkpedEIPBSEzxY7ph4uA9k1mI+l/GR0HjPNS6FKNZe8SBQ==}
|
||||
|
||||
pg-connection-string@2.8.5:
|
||||
resolution: {integrity: sha512-Ni8FuZ8yAF+sWZzojvtLE2b03cqjO5jNULcHFfM9ZZ0/JXrgom5pBREbtnAw7oxsxJqHw9Nz/XWORUEL3/IFow==}
|
||||
|
||||
pg-connection-string@2.9.1:
|
||||
resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
|
||||
|
||||
pg-int8@1.0.1:
|
||||
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
||||
pg-pool@3.9.6:
|
||||
resolution: {integrity: sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==}
|
||||
pg-pool@3.10.1:
|
||||
resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==}
|
||||
peerDependencies:
|
||||
pg: '>=8.0'
|
||||
|
||||
pg-protocol@1.9.5:
|
||||
resolution: {integrity: sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg==}
|
||||
pg-protocol@1.10.2:
|
||||
resolution: {integrity: sha512-Ci7jy8PbaWxfsck2dwZdERcDG2A0MG8JoQILs+uZNjABFuBuItAZCWUNz8sXRDMoui24rJw7WlXqgpMdBSN/vQ==}
|
||||
|
||||
pg-types@2.2.0:
|
||||
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
pg@8.15.6:
|
||||
resolution: {integrity: sha512-yvao7YI3GdmmrslNVsZgx9PfntfWrnXwtR+K/DjI0I/sTKif4Z623um+sjVZ1hk5670B+ODjvHDAckKdjmPTsg==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
pg@8.16.2:
|
||||
resolution: {integrity: sha512-OtLWF0mKLmpxelOt9BqVq83QV6bTfsS0XLegIeAKqKjurRnRKie1Dc1iL89MugmSLhftxw6NNCyZhm1yQFLMEQ==}
|
||||
engines: {node: '>= 16.0.0'}
|
||||
peerDependencies:
|
||||
pg-native: '>=3.0.1'
|
||||
peerDependenciesMeta:
|
||||
@@ -1528,6 +1606,10 @@ packages:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
range-parser@1.2.1:
|
||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
rc@1.2.8:
|
||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||
hasBin: true
|
||||
@@ -1630,6 +1712,10 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
send@1.2.0:
|
||||
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
sequelize-pool@7.1.0:
|
||||
resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -1682,6 +1768,9 @@ packages:
|
||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1767,6 +1856,14 @@ packages:
|
||||
standard-as-callback@2.1.0:
|
||||
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
|
||||
|
||||
statuses@2.0.1:
|
||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
statuses@2.0.2:
|
||||
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
stop-iteration-iterator@1.1.0:
|
||||
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1851,6 +1948,10 @@ packages:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
toidentifier@1.0.1:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
toposort-class@1.0.1:
|
||||
resolution: {integrity: sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==}
|
||||
|
||||
@@ -1907,6 +2008,9 @@ packages:
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
undici-types@7.8.0:
|
||||
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
|
||||
|
||||
unique-filename@1.1.1:
|
||||
resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==}
|
||||
|
||||
@@ -2040,8 +2144,8 @@ packages:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
zod@3.24.2:
|
||||
resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==}
|
||||
zod@3.25.67:
|
||||
resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
|
||||
|
||||
snapshots:
|
||||
|
||||
@@ -2099,26 +2203,41 @@ snapshots:
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@kevisual/ai-center@0.0.3': {}
|
||||
'@kevisual/ai@0.0.8':
|
||||
dependencies:
|
||||
'@kevisual/logger': 0.0.4
|
||||
|
||||
'@kevisual/auth@1.0.5': {}
|
||||
|
||||
'@kevisual/code-center-module@0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.13)(@kevisual/use-config@1.0.14(dotenv@16.5.0))(ioredis@5.6.1)(pg@8.15.6)(sequelize@6.37.7(pg@8.15.6))':
|
||||
'@kevisual/code-center-module@0.0.23(dotenv@16.5.0)':
|
||||
dependencies:
|
||||
'@kevisual/auth': 1.0.5
|
||||
'@kevisual/router': 0.0.13
|
||||
'@kevisual/use-config': 1.0.14(dotenv@16.5.0)
|
||||
'@kevisual/context': 0.0.3
|
||||
'@kevisual/router': 0.0.22
|
||||
'@kevisual/use-config': 1.0.19(dotenv@16.5.0)
|
||||
ioredis: 5.6.1
|
||||
nanoid: 5.1.5
|
||||
pg: 8.15.6
|
||||
sequelize: 6.37.7(pg@8.15.6)(sqlite3@5.1.7)
|
||||
pg: 8.16.2
|
||||
sequelize: 6.37.7(pg@8.16.2)(sqlite3@5.1.7)
|
||||
socket.io: 4.8.1
|
||||
zod: 3.24.2
|
||||
zod: 3.25.67
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- dotenv
|
||||
- ibm_db
|
||||
- mariadb
|
||||
- mysql2
|
||||
- oracledb
|
||||
- pg-hstore
|
||||
- pg-native
|
||||
- snowflake-sdk
|
||||
- sqlite3
|
||||
- supports-color
|
||||
- tedious
|
||||
- utf-8-validate
|
||||
|
||||
'@kevisual/context@0.0.3': {}
|
||||
|
||||
'@kevisual/db@0.0.1': {}
|
||||
|
||||
'@kevisual/dts@0.0.2(typescript@5.8.3)':
|
||||
@@ -2132,18 +2251,35 @@ snapshots:
|
||||
dependencies:
|
||||
eventemitter3: 5.0.1
|
||||
|
||||
'@kevisual/router@0.0.13':
|
||||
'@kevisual/logger@0.0.4': {}
|
||||
|
||||
'@kevisual/router@0.0.22':
|
||||
dependencies:
|
||||
path-to-regexp: 8.2.0
|
||||
selfsigned: 2.4.1
|
||||
|
||||
'@kevisual/types@0.0.9': {}
|
||||
'@kevisual/router@0.0.23':
|
||||
dependencies:
|
||||
path-to-regexp: 8.2.0
|
||||
selfsigned: 2.4.1
|
||||
send: 1.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@kevisual/types@0.0.10': {}
|
||||
|
||||
'@kevisual/use-config@1.0.14(dotenv@16.5.0)':
|
||||
dependencies:
|
||||
'@kevisual/load': 0.0.6
|
||||
dotenv: 16.5.0
|
||||
|
||||
'@kevisual/use-config@1.0.19(dotenv@16.5.0)':
|
||||
dependencies:
|
||||
'@kevisual/load': 0.0.6
|
||||
dotenv: 16.5.0
|
||||
|
||||
'@kevisual/xhs-core@0.0.3': {}
|
||||
|
||||
'@ljharb/resumer@0.1.3':
|
||||
dependencies:
|
||||
'@ljharb/through': 2.3.14
|
||||
@@ -2254,13 +2390,13 @@ snapshots:
|
||||
'@tootallnate/once@1.1.2':
|
||||
optional: true
|
||||
|
||||
'@types/bun@1.2.11':
|
||||
'@types/bun@1.2.16':
|
||||
dependencies:
|
||||
bun-types: 1.2.11
|
||||
bun-types: 1.2.16
|
||||
|
||||
'@types/cors@2.8.17':
|
||||
dependencies:
|
||||
'@types/node': 22.15.3
|
||||
'@types/node': 24.0.3
|
||||
|
||||
'@types/crypto-js@4.2.2': {}
|
||||
|
||||
@@ -2272,7 +2408,7 @@ snapshots:
|
||||
|
||||
'@types/formidable@3.4.5':
|
||||
dependencies:
|
||||
'@types/node': 22.15.3
|
||||
'@types/node': 24.0.3
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
dependencies:
|
||||
@@ -2284,12 +2420,16 @@ snapshots:
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
dependencies:
|
||||
'@types/node': 22.15.3
|
||||
'@types/node': 24.0.3
|
||||
|
||||
'@types/node@22.15.3':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@24.0.3':
|
||||
dependencies:
|
||||
undici-types: 7.8.0
|
||||
|
||||
'@types/validator@13.12.2': {}
|
||||
|
||||
abbrev@1.1.1:
|
||||
@@ -2433,9 +2573,21 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
bun-types@1.2.11:
|
||||
bullmq@5.55.0:
|
||||
dependencies:
|
||||
'@types/node': 22.15.3
|
||||
cron-parser: 4.9.0
|
||||
ioredis: 5.6.1
|
||||
msgpackr: 1.11.2
|
||||
node-abort-controller: 3.1.1
|
||||
semver: 7.7.1
|
||||
tslib: 2.8.1
|
||||
uuid: 9.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
bun-types@1.2.16:
|
||||
dependencies:
|
||||
'@types/node': 24.0.3
|
||||
|
||||
cacache@15.3.0:
|
||||
dependencies:
|
||||
@@ -2523,7 +2675,7 @@ snapshots:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
commander@13.1.0: {}
|
||||
commander@14.0.0: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
@@ -2655,6 +2807,8 @@ snapshots:
|
||||
|
||||
denque@2.1.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
detect-libc@2.0.4: {}
|
||||
|
||||
dezalgo@1.0.4:
|
||||
@@ -2678,10 +2832,14 @@ snapshots:
|
||||
|
||||
eastasianwidth@0.2.0: {}
|
||||
|
||||
ee-first@1.1.1: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
encoding@0.1.13:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
@@ -2696,7 +2854,7 @@ snapshots:
|
||||
engine.io@6.6.4:
|
||||
dependencies:
|
||||
'@types/cors': 2.8.17
|
||||
'@types/node': 22.15.3
|
||||
'@types/node': 24.0.3
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.7.2
|
||||
@@ -2806,6 +2964,10 @@ snapshots:
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-html@1.0.3: {}
|
||||
|
||||
etag@1.8.1: {}
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
|
||||
expand-template@2.0.3: {}
|
||||
@@ -2840,6 +3002,8 @@ snapshots:
|
||||
dezalgo: 1.0.4
|
||||
once: 1.4.0
|
||||
|
||||
fresh@2.0.0: {}
|
||||
|
||||
fs-constants@1.0.0: {}
|
||||
|
||||
fs-minipass@2.1.0:
|
||||
@@ -2978,6 +3142,14 @@ snapshots:
|
||||
http-cache-semantics@4.1.1:
|
||||
optional: true
|
||||
|
||||
http-errors@2.0.0:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
inherits: 2.0.4
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-proxy-agent@4.0.1:
|
||||
dependencies:
|
||||
'@tootallnate/once': 1.1.2
|
||||
@@ -3282,10 +3454,16 @@ snapshots:
|
||||
|
||||
mime-db@1.52.0: {}
|
||||
|
||||
mime-db@1.54.0: {}
|
||||
|
||||
mime-types@2.1.35:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
mime-types@3.0.1:
|
||||
dependencies:
|
||||
mime-db: 1.54.0
|
||||
|
||||
mimic-response@3.1.0: {}
|
||||
|
||||
minimatch@10.0.1:
|
||||
@@ -3466,10 +3644,19 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
object-keys: 1.1.1
|
||||
|
||||
on-finished@2.4.1:
|
||||
dependencies:
|
||||
ee-first: 1.1.1
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
openai@5.6.0(ws@8.18.1)(zod@3.25.67):
|
||||
optionalDependencies:
|
||||
ws: 8.18.1
|
||||
zod: 3.25.67
|
||||
|
||||
own-keys@1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.7
|
||||
@@ -3500,18 +3687,20 @@ snapshots:
|
||||
|
||||
path-to-regexp@8.2.0: {}
|
||||
|
||||
pg-cloudflare@1.2.5:
|
||||
pg-cloudflare@1.2.6:
|
||||
optional: true
|
||||
|
||||
pg-connection-string@2.8.5: {}
|
||||
|
||||
pg-connection-string@2.9.1: {}
|
||||
|
||||
pg-int8@1.0.1: {}
|
||||
|
||||
pg-pool@3.9.6(pg@8.15.6):
|
||||
pg-pool@3.10.1(pg@8.16.2):
|
||||
dependencies:
|
||||
pg: 8.15.6
|
||||
pg: 8.16.2
|
||||
|
||||
pg-protocol@1.9.5: {}
|
||||
pg-protocol@1.10.2: {}
|
||||
|
||||
pg-types@2.2.0:
|
||||
dependencies:
|
||||
@@ -3521,15 +3710,15 @@ snapshots:
|
||||
postgres-date: 1.0.7
|
||||
postgres-interval: 1.2.0
|
||||
|
||||
pg@8.15.6:
|
||||
pg@8.16.2:
|
||||
dependencies:
|
||||
pg-connection-string: 2.8.5
|
||||
pg-pool: 3.9.6(pg@8.15.6)
|
||||
pg-protocol: 1.9.5
|
||||
pg-connection-string: 2.9.1
|
||||
pg-pool: 3.10.1(pg@8.16.2)
|
||||
pg-protocol: 1.10.2
|
||||
pg-types: 2.2.0
|
||||
pgpass: 1.0.5
|
||||
optionalDependencies:
|
||||
pg-cloudflare: 1.2.5
|
||||
pg-cloudflare: 1.2.6
|
||||
|
||||
pgpass@1.0.5:
|
||||
dependencies:
|
||||
@@ -3587,6 +3776,8 @@ snapshots:
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
range-parser@1.2.1: {}
|
||||
|
||||
rc@1.2.8:
|
||||
dependencies:
|
||||
deep-extend: 0.6.0
|
||||
@@ -3727,9 +3918,25 @@ snapshots:
|
||||
|
||||
semver@7.7.1: {}
|
||||
|
||||
send@1.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 2.0.0
|
||||
http-errors: 2.0.0
|
||||
mime-types: 3.0.1
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
sequelize-pool@7.1.0: {}
|
||||
|
||||
sequelize@6.37.7(pg@8.15.6)(sqlite3@5.1.7):
|
||||
sequelize@6.37.7(pg@8.16.2)(sqlite3@5.1.7):
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/validator': 13.12.2
|
||||
@@ -3748,7 +3955,7 @@ snapshots:
|
||||
validator: 13.12.0
|
||||
wkx: 0.5.0
|
||||
optionalDependencies:
|
||||
pg: 8.15.6
|
||||
pg: 8.16.2
|
||||
sqlite3: 5.1.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -3778,6 +3985,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
@@ -3903,6 +4112,10 @@ snapshots:
|
||||
|
||||
standard-as-callback@2.1.0: {}
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
||||
statuses@2.0.2: {}
|
||||
|
||||
stop-iteration-iterator@1.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -4032,6 +4245,8 @@ snapshots:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
toposort-class@1.0.1: {}
|
||||
|
||||
touch@3.1.1: {}
|
||||
@@ -4098,6 +4313,8 @@ snapshots:
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
undici-types@7.8.0: {}
|
||||
|
||||
unique-filename@1.1.1:
|
||||
dependencies:
|
||||
unique-slug: 2.0.2
|
||||
@@ -4186,7 +4403,7 @@ snapshots:
|
||||
|
||||
wkx@0.5.0:
|
||||
dependencies:
|
||||
'@types/node': 22.15.3
|
||||
'@types/node': 24.0.3
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
@@ -4228,4 +4445,4 @@ snapshots:
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
zod@3.24.2: {}
|
||||
zod@3.25.67: {}
|
||||
|
||||
3
src/agent/agent.ts
Normal file
3
src/agent/agent.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { QueryRouterServer } from '@kevisual/router';
|
||||
|
||||
export const agent = new QueryRouterServer();
|
||||
24
src/agent/ai.ts
Normal file
24
src/agent/ai.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
import { BailianProvider } from '@kevisual/ai';
|
||||
import { config } from '../modules/config.ts';
|
||||
|
||||
const createBaiLian = () => {
|
||||
return new BailianProvider({
|
||||
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
model: 'qwen3-235b-a22b',
|
||||
apiKey: config.BAILIAN_API_KEY,
|
||||
});
|
||||
};
|
||||
|
||||
// export const ai = await useContextKey('ai', createBaiLian());
|
||||
export const ai = createBaiLian();
|
||||
|
||||
console.log('Bailian AI initialized with model:', config.BAILIAN_API_KEY);
|
||||
|
||||
export const bailianModel = useContextKey('bailianModel', () => {
|
||||
return {
|
||||
turbo: 'qwen-turbo-2025-04-28',
|
||||
plus: 'qwen-plus-2025-04-28',
|
||||
a22b235: 'qwen3-235b-a22b',
|
||||
};
|
||||
});
|
||||
80
src/agent/analyze/category.ts
Normal file
80
src/agent/analyze/category.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { agent } from '@/agent/agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
import { getJsonFromString } from './content.ts';
|
||||
export type Category = {
|
||||
category: string;
|
||||
description: string;
|
||||
};
|
||||
export const categoryList: Category[] = [
|
||||
{
|
||||
category: 'daily_poetry',
|
||||
description: '获取一篇今日诗词',
|
||||
},
|
||||
{
|
||||
category: 'irony',
|
||||
description: '反讽一下',
|
||||
},
|
||||
{
|
||||
category: 'random_beautiful_text',
|
||||
description: '随机生成一段优美的文字',
|
||||
},
|
||||
{
|
||||
category: 'random_joke',
|
||||
description: '随机生成一个笑话',
|
||||
},
|
||||
{
|
||||
category: '',
|
||||
description: '教教我,怎么做'
|
||||
},
|
||||
{
|
||||
category: 'default',
|
||||
description: '默认分类,无法识别的请求',
|
||||
},
|
||||
];
|
||||
agent
|
||||
.route({
|
||||
path: 'analyze',
|
||||
key: 'category',
|
||||
description: '分析文本内容,判断文本的请求分类。比如,1. 获取一篇今日诗词。2. 反讽一下 3. 随机生成一段优美的文字。',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const text = ctx.query?.text || '';
|
||||
|
||||
const prompt = `
|
||||
请分析以下文本内容,判断文本的请求分类,并返回一个JSON对象。
|
||||
|
||||
识别的分类包括:
|
||||
${categoryList.map((item) => `- ${item.category}: ${item.description}`).join('\n')}
|
||||
|
||||
返回内容示例:
|
||||
\`\`\`json
|
||||
{
|
||||
"category": "daily_poetry"
|
||||
}
|
||||
\`\`\`
|
||||
<context>
|
||||
${text}
|
||||
</context>
|
||||
`;
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((error) => {
|
||||
ctx.throw(500, 'AI 服务错误: ' + error.status);
|
||||
return error;
|
||||
});
|
||||
const content = res.choices?.[0]?.message?.content || '';
|
||||
const jsonResponse = getJsonFromString(content);
|
||||
const category = jsonResponse?.category || 'default';
|
||||
ctx.body = { category };
|
||||
})
|
||||
.addTo(agent);
|
||||
116
src/agent/analyze/cmd.ts
Normal file
116
src/agent/analyze/cmd.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { agent } from '@/agent/agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
import { getJsonFromString } from './content.ts';
|
||||
// import { getJsonFromString } from '@kevisual/ai/src/utils/json.ts';
|
||||
import { logger } from '../logger.ts';
|
||||
|
||||
export const cmdList: {
|
||||
category: string;
|
||||
description?: string;
|
||||
action?: {
|
||||
path?: string;
|
||||
key?: string;
|
||||
};
|
||||
}[] = [
|
||||
{
|
||||
category: '指令夸人',
|
||||
description: `进行夸奖`,
|
||||
action: {
|
||||
path: 'tools',
|
||||
key: 'good-job',
|
||||
},
|
||||
},
|
||||
{
|
||||
category: '指令来了',
|
||||
description: `召唤小助手过来`,
|
||||
action: {
|
||||
path: 'tools',
|
||||
key: 'call-xiaoxiao',
|
||||
},
|
||||
},
|
||||
{
|
||||
category: '指令总结',
|
||||
description: `总结当前的笔记,缩写当前笔记的内容`,
|
||||
action: {
|
||||
path: 'tools',
|
||||
key: 'summarize-note',
|
||||
},
|
||||
},
|
||||
{
|
||||
category: '指令安慰',
|
||||
description: `安慰用户`,
|
||||
action: {
|
||||
path: 'tools',
|
||||
key: 'comfort-user',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
agent
|
||||
.route({
|
||||
path: 'analyze',
|
||||
key: 'cmd',
|
||||
description: '分析文本内容,意图分析,判断对应的指令内容',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
let text = ctx.query?.text || '';
|
||||
if (text.length > 40) {
|
||||
text = text.slice(0, 40).trim();
|
||||
}
|
||||
let result = {
|
||||
category: 'default',
|
||||
};
|
||||
|
||||
const prompt = `
|
||||
请分析<context>包函的内容,判断是否程序运行指令,返回一个JSON对象。
|
||||
识别的分类包括:
|
||||
${cmdList.map((item) => `- ${item.category}: ${item.description}`).join('\n')}
|
||||
|
||||
返回内容示例:
|
||||
\`\`\`json
|
||||
{
|
||||
"category": "daily_poetry"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
分析的内容是
|
||||
<context>
|
||||
${text}
|
||||
</context>
|
||||
`;
|
||||
logger.info('Command analysis prompt:', prompt);
|
||||
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
console.log('Command analysis response:', ans);
|
||||
const json = getJsonFromString(ans);
|
||||
if (!json) {
|
||||
logger.error('Invalid JSON format in response:', ans);
|
||||
ctx.throw(400, 'Invalid JSON format in response');
|
||||
}
|
||||
result = {
|
||||
category: json.category || 'default',
|
||||
};
|
||||
const cmd = cmdList.find((item) => item.category === result.category);
|
||||
logger.info('Command analysis result:', cmd?.category, cmd?.action);
|
||||
ctx.body = { cmd, text: text };
|
||||
})
|
||||
.addTo(agent);
|
||||
95
src/agent/analyze/content.ts
Normal file
95
src/agent/analyze/content.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { agent } from '@/agent/agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
import { logger } from '@/agent/logger.ts';
|
||||
export const getJsonFromString = (str: string) => {
|
||||
// 尝试从字符串中提取JSON对象
|
||||
try {
|
||||
const jsonMatch = str.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
return JSON.parse(jsonMatch[1]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing JSON from string:', error);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
agent
|
||||
.route({
|
||||
path: 'analyze',
|
||||
key: 'content',
|
||||
description: '分析文本内容,意图分析,判断是否需要获取上下文包函小红书的图片,视频,文本。',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const text = ctx.query?.text || '';
|
||||
|
||||
let result = {
|
||||
image: false,
|
||||
video: false,
|
||||
text: false,
|
||||
comment: false,
|
||||
};
|
||||
|
||||
const prompt = `
|
||||
请分析<context>包函的内容,判断是否需要包含小红书的图片、视频或文本信息,返回一个JSON对象,包含三个布尔值:image(是否包含图片)、video(是否包含视频)、text(是否包含小红书文本),comment(是否是评论信息)。
|
||||
如果文本中提到图片或视频,请返回true,否则返回false。
|
||||
|
||||
文本示例:
|
||||
1. "解答一下这个笔记。" , text为true
|
||||
2. "分析一下这个图片。", image为true
|
||||
3. "这个视频介绍的是什么。", video为true
|
||||
4. "评价一下这个评论。", comment为true
|
||||
|
||||
返回内容示例:
|
||||
\`\`\`json
|
||||
{
|
||||
"image": true,
|
||||
"video": false,
|
||||
"text": true,
|
||||
"comment": false
|
||||
}
|
||||
\`\`\`
|
||||
分析的文本的内容是:
|
||||
<context>
|
||||
${text}
|
||||
</context>
|
||||
`;
|
||||
const now = Date.now();
|
||||
console.log('start');
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
|
||||
console.log('end', Date.now() - now, 'ms');
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
if (!ans) {
|
||||
logger.error('Empty response from AI:', res);
|
||||
}
|
||||
const json = getJsonFromString(ans);
|
||||
if (!json) {
|
||||
logger.error('Invalid JSON format in response:', ans);
|
||||
ctx.throw(400, 'Invalid JSON format in response');
|
||||
}
|
||||
result = {
|
||||
image: json.image || false,
|
||||
video: json.video || false,
|
||||
text: json.text || false,
|
||||
comment: json.comment || false,
|
||||
};
|
||||
ctx.body = result;
|
||||
})
|
||||
.addTo(agent);
|
||||
69
src/agent/fix/prompt.ts
Normal file
69
src/agent/fix/prompt.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { agent } from '@/agent/agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
import { logger } from '@/agent/logger.ts';
|
||||
|
||||
const getTagContent = (text: string) => {
|
||||
const match = text.match(/<content>([\s\S]*?)<\/content>/);
|
||||
return match ? match[1].trim() : text;
|
||||
};
|
||||
agent
|
||||
.route({
|
||||
path: 'fix',
|
||||
key: 'xhs',
|
||||
description: '对小红书的提示词进行修正和优化',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const text = ctx.query?.text || '';
|
||||
const now = Date.now();
|
||||
console.log('start');
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: `
|
||||
你是一个提示词优化的专家,请根据用户提供的提示词进行修正和优化,其中用户的提示词返回的要求如果没有或者不明确,请你修正为要求返回的文本在500字以内,如果有,保持原本的要求数字的文本。与此同时,要求内容是纯文本格式,不能是markdown模式,也不包含任何HTML标签或其他格式化内容。
|
||||
|
||||
只对提示词进行优化,并且不需要对内容进行分析或总结。并返回修改后的总的提示词内容。
|
||||
|
||||
示例1. 用户提示词
|
||||
<content>总结笔记</content>
|
||||
优化后的提示词
|
||||
<content>请总结一下这个笔记,要求返回的内容是纯文本格式,字数不超过500字。</content>
|
||||
示例2. 用户提示词
|
||||
<content>分析一下这个图片</content>
|
||||
优化后的提示词
|
||||
<content>请分析一下这个图片,要求返回的内容是纯文本格式,字数不超过500字。</content>
|
||||
示例3. 用户提示词
|
||||
<content>分析一下这个笔记,300字内</content>
|
||||
优化后的提示词
|
||||
<content>请分析一下这个笔记,要求返回的内容是纯文本格式,字数不超过300字。</content>
|
||||
|
||||
|
||||
用户的提示词是
|
||||
<content>
|
||||
${text}
|
||||
</content>
|
||||
`,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
|
||||
console.log('end', Date.now() - now, 'ms');
|
||||
console.log('AI response:', res);
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
if (!ans) {
|
||||
logger.error('Empty response from AI:', res);
|
||||
}
|
||||
ctx.body = getTagContent(ans);
|
||||
})
|
||||
.addTo(agent);
|
||||
14
src/agent/index.ts
Normal file
14
src/agent/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { agent } from './agent.ts';
|
||||
import './analyze/content.ts';
|
||||
import './analyze/category.ts';
|
||||
import './analyze/cmd.ts';
|
||||
|
||||
import './fix/prompt.ts';
|
||||
|
||||
import './xhs.ts';
|
||||
import './tools/kuaren.ts';
|
||||
import './tools/call-xiaoxiao.ts';
|
||||
import './tools/summarize-note.ts';
|
||||
import './tools/comfort-user.ts';
|
||||
|
||||
export { agent };
|
||||
3
src/agent/logger.ts
Normal file
3
src/agent/logger.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Logger } from '@kevisual/logger';
|
||||
|
||||
export const logger = new Logger();
|
||||
19
src/agent/test/analyze.ts
Normal file
19
src/agent/test/analyze.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { agent } from '../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text1 = '解答一下这个笔记。';
|
||||
const text2 = '分析一下这个图片';
|
||||
const text3 = '这个视频介绍的是什么';
|
||||
const text4 = '评价一下这个评论。';
|
||||
const text5 = '关于这个评论。';
|
||||
const text6 = '1+1=';
|
||||
const res = await agent.call({
|
||||
path: 'analyze',
|
||||
key: 'content',
|
||||
payload: {
|
||||
text: text6,
|
||||
},
|
||||
});
|
||||
console.log('analyze content res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
21
src/agent/test/category.ts
Normal file
21
src/agent/test/category.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { agent } from '../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text1 = '解答一下这个笔记。';
|
||||
const text2 = '分析一下这个图片';
|
||||
const text3 = '这个视频介绍的是什么';
|
||||
const text4 = '评价一下这个评论。';
|
||||
const text5 = '关于这个评论。';
|
||||
const text6 = '1+1=';
|
||||
const text7 = '反讽一下';
|
||||
const text8 = '随机生成一段优美的文字';
|
||||
const res = await agent.call({
|
||||
path: 'analyze',
|
||||
key: 'category',
|
||||
payload: {
|
||||
text: text1,
|
||||
},
|
||||
});
|
||||
console.log('analyze category res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
18
src/agent/test/cmd.ts
Normal file
18
src/agent/test/cmd.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { agent } from '../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text1 = '夸一下这个人。';
|
||||
const text2 = '指令这个人很不错';
|
||||
const text3 = '指令夸人, 这个人写代码写的非常好';
|
||||
const text4 = '我想飞';
|
||||
const text5 = '指令 笔记 安慰';
|
||||
const res = await agent.call({
|
||||
path: 'analyze',
|
||||
key: 'cmd',
|
||||
payload: {
|
||||
text: text5
|
||||
},
|
||||
});
|
||||
console.log('analyze category res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
0
src/agent/test/common.ts
Normal file
0
src/agent/test/common.ts
Normal file
19
src/agent/test/prompt-fix.ts
Normal file
19
src/agent/test/prompt-fix.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// 其中回复的要求是以纯文本,具体的内容在<content></content>当中
|
||||
import { agent } from '../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text = '请总结一下这个笔记。';
|
||||
const text2 = '告诉我1+1的值';
|
||||
const text3 = 'html和css的大纲是什么?';
|
||||
const text4 = '1+1=';
|
||||
const text5 = '请分析一下这个图片,300字内';
|
||||
const res = await agent.call({
|
||||
path: 'fix',
|
||||
key: 'xhs',
|
||||
payload: {
|
||||
text: text5,
|
||||
},
|
||||
});
|
||||
console.log('fix xhs res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
16
src/agent/test/tools/kuaren.ts
Normal file
16
src/agent/test/tools/kuaren.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { agent } from '../../index.ts';
|
||||
|
||||
const main = async () => {
|
||||
const text1 = '你长得真好看啊';
|
||||
const text2 = '你说话是撒了魔法金粉吗?听一句我灵魂都被净化了!';
|
||||
|
||||
const res = await agent.call({
|
||||
path: 'tools',
|
||||
key: 'good-job',
|
||||
payload: {
|
||||
text: text2,
|
||||
},
|
||||
});
|
||||
console.log('good job res', res.code, 'content', res.body);
|
||||
};
|
||||
main();
|
||||
15
src/agent/tools/call-xiaoxiao.ts
Normal file
15
src/agent/tools/call-xiaoxiao.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { agent } from '../agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
|
||||
const pickGoodJobPrompt = `
|
||||
`;
|
||||
|
||||
agent
|
||||
.route({
|
||||
path: 'tools',
|
||||
key: 'call-xiaoxiao',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = '来了,来了。';
|
||||
})
|
||||
.addTo(agent);
|
||||
53
src/agent/tools/comfort-user.ts
Normal file
53
src/agent/tools/comfort-user.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { agent } from '../agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
|
||||
agent
|
||||
.route({
|
||||
path: 'tools',
|
||||
key: 'comfort-user',
|
||||
})
|
||||
.define(async (ctx: any) => {
|
||||
const text = ctx?.query?.text || '';
|
||||
const note = ctx?.query?.note || '';
|
||||
if (!text) {
|
||||
ctx.throw('请提供要安慰的内容');
|
||||
}
|
||||
|
||||
const prompt = `
|
||||
用户感觉到不开心,请根据以下要求生成一段安慰的话:
|
||||
要求
|
||||
0. 如果没有必要安慰,直接返回 "你好,你好,我来了"。
|
||||
1. 内容要简洁明了,突出重点。
|
||||
2. 使用口语化的表达方式,易于理解。
|
||||
3. 安慰内容要有逻辑性,能够清晰地传达信息。
|
||||
4. 如果有必要,可以使用一些比喻或形象的表达方式来增强安慰的效果。
|
||||
5. 安慰内容要与原文内容相关,不能脱离主题。
|
||||
6. 200字以内。
|
||||
7. 不要包含除开安慰内容以外的其他内容。
|
||||
8. 其他要求:
|
||||
${text ? text : '无'}
|
||||
|
||||
当前的笔记内容是:
|
||||
${note}
|
||||
`;
|
||||
|
||||
const res = await ai.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
);
|
||||
const pickRes = res.choices[0]?.message?.content || '';
|
||||
if (!pickRes) {
|
||||
ctx.throw('AI 没有返回任何内容,请稍后再试');
|
||||
}
|
||||
// 返回总结内容
|
||||
ctx.body = pickRes.trim();
|
||||
})
|
||||
.addTo(agent);
|
||||
177
src/agent/tools/kuaren.ts
Normal file
177
src/agent/tools/kuaren.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { agent } from '../agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
const kuarenPrompt = `### 浮夸夸人
|
||||
|
||||
**核心要求**:
|
||||
⚠️ 用词极致夸张| ⚠️ 比喻突破天际| ⚠️ 语气充满崇拜| ⚠️ 营造“凡人 vs 神仙”对比感
|
||||
|
||||
---
|
||||
|
||||
#### **夸人维度 & 浮夸话术示例**
|
||||
|
||||
1. **颜值/气质类**
|
||||
|
||||
- ✨ **例句**:"你这张脸是上帝亲手雕的吧?下凡辛苦了!"
|
||||
- ✨ **关键词**:女娲毕设、建模脸、自带滤镜、呼吸都带仙气
|
||||
|
||||
2. **才华/能力类**
|
||||
|
||||
- ✨ **例句**:"你这大脑是装了个量子计算机吗?!建议直接保送诺贝尔奖!"
|
||||
- ✨ **关键词**:人类天花板、降维打击、天才操作、教科书成精
|
||||
|
||||
3. **性格/情商类**
|
||||
|
||||
- ✨ **例句**:"你说话是撒了魔法金粉吗?听一句我灵魂都被净化了!"
|
||||
- ✨ **关键词**:人间充电宝、社交天花板、灵魂按摩师、情商天花板
|
||||
|
||||
4. **细节/小事类**(_重点:把小事吹成神迹_)
|
||||
- ✨ **例句**:"你刚刚递咖啡的姿势,直接拍成广告能救活整个咖啡行业!"
|
||||
- ✨ **关键词**:随手拯救世界、文艺复兴级操作、人类文明之光
|
||||
|
||||
---
|
||||
|
||||
#### **浮夸技巧工具箱**
|
||||
|
||||
✅ **宇宙级比喻**:
|
||||
|
||||
> “你这创意是偷了宙斯的闪电吧?!”
|
||||
> “你的存在让地球自转加速了 0.1 秒!”
|
||||
|
||||
✅ **玄幻修辞法**:
|
||||
|
||||
> “建议科学家把你列入未解之谜!”
|
||||
> “你一笑,北极极光都暗淡了!”
|
||||
|
||||
✅ **凡尔赛对比**:
|
||||
|
||||
> “别人 XX 叫努力,你 XX 叫刷新人类极限!”
|
||||
> “你这水平还谦虚?让普通人怎么活啊?!”
|
||||
|
||||
✅ **动作加持**(_配合文字使用效果翻倍_):
|
||||
|
||||
> “给大佬递茶.jpg 🍵”
|
||||
> “跪着听讲.gif 🙇♂️”
|
||||
|
||||
---
|
||||
|
||||
#### **示例输出**
|
||||
|
||||
💥 **场景 1**(对方随手画了张小涂鸦)
|
||||
|
||||
> “这线条!这配色!达芬奇转世没你画得灵!!建议卢浮宫连夜来收购!!”
|
||||
|
||||
💥 **场景 2**(对方讲了个冷笑话)
|
||||
|
||||
> “你这幽默感是黑洞做的吗?!我笑到平行宇宙都裂开了!!🌌”
|
||||
|
||||
💥 **场景 3**(对方帮忙解决了小问题)
|
||||
|
||||
> “你是雅典娜派来的救世主吧?!这波操作够我刻成碑传家!!🗿”
|
||||
|
||||
#### 当前的场景是
|
||||
`;
|
||||
|
||||
const pickGoodJobPrompt = `对提供的文字,提取单个的夸奖内容,并丰富为纯口语化模式,同时在括号中添加对应的姿态语言描述,同时添加了括号中的姿态语言描述,使其更具临场感和情感色彩。
|
||||
|
||||
要求:
|
||||
1. 只返回单个的一条夸奖内容,不能有其他内容。
|
||||
2. 夸奖内容要口语化,富有情感色彩。
|
||||
3. 姿态语言描述要符合夸奖内容,且要在括号中描述。
|
||||
4. 夸奖内容要有夸张的比喻和形容词,突出对方的优点和成就。
|
||||
5. 夸奖内容要让人感到被认可和赞赏,能够激励对方。
|
||||
6. 不要返回任何其他内容或解释,只返回夸奖内容。
|
||||
7. 夸奖内容要简洁明了,易于理解,篇幅不易过长。
|
||||
|
||||
当前文字是:
|
||||
|
||||
`;
|
||||
|
||||
agent
|
||||
.route({
|
||||
path: 'tools',
|
||||
key: 'pick-good-job',
|
||||
description: '对用户的内容进行夸奖后,提取出夸奖的内容',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
let { text } = ctx.query;
|
||||
if (!text) {
|
||||
text = '真厉害啊';
|
||||
}
|
||||
|
||||
const prompt = `${pickGoodJobPrompt} ${text}`;
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
if (!ans) {
|
||||
ctx.throw(500, 'AI response is empty');
|
||||
}
|
||||
ctx.body = ans;
|
||||
})
|
||||
.addTo(agent);
|
||||
agent
|
||||
.route({
|
||||
path: 'tools',
|
||||
key: 'good-job',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
let { text } = ctx.query;
|
||||
if (!text) {
|
||||
text = '作者发了一篇好的文章';
|
||||
}
|
||||
const prompt = `${kuarenPrompt} ${text}`;
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error('AI service error:', err.status);
|
||||
ctx.throw(500, 'AI service error: ' + err.status);
|
||||
return err;
|
||||
});
|
||||
const ans = res.choices[0]?.message?.content || '';
|
||||
if (!ans) {
|
||||
ctx.throw(500, 'AI response is empty');
|
||||
}
|
||||
// ctx.body = ans;
|
||||
// console.log('AI response:', ans);
|
||||
const resPick = await agent.call({
|
||||
path: 'tools',
|
||||
key: 'pick-good-job',
|
||||
payload: {
|
||||
text: ans,
|
||||
},
|
||||
});
|
||||
if (resPick.code !== 200) {
|
||||
ctx.throw(500, 'AI pick good job error: ' + resPick.message);
|
||||
return;
|
||||
}
|
||||
const pickAns = resPick.body || '';
|
||||
if (!pickAns) {
|
||||
ctx.throw(500, 'AI pick good job response is empty');
|
||||
}
|
||||
ctx.body = pickAns;
|
||||
})
|
||||
.addTo(agent);
|
||||
53
src/agent/tools/summarize-note.ts
Normal file
53
src/agent/tools/summarize-note.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { agent } from '../agent.ts';
|
||||
import { ai } from '../ai.ts';
|
||||
|
||||
agent
|
||||
.route({
|
||||
path: 'tools',
|
||||
key: 'summarize-note',
|
||||
})
|
||||
.define(async (ctx: any) => {
|
||||
const text = ctx?.query?.text || '';
|
||||
const note = ctx?.query?.note || '';
|
||||
if (!text) {
|
||||
ctx.throw('请提供要总结的笔记内容');
|
||||
}
|
||||
|
||||
const prompt = `
|
||||
请根据以下内容生成一段总结:
|
||||
要求
|
||||
0. 如果没有必要总结,直接返回 "当前内容不需要总结"。
|
||||
1. 总结内容要简洁明了,突出重点。
|
||||
2. 使用口语化的表达方式,易于理解。
|
||||
3. 总结内容要有逻辑性,能够清晰地传达信息。
|
||||
4. 如果有必要,可以使用一些比喻或形象的表达方式来增强总结的效果。
|
||||
5. 总结内容要与原文内容相关,不能脱离主题。
|
||||
6. 250字以内。
|
||||
7. 不要包含除开总结内容以外的其他内容。
|
||||
8. 其他要求:
|
||||
${text ? text : '无'}
|
||||
|
||||
当前的笔记内容是:
|
||||
${note}
|
||||
`;
|
||||
|
||||
const res = await ai.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
);
|
||||
const pickRes = res.choices[0]?.message?.content || '';
|
||||
if (!pickRes) {
|
||||
ctx.throw('AI 没有返回任何内容,请稍后再试');
|
||||
}
|
||||
// 返回总结内容
|
||||
ctx.body = pickRes.trim();
|
||||
})
|
||||
.addTo(agent);
|
||||
105
src/agent/xhs.ts
Normal file
105
src/agent/xhs.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import { agent } from './agent.ts';
|
||||
import { ai } from './ai.ts';
|
||||
import { logger } from './logger.ts';
|
||||
/**
|
||||
* 清除文本中的@信息
|
||||
* @param text
|
||||
*/
|
||||
const clearAtInfo = (text: string = '') => {
|
||||
const newText = text.replace(/@[\u4e00-\u9fa5\w]+/g, '').replace(/#.*?#/g, '');
|
||||
return newText.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* 从文本中提取标签信息, 如 #标签
|
||||
* @param text
|
||||
*/
|
||||
export const pickTagsInfo = (text: string = '') => {
|
||||
if (!text) {
|
||||
return {
|
||||
tags: [],
|
||||
text: '',
|
||||
};
|
||||
}
|
||||
const _tags = text.match(/#([\u4e00-\u9fa5\w]+)/g) || [];
|
||||
const validTags = _tags.map((tag) => tag.replace(/#/g, '')).filter((tag) => tag.trim() !== '');
|
||||
const noTagsText = text.replace(/#([\u4e00-\u9fa5\w]+)/g, '').trim();
|
||||
return {
|
||||
tags: validTags,
|
||||
text: noTagsText,
|
||||
};
|
||||
};
|
||||
|
||||
agent
|
||||
.route({
|
||||
path: 'xhs',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { text = '', note = '', hasNote } = ctx.query || {};
|
||||
const id = nanoid();
|
||||
const no_at_text = clearAtInfo(text);
|
||||
const pickNote = pickTagsInfo(note);
|
||||
const no_tags_text = pickNote.text;
|
||||
const some_text = no_at_text.length > 20 ? no_at_text.slice(0, 20) : no_at_text;
|
||||
const hasCmd = some_text.includes('指令');
|
||||
if (hasCmd) {
|
||||
const analyzeRes = await agent.call({
|
||||
path: 'analyze',
|
||||
key: 'cmd',
|
||||
payload: {
|
||||
text: no_at_text,
|
||||
},
|
||||
});
|
||||
if (analyzeRes.code === 200) {
|
||||
const cmd = analyzeRes.body?.cmd;
|
||||
if (cmd) {
|
||||
const res = await agent.call({
|
||||
...cmd.action,
|
||||
payload: {
|
||||
text: no_at_text,
|
||||
note: no_tags_text,
|
||||
hasNote: hasNote || false,
|
||||
},
|
||||
});
|
||||
ctx.body = res.body || '';
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.error('指令分析错误:', analyzeRes.message);
|
||||
}
|
||||
}
|
||||
const resFix = await agent.call({
|
||||
path: 'fix',
|
||||
key: 'xhs',
|
||||
payload: {
|
||||
text: no_at_text,
|
||||
},
|
||||
});
|
||||
if (resFix.code !== 200) {
|
||||
ctx.throw(500, 'AI 小红书prompt优化错误: ' + resFix.message);
|
||||
return;
|
||||
} else {
|
||||
console.log('小红书优化的文本', resFix.body);
|
||||
}
|
||||
const prompt_text = resFix.body || '';
|
||||
const res = await ai
|
||||
.chat(
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
content: prompt_text,
|
||||
},
|
||||
],
|
||||
{
|
||||
// @ts-ignore
|
||||
enable_thinking: false,
|
||||
},
|
||||
)
|
||||
.catch((error) => {
|
||||
ctx.throw(500, 'AI 服务错误: ' + error.status);
|
||||
return error;
|
||||
});
|
||||
ctx.body = res.choices?.[0]?.message?.content || '';
|
||||
})
|
||||
.addTo(agent);
|
||||
@@ -1,5 +1,5 @@
|
||||
import { App } from '@kevisual/router';
|
||||
import { useContextKey } from '@kevisual/use-config/context';
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
|
||||
const init = () => {
|
||||
return new App();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useConfig } from '@kevisual/use-config/env';
|
||||
|
||||
export const config = useConfig();
|
||||
|
||||
export const isDev = config.ENV === 'development';
|
||||
|
||||
3
src/modules/notify.ts
Normal file
3
src/modules/notify.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const notify = () => {
|
||||
//
|
||||
};
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Redis } from 'ioredis';
|
||||
import { config } from './config.ts';
|
||||
import { useContextKey } from '@kevisual/use-config/context';
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
|
||||
type initRedisOpts = {
|
||||
onConnect?: () => void; // 连接成功的回调函数
|
||||
onError?: (msg?: any) => void; // 连接断开的回调函数
|
||||
const redisConfig = {
|
||||
host: config.REDIS_HOST || 'localhost',
|
||||
port: parseInt(config.REDIS_PORT || '6379'),
|
||||
password: config.REDIS_PASSWORD,
|
||||
};
|
||||
const initRedis = (config?: any, options?: initRedisOpts) => {
|
||||
const redis = new Redis({
|
||||
host: config?.REDIS_HOST || 'localhost', // Redis 服务器的主机名或 IP 地址
|
||||
port: config?.REDIS_PORT || 6379, // Redis 服务器的端口号
|
||||
export const createRedisClient = (options = {}) => {
|
||||
const redisClient = new Redis({
|
||||
host: 'localhost', // Redis 服务器的主机名或 IP 地址
|
||||
port: 6379, // Redis 服务器的端口号
|
||||
// password: 'your_password', // Redis 的密码 (如果有)
|
||||
db: 0, // 要使用的 Redis 数据库索引 (0-15)
|
||||
keyPrefix: '', // key 前缀
|
||||
@@ -18,27 +19,20 @@ const initRedis = (config?: any, options?: initRedisOpts) => {
|
||||
return Math.min(times * 50, 2000); // 每次重试时延迟增加
|
||||
},
|
||||
maxRetriesPerRequest: null, // 允许请求重试的次数 (如果需要无限次重试)
|
||||
...config, // 其他配置项
|
||||
...redisConfig,
|
||||
...options,
|
||||
});
|
||||
// 监听连接事件
|
||||
redis.on('connect', () => {
|
||||
if (options?.onConnect) {
|
||||
options.onConnect();
|
||||
console.log('Redis 连接成功');
|
||||
}
|
||||
redisClient.on('connect', () => {
|
||||
console.log('Redis client connected successfully');
|
||||
});
|
||||
|
||||
redis.on('error', (err) => {
|
||||
console.error('Redis 连接错误', err);
|
||||
if (options?.onError) {
|
||||
options.onError(err);
|
||||
}
|
||||
redisClient.on('error', (err) => {
|
||||
console.error('Redis client error:', err);
|
||||
});
|
||||
return redis;
|
||||
return redisClient;
|
||||
};
|
||||
// 配置 Redis 连接
|
||||
export const redis = useContextKey('redis', () => initRedis(config));
|
||||
export const redis = useContextKey('redis', () => createRedisClient());
|
||||
|
||||
// 初始化 Redis 客户端
|
||||
export const redisPublisher = new Redis(); // 用于发布消息
|
||||
export const redisSubscriber = new Redis(); // 用于订阅消息
|
||||
// export const redisPublisher = createRedisClient(); // 用于发布消息
|
||||
// export const redisSubscriber = createRedisClient(); // 用于订阅消息
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Sequelize } from 'sequelize';
|
||||
import { config } from './config.ts';
|
||||
import { useContextKey } from '@kevisual/use-config/context';
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
|
||||
export type PostgresConfig = {
|
||||
postgres: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { taskApp, queue, xhsApp } from '../task.ts';
|
||||
import { agent } from '@/agent/index.ts';
|
||||
import { taskApp, queue, xhsApp, xhsServices } from '../task.ts';
|
||||
import { random, omit } from 'lodash-es';
|
||||
import util from 'node:util';
|
||||
|
||||
@@ -10,6 +11,7 @@ taskApp
|
||||
.route({
|
||||
path: 'task',
|
||||
key: 'getUnread',
|
||||
description: '获取未读提及消息',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const res = await xhsApp.call({
|
||||
@@ -19,18 +21,20 @@ taskApp
|
||||
if (res.code === 200) {
|
||||
const data = res.body;
|
||||
const unread_count = data.unread_count;
|
||||
if (unread_count > 0) {
|
||||
const likes = data.likes;
|
||||
const unread = unread_count - likes;
|
||||
if (unread > 0) {
|
||||
queue.add(
|
||||
'mention',
|
||||
{
|
||||
path: 'task',
|
||||
key: 'getMention',
|
||||
payload: {
|
||||
unread_count,
|
||||
unread_count: unread,
|
||||
},
|
||||
},
|
||||
{
|
||||
attempts: 3,
|
||||
attempts: 1,
|
||||
delay: 0,
|
||||
removeOnComplete: true,
|
||||
removeOnFail: {
|
||||
@@ -40,7 +44,7 @@ taskApp
|
||||
);
|
||||
}
|
||||
ctx.body = {
|
||||
job: unread_count,
|
||||
job: unread,
|
||||
};
|
||||
}
|
||||
})
|
||||
@@ -50,6 +54,7 @@ taskApp
|
||||
.route({
|
||||
path: 'task',
|
||||
key: 'getMention',
|
||||
description: '获取提及消息',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { unread_count } = ctx.query;
|
||||
@@ -61,11 +66,9 @@ taskApp
|
||||
num: unread_count,
|
||||
},
|
||||
});
|
||||
console.log('mentionRes', mentionRes.body);
|
||||
if (mentionRes.code === 200) {
|
||||
let data = mentionRes.body || [];
|
||||
// data = data.map((item) => omit(item, 'mention'));
|
||||
console.log('queryMention', util.inspect(data, { depth: 10 }));
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i];
|
||||
queue.add(
|
||||
@@ -86,7 +89,6 @@ taskApp
|
||||
},
|
||||
},
|
||||
);
|
||||
console.log('add mention task', item);
|
||||
await sleep(200);
|
||||
}
|
||||
}
|
||||
@@ -94,9 +96,7 @@ taskApp
|
||||
path: 'mention',
|
||||
key: 'postRead',
|
||||
});
|
||||
console.log('postRead', postRead.body);
|
||||
}
|
||||
await sleep(1000);
|
||||
ctx.body = {
|
||||
job: unread_count,
|
||||
};
|
||||
@@ -109,16 +109,65 @@ taskApp
|
||||
key: 'ai',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const data = ctx.query.data;
|
||||
const data = ctx.query.data; // 为提及的相关信息
|
||||
const note_id = data.note_id;
|
||||
const note_userid = data.note_userid;
|
||||
const note_username = data.note_username;
|
||||
|
||||
// 检测是这个用户的username的笔记,如果是的话,需要有at的用户信息才继续。
|
||||
const isOwner = xhsServices.isOwner({ username: note_username, userid: note_userid });
|
||||
const isReplayAi = xhsServices.isReplayAi(data);
|
||||
const xsec_token = data.xsec_token;
|
||||
console.log(data);
|
||||
const comment_id = data.comment.comment_id;
|
||||
const content = data.comment?.content || 'test';
|
||||
let content: string = data.comment?.content || 'test';
|
||||
const postData = {
|
||||
note_id,
|
||||
content,
|
||||
comment_id,
|
||||
};
|
||||
if (isOwner) {
|
||||
// 如果是自己的笔记,且笔记不包含 @信息 则不需要AI回复,
|
||||
const hasAt = content.includes('@' + note_username) || content.includes('@' + note_userid);
|
||||
if (!hasAt) {
|
||||
// console.log('不需要AI回复自己的笔记', note_username, note_id, content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
content = content.replace('@' + note_username, '');
|
||||
const sliceContentCmd = content.slice(0, 20);
|
||||
let note = '';
|
||||
let hasNote = false;
|
||||
if (sliceContentCmd.includes('笔记') || sliceContentCmd.includes('总结')) {
|
||||
const res = await xhsApp.call({ path: 'note', key: 'getNote', payload: { node_id: note_id, xsec_token } });
|
||||
if (res.code === 200) {
|
||||
note = res.body?.desc || '';
|
||||
hasNote = note ? true : false;
|
||||
}
|
||||
}
|
||||
if (isReplayAi) {
|
||||
// 如果是对AI回复的评论,则不需要再回复
|
||||
console.log('不需要AI回复AI的评论', note_username, note_id, content);
|
||||
return;
|
||||
}
|
||||
const resAgent = await agent.call({
|
||||
path: 'xhs',
|
||||
payload: {
|
||||
text: content,
|
||||
note,
|
||||
hasNote,
|
||||
},
|
||||
});
|
||||
let responseText = '';
|
||||
let errorText = '';
|
||||
if (resAgent.code !== 200) {
|
||||
errorText = '【调用错误】';
|
||||
responseText = `${resAgent.message}`;
|
||||
} else {
|
||||
responseText = resAgent.body;
|
||||
}
|
||||
postData.content = responseText;
|
||||
const res = await xhsApp.call({
|
||||
path: 'mention',
|
||||
key: 'addComment',
|
||||
|
||||
@@ -3,13 +3,33 @@
|
||||
import { QueryRouterServer } from '@kevisual/router';
|
||||
import { redis } from '@/modules/redis.ts';
|
||||
import { Queue } from 'bullmq';
|
||||
import { app as xhsApp } from '@kevisual/xhs/index';
|
||||
import { app as xhsApp, xhsServices as xhs, xhsRootClient, XhsServices } from '@kevisual/xhs/index.ts';
|
||||
import { nanoid } from 'nanoid';
|
||||
export const XHS_GET_UNREAD = 'unread_count';
|
||||
export const XHS_QUEUE_NAME = 'XHS_QUEUE';
|
||||
import { config, isDev } from '../modules/config.ts';
|
||||
|
||||
const server: XhsServices = xhs;
|
||||
server.setCookie(config.XHS_ROOT_COOKIE || '');
|
||||
server.setUserInfo({
|
||||
userid: config.XHS_USER_ID || '',
|
||||
username: config.XHS_USER_NAME || '',
|
||||
});
|
||||
if (isDev) {
|
||||
server.setSignConfig({
|
||||
signUrl: 'http://localhost:5006/sign',
|
||||
});
|
||||
} else {
|
||||
server.setSignConfig({
|
||||
signUrl: config.XHS_API_SIGN_URL,
|
||||
});
|
||||
}
|
||||
console.log('XHS_USER_INFO', config.XHS_USER_ID, config.XHS_USER_NAME, 'XHS_ROOT_COOKIE', config.XHS_ROOT_COOKIE);
|
||||
console.log('XHS_SIGN_URL', server.getSignConfig().signUrl);
|
||||
export const taskApp = new QueryRouterServer();
|
||||
export { xhsApp };
|
||||
export const xhsServices = server;
|
||||
|
||||
export const queue = new Queue(XHS_QUEUE_NAME, {
|
||||
connection: redis,
|
||||
});
|
||||
@@ -25,6 +45,7 @@ export const addUnreadTask = async (nextTime = 0) => {
|
||||
{
|
||||
delay: nextTime,
|
||||
removeOnComplete: true,
|
||||
attempts: 1,
|
||||
removeOnFail: {
|
||||
age: 24 * 3600, // keep up to 24 hours
|
||||
},
|
||||
|
||||
59
src/task/utils/time.ts
Normal file
59
src/task/utils/time.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
/**
|
||||
* 根据当前时间返回对应的时间段
|
||||
* 返回的时间段是毫秒数
|
||||
* 例如:03:01 - 09:00 返回 120000 (120秒)
|
||||
* 如果当前时间不在任何时间段内,返回0
|
||||
* @returns
|
||||
*/
|
||||
export const getTimeDuration = () => {
|
||||
// 根据时间返回,返回需要
|
||||
const timeRangeList = [
|
||||
{
|
||||
start: '03:01',
|
||||
end: '09:00',
|
||||
duration: 240 * 1000, // 240s
|
||||
},
|
||||
{
|
||||
start: '09:01',
|
||||
end: '12:00',
|
||||
duration: 60 * 1000, // 60s
|
||||
},
|
||||
{
|
||||
start: '12:01',
|
||||
end: '14:00',
|
||||
duration: 30 * 1000, // 30s
|
||||
},
|
||||
{
|
||||
start: '14:01',
|
||||
end: '18:00',
|
||||
duration: 120 * 1000, // 120s
|
||||
},
|
||||
{
|
||||
start: '18:01',
|
||||
end: '22:00',
|
||||
duration: 10 * 1000, // 10s
|
||||
},
|
||||
{
|
||||
start: '22:01',
|
||||
end: '23:59',
|
||||
duration: 3 * 1000, // 3s
|
||||
},
|
||||
{
|
||||
start: '00:01',
|
||||
end: '03:00',
|
||||
duration: 20 * 1000, // 20s
|
||||
},
|
||||
];
|
||||
const currentTime = Date.now();
|
||||
const currentHour = dayjs(currentTime).format('HH:mm');
|
||||
for (const range of timeRangeList) {
|
||||
if (currentHour >= range.start && currentHour <= range.end) {
|
||||
return range.duration;
|
||||
}
|
||||
}
|
||||
// 如果没有匹配到,默认返回0
|
||||
|
||||
return 0;
|
||||
};
|
||||
@@ -5,6 +5,9 @@ import { nanoid } from 'nanoid';
|
||||
import { queue, XHS_QUEUE_NAME, taskApp } from './index.ts';
|
||||
import { addUnreadTask } from './task.ts';
|
||||
import dayjs from 'dayjs';
|
||||
import { getTimeDuration } from './utils/time.ts';
|
||||
import { config, isDev } from '../modules/config.ts';
|
||||
|
||||
export const sleep = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
@@ -13,7 +16,7 @@ class TimeRecorder {
|
||||
endTime: number;
|
||||
duration: number;
|
||||
updateTime: number;
|
||||
maxDuration: number = 60 * 1000; // 20s;
|
||||
maxDuration: number = 30 * 1000; // 30s;
|
||||
constructor() {
|
||||
const now = Date.now();
|
||||
this.startTime = now;
|
||||
@@ -34,13 +37,20 @@ class TimeRecorder {
|
||||
this.updateTime = Date.now();
|
||||
return this.updateTime;
|
||||
}
|
||||
getClampDuration() {
|
||||
const duration = Date.now() - this.updateTime;
|
||||
getClampDuration(random = false) {
|
||||
let randomDuration = 0;
|
||||
if (random) {
|
||||
randomDuration = Math.floor(Math.random() * 5) * 1000; // 随机0-5秒
|
||||
}
|
||||
const duration = Date.now() - this.updateTime + randomDuration;
|
||||
const nextTime = clamp(this.maxDuration - duration, 0, this.maxDuration);
|
||||
// console.log('getClampDuration', duration, this.maxDuration, 'nextTime', nextTime);
|
||||
|
||||
return {
|
||||
duration: duration,
|
||||
maxDuration: this.maxDuration,
|
||||
updateTime: this.updateTime,
|
||||
nextTime: clamp(this.maxDuration - duration, 0, this.maxDuration),
|
||||
nextTime: nextTime,
|
||||
};
|
||||
}
|
||||
time() {
|
||||
@@ -52,6 +62,7 @@ class TimeRecorder {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const timeRecorder = new TimeRecorder();
|
||||
let errorCount = 0;
|
||||
export const worker = new Worker(
|
||||
@@ -59,8 +70,9 @@ export const worker = new Worker(
|
||||
async (job) => {
|
||||
const timer = new TimeRecorder();
|
||||
const data = job.data;
|
||||
|
||||
if (data.path === 'task' && data.key === 'getUnread') {
|
||||
console.log('====run time', dayjs().format('YYYY-MM-DD HH:mm:ss'));
|
||||
console.log('====run time start', dayjs().format('YYYY-MM-DD HH:mm:ss'));
|
||||
timeRecorder.update();
|
||||
}
|
||||
const res = await taskApp.call(data);
|
||||
@@ -71,6 +83,9 @@ export const worker = new Worker(
|
||||
if (errorCount > 3) {
|
||||
queue.pause();
|
||||
console.log('error count', errorCount);
|
||||
if (data.path === 'task' && data.key === 'getUnread') {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
throw new Error('job error' + job.name + ' ' + job.id);
|
||||
}
|
||||
@@ -83,15 +98,17 @@ export const worker = new Worker(
|
||||
worker.on('completed', async (job) => {
|
||||
const jobCounts = await queue.getJobCounts('waiting', 'wait', 'delayed');
|
||||
if (job.name !== 'unread') {
|
||||
console.log('job completed', job.name, job.id, job.returnvalue, jobCounts.delayed, jobCounts.wait);
|
||||
console.log('job completed', job.name, 'run id', job.id, job.returnvalue, jobCounts.delayed, jobCounts.wait);
|
||||
}
|
||||
if (jobCounts.delayed + jobCounts.wait > 0) {
|
||||
// console.log('======has jobs, no need to add new job');
|
||||
} else {
|
||||
const up = timeRecorder.getClampDuration();
|
||||
const nextTime = up.nextTime;
|
||||
const up = timeRecorder.getClampDuration(true);
|
||||
const timeDuration = isDev ? 0 : getTimeDuration();
|
||||
const nextTime = up.nextTime + timeDuration;
|
||||
const unread = await queue.getJob('unread');
|
||||
if (!unread) {
|
||||
console.log('====add unread next-time', nextTime, timeDuration);
|
||||
addUnreadTask(nextTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProviderManager, SiliconFlowProvider } from '@kevisual/ai-center';
|
||||
import { ProviderManager, SiliconFlowProvider } from '@kevisual/ai';
|
||||
import dotenv from 'dotenv';
|
||||
import util from 'node:util';
|
||||
dotenv.config();
|
||||
|
||||
3
src/test/common.ts
Normal file
3
src/test/common.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { config } from '../modules/config.ts';
|
||||
|
||||
export { config };
|
||||
3
src/test/get-time-duration.ts
Normal file
3
src/test/get-time-duration.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { getTimeDuration } from '@/task/utils/time.ts';
|
||||
|
||||
console.log('getTimeDuration', getTimeDuration());
|
||||
57
src/test/tts.ts
Normal file
57
src/test/tts.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { config } from './common.ts';
|
||||
|
||||
const API_KEY = config.BAILIAN_API_KEY;
|
||||
// 使用DashScope API进行TTS (文本转语音) 请求
|
||||
const dashscopeTTS = async ({ text, voice = 'Chelsie', token }) => {
|
||||
try {
|
||||
const response = await fetch('https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
// model: 'qwen-tts',
|
||||
model: 'qwen-tts-latest',
|
||||
input: {
|
||||
text,
|
||||
voice,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('TTS 请求失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 使用示例
|
||||
const sampleText =
|
||||
'那我来给大家推荐一款T恤,这款呢真的是超级好看,这个颜色呢很显气质,而且呢也是搭配的绝佳单品,大家可以闭眼入,真的是非常好看,对身材的包容性也很好,不管啥身材的宝宝呢,穿上去都是很好看的。推荐宝宝们下单哦。';
|
||||
|
||||
async function main() {
|
||||
const voiceList = ['Cherry', 'Serena', 'Ethan', 'Chelsie'];
|
||||
const latestVoiceList = ['Dylan', 'Jada', 'Sunny'];
|
||||
try {
|
||||
const result = await dashscopeTTS({ text: sampleText, voice: latestVoiceList[2], token: API_KEY });
|
||||
console.log('TTS 生成成功:', result);
|
||||
|
||||
// 如果API返回音频数据(通常是base64格式),可以这样处理
|
||||
// 例如: 保存到文件或播放音频
|
||||
if (result.output?.audio) {
|
||||
// 处理音频数据
|
||||
console.log('获取到音频数据,长度:', result.output.audio.length);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
22
turbo.json
Normal file
22
turbo.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"dist/**"
|
||||
]
|
||||
},
|
||||
"dev:lib": {
|
||||
"persistent": true,
|
||||
"cache": true
|
||||
},
|
||||
"build:lib": {
|
||||
"dependsOn": [
|
||||
"^build:lib"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user