返回 AiToEarn
index.ts
1 /*
2 * @Author: nevin
3 * @Date: 2025-02-08 11:40:45
4 * @LastEditTime: 2025-03-31 09:42:25
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 {
24 DouyinPlatformSettingType,
25 douyinService,
26 } from '../../../../plat/douyin';
27 import { PlatType } from '../../../../../commont/AccountEnum';
28 import { AccountModel } from '../../../../db/models/account';
29 import { VisibleTypeEnum } from '../../../../../commont/publish/PublishEnum';
30 import { IRequestNetResult } from '../../../../plat/requestNet';
31 import { VideoModel } from '../../../../db/models/video';
32 import { ImgTextModel } from '../../../../db/models/imgText';
33 import { WorkData as WorkDataModel } from '../../../../db/models/workData';
34
35 export type PubVideoOptin = {
36 token: string;
37 cover: string;
38 topics: string[];
39 poiInfo?: {
40 poiId: string; // "6601136811005708292"
41 poiName: string;
42 };
43 };
44
45 export class Douyin extends PlatformBase {
46 constructor() {
47 super(PlatType.Douyin);
48 }
49
50 /**
51 * 登录
52 * @returns
53 */
54 async login() {
55 try {
56 const { success, data, error } = await douyinService.loginOrView('login');
57 if (!success || !data) {
58 console.log('Login process failed:', error);
59 return null;
60 }
61
62 const userInfo = await douyinService.getUserInfo(data.cookie);
63
64 const loginCookie =
65 typeof data.cookie === 'string'
66 ? data.cookie
67 : JSON.stringify(data.cookie);
68
69 return {
70 loginCookie,
71 loginTime: new Date(),
72 type: this.type,
73 uid: userInfo.authorId,
74 account: userInfo.uid,
75 avatar: userInfo.avatar,
76 nickname: userInfo.nickname,
77 token: data.localStorage,
78 };
79 } catch (error) {
80 console.error('Login process failed:', error);
81 return null;
82 }
83 }
84
85 async loginCheck(account: AccountModel) {
86 const online = await douyinService.checkLoginStatus(account.loginCookie);
87 return {
88 online,
89 };
90 }
91
92 async getAccountInfo(params: IAccountInfoParams): Promise<AccountInfoTypeRV> {
93 const res = await douyinService.getUserInfo(params.cookies);
94
95 return {
96 type: this.type,
97 uid: res.authorId,
98 account: res.authorId,
99 avatar: res.avatar,
100 nickname: res.nickname,
101 fansCount: res.fansCount,
102 };
103 }
104
105 async getStatistics(account: AccountModel) {
106 const cookie: CookiesType = JSON.parse(account.loginCookie);
107
108 const accountInfo = await douyinService.getUserInfo(cookie);
109 return {
110 fansCount: accountInfo.fansCount,
111 workCount: 0, // TODO: 作品数量
112 };
113 }
114
115 async getDashboard(account: AccountModel, time: string[] = []) {
116 const res: DashboardData[] = [];
117 try {
118 const ret = await douyinService.getDashboardFunc(
119 account.loginCookie,
120 time[0],
121 time[1],
122 );
123 if (!ret.success) throw new Error('获取三方平台数据失败');
124 // console.log('@@@ret.data', ret.data)
125 for (const item of ret.data) {
126 res.push({
127 time: item.date,
128 fans: item.zhangfen,
129 read: item.bofang,
130 comment: item.pinglun,
131 like: item.dianzan,
132 forward: item.fenxiang,
133 collect: 0,
134 });
135 }
136 } catch (error) {
137 console.log('------ getDashboard wxSph ---', error);
138 }
139
140 return res.reverse();
141 }
142
143 /**
144 * 获取作品列表
145 * @param pageInfo
146 * @returns
147 */
148 async getWorkList(account: AccountModel, pcursor?: string) {
149 const res = await douyinService.getCreatorItems(
150 JSON.parse(account.loginCookie),
151 pcursor,
152 );
153
154 const list: WorkData[] = [];
155 for (const element of res.data.item_info_list) {
156 list.push({
157 dataId: element.item_id,
158 readCount: undefined,
159 likeCount: undefined,
160 collectCount: undefined,
161 forwardCount: undefined,
162 commentCount: element.comment_count,
163 income: undefined,
164 title: element.title,
165 desc: undefined,
166 coverUrl: element.cover_image_url,
167 videoUrl: undefined,
168 createTime: element.create_time,
169 });
170
171 pcursor = element.cursor;
172 }
173
174 return {
175 list: list,
176 pageInfo: {
177 count: res.data.total_count,
178 hasMore: res.data.has_more,
179 pcursor: pcursor,
180 },
181 };
182 }
183
184 /**
185 * TODO: 未实现
186 * @returns
187 * @param dataId
188 */
189 async getWorkData(dataId: string) {
190 return {
191 dataId: '',
192 };
193 }
194
195 /**
196 * 获取搜索作品列表
197 * @param account
198 * @param data
199 * @param pcursor
200 * @returns
201 */
202 async getsearchNodeList(account: AccountModel, qe: string, pageInfo?: any) {
203 const cookie: CookiesType = JSON.parse(account.loginCookie);
204 console.log(
205 'getsearchNodeList',
206 pageInfo.count,
207 pageInfo.pcursor,
208 pageInfo.postFirstId,
209 );
210 const res = await douyinService.getSearchNodeList(cookie, qe, {
211 count: pageInfo.count,
212 pcursor: pageInfo.pcursor,
213 postFirstId: pageInfo.postFirstId,
214 });
215
216 const list: WorkData[] = [];
217 console.log('------douyin getsearchNodeList res: ', res.data.data);
218 // console.log('------douyin getsearchNodeList res.data.cursor: ', res.data.cursor);
219 for (const s of res.data.data) {
220 const v = s.aweme_info;
221 list.push({
222 dataId: v.aweme_id,
223 readCount: v.statistics?.digg_count,
224 likeCount: v.statistics?.digg_count,
225 collectCount: v.statistics?.collect_count,
226 commentCount: v.statistics?.comment_count,
227 title: v.desc,
228 coverUrl: v.video.cover.url_list[0] || '',
229 option: {
230 xsec_token: v.xsec_token || '',
231 },
232 author: {
233 name: v.author?.nickname,
234 id: v.author?.uid,
235 avatar: v.author?.avatar_thumb.url_list[0],
236 },
237 data: v,
238 });
239 }
240
241 // console.log('------douyin getsearchNodeList !!res.data.has_more^^: ', !!res.data.has_more);
242 return {
243 list,
244 orgList: res.data,
245 pageInfo: {
246 count: pageInfo.pcursor,
247 pcursor: res.data.cursor + '',
248 hasMore: !!res.data.has_more,
249 },
250 };
251 }
252
253 /**
254 * 获取评论列表
255 * @param account
256 * @param data
257 * @param pcursor
258 * @returns
259 */
260 async getCommentList(
261 account: AccountModel,
262 data: WorkData,
263 pcursor?: string,
264 ) {
265 const cookie: CookiesType = JSON.parse(account.loginCookie);
266 const res = await douyinService.getCreatorCommentList(cookie, data.dataId, {
267 count: pcursor ? 20 : undefined,
268 cursor: pcursor || undefined,
269 });
270
271 const list: CommentData[] = [];
272
273 for (const v of res.data.comment_info_list) {
274 const subList: CommentData[] = [];
275 if (v.level === 1 && Number.parseInt(v.reply_count) > 0) {
276 const res2 = await douyinService.getCreatorCommentReplyList(
277 cookie,
278 v.comment_id,
279 {
280 cursor: 0 + '',
281 count: 20,
282 },
283 );
284
285 if (res2.status === 200 && res2.data.status_code === 0) {
286 for (const element of res2.data.comment_info_list) {
287 subList.push({
288 userId: element.user_info.user_id,
289 dataId: data.dataId,
290 commentId: element.comment_id,
291 content: element.text,
292 likeCount: Number.parseInt(element.digg_count),
293 nikeName: element.user_info.screen_name,
294 headUrl: element.user_info.avatar_url,
295 data: element,
296 subCommentList: [],
297 });
298 }
299 }
300 }
301
302 list.push({
303 userId: v.user_info.user_id,
304 dataId: data.dataId,
305 commentId: v.comment_id,
306 content: v.text,
307 likeCount: Number.parseInt(v.digg_count),
308 nikeName: v.user_info.screen_name,
309 headUrl: v.user_info.avatar_url,
310 data: v,
311 subCommentList: subList,
312 });
313 }
314
315 return {
316 list,
317 pageInfo: {
318 count: res.data.total_count,
319 pcursor: res.data.cursor + '',
320 hasMore: res.data.has_more,
321 },
322 };
323 }
324
325 // 其他人作品评论列表
326 async getCreatorCommentListByOther(
327 account: AccountModel,
328 data: WorkData,
329 pcursor?: string,
330 ) {
331 const cookie: CookiesType = JSON.parse(account.loginCookie);
332 const res: any = await douyinService.getCreatorCommentListByOther(
333 cookie,
334 data.dataId,
335 {
336 count: pcursor ? 20 : undefined,
337 cursor: pcursor || undefined,
338 },
339 );
340
341 const list: any[] = [];
342 console.log(
343 '------ douyinService.getCreatorCommentListByOther',
344 res.data.comments,
345 );
346
347 for (const v of res.data.comments) {
348 list.push({
349 userId: v.user.uid,
350 dataId: v.aweme_id,
351 commentId: v.cid,
352 content: v.text,
353 likeCount: Number.parseInt(v.digg_count),
354 nikeName: v.user.nickname,
355 headUrl: v.user.avatar_thumb.url_list[0],
356 data: v,
357 subCommentList: [],
358 });
359 }
360
361 return {
362 list,
363 pageInfo: {
364 count: res.data.total_count,
365 pcursor: res.data.cursor + '',
366 hasMore: res.data.has_more,
367 },
368 };
369 }
370
371 async dianzanDyOther(
372 account: AccountModel,
373 dataId: string, // 作品ID
374 ): Promise<boolean> {
375 const cookie: CookiesType = JSON.parse(account.loginCookie);
376 try {
377 const res = await douyinService.creatorDianzanOther(cookie, {
378 aweme_id: dataId,
379 item_type: 0,
380 type: 1,
381 });
382
383 return res.status_code === 0;
384 } catch (error) {
385 console.log('------ error douyin dianzanDyOther ---- ', error);
386 return false;
387 }
388 }
389
390 async shoucangDyOther(
391 account: AccountModel,
392 dataId: string, // 作品ID
393 ) {
394 const cookie: CookiesType = JSON.parse(account.loginCookie);
395 const res = await douyinService.creatorShoucangOther(cookie, {
396 aweme_id: dataId,
397 action: 1,
398 aweme_type: 0,
399 });
400
401 console.log('------ res', res);
402
403 return res;
404 }
405
406 async createCommentByOther(
407 account: AccountModel,
408 dataId: string, // 作品ID
409 content: string,
410 ) {
411 const cookie: CookiesType = JSON.parse(account.loginCookie);
412 console.log('dataIddataId????:', dataId, content);
413 const res = await douyinService.creatorCommentReplyOther(cookie, {
414 aweme_id: dataId,
415 text: content,
416 one_level_comment_rank: -1,
417
418 // aweme_id: '7498682394024430907',
419 // comment_send_celltime: 46567,
420 // comment_video_celltime: 8969,
421 // one_level_comment_rank: -1,
422 // paste_edit_method: 'non_paste',
423 // text: '调',
424 });
425
426 console.log('-- 评论 ---- res', res);
427
428 return res;
429 }
430
431 async replyCommentByOther(
432 account: AccountModel,
433 commentId: string,
434 content: string,
435 option: {
436 dataId?: string; // 作品ID
437 comment: any; // 辅助数据,原数据
438 },
439 ) {
440 console.log('------ replyCommentByOther1', commentId, option.dataId);
441
442 const cookie: CookiesType = JSON.parse(account.loginCookie);
443 const res = await douyinService.creatorCommentReplyOther(cookie, {
444 aweme_id: option.dataId || '',
445 reply_id: commentId,
446 text: content,
447 one_level_comment_rank: 1,
448 });
449
450 console.log('------ res', res);
451
452 return res;
453 }
454
455 async createComment(
456 account: AccountModel,
457 dataId: string, // 作品ID
458 content: string,
459 ) {
460 const cookie: CookiesType = JSON.parse(account.loginCookie);
461 const res = await douyinService.creatorCommentReply(cookie, {
462 comment_Id: '',
463 item_id: dataId,
464 text: content,
465 });
466
467 return res.status === 200 && res.data.status_code === 0;
468 }
469
470 async replyComment(
471 account: AccountModel,
472 commentId: string,
473 content: string,
474 option: {
475 dataId?: string; // 作品ID
476 comment: any; // 辅助数据,原数据
477 },
478 ) {
479 const cookie: CookiesType = JSON.parse(account.loginCookie);
480 try {
481 const res = await douyinService.creatorCommentReply(cookie, {
482 comment_Id: commentId,
483 item_id: option.dataId!,
484 text: content,
485 });
486
487 return res.status === 200 && res.data.status_code === 0;
488 } catch (error) {
489 console.log('------ replyComment ----', error);
490 return false;
491 }
492 }
493
494 pubParamsParse(params: WorkDataModel): DouyinPlatformSettingType {
495 const douyinParams = params.diffParams![PlatType.Douyin];
496 return {
497 proxyIp: params.proxyIp || '',
498 userDeclare: douyinParams?.selfDeclare,
499 activity: douyinParams?.activitys?.map((v) => {
500 return {
501 label: v.label,
502 value: `${v.value}`,
503 };
504 }),
505 hot_sentence: douyinParams?.hotPoint?.label,
506 mentionedUserInfo: params.mentionedUserInfo?.map((v) => {
507 return {
508 nickName: v.label,
509 uid: `${v.value}`,
510 };
511 }),
512 mixInfo: params.mixInfo
513 ? {
514 mixId: `${params.mixInfo.value}`,
515 mixName: params.mixInfo.label,
516 }
517 : undefined,
518 title: params.title || '',
519 topics: params.topics,
520 caption: params.desc,
521 cover: params.coverPath || '',
522 timingTime: params.timingTime?.getTime(),
523 // 可见性
524 visibility_type:
525 params.visibleType === VisibleTypeEnum.Public
526 ? 0
527 : params.visibleType === VisibleTypeEnum.Private
528 ? 1
529 : 2,
530 // 地址
531 ...(params.location
532 ? {
533 poiInfo: {
534 poiId: `${params.location.id}`,
535 poiName: params.location.name,
536 },
537 }
538 : {}),
539 };
540 }
541
542 async videoPublish(
543 params: VideoModel,
544 callback: VideoCallbackType,
545 ): Promise<PublishVideoResult> {
546 return new Promise(async (resolve) => {
547 const result = await douyinService
548 .publishVideoWorkApi(
549 JSON.stringify(params.cookies),
550 params?.token,
551 params.videoPath!,
552 this.pubParamsParse(params),
553 callback,
554 )
555 .catch((e) => {
556 resolve({
557 code: 0,
558 msg: e,
559 });
560 });
561 if (!result.publishId)
562 return resolve({
563 code: 0,
564 msg: '网络繁忙,请稍后重试',
565 });
566
567 return resolve({
568 code: 1,
569 msg: '发布成功',
570 dataId: result.publishId,
571 previewVideoLink: result.shareLink,
572 });
573 });
574 }
575
576 async getTopics({ keyword }: IGetTopicsParams): Promise<IGetTopicsResponse> {
577 const topicsRes = await douyinService.getTopics({ keyword });
578 return {
579 status: this.getCode(topicsRes),
580 data: topicsRes?.data?.sug_list?.map((v) => {
581 return {
582 id: v.cid,
583 name: v.cha_name,
584 view_count: v.view_count,
585 };
586 }),
587 };
588 }
589
590 async getUsers(params: IGetUsersParams) {
591 const usersRes = await douyinService.getUsers(
592 JSON.parse(params.account.loginCookie),
593 params.keyword,
594 params.page,
595 );
596
597 return {
598 status: this.getCode(usersRes),
599 data: usersRes?.data?.user_list?.map((v) => {
600 return {
601 image: 'https://p26.douyinpic.com/aweme/' + v.avatar_thumb.uri,
602 id: v.uid,
603 name: v.nickname,
604 unique_id: v.unique_id,
605 des: '',
606 follower_count: v.follower_count,
607 };
608 }),
609 };
610 }
611
612 async getLocationData(params: IGetLocationDataParams) {
613 const locationRes = await douyinService.getLocation({
614 ...params,
615 cookie: params.cookie!,
616 });
617
618 return {
619 status: this.getCode(locationRes),
620 data: locationRes?.data?.poi_list?.map((v) => {
621 return {
622 name: v.poi_name,
623 simpleAddress: v.simple_address_str,
624 id: v.poi_id,
625 latitude: v.poi_latitude,
626 longitude: v.poi_longitude,
627 city: v.address_info.city,
628 };
629 }),
630 };
631 }
632
633 async imgTextPublish(params: ImgTextModel): Promise<PublishVideoResult> {
634 return new Promise(async (resolve) => {
635 const result = await douyinService
636 .publishImageWorkApi(
637 JSON.stringify(params.cookies),
638 params?.token,
639 params.imagesPath,
640 this.pubParamsParse(params),
641 )
642 .catch((e) => {
643 resolve({
644 code: 0,
645 msg: e,
646 });
647 });
648 if (!result?.publishId)
649 return resolve({
650 code: 0,
651 msg: '网络繁忙,请稍后重试',
652 });
653
654 return resolve({
655 code: 1,
656 msg: '发布成功',
657 dataId: result.publishId,
658 previewVideoLink: result.shareLink,
659 });
660 });
661 }
662
663 getCreatorSecondCommentListByOther(
664 account: AccountModel,
665 data: WorkData,
666 root_comment_id: string,
667 pcursor?: string,
668 ): Promise<any> {
669 return Promise.resolve(undefined);
670 }
671
672 getCode(res: IRequestNetResult<any>) {
673 return res?.data?.status_code === 8 || res?.data?.status_code === 7
674 ? 401
675 : res?.status;
676 }
677
678 async getMixList(cookie: CookiesType) {
679 const mixRes = await douyinService.getMixList(cookie);
680 return {
681 status: this.getCode(mixRes),
682 data: mixRes?.data.mix_list?.map((v) => {
683 return {
684 id: v.mix_id,
685 name: v.mix_name,
686 coverImg: v.cover_url.url_list[0],
687 feedCount: v.statis.updated_to_episode,
688 };
689 }),
690 };
691 }
692 }
693
694 const douyin = new Douyin();
695 export default douyin;
696
696 lines TYPESCRIPT