koishi-plugin-phimg2 1.0.4 → 1.0.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/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Context, Schema } from 'koishi';
2
+ export declare const name = "phimg2";
2
3
  export declare const inject: string[];
3
4
  declare module 'koishi' {
4
5
  interface Tables {
@@ -11,13 +12,11 @@ export interface Config {
11
12
  defaultTags: string[];
12
13
  enabledByDefault: boolean;
13
14
  useGlobalTagsByDefault: boolean;
14
- proxy: string;
15
- timeout: number;
16
15
  filterId: number;
17
16
  }
18
17
  export declare const Config: Schema<Config>;
19
18
  interface GroupConfig {
20
- id?: number;
19
+ id: number;
21
20
  groupId: string;
22
21
  enabled: boolean;
23
22
  useGlobalTags: boolean;
package/lib/index.js CHANGED
@@ -1,13 +1,11 @@
1
- var __create = Object.create;
2
1
  var __defProp = Object.defineProperty;
3
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
5
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
6
  var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
7
+ for (var name2 in all)
8
+ __defProp(target, name2, { get: all[name2], enumerable: true });
11
9
  };
12
10
  var __copyProps = (to, from, except, desc) => {
13
11
  if (from && typeof from === "object" || typeof from === "function") {
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
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
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
@@ -32,12 +22,13 @@ var src_exports = {};
32
22
  __export(src_exports, {
33
23
  Config: () => Config,
34
24
  apply: () => apply,
35
- inject: () => inject
25
+ inject: () => inject,
26
+ name: () => name
36
27
  });
37
28
  module.exports = __toCommonJS(src_exports);
38
29
  var import_koishi = require("koishi");
39
- var import_axios = __toESM(require("axios"));
40
- var inject = ["database"];
30
+ var name = "phimg2";
31
+ var inject = ["database", "http"];
41
32
  var translationTable = {
42
33
  ";": ";",
43
34
  ":": ":",
@@ -59,14 +50,12 @@ function translateText(text) {
59
50
  }
60
51
  __name(translateText, "translateText");
61
52
  var Config = import_koishi.Schema.object({
62
- apiKey: import_koishi.Schema.string().description("Philomena API 密钥").default(""),
63
- apiUrl: import_koishi.Schema.string().description("Philomena API 域名 (例如 derpibooru.org)").default("derpibooru.org"),
53
+ apiKey: import_koishi.Schema.string().description("Philomena API 密钥").role("secret").default(""),
54
+ apiUrl: import_koishi.Schema.string().description("Philomena API 域名 (无需 https://)").default("derpibooru.org"),
64
55
  defaultTags: import_koishi.Schema.array(String).description("全局默认标签").default(["safe"]),
65
56
  enabledByDefault: import_koishi.Schema.boolean().description("新群聊默认启用搜图功能").default(true),
66
57
  useGlobalTagsByDefault: import_koishi.Schema.boolean().description("新群聊默认启用全局标签").default(true),
67
- proxy: import_koishi.Schema.string().description("代理服务器 (例如 http://127.0.0.1:7890)").default(""),
68
- timeout: import_koishi.Schema.number().description("请求超时时间 (秒)").default(30),
69
- filterId: import_koishi.Schema.number().description("搜索使用的 Filter ID (例如 56027 为 Everything)").default(100073)
58
+ filterId: import_koishi.Schema.number().description("搜索使用的 Filter ID (例如 100073)").default(100073)
70
59
  });
71
60
  function apply(ctx, config) {
72
61
  ctx.model.extend("phimg_config", {
@@ -83,86 +72,72 @@ function apply(ctx, config) {
83
72
  const getGroupConfig = /* @__PURE__ */ __name(async (groupId) => {
84
73
  let [groupConfig] = await ctx.database.get("phimg_config", { groupId });
85
74
  if (!groupConfig) {
86
- groupConfig = await ctx.database.create("phimg_config", {
87
- groupId,
88
- enabled: config.enabledByDefault,
89
- useGlobalTags: config.useGlobalTagsByDefault,
90
- customTags: []
91
- });
75
+ try {
76
+ groupConfig = await ctx.database.create("phimg_config", {
77
+ groupId,
78
+ enabled: config.enabledByDefault,
79
+ useGlobalTags: config.useGlobalTagsByDefault,
80
+ customTags: []
81
+ });
82
+ } catch (e) {
83
+ [groupConfig] = await ctx.database.get("phimg_config", { groupId });
84
+ }
92
85
  }
93
86
  return groupConfig;
94
87
  }, "getGroupConfig");
95
88
  const updateGroupConfig = /* @__PURE__ */ __name(async (groupId, data) => {
89
+ await getGroupConfig(groupId);
96
90
  await ctx.database.set("phimg_config", { groupId }, data);
97
91
  }, "updateGroupConfig");
98
92
  const makeRequest = /* @__PURE__ */ __name(async (method, params) => {
99
- let domain = config.apiUrl;
100
- if (domain.includes("://")) {
101
- domain = domain.split("://")[1];
102
- }
103
- domain = domain.split("/")[0];
104
- const url = `https://${domain}/api/v1/json/search/${method}`;
105
- const axiosConfig = {
106
- headers: {
107
- "Accept": "application/json",
108
- "User-Agent": "Phimg for Koishi"
109
- },
110
- timeout: config.timeout * 1e3,
111
- params: {
112
- ...params,
113
- filter_id: config.filterId
114
- }
93
+ const host = config.apiUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
94
+ const endpoint = `https://${host}/api/v1/json/search/${method}`;
95
+ const queryParams = {
96
+ filter_id: config.filterId
115
97
  };
116
- if (config.proxy) {
117
- try {
118
- const urlParsed = new URL(config.proxy);
119
- axiosConfig.proxy = {
120
- host: urlParsed.hostname,
121
- port: parseInt(urlParsed.port),
122
- protocol: urlParsed.protocol.replace(":", "")
123
- };
124
- } catch (e) {
125
- ctx.logger.warn(`Invalid proxy URL: ${config.proxy}`);
126
- }
98
+ if (params.key) {
99
+ queryParams.key = params.key;
100
+ delete params.key;
127
101
  }
128
102
  try {
129
- let response;
103
+ let responseData;
130
104
  if (method === "images") {
131
- response = await import_axios.default.get(url, axiosConfig);
105
+ responseData = await ctx.http.get(endpoint, {
106
+ params: { ...queryParams, ...params },
107
+ headers: { "User-Agent": "Phimg for Koishi" }
108
+ });
132
109
  } else {
133
- const formData = new URLSearchParams();
134
- for (const key in params) {
135
- formData.append(key, params[key]);
136
- }
137
- response = await import_axios.default.post(url, formData, axiosConfig);
110
+ responseData = await ctx.http.post(endpoint, params, {
111
+ params: queryParams,
112
+ headers: {
113
+ "User-Agent": "Phimg for Koishi",
114
+ "Content-Type": "application/x-www-form-urlencoded"
115
+ }
116
+ });
138
117
  }
139
- if (!response.data.images || response.data.images.length === 0) {
118
+ if (!responseData.images || responseData.images.length === 0) {
140
119
  throw new Error("未找到匹配的图片");
141
120
  }
142
- return response.data;
121
+ return responseData;
143
122
  } catch (error) {
144
- if (import_axios.default.isAxiosError(error)) {
145
- if (error.response?.status === 404) throw new Error("未找到匹配的图片");
146
- throw new Error(`API 请求失败: ${error.message}`);
147
- }
148
- throw error;
123
+ if (error.response?.status === 404) throw new Error("未找到匹配的图片");
124
+ ctx.logger("phimg").warn(error);
125
+ throw new Error(`${error.message}`);
149
126
  }
150
127
  }, "makeRequest");
151
128
  const VIDEO_TYPES = ["webm", "mp4"];
152
129
  const getMediaElement = /* @__PURE__ */ __name((selected) => {
130
+ if (!selected?.representations) return import_koishi.h.text("图片数据解析错误");
153
131
  const file = selected.representations.full;
154
132
  const url = file.endsWith(".webm") ? selected.representations.medium : selected.representations.large;
155
133
  const fileType = file.split(".").pop()?.toLowerCase() || "";
156
134
  if (VIDEO_TYPES.includes(fileType)) {
157
135
  return import_koishi.h.video(url);
158
- } else {
159
- return import_koishi.h.image(url);
160
136
  }
137
+ return import_koishi.h.image(url);
161
138
  }, "getMediaElement");
162
139
  const searchHelp = `用法: /搜图 [tags|distance]
163
140
 
164
- 输入标签为tags搜图;输入匹配距离为图搜图
165
-
166
141
  可选项:
167
142
  --tags 获取当前群聊内置标签列表
168
143
  --status 获取当前群聊的搜图功能状态
@@ -172,12 +147,8 @@ function apply(ctx, config) {
172
147
  --sd [sd] 排序方向,默认为desc
173
148
  --i [index] 选择结果索引,默认为-1(即随机)
174
149
 
175
- 示例:
176
- /搜图 [tags] # 直接通过标签搜索图片
177
-
178
150
  提示:
179
- 图搜图使用方式为引用图片,默认匹配距离为0.25
180
- 所有参数及变量全部遵循呆站(Philomena系图站)搜索API规范`;
151
+ 图搜图使用方式为引用图片,默认匹配距离为0.25`;
181
152
  const configHelp = `用法: 搜图-c [选项]
182
153
 
183
154
  可选项:
@@ -186,11 +157,10 @@ function apply(ctx, config) {
186
157
  --on 开启当前群聊的搜图功能
187
158
  --off 关闭当前群聊的搜图功能
188
159
  --onglobal 启用全局标签
189
- --offglobal 关闭全局标签
190
- `;
191
- ctx.command("搜图 [...params]", "从图站搜索图片").option("tags", "--tags 获取当前群聊内置标签列表").option("status", "--status 获取当前群聊的搜图功能状态").option("pp", "--pp <per_page:number> 每页结果数量,默认为50", { fallback: 50 }).option("p", "--p <page:number> 页码,默认为1", { fallback: 1 }).option("sf", "--sf <sf:string> 排序字段,默认为score", { fallback: "score" }).option("sd", "--sd <sd:string> 排序方向,默认为desc", { fallback: "desc" }).option("i", "--i <index:number> 选择结果索引,默认为-1(即随机)", { fallback: -1 }).action(async ({ session, options }, ...paramsArray) => {
160
+ --offglobal 关闭全局标签`;
161
+ ctx.command("搜图 [...params]", "从图站搜索图片").option("tags", "--tags").option("status", "--status").option("pp", "--pp <per_page:number>", { fallback: 50 }).option("p", "--p <page:number>", { fallback: 1 }).option("sf", "--sf <sf:string>", { fallback: "score" }).option("sd", "--sd <sd:string>", { fallback: "desc" }).option("i", "--i <index:number>", { fallback: -1 }).action(async ({ session, options }, ...paramsArray) => {
192
162
  if (!session?.guildId) return "搜图仅限群聊使用。";
193
- let params = paramsArray.join(" ");
163
+ let params = paramsArray.join(" ").trim();
194
164
  if (!params && !session.quote && !options.tags && !options.status) {
195
165
  return searchHelp;
196
166
  }
@@ -198,9 +168,9 @@ function apply(ctx, config) {
198
168
  const groupConfig = await getGroupConfig(groupId);
199
169
  if (options.status) {
200
170
  return `当前群聊搜图功能状态:
201
- 启用:${groupConfig.enabled}
202
- 标签:${groupConfig.customTags.join(", ") || "无"}
203
- 全局标签:${groupConfig.useGlobalTags ? "启用" : "禁用"}`;
171
+ 启用:${groupConfig.enabled}
172
+ 标签:${groupConfig.customTags.join(", ") || "无"}
173
+ 全局标签:${groupConfig.useGlobalTags ? "启用" : "禁用"}`;
204
174
  }
205
175
  if (options.tags) {
206
176
  return `当前群聊内置标签:${groupConfig.customTags.join(", ") || "无"}`;
@@ -212,17 +182,18 @@ function apply(ctx, config) {
212
182
  let imageUrl;
213
183
  const quote = session.quote;
214
184
  if (quote) {
215
- const imgElement = import_koishi.h.select(quote.content, "image")[0];
185
+ const imgElement = import_koishi.h.select(quote.content, "image")[0] || import_koishi.h.select(quote.content, "img")[0];
216
186
  if (imgElement) {
217
- imageUrl = imgElement.attrs.url;
187
+ imageUrl = imgElement.attrs.url || imgElement.attrs.src;
218
188
  }
219
189
  }
220
190
  try {
221
191
  if (imageUrl) {
222
192
  let distance = 0.25;
223
193
  if (params) {
224
- if (!isNaN(Number(params))) {
225
- distance = Number(params);
194
+ const parsed = Number(params);
195
+ if (!isNaN(parsed)) {
196
+ distance = parsed;
226
197
  } else {
227
198
  return "图片搜索仅支持数字参数,表示相似度距离(distance)。";
228
199
  }
@@ -237,22 +208,23 @@ function apply(ctx, config) {
237
208
  if (images.length > 10) {
238
209
  return `搜索到过多图片 (${images.length} 张),请尝试减小距离参数。`;
239
210
  }
240
- let result = "";
241
- result += (0, import_koishi.h)("at", { id: session.userId }).toString();
242
- result += `
243
- distance: ${distance}`;
211
+ if (images.length === 0) return "未找到匹配的图片";
212
+ const result = [(0, import_koishi.h)("at", { id: session.userId }), import_koishi.h.text(`
213
+ distance: ${distance}
214
+ `)];
244
215
  for (const img of images) {
245
- result += getMediaElement(img).toString();
246
- result += `
247
- id: ${img.id}
248
- score: ${img.score}`;
216
+ result.push(getMediaElement(img));
217
+ result.push(import_koishi.h.text(`
218
+ id: ${img.id} | score: ${img.score}
219
+ `));
249
220
  }
250
221
  return result;
251
222
  } else {
252
223
  const userTags = params ? params.split(",").map((t) => t.trim()).filter((t) => t) : [];
253
224
  const globalTags = groupConfig.useGlobalTags ? config.defaultTags : [];
254
225
  const groupTags = groupConfig.customTags;
255
- const allTags = [.../* @__PURE__ */ new Set([...groupTags, ...globalTags, ...userTags])];
226
+ const allTags = Array.from(/* @__PURE__ */ new Set([...groupTags, ...globalTags, ...userTags]));
227
+ if (allTags.length === 0 && !params) return "请输入搜索标签。";
256
228
  const queryParams = {
257
229
  q: allTags.join(", "),
258
230
  key: config.apiKey,
@@ -266,40 +238,33 @@ score: ${img.score}`;
266
238
  let index = options.i;
267
239
  let additionalMsg = "";
268
240
  if (index < 0 || index >= images.length) {
269
- if (index >= 0) {
270
- additionalMsg = `索引 ${index} 超出单页范围,已随机选择图片`;
271
- }
241
+ if (index >= 0) additionalMsg = `索引 ${index} 超出单页范围,已随机选择图片`;
272
242
  index = Math.floor(Math.random() * images.length);
273
243
  }
274
244
  const selected = images[index];
275
- let result = "";
276
- result += (0, import_koishi.h)("at", { id: session.userId }).toString();
277
- result += getMediaElement(selected).toString();
278
- result += `
279
- id: ${selected.id}
280
- score: ${selected.score}`;
281
- result += `
282
- tags: ${queryParams.q}`;
283
- if (additionalMsg) result += `
284
- 提示:${additionalMsg}`;
285
- return result;
245
+ return [
246
+ (0, import_koishi.h)("at", { id: session.userId }),
247
+ getMediaElement(selected),
248
+ import_koishi.h.text(`
249
+ id: ${selected.id} | score: ${selected.score}`),
250
+ import_koishi.h.text(`
251
+ tags: ${queryParams.q}`),
252
+ additionalMsg ? import_koishi.h.text(`
253
+ 提示:${additionalMsg}`) : null
254
+ ];
286
255
  }
287
256
  } catch (error) {
288
257
  return error instanceof Error ? error.message : String(error);
289
258
  }
290
259
  });
291
260
  const confirmOffGlobal = /* @__PURE__ */ new Set();
292
- ctx.command("搜图-c", "配置搜图功能", { authority: 3 }).option("on", "--on 开启当前群聊的搜图功能").option("off", "--off 关闭当前群聊的搜图功能").option("onglobal", "--onglobal 启用全局标签").option("offglobal", "--offglobal 关闭全局标签").option("add", "--add <tags:string> 添加标签,多个标签用逗号分隔").option("rm", "--rm <tags:string> 删除标签,多个标签用逗号分隔").action(async ({ session, options }) => {
261
+ ctx.command("搜图-c", "配置搜图功能", { authority: 3 }).option("on", "--on").option("off", "--off").option("onglobal", "--onglobal").option("offglobal", "--offglobal").option("add", "--add <tags:string>").option("rm", "--rm <tags:string>").action(async ({ session, options }) => {
293
262
  if (!session?.guildId) return "搜图配置仅限群聊使用。";
294
- if (Object.keys(options).length === 0) {
295
- return configHelp;
296
- }
263
+ if (Object.keys(options).length === 0) return configHelp;
297
264
  const groupId = session.guildId;
298
265
  const groupConfig = await getGroupConfig(groupId);
299
266
  let response = "";
300
- if (options.on && options.off) {
301
- return "不能同时开启和关闭搜图功能,请选择一个操作";
302
- }
267
+ if (options.on && options.off) return "不能同时开启和关闭搜图功能";
303
268
  if (options.on) {
304
269
  await updateGroupConfig(groupId, { enabled: true });
305
270
  response += "搜图功能已在本群开启\n";
@@ -307,9 +272,7 @@ tags: ${queryParams.q}`;
307
272
  await updateGroupConfig(groupId, { enabled: false });
308
273
  response += "搜图功能已在本群关闭\n";
309
274
  }
310
- if (options.onglobal && options.offglobal) {
311
- return "不能同时开启和关闭全局标签,请选择一个操作";
312
- }
275
+ if (options.onglobal && options.offglobal) return "不能同时开启和关闭全局标签";
313
276
  if (options.onglobal) {
314
277
  await updateGroupConfig(groupId, { useGlobalTags: true });
315
278
  response += "全局标签已启用\n";
@@ -318,28 +281,27 @@ tags: ${queryParams.q}`;
318
281
  if (!confirmOffGlobal.has(confirmKey)) {
319
282
  confirmOffGlobal.add(confirmKey);
320
283
  ctx.setTimeout(() => confirmOffGlobal.delete(confirmKey), 6e4);
321
- return "关闭全局标签,机器人将会搜出非safe图片\n请自行承担可能的炸群风险\n再输入一遍指令确认关闭";
284
+ return "关闭全局标签,机器人将会搜出非safe图片\n请自行承担风险,再次输入指令确认关闭";
322
285
  }
323
286
  confirmOffGlobal.delete(confirmKey);
324
287
  await updateGroupConfig(groupId, { useGlobalTags: false });
325
288
  response += "全局标签已禁用\n";
326
289
  }
327
290
  if (options.add || options.rm) {
328
- const currentTags = groupConfig.customTags;
329
- let newTags = [...currentTags];
291
+ let newTags = [...groupConfig.customTags];
330
292
  if (options.add) {
331
- const tagsToAdd = translateText(options.add).split(",").map((t) => t.trim()).filter((t) => t);
293
+ const tagsToAdd = translateText(options.add).split(/[,,]/).map((t) => t.trim()).filter((t) => t);
332
294
  newTags = [.../* @__PURE__ */ new Set([...newTags, ...tagsToAdd])];
333
295
  }
334
296
  if (options.rm) {
335
- const tagsToRm = translateText(options.rm).split(",").map((t) => t.trim()).filter((t) => t);
297
+ const tagsToRm = translateText(options.rm).split(/[,,]/).map((t) => t.trim()).filter((t) => t);
336
298
  newTags = newTags.filter((t) => !tagsToRm.includes(t));
337
299
  }
338
300
  await updateGroupConfig(groupId, { customTags: newTags });
339
301
  response += `修改成功,本群标签现为: ${newTags.join(", ") || "无"}
340
302
  `;
341
303
  }
342
- return response.trim() || '请输入有效的配置选项。可用 "搜图-c --help" 查看帮助。';
304
+ return response.trim();
343
305
  });
344
306
  }
345
307
  __name(apply, "apply");
@@ -347,5 +309,6 @@ __name(apply, "apply");
347
309
  0 && (module.exports = {
348
310
  Config,
349
311
  apply,
350
- inject
312
+ inject,
313
+ name
351
314
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-phimg2",
3
- "description": "该插件可以让机器人通过Philomena API在使用Philomena搭建的图站上使用标签(tags)搜图",
4
- "version": "1.0.4",
3
+ "description": "Koishi 插件,让机器人通过Philomena API在使用Philomena搭建的图站上使用标签(tags)搜图或图搜图",
4
+ "version": "1.0.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [