koishi-plugin-booth-get 5.2.3 → 5.2.5
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 +145 -23
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -438,33 +438,126 @@ function apply(ctx, config) {
|
|
|
438
438
|
}
|
|
439
439
|
});
|
|
440
440
|
|
|
441
|
-
ctx.command("摊位名称 <query>")
|
|
442
|
-
.
|
|
443
|
-
|
|
441
|
+
ctx.command("摊位名称 <query:text>")
|
|
442
|
+
.option('author', '-a <author> 指定作者名称')
|
|
443
|
+
.action(async ({ session, options }, query) => {
|
|
444
|
+
if (!query) return "请输入搜索关键词";
|
|
445
|
+
|
|
446
|
+
let searchQuery = query;
|
|
447
|
+
let authorFilter = options.author;
|
|
448
|
+
|
|
449
|
+
if (!authorFilter && query.includes(' ')) {
|
|
450
|
+
const parts = query.split(' ');
|
|
451
|
+
if (parts.length >= 2) {
|
|
452
|
+
authorFilter = parts.pop();
|
|
453
|
+
searchQuery = parts.join(' ');
|
|
454
|
+
}
|
|
455
|
+
}
|
|
444
456
|
|
|
445
|
-
|
|
457
|
+
let searchUrl = `https://booth.pm/zh-cn/search/${encodeURIComponent(searchQuery)}?in_stock=true`;
|
|
458
|
+
|
|
459
|
+
const tags = ['3Dモデル', 'Vrchat'];
|
|
446
460
|
const tagsParams = tags.map(tag => `tags[]=${encodeURIComponent(tag)}`).join('&');
|
|
447
|
-
|
|
461
|
+
searchUrl += `&${tagsParams}&min_price=4500`;
|
|
448
462
|
|
|
449
463
|
const page = await ctx.puppeteer.page();
|
|
450
|
-
|
|
464
|
+
|
|
465
|
+
await page.setRequestInterception(true);
|
|
466
|
+
page.on('request', (request) => {
|
|
467
|
+
const resourceType = request.resourceType();
|
|
468
|
+
if (['image', 'stylesheet', 'font'].includes(resourceType)) {
|
|
469
|
+
request.abort();
|
|
470
|
+
} else {
|
|
471
|
+
request.continue();
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
451
475
|
try {
|
|
452
|
-
|
|
476
|
+
let retries = 3;
|
|
477
|
+
while (retries > 0) {
|
|
478
|
+
try {
|
|
479
|
+
await page.goto(searchUrl, { waitUntil: 'networkidle0', timeout: ctx.config.loadTimeout || import_koishi.Time.second * 10 });
|
|
480
|
+
break;
|
|
481
|
+
} catch (error) {
|
|
482
|
+
retries--;
|
|
483
|
+
if (retries === 0) throw error;
|
|
484
|
+
logger.warn(`页面加载失败,重试中... (剩余重试次数: ${retries})`);
|
|
485
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
453
489
|
const content = await page.content();
|
|
454
|
-
|
|
455
|
-
const itemRegex = /item-card__wrap"[\s\S]*?id="item_(\d+)"
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
490
|
+
|
|
491
|
+
const itemRegex = /item-card__wrap"[\s\S]*?id="item_(\d+)"[\s\S]*?<h2[^>]*class="[^"]*item-card__title[^"]*"[^>]*>([^<]+)<\/h2>/g;
|
|
492
|
+
let matches = [];
|
|
493
|
+
let match;
|
|
494
|
+
|
|
495
|
+
while ((match = itemRegex.exec(content)) !== null) {
|
|
496
|
+
matches.push({
|
|
497
|
+
id: match[1],
|
|
498
|
+
title: match[2].trim()
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (matches.length === 0) {
|
|
503
|
+
const simpleRegex = /item-card__wrap"[\s\S]*?id="item_(\d+)"/g;
|
|
504
|
+
let simpleMatch;
|
|
505
|
+
while ((simpleMatch = simpleRegex.exec(content)) !== null) {
|
|
506
|
+
matches.push({
|
|
507
|
+
id: simpleMatch[1],
|
|
508
|
+
title: '未知商品'
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (matches.length === 0) {
|
|
514
|
+
if (content.includes('検索結果はありません') ||
|
|
515
|
+
content.includes('没有找到') ||
|
|
516
|
+
content.includes('検索条件に合致する作品は見つかりませんでした') ||
|
|
517
|
+
content.includes('該当する作品はありません')) {
|
|
518
|
+
return "没有找到相关商品";
|
|
519
|
+
}
|
|
520
|
+
return "没有找到相关商品";
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
let selectedItemId;
|
|
524
|
+
if (authorFilter) {
|
|
525
|
+
for (const item of matches) {
|
|
526
|
+
try {
|
|
527
|
+
const itemDetail = await getBoothItem(item.id);
|
|
528
|
+
if (itemDetail && itemDetail.author &&
|
|
529
|
+
itemDetail.author.toLowerCase().includes(authorFilter.toLowerCase())) {
|
|
530
|
+
selectedItemId = item.id;
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
} catch (err) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (!selectedItemId) {
|
|
539
|
+
return `找不到作者"${authorFilter}"的相关商品`;
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
selectedItemId = matches[0].id;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
try {
|
|
546
|
+
const buffer = await captureCard(ctx, selectedItemId);
|
|
547
|
+
if (!buffer) {
|
|
548
|
+
return "卡片生成失败";
|
|
549
|
+
}
|
|
550
|
+
return import_koishi.h.image(buffer, "image/png");
|
|
551
|
+
} catch (error) {
|
|
552
|
+
logger.error('卡片生成失败:', error);
|
|
553
|
+
return "卡片生成失败";
|
|
554
|
+
}
|
|
465
555
|
} catch (error) {
|
|
466
|
-
logger.error(
|
|
467
|
-
|
|
556
|
+
logger.error('搜索失败:', error);
|
|
557
|
+
if (error.message.includes('ERR_EMPTY_RESPONSE') || error.message.includes('net::ERR_CONNECTION_TIMED_OUT')) {
|
|
558
|
+
return "搜索失败,连接BOOTH网站超时,请稍后再试";
|
|
559
|
+
}
|
|
560
|
+
return "搜索失败";
|
|
468
561
|
} finally {
|
|
469
562
|
await page.close();
|
|
470
563
|
}
|
|
@@ -489,14 +582,43 @@ function apply(ctx, config) {
|
|
|
489
582
|
|
|
490
583
|
ctx.middleware(async (session, next) => {
|
|
491
584
|
const boothUrlRegex = /https:\/\/booth.pm\/[\w-]+\/items\/(\d+)/;
|
|
585
|
+
const boothAuthorUrlRegex = /https:\/\/([\w-]+)\.booth\.pm\/items(?:\/(\d+))?/;
|
|
492
586
|
const match = session.content.match(boothUrlRegex);
|
|
587
|
+
const authorMatch = session.content.match(boothAuthorUrlRegex);
|
|
493
588
|
|
|
494
589
|
if (match) {
|
|
590
|
+
const itemId = match[1];
|
|
591
|
+
try {
|
|
592
|
+
const buffer = await captureCard(ctx, itemId);
|
|
593
|
+
if (buffer) {
|
|
594
|
+
await session.send(import_koishi.h.image(buffer, "image/png"));
|
|
595
|
+
return "";
|
|
596
|
+
} else {
|
|
597
|
+
return "商品解析失败";
|
|
598
|
+
}
|
|
599
|
+
} catch (error) {
|
|
600
|
+
logger.warn("链接解析失败:", error);
|
|
601
|
+
return "商品解析失败";
|
|
602
|
+
}
|
|
603
|
+
} else if (authorMatch) {
|
|
604
|
+
const authorName = authorMatch[1];
|
|
605
|
+
const itemId = authorMatch[2];
|
|
606
|
+
|
|
495
607
|
try {
|
|
496
|
-
|
|
497
|
-
|
|
608
|
+
if (itemId) {
|
|
609
|
+
const buffer = await captureCard(ctx, itemId);
|
|
610
|
+
if (buffer) {
|
|
611
|
+
await session.send(import_koishi.h.image(buffer, "image/png"));
|
|
612
|
+
return "";
|
|
613
|
+
} else {
|
|
614
|
+
return "商品解析失败";
|
|
615
|
+
}
|
|
616
|
+
} else {
|
|
617
|
+
return `检测到作者"${authorName}"的主页链接。请使用"摊位名称 <商品名> ${authorName}"来搜索该作者的商品。`;
|
|
618
|
+
}
|
|
498
619
|
} catch (error) {
|
|
499
|
-
logger.warn(error);
|
|
620
|
+
logger.warn("作者链接解析失败:", error);
|
|
621
|
+
return "作者链接解析失败";
|
|
500
622
|
}
|
|
501
623
|
}
|
|
502
624
|
return next();
|