koishi-plugin-luogu-saver-bot 0.1.1 → 0.1.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/lib/index.d.ts +18 -0
- package/lib/index.js +87 -2
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -38,6 +38,23 @@ export type ArticleHistory = {
|
|
|
38
38
|
content: string;
|
|
39
39
|
createdAt: string;
|
|
40
40
|
};
|
|
41
|
+
export type Paste = {
|
|
42
|
+
id: string;
|
|
43
|
+
content: string;
|
|
44
|
+
authorId: number;
|
|
45
|
+
deleted?: number | boolean;
|
|
46
|
+
createdAt?: string;
|
|
47
|
+
updatedAt?: string;
|
|
48
|
+
deleteReason?: string;
|
|
49
|
+
renderedContent?: string | null;
|
|
50
|
+
author?: {
|
|
51
|
+
id: number;
|
|
52
|
+
name: string;
|
|
53
|
+
color?: string;
|
|
54
|
+
createdAt?: string;
|
|
55
|
+
updatedAt?: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
41
58
|
export type CountResponse = {
|
|
42
59
|
count: number;
|
|
43
60
|
};
|
|
@@ -81,6 +98,7 @@ declare class LuoguSaverClient {
|
|
|
81
98
|
private buildUrl;
|
|
82
99
|
private headers;
|
|
83
100
|
getArticle(id: string, extraHeaders?: Record<string, string>): Promise<Article>;
|
|
101
|
+
getPaste(id: string, extraHeaders?: Record<string, string>): Promise<Paste>;
|
|
84
102
|
getRecent(opts?: {
|
|
85
103
|
count?: number;
|
|
86
104
|
updated_after?: string;
|
package/lib/index.js
CHANGED
|
@@ -75,6 +75,12 @@ var LuoguSaverClient = class {
|
|
|
75
75
|
if (res.code !== 200) return null;
|
|
76
76
|
return res.data;
|
|
77
77
|
}
|
|
78
|
+
async getPaste(id, extraHeaders) {
|
|
79
|
+
const url = this.buildUrl(`/paste/query/${encodeURIComponent(id)}`);
|
|
80
|
+
const res = await this.ctx.http.get(url, { headers: this.headers(extraHeaders) });
|
|
81
|
+
if (res.code !== 200) return null;
|
|
82
|
+
return res.data;
|
|
83
|
+
}
|
|
78
84
|
async getRecent(opts, extraHeaders) {
|
|
79
85
|
const params = new URLSearchParams();
|
|
80
86
|
if (opts?.count != null) params.set("count", String(opts.count));
|
|
@@ -144,16 +150,39 @@ function apply(ctx, config = {}) {
|
|
|
144
150
|
const content = art.renderedContent ?? art.content ?? "";
|
|
145
151
|
const title = art.title ?? "";
|
|
146
152
|
const escapeHtml = /* @__PURE__ */ __name((s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]), "escapeHtml");
|
|
147
|
-
const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style
|
|
153
|
+
const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{--bg:#f4f6fb;--card:#ffffff;--text:#111;--muted:#6b7280;--accent:#2563eb}html,body{height:100%}body{margin:0;background:var(--bg);-webkit-font-smoothing:antialiased;font-family:Inter,-apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial;padding:40px;color:var(--text)}.container{max-width:900px;margin:0 auto}.card{background:var(--card);padding:32px;border-radius:12px;box-shadow:0 10px 30px rgba(2,6,23,0.08)}h1{font-size:28px;margin:0 0 12px}.meta{color:var(--muted);font-size:13px;margin-bottom:12px}article{line-height:1.75;color:var(--text)}img{max-width:100%;border-radius:8px}pre,code{background:#f6f8fa;padding:12px;border-radius:8px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}blockquote{border-left:4px solid #e6e9ef;padding:8px 16px;color:var(--muted);margin:8px 0}a{color:var(--accent)}</style></head><body><div class="container"><div class="card"><h1>${escapeHtml(title)}</h1><div class="meta">作者 ${escapeHtml(String(art.authorId))}</div><article>${content}</article></div></div></body></html>`;
|
|
148
154
|
if (!ctx.puppeteer) return "当前没有可用的 puppeteer 服务。";
|
|
149
155
|
const page = await ctx.puppeteer.page();
|
|
150
156
|
try {
|
|
151
157
|
const width = Number(options.width) || 960;
|
|
152
|
-
await page.setViewport({ width, height: 800 });
|
|
158
|
+
await page.setViewport({ width, height: 800, deviceScaleFactor: 2 });
|
|
153
159
|
if (typeof page.emulateMediaFeatures === "function") {
|
|
154
160
|
await page.emulateMediaFeatures([{ name: "prefers-color-scheme", value: "light" }]);
|
|
155
161
|
}
|
|
156
162
|
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
163
|
+
try {
|
|
164
|
+
await page.evaluate(() => new Promise((resolve) => {
|
|
165
|
+
const imgs = Array.from(document.images);
|
|
166
|
+
if (!imgs.length) return resolve(null);
|
|
167
|
+
let loaded = 0;
|
|
168
|
+
imgs.forEach((img) => {
|
|
169
|
+
if (img.complete) {
|
|
170
|
+
loaded++;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
img.addEventListener("load", () => {
|
|
174
|
+
loaded++;
|
|
175
|
+
if (loaded === imgs.length) resolve(null);
|
|
176
|
+
});
|
|
177
|
+
img.addEventListener("error", () => {
|
|
178
|
+
loaded++;
|
|
179
|
+
if (loaded === imgs.length) resolve(null);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
if (loaded === imgs.length) resolve(null);
|
|
183
|
+
}));
|
|
184
|
+
} catch (e) {
|
|
185
|
+
}
|
|
157
186
|
try {
|
|
158
187
|
await page.evaluate(() => {
|
|
159
188
|
document.documentElement.style.background = "#ffffff";
|
|
@@ -170,6 +199,62 @@ function apply(ctx, config = {}) {
|
|
|
170
199
|
page.close();
|
|
171
200
|
}
|
|
172
201
|
});
|
|
202
|
+
ctx.command("获取剪贴板 <id>", "获取剪贴板内容并截取长图").option("width", "-w <width:number>", { fallback: 960 }).action(async ({ session, options }, id) => {
|
|
203
|
+
if (!id) return "请提供剪贴板 ID";
|
|
204
|
+
const paste = await ctx.luogu_saver.getPaste(id);
|
|
205
|
+
if (!paste) return "未找到剪贴板内容";
|
|
206
|
+
const content = paste.renderedContent ?? paste.content ?? "";
|
|
207
|
+
const title = paste.id ?? "";
|
|
208
|
+
const escapeHtml = /* @__PURE__ */ __name((s) => s.replace(/[&<>\"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]), "escapeHtml");
|
|
209
|
+
const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>:root{--bg:#f4f6fb;--card:#ffffff;--text:#111;--muted:#6b7280;--accent:#2563eb}html,body{height:100%}body{margin:0;background:var(--bg);-webkit-font-smoothing:antialiased;font-family:Inter,-apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial;padding:40px;color:var(--text)}.container{max-width:900px;margin:0 auto}.card{background:var(--card);padding:24px;border-radius:12px;box-shadow:0 8px 20px rgba(2,6,23,0.06)}h1{font-size:20px;margin:0 0 8px}pre,code{white-space:pre-wrap;word-break:break-word;background:#f6f8fa;padding:12px;border-radius:8px;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}img{max-width:100%;border-radius:6px}a{color:var(--accent)}</style></head><body><div class="container"><div class="card"><h1>${escapeHtml(title)}</h1><article>${content}</article></div></div></body></html>`;
|
|
210
|
+
if (!ctx.puppeteer) return "当前没有可用的 puppeteer 服务。";
|
|
211
|
+
const page = await ctx.puppeteer.page();
|
|
212
|
+
try {
|
|
213
|
+
const width = Number(options.width) || 960;
|
|
214
|
+
await page.setViewport({ width, height: 800, deviceScaleFactor: 2 });
|
|
215
|
+
if (typeof page.emulateMediaFeatures === "function") {
|
|
216
|
+
await page.emulateMediaFeatures([{ name: "prefers-color-scheme", value: "light" }]);
|
|
217
|
+
}
|
|
218
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
219
|
+
try {
|
|
220
|
+
await page.evaluate(() => new Promise((resolve) => {
|
|
221
|
+
const imgs = Array.from(document.images);
|
|
222
|
+
if (!imgs.length) return resolve(null);
|
|
223
|
+
let loaded = 0;
|
|
224
|
+
imgs.forEach((img) => {
|
|
225
|
+
if (img.complete) {
|
|
226
|
+
loaded++;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
img.addEventListener("load", () => {
|
|
230
|
+
loaded++;
|
|
231
|
+
if (loaded === imgs.length) resolve(null);
|
|
232
|
+
});
|
|
233
|
+
img.addEventListener("error", () => {
|
|
234
|
+
loaded++;
|
|
235
|
+
if (loaded === imgs.length) resolve(null);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
if (loaded === imgs.length) resolve(null);
|
|
239
|
+
}));
|
|
240
|
+
} catch (e) {
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
await page.evaluate(() => {
|
|
244
|
+
document.documentElement.style.background = "#ffffff";
|
|
245
|
+
if (document.body) document.body.style.background = "#ffffff";
|
|
246
|
+
});
|
|
247
|
+
} catch (e) {
|
|
248
|
+
}
|
|
249
|
+
const buffer = await page.screenshot({ fullPage: true, type: "png", omitBackground: false });
|
|
250
|
+
return import_koishi.h.image(buffer, "image/png");
|
|
251
|
+
} catch (err) {
|
|
252
|
+
ctx.logger.error("截图剪贴板失败", err);
|
|
253
|
+
return "获取失败。";
|
|
254
|
+
} finally {
|
|
255
|
+
page.close();
|
|
256
|
+
}
|
|
257
|
+
});
|
|
173
258
|
}
|
|
174
259
|
__name(apply, "apply");
|
|
175
260
|
// Annotate the CommonJS export names for ESM import in node:
|