koishi-plugin-booth-get 6.0.3 → 6.0.4
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/card-generator.js +506 -181
- package/lib/card-preview.html +526 -0
- package/lib/command-handler.js +355 -48
- package/lib/discount-tracker.js +181 -409
- package/lib/index.js +33 -31
- package/package.json +1 -1
package/lib/card-generator.js
CHANGED
|
@@ -11,7 +11,386 @@ class CardGenerator {
|
|
|
11
11
|
this.config = config;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
generateCardHTML(item, relatedItems = []) {
|
|
14
|
+
generateCardHTML(item, relatedItems = [], customConfig = {}) {
|
|
15
|
+
const cardWidth = customConfig.cardWidth || 900;
|
|
16
|
+
const leftColumnWidth = customConfig.leftColumnWidth || 280;
|
|
17
|
+
const topGradientColor1 = customConfig.topGradientColor1 || '#e8bdf6';
|
|
18
|
+
const topGradientColor2 = customConfig.topGradientColor2 || '#fceabb';
|
|
19
|
+
const footerGradientColor1 = customConfig.footerGradientColor1 || '#fceabb';
|
|
20
|
+
const footerGradientColor2 = customConfig.footerGradientColor2 || '#e8bdf6';
|
|
21
|
+
const priceTagBgColor = customConfig.priceTagBgColor || '#ff66b2';
|
|
22
|
+
const infoBubbleBgColor = customConfig.infoBubbleBgColor || '#aafcf5';
|
|
23
|
+
const textColor = customConfig.textColor || '#d63384';
|
|
24
|
+
const shopBannerBgColor = customConfig.shopBannerBgColor || '#fcfcd0';
|
|
25
|
+
|
|
26
|
+
return `
|
|
27
|
+
<!DOCTYPE html>
|
|
28
|
+
<html lang="zh-CN">
|
|
29
|
+
<head>
|
|
30
|
+
<meta charset="UTF-8">
|
|
31
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
32
|
+
<title>BOOTH Card Replica</title>
|
|
33
|
+
<style>
|
|
34
|
+
* {
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
margin: 0;
|
|
37
|
+
padding: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
body {
|
|
41
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
42
|
+
background-color: #f0f0f0;
|
|
43
|
+
display: flex;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
align-items: center;
|
|
46
|
+
min-height: 100vh;
|
|
47
|
+
padding: 20px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.card-container {
|
|
51
|
+
width: ${cardWidth}px;
|
|
52
|
+
max-width: 100%;
|
|
53
|
+
background-color: #fff;
|
|
54
|
+
border-radius: 30px;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
|
57
|
+
position: relative;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.top-gradient-bg {
|
|
61
|
+
height: 60px;
|
|
62
|
+
background: linear-gradient(to right, ${topGradientColor1}, ${topGradientColor2});
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 0;
|
|
65
|
+
left: 0;
|
|
66
|
+
right: 0;
|
|
67
|
+
z-index: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.header-logo-area {
|
|
71
|
+
position: absolute;
|
|
72
|
+
top: 20px;
|
|
73
|
+
right: 30px;
|
|
74
|
+
z-index: 10;
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: 8px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.booth-text {
|
|
81
|
+
font-weight: bold;
|
|
82
|
+
font-size: 18px;
|
|
83
|
+
color: #333;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.booth-icon {
|
|
87
|
+
width: 30px;
|
|
88
|
+
height: 30px;
|
|
89
|
+
background-color: #ff4d4d;
|
|
90
|
+
border-radius: 4px;
|
|
91
|
+
display: flex;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
align-items: center;
|
|
94
|
+
color: white;
|
|
95
|
+
font-size: 12px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.content-wrapper {
|
|
99
|
+
display: flex;
|
|
100
|
+
padding: 20px 30px 40px 30px;
|
|
101
|
+
position: relative;
|
|
102
|
+
z-index: 5;
|
|
103
|
+
margin-top: 40px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.left-column {
|
|
107
|
+
width: ${leftColumnWidth}px;
|
|
108
|
+
flex-shrink: 0;
|
|
109
|
+
margin-right: 30px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.main-image {
|
|
113
|
+
width: 100%;
|
|
114
|
+
aspect-ratio: 1/1;
|
|
115
|
+
background-color: #ddd;
|
|
116
|
+
border-radius: 8px;
|
|
117
|
+
object-fit: cover;
|
|
118
|
+
margin-bottom: 10px;
|
|
119
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.price-row {
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
margin-bottom: 10px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.price-tag {
|
|
129
|
+
background-color: ${priceTagBgColor};
|
|
130
|
+
color: white;
|
|
131
|
+
font-weight: bold;
|
|
132
|
+
font-size: 20px;
|
|
133
|
+
padding: 5px 10px;
|
|
134
|
+
border-radius: 4px;
|
|
135
|
+
margin-right: 15px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.like-count {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
color: #666;
|
|
142
|
+
font-weight: bold;
|
|
143
|
+
font-size: 16px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.heart-icon {
|
|
147
|
+
color: #333;
|
|
148
|
+
margin-right: 5px;
|
|
149
|
+
font-size: 18px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.item-title {
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
color: #333;
|
|
155
|
+
line-height: 1.4;
|
|
156
|
+
margin-bottom: 5px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.item-subtitle {
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
color: #666;
|
|
162
|
+
margin-bottom: 20px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.other-works-title {
|
|
166
|
+
font-size: 14px;
|
|
167
|
+
font-weight: bold;
|
|
168
|
+
color: #333;
|
|
169
|
+
margin-bottom: 10px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.thumbnails {
|
|
173
|
+
display: flex;
|
|
174
|
+
gap: 10px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.thumb-img {
|
|
178
|
+
width: 70px;
|
|
179
|
+
height: 70px;
|
|
180
|
+
background-color: #eee;
|
|
181
|
+
border-radius: 6px;
|
|
182
|
+
object-fit: cover;
|
|
183
|
+
border: 1px solid #eee;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.right-column {
|
|
187
|
+
flex-grow: 1;
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.shop-banner {
|
|
193
|
+
width: 100%;
|
|
194
|
+
height: 80px;
|
|
195
|
+
background-color: ${shopBannerBgColor};
|
|
196
|
+
border-radius: 8px;
|
|
197
|
+
margin-bottom: 20px;
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: space-between;
|
|
201
|
+
padding: 0 20px;
|
|
202
|
+
position: relative;
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
border: 1px solid #eee;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.banner-text {
|
|
208
|
+
font-family: 'Courier New', Courier, monospace;
|
|
209
|
+
font-weight: bold;
|
|
210
|
+
font-size: 24px;
|
|
211
|
+
color: #fff;
|
|
212
|
+
text-shadow: 1px 1px 0 #aaa;
|
|
213
|
+
letter-spacing: 1px;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.banner-char {
|
|
217
|
+
height: 90%;
|
|
218
|
+
position: absolute;
|
|
219
|
+
right: 0;
|
|
220
|
+
bottom: 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.author-info {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
margin-bottom: 20px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.author-avatar {
|
|
230
|
+
width: 40px;
|
|
231
|
+
height: 40px;
|
|
232
|
+
border-radius: 50%;
|
|
233
|
+
background-color: #ddd;
|
|
234
|
+
margin-right: 10px;
|
|
235
|
+
object-fit: cover;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.author-name {
|
|
239
|
+
color: ${textColor};
|
|
240
|
+
font-weight: bold;
|
|
241
|
+
font-size: 14px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.info-bubble {
|
|
245
|
+
background-color: ${infoBubbleBgColor};
|
|
246
|
+
border-radius: 15px;
|
|
247
|
+
padding: 25px;
|
|
248
|
+
color: ${textColor};
|
|
249
|
+
font-size: 13px;
|
|
250
|
+
line-height: 1.6;
|
|
251
|
+
position: relative;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.bubble-header {
|
|
255
|
+
font-weight: bold;
|
|
256
|
+
margin-bottom: 10px;
|
|
257
|
+
display: block;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.bubble-text p {
|
|
261
|
+
margin-bottom: 10px;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.bubble-link {
|
|
265
|
+
display: block;
|
|
266
|
+
margin-top: 20px;
|
|
267
|
+
text-align: right;
|
|
268
|
+
color: ${textColor};
|
|
269
|
+
font-weight: bold;
|
|
270
|
+
text-decoration: none;
|
|
271
|
+
font-size: 14px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.footer-bar {
|
|
275
|
+
background: linear-gradient(to right, ${footerGradientColor1}, ${footerGradientColor2});
|
|
276
|
+
padding: 15px 30px;
|
|
277
|
+
display: flex;
|
|
278
|
+
justify-content: space-between;
|
|
279
|
+
align-items: center;
|
|
280
|
+
border-top: 1px solid rgba(0,0,0,0.05);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.footer-left {
|
|
284
|
+
display: flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
gap: 8px;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.footer-center {
|
|
290
|
+
font-weight: bold;
|
|
291
|
+
color: #333;
|
|
292
|
+
font-size: 14px;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@media (max-width: 768px) {
|
|
296
|
+
.content-wrapper {
|
|
297
|
+
flex-direction: column;
|
|
298
|
+
}
|
|
299
|
+
.left-column {
|
|
300
|
+
width: 100%;
|
|
301
|
+
margin-right: 0;
|
|
302
|
+
margin-bottom: 20px;
|
|
303
|
+
}
|
|
304
|
+
.shop-banner {
|
|
305
|
+
height: 60px;
|
|
306
|
+
}
|
|
307
|
+
.banner-text {
|
|
308
|
+
font-size: 18px;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
</style>
|
|
312
|
+
</head>
|
|
313
|
+
<body>
|
|
314
|
+
<div class="card-container">
|
|
315
|
+
<div class="top-gradient-bg"></div>
|
|
316
|
+
|
|
317
|
+
<div class="header-logo-area">
|
|
318
|
+
<span class="booth-text">BOOTH</span>
|
|
319
|
+
<div class="booth-icon">
|
|
320
|
+
<svg viewBox="0 0 24 24" width="20" height="20" fill="white">
|
|
321
|
+
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
|
322
|
+
</svg>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<div class="content-wrapper">
|
|
327
|
+
<div class="left-column">
|
|
328
|
+
<img src="${item.image_url}" alt="${item.title || 'Item'}" class="main-image" onerror="this.style.backgroundColor='#f0f0f0'; this.alt='图片加载失败'">
|
|
329
|
+
|
|
330
|
+
<div class="price-row">
|
|
331
|
+
<div class="price-tag">¥${item.price ? item.price.toLocaleString() : '0'}</div>
|
|
332
|
+
<div class="like-count">
|
|
333
|
+
<span class="heart-icon">♥</span> ${item.likes || 0}
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<div class="item-title">${item.title || 'Untitled Item'}</div>
|
|
338
|
+
<div class="item-subtitle">${item.category || '3D Models/3D Characters'}</div>
|
|
339
|
+
|
|
340
|
+
<div class="other-works-title">其余作品</div>
|
|
341
|
+
<div class="thumbnails">
|
|
342
|
+
${(relatedItems || []).slice(0, 3).map(work => {
|
|
343
|
+
if (work.image_url && work.image_url.trim() !== '') {
|
|
344
|
+
return `<img src="${work.image_url}" class="thumb-img" onerror="this.style.display='none'">`;
|
|
345
|
+
}
|
|
346
|
+
return '';
|
|
347
|
+
}).filter(Boolean).join('')}
|
|
348
|
+
${Array(Math.max(0, 3 - (relatedItems || []).filter(w => w.image_url && w.image_url.trim() !== '').length)).fill(0).map((_, i) => `
|
|
349
|
+
<div style="width:70px;height:70px;background:#f0f0f0;border-radius:6px;"></div>
|
|
350
|
+
`).join('')}
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
|
|
354
|
+
<div class="right-column">
|
|
355
|
+
<div class="shop-banner">
|
|
356
|
+
<div class="banner-text">${item.author || 'Unknown'}'s SHOP</div>
|
|
357
|
+
${item.author_thumbnail_url ? `<img src="${item.author_thumbnail_url}" class="banner-char" style="opacity: 0.5; height:90%; position:absolute; right:0; bottom:0;" onerror="this.style.display='none'">` : ''}
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<div class="author-info">
|
|
361
|
+
${item.author_thumbnail_url ? `<img src="${item.author_thumbnail_url}" alt="Avatar" class="author-avatar" onerror="this.style.backgroundColor='#ddd'">` : '<div class="author-avatar" style="background:#ddd;"></div>'}
|
|
362
|
+
<span class="author-name">作者:${item.author || 'Unknown'}</span>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<div class="info-bubble">
|
|
366
|
+
<span class="bubble-header">🏠 商品说明 🏠</span>
|
|
367
|
+
<div class="bubble-text">
|
|
368
|
+
<p>${(item.description || '暂无详细说明').slice(0, 200)}${(item.description || '').length > 200 ? '...' : ''}</p>
|
|
369
|
+
</div>
|
|
370
|
+
<a href="https://booth.pm/zh-cn/items/${item.id}" class="bubble-link">链接:https://booth.pm/zh-cn/items/${item.id}</a>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
<div class="footer-bar">
|
|
376
|
+
<div class="footer-left">
|
|
377
|
+
<span class="booth-text">BOOTH</span>
|
|
378
|
+
<div class="booth-icon">
|
|
379
|
+
<svg viewBox="0 0 24 24" width="20" height="20" fill="white">
|
|
380
|
+
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
|
381
|
+
</svg>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
<div class="footer-center">
|
|
385
|
+
由乃花生成 | BOOTH 提供
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</body>
|
|
390
|
+
</html>`;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
generateDiscountCardHTML(item) {
|
|
15
394
|
return `
|
|
16
395
|
<html>
|
|
17
396
|
<head>
|
|
@@ -22,7 +401,7 @@ class CardGenerator {
|
|
|
22
401
|
margin: 0;
|
|
23
402
|
padding: 0;
|
|
24
403
|
font-family: 'Noto Sans SC', sans-serif;
|
|
25
|
-
background: linear-gradient(135deg, #
|
|
404
|
+
background: linear-gradient(135deg, #ff6b6b 0%, #ffa502 100%);
|
|
26
405
|
display: flex;
|
|
27
406
|
justify-content: center;
|
|
28
407
|
align-items: center;
|
|
@@ -43,15 +422,17 @@ class CardGenerator {
|
|
|
43
422
|
position: relative;
|
|
44
423
|
color: white;
|
|
45
424
|
}
|
|
46
|
-
.
|
|
47
|
-
content: "";
|
|
425
|
+
.discount-badge {
|
|
48
426
|
position: absolute;
|
|
49
|
-
top:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
427
|
+
top: 20px;
|
|
428
|
+
right: 20px;
|
|
429
|
+
background: #e74c3c;
|
|
430
|
+
color: white;
|
|
431
|
+
padding: 10px 15px;
|
|
432
|
+
border-radius: 30px;
|
|
433
|
+
font-weight: 700;
|
|
434
|
+
font-size: 18px;
|
|
435
|
+
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
|
55
436
|
}
|
|
56
437
|
.label {
|
|
57
438
|
background: rgba(255, 255, 255, 0.2);
|
|
@@ -90,6 +471,33 @@ class CardGenerator {
|
|
|
90
471
|
font-weight: 700;
|
|
91
472
|
line-height: 1.4;
|
|
92
473
|
}
|
|
474
|
+
.price-section {
|
|
475
|
+
display: flex;
|
|
476
|
+
align-items: center;
|
|
477
|
+
justify-content: center;
|
|
478
|
+
gap: 20px;
|
|
479
|
+
margin-bottom: 30px;
|
|
480
|
+
padding: 20px;
|
|
481
|
+
background: #fff9f9;
|
|
482
|
+
border-radius: 12px;
|
|
483
|
+
border: 2px dashed #e74c3c;
|
|
484
|
+
}
|
|
485
|
+
.original-price {
|
|
486
|
+
font-size: 24px;
|
|
487
|
+
color: #7f8c8d;
|
|
488
|
+
text-decoration: line-through;
|
|
489
|
+
}
|
|
490
|
+
.current-price {
|
|
491
|
+
font-size: 32px;
|
|
492
|
+
font-weight: 700;
|
|
493
|
+
color: #e74c3c;
|
|
494
|
+
}
|
|
495
|
+
.discount-info {
|
|
496
|
+
text-align: center;
|
|
497
|
+
font-size: 18px;
|
|
498
|
+
color: #e74c3c;
|
|
499
|
+
font-weight: 600;
|
|
500
|
+
}
|
|
93
501
|
.author-section {
|
|
94
502
|
display: flex;
|
|
95
503
|
align-items: center;
|
|
@@ -117,96 +525,19 @@ class CardGenerator {
|
|
|
117
525
|
color: #2c3e50;
|
|
118
526
|
margin-bottom: 4px;
|
|
119
527
|
}
|
|
120
|
-
.
|
|
121
|
-
font-size: 14px;
|
|
122
|
-
color: #7f8c8d;
|
|
123
|
-
}
|
|
124
|
-
.price-section {
|
|
125
|
-
font-size: 32px;
|
|
126
|
-
font-weight: 700;
|
|
127
|
-
color: #e74c3c;
|
|
128
|
-
margin-bottom: 30px;
|
|
129
|
-
text-align: center;
|
|
130
|
-
background: #fff9f9;
|
|
131
|
-
padding: 15px;
|
|
132
|
-
border-radius: 12px;
|
|
133
|
-
border: 2px dashed #e74c3c;
|
|
134
|
-
}
|
|
135
|
-
.description {
|
|
136
|
-
color: #34495e;
|
|
137
|
-
line-height: 1.7;
|
|
138
|
-
padding: 20px;
|
|
139
|
-
background: #f8f9fa;
|
|
140
|
-
border-radius: 12px;
|
|
141
|
-
margin-bottom: 30px;
|
|
142
|
-
font-size: 15px;
|
|
143
|
-
}
|
|
144
|
-
.stats {
|
|
528
|
+
.tags {
|
|
145
529
|
display: flex;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
.stat-item {
|
|
151
|
-
padding: 15px;
|
|
530
|
+
flex-wrap: wrap;
|
|
531
|
+
gap: 8px;
|
|
532
|
+
margin-bottom: 25px;
|
|
152
533
|
}
|
|
153
|
-
.
|
|
154
|
-
|
|
155
|
-
font-weight: 700;
|
|
534
|
+
.tag {
|
|
535
|
+
background: #e1f0fa;
|
|
156
536
|
color: #3498db;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
font-size: 14px;
|
|
160
|
-
color: #7f8c8d;
|
|
161
|
-
margin-top: 5px;
|
|
162
|
-
}
|
|
163
|
-
.related-works {
|
|
164
|
-
margin-top: 30px;
|
|
165
|
-
border-top: 1px solid #eee;
|
|
166
|
-
padding-top: 25px;
|
|
167
|
-
}
|
|
168
|
-
.related-title {
|
|
169
|
-
font-size: 20px;
|
|
170
|
-
color: #2c3e50;
|
|
171
|
-
margin-bottom: 20px;
|
|
172
|
-
text-align: center;
|
|
173
|
-
font-weight: 600;
|
|
174
|
-
}
|
|
175
|
-
.works-grid {
|
|
176
|
-
display: grid;
|
|
177
|
-
grid-template-columns: repeat(3, 1fr);
|
|
178
|
-
gap: 15px;
|
|
179
|
-
}
|
|
180
|
-
.work-item {
|
|
181
|
-
background: white;
|
|
182
|
-
border-radius: 12px;
|
|
183
|
-
overflow: hidden;
|
|
184
|
-
box-shadow: 0 4px 8px rgba(0,0,0,0.08);
|
|
185
|
-
transition: all 0.3s ease;
|
|
186
|
-
}
|
|
187
|
-
.work-item:hover {
|
|
188
|
-
transform: translateY(-5px);
|
|
189
|
-
box-shadow: 0 10px 20px rgba(0,0,0,0.15);
|
|
190
|
-
}
|
|
191
|
-
.work-image {
|
|
192
|
-
height: 100px;
|
|
193
|
-
background-size: cover;
|
|
194
|
-
background-position: center;
|
|
195
|
-
}
|
|
196
|
-
.work-info {
|
|
197
|
-
padding: 12px;
|
|
198
|
-
}
|
|
199
|
-
.work-title {
|
|
537
|
+
padding: 6px 12px;
|
|
538
|
+
border-radius: 20px;
|
|
200
539
|
font-size: 13px;
|
|
201
|
-
|
|
202
|
-
color: #2c3e50;
|
|
203
|
-
height: 36px;
|
|
204
|
-
overflow: hidden;
|
|
205
|
-
}
|
|
206
|
-
.work-price {
|
|
207
|
-
font-size: 15px;
|
|
208
|
-
font-weight: 600;
|
|
209
|
-
color: #e74c3c;
|
|
540
|
+
font-weight: 500;
|
|
210
541
|
}
|
|
211
542
|
.footer {
|
|
212
543
|
background: #2c3e50;
|
|
@@ -220,29 +551,13 @@ class CardGenerator {
|
|
|
220
551
|
text-decoration: none;
|
|
221
552
|
font-weight: 500;
|
|
222
553
|
}
|
|
223
|
-
.link:hover {
|
|
224
|
-
text-decoration: underline;
|
|
225
|
-
}
|
|
226
|
-
.tags {
|
|
227
|
-
display: flex;
|
|
228
|
-
flex-wrap: wrap;
|
|
229
|
-
gap: 8px;
|
|
230
|
-
margin-bottom: 25px;
|
|
231
|
-
}
|
|
232
|
-
.tag {
|
|
233
|
-
background: #e1f0fa;
|
|
234
|
-
color: #3498db;
|
|
235
|
-
padding: 6px 12px;
|
|
236
|
-
border-radius: 20px;
|
|
237
|
-
font-size: 13px;
|
|
238
|
-
font-weight: 500;
|
|
239
|
-
}
|
|
240
554
|
</style>
|
|
241
555
|
</head>
|
|
242
556
|
<body>
|
|
243
557
|
<div class="container">
|
|
244
558
|
<div class="header">
|
|
245
|
-
<div class="
|
|
559
|
+
<div class="discount-badge">-${item.discount_rate}% OFF</div>
|
|
560
|
+
<div class="label">DISCOUNT ITEM</div>
|
|
246
561
|
<div class="booth-logo">BOOTH</div>
|
|
247
562
|
</div>
|
|
248
563
|
|
|
@@ -260,95 +575,105 @@ class CardGenerator {
|
|
|
260
575
|
</div>
|
|
261
576
|
</div>
|
|
262
577
|
|
|
263
|
-
<div class="price-section"
|
|
578
|
+
<div class="price-section">
|
|
579
|
+
<div class="original-price">¥${item.original_price.toLocaleString()}</div>
|
|
580
|
+
<div class="current-price">¥${item.price.toLocaleString()}</div>
|
|
581
|
+
</div>
|
|
264
582
|
|
|
265
|
-
<div class="
|
|
266
|
-
|
|
267
|
-
<div class="stat-value">${item.likes || 0}</div>
|
|
268
|
-
<div class="stat-label">收藏数</div>
|
|
269
|
-
</div>
|
|
270
|
-
<div class="stat-item">
|
|
271
|
-
<div class="stat-value">${item.category || '未分类'}</div>
|
|
272
|
-
<div class="stat-label">分类</div>
|
|
273
|
-
</div>
|
|
274
|
-
<div class="stat-item">
|
|
275
|
-
<div class="stat-value">#${item.id}</div>
|
|
276
|
-
<div class="stat-label">商品ID</div>
|
|
277
|
-
</div>
|
|
583
|
+
<div class="discount-info">
|
|
584
|
+
节省 ¥${(item.original_price - item.price).toLocaleString()} (${item.discount_rate}% 折扣)
|
|
278
585
|
</div>
|
|
279
586
|
|
|
280
587
|
<div class="tags">
|
|
281
588
|
${(item.tags || []).slice(0, 5).map(tag => `<div class="tag">${tag.name}</div>`).join('')}
|
|
282
589
|
</div>
|
|
283
|
-
|
|
284
|
-
<div class="description">
|
|
285
|
-
<p>${(item.description || "").slice(0, 300)}${(item.description||"").length > 300 ? '...' : ''}</p>
|
|
286
|
-
</div>
|
|
287
|
-
|
|
288
|
-
${relatedItems && relatedItems.length > 0 ? `
|
|
289
|
-
<div class="related-works">
|
|
290
|
-
<h3 class="related-title">同じ作者の作品</h3>
|
|
291
|
-
<div class="works-grid">
|
|
292
|
-
${relatedItems.map(work => `
|
|
293
|
-
<div class="work-item">
|
|
294
|
-
<div class="work-image" style="background-image:url('${work.image_url}')"></div>
|
|
295
|
-
<div class="work-info">
|
|
296
|
-
<div class="work-title">${work.title.slice(0, 20)}${work.title.length > 20 ? '...' : ''}</div>
|
|
297
|
-
<div class="work-price">¥${work.price?.toLocaleString?.() ?? work.price}</div>
|
|
298
|
-
</div>
|
|
299
|
-
</div>
|
|
300
|
-
`).join('')}
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
` : ''}
|
|
304
590
|
</div>
|
|
305
591
|
|
|
306
592
|
<div class="footer">
|
|
307
|
-
由VRCBBS提供 |
|
|
308
|
-
<a href="
|
|
309
|
-
class="link">
|
|
310
|
-
https://booth.pm/zh-cn/items/${item.id}
|
|
311
|
-
</a>
|
|
593
|
+
由VRCBBS提供 | 商品链接:
|
|
594
|
+
<a href="${item.url}" class="link">${item.url}</a>
|
|
312
595
|
</div>
|
|
313
596
|
</div>
|
|
314
597
|
</body>
|
|
315
598
|
</html>`;
|
|
316
599
|
}
|
|
317
600
|
|
|
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 折扣商品推送 🔥
|
|
323
|
-
|
|
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}
|
|
331
|
-
|
|
332
|
-
📝 商品简介:
|
|
333
|
-
${(item.description || "").slice(0, 200)}${(item.description||"").length > 200 ? '...' : ''}
|
|
334
|
-
|
|
335
|
-
🔗 商品链接: https://booth.pm/zh-cn/items/${item.id}`;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
601
|
async captureCardHTML(html, config) {
|
|
339
602
|
const page = await this.ctx.puppeteer.page();
|
|
340
603
|
try {
|
|
341
|
-
page.
|
|
342
|
-
|
|
604
|
+
await page.setRequestInterception(true);
|
|
605
|
+
page.on('request', (request) => request.continue());
|
|
606
|
+
|
|
343
607
|
await page.setContent(html, {
|
|
344
|
-
waitUntil: '
|
|
345
|
-
timeout: config.loadTimeout
|
|
608
|
+
waitUntil: 'domcontentloaded',
|
|
609
|
+
timeout: config.loadTimeout
|
|
346
610
|
});
|
|
347
611
|
|
|
348
|
-
|
|
612
|
+
// 获取卡片实际宽度
|
|
613
|
+
const cardWidth = config.cardWidth || 900;
|
|
614
|
+
await page.setViewport({ width: cardWidth + 40, height: 1600 });
|
|
349
615
|
|
|
350
|
-
|
|
351
|
-
const
|
|
616
|
+
// 等待所有图片加载完成(包括主图、头像、缩略图等)
|
|
617
|
+
const imageStatus = await page.evaluate(() => {
|
|
618
|
+
return new Promise((resolve) => {
|
|
619
|
+
const images = Array.from(document.querySelectorAll('img'));
|
|
620
|
+
if (images.length === 0) {
|
|
621
|
+
resolve({ total: 0, loaded: 0, failed: 0, urls: [] });
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
let loadedCount = 0;
|
|
626
|
+
let failedCount = 0;
|
|
627
|
+
const totalImages = images.length;
|
|
628
|
+
const results = [];
|
|
629
|
+
|
|
630
|
+
images.forEach((img, index) => {
|
|
631
|
+
const url = img.src;
|
|
632
|
+
results.push({ url, status: 'pending' });
|
|
633
|
+
|
|
634
|
+
if (img.complete) {
|
|
635
|
+
if (img.naturalHeight !== 0) {
|
|
636
|
+
loadedCount++;
|
|
637
|
+
results[index].status = 'loaded';
|
|
638
|
+
} else {
|
|
639
|
+
failedCount++;
|
|
640
|
+
results[index].status = 'failed';
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (loadedCount + failedCount === totalImages) {
|
|
644
|
+
resolve({ total: totalImages, loaded: loadedCount, failed: failedCount, urls: results });
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
img.onload = () => {
|
|
648
|
+
loadedCount++;
|
|
649
|
+
results[index].status = 'loaded';
|
|
650
|
+
if (loadedCount + failedCount === totalImages) {
|
|
651
|
+
resolve({ total: totalImages, loaded: loadedCount, failed: failedCount, urls: results });
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
img.onerror = () => {
|
|
655
|
+
failedCount++;
|
|
656
|
+
results[index].status = 'failed';
|
|
657
|
+
if (loadedCount + failedCount === totalImages) {
|
|
658
|
+
resolve({ total: totalImages, loaded: loadedCount, failed: failedCount, urls: results });
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
// 设置超时,最多等待8秒
|
|
665
|
+
setTimeout(() => {
|
|
666
|
+
resolve({ total: totalImages, loaded: loadedCount, failed: failedCount, urls: results });
|
|
667
|
+
}, 8000);
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
this.ctx.logger("booth-get").info(`图片加载状态:`, imageStatus);
|
|
672
|
+
|
|
673
|
+
// 额外等待一小段时间确保渲染完成
|
|
674
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
675
|
+
|
|
676
|
+
const container = await page.$('.card-container') || await page.$('body');
|
|
352
677
|
return await container.screenshot({
|
|
353
678
|
type: 'png',
|
|
354
679
|
encoding: 'binary',
|