generated from tailored/router-template
temp
This commit is contained in:
parent
e58adbc46b
commit
ee483aa87e
@ -1,6 +1,7 @@
|
|||||||
// ErrorTuple and ErrorEnum
|
// ErrorTuple and ErrorEnum
|
||||||
const ErrorEnum = {
|
const ErrorEnum = {
|
||||||
IP_BLOCK: { code: 300012, msg: '网络连接异常,请检查网络设置或重启试试' },
|
IP_BLOCK: { code: 300012, msg: '网络连接异常,请检查网络设置或重启试试' },
|
||||||
|
NOTE_CANT_GET: { code: 300031, msg: '当前笔记暂时无法浏览' },
|
||||||
NOTE_ABNORMAL: { code: -510001, msg: '笔记状态异常,请稍后查看' },
|
NOTE_ABNORMAL: { code: -510001, msg: '笔记状态异常,请稍后查看' },
|
||||||
NOTE_SECRETE_FAULT: { code: -510001, msg: '当前内容无法展示' },
|
NOTE_SECRETE_FAULT: { code: -510001, msg: '当前内容无法展示' },
|
||||||
SIGN_FAULT: { code: 300015, msg: '浏览器异常,请尝试关闭/卸载风险插件或重启试试!' },
|
SIGN_FAULT: { code: 300015, msg: '浏览器异常,请尝试关闭/卸载风险插件或重启试试!' },
|
||||||
|
@ -58,7 +58,12 @@ class XhsClient {
|
|||||||
this.cookie = cookie;
|
this.cookie = cookie;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @params {*} args
|
||||||
|
*/
|
||||||
|
printResult(...args) {
|
||||||
|
//
|
||||||
|
}
|
||||||
// Getter for cookie
|
// Getter for cookie
|
||||||
get cookie() {
|
get cookie() {
|
||||||
return this.axiosInstance.defaults.headers.Cookie;
|
return this.axiosInstance.defaults.headers.Cookie;
|
||||||
@ -82,14 +87,11 @@ class XhsClient {
|
|||||||
const X_S = x_s_result['X-s'];
|
const X_S = x_s_result['X-s'];
|
||||||
const X_t = x_s_result['X-t'].toString();
|
const X_t = x_s_result['X-t'].toString();
|
||||||
const X_S_COMMON = getXCommon(a1, b1, X_S, X_t);
|
const X_S_COMMON = getXCommon(a1, b1, X_S, X_t);
|
||||||
// this.axiosInstance.defaults.headers['X-s'] = X_S;
|
|
||||||
// this.axiosInstance.defaults.headers['X-t'] = X_t;
|
|
||||||
// this.axiosInstance.defaults.headers['X-s-common'] = X_S_COMMON;
|
|
||||||
return {
|
return {
|
||||||
headers: {
|
headers: {
|
||||||
'X-s': X_S,
|
'x-s': X_S,
|
||||||
'X-t': X_t,
|
'x-t': X_t,
|
||||||
'X-s-common': X_S_COMMON,
|
'xs-common': X_S_COMMON,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -123,9 +125,11 @@ class XhsClient {
|
|||||||
|
|
||||||
async request(method, url, config = {}) {
|
async request(method, url, config = {}) {
|
||||||
try {
|
try {
|
||||||
const response = await this.axiosInstance({ method, url, ...config });
|
delete config.sign;
|
||||||
|
const requestOptions = { method, url, ...config };
|
||||||
|
this.printResult('request', requestOptions);
|
||||||
|
const response = await this.axiosInstance(requestOptions);
|
||||||
if (!response.data) return response;
|
if (!response.data) return response;
|
||||||
// console.log('response', response)
|
|
||||||
if (response.status === 471 || response.status === 461) {
|
if (response.status === 471 || response.status === 461) {
|
||||||
const verifyType = response.headers['verifytype'];
|
const verifyType = response.headers['verifytype'];
|
||||||
const verifyUuid = response.headers['verifyuuid'];
|
const verifyUuid = response.headers['verifyuuid'];
|
||||||
@ -133,8 +137,12 @@ class XhsClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
|
this.printResult('reponse', {
|
||||||
|
url: url,
|
||||||
|
response,
|
||||||
|
});
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
return data.data || data.success;
|
return data;
|
||||||
} else if (data.code === ErrorEnum.IP_BLOCK.code) {
|
} else if (data.code === ErrorEnum.IP_BLOCK.code) {
|
||||||
throw new IPBlockError(ErrorEnum.IP_BLOCK.msg, response);
|
throw new IPBlockError(ErrorEnum.IP_BLOCK.msg, response);
|
||||||
} else if (data.code === ErrorEnum.SIGN_FAULT.code) {
|
} else if (data.code === ErrorEnum.SIGN_FAULT.code) {
|
||||||
@ -167,9 +175,11 @@ class XhsClient {
|
|||||||
if (params) {
|
if (params) {
|
||||||
uri = `${uri}?${qs.stringify(params)}`;
|
uri = `${uri}?${qs.stringify(params)}`;
|
||||||
}
|
}
|
||||||
if (config.sign) {
|
const header = config.headers || {};
|
||||||
await config.sign(uri, data, config);
|
const xs = header['x-s'];
|
||||||
} else {
|
if (config.sign && !xs) {
|
||||||
|
await config.sign(uri, null, config);
|
||||||
|
} else if (!xs) {
|
||||||
const { headers } = this._preHeaders(uri, null);
|
const { headers } = this._preHeaders(uri, null);
|
||||||
config = { ...config, headers: { ...config.headers, ...headers } };
|
config = { ...config, headers: { ...config.headers, ...headers } };
|
||||||
}
|
}
|
||||||
@ -197,9 +207,11 @@ class XhsClient {
|
|||||||
async post(uri, data = null, config = {}) {
|
async post(uri, data = null, config = {}) {
|
||||||
let jsonStr = data ? JSON.stringify(data) : null;
|
let jsonStr = data ? JSON.stringify(data) : null;
|
||||||
let endpoint = this._host;
|
let endpoint = this._host;
|
||||||
if (config.sign) {
|
const header = config.headers || {};
|
||||||
|
const xs = header['x-s'];
|
||||||
|
if (config.sign && !xs) {
|
||||||
await config.sign(uri, data, config);
|
await config.sign(uri, data, config);
|
||||||
} else {
|
} else if (!xs) {
|
||||||
const { headers } = this._preHeaders(uri, data);
|
const { headers } = this._preHeaders(uri, data);
|
||||||
config = { ...config, headers: { ...config.headers, ...headers } };
|
config = { ...config, headers: { ...config.headers, ...headers } };
|
||||||
}
|
}
|
||||||
@ -226,10 +238,11 @@ class XhsClient {
|
|||||||
/**
|
/**
|
||||||
* 获取笔记详情
|
* 获取笔记详情
|
||||||
* 注意: 需要xsec_token
|
* 注意: 需要xsec_token
|
||||||
|
* @uri /api/sns/web/v1/feed
|
||||||
* @param {string} noteId
|
* @param {string} noteId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getNoteById(noteId, xsecToken, xsecSource = 'pc_feed') {
|
async getNoteById(noteId, xsecToken, xsecSource = 'pc_feed', config = {}) {
|
||||||
if (!xsecToken) {
|
if (!xsecToken) {
|
||||||
throw new Error('xsecToken is required');
|
throw new Error('xsecToken is required');
|
||||||
}
|
}
|
||||||
@ -242,16 +255,24 @@ class XhsClient {
|
|||||||
const uri = '/api/sns/web/v1/feed';
|
const uri = '/api/sns/web/v1/feed';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await this.post(uri, data);
|
const res = await this.post(uri, data, config);
|
||||||
return res.items[0].note_card;
|
return res.items[0].note_card;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching note:', error);
|
console.error('Error fetching note:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取笔记详情
|
||||||
|
* @uri /api/sns/web/v1/feed
|
||||||
|
* @param {string} noteId
|
||||||
|
* @param {string} xsecToken
|
||||||
|
* @param {string} [xsecSource=pc_feed]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async getNoteByIdFromHtml(noteId, xsecToken, xsecSource = 'pc_feed') {
|
async getNoteByIdFromHtml(noteId, xsecToken, xsecSource = 'pc_feed') {
|
||||||
const url = `https://www.xiaohongshu.com/explore/${noteId}?xsec_token=${xsecToken}&xsec_source=${xsecSource}`;
|
const url = `https://www.xiaohongshu.com/explore/${noteId}?xsec_token=${xsecToken}&xsec_source=${xsecSource}`;
|
||||||
|
this.printResult('html', { url, noteId, xsecToken, xsecSource });
|
||||||
let html = '';
|
let html = '';
|
||||||
try {
|
try {
|
||||||
const response = await this.axiosInstance.get(url, {
|
const response = await this.axiosInstance.get(url, {
|
||||||
@ -283,17 +304,29 @@ class XhsClient {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
* @uri /api/sns/web/v1/user/selfinfo
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async getSelfInfo() {
|
async getSelfInfo() {
|
||||||
const uri = '/api/sns/web/v1/user/selfinfo';
|
const uri = '/api/sns/web/v1/user/selfinfo';
|
||||||
return this.get(uri);
|
return this.get(uri);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @uri /api/sns/web/v2/user/me
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async getSelfInfoV2() {
|
async getSelfInfoV2() {
|
||||||
const uri = '/api/sns/web/v2/user/me';
|
const uri = '/api/sns/web/v2/user/me';
|
||||||
return this.get(uri);
|
return this.get(uri);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
* @uri /api/sns/web/v1/user/otherinfo
|
||||||
|
* @param {string} userId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async getUserInfo(userId) {
|
async getUserInfo(userId) {
|
||||||
const uri = '/api/sns/web/v1/user/otherinfo';
|
const uri = '/api/sns/web/v1/user/otherinfo';
|
||||||
const params = {
|
const params = {
|
||||||
@ -304,6 +337,7 @@ class XhsClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
* @uri /api/sns/web/v1/search/notes
|
||||||
* @param {string} keyword 关键词
|
* @param {string} keyword 关键词
|
||||||
* @param {number} page 页码
|
* @param {number} page 页码
|
||||||
* @param {number} pageSize 分页查询的数量
|
* @param {number} pageSize 分页查询的数量
|
||||||
@ -329,21 +363,26 @@ class XhsClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取笔记评论
|
* 获取笔记评论
|
||||||
|
* @uri /api/sns/web/v2/comment/page
|
||||||
* @param {string} noteId 笔记id
|
* @param {string} noteId 笔记id
|
||||||
* @param {string} cursor 分页查询的下标,默认为""
|
* @param {string} cursor 分页查询的下标,默认为""
|
||||||
|
* @param {Object} params 其他参数
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getNoteComments(noteId, cursor = '') {
|
async getNoteComments(noteId, cursor = '', otherParams = {}) {
|
||||||
const uri = '/api/sns/web/v2/comment/page';
|
const uri = '/api/sns/web/v2/comment/page';
|
||||||
const params = {
|
const params = {
|
||||||
note_id: noteId,
|
note_id: noteId,
|
||||||
cursor: cursor,
|
cursor: cursor,
|
||||||
|
image_formats: 'jpg,webp,avif',
|
||||||
|
...otherParams,
|
||||||
};
|
};
|
||||||
return this.get(uri, params);
|
return this.get(uri, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户笔记
|
* 获取用户笔记
|
||||||
|
* @uri /api/sns/web/v1/user_posted
|
||||||
* @param {*} userId
|
* @param {*} userId
|
||||||
* @param {*} cursor
|
* @param {*} cursor
|
||||||
* @returns
|
* @returns
|
||||||
@ -361,6 +400,7 @@ class XhsClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取账号@我通知
|
* 获取账号@我通知
|
||||||
|
* @uri /api/sns/web/v1/you/mentions
|
||||||
* @param {*} num
|
* @param {*} num
|
||||||
* @param {*} cursor
|
* @param {*} cursor
|
||||||
* @returns
|
* @returns
|
||||||
@ -373,6 +413,7 @@ class XhsClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取点赞通知
|
* 获取点赞通知
|
||||||
|
* @uri /api/sns/web/v1/you/likes
|
||||||
* @param {*} num
|
* @param {*} num
|
||||||
* @param {*} cursor
|
* @param {*} cursor
|
||||||
* @returns
|
* @returns
|
||||||
@ -385,16 +426,23 @@ class XhsClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取关注通知
|
* 获取关注通知
|
||||||
|
* @uri /api/sns/web/v1/you/connections
|
||||||
* @param {*} num
|
* @param {*} num
|
||||||
* @param {*} cursor
|
* @param {*} cursor
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async getFollowNotifications(num = 20, cursor = '') {
|
async getFollowNotifications(num = 20, cursor = '', config = {}) {
|
||||||
const uri = '/api/sns/web/v1/you/connections';
|
const uri = '/api/sns/web/v1/you/connections';
|
||||||
const params = { num: num, cursor: cursor };
|
const params = { num: num, cursor: cursor };
|
||||||
return this.get(uri, params);
|
return this.get(uri, params, config);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
* @uri /user/profile/{userId}
|
||||||
|
* @description 通过用户ID获取用户信息
|
||||||
|
* @param {string} userId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
async getUserInfoFromHtml(userId) {
|
async getUserInfoFromHtml(userId) {
|
||||||
const url = `https://www.xiaohongshu.com/user/profile/${userId}`;
|
const url = `https://www.xiaohongshu.com/user/profile/${userId}`;
|
||||||
try {
|
try {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -7,6 +7,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun run bun.config.mjs",
|
"build": "bun run bun.config.mjs",
|
||||||
"postbuild": "dts -i src/index.js -o app.d.ts",
|
"postbuild": "dts -i src/index.js -o app.d.ts",
|
||||||
|
"cmd": "tsx src/test/command.ts ",
|
||||||
"dts": "dts -i src/index.js -o app.d.ts"
|
"dts": "dts -i src/index.js -o app.d.ts"
|
||||||
},
|
},
|
||||||
"file": [
|
"file": [
|
||||||
@ -20,8 +21,8 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"packageManager": "pnpm@10.10.0",
|
"packageManager": "pnpm@10.10.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kevisual/xhs-core": "workspace:*"
|
"@kevisual/xhs-core": "workspace:*",
|
||||||
|
"@types/node": "^22.15.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
11
packages/xhs/src/app.ts
Normal file
11
packages/xhs/src/app.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { QueryRouterServer } from '@kevisual/router';
|
||||||
|
import { XhsServices } from '@/services/xhs-services.ts';
|
||||||
|
|
||||||
|
export const app = new QueryRouterServer();
|
||||||
|
export const xhsServices = new XhsServices();
|
||||||
|
export const cookie =
|
||||||
|
'a1=1968ba02ff4xrt6hfrzdiz7ubs82j9y3vx11vfw9c40000317680;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;';
|
||||||
|
|
||||||
|
xhsServices.createRoot({
|
||||||
|
cookie,
|
||||||
|
});
|
@ -1,66 +1,5 @@
|
|||||||
import { XhsClient as XhsClientBase } from '@kevisual/xhs-core';
|
import { XhsClient } from './libs/xhs.ts';
|
||||||
|
import { app, xhsServices } from './app.ts';
|
||||||
|
import './routes/index.ts';
|
||||||
|
|
||||||
export const getSign = async (uri: string, data: any, a1: string, web_session?: string) => {
|
export { XhsClient, app, xhsServices };
|
||||||
const signs = await fetch('http://light.xiongxiao.me:5006/sign', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
uri,
|
|
||||||
data,
|
|
||||||
a1,
|
|
||||||
web_session: web_session,
|
|
||||||
}),
|
|
||||||
}).then((res) => res.json());
|
|
||||||
return signs;
|
|
||||||
};
|
|
||||||
type XhsOptions = {
|
|
||||||
cookie?: string;
|
|
||||||
};
|
|
||||||
export class XhsClient extends XhsClientBase {
|
|
||||||
constructor(opts: XhsOptions) {
|
|
||||||
super(opts as any);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取未读消息
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async getUnread(): Promise<UnreadCount> {
|
|
||||||
const url = '/api/sns/web/unread_count';
|
|
||||||
const response = await this.get(url);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
async sign(uri: string, data: any, config: any) {
|
|
||||||
let header = config?.header || {};
|
|
||||||
const cookieDist = this.getCookieMap();
|
|
||||||
const web_session = cookieDist['web_session'];
|
|
||||||
const a1 = cookieDist['a1'];
|
|
||||||
const res = await getSign(uri, data, a1, web_session);
|
|
||||||
header['x-s'] = res['x-s'];
|
|
||||||
header['x-t'] = res['x-t'];
|
|
||||||
config.header = header;
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
async getNoteById(noteId: string, xsecToken?: string) {
|
|
||||||
const data = {
|
|
||||||
source_note_id: noteId,
|
|
||||||
image_scenes: ['CRD_WM_WEBP'],
|
|
||||||
};
|
|
||||||
const uri = '/api/sns/web/v1/feed';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await this.post(uri, data, { sign: this.sign.bind(this) });
|
|
||||||
return response['items'][0]['node_card'];
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnreadCount = {
|
|
||||||
unread_count: number;
|
|
||||||
likes: number;
|
|
||||||
connections: number;
|
|
||||||
mentions: number;
|
|
||||||
};
|
|
||||||
|
61
packages/xhs/src/libs/parse.ts
Normal file
61
packages/xhs/src/libs/parse.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Connection } from './xhs-type/connection.ts';
|
||||||
|
import { Mention, CommonentInfo } from './xhs-type/mention.ts';
|
||||||
|
import { NoteFromHtml } from './xhs-type/note.ts';
|
||||||
|
const parseComment = (comment: CommonentInfo) => {
|
||||||
|
if (!comment) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
comment_id: comment.id,
|
||||||
|
content: comment.content,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export class Parse {
|
||||||
|
static getComment(mention: Mention) {
|
||||||
|
if (mention.type === 'mention/comment') {
|
||||||
|
const comment_info = mention.comment_info;
|
||||||
|
const comment = parseComment(comment_info);
|
||||||
|
const target_comment = parseComment(comment_info.target_comment);
|
||||||
|
return {
|
||||||
|
...comment,
|
||||||
|
target_comment,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
static getUser(mention: Mention) {
|
||||||
|
const user_info = mention?.item_info?.user_info;
|
||||||
|
if (user_info) {
|
||||||
|
return {
|
||||||
|
user_id: user_info.userid,
|
||||||
|
nickname: user_info.nickname,
|
||||||
|
image: user_info.image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
static getNote(note: NoteFromHtml) {
|
||||||
|
return {
|
||||||
|
node_id: note.note_id,
|
||||||
|
xsec_token: note.xsec_token,
|
||||||
|
time: note.time,
|
||||||
|
last_update_time: note.last_update_time,
|
||||||
|
desc: note.desc,
|
||||||
|
title: note.title,
|
||||||
|
image_list: note.image_list,
|
||||||
|
user: note.user,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static getConnects(message: Connection[]) {
|
||||||
|
return message.map((item) => {
|
||||||
|
const user = item.user;
|
||||||
|
return {
|
||||||
|
fstatus: user.fstatus,
|
||||||
|
xsec_token: user.xsec_token,
|
||||||
|
userid: user.userid,
|
||||||
|
nickname: user.nickname,
|
||||||
|
images: user.images,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
9
packages/xhs/src/libs/xhs-type/connection.ts
Normal file
9
packages/xhs/src/libs/xhs-type/connection.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export type Connection = {
|
||||||
|
type: 'fllow/you';
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
track_type: string;
|
||||||
|
user: { fstatus: 'both' | 'follows' | 'fans'; red_official_verify_type: number; xsec_token: string; userid: string; nickname: string; images: string };
|
||||||
|
time: number;
|
||||||
|
score: number;
|
||||||
|
};
|
47
packages/xhs/src/libs/xhs-type/mention.ts
Normal file
47
packages/xhs/src/libs/xhs-type/mention.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
type NoteBase = {
|
||||||
|
/**
|
||||||
|
* note id
|
||||||
|
**/
|
||||||
|
id: string;
|
||||||
|
xsec_token: string;
|
||||||
|
type: 'note_info';
|
||||||
|
user_info: UserInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserInfo = {
|
||||||
|
image: string;
|
||||||
|
indicator: '作者';
|
||||||
|
nickname: string;
|
||||||
|
red_official_verify_type: 0;
|
||||||
|
userid: string;
|
||||||
|
xsec_token: string;
|
||||||
|
};
|
||||||
|
export type CommonentInfo = {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
target_comment: CommonentInfo;
|
||||||
|
user_info?: UserInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MentionItem = {
|
||||||
|
type: 'mention/item';
|
||||||
|
track_type: '2';
|
||||||
|
title: string;
|
||||||
|
user_info: UserInfo;
|
||||||
|
item_info: {} & NoteBase;
|
||||||
|
};
|
||||||
|
export type MentionComment = {
|
||||||
|
type: 'mention/comment';
|
||||||
|
track_type: '8';
|
||||||
|
title: string;
|
||||||
|
item_info: {} & NoteBase;
|
||||||
|
comment_info: CommonentInfo;
|
||||||
|
};
|
||||||
|
export type Mention = MentionItem | MentionComment;
|
||||||
|
|
||||||
|
export type ResponseMession = {
|
||||||
|
has_more: boolean;
|
||||||
|
cursor: string;
|
||||||
|
strCursor: string;
|
||||||
|
message_list: Mention[];
|
||||||
|
};
|
47
packages/xhs/src/libs/xhs-type/note.ts
Normal file
47
packages/xhs/src/libs/xhs-type/note.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
type InteractInfo = {
|
||||||
|
collected_count: string | number;
|
||||||
|
comment_count: string | number;
|
||||||
|
share_count: string | number;
|
||||||
|
followed: boolean;
|
||||||
|
relation: 'both';
|
||||||
|
liked: boolean;
|
||||||
|
liked_count: string | number;
|
||||||
|
collected: boolean;
|
||||||
|
};
|
||||||
|
export type NoteFromHtml = {
|
||||||
|
xsec_token: string;
|
||||||
|
interact_info: InteractInfo;
|
||||||
|
time: number;
|
||||||
|
last_update_time: number;
|
||||||
|
ip_location: string;
|
||||||
|
share_info: {
|
||||||
|
un_share: boolean;
|
||||||
|
};
|
||||||
|
note_id: string;
|
||||||
|
type: 'normal';
|
||||||
|
desc: string;
|
||||||
|
tag_list: string[];
|
||||||
|
at_user_list: { user_id: string; nickname: string; xsec_token: string }[];
|
||||||
|
title: string;
|
||||||
|
user: {
|
||||||
|
user_id: string;
|
||||||
|
nickname: string;
|
||||||
|
avatar: string;
|
||||||
|
xsec_token: string;
|
||||||
|
};
|
||||||
|
image_list: {
|
||||||
|
url: string;
|
||||||
|
trace_id: string;
|
||||||
|
url_per: string;
|
||||||
|
live_photo: boolean;
|
||||||
|
file_id: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
info_list: {
|
||||||
|
image_scend: string; // WB_PRV WB_DFT,
|
||||||
|
url: string;
|
||||||
|
}[];
|
||||||
|
url_default: string;
|
||||||
|
stream: any;
|
||||||
|
}[];
|
||||||
|
};
|
210
packages/xhs/src/libs/xhs.ts
Normal file
210
packages/xhs/src/libs/xhs.ts
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import { XhsClient as XhsClientBase } from '@kevisual/xhs-core';
|
||||||
|
import { Mention, CommonentInfo } from './xhs-type/mention.ts';
|
||||||
|
|
||||||
|
export type Result<T> = {
|
||||||
|
code: number; // 0: success
|
||||||
|
msg?: string;
|
||||||
|
data?: T;
|
||||||
|
success?: boolean;
|
||||||
|
};
|
||||||
|
type SignInfo = {
|
||||||
|
uri: string;
|
||||||
|
data: any;
|
||||||
|
a1: string;
|
||||||
|
web_session?: string;
|
||||||
|
};
|
||||||
|
type SignOptions = {
|
||||||
|
signUrl?: string;
|
||||||
|
};
|
||||||
|
export const getSign = async (signInfo: SignInfo, options?: SignOptions) => {
|
||||||
|
const { uri, data, a1, web_session } = signInfo;
|
||||||
|
// console.log('getSign', uri, data, a1, web_session);
|
||||||
|
// let signUri = new URL(uri, 'http://light.xiongxiao.me:5006').pathname;
|
||||||
|
// signUri = '/api/sns/web/v2/user/me';
|
||||||
|
try {
|
||||||
|
let signUrl = options?.signUrl || 'http://light.xiongxiao.me:5006/sign';
|
||||||
|
// signUrl = 'http://localhost:5005/sign'
|
||||||
|
const signs = await fetch(signUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
uri: uri,
|
||||||
|
data: data,
|
||||||
|
a1,
|
||||||
|
web_session: web_session,
|
||||||
|
}),
|
||||||
|
}).then((res) => res.json());
|
||||||
|
return signs;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
type XhsOptions = {
|
||||||
|
cookie?: string;
|
||||||
|
};
|
||||||
|
type XhsSign = {
|
||||||
|
signUrl?: string;
|
||||||
|
};
|
||||||
|
export class XhsClient extends XhsClientBase {
|
||||||
|
signConfig?: XhsSign;
|
||||||
|
constructor(opts: XhsOptions) {
|
||||||
|
super(opts as any);
|
||||||
|
}
|
||||||
|
printResult(msg: string, response: any) {
|
||||||
|
if (msg === 'response') {
|
||||||
|
console.log('url', response.url);
|
||||||
|
if (response.response) {
|
||||||
|
console.log('status', response.response.status);
|
||||||
|
console.log('data', response.response.data);
|
||||||
|
}
|
||||||
|
} else if (msg === 'request') {
|
||||||
|
// console.log('request', response);
|
||||||
|
} else if (msg === 'html') {
|
||||||
|
// console.log('html', response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取未读消息
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getUnread(): Promise<Result<UnreadCount>> {
|
||||||
|
const url = '/api/sns/web/unread_count';
|
||||||
|
const response = await this.get(url);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
async postRead(type: 'unread_count' | 'likes' | 'mentions' | 'unread_count' = 'mentions') {
|
||||||
|
const uri = '/api/sns/web/v1/message/read';
|
||||||
|
const data = {
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
type RetrunData = {
|
||||||
|
code: number;
|
||||||
|
msg: string;
|
||||||
|
success: boolean;
|
||||||
|
};
|
||||||
|
const response = await this.post(uri, data, {
|
||||||
|
sign: this.sign.bind(this),
|
||||||
|
});
|
||||||
|
return response as Result<RetrunData>;
|
||||||
|
}
|
||||||
|
async getUserInfoFromHtml(userId: string) {
|
||||||
|
type ReturnData = {
|
||||||
|
ip_location: string;
|
||||||
|
desc: string;
|
||||||
|
imageb: string;
|
||||||
|
nickname: string;
|
||||||
|
images: string;
|
||||||
|
red_id: string;
|
||||||
|
gender: number;
|
||||||
|
};
|
||||||
|
const response = await super.getUserInfoFromHtml(userId);
|
||||||
|
return response as ReturnData;
|
||||||
|
}
|
||||||
|
async getFollowNotifications(num = 10, cursor = '') {
|
||||||
|
const url = '/api/sns/web/v1/you/connections';
|
||||||
|
type Connection = {
|
||||||
|
type: 'fllow/you';
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
track_type: string;
|
||||||
|
user: { fstatus: 'both' | 'follows' | 'fans'; red_official_verify_type: number; xsec_token: string; userid: string; nickname: string; images: string };
|
||||||
|
time: number;
|
||||||
|
score: number;
|
||||||
|
};
|
||||||
|
type ReturnData = {
|
||||||
|
message_list: Connection[];
|
||||||
|
has_more: boolean;
|
||||||
|
cursor: number;
|
||||||
|
strCursor: string;
|
||||||
|
};
|
||||||
|
const response = await this.get(url, { num, cursor }, { sign: this.sign.bind(this) });
|
||||||
|
return response as Result<ReturnData>;
|
||||||
|
}
|
||||||
|
async getComment(noteId: string, xsecToken?: string) {}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @uri /api/sns/web/v1/you/mentions
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getMention(num = 10): Promise<Result<Mention>> {
|
||||||
|
const url = '/api/sns/web/v1/you/mentions';
|
||||||
|
const response = await this.get(
|
||||||
|
url,
|
||||||
|
{ num },
|
||||||
|
{
|
||||||
|
sign: this.sign.bind(this),
|
||||||
|
// headers: {
|
||||||
|
// 'x-s':
|
||||||
|
// 'XYW_eyJzaWduU3ZuIjoiNTYiLCJzaWduVHlwZSI6IngyIiwiYXBwSWQiOiJsb2dpbiIsInNpZ25WZXJzaW9uIjoiMSIsInBheWxvYWQiOiJiNGJmMGI2MDVkZTlkOWMyY2RlNTI2YmVjNjM2ZmIxMjkxYzUxMTIyYWQyOTk5MzIyMzNjMmU0OTEzMWFmYzgzY2FmOGQzZDIzMTA0Y2RlNWUzZDZlZDczMDg0MmUzYzAxOTNkY2FjZjEyZjk1NTMzZGQzY2ZkMGFmOTg5MGZmMDIwNWI0MmQwOTNiYmJjMGNkZWU3MzdmOGE2MmRkYWVlYjZhMjcxZDViNjZkNGRjYjA1NDg2MGZhNTllN2M5MjE0ZDE2OTJjYWQyZjZmNzE1NThmYWQ3YjQxZjlhZTNiYjA1ZDExN2YzYWI2ZjRjYzY5MzcyMzRhOTY1OTkxYzMwMWY2YjI1MzY4MTZiNzM1YzhmMWEzOTk2ODhkMWU0NDFiODljYTNlNzQ3YWNlN2M2MGIzZDlhZWQwZDVlZDZlNGFhMDE5MmQ5YzZjNDE1M2IxM2RjODAwYjUzZTQxYWEzOTU4MjJhMzYyMmJjODEwYmY4MzA3MjkwMjY2ZDUzNmQwMjdkMTJlOWEwMzhlZmY1YWU4OTM5NDVlNDhmYmY2MCJ9',
|
||||||
|
// 'x-t': '1746097556685',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
async sign(uri: string, data: any, config: any) {
|
||||||
|
let headers = config?.headers || {};
|
||||||
|
const cookieDist = this.getCookieMap();
|
||||||
|
const web_session = cookieDist['web_session'];
|
||||||
|
const a1 = cookieDist['a1'];
|
||||||
|
const res = await getSign({ uri, data, a1, web_session }, this.signConfig);
|
||||||
|
if (res) {
|
||||||
|
headers['x-s'] = res?.['x-s'];
|
||||||
|
headers['x-t'] = res?.['x-t'];
|
||||||
|
config.headers = headers;
|
||||||
|
} else {
|
||||||
|
console.log('get sign error');
|
||||||
|
throw new Error('get sign error');
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
async getNoteById(noteId: string, xsecToken?: string) {
|
||||||
|
const data = {
|
||||||
|
source_note_id: noteId,
|
||||||
|
image_scenes: ['CRD_WM_WEBP'],
|
||||||
|
};
|
||||||
|
const uri = '/api/sns/web/v1/feed';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.post(uri, data, { sign: this.sign.bind(this) });
|
||||||
|
return response['items'][0]['node_card'];
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 发送评论,content最多为300个字符,如果多余300个字符,发送两次
|
||||||
|
* @param comment
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async postComment(comment: { note_id: string; comment_id: string; content: string }) {
|
||||||
|
const uri = '/api/sns/web/v1/comment/post';
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
note_id: comment.note_id,
|
||||||
|
content: comment.content,
|
||||||
|
target_comment_id: comment.comment_id,
|
||||||
|
at_users: [], //
|
||||||
|
};
|
||||||
|
type PostCommentResponse = {
|
||||||
|
comment: CommonentInfo;
|
||||||
|
time: number;
|
||||||
|
toast: string;
|
||||||
|
};
|
||||||
|
const response = await this.post(uri, data, { sign: this.sign.bind(this) });
|
||||||
|
return response as Result<PostCommentResponse>;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnreadCount = {
|
||||||
|
unread_count: number;
|
||||||
|
likes: number;
|
||||||
|
connections: number;
|
||||||
|
mentions: number;
|
||||||
|
};
|
20
packages/xhs/src/routes/fans/fans.ts
Normal file
20
packages/xhs/src/routes/fans/fans.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { app, xhsServices } from '@/app.ts';
|
||||||
|
import { Parse } from '@/libs/parse.ts';
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'fans',
|
||||||
|
key: 'init-fans',
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
const client = xhsServices.getClient();
|
||||||
|
const res = await client.getFollowNotifications();
|
||||||
|
if (res.code === 0) {
|
||||||
|
const data = res.data;
|
||||||
|
const fans = data.message_list;
|
||||||
|
const fansList = Parse.getConnects(fans);
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
ctx.body = res;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addTo(app);
|
1
packages/xhs/src/routes/index.ts
Normal file
1
packages/xhs/src/routes/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './mentions/index.ts'
|
2
packages/xhs/src/routes/mentions/index.ts
Normal file
2
packages/xhs/src/routes/mentions/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import './mention.ts'
|
||||||
|
import './unread-task.ts'
|
89
packages/xhs/src/routes/mentions/mention.ts
Normal file
89
packages/xhs/src/routes/mentions/mention.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { app, xhsServices } from '@/app.ts';
|
||||||
|
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'getUnread',
|
||||||
|
description: '获取提及列表',
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
//
|
||||||
|
const client = xhsServices.getClient();
|
||||||
|
const res = await client.getUnread();
|
||||||
|
if (res.code === 0) {
|
||||||
|
const unread_count = res.data.unread_count;
|
||||||
|
ctx.body = res.data;
|
||||||
|
} else {
|
||||||
|
ctx.body = {
|
||||||
|
unread_count: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addTo(app);
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'getNote',
|
||||||
|
description: '获取笔记',
|
||||||
|
validator: {
|
||||||
|
node_id: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
xsec_token: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isDebug: true,
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
const node_id = ctx.query.node_id;
|
||||||
|
const xsec_token = ctx.query.xsec_token;
|
||||||
|
const client = xhsServices.getClient();
|
||||||
|
try {
|
||||||
|
console.log('get note by node_id', node_id, 'xsec_token', xsec_token);
|
||||||
|
const res = await client.getNoteByIdFromHtml(node_id, xsec_token);
|
||||||
|
console.log('res====', res);
|
||||||
|
ctx.body = res;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('res', error);
|
||||||
|
ctx.throw(500, '获取笔记失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addTo(app);
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'addComment',
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
const { node_id, comment_id, content } = ctx.query;
|
||||||
|
const client = xhsServices.getClient();
|
||||||
|
const res = await client.postComment({
|
||||||
|
note_id: node_id,
|
||||||
|
comment_id: comment_id,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
ctx.body = res.data;
|
||||||
|
})
|
||||||
|
.addTo(app);
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'getMention',
|
||||||
|
description: '获取提及列表',
|
||||||
|
validator: {
|
||||||
|
num: {
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
const num = ctx.query.num;
|
||||||
|
const client = xhsServices.getClient();
|
||||||
|
const res = await client.getMention(num);
|
||||||
|
ctx.body = res.data;
|
||||||
|
})
|
||||||
|
.addTo(app);
|
58
packages/xhs/src/routes/mentions/unread-task.ts
Normal file
58
packages/xhs/src/routes/mentions/unread-task.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { app, xhsServices } from '@/app.ts';
|
||||||
|
import { Mention } from '@/libs/xhs-type/mention.ts';
|
||||||
|
import { Parse } from '@/libs/parse.ts';
|
||||||
|
import { client } from '@/test/common.ts';
|
||||||
|
app
|
||||||
|
.route({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'run-unread-task',
|
||||||
|
description: '运行未读任务',
|
||||||
|
})
|
||||||
|
.define(async (ctx) => {
|
||||||
|
const unredRes = await app.call({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'getUnread',
|
||||||
|
});
|
||||||
|
console.log('unredRes', unredRes.body, unredRes.code);
|
||||||
|
if (unredRes.code === 200) {
|
||||||
|
const unread_count = unredRes.body.unread_count;
|
||||||
|
const mentionRes = await app.call({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'getMention',
|
||||||
|
payload: {
|
||||||
|
num: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (mentionRes.code === 200) {
|
||||||
|
const mentionList = mentionRes.body.message_list as Mention[];
|
||||||
|
console.log('mentionList', mentionList);
|
||||||
|
for (const mention of mentionList) {
|
||||||
|
const node_id = mention.item_info.id;
|
||||||
|
const xsec_token = mention.item_info.xsec_token;
|
||||||
|
let comment: any = Parse.getComment(mention);
|
||||||
|
const user = Parse.getUser(mentionList[0]);
|
||||||
|
console.log('node_id', node_id, 'xsec_token', xsec_token, comment);
|
||||||
|
console.log('user', user);
|
||||||
|
const noteRes = await app.call({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'getNote',
|
||||||
|
payload: {
|
||||||
|
node_id,
|
||||||
|
xsec_token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const note = Parse.getNote(noteRes.body);
|
||||||
|
console.log('getNote', noteRes.body);
|
||||||
|
const note_id = note.node_id;
|
||||||
|
const comment_id = comment?.comment_id || '';
|
||||||
|
// const commentRes = await client.postComment({
|
||||||
|
// note_id: note_id,
|
||||||
|
// comment_id: comment_id,
|
||||||
|
// content: 'comment',
|
||||||
|
// });
|
||||||
|
// console.log('commentRes', commentRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addTo(app);
|
56
packages/xhs/src/services/xhs-services.ts
Normal file
56
packages/xhs/src/services/xhs-services.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { XhsClient } from '@/libs/xhs.ts';
|
||||||
|
|
||||||
|
type XhsClientOptions = {
|
||||||
|
key: string;
|
||||||
|
cookie: string;
|
||||||
|
signConfig?: {
|
||||||
|
signUrl: string;
|
||||||
|
};
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
type XhsClientMap = {
|
||||||
|
client: XhsClient;
|
||||||
|
key: string;
|
||||||
|
options: XhsClientOptions;
|
||||||
|
};
|
||||||
|
type XhsServicesOptions = {
|
||||||
|
root?: string;
|
||||||
|
};
|
||||||
|
export class XhsServices {
|
||||||
|
map: Map<string, XhsClientMap> = new Map();
|
||||||
|
root: string = 'root';
|
||||||
|
constructor(opts?: XhsServicesOptions) {
|
||||||
|
this.root = opts?.root || this.root;
|
||||||
|
}
|
||||||
|
createClient(options: XhsClientOptions) {
|
||||||
|
const { key, cookie, signConfig } = options;
|
||||||
|
if (this.map.has(key)) {
|
||||||
|
return this.map.get(key);
|
||||||
|
}
|
||||||
|
const client = new XhsClient({ cookie });
|
||||||
|
client.signConfig = signConfig;
|
||||||
|
this.map.set(key, { client, key, options });
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
createRoot(options: Partial<XhsClientOptions>) {
|
||||||
|
options.key = options.key || this.root;
|
||||||
|
return this.createClient(options as XhsClientOptions);
|
||||||
|
}
|
||||||
|
getKey(key?: string) {
|
||||||
|
if (!key) key = this.root;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the XhsClient instance by key
|
||||||
|
* @param key
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getClient(key?: string) {
|
||||||
|
const xhsClient = this.map.get(this.getKey(key));
|
||||||
|
return xhsClient.client;
|
||||||
|
}
|
||||||
|
getXhsClient(key?: string) {
|
||||||
|
const xhsClient = this.map.get(this.getKey(key));
|
||||||
|
return xhsClient;
|
||||||
|
}
|
||||||
|
}
|
14
packages/xhs/src/test/command.ts
Normal file
14
packages/xhs/src/test/command.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { program } from 'commander';
|
||||||
|
import './query/get-components.ts';
|
||||||
|
import './query/get-note.ts';
|
||||||
|
import './query/mention.ts'
|
||||||
|
import './query/unread.ts';
|
||||||
|
import './query/get-userinfo.ts';
|
||||||
|
import './query/get-connections.ts'
|
||||||
|
program
|
||||||
|
.command('test')
|
||||||
|
.description('test command')
|
||||||
|
.action(() => {
|
||||||
|
console.log('test command');
|
||||||
|
});
|
||||||
|
program.parse(process.argv);
|
@ -1,4 +1,8 @@
|
|||||||
import { XhsClient } from '../index.ts';
|
import { XhsClient, xhsServices } from '../index.ts';
|
||||||
export 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;"
|
import { program } from 'commander';
|
||||||
|
export const cookie =
|
||||||
|
'a1=1968ba02ff4xrt6hfrzdiz7ubs82j9y3vx11vfw9c40000317680;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;';
|
||||||
|
|
||||||
export const client = new XhsClient({ cookie } as any);
|
export const client = new XhsClient({ cookie } as any);
|
||||||
|
|
||||||
|
export { program, xhsServices };
|
||||||
|
14
packages/xhs/src/test/query/get-components.ts
Normal file
14
packages/xhs/src/test/query/get-components.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { client } from '../common.ts';
|
||||||
|
import { program } from 'commander';
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('get-commonents')
|
||||||
|
.description('get commonents')
|
||||||
|
.action(async () => {
|
||||||
|
const res = await client.getNoteComments('6810d722000000002100f139', '', {
|
||||||
|
//
|
||||||
|
xsec_token: 'LBEqTFigLzp41AdwQ-E3hbQScnvrx2flLgHElHpQ8zHWc=',
|
||||||
|
top_comment_id: '6810e7d80000000012020c7a'
|
||||||
|
});
|
||||||
|
console.log(res);
|
||||||
|
});
|
14
packages/xhs/src/test/query/get-connections.ts
Normal file
14
packages/xhs/src/test/query/get-connections.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { xhsServices, program } from '../common.ts';
|
||||||
|
import util from 'node:util';
|
||||||
|
const getConnections = async () => {
|
||||||
|
const client = xhsServices.getClient();
|
||||||
|
const res = await client.getFollowNotifications(10, '');
|
||||||
|
console.log(util.inspect(res, { depth: null, colors: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('get-connections')
|
||||||
|
.description('获取关注通知')
|
||||||
|
.action(async () => {
|
||||||
|
await getConnections();
|
||||||
|
});
|
33
packages/xhs/src/test/query/get-note.ts
Normal file
33
packages/xhs/src/test/query/get-note.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { client } from '../common.ts';
|
||||||
|
import { program } from 'commander';
|
||||||
|
|
||||||
|
// client.getNoteComments()
|
||||||
|
|
||||||
|
// client.getNoteByIdFromHtml('67dcc34e000000000602a8eb', 'ABuYS8Xb1o08DlRmMLIabdqnW0OKnLR9nMpDGq5bVRdvk').then((res) => {
|
||||||
|
// console.log(res);
|
||||||
|
// });
|
||||||
|
const getNoteById = async () => {
|
||||||
|
client.getNoteById('67dcc34e000000000602a8eb', 'ABuYS8Xb1o08DlRmMLIabdqnW0OKnLR9nMpDGq5bVRdvk').then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getNote = async () => {
|
||||||
|
const id = '6810d722000000002100f139';
|
||||||
|
const x = 'LBEqTFigLzp41AdwQ-E3hbQScnvrx2flLgHElHpQ8zHWc=';
|
||||||
|
client.getNoteByIdFromHtml(id, x).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
program
|
||||||
|
.command('get-note')
|
||||||
|
.description('get note')
|
||||||
|
.action(async () => {
|
||||||
|
getNote();
|
||||||
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('get-note-by-id')
|
||||||
|
.description('get note by id')
|
||||||
|
.action(async () => {
|
||||||
|
getNoteById();
|
||||||
|
});
|
12
packages/xhs/src/test/query/get-sign.ts
Normal file
12
packages/xhs/src/test/query/get-sign.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { client, program } from '../common.ts';
|
||||||
|
|
||||||
|
const getSign = async () => {
|
||||||
|
const uri = '/api/sns/web/v1/you/mentions';
|
||||||
|
const data = null;
|
||||||
|
const config = { headers: {} };
|
||||||
|
const res = await client.sign(uri, data, config);
|
||||||
|
console.log('getSign', res);
|
||||||
|
};
|
||||||
|
// getSign();
|
||||||
|
|
||||||
|
program.command('sign').description('get sign').action(getSign);
|
21
packages/xhs/src/test/query/get-userinfo.ts
Normal file
21
packages/xhs/src/test/query/get-userinfo.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { client, program } from '../common.ts';
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('get-userinfo')
|
||||||
|
.description('获取用户信息')
|
||||||
|
.action(async () => {
|
||||||
|
const res = await client.getSelfInfoV2();
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getUserById = async (userId: string) => {
|
||||||
|
const res = await client.getUserInfoFromHtml(userId);
|
||||||
|
console.log(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('get-userinfo-by-id <userId>')
|
||||||
|
.description('获取用户信息')
|
||||||
|
.action(async (userId: string) => {
|
||||||
|
await getUserById(userId);
|
||||||
|
});
|
@ -1,15 +0,0 @@
|
|||||||
import { client } from '../common.ts';
|
|
||||||
|
|
||||||
// client.getNoteComments()
|
|
||||||
|
|
||||||
// client.getNoteById('67dcc34e000000000602a8eb', 'ABuYS8Xb1o08DlRmMLIabdqnW0OKnLR9nMpDGq5bVRdvk').then((res) => {
|
|
||||||
// console.log(res);
|
|
||||||
// });
|
|
||||||
// client.getNoteByIdFromHtml('67dcc34e000000000602a8eb', 'ABuYS8Xb1o08DlRmMLIabdqnW0OKnLR9nMpDGq5bVRdvk').then((res) => {
|
|
||||||
// console.log(res);
|
|
||||||
// });
|
|
||||||
const id = '6810d722000000002100f139';
|
|
||||||
const x = 'LBEqTFigLzp41AdwQ-E3hbQScnvrx2flLgHElHpQ8zHWc=';
|
|
||||||
client.getNoteByIdFromHtml(id, x).then((res) => {
|
|
||||||
console.log(res);
|
|
||||||
});
|
|
@ -1,5 +0,0 @@
|
|||||||
import { client } from '../common.ts';
|
|
||||||
|
|
||||||
client.getSelfInfoV2().then((res) => {
|
|
||||||
console.log(res);
|
|
||||||
});
|
|
26
packages/xhs/src/test/query/mention.ts
Normal file
26
packages/xhs/src/test/query/mention.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { client, program } from '../common.ts';
|
||||||
|
import util from 'node:util';
|
||||||
|
const getMentions = async () => {
|
||||||
|
try {
|
||||||
|
const res = await client.getMention();
|
||||||
|
if (res.code === 0) {
|
||||||
|
console.log('getMentionNotifications', util.inspect(res, { depth: 10 }));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
program.command('mention').description('get mention notifications').action(getMentions);
|
||||||
|
|
||||||
|
const getTestMentionNote = async () => {
|
||||||
|
const id = '68136dab0000000007034c46';
|
||||||
|
const xsec_token = 'LByEmonX8WfJ9ebpAowVbOZcNgtJAAC8K2zx5Rmr_9Q7Y=';
|
||||||
|
const title = '在笔记中@了你';
|
||||||
|
const type = 'mention/item';
|
||||||
|
const track_type = '2';
|
||||||
|
const note = await client.getNoteByIdFromHtml(id, xsec_token);
|
||||||
|
console.log('note', note);
|
||||||
|
};
|
||||||
|
|
||||||
|
program.command('test-mention').description('get mention note').action(getTestMentionNote);
|
@ -1,5 +1,9 @@
|
|||||||
import { client } from '../common.ts';
|
import { client, program } from '../common.ts';
|
||||||
|
|
||||||
client.getUnread().then((res) => {
|
const getUnread = () => {
|
||||||
console.log(res);
|
client.getUnread().then((res) => {
|
||||||
});
|
console.log(res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
program.command('unread').description('获取未读消息').action(getUnread);
|
||||||
|
6
packages/xhs/src/test/run/mention.ts
Normal file
6
packages/xhs/src/test/run/mention.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { app } from '@/index.ts';
|
||||||
|
|
||||||
|
app.parse({
|
||||||
|
path: 'mention',
|
||||||
|
key: 'run-unread-task',
|
||||||
|
});
|
15
packages/xhs/tsconfig.json
Normal file
15
packages/xhs/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"extends": "@kevisual/types/json/backend.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
],
|
||||||
|
"exclude": [],
|
||||||
|
}
|
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@ -112,6 +112,9 @@ importers:
|
|||||||
'@kevisual/xhs-core':
|
'@kevisual/xhs-core':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../xhs-core
|
version: link:../xhs-core
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^22.15.3
|
||||||
|
version: 22.15.3
|
||||||
|
|
||||||
packages/xhs-core:
|
packages/xhs-core:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
@ -292,67 +295,56 @@ packages:
|
|||||||
resolution: {integrity: sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==}
|
resolution: {integrity: sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm-musleabihf@4.40.1':
|
'@rollup/rollup-linux-arm-musleabihf@4.40.1':
|
||||||
resolution: {integrity: sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==}
|
resolution: {integrity: sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-gnu@4.40.1':
|
'@rollup/rollup-linux-arm64-gnu@4.40.1':
|
||||||
resolution: {integrity: sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==}
|
resolution: {integrity: sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-musl@4.40.1':
|
'@rollup/rollup-linux-arm64-musl@4.40.1':
|
||||||
resolution: {integrity: sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==}
|
resolution: {integrity: sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-loongarch64-gnu@4.40.1':
|
'@rollup/rollup-linux-loongarch64-gnu@4.40.1':
|
||||||
resolution: {integrity: sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==}
|
resolution: {integrity: sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-powerpc64le-gnu@4.40.1':
|
'@rollup/rollup-linux-powerpc64le-gnu@4.40.1':
|
||||||
resolution: {integrity: sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==}
|
resolution: {integrity: sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-gnu@4.40.1':
|
'@rollup/rollup-linux-riscv64-gnu@4.40.1':
|
||||||
resolution: {integrity: sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==}
|
resolution: {integrity: sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-musl@4.40.1':
|
'@rollup/rollup-linux-riscv64-musl@4.40.1':
|
||||||
resolution: {integrity: sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==}
|
resolution: {integrity: sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-s390x-gnu@4.40.1':
|
'@rollup/rollup-linux-s390x-gnu@4.40.1':
|
||||||
resolution: {integrity: sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==}
|
resolution: {integrity: sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-gnu@4.40.1':
|
'@rollup/rollup-linux-x64-gnu@4.40.1':
|
||||||
resolution: {integrity: sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==}
|
resolution: {integrity: sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-musl@4.40.1':
|
'@rollup/rollup-linux-x64-musl@4.40.1':
|
||||||
resolution: {integrity: sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==}
|
resolution: {integrity: sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-win32-arm64-msvc@4.40.1':
|
'@rollup/rollup-win32-arm64-msvc@4.40.1':
|
||||||
resolution: {integrity: sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==}
|
resolution: {integrity: sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==}
|
||||||
|
55
src/task/test/worker.ts
Normal file
55
src/task/test/worker.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { redis } from '@/modules/redis.ts';
|
||||||
|
import { Queue, Worker } from 'bullmq';
|
||||||
|
import { clamp } from 'lodash-es';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
export const queue = new Queue('TEST_QUEUE');
|
||||||
|
export const sleep = (ms: number) => {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
};
|
||||||
|
export const worker = new Worker(
|
||||||
|
'TEST_QUEUE',
|
||||||
|
async (job) => {
|
||||||
|
const startTime = Date.now();
|
||||||
|
console.log('job', job.name, job.data);
|
||||||
|
await sleep(1000);
|
||||||
|
const endTime = Date.now();
|
||||||
|
const duration = endTime - startTime;
|
||||||
|
job.updateProgress(50);
|
||||||
|
const isRetry = job.attemptsMade > 0;
|
||||||
|
console.log('job attemptsMade', job.attemptsMade, 'isRetry', isRetry);
|
||||||
|
if (job.data.index === 1 && !isRetry) {
|
||||||
|
// set fail
|
||||||
|
throw new Error('test error');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
startTime: startTime,
|
||||||
|
endTime: endTime,
|
||||||
|
duration: duration,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
connection: redis,
|
||||||
|
limiter: {
|
||||||
|
max: 1,
|
||||||
|
duration: 10000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
worker.on('completed', async (job) => {
|
||||||
|
console.log('job completed', job.name, job.id, job.returnvalue);
|
||||||
|
});
|
||||||
|
worker.on('failed', async (job) => {
|
||||||
|
console.log('job failed', job.name, job.id, job.failedReason, await job.getState());
|
||||||
|
});
|
||||||
|
|
||||||
|
queue.pause();
|
||||||
|
await queue.drain();
|
||||||
|
const arr = Array.from({ length: 2 }, (_, i) => i + 1);
|
||||||
|
for (const i of arr) {
|
||||||
|
queue.add('job' + i, { name: 'test' + i, index: i }, { jobId: 'job' + i, removeOnFail: true, removeOnComplete: true, attempts: 3 });
|
||||||
|
}
|
||||||
|
const job = await queue.getJob('job1');
|
||||||
|
console.log('job', job);
|
||||||
|
await sleep(4000)
|
||||||
|
queue.resume();
|
Loading…
x
Reference in New Issue
Block a user