@zhin.js/plugin-group-suite 1.1.21 → 1.1.22
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 +1 -0
- package/lib/console.d.ts +27 -0
- package/lib/console.d.ts.map +1 -0
- package/lib/console.js +130 -0
- package/lib/console.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/package.json +24 -12
- package/pages/index/index.js +57 -0
- package/pages/index/index.ts +63 -0
- package/plugin.js +3 -1
- package/src/console.ts +178 -0
- package/src/index.ts +10 -0
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ group-suite:
|
|
|
73
73
|
| `keyword-add <keyword> <reply...>` / `keyword-list` / `keyword-remove <keyword>` | 当前群词库 |
|
|
74
74
|
| `welcomeMessage` | 当前群增加成员时欢迎,默认关闭,只在设置非空文本后发送 |
|
|
75
75
|
| `skills/<name>/tools/<tool>/index.ts` | Skill 激活后披露签到查询、排行、统计和群公告 Tool |
|
|
76
|
+
| `pages/index` + `pluginConsoleRpcToken` | Console「群管套件」页面(`group-suite:read`),展示签到记录数、问答词条数、签到排行榜与关键词问答规则 |
|
|
76
77
|
|
|
77
78
|
关键词与问答修改命令要求群主、管理员、master 或 trusted;普通成员不能修改。查询命令仍按其
|
|
78
79
|
各自公开 permit 执行。Tool 保持既有审批策略,命令角色判断不能替代 Tool 审批或平台权限。
|
package/lib/console.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type PluginConsoleRpc } from 'zhin.js';
|
|
2
|
+
import { type GroupSuiteRuntime } from './runtime-state.js';
|
|
3
|
+
export declare const GROUP_SUITE_CONSOLE_PAGE = "index";
|
|
4
|
+
export declare const GROUP_SUITE_READ_PERMISSION = "group-suite:read";
|
|
5
|
+
export interface GroupSuiteCheckinLeaderboardRow {
|
|
6
|
+
readonly userId: string;
|
|
7
|
+
readonly groupId: string;
|
|
8
|
+
readonly totalDays: number;
|
|
9
|
+
readonly streakDays: number;
|
|
10
|
+
readonly points: number;
|
|
11
|
+
readonly lastDate: string;
|
|
12
|
+
}
|
|
13
|
+
export interface GroupSuiteKeywordRow {
|
|
14
|
+
readonly groupId: string;
|
|
15
|
+
readonly keyword: string;
|
|
16
|
+
readonly reply: string;
|
|
17
|
+
readonly creatorId: string;
|
|
18
|
+
}
|
|
19
|
+
export interface GroupSuiteConsoleStatus {
|
|
20
|
+
readonly checkinCount: number;
|
|
21
|
+
readonly keywordCount: number;
|
|
22
|
+
readonly dailyStatsCount: number;
|
|
23
|
+
readonly topCheckins: readonly GroupSuiteCheckinLeaderboardRow[];
|
|
24
|
+
readonly keywords: readonly GroupSuiteKeywordRow[];
|
|
25
|
+
}
|
|
26
|
+
export declare function createGroupSuiteConsoleRpc(defaultRuntime?: GroupSuiteRuntime): PluginConsoleRpc;
|
|
27
|
+
//# sourceMappingURL=console.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EAItB,MAAM,SAAS,CAAC;AAQjB,OAAO,EAA0B,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,eAAO,MAAM,wBAAwB,UAAU,CAAC;AAChD,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAE9D,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,SAAS,+BAA+B,EAAE,CAAC;IACjE,QAAQ,CAAC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACpD;AAoGD,wBAAgB,0BAA0B,CACxC,cAAc,CAAC,EAAE,iBAAiB,GACjC,gBAAgB,CAkClB"}
|
package/lib/console.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { definePluginConsoleRpc, } from 'zhin.js';
|
|
2
|
+
import { CHECKIN_TABLE, STATS_TABLE, TEACH_TABLE, } from './memory-store.js';
|
|
3
|
+
import { groupSuiteRuntimeToken } from './runtime-state.js';
|
|
4
|
+
export const GROUP_SUITE_CONSOLE_PAGE = 'index';
|
|
5
|
+
export const GROUP_SUITE_READ_PERMISSION = 'group-suite:read';
|
|
6
|
+
function resolveRuntime(call, fallbackRuntime) {
|
|
7
|
+
const runtimeWithUse = call.runtime;
|
|
8
|
+
if (typeof runtimeWithUse?.use === 'function') {
|
|
9
|
+
try {
|
|
10
|
+
const resolved = runtimeWithUse.use(groupSuiteRuntimeToken);
|
|
11
|
+
if (resolved)
|
|
12
|
+
return resolved;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Fall back to the plugin setup runtime when call.runtime.use is unavailable.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return fallbackRuntime;
|
|
19
|
+
}
|
|
20
|
+
async function queryModelRows(model) {
|
|
21
|
+
if (!model || typeof model.select !== 'function')
|
|
22
|
+
return [];
|
|
23
|
+
try {
|
|
24
|
+
const selection = model.select();
|
|
25
|
+
if (Array.isArray(selection))
|
|
26
|
+
return selection;
|
|
27
|
+
if (selection && typeof selection.then === 'function') {
|
|
28
|
+
const rows = await selection;
|
|
29
|
+
return Array.isArray(rows) ? rows : [];
|
|
30
|
+
}
|
|
31
|
+
if (selection && typeof selection.where === 'function') {
|
|
32
|
+
const rows = await selection.where({});
|
|
33
|
+
return Array.isArray(rows) ? rows : [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Handle any database query failure gracefully.
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
async function readTableRows(runtime, tableNames) {
|
|
42
|
+
const getModel = runtime?.db?.models?.get?.bind(runtime.db.models);
|
|
43
|
+
if (typeof getModel !== 'function')
|
|
44
|
+
return [];
|
|
45
|
+
const collected = [];
|
|
46
|
+
const visited = new Set();
|
|
47
|
+
for (const name of tableNames) {
|
|
48
|
+
let model;
|
|
49
|
+
try {
|
|
50
|
+
model = getModel(name);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
model = undefined;
|
|
54
|
+
}
|
|
55
|
+
if (!model || visited.has(model))
|
|
56
|
+
continue;
|
|
57
|
+
visited.add(model);
|
|
58
|
+
const rows = await queryModelRows(model);
|
|
59
|
+
if (rows.length > 0)
|
|
60
|
+
collected.push(...rows);
|
|
61
|
+
}
|
|
62
|
+
return collected;
|
|
63
|
+
}
|
|
64
|
+
function mapCheckinRow(row) {
|
|
65
|
+
return {
|
|
66
|
+
userId: String(row.userId ?? row.user_id ?? ''),
|
|
67
|
+
groupId: String(row.groupId ?? row.group_id ?? row.context_id ?? ''),
|
|
68
|
+
totalDays: Number(row.totalDays ?? row.total_days ?? row.total_checkins ?? 0) || 0,
|
|
69
|
+
streakDays: Number(row.streakDays ?? row.streak_days ?? row.streak ?? 0) || 0,
|
|
70
|
+
points: Number(row.points ?? 0) || 0,
|
|
71
|
+
lastDate: String(row.lastDate ?? row.last_date ?? row.last_checkin ?? ''),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function mapKeywordRow(row) {
|
|
75
|
+
return {
|
|
76
|
+
groupId: String(row.groupId ?? row.group_id ?? row.context_id ?? ''),
|
|
77
|
+
keyword: String(row.keyword ?? row.question ?? ''),
|
|
78
|
+
reply: String(row.reply ?? row.answer ?? ''),
|
|
79
|
+
creatorId: String(row.creatorId ?? row.creator_id ?? ''),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function collectEphemeralKeywords(runtime) {
|
|
83
|
+
if (!runtime?.keywords)
|
|
84
|
+
return [];
|
|
85
|
+
const rows = [];
|
|
86
|
+
for (const [groupId, entries] of runtime.keywords.entries()) {
|
|
87
|
+
for (const [keyword, reply] of entries.entries()) {
|
|
88
|
+
rows.push({
|
|
89
|
+
groupId,
|
|
90
|
+
keyword,
|
|
91
|
+
reply,
|
|
92
|
+
creatorId: '',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return rows;
|
|
97
|
+
}
|
|
98
|
+
export function createGroupSuiteConsoleRpc(defaultRuntime) {
|
|
99
|
+
return definePluginConsoleRpc({
|
|
100
|
+
status: {
|
|
101
|
+
page: GROUP_SUITE_CONSOLE_PAGE,
|
|
102
|
+
requiredPermissions: [GROUP_SUITE_READ_PERMISSION],
|
|
103
|
+
async handler(_input, call) {
|
|
104
|
+
const runtime = resolveRuntime(call, defaultRuntime);
|
|
105
|
+
const [checkinRows, keywordDbRows, statsRows] = await Promise.all([
|
|
106
|
+
readTableRows(runtime, ['group_checkin', CHECKIN_TABLE]),
|
|
107
|
+
readTableRows(runtime, ['group_keywords', TEACH_TABLE]),
|
|
108
|
+
readTableRows(runtime, ['group_daily_stats', STATS_TABLE]),
|
|
109
|
+
]);
|
|
110
|
+
const mappedCheckins = checkinRows.map(mapCheckinRow);
|
|
111
|
+
const topCheckins = [...mappedCheckins]
|
|
112
|
+
.sort((a, b) => (b.points - a.points) || (b.totalDays - a.totalDays))
|
|
113
|
+
.slice(0, 20);
|
|
114
|
+
const mappedKeywords = [
|
|
115
|
+
...keywordDbRows.map(mapKeywordRow),
|
|
116
|
+
...collectEphemeralKeywords(runtime),
|
|
117
|
+
];
|
|
118
|
+
const keywords = mappedKeywords.slice(0, 50);
|
|
119
|
+
return {
|
|
120
|
+
checkinCount: mappedCheckins.length,
|
|
121
|
+
keywordCount: mappedKeywords.length,
|
|
122
|
+
dailyStatsCount: statsRows.length,
|
|
123
|
+
topCheckins: topCheckins,
|
|
124
|
+
keywords: keywords,
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.js","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -7,4 +7,6 @@ export { createGroupSuiteRuntime, groupSuiteRuntimeToken, resolveGroupSuiteRunti
|
|
|
7
7
|
export type { GroupSuiteRuntime, PendingStatsIncrement } from './runtime-state.js';
|
|
8
8
|
export { parseTeachPair, teachAdd, teachForget, teachList, tryTeachReply, } from './teach-lib.js';
|
|
9
9
|
export { flushStatsBuffer, monthStartStr, myStatsText, queryStats, recordMessage, statsRankText, weekStartStr, } from './stats-lib.js';
|
|
10
|
+
export { createGroupSuiteConsoleRpc, GROUP_SUITE_CONSOLE_PAGE, GROUP_SUITE_READ_PERMISSION, } from './console.js';
|
|
11
|
+
export type { GroupSuiteCheckinLeaderboardRow, GroupSuiteConsoleStatus, GroupSuiteKeywordRow, } from './console.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EACL,cAAc,EACd,QAAQ,EACR,WAAW,EACX,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EACL,cAAc,EACd,QAAQ,EACR,WAAW,EACX,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,+BAA+B,EAC/B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,cAAc,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export { getCheckinModel, getStatsModel, getTeachModel, } from './db-store.js';
|
|
|
5
5
|
export { createGroupSuiteRuntime, groupSuiteRuntimeToken, resolveGroupSuiteRuntime, } from './runtime-state.js';
|
|
6
6
|
export { parseTeachPair, teachAdd, teachForget, teachList, tryTeachReply, } from './teach-lib.js';
|
|
7
7
|
export { flushStatsBuffer, monthStartStr, myStatsText, queryStats, recordMessage, statsRankText, weekStartStr, } from './stats-lib.js';
|
|
8
|
+
export { createGroupSuiteConsoleRpc, GROUP_SUITE_CONSOLE_PAGE, GROUP_SUITE_READ_PERMISSION, } from './console.js';
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,QAAQ,EACR,WAAW,EACX,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,QAAQ,EACR,WAAW,EACX,SAAS,EACT,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-group-suite",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.22",
|
|
4
4
|
"description": "Zhin.js 群运营套件:签到、教学、统计、持久词库与群欢迎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"middlewares",
|
|
20
20
|
"handlers",
|
|
21
21
|
"schedules",
|
|
22
|
+
"pages",
|
|
22
23
|
"src",
|
|
23
24
|
"lib",
|
|
24
25
|
"skills",
|
|
@@ -41,25 +42,29 @@
|
|
|
41
42
|
},
|
|
42
43
|
"license": "MIT",
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@zhin.js/
|
|
45
|
-
"@zhin.js/
|
|
46
|
-
"@zhin.js/
|
|
47
|
-
"@zhin.js/
|
|
48
|
-
"@zhin.js/
|
|
45
|
+
"@zhin.js/client": "1.1.25",
|
|
46
|
+
"@zhin.js/components": "1.1.11",
|
|
47
|
+
"@zhin.js/console-contract": "1.1.3",
|
|
48
|
+
"@zhin.js/core": "1.1.57",
|
|
49
|
+
"@zhin.js/jsx": "1.1.11",
|
|
50
|
+
"@zhin.js/page": "1.1.15",
|
|
51
|
+
"@zhin.js/skill": "1.1.14",
|
|
52
|
+
"@zhin.js/tool": "1.1.16"
|
|
49
53
|
},
|
|
50
54
|
"devDependencies": {
|
|
51
55
|
"typescript": "^6.0.3",
|
|
52
56
|
"vitest": "^4.1.10",
|
|
53
57
|
"zod": "^4.4.3",
|
|
54
|
-
"zhin.js": "1.1.
|
|
58
|
+
"zhin.js": "1.1.22"
|
|
55
59
|
},
|
|
56
60
|
"peerDependencies": {
|
|
57
61
|
"zod": "^4.0.0",
|
|
58
|
-
"@zhin.js/command": "^1.1.
|
|
59
|
-
"@zhin.js/middleware": "^1.1.
|
|
60
|
-
"zhin.js": "^1.1.
|
|
61
|
-
"
|
|
62
|
-
"@zhin.js/
|
|
62
|
+
"@zhin.js/command": "^1.1.15",
|
|
63
|
+
"@zhin.js/middleware": "^1.1.14",
|
|
64
|
+
"@zhin.js/page": "^1.1.15",
|
|
65
|
+
"zhin.js": "^1.1.22",
|
|
66
|
+
"@zhin.js/handler": "^1.1.13",
|
|
67
|
+
"@zhin.js/schedule-feature": "^1.1.13"
|
|
63
68
|
},
|
|
64
69
|
"peerDependenciesMeta": {
|
|
65
70
|
"@zhin.js/command": {
|
|
@@ -68,6 +73,9 @@
|
|
|
68
73
|
"@zhin.js/middleware": {
|
|
69
74
|
"optional": true
|
|
70
75
|
},
|
|
76
|
+
"@zhin.js/page": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
71
79
|
"zod": {
|
|
72
80
|
"optional": true
|
|
73
81
|
},
|
|
@@ -120,6 +128,10 @@
|
|
|
120
128
|
{
|
|
121
129
|
"package": "@zhin.js/schedule-feature",
|
|
122
130
|
"api": "^1.0.0"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"package": "@zhin.js/page",
|
|
134
|
+
"api": "^1.0.0"
|
|
123
135
|
}
|
|
124
136
|
],
|
|
125
137
|
"plugins": []
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
3
|
+
import { createDeclarativePage } from '@zhin.js/client';
|
|
4
|
+
export const meta = definePage({
|
|
5
|
+
title: '群管套件',
|
|
6
|
+
titleI18n: { en: 'Group Suite' },
|
|
7
|
+
icon: 'Users',
|
|
8
|
+
order: 54,
|
|
9
|
+
requiredPermissions: ['group-suite:read'],
|
|
10
|
+
});
|
|
11
|
+
export default createDeclarativePage({
|
|
12
|
+
title: { zh: '群管套件', en: 'Group Suite' },
|
|
13
|
+
description: {
|
|
14
|
+
zh: '群签到积分排行榜与关键词问答规则概览',
|
|
15
|
+
en: 'Group check-in leaderboard and keyword Q&A rules overview',
|
|
16
|
+
},
|
|
17
|
+
stats: [
|
|
18
|
+
{
|
|
19
|
+
key: 'checkinCount',
|
|
20
|
+
label: { zh: '签到记录数', en: 'Check-in Records' },
|
|
21
|
+
value: 'checkinCount',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
key: 'keywordCount',
|
|
25
|
+
label: { zh: '问答词条数', en: 'Keyword Rules' },
|
|
26
|
+
value: 'keywordCount',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
sections: [
|
|
30
|
+
{
|
|
31
|
+
id: 'topCheckins',
|
|
32
|
+
title: { zh: '签到排行榜', en: 'Check-in Leaderboard' },
|
|
33
|
+
emptyText: { zh: '暂无签到记录', en: 'No check-in records yet' },
|
|
34
|
+
items: 'topCheckins',
|
|
35
|
+
columns: [
|
|
36
|
+
{ key: 'userId', label: { zh: '用户 ID', en: 'User ID' } },
|
|
37
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
38
|
+
{ key: 'points', label: { zh: '积分', en: 'Points' } },
|
|
39
|
+
{ key: 'totalDays', label: { zh: '累计天数', en: 'Total Days' } },
|
|
40
|
+
{ key: 'streakDays', label: { zh: '连续天数', en: 'Streak Days' } },
|
|
41
|
+
{ key: 'lastDate', label: { zh: '最近签到', en: 'Last Date' } },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'keywords',
|
|
46
|
+
title: { zh: '关键词问答规则', en: 'Keyword Q&A Rules' },
|
|
47
|
+
emptyText: { zh: '暂无关键词问答规则', en: 'No keyword rules configured' },
|
|
48
|
+
items: 'keywords',
|
|
49
|
+
columns: [
|
|
50
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
51
|
+
{ key: 'keyword', label: { zh: '关键词', en: 'Keyword' } },
|
|
52
|
+
{ key: 'reply', label: { zh: '回复内容', en: 'Reply' } },
|
|
53
|
+
{ key: 'creatorId', label: { zh: '创建者', en: 'Creator ID' } },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
2
|
+
import { createDeclarativePage } from '@zhin.js/client';
|
|
3
|
+
import type {
|
|
4
|
+
GroupSuiteCheckinLeaderboardRow,
|
|
5
|
+
GroupSuiteConsoleStatus,
|
|
6
|
+
GroupSuiteKeywordRow,
|
|
7
|
+
} from '../../src/console.js';
|
|
8
|
+
|
|
9
|
+
export const meta = definePage({
|
|
10
|
+
title: '群管套件',
|
|
11
|
+
titleI18n: { en: 'Group Suite' },
|
|
12
|
+
icon: 'Users',
|
|
13
|
+
order: 54,
|
|
14
|
+
requiredPermissions: ['group-suite:read'],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export default createDeclarativePage<GroupSuiteConsoleStatus>({
|
|
18
|
+
title: { zh: '群管套件', en: 'Group Suite' },
|
|
19
|
+
description: {
|
|
20
|
+
zh: '群签到积分排行榜与关键词问答规则概览',
|
|
21
|
+
en: 'Group check-in leaderboard and keyword Q&A rules overview',
|
|
22
|
+
},
|
|
23
|
+
stats: [
|
|
24
|
+
{
|
|
25
|
+
key: 'checkinCount',
|
|
26
|
+
label: { zh: '签到记录数', en: 'Check-in Records' },
|
|
27
|
+
value: 'checkinCount',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: 'keywordCount',
|
|
31
|
+
label: { zh: '问答词条数', en: 'Keyword Rules' },
|
|
32
|
+
value: 'keywordCount',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
sections: [
|
|
36
|
+
{
|
|
37
|
+
id: 'topCheckins',
|
|
38
|
+
title: { zh: '签到排行榜', en: 'Check-in Leaderboard' },
|
|
39
|
+
emptyText: { zh: '暂无签到记录', en: 'No check-in records yet' },
|
|
40
|
+
items: 'topCheckins',
|
|
41
|
+
columns: [
|
|
42
|
+
{ key: 'userId', label: { zh: '用户 ID', en: 'User ID' } },
|
|
43
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
44
|
+
{ key: 'points', label: { zh: '积分', en: 'Points' } },
|
|
45
|
+
{ key: 'totalDays', label: { zh: '累计天数', en: 'Total Days' } },
|
|
46
|
+
{ key: 'streakDays', label: { zh: '连续天数', en: 'Streak Days' } },
|
|
47
|
+
{ key: 'lastDate', label: { zh: '最近签到', en: 'Last Date' } },
|
|
48
|
+
] satisfies readonly { key: keyof GroupSuiteCheckinLeaderboardRow & string; label: { zh: string; en: string } }[],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'keywords',
|
|
52
|
+
title: { zh: '关键词问答规则', en: 'Keyword Q&A Rules' },
|
|
53
|
+
emptyText: { zh: '暂无关键词问答规则', en: 'No keyword rules configured' },
|
|
54
|
+
items: 'keywords',
|
|
55
|
+
columns: [
|
|
56
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
57
|
+
{ key: 'keyword', label: { zh: '关键词', en: 'Keyword' } },
|
|
58
|
+
{ key: 'reply', label: { zh: '回复内容', en: 'Reply' } },
|
|
59
|
+
{ key: 'creatorId', label: { zh: '创建者', en: 'Creator ID' } },
|
|
60
|
+
] satisfies readonly { key: keyof GroupSuiteKeywordRow & string; label: { zh: string; en: string } }[],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
});
|
package/plugin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { definePlugin, databaseHostToken } from 'zhin.js';
|
|
2
|
+
import { definePlugin, databaseHostToken, pluginConsoleRpcToken } from 'zhin.js';
|
|
3
3
|
import { resolveGroupSuiteConfig } from "./lib/config.js";
|
|
4
|
+
import { createGroupSuiteConsoleRpc } from "./lib/console.js";
|
|
4
5
|
import { createInMemoryGroupSuiteDb } from "./lib/memory-store.js";
|
|
5
6
|
import { createGroupSuiteRuntime, groupSuiteRuntimeToken } from "./lib/runtime-state.js";
|
|
6
7
|
import { defineGroupSuiteTables } from "./lib/tables.js";
|
|
@@ -34,5 +35,6 @@ export default definePlugin({
|
|
|
34
35
|
}
|
|
35
36
|
const runtime = createGroupSuiteRuntime(db);
|
|
36
37
|
context.resources.provide(groupSuiteRuntimeToken, runtime);
|
|
38
|
+
context.resources.provide(pluginConsoleRpcToken, createGroupSuiteConsoleRpc(runtime));
|
|
37
39
|
},
|
|
38
40
|
});
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePluginConsoleRpc,
|
|
3
|
+
type PluginConsoleRpc,
|
|
4
|
+
type PluginConsoleRpcCall,
|
|
5
|
+
type PluginConsoleRpcJson,
|
|
6
|
+
type Token,
|
|
7
|
+
} from 'zhin.js';
|
|
8
|
+
import {
|
|
9
|
+
CHECKIN_TABLE,
|
|
10
|
+
STATS_TABLE,
|
|
11
|
+
TEACH_TABLE,
|
|
12
|
+
type GroupSuiteModel,
|
|
13
|
+
type GroupSuiteRow,
|
|
14
|
+
} from './memory-store.js';
|
|
15
|
+
import { groupSuiteRuntimeToken, type GroupSuiteRuntime } from './runtime-state.js';
|
|
16
|
+
|
|
17
|
+
export const GROUP_SUITE_CONSOLE_PAGE = 'index';
|
|
18
|
+
export const GROUP_SUITE_READ_PERMISSION = 'group-suite:read';
|
|
19
|
+
|
|
20
|
+
export interface GroupSuiteCheckinLeaderboardRow {
|
|
21
|
+
readonly userId: string;
|
|
22
|
+
readonly groupId: string;
|
|
23
|
+
readonly totalDays: number;
|
|
24
|
+
readonly streakDays: number;
|
|
25
|
+
readonly points: number;
|
|
26
|
+
readonly lastDate: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface GroupSuiteKeywordRow {
|
|
30
|
+
readonly groupId: string;
|
|
31
|
+
readonly keyword: string;
|
|
32
|
+
readonly reply: string;
|
|
33
|
+
readonly creatorId: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface GroupSuiteConsoleStatus {
|
|
37
|
+
readonly checkinCount: number;
|
|
38
|
+
readonly keywordCount: number;
|
|
39
|
+
readonly dailyStatsCount: number;
|
|
40
|
+
readonly topCheckins: readonly GroupSuiteCheckinLeaderboardRow[];
|
|
41
|
+
readonly keywords: readonly GroupSuiteKeywordRow[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type RuntimeWithUse = {
|
|
45
|
+
use?: <T>(token: Token<T>) => T;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function resolveRuntime(
|
|
49
|
+
call: PluginConsoleRpcCall,
|
|
50
|
+
fallbackRuntime?: GroupSuiteRuntime,
|
|
51
|
+
): GroupSuiteRuntime | undefined {
|
|
52
|
+
const runtimeWithUse = call.runtime as RuntimeWithUse | undefined;
|
|
53
|
+
if (typeof runtimeWithUse?.use === 'function') {
|
|
54
|
+
try {
|
|
55
|
+
const resolved = runtimeWithUse.use(groupSuiteRuntimeToken);
|
|
56
|
+
if (resolved) return resolved;
|
|
57
|
+
} catch {
|
|
58
|
+
// Fall back to the plugin setup runtime when call.runtime.use is unavailable.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return fallbackRuntime;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function queryModelRows(model: GroupSuiteModel | undefined): Promise<GroupSuiteRow[]> {
|
|
65
|
+
if (!model || typeof model.select !== 'function') return [];
|
|
66
|
+
try {
|
|
67
|
+
const selection = model.select();
|
|
68
|
+
if (Array.isArray(selection)) return selection as GroupSuiteRow[];
|
|
69
|
+
if (selection && typeof (selection as PromiseLike<GroupSuiteRow[]>).then === 'function') {
|
|
70
|
+
const rows = await selection;
|
|
71
|
+
return Array.isArray(rows) ? rows : [];
|
|
72
|
+
}
|
|
73
|
+
if (selection && typeof selection.where === 'function') {
|
|
74
|
+
const rows = await selection.where({});
|
|
75
|
+
return Array.isArray(rows) ? rows : [];
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// Handle any database query failure gracefully.
|
|
79
|
+
}
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function readTableRows(
|
|
84
|
+
runtime: GroupSuiteRuntime | undefined,
|
|
85
|
+
tableNames: readonly string[],
|
|
86
|
+
): Promise<GroupSuiteRow[]> {
|
|
87
|
+
const getModel = runtime?.db?.models?.get?.bind(runtime.db.models);
|
|
88
|
+
if (typeof getModel !== 'function') return [];
|
|
89
|
+
const collected: GroupSuiteRow[] = [];
|
|
90
|
+
const visited = new Set<GroupSuiteModel>();
|
|
91
|
+
for (const name of tableNames) {
|
|
92
|
+
let model: GroupSuiteModel | undefined;
|
|
93
|
+
try {
|
|
94
|
+
model = getModel(name);
|
|
95
|
+
} catch {
|
|
96
|
+
model = undefined;
|
|
97
|
+
}
|
|
98
|
+
if (!model || visited.has(model)) continue;
|
|
99
|
+
visited.add(model);
|
|
100
|
+
const rows = await queryModelRows(model);
|
|
101
|
+
if (rows.length > 0) collected.push(...rows);
|
|
102
|
+
}
|
|
103
|
+
return collected;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function mapCheckinRow(row: GroupSuiteRow): GroupSuiteCheckinLeaderboardRow {
|
|
107
|
+
return {
|
|
108
|
+
userId: String(row.userId ?? row.user_id ?? ''),
|
|
109
|
+
groupId: String(row.groupId ?? row.group_id ?? row.context_id ?? ''),
|
|
110
|
+
totalDays: Number(row.totalDays ?? row.total_days ?? row.total_checkins ?? 0) || 0,
|
|
111
|
+
streakDays: Number(row.streakDays ?? row.streak_days ?? row.streak ?? 0) || 0,
|
|
112
|
+
points: Number(row.points ?? 0) || 0,
|
|
113
|
+
lastDate: String(row.lastDate ?? row.last_date ?? row.last_checkin ?? ''),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function mapKeywordRow(row: GroupSuiteRow): GroupSuiteKeywordRow {
|
|
118
|
+
return {
|
|
119
|
+
groupId: String(row.groupId ?? row.group_id ?? row.context_id ?? ''),
|
|
120
|
+
keyword: String(row.keyword ?? row.question ?? ''),
|
|
121
|
+
reply: String(row.reply ?? row.answer ?? ''),
|
|
122
|
+
creatorId: String(row.creatorId ?? row.creator_id ?? ''),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function collectEphemeralKeywords(runtime: GroupSuiteRuntime | undefined): GroupSuiteKeywordRow[] {
|
|
127
|
+
if (!runtime?.keywords) return [];
|
|
128
|
+
const rows: GroupSuiteKeywordRow[] = [];
|
|
129
|
+
for (const [groupId, entries] of runtime.keywords.entries()) {
|
|
130
|
+
for (const [keyword, reply] of entries.entries()) {
|
|
131
|
+
rows.push({
|
|
132
|
+
groupId,
|
|
133
|
+
keyword,
|
|
134
|
+
reply,
|
|
135
|
+
creatorId: '',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return rows;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function createGroupSuiteConsoleRpc(
|
|
143
|
+
defaultRuntime?: GroupSuiteRuntime,
|
|
144
|
+
): PluginConsoleRpc {
|
|
145
|
+
return definePluginConsoleRpc({
|
|
146
|
+
status: {
|
|
147
|
+
page: GROUP_SUITE_CONSOLE_PAGE,
|
|
148
|
+
requiredPermissions: [GROUP_SUITE_READ_PERMISSION],
|
|
149
|
+
async handler(_input, call): Promise<PluginConsoleRpcJson> {
|
|
150
|
+
const runtime = resolveRuntime(call, defaultRuntime);
|
|
151
|
+
const [checkinRows, keywordDbRows, statsRows] = await Promise.all([
|
|
152
|
+
readTableRows(runtime, ['group_checkin', CHECKIN_TABLE]),
|
|
153
|
+
readTableRows(runtime, ['group_keywords', TEACH_TABLE]),
|
|
154
|
+
readTableRows(runtime, ['group_daily_stats', STATS_TABLE]),
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
const mappedCheckins = checkinRows.map(mapCheckinRow);
|
|
158
|
+
const topCheckins = [...mappedCheckins]
|
|
159
|
+
.sort((a, b) => (b.points - a.points) || (b.totalDays - a.totalDays))
|
|
160
|
+
.slice(0, 20);
|
|
161
|
+
|
|
162
|
+
const mappedKeywords = [
|
|
163
|
+
...keywordDbRows.map(mapKeywordRow),
|
|
164
|
+
...collectEphemeralKeywords(runtime),
|
|
165
|
+
];
|
|
166
|
+
const keywords = mappedKeywords.slice(0, 50);
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
checkinCount: mappedCheckins.length,
|
|
170
|
+
keywordCount: mappedKeywords.length,
|
|
171
|
+
dailyStatsCount: statsRows.length,
|
|
172
|
+
topCheckins: topCheckins as unknown as PluginConsoleRpcJson,
|
|
173
|
+
keywords: keywords as unknown as PluginConsoleRpcJson,
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -38,3 +38,13 @@ export {
|
|
|
38
38
|
statsRankText,
|
|
39
39
|
weekStartStr,
|
|
40
40
|
} from './stats-lib.js';
|
|
41
|
+
export {
|
|
42
|
+
createGroupSuiteConsoleRpc,
|
|
43
|
+
GROUP_SUITE_CONSOLE_PAGE,
|
|
44
|
+
GROUP_SUITE_READ_PERMISSION,
|
|
45
|
+
} from './console.js';
|
|
46
|
+
export type {
|
|
47
|
+
GroupSuiteCheckinLeaderboardRow,
|
|
48
|
+
GroupSuiteConsoleStatus,
|
|
49
|
+
GroupSuiteKeywordRow,
|
|
50
|
+
} from './console.js';
|