@zhin.js/plugin-group-suite 1.1.10 → 1.1.13
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 +51 -16
- package/commands/checkin/index.js +1 -0
- package/commands/checkin/index.ts +1 -0
- package/commands/mypoints/index.js +1 -0
- package/commands/mypoints/index.ts +1 -0
- package/commands/rank/index.js +1 -0
- package/commands/rank/index.ts +1 -0
- package/handlers/welcome/index.js +6 -2
- package/handlers/welcome/index.ts +5 -2
- package/lib/config.d.ts +3 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +4 -0
- package/lib/config.js.map +1 -1
- package/lib/welcome-scope.d.ts +6 -0
- package/lib/welcome-scope.d.ts.map +1 -0
- package/lib/welcome-scope.js +21 -0
- package/lib/welcome-scope.js.map +1 -0
- package/package.json +12 -12
- package/schema.json +34 -0
- package/src/config.ts +8 -0
- package/src/welcome-scope.ts +19 -0
package/README.md
CHANGED
|
@@ -9,10 +9,12 @@ Plugin Runtime 群运营套件,提供签到积分、消息统计、群问答
|
|
|
9
9
|
pnpm exec zhin install @zhin.js/plugin-group-suite
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
安装会将实例声明写入主配置 `plugins` 映射。手工声明:
|
|
13
13
|
|
|
14
|
-
```
|
|
15
|
-
|
|
14
|
+
```yaml
|
|
15
|
+
configVersion: 2
|
|
16
|
+
plugins:
|
|
17
|
+
group-suite: '@zhin.js/plugin-group-suite'
|
|
16
18
|
```
|
|
17
19
|
|
|
18
20
|
应用 Host 必须共享同一物理实例的 `@zhin.js/command`、`@zhin.js/middleware`、
|
|
@@ -20,20 +22,22 @@ pnpm exec zhin install @zhin.js/plugin-group-suite
|
|
|
20
22
|
Feature provider,并装配 Schedule Host。正常 CLI 负责 Host 装配;手写测试 Host 也需要显式安装这些资源。
|
|
21
23
|
|
|
22
24
|
```yaml
|
|
25
|
+
configVersion: 2
|
|
23
26
|
plugins:
|
|
24
|
-
group-suite:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
group-suite: "@zhin.js/plugin-group-suite"
|
|
28
|
+
group-suite:
|
|
29
|
+
keywordReply: true
|
|
30
|
+
ephemeralKeywords: false
|
|
31
|
+
welcomeMessage: "" # 默认关闭;可设为 欢迎 {name} ({id})
|
|
32
|
+
basePointsMin: 10
|
|
33
|
+
basePointsMax: 30
|
|
34
|
+
streakBonus: 5
|
|
35
|
+
streakCap: 50
|
|
36
|
+
rankSize: 10
|
|
37
|
+
teachMaxPerGroup: 200
|
|
38
|
+
teachCooldownMs: 3000
|
|
39
|
+
teachAllowRegex: true
|
|
40
|
+
teachPageSize: 10
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
约束以 [schema.json](./schema.json) 为准。启用关键词回复时默认要求 Database Host 的原子
|
|
@@ -57,10 +61,12 @@ plugins:
|
|
|
57
61
|
|
|
58
62
|
未配置命名空间时直接使用表内指令名;如果实例设 `commandNamespace: suite`,使用
|
|
59
63
|
`suite checkin` 等形式。实际前缀由 Endpoint 决定。
|
|
64
|
+
别名同样位于命名空间之下(如 `suite 签到`、`suite checkin rank`)。
|
|
60
65
|
|
|
61
66
|
| 指令/能力 | 说明 |
|
|
62
67
|
|---|---|
|
|
63
68
|
| `checkin` / `mypoints` / `rank` | 签到、积分与排行 |
|
|
69
|
+
| `checkin do` / `签到` · `checkin streak` · `checkin rank` | 兼容别名:分别等同 `checkin`、`mypoints`、`rank`,方便从 `checkin <子命令>` 风格的本地签到插件迁移 |
|
|
64
70
|
| `stats` / `stats-week` / `stats-rank` / `mystats` | 消息统计 |
|
|
65
71
|
| `stats-card` | 当日统计 HTML 图卡;没有渲染服务时走统一文本降级 |
|
|
66
72
|
| `teach` / `teach-regex` / `teach-list` / `forget` | 群问答与管理 |
|
|
@@ -73,6 +79,35 @@ plugins:
|
|
|
73
79
|
欢迎通过 `outboundMessageToken` 走统一发送链路,投递失败/未知会报错,不盲目重试。
|
|
74
80
|
`stats-card` 是 HTML 图片/文本展示,不是平台原生 `card` 载荷。
|
|
75
81
|
|
|
82
|
+
### 限定群欢迎范围
|
|
83
|
+
|
|
84
|
+
`welcomeConversations` 是当前插件实例的精确会话白名单,最多 100 项。省略此字段保留原行为:
|
|
85
|
+
设置非空 `welcomeMessage` 后,所有符合条件的群成员加入事件都可触发欢迎。显式配置 `[]`
|
|
86
|
+
则不欢迎任何会话;`welcomeMessage` 为空时仍始终关闭欢迎。
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
group-suite:
|
|
90
|
+
welcomeMessage: "欢迎 {name} ({id})"
|
|
91
|
+
welcomeConversations:
|
|
92
|
+
- endpoint:
|
|
93
|
+
adapter: "<复制实际 conversation.endpoint.adapter>"
|
|
94
|
+
id: "root/qq\u0000zhin.adapter\u0000icqq~main"
|
|
95
|
+
kind: group
|
|
96
|
+
id: "123456"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
示例 Endpoint ID 仅说明格式,实际请复制当前 canonical `conversation.endpoint`,不要按昵称、
|
|
100
|
+
账号、Endpoint 简称或实例名拼接。完整 Endpoint ID 中的 NUL 在双引号 YAML 中写为 `\u0000`。
|
|
101
|
+
`endpoint.adapter` 也须与事件完全一致。
|
|
102
|
+
|
|
103
|
+
每项同时匹配完整 Endpoint 身份、`kind`、会话 `id`、`parent.kind/id` 和 `threadId`,没有通配符。
|
|
104
|
+
省略 `parent` 或 `threadId` 只匹配同样没有该字段的群根会话;需要父容器或线程时须完整填写。
|
|
105
|
+
`null`、空 ID、残缺父容器和额外字段会被拒绝。重复条目不会重复发送欢迎。
|
|
106
|
+
白名单只筛选入站群成员加入事件,不改写原会话投递目标,也不限制签到、词库、统计等能力。
|
|
107
|
+
|
|
108
|
+
这不补充平台本身缺少的入站事件或目标:未提供 canonical 群会话的成员加入事件不会发送欢迎,
|
|
109
|
+
也不会推测频道。配置可热更新,校验失败保留旧配置与 generation;没有单独的范围数据库。
|
|
110
|
+
|
|
76
111
|
所有新数据按插件实例与完整会话隔离:平台、Endpoint 身份、群/频道/私聊类型与 ID,以及父会话、
|
|
77
112
|
线程信息共同参与键。相同群号的两个 Bot 不共享词库、签到、教学或统计。群专用 Tool 只接受 IM
|
|
78
113
|
群上下文,并通过当前 AdapterIndex 精确解析 Endpoint,无法确定唯一来源时拒绝执行。当前群的查询保留当前线程,只查该完整会话,不聚合其他线程。
|
|
@@ -5,6 +5,7 @@ import { doCheckin } from "../../lib/checkin-lib.js";
|
|
|
5
5
|
import { resolveGroupSuiteRuntime } from "../../lib/runtime-state.js";
|
|
6
6
|
export default defineCommand({
|
|
7
7
|
description: '每日签到获得积分',
|
|
8
|
+
alias: ['checkin do', '签到'],
|
|
8
9
|
execute({ config, input, owner, use }) {
|
|
9
10
|
return doCheckin(input ?? {}, resolveGroupSuiteConfig(config), resolveGroupSuiteRuntime({ owner, use }));
|
|
10
11
|
},
|
|
@@ -6,6 +6,7 @@ import { resolveGroupSuiteRuntime } from '../../src/runtime-state.js';
|
|
|
6
6
|
|
|
7
7
|
export default defineCommand<GroupSuiteConfig, string, Message>({
|
|
8
8
|
description: '每日签到获得积分',
|
|
9
|
+
alias: ['checkin do', '签到'],
|
|
9
10
|
execute({ config, input, owner, use }) {
|
|
10
11
|
return doCheckin(input ?? {}, resolveGroupSuiteConfig(config), resolveGroupSuiteRuntime({ owner, use }));
|
|
11
12
|
},
|
|
@@ -4,6 +4,7 @@ import { myPoints } from "../../lib/checkin-lib.js";
|
|
|
4
4
|
import { resolveGroupSuiteRuntime } from "../../lib/runtime-state.js";
|
|
5
5
|
export default defineCommand({
|
|
6
6
|
description: '查看个人积分与签到信息',
|
|
7
|
+
alias: ['checkin streak'],
|
|
7
8
|
execute({ input, owner, use }) {
|
|
8
9
|
return myPoints(input ?? {}, resolveGroupSuiteRuntime({ owner, use }));
|
|
9
10
|
},
|
|
@@ -6,6 +6,7 @@ import { resolveGroupSuiteRuntime } from '../../src/runtime-state.js';
|
|
|
6
6
|
|
|
7
7
|
export default defineCommand<GroupSuiteConfig, string, Message>({
|
|
8
8
|
description: '查看个人积分与签到信息',
|
|
9
|
+
alias: ['checkin streak'],
|
|
9
10
|
execute({ input, owner, use }) {
|
|
10
11
|
return myPoints(input ?? {}, resolveGroupSuiteRuntime({ owner, use }));
|
|
11
12
|
},
|
package/commands/rank/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { pointsRank } from "../../lib/checkin-lib.js";
|
|
|
5
5
|
import { resolveGroupSuiteRuntime } from "../../lib/runtime-state.js";
|
|
6
6
|
export default defineCommand({
|
|
7
7
|
description: '查看积分排行榜',
|
|
8
|
+
alias: ['checkin rank'],
|
|
8
9
|
execute({ config, input, owner, use }) {
|
|
9
10
|
return pointsRank(input ?? {}, resolveGroupSuiteConfig(config), resolveGroupSuiteRuntime({ owner, use }));
|
|
10
11
|
},
|
package/commands/rank/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { resolveGroupSuiteRuntime } from '../../src/runtime-state.js';
|
|
|
6
6
|
|
|
7
7
|
export default defineCommand<GroupSuiteConfig, string, Message>({
|
|
8
8
|
description: '查看积分排行榜',
|
|
9
|
+
alias: ['checkin rank'],
|
|
9
10
|
execute({ config, input, owner, use }) {
|
|
10
11
|
return pointsRank(input ?? {}, resolveGroupSuiteConfig(config), resolveGroupSuiteRuntime({ owner, use }));
|
|
11
12
|
},
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
import { defineHandler } from 'zhin.js/handler';
|
|
3
3
|
import { outboundMessageToken } from '@zhin.js/core/runtime';
|
|
4
4
|
import { resolveGroupSuiteConfig } from "../../lib/config.js";
|
|
5
|
+
import { welcomeConversationAllowed } from "../../lib/welcome-scope.js";
|
|
5
6
|
/** Canonical notice input only; no SDK-specific callbacks or direct platform sends. */
|
|
6
7
|
export default defineHandler({
|
|
7
8
|
event: 'notice.receive',
|
|
8
9
|
async handle({ payload: notice }) {
|
|
9
|
-
const
|
|
10
|
+
const config = resolveGroupSuiteConfig(this.config);
|
|
11
|
+
const text = config.welcomeMessage;
|
|
10
12
|
if (!text || notice.name !== 'notice.group.member_increase'
|
|
11
13
|
|| notice.conversation?.kind !== 'group' || !notice.target?.id)
|
|
12
14
|
return;
|
|
15
|
+
if (!welcomeConversationAllowed(config.welcomeConversations, notice.conversation))
|
|
16
|
+
return;
|
|
13
17
|
const content = text.replaceAll('{name}', notice.target.name ?? notice.target.id)
|
|
14
18
|
.replaceAll('{id}', notice.target.id);
|
|
15
19
|
const result = await this.use(outboundMessageToken).send({
|
|
16
|
-
requester: this.owner,
|
|
20
|
+
requester: this.owner.id,
|
|
17
21
|
conversation: notice.conversation,
|
|
18
22
|
content: { type: 'text', data: { text: content } },
|
|
19
23
|
});
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { defineHandler } from 'zhin.js/handler';
|
|
2
2
|
import { outboundMessageToken } from '@zhin.js/core/runtime';
|
|
3
3
|
import { resolveGroupSuiteConfig, type GroupSuiteConfig } from '../../src/config.js';
|
|
4
|
+
import { welcomeConversationAllowed } from '../../src/welcome-scope.js';
|
|
4
5
|
|
|
5
6
|
/** Canonical notice input only; no SDK-specific callbacks or direct platform sends. */
|
|
6
7
|
export default defineHandler({
|
|
7
8
|
event: 'notice.receive',
|
|
8
9
|
async handle({ payload: notice }) {
|
|
9
|
-
const
|
|
10
|
+
const config = resolveGroupSuiteConfig(this.config as Partial<GroupSuiteConfig>);
|
|
11
|
+
const text = config.welcomeMessage;
|
|
10
12
|
if (!text || notice.name !== 'notice.group.member_increase'
|
|
11
13
|
|| notice.conversation?.kind !== 'group' || !notice.target?.id) return;
|
|
14
|
+
if (!welcomeConversationAllowed(config.welcomeConversations, notice.conversation)) return;
|
|
12
15
|
const content = text.replaceAll('{name}', notice.target.name ?? notice.target.id)
|
|
13
16
|
.replaceAll('{id}', notice.target.id);
|
|
14
17
|
const result = await this.use(outboundMessageToken).send({
|
|
15
|
-
requester: this.owner,
|
|
18
|
+
requester: this.owner.id,
|
|
16
19
|
conversation: notice.conversation,
|
|
17
20
|
content: { type: 'text', data: { text: content } },
|
|
18
21
|
});
|
package/lib/config.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import type { DeliveryTarget } from 'zhin.js';
|
|
1
2
|
export interface GroupSuiteConfig {
|
|
2
3
|
keywordReply: boolean;
|
|
3
4
|
/** Explicitly volatile keywords for demos without a persistent Database Host. */
|
|
4
5
|
ephemeralKeywords: boolean;
|
|
5
6
|
/** Empty disables group member welcomes. {name} and {id} are literal placeholders. */
|
|
6
7
|
welcomeMessage: string;
|
|
8
|
+
/** Omitted preserves all groups; [] disables welcomes. Each address matches exactly. */
|
|
9
|
+
welcomeConversations?: readonly DeliveryTarget[];
|
|
7
10
|
basePointsMin: number;
|
|
8
11
|
basePointsMax: number;
|
|
9
12
|
streakBonus: number;
|
package/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,iFAAiF;IACjF,iBAAiB,EAAE,OAAO,CAAC;IAC3B,sFAAsF;IACtF,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAaxC,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,GACzC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,iFAAiF;IACjF,iBAAiB,EAAE,OAAO,CAAC;IAC3B,sFAAsF;IACtF,cAAc,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,oBAAoB,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAaxC,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,GACzC,gBAAgB,CAQlB"}
|
package/lib/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeWelcomeConversations } from './welcome-scope.js';
|
|
1
2
|
export const DEFAULT_GROUP_SUITE_CONFIG = {
|
|
2
3
|
keywordReply: false,
|
|
3
4
|
ephemeralKeywords: false,
|
|
@@ -16,6 +17,9 @@ export function resolveGroupSuiteConfig(raw) {
|
|
|
16
17
|
return {
|
|
17
18
|
...DEFAULT_GROUP_SUITE_CONFIG,
|
|
18
19
|
...raw,
|
|
20
|
+
...(raw?.welcomeConversations === undefined ? {} : {
|
|
21
|
+
welcomeConversations: normalizeWelcomeConversations(raw.welcomeConversations),
|
|
22
|
+
}),
|
|
19
23
|
};
|
|
20
24
|
}
|
|
21
25
|
//# sourceMappingURL=config.js.map
|
package/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAqBnE,MAAM,CAAC,MAAM,0BAA0B,GAAqB;IAC1D,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,cAAc,EAAE,EAAE;IAClB,aAAa,EAAE,EAAE;IACjB,aAAa,EAAE,EAAE;IACjB,WAAW,EAAE,CAAC;IACd,SAAS,EAAE,EAAE;IACb,QAAQ,EAAE,EAAE;IACZ,gBAAgB,EAAE,GAAG;IACrB,eAAe,EAAE,IAAI;IACrB,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,EAAE;CAClB,CAAC;AAEF,MAAM,UAAU,uBAAuB,CACrC,GAA0C;IAE1C,OAAO;QACL,GAAG,0BAA0B;QAC7B,GAAG,GAAG;QACN,GAAG,CAAC,GAAG,EAAE,oBAAoB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACjD,oBAAoB,EAAE,6BAA6B,CAAC,GAAG,CAAC,oBAAoB,CAAC;SAC9E,CAAC;KACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type DeliveryTarget } from 'zhin.js';
|
|
2
|
+
/** Validate without resolving aliases, retaining clients or introducing another scope store. */
|
|
3
|
+
export declare function normalizeWelcomeConversations(value: unknown): readonly DeliveryTarget[];
|
|
4
|
+
/** Missing parent/thread matches absence, never all descendants or threads. */
|
|
5
|
+
export declare function welcomeConversationAllowed(scopes: readonly DeliveryTarget[] | undefined, target: DeliveryTarget): boolean;
|
|
6
|
+
//# sourceMappingURL=welcome-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"welcome-scope.d.ts","sourceRoot":"","sources":["../src/welcome-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,SAAS,CAAC;AAGpE,gGAAgG;AAChG,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,cAAc,EAAE,CAOvF;AAED,+EAA+E;AAC/E,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAIzH"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createDeliveryTarget } from 'zhin.js';
|
|
2
|
+
import { conversationKey } from './conversation-scope.js';
|
|
3
|
+
/** Validate without resolving aliases, retaining clients or introducing another scope store. */
|
|
4
|
+
export function normalizeWelcomeConversations(value) {
|
|
5
|
+
if (!Array.isArray(value) || value.length > 100)
|
|
6
|
+
throw new TypeError('welcomeConversations must contain at most 100 exact group conversations');
|
|
7
|
+
return Object.freeze(value.map(entry => {
|
|
8
|
+
const target = createDeliveryTarget({ conversation: entry });
|
|
9
|
+
if (target.kind !== 'group')
|
|
10
|
+
throw new TypeError('welcomeConversations only supports group conversations');
|
|
11
|
+
return target;
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
/** Missing parent/thread matches absence, never all descendants or threads. */
|
|
15
|
+
export function welcomeConversationAllowed(scopes, target) {
|
|
16
|
+
if (scopes === undefined)
|
|
17
|
+
return true;
|
|
18
|
+
const key = conversationKey(target);
|
|
19
|
+
return scopes.some(scope => conversationKey(scope) === key);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=welcome-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"welcome-scope.js","sourceRoot":"","sources":["../src/welcome-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAuB,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,gGAAgG;AAChG,MAAM,UAAU,6BAA6B,CAAC,KAAc;IAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG;QAAE,MAAM,IAAI,SAAS,CAAC,yEAAyE,CAAC,CAAC;IAChJ,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACrC,MAAM,MAAM,GAAG,oBAAoB,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;YAAE,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;QAC3G,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,0BAA0B,CAAC,MAA6C,EAAE,MAAsB;IAC9G,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;AAC9D,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.13",
|
|
4
4
|
"description": "Zhin.js 群运营套件:签到、教学、统计、持久词库与群欢迎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -41,25 +41,25 @@
|
|
|
41
41
|
},
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@zhin.js/components": "1.1.
|
|
45
|
-
"@zhin.js/core": "1.1.
|
|
46
|
-
"@zhin.js/jsx": "1.1.
|
|
47
|
-
"@zhin.js/skill": "1.1.
|
|
48
|
-
"@zhin.js/tool": "1.1.
|
|
44
|
+
"@zhin.js/components": "1.1.4",
|
|
45
|
+
"@zhin.js/core": "1.1.48",
|
|
46
|
+
"@zhin.js/jsx": "1.1.4",
|
|
47
|
+
"@zhin.js/skill": "1.1.8",
|
|
48
|
+
"@zhin.js/tool": "1.1.8"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"typescript": "^6.0.3",
|
|
52
52
|
"vitest": "^4.1.10",
|
|
53
53
|
"zod": "^4.4.3",
|
|
54
|
-
"zhin.js": "1.1.
|
|
54
|
+
"zhin.js": "1.1.13"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"zod": "^4.0.0",
|
|
58
|
-
"@zhin.js/command": "^1.1.
|
|
59
|
-
"@zhin.js/middleware": "^1.1.
|
|
60
|
-
"zhin.js": "^1.1.
|
|
61
|
-
"@zhin.js/handler": "^1.1.
|
|
62
|
-
"@zhin.js/schedule-feature": "^1.1.
|
|
58
|
+
"@zhin.js/command": "^1.1.9",
|
|
59
|
+
"@zhin.js/middleware": "^1.1.8",
|
|
60
|
+
"zhin.js": "^1.1.13",
|
|
61
|
+
"@zhin.js/handler": "^1.1.7",
|
|
62
|
+
"@zhin.js/schedule-feature": "^1.1.7"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@zhin.js/command": {
|
package/schema.json
CHANGED
|
@@ -49,6 +49,40 @@
|
|
|
49
49
|
"default": "",
|
|
50
50
|
"description": "Group welcome text. Empty disables; {name}/{id} refer to the joined member."
|
|
51
51
|
},
|
|
52
|
+
"welcomeConversations": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"maxItems": 100,
|
|
55
|
+
"description": "Exact welcome conversations. Omitted welcomes all groups; [] welcomes none. Missing parent/thread requires their absence; no wildcards.",
|
|
56
|
+
"x-zhin-config": { "component": "conversation-scope" },
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": ["endpoint", "kind", "id"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"endpoint": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": false,
|
|
65
|
+
"required": ["adapter", "id"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"adapter": { "type": "string", "minLength": 1 },
|
|
68
|
+
"id": { "type": "string", "minLength": 1 }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"kind": { "type": "string", "enum": ["group"] },
|
|
72
|
+
"id": { "type": "string", "minLength": 1 },
|
|
73
|
+
"parent": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"required": ["kind", "id"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"kind": { "type": "string", "enum": ["private", "group", "channel"] },
|
|
79
|
+
"id": { "type": "string", "minLength": 1 }
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"threadId": { "type": "string", "minLength": 1 }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
52
86
|
"ephemeralKeywords": {
|
|
53
87
|
"type": "boolean",
|
|
54
88
|
"default": false,
|
package/src/config.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import type { DeliveryTarget } from 'zhin.js';
|
|
2
|
+
import { normalizeWelcomeConversations } from './welcome-scope.js';
|
|
3
|
+
|
|
1
4
|
export interface GroupSuiteConfig {
|
|
2
5
|
keywordReply: boolean;
|
|
3
6
|
/** Explicitly volatile keywords for demos without a persistent Database Host. */
|
|
4
7
|
ephemeralKeywords: boolean;
|
|
5
8
|
/** Empty disables group member welcomes. {name} and {id} are literal placeholders. */
|
|
6
9
|
welcomeMessage: string;
|
|
10
|
+
/** Omitted preserves all groups; [] disables welcomes. Each address matches exactly. */
|
|
11
|
+
welcomeConversations?: readonly DeliveryTarget[];
|
|
7
12
|
basePointsMin: number;
|
|
8
13
|
basePointsMax: number;
|
|
9
14
|
streakBonus: number;
|
|
@@ -36,5 +41,8 @@ export function resolveGroupSuiteConfig(
|
|
|
36
41
|
return {
|
|
37
42
|
...DEFAULT_GROUP_SUITE_CONFIG,
|
|
38
43
|
...raw,
|
|
44
|
+
...(raw?.welcomeConversations === undefined ? {} : {
|
|
45
|
+
welcomeConversations: normalizeWelcomeConversations(raw.welcomeConversations),
|
|
46
|
+
}),
|
|
39
47
|
};
|
|
40
48
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createDeliveryTarget, type DeliveryTarget } from 'zhin.js';
|
|
2
|
+
import { conversationKey } from './conversation-scope.js';
|
|
3
|
+
|
|
4
|
+
/** Validate without resolving aliases, retaining clients or introducing another scope store. */
|
|
5
|
+
export function normalizeWelcomeConversations(value: unknown): readonly DeliveryTarget[] {
|
|
6
|
+
if (!Array.isArray(value) || value.length > 100) throw new TypeError('welcomeConversations must contain at most 100 exact group conversations');
|
|
7
|
+
return Object.freeze(value.map(entry => {
|
|
8
|
+
const target = createDeliveryTarget({ conversation: entry });
|
|
9
|
+
if (target.kind !== 'group') throw new TypeError('welcomeConversations only supports group conversations');
|
|
10
|
+
return target;
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Missing parent/thread matches absence, never all descendants or threads. */
|
|
15
|
+
export function welcomeConversationAllowed(scopes: readonly DeliveryTarget[] | undefined, target: DeliveryTarget): boolean {
|
|
16
|
+
if (scopes === undefined) return true;
|
|
17
|
+
const key = conversationKey(target);
|
|
18
|
+
return scopes.some(scope => conversationKey(scope) === key);
|
|
19
|
+
}
|