koishi-plugin-bilibili-notify 3.2.9-alpha.4 → 3.2.9-rc.1

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.
package/lib/index.d.mts CHANGED
@@ -34,6 +34,7 @@ interface BAConfig {
34
34
  pushImgsInDynamic: boolean;
35
35
  live: {};
36
36
  liveDetectType: "WS" | "API";
37
+ wordcloud: boolean;
37
38
  restartPush: boolean;
38
39
  pushTime: number;
39
40
  customLiveStart: string;
package/lib/index.d.ts CHANGED
@@ -34,6 +34,7 @@ interface BAConfig {
34
34
  pushImgsInDynamic: boolean;
35
35
  live: {};
36
36
  liveDetectType: "WS" | "API";
37
+ wordcloud: boolean;
37
38
  restartPush: boolean;
38
39
  pushTime: number;
39
40
  customLiveStart: string;
package/lib/index.js CHANGED
@@ -112,6 +112,7 @@ const BAConfigSchema = koishi.Schema.object({
112
112
  pushImgsInDynamic: koishi.Schema.boolean().default(false).description("是否推送动态中的图片,默认不开启。开启后会单独推送动态中的图片,该功能容易导致QQ风控"),
113
113
  live: koishi.Schema.object({}).description("直播推送设置"),
114
114
  liveDetectType: koishi.Schema.union([koishi.Schema.const("WS").description("使用WebSocket连接到B站消息服务器进行直播检测,推荐使用"), koishi.Schema.const("API").description("通过轮询API发送请求监测直播状态,此模式理论可无限订阅,但容易产生其他问题,功能没有WS模式全面").experimental()]).role("radio").default("WS").description("直播检测方式,WS为连接到B站消息服务器,API为通过轮询发送请求监测,默认使用WS检测"),
115
+ wordcloud: koishi.Schema.boolean().default(false).description("直播结束后,是否生成本场直播弹幕词云"),
115
116
  restartPush: koishi.Schema.boolean().default(true).description("插件重启后,如果订阅的主播正在直播,是否进行一次推送,默认开启"),
116
117
  pushTime: koishi.Schema.number().min(0).max(12).step(.5).default(1).description("设定间隔多长时间推送一次直播状态,单位为小时,默认为一小时"),
117
118
  customLiveStart: koishi.Schema.string().default("-name开播啦,当前粉丝数:-follower\\n-link").description("自定义开播提示语,-name代表UP昵称,-follower代表当前粉丝数,-link代表直播间链接(如果使用的是QQ官方机器人,请不要使用),\\n为换行。例如-name开播啦,会发送为xxxUP开播啦"),
@@ -798,56 +799,56 @@ var ComRegister = class {
798
799
  });
799
800
  biliCom.subcommand(".wc").action(async ({ session }) => {
800
801
  const words = [
801
- ["kq8z1", 57],
802
- ["v2n3a", 142],
803
- ["b7x9p", 13],
804
- ["w4m2s", 199],
805
- ["t1j6u", 85],
806
- ["z8c5l", 120],
807
- ["h3r7y", 34],
808
- ["n6d2q", 178],
809
- ["p9s4e", 66],
810
- ["f2g8b", 101],
811
- ["m5v1k", 12],
812
- ["x7a3w", 154],
813
- ["c4t9z", 47],
814
- ["u1b6n", 193],
815
- ["e8y2h", 23],
816
- ["j3l7p", 88],
817
- ["s6q4d", 132],
818
- ["g9m5x", 59],
819
- ["a2w8c", 175],
820
- ["l5h1v", 99],
821
- ["y7k3t", 41],
822
- ["d4z9u", 186],
823
- ["q1e6j", 27],
824
- ["r8p2s", 112],
825
- ["b3n7f", 73],
826
- ["v6x4m", 160],
827
- ["t9c5w", 53],
828
- ["z2u8h", 141],
829
- ["h5y1j", 36],
830
- ["n7l3s", 190],
831
- ["p4d9g", 18],
832
- ["f1b6a", 124],
833
- ["m8v2x", 62],
834
- ["x3a7c", 157],
835
- ["c6t4u", 44],
836
- ["u9b5e", 183],
837
- ["e2y8k", 29],
838
- ["j1l6q", 91],
839
- ["s8g3m", 138],
840
- ["g5w1t", 55],
841
- ["a7z4h", 172],
842
- ["l2h8p", 97],
843
- ["y5k1n", 39],
844
- ["d7u3f", 188],
845
- ["q4e9v", 21],
846
- ["r1p6x", 117],
847
- ["b8n5c", 70],
848
- ["v3x7w", 163],
849
- ["t6c4a", 49],
850
- ["z9u2j", 146]
802
+ ["弹幕护体", 77],
803
+ ["笑死", 30],
804
+ ["泪目", 45],
805
+ ["加油", 70],
806
+ ["???", 80],
807
+ ["我可以", 66],
808
+ ["无语", 55],
809
+ ["梦开始的地方", 1],
810
+ ["太真实了", 8],
811
+ ["我哭死", 1],
812
+ ["人呢", 2],
813
+ ["有点意思", 4],
814
+ ["妙啊", 4],
815
+ ["这波啊", 11],
816
+ ["懂了", 6],
817
+ ["破防了", 65],
818
+ ["蚌埠住了", 92],
819
+ ["", 100],
820
+ ["针不戳", 68],
821
+ ["yyds", 50],
822
+ ["DNA动了", 58],
823
+ ["猝不及防", 40],
824
+ ["建议加精", 15],
825
+ ["保护", 22],
826
+ ["害怕", 18],
827
+ ["就这?", 99],
828
+ ["2333", 20],
829
+ ["公开处刑", 35],
830
+ ["血压上来了", 45],
831
+ ["整不会了", 32],
832
+ ["见证历史", 88],
833
+ ["下次一定", 65],
834
+ ["奥利给", 56],
835
+ ["求更新", 18],
836
+ ["真实", 20],
837
+ ["好活当赏", 25],
838
+ ["泪,冲了出来", 68],
839
+ ["刻进DNA", 70],
840
+ ["我直接好家伙", 55],
841
+ ["夺笋啊", 40],
842
+ ["歪歪滴艾斯", 1],
843
+ ["典中典", 5],
844
+ ["麻中麻", 3],
845
+ ["绷不住了", 8],
846
+ ["逆天", 95],
847
+ ["", 65],
848
+ ["", 20],
849
+ ["", 101],
850
+ ["急急急", 3],
851
+ ["", 5]
851
852
  ];
852
853
  await session.send(/* @__PURE__ */ (0, __satorijs_element_jsx_runtime.jsx)("message", { children: koishi.h.image(await this.ctx.gi.generateWordCloudImg(words, "词云测试"), "image/jpg") }));
853
854
  });
@@ -1385,7 +1386,7 @@ var ComRegister = class {
1385
1386
  masterInfo,
1386
1387
  cardStyle
1387
1388
  }, uid, liveStartMsg);
1388
- if (!pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1389
+ if (this.config.pushTime !== 0 && !pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1389
1390
  },
1390
1391
  onLiveEnd: async () => {
1391
1392
  liveStatus = false;
@@ -1407,7 +1408,7 @@ var ComRegister = class {
1407
1408
  }, uid, liveEndMsg);
1408
1409
  pushAtTimeTimer();
1409
1410
  pushAtTimeTimer = null;
1410
- await sendDanmakuWordCloud();
1411
+ if (this.config.wordcloud) await sendDanmakuWordCloud();
1411
1412
  }
1412
1413
  };
1413
1414
  await this.ctx.bl.startLiveRoomListener(roomId, handler);
@@ -1421,7 +1422,7 @@ var ComRegister = class {
1421
1422
  masterInfo,
1422
1423
  cardStyle
1423
1424
  }, uid, liveMsg);
