@zhin.js/plugin-group-suite 1.1.14 → 1.1.16

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.
Files changed (97) hide show
  1. package/README.md +12 -0
  2. package/commands/checkin/index.js +3 -3
  3. package/commands/checkin/index.ts +3 -3
  4. package/commands/forget/index.js +3 -3
  5. package/commands/forget/index.ts +3 -3
  6. package/commands/keyword-add/[keyword]/index.js +4 -3
  7. package/commands/keyword-add/[keyword]/index.ts +4 -3
  8. package/commands/keyword-list/index.js +4 -3
  9. package/commands/keyword-list/index.ts +4 -3
  10. package/commands/keyword-remove/[keyword]/index.js +5 -4
  11. package/commands/keyword-remove/[keyword]/index.ts +5 -4
  12. package/commands/mypoints/index.js +3 -3
  13. package/commands/mypoints/index.ts +3 -3
  14. package/commands/mystats/index.js +3 -3
  15. package/commands/mystats/index.ts +3 -3
  16. package/commands/rank/index.js +3 -3
  17. package/commands/rank/index.ts +3 -3
  18. package/commands/stats/index.js +6 -5
  19. package/commands/stats/index.ts +6 -5
  20. package/commands/stats-card/index.js +4 -3
  21. package/commands/stats-card/index.ts +4 -3
  22. package/commands/stats-rank/index.js +6 -5
  23. package/commands/stats-rank/index.ts +6 -5
  24. package/commands/stats-week/index.js +6 -5
  25. package/commands/stats-week/index.ts +6 -5
  26. package/commands/teach/index.js +3 -3
  27. package/commands/teach/index.ts +3 -3
  28. package/commands/teach-list/[[page]]/index.js +3 -3
  29. package/commands/teach-list/[[page]]/index.ts +3 -3
  30. package/commands/teach-regex/index.js +3 -3
  31. package/commands/teach-regex/index.ts +3 -3
  32. package/handlers/welcome/index.js +9 -1
  33. package/handlers/welcome/index.ts +8 -1
  34. package/lib/analysis-card.d.ts +1 -1
  35. package/lib/analysis-card.d.ts.map +1 -1
  36. package/lib/analysis-card.js +12 -11
  37. package/lib/analysis-card.js.map +1 -1
  38. package/lib/analysis.d.ts +2 -2
  39. package/lib/analysis.d.ts.map +1 -1
  40. package/lib/analysis.js +14 -20
  41. package/lib/analysis.js.map +1 -1
  42. package/lib/checkin-lib.d.ts +1 -1
  43. package/lib/checkin-lib.d.ts.map +1 -1
  44. package/lib/checkin-lib.js +23 -22
  45. package/lib/checkin-lib.js.map +1 -1
  46. package/lib/conversation-scope.d.ts +1 -0
  47. package/lib/conversation-scope.d.ts.map +1 -1
  48. package/lib/conversation-scope.js.map +1 -1
  49. package/lib/i18n.d.ts +2 -0
  50. package/lib/i18n.d.ts.map +1 -0
  51. package/lib/i18n.js +480 -0
  52. package/lib/i18n.js.map +1 -0
  53. package/lib/keywords.d.ts +1 -0
  54. package/lib/keywords.d.ts.map +1 -1
  55. package/lib/keywords.js +8 -7
  56. package/lib/keywords.js.map +1 -1
  57. package/lib/shared-runtime.d.ts +1 -0
  58. package/lib/shared-runtime.d.ts.map +1 -1
  59. package/lib/shared-runtime.js +2 -1
  60. package/lib/shared-runtime.js.map +1 -1
  61. package/lib/stats-card.d.ts +2 -2
  62. package/lib/stats-card.d.ts.map +1 -1
  63. package/lib/stats-card.js +15 -14
  64. package/lib/stats-card.js.map +1 -1
  65. package/lib/stats-data.d.ts +3 -2
  66. package/lib/stats-data.d.ts.map +1 -1
  67. package/lib/stats-data.js +13 -12
  68. package/lib/stats-data.js.map +1 -1
  69. package/lib/stats-lib.d.ts.map +1 -1
  70. package/lib/stats-lib.js +8 -7
  71. package/lib/stats-lib.js.map +1 -1
  72. package/lib/teach-lib.d.ts +1 -1
  73. package/lib/teach-lib.d.ts.map +1 -1
  74. package/lib/teach-lib.js +27 -26
  75. package/lib/teach-lib.js.map +1 -1
  76. package/package.json +12 -12
  77. package/skills/checkin/tools/checkin_query/index.js +5 -4
  78. package/skills/checkin/tools/checkin_query/index.ts +5 -4
  79. package/skills/checkin/tools/checkin_rank/index.js +4 -3
  80. package/skills/checkin/tools/checkin_rank/index.ts +4 -3
  81. package/skills/group-daily-analysis/tools/group_announce/index.js +3 -2
  82. package/skills/group-daily-analysis/tools/group_announce/index.ts +3 -2
  83. package/skills/group-daily-analysis/tools/stats_query/index.js +4 -3
  84. package/skills/group-daily-analysis/tools/stats_query/index.ts +4 -3
  85. package/skills/group-daily-analysis/tools/stats_user/index.js +4 -3
  86. package/skills/group-daily-analysis/tools/stats_user/index.ts +4 -3
  87. package/src/analysis-card.ts +13 -11
  88. package/src/analysis.ts +15 -13
  89. package/src/checkin-lib.ts +23 -21
  90. package/src/conversation-scope.ts +1 -0
  91. package/src/i18n.ts +480 -0
  92. package/src/keywords.ts +9 -7
  93. package/src/shared-runtime.ts +3 -1
  94. package/src/stats-card.ts +17 -16
  95. package/src/stats-data.ts +14 -12
  96. package/src/stats-lib.ts +8 -7
  97. package/src/teach-lib.ts +27 -26
