This commit is contained in:
2025-05-02 15:39:44 +08:00
parent e58adbc46b
commit ee483aa87e
32 changed files with 919 additions and 621 deletions

View 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;
};