@zhin.js/plugin-group-suite 0.1.7 → 0.1.8
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 +11 -0
- package/lib/analysis-card.d.ts +4 -0
- package/lib/analysis-card.d.ts.map +1 -0
- package/lib/analysis-card.js +78 -0
- package/lib/analysis-card.js.map +1 -0
- package/lib/analysis.d.ts +12 -0
- package/lib/analysis.d.ts.map +1 -1
- package/lib/analysis.js +3 -0
- package/lib/analysis.js.map +1 -1
- package/lib/card-layout.d.ts +27 -0
- package/lib/card-layout.d.ts.map +1 -0
- package/lib/card-layout.js +11 -0
- package/lib/card-layout.js.map +1 -0
- package/lib/config.d.ts +0 -3
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +0 -3
- package/lib/config.js.map +1 -1
- package/lib/daily-analysis.d.ts.map +1 -1
- package/lib/daily-analysis.js +23 -35
- package/lib/daily-analysis.js.map +1 -1
- package/lib/stats-card.d.ts +5 -0
- package/lib/stats-card.d.ts.map +1 -0
- package/lib/stats-card.js +61 -0
- package/lib/stats-card.js.map +1 -0
- package/lib/stats-data.d.ts +32 -0
- package/lib/stats-data.d.ts.map +1 -0
- package/lib/stats-data.js +64 -0
- package/lib/stats-data.js.map +1 -0
- package/lib/stats.d.ts.map +1 -1
- package/lib/stats.js +33 -30
- package/lib/stats.js.map +1 -1
- package/package.json +8 -6
- package/src/analysis-card.ts +99 -0
- package/src/analysis.ts +21 -0
- package/src/card-layout.ts +21 -0
- package/src/config.ts +0 -4
- package/src/daily-analysis.ts +25 -34
- package/src/stats-card.ts +73 -0
- package/src/stats-data.ts +101 -0
- package/src/stats.ts +35 -35
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export interface StatsRankEntry {
|
|
2
|
+
rank: number;
|
|
3
|
+
userId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
count: number;
|
|
6
|
+
percent: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface StatsRankReportData {
|
|
10
|
+
title: string;
|
|
11
|
+
entries: StatsRankEntry[];
|
|
12
|
+
participantCount: number;
|
|
13
|
+
totalMessages: number;
|
|
14
|
+
myRank?: number;
|
|
15
|
+
myCount?: number;
|
|
16
|
+
empty?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MyStatsReportData {
|
|
20
|
+
userName: string;
|
|
21
|
+
scope: string;
|
|
22
|
+
todayCount: number;
|
|
23
|
+
weekCount: number;
|
|
24
|
+
monthCount: number;
|
|
25
|
+
totalCount: number;
|
|
26
|
+
activeDays: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function buildStatsRankReportData(
|
|
30
|
+
stats: Map<string, { name: string; count: number }>,
|
|
31
|
+
title: string,
|
|
32
|
+
size: number,
|
|
33
|
+
myUserId?: string,
|
|
34
|
+
): StatsRankReportData {
|
|
35
|
+
const sorted = [...stats.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
36
|
+
const total = sorted.reduce((s, [, v]) => s + v.count, 0);
|
|
37
|
+
const topCount = sorted[0]?.[1].count || 1;
|
|
38
|
+
const top = sorted.slice(0, size);
|
|
39
|
+
|
|
40
|
+
if (top.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
title,
|
|
43
|
+
entries: [],
|
|
44
|
+
participantCount: 0,
|
|
45
|
+
totalMessages: 0,
|
|
46
|
+
empty: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const entries: StatsRankEntry[] = top.map(([userId, v], i) => ({
|
|
51
|
+
rank: i + 1,
|
|
52
|
+
userId,
|
|
53
|
+
name: v.name,
|
|
54
|
+
count: v.count,
|
|
55
|
+
percent: Math.max(4, Math.round((v.count / topCount) * 100)),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
let myRank: number | undefined;
|
|
59
|
+
let myCount: number | undefined;
|
|
60
|
+
if (myUserId) {
|
|
61
|
+
const idx = sorted.findIndex(([uid]) => uid === myUserId);
|
|
62
|
+
if (idx >= 0) {
|
|
63
|
+
myRank = idx + 1;
|
|
64
|
+
myCount = sorted[idx][1].count;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
title,
|
|
70
|
+
entries,
|
|
71
|
+
participantCount: stats.size,
|
|
72
|
+
totalMessages: total,
|
|
73
|
+
myRank,
|
|
74
|
+
myCount,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function formatRankText(data: StatsRankReportData): string {
|
|
79
|
+
if (data.empty) return `${data.title}\n暂无数据`;
|
|
80
|
+
const medals = ["🥇", "🥈", "🥉"];
|
|
81
|
+
const lines = data.entries.map((entry) => {
|
|
82
|
+
const prefix = entry.rank <= 3 ? medals[entry.rank - 1] : `${entry.rank}.`;
|
|
83
|
+
return `${prefix} ${entry.name} — ${entry.count}条`;
|
|
84
|
+
});
|
|
85
|
+
let footer = `\n共 ${data.participantCount} 人, ${data.totalMessages} 条消息`;
|
|
86
|
+
if (data.myRank != null && data.myCount != null) {
|
|
87
|
+
footer += ` | 你第${data.myRank}名 (${data.myCount}条)`;
|
|
88
|
+
}
|
|
89
|
+
return `${data.title}\n${lines.join("\n")}${footer}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function formatMyStatsText(data: MyStatsReportData): string {
|
|
93
|
+
return [
|
|
94
|
+
`${data.userName} 的${data.scope}消息统计`,
|
|
95
|
+
`今日: ${data.todayCount} 条`,
|
|
96
|
+
`本周: ${data.weekCount} 条`,
|
|
97
|
+
`本月: ${data.monthCount} 条`,
|
|
98
|
+
`总计: ${data.totalCount} 条`,
|
|
99
|
+
`活跃天数: ${data.activeDays} 天`,
|
|
100
|
+
].join("\n");
|
|
101
|
+
}
|
package/src/stats.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { formatCompact, MessageCommand, type Plugin } from "zhin.js";
|
|
1
|
+
import { formatCompact, MessageCommand, segment, type Plugin } from "zhin.js";
|
|
2
2
|
import type { GroupSuiteConfig } from "./config.js";
|
|
3
|
-
import {
|
|
3
|
+
import { todayStr, ts } from "./shared.js";
|
|
4
|
+
import { buildStatsRankReportData, type MyStatsReportData } from "./stats-data.js";
|
|
5
|
+
import { buildMyStatsHtml, buildStatsRankHtml, STATS_REPORT_CANVAS } from "./stats-card.js";
|
|
4
6
|
|
|
5
7
|
export function weekStartStr(): string {
|
|
6
8
|
const d = new Date();
|
|
@@ -105,29 +107,22 @@ export async function queryStats(
|
|
|
105
107
|
return result;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
function statsRankReply(data: ReturnType<typeof buildStatsRankReportData>) {
|
|
111
|
+
return segment.html({
|
|
112
|
+
html: buildStatsRankHtml(data),
|
|
113
|
+
width: 540,
|
|
114
|
+
backgroundColor: STATS_REPORT_CANVAS,
|
|
115
|
+
fileName: "message-stats-rank.png",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function myStatsReply(data: MyStatsReportData) {
|
|
120
|
+
return segment.html({
|
|
121
|
+
html: buildMyStatsHtml(data),
|
|
122
|
+
width: 540,
|
|
123
|
+
backgroundColor: STATS_REPORT_CANVAS,
|
|
124
|
+
fileName: "my-message-stats.png",
|
|
122
125
|
});
|
|
123
|
-
let footer = `\n共 ${stats.size} 人, ${total} 条消息`;
|
|
124
|
-
if (myUserId) {
|
|
125
|
-
const myRank = sorted.findIndex(([uid]) => uid === myUserId);
|
|
126
|
-
if (myRank >= 0) {
|
|
127
|
-
footer += ` | 你第${myRank + 1}名 (${sorted[myRank][1].count}条)`;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return `${title}\n${lines.join("\n")}${footer}`;
|
|
131
126
|
}
|
|
132
127
|
|
|
133
128
|
export function registerStats(plugin: Plugin, cfg: GroupSuiteConfig): void {
|
|
@@ -181,12 +176,13 @@ export function registerStats(plugin: Plugin, cfg: GroupSuiteConfig): void {
|
|
|
181
176
|
const groupId = getGroupId(message);
|
|
182
177
|
const stats = await queryStats(groupId, todayStr());
|
|
183
178
|
const title = groupId ? "今日本群消息统计" : "今日全局消息统计";
|
|
184
|
-
|
|
179
|
+
const data = buildStatsRankReportData(
|
|
185
180
|
stats,
|
|
186
181
|
title,
|
|
187
182
|
cfg.rankSize,
|
|
188
183
|
String(message.$sender?.id || ""),
|
|
189
184
|
);
|
|
185
|
+
return statsRankReply(data);
|
|
190
186
|
}),
|
|
191
187
|
);
|
|
192
188
|
|
|
@@ -198,12 +194,13 @@ export function registerStats(plugin: Plugin, cfg: GroupSuiteConfig): void {
|
|
|
198
194
|
const groupId = getGroupId(message);
|
|
199
195
|
const stats = await queryStats(groupId, weekStartStr());
|
|
200
196
|
const title = groupId ? "本周本群消息统计" : "本周全局消息统计";
|
|
201
|
-
|
|
197
|
+
const data = buildStatsRankReportData(
|
|
202
198
|
stats,
|
|
203
199
|
title,
|
|
204
200
|
cfg.rankSize,
|
|
205
201
|
String(message.$sender?.id || ""),
|
|
206
202
|
);
|
|
203
|
+
return statsRankReply(data);
|
|
207
204
|
}),
|
|
208
205
|
);
|
|
209
206
|
|
|
@@ -215,12 +212,13 @@ export function registerStats(plugin: Plugin, cfg: GroupSuiteConfig): void {
|
|
|
215
212
|
const groupId = getGroupId(message);
|
|
216
213
|
const stats = await queryStats(groupId, monthStartStr());
|
|
217
214
|
const title = groupId ? "本月话唠排行" : "全局话唠排行";
|
|
218
|
-
|
|
215
|
+
const data = buildStatsRankReportData(
|
|
219
216
|
stats,
|
|
220
217
|
title,
|
|
221
218
|
cfg.rankSize,
|
|
222
219
|
String(message.$sender?.id || ""),
|
|
223
220
|
);
|
|
221
|
+
return statsRankReply(data);
|
|
224
222
|
}),
|
|
225
223
|
);
|
|
226
224
|
|
|
@@ -253,14 +251,16 @@ export function registerStats(plugin: Plugin, cfg: GroupSuiteConfig): void {
|
|
|
253
251
|
if (row.date === today) todayCount += c;
|
|
254
252
|
}
|
|
255
253
|
const scope = groupId ? "本群" : "全局";
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
254
|
+
const data: MyStatsReportData = {
|
|
255
|
+
userName: String(message.$sender?.name || "你"),
|
|
256
|
+
scope,
|
|
257
|
+
todayCount,
|
|
258
|
+
weekCount,
|
|
259
|
+
monthCount,
|
|
260
|
+
totalCount,
|
|
261
|
+
activeDays: rows.length,
|
|
262
|
+
};
|
|
263
|
+
return myStatsReply(data);
|
|
264
264
|
}),
|
|
265
265
|
);
|
|
266
266
|
|