kokkoro-plugin-hitokoto 3.0.2 → 3.0.4

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/README.md CHANGED
@@ -27,20 +27,14 @@ bun add kokkoro-plugin-hitokoto
27
27
 
28
28
  ## 快捷方式
29
29
 
30
- 快捷方式匹配以下正则表达式:
31
-
32
- ```regexp
33
- /^来点(?<types>.+)?骚话$/
34
- ```
35
-
36
- 其中的 `types` 表示「来点」和「骚话」之间可选的类型名称。以下消息都能触发快捷方式:
30
+ 发送「来点骚话」获取随机语句,也可以在「来点」和「骚话」之间加入类型名称:
37
31
 
38
32
  ```text
39
33
  来点骚话
40
34
  来点诗词骚话
41
35
  ```
42
36
 
43
- 要让普通群消息触发快捷方式,需要在对应群聊中开启「获取群内全部消息」权限。未开启时,插件只能处理 @ 机器人的群消息。
37
+ 要让普通群消息触发快捷方式,需要开启「获取群内全部消息」权限。
44
38
 
45
39
  ## API
46
40
 
@@ -54,7 +48,9 @@ const sentence = await fetchSentence('i');
54
48
 
55
49
  `fetchSentence()` 接收一言接口的类型代码或代码数组。`'i'` 表示诗词,`['a', 'b']` 表示动画和漫画。传入空数组时,函数不限制语句类型。
56
50
 
57
- 请求失败时,`fetchSentence()` 会抛出 `Error`。一言错误响应使用上游的 `message`,其他请求错误使用 HTTP 状态码。包同时导出 `Sentence`、`SentenceType` 和 `ErrorResponse` 类型,以及 `isErrorResponse()` 类型守卫。
51
+ 收到非 2xx 响应时,`fetchSentence()` 会抛出 `Error`,优先使用一言错误响应中的 `message`,否则使用 HTTP 状态码。网络请求或成功响应的 JSON 解析失败时,函数直接传播原始错误。
52
+
53
+ 包同时导出 `Sentence`、`SentenceType` 和 `ErrorResponse` 类型,以及 `isErrorResponse()` 类型守卫。
58
54
 
59
55
  ## 环境变量
60
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kokkoro-plugin-hitokoto",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "description": "Hitokoto 一言,随机获取动漫、小说、诗词等类型的语句。",
5
5
  "keywords": [
6
6
  "bot",
package/src/index.ts CHANGED
@@ -1,23 +1,15 @@
1
1
  import { useCommand, useLogger } from '@kokkoro/core';
2
2
 
3
- import { fetchSentence, HITOKOTO_API, resolveTypeCodes } from './service';
3
+ import { fetchSentence, resolveTypeCodes } from './service';
4
4
 
5
5
  const logger = useLogger();
6
6
 
7
7
  export default () => {
8
8
  useCommand('/一言 [types]...', async context => {
9
- const payload = { c: resolveTypeCodes(context.args.types) };
10
-
11
- logger.debug('发送 Hitokoto 请求', {
12
- method: 'GET',
13
- url: HITOKOTO_API,
14
- payload,
15
- });
16
-
17
- const sentence = await fetchSentence(payload.c);
9
+ const types = resolveTypeCodes(context.args.types);
10
+ const sentence = await fetchSentence(types, logger);
18
11
  const { from, hitokoto, id, type } = sentence;
19
12
 
20
- logger.debug('收到 Hitokoto 响应', sentence);
21
13
  logger.info('已获取一言', { id, type });
22
14
 
23
15
  return `『${hitokoto}』——「${from}」`;
package/src/service.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { type Logger } from '@kokkoro/core';
2
+
1
3
  /** 一言 v1 语句接口的请求地址。 */
2
4
  export const HITOKOTO_API = 'https://v1.hitokoto.cn';
3
5
 
@@ -27,6 +29,9 @@ export const TYPE_NAMES = Object.keys(TYPE_CODES).join('、');
27
29
  /** 一言接口 `c` 参数接受的句子类型代码。 */
28
30
  export type SentenceType = (typeof TYPE_CODES)[keyof typeof TYPE_CODES];
29
31
 
32
+ /** 判断名称是否为 {@link TYPE_CODES} 自身定义的键。 */
33
+ export const isTypeName = (name: string): name is keyof typeof TYPE_CODES => Object.hasOwn(TYPE_CODES, name);
34
+
30
35
  /**
31
36
  * 将中文句子类型名称转换为一言接口的类型代码。
32
37
  *
@@ -47,12 +52,10 @@ export type SentenceType = (typeof TYPE_CODES)[keyof typeof TYPE_CODES];
47
52
  */
48
53
  export function resolveTypeCodes(names: string[]): SentenceType[] {
49
54
  return names.map(name => {
50
- const code: SentenceType = TYPE_CODES[<keyof typeof TYPE_CODES>name];
51
-
52
- if (!code) {
55
+ if (!isTypeName(name)) {
53
56
  throw new Error(`类型「${name}」不是有效值,支持的句子类型有:${TYPE_NAMES}`);
54
57
  }
55
- return code;
58
+ return TYPE_CODES[name];
56
59
  });
57
60
  }
58
61
 
@@ -150,6 +153,7 @@ export function isErrorResponse(value: unknown): value is ErrorResponse {
150
153
  * 显式传入空数组时,不会读取环境变量,也不会发送 `c` 查询参数。
151
154
  *
152
155
  * @param types - 单个句子类型代码或句子类型代码数组。
156
+ * @param logger - 在 debug 日志中记录实际请求参数和解析后的接口响应。
153
157
  * @returns 一个 Promise,成功时返回完整的 {@link Sentence}。
154
158
  * @throws 收到非 2xx 响应时抛出 `Error`。如果响应体符合 {@link ErrorResponse},错误信息使用其 `message`,
155
159
  * 否则错误信息包含 HTTP 状态码。
@@ -164,18 +168,27 @@ export function isErrorResponse(value: unknown): value is ErrorResponse {
164
168
  *
165
169
  * @see {@link https://developer.hitokoto.cn/sentence/ | 一言语句接口}
166
170
  */
167
- export async function fetchSentence(types?: SentenceType | SentenceType[]): Promise<Sentence> {
171
+ export async function fetchSentence(types?: SentenceType | SentenceType[], logger?: Logger): Promise<Sentence> {
172
+ const payload = { c: resolveTypes(types) };
168
173
  const url = new URL(HITOKOTO_API);
169
174
 
170
- for (const type of resolveTypes(types)) {
175
+ for (const type of payload.c) {
171
176
  url.searchParams.append('c', type);
172
177
  }
178
+ logger?.debug('发送 Hitokoto 请求', {
179
+ method: 'GET',
180
+ url: HITOKOTO_API,
181
+ payload,
182
+ });
183
+
173
184
  const response = await fetch(url);
185
+ const body = response.ok ? await response.json() : await response.json().catch(() => null);
186
+
187
+ logger?.debug('收到 Hitokoto 响应', body);
174
188
 
175
189
  if (response.ok) {
176
- return <Sentence>await response.json();
190
+ return <Sentence>body;
177
191
  }
178
- const body = await response.json().catch(() => null);
179
192
 
180
193
  if (isErrorResponse(body)) {
181
194
  throw new Error(body.message);