This commit is contained in:
熊潇 2025-05-05 22:41:18 +08:00
parent a412c09da0
commit 8891b196ba
12 changed files with 249 additions and 41 deletions

View File

@ -46,9 +46,10 @@
"devDependencies": { "devDependencies": {
"@kevisual/ai-center": "^0.0.3", "@kevisual/ai-center": "^0.0.3",
"@kevisual/app-assistant": "workspace:*", "@kevisual/app-assistant": "workspace:*",
"@kevisual/xhs": "workspace:*", "@kevisual/social-prompts": "workspace:*",
"@kevisual/types": "^0.0.9", "@kevisual/types": "^0.0.9",
"@kevisual/use-config": "^1.0.12", "@kevisual/use-config": "^1.0.12",
"@kevisual/xhs": "workspace:*",
"@types/bun": "^1.2.11", "@types/bun": "^1.2.11",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/formidable": "^3.4.5", "@types/formidable": "^3.4.5",
@ -58,6 +59,7 @@
"commander": "^13.1.0", "commander": "^13.1.0",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "^16.5.0",
"inquire": "^0.4.8", "inquire": "^0.4.8",
"ioredis": "^5.6.1", "ioredis": "^5.6.1",
"nodemon": "^3.1.10", "nodemon": "^3.1.10",

View File

@ -0,0 +1,14 @@
{
"$vars": {
"root": "https://kevisual.cn/root/ai"
},
"registry": {
"root": "https://kevisual.cn/root/ai"
},
"match": {
"./xhs/answer.ts": {
"url": "${root}/xhs/answer.ts"
},
"./kevisual.json": {}
}
}

View File

@ -0,0 +1,14 @@
{
"name": "@kevisual/social-prompts",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.10.0",
"type": "module"
}

View File

@ -0,0 +1,15 @@
{
"extends": "@kevisual/types/json/backend.json",
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
"include": [
"xhs/**/*.ts",
],
"exclude": [],
}

View File

@ -0,0 +1,34 @@
export const answer = [
{
role: 'system',
content: `你是一个小红书的内容回答问题的助手,用户提供一条笔记的信息,属于内容的上下文,和用户提供的问题,你需要给出一个简短的回答,回答要简洁明了,符合小红书的风格。
1. "." "点" https://www.baidu.com 替换为 https://www点baidu点com
2. md格式
3.
html的内容
vscode打开html网页
vscodehtml文件拖到vscode中vscode中打开html文件
`,
},
];
export const createQuestion = (question: string, note: string, answers: any[] = []) => {
return [
...answers,
{
role: 'assistant',
content: `上下文的内参考的内容是: ${note}`,
},
{
role: 'user',
content: `用户: ${question}`,
},
];
};

View File

@ -344,7 +344,7 @@ class XhsClient {
const state = stateMatch[1].replace(/undefined/g, '""'); const state = stateMatch[1].replace(/undefined/g, '""');
if (state !== '{}') { if (state !== '{}') {
const noteDict = transformJsonKeys(JSON.parse(state)); const noteDict = transformJsonKeys(JSON.parse(state));
return noteDict.note.note_detail_map[noteId].note; return { code: 0, data: noteDict.note.note_detail_map[noteId].note };
} }
} }
@ -355,8 +355,12 @@ class XhsClient {
throw new DataFetchError(html); throw new DataFetchError(html);
} catch (error) { } catch (error) {
console.error('Error fetching note:', error); console.error('Error fetching note:', error);
fs.writeFileSync('a.html', html); return {
throw error; code: 500,
msg: '请求失败',
error: error.message,
data: null,
};
} }
} }
/** /**

View File

@ -0,0 +1,68 @@
import { XhsClient, Result } from '../xhs.ts';
interface ImageInfo {
image_scene: string;
url: string;
}
interface ImageItem {
file_id: string;
height: number;
width: number;
url: string;
trace_id: string;
url_pre: string;
stream: Record<string, never>; // 空对象类型
info_list: ImageInfo[];
url_default: string;
live_photo: boolean;
}
interface AtUser {
xsec_token: string;
user_id: string;
nickname: string;
}
interface User {
user_id: string;
nickname: string;
avatar: string;
xsec_token: string;
}
interface InteractInfo {
collected_count: string;
comment_count: string;
share_count: string;
followed: boolean;
relation: string;
liked: boolean;
liked_count: string;
collected: boolean;
}
interface ShareInfo {
un_share: boolean;
}
interface NoteData {
xsec_token: string;
desc: string;
user: User;
interact_info: InteractInfo;
tag_list: never[]; // 空数组类型
at_user_list: AtUser[];
ip_location: string;
share_info: ShareInfo;
note_id: string;
type: string;
title: string;
image_list: ImageItem[];
time: number;
last_update_time: number;
}
export const getNote = async function (id: string, x: string) {
const that = this as XhsClient;
const res = await that.getNoteByIdFromHtml(id, x);
return res as Result<NoteData>;
};

View File

@ -2,6 +2,7 @@ import { getApiInfo } from './xhs-api/api.ts';
import { XhsClient as XhsClientBase } from '@kevisual/xhs-core'; import { XhsClient as XhsClientBase } from '@kevisual/xhs-core';
import { Mention, CommonentInfo, ResponseMession } from './xhs-type/mention.ts'; import { Mention, CommonentInfo, ResponseMession } from './xhs-type/mention.ts';
import { pick } from 'lodash-es'; import { pick } from 'lodash-es';
import { getNote } from './modules/get-note.ts';
export type Result<T> = { export type Result<T> = {
code: number; // 0: success code: number; // 0: success
msg?: string; msg?: string;
@ -211,7 +212,8 @@ export class XhsClient extends XhsClientBase {
try { try {
const response = await this.post(uri, data, { sign: this.sign.bind(this) }); const response = await this.post(uri, data, { sign: this.sign.bind(this) });
return response['items'][0]['node_card']; // return response['items'][0]['node_card'];
return response;
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
@ -221,7 +223,7 @@ export class XhsClient extends XhsClientBase {
* @param comment * @param comment
* @returns * @returns
*/ */
async postComment(comment: { note_id: string; comment_id?: string; content: string; images_info?: any, images?: string[] }) { async postComment(comment: { note_id: string; comment_id?: string; content: string; images_info?: any; images?: string[] }) {
const uri = '/api/sns/web/v1/comment/post'; const uri = '/api/sns/web/v1/comment/post';
try { try {
const data = { const data = {
@ -249,6 +251,7 @@ export class XhsClient extends XhsClientBase {
console.log(error); console.log(error);
} }
} }
getNote = getNote;
} }
type UnreadCount = { type UnreadCount = {

View File

@ -1,5 +1,5 @@
import { xhsServices, program } from '../common.ts'; import { xhsServices, program } from '../common.ts';
import util from 'node:util';
// client.getNoteComments() // client.getNoteComments()
@ -8,7 +8,7 @@ import { xhsServices, program } from '../common.ts';
// }); // });
const getNoteById = async () => { const getNoteById = async () => {
const client = xhsServices.getClient(); const client = xhsServices.getClient();
client.getNoteById('67dcc34e000000000602a8eb', 'ABuYS8Xb1o08DlRmMLIabdqnW0OKnLR9nMpDGq5bVRdvk').then((res) => { client.getNoteById('68136dab0000000007034c46', 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM').then((res) => {
console.log(res); console.log(res);
}); });
}; };
@ -16,8 +16,8 @@ const getNote = async () => {
const id = '68136dab0000000007034c46'; const id = '68136dab0000000007034c46';
const x = 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM='; const x = 'LByEmonX8WfJ9ebpAowVbOZX9Xh8T0Qkjil5KRFqDD6LM=';
const client = xhsServices.getClient(); const client = xhsServices.getClient();
client.getNoteByIdFromHtml(id, x).then((res) => { client.getNote(id, x).then((res) => {
console.log(res); console.log(util.inspect(res, { depth: null }));
}); });
}; };
program program
@ -32,4 +32,4 @@ program
.description('get note by id') .description('get note by id')
.action(async () => { .action(async () => {
getNoteById(); getNoteById();
}); });

26
pnpm-lock.yaml generated
View File

@ -10,13 +10,13 @@ importers:
dependencies: dependencies:
'@kevisual/code-center-module': '@kevisual/code-center-module':
specifier: 0.0.18 specifier: 0.0.18
version: 0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.13)(@kevisual/use-config@1.0.12(dotenv@16.5.0))(ioredis@5.6.1)(pg@8.15.6)(sequelize@6.37.7(pg@8.15.6)) 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))
'@kevisual/router': '@kevisual/router':
specifier: 0.0.13 specifier: 0.0.13
version: 0.0.13 version: 0.0.13
'@kevisual/use-config': '@kevisual/use-config':
specifier: ^1.0.12 specifier: ^1.0.12
version: 1.0.12(dotenv@16.5.0) version: 1.0.14(dotenv@16.5.0)
cookie: cookie:
specifier: ^1.0.2 specifier: ^1.0.2
version: 1.0.2 version: 1.0.2
@ -39,6 +39,9 @@ importers:
'@kevisual/app-assistant': '@kevisual/app-assistant':
specifier: workspace:* specifier: workspace:*
version: link:packages/app-assistant version: link:packages/app-assistant
'@kevisual/social-prompts':
specifier: workspace:*
version: link:packages/social-prompts
'@kevisual/types': '@kevisual/types':
specifier: ^0.0.9 specifier: ^0.0.9
version: 0.0.9 version: 0.0.9
@ -72,6 +75,9 @@ importers:
cross-env: cross-env:
specifier: ^7.0.3 specifier: ^7.0.3
version: 7.0.3 version: 7.0.3
dotenv:
specifier: ^16.5.0
version: 16.5.0
inquire: inquire:
specifier: ^0.4.8 specifier: ^0.4.8
version: 0.4.8 version: 0.4.8
@ -110,6 +116,8 @@ importers:
specifier: ^5.51.1 specifier: ^5.51.1
version: 5.51.1 version: 5.51.1
packages/social-prompts: {}
packages/xhs: packages/xhs:
dependencies: dependencies:
'@kevisual/db': '@kevisual/db':
@ -236,11 +244,6 @@ packages:
'@kevisual/types@0.0.9': '@kevisual/types@0.0.9':
resolution: {integrity: sha512-SDJ7GMbOx7Ghz2kreHqym56ccAJS3t93y+NS0+afTLxcq2+cKcoEy2F8WXEv0mnJ6EsDp5AbA7Jv5TZA1Jbc3A==} resolution: {integrity: sha512-SDJ7GMbOx7Ghz2kreHqym56ccAJS3t93y+NS0+afTLxcq2+cKcoEy2F8WXEv0mnJ6EsDp5AbA7Jv5TZA1Jbc3A==}
'@kevisual/use-config@1.0.12':
resolution: {integrity: sha512-PNoZqj6vdhv6DvjRMNwoGH9HJupm7QvjkvtCEYW2ryK7J8sI73r2ThCl4OEbXdRYVgl1EeK/e2IJh0Rf51bVwA==}
peerDependencies:
dotenv: ^16.4.7
'@kevisual/use-config@1.0.14': '@kevisual/use-config@1.0.14':
resolution: {integrity: sha512-U4fmPFJre+Ph+hdg+EKVaRGxdpD4T4ZoOz5QLEyC6o3ekg8NC4n1i7Myo1ektqATyoG9Y0q/bJdOhiJwnLEt4g==} resolution: {integrity: sha512-U4fmPFJre+Ph+hdg+EKVaRGxdpD4T4ZoOz5QLEyC6o3ekg8NC4n1i7Myo1ektqATyoG9Y0q/bJdOhiJwnLEt4g==}
peerDependencies: peerDependencies:
@ -2100,11 +2103,11 @@ snapshots:
'@kevisual/auth@1.0.5': {} '@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.12(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.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))':
dependencies: dependencies:
'@kevisual/auth': 1.0.5 '@kevisual/auth': 1.0.5
'@kevisual/router': 0.0.13 '@kevisual/router': 0.0.13
'@kevisual/use-config': 1.0.12(dotenv@16.5.0) '@kevisual/use-config': 1.0.14(dotenv@16.5.0)
ioredis: 5.6.1 ioredis: 5.6.1
nanoid: 5.1.5 nanoid: 5.1.5
pg: 8.15.6 pg: 8.15.6
@ -2136,11 +2139,6 @@ snapshots:
'@kevisual/types@0.0.9': {} '@kevisual/types@0.0.9': {}
'@kevisual/use-config@1.0.12(dotenv@16.5.0)':
dependencies:
'@kevisual/load': 0.0.6
dotenv: 16.5.0
'@kevisual/use-config@1.0.14(dotenv@16.5.0)': '@kevisual/use-config@1.0.14(dotenv@16.5.0)':
dependencies: dependencies:
'@kevisual/load': 0.0.6 '@kevisual/load': 0.0.6

View File

@ -19,7 +19,6 @@ taskApp
if (res.code === 200) { if (res.code === 200) {
const data = res.body; const data = res.body;
const unread_count = data.unread_count; const unread_count = data.unread_count;
console.log('unread_count====', data);
if (unread_count > 0) { if (unread_count > 0) {
queue.add( queue.add(
'mention', 'mention',
@ -69,21 +68,26 @@ taskApp
console.log('queryMention', util.inspect(data, { depth: 10 })); console.log('queryMention', util.inspect(data, { depth: 10 }));
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const item = data[i]; const item = data[i];
const note_id = item.note_id; queue.add(
const xsec_token = item.xsec_token; 'mention-ai',
const comment_id = item.comment.comment_id; {
const content = item.comment?.content || 'test'; path: 'task',
const postData = { key: 'ai',
note_id, payload: {
content, data: item,
comment_id, },
}; },
const res = await xhsApp.call({ {
path: 'mention', attempts: 3,
key: 'addComment', delay: 0,
payload: postData, removeOnComplete: true,
}); removeOnFail: {
console.log('addComment', postData, 'res', res.body); age: 24 * 3600, // keep up to 24 hours
},
},
);
console.log('add mention task', item);
await sleep(200);
} }
} }
const postRead = await xhsApp.call({ const postRead = await xhsApp.call({
@ -98,3 +102,28 @@ taskApp
}; };
}) })
.addTo(taskApp); .addTo(taskApp);
taskApp
.route({
path: 'task',
key: 'ai',
})
.define(async (ctx) => {
const data = ctx.query.data;
const note_id = data.note_id;
const xsec_token = data.xsec_token;
const comment_id = data.comment.comment_id;
const content = data.comment?.content || 'test';
const postData = {
note_id,
content,
comment_id,
};
const res = await xhsApp.call({
path: 'mention',
key: 'addComment',
payload: postData,
});
console.log('addComment', postData, 'res', res.body);
})
.addTo(taskApp);

27
src/test/ai.ts Normal file
View File

@ -0,0 +1,27 @@
import { ProviderManager, SiliconFlowProvider } from '@kevisual/ai-center';
import dotenv from 'dotenv';
import util from 'node:util';
dotenv.config();
import { createQuestion, answer } from '@kevisual/social-prompts/xhs/answer.ts';
const pm = new SiliconFlowProvider({
model: 'Qwen/Qwen3-32B',
apiKey: process.env.SILICONFLOW_API_KEY,
});
const main = async () => {
const usage = await pm.getUsageInfo();
console.log('usage', usage);
};
// main();
const ques = async () => {
// const msg = createQuestion('介绍一下如何安装node在mac当中', '前端的node如何安装环境', answer);
const msg = createQuestion('介绍一下如何安装node在mac当中', '前端的node如何安装环境', answer);
console.log('msg', msg);
const res = await pm.chat(msg);
console.log(util.inspect(res.choices, { depth: null }));
const ans = res.choices[0].message.content;
console.log('ans', ans, ans.length);
};
ques();