| 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 |