@zhin.js/plugin-group-suite 3.0.1 → 3.0.3

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 (114) hide show
  1. package/README.md +42 -58
  2. package/agent/tools/group_announce.ts +12 -0
  3. package/commands/checkin.ts +12 -0
  4. package/commands/forget.ts +12 -0
  5. package/commands/keyword-add/[keyword:string].ts +15 -0
  6. package/commands/keyword-list.ts +14 -0
  7. package/commands/keyword-remove/[keyword:string].ts +14 -0
  8. package/commands/mypoints.ts +12 -0
  9. package/commands/mystats.ts +12 -0
  10. package/commands/rank.ts +12 -0
  11. package/commands/stats-rank.ts +20 -0
  12. package/commands/stats-week.ts +20 -0
  13. package/commands/stats.ts +21 -0
  14. package/commands/teach-list/[page:number=1].ts +13 -0
  15. package/commands/teach-regex.ts +12 -0
  16. package/commands/teach.ts +12 -0
  17. package/lib/checkin-lib.d.ts +18 -0
  18. package/lib/checkin-lib.d.ts.map +1 -0
  19. package/lib/checkin-lib.js +118 -0
  20. package/lib/checkin-lib.js.map +1 -0
  21. package/lib/config.d.ts +4 -57
  22. package/lib/config.d.ts.map +1 -1
  23. package/lib/config.js +18 -40
  24. package/lib/config.js.map +1 -1
  25. package/lib/db-store.d.ts +12 -0
  26. package/lib/db-store.d.ts.map +1 -0
  27. package/lib/db-store.js +38 -0
  28. package/lib/db-store.js.map +1 -0
  29. package/lib/index.d.ts +9 -1
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +7 -29
  32. package/lib/index.js.map +1 -1
  33. package/lib/keyword-store.d.ts +6 -0
  34. package/lib/keyword-store.d.ts.map +1 -0
  35. package/lib/keyword-store.js +21 -0
  36. package/lib/keyword-store.js.map +1 -0
  37. package/lib/memory-store.d.ts +29 -0
  38. package/lib/memory-store.d.ts.map +1 -0
  39. package/lib/memory-store.js +59 -0
  40. package/lib/memory-store.js.map +1 -0
  41. package/lib/runtime-state.d.ts +24 -0
  42. package/lib/runtime-state.d.ts.map +1 -0
  43. package/lib/runtime-state.js +21 -0
  44. package/lib/runtime-state.js.map +1 -0
  45. package/lib/shared-runtime.d.ts +20 -0
  46. package/lib/shared-runtime.d.ts.map +1 -0
  47. package/lib/shared-runtime.js +34 -0
  48. package/lib/shared-runtime.js.map +1 -0
  49. package/lib/stats-lib.d.ts +29 -0
  50. package/lib/stats-lib.d.ts.map +1 -0
  51. package/lib/stats-lib.js +173 -0
  52. package/lib/stats-lib.js.map +1 -0
  53. package/lib/tables.d.ts +4 -0
  54. package/lib/tables.d.ts.map +1 -0
  55. package/lib/tables.js +40 -0
  56. package/lib/tables.js.map +1 -0
  57. package/lib/teach-lib.d.ts +22 -0
  58. package/lib/teach-lib.d.ts.map +1 -0
  59. package/lib/teach-lib.js +233 -0
  60. package/lib/teach-lib.js.map +1 -0
  61. package/middlewares/keyword-reply.ts +37 -0
  62. package/middlewares/stats-count.ts +22 -0
  63. package/middlewares/teach-reply.ts +22 -0
  64. package/package.json +46 -13
  65. package/plugin.ts +39 -0
  66. package/schema.json +17 -0
  67. package/src/checkin-lib.ts +148 -0
  68. package/src/config.ts +23 -57
  69. package/src/db-store.ts +51 -0
  70. package/src/index.ts +47 -33
  71. package/src/keyword-store.ts +24 -0
  72. package/src/memory-store.ts +88 -0
  73. package/src/runtime-state.ts +44 -0
  74. package/src/shared-runtime.ts +44 -0
  75. package/src/stats-lib.ts +202 -0
  76. package/src/tables.ts +46 -0
  77. package/src/teach-lib.ts +301 -0
  78. package/tools/checkin-query/handler.ts +2 -2
  79. package/tools/checkin-rank/handler.ts +1 -1
  80. package/tools/stats-query/handler.ts +2 -2
  81. package/tools/stats-user/handler.ts +3 -2
  82. package/lib/admin.d.ts +0 -4
  83. package/lib/admin.d.ts.map +0 -1
  84. package/lib/admin.js +0 -123
  85. package/lib/admin.js.map +0 -1
  86. package/lib/checkin.d.ts +0 -5
  87. package/lib/checkin.d.ts.map +0 -1
  88. package/lib/checkin.js +0 -168
  89. package/lib/checkin.js.map +0 -1
  90. package/lib/daily-analysis.d.ts +0 -7
  91. package/lib/daily-analysis.d.ts.map +0 -1
  92. package/lib/daily-analysis.js +0 -333
  93. package/lib/daily-analysis.js.map +0 -1
  94. package/lib/shared.d.ts +0 -25
  95. package/lib/shared.d.ts.map +0 -1
  96. package/lib/shared.js +0 -40
  97. package/lib/shared.js.map +0 -1
  98. package/lib/stats.d.ts +0 -12
  99. package/lib/stats.d.ts.map +0 -1
  100. package/lib/stats.js +0 -258
  101. package/lib/stats.js.map +0 -1
  102. package/lib/teach.d.ts +0 -4
  103. package/lib/teach.d.ts.map +0 -1
  104. package/lib/teach.js +0 -294
  105. package/lib/teach.js.map +0 -1
  106. package/plugin.yml +0 -3
  107. package/src/admin.ts +0 -145
  108. package/src/checkin.ts +0 -196
  109. package/src/daily-analysis.ts +0 -458
  110. package/src/shared.ts +0 -51
  111. package/src/stats.ts +0 -298
  112. package/src/teach.ts +0 -307
  113. /package/{skills/checkin/SKILL.md → agent/skills/checkin.md} +0 -0
  114. /package/{skills/group-daily-analysis/SKILL.md → agent/skills/group-daily-analysis.md} +0 -0
