@zhin.js/plugin-group-suite 1.1.21 → 1.1.23
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/lib/runtime-state.d.ts +2 -1
- package/lib/runtime-state.d.ts.map +1 -1
- package/lib/runtime-state.js +4 -3
- package/lib/runtime-state.js.map +1 -1
- package/package.json +23 -12
- package/pages/index/index.js +56 -0
- package/pages/index/index.ts +62 -0
- package/plugin.js +36 -4
- package/src/console.ts +178 -0
- package/src/index.ts +10 -0
- package/src/runtime-state.ts +4 -3
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/lib/runtime-state.d.ts
CHANGED
|
@@ -19,7 +19,8 @@ export interface GroupSuiteRuntime {
|
|
|
19
19
|
statsFlush?: Promise<void>;
|
|
20
20
|
readonly checkinChains: Map<string, Promise<unknown>>;
|
|
21
21
|
}
|
|
22
|
-
export declare const
|
|
22
|
+
export declare const groupSuiteRuntimeStore: import("zhin.js").PluginStore<GroupSuiteRuntime, unknown>;
|
|
23
|
+
export declare const groupSuiteRuntimeToken: import("zhin.js").PluginStore<GroupSuiteRuntime, unknown>;
|
|
23
24
|
export declare function createGroupSuiteRuntime(db: GroupSuiteMemoryDb): GroupSuiteRuntime;
|
|
24
25
|
export declare function resolveGroupSuiteRuntime(context: {
|
|
25
26
|
readonly owner?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-state.d.ts","sourceRoot":"","sources":["../src/runtime-state.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"runtime-state.d.ts","sourceRoot":"","sources":["../src/runtime-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,kBAAkB,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACzD,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;CACvD;AAED,eAAO,MAAM,sBAAsB,2DAGlC,CAAC;AACF,eAAO,MAAM,sBAAsB,2DAAyB,CAAC;AAE7D,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,kBAAkB,GAAG,iBAAiB,CAQjF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;CAC5B,GAAG,iBAAiB,CAEpB"}
|
package/lib/runtime-state.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const
|
|
1
|
+
import { definePluginStore } from 'zhin.js';
|
|
2
|
+
export const groupSuiteRuntimeStore = definePluginStore('zhin.group-suite.runtime', 'Owner-scoped Group Suite database and ephemeral state');
|
|
3
|
+
export const groupSuiteRuntimeToken = groupSuiteRuntimeStore;
|
|
3
4
|
export function createGroupSuiteRuntime(db) {
|
|
4
5
|
return {
|
|
5
6
|
db,
|
|
@@ -10,6 +11,6 @@ export function createGroupSuiteRuntime(db) {
|
|
|
10
11
|
};
|
|
11
12
|
}
|
|
12
13
|
export function resolveGroupSuiteRuntime(context) {
|
|
13
|
-
return
|
|
14
|
+
return groupSuiteRuntimeStore.use(context);
|
|
14
15
|
}
|
|
15
16
|
//# sourceMappingURL=runtime-state.js.map
|
package/lib/runtime-state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-state.js","sourceRoot":"","sources":["../src/runtime-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"runtime-state.js","sourceRoot":"","sources":["../src/runtime-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAc,MAAM,SAAS,CAAC;AAwBxD,MAAM,CAAC,MAAM,sBAAsB,GAAG,iBAAiB,CACrD,0BAA0B,EAC1B,uDAAuD,CACxD,CAAC;AACF,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAE7D,MAAM,UAAU,uBAAuB,CAAC,EAAsB;IAC5D,OAAO;QACL,EAAE;QACF,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,cAAc,EAAE,IAAI,GAAG,EAAE;QACzB,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,aAAa,EAAE,IAAI,GAAG,EAAE;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,OAGxC;IACC,OAAO,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC7C,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.23",
|
|
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,28 @@
|
|
|
41
42
|
},
|
|
42
43
|
"license": "MIT",
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@zhin.js/components": "1.1.
|
|
45
|
-
"@zhin.js/
|
|
46
|
-
"@zhin.js/
|
|
47
|
-
"@zhin.js/
|
|
48
|
-
"@zhin.js/
|
|
45
|
+
"@zhin.js/components": "1.1.12",
|
|
46
|
+
"@zhin.js/console-contract": "1.1.4",
|
|
47
|
+
"@zhin.js/core": "1.1.58",
|
|
48
|
+
"@zhin.js/jsx": "1.1.12",
|
|
49
|
+
"@zhin.js/page": "1.1.16",
|
|
50
|
+
"@zhin.js/skill": "1.1.15",
|
|
51
|
+
"@zhin.js/tool": "1.1.17"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
51
54
|
"typescript": "^6.0.3",
|
|
52
55
|
"vitest": "^4.1.10",
|
|
53
56
|
"zod": "^4.4.3",
|
|
54
|
-
"zhin.js": "1.1.
|
|
57
|
+
"zhin.js": "1.1.23"
|
|
55
58
|
},
|
|
56
59
|
"peerDependencies": {
|
|
57
60
|
"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/
|
|
61
|
+
"@zhin.js/command": "^1.1.16",
|
|
62
|
+
"@zhin.js/middleware": "^1.1.15",
|
|
63
|
+
"@zhin.js/page": "^1.1.16",
|
|
64
|
+
"zhin.js": "^1.1.23",
|
|
65
|
+
"@zhin.js/handler": "^1.1.14",
|
|
66
|
+
"@zhin.js/schedule-feature": "^1.1.14"
|
|
63
67
|
},
|
|
64
68
|
"peerDependenciesMeta": {
|
|
65
69
|
"@zhin.js/command": {
|
|
@@ -68,6 +72,9 @@
|
|
|
68
72
|
"@zhin.js/middleware": {
|
|
69
73
|
"optional": true
|
|
70
74
|
},
|
|
75
|
+
"@zhin.js/page": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
71
78
|
"zod": {
|
|
72
79
|
"optional": true
|
|
73
80
|
},
|
|
@@ -120,6 +127,10 @@
|
|
|
120
127
|
{
|
|
121
128
|
"package": "@zhin.js/schedule-feature",
|
|
122
129
|
"api": "^1.0.0"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"package": "@zhin.js/page",
|
|
133
|
+
"api": "^1.0.0"
|
|
123
134
|
}
|
|
124
135
|
],
|
|
125
136
|
"plugins": []
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { definePage, createDeclarativePage } from '@zhin.js/console-contract';
|
|
3
|
+
export const meta = definePage({
|
|
4
|
+
title: '群管套件',
|
|
5
|
+
titleI18n: { en: 'Group Suite' },
|
|
6
|
+
icon: 'Users',
|
|
7
|
+
order: 54,
|
|
8
|
+
requiredPermissions: ['group-suite:read'],
|
|
9
|
+
});
|
|
10
|
+
export default createDeclarativePage({
|
|
11
|
+
title: { zh: '群管套件', en: 'Group Suite' },
|
|
12
|
+
description: {
|
|
13
|
+
zh: '群签到积分排行榜与关键词问答规则概览',
|
|
14
|
+
en: 'Group check-in leaderboard and keyword Q&A rules overview',
|
|
15
|
+
},
|
|
16
|
+
stats: [
|
|
17
|
+
{
|
|
18
|
+
key: 'checkinCount',
|
|
19
|
+
label: { zh: '签到记录数', en: 'Check-in Records' },
|
|
20
|
+
value: 'checkinCount',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'keywordCount',
|
|
24
|
+
label: { zh: '问答词条数', en: 'Keyword Rules' },
|
|
25
|
+
value: 'keywordCount',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
sections: [
|
|
29
|
+
{
|
|
30
|
+
id: 'topCheckins',
|
|
31
|
+
title: { zh: '签到排行榜', en: 'Check-in Leaderboard' },
|
|
32
|
+
emptyText: { zh: '暂无签到记录', en: 'No check-in records yet' },
|
|
33
|
+
items: 'topCheckins',
|
|
34
|
+
columns: [
|
|
35
|
+
{ key: 'userId', label: { zh: '用户 ID', en: 'User ID' } },
|
|
36
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
37
|
+
{ key: 'points', label: { zh: '积分', en: 'Points' } },
|
|
38
|
+
{ key: 'totalDays', label: { zh: '累计天数', en: 'Total Days' } },
|
|
39
|
+
{ key: 'streakDays', label: { zh: '连续天数', en: 'Streak Days' } },
|
|
40
|
+
{ key: 'lastDate', label: { zh: '最近签到', en: 'Last Date' } },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'keywords',
|
|
45
|
+
title: { zh: '关键词问答规则', en: 'Keyword Q&A Rules' },
|
|
46
|
+
emptyText: { zh: '暂无关键词问答规则', en: 'No keyword rules configured' },
|
|
47
|
+
items: 'keywords',
|
|
48
|
+
columns: [
|
|
49
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
50
|
+
{ key: 'keyword', label: { zh: '关键词', en: 'Keyword' } },
|
|
51
|
+
{ key: 'reply', label: { zh: '回复内容', en: 'Reply' } },
|
|
52
|
+
{ key: 'creatorId', label: { zh: '创建者', en: 'Creator ID' } },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { definePage, createDeclarativePage } from '@zhin.js/console-contract';
|
|
2
|
+
import type {
|
|
3
|
+
GroupSuiteCheckinLeaderboardRow,
|
|
4
|
+
GroupSuiteConsoleStatus,
|
|
5
|
+
GroupSuiteKeywordRow,
|
|
6
|
+
} from '../../src/console.js';
|
|
7
|
+
|
|
8
|
+
export const meta = definePage({
|
|
9
|
+
title: '群管套件',
|
|
10
|
+
titleI18n: { en: 'Group Suite' },
|
|
11
|
+
icon: 'Users',
|
|
12
|
+
order: 54,
|
|
13
|
+
requiredPermissions: ['group-suite:read'],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export default createDeclarativePage<GroupSuiteConsoleStatus>({
|
|
17
|
+
title: { zh: '群管套件', en: 'Group Suite' },
|
|
18
|
+
description: {
|
|
19
|
+
zh: '群签到积分排行榜与关键词问答规则概览',
|
|
20
|
+
en: 'Group check-in leaderboard and keyword Q&A rules overview',
|
|
21
|
+
},
|
|
22
|
+
stats: [
|
|
23
|
+
{
|
|
24
|
+
key: 'checkinCount',
|
|
25
|
+
label: { zh: '签到记录数', en: 'Check-in Records' },
|
|
26
|
+
value: 'checkinCount',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: 'keywordCount',
|
|
30
|
+
label: { zh: '问答词条数', en: 'Keyword Rules' },
|
|
31
|
+
value: 'keywordCount',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
sections: [
|
|
35
|
+
{
|
|
36
|
+
id: 'topCheckins',
|
|
37
|
+
title: { zh: '签到排行榜', en: 'Check-in Leaderboard' },
|
|
38
|
+
emptyText: { zh: '暂无签到记录', en: 'No check-in records yet' },
|
|
39
|
+
items: 'topCheckins',
|
|
40
|
+
columns: [
|
|
41
|
+
{ key: 'userId', label: { zh: '用户 ID', en: 'User ID' } },
|
|
42
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
43
|
+
{ key: 'points', label: { zh: '积分', en: 'Points' } },
|
|
44
|
+
{ key: 'totalDays', label: { zh: '累计天数', en: 'Total Days' } },
|
|
45
|
+
{ key: 'streakDays', label: { zh: '连续天数', en: 'Streak Days' } },
|
|
46
|
+
{ key: 'lastDate', label: { zh: '最近签到', en: 'Last Date' } },
|
|
47
|
+
] satisfies readonly { key: keyof GroupSuiteCheckinLeaderboardRow & string; label: { zh: string; en: string } }[],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'keywords',
|
|
51
|
+
title: { zh: '关键词问答规则', en: 'Keyword Q&A Rules' },
|
|
52
|
+
emptyText: { zh: '暂无关键词问答规则', en: 'No keyword rules configured' },
|
|
53
|
+
items: 'keywords',
|
|
54
|
+
columns: [
|
|
55
|
+
{ key: 'groupId', label: { zh: '群号 / 范围', en: 'Group ID' } },
|
|
56
|
+
{ key: 'keyword', label: { zh: '关键词', en: 'Keyword' } },
|
|
57
|
+
{ key: 'reply', label: { zh: '回复内容', en: 'Reply' } },
|
|
58
|
+
{ key: 'creatorId', label: { zh: '创建者', en: 'Creator ID' } },
|
|
59
|
+
] satisfies readonly { key: keyof GroupSuiteKeywordRow & string; label: { zh: string; en: string } }[],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
package/plugin.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import { definePlugin, databaseHostToken } from 'zhin.js';
|
|
2
|
+
import { definePlugin, definePluginCapabilityGraph, 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
|
-
import { createGroupSuiteRuntime,
|
|
6
|
+
import { createGroupSuiteRuntime, groupSuiteRuntimeStore } from "./lib/runtime-state.js";
|
|
6
7
|
import { defineGroupSuiteTables } from "./lib/tables.js";
|
|
7
8
|
/**
|
|
8
9
|
* Plugin Runtime:
|
|
@@ -15,6 +16,35 @@ export default definePlugin({
|
|
|
15
16
|
metadata: {
|
|
16
17
|
displayName: 'Group Suite',
|
|
17
18
|
},
|
|
19
|
+
capabilityGraph: definePluginCapabilityGraph({
|
|
20
|
+
provides: [groupSuiteRuntimeStore, pluginConsoleRpcToken],
|
|
21
|
+
injects: [databaseHostToken],
|
|
22
|
+
features: {
|
|
23
|
+
commands: [
|
|
24
|
+
'checkin',
|
|
25
|
+
'forget',
|
|
26
|
+
'keyword-add',
|
|
27
|
+
'keyword-list',
|
|
28
|
+
'keyword-remove',
|
|
29
|
+
'mypoints',
|
|
30
|
+
'mystats',
|
|
31
|
+
'rank',
|
|
32
|
+
'stats',
|
|
33
|
+
'stats-card',
|
|
34
|
+
'stats-rank',
|
|
35
|
+
'stats-week',
|
|
36
|
+
'teach',
|
|
37
|
+
'teach-list',
|
|
38
|
+
'teach-regex',
|
|
39
|
+
],
|
|
40
|
+
handlers: ['welcome'],
|
|
41
|
+
middlewares: ['keyword-reply', 'stats-count', 'teach-reply'],
|
|
42
|
+
schedules: ['flush-stats'],
|
|
43
|
+
skills: ['checkin', 'group-daily-analysis'],
|
|
44
|
+
tools: ['checkin_query', 'checkin_rank', 'group_announce', 'stats_query', 'stats_user'],
|
|
45
|
+
pages: ['index'],
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
18
48
|
setup(context) {
|
|
19
49
|
// host 与 memory 模型表面结构兼容(where 均返回 PromiseLike),可直接互换。
|
|
20
50
|
const db = (() => {
|
|
@@ -32,7 +62,9 @@ export default definePlugin({
|
|
|
32
62
|
throw new Error('Persistent keywords require a Database Host; demos may explicitly set ephemeralKeywords: true');
|
|
33
63
|
void context.resources.use(databaseHostToken).state;
|
|
34
64
|
}
|
|
35
|
-
const runtime = createGroupSuiteRuntime(db);
|
|
36
|
-
|
|
65
|
+
const runtime = groupSuiteRuntimeStore.provide(context, createGroupSuiteRuntime(db));
|
|
66
|
+
return {
|
|
67
|
+
consoleRpc: createGroupSuiteConsoleRpc(runtime),
|
|
68
|
+
};
|
|
37
69
|
},
|
|
38
70
|
});
|
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';
|
package/src/runtime-state.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { definePluginStore, type Token } from 'zhin.js';
|
|
2
2
|
import type { GroupSuiteMemoryDb } from './memory-store.js';
|
|
3
3
|
|
|
4
4
|
export interface PendingStatsIncrement {
|
|
@@ -22,10 +22,11 @@ export interface GroupSuiteRuntime {
|
|
|
22
22
|
readonly checkinChains: Map<string, Promise<unknown>>;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export const
|
|
25
|
+
export const groupSuiteRuntimeStore = definePluginStore<GroupSuiteRuntime>(
|
|
26
26
|
'zhin.group-suite.runtime',
|
|
27
27
|
'Owner-scoped Group Suite database and ephemeral state',
|
|
28
28
|
);
|
|
29
|
+
export const groupSuiteRuntimeToken = groupSuiteRuntimeStore;
|
|
29
30
|
|
|
30
31
|
export function createGroupSuiteRuntime(db: GroupSuiteMemoryDb): GroupSuiteRuntime {
|
|
31
32
|
return {
|
|
@@ -41,5 +42,5 @@ export function resolveGroupSuiteRuntime(context: {
|
|
|
41
42
|
readonly owner?: unknown;
|
|
42
43
|
use<T>(token: Token<T>): T;
|
|
43
44
|
}): GroupSuiteRuntime {
|
|
44
|
-
return
|
|
45
|
+
return groupSuiteRuntimeStore.use(context);
|
|
45
46
|
}
|