koishi-plugin-luogu-saver-bot 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/lib/index.js +239 -26
  2. package/package.json +7 -4
package/lib/index.js CHANGED
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
8
  var __export = (target, all) => {
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -43,6 +53,9 @@ function statusToString(status) {
43
53
  __name(statusToString, "statusToString");
44
54
 
45
55
  // src/index.ts
56
+ var import_markdown_it = __toESM(require("markdown-it"));
57
+ var import_markdown_it_katex = __toESM(require("markdown-it-katex"));
58
+ var import_highlight = __toESM(require("highlight.js"));
46
59
  var name = "luogu-saver-bot";
47
60
  var inject = ["puppeteer"];
48
61
  var Config = import_koishi.Schema.object({
@@ -62,7 +75,6 @@ var LuoguSaverClient = class {
62
75
  buildUrl(path) {
63
76
  const base = this.endpoint.replace(/\/$/, "");
64
77
  if (!base) return path;
65
- console.log(`${base}${path}`);
66
78
  if (path.startsWith("/")) return `${base}${path}`;
67
79
  return `${base}/${path}`;
68
80
  }
@@ -119,6 +131,183 @@ var LuoguSaverClient = class {
119
131
  return res.data;
120
132
  }
121
133
  };
134
+ var md = new import_markdown_it.default({
135
+ html: true,
136
+ // 允许 HTML 标签
137
+ breaks: true,
138
+ // 转换换行符为 <br>
139
+ linkify: true,
140
+ // 自动识别链接
141
+ highlight: /* @__PURE__ */ __name(function(str, lang) {
142
+ if (lang && import_highlight.default.getLanguage(lang)) {
143
+ try {
144
+ return '<pre class="hljs"><code>' + import_highlight.default.highlight(str, { language: lang, ignoreIllegals: true }).value + "</code></pre>";
145
+ } catch (__) {
146
+ }
147
+ }
148
+ return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + "</code></pre>";
149
+ }, "highlight")
150
+ });
151
+ md.use(import_markdown_it_katex.default);
152
+ function generateHtml(title, authorInfo, markdownContent) {
153
+ const renderedBody = md.render(markdownContent);
154
+ return `<!doctype html>
155
+ <html>
156
+ <head>
157
+ <meta charset="utf-8">
158
+ <meta name="viewport" content="width=device-width,initial-scale=1">
159
+
160
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/styles/github.min.css">
161
+
162
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css">
163
+
164
+ <style>
165
+ /* 基础重置与变量 */
166
+ :root {
167
+ --bg-color: #f6f8fa;
168
+ --card-bg: #ffffff;
169
+ --text-primary: #24292f;
170
+ --text-secondary: #57606a;
171
+ --border-color: #d0d7de;
172
+ --accent-color: #0969da;
173
+ }
174
+
175
+ body {
176
+ margin: 0;
177
+ padding: 40px;
178
+ background-color: var(--bg-color);
179
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
180
+ color: var(--text-primary);
181
+ line-height: 1.5;
182
+ }
183
+
184
+ .container {
185
+ max-width: 900px;
186
+ margin: 0 auto;
187
+ }
188
+
189
+ .card {
190
+ background: var(--card-bg);
191
+ border: 1px solid var(--border-color);
192
+ border-radius: 6px;
193
+ padding: 32px 40px;
194
+ box-shadow: 0 3px 6px rgba(140, 149, 159, 0.15);
195
+ }
196
+
197
+ /* 头部信息 */
198
+ header {
199
+ border-bottom: 1px solid var(--border-color);
200
+ padding-bottom: 20px;
201
+ margin-bottom: 24px;
202
+ }
203
+
204
+ h1.title {
205
+ margin: 0 0 8px 0;
206
+ font-size: 28px;
207
+ font-weight: 600;
208
+ }
209
+
210
+ .meta {
211
+ font-size: 14px;
212
+ color: var(--text-secondary);
213
+ }
214
+
215
+ /* Markdown 内容美化 (仿 GitHub 风格) */
216
+ .markdown-body {
217
+ font-size: 16px;
218
+ line-height: 1.6;
219
+ }
220
+
221
+ .markdown-body h1, .markdown-body h2, .markdown-body h3 {
222
+ margin-top: 24px;
223
+ margin-bottom: 16px;
224
+ font-weight: 600;
225
+ line-height: 1.25;
226
+ }
227
+ .markdown-body h1 { font-size: 2em; padding-bottom: .3em; border-bottom: 1px solid #d0d7de; }
228
+ .markdown-body h2 { font-size: 1.5em; padding-bottom: .3em; border-bottom: 1px solid #d0d7de; }
229
+ .markdown-body h3 { font-size: 1.25em; }
230
+
231
+ .markdown-body p { margin-bottom: 16px; }
232
+
233
+ .markdown-body a { color: var(--accent-color); text-decoration: none; }
234
+ .markdown-body a:hover { text-decoration: underline; }
235
+
236
+ .markdown-body blockquote {
237
+ margin: 0 0 16px;
238
+ padding: 0 1em;
239
+ color: var(--text-secondary);
240
+ border-left: 0.25em solid var(--border-color);
241
+ }
242
+
243
+ .markdown-body ul, .markdown-body ol { padding-left: 2em; margin-bottom: 16px; }
244
+
245
+ /* 表格样式 */
246
+ .markdown-body table {
247
+ border-spacing: 0;
248
+ border-collapse: collapse;
249
+ margin-bottom: 16px;
250
+ width: max-content;
251
+ max-width: 100%;
252
+ overflow: auto;
253
+ display: block;
254
+ }
255
+ .markdown-body table th, .markdown-body table td {
256
+ padding: 6px 13px;
257
+ border: 1px solid var(--border-color);
258
+ }
259
+ .markdown-body table tr { background-color: #fff; border-top: 1px solid #c6cbd1; }
260
+ .markdown-body table tr:nth-child(2n) { background-color: #f6f8fa; }
261
+
262
+ /* 代码块微调 */
263
+ .markdown-body pre {
264
+ padding: 16px;
265
+ overflow: auto;
266
+ font-size: 85%;
267
+ line-height: 1.45;
268
+ background-color: #f6f8fa;
269
+ border-radius: 6px;
270
+ }
271
+ .markdown-body code {
272
+ padding: 0.2em 0.4em;
273
+ margin: 0;
274
+ font-size: 85%;
275
+ background-color: rgba(175,184,193,0.2);
276
+ border-radius: 6px;
277
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
278
+ }
279
+ .markdown-body pre code {
280
+ padding: 0;
281
+ background-color: transparent;
282
+ }
283
+
284
+ /* 图片自适应 */
285
+ .markdown-body img {
286
+ max-width: 100%;
287
+ box-sizing: content-box;
288
+ background-color: #fff;
289
+ }
290
+
291
+ /* KaTeX 字体修复 */
292
+ .katex { font-size: 1.1em; }
293
+ </style>
294
+ </head>
295
+ <body>
296
+ <div class="container">
297
+ <div class="card">
298
+ <header>
299
+ <h1 class="title">${md.utils.escapeHtml(title)}</h1>
300
+ <div class="meta">${md.utils.escapeHtml(authorInfo)}</div>
301
+ </header>
302
+ <article class="markdown-body">
303
+ ${renderedBody}
304
+ </article>
305
+ </div>
306
+ </div>
307
+ </body>
308
+ </html>`;
309
+ }
310
+ __name(generateHtml, "generateHtml");
122
311
  function apply(ctx, config = {}) {
123
312
  const endpoint = config.endpoint || "";
124
313
  const userAgent = config.userAgent || "Uptime-Kuma";
@@ -147,27 +336,39 @@ function apply(ctx, config = {}) {
147
336
  if (!id) return "请提供文章 ID";
148
337
  const art = await ctx.luogu_saver.getArticle(id);
149
338
  if (!art) return "未找到文章";
150
- const content = art.renderedContent ?? art.content ?? "";
339
+ const rawContent = art.content ?? art.renderedContent ?? "";
151
340
  const title = art.title ?? "";
152
- const escapeHtml = /* @__PURE__ */ __name((s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escapeHtml");
153
- const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial;padding:20px;line-height:1.8;color:#222}img{max-width:100%}h1{font-size:24px;margin-bottom:12px}</style></head><body><h1>${escapeHtml(title)}</h1>${content}</body></html>`;
341
+ const authorInfo = `作者 UID: ${art.authorId}`;
342
+ const html = generateHtml(title, authorInfo, rawContent);
154
343
  if (!ctx.puppeteer) return "当前没有可用的 puppeteer 服务。";
155
344
  const page = await ctx.puppeteer.page();
156
345
  try {
157
346
  const width = Number(options.width) || 960;
158
- await page.setViewport({ width, height: 800 });
159
- if (typeof page.emulateMediaFeatures === "function") {
160
- await page.emulateMediaFeatures([{ name: "prefers-color-scheme", value: "light" }]);
161
- }
347
+ await page.setViewport({ width, height: 800, deviceScaleFactor: 2 });
162
348
  await page.setContent(html, { waitUntil: "networkidle0" });
163
349
  try {
164
- await page.evaluate(() => {
165
- document.documentElement.style.background = "#ffffff";
166
- if (document.body) document.body.style.background = "#ffffff";
167
- });
350
+ await page.evaluate(() => new Promise((resolve) => {
351
+ const imgs = Array.from(document.images);
352
+ if (!imgs.length) return resolve(null);
353
+ let loaded = 0;
354
+ imgs.forEach((img) => {
355
+ if (img.complete) {
356
+ loaded++;
357
+ return;
358
+ }
359
+ const handler = /* @__PURE__ */ __name(() => {
360
+ loaded++;
361
+ if (loaded === imgs.length) resolve(null);
362
+ }, "handler");
363
+ img.addEventListener("load", handler);
364
+ img.addEventListener("error", handler);
365
+ });
366
+ if (loaded === imgs.length) resolve(null);
367
+ setTimeout(() => resolve(null), 5e3);
368
+ }));
168
369
  } catch (e) {
169
370
  }
170
- const buffer = await page.screenshot({ fullPage: true, type: "png", omitBackground: false });
371
+ const buffer = await page.screenshot({ fullPage: true, type: "png" });
171
372
  return import_koishi.h.image(buffer, "image/png");
172
373
  } catch (err) {
173
374
  ctx.logger.error("截图文章失败", err);
@@ -180,27 +381,39 @@ function apply(ctx, config = {}) {
180
381
  if (!id) return "请提供剪贴板 ID";
181
382
  const paste = await ctx.luogu_saver.getPaste(id);
182
383
  if (!paste) return "未找到剪贴板内容";
183
- const content = paste.renderedContent ?? paste.content ?? "";
184
- const title = paste.id ?? "";
185
- const escapeHtml = /* @__PURE__ */ __name((s) => s.replace(/[&<>\"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escapeHtml");
186
- const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial;padding:20px;line-height:1.8;color:#222}pre,code{white-space:pre-wrap;word-break:break-word;background:#f8f8f8;padding:12px;border-radius:6px}h1{font-size:18px;margin-bottom:8px}</style></head><body><h1>${escapeHtml(title)}</h1>${content}</body></html>`;
384
+ const rawContent = paste.content ?? paste.renderedContent ?? "";
385
+ const title = `剪贴板: ${paste.id}`;
386
+ const authorInfo = paste.author ? `创建者: ${paste.author.name} (UID: ${paste.author.id})` : `创建者 UID: ${paste.authorId}`;
387
+ const html = generateHtml(title, authorInfo, rawContent);
187
388
  if (!ctx.puppeteer) return "当前没有可用的 puppeteer 服务。";
188
389
  const page = await ctx.puppeteer.page();
189
390
  try {
190
391
  const width = Number(options.width) || 960;
191
- await page.setViewport({ width, height: 800 });
192
- if (typeof page.emulateMediaFeatures === "function") {
193
- await page.emulateMediaFeatures([{ name: "prefers-color-scheme", value: "light" }]);
194
- }
392
+ await page.setViewport({ width, height: 800, deviceScaleFactor: 2 });
195
393
  await page.setContent(html, { waitUntil: "networkidle0" });
196
394
  try {
197
- await page.evaluate(() => {
198
- document.documentElement.style.background = "#ffffff";
199
- if (document.body) document.body.style.background = "#ffffff";
200
- });
395
+ await page.evaluate(() => new Promise((resolve) => {
396
+ const imgs = Array.from(document.images);
397
+ if (!imgs.length) return resolve(null);
398
+ let loaded = 0;
399
+ imgs.forEach((img) => {
400
+ if (img.complete) {
401
+ loaded++;
402
+ return;
403
+ }
404
+ const handler = /* @__PURE__ */ __name(() => {
405
+ loaded++;
406
+ if (loaded === imgs.length) resolve(null);
407
+ }, "handler");
408
+ img.addEventListener("load", handler);
409
+ img.addEventListener("error", handler);
410
+ });
411
+ if (loaded === imgs.length) resolve(null);
412
+ setTimeout(() => resolve(null), 5e3);
413
+ }));
201
414
  } catch (e) {
202
415
  }
203
- const buffer = await page.screenshot({ fullPage: true, type: "png", omitBackground: false });
416
+ const buffer = await page.screenshot({ fullPage: true, type: "png" });
204
417
  return import_koishi.h.image(buffer, "image/png");
205
418
  } catch (err) {
206
419
  ctx.logger.error("截图剪贴板失败", err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-luogu-saver-bot",
3
3
  "description": "洛谷保存站机器人",
4
- "version": "0.1.2",
4
+ "version": "0.1.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -15,9 +15,12 @@
15
15
  "koishi",
16
16
  "plugin"
17
17
  ],
18
- "devDependencies": {},
19
18
  "peerDependencies": {
20
- "koishi": "^4.18.7",
21
- "puppeteer": "^3.9.0"
19
+ "koishi": "^4.18.7"
20
+ },
21
+ "dependencies": {
22
+ "highlight.js": "^11.11.1",
23
+ "markdown-it": "^14.1.0",
24
+ "markdown-it-katex": "^2.0.3"
22
25
  }
23
26
  }