@zhin.js/plugin-group-suite 1.1.0 → 1.1.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.
- package/README.md +2 -2
- package/commands/{checkin.js → checkin/index.js} +3 -3
- package/commands/{checkin.ts → checkin/index.ts} +3 -3
- package/commands/{forget.js → forget/index.js} +2 -2
- package/commands/{forget.ts → forget/index.ts} +3 -3
- package/commands/keyword-add/{[keyword].js → [keyword]/index.js} +3 -3
- package/commands/keyword-add/{[keyword].ts → [keyword]/index.ts} +4 -4
- package/commands/{keyword-list.js → keyword-list/index.js} +3 -3
- package/commands/{keyword-list.ts → keyword-list/index.ts} +4 -4
- package/commands/keyword-remove/{[keyword].js → [keyword]/index.js} +3 -3
- package/commands/keyword-remove/{[keyword].ts → [keyword]/index.ts} +4 -4
- package/commands/{mypoints.js → mypoints/index.js} +2 -2
- package/commands/{mypoints.ts → mypoints/index.ts} +3 -3
- package/commands/{mystats.js → mystats/index.js} +2 -2
- package/commands/{mystats.ts → mystats/index.ts} +3 -3
- package/commands/{rank.js → rank/index.js} +3 -3
- package/commands/{rank.ts → rank/index.ts} +3 -3
- package/commands/{stats.js → stats/index.js} +4 -4
- package/commands/{stats.ts → stats/index.ts} +4 -4
- package/commands/{stats-rank.js → stats-rank/index.js} +3 -3
- package/commands/{stats-rank.ts → stats-rank/index.ts} +3 -3
- package/commands/{stats-week.js → stats-week/index.js} +3 -3
- package/commands/{stats-week.ts → stats-week/index.ts} +3 -3
- package/commands/{teach.js → teach/index.js} +3 -3
- package/commands/{teach.ts → teach/index.ts} +3 -3
- package/commands/teach-list/{[[page]].js → [[page]]/index.js} +3 -3
- package/commands/teach-list/{[[page]].ts → [[page]]/index.ts} +3 -3
- package/commands/{teach-regex.js → teach-regex/index.js} +3 -3
- package/commands/{teach-regex.ts → teach-regex/index.ts} +3 -3
- package/lib/checkin-lib.d.ts +3 -3
- package/lib/checkin-lib.d.ts.map +1 -1
- package/lib/checkin-lib.js +9 -10
- package/lib/checkin-lib.js.map +1 -1
- package/lib/db-store.d.ts +3 -10
- package/lib/db-store.d.ts.map +1 -1
- package/lib/db-store.js +7 -34
- package/lib/db-store.js.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -4
- package/lib/index.js.map +1 -1
- package/lib/keyword-store.d.ts +4 -5
- package/lib/keyword-store.d.ts.map +1 -1
- package/lib/keyword-store.js +4 -8
- package/lib/keyword-store.js.map +1 -1
- package/lib/memory-store.d.ts +1 -1
- package/lib/memory-store.js +1 -1
- package/lib/runtime-state.d.ts +3 -4
- package/lib/runtime-state.d.ts.map +1 -1
- package/lib/runtime-state.js +2 -8
- package/lib/runtime-state.js.map +1 -1
- package/lib/stats-lib.d.ts +5 -7
- package/lib/stats-lib.d.ts.map +1 -1
- package/lib/stats-lib.js +5 -10
- package/lib/stats-lib.js.map +1 -1
- package/lib/tables.js +1 -1
- package/lib/teach-lib.d.ts +4 -6
- package/lib/teach-lib.d.ts.map +1 -1
- package/lib/teach-lib.js +6 -11
- package/lib/teach-lib.js.map +1 -1
- package/middlewares/{keyword-reply.js → keyword-reply/index.js} +4 -4
- package/middlewares/{keyword-reply.ts → keyword-reply/index.ts} +4 -4
- package/middlewares/{stats-count.js → stats-count/index.js} +2 -2
- package/middlewares/{stats-count.ts → stats-count/index.ts} +3 -3
- package/middlewares/{teach-reply.js → teach-reply/index.js} +3 -3
- package/middlewares/{teach-reply.ts → teach-reply/index.ts} +3 -3
- package/package.json +18 -14
- package/plugin.js +0 -2
- package/skills/checkin/tools/checkin_query/index.js +42 -0
- package/skills/checkin/tools/checkin_query/index.ts +41 -0
- package/skills/checkin/tools/checkin_rank/index.js +34 -0
- package/skills/checkin/tools/checkin_rank/index.ts +34 -0
- package/skills/group-daily-analysis/SKILL.md +36 -0
- package/skills/group-daily-analysis/tools/group_announce/index.js +14 -0
- package/skills/group-daily-analysis/tools/group_announce/index.ts +14 -0
- package/skills/group-daily-analysis/tools/stats_query/index.js +35 -0
- package/skills/group-daily-analysis/tools/stats_query/index.ts +36 -0
- package/skills/group-daily-analysis/tools/stats_user/index.js +50 -0
- package/skills/group-daily-analysis/tools/stats_user/index.ts +46 -0
- package/src/checkin-lib.ts +15 -13
- package/src/db-store.ts +6 -38
- package/src/index.ts +0 -8
- package/src/keyword-store.ts +4 -10
- package/src/memory-store.ts +1 -1
- package/src/runtime-state.ts +5 -8
- package/src/stats-lib.ts +10 -17
- package/src/tables.ts +1 -1
- package/src/teach-lib.ts +11 -18
- package/agent/skills/group-daily-analysis.md +0 -29
- package/agent/tools/group_announce.ts +0 -12
- package/tools/checkin-query/checkin-query.tool.md +0 -11
- package/tools/checkin-query/handler.js +0 -22
- package/tools/checkin-query/handler.ts +0 -22
- package/tools/checkin-rank/checkin-rank.tool.md +0 -11
- package/tools/checkin-rank/handler.js +0 -15
- package/tools/checkin-rank/handler.ts +0 -16
- package/tools/stats-query/handler.js +0 -28
- package/tools/stats-query/handler.ts +0 -31
- package/tools/stats-query/stats-query.tool.md +0 -16
- package/tools/stats-user/handler.js +0 -29
- package/tools/stats-user/handler.ts +0 -29
- package/tools/stats-user/stats-user.tool.md +0 -12
- /package/{agent/skills/checkin.md → skills/checkin/SKILL.md} +0 -0
package/src/stats-lib.ts
CHANGED
|
@@ -25,8 +25,6 @@ export function monthStartStr(): string {
|
|
|
25
25
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-01`;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const buffer = new Map<string, PendingStatsIncrement>();
|
|
29
|
-
|
|
30
28
|
function bufferKey(userId: string, groupId: string, date: string): string {
|
|
31
29
|
return `${userId}:${groupId}:${date}`;
|
|
32
30
|
}
|
|
@@ -50,8 +48,8 @@ export function recordMessage(input: {
|
|
|
50
48
|
sender?: { readonly id: string; readonly name?: string };
|
|
51
49
|
conversation?: { readonly kind?: string; readonly id?: string };
|
|
52
50
|
metadata?: Readonly<Record<string, unknown>>;
|
|
53
|
-
}, runtime
|
|
54
|
-
const targetBuffer = runtime
|
|
51
|
+
}, runtime: GroupSuiteRuntime): void {
|
|
52
|
+
const targetBuffer = runtime.statsBuffer;
|
|
55
53
|
const { id: userId, name: userName } = resolveSender(input);
|
|
56
54
|
if (!userId) return;
|
|
57
55
|
const groupId = resolveGroupId(input);
|
|
@@ -71,9 +69,9 @@ export function recordMessage(input: {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
export async function flushStatsBuffer(runtime
|
|
75
|
-
const targetBuffer = runtime
|
|
76
|
-
const M = getStatsModel(runtime
|
|
72
|
+
export async function flushStatsBuffer(runtime: GroupSuiteRuntime): Promise<void> {
|
|
73
|
+
const targetBuffer = runtime.statsBuffer;
|
|
74
|
+
const M = getStatsModel(runtime.db);
|
|
77
75
|
if (!M || targetBuffer.size === 0) return;
|
|
78
76
|
for (const [key, entry] of [...targetBuffer.entries()]) {
|
|
79
77
|
// 快照本次要落库的计数;flush 期间新累计的部分留在缓冲
|
|
@@ -116,9 +114,9 @@ export async function flushStatsBuffer(runtime?: GroupSuiteRuntime): Promise<voi
|
|
|
116
114
|
export async function queryStats(
|
|
117
115
|
groupId: string,
|
|
118
116
|
fromDate: string,
|
|
119
|
-
runtime
|
|
117
|
+
runtime: GroupSuiteRuntime,
|
|
120
118
|
): Promise<Map<string, { name: string; count: number }>> {
|
|
121
|
-
const M = getStatsModel(runtime
|
|
119
|
+
const M = getStatsModel(runtime.db);
|
|
122
120
|
if (!M) return new Map();
|
|
123
121
|
const all = (groupId
|
|
124
122
|
? await M.select().where({ group_id: groupId })
|
|
@@ -152,7 +150,7 @@ export async function statsRankText(
|
|
|
152
150
|
cfg: GroupSuiteConfig,
|
|
153
151
|
fromDate: string,
|
|
154
152
|
title: string,
|
|
155
|
-
runtime
|
|
153
|
+
runtime: GroupSuiteRuntime,
|
|
156
154
|
): Promise<string> {
|
|
157
155
|
await flushStatsBuffer(runtime);
|
|
158
156
|
const groupId = resolveGroupId(input);
|
|
@@ -162,9 +160,9 @@ export async function statsRankText(
|
|
|
162
160
|
return formatRankText(data);
|
|
163
161
|
}
|
|
164
162
|
|
|
165
|
-
export async function myStatsText(input: MessageInput, runtime
|
|
163
|
+
export async function myStatsText(input: MessageInput, runtime: GroupSuiteRuntime): Promise<string> {
|
|
166
164
|
await flushStatsBuffer(runtime);
|
|
167
|
-
const M = getStatsModel(runtime
|
|
165
|
+
const M = getStatsModel(runtime.db);
|
|
168
166
|
if (!M) return '统计数据库尚未就绪';
|
|
169
167
|
const { id: userId, name: userName } = resolveSender(input);
|
|
170
168
|
if (!userId) return '无法获取用户信息';
|
|
@@ -198,8 +196,3 @@ export async function myStatsText(input: MessageInput, runtime?: GroupSuiteRunti
|
|
|
198
196
|
};
|
|
199
197
|
return formatMyStatsText(data);
|
|
200
198
|
}
|
|
201
|
-
|
|
202
|
-
/** Test helper */
|
|
203
|
-
export function resetStatsBuffer(): void {
|
|
204
|
-
buffer.clear();
|
|
205
|
-
}
|
package/src/tables.ts
CHANGED
package/src/teach-lib.ts
CHANGED
|
@@ -7,8 +7,6 @@ import {
|
|
|
7
7
|
} from './shared-runtime.js';
|
|
8
8
|
import type { GroupSuiteRuntime } from './runtime-state.js';
|
|
9
9
|
|
|
10
|
-
const cooldownMap = new Map<string, number>();
|
|
11
|
-
|
|
12
10
|
export const TEACH_USAGE_HINT =
|
|
13
11
|
'格式:teach 关键词 回答(回答可含空格),或 teach 问题|答案';
|
|
14
12
|
|
|
@@ -84,9 +82,9 @@ async function findMatch(
|
|
|
84
82
|
ctxType: string,
|
|
85
83
|
ctxId: string,
|
|
86
84
|
cfg: GroupSuiteConfig,
|
|
87
|
-
runtime
|
|
85
|
+
runtime: GroupSuiteRuntime,
|
|
88
86
|
): Promise<Record<string, unknown> | null> {
|
|
89
|
-
const QA = getTeachModel(runtime
|
|
87
|
+
const QA = getTeachModel(runtime.db);
|
|
90
88
|
if (!QA) return null;
|
|
91
89
|
const allItems = (await QA.select()) as Record<string, unknown>[];
|
|
92
90
|
const candidates = allItems.filter(
|
|
@@ -115,9 +113,9 @@ export async function teachAdd(
|
|
|
115
113
|
cfg: GroupSuiteConfig,
|
|
116
114
|
payload: string,
|
|
117
115
|
regex = false,
|
|
118
|
-
runtime
|
|
116
|
+
runtime: GroupSuiteRuntime,
|
|
119
117
|
): Promise<string> {
|
|
120
|
-
const QA = getTeachModel(runtime
|
|
118
|
+
const QA = getTeachModel(runtime.db);
|
|
121
119
|
if (!QA) return '问答数据库尚未就绪,请稍后重试';
|
|
122
120
|
if (regex && !cfg.teachAllowRegex) return '管理员已禁用正则问答';
|
|
123
121
|
|
|
@@ -191,9 +189,9 @@ export async function teachList(
|
|
|
191
189
|
input: MessageInput,
|
|
192
190
|
cfg: GroupSuiteConfig,
|
|
193
191
|
page = 1,
|
|
194
|
-
runtime
|
|
192
|
+
runtime: GroupSuiteRuntime,
|
|
195
193
|
): Promise<string> {
|
|
196
|
-
const QA = getTeachModel(runtime
|
|
194
|
+
const QA = getTeachModel(runtime.db);
|
|
197
195
|
if (!QA) return '问答数据库尚未就绪';
|
|
198
196
|
const safePage = Math.max(1, page);
|
|
199
197
|
const { type: ctxType, id: ctxId } = resolveContextKey(input);
|
|
@@ -219,9 +217,9 @@ export async function teachList(
|
|
|
219
217
|
export async function teachForget(
|
|
220
218
|
input: MessageInput,
|
|
221
219
|
questionRaw: string,
|
|
222
|
-
runtime
|
|
220
|
+
runtime: GroupSuiteRuntime,
|
|
223
221
|
): Promise<string> {
|
|
224
|
-
const QA = getTeachModel(runtime
|
|
222
|
+
const QA = getTeachModel(runtime.db);
|
|
225
223
|
if (!QA) return '问答数据库尚未就绪';
|
|
226
224
|
const question = questionRaw.trim();
|
|
227
225
|
if (!question) return '请提供要删除的问题';
|
|
@@ -247,7 +245,7 @@ export async function teachForget(
|
|
|
247
245
|
export async function tryTeachReply(
|
|
248
246
|
input: MessageInput,
|
|
249
247
|
cfg: GroupSuiteConfig,
|
|
250
|
-
runtime
|
|
248
|
+
runtime: GroupSuiteRuntime,
|
|
251
249
|
): Promise<string | null> {
|
|
252
250
|
const content = typeof input.content === 'string' ? input.content.trim() : '';
|
|
253
251
|
if (!content) return null;
|
|
@@ -256,12 +254,12 @@ export async function tryTeachReply(
|
|
|
256
254
|
if (!matched) return null;
|
|
257
255
|
|
|
258
256
|
const cooldownKey = `${matched.question}:${ctxType}:${ctxId}:${matched.is_regex ?? 0}`;
|
|
259
|
-
const cooldowns = runtime
|
|
257
|
+
const cooldowns = runtime.teachCooldowns;
|
|
260
258
|
const last = cooldowns.get(cooldownKey);
|
|
261
259
|
if (last && Date.now() - last < cfg.teachCooldownMs) return null;
|
|
262
260
|
cooldowns.set(cooldownKey, Date.now());
|
|
263
261
|
|
|
264
|
-
const QA = getTeachModel(runtime
|
|
262
|
+
const QA = getTeachModel(runtime.db);
|
|
265
263
|
if (QA) {
|
|
266
264
|
try {
|
|
267
265
|
await QA.update({
|
|
@@ -294,8 +292,3 @@ export async function tryTeachReply(
|
|
|
294
292
|
}
|
|
295
293
|
return answer;
|
|
296
294
|
}
|
|
297
|
-
|
|
298
|
-
/** Test helper */
|
|
299
|
-
export function resetTeachCooldown(): void {
|
|
300
|
-
cooldownMap.clear();
|
|
301
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: group-daily-analysis
|
|
3
|
-
description: >-
|
|
4
|
-
群日常分析:基于收件箱统计群消息数量、参与人数、活跃时段,
|
|
5
|
-
支持按群启用/禁用和定时分析报告。通过命令和定时任务运行,无 AI 工具。
|
|
6
|
-
keywords:
|
|
7
|
-
- 群分析
|
|
8
|
-
- 日常分析
|
|
9
|
-
- 群统计
|
|
10
|
-
- 活跃
|
|
11
|
-
- 报告
|
|
12
|
-
tags:
|
|
13
|
-
- group
|
|
14
|
-
- analysis
|
|
15
|
-
- stats
|
|
16
|
-
- inbox
|
|
17
|
-
tools: []
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
# 群日常分析
|
|
21
|
-
|
|
22
|
-
基于收件箱数据自动分析群活跃度。无 AI 工具可调用。
|
|
23
|
-
|
|
24
|
-
## 能力
|
|
25
|
-
|
|
26
|
-
- 统计群消息数量、参与人数、活跃时段分布
|
|
27
|
-
- 支持按群启用/禁用分析功能
|
|
28
|
-
- 定时任务自动生成分析报告推送到群内
|
|
29
|
-
- 通过聊天命令管理分析设置
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ message: string }>({
|
|
5
|
-
description: '向群聊发送公告/通知消息',
|
|
6
|
-
inputSchema: z.object({ message: z.string().min(1) }),
|
|
7
|
-
keywords: ['群公告', '通知', 'announce'],
|
|
8
|
-
async execute({ message }) {
|
|
9
|
-
if (!message?.trim()) return '公告内容不能为空';
|
|
10
|
-
return `📢 群公告:\n${message}`;
|
|
11
|
-
},
|
|
12
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { getCheckinModel } from "../../lib/db-store.js";
|
|
3
|
-
import { todayStr } from "../../lib/shared-runtime.js";
|
|
4
|
-
export default async function (args) {
|
|
5
|
-
const M = getCheckinModel();
|
|
6
|
-
if (!M)
|
|
7
|
-
return '签到数据库尚未就绪';
|
|
8
|
-
const userId = args.user_id;
|
|
9
|
-
if (userId) {
|
|
10
|
-
const rows = await M.select().where({ user_id: userId });
|
|
11
|
-
if (rows.length === 0)
|
|
12
|
-
return `用户 ${userId} 没有签到记录`;
|
|
13
|
-
const u = rows[0];
|
|
14
|
-
return `${u.user_name}: 积分=${u.points}, 累计=${u.total_checkins}天, 连续=${u.streak}天, 最长=${u.max_streak}天, 上次=${u.last_checkin}`;
|
|
15
|
-
}
|
|
16
|
-
const all = await M.select();
|
|
17
|
-
const totalUsers = all.length;
|
|
18
|
-
const totalPoints = all.reduce((s, u) => s + (u.points || 0), 0);
|
|
19
|
-
const today = todayStr();
|
|
20
|
-
const todayCount = all.filter((u) => u.last_checkin === today).length;
|
|
21
|
-
return `签到系统统计\n总用户: ${totalUsers}\n总积分: ${totalPoints}\n今日签到: ${todayCount}人`;
|
|
22
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { getCheckinModel } from '../../src/db-store.js';
|
|
2
|
-
import { todayStr } from '../../src/shared-runtime.js';
|
|
3
|
-
|
|
4
|
-
export default async function (args: { user_id?: string }) {
|
|
5
|
-
const M = getCheckinModel();
|
|
6
|
-
if (!M) return '签到数据库尚未就绪';
|
|
7
|
-
|
|
8
|
-
const userId = args.user_id;
|
|
9
|
-
if (userId) {
|
|
10
|
-
const rows: any[] = await M.select().where({ user_id: userId });
|
|
11
|
-
if (rows.length === 0) return `用户 ${userId} 没有签到记录`;
|
|
12
|
-
const u = rows[0];
|
|
13
|
-
return `${u.user_name}: 积分=${u.points}, 累计=${u.total_checkins}天, 连续=${u.streak}天, 最长=${u.max_streak}天, 上次=${u.last_checkin}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const all: any[] = await M.select();
|
|
17
|
-
const totalUsers = all.length;
|
|
18
|
-
const totalPoints = all.reduce((s: number, u: any) => s + (u.points || 0), 0);
|
|
19
|
-
const today = todayStr();
|
|
20
|
-
const todayCount = all.filter((u: any) => u.last_checkin === today).length;
|
|
21
|
-
return `签到系统统计\n总用户: ${totalUsers}\n总积分: ${totalPoints}\n今日签到: ${todayCount}人`;
|
|
22
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { getCheckinModel } from "../../lib/db-store.js";
|
|
3
|
-
export default async function (args) {
|
|
4
|
-
const M = getCheckinModel();
|
|
5
|
-
if (!M)
|
|
6
|
-
return '签到数据库尚未就绪';
|
|
7
|
-
const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50);
|
|
8
|
-
const all = await M.select();
|
|
9
|
-
const sorted = all.sort((a, b) => (b.points || 0) - (a.points || 0)).slice(0, limit);
|
|
10
|
-
if (sorted.length === 0)
|
|
11
|
-
return '暂无排行数据';
|
|
12
|
-
return sorted
|
|
13
|
-
.map((u, i) => `${i + 1}. ${u.user_name || u.user_id} — ${u.points}分 (连续${u.streak}天)`)
|
|
14
|
-
.join('\n');
|
|
15
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { getCheckinModel } from '../../src/db-store.js';
|
|
2
|
-
|
|
3
|
-
export default async function (args: { limit?: number }) {
|
|
4
|
-
const M = getCheckinModel();
|
|
5
|
-
if (!M) return '签到数据库尚未就绪';
|
|
6
|
-
|
|
7
|
-
const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50);
|
|
8
|
-
const all: any[] = await M.select();
|
|
9
|
-
const sorted = all.sort((a: any, b: any) => (b.points || 0) - (a.points || 0)).slice(0, limit);
|
|
10
|
-
|
|
11
|
-
if (sorted.length === 0) return '暂无排行数据';
|
|
12
|
-
|
|
13
|
-
return sorted
|
|
14
|
-
.map((u: any, i: number) => `${i + 1}. ${u.user_name || u.user_id} — ${u.points}分 (连续${u.streak}天)`)
|
|
15
|
-
.join('\n');
|
|
16
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { flushStatsBuffer, queryStats, weekStartStr, monthStartStr, } from "../../lib/stats-lib.js";
|
|
3
|
-
import { todayStr } from "../../lib/shared-runtime.js";
|
|
4
|
-
export default async function (args) {
|
|
5
|
-
await flushStatsBuffer();
|
|
6
|
-
const groupId = args.group_id || '';
|
|
7
|
-
const period = args.period || 'today';
|
|
8
|
-
let fromDate;
|
|
9
|
-
switch (period) {
|
|
10
|
-
case 'week':
|
|
11
|
-
fromDate = weekStartStr();
|
|
12
|
-
break;
|
|
13
|
-
case 'month':
|
|
14
|
-
fromDate = monthStartStr();
|
|
15
|
-
break;
|
|
16
|
-
default:
|
|
17
|
-
fromDate = todayStr();
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
const stats = await queryStats(groupId, fromDate);
|
|
21
|
-
if (stats.size === 0)
|
|
22
|
-
return `${period} 暂无消息统计数据`;
|
|
23
|
-
const sorted = [...stats.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
24
|
-
const total = sorted.reduce((s, [, v]) => s + v.count, 0);
|
|
25
|
-
const top10 = sorted.slice(0, 10);
|
|
26
|
-
const lines = top10.map(([, v], i) => `${i + 1}. ${v.name} — ${v.count}条`);
|
|
27
|
-
return `消息统计 (${period})\n总消息: ${total}条, 活跃用户: ${stats.size}人\n${lines.join('\n')}`;
|
|
28
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
flushStatsBuffer,
|
|
3
|
-
queryStats,
|
|
4
|
-
weekStartStr,
|
|
5
|
-
monthStartStr,
|
|
6
|
-
} from '../../src/stats-lib.js';
|
|
7
|
-
import { todayStr } from '../../src/shared-runtime.js';
|
|
8
|
-
|
|
9
|
-
export default async function (args: { group_id?: string; period?: string }) {
|
|
10
|
-
await flushStatsBuffer();
|
|
11
|
-
const groupId = args.group_id || '';
|
|
12
|
-
const period = args.period || 'today';
|
|
13
|
-
|
|
14
|
-
let fromDate: string;
|
|
15
|
-
switch (period) {
|
|
16
|
-
case 'week': fromDate = weekStartStr(); break;
|
|
17
|
-
case 'month': fromDate = monthStartStr(); break;
|
|
18
|
-
default: fromDate = todayStr(); break;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const stats = await queryStats(groupId, fromDate);
|
|
22
|
-
if (stats.size === 0) return `${period} 暂无消息统计数据`;
|
|
23
|
-
|
|
24
|
-
const sorted = [...stats.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
25
|
-
const total = sorted.reduce((s, [, v]) => s + v.count, 0);
|
|
26
|
-
|
|
27
|
-
const top10 = sorted.slice(0, 10);
|
|
28
|
-
const lines = top10.map(([, v], i) => `${i + 1}. ${v.name} — ${v.count}条`);
|
|
29
|
-
|
|
30
|
-
return `消息统计 (${period})\n总消息: ${total}条, 活跃用户: ${stats.size}人\n${lines.join('\n')}`;
|
|
31
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: stats_query
|
|
3
|
-
description: 查询消息统计数据
|
|
4
|
-
parameters:
|
|
5
|
-
group_id:
|
|
6
|
-
type: string
|
|
7
|
-
description: 群ID(可选,不填查全局)
|
|
8
|
-
period:
|
|
9
|
-
type: string
|
|
10
|
-
description: "时段: today/week/month(默认today)"
|
|
11
|
-
enum: [today, week, month]
|
|
12
|
-
default: today
|
|
13
|
-
tags: [统计, 消息]
|
|
14
|
-
keywords: [统计, 消息统计, 消息量]
|
|
15
|
-
handler: ./handler.ts
|
|
16
|
-
---
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { flushStatsBuffer, weekStartStr } from "../../lib/stats-lib.js";
|
|
3
|
-
import { getStatsModel } from "../../lib/db-store.js";
|
|
4
|
-
import { todayStr } from "../../lib/shared-runtime.js";
|
|
5
|
-
export default async function (args) {
|
|
6
|
-
await flushStatsBuffer();
|
|
7
|
-
const M = getStatsModel();
|
|
8
|
-
if (!M)
|
|
9
|
-
return '统计数据库尚未就绪';
|
|
10
|
-
const userId = args.user_id;
|
|
11
|
-
if (!userId)
|
|
12
|
-
return '请提供用户ID';
|
|
13
|
-
const rows = await M.select().where({ user_id: userId });
|
|
14
|
-
if (rows.length === 0)
|
|
15
|
-
return `用户 ${userId} 暂无消息记录`;
|
|
16
|
-
const today = todayStr();
|
|
17
|
-
const weekStart = weekStartStr();
|
|
18
|
-
let todayCount = 0, weekCount = 0, totalCount = 0;
|
|
19
|
-
for (const row of rows) {
|
|
20
|
-
const c = row.count || 0;
|
|
21
|
-
totalCount += c;
|
|
22
|
-
if (row.date >= weekStart)
|
|
23
|
-
weekCount += c;
|
|
24
|
-
if (row.date === today)
|
|
25
|
-
todayCount += c;
|
|
26
|
-
}
|
|
27
|
-
const name = rows[0].user_name || userId;
|
|
28
|
-
return `${name} 的统计\n今日: ${todayCount}条\n本周: ${weekCount}条\n总计: ${totalCount}条\n活跃天数: ${rows.length}天`;
|
|
29
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { flushStatsBuffer, weekStartStr } from '../../src/stats-lib.js';
|
|
2
|
-
import { getStatsModel } from '../../src/db-store.js';
|
|
3
|
-
import { todayStr } from '../../src/shared-runtime.js';
|
|
4
|
-
|
|
5
|
-
export default async function (args: { user_id: string }) {
|
|
6
|
-
await flushStatsBuffer();
|
|
7
|
-
const M = getStatsModel();
|
|
8
|
-
if (!M) return '统计数据库尚未就绪';
|
|
9
|
-
|
|
10
|
-
const userId = args.user_id;
|
|
11
|
-
if (!userId) return '请提供用户ID';
|
|
12
|
-
|
|
13
|
-
const rows: any[] = await M.select().where({ user_id: userId });
|
|
14
|
-
if (rows.length === 0) return `用户 ${userId} 暂无消息记录`;
|
|
15
|
-
|
|
16
|
-
const today = todayStr();
|
|
17
|
-
const weekStart = weekStartStr();
|
|
18
|
-
let todayCount = 0, weekCount = 0, totalCount = 0;
|
|
19
|
-
|
|
20
|
-
for (const row of rows) {
|
|
21
|
-
const c = row.count || 0;
|
|
22
|
-
totalCount += c;
|
|
23
|
-
if (row.date >= weekStart) weekCount += c;
|
|
24
|
-
if (row.date === today) todayCount += c;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const name = rows[0].user_name || userId;
|
|
28
|
-
return `${name} 的统计\n今日: ${todayCount}条\n本周: ${weekCount}条\n总计: ${totalCount}条\n活跃天数: ${rows.length}天`;
|
|
29
|
-
}
|
|
File without changes
|