koishi-plugin-chat-analyse 1.0.8 → 1.1.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
@@ -184,11 +184,19 @@ var Collector = class _Collector {
184
184
  * @param elements - 消息元素数组。
185
185
  * @returns 净化后的纯文本。
186
186
  */
187
- sanitizeContent = /* @__PURE__ */ __name((elements) => import_koishi.h.transform(elements, {
188
- text: /* @__PURE__ */ __name((attrs) => attrs.content, "text"),
189
- img: /* @__PURE__ */ __name((attrs) => attrs.summary === "[动画表情]" ? "[gif]" : "[img]", "img"),
190
- at: /* @__PURE__ */ __name((attrs) => `[at:${attrs.id}]`, "at")
191
- }, "").join(""), "sanitizeContent");
187
+ sanitizeContent = /* @__PURE__ */ __name((elements) => elements.map((e) => {
188
+ if (!e || !e.attrs) return "";
189
+ switch (e.type) {
190
+ case "text":
191
+ return e.attrs.content || "";
192
+ case "img":
193
+ return e.attrs.summary === "[动画表情]" ? "[gif]" : "[img]";
194
+ case "at":
195
+ return `[at:${e.attrs.id}]`;
196
+ default:
197
+ return `[${e.type}]`;
198
+ }
199
+ }).join(""), "sanitizeContent");
192
200
  /**
193
201
  * @private @method flushBuffers
194
202
  * @description 将所有内存中的数据缓冲区批量写入数据库,并清空缓冲区。
@@ -223,6 +231,1249 @@ var import_koishi3 = require("koishi");
223
231
 
224
232
  // src/Renderer.ts
225
233
  var import_koishi2 = require("koishi");
234
+
235
+ // src/wordcloud.ts
236
+ var wordCloudScript = `
237
+ /*!
238
+ * wordcloud2.js
239
+ * http://timdream.org/wordcloud2.js/
240
+ *
241
+ * Copyright 2011 - 2019 Tim Guan-tin Chien and contributors.
242
+ * Released under the MIT license
243
+ */
244
+
245
+ 'use strict'
246
+
247
+ // setImmediate
248
+ if (!window.setImmediate) {
249
+ window.setImmediate = (function setupSetImmediate () {
250
+ return window.msSetImmediate ||
251
+ window.webkitSetImmediate ||
252
+ window.mozSetImmediate ||
253
+ window.oSetImmediate ||
254
+ (function setupSetZeroTimeout () {
255
+ if (!window.postMessage || !window.addEventListener) {
256
+ return null
257
+ }
258
+
259
+ var callbacks = [undefined]
260
+ var message = 'zero-timeout-message'
261
+
262
+ // Like setTimeout, but only takes a function argument. There's
263
+ // no time argument (always zero) and no arguments (you have to
264
+ // use a closure).
265
+ var setZeroTimeout = function setZeroTimeout (callback) {
266
+ var id = callbacks.length
267
+ callbacks.push(callback)
268
+ window.postMessage(message + id.toString(36), '*')
269
+
270
+ return id
271
+ }
272
+
273
+ window.addEventListener('message', function setZeroTimeoutMessage (evt) {
274
+ // Skipping checking event source, retarded IE confused this window
275
+ // object with another in the presence of iframe
276
+ if (typeof evt.data !== 'string' ||
277
+ evt.data.substr(0, message.length) !== message/* ||
278
+ evt.source !== window */) {
279
+ return
280
+ }
281
+
282
+ evt.stopImmediatePropagation()
283
+
284
+ var id = parseInt(evt.data.substr(message.length), 36)
285
+ if (!callbacks[id]) {
286
+ return
287
+ }
288
+
289
+ callbacks[id]()
290
+ callbacks[id] = undefined
291
+ }, true)
292
+
293
+ /* specify clearImmediate() here since we need the scope */
294
+ window.clearImmediate = function clearZeroTimeout (id) {
295
+ if (!callbacks[id]) {
296
+ return
297
+ }
298
+
299
+ callbacks[id] = undefined
300
+ }
301
+
302
+ return setZeroTimeout
303
+ })() ||
304
+ // fallback
305
+ function setImmediateFallback (fn) {
306
+ window.setTimeout(fn, 0)
307
+ }
308
+ })()
309
+ }
310
+
311
+ if (!window.clearImmediate) {
312
+ window.clearImmediate = (function setupClearImmediate () {
313
+ return window.msClearImmediate ||
314
+ window.webkitClearImmediate ||
315
+ window.mozClearImmediate ||
316
+ window.oClearImmediate ||
317
+ // "clearZeroTimeout" is implement on the previous block ||
318
+ // fallback
319
+ function clearImmediateFallback (timer) {
320
+ window.clearTimeout(timer)
321
+ }
322
+ })()
323
+ }
324
+
325
+ (function (global) {
326
+ // Check if WordCloud can run on this browser
327
+ var isSupported = (function isSupported () {
328
+ var canvas = document.createElement('canvas')
329
+ if (!canvas || !canvas.getContext) {
330
+ return false
331
+ }
332
+
333
+ var ctx = canvas.getContext('2d')
334
+ if (!ctx) {
335
+ return false
336
+ }
337
+ if (!ctx.getImageData) {
338
+ return false
339
+ }
340
+ if (!ctx.fillText) {
341
+ return false
342
+ }
343
+
344
+ if (!Array.prototype.some) {
345
+ return false
346
+ }
347
+ if (!Array.prototype.push) {
348
+ return false
349
+ }
350
+
351
+ return true
352
+ }())
353
+
354
+ // Find out if the browser impose minium font size by
355
+ // drawing small texts on a canvas and measure it's width.
356
+ var minFontSize = (function getMinFontSize () {
357
+ if (!isSupported) {
358
+ return
359
+ }
360
+
361
+ var ctx = document.createElement('canvas').getContext('2d')
362
+
363
+ // start from 20
364
+ var size = 20
365
+
366
+ // two sizes to measure
367
+ var hanWidth, mWidth
368
+
369
+ while (size) {
370
+ ctx.font = size.toString(10) + 'px sans-serif'
371
+ if ((ctx.measureText('W').width === hanWidth) &&
372
+ (ctx.measureText('m').width) === mWidth) {
373
+ return (size + 1)
374
+ }
375
+
376
+ hanWidth = ctx.measureText('W').width
377
+ mWidth = ctx.measureText('m').width
378
+
379
+ size--
380
+ }
381
+
382
+ return 0
383
+ })()
384
+
385
+ var getItemExtraData = function (item) {
386
+ if (Array.isArray(item)) {
387
+ var itemCopy = item.slice()
388
+ // remove data we already have (word and weight)
389
+ itemCopy.splice(0, 2)
390
+ return itemCopy
391
+ } else {
392
+ return []
393
+ }
394
+ }
395
+
396
+ // Based on http://jsfromhell.com/array/shuffle
397
+ var shuffleArray = function shuffleArray (arr) {
398
+ for (var j, x, i = arr.length; i;) {
399
+ j = Math.floor(Math.random() * i)
400
+ x = arr[--i]
401
+ arr[i] = arr[j]
402
+ arr[j] = x
403
+ }
404
+ return arr
405
+ }
406
+
407
+ var timer = {};
408
+ var WordCloud = function WordCloud (elements, options) {
409
+ if (!isSupported) {
410
+ return
411
+ }
412
+
413
+ var timerId = Math.floor(Math.random() * Date.now())
414
+
415
+ if (!Array.isArray(elements)) {
416
+ elements = [elements]
417
+ }
418
+
419
+ elements.forEach(function (el, i) {
420
+ if (typeof el === 'string') {
421
+ elements[i] = document.getElementById(el)
422
+ if (!elements[i]) {
423
+ throw new Error('The element id specified is not found.')
424
+ }
425
+ } else if (!el.tagName && !el.appendChild) {
426
+ throw new Error('You must pass valid HTML elements, or ID of the element.')
427
+ }
428
+ })
429
+
430
+ /* Default values to be overwritten by options object */
431
+ var settings = {
432
+ list: [],
433
+ fontFamily: '"Trebuchet MS", "Heiti TC", "微軟正黑體", ' +
434
+ '"Arial Unicode MS", "Droid Fallback Sans", sans-serif',
435
+ fontWeight: 'normal',
436
+ color: 'random-dark',
437
+ minSize: 0, // 0 to disable
438
+ weightFactor: 1,
439
+ clearCanvas: true,
440
+ backgroundColor: '#fff', // opaque white = rgba(255, 255, 255, 1)
441
+
442
+ gridSize: 8,
443
+ drawOutOfBound: false,
444
+ shrinkToFit: false,
445
+ origin: null,
446
+
447
+ drawMask: false,
448
+ maskColor: 'rgba(255,0,0,0.3)',
449
+ maskGapWidth: 0.3,
450
+
451
+ wait: 0,
452
+ abortThreshold: 0, // disabled
453
+ abort: function noop () {},
454
+
455
+ minRotation: -Math.PI / 2,
456
+ maxRotation: Math.PI / 2,
457
+ rotationSteps: 0,
458
+
459
+ shuffle: true,
460
+ rotateRatio: 0.1,
461
+
462
+ shape: 'circle',
463
+ ellipticity: 0.65,
464
+
465
+ classes: null,
466
+
467
+ hover: null,
468
+ click: null
469
+ }
470
+
471
+ if (options) {
472
+ for (var key in options) {
473
+ if (key in settings) {
474
+ settings[key] = options[key]
475
+ }
476
+ }
477
+ }
478
+
479
+ /* Convert weightFactor into a function */
480
+ if (typeof settings.weightFactor !== 'function') {
481
+ var factor = settings.weightFactor
482
+ settings.weightFactor = function weightFactor (pt) {
483
+ return pt * factor // in px
484
+ }
485
+ }
486
+
487
+ /* Convert shape into a function */
488
+ if (typeof settings.shape !== 'function') {
489
+ switch (settings.shape) {
490
+ case 'circle':
491
+ /* falls through */
492
+ default:
493
+ // 'circle' is the default and a shortcut in the code loop.
494
+ settings.shape = 'circle'
495
+ break
496
+
497
+ case 'cardioid':
498
+ settings.shape = function shapeCardioid (theta) {
499
+ return 1 - Math.sin(theta)
500
+ }
501
+ break
502
+
503
+ /*
504
+ To work out an X-gon, one has to calculate "m",
505
+ where 1/(cos(2*PI/X)+m*sin(2*PI/X)) = 1/(cos(0)+m*sin(0))
506
+ http://www.wolframalpha.com/input/?i=1%2F%28cos%282*PI%2FX%29%2Bm*sin%28
507
+ 2*PI%2FX%29%29+%3D+1%2F%28cos%280%29%2Bm*sin%280%29%29
508
+ Copy the solution into polar equation r = 1/(cos(t') + m*sin(t'))
509
+ where t' equals to mod(t, 2PI/X)
510
+ */
511
+
512
+ case 'diamond':
513
+ // http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+
514
+ // %28t%2C+PI%2F2%29%29%2Bsin%28mod+%28t%2C+PI%2F2%29%29%29%2C+t+%3D
515
+ // +0+..+2*PI
516
+ settings.shape = function shapeSquare (theta) {
517
+ var thetaPrime = theta % (2 * Math.PI / 4)
518
+ return 1 / (Math.cos(thetaPrime) + Math.sin(thetaPrime))
519
+ }
520
+ break
521
+
522
+ case 'square':
523
+ // http://www.wolframalpha.com/input/?i=plot+r+%3D+min(1%2Fabs(cos(t
524
+ // )),1%2Fabs(sin(t)))),+t+%3D+0+..+2*PI
525
+ settings.shape = function shapeSquare (theta) {
526
+ return Math.min(
527
+ 1 / Math.abs(Math.cos(theta)),
528
+ 1 / Math.abs(Math.sin(theta))
529
+ )
530
+ }
531
+ break
532
+
533
+ case 'triangle-forward':
534
+ // http://www.wolframalpha.com/input/?i=plot+r+%3D+1%2F%28cos%28mod+
535
+ // %28t%2C+2*PI%2F3%29%29%2Bsqrt%283%29sin%28mod+%28t%2C+2*PI%2F3%29
536
+ // %29%29%2C+t+%3D+0+..+2*PI
537
+ settings.shape = function shapeTriangle (theta) {
538
+ var thetaPrime = theta % (2 * Math.PI / 3)
539
+ return 1 / (Math.cos(thetaPrime) +
540
+ Math.sqrt(3) * Math.sin(thetaPrime))
541
+ }
542
+ break
543
+
544
+ case 'triangle':
545
+ case 'triangle-upright':
546
+ settings.shape = function shapeTriangle (theta) {
547
+ var thetaPrime = (theta + Math.PI * 3 / 2) % (2 * Math.PI / 3)
548
+ return 1 / (Math.cos(thetaPrime) +
549
+ Math.sqrt(3) * Math.sin(thetaPrime))
550
+ }
551
+ break
552
+
553
+ case 'pentagon':
554
+ settings.shape = function shapePentagon (theta) {
555
+ var thetaPrime = (theta + 0.955) % (2 * Math.PI / 5)
556
+ return 1 / (Math.cos(thetaPrime) +
557
+ 0.726543 * Math.sin(thetaPrime))
558
+ }
559
+ break
560
+
561
+ case 'star':
562
+ settings.shape = function shapeStar (theta) {
563
+ var thetaPrime = (theta + 0.955) % (2 * Math.PI / 10)
564
+ if ((theta + 0.955) % (2 * Math.PI / 5) - (2 * Math.PI / 10) >= 0) {
565
+ return 1 / (Math.cos((2 * Math.PI / 10) - thetaPrime) +
566
+ 3.07768 * Math.sin((2 * Math.PI / 10) - thetaPrime))
567
+ } else {
568
+ return 1 / (Math.cos(thetaPrime) +
569
+ 3.07768 * Math.sin(thetaPrime))
570
+ }
571
+ }
572
+ break
573
+ }
574
+ }
575
+
576
+ /* Make sure gridSize is a whole number and is not smaller than 4px */
577
+ settings.gridSize = Math.max(Math.floor(settings.gridSize), 4)
578
+
579
+ /* shorthand */
580
+ var g = settings.gridSize
581
+ var maskRectWidth = g - settings.maskGapWidth
582
+
583
+ /* normalize rotation settings */
584
+ var rotationRange = Math.abs(settings.maxRotation - settings.minRotation)
585
+ var rotationSteps = Math.abs(Math.floor(settings.rotationSteps))
586
+ var minRotation = Math.min(settings.maxRotation, settings.minRotation)
587
+
588
+ /* information/object available to all functions, set when start() */
589
+ var grid, // 2d array containing filling information
590
+ ngx, ngy, // width and height of the grid
591
+ center, // position of the center of the cloud
592
+ maxRadius
593
+
594
+ /* timestamp for measuring each putWord() action */
595
+ var escapeTime
596
+
597
+ /* function for getting the color of the text */
598
+ var getTextColor
599
+ function randomHslColor (min, max) {
600
+ return 'hsl(' +
601
+ (Math.random() * 360).toFixed() + ',' +
602
+ (Math.random() * 30 + 70).toFixed() + '%,' +
603
+ (Math.random() * (max - min) + min).toFixed() + '%)'
604
+ }
605
+ switch (settings.color) {
606
+ case 'random-dark':
607
+ getTextColor = function getRandomDarkColor () {
608
+ return randomHslColor(10, 50)
609
+ }
610
+ break
611
+
612
+ case 'random-light':
613
+ getTextColor = function getRandomLightColor () {
614
+ return randomHslColor(50, 90)
615
+ }
616
+ break
617
+
618
+ default:
619
+ if (typeof settings.color === 'function') {
620
+ getTextColor = settings.color
621
+ }
622
+ break
623
+ }
624
+
625
+ /* function for getting the font-weight of the text */
626
+ var getTextFontWeight
627
+ if (typeof settings.fontWeight === 'function') {
628
+ getTextFontWeight = settings.fontWeight
629
+ }
630
+
631
+ /* function for getting the classes of the text */
632
+ var getTextClasses = null
633
+ if (typeof settings.classes === 'function') {
634
+ getTextClasses = settings.classes
635
+ }
636
+
637
+ /* Interactive */
638
+ var interactive = false
639
+ var infoGrid = []
640
+ var hovered
641
+
642
+ var getInfoGridFromMouseTouchEvent =
643
+ function getInfoGridFromMouseTouchEvent (evt) {
644
+ var canvas = evt.currentTarget
645
+ var rect = canvas.getBoundingClientRect()
646
+ var clientX
647
+ var clientY
648
+ /** Detect if touches are available */
649
+ if (evt.touches) {
650
+ clientX = evt.touches[0].clientX
651
+ clientY = evt.touches[0].clientY
652
+ } else {
653
+ clientX = evt.clientX
654
+ clientY = evt.clientY
655
+ }
656
+ var eventX = clientX - rect.left
657
+ var eventY = clientY - rect.top
658
+
659
+ var x = Math.floor(eventX * ((canvas.width / rect.width) || 1) / g)
660
+ var y = Math.floor(eventY * ((canvas.height / rect.height) || 1) / g)
661
+
662
+ return infoGrid[x][y]
663
+ }
664
+
665
+ var wordcloudhover = function wordcloudhover (evt) {
666
+ var info = getInfoGridFromMouseTouchEvent(evt)
667
+
668
+ if (hovered === info) {
669
+ return
670
+ }
671
+
672
+ hovered = info
673
+ if (!info) {
674
+ settings.hover(undefined, undefined, evt)
675
+
676
+ return
677
+ }
678
+
679
+ settings.hover(info.item, info.dimension, evt)
680
+ }
681
+
682
+ var wordcloudclick = function wordcloudclick (evt) {
683
+ var info = getInfoGridFromMouseTouchEvent(evt)
684
+ if (!info) {
685
+ return
686
+ }
687
+
688
+ settings.click(info.item, info.dimension, evt)
689
+ evt.preventDefault()
690
+ }
691
+
692
+ /* Get points on the grid for a given radius away from the center */
693
+ var pointsAtRadius = []
694
+ var getPointsAtRadius = function getPointsAtRadius (radius) {
695
+ if (pointsAtRadius[radius]) {
696
+ return pointsAtRadius[radius]
697
+ }
698
+
699
+ // Look for these number of points on each radius
700
+ var T = radius * 8
701
+
702
+ // Getting all the points at this radius
703
+ var t = T
704
+ var points = []
705
+
706
+ if (radius === 0) {
707
+ points.push([center[0], center[1], 0])
708
+ }
709
+
710
+ while (t--) {
711
+ // distort the radius to put the cloud in shape
712
+ var rx = 1
713
+ if (settings.shape !== 'circle') {
714
+ rx = settings.shape(t / T * 2 * Math.PI) // 0 to 1
715
+ }
716
+
717
+ // Push [x, y, t] t is used solely for getTextColor()
718
+ points.push([
719
+ center[0] + radius * rx * Math.cos(-t / T * 2 * Math.PI),
720
+ center[1] + radius * rx * Math.sin(-t / T * 2 * Math.PI) *
721
+ settings.ellipticity,
722
+ t / T * 2 * Math.PI])
723
+ }
724
+
725
+ pointsAtRadius[radius] = points
726
+ return points
727
+ }
728
+
729
+ /* Return true if we had spent too much time */
730
+ var exceedTime = function exceedTime () {
731
+ return ((settings.abortThreshold > 0) &&
732
+ ((new Date()).getTime() - escapeTime > settings.abortThreshold))
733
+ }
734
+
735
+ /* Get the deg of rotation according to settings, and luck. */
736
+ var getRotateDeg = function getRotateDeg () {
737
+ if (settings.rotateRatio === 0) {
738
+ return 0
739
+ }
740
+
741
+ if (Math.random() > settings.rotateRatio) {
742
+ return 0
743
+ }
744
+
745
+ if (rotationRange === 0) {
746
+ return minRotation
747
+ }
748
+
749
+ if (rotationSteps > 0) {
750
+ // Min rotation + zero or more steps * span of one step
751
+ return minRotation +
752
+ Math.floor(Math.random() * rotationSteps) *
753
+ rotationRange / (rotationSteps - 1)
754
+ } else {
755
+ return minRotation + Math.random() * rotationRange
756
+ }
757
+ }
758
+
759
+ var getTextInfo = function getTextInfo (word, weight, rotateDeg, extraDataArray) {
760
+ // calculate the acutal font size
761
+ // fontSize === 0 means weightFactor function wants the text skipped,
762
+ // and size < minSize means we cannot draw the text.
763
+ var debug = false
764
+ var fontSize = settings.weightFactor(weight)
765
+ if (fontSize <= settings.minSize) {
766
+ return false
767
+ }
768
+
769
+ // Scale factor here is to make sure fillText is not limited by
770
+ // the minium font size set by browser.
771
+ // It will always be 1 or 2n.
772
+ var mu = 1
773
+ if (fontSize < minFontSize) {
774
+ mu = (function calculateScaleFactor () {
775
+ var mu = 2
776
+ while (mu * fontSize < minFontSize) {
777
+ mu += 2
778
+ }
779
+ return mu
780
+ })()
781
+ }
782
+
783
+ // Get fontWeight that will be used to set fctx.font
784
+ var fontWeight
785
+ if (getTextFontWeight) {
786
+ fontWeight = getTextFontWeight(word, weight, fontSize, extraDataArray)
787
+ } else {
788
+ fontWeight = settings.fontWeight
789
+ }
790
+
791
+ var fcanvas = document.createElement('canvas')
792
+ var fctx = fcanvas.getContext('2d', { willReadFrequently: true })
793
+
794
+ fctx.font = fontWeight + ' ' +
795
+ (fontSize * mu).toString(10) + 'px ' + settings.fontFamily
796
+
797
+ // Estimate the dimension of the text with measureText().
798
+ var fw = fctx.measureText(word).width / mu
799
+ var fh = Math.max(fontSize * mu,
800
+ fctx.measureText('m').width,
801
+ fctx.measureText('W').width
802
+ ) / mu
803
+
804
+ // Create a boundary box that is larger than our estimates,
805
+ // so text don't get cut of (it sill might)
806
+ var boxWidth = fw + fh * 2
807
+ var boxHeight = fh * 3
808
+ var fgw = Math.ceil(boxWidth / g)
809
+ var fgh = Math.ceil(boxHeight / g)
810
+ boxWidth = fgw * g
811
+ boxHeight = fgh * g
812
+
813
+ // Calculate the proper offsets to make the text centered at
814
+ // the preferred position.
815
+
816
+ // This is simply half of the width.
817
+ var fillTextOffsetX = -fw / 2
818
+ // Instead of moving the box to the exact middle of the preferred
819
+ // position, for Y-offset we move 0.4 instead, so Latin alphabets look
820
+ // vertical centered.
821
+ var fillTextOffsetY = -fh * 0.4
822
+
823
+ // Calculate the actual dimension of the canvas, considering the rotation.
824
+ var cgh = Math.ceil((boxWidth * Math.abs(Math.sin(rotateDeg)) +
825
+ boxHeight * Math.abs(Math.cos(rotateDeg))) / g)
826
+ var cgw = Math.ceil((boxWidth * Math.abs(Math.cos(rotateDeg)) +
827
+ boxHeight * Math.abs(Math.sin(rotateDeg))) / g)
828
+ var width = cgw * g
829
+ var height = cgh * g
830
+
831
+ fcanvas.setAttribute('width', width)
832
+ fcanvas.setAttribute('height', height)
833
+
834
+ if (debug) {
835
+ // Attach fcanvas to the DOM
836
+ document.body.appendChild(fcanvas)
837
+ // Save it's state so that we could restore and draw the grid correctly.
838
+ fctx.save()
839
+ }
840
+
841
+ // Scale the canvas with |mu|.
842
+ fctx.scale(1 / mu, 1 / mu)
843
+ fctx.translate(width * mu / 2, height * mu / 2)
844
+ fctx.rotate(-rotateDeg)
845
+
846
+ // Once the width/height is set, ctx info will be reset.
847
+ // Set it again here.
848
+ fctx.font = fontWeight + ' ' +
849
+ (fontSize * mu).toString(10) + 'px ' + settings.fontFamily
850
+
851
+ // Fill the text into the fcanvas.
852
+ // XXX: We cannot because textBaseline = 'top' here because
853
+ // Firefox and Chrome uses different default line-height for canvas.
854
+ // Please read https://bugzil.la/737852#c6.
855
+ // Here, we use textBaseline = 'middle' and draw the text at exactly
856
+ // 0.5 * fontSize lower.
857
+ fctx.fillStyle = '#000'
858
+ fctx.textBaseline = 'middle'
859
+ fctx.fillText(
860
+ word, fillTextOffsetX * mu,
861
+ (fillTextOffsetY + fontSize * 0.5) * mu
862
+ )
863
+
864
+ // Get the pixels of the text
865
+ var imageData = fctx.getImageData(0, 0, width, height).data
866
+
867
+ if (exceedTime()) {
868
+ return false
869
+ }
870
+
871
+ if (debug) {
872
+ // Draw the box of the original estimation
873
+ fctx.strokeRect(
874
+ fillTextOffsetX * mu,
875
+ fillTextOffsetY, fw * mu, fh * mu
876
+ )
877
+ fctx.restore()
878
+ }
879
+
880
+ // Read the pixels and save the information to the occupied array
881
+ var occupied = []
882
+ var gx = cgw
883
+ var gy, x, y
884
+ var bounds = [cgh / 2, cgw / 2, cgh / 2, cgw / 2]
885
+ while (gx--) {
886
+ gy = cgh
887
+ while (gy--) {
888
+ y = g
889
+ /* eslint no-labels: ["error", { "allowLoop": true }] */
890
+ singleGridLoop: while (y--) {
891
+ x = g
892
+ while (x--) {
893
+ if (imageData[((gy * g + y) * width +
894
+ (gx * g + x)) * 4 + 3]) {
895
+ occupied.push([gx, gy])
896
+
897
+ if (gx < bounds[3]) {
898
+ bounds[3] = gx
899
+ }
900
+ if (gx > bounds[1]) {
901
+ bounds[1] = gx
902
+ }
903
+ if (gy < bounds[0]) {
904
+ bounds[0] = gy
905
+ }
906
+ if (gy > bounds[2]) {
907
+ bounds[2] = gy
908
+ }
909
+
910
+ if (debug) {
911
+ fctx.fillStyle = 'rgba(255, 0, 0, 0.5)'
912
+ fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5)
913
+ }
914
+ break singleGridLoop
915
+ }
916
+ }
917
+ }
918
+ if (debug) {
919
+ fctx.fillStyle = 'rgba(0, 0, 255, 0.5)'
920
+ fctx.fillRect(gx * g, gy * g, g - 0.5, g - 0.5)
921
+ }
922
+ }
923
+ }
924
+
925
+ if (debug) {
926
+ fctx.fillStyle = 'rgba(0, 255, 0, 0.5)'
927
+ fctx.fillRect(
928
+ bounds[3] * g,
929
+ bounds[0] * g,
930
+ (bounds[1] - bounds[3] + 1) * g,
931
+ (bounds[2] - bounds[0] + 1) * g
932
+ )
933
+ }
934
+
935
+ // Return information needed to create the text on the real canvas
936
+ return {
937
+ mu: mu,
938
+ occupied: occupied,
939
+ bounds: bounds,
940
+ gw: cgw,
941
+ gh: cgh,
942
+ fillTextOffsetX: fillTextOffsetX,
943
+ fillTextOffsetY: fillTextOffsetY,
944
+ fillTextWidth: fw,
945
+ fillTextHeight: fh,
946
+ fontSize: fontSize
947
+ }
948
+ }
949
+
950
+ /* Determine if there is room available in the given dimension */
951
+ var canFitText = function canFitText (gx, gy, gw, gh, occupied) {
952
+ // Go through the occupied points,
953
+ // return false if the space is not available.
954
+ var i = occupied.length
955
+ while (i--) {
956
+ var px = gx + occupied[i][0]
957
+ var py = gy + occupied[i][1]
958
+
959
+ if (px >= ngx || py >= ngy || px < 0 || py < 0) {
960
+ if (!settings.drawOutOfBound) {
961
+ return false
962
+ }
963
+ continue
964
+ }
965
+
966
+ if (!grid[px][py]) {
967
+ return false
968
+ }
969
+ }
970
+ return true
971
+ }
972
+
973
+ /* Actually draw the text on the grid */
974
+ var drawText = function drawText (gx, gy, info, word, weight, distance, theta, rotateDeg, attributes, extraDataArray) {
975
+ var fontSize = info.fontSize
976
+ var color
977
+ if (getTextColor) {
978
+ color = getTextColor(word, weight, fontSize, distance, theta, extraDataArray)
979
+ } else {
980
+ color = settings.color
981
+ }
982
+
983
+ // get fontWeight that will be used to set ctx.font and font style rule
984
+ var fontWeight
985
+ if (getTextFontWeight) {
986
+ fontWeight = getTextFontWeight(word, weight, fontSize, extraDataArray)
987
+ } else {
988
+ fontWeight = settings.fontWeight
989
+ }
990
+
991
+ var classes
992
+ if (getTextClasses) {
993
+ classes = getTextClasses(word, weight, fontSize, extraDataArray)
994
+ } else {
995
+ classes = settings.classes
996
+ }
997
+
998
+ elements.forEach(function (el) {
999
+ if (el.getContext) {
1000
+ var ctx = el.getContext('2d')
1001
+ var mu = info.mu
1002
+
1003
+ // Save the current state before messing it
1004
+ ctx.save()
1005
+ ctx.scale(1 / mu, 1 / mu)
1006
+
1007
+ ctx.font = fontWeight + ' ' +
1008
+ (fontSize * mu).toString(10) + 'px ' + settings.fontFamily
1009
+ ctx.fillStyle = color
1010
+
1011
+ // Translate the canvas position to the origin coordinate of where
1012
+ // the text should be put.
1013
+ ctx.translate(
1014
+ (gx + info.gw / 2) * g * mu,
1015
+ (gy + info.gh / 2) * g * mu
1016
+ )
1017
+
1018
+ if (rotateDeg !== 0) {
1019
+ ctx.rotate(-rotateDeg)
1020
+ }
1021
+
1022
+ // Finally, fill the text.
1023
+
1024
+ // XXX: We cannot because textBaseline = 'top' here because
1025
+ // Firefox and Chrome uses different default line-height for canvas.
1026
+ // Please read https://bugzil.la/737852#c6.
1027
+ // Here, we use textBaseline = 'middle' and draw the text at exactly
1028
+ // 0.5 * fontSize lower.
1029
+ ctx.textBaseline = 'middle'
1030
+ ctx.fillText(
1031
+ word, info.fillTextOffsetX * mu,
1032
+ (info.fillTextOffsetY + fontSize * 0.5) * mu
1033
+ )
1034
+
1035
+ // The below box is always matches how <span>s are positioned
1036
+ /* ctx.strokeRect(info.fillTextOffsetX, info.fillTextOffsetY,
1037
+ info.fillTextWidth, info.fillTextHeight) */
1038
+
1039
+ // Restore the state.
1040
+ ctx.restore()
1041
+ } else {
1042
+ // drawText on DIV element
1043
+ var span = document.createElement('span')
1044
+ var transformRule = ''
1045
+ transformRule = 'rotate(' + (-rotateDeg / Math.PI * 180) + 'deg) '
1046
+ if (info.mu !== 1) {
1047
+ transformRule +=
1048
+ 'translateX(-' + (info.fillTextWidth / 4) + 'px) ' +
1049
+ 'scale(' + (1 / info.mu) + ')'
1050
+ }
1051
+ var styleRules = {
1052
+ position: 'absolute',
1053
+ display: 'block',
1054
+ font: fontWeight + ' ' +
1055
+ (fontSize * info.mu) + 'px ' + settings.fontFamily,
1056
+ left: ((gx + info.gw / 2) * g + info.fillTextOffsetX) + 'px',
1057
+ top: ((gy + info.gh / 2) * g + info.fillTextOffsetY) + 'px',
1058
+ width: info.fillTextWidth + 'px',
1059
+ height: info.fillTextHeight + 'px',
1060
+ lineHeight: fontSize + 'px',
1061
+ whiteSpace: 'nowrap',
1062
+ transform: transformRule,
1063
+ webkitTransform: transformRule,
1064
+ msTransform: transformRule,
1065
+ transformOrigin: '50% 40%',
1066
+ webkitTransformOrigin: '50% 40%',
1067
+ msTransformOrigin: '50% 40%'
1068
+ }
1069
+ if (color) {
1070
+ styleRules.color = color
1071
+ }
1072
+ span.textContent = word
1073
+ for (var cssProp in styleRules) {
1074
+ span.style[cssProp] = styleRules[cssProp]
1075
+ }
1076
+ if (attributes) {
1077
+ for (var attribute in attributes) {
1078
+ span.setAttribute(attribute, attributes[attribute])
1079
+ }
1080
+ }
1081
+ if (classes) {
1082
+ span.className += classes
1083
+ }
1084
+ el.appendChild(span)
1085
+ }
1086
+ })
1087
+ }
1088
+
1089
+ /* Help function to updateGrid */
1090
+ var fillGridAt = function fillGridAt (x, y, drawMask, dimension, item) {
1091
+ if (x >= ngx || y >= ngy || x < 0 || y < 0) {
1092
+ return
1093
+ }
1094
+
1095
+ grid[x][y] = false
1096
+
1097
+ if (drawMask) {
1098
+ var ctx = elements[0].getContext('2d')
1099
+ ctx.fillRect(x * g, y * g, maskRectWidth, maskRectWidth)
1100
+ }
1101
+
1102
+ if (interactive) {
1103
+ infoGrid[x][y] = { item: item, dimension: dimension }
1104
+ }
1105
+ }
1106
+
1107
+ /* Update the filling information of the given space with occupied points.
1108
+ Draw the mask on the canvas if necessary. */
1109
+ var updateGrid = function updateGrid (gx, gy, gw, gh, info, item) {
1110
+ var occupied = info.occupied
1111
+ var drawMask = settings.drawMask
1112
+ var ctx
1113
+ if (drawMask) {
1114
+ ctx = elements[0].getContext('2d')
1115
+ ctx.save()
1116
+ ctx.fillStyle = settings.maskColor
1117
+ }
1118
+
1119
+ var dimension
1120
+ if (interactive) {
1121
+ var bounds = info.bounds
1122
+ dimension = {
1123
+ x: (gx + bounds[3]) * g,
1124
+ y: (gy + bounds[0]) * g,
1125
+ w: (bounds[1] - bounds[3] + 1) * g,
1126
+ h: (bounds[2] - bounds[0] + 1) * g
1127
+ }
1128
+ }
1129
+
1130
+ var i = occupied.length
1131
+ while (i--) {
1132
+ var px = gx + occupied[i][0]
1133
+ var py = gy + occupied[i][1]
1134
+
1135
+ if (px >= ngx || py >= ngy || px < 0 || py < 0) {
1136
+ continue
1137
+ }
1138
+
1139
+ fillGridAt(px, py, drawMask, dimension, item)
1140
+ }
1141
+
1142
+ if (drawMask) {
1143
+ ctx.restore()
1144
+ }
1145
+ }
1146
+
1147
+ /* putWord() processes each item on the list,
1148
+ calculate it's size and determine it's position, and actually
1149
+ put it on the canvas. */
1150
+ var putWord = function putWord (item) {
1151
+ var word, weight, attributes
1152
+ if (Array.isArray(item)) {
1153
+ word = item[0]
1154
+ weight = item[1]
1155
+ } else {
1156
+ word = item.word
1157
+ weight = item.weight
1158
+ attributes = item.attributes
1159
+ }
1160
+ var rotateDeg = getRotateDeg()
1161
+
1162
+ var extraDataArray = getItemExtraData(item)
1163
+
1164
+ // get info needed to put the text onto the canvas
1165
+ var info = getTextInfo(word, weight, rotateDeg, extraDataArray)
1166
+
1167
+ // not getting the info means we shouldn't be drawing this one.
1168
+ if (!info) {
1169
+ return false
1170
+ }
1171
+
1172
+ if (exceedTime()) {
1173
+ return false
1174
+ }
1175
+
1176
+ // If drawOutOfBound is set to false,
1177
+ // skip the loop if we have already know the bounding box of
1178
+ // word is larger than the canvas.
1179
+ if (!settings.drawOutOfBound && !settings.shrinkToFit) {
1180
+ var bounds = info.bounds;
1181
+ if ((bounds[1] - bounds[3] + 1) > ngx ||
1182
+ (bounds[2] - bounds[0] + 1) > ngy) {
1183
+ return false
1184
+ }
1185
+ }
1186
+
1187
+ // Determine the position to put the text by
1188
+ // start looking for the nearest points
1189
+ var r = maxRadius + 1
1190
+
1191
+ var tryToPutWordAtPoint = function (gxy) {
1192
+ var gx = Math.floor(gxy[0] - info.gw / 2)
1193
+ var gy = Math.floor(gxy[1] - info.gh / 2)
1194
+ var gw = info.gw
1195
+ var gh = info.gh
1196
+
1197
+ // If we cannot fit the text at this position, return false
1198
+ // and go to the next position.
1199
+ if (!canFitText(gx, gy, gw, gh, info.occupied)) {
1200
+ return false
1201
+ }
1202
+
1203
+ // Actually put the text on the canvas
1204
+ drawText(gx, gy, info, word, weight,
1205
+ (maxRadius - r), gxy[2], rotateDeg, attributes, extraDataArray)
1206
+
1207
+ // Mark the spaces on the grid as filled
1208
+ updateGrid(gx, gy, gw, gh, info, item)
1209
+
1210
+ // Return true so some() will stop and also return true.
1211
+ return true
1212
+ }
1213
+
1214
+ while (r--) {
1215
+ var points = getPointsAtRadius(maxRadius - r)
1216
+
1217
+ if (settings.shuffle) {
1218
+ points = [].concat(points)
1219
+ shuffleArray(points)
1220
+ }
1221
+
1222
+ // Try to fit the words by looking at each point.
1223
+ // array.some() will stop and return true
1224
+ // when putWordAtPoint() returns true.
1225
+ // If all the points returns false, array.some() returns false.
1226
+ var drawn = points.some(tryToPutWordAtPoint)
1227
+
1228
+ if (drawn) {
1229
+ // leave putWord() and return true
1230
+ return true
1231
+ }
1232
+ }
1233
+ if (settings.shrinkToFit) {
1234
+ if (Array.isArray(item)) {
1235
+ item[1] = item[1] * 3 / 4
1236
+ } else {
1237
+ item.weight = item.weight * 3 / 4
1238
+ }
1239
+ return putWord(item)
1240
+ }
1241
+ // we tried all distances but text won't fit, return false
1242
+ return false
1243
+ }
1244
+
1245
+ /* Send DOM event to all elements. Will stop sending event and return
1246
+ if the previous one is canceled (for cancelable events). */
1247
+ var sendEvent = function sendEvent (type, cancelable, details) {
1248
+ if (cancelable) {
1249
+ return !elements.some(function (el) {
1250
+ var event = new CustomEvent(type, {
1251
+ detail: details || {}
1252
+ })
1253
+ return !el.dispatchEvent(event)
1254
+ }, this)
1255
+ } else {
1256
+ elements.forEach(function (el) {
1257
+ var event = new CustomEvent(type, {
1258
+ detail: details || {}
1259
+ })
1260
+ el.dispatchEvent(event)
1261
+ }, this)
1262
+ }
1263
+ }
1264
+
1265
+ /* Start drawing on a canvas */
1266
+ var start = function start () {
1267
+ // For dimensions, clearCanvas etc.,
1268
+ // we only care about the first element.
1269
+ var canvas = elements[0]
1270
+
1271
+ if (canvas.getContext) {
1272
+ ngx = Math.ceil(canvas.width / g)
1273
+ ngy = Math.ceil(canvas.height / g)
1274
+ } else {
1275
+ var rect = canvas.getBoundingClientRect()
1276
+ ngx = Math.ceil(rect.width / g)
1277
+ ngy = Math.ceil(rect.height / g)
1278
+ }
1279
+
1280
+ // Sending a wordcloudstart event which cause the previous loop to stop.
1281
+ // Do nothing if the event is canceled.
1282
+ if (!sendEvent('wordcloudstart', true)) {
1283
+ return
1284
+ }
1285
+
1286
+ // Determine the center of the word cloud
1287
+ center = (settings.origin)
1288
+ ? [settings.origin[0] / g, settings.origin[1] / g]
1289
+ : [ngx / 2, ngy / 2]
1290
+
1291
+ // Maxium radius to look for space
1292
+ maxRadius = Math.floor(Math.sqrt(ngx * ngx + ngy * ngy))
1293
+
1294
+ /* Clear the canvas only if the clearCanvas is set,
1295
+ if not, update the grid to the current canvas state */
1296
+ grid = []
1297
+
1298
+ var gx, gy, i
1299
+ if (!canvas.getContext || settings.clearCanvas) {
1300
+ elements.forEach(function (el) {
1301
+ if (el.getContext) {
1302
+ var ctx = el.getContext('2d')
1303
+ ctx.fillStyle = settings.backgroundColor
1304
+ ctx.clearRect(0, 0, ngx * (g + 1), ngy * (g + 1))
1305
+ ctx.fillRect(0, 0, ngx * (g + 1), ngy * (g + 1))
1306
+ } else {
1307
+ el.textContent = ''
1308
+ el.style.backgroundColor = settings.backgroundColor
1309
+ el.style.position = 'relative'
1310
+ }
1311
+ })
1312
+
1313
+ /* fill the grid with empty state */
1314
+ gx = ngx
1315
+ while (gx--) {
1316
+ grid[gx] = []
1317
+ gy = ngy
1318
+ while (gy--) {
1319
+ grid[gx][gy] = true
1320
+ }
1321
+ }
1322
+ } else {
1323
+ /* Determine bgPixel by creating
1324
+ another canvas and fill the specified background color. */
1325
+ var bctx = document.createElement('canvas').getContext('2d')
1326
+
1327
+ bctx.fillStyle = settings.backgroundColor
1328
+ bctx.fillRect(0, 0, 1, 1)
1329
+ var bgPixel = bctx.getImageData(0, 0, 1, 1).data
1330
+
1331
+ /* Read back the pixels of the canvas we got to tell which part of the
1332
+ canvas is empty.
1333
+ (no clearCanvas only works with a canvas, not divs) */
1334
+ var imageData =
1335
+ canvas.getContext('2d').getImageData(0, 0, ngx * g, ngy * g).data
1336
+
1337
+ gx = ngx
1338
+ var x, y
1339
+ while (gx--) {
1340
+ grid[gx] = []
1341
+ gy = ngy
1342
+ while (gy--) {
1343
+ y = g
1344
+ /* eslint no-labels: ["error", { "allowLoop": true }] */
1345
+ singleGridLoop: while (y--) {
1346
+ x = g
1347
+ while (x--) {
1348
+ i = 4
1349
+ while (i--) {
1350
+ if (imageData[((gy * g + y) * ngx * g +
1351
+ (gx * g + x)) * 4 + i] !== bgPixel[i]) {
1352
+ grid[gx][gy] = false
1353
+ break singleGridLoop
1354
+ }
1355
+ }
1356
+ }
1357
+ }
1358
+ if (grid[gx][gy] !== false) {
1359
+ grid[gx][gy] = true
1360
+ }
1361
+ }
1362
+ }
1363
+
1364
+ imageData = bctx = bgPixel = undefined
1365
+ }
1366
+
1367
+ // fill the infoGrid with empty state if we need it
1368
+ if (settings.hover || settings.click) {
1369
+ interactive = true
1370
+
1371
+ /* fill the grid with empty state */
1372
+ gx = ngx + 1
1373
+ while (gx--) {
1374
+ infoGrid[gx] = []
1375
+ }
1376
+
1377
+ if (settings.hover) {
1378
+ canvas.addEventListener('mousemove', wordcloudhover)
1379
+ }
1380
+
1381
+ if (settings.click) {
1382
+ canvas.addEventListener('click', wordcloudclick)
1383
+ canvas.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'
1384
+ }
1385
+
1386
+ canvas.addEventListener('wordcloudstart', function stopInteraction () {
1387
+ canvas.removeEventListener('wordcloudstart', stopInteraction)
1388
+ canvas.removeEventListener('mousemove', wordcloudhover)
1389
+ canvas.removeEventListener('click', wordcloudclick)
1390
+ hovered = undefined
1391
+ })
1392
+ }
1393
+
1394
+ i = 0
1395
+ var loopingFunction, stoppingFunction
1396
+ if (settings.wait !== 0) {
1397
+ loopingFunction = window.setTimeout
1398
+ stoppingFunction = window.clearTimeout
1399
+ } else {
1400
+ loopingFunction = window.setImmediate
1401
+ stoppingFunction = window.clearImmediate
1402
+ }
1403
+
1404
+ var addEventListener = function addEventListener (type, listener) {
1405
+ elements.forEach(function (el) {
1406
+ el.addEventListener(type, listener)
1407
+ }, this)
1408
+ }
1409
+
1410
+ var removeEventListener = function removeEventListener (type, listener) {
1411
+ elements.forEach(function (el) {
1412
+ el.removeEventListener(type, listener)
1413
+ }, this)
1414
+ }
1415
+
1416
+ var anotherWordCloudStart = function anotherWordCloudStart () {
1417
+ removeEventListener('wordcloudstart', anotherWordCloudStart)
1418
+ stoppingFunction(timer[timerId])
1419
+ }
1420
+
1421
+ addEventListener('wordcloudstart', anotherWordCloudStart)
1422
+ timer[timerId] = loopingFunction(function loop () {
1423
+ if (i >= settings.list.length) {
1424
+ stoppingFunction(timer[timerId])
1425
+ sendEvent('wordcloudstop', false)
1426
+ removeEventListener('wordcloudstart', anotherWordCloudStart)
1427
+ delete timer[timerId];
1428
+ return
1429
+ }
1430
+ escapeTime = (new Date()).getTime()
1431
+ var drawn = putWord(settings.list[i])
1432
+ var canceled = !sendEvent('wordclouddrawn', true, {
1433
+ item: settings.list[i],
1434
+ drawn: drawn
1435
+ })
1436
+ if (exceedTime() || canceled) {
1437
+ stoppingFunction(timer[timerId])
1438
+ settings.abort()
1439
+ sendEvent('wordcloudabort', false)
1440
+ sendEvent('wordcloudstop', false)
1441
+ removeEventListener('wordcloudstart', anotherWordCloudStart)
1442
+ delete timer[timerId]
1443
+ return
1444
+ }
1445
+ i++
1446
+ timer[timerId] = loopingFunction(loop, settings.wait)
1447
+ }, settings.wait)
1448
+ }
1449
+
1450
+ // All set, start the drawing
1451
+ start()
1452
+ }
1453
+
1454
+ WordCloud.isSupported = isSupported
1455
+ WordCloud.minFontSize = minFontSize
1456
+ WordCloud.stop = function stop () {
1457
+ if (timer) {
1458
+ for (var timerId in timer) {
1459
+ window.clearImmediate(timer[timerId])
1460
+ }
1461
+ }
1462
+ }
1463
+
1464
+ // Expose the library as an AMD module
1465
+ if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef
1466
+ global.WordCloud = WordCloud
1467
+ define('wordcloud', [], function () { return WordCloud }) // eslint-disable-line no-undef
1468
+ } else if (typeof module !== 'undefined' && module.exports) { // eslint-disable-line no-undef
1469
+ module.exports = WordCloud // eslint-disable-line no-undef
1470
+ } else {
1471
+ global.WordCloud = WordCloud
1472
+ }
1473
+ })(this) // jshint ignore:line
1474
+ `;
1475
+
1476
+ // src/Renderer.ts
226
1477
  var Renderer = class {
227
1478
  /**
228
1479
  * @constructor
@@ -479,7 +1730,7 @@ var Renderer = class {
479
1730
  <div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
480
1731
  </div>
481
1732
  <div id="wordcloud-container" style="width: 800px; height: 600px; margin: auto;"></div>
482
- <script src="https://unpkg.com/wordcloud@1.2.2/src/wordcloud2.js"></script>
1733
+ <script>${wordCloudScript}</script>
483
1734
  <script>
484
1735
  WordCloud(document.getElementById('wordcloud-container'), {
485
1736
  list: ${wordListJson},
@@ -906,31 +2157,17 @@ var Analyse = class {
906
2157
  const since = new Date(Date.now() - options.hours * import_koishi6.Time.hour);
907
2158
  const records = await this.ctx.database.get("analyse_cache", { uid: { $in: scope.uids }, timestamp: { $gte: since } }, ["content"]);
908
2159
  if (!records.length) return "暂无统计数据";
909
- const allText = records.map((r) => r.content).join(" ");
910
- const exclusionSet = /* @__PURE__ */ new Set([
911
- "[face]",
912
- "[file]",
913
- "[forward]",
914
- "[img]",
915
- "[audio]",
916
- "[video]",
917
- "[json]",
918
- "[rps]",
919
- "[markdown]",
920
- "[dice]"
921
- ]);
2160
+ const exclusionRegex = /\[(face|file|forward|img|gif|audio|video|json|rps|markdown|dice|at:.*?)\]/g;
2161
+ const allText = records.map((r) => r.content.replace(exclusionRegex, "")).join(" ");
922
2162
  const words = this.jieba.cut(allText).filter((w) => {
923
- if (w.length <= 1) return false;
2163
+ if (w.trim().length <= 1) return false;
924
2164
  if (/^\d+$/.test(w)) return false;
925
- if (exclusionSet.has(w)) return false;
926
- if (/^\[at:.*?\]$/.test(w)) return false;
927
2165
  return true;
928
2166
  });
929
2167
  if (!words.length) return "暂无有效词语";
930
2168
  const wordCounts = words.reduce((map, word) => map.set(word, (map.get(word) || 0) + 1), /* @__PURE__ */ new Map());
931
2169
  const wordList = Array.from(wordCounts.entries()).sort((a, b) => b[1] - a[1]);
932
2170
  const title = await generateTitle(this.ctx, scope.scopeDesc, { main: "词云" });
933
- this.ctx.logger.info(`准备渲染词云(词数:${wordList.length}):${JSON.stringify(wordList.slice(0, 10))}`);
934
2171
  const result = await this.renderer.renderWordCloud({ title, time: /* @__PURE__ */ new Date(), words: wordList });
935
2172
  if (typeof result === "string") return result;
936
2173
  if (Array.isArray(result) && result.length > 0) {