1424
- if (!pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1425
+ if (this.config.pushTime !== 0 && !pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1425
1426
  liveStatus = true;
1426
1427
  }
1427
1428
  }
@@ -1827,6 +1828,7 @@ var ComRegister = class {
1827
1828
  masterAccountGuildId: koishi.Schema.string()
1828
1829
  }),
1829
1830
  liveDetectType: koishi.Schema.string(),
1831
+ wordcloud: koishi.Schema.boolean(),
1830
1832
  restartPush: koishi.Schema.boolean().required(),
1831
1833
  pushTime: koishi.Schema.number().required(),
1832
1834
  pushImgsInDynamic: koishi.Schema.boolean().required(),
@@ -84243,6 +84245,7 @@ var require_tar_fs = __commonJS$1({ "node_modules/tar-fs/index.js"(exports) {
84243
84245
  const now = /* @__PURE__ */ new Date();
84244
84246
  const umask = typeof opts.umask === "number" ? ~opts.umask : ~processUmask();
84245
84247
  const strict = opts.strict !== false;
84248
+ const validateSymLinks = opts.validateSymlinks !== false;
84246
84249
  let map = opts.map || noop;
84247
84250
  let dmode = typeof opts.dmode === "number" ? opts.dmode : 0;
84248
84251
  let fmode = typeof opts.fmode === "number" ? opts.fmode : 0;
@@ -84310,7 +84313,7 @@ var require_tar_fs = __commonJS$1({ "node_modules/tar-fs/index.js"(exports) {
84310
84313
  if (win32$1) return next();
84311
84314
  xfs.unlink(name$3, function() {
84312
84315
  const dst = path$4.resolve(path$4.dirname(name$3), header.linkname);
84313
- if (!inCwd(dst)) return next(/* @__PURE__ */ new Error(name$3 + " is not a valid symlink"));
84316
+ if (!inCwd(dst) && validateSymLinks) return next(/* @__PURE__ */ new Error(name$3 + " is not a valid symlink"));
84314
84317
  xfs.symlink(header.linkname, name$3, stat);
84315
84318
  });
84316
84319
  }
@@ -94391,6 +94394,7 @@ var GenerateImg = class extends koishi.Service {
94391
94394
  async generateWordCloudImg(words, masterName) {
94392
94395
  const fontURL = (0, node_url.pathToFileURL)((0, node_path.resolve)(__dirname, "font/HYZhengYuan-75W.ttf"));
94393
94396
  const wordcloudJS = (0, node_url.pathToFileURL)((0, node_path.resolve)(__dirname, "static/wordcloud2.min.js"));
94397
+ const renderFunc = (0, node_url.pathToFileURL)((0, node_path.resolve)(__dirname, "static/render.js"));
94394
94398
  const html = `
94395
94399
  <!DOCTYPE html>
94396
94400
  <html lang="zh-CN">
@@ -94398,7 +94402,6 @@ var GenerateImg = class extends koishi.Service {
94398
94402
  <head>
94399
94403
  <meta charset="UTF-8">
94400
94404
  <title>高清词云展示</title>
94401
- <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap" rel="stylesheet">
94402
94405
  <style>
94403
94406
  @font-face {
94404
94407
  font-family: "Custom Font";
@@ -94464,6 +94467,7 @@ var GenerateImg = class extends koishi.Service {
94464
94467
  </div>
94465
94468
 
94466
94469
  <script src="${wordcloudJS}"></script>
94470
+ <script src="${renderFunc}"></script>
94467
94471
  <script>
94468
94472
  const canvas = document.getElementById('wordCloudCanvas');
94469
94473
  const ctx = canvas.getContext('2d');
@@ -94481,38 +94485,11 @@ var GenerateImg = class extends koishi.Service {
94481
94485
 
94482
94486
  const words = ${JSON.stringify(words)}
94483
94487
 
94484
- // 词云大小缩放
94485
- const maxWeight = Math.max(...words.map(w => w[1]));
94486
- const minWeight = Math.min(...words.map(w => w[1]));
94487
-
94488
- // 设置最大字体大小、最小字体大小(以像素为单位)
94489
- const maxFontSize = 60;
94490
- const minFontSize = 14;
94491
-
94492
- // 用映射函数代替 weightFactor
94493
- function getWeightFactor(size) {
94494
- if (maxWeight === minWeight) return maxFontSize; // 防止除0
94495
- const ratio = (size - minWeight) / (maxWeight - minWeight);
94496
- return minFontSize + (maxFontSize - minFontSize) * ratio;
94497
- }
94498
-
94499
- WordCloud(canvas, {
94500
- list: words,
94501
- gridSize: Math.round(8 * (cssWidth / 1024)), // 自动适配大小
94502
- weightFactor: getWeightFactor,
94503
- fontFamily: 'Quicksand, sans-serif',
94504
- color: () => {
94505
- const colors = ['#007CF0', '#00DFD8', '#7928CA', '#FF0080', '#FF4D4D', '#F9CB28'];
94506
- return colors[Math.floor(Math.random() * colors.length)];
94507
- },
94508
- rotateRatio: 0.5,
94509
- rotationSteps: 2,
94510
- backgroundColor: 'transparent',
94511
- drawOutOfBound: false,
94512
- origin: [cssWidth / 2, cssHeight / 2], // 居中关键点
94513
- // 明确告诉 wordcloud2 使用这个宽高(以 CSS 尺寸为准)
94514
- width: cssWidth,
94515
- height: cssHeight,
94488
+ renderAutoFitWordCloud(canvas, words, {
94489
+ maxFontSize: 40,
94490
+ minFontSize: 12,
94491
+ densityTarget: 0.3,
94492
+ weightExponent: 0.5
94516
94493
  });
94517
94494
  </script>
94518
94495
  </body>
@@ -95415,6 +95392,7 @@ var ServerManager = class extends koishi.Service {
95415
95392
  const cr = this.ctx.plugin(comRegister_default, {
95416
95393
  sub: globalConfig.sub,
95417
95394
  master: globalConfig.master,
95395
+ wordcloud: globalConfig.wordcloud,
95418
95396
  liveDetectType: globalConfig.liveDetectType,
95419
95397
  restartPush: globalConfig.restartPush,
95420
95398
  pushTime: globalConfig.pushTime,
package/lib/index.mjs CHANGED
@@ -114,6 +114,7 @@ const BAConfigSchema = Schema.object({
114
114
  pushImgsInDynamic: Schema.boolean().default(false).description("是否推送动态中的图片,默认不开启。开启后会单独推送动态中的图片,该功能容易导致QQ风控"),
115
115
  live: Schema.object({}).description("直播推送设置"),
116
116
  liveDetectType: Schema.union([Schema.const("WS").description("使用WebSocket连接到B站消息服务器进行直播检测,推荐使用"), Schema.const("API").description("通过轮询API发送请求监测直播状态,此模式理论可无限订阅,但容易产生其他问题,功能没有WS模式全面").experimental()]).role("radio").default("WS").description("直播检测方式,WS为连接到B站消息服务器,API为通过轮询发送请求监测,默认使用WS检测"),
117
+ wordcloud: Schema.boolean().default(false).description("直播结束后,是否生成本场直播弹幕词云"),
117
118
  restartPush: Schema.boolean().default(true).description("插件重启后,如果订阅的主播正在直播,是否进行一次推送,默认开启"),
118
119
  pushTime: Schema.number().min(0).max(12).step(.5).default(1).description("设定间隔多长时间推送一次直播状态,单位为小时,默认为一小时"),
119
120
  customLiveStart: Schema.string().default("-name开播啦,当前粉丝数:-follower\\n-link").description("自定义开播提示语,-name代表UP昵称,-follower代表当前粉丝数,-link代表直播间链接(如果使用的是QQ官方机器人,请不要使用),\\n为换行。例如-name开播啦,会发送为xxxUP开播啦"),
@@ -800,56 +801,56 @@ var ComRegister = class {
800
801
  });
801
802
  biliCom.subcommand(".wc").action(async ({ session }) => {
802
803
  const words = [
803
- ["kq8z1", 57],
804
- ["v2n3a", 142],
805
- ["b7x9p", 13],
806
- ["w4m2s", 199],
807
- ["t1j6u", 85],
808
- ["z8c5l", 120],
809
- ["h3r7y", 34],
810
- ["n6d2q", 178],
811
- ["p9s4e", 66],
812
- ["f2g8b", 101],
813
- ["m5v1k", 12],
814
- ["x7a3w", 154],
815
- ["c4t9z", 47],
816
- ["u1b6n", 193],
817
- ["e8y2h", 23],
818
- ["j3l7p", 88],
819
- ["s6q4d", 132],
820
- ["g9m5x", 59],
821
- ["a2w8c", 175],
822
- ["l5h1v", 99],
823
- ["y7k3t", 41],
824
- ["d4z9u", 186],
825
- ["q1e6j", 27],
826
- ["r8p2s", 112],
827
- ["b3n7f", 73],
828
- ["v6x4m", 160],
829
- ["t9c5w", 53],
830
- ["z2u8h", 141],
831
- ["h5y1j", 36],
832
- ["n7l3s", 190],
833
- ["p4d9g", 18],
834
- ["f1b6a", 124],
835
- ["m8v2x", 62],
836
- ["x3a7c", 157],
837
- ["c6t4u", 44],
838
- ["u9b5e", 183],
839
- ["e2y8k", 29],
840
- ["j1l6q", 91],
841
- ["s8g3m", 138],
842
- ["g5w1t", 55],
843
- ["a7z4h", 172],
844
- ["l2h8p", 97],
845
- ["y5k1n", 39],
846
- ["d7u3f", 188],
847
- ["q4e9v", 21],
848
- ["r1p6x", 117],
849
- ["b8n5c", 70],
850
- ["v3x7w", 163],
851
- ["t6c4a", 49],
852
- ["z9u2j", 146]
804
+ ["弹幕护体", 77],
805
+ ["笑死", 30],
806
+ ["泪目", 45],
807
+ ["加油", 70],
808
+ ["???", 80],
809
+ ["我可以", 66],
810
+ ["无语", 55],
811
+ ["梦开始的地方", 1],
812
+ ["太真实了", 8],
813
+ ["我哭死", 1],
814
+ ["人呢", 2],
815
+ ["有点意思", 4],
816
+ ["妙啊", 4],
817
+ ["这波啊", 11],
818
+ ["懂了", 6],
819
+ ["破防了", 65],
820
+ ["蚌埠住了", 92],
821
+ ["", 100],
822
+ ["针不戳", 68],
823
+ ["yyds", 50],
824
+ ["DNA动了", 58],
825
+ ["猝不及防", 40],
826
+ ["建议加精", 15],
827
+ ["保护", 22],
828
+ ["害怕", 18],
829
+ ["就这?", 99],
830
+ ["2333", 20],
831
+ ["公开处刑", 35],
832
+ ["血压上来了", 45],
833
+ ["整不会了", 32],
834
+ ["见证历史", 88],
835
+ ["下次一定", 65],
836
+ ["奥利给", 56],
837
+ ["求更新", 18],
838
+ ["真实", 20],
839
+ ["好活当赏", 25],
840
+ ["泪,冲了出来", 68],
841
+ ["刻进DNA", 70],
842
+ ["我直接好家伙", 55],
843
+ ["夺笋啊", 40],
844
+ ["歪歪滴艾斯", 1],
845
+ ["典中典", 5],
846
+ ["麻中麻", 3],
847
+ ["绷不住了", 8],
848
+ ["逆天", 95],
849
+ ["", 65],
850
+ ["", 20],
851
+ ["", 101],
852
+ ["急急急", 3],
853
+ ["", 5]
853
854
  ];
854
855
  await session.send(/* @__PURE__ */ jsx("message", { children: h.image(await this.ctx.gi.generateWordCloudImg(words, "词云测试"), "image/jpg") }));
855
856
  });
@@ -1387,7 +1388,7 @@ var ComRegister = class {
1387
1388
  masterInfo,
1388
1389
  cardStyle
1389
1390
  }, uid, liveStartMsg);
1390
- if (!pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1391
+ if (this.config.pushTime !== 0 && !pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1391
1392
  },
1392
1393
  onLiveEnd: async () => {
1393
1394
  liveStatus = false;
@@ -1409,7 +1410,7 @@ var ComRegister = class {
1409
1410
  }, uid, liveEndMsg);
1410
1411
  pushAtTimeTimer();
1411
1412
  pushAtTimeTimer = null;
1412
- await sendDanmakuWordCloud();
1413
+ if (this.config.wordcloud) await sendDanmakuWordCloud();
1413
1414
  }
1414
1415
  };
1415
1416
  await this.ctx.bl.startLiveRoomListener(roomId, handler);
@@ -1423,7 +1424,7 @@ var ComRegister = class {
1423
1424
  masterInfo,
1424
1425
  cardStyle
1425
1426
  }, uid, liveMsg);
1426
- if (!pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1427
+ if (this.config.pushTime !== 0 && !pushAtTimeTimer) pushAtTimeTimer = this.ctx.setInterval(pushAtTimeFunc, this.config.pushTime * 1e3 * 60 * 60);
1427
1428
  liveStatus = true;
1428
1429
  }
1429
1430
  }
@@ -1829,6 +1830,7 @@ var ComRegister = class {
1829
1830
  masterAccountGuildId: Schema.string()
1830
1831
  }),
1831
1832
  liveDetectType: Schema.string(),
1833
+ wordcloud: Schema.boolean(),
1832
1834
  restartPush: Schema.boolean().required(),
1833
1835
  pushTime: Schema.number().required(),
1834
1836
  pushImgsInDynamic: Schema.boolean().required(),
@@ -84245,6 +84247,7 @@ var require_tar_fs = __commonJS$1({ "node_modules/tar-fs/index.js"(exports) {
84245
84247
  const now = /* @__PURE__ */ new Date();
84246
84248
  const umask = typeof opts.umask === "number" ? ~opts.umask : ~processUmask();
84247
84249
  const strict = opts.strict !== false;
84250
+ const validateSymLinks = opts.validateSymlinks !== false;
84248
84251
  let map = opts.map || noop;
84249
84252
  let dmode = typeof opts.dmode === "number" ? opts.dmode : 0;
84250
84253
  let fmode = typeof opts.fmode === "number" ? opts.fmode : 0;
@@ -84312,7 +84315,7 @@ var require_tar_fs = __commonJS$1({ "node_modules/tar-fs/index.js"(exports) {
84312
84315
  if (win32$1) return next();
84313
84316
  xfs.unlink(name$3, function() {
84314
84317
  const dst = path$3.resolve(path$3.dirname(name$3), header.linkname);
84315
- if (!inCwd(dst)) return next(/* @__PURE__ */ new Error(name$3 + " is not a valid symlink"));
84318
+ if (!inCwd(dst) && validateSymLinks) return next(/* @__PURE__ */ new Error(name$3 + " is not a valid symlink"));
84316
84319
  xfs.symlink(header.linkname, name$3, stat);
84317
84320
  });
84318
84321
  }
@@ -94393,6 +94396,7 @@ var GenerateImg = class extends Service {
94393
94396
  async generateWordCloudImg(words, masterName) {
94394
94397
  const fontURL = pathToFileURL(resolve$1(__dirname, "font/HYZhengYuan-75W.ttf"));
94395
94398
  const wordcloudJS = pathToFileURL(resolve$1(__dirname, "static/wordcloud2.min.js"));
94399
+ const renderFunc = pathToFileURL(resolve$1(__dirname, "static/render.js"));
94396
94400
  const html = `
94397
94401
  <!DOCTYPE html>
94398
94402
  <html lang="zh-CN">
@@ -94400,7 +94404,6 @@ var GenerateImg = class extends Service {
94400
94404
  <head>
94401
94405
  <meta charset="UTF-8">
94402
94406
  <title>高清词云展示</title>
94403
- <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap" rel="stylesheet">
94404
94407
  <style>
94405
94408
  @font-face {
94406
94409
  font-family: "Custom Font";
@@ -94466,6 +94469,7 @@ var GenerateImg = class extends Service {
94466
94469
  </div>
94467
94470
 
94468
94471
  <script src="${wordcloudJS}"></script>
94472
+ <script src="${renderFunc}"></script>
94469
94473
  <script>
94470
94474
  const canvas = document.getElementById('wordCloudCanvas');
94471
94475
  const ctx = canvas.getContext('2d');
@@ -94483,38 +94487,11 @@ var GenerateImg = class extends Service {
94483
94487
 
94484
94488
  const words = ${JSON.stringify(words)}
94485
94489
 
94486
- // 词云大小缩放
94487
- const maxWeight = Math.max(...words.map(w => w[1]));
94488
- const minWeight = Math.min(...words.map(w => w[1]));
94489
-
94490
- // 设置最大字体大小、最小字体大小(以像素为单位)
94491
- const maxFontSize = 60;
94492
- const minFontSize = 14;
94493
-
94494
- // 用映射函数代替 weightFactor
94495
- function getWeightFactor(size) {
94496
- if (maxWeight === minWeight) return maxFontSize; // 防止除0
94497
- const ratio = (size - minWeight) / (maxWeight - minWeight);
94498
- return minFontSize + (maxFontSize - minFontSize) * ratio;
94499
- }
94500
-
94501
- WordCloud(canvas, {
94502
- list: words,
94503
- gridSize: Math.round(8 * (cssWidth / 1024)), // 自动适配大小
94504
- weightFactor: getWeightFactor,
94505
- fontFamily: 'Quicksand, sans-serif',
94506
- color: () => {
94507
- const colors = ['#007CF0', '#00DFD8', '#7928CA', '#FF0080', '#FF4D4D', '#F9CB28'];
94508
- return colors[Math.floor(Math.random() * colors.length)];
94509
- },
94510
- rotateRatio: 0.5,
94511
- rotationSteps: 2,
94512
- backgroundColor: 'transparent',
94513
- drawOutOfBound: false,
94514
- origin: [cssWidth / 2, cssHeight / 2], // 居中关键点
94515
- // 明确告诉 wordcloud2 使用这个宽高(以 CSS 尺寸为准)
94516
- width: cssWidth,
94517
- height: cssHeight,
94490
+ renderAutoFitWordCloud(canvas, words, {
94491
+ maxFontSize: 40,
94492
+ minFontSize: 12,
94493
+ densityTarget: 0.3,
94494
+ weightExponent: 0.5
94518
94495
  });
94519
94496
  </script>
94520
94497
  </body>
@@ -95417,6 +95394,7 @@ var ServerManager = class extends Service {
95417
95394
  const cr = this.ctx.plugin(comRegister_default, {
95418
95395
  sub: globalConfig.sub,
95419
95396
  master: globalConfig.master,
95397
+ wordcloud: globalConfig.wordcloud,
95420
95398
  liveDetectType: globalConfig.liveDetectType,
95421
95399
  restartPush: globalConfig.restartPush,
95422
95400
  pushTime: globalConfig.pushTime,
@@ -0,0 +1,107 @@
1
+ /**
2
+ * 自动适配的词云渲染函数,直到填满画布
3
+ * @param {HTMLCanvasElement} canvas - canvas 元素
4
+ * @param {Array<[string, number]>} words - 词数组 [['哈哈', 20], ['666', 10]]
5
+ * @param {Object} options - 选项
6
+ */
7
+ function renderAutoFitWordCloud(canvas, words, options = {}) {
8
+ const ctx = canvas.getContext("2d");
9
+ const style = getComputedStyle(canvas);
10
+ const cssWidth = parseInt(style.width);
11
+ const cssHeight = parseInt(style.height);
12
+ const ratio = window.devicePixelRatio || 1;
13
+
14
+ // 设置高清分辨率
15
+ canvas.width = cssWidth * ratio;
16
+ canvas.height = cssHeight * ratio;
17
+ ctx.scale(ratio, ratio);
18
+
19
+ const {
20
+ fontFamily = 'sans-serif',
21
+ maxFontSize = 72,
22
+ minFontSize = 12,
23
+ densityTarget = 0.25,
24
+ weightExponent = 0.5,
25
+ rotationSteps = 2,
26
+ rotateRatio = 0.4,
27
+ color = () => ['#007CF0', '#00DFD8', '#7928CA', '#FF0080', '#FF4D4D', '#F9CB28'][Math.floor(Math.random() * 6)],
28
+ backgroundColor = 'transparent'
29
+ } = options;
30
+
31
+ const weightFactor = createDynamicWeightFactor(words, cssWidth, cssHeight, {
32
+ maxFontSize,
33
+ minFontSize,
34
+ densityTarget,
35
+ weightExponent
36
+ });
37
+
38
+ WordCloud(canvas, {
39
+ list: words,
40
+ gridSize: Math.max(2, Math.floor(cssWidth / 100)), // 自动调整 gridSize
41
+ weightFactor,
42
+ fontFamily,
43
+ color,
44
+ rotateRatio,
45
+ rotationSteps,
46
+ backgroundColor,
47
+ drawOutOfBound: false,
48
+ origin: [cssWidth / 2, cssHeight / 2],
49
+ width: cssWidth,
50
+ height: cssHeight,
51
+ });
52
+ }
53
+
54
+ /* function createDynamicWeightFactor(words, width, height, {
55
+ maxFontSize = 72,
56
+ minFontSize = 12,
57
+ densityTarget = 0.25
58
+ } = {}) {
59
+ const weights = words.map(w => w[1]);
60
+ const maxWeight = Math.max(...weights);
61
+ const minWeight = Math.min(...weights);
62
+ const range = maxWeight - minWeight || 1;
63
+
64
+ // 移除基于词数的自动缩放
65
+ const realMax = maxFontSize;
66
+ const realMin = minFontSize;
67
+
68
+ // 改为基于画布面积的密度控制
69
+ const areaPerWord = (width * height * densityTarget) / words.length;
70
+ const sizeScale = Math.min(1, Math.sqrt(areaPerWord / 500)); // 500是可调基准值
71
+
72
+ return function weightFactor(weight) {
73
+ const norm = (weight - minWeight) / range;
74
+ // 应用三次方曲线让大小差异更明显
75
+ const sizeRatio = Math.pow(norm, 0.33);
76
+ return realMin + (realMax - realMin) * sizeRatio * sizeScale;
77
+ };
78
+ } */
79
+
80
+ function createDynamicWeightFactor(words, width, height, {
81
+ maxFontSize = 72,
82
+ minFontSize = 12,
83
+ densityTarget = 0.25,
84
+ weightExponent = 0.5 // 新增权重指数参数
85
+ } = {}) {
86
+ const weights = words.map(w => w[1]);
87
+ const maxWeight = Math.max(...weights);
88
+ const minWeight = Math.min(...weights);
89
+ const range = Math.max(1, maxWeight - minWeight); // 确保不为0
90
+
91
+ // 计算权重转换曲线
92
+ const weightCurve = (weight) => {
93
+ const normalized = (weight - minWeight) / range;
94
+ // 使用指数曲线放大差异
95
+ return Math.pow(normalized, weightExponent);
96
+ };
97
+
98
+ // 计算动态缩放比例(基于画布面积和词数)
99
+ const areaScale = Math.sqrt((width * height * densityTarget) / words.length) / 20;
100
+ const sizeScale = Math.min(1, Math.max(0.3, areaScale)); // 限制在0.3-1之间
101
+
102
+ return function (weight) {
103
+ const curveValue = weightCurve(weight);
104
+ // 应用非线性放大
105
+ return minFontSize + (maxFontSize - minFontSize) * curveValue * sizeScale;
106
+ };
107
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-bilibili-notify",
3
3
  "description": "Koishi bilibili notify plugin",
4
- "version": "3.2.9-alpha.4",
4
+ "version": "3.2.9-rc.1",
5
5
  "contributors": [
6
6
  "Akokko <admin@akokko.com>"
7
7
  ],
package/readme.md CHANGED
@@ -305,6 +305,8 @@ uid为必填参数,为要推送的UP主的UID,index为可选参数,为要
305
305
  > - ver 3.2.9-alpha.2 修复:`AxiosError: Request failed with status code 404 xxx at async BiliAPI.checkIfTokenNeedRefresh`、潜在cookie相关bug、弹幕词云bug `Error: 生成图片失败!错误: TimeoutError: Navigation timeout of 30000 ms exceeded`
306
306
  > - ver 3.2.9-alpha.3 修复:词云生成空白
307
307
  > - ver 3.2.9-alpha.4 修复:弹幕词云bug `Error: 生成图片失败!错误: TimeoutError: Navigation timeout of 30000 ms exceeded`
308
+ > - ver 3.2.9-rc.0 优化:弹幕词云生成效果、选项 `pushTime` 设置为0时可关闭该功能; 新增:选项 `wordcloud` 可选择在直播结束后是否生成弹幕词云
309
+ > - ver 3.2.9-rc.1 优化:弹幕词云生成效果;
308
310
 
309
311
  ## 交流群
310
312