koishi-plugin-meme-upload-114 0.0.1 → 0.0.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 CHANGED
@@ -11,6 +11,7 @@ export interface Config {
11
11
  defaultTimeout: number;
12
12
  autoWithdrawSuccess: boolean;
13
13
  successWithdrawTime: number;
14
+ associations: string[][];
14
15
  }
15
16
  export declare const Config: Schema<Config>;
16
17
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -45,7 +45,9 @@ var Config = import_koishi.Schema.object({
45
45
  rootDir: import_koishi.Schema.path({ filters: ["directory"], allowCreate: true }).default("data/images").description("图片存储的根目录"),
46
46
  defaultTimeout: import_koishi.Schema.number().default(1e4).description("创建文件夹确认的等待时间(毫秒)"),
47
47
  autoWithdrawSuccess: import_koishi.Schema.boolean().default(true).description("是否自动撤回“上传成功”的提示"),
48
- successWithdrawTime: import_koishi.Schema.number().default(1e4).description("上传成功提示的撤回延迟(毫秒)")
48
+ successWithdrawTime: import_koishi.Schema.number().default(1e4).description("上传成功提示的撤回延迟(毫秒)"),
49
+ // === 新增配置项 ===
50
+ associations: import_koishi.Schema.array(import_koishi.Schema.array(import_koishi.Schema.string())).default([]).description('文件夹关联/分组设置。\n点击添加一行,然后在行内添加多个文件夹名。\n例如:["1", "2"] 表示发送 1 或 2 时,会从这两个文件夹的所有图片中随机选取。')
49
51
  });
50
52
  function apply(ctx, config) {
51
53
  const root = path.resolve(ctx.baseDir, config.rootDir);
@@ -57,27 +59,30 @@ function apply(ctx, config) {
57
59
  <html>
58
60
  <head>
59
61
  <style>
60
- body { font-family: sans-serif; padding: 20px; background: transparent; width: fit-content; }
62
+ body { font-family: "Microsoft YaHei", "SimHei", sans-serif; padding: 20px; background: transparent; width: fit-content; }
61
63
  .container {
62
64
  display: flex;
63
65
  align-items: flex-start;
64
- max-width: 400px;
66
+ max-width: 450px;
67
+ min-width: 260px;
65
68
  background: #ffffff;
66
- padding: 15px;
69
+ padding: 16px 22px;
67
70
  border-radius: 12px;
68
- box-shadow: 0 4px 10px rgba(0,0,0,0.1);
71
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
72
+ border: 1px solid #f2f2f2;
69
73
  }
70
74
  .avatar {
71
75
  width: 50px;
72
76
  height: 50px;
73
77
  border-radius: 50%;
74
- margin-right: 15px;
78
+ margin-right: 16px;
75
79
  border: 1px solid #eee;
76
80
  flex-shrink: 0;
81
+ object-fit: cover;
77
82
  }
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; }
83
+ .content-box { display: flex; flex-direction: column; justify-content: center; min-height: 50px; }
84
+ .name { font-size: 15px; color: #888; margin-bottom: 6px; font-weight: bold; }
85
+ .text { font-size: 18px; color: #333; line-height: 1.5; word-wrap: break-word; white-space: pre-wrap; }
81
86
  </style>
82
87
  </head>
83
88
  <body>
@@ -92,32 +97,58 @@ function apply(ctx, config) {
92
97
  </html>
93
98
  `;
94
99
  }, "generateQuoteHtml");
100
+ const getDisplayName = /* @__PURE__ */ __name(async (session, quote) => {
101
+ const userId = quote.user?.id || quote.author?.id;
102
+ const guildId = session.guildId || quote.guildId;
103
+ if (guildId && userId) {
104
+ try {
105
+ const member = await session.bot.getGuildMember(guildId, userId);
106
+ if (member.nick && member.nick !== userId) return member.nick;
107
+ if (member.nickname && member.nickname !== userId) return member.nickname;
108
+ } catch (e) {
109
+ }
110
+ }
111
+ if (userId) {
112
+ try {
113
+ const user = await session.bot.getUser(userId, guildId);
114
+ if (user.nick && user.nick !== userId) return user.nick;
115
+ if (user.name && user.name !== userId) return user.name;
116
+ if (user.username && user.username !== userId) return user.username;
117
+ } catch (e) {
118
+ }
119
+ }
120
+ return quote.member?.nick || quote.member?.name || quote.author?.nickname || quote.author?.username || userId || "用户";
121
+ }, "getDisplayName");
95
122
  const getImageBuffer = /* @__PURE__ */ __name(async (session) => {
96
123
  let targetImgUrl = null;
97
124
  const currImg = session.elements.find((e) => e.type === "img" || e.type === "image");
98
- if (currImg) {
99
- targetImgUrl = currImg.attrs.src || currImg.attrs.url;
100
- }
125
+ if (currImg) targetImgUrl = currImg.attrs.src || currImg.attrs.url;
101
126
  if (!targetImgUrl && session.quote) {
102
- const quoteElements = session.quote.elements;
103
- const quoteImg = quoteElements.find((e) => e.type === "img" || e.type === "image");
127
+ const quote = session.quote;
128
+ const quoteImg = quote.elements.find((e) => e.type === "img" || e.type === "image");
104
129
  if (quoteImg) {
105
130
  targetImgUrl = quoteImg.attrs.src || quoteImg.attrs.url;
106
131
  } 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 || " ";
132
+ const userId = quote.user?.id || quote.author?.id;
133
+ let avatarUrl = "https://koishi.chat/logo.png";
134
+ if (userId && /^\d+$/.test(userId)) {
135
+ avatarUrl = `https://q1.qlogo.cn/g?b=qq&nk=${userId}&s=640`;
136
+ } else if (quote.author?.avatar) {
137
+ avatarUrl = quote.author.avatar;
138
+ }
139
+ const username = await getDisplayName(session, quote);
140
+ const textContent = quote.content || " ";
111
141
  const html = generateQuoteHtml(avatarUrl, username, textContent);
112
142
  try {
113
143
  const page = await ctx.puppeteer.page();
114
144
  await page.setContent(html);
145
+ await (0, import_koishi.sleep)(200);
115
146
  const element = await page.$(".container");
116
147
  const buffer = await element?.screenshot({ type: "png" });
117
148
  await page.close();
118
149
  return { buffer, ext: ".png" };
119
150
  } catch (e) {
120
- ctx.logger.error("Puppeteer 生成图片失败", e);
151
+ ctx.logger.error("Puppeteer 截图失败", e);
121
152
  return { buffer: null, ext: "" };
122
153
  }
123
154
  }
@@ -131,7 +162,6 @@ function apply(ctx, config) {
131
162
  else if (targetImgUrl.includes(".webp")) ext = ".webp";
132
163
  return { buffer: Buffer.from(buffer), ext };
133
164
  } catch (e) {
134
- ctx.logger.error("下载图片失败", e);
135
165
  return { buffer: null, ext: "" };
136
166
  }
137
167
  }
@@ -144,12 +174,10 @@ function apply(ctx, config) {
144
174
  } catch (e) {
145
175
  }
146
176
  }, "withdrawLater");
