koishi-plugin-booth-get 5.2.1 → 5.2.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.
- package/README.md +8 -1
- package/lib/index.js +33 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
简单获取booth.pm页面的插件
|
|
1
|
+
简单获取booth.pm页面的插件
|
|
2
|
+
适用用于“vrchat”“MMD模型”“周边”“游戏”“Live2D”“视频”
|
|
3
|
+
在使用vrchat的搜索时由于booth.pm的特殊性《指令摊:位搜索》会以最新发布商品为第一检索
|
|
4
|
+
默认情况下,插件会自动获取最新发布商品,如需获取其他商品,请自行修改插件代码
|
|
5
|
+
============================================================
|
|
6
|
+
后续更新会以卡片样式web版进行更新
|
|
7
|
+
优化更好且简介的样式面板
|
|
8
|
+
============================================================
|
package/lib/index.js
CHANGED
|
@@ -31,10 +31,9 @@ var QRCode = require("qrcode");
|
|
|
31
31
|
var name = "booth-get";
|
|
32
32
|
var inject = ["puppeteer"];
|
|
33
33
|
var Config = import_koishi.Schema.object({
|
|
34
|
-
loadTimeout: import_koishi.Schema.natural().role("ms").description("加载页面的最长时间").default(import_koishi.Time.second *
|
|
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: import_koishi.Schema.string().description("代理服务器地址").default(""),
|
|
37
|
-
Text: import_koishi.Schema.title("摊位卡片生成器").description("生成BOOTH商品的摊位卡片,由VRCBBS提供"),
|
|
36
|
+
proxyServer: import_koishi.Schema.string().description("代理服务器地址").default("61.216.156.222:60808"),
|
|
38
37
|
|
|
39
38
|
}).description("booth-get");
|
|
40
39
|
|
|
@@ -348,7 +347,7 @@ async function getBoothItem(id) {
|
|
|
348
347
|
fetch(`https://booth.pm/zh-cn/items/${id}.json`),
|
|
349
348
|
fetch(`https://accounts.booth.pm/wish_lists.json?item_ids%5B%5D=${id}`)
|
|
350
349
|
]);
|
|
351
|
-
|
|
350
|
+
|
|
352
351
|
const itemData = await itemRes.json();
|
|
353
352
|
const wishData = await wishRes.json();
|
|
354
353
|
|
|
@@ -361,7 +360,7 @@ async function getBoothItem(id) {
|
|
|
361
360
|
category: itemData.category?.name,
|
|
362
361
|
parent_category: itemData.category?.parent?.name,
|
|
363
362
|
author: itemData.shop?.name,
|
|
364
|
-
author_thumbnail_url: itemData.shop?.
|
|
363
|
+
author_thumbnail_url: itemData.shop?.thumbnail_url,
|
|
365
364
|
likes: wishData.wishlists_counts[id] || 0,
|
|
366
365
|
tags: itemData.tags
|
|
367
366
|
};
|
|
@@ -394,19 +393,21 @@ async function captureCard(ctx, id) {
|
|
|
394
393
|
if (!item) return null;
|
|
395
394
|
|
|
396
395
|
const relatedItems = await fetchRelatedItems(item.author);
|
|
397
|
-
|
|
396
|
+
|
|
398
397
|
const html = generateCardHTML(item, relatedItems);
|
|
399
398
|
|
|
400
399
|
const page = await ctx.puppeteer.page();
|
|
401
400
|
try {
|
|
402
401
|
await page.setRequestInterception(true);
|
|
403
402
|
page.on('request', (request) => request.continue());
|
|
404
|
-
|
|
405
|
-
await page.setContent(html, {
|
|
406
|
-
waitUntil: '
|
|
407
|
-
timeout: ctx.config.loadTimeout
|
|
403
|
+
|
|
404
|
+
await page.setContent(html, {
|
|
405
|
+
waitUntil: 'domcontentloaded',
|
|
406
|
+
timeout: ctx.config.loadTimeout || import_koishi.Time.second * 10
|
|
408
407
|
});
|
|
409
|
-
|
|
408
|
+
|
|
409
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
410
|
+
|
|
410
411
|
await page.setViewport({ width: 640, height: 1200 });
|
|
411
412
|
const container = await page.$('.container');
|
|
412
413
|
return await container.screenshot({
|
|
@@ -437,52 +438,30 @@ function apply(ctx, config) {
|
|
|
437
438
|
}
|
|
438
439
|
});
|
|
439
440
|
|
|
440
|
-
|
|
441
|
+
ctx.command("摊位名称 <query>")
|
|
441
442
|
.action(async ({ session }, query) => {
|
|
442
443
|
if (!query) return "请输入商品名称";
|
|
443
|
-
|
|
444
|
-
const
|
|
444
|
+
|
|
445
|
+
const tags = ['Vrchat'];
|
|
446
|
+
const tagsParams = tags.map(tag => `tags[]=${encodeURIComponent(tag)}`).join('&');
|
|
447
|
+
const searchUrl = `https://booth.pm/zh-cn/search/${encodeURIComponent(query)}?${tagsParams}&min_price=0&in_stock=true`;
|
|
448
|
+
|
|
445
449
|
const page = await ctx.puppeteer.page();
|
|
446
|
-
|
|
450
|
+
|
|
447
451
|
try {
|
|
448
|
-
await page.goto(searchUrl, { waitUntil: 'networkidle0', timeout:
|
|
452
|
+
await page.goto(searchUrl, { waitUntil: 'networkidle0', timeout: ctx.config.loadTimeout || import_koishi.Time.second * 10 });
|
|
449
453
|
const content = await page.content();
|
|
450
|
-
|
|
451
|
-
const itemRegex = /item-card__wrap"
|
|
454
|
+
|
|
455
|
+
const itemRegex = /item-card__wrap"[\s\S]*?id="item_(\d+)"/g;
|
|
452
456
|
const matches = [...content.matchAll(itemRegex)];
|
|
453
|
-
|
|
457
|
+
|
|
454
458
|
if (!matches.length) return "没有找到相关商品";
|
|
455
|
-
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}));
|
|
460
|
-
|
|
461
|
-
const exactMatches = items.filter(item => item.name === query);
|
|
462
|
-
if (exactMatches.length === 1) {
|
|
463
|
-
|
|
464
|
-
const buffer = await captureCard(ctx, exactMatches[0].id);
|
|
465
|
-
return buffer ? import_koishi.h.image(buffer, "image/png") : "卡片生成失败";
|
|
466
|
-
} else if (exactMatches.length > 1) {
|
|
467
|
-
|
|
468
|
-
const buffer = await captureCard(ctx, exactMatches[0].id);
|
|
469
|
-
return buffer ? import_koishi.h.image(buffer, "image/png") : "卡片生成失败";
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const queryLower = query.toLowerCase();
|
|
473
|
-
const scoredItems = items.map(item => ({
|
|
474
|
-
...item,
|
|
475
|
-
score: getSimilarity(queryLower, item.name.toLowerCase())
|
|
476
|
-
})).sort((a, b) => b.score - a.score);
|
|
477
|
-
|
|
478
|
-
const bestMatch = scoredItems[0];
|
|
479
|
-
if (bestMatch.score < 0.3) {
|
|
480
|
-
return `没有找到完全匹配的商品,最接近的是:${bestMatch.name}`;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const buffer = await captureCard(ctx, bestMatch.id);
|
|
459
|
+
|
|
460
|
+
const itemId = matches[0][1];
|
|
461
|
+
|
|
462
|
+
const buffer = await captureCard(ctx, itemId);
|
|
484
463
|
return buffer ? import_koishi.h.image(buffer, "image/png") : "卡片生成失败";
|
|
485
|
-
|
|
464
|
+
|
|
486
465
|
} catch (error) {
|
|
487
466
|
logger.error("搜索失败:", error);
|
|
488
467
|
return "搜索失败,请检查商品名称或稍后再试";
|
|
@@ -490,28 +469,28 @@ function apply(ctx, config) {
|
|
|
490
469
|
await page.close();
|
|
491
470
|
}
|
|
492
471
|
});
|
|
493
|
-
|
|
472
|
+
|
|
494
473
|
function getSimilarity(a, b) {
|
|
495
474
|
if (a === b) return 1;
|
|
496
475
|
if (a.length < 2 || b.length < 2) return 0;
|
|
497
|
-
|
|
476
|
+
|
|
498
477
|
const bigramsA = new Set();
|
|
499
478
|
for (let i = 0; i < a.length - 1; i++) {
|
|
500
479
|
bigramsA.add(a.substring(i, i + 2));
|
|
501
480
|
}
|
|
502
|
-
|
|
481
|
+
|
|
503
482
|
let matches = 0;
|
|
504
483
|
for (let i = 0; i < b.length - 1; i++) {
|
|
505
484
|
if (bigramsA.has(b.substring(i, i + 2))) matches++;
|
|
506
485
|
}
|
|
507
|
-
|
|
486
|
+
|
|
508
487
|
return (2 * matches) / (a.length + b.length - 2);
|
|
509
488
|
}
|
|
510
489
|
|
|
511
490
|
ctx.middleware(async (session, next) => {
|
|
512
491
|
const boothUrlRegex = /https:\/\/booth.pm\/[\w-]+\/items\/(\d+)/;
|
|
513
492
|
const match = session.content.match(boothUrlRegex);
|
|
514
|
-
|
|
493
|
+
|
|
515
494
|
if (match) {
|
|
516
495
|
try {
|
|
517
496
|
const buffer = await captureCard(ctx, match[1]);
|