koishi-plugin-steam-info-check 1.0.0
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/.editorconfig +9 -0
- package/.gitattributes +7 -0
- package/LICENSE +21 -0
- package/dist/database.d.ts +19 -0
- package/dist/database.js +2 -0
- package/dist/drawer.d.ts +22 -0
- package/dist/drawer.js +477 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +265 -0
- package/dist/locales/zh-CN.json +1 -0
- package/dist/service.d.ts +42 -0
- package/dist/service.js +119 -0
- package/fonts/MiSans-Bold.ttf +0 -0
- package/fonts/MiSans-Light.ttf +0 -0
- package/fonts/MiSans-Regular.ttf +0 -0
- package/package.json +56 -0
- package/readme.md +5 -0
- package/src/database.ts +21 -0
- package/src/drawer.ts +505 -0
- package/src/index.ts +304 -0
- package/src/locales/zh-CN.yml +14 -0
- package/src/service.ts +162 -0
- package/tsconfig.json +16 -0
package/.editorconfig
ADDED
package/.gitattributes
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mux_Yang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface SteamBind {
|
|
2
|
+
id: number;
|
|
3
|
+
userId: string;
|
|
4
|
+
channelId: string;
|
|
5
|
+
steamId: string;
|
|
6
|
+
nickname?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SteamChannel {
|
|
9
|
+
id: string;
|
|
10
|
+
enable: boolean;
|
|
11
|
+
name?: string;
|
|
12
|
+
avatar?: string;
|
|
13
|
+
}
|
|
14
|
+
declare module 'koishi' {
|
|
15
|
+
interface Tables {
|
|
16
|
+
steam_bind: SteamBind;
|
|
17
|
+
steam_channel: SteamChannel;
|
|
18
|
+
}
|
|
19
|
+
}
|
package/dist/database.js
ADDED
package/dist/drawer.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Context, Service } from 'koishi';
|
|
2
|
+
import { Config } from './index';
|
|
3
|
+
import { SteamBind } from './database';
|
|
4
|
+
import { PlayerSummary, SteamProfile } from './service';
|
|
5
|
+
declare module 'koishi' {
|
|
6
|
+
interface Context {
|
|
7
|
+
puppeteer: any;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export declare class DrawService extends Service {
|
|
11
|
+
config: Config;
|
|
12
|
+
constructor(ctx: Context, config: Config);
|
|
13
|
+
private getFontCss;
|
|
14
|
+
private imageToBase64;
|
|
15
|
+
drawStartGaming(player: PlayerSummary, nickname?: string): Promise<Buffer | string>;
|
|
16
|
+
drawFriendsStatus(parentAvatar: Buffer | string, parentName: string, players: PlayerSummary[], binds: SteamBind[]): Promise<Buffer | string>;
|
|
17
|
+
drawPlayerStatus(profile: SteamProfile, steamId: string): Promise<Buffer | string>;
|
|
18
|
+
concatImages(images: (Buffer | string)[]): Promise<Buffer | string | null>;
|
|
19
|
+
getDefaultAvatar(): Promise<Buffer | string>;
|
|
20
|
+
private getPlayerStateOrder;
|
|
21
|
+
private getPersonaStateText;
|
|
22
|
+
}
|
package/dist/drawer.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DrawService = void 0;
|
|
4
|
+
const koishi_1 = require("koishi");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
class DrawService extends koishi_1.Service {
|
|
8
|
+
constructor(ctx, config) {
|
|
9
|
+
super(ctx, 'drawer', true);
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
getFontCss() {
|
|
13
|
+
// We can try to load fonts as base64 or just reference them if we assume the browser environment can see them.
|
|
14
|
+
// For simplicity and robustness, passing them as base64 in CSS @font-face is good but heavy.
|
|
15
|
+
// If we assume standard fonts or just let the browser handle it, it's easier.
|
|
16
|
+
// However, the original plugin used MiSans.
|
|
17
|
+
// Let's try to load them if possible, or fallback to sans-serif.
|
|
18
|
+
// Since we are in Node, we can read the files.
|
|
19
|
+
// Note: Puppeteer page.setContent usually runs in a context where local file access might be restricted
|
|
20
|
+
// unless we use `file://` protocol or base64. Base64 is safest.
|
|
21
|
+
let css = `
|
|
22
|
+
body { font-family: 'MiSans', sans-serif; margin: 0; padding: 0; background-color: #1e2024; color: #fff; }
|
|
23
|
+
`;
|
|
24
|
+
// Helper to load font
|
|
25
|
+
const loadFont = (name, path, weight) => {
|
|
26
|
+
try {
|
|
27
|
+
const fullPath = (0, path_1.resolve)(this.ctx.baseDir, path);
|
|
28
|
+
const buffer = (0, fs_1.readFileSync)(fullPath);
|
|
29
|
+
const base64 = buffer.toString('base64');
|
|
30
|
+
css += `
|
|
31
|
+
@font-face {
|
|
32
|
+
font-family: '${name}';
|
|
33
|
+
src: url(data:font/ttf;base64,${base64}) format('truetype');
|
|
34
|
+
font-weight: ${weight};
|
|
35
|
+
font-style: normal;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
// Font not found, ignore
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
loadFont('MiSans', this.config.fonts.regular, 'normal');
|
|
44
|
+
loadFont('MiSans', this.config.fonts.light, '300');
|
|
45
|
+
loadFont('MiSans', this.config.fonts.bold, 'bold');
|
|
46
|
+
return css;
|
|
47
|
+
}
|
|
48
|
+
imageToBase64(img) {
|
|
49
|
+
if (Buffer.isBuffer(img)) {
|
|
50
|
+
return `data:image/png;base64,${img.toString('base64')}`;
|
|
51
|
+
}
|
|
52
|
+
return img; // URL
|
|
53
|
+
}
|
|
54
|
+
async drawStartGaming(player, nickname) {
|
|
55
|
+
const avatarUrl = player.avatarfull;
|
|
56
|
+
const name = nickname || player.personaname;
|
|
57
|
+
const game = player.gameextrainfo || 'Unknown Game';
|
|
58
|
+
const html = `
|
|
59
|
+
<html>
|
|
60
|
+
<head>
|
|
61
|
+
<style>
|
|
62
|
+
${this.getFontCss()}
|
|
63
|
+
.container {
|
|
64
|
+
width: 400px;
|
|
65
|
+
height: 100px;
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
background-color: #1e2024;
|
|
69
|
+
padding: 15px;
|
|
70
|
+
box-sizing: border-box;
|
|
71
|
+
}
|
|
72
|
+
.avatar {
|
|
73
|
+
width: 66px;
|
|
74
|
+
height: 66px;
|
|
75
|
+
margin-right: 20px;
|
|
76
|
+
border-radius: 4px;
|
|
77
|
+
}
|
|
78
|
+
.info {
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
}
|
|
83
|
+
.name {
|
|
84
|
+
font-size: 19px;
|
|
85
|
+
color: #e3ffc2;
|
|
86
|
+
margin-bottom: 4px;
|
|
87
|
+
}
|
|
88
|
+
.status {
|
|
89
|
+
font-size: 17px;
|
|
90
|
+
color: #969696;
|
|
91
|
+
margin-bottom: 4px;
|
|
92
|
+
}
|
|
93
|
+
.game {
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
font-weight: bold;
|
|
96
|
+
color: #91c257;
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
99
|
+
</head>
|
|
100
|
+
<body>
|
|
101
|
+
<div class="container">
|
|
102
|
+
<img class="avatar" src="${avatarUrl}" />
|
|
103
|
+
<div class="info">
|
|
104
|
+
<div class="name">${name}</div>
|
|
105
|
+
<div class="status">正在玩</div>
|
|
106
|
+
<div class="game">${game}</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
|
111
|
+
`;
|
|
112
|
+
const page = await this.ctx.puppeteer.page();
|
|
113
|
+
await page.setContent(html);
|
|
114
|
+
const element = await page.$('.container');
|
|
115
|
+
const buffer = await element.screenshot({ type: 'png' });
|
|
116
|
+
await page.close();
|
|
117
|
+
return buffer;
|
|
118
|
+
}
|
|
119
|
+
async drawFriendsStatus(parentAvatar, parentName, players, binds) {
|
|
120
|
+
const sorted = [...players].sort((a, b) => {
|
|
121
|
+
const stateA = this.getPlayerStateOrder(a);
|
|
122
|
+
const stateB = this.getPlayerStateOrder(b);
|
|
123
|
+
return stateA - stateB;
|
|
124
|
+
});
|
|
125
|
+
const groups = [
|
|
126
|
+
{ title: '游戏中', items: sorted.filter(p => p.gameextrainfo) },
|
|
127
|
+
{ title: '在线好友', items: sorted.filter(p => !p.gameextrainfo && p.personastate !== 0) },
|
|
128
|
+
{ title: '离线', items: sorted.filter(p => p.personastate === 0) }
|
|
129
|
+
].filter(g => g.items.length > 0);
|
|
130
|
+
const parentAvatarSrc = this.imageToBase64(parentAvatar);
|
|
131
|
+
let listHtml = '';
|
|
132
|
+
for (const group of groups) {
|
|
133
|
+
listHtml += `<div class="group-title">${group.title} (${group.items.length})</div>`;
|
|
134
|
+
for (const player of group.items) {
|
|
135
|
+
const bind = binds.find(b => b.steamId === player.steamid);
|
|
136
|
+
const name = bind?.nickname || player.personaname;
|
|
137
|
+
const avatar = player.avatarmedium || player.avatar;
|
|
138
|
+
let statusText = '离线';
|
|
139
|
+
let nameColor = '#656565';
|
|
140
|
+
let statusColor = '#656565';
|
|
141
|
+
if (player.gameextrainfo) {
|
|
142
|
+
statusText = player.gameextrainfo;
|
|
143
|
+
nameColor = '#91c257';
|
|
144
|
+
statusColor = '#91c257';
|
|
145
|
+
}
|
|
146
|
+
else if (player.personastate !== 0) {
|
|
147
|
+
statusText = this.getPersonaStateText(player.personastate);
|
|
148
|
+
nameColor = '#6dcff6';
|
|
149
|
+
statusColor = '#6dcff6'; // Usually blue for online
|
|
150
|
+
}
|
|
151
|
+
listHtml += `
|
|
152
|
+
<div class="friend-item">
|
|
153
|
+
<img class="friend-avatar" src="${avatar}" />
|
|
154
|
+
<div class="friend-info">
|
|
155
|
+
<div class="friend-name" style="color: ${nameColor}">${name}</div>
|
|
156
|
+
<div class="friend-status" style="color: ${statusColor}">${statusText}</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const html = `
|
|
163
|
+
<html>
|
|
164
|
+
<head>
|
|
165
|
+
<style>
|
|
166
|
+
${this.getFontCss()}
|
|
167
|
+
body {
|
|
168
|
+
width: 400px;
|
|
169
|
+
background-color: #1e2024;
|
|
170
|
+
}
|
|
171
|
+
.header {
|
|
172
|
+
padding: 16px;
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
height: 120px;
|
|
176
|
+
box-sizing: border-box;
|
|
177
|
+
background: linear-gradient(to bottom, #2b2e34 0%, #1e2024 100%);
|
|
178
|
+
}
|
|
179
|
+
.parent-avatar {
|
|
180
|
+
width: 72px;
|
|
181
|
+
height: 72px;
|
|
182
|
+
border-radius: 4px;
|
|
183
|
+
margin-right: 16px;
|
|
184
|
+
}
|
|
185
|
+
.parent-info {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
}
|
|
189
|
+
.parent-name {
|
|
190
|
+
font-size: 20px;
|
|
191
|
+
font-weight: bold;
|
|
192
|
+
color: #6dcff6;
|
|
193
|
+
margin-bottom: 4px;
|
|
194
|
+
}
|
|
195
|
+
.parent-status {
|
|
196
|
+
font-size: 18px;
|
|
197
|
+
color: #4c91ac;
|
|
198
|
+
}
|
|
199
|
+
.search-bar {
|
|
200
|
+
height: 50px;
|
|
201
|
+
background-color: #434953;
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
padding-left: 24px;
|
|
205
|
+
color: #b7ccd5;
|
|
206
|
+
font-size: 20px;
|
|
207
|
+
}
|
|
208
|
+
.list-container {
|
|
209
|
+
padding: 16px 0;
|
|
210
|
+
}
|
|
211
|
+
.group-title {
|
|
212
|
+
color: #c5d6d4;
|
|
213
|
+
font-size: 22px;
|
|
214
|
+
margin: 10px 22px;
|
|
215
|
+
}
|
|
216
|
+
.friend-item {
|
|
217
|
+
display: flex;
|
|
218
|
+
align-items: center;
|
|
219
|
+
height: 64px;
|
|
220
|
+
padding: 0 22px;
|
|
221
|
+
}
|
|
222
|
+
.friend-item:hover {
|
|
223
|
+
background-color: #3d4450;
|
|
224
|
+
}
|
|
225
|
+
.friend-avatar {
|
|
226
|
+
width: 50px;
|
|
227
|
+
height: 50px;
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
margin-right: 16px;
|
|
230
|
+
}
|
|
231
|
+
.friend-info {
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
}
|
|
235
|
+
.friend-name {
|
|
236
|
+
font-size: 18px;
|
|
237
|
+
font-weight: bold;
|
|
238
|
+
margin-bottom: 4px;
|
|
239
|
+
}
|
|
240
|
+
.friend-status {
|
|
241
|
+
font-size: 16px;
|
|
242
|
+
}
|
|
243
|
+
</style>
|
|
244
|
+
</head>
|
|
245
|
+
<body>
|
|
246
|
+
<div class="main">
|
|
247
|
+
<div class="header">
|
|
248
|
+
<img class="parent-avatar" src="${parentAvatarSrc}" />
|
|
249
|
+
<div class="parent-info">
|
|
250
|
+
<div class="parent-name">${parentName}</div>
|
|
251
|
+
<div class="parent-status">在线</div>
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
<div class="search-bar">好友</div>
|
|
255
|
+
<div class="list-container">
|
|
256
|
+
${listHtml}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
</body>
|
|
260
|
+
</html>
|
|
261
|
+
`;
|
|
262
|
+
const page = await this.ctx.puppeteer.page();
|
|
263
|
+
await page.setContent(html);
|
|
264
|
+
// We need to capture the full height.
|
|
265
|
+
// We can select 'body' or '.main'.
|
|
266
|
+
const element = await page.$('body');
|
|
267
|
+
const buffer = await element.screenshot({ type: 'png' });
|
|
268
|
+
await page.close();
|
|
269
|
+
return buffer;
|
|
270
|
+
}
|
|
271
|
+
async drawPlayerStatus(profile, steamId) {
|
|
272
|
+
const bgSrc = this.imageToBase64(profile.background);
|
|
273
|
+
const avatarSrc = this.imageToBase64(profile.avatar);
|
|
274
|
+
let gamesHtml = '';
|
|
275
|
+
for (const game of profile.game_data) {
|
|
276
|
+
const gameImg = this.imageToBase64(game.game_image);
|
|
277
|
+
gamesHtml += `
|
|
278
|
+
<div class="game-row">
|
|
279
|
+
<img class="game-img" src="${gameImg}" />
|
|
280
|
+
<div class="game-info">
|
|
281
|
+
<div class="game-name">${game.game_name}</div>
|
|
282
|
+
<div class="game-stats">
|
|
283
|
+
<span class="play-time">${game.play_time ? `总时数 ${game.play_time} 小时` : ''}</span>
|
|
284
|
+
<span class="last-played">${game.last_played}</span>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
const html = `
|
|
291
|
+
<html>
|
|
292
|
+
<head>
|
|
293
|
+
<style>
|
|
294
|
+
${this.getFontCss()}
|
|
295
|
+
body {
|
|
296
|
+
width: 960px;
|
|
297
|
+
background-color: #1b2838;
|
|
298
|
+
position: relative;
|
|
299
|
+
}
|
|
300
|
+
.bg-container {
|
|
301
|
+
position: fixed;
|
|
302
|
+
top: 0;
|
|
303
|
+
left: 0;
|
|
304
|
+
width: 100%;
|
|
305
|
+
height: 100%;
|
|
306
|
+
z-index: -1;
|
|
307
|
+
background-image: url('${bgSrc}');
|
|
308
|
+
background-size: cover;
|
|
309
|
+
background-position: center;
|
|
310
|
+
}
|
|
311
|
+
.bg-overlay {
|
|
312
|
+
position: fixed;
|
|
313
|
+
top: 0;
|
|
314
|
+
left: 0;
|
|
315
|
+
width: 100%;
|
|
316
|
+
height: 100%;
|
|
317
|
+
z-index: -1;
|
|
318
|
+
background-color: rgba(0, 0, 0, 0.7);
|
|
319
|
+
}
|
|
320
|
+
.content {
|
|
321
|
+
padding: 40px;
|
|
322
|
+
z-index: 1;
|
|
323
|
+
}
|
|
324
|
+
.header {
|
|
325
|
+
display: flex;
|
|
326
|
+
align-items: flex-start;
|
|
327
|
+
margin-bottom: 40px;
|
|
328
|
+
}
|
|
329
|
+
.profile-avatar {
|
|
330
|
+
width: 200px;
|
|
331
|
+
height: 200px;
|
|
332
|
+
border: 3px solid #53a4c4;
|
|
333
|
+
margin-right: 40px;
|
|
334
|
+
}
|
|
335
|
+
.profile-info {
|
|
336
|
+
flex: 1;
|
|
337
|
+
}
|
|
338
|
+
.profile-name {
|
|
339
|
+
font-size: 40px;
|
|
340
|
+
color: white;
|
|
341
|
+
margin-bottom: 10px;
|
|
342
|
+
}
|
|
343
|
+
.profile-id {
|
|
344
|
+
font-size: 20px;
|
|
345
|
+
color: #bfbfbf;
|
|
346
|
+
margin-bottom: 20px;
|
|
347
|
+
}
|
|
348
|
+
.profile-desc {
|
|
349
|
+
font-size: 22px;
|
|
350
|
+
color: #bfbfbf;
|
|
351
|
+
white-space: pre-wrap;
|
|
352
|
+
max-width: 640px;
|
|
353
|
+
}
|
|
354
|
+
.games-section {
|
|
355
|
+
margin-top: 20px;
|
|
356
|
+
}
|
|
357
|
+
.recent-header {
|
|
358
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
359
|
+
padding: 10px 20px;
|
|
360
|
+
display: flex;
|
|
361
|
+
justify-content: space-between;
|
|
362
|
+
color: white;
|
|
363
|
+
font-size: 26px;
|
|
364
|
+
margin-bottom: 10px;
|
|
365
|
+
}
|
|
366
|
+
.game-row {
|
|
367
|
+
background-color: rgba(0, 0, 0, 0.3);
|
|
368
|
+
height: 100px;
|
|
369
|
+
display: flex;
|
|
370
|
+
align-items: center;
|
|
371
|
+
padding: 10px 20px;
|
|
372
|
+
margin-bottom: 10px;
|
|
373
|
+
}
|
|
374
|
+
.game-img {
|
|
375
|
+
width: 184px; /* Standard capsule width */
|
|
376
|
+
height: 69px;
|
|
377
|
+
margin-right: 20px;
|
|
378
|
+
}
|
|
379
|
+
.game-info {
|
|
380
|
+
flex: 1;
|
|
381
|
+
display: flex;
|
|
382
|
+
flex-direction: column;
|
|
383
|
+
justify-content: center;
|
|
384
|
+
}
|
|
385
|
+
.game-name {
|
|
386
|
+
font-size: 26px;
|
|
387
|
+
color: white;
|
|
388
|
+
margin-bottom: 8px;
|
|
389
|
+
}
|
|
390
|
+
.game-stats {
|
|
391
|
+
font-size: 20px;
|
|
392
|
+
color: #969696;
|
|
393
|
+
display: flex;
|
|
394
|
+
gap: 20px;
|
|
395
|
+
}
|
|
396
|
+
</style>
|
|
397
|
+
</head>
|
|
398
|
+
<body>
|
|
399
|
+
<div class="bg-container"></div>
|
|
400
|
+
<div class="bg-overlay"></div>
|
|
401
|
+
<div class="content">
|
|
402
|
+
<div class="header">
|
|
403
|
+
<img class="profile-avatar" src="${avatarSrc}" />
|
|
404
|
+
<div class="profile-info">
|
|
405
|
+
<div class="profile-name">${profile.player_name}</div>
|
|
406
|
+
<div class="profile-id">ID: ${steamId}</div>
|
|
407
|
+
<div class="profile-desc">${profile.description}</div>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
<div class="games-section">
|
|
411
|
+
<div class="recent-header">
|
|
412
|
+
<span>最近游戏</span>
|
|
413
|
+
<span>${profile.recent_2_week_play_time || ''}</span>
|
|
414
|
+
</div>
|
|
415
|
+
${gamesHtml}
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
</body>
|
|
419
|
+
</html>
|
|
420
|
+
`;
|
|
421
|
+
const page = await this.ctx.puppeteer.page();
|
|
422
|
+
await page.setContent(html);
|
|
423
|
+
const element = await page.$('body');
|
|
424
|
+
const buffer = await element.screenshot({ type: 'png' });
|
|
425
|
+
await page.close();
|
|
426
|
+
return buffer;
|
|
427
|
+
}
|
|
428
|
+
async concatImages(images) {
|
|
429
|
+
if (images.length === 0)
|
|
430
|
+
return null;
|
|
431
|
+
if (images.length === 1)
|
|
432
|
+
return images[0];
|
|
433
|
+
// We can use a simple HTML page to stack images vertically
|
|
434
|
+
let imgsHtml = '';
|
|
435
|
+
for (const img of images) {
|
|
436
|
+
const src = this.imageToBase64(img);
|
|
437
|
+
imgsHtml += `<img src="${src}" style="display: block;" />`;
|
|
438
|
+
}
|
|
439
|
+
const html = `
|
|
440
|
+
<html>
|
|
441
|
+
<body style="margin:0; padding:0; display: flex; flex-direction: column; width: fit-content;">
|
|
442
|
+
${imgsHtml}
|
|
443
|
+
</body>
|
|
444
|
+
</html>
|
|
445
|
+
`;
|
|
446
|
+
const page = await this.ctx.puppeteer.page();
|
|
447
|
+
await page.setContent(html);
|
|
448
|
+
const element = await page.$('body');
|
|
449
|
+
const buffer = await element.screenshot({ type: 'png', omitBackground: true });
|
|
450
|
+
await page.close();
|
|
451
|
+
return buffer;
|
|
452
|
+
}
|
|
453
|
+
async getDefaultAvatar() {
|
|
454
|
+
// Just a placeholder color
|
|
455
|
+
const html = `
|
|
456
|
+
<html><body style="margin:0;padding:0;"><div style="width:100px;height:100px;background-color:#ccc;"></div></body></html>
|
|
457
|
+
`;
|
|
458
|
+
const page = await this.ctx.puppeteer.page();
|
|
459
|
+
await page.setContent(html);
|
|
460
|
+
const element = await page.$('div');
|
|
461
|
+
const buffer = await element.screenshot({ type: 'png' });
|
|
462
|
+
await page.close();
|
|
463
|
+
return buffer;
|
|
464
|
+
}
|
|
465
|
+
getPlayerStateOrder(p) {
|
|
466
|
+
if (p.gameextrainfo)
|
|
467
|
+
return 0;
|
|
468
|
+
if (p.personastate !== 0)
|
|
469
|
+
return 1;
|
|
470
|
+
return 2;
|
|
471
|
+
}
|
|
472
|
+
getPersonaStateText(state) {
|
|
473
|
+
const map = ['离线', '在线', '忙碌', '离开', '打盹', '寻求交易', '寻求游戏'];
|
|
474
|
+
return map[state] || '未知';
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
exports.DrawService = DrawService;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Context, Schema, Logger } from 'koishi';
|
|
2
|
+
import { SteamService } from './service';
|
|
3
|
+
import { DrawService } from './drawer';
|
|
4
|
+
export declare const name = "steam-info";
|
|
5
|
+
export declare const inject: string[];
|
|
6
|
+
export interface Config {
|
|
7
|
+
steamApiKey: string[];
|
|
8
|
+
proxy?: string;
|
|
9
|
+
steamRequestInterval: number;
|
|
10
|
+
steamBroadcastType: 'all' | 'part' | 'none';
|
|
11
|
+
steamDisableBroadcastOnStartup: boolean;
|
|
12
|
+
fonts: {
|
|
13
|
+
regular: string;
|
|
14
|
+
light: string;
|
|
15
|
+
bold: string;
|
|
16
|
+
};
|
|
17
|
+
commandAuthority: {
|
|
18
|
+
bind: number;
|
|
19
|
+
unbind: number;
|
|
20
|
+
info: number;
|
|
21
|
+
check: number;
|
|
22
|
+
enable: number;
|
|
23
|
+
disable: number;
|
|
24
|
+
update: number;
|
|
25
|
+
nickname: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export declare const Config: Schema<Config>;
|
|
29
|
+
export declare const logger: Logger;
|
|
30
|
+
declare module 'koishi' {
|
|
31
|
+
interface Context {
|
|
32
|
+
steam: SteamService;
|
|
33
|
+
drawer: DrawService;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export declare function apply(ctx: Context, config: Config): void;
|