koishi-plugin-luogu-saver-bot 0.1.0 → 0.1.2
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 +20 -0
- package/lib/index.js +75 -0
- package/package.json +3 -2
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context, Schema } from 'koishi';
|
|
2
2
|
export declare const name = "luogu-saver-bot";
|
|
3
|
+
export declare const inject: string[];
|
|
3
4
|
export interface Config {
|
|
4
5
|
endpoint?: string;
|
|
5
6
|
userAgent?: string;
|
|
@@ -37,6 +38,23 @@ export type ArticleHistory = {
|
|
|
37
38
|
content: string;
|
|
38
39
|
createdAt: string;
|
|
39
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
|
+
};
|
|
40
58
|
export type CountResponse = {
|
|
41
59
|
count: number;
|
|
42
60
|
};
|
|
@@ -80,6 +98,7 @@ declare class LuoguSaverClient {
|
|
|
80
98
|
private buildUrl;
|
|
81
99
|
private headers;
|
|
82
100
|
getArticle(id: string, extraHeaders?: Record<string, string>): Promise<Article>;
|
|
101
|
+
getPaste(id: string, extraHeaders?: Record<string, string>): Promise<Paste>;
|
|
83
102
|
getRecent(opts?: {
|
|
84
103
|
count?: number;
|
|
85
104
|
updated_after?: string;
|
|
@@ -94,6 +113,7 @@ declare class LuoguSaverClient {
|
|
|
94
113
|
declare module 'koishi' {
|
|
95
114
|
interface Context {
|
|
96
115
|
luogu_saver: LuoguSaverClient;
|
|
116
|
+
puppeteer?: any;
|
|
97
117
|
}
|
|
98
118
|
}
|
|
99
119
|
export declare function apply(ctx: Context, config?: Config): void;
|
package/lib/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
Config: () => Config,
|
|
24
24
|
apply: () => apply,
|
|
25
|
+
inject: () => inject,
|
|
25
26
|
name: () => name
|
|
26
27
|
});
|
|
27
28
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -43,6 +44,7 @@ __name(statusToString, "statusToString");
|
|
|
43
44
|
|
|
44
45
|
// src/index.ts
|
|
45
46
|
var name = "luogu-saver-bot";
|
|
47
|
+
var inject = ["puppeteer"];
|
|
46
48
|
var Config = import_koishi.Schema.object({
|
|
47
49
|
endpoint: import_koishi.Schema.string().description("自定义 API endpoint,结尾无需斜杠").role("input").default(""),
|
|
48
50
|
userAgent: import_koishi.Schema.string().description("自定义 User-Agent").role("input").default("Uptime-Kuma")
|
|
@@ -73,6 +75,12 @@ var LuoguSaverClient = class {
|
|
|
73
75
|
if (res.code !== 200) return null;
|
|
74
76
|
return res.data;
|
|
75
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
|
+
}
|
|
76
84
|
async getRecent(opts, extraHeaders) {
|
|
77
85
|
const params = new URLSearchParams();
|
|
78
86
|
if (opts?.count != null) params.set("count", String(opts.count));
|
|
@@ -135,11 +143,78 @@ function apply(ctx, config = {}) {
|
|
|
135
143
|
if (typeof task === "object" && "status" in task) return `任务 ${id} 状态: ${statusToString(task.status)}`;
|
|
136
144
|
return JSON.stringify(task);
|
|
137
145
|
});
|
|
146
|
+
ctx.command("获取文章 <id>", "获取文章并截取长图").option("width", "-w <width:number>", { fallback: 960 }).action(async ({ session, options }, id) => {
|
|
147
|
+
if (!id) return "请提供文章 ID";
|
|
148
|
+
const art = await ctx.luogu_saver.getArticle(id);
|
|
149
|
+
if (!art) return "未找到文章";
|
|
150
|
+
const content = art.renderedContent ?? art.content ?? "";
|
|
151
|
+
const title = art.title ?? "";
|
|
152
|
+
const escapeHtml = /* @__PURE__ */ __name((s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[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>`;
|
|
154
|
+
if (!ctx.puppeteer) return "当前没有可用的 puppeteer 服务。";
|
|
155
|
+
const page = await ctx.puppeteer.page();
|
|
156
|
+
try {
|
|
157
|
+
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
|
+
}
|
|
162
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
163
|
+
try {
|
|
164
|
+
await page.evaluate(() => {
|
|
165
|
+
document.documentElement.style.background = "#ffffff";
|
|
166
|
+
if (document.body) document.body.style.background = "#ffffff";
|
|
167
|
+
});
|
|
168
|
+
} catch (e) {
|
|
169
|
+
}
|
|
170
|
+
const buffer = await page.screenshot({ fullPage: true, type: "png", omitBackground: false });
|
|
171
|
+
return import_koishi.h.image(buffer, "image/png");
|
|
172
|
+
} catch (err) {
|
|
173
|
+
ctx.logger.error("截图文章失败", err);
|
|
174
|
+
return "获取失败";
|
|
175
|
+
} finally {
|
|
176
|
+
page.close();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
ctx.command("获取剪贴板 <id>", "获取剪贴板内容并截取长图").option("width", "-w <width:number>", { fallback: 960 }).action(async ({ session, options }, id) => {
|
|
180
|
+
if (!id) return "请提供剪贴板 ID";
|
|
181
|
+
const paste = await ctx.luogu_saver.getPaste(id);
|
|
182
|
+
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) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[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>`;
|
|
187
|
+
if (!ctx.puppeteer) return "当前没有可用的 puppeteer 服务。";
|
|
188
|
+
const page = await ctx.puppeteer.page();
|
|
189
|
+
try {
|
|
190
|
+
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
|
+
}
|
|
195
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
196
|
+
try {
|
|
197
|
+
await page.evaluate(() => {
|
|
198
|
+
document.documentElement.style.background = "#ffffff";
|
|
199
|
+
if (document.body) document.body.style.background = "#ffffff";
|
|
200
|
+
});
|
|
201
|
+
} catch (e) {
|
|
202
|
+
}
|
|
203
|
+
const buffer = await page.screenshot({ fullPage: true, type: "png", omitBackground: false });
|
|
204
|
+
return import_koishi.h.image(buffer, "image/png");
|
|
205
|
+
} catch (err) {
|
|
206
|
+
ctx.logger.error("截图剪贴板失败", err);
|
|
207
|
+
return "获取失败。";
|
|
208
|
+
} finally {
|
|
209
|
+
page.close();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
138
212
|
}
|
|
139
213
|
__name(apply, "apply");
|
|
140
214
|
// Annotate the CommonJS export names for ESM import in node:
|
|
141
215
|
0 && (module.exports = {
|
|
142
216
|
Config,
|
|
143
217
|
apply,
|
|
218
|
+
inject,
|
|
144
219
|
name
|
|
145
220
|
});
|
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.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"devDependencies": {},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"koishi": "^4.18.7"
|
|
20
|
+
"koishi": "^4.18.7",
|
|
21
|
+
"puppeteer": "^3.9.0"
|
|
21
22
|
}
|
|
22
23
|
}
|