karin-plugin-kkk 2.21.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { n as __esmMin, o as __toESM, r as __export } from "./rolldown-runtime-BMXAG3ag.js";
2
- import { $ as init_date_fns, T as zhCN, _n as init_dist, a as Window, an as require_png, cn as require_heic_decode, dn as Chalk, et as fromUnixTime, fn as init_source, gn as Xhshow, hn as axios_default, i as init_lib, ln as require_express, mn as init_axios, n as require_lib, nt as format, on as require_jsQR, pn as AxiosError$1, r as require_qr_code_styling, rt as differenceInSeconds, sn as require_jpeg_js, t as require_dist, tt as formatDistanceToNow, un as require_protobufjs, vn as init_zod, w as init_locale, yn as zod_default } from "./vendor-CYCcUtqE.js";
3
- import { n as init_client, r as reactServerRender } from "./template-n3eb7E79.js";
2
+ import { $ as init_date_fns, C as zhCN, S as init_locale, _n as init_dist, a as Window, an as require_png, cn as require_heic_decode, dn as Chalk, et as fromUnixTime, fn as init_source, gn as Xhshow, hn as axios_default, i as init_lib, ln as require_express, mn as init_axios, n as require_lib, nt as format, on as require_jsQR, pn as AxiosError$1, r as require_qr_code_styling, rt as differenceInSeconds, sn as require_jpeg_js, t as require_dist, tt as formatDistanceToNow, un as require_protobufjs, vn as init_zod, yn as zod_default } from "./vendor-BcMLByns.js";
3
+ import { n as init_client, r as reactServerRender } from "./template-B9tgdrVz.js";
4
4
  import { createRequire } from "node:module";
5
5
  import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, components, config, copyConfigSync, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, filesByExt, getBot, hooks, karin, karinPathHtml, karinPathTemp, logger, logs, mkdirSync, range, render, requireFileSync, restart, segment, updatePkg, watch } from "node-karin";
6
6
  import fs from "node:fs";
@@ -5473,6 +5473,7 @@ var init_Bilibili = __esmMin(() => {
5473
5473
  init_VideoPlayurlNoLogin();
5474
5474
  init_WorkComments$2();
5475
5475
  });
5476
+ var init_ArticleWork = __esmMin(() => {});
5476
5477
  var init_CommentReply = __esmMin(() => {});
5477
5478
  var init_DyDanmakuList = __esmMin(() => {});
5478
5479
  var init_EmojiList$1 = __esmMin(() => {});
@@ -5489,13 +5490,13 @@ var init_SearchInfo = __esmMin(() => {
5489
5490
  });
5490
5491
  var init_SlidesWork = __esmMin(() => {});
5491
5492
  var init_SuggestWords = __esmMin(() => {});
5492
- var init_TextWork = __esmMin(() => {});
5493
5493
  var init_UserInfo = __esmMin(() => {});
5494
5494
  var init_UserLiveVideos = __esmMin(() => {});
5495
5495
  var init_UserPostVideos = __esmMin(() => {});
5496
5496
  var init_VideoWork = __esmMin(() => {});
5497
5497
  var init_WorkComments$1 = __esmMin(() => {});