147
- ctx.command("上传 <folderName>", "上传图片或引用转换").action(async ({ session }, folderName) => {
177
+ ctx.command("上传 <folderName>").action(async ({ session }, folderName) => {
148
178
  if (!folderName) return "请输入文件夹名称。";
149
179
  const { buffer, ext } = await getImageBuffer(session);
150
- if (!buffer) {
151
- return "无法获取图片。请直接附带图片,或引用含有图片/文字的消息。";
152
- }
180
+ if (!buffer) return "无法获取图片。请附带图片或引用消息。";
153
181
  const targetDir = path.join(root, folderName);
154
182
  let shouldUpload = false;
155
183
  if (!fs.existsSync(targetDir)) {
@@ -189,21 +217,37 @@ function apply(ctx, config) {
189
217
  }
190
218
  } catch (error) {
191
219
  ctx.logger.error(error);
192
- return "保存文件出错。";
220
+ return "保存失败。";
193
221
  }
194
222
  }
195
223
  });
196
224
  ctx.middleware(async (session, next) => {
197
225
  const content = session.content.trim();
198
226
  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));
227
+ let foldersToSearch = [content];
228
+ for (const group of config.associations) {
229
+ if (group.includes(content)) {
230
+ foldersToSearch = group;
231
+ break;
232
+ }
233
+ }
234
+ let allImages = [];
235
+ for (const folder of foldersToSearch) {
236
+ const dirPath = path.join(root, folder);
237
+ if (dirPath.startsWith(root) && fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
238
+ const files = fs.readdirSync(dirPath).filter((file) => /\.(jpg|jpeg|png|gif|webp)$/i.test(file));
239
+ files.forEach((f) => {
240
+ allImages.push({
241
+ path: path.join(dirPath, f),
242
+ folder
243
+ });
244
+ });
245
+ }
246
+ }
247
+ if (allImages.length > 0) {
248
+ const randomImg = allImages[Math.floor(Math.random() * allImages.length)];
249
+ await session.send(import_koishi.h.image("file://" + randomImg.path));
250
+ return;
207
251
  }
208
252
  return next();
209
253
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-meme-upload-114",
3
3
  "description": "图片上传发送",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [