koishi-plugin-bilibili-notify 3.2.8-alpha.1 → 3.2.9-alpha.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.
package/lib/index.js CHANGED
@@ -39,6 +39,7 @@ const path = __toESM$1(require("path"));
39
39
  const qrcode = __toESM$1(require("qrcode"));
40
40
  const cron = __toESM$1(require("cron"));
41
41
  const luxon = __toESM$1(require("luxon"));
42
+ const segmentit = __toESM$1(require("segmentit"));
42
43
  const __satorijs_element_jsx_runtime = __toESM$1(require("@satorijs/element/jsx-runtime"));
43
44
  const node_path = __toESM$1(require("node:path"));
44
45
  const node_url = __toESM$1(require("node:url"));
@@ -646,6 +647,7 @@ var ComRegister = class {
646
647
  privateBot;
647
648
  dynamicJob;
648
649
  liveJob;
650
+ _segmentit = (0, segmentit.useDefault)(new segmentit.Segment());
649
651
  constructor(ctx, config) {
650
652
  this.ctx = ctx;
651
653
  this.init(config);
@@ -1221,10 +1223,16 @@ var ComRegister = class {
1221
1223
  const msg = /* @__PURE__ */ (0, __satorijs_element_jsx_runtime.jsxs)("message", { children: [koishi.h.image(buffer, "image/jpeg"), liveNotifyMsg || ""] });
1222
1224
  return await this.broadcastToTargets(uid, msg, liveType === LiveType.StartBroadcasting ? PushType.StartBroadcasting : PushType.Live);
1223
1225
  }
1226
+ async segmentDanmaku(danmaku, danmakuWeightRecord) {
1227
+ this._segmentit.doSegment(danmaku).map(({ w: w$3, p: p$1 }) => {
1228
+ if (p$1 && p$1 === 2048) return;
1229
+ danmakuWeightRecord[w$3] = (danmakuWeightRecord[w$3] || 0) + 1;
1230
+ });
1231
+ }
1224
1232
  async liveDetectWithListener(roomId, uid, cardStyle) {
1225
1233
  let liveTime;
1226
1234
  let pushAtTimeTimer;
1227
- const currentLiveDanmakuArr = [];
1235
+ const danmakuWeightRecord = {};
1228
1236
  let liveStatus = false;
1229
1237
  let liveRoomInfo;
1230
1238
  let masterInfo;
@@ -1243,7 +1251,7 @@ var ComRegister = class {
1243
1251
  }
1244
1252
  liveTime = liveRoomInfo.live_time;
1245
1253
  const watched = watchedNum || "暂未获取到";
1246
- const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1254
+ const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1247
1255
  await this.sendLiveNotifyCard(LiveType.LiveBroadcast, watched, {
1248
1256
  liveRoomInfo,
1249
1257
  masterInfo,
@@ -1275,10 +1283,10 @@ var ComRegister = class {
1275
1283
  this.logger.error(`[${roomId}]直播间连接发生错误!`);
1276
1284
  },
1277
1285
  onIncomeDanmu: ({ body }) => {
1278
- currentLiveDanmakuArr.push(body.content);
1286
+ this.segmentDanmaku(body.content, danmakuWeightRecord);
1279
1287
  },
1280
1288
  onIncomeSuperChat: ({ body }) => {
1281
- currentLiveDanmakuArr.push(body.content);
1289
+ this.segmentDanmaku(body.content, danmakuWeightRecord);
1282
1290
  },
1283
1291
  onWatchedChange: ({ body }) => {
1284
1292
  watchedNum = body.text_small;
@@ -1305,7 +1313,7 @@ var ComRegister = class {
1305
1313
  }
1306
1314
  liveTime = liveRoomInfo.live_time;
1307
1315
  const follower = masterInfo.liveOpenFollowerNum >= 1e4 ? `${(masterInfo.liveOpenFollowerNum / 1e4).toFixed(1)}万` : masterInfo.liveOpenFollowerNum.toString();
1308
- const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower", follower).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1316
+ const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower", follower).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1309
1317
  await this.sendLiveNotifyCard(LiveType.StartBroadcasting, follower, {
1310
1318
  liveRoomInfo,
1311
1319
  masterInfo,
@@ -1325,7 +1333,7 @@ var ComRegister = class {
1325
1333
  if (liveFollowerChangeNum > 0) return liveFollowerChangeNum >= 1e4 ? `+${liveFollowerChangeNum.toFixed(1)}万` : `+${liveFollowerChangeNum}`;
1326
1334
  return liveFollowerChangeNum <= -1e4 ? `${liveFollowerChangeNum.toFixed(1)}万` : liveFollowerChangeNum.toString();
1327
1335
  })();
1328
- const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower_change", followerChange).replace("\\n", "\n");
1336
+ const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower_change", followerChange).replaceAll("\\n", "\n");
1329
1337
  await this.sendLiveNotifyCard(LiveType.StopBroadcast, followerChange, {
1330
1338
  liveRoomInfo,
1331
1339
  masterInfo,
@@ -1333,6 +1341,9 @@ var ComRegister = class {
1333
1341
  }, uid, liveEndMsg);
1334
1342
  pushAtTimeTimer();
1335
1343
  pushAtTimeTimer = null;
1344
+ const words = Object.entries(danmakuWeightRecord);
1345
+ const buffer = await this.ctx.gi.generateWordCloudImg(words, masterInfo.username);
1346
+ await this.broadcastToTargets(uid, koishi.h.image(buffer, "image/jpeg"), PushType.Live);
1336
1347
  }
1337
1348
  };
1338
1349
  await this.ctx.bl.startLiveRoomListener(roomId, handler);
@@ -1340,7 +1351,7 @@ var ComRegister = class {
1340
1351
  if (liveRoomInfo.live_status === 1) {
1341
1352
  liveTime = liveRoomInfo.live_time;
1342
1353
  const watched = watchedNum || "暂未获取到";
1343
- const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1354
+ const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1344
1355
  if (this.config.restartPush) await this.sendLiveNotifyCard(LiveType.LiveBroadcast, watched, {
1345
1356
  liveRoomInfo,
1346
1357
  masterInfo,
@@ -1392,7 +1403,7 @@ var ComRegister = class {
1392
1403
  liveStatus.liveStartTime = liveStatus.liveRoomInfo.live_time;
1393
1404
  liveStatus.liveStartTimeInit = true;
1394
1405
  }
1395
- const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1406
+ const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1396
1407
  await this.sendLiveNotifyCard(LiveType.LiveBroadcast, "API", {
1397
1408
  liveRoomInfo: liveStatus.liveRoomInfo,
1398
1409
  masterInfo: liveStatus.masterInfo,
@@ -1425,7 +1436,7 @@ var ComRegister = class {
1425
1436
  if (liveFollowerChangeNum > 0) return liveFollowerChangeNum >= 1e4 ? `+${liveFollowerChangeNum.toFixed(1)}万` : `+${liveFollowerChangeNum}`;
1426
1437
  return liveFollowerChangeNum <= -1e4 ? `${liveFollowerChangeNum.toFixed(1)}万` : liveFollowerChangeNum.toString();
1427
1438
  })();
1428
- const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower_change", followerChange).replace("\\n", "\n");
1439
+ const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower_change", followerChange).replaceAll("\\n", "\n");
1429
1440
  await this.sendLiveNotifyCard(LiveType.StopBroadcast, followerChange, {
1430
1441
  liveRoomInfo: liveStatus.liveRoomInfo,
1431
1442
  masterInfo: liveStatus.masterInfo,
@@ -1444,7 +1455,7 @@ var ComRegister = class {
1444
1455
  liveStatus.liveStartTime = liveStatus.liveRoomInfo.live_time;
1445
1456
  liveStatus.liveStartTimeInit = true;
1446
1457
  const follower = liveStatus.masterInfo.liveOpenFollowerNum >= 1e4 ? `${(liveStatus.masterInfo.liveOpenFollowerNum / 1e4).toFixed(1)}万` : liveStatus.masterInfo.liveOpenFollowerNum.toString();
1447
- const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower", follower).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1458
+ const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower", follower).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1448
1459
  await this.sendLiveNotifyCard(LiveType.StartBroadcasting, follower, {
1449
1460
  liveRoomInfo: liveStatus.liveRoomInfo,
1450
1461
  masterInfo: liveStatus.masterInfo,
@@ -1465,7 +1476,7 @@ var ComRegister = class {
1465
1476
  liveStatus.liveStartTime = liveStatus.liveRoomInfo.live_time;
1466
1477
  liveStatus.liveStartTimeInit = true;
1467
1478
  }
1468
- const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1479
+ const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1469
1480
  await this.sendLiveNotifyCard(LiveType.LiveBroadcast, "API", {
1470
1481
  liveRoomInfo: liveStatus.liveRoomInfo,
1471
1482
  masterInfo: liveStatus.masterInfo,
@@ -59790,8 +59801,6 @@ var require_parseListUnix = __commonJS$1({ "node_modules/basic-ftp/dist/parseLis
59790
59801
  exports.transformList = exports.parseLine = exports.testLine = void 0;
59791
59802
  const FileInfo_1$1 = require_FileInfo();
59792
59803
  const JA_MONTH = "月";
59793
- const JA_DAY = "日";
59794
- const JA_YEAR = "年";
59795
59804
  /**
59796
59805
  * This parser is based on the FTP client library source code in Apache Commons Net provided
59797
59806
  * under the Apache 2.0 license. It has been simplified and rewritten to better fit the Javascript language.
@@ -59822,7 +59831,7 @@ var require_parseListUnix = __commonJS$1({ "node_modules/basic-ftp/dist/parseLis
59822
59831
  * this is not allowed for here as does not appear to be shown by FTP servers
59823
59832
  * {@code @} file has extended attributes
59824
59833
  */
59825
- const RE_LINE = /* @__PURE__ */ new RegExp("([bcdelfmpSs-])(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]?)))\\+?\\s*(\\d+)\\s+(?:(\\S+(?:\\s\\S+)*?)\\s+)?(?:(\\S+(?:\\s\\S+)*)\\s+)?(\\d+(?:,\\s*\\d+)?)\\s+((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3})|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + "))\\s+((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))\\s(.*)");
59834
+ const RE_LINE = /* @__PURE__ */ new RegExp("([bcdelfmpSs-])(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]?)))\\+?\\s*(\\d+)\\s+(?:(\\S+(?:\\s\\S+)*?)\\s+)?(?:(\\S+(?:\\s\\S+)*)\\s+)?(\\d+(?:,\\s*\\d+)?)\\s+((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3})|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}))\\s+((?:\\d+(?::\\d+)?)|(?:\\d{4}))\\s(.*)");
59826
59835
  /**
59827
59836
  * Returns true if a given line might be a Unix-style listing.
59828
59837
  *
@@ -82365,32 +82374,32 @@ var require_streamx = __commonJS$1({ "node_modules/streamx/index.js"(exports, mo
82365
82374
  const ACTIVE = READ_ACTIVE | WRITE_ACTIVE;
82366
82375
  const NOT_ACTIVE = MAX ^ ACTIVE;
82367
82376
  const DONE = READ_DONE | WRITE_DONE;
82368
- const DESTROY_STATUS = DESTROYING | DESTROYED | PREDESTROYING;
82377
+ const DESTROY_STATUS = DESTROYED | 6;
82369
82378
  const OPEN_STATUS = DESTROY_STATUS | OPENING;
82370
82379
  const AUTO_DESTROY = DESTROY_STATUS | DONE;
82371
82380
  const NON_PRIMARY = WRITE_NON_PRIMARY & READ_NON_PRIMARY;
82372
82381
  const ACTIVE_OR_TICKING = WRITE_NEXT_TICK | READ_NEXT_TICK;
82373
82382
  const TICKING = ACTIVE_OR_TICKING & NOT_ACTIVE;
82374
82383
  const IS_OPENING = OPEN_STATUS | TICKING;
82375
- const READ_PRIMARY_STATUS = OPEN_STATUS | READ_ENDING | READ_DONE;
82376
- const READ_STATUS = OPEN_STATUS | READ_DONE | READ_QUEUED;
82377
- const READ_ENDING_STATUS = OPEN_STATUS | READ_ENDING | READ_QUEUED;
82378
- const READ_READABLE_STATUS = OPEN_STATUS | READ_EMIT_READABLE | READ_QUEUED | READ_EMITTED_READABLE;
82379
- const SHOULD_NOT_READ = OPEN_STATUS | READ_ACTIVE | READ_ENDING | READ_DONE | READ_NEEDS_PUSH | READ_READ_AHEAD;
82380
- const READ_BACKPRESSURE_STATUS = DESTROY_STATUS | READ_ENDING | READ_DONE;
82381
- const READ_UPDATE_SYNC_STATUS = READ_UPDATING | OPEN_STATUS | READ_NEXT_TICK | READ_PRIMARY;
82384
+ const READ_PRIMARY_STATUS = READ_ENDING | 16399;
82385
+ const READ_STATUS = READ_DONE | 143;
82386
+ const READ_ENDING_STATUS = READ_ENDING | 143;
82387
+ const READ_READABLE_STATUS = 143 | 12288;
82388
+ const SHOULD_NOT_READ = 66575 | 147472;
82389
+ const READ_BACKPRESSURE_STATUS = READ_ENDING | 16398;
82390
+ const READ_UPDATE_SYNC_STATUS = 32800 | 79;
82382
82391
  const READ_NEXT_TICK_OR_OPENING = READ_NEXT_TICK | OPENING;
82383
- const WRITE_PRIMARY_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_DONE;
82392
+ const WRITE_PRIMARY_STATUS = WRITE_FINISHING | 8388623;
82384
82393
  const WRITE_QUEUED_AND_UNDRAINED = WRITE_QUEUED | WRITE_UNDRAINED;
82385
82394
  const WRITE_QUEUED_AND_ACTIVE = WRITE_QUEUED | WRITE_ACTIVE;
82386
- const WRITE_DRAIN_STATUS = WRITE_QUEUED | WRITE_UNDRAINED | OPEN_STATUS | WRITE_ACTIVE;
82387
- const WRITE_STATUS = OPEN_STATUS | WRITE_ACTIVE | WRITE_QUEUED | WRITE_CORKED;
82395
+ const WRITE_DRAIN_STATUS = 2097167 | 4456448;
82396
+ const WRITE_STATUS = 2097167 | 268697600;
82388
82397
  const WRITE_PRIMARY_AND_ACTIVE = WRITE_PRIMARY | WRITE_ACTIVE;
82389
82398
  const WRITE_ACTIVE_AND_WRITING = WRITE_ACTIVE | WRITE_WRITING;
82390
- const WRITE_FINISHING_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_QUEUED_AND_ACTIVE | WRITE_DONE;
82391
- const WRITE_BACKPRESSURE_STATUS = WRITE_UNDRAINED | DESTROY_STATUS | WRITE_FINISHING | WRITE_DONE;
82392
- const WRITE_UPDATE_SYNC_STATUS = WRITE_UPDATING | OPEN_STATUS | WRITE_NEXT_TICK | WRITE_PRIMARY;
82393
- const WRITE_DROP_DATA = WRITE_FINISHING | WRITE_DONE | DESTROY_STATUS;
82399
+ const WRITE_FINISHING_STATUS = 2359311 | 142606336;
82400
+ const WRITE_BACKPRESSURE_STATUS = 138412032 | 8388622;
82401
+ const WRITE_UPDATE_SYNC_STATUS = 34078720 | 1048591;
82402
+ const WRITE_DROP_DATA = WRITE_DONE | 134217742;
82394
82403
  const asyncIterator = Symbol.asyncIterator || Symbol("asyncIterator");
82395
82404
  var WritableState = class {
82396
82405
  constructor(stream$3, { highWaterMark = 16384, map = null, mapWritable: mapWritable$1, byteLength: byteLength$1, byteLengthWritable } = {}) {
@@ -82835,7 +82844,7 @@ var require_streamx = __commonJS$1({ "node_modules/streamx/index.js"(exports, mo
82835
82844
  var Readable$2 = class Readable$2 extends Stream$1 {
82836
82845
  constructor(opts) {
82837
82846
  super(opts);
82838
- this._duplexState |= OPENING | WRITE_DONE | READ_READ_AHEAD;
82847
+ this._duplexState |= WRITE_DONE | 131073;
82839
82848
  this._readableState = new ReadableState(this, opts);
82840
82849
  if (opts) {
82841
82850
  if (this._readableState.readAhead === false) this._duplexState &= READ_NO_READ_AHEAD;
@@ -94314,6 +94323,118 @@ var GenerateImg = class extends koishi.Service {
94314
94323
  throw new Error(`生成图片失败!错误: ${e$1.toString()}`);
94315
94324
  });
94316
94325
  }
94326
+ async generateWordCloudImg(words, masterName) {
94327
+ const html = `
94328
+ <!DOCTYPE html>
94329
+ <html lang="zh-CN">
94330
+
94331
+ <head>
94332
+ <meta charset="UTF-8">
94333
+ <title>高清词云展示</title>
94334
+ <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap" rel="stylesheet">
94335
+ <style>
94336
+ * {
94337
+ margin: 0;
94338
+ padding: 0;
94339
+ box-sizing: border-box;
94340
+ }
94341
+
94342
+ html {
94343
+ width: 720px;
94344
+ height: 520px;
94345
+ }
94346
+
94347
+ .wordcloud-bg {
94348
+ width: 720px;
94349
+ height: 520px;
94350
+ background: linear-gradient(to right, #e0eafc, #cfdef3);
94351
+ font-family: 'Quicksand', sans-serif;
94352
+ display: flex;
94353
+ justify-content: center;
94354
+ align-items: center;
94355
+ }
94356
+
94357
+ .wordcloud-card {
94358
+ width: 700px;
94359
+ height: 500px;
94360
+ backdrop-filter: blur(10px);
94361
+ background: rgba(255, 255, 255, 0.25);
94362
+ border-radius: 20px;
94363
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
94364
+ padding: 20px;
94365
+ display: flex;
94366
+ flex-direction: column;
94367
+ align-items: center;
94368
+ justify-content: center;
94369
+ }
94370
+
94371
+ h2 {
94372
+ margin: 0 0 10px;
94373
+ color: #333;
94374
+ font-size: 24px;
94375
+ }
94376
+
94377
+ canvas {
94378
+ width: 100%;
94379
+ height: 100%;
94380
+ display: block;
94381
+ }
94382
+ </style>
94383
+ </head>
94384
+
94385
+ <body>
94386
+ <div class="wordcloud-bg">
94387
+ <div class="wordcloud-card">
94388
+ <h2>${masterName}直播弹幕词云</h2>
94389
+ <canvas id="wordCloudCanvas"></canvas>
94390
+ </div>
94391
+ </div>
94392
+
94393
+ <script src="https://cdn.jsdelivr.net/npm/wordcloud@1.1.2/src/wordcloud2.min.js"></script>
94394
+ <script>
94395
+ const canvas = document.getElementById('wordCloudCanvas');
94396
+ const ctx = canvas.getContext('2d');
94397
+
94398
+ // 获取 CSS 大小
94399
+ const style = getComputedStyle(canvas);
94400
+ const cssWidth = parseInt(style.width);
94401
+ const cssHeight = parseInt(style.height);
94402
+ const ratio = window.devicePixelRatio || 1;
94403
+
94404
+ // 设置 canvas 分辨率 & 缩放
94405
+ canvas.width = cssWidth * ratio;
94406
+ canvas.height = cssHeight * ratio;
94407
+ ctx.scale(ratio, ratio);
94408
+
94409
+ const words = ${JSON.stringify(words)}
94410
+
94411
+ WordCloud(canvas, {
94412
+ list: words,
94413
+ gridSize: Math.round(8 * (cssWidth / 1024)), // 自动适配大小
94414
+ weightFactor: size => size * (cssWidth / 1024) * 1.2,
94415
+ fontFamily: 'Quicksand, sans-serif',
94416
+ color: () => {
94417
+ const colors = ['#007CF0', '#00DFD8', '#7928CA', '#FF0080', '#FF4D4D', '#F9CB28'];
94418
+ return colors[Math.floor(Math.random() * colors.length)];
94419
+ },
94420
+ rotateRatio: 0.5,
94421
+ rotationSteps: 2,
94422
+ backgroundColor: 'transparent',
94423
+ drawOutOfBound: false,
94424
+ origin: [cssWidth / 2, cssHeight / 2], // 居中关键点
94425
+ // 明确告诉 wordcloud2 使用这个宽高(以 CSS 尺寸为准)
94426
+ width: cssWidth,
94427
+ height: cssHeight,
94428
+ });
94429
+ </script>
94430
+ </body>
94431
+
94432
+ </html>
94433
+ `;
94434
+ return await withRetry(() => this.imgHandler(html)).catch((e$1) => {
94435
+ throw new Error(`生成图片失败!错误: ${e$1.toString()}`);
94436
+ });
94437
+ }
94317
94438
  async getLiveStatus(time, liveStatus) {
94318
94439
  let titleStatus;
94319
94440
  let liveTime;
package/lib/index.mjs CHANGED
@@ -4,6 +4,7 @@ import { resolve } from "path";
4
4
  import QRCode from "qrcode";
5
5
  import { CronJob } from "cron";
6
6
  import { DateTime } from "luxon";
7
+ import { Segment, useDefault } from "segmentit";
7
8
  import { Fragment, jsx, jsxs } from "@satorijs/element/jsx-runtime";
8
9
  import { resolve as resolve$1 } from "node:path";
9
10
  import { pathToFileURL } from "node:url";
@@ -648,6 +649,7 @@ var ComRegister = class {
648
649
  privateBot;
649
650
  dynamicJob;
650
651
  liveJob;
652
+ _segmentit = useDefault(new Segment());
651
653
  constructor(ctx, config) {
652
654
  this.ctx = ctx;
653
655
  this.init(config);
@@ -1223,10 +1225,16 @@ var ComRegister = class {
1223
1225
  const msg = /* @__PURE__ */ jsxs("message", { children: [h.image(buffer, "image/jpeg"), liveNotifyMsg || ""] });
1224
1226
  return await this.broadcastToTargets(uid, msg, liveType === LiveType.StartBroadcasting ? PushType.StartBroadcasting : PushType.Live);
1225
1227
  }
1228
+ async segmentDanmaku(danmaku, danmakuWeightRecord) {
1229
+ this._segmentit.doSegment(danmaku).map(({ w: w$3, p: p$1 }) => {
1230
+ if (p$1 && p$1 === 2048) return;
1231
+ danmakuWeightRecord[w$3] = (danmakuWeightRecord[w$3] || 0) + 1;
1232
+ });
1233
+ }
1226
1234
  async liveDetectWithListener(roomId, uid, cardStyle) {
1227
1235
  let liveTime;
1228
1236
  let pushAtTimeTimer;
1229
- const currentLiveDanmakuArr = [];
1237
+ const danmakuWeightRecord = {};
1230
1238
  let liveStatus = false;
1231
1239
  let liveRoomInfo;
1232
1240
  let masterInfo;
@@ -1245,7 +1253,7 @@ var ComRegister = class {
1245
1253
  }
1246
1254
  liveTime = liveRoomInfo.live_time;
1247
1255
  const watched = watchedNum || "暂未获取到";
1248
- const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1256
+ const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1249
1257
  await this.sendLiveNotifyCard(LiveType.LiveBroadcast, watched, {
1250
1258
  liveRoomInfo,
1251
1259
  masterInfo,
@@ -1277,10 +1285,10 @@ var ComRegister = class {
1277
1285
  this.logger.error(`[${roomId}]直播间连接发生错误!`);
1278
1286
  },
1279
1287
  onIncomeDanmu: ({ body }) => {
1280
- currentLiveDanmakuArr.push(body.content);
1288
+ this.segmentDanmaku(body.content, danmakuWeightRecord);
1281
1289
  },
1282
1290
  onIncomeSuperChat: ({ body }) => {
1283
- currentLiveDanmakuArr.push(body.content);
1291
+ this.segmentDanmaku(body.content, danmakuWeightRecord);
1284
1292
  },
1285
1293
  onWatchedChange: ({ body }) => {
1286
1294
  watchedNum = body.text_small;
@@ -1307,7 +1315,7 @@ var ComRegister = class {
1307
1315
  }
1308
1316
  liveTime = liveRoomInfo.live_time;
1309
1317
  const follower = masterInfo.liveOpenFollowerNum >= 1e4 ? `${(masterInfo.liveOpenFollowerNum / 1e4).toFixed(1)}万` : masterInfo.liveOpenFollowerNum.toString();
1310
- const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower", follower).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1318
+ const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower", follower).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1311
1319
  await this.sendLiveNotifyCard(LiveType.StartBroadcasting, follower, {
1312
1320
  liveRoomInfo,
1313
1321
  masterInfo,
@@ -1327,7 +1335,7 @@ var ComRegister = class {
1327
1335
  if (liveFollowerChangeNum > 0) return liveFollowerChangeNum >= 1e4 ? `+${liveFollowerChangeNum.toFixed(1)}万` : `+${liveFollowerChangeNum}`;
1328
1336
  return liveFollowerChangeNum <= -1e4 ? `${liveFollowerChangeNum.toFixed(1)}万` : liveFollowerChangeNum.toString();
1329
1337
  })();
1330
- const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower_change", followerChange).replace("\\n", "\n");
1338
+ const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-follower_change", followerChange).replaceAll("\\n", "\n");
1331
1339
  await this.sendLiveNotifyCard(LiveType.StopBroadcast, followerChange, {
1332
1340
  liveRoomInfo,
1333
1341
  masterInfo,
@@ -1335,6 +1343,9 @@ var ComRegister = class {
1335
1343
  }, uid, liveEndMsg);
1336
1344
  pushAtTimeTimer();
1337
1345
  pushAtTimeTimer = null;
1346
+ const words = Object.entries(danmakuWeightRecord);
1347
+ const buffer = await this.ctx.gi.generateWordCloudImg(words, masterInfo.username);
1348
+ await this.broadcastToTargets(uid, h.image(buffer, "image/jpeg"), PushType.Live);
1338
1349
  }
1339
1350
  };
1340
1351
  await this.ctx.bl.startLiveRoomListener(roomId, handler);
@@ -1342,7 +1353,7 @@ var ComRegister = class {
1342
1353
  if (liveRoomInfo.live_status === 1) {
1343
1354
  liveTime = liveRoomInfo.live_time;
1344
1355
  const watched = watchedNum || "暂未获取到";
1345
- const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1356
+ const liveMsg = liveMsgObj?.customLive.replace("-name", masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveTime)).replace("-watched", watched).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`);
1346
1357
  if (this.config.restartPush) await this.sendLiveNotifyCard(LiveType.LiveBroadcast, watched, {
1347
1358
  liveRoomInfo,
1348
1359
  masterInfo,
@@ -1394,7 +1405,7 @@ var ComRegister = class {
1394
1405
  liveStatus.liveStartTime = liveStatus.liveRoomInfo.live_time;
1395
1406
  liveStatus.liveStartTimeInit = true;
1396
1407
  }
1397
- const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1408
+ const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1398
1409
  await this.sendLiveNotifyCard(LiveType.LiveBroadcast, "API", {
1399
1410
  liveRoomInfo: liveStatus.liveRoomInfo,
1400
1411
  masterInfo: liveStatus.masterInfo,
@@ -1427,7 +1438,7 @@ var ComRegister = class {
1427
1438
  if (liveFollowerChangeNum > 0) return liveFollowerChangeNum >= 1e4 ? `+${liveFollowerChangeNum.toFixed(1)}万` : `+${liveFollowerChangeNum}`;
1428
1439
  return liveFollowerChangeNum <= -1e4 ? `${liveFollowerChangeNum.toFixed(1)}万` : liveFollowerChangeNum.toString();
1429
1440
  })();
1430
- const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower_change", followerChange).replace("\\n", "\n");
1441
+ const liveEndMsg = liveMsgObj?.customLiveEnd.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower_change", followerChange).replaceAll("\\n", "\n");
1431
1442
  await this.sendLiveNotifyCard(LiveType.StopBroadcast, followerChange, {
1432
1443
  liveRoomInfo: liveStatus.liveRoomInfo,
1433
1444
  masterInfo: liveStatus.masterInfo,
@@ -1446,7 +1457,7 @@ var ComRegister = class {
1446
1457
  liveStatus.liveStartTime = liveStatus.liveRoomInfo.live_time;
1447
1458
  liveStatus.liveStartTimeInit = true;
1448
1459
  const follower = liveStatus.masterInfo.liveOpenFollowerNum >= 1e4 ? `${(liveStatus.masterInfo.liveOpenFollowerNum / 1e4).toFixed(1)}万` : liveStatus.masterInfo.liveOpenFollowerNum.toString();
1449
- const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower", follower).replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1460
+ const liveStartMsg = liveMsgObj?.customLiveStart.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-follower", follower).replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1450
1461
  await this.sendLiveNotifyCard(LiveType.StartBroadcasting, follower, {
1451
1462
  liveRoomInfo: liveStatus.liveRoomInfo,
1452
1463
  masterInfo: liveStatus.masterInfo,
@@ -1467,7 +1478,7 @@ var ComRegister = class {
1467
1478
  liveStatus.liveStartTime = liveStatus.liveRoomInfo.live_time;
1468
1479
  liveStatus.liveStartTimeInit = true;
1469
1480
  }
1470
- const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replace("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1481
+ const liveMsg = liveMsgObj?.customLive.replace("-name", liveStatus.masterInfo.username).replace("-time", await this.ctx.gi.getTimeDifference(liveStatus.liveStartTime)).replace("-watched", "API模式无法获取").replaceAll("\\n", "\n").replace("-link", `https://live.bilibili.com/${liveStatus.liveRoomInfo.short_id === 0 ? liveStatus.liveRoomInfo.room_id : liveStatus.liveRoomInfo.short_id}`);
1471
1482
  await this.sendLiveNotifyCard(LiveType.LiveBroadcast, "API", {
1472
1483
  liveRoomInfo: liveStatus.liveRoomInfo,
1473
1484
  masterInfo: liveStatus.masterInfo,
@@ -59792,8 +59803,6 @@ var require_parseListUnix = __commonJS$1({ "node_modules/basic-ftp/dist/parseLis
59792
59803
  exports.transformList = exports.parseLine = exports.testLine = void 0;
59793
59804
  const FileInfo_1$1 = require_FileInfo();
59794
59805
  const JA_MONTH = "月";
59795
- const JA_DAY = "日";
59796
- const JA_YEAR = "年";
59797
59806
  /**
59798
59807
  * This parser is based on the FTP client library source code in Apache Commons Net provided
59799
59808
  * under the Apache 2.0 license. It has been simplified and rewritten to better fit the Javascript language.
@@ -59824,7 +59833,7 @@ var require_parseListUnix = __commonJS$1({ "node_modules/basic-ftp/dist/parseLis
59824
59833
  * this is not allowed for here as does not appear to be shown by FTP servers
59825
59834
  * {@code @} file has extended attributes
59826
59835
  */
59827
- const RE_LINE = /* @__PURE__ */ new RegExp("([bcdelfmpSs-])(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]?)))\\+?\\s*(\\d+)\\s+(?:(\\S+(?:\\s\\S+)*?)\\s+)?(?:(\\S+(?:\\s\\S+)*)\\s+)?(\\d+(?:,\\s*\\d+)?)\\s+((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3})|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}" + JA_DAY + "))\\s+((?:\\d+(?::\\d+)?)|(?:\\d{4}" + JA_YEAR + "))\\s(.*)");
59836
+ const RE_LINE = /* @__PURE__ */ new RegExp("([bcdelfmpSs-])(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]?)))\\+?\\s*(\\d+)\\s+(?:(\\S+(?:\\s\\S+)*?)\\s+)?(?:(\\S+(?:\\s\\S+)*)\\s+)?(\\d+(?:,\\s*\\d+)?)\\s+((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3})|(?:\\d{1,2}" + JA_MONTH + "\\s+\\d{1,2}))\\s+((?:\\d+(?::\\d+)?)|(?:\\d{4}))\\s(.*)");
59828
59837
  /**
59829
59838
  * Returns true if a given line might be a Unix-style listing.
59830
59839
  *
@@ -82367,32 +82376,32 @@ var require_streamx = __commonJS$1({ "node_modules/streamx/index.js"(exports, mo
82367
82376
  const ACTIVE = READ_ACTIVE | WRITE_ACTIVE;
82368
82377
  const NOT_ACTIVE = MAX ^ ACTIVE;
82369
82378
  const DONE = READ_DONE | WRITE_DONE;
82370
- const DESTROY_STATUS = DESTROYING | DESTROYED | PREDESTROYING;
82379
+ const DESTROY_STATUS = DESTROYED | 6;
82371
82380
  const OPEN_STATUS = DESTROY_STATUS | OPENING;
82372
82381
  const AUTO_DESTROY = DESTROY_STATUS | DONE;
82373
82382
  const NON_PRIMARY = WRITE_NON_PRIMARY & READ_NON_PRIMARY;
82374
82383
  const ACTIVE_OR_TICKING = WRITE_NEXT_TICK | READ_NEXT_TICK;
82375
82384
  const TICKING = ACTIVE_OR_TICKING & NOT_ACTIVE;
82376
82385
  const IS_OPENING = OPEN_STATUS | TICKING;
82377
- const READ_PRIMARY_STATUS = OPEN_STATUS | READ_ENDING | READ_DONE;
82378
- const READ_STATUS = OPEN_STATUS | READ_DONE | READ_QUEUED;
82379
- const READ_ENDING_STATUS = OPEN_STATUS | READ_ENDING | READ_QUEUED;
82380
- const READ_READABLE_STATUS = OPEN_STATUS | READ_EMIT_READABLE | READ_QUEUED | READ_EMITTED_READABLE;
82381
- const SHOULD_NOT_READ = OPEN_STATUS | READ_ACTIVE | READ_ENDING | READ_DONE | READ_NEEDS_PUSH | READ_READ_AHEAD;
82382
- const READ_BACKPRESSURE_STATUS = DESTROY_STATUS | READ_ENDING | READ_DONE;
82383
- const READ_UPDATE_SYNC_STATUS = READ_UPDATING | OPEN_STATUS | READ_NEXT_TICK | READ_PRIMARY;
82386
+ const READ_PRIMARY_STATUS = READ_ENDING | 16399;
82387
+ const READ_STATUS = READ_DONE | 143;
82388
+ const READ_ENDING_STATUS = READ_ENDING | 143;
82389
+ const READ_READABLE_STATUS = 143 | 12288;
82390
+ const SHOULD_NOT_READ = 66575 | 147472;
82391
+ const READ_BACKPRESSURE_STATUS = READ_ENDING | 16398;
82392
+ const READ_UPDATE_SYNC_STATUS = 32800 | 79;
82384
82393
  const READ_NEXT_TICK_OR_OPENING = READ_NEXT_TICK | OPENING;
82385
- const WRITE_PRIMARY_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_DONE;
82394
+ const WRITE_PRIMARY_STATUS = WRITE_FINISHING | 8388623;
82386
82395
  const WRITE_QUEUED_AND_UNDRAINED = WRITE_QUEUED | WRITE_UNDRAINED;
82387
82396
  const WRITE_QUEUED_AND_ACTIVE = WRITE_QUEUED | WRITE_ACTIVE;
82388
- const WRITE_DRAIN_STATUS = WRITE_QUEUED | WRITE_UNDRAINED | OPEN_STATUS | WRITE_ACTIVE;
82389
- const WRITE_STATUS = OPEN_STATUS | WRITE_ACTIVE | WRITE_QUEUED | WRITE_CORKED;
82397
+ const WRITE_DRAIN_STATUS = 2097167 | 4456448;
82398
+ const WRITE_STATUS = 2097167 | 268697600;
82390
82399
  const WRITE_PRIMARY_AND_ACTIVE = WRITE_PRIMARY | WRITE_ACTIVE;
82391
82400
  const WRITE_ACTIVE_AND_WRITING = WRITE_ACTIVE | WRITE_WRITING;
82392
- const WRITE_FINISHING_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_QUEUED_AND_ACTIVE | WRITE_DONE;
82393
- const WRITE_BACKPRESSURE_STATUS = WRITE_UNDRAINED | DESTROY_STATUS | WRITE_FINISHING | WRITE_DONE;
82394
- const WRITE_UPDATE_SYNC_STATUS = WRITE_UPDATING | OPEN_STATUS | WRITE_NEXT_TICK | WRITE_PRIMARY;
82395
- const WRITE_DROP_DATA = WRITE_FINISHING | WRITE_DONE | DESTROY_STATUS;
82401
+ const WRITE_FINISHING_STATUS = 2359311 | 142606336;
82402
+ const WRITE_BACKPRESSURE_STATUS = 138412032 | 8388622;
82403
+ const WRITE_UPDATE_SYNC_STATUS = 34078720 | 1048591;
82404
+ const WRITE_DROP_DATA = WRITE_DONE | 134217742;
82396
82405
  const asyncIterator = Symbol.asyncIterator || Symbol("asyncIterator");
82397
82406
  var WritableState = class {
82398
82407
  constructor(stream$3, { highWaterMark = 16384, map = null, mapWritable: mapWritable$1, byteLength: byteLength$1, byteLengthWritable } = {}) {
@@ -82837,7 +82846,7 @@ var require_streamx = __commonJS$1({ "node_modules/streamx/index.js"(exports, mo
82837
82846
  var Readable$2 = class Readable$2 extends Stream$1 {
82838
82847
  constructor(opts) {
82839
82848
  super(opts);
82840
- this._duplexState |= OPENING | WRITE_DONE | READ_READ_AHEAD;
82849
+ this._duplexState |= WRITE_DONE | 131073;
82841
82850
  this._readableState = new ReadableState(this, opts);
82842
82851
  if (opts) {
82843
82852
  if (this._readableState.readAhead === false) this._duplexState &= READ_NO_READ_AHEAD;
@@ -94316,6 +94325,118 @@ var GenerateImg = class extends Service {
94316
94325
  throw new Error(`生成图片失败!错误: ${e$1.toString()}`);
94317
94326
  });
94318
94327
  }
94328
+ async generateWordCloudImg(words, masterName) {
94329
+ const html = `
94330
+ <!DOCTYPE html>
94331
+ <html lang="zh-CN">
94332
+
94333
+ <head>
94334
+ <meta charset="UTF-8">
94335
+ <title>高清词云展示</title>
94336
+ <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap" rel="stylesheet">
94337
+ <style>
94338
+ * {
94339
+ margin: 0;
94340
+ padding: 0;
94341
+ box-sizing: border-box;
94342
+ }
94343
+
94344
+ html {
94345
+ width: 720px;
94346
+ height: 520px;
94347
+ }
94348
+
94349
+ .wordcloud-bg {
94350
+ width: 720px;
94351
+ height: 520px;
94352
+ background: linear-gradient(to right, #e0eafc, #cfdef3);
94353
+ font-family: 'Quicksand', sans-serif;
94354
+ display: flex;
94355
+ justify-content: center;
94356
+ align-items: center;
94357
+ }
94358
+
94359
+ .wordcloud-card {
94360
+ width: 700px;
94361
+ height: 500px;
94362
+ backdrop-filter: blur(10px);
94363
+ background: rgba(255, 255, 255, 0.25);
94364
+ border-radius: 20px;
94365
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
94366
+ padding: 20px;
94367
+ display: flex;
94368
+ flex-direction: column;
94369
+ align-items: center;
94370
+ justify-content: center;
94371
+ }
94372
+
94373
+ h2 {
94374
+ margin: 0 0 10px;
94375
+ color: #333;
94376
+ font-size: 24px;
94377
+ }
94378
+
94379
+ canvas {
94380
+ width: 100%;
94381
+ height: 100%;
94382
+ display: block;
94383
+ }
94384
+ </style>
94385
+ </head>
94386
+
94387
+ <body>
94388
+ <div class="wordcloud-bg">
94389
+ <div class="wordcloud-card">
94390
+ <h2>${masterName}直播弹幕词云</h2>
94391
+ <canvas id="wordCloudCanvas"></canvas>
94392
+ </div>
94393
+ </div>
94394
+
94395
+ <script src="https://cdn.jsdelivr.net/npm/wordcloud@1.1.2/src/wordcloud2.min.js"></script>
94396
+ <script>
94397
+ const canvas = document.getElementById('wordCloudCanvas');
94398
+ const ctx = canvas.getContext('2d');
94399
+
94400
+ // 获取 CSS 大小
94401
+ const style = getComputedStyle(canvas);
94402
+ const cssWidth = parseInt(style.width);
94403
+ const cssHeight = parseInt(style.height);
94404
+ const ratio = window.devicePixelRatio || 1;
94405
+
94406
+ // 设置 canvas 分辨率 & 缩放
94407
+ canvas.width = cssWidth * ratio;
94408
+ canvas.height = cssHeight * ratio;
94409
+ ctx.scale(ratio, ratio);
94410
+
94411
+ const words = ${JSON.stringify(words)}
94412
+
94413
+ WordCloud(canvas, {
94414
+ list: words,
94415
+ gridSize: Math.round(8 * (cssWidth / 1024)), // 自动适配大小
94416
+ weightFactor: size => size * (cssWidth / 1024) * 1.2,
94417
+ fontFamily: 'Quicksand, sans-serif',
94418
+ color: () => {
94419
+ const colors = ['#007CF0', '#00DFD8', '#7928CA', '#FF0080', '#FF4D4D', '#F9CB28'];
94420
+ return colors[Math.floor(Math.random() * colors.length)];
94421
+ },
94422
+ rotateRatio: 0.5,
94423
+ rotationSteps: 2,
94424
+ backgroundColor: 'transparent',
94425
+ drawOutOfBound: false,
94426
+ origin: [cssWidth / 2, cssHeight / 2], // 居中关键点
94427
+ // 明确告诉 wordcloud2 使用这个宽高(以 CSS 尺寸为准)
94428
+ width: cssWidth,
94429
+ height: cssHeight,
94430
+ });
94431
+ </script>
94432
+ </body>
94433
+
94434
+ </html>
94435
+ `;
94436
+ return await withRetry(() => this.imgHandler(html)).catch((e$1) => {
94437
+ throw new Error(`生成图片失败!错误: ${e$1.toString()}`);
94438
+ });
94439
+ }
94319
94440
  async getLiveStatus(time, liveStatus) {
94320
94441
  let titleStatus;
94321
94442
  let liveTime;
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.8-alpha.1",
4
+ "version": "3.2.9-alpha.0",
5
5
  "contributors": [
6
6
  "Akokko <admin@akokko.com>"
7
7
  ],
@@ -45,6 +45,7 @@
45
45
  "luxon": "^3.6.1",
46
46
  "md5": "^2.3.0",
47
47
  "qrcode": "^1.5.4",
48
+ "segmentit": "^2.0.3",
48
49
  "tough-cookie": "^5.1.2"
49
50
  },
50
51
  "devDependencies": {
package/readme.md CHANGED
@@ -297,6 +297,11 @@ uid为必填参数,为要推送的UP主的UID,index为可选参数,为要
297
297
  > - ver 3.2.7-alpha.1 修复:加载 `bili_ticket` 失败会导致插件加载失败;
298
298
  > - ver 3.2.8-alpha.0 修复:插件重启报错 `RangeError: Invalid time value` ; 新增:选项 `sub.liveMsg` ,可自定义每个UP个性化的直播推送语;
299
299
  > - ver 3.2.8-alpha.1 修复:直播推送没有推送语;
300
+ > - ver 3.2.8-alpha.2 优化:直播推送语中,会换行所有换行符而不是第一个,其余参数仍只会替换第一个
301
+
302
+ > [!CAUTION]
303
+ > - ver 3.2.9-alpha.0 新增:弹幕词云; 不建议更新,目前仅做测试用!
304
+
300
305
 
301
306
  ## 交流群
302
307