koishi-plugin-meme-upload-114 0.0.1
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 +16 -0
- package/lib/index.js +218 -0
- package/package.json +23 -0
- package/readme.md +5 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context, Schema } from 'koishi';
|
|
2
|
+
declare module 'koishi' {
|
|
3
|
+
interface Context {
|
|
4
|
+
puppeteer: any;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export declare const name = "local-image-manager";
|
|
8
|
+
export declare const inject: string[];
|
|
9
|
+
export interface Config {
|
|
10
|
+
rootDir: string;
|
|
11
|
+
defaultTimeout: number;
|
|
12
|
+
autoWithdrawSuccess: boolean;
|
|
13
|
+
successWithdrawTime: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const Config: Schema<Config>;
|
|
16
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name2 in all)
|
|
10
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
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
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
Config: () => Config,
|
|
34
|
+
apply: () => apply,
|
|
35
|
+
inject: () => inject,
|
|
36
|
+
name: () => name
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
39
|
+
var import_koishi = require("koishi");
|
|
40
|
+
var fs = __toESM(require("fs"));
|
|
41
|
+
var path = __toESM(require("path"));
|
|
42
|
+
var name = "local-image-manager";
|
|
43
|
+
var inject = ["puppeteer"];
|
|
44
|
+
var Config = import_koishi.Schema.object({
|
|
45
|
+
rootDir: import_koishi.Schema.path({ filters: ["directory"], allowCreate: true }).default("data/images").description("图片存储的根目录"),
|
|
46
|
+
defaultTimeout: import_koishi.Schema.number().default(1e4).description("创建文件夹确认的等待时间(毫秒)"),
|
|
47
|
+
autoWithdrawSuccess: import_koishi.Schema.boolean().default(true).description("是否自动撤回“上传成功”的提示"),
|
|
48
|
+
successWithdrawTime: import_koishi.Schema.number().default(1e4).description("上传成功提示的撤回延迟(毫秒)")
|
|
49
|
+
});
|
|
50
|
+
function apply(ctx, config) {
|
|
51
|
+
const root = path.resolve(ctx.baseDir, config.rootDir);
|
|
52
|
+
if (!fs.existsSync(root)) {
|
|
53
|
+
fs.mkdirSync(root, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
const generateQuoteHtml = /* @__PURE__ */ __name((avatar, name2, content) => {
|
|
56
|
+
return `
|
|
57
|
+
<html>
|
|
58
|
+
<head>
|
|
59
|
+
<style>
|
|
60
|
+
body { font-family: sans-serif; padding: 20px; background: transparent; width: fit-content; }
|
|
61
|
+
.container {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: flex-start;
|
|
64
|
+
max-width: 400px;
|
|
65
|
+
background: #ffffff;
|
|
66
|
+
padding: 15px;
|
|
67
|
+
border-radius: 12px;
|
|
68
|
+
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
|
|
69
|
+
}
|
|
70
|
+
.avatar {
|
|
71
|
+
width: 50px;
|
|
72
|
+
height: 50px;
|
|
73
|
+
border-radius: 50%;
|
|
74
|
+
margin-right: 15px;
|
|
75
|
+
border: 1px solid #eee;
|
|
76
|
+
flex-shrink: 0;
|
|
77
|
+
}
|
|
78
|
+
.content-box { display: flex; flex-direction: column; }
|
|
79
|
+
.name { font-size: 14px; color: #888; margin-bottom: 6px; font-weight: bold; }
|
|
80
|
+
.text { font-size: 17px; color: #333; line-height: 1.5; word-wrap: break-word; }
|
|
81
|
+
</style>
|
|
82
|
+
</head>
|
|
83
|
+
<body>
|
|
84
|
+
<div class="container">
|
|
85
|
+
<img class="avatar" src="${avatar}" />
|
|
86
|
+
<div class="content-box">
|
|
87
|
+
<div class="name">${name2}</div>
|
|
88
|
+
<div class="text">${content}</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</body>
|
|
92
|
+
</html>
|
|
93
|
+
`;
|
|
94
|
+
}, "generateQuoteHtml");
|
|
95
|
+
const getImageBuffer = /* @__PURE__ */ __name(async (session) => {
|
|
96
|
+
let targetImgUrl = null;
|
|
97
|
+
const currImg = session.elements.find((e) => e.type === "img" || e.type === "image");
|
|
98
|
+
if (currImg) {
|
|
99
|
+
targetImgUrl = currImg.attrs.src || currImg.attrs.url;
|
|
100
|
+
}
|
|
101
|
+
if (!targetImgUrl && session.quote) {
|
|
102
|
+
const quoteElements = session.quote.elements;
|
|
103
|
+
const quoteImg = quoteElements.find((e) => e.type === "img" || e.type === "image");
|
|
104
|
+
if (quoteImg) {
|
|
105
|
+
targetImgUrl = quoteImg.attrs.src || quoteImg.attrs.url;
|
|
106
|
+
} else {
|
|
107
|
+
const author = session.quote.author;
|
|
108
|
+
const avatarUrl = author?.avatar || "https://koishi.chat/logo.png";
|
|
109
|
+
const username = author?.nickname || author?.username || "用户";
|
|
110
|
+
const textContent = session.quote.content || " ";
|
|
111
|
+
const html = generateQuoteHtml(avatarUrl, username, textContent);
|
|
112
|
+
try {
|
|
113
|
+
const page = await ctx.puppeteer.page();
|
|
114
|
+
await page.setContent(html);
|
|
115
|
+
const element = await page.$(".container");
|
|
116
|
+
const buffer = await element?.screenshot({ type: "png" });
|
|
117
|
+
await page.close();
|
|
118
|
+
return { buffer, ext: ".png" };
|
|
119
|
+
} catch (e) {
|
|
120
|
+
ctx.logger.error("Puppeteer 生成图片失败", e);
|
|
121
|
+
return { buffer: null, ext: "" };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (targetImgUrl) {
|
|
126
|
+
try {
|
|
127
|
+
const buffer = await ctx.http.get(targetImgUrl, { responseType: "arraybuffer" });
|
|
128
|
+
let ext = ".jpg";
|
|
129
|
+
if (targetImgUrl.includes(".png")) ext = ".png";
|
|
130
|
+
else if (targetImgUrl.includes(".gif")) ext = ".gif";
|
|
131
|
+
else if (targetImgUrl.includes(".webp")) ext = ".webp";
|
|
132
|
+
return { buffer: Buffer.from(buffer), ext };
|
|
133
|
+
} catch (e) {
|
|
134
|
+
ctx.logger.error("下载图片失败", e);
|
|
135
|
+
return { buffer: null, ext: "" };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { buffer: null, ext: "" };
|
|
139
|
+
}, "getImageBuffer");
|
|
140
|
+
const withdrawLater = /* @__PURE__ */ __name(async (session, messageId, delay) => {
|
|
141
|
+
await (0, import_koishi.sleep)(delay);
|
|
142
|
+
try {
|
|
143
|
+
await session.bot.deleteMessage(session.channelId, messageId);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
}
|
|
146
|
+
}, "withdrawLater");
|
|
147
|
+
ctx.command("上传 <folderName>", "上传图片或引用转换").action(async ({ session }, folderName) => {
|
|
148
|
+
if (!folderName) return "请输入文件夹名称。";
|
|
149
|
+
const { buffer, ext } = await getImageBuffer(session);
|
|
150
|
+
if (!buffer) {
|
|
151
|
+
return "无法获取图片。请直接附带图片,或引用含有图片/文字的消息。";
|
|
152
|
+
}
|
|
153
|
+
const targetDir = path.join(root, folderName);
|
|
154
|
+
let shouldUpload = false;
|
|
155
|
+
if (!fs.existsSync(targetDir)) {
|
|
156
|
+
const promptMsgIds = await session.send(`文件夹 [${folderName}] 不存在,是否创建?请发送 y 确认。`);
|
|
157
|
+
const promptMsgId = promptMsgIds[0];
|
|
158
|
+
const timeoutTimer = setTimeout(() => {
|
|
159
|
+
session.bot.deleteMessage(session.channelId, promptMsgId).catch(() => {
|
|
160
|
+
});
|
|
161
|
+
}, config.defaultTimeout);
|
|
162
|
+
const input = await session.prompt(config.defaultTimeout);
|
|
163
|
+
clearTimeout(timeoutTimer);
|
|
164
|
+
if (input && input.toLowerCase() === "y") {
|
|
165
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
166
|
+
shouldUpload = true;
|
|
167
|
+
try {
|
|
168
|
+
await session.bot.deleteMessage(session.channelId, promptMsgId);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
try {
|
|
173
|
+
await session.bot.deleteMessage(session.channelId, promptMsgId);
|
|
174
|
+
} catch (e) {
|
|
175
|
+
}
|
|
176
|
+
return "已取消。";
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
shouldUpload = true;
|
|
180
|
+
}
|
|
181
|
+
if (shouldUpload) {
|
|
182
|
+
try {
|
|
183
|
+
const filename = `${Date.now()}_${Math.floor(Math.random() * 1e3)}${ext}`;
|
|
184
|
+
const filePath = path.join(targetDir, filename);
|
|
185
|
+
fs.writeFileSync(filePath, buffer);
|
|
186
|
+
const successMsgIds = await session.send(`已成功上传至 ${folderName}`);
|
|
187
|
+
if (config.autoWithdrawSuccess && successMsgIds.length > 0) {
|
|
188
|
+
withdrawLater(session, successMsgIds[0], config.successWithdrawTime);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
ctx.logger.error(error);
|
|
192
|
+
return "保存文件出错。";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
ctx.middleware(async (session, next) => {
|
|
197
|
+
const content = session.content.trim();
|
|
198
|
+
if (!content) return next();
|
|
199
|
+
const targetDir = path.join(root, content);
|
|
200
|
+
if (!targetDir.startsWith(root)) return next();
|
|
201
|
+
if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {
|
|
202
|
+
const files = fs.readdirSync(targetDir).filter((file) => /\.(jpg|jpeg|png|gif|webp)$/i.test(file));
|
|
203
|
+
if (files.length === 0) return next();
|
|
204
|
+
const randomFile = files[Math.floor(Math.random() * files.length)];
|
|
205
|
+
const absPath = path.join(targetDir, randomFile);
|
|
206
|
+
return session.send(import_koishi.h.image("file://" + absPath));
|
|
207
|
+
}
|
|
208
|
+
return next();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
__name(apply, "apply");
|
|
212
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
213
|
+
0 && (module.exports = {
|
|
214
|
+
Config,
|
|
215
|
+
apply,
|
|
216
|
+
inject,
|
|
217
|
+
name
|
|
218
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "koishi-plugin-meme-upload-114",
|
|
3
|
+
"description": "图片上传发送",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"chatbot",
|
|
14
|
+
"koishi",
|
|
15
|
+
"plugin"
|
|
16
|
+
],
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"koishi": "^4.18.7"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"puppeteer": "^24.36.0"
|
|
22
|
+
}
|
|
23
|
+
}
|