@@ -0,0 +1,173 @@
1
+ import { getStatsModel } from './db-store.js';
2
+ import { resolveSender, todayStr, ts, } from './shared-runtime.js';
3
+ import { buildStatsRankReportData, formatMyStatsText, formatRankText, } from './stats-data.js';
4
+ export function weekStartStr() {
5
+ const d = new Date();
6
+ const day = d.getDay() || 7;
7
+ d.setDate(d.getDate() - day + 1);
8
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
9
+ }
10
+ export function monthStartStr() {
11
+ const d = new Date();
12
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-01`;
13
+ }
14
+ const buffer = new Map();
15
+ function bufferKey(userId, groupId, date) {
16
+ return `${userId}:${groupId}:${date}`;
17
+ }
18
+ export function resolveGroupId(input) {
19
+ const meta = input.metadata ?? {};
20
+ const channelType = String(meta.type ?? meta.channelType ?? '');
21
+ if (channelType === 'group' || channelType === 'guild') {
22
+ return String(input.target ?? meta.channelId ?? '');
23
+ }
24
+ if (input.target && channelType !== 'private') {
25
+ return String(input.target);
26
+ }
27
+ return '';
28
+ }
29
+ export function recordMessage(input, runtime) {
30
+ const targetBuffer = runtime?.statsBuffer ?? buffer;
31
+ const { id: userId, name: userName } = resolveSender(input);
32
+ if (!userId)
33
+ return;
34
+ const groupId = resolveGroupId(input);
35
+ const key = bufferKey(userId, groupId, todayStr());
36
+ const pending = targetBuffer.get(key);
37
+ if (pending) {
38
+ pending.count++;
39
+ pending.user_name = userName;
40
+ }
41
+ else {
42
+ targetBuffer.set(key, {
43
+ user_id: userId,
44
+ user_name: userName,
45
+ group_id: groupId,
46
+ date: todayStr(),
47
+ count: 1,
48
+ });
49
+ }
50
+ }
51
+ export async function flushStatsBuffer(runtime) {
52
+ const targetBuffer = runtime?.statsBuffer ?? buffer;
53
+ const M = getStatsModel(runtime?.db);
54
+ if (!M || targetBuffer.size === 0)
55
+ return;
56
+ const entries = [...targetBuffer.values()];
57
+ targetBuffer.clear();
58
+ for (const entry of entries) {
59
+ try {
60
+ const existing = (await M.select().where({
61
+ user_id: entry.user_id,
62
+ group_id: entry.group_id,
63
+ date: entry.date,
64
+ }));
65
+ if (existing.length > 0) {
66
+ await M.update({
67
+ count: Number(existing[0].count || 0) + entry.count,
68
+ user_name: entry.user_name,
69
+ updated_at: ts(),
70
+ }).where({
71
+ user_id: entry.user_id,
72
+ group_id: entry.group_id,
73
+ date: entry.date,
74
+ });
75
+ }
76
+ else {
77
+ await M.insert({
78
+ user_id: entry.user_id,
79
+ user_name: entry.user_name,
80
+ group_id: entry.group_id,
81
+ date: entry.date,
82
+ count: entry.count,
83
+ updated_at: ts(),
84
+ });
85
+ }
86
+ }
87
+ catch {
88
+ /* single row failure */
89
+ }
90
+ }
91
+ }
92
+ export async function queryStats(groupId, fromDate, runtime) {
93
+ const M = getStatsModel(runtime?.db);
94
+ if (!M)
95
+ return new Map();
96
+ const all = (groupId
97
+ ? await M.select().where({ group_id: groupId })
98
+ : await M.select());
99
+ const result = new Map();
100
+ for (const row of all) {
101
+ if (String(row.date) < fromDate)
102
+ continue;
103
+ const userId = String(row.user_id);
104
+ const existing = result.get(userId);
105
+ if (existing) {
106
+ existing.count += Number(row.count || 0);
107
+ if (row.user_name)
108
+ existing.name = String(row.user_name);
109
+ }
110
+ else {
111
+ result.set(userId, {
112
+ name: String(row.user_name || row.user_id),
113
+ count: Number(row.count || 0),
114
+ });
115
+ }
116
+ }
117
+ return result;
118
+ }
119
+ export async function statsRankText(input, cfg, fromDate, title, runtime) {
120
+ await flushStatsBuffer(runtime);
121
+ const groupId = resolveGroupId(input);
122
+ const stats = await queryStats(groupId, fromDate, runtime);
123
+ const { id: userId } = resolveSender(input);
124
+ const data = buildStatsRankReportData(stats, title, cfg.rankSize, userId);
125
+ return formatRankText(data);
126
+ }
127
+ export async function myStatsText(input, runtime) {
128
+ await flushStatsBuffer(runtime);
129
+ const M = getStatsModel(runtime?.db);
130
+ if (!M)
131
+ return '统计数据库尚未就绪';
132
+ const { id: userId, name: userName } = resolveSender(input);
133
+ if (!userId)
134
+ return '无法获取用户信息';
135
+ const groupId = resolveGroupId(input);
136
+ const rows = (groupId
137
+ ? await M.select().where({ user_id: userId, group_id: groupId })
138
+ : await M.select().where({ user_id: userId }));
139
+ if (rows.length === 0)
140
+ return '暂无你的消息记录';
141
+ const today = todayStr();
142
+ const weekStart = weekStartStr();
143
+ const monthStart = monthStartStr();
144
+ let todayCount = 0;
145
+ let weekCount = 0;
146
+ let monthCount = 0;
147
+ let totalCount = 0;
148
+ for (const row of rows) {
149
+ const c = Number(row.count || 0);
150
+ totalCount += c;
151
+ if (String(row.date) >= monthStart)
152
+ monthCount += c;
153
+ if (String(row.date) >= weekStart)
154
+ weekCount += c;
155
+ if (row.date === today)
156
+ todayCount += c;
157
+ }
158
+ const data = {
159
+ userName: userName || '你',
160
+ scope: groupId ? '本群' : '全局',
161
+ todayCount,
162
+ weekCount,
163
+ monthCount,
164
+ totalCount,
165
+ activeDays: rows.length,
166
+ };
167
+ return formatMyStatsText(data);
168
+ }
169
+ /** Test helper */
170
+ export function resetStatsBuffer() {
171
+ buffer.clear();
172
+ }
173
+ //# sourceMappingURL=stats-lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats-lib.js","sourceRoot":"","sources":["../src/stats-lib.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function defineGroupSuiteTables(db: {
2
+ define: (name: string, schema: Record<string, unknown>) => void;
3
+ }): void;
4
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AASA,wBAAgB,sBAAsB,CACpC,EAAE,EAAE;IAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CAAE,GACtE,IAAI,CAkCN"}
package/lib/tables.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Group-suite table schemas for DatabaseHost / legacy DatabaseFeature.
3
+ */
4
+ import { CHECKIN_TABLE, TEACH_TABLE, STATS_TABLE, } from './memory-store.js';
5
+ export function defineGroupSuiteTables(db) {
6
+ db.define(CHECKIN_TABLE, {
7
+ user_id: { type: 'text', nullable: false },
8
+ user_name: { type: 'text', default: '' },
9
+ points: { type: 'integer', default: 0 },
10
+ total_checkins: { type: 'integer', default: 0 },
11
+ streak: { type: 'integer', default: 0 },
12
+ max_streak: { type: 'integer', default: 0 },
13
+ last_checkin: { type: 'text', default: '' },
14
+ context_type: { type: 'text', default: 'global' },
15
+ context_id: { type: 'text', default: '' },
16
+ created_at: { type: 'text', default: '' },
17
+ updated_at: { type: 'text', default: '' },
18
+ });
19
+ db.define(TEACH_TABLE, {
20
+ question: { type: 'text', nullable: false },
21
+ answer: { type: 'text', nullable: false },
22
+ is_regex: { type: 'integer', default: 0 },
23
+ context_type: { type: 'text', default: 'global' },
24
+ context_id: { type: 'text', default: '' },
25
+ creator_id: { type: 'text', default: '' },
26
+ creator_name: { type: 'text', default: '' },
27
+ hit_count: { type: 'integer', default: 0 },
28
+ created_at: { type: 'text', default: '' },
29
+ updated_at: { type: 'text', default: '' },
30
+ });
31
+ db.define(STATS_TABLE, {
32
+ user_id: { type: 'text', nullable: false },
33
+ user_name: { type: 'text', default: '' },
34
+ group_id: { type: 'text', default: '' },
35
+ date: { type: 'text', nullable: false },
36
+ count: { type: 'integer', default: 0 },
37
+ updated_at: { type: 'text', default: '' },
38
+ });
39
+ }
40
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,MAAM,UAAU,sBAAsB,CACpC,EAAuE;IAEvE,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE;QACvB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC1C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACxC,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACvC,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC/C,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACvC,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC3C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QACjD,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;KAC1C,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE;QACrB,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QACzC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACzC,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QACjD,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC3C,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC1C,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;KAC1C,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE;QACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC1C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACxC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACvC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QACvC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QACtC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;KAC1C,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { GroupSuiteConfig } from './config.js';
2
+ import type { GroupSuiteRuntime } from './runtime-state.js';
3
+ export declare const TEACH_USAGE_HINT = "\u683C\u5F0F\uFF1Ateach \u5173\u952E\u8BCD \u56DE\u7B54\uFF08\u56DE\u7B54\u53EF\u542B\u7A7A\u683C\uFF09\uFF0C\u6216 teach \u95EE\u9898|\u7B54\u6848";
4
+ export declare function parseTeachPair(raw: string): {
5
+ question: string;
6
+ answer: string;
7
+ } | null;
8
+ type MessageInput = {
9
+ sender?: string;
10
+ target?: string;
11
+ content?: unknown;
12
+ metadata?: Readonly<Record<string, unknown>>;
13
+ };
14
+ export declare function teachAdd(input: MessageInput, cfg: GroupSuiteConfig, payload: string, regex?: boolean, runtime?: GroupSuiteRuntime): Promise<string>;
15
+ export declare function teachList(input: MessageInput, cfg: GroupSuiteConfig, page?: number, runtime?: GroupSuiteRuntime): Promise<string>;
16
+ export declare function teachForget(input: MessageInput, questionRaw: string, runtime?: GroupSuiteRuntime): Promise<string>;
17
+ /** Try teach auto-reply; returns reply text or null. */
18
+ export declare function tryTeachReply(input: MessageInput, cfg: GroupSuiteConfig, runtime?: GroupSuiteRuntime): Promise<string | null>;
19
+ /** Test helper */
20
+ export declare function resetTeachCooldown(): void;
21
+ export {};
22
+ //# sourceMappingURL=teach-lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teach-lib.d.ts","sourceRoot":"","sources":["../src/teach-lib.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAOpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,eAAO,MAAM,gBAAgB,wJACY,CAAC;AAa1C,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAevF;AAkCD,KAAK,YAAY,GAAG;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC9C,CAAC;AAiCF,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,UAAQ,EACb,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAqEjB;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,gBAAgB,EACrB,IAAI,SAAI,EACR,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,YAAY,EACnB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED,wDAAwD;AACxD,wBAAsB,aAAa,CACjC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,gBAAgB,EACrB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6CxB;AAED,kBAAkB;AAClB,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -0,0 +1,233 @@
1
+ import { getTeachModel } from './db-store.js';
2
+ import { resolveContextKey, resolveSender, ts, } from './shared-runtime.js';
3
+ const cooldownMap = new Map();
4
+ export const TEACH_USAGE_HINT = '格式:teach 关键词 回答(回答可含空格),或 teach 问题|答案';
5
+ function isSafeRegex(pattern) {
6
+ if (pattern.length > 200)
7
+ return false;
8
+ if (/(\+|\*|\{)\)?(\+|\*|\{)/.test(pattern))
9
+ return false;
10
+ if (/\([^)]*(\+|\*)[^)]*(\+|\*)[^)]*\)[+*]/.test(pattern))
11
+ return false;
12
+ const altGroups = pattern.match(/\([^)]*\|[^)]*\)/g) || [];
13
+ for (const g of altGroups) {
14
+ if ((g.match(/\|/g) || []).length > 10)
15
+ return false;
16
+ }
17
+ return true;
18
+ }
19
+ export function parseTeachPair(raw) {
20
+ if (!raw)
21
+ return null;
22
+ const pipe = raw.indexOf('|');
23
+ if (pipe >= 0) {
24
+ const question = raw.slice(0, pipe).trim();
25
+ const answer = raw.slice(pipe + 1).trim();
26
+ if (question && answer)
27
+ return { question, answer };
28
+ return null;
29
+ }
30
+ const lastSpace = raw.lastIndexOf(' ');
31
+ if (lastSpace < 0)
32
+ return null;
33
+ const question = raw.slice(0, lastSpace).trim();
34
+ const answer = raw.slice(lastSpace + 1).trim();
35
+ if (!question || !answer)
36
+ return null;
37
+ return { question, answer };
38
+ }
39
+ function processAnswer(answer, sender, match) {
40
+ let result = answer;
41
+ result = result.replace(/\{sender\}/g, sender.name || '你');
42
+ result = result.replace(/\{sender\.id\}/g, sender.id || '');
43
+ result = result.replace(/\{time\}/g, new Date().toLocaleTimeString('zh-CN'));
44
+ result = result.replace(/\{date\}/g, new Date().toLocaleDateString('zh-CN'));
45
+ if (match) {
46
+ for (let i = 1; i < match.length; i++) {
47
+ result = result.replace(new RegExp(`\\$${i}`, 'g'), match[i] || '');
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+ function qaWhereKey(row) {
53
+ return {
54
+ question: row.question,
55
+ context_type: row.context_type,
56
+ context_id: row.context_id,
57
+ is_regex: row.is_regex ? 1 : 0,
58
+ };
59
+ }
60
+ async function findMatch(content, ctxType, ctxId, cfg, runtime) {
61
+ const QA = getTeachModel(runtime?.db);
62
+ if (!QA)
63
+ return null;
64
+ const allItems = (await QA.select());
65
+ const candidates = allItems.filter((item) => item.context_type === 'global' ||
66
+ (item.context_type === ctxType && item.context_id === ctxId));
67
+ for (const item of candidates) {
68
+ if (!item.is_regex && item.question === content)
69
+ return item;
70
+ }
71
+ if (cfg.teachAllowRegex) {
72
+ for (const item of candidates) {
73
+ if (!item.is_regex)
74
+ continue;
75
+ try {
76
+ if (new RegExp(String(item.question), 'i').test(content))
77
+ return item;
78
+ }
79
+ catch {
80
+ continue;
81
+ }
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ export async function teachAdd(input, cfg, payload, regex = false, runtime) {
87
+ const QA = getTeachModel(runtime?.db);
88
+ if (!QA)
89
+ return '问答数据库尚未就绪,请稍后重试';
90
+ if (regex && !cfg.teachAllowRegex)
91
+ return '管理员已禁用正则问答';
92
+ const parsed = parseTeachPair(payload.trim());
93
+ if (!parsed) {
94
+ return regex
95
+ ? '格式:teach-regex 正则 回答,或 teach-regex 正则|回答'
96
+ : TEACH_USAGE_HINT;
97
+ }
98
+ const { question, answer } = parsed;
99
+ if (regex) {
100
+ try {
101
+ new RegExp(question, 'i');
102
+ }
103
+ catch (e) {
104
+ return `正则表达式不合法: ${e.message}`;
105
+ }
106
+ if (!isSafeRegex(question))
107
+ return '该正则可能导致性能问题,请简化';
108
+ }
109
+ const { type: ctxType, id: ctxId } = resolveContextKey(input);
110
+ const sender = resolveSender(input);
111
+ if (ctxType === 'group') {
112
+ const existing = (await QA.select().where({
113
+ context_type: ctxType,
114
+ context_id: ctxId,
115
+ }));
116
+ if (existing.length >= cfg.teachMaxPerGroup) {
117
+ return `该群问答数已达上限 (${cfg.teachMaxPerGroup})`;
118
+ }
119
+ }
120
+ const duplicate = (await QA.select().where({
121
+ question,
122
+ context_type: ctxType,
123
+ context_id: ctxId,
124
+ }));
125
+ if (duplicate.length > 0 && !regex) {
126
+ await QA.update({ answer, updated_at: ts() }).where(qaWhereKey(duplicate[0]));
127
+ return `已更新问答:「${question}」→「${answer}」`;
128
+ }
129
+ await QA.insert({
130
+ question,
131
+ answer,
132
+ is_regex: regex ? 1 : 0,
133
+ context_type: ctxType,
134
+ context_id: ctxId,
135
+ creator_id: sender.id,
136
+ creator_name: sender.name,
137
+ hit_count: 0,
138
+ created_at: ts(),
139
+ updated_at: ts(),
140
+ });
141
+ return regex
142
+ ? `学会了!匹配 /${question}/ 的消息我会回复「${answer}」`
143
+ : `学会了!发送「${question}」我会回复「${answer}」`;
144
+ }
145
+ export async function teachList(input, cfg, page = 1, runtime) {
146
+ const QA = getTeachModel(runtime?.db);
147
+ if (!QA)
148
+ return '问答数据库尚未就绪';
149
+ const safePage = Math.max(1, page);
150
+ const { type: ctxType, id: ctxId } = resolveContextKey(input);
151
+ const allItems = (await QA.select().where(ctxType === 'global'
152
+ ? { context_type: 'global' }
153
+ : { context_type: ctxType, context_id: ctxId }));
154
+ if (allItems.length === 0)
155
+ return '还没有教过任何问答哦~';
156
+ const totalPages = Math.ceil(allItems.length / cfg.teachPageSize);
157
+ const pageNum = Math.min(safePage, totalPages);
158
+ const start = (pageNum - 1) * cfg.teachPageSize;
159
+ const pageItems = allItems.slice(start, start + cfg.teachPageSize);
160
+ const lines = pageItems.map((item, i) => {
161
+ const prefix = item.is_regex ? '[正则]' : '[精确]';
162
+ const q = item.is_regex ? `/${item.question}/` : item.question;
163
+ return `${start + i + 1}. ${prefix} ${q} → ${item.answer}`;
164
+ });
165
+ const header = ctxType === 'global' ? '全局问答列表' : '本群问答列表';
166
+ return `${header}\n${lines.join('\n')}\n第 ${pageNum}/${totalPages} 页 · 共 ${allItems.length} 条`;
167
+ }
168
+ export async function teachForget(input, questionRaw, runtime) {
169
+ const QA = getTeachModel(runtime?.db);
170
+ if (!QA)
171
+ return '问答数据库尚未就绪';
172
+ const question = questionRaw.trim();
173
+ if (!question)
174
+ return '请提供要删除的问题';
175
+ const { type: ctxType, id: ctxId } = resolveContextKey(input);
176
+ const items = (await QA.select().where({
177
+ question,
178
+ context_type: ctxType,
179
+ context_id: ctxId,
180
+ }));
181
+ if (items.length === 0)
182
+ return `没有找到问题「${question}」`;
183
+ await QA.delete().where(qaWhereKey(items[0]));
184
+ return `已忘记「${question}」的回答`;
185
+ }
186
+ /** Try teach auto-reply; returns reply text or null. */
187
+ export async function tryTeachReply(input, cfg, runtime) {
188
+ const content = typeof input.content === 'string' ? input.content.trim() : '';
189
+ if (!content)
190
+ return null;
191
+ const { type: ctxType, id: ctxId } = resolveContextKey(input);
192
+ const matched = await findMatch(content, ctxType, ctxId, cfg, runtime);
193
+ if (!matched)
194
+ return null;
195
+ const cooldownKey = `${matched.question}:${ctxType}:${ctxId}:${matched.is_regex ?? 0}`;
196
+ const cooldowns = runtime?.teachCooldowns ?? cooldownMap;
197
+ const last = cooldowns.get(cooldownKey);
198
+ if (last && Date.now() - last < cfg.teachCooldownMs)
199
+ return null;
200
+ cooldowns.set(cooldownKey, Date.now());
201
+ const QA = getTeachModel(runtime?.db);
202
+ if (QA) {
203
+ try {
204
+ await QA.update({
205
+ hit_count: Number(matched.hit_count || 0) + 1,
206
+ updated_at: ts(),
207
+ }).where(qaWhereKey(matched));
208
+ }
209
+ catch {
210
+ /* ignore */
211
+ }
212
+ }
213
+ const sender = resolveSender(input);
214
+ let answer = String(matched.answer ?? '');
215
+ if (matched.is_regex) {
216
+ try {
217
+ const m = content.match(new RegExp(String(matched.question), 'i'));
218
+ answer = processAnswer(answer, sender, m || undefined);
219
+ }
220
+ catch {
221
+ /* ignore */
222
+ }
223
+ }
224
+ else {
225
+ answer = processAnswer(answer, sender);
226
+ }
227
+ return answer;
228
+ }
229
+ /** Test helper */
230
+ export function resetTeachCooldown() {
231
+ cooldownMap.clear();
232
+ }
233
+ //# sourceMappingURL=teach-lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teach-lib.js","sourceRoot":"","sources":["../src/teach-lib.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { defineMiddleware } from '@zhin.js/middleware';
2
+ import type { Message } from '@zhin.js/core/runtime';
3
+ import { resolveGroupSuiteConfig, type GroupSuiteConfig } from '../src/config.js';
4
+ import { matchKeyword } from '../src/keyword-store.js';
5
+ import { resolveGroupSuiteRuntime } from '../src/runtime-state.js';
6
+
7
+ /**
8
+ * Keyword auto-reply (enabled via config.keywordReply).
9
+ * Uses Runtime Message.content / metadata (legacy used $raw / $channel).
10
+ */
11
+ export default defineMiddleware<Message, GroupSuiteConfig>({
12
+ target: 'inbound',
13
+ async handle(context, next) {
14
+ const config = resolveGroupSuiteConfig(context.config);
15
+ if (!config.keywordReply) {
16
+ await next();
17
+ return;
18
+ }
19
+ const meta = context.input.metadata ?? {};
20
+ const channelType = String(meta.type ?? meta.channelType ?? '');
21
+ if (channelType === 'private') {
22
+ await next();
23
+ return;
24
+ }
25
+ const text = typeof context.input.content === 'string' ? context.input.content.trim() : '';
26
+ if (!text) {
27
+ await next();
28
+ return;
29
+ }
30
+ const reply = matchKeyword(text, resolveGroupSuiteRuntime(context)?.keywords);
31
+ if (reply) {
32
+ await context.input.$reply(reply);
33
+ return;
34
+ }
35
+ await next();
36
+ },
37
+ });
@@ -0,0 +1,22 @@
1
+ import { defineMiddleware } from '@zhin.js/middleware';
2
+ import type { Message } from '@zhin.js/core/runtime';
3
+ import type { GroupSuiteConfig } from '../src/config.js';
4
+ import { recordMessage } from '../src/stats-lib.js';
5
+ import { resolveGroupSuiteRuntime } from '../src/runtime-state.js';
6
+
7
+ /**
8
+ * Buffer inbound messages into in-memory message_stats (flushed on query).
9
+ * Skips command messages (legacy used `isActionMessage` to skip interactive
10
+ * action/command messages; the Runtime pipeline represents commands as
11
+ * text starting with the dispatcher prefix '/').
12
+ */
13
+ export default defineMiddleware<Message, GroupSuiteConfig>({
14
+ target: 'inbound',
15
+ async handle(context, next) {
16
+ const content = typeof context.input.content === 'string' ? context.input.content : '';
17
+ if (!content.startsWith('/')) {
18
+ recordMessage(context.input, resolveGroupSuiteRuntime(context));
19
+ }
20
+ await next();
21
+ },
22
+ });
@@ -0,0 +1,22 @@
1
+ import { defineMiddleware } from '@zhin.js/middleware';
2
+ import type { Message } from '@zhin.js/core/runtime';
3
+ import { resolveGroupSuiteConfig, type GroupSuiteConfig } from '../src/config.js';
4
+ import { tryTeachReply } from '../src/teach-lib.js';
5
+ import { resolveGroupSuiteRuntime } from '../src/runtime-state.js';
6
+
7
+ /**
8
+ * Teach Q&A auto-reply (exact / optional regex).
9
+ * Uses Runtime Message.content / metadata.
10
+ */
11
+ export default defineMiddleware<Message, GroupSuiteConfig>({
12
+ target: 'inbound',
13
+ async handle(context, next) {
14
+ const config = resolveGroupSuiteConfig(context.config);
15
+ const reply = await tryTeachReply(context.input, config, resolveGroupSuiteRuntime(context));
16
+ if (reply) {
17
+ await context.input.$reply(reply);
18
+ return;
19
+ }
20
+ await next();
21
+ },
22
+ });