mioku-service-webui 4.4.3 → 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.3",
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);
package/routes/db.ts CHANGED
@@ -306,10 +306,10 @@ export function createServiceConfigRoutes() {
306
306
  return c.json({ ok: true, data: overview });
307
307
  });
308
308
 
309
- app.get("/:name/page", (c) => {
309
+ app.get("/:name/page", async (c) => {
310
310
  const name = c.req.param("name");
311
311
  const manifest = loadServiceConfigPage(name);
312
- const configs = getServiceConfigs(name);
312
+ const configs = await getServiceConfigs(name);
313
313
 
314
314
  if (!manifest) {
315
315
  return c.json({ ok: true, data: null });
@@ -329,16 +329,16 @@ export function createServiceConfigRoutes() {
329
329
  });
330
330
  });
331
331
 
332
- app.get("/:name", (c) => {
332
+ app.get("/:name", async (c) => {
333
333
  const name = c.req.param("name");
334
- return c.json({ ok: true, data: getServiceConfigs(name) });
334
+ return c.json({ ok: true, data: await getServiceConfigs(name) });
335
335
  });
336
336
 
337
337
  app.put("/:name/:config", async (c) => {
338
338
  const serviceName = c.req.param("name");
339
339
  const configName = c.req.param("config");
340
340
  const body = await c.req.json();
341
- updateServiceConfig(serviceName, configName, body);
341
+ await updateServiceConfig(serviceName, configName, body);
342
342
  return c.json({ ok: true });
343
343
  });
344
344