5498
5498
  var init_Douyin = __esmMin(() => {
5499
+ init_ArticleWork();
5499
5500
  init_CommentReply();
5500
5501
  init_DyDanmakuList();
5501
5502
  init_EmojiList$1();
@@ -5505,7 +5506,6 @@ var init_Douyin = __esmMin(() => {
5505
5506
  init_SearchInfo();
5506
5507
  init_SlidesWork();
5507
5508
  init_SuggestWords();
5508
- init_TextWork();
5509
5509
  init_UserInfo();
5510
5510
  init_UserLiveVideos();
5511
5511
  init_UserPostVideos();
@@ -6225,7 +6225,7 @@ var init_Base = __esmMin(() => {
6225
6225
  } catch (error) {
6226
6226
  if (options && options.active === false) await event.reply("视频文件上传失败" + JSON.stringify(error, null, 2));
6227
6227
  logger.error("视频文件上传错误," + String(error));
6228
- return false;
6228
+ throw error;
6229
6229
  } finally {
6230
6230
  const filePath = file.filepath;
6231
6231
  logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/api/kkk/video/${encodeURIComponent(filePath.split("/").pop() ?? "")}`);
@@ -8279,29 +8279,55 @@ var init_ImageDownloader = __esmMin(() => {
8279
8279
  ImageDownloader = class {
8280
8280
  axiosInstance;
8281
8281
  tempDir;
8282
+ maxRetries = 3;
8283
+ retryDelay = 1e3;
8282
8284
  constructor(axiosInstance, tempDir) {
8283
8285
  this.axiosInstance = axiosInstance;
8284
8286
  this.tempDir = tempDir;
8285
8287
  if (!fs.existsSync(this.tempDir)) fs.mkdirSync(this.tempDir, { recursive: true });
8286
8288
  }
8287
8289
  async processImage(imageUrl, title, index) {
8288
- if (!Config.app.downloadImageLocally) return imageUrl;
8290
+ switch (Config.upload.imageSendMode) {
8291
+ case "base64": try {
8292
+ return await this.downloadAndConvertToBase64(imageUrl);
8293
+ } catch (error) {
8294
+ logger.error(`图片转换 base64 失败,回退到原始 URL: ${imageUrl}`, error);
8295
+ return imageUrl;
8296
+ }
8297
+ case "file": try {
8298
+ const result = await this.downloadImage(imageUrl, title, index);
8299
+ if (result.shouldDelete) this.scheduleDelete(result.filePath);
8300
+ return result.filePath;
8301
+ } catch (error) {
8302
+ logger.error(`图片下载失败,回退到原始 URL: ${imageUrl}`, error);
8303
+ return imageUrl;
8304
+ }
8305
+ case "url":
8306
+ default: return imageUrl;
8307
+ }
8308
+ }
8309
+ async downloadAndConvertToBase64(imageUrl) {
8310
+ const filename = this.generateFilename(imageUrl);
8311
+ const filePath = path.join(this.tempDir, filename);
8289
8312
  try {
8290
- const result = await this.downloadImage(imageUrl, title, index);
8291
- if (result.shouldDelete) this.scheduleDelete(result.filePath);
8292
- return result.filePath;
8293
- } catch (error) {
8294
- logger.error(`图片下载失败,回退到原始 URL: ${imageUrl}`, error);
8295
- return imageUrl;
8313
+ const response = await this.downloadWithRetry(imageUrl);
8314
+ fs.writeFileSync(filePath, response.data);
8315
+ const base64 = fs.readFileSync(filePath).toString("base64");
8316
+ logger.debug(`图片已下载并转换为 base64: ${imageUrl.substring(0, 50)}...`);
8317
+ return `base64://${base64}`;
8318
+ } finally {
8319
+ if (Config.app.removeCache && fs.existsSync(filePath)) try {
8320
+ fs.unlinkSync(filePath);
8321
+ logger.debug(`临时文件已删除: ${filePath}`);
8322
+ } catch (error) {
8323
+ logger.error(`删除临时文件失败: ${filePath}`, error);
8324
+ }
8296
8325
  }
8297
8326
  }
8298
8327
  async downloadImage(imageUrl, title, index) {
8299
8328
  const filename = this.generateFilename(imageUrl, title, index);
8300
8329
  const filePath = path.join(this.tempDir, filename);
8301
- const response = await this.axiosInstance.get(imageUrl, {
8302
- responseType: "arraybuffer",
8303
- timeout: 3e4
8304
- });
8330
+ const response = await this.downloadWithRetry(imageUrl);
8305
8331
  fs.writeFileSync(filePath, response.data);
8306
8332
  logger.debug(`图片已下载: ${filePath}`);
8307
8333
  return {
@@ -8309,6 +8335,28 @@ var init_ImageDownloader = __esmMin(() => {
8309
8335
  shouldDelete: true
8310
8336
  };
8311
8337
  }
8338
+ async downloadWithRetry(imageUrl, retryCount = 0) {
8339
+ try {
8340
+ const response = await this.axiosInstance.get(imageUrl, {
8341
+ responseType: "arraybuffer",
8342
+ timeout: 3e4
8343
+ });
8344
+ if (retryCount > 0) logger.info(`图片下载成功(重试 ${retryCount} 次后): ${imageUrl.substring(0, 50)}...`);
8345
+ return response;
8346
+ } catch (error) {
8347
+ if (retryCount < this.maxRetries) {
8348
+ const delay$1 = this.retryDelay * Math.pow(2, retryCount);
8349
+ logger.warn(`图片下载失败,${delay$1}ms 后进行第 ${retryCount + 1}/${this.maxRetries} 次重试: ${imageUrl.substring(0, 50)}...`, error);
8350
+ await this.sleep(delay$1);
8351
+ return this.downloadWithRetry(imageUrl, retryCount + 1);
8352
+ }
8353
+ logger.error(`图片下载失败,已重试 ${this.maxRetries} 次: ${imageUrl}`, error);
8354
+ throw error;
8355
+ }
8356
+ }
8357
+ sleep(ms) {
8358
+ return new Promise((resolve$1) => setTimeout(resolve$1, ms));
8359
+ }
8312
8360
  generateFilename(imageUrl, title, index) {
8313
8361
  const ext = this.getExtension(imageUrl);
8314
8362
  if (Config.app.removeCache) return `tmp_${Date.now()}${index !== void 0 ? `_${index}` : ""}${ext}`;
@@ -11225,12 +11273,6 @@ var init_app_schema = __esmMin(() => {
11225
11273
  label: "缓存删除",
11226
11274
  description: "下载的视频缓存自动删除,非必要不修改!"
11227
11275
  },
11228
- {
11229
- key: "downloadImageLocally",
11230
- type: "switch",
11231
- label: "本地下载图片",
11232
- description: "发送图片时由插件本地下载后使用 file 协议传递,而非直接传递 HTTP 链接给上游下载"
11233
- },
11234
11276
  {
11235
11277
  type: "divider",
11236
11278
  title: "解析优先级设置"
@@ -12622,6 +12664,26 @@ var init_upload_schema = __esmMin(() => {
12622
12664
  disabled: $or($not("usegroupfile"), $var("sendbase64")),
12623
12665
  rules: [{ min: 1 }]
12624
12666
  },
12667
+ {
12668
+ key: "imageSendMode",
12669
+ type: "radio",
12670
+ label: "网络图片发送方式",
12671
+ description: "选择发送网络图片的方式:\n• URL - 直接传递链接给上游(可能因上游网络问题超时)\n• File - 下载后用 file 协议发送(需 Karin 与协议端同系统)\n• Base64 - 转 base64 发送(传输数据增大 1/3,不在同一网络环境可能导致额外带宽成本)",
12672
+ options: [
12673
+ {
12674
+ label: "URL 链接(直接传递)",
12675
+ value: "url"
12676
+ },
12677
+ {
12678
+ label: "File 协议(本地文件)",
12679
+ value: "file"
12680
+ },
12681
+ {
12682
+ label: "Base64(编码传输)",
12683
+ value: "base64"
12684
+ }
12685
+ ]
12686
+ },
12625
12687
  {
12626
12688
  type: "divider",
12627
12689
  title: "上传拦截配置"
@@ -13272,22 +13334,27 @@ var init_handler = __esmMin(async () => {
13272
13334
  };
13273
13335
  wrapWithErrorHandler = (fn, options) => async (e, next) => {
13274
13336
  const emojiManager = e ? new EmojiReactionManager(e) : void 0;
13337
+ let processingTimer = null;
13338
+ let successTimer = null;
13275
13339
  if (emojiManager) {
13276
13340
  await emojiManager.add("EYES");
13277
- setTimeout(() => {
13341
+ processingTimer = setTimeout(() => {
13278
13342
  emojiManager.add("PROCESSING").catch(() => {});
13279
13343
  }, 1500);
13280
13344
  }
13281
13345
  const ctx = logger.runContext(async () => fn(e, next));
13282
13346
  try {
13283
13347
  const result = await ctx.run();
13284
- if (emojiManager) setTimeout(() => {
13348
+ if (emojiManager) successTimer = setTimeout(() => {
13285
13349
  emojiManager.replace("PROCESSING", "SUCCESS").catch(() => {});
13286
13350
  }, 1500);
13287
13351
  return result;
13288
13352
  } catch (error) {
13353
+ if (processingTimer) clearTimeout(processingTimer);
13354
+ if (successTimer) clearTimeout(successTimer);
13289
13355
  if (emojiManager) {
13290
- await emojiManager.clearAll();
13356
+ const processingEmojiId = emojiManager["getPlatformEmojiId"]("PROCESSING");
13357
+ if (emojiManager.has(processingEmojiId)) await emojiManager.remove("PROCESSING");
13291
13358
  await emojiManager.add("ERROR");
13292
13359
  }
13293
13360
  await new Promise((resolve$1) => setTimeout(resolve$1, 100));
@@ -14787,6 +14854,28 @@ const webConfig = defineConfig({
14787
14854
  rules: [{ min: 1 }],
14788
14855
  isDisabled: !all.upload.usegroupfile || all.upload.sendbase64
14789
14856
  }),
14857
+ components.radio.group("imageSendMode", {
14858
+ label: "网络图片发送方式",
14859
+ orientation: "vertical",
14860
+ defaultValue: all.upload.imageSendMode,
14861
+ radio: [
14862
+ components.radio.create("imageSendMode:radio-1", {
14863
+ label: "URL 链接(直接传递)",
14864
+ description: "直接传递 HTTP 链接给上游下载,可能因上游网络问题导致下载超时",
14865
+ value: "url"
14866
+ }),
14867
+ components.radio.create("imageSendMode:radio-2", {
14868
+ label: "File 协议(本地文件)",
14869
+ description: "下载到本地后使用 file 协议发送,需 Karin 与协议端在同一系统",
14870
+ value: "file"
14871
+ }),
14872
+ components.radio.create("imageSendMode:radio-3", {
14873
+ label: "Base64(编码传输)",
14874
+ description: "下载后转换为 base64 发送,传输数据增大约 1/3,不在同一网络环境可能导致额外带宽成本",
14875
+ value: "base64"
14876
+ })
14877
+ ]
14878
+ }),
14790
14879
  components.divider.create("divider-upload-limit", {
14791
14880
  description: "上传拦截配置",
14792
14881
  descPosition: 20
@@ -15617,6 +15706,7 @@ var Bilibili = class extends Base {
15617
15706
  }
15618
15707
  this.e.reply(await Render("bilibili/dynamic/DYNAMIC_TYPE_DRAW", {
15619
15708
  image_url: dynamicCARD$1.item.pictures && cover(dynamicCARD$1.item.pictures),
15709
+ title: dynamicInfo.data.data.item.modules.module_dynamic.major.opus.title ?? void 0,
15620
15710
  text: dynamicInfo.data.data.item.modules.module_dynamic.major ? replacetext(br$3(dynamicInfo.data.data.item.modules.module_dynamic.major.opus?.summary?.text ?? ""), dynamicInfo.data.data.item.modules.module_dynamic.major.opus?.summary?.rich_text_nodes ?? []) : "",
15621
15711
  dianzan: Count(dynamicInfo.data.data.item.modules.module_stat.like.count),
15622
15712
  pinglun: Count(dynamicInfo.data.data.item.modules.module_stat.comment.count),
@@ -17008,6 +17098,7 @@ var Bilibilipush = class extends Base {
17008
17098
  }
17009
17099
  img$2 = await Render("bilibili/dynamic/DYNAMIC_TYPE_DRAW", {
17010
17100
  image_url: dycrad.item.pictures && cover(dycrad.item.pictures),
17101
+ title: data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.title ?? void 0,
17011
17102
  text: replacetext(br$1(data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.text ?? ""), data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.summary?.rich_text_nodes ?? []),
17012
17103
  dianzan: Count(data$1[dynamicId].Dynamic_Data.modules.module_stat.like.count),
17013
17104
  pinglun: Count(data$1[dynamicId].Dynamic_Data.modules.module_stat.comment.count),
@@ -17786,6 +17877,7 @@ var getRelativeTimeFromTimestamp$2 = (timestamp) => {
17786
17877
  });
17787
17878
  return format(commentDate, "yyyy-MM-dd");
17788
17879
  };
17880
+ await init_date_fns();
17789
17881
  await init_utils$1();
17790
17882
  await init_Config();
17791
17883
  await init_danmaku();
@@ -17810,6 +17902,34 @@ var DouYin = class extends Base {
17810
17902
  this.forceBurnDanmaku = options?.forceBurnDanmaku ?? false;
17811
17903
  this.hasProcessedLiveImage = false;
17812
17904
  }
17905
+ async handleArticleWork(VideoData, _data) {
17906
+ const aweme = VideoData.data.aweme_detail;
17907
+ const content = JSON.parse(aweme.article_info.article_content);
17908
+ const fe_data = JSON.parse(aweme.article_info.fe_data);
17909
+ logger.debug("文章数据提取完成");
17910
+ logger.debug(`文章标题: ${aweme.article_info.article_title}`);
17911
+ logger.debug(`图片数量: ${fe_data.image_list?.length || 0}`);
17912
+ const img$2 = await Render("douyin/article-work", {
17913
+ title: aweme.article_info.article_title,
17914
+ markdown: content.markdown,
17915
+ images: fe_data.image_list || [],
17916
+ read_time: fe_data.read_time || 0,
17917
+ dianzan: Count(aweme.statistics.digg_count),
17918
+ pinglun: Count(aweme.statistics.comment_count),
17919
+ shouchang: Count(aweme.statistics.collect_count),
17920
+ share: Count(aweme.statistics.share_count),
17921
+ create_time: format(fromUnixTime(aweme.create_time), "yyyy-MM-dd HH:mm"),
17922
+ avater_url: aweme.author.avatar_thumb.url_list[0],
17923
+ username: aweme.author.nickname,
17924
+ "抖音号": aweme.author.unique_id || aweme.author.short_id,
17925
+ "获赞": Count(aweme.author.total_favorited),
17926
+ "关注": Count(aweme.author.following_count),
17927
+ "粉丝": Count(aweme.author.follower_count),
17928
+ share_url: `https://www.douyin.com/article/${aweme.aweme_id}`
17929
+ });
17930
+ await this.e.reply(img$2);
17931
+ return true;
17932
+ }
17813
17933
  async DouyinHandler(data$1) {
17814
17934
  Config.app.parseTip && this.e.reply("检测到抖音链接,开始解析");
17815
17935
  switch (this.type) {
@@ -17818,6 +17938,10 @@ var DouYin = class extends Base {
17818
17938
  aweme_id: data$1.aweme_id,
17819
17939
  typeMode: "strict"
17820
17940
  });
17941
+ const aweme_type = VideoData.data.aweme_detail.aweme_type;
17942
+ const isArticle = aweme_type === 163;
17943
+ const isVideo = aweme_type === 0;
17944
+ if (this.is_mp4 === void 0) this.is_mp4 = isVideo;
17821
17945
  const CommentsData = await this.amagi.douyin.fetcher.fetchWorkComments({
17822
17946
  aweme_id: data$1.aweme_id,
17823
17947
  number: Config.douyin.numcomment,
@@ -17828,7 +17952,7 @@ var DouYin = class extends Base {
17828
17952
  let g_title;
17829
17953
  let imagenum = 0;
17830
17954
  const image_res = [];
17831
- if (this.is_mp4 === false) switch (true) {
17955
+ if (this.is_mp4 === false && !isArticle) switch (true) {
17832
17956
  case this.is_slides === false && VideoData.data.aweme_detail.images !== null: {
17833
17957
  const image_data = [];
17834
17958
  const imageres = [];
@@ -18057,7 +18181,7 @@ var DouYin = class extends Base {
18057
18181
  } catch (error) {
18058
18182
  console.log(error);
18059
18183
  }
18060
- music_url && this.is_mp4 === false && music_url !== void 0 && !this.hasProcessedLiveImage && await this.e.reply(segment.record(music_url, false));
18184
+ music_url && (this.is_mp4 === false || isArticle) && music_url !== void 0 && !this.hasProcessedLiveImage && await this.e.reply(segment.record(music_url, false));
18061
18185
  }
18062
18186
  let FPS;
18063
18187
  let video = null;
@@ -18079,7 +18203,7 @@ var DouYin = class extends Base {
18079
18203
  if (Config.douyin.sendContent.includes("info")) if (Config.douyin.videoInfoMode === "text") {
18080
18204
  const replyContent = [];
18081
18205
  const { digg_count, share_count, collect_count, comment_count, recommend_count } = VideoData.data.aweme_detail.statistics;
18082
- const coverUrl = await processImageUrl(this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0], VideoData.data.aweme_detail.desc);
18206
+ const coverUrl = await processImageUrl(isArticle ? VideoData.data.aweme_detail.video.origin_cover.url_list[0] : this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0], VideoData.data.aweme_detail.desc);
18083
18207
  const contentMap = {
18084
18208
  cover: segment.image(coverUrl),
18085
18209
  title: segment.text(`\n📺 标题: ${VideoData.data.aweme_detail.desc}\n`),
@@ -18101,7 +18225,7 @@ var DouYin = class extends Base {
18101
18225
  typeMode: "strict"
18102
18226
  });
18103
18227
  const videoInfoImg = await Render("douyin/videoInfo", {
18104
- desc: VideoData.data.aweme_detail.desc,
18228
+ desc: isArticle ? VideoData.data.aweme_detail.preview_title : VideoData.data.aweme_detail.desc,
18105
18229
  statistics: VideoData.data.aweme_detail.statistics,
18106
18230
  aweme_id: VideoData.data.aweme_detail.aweme_id,
18107
18231
  author: {
@@ -18117,8 +18241,11 @@ var DouYin = class extends Base {
18117
18241
  gender: userProfile.data.user.gender ?? 0,
18118
18242
  user_age: userProfile.data.user.user_age ?? 0
18119
18243
  } : void 0,
18120
- image_url: this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
18121
- cover_size: this.is_mp4 ? VideoData.data.aweme_detail.video.cover ? {
18244
+ image_url: isArticle ? VideoData.data.aweme_detail.video.origin_cover.url_list[0] : this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
18245
+ cover_size: isArticle ? VideoData.data.aweme_detail.video.origin_cover ? {
18246
+ width: VideoData.data.aweme_detail.video.origin_cover.width,
18247
+ height: VideoData.data.aweme_detail.video.origin_cover.height
18248
+ } : void 0 : this.is_mp4 ? VideoData.data.aweme_detail.video.cover ? {
18122
18249
  width: VideoData.data.aweme_detail.video.cover_original_scale.width,
18123
18250
  height: VideoData.data.aweme_detail.video.cover_original_scale.height
18124
18251
  } : void 0 : VideoData.data.aweme_detail.images?.[0] ? {
@@ -18140,6 +18267,7 @@ var DouYin = class extends Base {
18140
18267
  });
18141
18268
  this.e.reply(videoInfoImg);
18142
18269
  }
18270
+ if (isArticle) await this.handleArticleWork(VideoData, data$1);
18143
18271
  if (Config.douyin.sendContent.includes("comment")) {
18144
18272
  const douyinCommentsRes = await douyinComments(CommentsData, Emoji((await this.amagi.douyin.fetcher.fetchEmojiList({ typeMode: "loose" })).data));
18145
18273
  if (!douyinCommentsRes.CommentsData.length) await this.e.reply("这个作品没有评论 ~");
@@ -18149,7 +18277,7 @@ var DouYin = class extends Base {
18149
18277
  for (const item of VideoData.data.aweme_detail.suggest_words.suggest_words) if (item.words && item.scene === "comment_top_rec") for (const v of item.words) v.word && suggest.push(v.word);
18150
18278
  }
18151
18279
  const img$2 = await Render("douyin/comment", {
18152
- Type: this.is_mp4 ? "视频" : this.is_slides ? "合辑" : "图集",
18280
+ Type: isArticle ? "文章" : this.is_mp4 ? "视频" : this.is_slides ? "合辑" : "图集",
18153
18281
  CommentsData: douyinCommentsRes.CommentsData,
18154
18282
  CommentLength: Config.douyin.realCommentCount ? VideoData.data.aweme_detail.statistics.comment_count : douyinCommentsRes.CommentsData.length ?? 0,
18155
18283
  share_url: this.is_mp4 ? `https://aweme.snssdk.com/aweme/v1/play/?video_id=${VideoData.data.aweme_detail.video.play_addr.uri}&ratio=1080p&line=0` : VideoData.data.aweme_detail.share_url,
@@ -18178,7 +18306,7 @@ var DouYin = class extends Base {
18178
18306
  this.e.reply(img$2);
18179
18307
  }
18180
18308
  }
18181
- if (this.is_mp4 && Config.douyin.sendContent.includes("video")) {
18309
+ if (this.is_mp4 && !isArticle && Config.douyin.sendContent.includes("video")) {
18182
18310
  let danmakuList = [];
18183
18311
  if ((this.forceBurnDanmaku || Config.douyin.burnDanmaku) && video) try {
18184
18312
  const duration = video.duration;
@@ -18488,8 +18616,69 @@ var getStringDisplayWidth = (str) => {
18488
18616
  }
18489
18617
  return width;
18490
18618
  };
18619
+ let DouyinWorkMainType = function(DouyinWorkMainType$1) {
18620
+ DouyinWorkMainType$1["VIDEO"] = "video";
18621
+ DouyinWorkMainType$1["IMAGE"] = "image";
18622
+ DouyinWorkMainType$1["ARTICLE"] = "article";
18623
+ DouyinWorkMainType$1["LIVE"] = "live";
18624
+ DouyinWorkMainType$1["MUSIC"] = "music";
18625
+ DouyinWorkMainType$1["UNKNOWN"] = "unknown";
18626
+ return DouyinWorkMainType$1;
18627
+ }({});
18628
+ let DouyinImageSubType = function(DouyinImageSubType$1) {
18629
+ DouyinImageSubType$1["GALLERY"] = "gallery";
18630
+ DouyinImageSubType$1["COLLECTION"] = "collection";
18631
+ return DouyinImageSubType$1;
18632
+ }({});
18633
+ function getWorkTypeInfo(data$1) {
18634
+ if (data$1.aweme_type === 163 || data$1.article_info) return {
18635
+ mainType: DouyinWorkMainType.ARTICLE,
18636
+ isVideo: false,
18637
+ isImage: false,
18638
+ isArticle: true,
18639
+ isGallery: false,
18640
+ isCollection: false,
18641
+ templatePath: "douyin/article-work"
18642
+ };
18643
+ if (data$1.images && data$1.images.length > 0) {
18644
+ const subType = data$1.is_slides === true ? DouyinImageSubType.COLLECTION : DouyinImageSubType.GALLERY;
18645
+ return {
18646
+ mainType: DouyinWorkMainType.IMAGE,
18647
+ subType,
18648
+ isVideo: false,
18649
+ isImage: true,
18650
+ isArticle: false,
18651
+ isGallery: subType === DouyinImageSubType.GALLERY,
18652
+ isCollection: subType === DouyinImageSubType.COLLECTION,
18653
+ templatePath: "douyin/image-work"
18654
+ };
18655
+ }
18656
+ return {
18657
+ mainType: DouyinWorkMainType.VIDEO,
18658
+ isVideo: true,
18659
+ isImage: false,
18660
+ isArticle: false,
18661
+ isGallery: false,
18662
+ isCollection: false,
18663
+ templatePath: "douyin/video-work"
18664
+ };
18665
+ }
18666
+ function getWorkCoverUrl(workTypeInfo, data$1) {
18667
+ if (workTypeInfo.isVideo && data$1.video) return data$1.video.animated_cover?.url_list[0] ?? data$1.video.cover_original_scale?.url_list[0] ?? data$1.video.cover?.url_list[0] ?? "";
18668
+ if (workTypeInfo.isImage && data$1.images && data$1.images.length > 0) return data$1.images[0].url_list[0] ?? "";
18669
+ if (workTypeInfo.isArticle && data$1.article_info?.article_content) try {
18670
+ return JSON.parse(data$1.article_info.article_content).head_poster_list?.url_list?.[0] ?? "";
18671
+ } catch {
18672
+ return "";
18673
+ }
18674
+ return "";
18675
+ }
18491
18676
  const getDouyinID = async (event, url, log = true) => {
18492
- const longLink = (await axios.get(url, { headers: { "User-Agent": "Apifox/1.0.0 (https://apifox.com)" } })).request.res.responseUrl;
18677
+ const resp = await axios.get(url, {
18678
+ headers: { "User-Agent": "Apifox/1.0.0 (https://apifox.com)" },
18679
+ maxRedirects: 10
18680
+ });
18681
+ const longLink = resp.request?.res?.responseUrl || resp.request?.responseURL || url;
18493
18682
  let result = {};
18494
18683
  switch (true) {
18495
18684
  case longLink.includes("webcast.amemv.com"):
@@ -18505,21 +18694,13 @@ const getDouyinID = async (event, url, log = true) => {
18505
18694
  room_id: longLink.split("/").pop()
18506
18695
  };
18507
18696
  break;
18508
- case /video\/(\d+)/.test(longLink): {
18509
- const videoMatch = /video\/(\d+)/.exec(longLink);
18510
- result = {
18511
- type: "one_work",
18512
- aweme_id: videoMatch ? videoMatch[1] : void 0,
18513
- is_mp4: true
18514
- };
18515
- break;
18516
- }
18697
+ case /video\/(\d+)/.test(longLink):
18698
+ case /article\/(\d+)/.test(longLink):
18517
18699
  case /note\/(\d+)/.test(longLink): {
18518
- const noteMatch = /note\/(\d+)/.exec(longLink);
18700
+ const match = /(?:video|article|note)\/(\d+)/.exec(longLink);
18519
18701
  result = {
18520
18702
  type: "one_work",
18521
- aweme_id: noteMatch ? noteMatch[1] : void 0,
18522
- is_mp4: false
18703
+ aweme_id: match ? match[1] : void 0
18523
18704
  };
18524
18705
  break;
18525
18706
  }
@@ -18528,7 +18709,8 @@ const getDouyinID = async (event, url, log = true) => {
18528
18709
  result = {
18529
18710
  type: "one_work",
18530
18711
  aweme_id: modalMatch ? modalMatch[1] : void 0,
18531
- is_mp4: true
18712
+ is_mp4: true,
18713
+ work_type: DouyinWorkMainType.VIDEO
18532
18714
  };
18533
18715
  break;
18534
18716
  }
@@ -18871,7 +19053,7 @@ var DouYinpush = class extends Base {
18871
19053
  if (pushItem.pushType === "favorite") {
18872
19054
  const authorUserInfo = "author_user_info" in Detail_Data ? Detail_Data.author_user_info : void 0;
18873
19055
  img$2 = await Render("douyin/favorite-list", {
18874
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
19056
+ image_url: getWorkCoverUrl(getWorkTypeInfo(Detail_Data), Detail_Data),
18875
19057
  desc: this.desc(Detail_Data, Detail_Data.desc),
18876
19058
  dianzan: this.count(Detail_Data.statistics.digg_count),
18877
19059
  pinglun: this.count(Detail_Data.statistics.comment_count),
@@ -18890,7 +19072,7 @@ var DouYinpush = class extends Base {
18890
19072
  } else if (pushItem.pushType === "recommend") {
18891
19073
  const authorUserInfo = "author_user_info" in Detail_Data ? Detail_Data.author_user_info : void 0;
18892
19074
  img$2 = await Render("douyin/recommend-list", {
18893
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
19075
+ image_url: getWorkCoverUrl(getWorkTypeInfo(Detail_Data), Detail_Data),
18894
19076
  desc: this.desc(Detail_Data, Detail_Data.desc),
18895
19077
  dianzan: this.count(Detail_Data.statistics.digg_count),
18896
19078
  pinglun: this.count(Detail_Data.statistics.comment_count),
@@ -18906,45 +19088,72 @@ var DouYinpush = class extends Base {
18906
19088
  author_douyin_id: authorUserInfo.data.user.unique_id === "" ? authorUserInfo.data.user.short_id : authorUserInfo.data.user.unique_id,
18907
19089
  share_url: Config.douyin.push.shareType === "web" ? realUrl : `https://aweme.snssdk.com/aweme/v1/play/?video_id=${Detail_Data.video.play_addr.uri}&ratio=1080p&line=0`
18908
19090
  });
18909
- } else img$2 = await Render("douyin/dynamic", {
18910
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
18911
- desc: this.desc(Detail_Data, Detail_Data.desc),
18912
- dianzan: this.count(Detail_Data.statistics.digg_count),
18913
- pinglun: this.count(Detail_Data.statistics.comment_count),
18914
- share: this.count(Detail_Data.statistics.share_count),
18915
- shouchang: this.count(Detail_Data.statistics.collect_count),
18916
- create_time: format(fromUnixTime(pushItem.create_time), "yyyy-MM-dd HH:mm"),
18917
- avater_url: "https://p3-pc.douyinpic.com/aweme/1080x1080/" + Detail_Data.user_info.data.user.avatar_larger.uri,
18918
- share_url: Config.douyin.push.shareType === "web" ? realUrl : `https://aweme.snssdk.com/aweme/v1/play/?video_id=${Detail_Data.video.play_addr.uri}&ratio=1080p&line=0`,
18919
- username: Detail_Data.author.nickname,
18920
- "抖音号": Detail_Data.user_info.data.user.unique_id === "" ? Detail_Data.user_info.data.user.short_id : Detail_Data.user_info.data.user.unique_id,
18921
- "粉丝": this.count(Detail_Data.user_info.data.user.follower_count),
18922
- "获赞": this.count(Detail_Data.user_info.data.user.total_favorited),
18923
- "关注": this.count(Detail_Data.user_info.data.user.following_count),
18924
- dynamicTYPE: "作品动态推送",
18925
- cooperation_info: (() => {
18926
- const raw = Detail_Data.cooperation_info;
18927
- if (!raw) return void 0;
18928
- const rawCreators = Array.isArray(raw.co_creators) ? raw.co_creators : [];
18929
- const author = Detail_Data.author;
18930
- const authorUid = author?.uid;
18931
- const authorSecUid = author?.sec_uid;
18932
- const authorNickname = author?.nickname;
18933
- const authorInCreators = rawCreators.some((c) => authorUid && c.uid && c.uid === authorUid || authorSecUid && c.sec_uid && c.sec_uid === authorSecUid || authorNickname && c.nickname && c.nickname === authorNickname);
18934
- const co_creators = rawCreators.map((c) => {
18935
- const firstUrl = c.avatar_thumb?.url_list?.[0] ?? (c.avatar_thumb?.uri ? `https://p3.douyinpic.com/${c.avatar_thumb.uri}` : void 0);
19091
+ } else {
19092
+ const dynamicTypeLabel = "作品动态推送";
19093
+ const workTypeInfo = getWorkTypeInfo(Detail_Data);
19094
+ const coverUrl = getWorkCoverUrl(workTypeInfo, Detail_Data);
19095
+ if (workTypeInfo.isArticle) {
19096
+ const content = JSON.parse(Detail_Data.article_info.article_content);
19097
+ const fe_data = JSON.parse(Detail_Data.article_info.fe_data);
19098
+ img$2 = await Render("douyin/article-work", {
19099
+ title: Detail_Data.article_info.article_title,
19100
+ markdown: content.markdown,
19101
+ images: fe_data.image_list || [],
19102
+ read_time: fe_data.read_time || 0,
19103
+ dianzan: this.count(Detail_Data.statistics.digg_count),
19104
+ pinglun: this.count(Detail_Data.statistics.comment_count),
19105
+ shouchang: this.count(Detail_Data.statistics.collect_count),
19106
+ share: this.count(Detail_Data.statistics.share_count),
19107
+ create_time: format(fromUnixTime(pushItem.create_time), "yyyy-MM-dd HH:mm"),
19108
+ avater_url: "https://p3-pc.douyinpic.com/aweme/1080x1080/" + Detail_Data.user_info.data.user.avatar_larger.uri,
19109
+ username: Detail_Data.author.nickname,
19110
+ "抖音号": Detail_Data.user_info.data.user.unique_id === "" ? Detail_Data.user_info.data.user.short_id : Detail_Data.user_info.data.user.unique_id,
19111
+ "获赞": this.count(Detail_Data.user_info.data.user.total_favorited),
19112
+ "关注": this.count(Detail_Data.user_info.data.user.following_count),
19113
+ "粉丝": this.count(Detail_Data.user_info.data.user.follower_count),
19114
+ share_url: Detail_Data.share_url,
19115
+ useDarkTheme: false
19116
+ });
19117
+ } else img$2 = await Render(workTypeInfo.templatePath, {
19118
+ image_url: coverUrl,
19119
+ desc: this.desc(Detail_Data, Detail_Data.desc),
19120
+ dianzan: this.count(Detail_Data.statistics.digg_count),
19121
+ pinglun: this.count(Detail_Data.statistics.comment_count),
19122
+ share: this.count(Detail_Data.statistics.share_count),
19123
+ shouchang: this.count(Detail_Data.statistics.collect_count),
19124
+ create_time: format(fromUnixTime(pushItem.create_time), "yyyy-MM-dd HH:mm"),
19125
+ avater_url: "https://p3-pc.douyinpic.com/aweme/1080x1080/" + Detail_Data.user_info.data.user.avatar_larger.uri,
19126
+ share_url: Config.douyin.push.shareType === "web" ? realUrl : `https://aweme.snssdk.com/aweme/v1/play/?video_id=${Detail_Data.video.play_addr.uri}&ratio=1080p&line=0`,
19127
+ username: Detail_Data.author.nickname,
19128
+ "抖音号": Detail_Data.user_info.data.user.unique_id === "" ? Detail_Data.user_info.data.user.short_id : Detail_Data.user_info.data.user.unique_id,
19129
+ "粉丝": this.count(Detail_Data.user_info.data.user.follower_count),
19130
+ "获赞": this.count(Detail_Data.user_info.data.user.total_favorited),
19131
+ "关注": this.count(Detail_Data.user_info.data.user.following_count),
19132
+ dynamicTYPE: dynamicTypeLabel,
19133
+ cooperation_info: (() => {
19134
+ const raw = Detail_Data.cooperation_info;
19135
+ if (!raw) return void 0;
19136
+ const rawCreators = Array.isArray(raw.co_creators) ? raw.co_creators : [];
19137
+ const author = Detail_Data.author;
19138
+ const authorUid = author?.uid;
19139
+ const authorSecUid = author?.sec_uid;
19140
+ const authorNickname = author?.nickname;
19141
+ const authorInCreators = rawCreators.some((c) => authorUid && c.uid && c.uid === authorUid || authorSecUid && c.sec_uid && c.sec_uid === authorSecUid || authorNickname && c.nickname && c.nickname === authorNickname);
19142
+ const co_creators = rawCreators.map((c) => {
19143
+ const firstUrl = c.avatar_thumb?.url_list?.[0] ?? (c.avatar_thumb?.uri ? `https://p3.douyinpic.com/${c.avatar_thumb.uri}` : void 0);
19144
+ return {
19145
+ avatar_thumb: firstUrl ? { url_list: [firstUrl] } : void 0,
19146
+ nickname: c.nickname,
19147
+ role_title: c.role_title
19148
+ };
19149
+ });
18936
19150
  return {
18937
- avatar_thumb: firstUrl ? { url_list: [firstUrl] } : void 0,
18938
- nickname: c.nickname,
18939
- role_title: c.role_title
19151
+ co_creator_nums: Math.max(Number(raw.co_creator_nums || 0), co_creators.length) + (authorInCreators ? 0 : 1),
19152
+ co_creators
18940
19153
  };
18941
- });
18942
- return {
18943
- co_creator_nums: Math.max(Number(raw.co_creator_nums || 0), co_creators.length) + (authorInCreators ? 0 : 1),
18944
- co_creators
18945
- };
18946
- })()
18947
- });
19154
+ })()
19155
+ });
19156
+ }
18948
19157
  }
18949
19158
  for (const target of pushItem.targets) {
18950
19159
  let status = { message_id: "" };
@@ -20250,8 +20459,35 @@ var handleTestDouyinPush = wrapWithErrorHandler(async (e) => {
20250
20459
  Connection: "keep-alive"
20251
20460
  }
20252
20461
  }).getLocation();
20253
- const img$2 = await Render("douyin/dynamic", {
20254
- image_url: iddata.is_mp4 ? workInfo.data.aweme_detail.video.animated_cover?.url_list[0] ?? workInfo.data.aweme_detail.video.cover.url_list[0] : workInfo.data.aweme_detail.images[0].url_list[0],
20462
+ const workTypeInfo = getWorkTypeInfo(workInfo.data.aweme_detail);
20463
+ const coverUrl = getWorkCoverUrl(workTypeInfo, workInfo.data.aweme_detail);
20464
+ if (workTypeInfo.isArticle) {
20465
+ const content = JSON.parse(workInfo.data.aweme_detail.article_info.article_content);
20466
+ const fe_data = JSON.parse(workInfo.data.aweme_detail.article_info.fe_data);
20467
+ const img$3 = await Render("douyin/article-work", {
20468
+ title: workInfo.data.aweme_detail.article_info.article_title,
20469
+ markdown: content.markdown,
20470
+ images: fe_data.image_list || [],
20471
+ read_time: fe_data.read_time || 0,
20472
+ dianzan: Common.count(workInfo.data.aweme_detail.statistics.digg_count),
20473
+ pinglun: Common.count(workInfo.data.aweme_detail.statistics.comment_count),
20474
+ shouchang: Common.count(workInfo.data.aweme_detail.statistics.collect_count),
20475
+ share: Common.count(workInfo.data.aweme_detail.statistics.share_count),
20476
+ create_time: format(fromUnixTime(workInfo.data.aweme_detail.create_time), "yyyy-MM-dd HH:mm"),
20477
+ avater_url: "https://p3-pc.douyinpic.com/aweme/1080x1080/" + userProfile.data.user.avatar_larger.uri,
20478
+ username: workInfo.data.aweme_detail.author.nickname,
20479
+ "抖音号": userProfile.data.user.unique_id === "" ? userProfile.data.user.short_id : userProfile.data.user.unique_id,
20480
+ "获赞": Common.count(userProfile.data.user.total_favorited),
20481
+ "关注": Common.count(userProfile.data.user.following_count),
20482
+ "粉丝": Common.count(userProfile.data.user.follower_count),
20483
+ share_url: workInfo.data.aweme_detail.share_url,
20484
+ useDarkTheme: false
20485
+ });
20486
+ e.reply(img$3);
20487
+ return true;
20488
+ }
20489
+ const img$2 = await Render(workTypeInfo.templatePath, {
20490
+ image_url: coverUrl,
20255
20491
  desc: workInfo.data.aweme_detail.desc,
20256
20492
  dianzan: Common.count(workInfo.data.aweme_detail.statistics.digg_count),
20257
20493
  pinglun: Common.count(workInfo.data.aweme_detail.statistics.comment_count),