package/src/stats-data.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { Locale } from 'zhin.js/i18n';
2
+ import { messages as t } from './i18n.js';
1
3
  export interface StatsRankEntry {
2
4
  rank: number;
3
5
  userId: string;
@@ -75,27 +77,27 @@ export function buildStatsRankReportData(
75
77
  };
76
78
  }
77
79
 
78
- export function formatRankText(data: StatsRankReportData): string {
79
- if (data.empty) return `${data.title}\n暂无数据`;
80
+ export function formatRankText(data: StatsRankReportData, locale?: Locale): string {
81
+ if (data.empty) return t(locale, 'statsEmpty', { title: data.title });
80
82
  const medals = ["🥇", "🥈", "🥉"];
81
83
  const lines = data.entries.map((entry) => {
82
84
  const prefix = entry.rank <= 3 ? medals[entry.rank - 1] : `${entry.rank}.`;
83
- return `${prefix} ${entry.name} — ${entry.count}条`;
85
+ return t(locale, 'statsRow', { prefix, name: entry.name, count: entry.count });
84
86
  });
85
- let footer = `\n共 ${data.participantCount} 人, ${data.totalMessages} 条消息`;
87
+ let footer = t(locale, 'statsFooter', { people: data.participantCount, messages: data.totalMessages });
86
88
  if (data.myRank != null && data.myCount != null) {
87
- footer += ` | 你第${data.myRank}名 (${data.myCount}条)`;
89
+ footer += t(locale, 'statsMyFooter', { rank: data.myRank, count: data.myCount });
88
90
  }
89
91
  return `${data.title}\n${lines.join("\n")}${footer}`;
90
92
  }
91
93
 
92
- export function formatMyStatsText(data: MyStatsReportData): string {
94
+ export function formatMyStatsText(data: MyStatsReportData, locale?: Locale): string {
93
95
  return [
94
- `${data.userName} 的${data.scope}消息统计`,
95
- `今日: ${data.todayCount} 条`,
96
- `本周: ${data.weekCount} 条`,
97
- `本月: ${data.monthCount} 条`,
98
- `总计: ${data.totalCount} 条`,
99
- `活跃天数: ${data.activeDays} 天`,
96
+ t(locale, 'myStatsTitle', { name: data.userName, scope: data.scope }),
97
+ t(locale, 'todayMessages', { count: data.todayCount }),
98
+ t(locale, 'weekMessages', { count: data.weekCount }),
99
+ t(locale, 'monthMessages', { count: data.monthCount }),
100
+ t(locale, 'totalMessages', { count: data.totalCount }),
101
+ t(locale, 'activeDays', { count: data.activeDays }),
100
102
  ].join("\n");
101
103
  }
package/src/stats-lib.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { messages as t } from './i18n.js';
1
2
  import { conversationKey, requireConversation, type ConversationInput } from './conversation-scope.js';
2
3
  import type { GroupSuiteConfig } from './config.js';
3
4
  import { getStatsModel } from './db-store.js';
@@ -164,20 +165,20 @@ export async function statsRankText(
164
165
  const stats = await queryStats(groupId, fromDate, runtime);
165
166
  const { id: userId } = resolveSender(input);
166
167
  const data = buildStatsRankReportData(stats, title, cfg.rankSize, userId);
167
- return formatRankText(data);
168
+ return formatRankText(data, input.locale);
168
169
  }
169
170
 
170
171
  export async function myStatsText(input: MessageInput, runtime: GroupSuiteRuntime): Promise<string> {
171
172
  await flushStatsBuffer(runtime);
172
173
  const M = getStatsModel(runtime.db);
173
- if (!M) return '统计数据库尚未就绪';
174
+ if (!M) return t(input.locale, 'statsDb');
174
175
  const { id: userId, name: userName } = resolveSender(input);
175
- if (!userId) return '无法获取用户信息';
176
+ if (!userId) return t(input.locale, 'senderMissing');
176
177
  const groupId = resolveGroupId(input);
177
178
  const rows = (groupId
178
179
  ? await M.select().where({ user_id: userId, group_id: groupId })
179
180
  : await M.select().where({ user_id: userId })) as Record<string, unknown>[];
180
- if (rows.length === 0) return '暂无你的消息记录';
181
+ if (rows.length === 0) return t(input.locale, 'myStatsEmpty');
181
182
  const today = todayStr();
182
183
  const weekStart = weekStartStr();
183
184
  const monthStart = monthStartStr();
@@ -193,13 +194,13 @@ export async function myStatsText(input: MessageInput, runtime: GroupSuiteRuntim
193
194
  if (row.date === today) todayCount += c;
194
195
  }
195
196
  const data: MyStatsReportData = {
196
- userName: userName || '你',
197
- scope: groupId ? '本群' : '全局',
197
+ userName: userName || t(input.locale, 'you'),
198
+ scope: groupId ? t(input.locale, 'thisGroup') : t(input.locale, 'global'),
198
199
  todayCount,
199
200
  weekCount,
200
201
  monthCount,
201
202
  totalCount,
202
203
  activeDays: rows.length,
203
204
  };
204
- return formatMyStatsText(data);
205
+ return formatMyStatsText(data, input.locale);
205
206
  }
package/src/teach-lib.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { messages as t } from './i18n.js';
1
2
  import type { ConversationInput } from './conversation-scope.js';
2
3
  import type { GroupSuiteConfig } from './config.js';
3
4
  import { getTeachModel } from './db-store.js';
@@ -8,8 +9,7 @@ import {
8
9
  } from './shared-runtime.js';
9
10
  import type { GroupSuiteRuntime } from './runtime-state.js';
10
11
 
11
- export const TEACH_USAGE_HINT =
12
- '格式:teach 关键词 回答(回答可含空格),或 teach 问题|答案';
12
+ export const TEACH_USAGE_HINT = '格式:关键词 回答,或 问题|答案';
13
13
 
14
14
  function isSafeRegex(pattern: string): boolean {
15
15
  if (pattern.length > 200) return false;
@@ -43,12 +43,13 @@ function processAnswer(
43
43
  answer: string,
44
44
  sender: { id: string; name: string },
45
45
  match?: RegExpMatchArray,
46
+ locale?: import('zhin.js/i18n').Locale,
46
47
  ): string {
47
48
  let result = answer;
48
- result = result.replace(/\{sender\}/g, sender.name || '你');
49
+ result = result.replace(/\{sender\}/g, sender.name || t(locale, 'you'));
49
50
  result = result.replace(/\{sender\.id\}/g, sender.id || '');
50
- result = result.replace(/\{time\}/g, new Date().toLocaleTimeString('zh-CN'));
51
- result = result.replace(/\{date\}/g, new Date().toLocaleDateString('zh-CN'));
51
+ result = result.replace(/\{time\}/g, new Date().toLocaleTimeString(locale ?? 'zh-CN'));
52
+ result = result.replace(/\{date\}/g, new Date().toLocaleDateString(locale ?? 'zh-CN'));
52
53
  if (match) {
53
54
  for (let i = 1; i < match.length; i++) {
54
55
  result = result.replace(new RegExp(`\\$${i}`, 'g'), match[i] || '');
@@ -109,14 +110,14 @@ export async function teachAdd(
109
110
  runtime: GroupSuiteRuntime,
110
111
  ): Promise<string> {
111
112
  const QA = getTeachModel(runtime.db);
112
- if (!QA) return '问答数据库尚未就绪,请稍后重试';
113
- if (regex && !cfg.teachAllowRegex) return '管理员已禁用正则问答';
113
+ if (!QA) return t(input.locale, 'teachRetry');
114
+ if (regex && !cfg.teachAllowRegex) return t(input.locale, 'regexDisabled');
114
115
 
115
116
  const parsed = parseTeachPair(payload.trim());
116
117
  if (!parsed) {
117
118
  return regex
118
- ? '格式:teach-regex 正则 回答,或 teach-regex 正则|回答'
119
- : TEACH_USAGE_HINT;
119
+ ? t(input.locale, 'regexUsage')
120
+ : t(input.locale, 'teachUsage');
120
121
  }
121
122
  const { question, answer } = parsed;
122
123
 
@@ -124,9 +125,9 @@ export async function teachAdd(
124
125
  try {
125
126
  new RegExp(question, 'i');
126
127
  } catch (e) {
127
- return `正则表达式不合法: ${(e as Error).message}`;
128
+ return t(input.locale, 'invalidRegex', { error: (e as Error).message });
128
129
  }
129
- if (!isSafeRegex(question)) return '该正则可能导致性能问题,请简化';
130
+ if (!isSafeRegex(question)) return t(input.locale, 'unsafeRegex');
130
131
  }
131
132
 
132
133
  const { type: ctxType, id: ctxId } = resolveContextKey(input);
@@ -138,7 +139,7 @@ export async function teachAdd(
138
139
  context_id: ctxId,
139
140
  })) as Record<string, unknown>[];
140
141
  if (existing.length >= cfg.teachMaxPerGroup) {
141
- return `该群问答数已达上限 (${cfg.teachMaxPerGroup})`;
142
+ return t(input.locale, 'teachLimit', { limit: cfg.teachMaxPerGroup });
142
143
  }
143
144
  }
144
145
 
@@ -157,7 +158,7 @@ export async function teachAdd(
157
158
  is_regex?: number | boolean;
158
159
  }),
159
160
  );
160
- return `已更新问答:「${question}」→「${answer}」`;
161
+ return t(input.locale, 'teachUpdated', { question, answer });
161
162
  }
162
163
 
163
164
  await QA.insert({
@@ -174,8 +175,8 @@ export async function teachAdd(
174
175
  });
175
176
 
176
177
  return regex
177
- ? `学会了!匹配 /${question}/ 的消息我会回复「${answer}」`
178
- : `学会了!发送「${question}」我会回复「${answer}」`;
178
+ ? t(input.locale, 'regexLearned', { question, answer })
179
+ : t(input.locale, 'teachLearned', { question, answer });
179
180
  }
180
181
 
181
182
  export async function teachList(
@@ -185,24 +186,24 @@ export async function teachList(
185
186
  runtime: GroupSuiteRuntime,
186
187
  ): Promise<string> {
187
188
  const QA = getTeachModel(runtime.db);
188
- if (!QA) return '问答数据库尚未就绪';
189
+ if (!QA) return t(input.locale, 'teachDb');
189
190
  const safePage = Math.max(1, page);
190
191
  const { type: ctxType, id: ctxId } = resolveContextKey(input);
191
192
  const allItems = (await QA.select().where(
192
193
  { context_type: ctxType, context_id: ctxId },
193
194
  )) as Record<string, unknown>[];
194
- if (allItems.length === 0) return '还没有教过任何问答哦~';
195
+ if (allItems.length === 0) return t(input.locale, 'teachEmpty');
195
196
  const totalPages = Math.ceil(allItems.length / cfg.teachPageSize);
196
197
  const pageNum = Math.min(safePage, totalPages);
197
198
  const start = (pageNum - 1) * cfg.teachPageSize;
198
199
  const pageItems = allItems.slice(start, start + cfg.teachPageSize);
199
200
  const lines = pageItems.map((item, i) => {
200
- const prefix = item.is_regex ? '[正则]' : '[精确]';
201
+ const prefix = item.is_regex ? t(input.locale, 'regex') : t(input.locale, 'exact');
201
202
  const q = item.is_regex ? `/${item.question}/` : item.question;
202
203
  return `${start + i + 1}. ${prefix} ${q} → ${item.answer}`;
203
204
  });
204
- const header = '本会话问答列表';
205
- return `${header}\n${lines.join('\n')}\n第 ${pageNum}/${totalPages} 页 · 共 ${allItems.length} 条`;
205
+ const header = t(input.locale, 'teachTitle');
206
+ return t(input.locale, 'teachPage', { header, lines: lines.join('\n'), page: pageNum, pages: totalPages, count: allItems.length });
206
207
  }
207
208
 
208
209
  export async function teachForget(
@@ -211,16 +212,16 @@ export async function teachForget(
211
212
  runtime: GroupSuiteRuntime,
212
213
  ): Promise<string> {
213
214
  const QA = getTeachModel(runtime.db);
214
- if (!QA) return '问答数据库尚未就绪';
215
+ if (!QA) return t(input.locale, 'teachDb');
215
216
  const question = questionRaw.trim();
216
- if (!question) return '请提供要删除的问题';
217
+ if (!question) return t(input.locale, 'questionRequired');
217
218
  const { type: ctxType, id: ctxId } = resolveContextKey(input);
218
219
  const items = (await QA.select().where({
219
220
  question,
220
221
  context_type: ctxType,
221
222
  context_id: ctxId,
222
223
  })) as Record<string, unknown>[];
223
- if (items.length === 0) return `没有找到问题「${question}」`;
224
+ if (items.length === 0) return t(input.locale, 'questionMissing', { question });
224
225
  await QA.delete().where(
225
226
  qaWhereKey(items[0] as {
226
227
  question: string;
@@ -229,7 +230,7 @@ export async function teachForget(
229
230
  is_regex?: number | boolean;
230
231
  }),
231
232
  );
232
- return `已忘记「${question}」的回答`;
233
+ return t(input.locale, 'questionRemoved', { question });
233
234
  }
234
235
 
235
236
  /** Try teach auto-reply; returns reply text or null. */
@@ -274,12 +275,12 @@ export async function tryTeachReply(
274
275
  if (matched.is_regex) {
275
276
  try {
276
277
  const m = content.match(new RegExp(String(matched.question), 'i'));
277
- answer = processAnswer(answer, sender, m || undefined);
278
+ answer = processAnswer(answer, sender, m || undefined, input.locale);
278
279
  } catch {
279
280
  /* ignore */
280
281
  }
281
282
  } else {
282
- answer = processAnswer(answer, sender);
283
+ answer = processAnswer(answer, sender, undefined, input.locale);
283
284
  }
284
285
  return answer;
285
286
  }