返回 AiToEarn
index.ts
1 /*
2 * @Author: nevin
3 * @Date: 2025-02-19 17:54:53
4 * @LastEditTime: 2025-03-25 13:18:41
5 * @LastEditors: nevin
6 * @Description:
7 */
8 import { PlatformBase } from '../../PlatformBase';
9 import {
10 AccountInfoTypeRV,
11 CommentData,
12 CookiesType,
13 DashboardData,
14 IAccountInfoParams,
15 IGetLocationDataParams,
16 IGetTopicsParams,
17 IGetTopicsResponse,
18 IGetUsersParams,
19 VideoCallbackType,
20 WorkData,
21 } from '../../plat.type';
22 import { PublishVideoResult } from '../../module';
23 import { kwaiPub } from '../../../../plat/Kwai';
24 import { IRequestNetResult } from '../../../../plat/requestNet';
25 import { IKwaiUserInfoResponse } from '../../../../plat/Kwai/kwai.type';
26 import { PlatType } from '../../../../../commont/AccountEnum';
27 import { AccountModel } from '../../../../db/models/account';
28 import dayjs from 'dayjs';
29 import { VideoModel } from '../../../../db/models/video';
30 import {
31 PubStatus,
32 VisibleTypeEnum,
33 } from '../../../../../commont/publish/PublishEnum';
34 import KwaiPubListener from './KwaiPubListener';
35
36 export class Kwai extends PlatformBase {
37 constructor() {
38 super(PlatType.KWAI);
39 }
40
41 /**
42 * 快手账户登录
43 */
44 async login() {
45 const req = await kwaiPub.login();
46 const userInfo = await this.formatUserInfo(req.userInfo, req.cookies);
47 if (!userInfo) return null;
48 userInfo.loginCookie = JSON.stringify(req.cookies);
49 return userInfo;
50 }
51
52 /**
53 * 格式化用户信息
54 * @param req
55 * @param cookies
56 */
57 async formatUserInfo(
58 req: IRequestNetResult<IKwaiUserInfoResponse>,
59 cookies: Electron.Cookie[],
60 ) {
61 const { data } = req.data;
62 const res = await kwaiPub.getHomeInfo(cookies);
63 if (!data) return null;
64 return {
65 userId: '',
66 loginCookie: '',
67 type: this.type,
68 uid: `${data.userInfo.userId}` || '',
69 account: `${data.userInfo.userId}` || '',
70 avatar: data.userInfo.avatar || '',
71 nickname: data.userInfo.name || '',
72 fansCount: res.data.data.fansCnt,
73 };
74 }
75
76 /**
77 * 获取账号信息
78 * @param params
79 */
80 async getAccountInfo(params: IAccountInfoParams): Promise<AccountInfoTypeRV> {
81 const res = await kwaiPub.getAccountInfo(params.cookies);
82 return await this.formatUserInfo(res, params.cookies);
83 }
84
85 async videoPublish(
86 params: VideoModel,
87 callback: VideoCallbackType,
88 ): Promise<PublishVideoResult> {
89 return new Promise(async (resolve) => {
90 const result = await kwaiPub
91 .pubVideo({
92 proxy: params.proxyIp || '',
93 publishTime: params.timingTime?.getTime(),
94 mentions: params.mentionedUserInfo.map((v) => v.label),
95 topics: params.topics || [],
96 videoPath: params.videoPath || '',
97 coverPath: params.coverPath || '',
98 cookies: params.cookies!,
99 desc: params.desc + params.topics.map((v) => `#${v}`).join(' '),
100 callback,
101 photoStatus:
102 params.visibleType === VisibleTypeEnum.Public
103 ? 1
104 : params.visibleType === VisibleTypeEnum.Private
105 ? 2
106 : 4,
107 poiInfo: params.location
108 ? {
109 poiId: params.location.id,
110 latitude: `${params.location.latitude}`,
111 longitude: `${params.location.longitude}`,
112 }
113 : undefined,
114 })
115 .catch((e) => {
116 resolve({
117 code: 0,
118 msg: e,
119 });
120 });
121 if (!result || !result.publishId)
122 return resolve({
123 code: 0,
124 msg: '网络繁忙,请稍后重试',
125 });
126
127 KwaiPubListener.start(JSON.stringify(params.cookies), +result.publishId);
128 return resolve({
129 code: 1,
130 msg: '发布成功',
131 dataId: result.publishId,
132 previewVideoLink: result.shareLink,
133 pubStatus: PubStatus.Audit,
134 });
135 });
136 }
137
138 async getStatistics(account: AccountModel) {
139 const res = await kwaiPub.getHomeInfo(JSON.parse(account.loginCookie));
140 return {
141 fansCount: res?.data?.data?.fansCnt,
142 workCount: 0,
143 };
144 }
145
146 async getDashboard(account: AccountModel, time: string[] = []) {
147 const res = await kwaiPub.getHomeOverview(JSON.parse(account.loginCookie));
148 const dashboard: DashboardData[] = [];
149 const startTime = new Date(time[0]).getTime() - 86400001;
150 const endTime = new Date(time[1]).getTime() + 1;
151
152 res?.data?.data?.basicData?.map((v1, i1) => {
153 v1.trendData.map((v2, i2) => {
154 const currTime = dayjs(v2.date, 'YYYYMMDD').valueOf();
155 if (currTime >= startTime && currTime <= endTime) {
156 if (!dashboard[i2])
157 dashboard[i2] = {
158 comment: 0,
159 fans: 0,
160 forward: 0,
161 like: 0,
162 read: 0,
163 time: '',
164 collect: 0, // TODO: 获取收藏数据
165 };
166 const item = dashboard[i2];
167
168 item.time = v2.date;
169 if (v1.tab === 'LIKE') {
170 item.like = v2.count;
171 } else if (v1.tab === 'PURE_INCREASE_FAN') {
172 item.fans = v2.count;
173 } else if (v1.tab === ' COMMENT') {
174 item.comment = v2.count;
175 } else if (v1.tab === 'SHARE') {
176 item.forward = v2.count;
177 } else if (v1.tab === 'PLAY') {
178 item.read = v2.count;
179 }
180 }
181 });
182 });
183
184 return dashboard.filter(Boolean);
185 }
186
187 /**
188 * 获取作品列表
189 * @param pageInfo
190 * @returns
191 */
192 async getWorkList(account: AccountModel, pcursor?: string) {
193 const cookie: CookiesType = JSON.parse(account.loginCookie);
194 const res = await kwaiPub.getPhotoList(cookie, Number(pcursor));
195
196 const photoList = res.data.data.photoList;
197 const list: WorkData[] = photoList.map((v) => {
198 return {
199 dataId: v.photoId,
200 readCount: v.playCount,
201 likeCount: v.likeCount,
202 commentCount: v.commentCount,
203 title: v.title,
204 coverUrl: v.cover,
205 };
206 });
207
208 return {
209 list,
210 pageInfo: {
211 hasMore: !!res.data.data.pcursor,
212 count: res.data.data.totalCount,
213 pcursor: res.data.data.pcursor + '',
214 },
215 };
216 }
217
218 /**
219 * 搜索作品列表
220 * @param pageInfo
221 * @returns
222 */
223 async getsearchNodeList(
224 account: AccountModel,
225 qe: string,
226 pageInfo?: any,
227 ): Promise<{
228 list: WorkData[];
229 orgList: any[];
230 pageInfo: any;
231 }> {
232 const cookie: CookiesType = JSON.parse(account.loginCookie);
233 const res = await kwaiPub.getsearchNodeList(cookie, qe, pageInfo);
234 console.log('----------- getsearchNodeList --- res: ', res.data);
235 const photoList = res.data.data?.visionSearchPhoto.feeds || [];
236 // console.log('----------- getsearchNodeList --- photoList: ', photoList[0]);
237 // const list: WorkData[] = photoList.map((v) => {
238 // return {
239 // dataId: v.photo.id,
240 // readCount: v.photo.viewCount,
241 // likeCount: v.photo.likeCount,
242 // commentCount: v.photo.commentCount,
243 // title: v.photo.caption,
244 // coverUrl: v.photo.coverUrl,
245 // };
246 // });
247 const list: WorkData[] = [];
248 for (const s of photoList) {
249 list.push({
250 dataId: s.photo.id,
251 readCount: s.photo.viewCount,
252 likeCount: s.photo.likeCount,
253 collectCount: s.photo.collectCount,
254 commentCount: s.photo.commentCount,
255 title: s.photo.caption,
256 coverUrl: s.photo.coverUrl,
257 option: {
258 xsec_token: s.xsec_token || '',
259 },
260 author: {
261 name: s.author?.name,
262 id: s.author?.id,
263 avatar: s.author?.headerUrl,
264 },
265 data: s,
266 });
267 }
268
269 return {
270 list: list,
271 orgList: res.data.data?.visionSearchPhoto,
272 pageInfo: {
273 hasMore: photoList.length > 1 ? true : false,
274 count: res.data.data?.visionSearchPhoto.length,
275 pcursor: Number(res.data.data?.visionSearchPhoto?.pcursor) + 1 || 1,
276 },
277 };
278 }
279
280 /**
281 * TODO: 未实现
282 * @returns
283 * @param dataId
284 */
285 async getWorkData(dataId: string) {
286 return {
287 dataId: '',
288 };
289 }
290
291 async getCommentList(
292 account: AccountModel,
293 data: WorkData,
294 pcursor?: string,
295 ) {
296 const cookie: CookiesType = JSON.parse(account.loginCookie);
297 const res = await kwaiPub.getCommentList(
298 cookie,
299 data.dataId,
300 pcursor ? Number.parseInt(pcursor) : undefined,
301 );
302
303 const list: CommentData[] = [];
304 for (const v of res.data.data.list) {
305 const subList: CommentData[] = [];
306
307 if (!!v.subCommentCount) {
308 const subRes = await kwaiPub.getSubCommentList(
309 cookie,
310 data.dataId,
311 v.commentId,
312 );
313
314 for (const v1 of subRes.data.data.list) {
315 subList.push({
316 userId: v1.authorId + '',
317 dataId: v1.photoId + '',
318 commentId: v1.commentId + '',
319 content: v1.content,
320 likeCount: undefined,
321 nikeName: v1.headurl,
322 headUrl: v1.headurl,
323 data: v1,
324 subCommentList: [],
325 });
326 }
327 }
328
329 list.push({
330 userId: v.authorId + '',
331 dataId: v.photoId + '',
332 commentId: v.commentId + '',
333 parentCommentId: undefined,
334 content: v.content,
335 likeCount: undefined,
336 nikeName: v.headurl,
337 headUrl: v.headurl,
338 data: v,
339 subCommentList: subList,
340 });
341 }
342
343 return {
344 list: list,
345 pageInfo: {
346 count: 0,
347 pcursor: res.data.data.pcursor + '',
348 hasMore: !!res.data.data.pcursor,
349 },
350 };
351 }
352
353 /**
354 * 获取其他视频评论列表
355 * @param account
356 * @param data
357 * @param pcursor
358 * @returns
359 */
360 async getCreatorCommentListByOther(
361 account: AccountModel,
362 data: WorkData,
363 pcursor?: string,
364 ) {
365 const cookie: CookiesType = JSON.parse(account.loginCookie);
366 const res = await kwaiPub.getVideoCommentList(cookie, data.dataId, pcursor);
367
368 const list: CommentData[] = [];
369 for (const v of res.data.data.visionCommentList?.rootComments || []) {
370 list.push({
371 userId: v.authorId + '',
372 dataId: data.dataId + '',
373 commentId: v.commentId + '',
374 parentCommentId: undefined,
375 content: v.content,
376 likeCount: v.likedCount,
377 nikeName: v.authorName,
378 headUrl: v.headurl,
379 data: v,
380 subCommentList: v.subComments,
381 });
382 }
383
384 return {
385 list: list,
386 pageInfo: {
387 count: 0,
388 pcursor: res.data.data.pcursor + '',
389 hasMore: !!res.data.data.pcursor,
390 },
391 };
392 }
393
394 getCreatorSecondCommentListByOther(
395 account: AccountModel,
396 data: WorkData,
397 root_comment_id: string,
398 pcursor?: string,
399 ): Promise<any> {
400 return new Promise((resolve, reject) => {});
401 }
402
403 // 创建其他视频评论
404 async createCommentByOther(
405 account: AccountModel,
406 dataId: string, // 作品ID
407 content: string,
408 authorId?: string,
409 ) {
410 const cookie: CookiesType = JSON.parse(account.loginCookie);
411 const res = await kwaiPub.videoCommentByOther(
412 cookie,
413 dataId,
414 content,
415 authorId,
416 );
417 console.log('------ kaishou createComment res ----', res);
418
419 return res.data;
420 }
421
422 // 回复其他评论
423 async replyCommentByOther(
424 account: AccountModel,
425 commentId: string,
426 content: string,
427 option: {
428 dataId?: string; // 作品ID
429 comment: any; // 辅助数据,原数据
430 videoAuthId?: string; // 视频作者ID
431 },
432 ) {
433 const cookie: CookiesType = JSON.parse(account.loginCookie);
434
435 const res = await kwaiPub.replyCommentByOther(cookie, content, {
436 photoId: option.dataId,
437 replyToCommentId: commentId,
438 replyTo: option.comment.authorId,
439 photoAuthorId: option.videoAuthId,
440 });
441
442 return res;
443 }
444
445 async createComment(
446 account: AccountModel,
447 dataId: string, // 作品ID
448 content: string,
449 ) {
450 const cookie: CookiesType = JSON.parse(account.loginCookie);
451 const res = await kwaiPub.commentAdd(cookie, content, {
452 photoId: dataId,
453 });
454 console.log('------ kaishou createComment res ----', res);
455
456 return false;
457 }
458
459 /**
460 * 回复评论
461 * @param account
462 * @param commentId
463 * @param content
464 * @param option
465 * @returns
466 */
467 async replyComment(
468 account: AccountModel,
469 commentId: string,
470 content: string,
471 option: {
472 dataId?: string; // 作品ID
473 comment: any; // 辅助数据,原数据
474 },
475 ) {
476 const cookie: CookiesType = JSON.parse(account.loginCookie);
477
478 const res = await kwaiPub.commentAdd(cookie, content, {
479 photoId: option.dataId,
480 replyToCommentId: Number.parseInt(commentId),
481 replyTo: option.comment.authorId,
482 });
483
484 if (res.status !== 200 || res.data.result !== 1) return false;
485 return false;
486 }
487
488 async loginCheck(account: AccountModel) {
489 let online = false;
490 try {
491 const res = await kwaiPub.getAccountInfo(JSON.parse(account.loginCookie));
492 online = !(res?.status !== 200 || !res?.data?.data?.userInfo?.userId);
493 } catch (e) {
494 console.warn('快手登录状态检测错误:', e);
495 online = false;
496 }
497 return {
498 online,
499 };
500 }
501
502 async getTopics({
503 keyword,
504 account,
505 }: IGetTopicsParams): Promise<IGetTopicsResponse> {
506 const res = await kwaiPub.getTopics({
507 keyword,
508 cookies: JSON.parse(account.loginCookie),
509 });
510 return {
511 status: res.status,
512 data: res.data?.data?.tags?.map((v) => {
513 return {
514 id: v.tag.id,
515 name: v.tag.name,
516 view_count: v.viewCount,
517 };
518 }),
519 };
520 }
521
522 async getUsers(params: IGetUsersParams) {
523 const res = await kwaiPub.getUsers({
524 page: params.page,
525 cookies: JSON.parse(params.account.loginCookie),
526 });
527
528 return {
529 status: res.status,
530 data: res.data?.data?.list?.map((v) => {
531 return {
532 image: v.headUrl,
533 id: `${v.userId}`,
534 name: v.userName,
535 follower_count: v.fansCount,
536 };
537 }),
538 };
539 }
540
541 async getLocationData(params: IGetLocationDataParams) {
542 const res = await kwaiPub.getLocations({
543 cookies: params.cookie!,
544 cityName: params.cityName,
545 keyword: params.keywords,
546 });
547 return {
548 status: res.status,
549 data: res.data?.locations?.map((v) => {
550 return {
551 name: v.title,
552 simpleAddress: v.address,
553 id: `${v.id}`,
554 latitude: v.latitude,
555 longitude: v.longitude,
556 city: v.city,
557 };
558 }),
559 };
560 }
561
562 /**
563 * 点赞
564 */
565 async dianzanDyOther(
566 account: AccountModel,
567 dataId: string,
568 option?: any,
569 ): Promise<any> {
570 const cookie: CookiesType = JSON.parse(account.loginCookie);
571 const res = await kwaiPub.dianzanDyOther(cookie, dataId, option);
572 console.log('------ dianzanDyOther -- Kwai --- res: ', res);
573
574 return res.data;
575 }
576
577 /**
578 * 收藏
579 */
580 shoucangDyOther(account: AccountModel, pcursor?: string): Promise<any> {
581 return new Promise((resolve, reject) => {});
582 }
583 }
584
585 const kwai = new Kwai();
586 export default kwai;
587
587 lines TYPESCRIPT