koishi-plugin-booth-get 6.0.2 → 6.0.3

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.
@@ -315,350 +315,38 @@ class CardGenerator {
315
315
  </html>`;
316
316
  }
317
317
 
318
- generateDiscountCardHTML(item) {
319
- const savedAmount = item.original_price - item.price;
320
- const discountRate = Math.round((1 - item.price/item.original_price)*100);
321
-
322
- return `
323
- <html>
324
- <head>
325
- <meta charset="utf-8">
326
- <style>
327
- @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=Montserrat:wght@600;700;800&display=swap');
328
- body {
329
- margin: 0;
330
- padding: 0;
331
- font-family: 'Noto Sans SC', sans-serif;
332
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
333
- display: flex;
334
- justify-content: center;
335
- align-items: center;
336
- min-height: 100vh;
337
- }
338
- .container {
339
- width: 640px;
340
- background: white;
341
- border-radius: 20px;
342
- overflow: hidden;
343
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
344
- position: relative;
345
- }
346
- .header {
347
- background: linear-gradient(90deg, #ff6b6b, #ffa502);
348
- padding: 25px;
349
- text-align: center;
350
- position: relative;
351
- color: white;
352
- }
353
- .header::before {
354
- content: "";
355
- position: absolute;
356
- top: 0;
357
- left: 0;
358
- right: 0;
359
- bottom: 0;
360
- background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><polygon points="0,0 100,100 0,100" fill="rgba(255,255,255,0.1)"/></svg>');
361
- background-size: 100px 100px;
362
- }
363
- .label {
364
- background: rgba(255, 255, 255, 0.2);
365
- backdrop-filter: blur(10px);
366
- padding: 8px 20px;
367
- border-radius: 30px;
368
- font-size: 14px;
369
- font-weight: 500;
370
- display: inline-block;
371
- margin-bottom: 15px;
372
- border: 1px solid rgba(255, 255, 255, 0.3);
373
- }
374
- .booth-logo {
375
- font-family: 'Montserrat', sans-serif;
376
- font-weight: 800;
377
- font-size: 36px;
378
- letter-spacing: 2px;
379
- text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
380
- }
381
- .content {
382
- padding: 30px;
383
- }
384
- .main-image {
385
- width: 100%;
386
- height: 320px;
387
- background: #f0f0f0 url('${item.image_url}') center/cover;
388
- border-radius: 15px;
389
- margin-bottom: 25px;
390
- box-shadow: 0 10px 20px rgba(0,0,0,0.1);
391
- border: 1px solid rgba(0,0,0,0.05);
392
- }
393
- .product-title {
394
- font-size: 26px;
395
- margin: 0 0 20px 0;
396
- color: #2c3e50;
397
- font-weight: 700;
398
- line-height: 1.4;
399
- }
400
- .author-section {
401
- display: flex;
402
- align-items: center;
403
- gap: 15px;
404
- margin-bottom: 25px;
405
- padding: 15px;
406
- background: #f8f9fa;
407
- border-radius: 12px;
408
- border-left: 4px solid #3498db;
409
- }
410
- .author-avatar {
411
- width: 60px;
412
- height: 60px;
413
- border-radius: 50%;
414
- border: 3px solid #fff;
415
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
416
- object-fit: cover;
417
- }
418
- .author-info {
419
- flex: 1;
420
- }
421
- .author-name {
422
- font-size: 18px;
423
- font-weight: 600;
424
- color: #2c3e50;
425
- margin-bottom: 4px;
426
- }
427
- .author-label {
428
- font-size: 14px;
429
- color: #7f8c8d;
430
- }
431
- .price-section {
432
- font-size: 32px;
433
- font-weight: 700;
434
- color: #e74c3c;
435
- margin-bottom: 30px;
436
- text-align: center;
437
- background: #fff9f9;
438
- padding: 15px;
439
- border-radius: 12px;
440
- border: 2px dashed #e74c3c;
441
- }
442
- /* 新增价格对比样式 */
443
- .price-comparison {
444
- display: flex;
445
- justify-content: center;
446
- align-items: center;
447
- gap: 15px;
448
- margin-bottom: 30px;
449
- text-align: center;
450
- }
451
- .original-price {
452
- font-size: 24px;
453
- color: #7f8c8d;
454
- text-decoration: line-through;
455
- }
456
- .current-price {
457
- font-size: 32px;
458
- font-weight: 700;
459
- color: #e74c3c;
460
- }
461
- .discount-badge {
462
- background: #e74c3c;
463
- color: white;
464
- padding: 5px 10px;
465
- border-radius: 15px;
466
- font-size: 16px;
467
- font-weight: 600;
468
- }
469
- .savings {
470
- text-align: center;
471
- font-size: 16px;
472
- color: #27ae60;
473
- font-weight: 600;
474
- margin-bottom: 20px;
475
- }
476
- .description {
477
- color: #34495e;
478
- line-height: 1.7;
479
- padding: 20px;
480
- background: #f8f9fa;
481
- border-radius: 12px;
482
- margin-bottom: 30px;
483
- font-size: 15px;
484
- }
485
- .stats {
486
- display: flex;
487
- justify-content: space-around;
488
- margin-bottom: 30px;
489
- text-align: center;
490
- }
491
- .stat-item {
492
- padding: 15px;
493
- }
494
- .stat-value {
495
- font-size: 24px;
496
- font-weight: 700;
497
- color: #3498db;
498
- }
499
- .stat-label {
500
- font-size: 14px;
501
- color: #7f8c8d;
502
- margin-top: 5px;
503
- }
504
- .related-works {
505
- margin-top: 30px;
506
- border-top: 1px solid #eee;
507
- padding-top: 25px;
508
- }
509
- .related-title {
510
- font-size: 20px;
511
- color: #2c3e50;
512
- margin-bottom: 20px;
513
- text-align: center;
514
- font-weight: 600;
515
- }
516
- .works-grid {
517
- display: grid;
518
- grid-template-columns: repeat(3, 1fr);
519
- gap: 15px;
520
- }
521
- .work-item {
522
- background: white;
523
- border-radius: 12px;
524
- overflow: hidden;
525
- box-shadow: 0 4px 8px rgba(0,0,0,0.08);
526
- transition: all 0.3s ease;
527
- }
528
- .work-item:hover {
529
- transform: translateY(-5px);
530
- box-shadow: 0 10px 20px rgba(0,0,0,0.15);
531
- }
532
- .work-image {
533
- height: 100px;
534
- background-size: cover;
535
- background-position: center;
536
- }
537
- .work-info {
538
- padding: 12px;
539
- }
540
- .work-title {
541
- font-size: 13px;
542
- margin-bottom: 8px;
543
- color: #2c3e50;
544
- height: 36px;
545
- overflow: hidden;
546
- }
547
- .work-price {
548
- font-size: 15px;
549
- font-weight: 600;
550
- color: #e74c3c;
551
- }
552
- .footer {
553
- background: #2c3e50;
554
- padding: 20px;
555
- text-align: center;
556
- color: #ecf0f1;
557
- font-size: 14px;
558
- }
559
- .link {
560
- color: #3498db;
561
- text-decoration: none;
562
- font-weight: 500;
563
- }
564
- .link:hover {
565
- text-decoration: underline;
566
- }
567
- .tags {
568
- display: flex;
569
- flex-wrap: wrap;
570
- gap: 8px;
571
- margin-bottom: 25px;
572
- }
573
- .tag {
574
- background: #e1f0fa;
575
- color: #3498db;
576
- padding: 6px 12px;
577
- border-radius: 20px;
578
- font-size: 13px;
579
- font-weight: 500;
580
- }
581
- </style>
582
- </head>
583
- <body>
584
- <div class="container">
585
- <div class="header">
586
- <div class="label">折扣商品 -${discountRate}%</div>
587
- <div class="booth-logo">BOOTH</div>
588
- </div>
589
-
590
- <div class="content">
591
- <div class="main-image"></div>
592
- <h1 class="product-title">${item.title}</h1>
593
-
594
- <div class="author-section">
595
- <img src="${item.author_thumbnail_url || 'https://s2.booth.pm/static-images/user/guest-32.png'}"
596
- class="author-avatar"
597
- alt="作者头像" onerror="this.src='https://s2.booth.pm/static-images/user/guest-32.png'">
598
- <div class="author-info">
599
- <div class="author-name">${item.author}</div>
600
- <div class="author-label">BOOTHクリエイター</div>
601
- </div>
602
- </div>
318
+ generateDiscountText(item) {
319
+ const savedAmount = item.original_price - item.price;
320
+ const discountRate = Math.round((1 - item.price/item.original_price)*100);
321
+
322
+ return `🔥 BOOTH 折扣商品推送 🔥
603
323
 
604
- <!-- 折扣价格对比显示 -->
605
- <div class="price-comparison">
606
- <div class="original-price">¥${item.original_price.toLocaleString()}</div>
607
- <div class="discount-badge">-${discountRate}%</div>
608
- <div class="current-price">¥${item.price.toLocaleString()}</div>
609
- </div>
610
-
611
- <div class="savings">
612
- 节省 ¥${savedAmount.toLocaleString()}
613
- </div>
614
-
615
- <div class="stats">
616
- <div class="stat-item">
617
- <div class="stat-value">${item.likes || 0}</div>
618
- <div class="stat-label">收藏数</div>
619
- </div>
620
- <div class="stat-item">
621
- <div class="stat-value">${item.category || '未分类'}</div>
622
- <div class="stat-label">分类</div>
623
- </div>
624
- <div class="stat-item">
625
- <div class="stat-value">#${item.id}</div>
626
- <div class="stat-label">商品ID</div>
627
- </div>
628
- </div>
629
-
630
- <div class="tags">
631
- ${(item.tags || []).slice(0, 5).map(tag => `<div class="tag">${tag.name}</div>`).join('')}
632
- </div>
633
-
634
- <div class="description">
635
- <p>${(item.description || "").slice(0, 300)}${(item.description||"").length > 300 ? '...' : ''}</p>
636
- </div>
637
- </div>
324
+ 📌 商品名称: ${item.title}
325
+ 👤 作者: ${item.author}
326
+ 💰 价格: ¥${item.price.toLocaleString()} (原价: ¥${item.original_price.toLocaleString()})
327
+ 💥 折扣: -${discountRate}% (节省 ¥${savedAmount.toLocaleString()})
328
+ ❤️ 收藏数: ${item.likes || 0}
329
+ 🏷️ 分类: ${item.category || '未分类'}
330
+ #${item.id}
638
331
 
639
- <div class="footer">
640
- 由VRCBBS提供 | BOOTH链接:
641
- <a href="https://booth.pm/zh-cn/items/${item.id}"
642
- class="link">
643
- https://booth.pm/zh-cn/items/${item.id}
644
- </a>
645
- </div>
646
- </div>
647
- </body>
648
- </html>\``;
649
- }
332
+ 📝 商品简介:
333
+ ${(item.description || "").slice(0, 200)}${(item.description||"").length > 200 ? '...' : ''}
334
+
335
+ 🔗 商品链接: https://booth.pm/zh-cn/items/${item.id}`;
336
+ }
650
337
 
651
338
  async captureCardHTML(html, config) {
652
339
  const page = await this.ctx.puppeteer.page();
653
340
  try {
654
-
341
+ page.setDefaultTimeout(config.loadTimeout || 30000);
342
+
655
343
  await page.setContent(html, {
656
- waitUntil: 'networkidle0',
344
+ waitUntil: 'networkidle0',
657
345
  timeout: config.loadTimeout || 30000
658
346
  });
659
347
 
660
348
  await new Promise(resolve => setTimeout(resolve, 2000));
661
-
349
+
662
350
  await page.setViewport({ width: 640, height: 1200 });
663
351
  const container = await page.$('.container') || await page.$('body');
664
352
  return await container.screenshot({
@@ -376,67 +376,49 @@ class DiscountTracker {
376
376
  this.lastDiscountItems = discountItems.slice();
377
377
  }
378
378
 
379
- async sendItemsToGroups(items) {
380
- const logger = this.ctx.logger('booth-discount');
381
- const cardGenerator = require('./card-generator');
382
-
383
- for (const item of items) {
384
- try {
385
- const html = cardGenerator.generateDiscountCardHTML(item);
386
- const buffer = await cardGenerator.captureCardHTML(html, this.config);
387
-
388
- if (!buffer) {
389
- logger.warn(`生成卡片失败: ${item.title}`);
390
- continue;
391
- }
392
-
393
- const savedAmount = (item.original_price || 0) - (item.price || 0);
394
- let message = `🎉 发现折扣商品!\n\n${item.title}\n作者:${item.author || ''}\n`;
395
-
396
- if (item.has_discount_keyword) {
397
- message += `🔍 折扣关键词: ${item.discount_keyword}\n`;
398
- }
399
-
400
- message += `💰 价格:¥${(item.price||0).toLocaleString()}(原价:¥${(item.original_price||0).toLocaleString()})\n`;
401
- message += `🎊 折扣:${item.discount_rate}% OFF\n`;
402
- message += `💵 节省:¥${savedAmount.toLocaleString()}\n`;
403
- message += `🔗 链接:${item.url}`;
404
-
405
- for (const channelId of this.config.targetGroups) {
406
- try {
407
- if (channelId.includes(':')) {
408
- const [platform, id] = channelId.split(':', 2);
409
- for (const bot of this.ctx.bots) {
410
- if (bot.platform === platform) {
411
- await bot.sendMessage(id, [message, h.image(buffer, 'image/png')]);
412
- break;
379
+ async sendItemsToGroups(items) {
380
+ const logger = this.ctx.logger('booth-discount');
381
+ const cardGenerator = require('./card-generator');
382
+
383
+ for (const item of items) {
384
+ try {
385
+ const message = cardGenerator.generateDiscountText(item);
386
+
387
+ for (const channelId of this.config.targetGroups) {
388
+ try {
389
+ if (channelId.includes(':')) {
390
+ const [platform, id] = channelId.split(':', 2);
391
+ for (const bot of this.ctx.bots) {
392
+ if (bot.platform === platform) {
393
+ await bot.sendMessage(id, message);
394
+ break;
395
+ }
413
396
  }
414
- }
415
- } else {
416
- for (const bot of this.ctx.bots) {
417
- try {
418
- await bot.sendMessage(channelId, [message, h.image(buffer, 'image/png')]);
419
- break;
420
- } catch (e) {
421
- continue;
397
+ } else {
398
+ for (const bot of this.ctx.bots) {
399
+ try {
400
+ await bot.sendMessage(channelId, message);
401
+ break;
402
+ } catch (e) {
403
+ continue;
404
+ }
422
405
  }
423
406
  }
407
+ await new Promise(r => setTimeout(r, 2000));
408
+ } catch (e) {
409
+ logger.warn(`推送失败 ${channelId}: ${e.message}`);
424
410
  }
425
- await new Promise(r => setTimeout(r, 2000));
426
- } catch (e) {
427
- logger.warn(`推送失败 ${channelId}: ${e.message}`);
428
411
  }
429
- }
430
412
 
431
- this.recentlyPushed.set(item.id, Date.now());
432
- this.discountCache.set(item.id, Date.now());
433
- await new Promise(r => setTimeout(r, 3000));
413
+ this.recentlyPushed.set(item.id, Date.now());
414
+ this.discountCache.set(item.id, Date.now());
415
+ await new Promise(r => setTimeout(r, 3000));
434
416
 
435
- } catch (err) {
436
- logger.error(`推送错误: ${err.message}`);
417
+ } catch (err) {
418
+ logger.error(`推送错误: ${err.message}`);
419
+ }
437
420
  }
438
421
  }
439
- }
440
422
 
441
423
  startDiscountTracking() {
442
424
  const logger = this.ctx.logger('booth-discount');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-booth-get",
3
3
  "description": "通过url与名称检查摊位物品并反馈用户搜索的图片",
4
- "version": "6.0.2",
4
+ "version": "6.0.3",
5
5
  "contributors": [
6
6
  "rixiang <1148147857@qq.com>"
7
7
  ],