mioku-service-webui 4.4.4 → 4.4.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.
package/database.ts CHANGED
@@ -5,6 +5,14 @@ import { CHAT_DATA_DIR, readJsonFile, writeJsonFile } from "./utils";
5
5
 
6
6
  const DB_PATH = path.join(CHAT_DATA_DIR, "chat.db");
7
7
  const SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
8
+ const STICKER_FILE_PATTERN = /\.(png|jpg|jpeg|gif|webp)$/i;
9
+
10
+ export interface StickerCatalogItem {
11
+ id: string;
12
+ character: string;
13
+ label: string;
14
+ url: string;
15
+ }
8
16
 
9
17
  function openDb(): any {
10
18
  if (!fs.existsSync(DB_PATH)) {
@@ -299,6 +307,40 @@ export function listMemeTree(): Record<string, Record<string, string[]>> {
299
307
  return tree;
300
308
  }
301
309
 
310
+ export function listStickerCatalog(): StickerCatalogItem[] {
311
+ const root = path.join(CHAT_DATA_DIR, "meme");
312
+ if (!fs.existsSync(root)) return [];
313
+
314
+ const items: StickerCatalogItem[] = [];
315
+ const visit = (dir: string, character: string) => {
316
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
317
+ const entryPath = path.join(dir, entry.name);
318
+ if (entry.isDirectory()) {
319
+ visit(entryPath, character);
320
+ continue;
321
+ }
322
+ if (!entry.isFile() || !STICKER_FILE_PATTERN.test(entry.name)) continue;
323
+
324
+ const id = path.relative(root, entryPath).split(path.sep).join("/");
325
+ const label = path
326
+ .basename(entry.name, path.extname(entry.name))
327
+ .replace(/[_-]+/g, " ")
328
+ .trim();
329
+ const url = `/meme/${id
330
+ .split("/")
331
+ .map((segment) => encodeURIComponent(segment))
332
+ .join("/")}`;
333
+ items.push({ id, character, label, url });
334
+ }
335
+ };
336
+
337
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
338
+ if (entry.isDirectory()) visit(path.join(root, entry.name), entry.name);
339
+ }
340
+
341
+ return items.sort((a, b) => a.id.localeCompare(b.id));
342
+ }
343
+
302
344
  export function savePluginConfig(pluginName: string, fileName: string, value: any): void {
303
345
  const filePath = path.join(process.cwd(), "config", pluginName, `${fileName}.json`);
304
346
  writeJsonFile(filePath, value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-service-webui",
3
- "version": "4.4.4",
3
+ "version": "4.4.5",
4
4
  "description": "Mioku WebUI 服务",
5
5
  "main": "index.ts",
6
6
  "type": "module",
package/routes/ai.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Hono } from "hono";
2
2
  import { logger } from "mioki";
3
3
  import { getChatConfig, updateChatConfig } from "../system";
4
+ import { listStickerCatalog } from "../database";
4
5
  import aiService from "mioku-service-ai";
5
6
  import type { AIUsageRange } from "mioku-service-ai/usage/types";
6
7
 
@@ -96,6 +97,28 @@ function normalizePersonalizationConfig(input: any): any {
96
97
  delete rawExpression.maxExpressions;
97
98
 
98
99
  data.expression = rawExpression;
100
+
101
+ const rawEmoji =
102
+ data.emoji && typeof data.emoji === "object" && !Array.isArray(data.emoji)
103
+ ? { ...data.emoji }
104
+ : {};
105
+ rawEmoji.enabled =
106
+ typeof rawEmoji.enabled === "boolean" ? rawEmoji.enabled : true;
107
+ rawEmoji.characters = Array.isArray(rawEmoji.characters)
108
+ ? rawEmoji.characters
109
+ .map(String)
110
+ .map((value: string) => value.trim())
111
+ .filter(Boolean)
112
+ : [];
113
+ rawEmoji.stickers = Array.isArray(rawEmoji.stickers)
114
+ ? rawEmoji.stickers
115
+ .map(String)
116
+ .map((value: string) => value.trim())
117
+ .filter(Boolean)
118
+ : [];
119
+ delete rawEmoji.useAISelection;
120
+ data.emoji = rawEmoji;
121
+
99
122
  return data;
100
123
  }
101
124
 
@@ -189,6 +212,10 @@ export function createAIRoutes() {
189
212
  });
190
213
  });
191
214
 
215
+ app.get("/stickers", (c) =>
216
+ c.json({ ok: true, data: listStickerCatalog() }),
217
+ );
218
+
192
219
  app.get("/usage", (c) => {
193
220
  if (!aiService?.api?.getUsageSummary) {
194
221
  return c.json({ ok: false, error: "AI_USAGE_UNAVAILABLE" }, 503);