koishi-plugin-booth-get 5.2.5 → 5.2.7
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 +431 -9
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -33,10 +33,45 @@ var inject = ["puppeteer"];
|
|
|
33
33
|
var Config = import_koishi.Schema.object({
|
|
34
34
|
loadTimeout: import_koishi.Schema.natural().role("ms").description("加载页面的最长时间").default(import_koishi.Time.second * 10),
|
|
35
35
|
idleTimeout: import_koishi.Schema.natural().role("ms").description("等待页面空闲的最长时间").default(import_koishi.Time.second * 30),
|
|
36
|
-
proxyServer:
|
|
36
|
+
proxyServer: import_booth.Schema.string().description("代理服务器地址").default("61.216.156.222:60808"),
|
|
37
|
+
enableR18Check: import_koishi.Schema.boolean().description("启用R18内容检测").default(true),
|
|
38
|
+
r18Tags: import_koishi.Schema.array(String).description("R18标签列表").default(["R-18", "R18", "成人向け", "NSFW", "18禁"])
|
|
37
39
|
|
|
38
40
|
}).description("booth-get");
|
|
39
41
|
|
|
42
|
+
// R18内容检测函数
|
|
43
|
+
function checkR18(item, config) {
|
|
44
|
+
if (!config.enableR18Check) return false;
|
|
45
|
+
|
|
46
|
+
// 检查标签
|
|
47
|
+
if (item.tags && Array.isArray(item.tags)) {
|
|
48
|
+
const hasR18Tag = item.tags.some(tag =>
|
|
49
|
+
config.r18Tags.some(r18Tag =>
|
|
50
|
+
tag.name && tag.name.toLowerCase().includes(r18Tag.toLowerCase())
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
if (hasR18Tag) return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 检查标题
|
|
57
|
+
if (item.title) {
|
|
58
|
+
const hasR18InTitle = config.r18Tags.some(r18Tag =>
|
|
59
|
+
item.title.toLowerCase().includes(r18Tag.toLowerCase())
|
|
60
|
+
);
|
|
61
|
+
if (hasR18InTitle) return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 检查描述
|
|
65
|
+
if (item.description) {
|
|
66
|
+
const hasR18InDesc = config.r18Tags.some(r18Tag =>
|
|
67
|
+
item.description.toLowerCase().includes(r18Tag.toLowerCase())
|
|
68
|
+
);
|
|
69
|
+
if (hasR18InDesc) return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
40
75
|
function generateCardHTML(item, relatedItems = []) {
|
|
41
76
|
return `
|
|
42
77
|
<html>
|
|
@@ -387,11 +422,17 @@ async function fetchRelatedItems(author) {
|
|
|
387
422
|
}
|
|
388
423
|
}
|
|
389
424
|
|
|
390
|
-
async function captureCard(ctx, id) {
|
|
425
|
+
async function captureCard(ctx, id, config) {
|
|
391
426
|
const logger = ctx.logger("booth-get");
|
|
392
427
|
const item = await getBoothItem(id);
|
|
393
428
|
if (!item) return null;
|
|
394
429
|
|
|
430
|
+
// R18内容检测
|
|
431
|
+
if (checkR18(item, config)) {
|
|
432
|
+
logger.warn(`检测到R18内容,已跳过商品: ${id}`);
|
|
433
|
+
return "R18_CONTENT";
|
|
434
|
+
}
|
|
435
|
+
|
|
395
436
|
const relatedItems = await fetchRelatedItems(item.author);
|
|
396
437
|
|
|
397
438
|
const html = generateCardHTML(item, relatedItems);
|
|
@@ -403,7 +444,7 @@ async function captureCard(ctx, id) {
|
|
|
403
444
|
|
|
404
445
|
await page.setContent(html, {
|
|
405
446
|
waitUntil: 'domcontentloaded',
|
|
406
|
-
timeout:
|
|
447
|
+
timeout: config.loadTimeout || import_koishi.Time.second * 10
|
|
407
448
|
});
|
|
408
449
|
|
|
409
450
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
@@ -430,7 +471,8 @@ function apply(ctx, config) {
|
|
|
430
471
|
.action(async ({ session }, id) => {
|
|
431
472
|
if (!id) return "请输入商品ID";
|
|
432
473
|
try {
|
|
433
|
-
const buffer = await captureCard(ctx, id);
|
|
474
|
+
const buffer = await captureCard(ctx, id, config);
|
|
475
|
+
if (buffer === "R18_CONTENT") return "该商品可能包含R18内容,已跳过";
|
|
434
476
|
return buffer ? import_koishi.h.image(buffer, "image/png") : "商品获取失败";
|
|
435
477
|
} catch (error) {
|
|
436
478
|
logger.warn(error);
|
|
@@ -476,7 +518,7 @@ function apply(ctx, config) {
|
|
|
476
518
|
let retries = 3;
|
|
477
519
|
while (retries > 0) {
|
|
478
520
|
try {
|
|
479
|
-
await page.goto(searchUrl, { waitUntil: 'networkidle0', timeout:
|
|
521
|
+
await page.goto(searchUrl, { waitUntil: 'networkidle0', timeout: config.loadTimeout || import_koishi.Time.second * 10 });
|
|
480
522
|
break;
|
|
481
523
|
} catch (error) {
|
|
482
524
|
retries--;
|
|
@@ -543,7 +585,10 @@ function apply(ctx, config) {
|
|
|
543
585
|
}
|
|
544
586
|
|
|
545
587
|
try {
|
|
546
|
-
const buffer = await captureCard(ctx, selectedItemId);
|
|
588
|
+
const buffer = await captureCard(ctx, selectedItemId, config);
|
|
589
|
+
if (buffer === "R18_CONTENT") {
|
|
590
|
+
return "搜索到的商品可能包含R18内容,已跳过";
|
|
591
|
+
}
|
|
547
592
|
if (!buffer) {
|
|
548
593
|
return "卡片生成失败";
|
|
549
594
|
}
|
|
@@ -580,6 +625,313 @@ function apply(ctx, config) {
|
|
|
580
625
|
return (2 * matches) / (a.length + b.length - 2);
|
|
581
626
|
}
|
|
582
627
|
|
|
628
|
+
async function fetchAuthorItems(ctx, authorName, limit = 6) {
|
|
629
|
+
try {
|
|
630
|
+
const page = await ctx.puppeteer.page();
|
|
631
|
+
|
|
632
|
+
try {
|
|
633
|
+
await page.setRequestInterception(true);
|
|
634
|
+
page.on('request', (request) => {
|
|
635
|
+
const resourceType = request.resourceType();
|
|
636
|
+
if (['stylesheet', 'font'].includes(resourceType)) {
|
|
637
|
+
request.abort();
|
|
638
|
+
} else {
|
|
639
|
+
request.continue();
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
await page.goto(`https://${authorName}.booth.pm/items`, {
|
|
644
|
+
waitUntil: 'networkidle0',
|
|
645
|
+
timeout: config.loadTimeout || import_koishi.Time.second * 10
|
|
646
|
+
});
|
|
647
|
+
await page.waitForSelector('.item-list', { timeout: 5000 });
|
|
648
|
+
|
|
649
|
+
const items = await page.evaluate((limit) => {
|
|
650
|
+
const itemElements = Array.from(document.querySelectorAll('.js-mount-point-shop-item-card'));
|
|
651
|
+
return itemElements.slice(0, limit).map(el => {
|
|
652
|
+
try {
|
|
653
|
+
const dataItem = JSON.parse(el.getAttribute('data-item'));
|
|
654
|
+
const imageUrl = dataItem.thumbnail_image_urls?.[0] ||
|
|
655
|
+
dataItem.images?.[0]?.original ||
|
|
656
|
+
el.querySelector('.swap-image img')?.src ||
|
|
657
|
+
'https://s2.booth.pm/static-images/item/empty-preview.png';
|
|
658
|
+
|
|
659
|
+
let price = dataItem.price;
|
|
660
|
+
if (typeof price === 'string') {
|
|
661
|
+
const priceMatch = price.match(/[\d,]+/);
|
|
662
|
+
if (priceMatch) {
|
|
663
|
+
price = parseInt(priceMatch[0].replace(/,/g, '')) || 0;
|
|
664
|
+
} else {
|
|
665
|
+
price = 0;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return {
|
|
670
|
+
id: dataItem.id,
|
|
671
|
+
title: dataItem.name,
|
|
672
|
+
price: price,
|
|
673
|
+
image_url: imageUrl,
|
|
674
|
+
author: dataItem.shop?.name,
|
|
675
|
+
author_thumbnail_url: dataItem.shop?.thumbnail_url
|
|
676
|
+
};
|
|
677
|
+
} catch (e) {
|
|
678
|
+
try {
|
|
679
|
+
const titleEl = el.querySelector('.item-name a');
|
|
680
|
+
const priceEl = el.querySelector('.price');
|
|
681
|
+
const imgEl = el.querySelector('.swap-image img');
|
|
682
|
+
|
|
683
|
+
let price = 0;
|
|
684
|
+
if (priceEl) {
|
|
685
|
+
const priceText = priceEl.textContent.trim();
|
|
686
|
+
const priceMatch = priceText.match(/[\d,]+/);
|
|
687
|
+
if (priceMatch) {
|
|
688
|
+
price = parseInt(priceMatch[0].replace(/,/g, '')) || 0;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
return {
|
|
693
|
+
id: null,
|
|
694
|
+
title: titleEl ? titleEl.textContent.trim() : '未知商品',
|
|
695
|
+
price: price,
|
|
696
|
+
image_url: imgEl ? imgEl.src : 'https://s2.booth.pm/static-images/item/empty-preview.png',
|
|
697
|
+
author: null,
|
|
698
|
+
author_thumbnail_url: null
|
|
699
|
+
};
|
|
700
|
+
} catch (e2) {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}).filter(item => item !== null && item.image_url);
|
|
705
|
+
}, limit);
|
|
706
|
+
|
|
707
|
+
await page.close();
|
|
708
|
+
return items;
|
|
709
|
+
} catch (error) {
|
|
710
|
+
await page.close();
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
713
|
+
} catch (error) {
|
|
714
|
+
console.error('获取作者商品失败:', error);
|
|
715
|
+
return [];
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function generateAuthorShopCardHTML(authorName, items = []) {
|
|
720
|
+
return `
|
|
721
|
+
<html>
|
|
722
|
+
<head>
|
|
723
|
+
<meta charset="utf-8">
|
|
724
|
+
<style>
|
|
725
|
+
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=Montserrat:wght@600;700;800&display=swap');
|
|
726
|
+
body {
|
|
727
|
+
margin: 0;
|
|
728
|
+
padding: 0;
|
|
729
|
+
font-family: 'Noto Sans SC', sans-serif;
|
|
730
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
731
|
+
display: flex;
|
|
732
|
+
justify-content: center;
|
|
733
|
+
align-items: center;
|
|
734
|
+
min-height: 100vh;
|
|
735
|
+
}
|
|
736
|
+
.container {
|
|
737
|
+
width: 640px;
|
|
738
|
+
background: white;
|
|
739
|
+
border-radius: 20px;
|
|
740
|
+
overflow: hidden;
|
|
741
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
742
|
+
position: relative;
|
|
743
|
+
}
|
|
744
|
+
.header {
|
|
745
|
+
background: linear-gradient(90deg, #9b59b6, #3498db);
|
|
746
|
+
padding: 25px;
|
|
747
|
+
text-align: center;
|
|
748
|
+
position: relative;
|
|
749
|
+
color: white;
|
|
750
|
+
}
|
|
751
|
+
.header::before {
|
|
752
|
+
content: "";
|
|
753
|
+
position: absolute;
|
|
754
|
+
top: 0;
|
|
755
|
+
left: 0;
|
|
756
|
+
right: 0;
|
|
757
|
+
bottom: 0;
|
|
758
|
+
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><circle cx="20" cy="20" r="10" fill="rgba(255,255,255,0.1)"/><circle cx="50" cy="50" r="15" fill="rgba(255,255,255,0.1)"/><circle cx="80" cy="80" r="8" fill="rgba(255,255,255,0.1)"/></svg>');
|
|
759
|
+
background-size: 100px 100px;
|
|
760
|
+
}
|
|
761
|
+
.label {
|
|
762
|
+
background: rgba(255, 255, 255, 0.2);
|
|
763
|
+
backdrop-filter: blur(10px);
|
|
764
|
+
padding: 8px 20px;
|
|
765
|
+
border-radius: 30px;
|
|
766
|
+
font-size: 14px;
|
|
767
|
+
font-weight: 500;
|
|
768
|
+
display: inline-block;
|
|
769
|
+
margin-bottom: 15px;
|
|
770
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
771
|
+
}
|
|
772
|
+
.booth-logo {
|
|
773
|
+
font-family: 'Montserrat', sans-serif;
|
|
774
|
+
font-weight: 800;
|
|
775
|
+
font-size: 36px;
|
|
776
|
+
letter-spacing: 2px;
|
|
777
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
|
778
|
+
}
|
|
779
|
+
.author-name-card {
|
|
780
|
+
font-size: 24px;
|
|
781
|
+
font-weight: 600;
|
|
782
|
+
margin-top: 10px;
|
|
783
|
+
}
|
|
784
|
+
.content {
|
|
785
|
+
padding: 30px;
|
|
786
|
+
}
|
|
787
|
+
.shop-info {
|
|
788
|
+
text-align: center;
|
|
789
|
+
margin-bottom: 30px;
|
|
790
|
+
padding: 20px;
|
|
791
|
+
background: #f8f9fa;
|
|
792
|
+
border-radius: 12px;
|
|
793
|
+
}
|
|
794
|
+
.shop-title {
|
|
795
|
+
font-size: 24px;
|
|
796
|
+
color: #2c3e50;
|
|
797
|
+
margin-bottom: 10px;
|
|
798
|
+
font-weight: 700;
|
|
799
|
+
}
|
|
800
|
+
.shop-description {
|
|
801
|
+
color: #7f8c8d;
|
|
802
|
+
font-size: 16px;
|
|
803
|
+
}
|
|
804
|
+
.items-grid {
|
|
805
|
+
display: grid;
|
|
806
|
+
grid-template-columns: repeat(2, 1fr);
|
|
807
|
+
gap: 20px;
|
|
808
|
+
margin-top: 20px;
|
|
809
|
+
}
|
|
810
|
+
.item-card {
|
|
811
|
+
background: white;
|
|
812
|
+
border-radius: 12px;
|
|
813
|
+
overflow: hidden;
|
|
814
|
+
box-shadow: 0 4px 8px rgba(0,0,0,0.08);
|
|
815
|
+
transition: all 0.3s ease;
|
|
816
|
+
}
|
|
817
|
+
.item-card:hover {
|
|
818
|
+
transform: translateY(-5px);
|
|
819
|
+
box-shadow: 0 10px 20px rgba(0,0,0,0.15);
|
|
820
|
+
}
|
|
821
|
+
.item-image {
|
|
822
|
+
height: 150px;
|
|
823
|
+
background-size: cover;
|
|
824
|
+
background-position: center;
|
|
825
|
+
}
|
|
826
|
+
.item-info {
|
|
827
|
+
padding: 15px;
|
|
828
|
+
}
|
|
829
|
+
.item-title {
|
|
830
|
+
font-size: 14px;
|
|
831
|
+
margin-bottom: 10px;
|
|
832
|
+
color: #2c3e50;
|
|
833
|
+
height: 40px;
|
|
834
|
+
overflow: hidden;
|
|
835
|
+
}
|
|
836
|
+
.item-price {
|
|
837
|
+
font-size: 18px;
|
|
838
|
+
font-weight: 700;
|
|
839
|
+
color: #e74c3c;
|
|
840
|
+
}
|
|
841
|
+
.footer {
|
|
842
|
+
background: #2c3e50;
|
|
843
|
+
padding: 20px;
|
|
844
|
+
text-align: center;
|
|
845
|
+
color: #ecf0f1;
|
|
846
|
+
font-size: 14px;
|
|
847
|
+
}
|
|
848
|
+
.link {
|
|
849
|
+
color: #3498db;
|
|
850
|
+
text-decoration: none;
|
|
851
|
+
font-weight: 500;
|
|
852
|
+
}
|
|
853
|
+
.link:hover {
|
|
854
|
+
text-decoration: underline;
|
|
855
|
+
}
|
|
856
|
+
</style>
|
|
857
|
+
</head>
|
|
858
|
+
<body>
|
|
859
|
+
<div class="container">
|
|
860
|
+
<div class="header">
|
|
861
|
+
<div class="label">AUTHOR'S SHOP</div>
|
|
862
|
+
<div class="booth-logo">${authorName}.booth.pm</div>
|
|
863
|
+
</div>
|
|
864
|
+
|
|
865
|
+
<div class="content">
|
|
866
|
+
<div class="shop-info">
|
|
867
|
+
<div class="shop-title">${authorName} 的店铺</div>
|
|
868
|
+
<div class="shop-description">以下是该作者的部分商品</div>
|
|
869
|
+
</div>
|
|
870
|
+
|
|
871
|
+
${items.length > 0 ? `
|
|
872
|
+
<div class="items-grid">
|
|
873
|
+
${items.map(item => `
|
|
874
|
+
<div class="item-card">
|
|
875
|
+
<div class="item-image" style="background-image:url('${item.image_url}')"></div>
|
|
876
|
+
<div class="item-info">
|
|
877
|
+
<div class="item-title">${item.title.slice(0, 25)}${item.title.length > 25 ? '...' : ''}</div>
|
|
878
|
+
<div class="item-price">¥${item.price.toLocaleString()}</div>
|
|
879
|
+
</div>
|
|
880
|
+
</div>
|
|
881
|
+
`).join('')}
|
|
882
|
+
</div>
|
|
883
|
+
` : `
|
|
884
|
+
<div style="text-align: center; padding: 40px; color: #7f8c8d;">
|
|
885
|
+
<h3>暂无商品信息</h3>
|
|
886
|
+
<p>该作者店铺暂无商品或获取商品信息失败</p>
|
|
887
|
+
</div>
|
|
888
|
+
`}
|
|
889
|
+
</div>
|
|
890
|
+
|
|
891
|
+
<div class="footer">
|
|
892
|
+
由VRCBBS提供 | BOOTH链接:
|
|
893
|
+
<a href="https://${authorName}.booth.pm/items/"
|
|
894
|
+
class="link">
|
|
895
|
+
https://${authorName}.booth.pm/items/
|
|
896
|
+
</a>
|
|
897
|
+
</div>
|
|
898
|
+
</div>
|
|
899
|
+
</body>
|
|
900
|
+
</html>`;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
async function captureAuthorShopCard(ctx, authorName) {
|
|
904
|
+
const logger = ctx.logger("booth-get");
|
|
905
|
+
const items = await fetchAuthorItems(ctx, authorName);
|
|
906
|
+
|
|
907
|
+
const html = generateAuthorShopCardHTML(authorName, items);
|
|
908
|
+
|
|
909
|
+
const page = await ctx.puppeteer.page();
|
|
910
|
+
try {
|
|
911
|
+
await page.setRequestInterception(true);
|
|
912
|
+
page.on('request', (request) => request.continue());
|
|
913
|
+
|
|
914
|
+
await page.setContent(html, {
|
|
915
|
+
waitUntil: 'domcontentloaded',
|
|
916
|
+
timeout: config.loadTimeout || import_koishi.Time.second * 10
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
920
|
+
|
|
921
|
+
await page.setViewport({ width: 640, height: 1200 });
|
|
922
|
+
const container = await page.$('.container');
|
|
923
|
+
return await container.screenshot({
|
|
924
|
+
type: 'png',
|
|
925
|
+
encoding: 'binary',
|
|
926
|
+
captureBeyondViewport: false
|
|
927
|
+
});
|
|
928
|
+
} catch (error) {
|
|
929
|
+
logger.error('生成失败:', error);
|
|
930
|
+
return null;
|
|
931
|
+
} finally {
|
|
932
|
+
await page.close();
|
|
933
|
+
}
|
|
934
|
+
}
|
|
583
935
|
ctx.middleware(async (session, next) => {
|
|
584
936
|
const boothUrlRegex = /https:\/\/booth.pm\/[\w-]+\/items\/(\d+)/;
|
|
585
937
|
const boothAuthorUrlRegex = /https:\/\/([\w-]+)\.booth\.pm\/items(?:\/(\d+))?/;
|
|
@@ -589,7 +941,11 @@ function apply(ctx, config) {
|
|
|
589
941
|
if (match) {
|
|
590
942
|
const itemId = match[1];
|
|
591
943
|
try {
|
|
592
|
-
const buffer = await captureCard(ctx, itemId);
|
|
944
|
+
const buffer = await captureCard(ctx, itemId, config);
|
|
945
|
+
if (buffer === "R18_CONTENT") {
|
|
946
|
+
await session.send("该商品可能包含R18内容,已跳过");
|
|
947
|
+
return "";
|
|
948
|
+
}
|
|
593
949
|
if (buffer) {
|
|
594
950
|
await session.send(import_koishi.h.image(buffer, "image/png"));
|
|
595
951
|
return "";
|
|
@@ -606,7 +962,11 @@ function apply(ctx, config) {
|
|
|
606
962
|
|
|
607
963
|
try {
|
|
608
964
|
if (itemId) {
|
|
609
|
-
const buffer = await captureCard(ctx, itemId);
|
|
965
|
+
const buffer = await captureCard(ctx, itemId, config);
|
|
966
|
+
if (buffer === "R18_CONTENT") {
|
|
967
|
+
await session.send("该商品可能包含R18内容,已跳过");
|
|
968
|
+
return "";
|
|
969
|
+
}
|
|
610
970
|
if (buffer) {
|
|
611
971
|
await session.send(import_koishi.h.image(buffer, "image/png"));
|
|
612
972
|
return "";
|
|
@@ -614,7 +974,13 @@ function apply(ctx, config) {
|
|
|
614
974
|
return "商品解析失败";
|
|
615
975
|
}
|
|
616
976
|
} else {
|
|
617
|
-
|
|
977
|
+
const buffer = await captureAuthorShopCard(ctx, authorName);
|
|
978
|
+
if (buffer) {
|
|
979
|
+
await session.send(import_koishi.h.image(buffer, "image/png"));
|
|
980
|
+
return "";
|
|
981
|
+
} else {
|
|
982
|
+
return "作者店铺卡片生成失败";
|
|
983
|
+
}
|
|
618
984
|
}
|
|
619
985
|
} catch (error) {
|
|
620
986
|
logger.warn("作者链接解析失败:", error);
|
|
@@ -623,6 +989,62 @@ function apply(ctx, config) {
|
|
|
623
989
|
}
|
|
624
990
|
return next();
|
|
625
991
|
});
|
|
992
|
+
|
|
993
|
+
ctx.command("摊位作者 <authorName:text>")
|
|
994
|
+
.action(async ({ session }, authorName) => {
|
|
995
|
+
if (!authorName) return "请输入作者名称";
|
|
996
|
+
|
|
997
|
+
try {
|
|
998
|
+
const searchUrl = `https://booth.pm/zh-cn/search/${encodeURIComponent(authorName)}?in_stock=true`;
|
|
999
|
+
const page = await ctx.puppeteer.page();
|
|
1000
|
+
|
|
1001
|
+
await page.setRequestInterception(true);
|
|
1002
|
+
page.on('request', (request) => {
|
|
1003
|
+
const resourceType = request.resourceType();
|
|
1004
|
+
if (['image', 'stylesheet', 'font'].includes(resourceType)) {
|
|
1005
|
+
request.abort();
|
|
1006
|
+
} else {
|
|
1007
|
+
request.continue();
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
try {
|
|
1012
|
+
await page.goto(searchUrl, { waitUntil: 'networkidle0', timeout: config.loadTimeout || import_koishi.Time.second * 10 });
|
|
1013
|
+
|
|
1014
|
+
const content = await page.content();
|
|
1015
|
+
|
|
1016
|
+
const brandRegex = /data-product-brand="([^"]+)"/g;
|
|
1017
|
+
const brands = new Set();
|
|
1018
|
+
let brandMatch;
|
|
1019
|
+
|
|
1020
|
+
while ((brandMatch = brandRegex.exec(content)) !== null) {
|
|
1021
|
+
brands.add(brandMatch[1]);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const brandArray = Array.from(brands);
|
|
1025
|
+
|
|
1026
|
+
if (brandArray.length > 0) {
|
|
1027
|
+
const matchedAuthor = brandArray[0];
|
|
1028
|
+
const buffer = await captureAuthorShopCard(ctx, matchedAuthor);
|
|
1029
|
+
if (buffer) {
|
|
1030
|
+
return import_koishi.h.image(buffer, "image/png");
|
|
1031
|
+
} else {
|
|
1032
|
+
return "作者店铺卡片生成失败";
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
return `未找到作者 "${authorName}" 的店铺`;
|
|
1036
|
+
}
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
logger.error('搜索作者失败:', error);
|
|
1039
|
+
return "搜索作者失败";
|
|
1040
|
+
} finally {
|
|
1041
|
+
await page.close();
|
|
1042
|
+
}
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
logger.error('处理作者搜索失败:', error);
|
|
1045
|
+
return "处理作者搜索失败";
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
626
1048
|
}
|
|
627
1049
|
|
|
628
1050
|
__name(apply, "apply");
|