weaver-work-cli 0.1.5 → 0.1.7
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 +14 -9
- package/dist/cmd/skills/index.js +60 -0
- package/dist/internal/e10/auth/commands.js +48 -32
- package/dist/internal/e10/auth/session.js +44 -3
- package/dist/internal/e10/auth/xiaoe.js +363 -11
- package/dist/internal/e10/context.js +4 -2
- package/dist/internal/skills/detector.js +42 -0
- package/dist/internal/skills/install.js +50 -3
- package/dist/shortcuts/archive/render/search-format.js +12 -3
- package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
- package/dist/shortcuts/ebuilder-form/errors.js +54 -0
- package/dist/shortcuts/ebuilder-form/host.js +1238 -0
- package/dist/shortcuts/ebuilder-form/index.js +108 -0
- package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
- package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
- package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
- package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
- package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
- package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
- package/dist/shortcuts/ebuilder-form/operations.js +8 -0
- package/dist/shortcuts/ehr/operations/salary.js +2 -140
- package/dist/shortcuts/im/continuation.js +70 -0
- package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
- package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
- package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
- package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
- package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
- package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
- package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
- package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
- package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
- package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
- package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
- package/dist/shortcuts/im/operations/shared.js +442 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
- package/dist/shortcuts/im/operations/user-remind.js +201 -0
- package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
- package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
- package/dist/shortcuts/index.js +10 -2
- package/dist/shortcuts/invoice/manifest.js +113 -10
- package/dist/shortcuts/invoice/operations/import.js +39 -14
- package/dist/shortcuts/invoice/operations/reim.js +38 -18
- package/dist/shortcuts/invoice/operations/shared.js +35 -6
- package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
- package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
- package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
- package/dist/shortcuts/okr/errors.js +70 -0
- package/dist/shortcuts/okr/host.js +169 -0
- package/dist/shortcuts/okr/index.js +155 -0
- package/dist/shortcuts/okr/manifest.js +70 -0
- package/dist/shortcuts/okr/operations/link.js +305 -0
- package/dist/shortcuts/okr/operations/read.js +481 -0
- package/dist/shortcuts/okr/operations/registry.js +31 -0
- package/dist/shortcuts/okr/operations/shared.js +381 -0
- package/dist/shortcuts/okr/operations/types.js +88 -0
- package/dist/shortcuts/okr/operations/write.js +838 -0
- package/dist/shortcuts/okr/operations.js +8 -0
- package/dist/shortcuts/project/continuation.js +70 -0
- package/dist/shortcuts/project/errors.js +53 -0
- package/dist/shortcuts/project/host.js +160 -0
- package/dist/shortcuts/project/index.js +106 -0
- package/dist/shortcuts/project/manifest.js +36 -0
- package/dist/shortcuts/project/operations/read.js +551 -0
- package/dist/shortcuts/project/operations/registry.js +31 -0
- package/dist/shortcuts/project/operations/shared.js +68 -0
- package/dist/shortcuts/project/operations/types.js +39 -0
- package/dist/shortcuts/project/operations/write.js +266 -0
- package/dist/shortcuts/project/operations.js +8 -0
- package/dist/shortcuts/workflow/continuation.js +508 -0
- package/dist/shortcuts/workflow/endpoints.js +149 -0
- package/dist/shortcuts/workflow/errors.js +112 -0
- package/dist/shortcuts/workflow/host.js +224 -0
- package/dist/shortcuts/workflow/index.js +116 -0
- package/dist/shortcuts/workflow/manifest.js +30 -0
- package/dist/shortcuts/workflow/operations/form.js +4159 -0
- package/dist/shortcuts/workflow/operations/read.js +3778 -0
- package/dist/shortcuts/workflow/operations/registry.js +33 -0
- package/dist/shortcuts/workflow/operations/shared.js +167 -0
- package/dist/shortcuts/workflow/operations/types.js +44 -0
- package/dist/shortcuts/workflow/operations/write.js +1991 -0
- package/dist/shortcuts/workflow/operations.js +119 -0
- package/dist/shortcuts/workflow/state.js +182 -0
- package/docs/SKILL.md +5 -3
- package/docs/_catalog.md +10 -1
- package/docs/agent-invoice.md +2 -2
- package/docs/agent-skill-install.md +2 -2
- package/docs/e10-auth.md +3 -1
- package/docs/ebuilder-form.md +38 -0
- package/docs/im.md +104 -0
- package/docs/invoice.md +5 -5
- package/docs/okr.md +122 -0
- package/docs/operation-manual.md +10 -7
- package/docs/project.md +69 -0
- package/docs/skill-review-2026-09-19.md +68 -0
- package/package.json +3 -2
- package/scripts/package-skill.mjs +38 -11
- package/scripts/sync-connector-skills.mjs +172 -0
- package/skill-template/business-info.json +357 -16
- package/skill-template/domains/ebuilder-form.md +5 -0
- package/skill-template/domains/okr.md +18 -0
- package/skill-template/domains/shijingran.md +3 -0
- package/skill-template/domains/skill-maker.md +4 -4
- package/skill-template/domains/workflow.md +22 -0
- package/skill-template/domains/yepiaotong.md +4 -2
- package/skill-template/domains/yimiaoban.md +13 -10
- package/skill-template/product.json +5 -0
- package/skill-template/skill-template.md +1 -0
- package/skills/weaver-e10-calendar/SKILL.md +5 -1
- package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
- package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
- package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
- package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
- package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
- package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
- package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
- package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
- package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
- package/skills/weaver-e10-esb/SKILL.md +1 -1
- package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
- package/skills/weaver-e10-hrm/SKILL.md +1 -1
- package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
- package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
- package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
- package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
- package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
- package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
- package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
- package/skills/weaver-e10-jucailin/SKILL.md +2 -2
- package/skills/weaver-e10-mail/SKILL.md +22 -6
- package/skills/weaver-e10-meeting/SKILL.md +22 -1
- package/skills/weaver-e10-okr/SKILL.md +139 -0
- package/skills/weaver-e10-okr/references/okr-align.md +59 -0
- package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
- package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
- package/skills/weaver-e10-okr/references/okr-query.md +105 -0
- package/skills/weaver-e10-okr/references/okr-write.md +94 -0
- package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
- package/skills/weaver-e10-plan/SKILL.md +3 -3
- package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
- package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
- package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
- package/skills/weaver-e10-shared/SKILL.md +79 -11
- package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
- package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
- package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
- package/skills/weaver-e10-shijingran/SKILL.md +83 -0
- package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
- package/skills/weaver-e10-shijingran/references/operations.md +154 -0
- package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
- package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
- package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
- package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
- package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
- package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
- package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
- package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
- package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
- package/skills/weaver-e10-workflow/SKILL.md +250 -0
- package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
- package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
- package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
- package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
- package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
- package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
- package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
- package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
- package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
- package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
- package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
- package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
- package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
- package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
- package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
- package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
- package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
- package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
- package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
- package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
- package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
- package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
- package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
- package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
- package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
- package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
- package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
- package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
- package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
- package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
- package/docs/yimiaoban.md +0 -86
- package/skills/weaver-e10-calendar/product.json +0 -8
- package/skills/weaver-e10-esb/product.json +0 -8
- package/skills/weaver-e10-hrm/product.json +0 -8
- package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
- package/skills/weaver-e10-jucailin/product.json +0 -8
- package/skills/weaver-e10-mail/product.json +0 -8
- package/skills/weaver-e10-meeting/product.json +0 -8
- package/skills/weaver-e10-plan/product.json +0 -8
- package/skills/weaver-e10-qiyecheng/product.json +0 -8
- package/skills/weaver-e10-skill-maker/product.json +0 -8
- package/skills/weaver-e10-wenshuding/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/product.json +0 -8
- package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
- package/skills/weaver-e10-yimiaoban/product.json +0 -8
- package/skills/weaver-e10-ziguanjia/product.json +0 -8
- /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { imValidationError } from '../errors.js';
|
|
2
2
|
import { schemaOf, WRITE, } from './types.js';
|
|
3
|
-
import { assertImOk, compact, splitIds, str, toUint } from './shared.js';
|
|
3
|
+
import { assertImOk, compact, splitIds, str, toUint, unwrapInner } from './shared.js';
|
|
4
4
|
import { makeWritePair } from './write-util.js';
|
|
5
5
|
/** 解析 uid 列表为 {cid,uid} 成员,支持 failed 移除。 */
|
|
6
6
|
async function resolveMembers(args, env) {
|
|
@@ -50,23 +50,23 @@ async function defaultGroupName(members, nameOf, selfUid, env) {
|
|
|
50
50
|
}
|
|
51
51
|
// ── 建群 ──
|
|
52
52
|
const groupCreatePair = makeWritePair({
|
|
53
|
-
action: '
|
|
53
|
+
action: 'im.group.create',
|
|
54
54
|
async plan(args, env) {
|
|
55
55
|
const rawUsers = Array.isArray(args.users) ? args.users.map((x) => String(x)) : splitIds(args.users, 'users');
|
|
56
56
|
const names = Array.isArray(args.names) ? args.names.map((x) => String(x)) : [];
|
|
57
57
|
if (names.length && names.length !== rawUsers.length) {
|
|
58
|
-
throw
|
|
58
|
+
throw imValidationError('names_mismatch', 'names 数量必须与 users 一致(顺序一一对应)');
|
|
59
59
|
}
|
|
60
60
|
const { users, failed, names: resolvedNames } = await resolveMembers(args, env);
|
|
61
61
|
if (failed.length && args.removeFailed !== true) {
|
|
62
|
-
throw
|
|
62
|
+
throw imValidationError('members_resolve_failed', `以下成员信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`, { details: failed });
|
|
63
63
|
}
|
|
64
64
|
if (users.length === 0)
|
|
65
|
-
throw
|
|
65
|
+
throw imValidationError('members_empty', '没有可入群的成员');
|
|
66
66
|
// 🔴 群成员必须包含操作者自己(服务端依据 eteamsid 把操作者设为群主)
|
|
67
67
|
const selfUid = str((await env.host.getSession()).userId);
|
|
68
68
|
if (selfUid && !users.some((u) => u.uid === selfUid)) {
|
|
69
|
-
throw
|
|
69
|
+
throw imValidationError('self_not_included', `不允许创建不包含自己的群聊:users 必须包含操作者自己(uid=${selfUid})`);
|
|
70
70
|
}
|
|
71
71
|
// 默认群名:调用方 names(与 users 顺序对应)优先,其次 hrm 解析姓名,最后回退 uid
|
|
72
72
|
const providedNames = {};
|
|
@@ -86,7 +86,7 @@ const groupCreatePair = makeWritePair({
|
|
|
86
86
|
return !uid || uid === '0' || !cid || cid === '0';
|
|
87
87
|
});
|
|
88
88
|
if (adminFailed.length) {
|
|
89
|
-
throw
|
|
89
|
+
throw imValidationError('admins_resolve_failed', `管理员信息获取失败(cid/uid 缺失或为 0):${adminFailed.map((uid) => adminMap[uid]?.name || uid).join('、')};请确认后重试(不可静默替换操作人身份)`, { details: adminFailed });
|
|
90
90
|
}
|
|
91
91
|
admins = adminRaw.map((uid) => ({ cid: adminMap[uid]?.cid || '', uid }));
|
|
92
92
|
}
|
|
@@ -114,7 +114,7 @@ const groupCreatePair = makeWritePair({
|
|
|
114
114
|
method: 'POST',
|
|
115
115
|
path: '/api/em/msg/executeIm/group/groupCreate',
|
|
116
116
|
body,
|
|
117
|
-
operation: '
|
|
117
|
+
operation: 'im.group.create',
|
|
118
118
|
});
|
|
119
119
|
assertImOk(res, '创建群');
|
|
120
120
|
// 群 id 可能位于 data / data.data 的 groupId / group_id / id 字段,统一扫描;同时回传服务端原始数据便于核对
|
|
@@ -128,17 +128,17 @@ const groupCreatePair = makeWritePair({
|
|
|
128
128
|
});
|
|
129
129
|
// ── 邀请入群 ──
|
|
130
130
|
const groupInvitePair = makeWritePair({
|
|
131
|
-
action: '
|
|
131
|
+
action: 'im.group.invite',
|
|
132
132
|
async plan(args, env) {
|
|
133
133
|
const groupId = str(args.groupId);
|
|
134
134
|
if (!groupId || groupId === '0')
|
|
135
|
-
throw
|
|
135
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
136
136
|
const { users, failed } = await resolveMembers(args, env);
|
|
137
137
|
if (failed.length && args.removeFailed !== true) {
|
|
138
|
-
throw
|
|
138
|
+
throw imValidationError('members_resolve_failed', `以下成员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`);
|
|
139
139
|
}
|
|
140
140
|
if (users.length === 0)
|
|
141
|
-
throw
|
|
141
|
+
throw imValidationError('members_empty', '没有可邀请的成员');
|
|
142
142
|
return {
|
|
143
143
|
summary: { action: '邀请入群', groupId, inviteCount: users.length, users: users.map((u) => u.uid), removeFailed: args.removeFailed === true ? failed.map((f) => f.uid) : [] },
|
|
144
144
|
snapshot: { groupId, users },
|
|
@@ -150,7 +150,7 @@ const groupInvitePair = makeWritePair({
|
|
|
150
150
|
method: 'POST',
|
|
151
151
|
path: '/api/em/msg/executeIm/group/inviteGroupUsers',
|
|
152
152
|
body,
|
|
153
|
-
operation: '
|
|
153
|
+
operation: 'im.group.invite',
|
|
154
154
|
});
|
|
155
155
|
assertImOk(res, '邀请入群');
|
|
156
156
|
return { groupId: plan.groupId, invited: plan.users.length };
|
|
@@ -158,17 +158,17 @@ const groupInvitePair = makeWritePair({
|
|
|
158
158
|
});
|
|
159
159
|
// ── 踢出群 ──
|
|
160
160
|
const groupKickPair = makeWritePair({
|
|
161
|
-
action: '
|
|
161
|
+
action: 'im.group.kick',
|
|
162
162
|
async plan(args, env) {
|
|
163
163
|
const groupId = str(args.groupId);
|
|
164
164
|
if (!groupId || groupId === '0')
|
|
165
|
-
throw
|
|
165
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
166
166
|
const { users, failed } = await resolveMembers(args, env);
|
|
167
167
|
if (failed.length && args.removeFailed !== true) {
|
|
168
|
-
throw
|
|
168
|
+
throw imValidationError('members_resolve_failed', `以下成员解析失败:${failed.map((f) => f.name || f.uid).join('、')};确认移除请传 removeFailed:true`);
|
|
169
169
|
}
|
|
170
170
|
if (users.length === 0)
|
|
171
|
-
throw
|
|
171
|
+
throw imValidationError('members_empty', '没有可移除的成员');
|
|
172
172
|
return {
|
|
173
173
|
summary: { action: '踢出群成员', groupId, kickCount: users.length, users: users.map((u) => u.uid) },
|
|
174
174
|
snapshot: { groupId, users },
|
|
@@ -180,7 +180,7 @@ const groupKickPair = makeWritePair({
|
|
|
180
180
|
method: 'POST',
|
|
181
181
|
path: '/api/em/msg/executeIm/group/kickGroupUsers',
|
|
182
182
|
body,
|
|
183
|
-
operation: '
|
|
183
|
+
operation: 'im.group.kick',
|
|
184
184
|
});
|
|
185
185
|
assertImOk(res, '踢出群成员');
|
|
186
186
|
return { groupId: plan.groupId, kicked: plan.users.length };
|
|
@@ -188,14 +188,57 @@ const groupKickPair = makeWritePair({
|
|
|
188
188
|
});
|
|
189
189
|
// ── 退群 ──
|
|
190
190
|
const groupExitPair = makeWritePair({
|
|
191
|
-
action: '
|
|
191
|
+
action: 'im.group.exit',
|
|
192
192
|
async plan(args, env) {
|
|
193
193
|
const groupId = str(args.groupId);
|
|
194
194
|
if (!groupId || groupId === '0')
|
|
195
|
-
throw
|
|
195
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
196
|
+
/**
|
|
197
|
+
* 只读回查群人数与群主:实测「群主/最后一个成员退群」后该群会变成无主空群,
|
|
198
|
+
* 之后 join / destroy 都会被服务端拒绝(1210),必须由客户端或管理员处理。
|
|
199
|
+
* 因此这两种情形要求调用方显式传 acknowledgeOrphanRisk:true 才签发 continuation。
|
|
200
|
+
*/
|
|
201
|
+
let memberCount;
|
|
202
|
+
let isOwner = false;
|
|
203
|
+
try {
|
|
204
|
+
const infoRes = await env.host.request({
|
|
205
|
+
method: 'POST',
|
|
206
|
+
path: '/api/em/msg/executeIm/group/queryGroupInfo',
|
|
207
|
+
body: { trans_id: 0, type: 0, mask: 0, group_id: [groupId], flag: 0 },
|
|
208
|
+
operation: 'im.group.exit.plan.info',
|
|
209
|
+
});
|
|
210
|
+
const info = (unwrapInner(infoRes)?.datas || [])[0];
|
|
211
|
+
if (info) {
|
|
212
|
+
const raw = info.user_num ?? info.userNum;
|
|
213
|
+
memberCount = raw === undefined || raw === null ? undefined : Number(raw);
|
|
214
|
+
const hostUid = str(info.host_uid ?? info.hostUid ?? '');
|
|
215
|
+
if (hostUid) {
|
|
216
|
+
const self = await env.host.getSelfIdentity();
|
|
217
|
+
isOwner = hostUid === String(self.uid);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// 回查失败(如已不在群内)不阻断退群,仅在摘要里说明未能核对风险
|
|
223
|
+
}
|
|
224
|
+
const orphanRisk = isOwner || (memberCount !== undefined && memberCount <= 1);
|
|
225
|
+
if (orphanRisk && args.acknowledgeOrphanRisk !== true) {
|
|
226
|
+
throw imValidationError('owner_exit_ack_required', `退群风险确认缺失:${isOwner ? '你是该群群主' : '你是该群最后一个成员'},退群后该群会变成无主空群,之后 join/destroy 都会被服务端拒绝(1210),只能由客户端或管理员处理。确认仍要退群请传 acknowledgeOrphanRisk:true`, { details: { groupId, isOwner, memberCount } });
|
|
227
|
+
}
|
|
196
228
|
return {
|
|
197
|
-
summary: {
|
|
198
|
-
|
|
229
|
+
summary: {
|
|
230
|
+
action: '退出群聊',
|
|
231
|
+
groupId,
|
|
232
|
+
...(memberCount === undefined ? {} : { memberCount }),
|
|
233
|
+
isOwner,
|
|
234
|
+
...(orphanRisk
|
|
235
|
+
? {
|
|
236
|
+
orphanRisk: true,
|
|
237
|
+
riskNote: `${isOwner ? '你是群主' : '你是最后一个成员'}:退群后该群会成为无主空群,无法再从 CLI 重新进群或解散(实测 1210 没有权限),需由客户端或管理员处理。`,
|
|
238
|
+
}
|
|
239
|
+
: {}),
|
|
240
|
+
},
|
|
241
|
+
snapshot: { groupId, acknowledgeOrphanRisk: args.acknowledgeOrphanRisk === true },
|
|
199
242
|
};
|
|
200
243
|
},
|
|
201
244
|
async execute(plan, env) {
|
|
@@ -203,7 +246,7 @@ const groupExitPair = makeWritePair({
|
|
|
203
246
|
method: 'POST',
|
|
204
247
|
path: '/api/em/msg/executeIm/group/exit',
|
|
205
248
|
body: { groupId: plan.groupId },
|
|
206
|
-
operation: '
|
|
249
|
+
operation: 'im.group.exit',
|
|
207
250
|
});
|
|
208
251
|
assertImOk(res, '退出群聊');
|
|
209
252
|
return { groupId: plan.groupId };
|
|
@@ -211,11 +254,11 @@ const groupExitPair = makeWritePair({
|
|
|
211
254
|
});
|
|
212
255
|
// ── 解散群 ──
|
|
213
256
|
const groupDestroyPair = makeWritePair({
|
|
214
|
-
action: '
|
|
257
|
+
action: 'im.group.destroy',
|
|
215
258
|
async plan(args, env) {
|
|
216
259
|
const groupId = str(args.groupId);
|
|
217
260
|
if (!groupId || groupId === '0')
|
|
218
|
-
throw
|
|
261
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
219
262
|
return {
|
|
220
263
|
summary: { action: '解散群聊(仅群主可执行,不可恢复)', groupId },
|
|
221
264
|
snapshot: { groupId },
|
|
@@ -226,7 +269,7 @@ const groupDestroyPair = makeWritePair({
|
|
|
226
269
|
method: 'POST',
|
|
227
270
|
path: '/api/em/msg/executeIm/group/destroy',
|
|
228
271
|
body: { groupId: plan.groupId },
|
|
229
|
-
operation: '
|
|
272
|
+
operation: 'im.group.destroy',
|
|
230
273
|
});
|
|
231
274
|
assertImOk(res, '解散群聊');
|
|
232
275
|
return { groupId: plan.groupId };
|
|
@@ -234,19 +277,19 @@ const groupDestroyPair = makeWritePair({
|
|
|
234
277
|
});
|
|
235
278
|
// ── 申请入群 ──
|
|
236
279
|
const groupJoinPair = makeWritePair({
|
|
237
|
-
action: '
|
|
280
|
+
action: 'im.group.join',
|
|
238
281
|
async plan(args, env) {
|
|
239
282
|
const groupId = args.groupId !== undefined && args.groupId !== '' ? str(args.groupId) : '';
|
|
240
283
|
const token = args.token !== undefined && args.token !== '' ? String(args.token) : '';
|
|
241
284
|
if (!groupId && !token)
|
|
242
|
-
throw
|
|
285
|
+
throw imValidationError('target_required', 'groupId 与 token 至少其一');
|
|
243
286
|
if (groupId && groupId === '0')
|
|
244
|
-
throw
|
|
287
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
245
288
|
if (token && token.length > 500)
|
|
246
|
-
throw
|
|
289
|
+
throw imValidationError('token_invalid', 'token 过长');
|
|
247
290
|
const mark = args.mark !== undefined && args.mark !== '' ? String(args.mark) : '';
|
|
248
291
|
if (mark.length > 100)
|
|
249
|
-
throw
|
|
292
|
+
throw imValidationError('mark_too_long', 'mark 不能超过 100 字节');
|
|
250
293
|
return {
|
|
251
294
|
summary: { action: '申请加入群聊', groupId: groupId || '(token)', hasMark: Boolean(mark), needApproval: true },
|
|
252
295
|
snapshot: compact({ groupId: groupId || undefined, token: token || undefined, mark: mark || undefined }),
|
|
@@ -266,7 +309,7 @@ const groupJoinPair = makeWritePair({
|
|
|
266
309
|
method: 'POST',
|
|
267
310
|
path: '/api/em/msg/executeIm/group/joinGroup',
|
|
268
311
|
body,
|
|
269
|
-
operation: '
|
|
312
|
+
operation: 'im.group.join',
|
|
270
313
|
});
|
|
271
314
|
assertImOk(res, '申请入群');
|
|
272
315
|
const flag = Number(res?.data?.data?.flag ?? res?.data?.flag ?? -1);
|
|
@@ -286,20 +329,20 @@ function parseSwitchs(raw) {
|
|
|
286
329
|
const flag = Number(flagRaw);
|
|
287
330
|
const onOff = Number(onoffRaw);
|
|
288
331
|
if (!Number.isInteger(flag) || !Number.isInteger(onOff)) {
|
|
289
|
-
throw
|
|
332
|
+
throw imValidationError('switchs_invalid', `switchs 必须形如 flag:onoff(如 1:1,2:0),收到:${pair}`);
|
|
290
333
|
}
|
|
291
334
|
return { flag, on_off: onOff };
|
|
292
335
|
});
|
|
293
336
|
}
|
|
294
337
|
// ── 批量改群属性(batModGroupInfo)──
|
|
295
338
|
const groupModifyPair = makeWritePair({
|
|
296
|
-
action: '
|
|
339
|
+
action: 'im.group.modify',
|
|
297
340
|
async plan(args, env) {
|
|
298
341
|
const groupId = str(args.groupId);
|
|
299
342
|
if (!groupId || groupId === '0')
|
|
300
|
-
throw
|
|
343
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
301
344
|
if (args.onoff !== undefined) {
|
|
302
|
-
throw
|
|
345
|
+
throw imValidationError('field_forbidden', 'onoff 字段服务端不处理,请用 switchs');
|
|
303
346
|
}
|
|
304
347
|
const body = { group_id: groupId };
|
|
305
348
|
let mask = 0;
|
|
@@ -330,31 +373,31 @@ const groupModifyPair = makeWritePair({
|
|
|
330
373
|
const resolveOrFail = async (raw, label) => {
|
|
331
374
|
const { users, failed } = await resolveMembers({ users: raw }, env);
|
|
332
375
|
if (failed.length && args.removeFailed !== true) {
|
|
333
|
-
throw
|
|
376
|
+
throw imValidationError('members_resolve_failed', `${label}信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name ? `${f.name}(${f.uid})` : f.uid).join('、')};确认移除请传 removeFailed:true`, { details: failed });
|
|
334
377
|
}
|
|
335
378
|
return users;
|
|
336
379
|
};
|
|
337
380
|
if (args.admins) {
|
|
338
381
|
const users = await resolveOrFail(args.admins, '管理员');
|
|
339
382
|
if (users.length === 0)
|
|
340
|
-
throw
|
|
383
|
+
throw imValidationError('members_empty', '管理员解析为空');
|
|
341
384
|
set(1 << 7, 'admins', users);
|
|
342
385
|
}
|
|
343
386
|
if (args.delAdmins) {
|
|
344
387
|
const users = await resolveOrFail(args.delAdmins, '待移除管理员');
|
|
345
388
|
if (users.length === 0)
|
|
346
|
-
throw
|
|
389
|
+
throw imValidationError('members_empty', '待移除管理员解析为空');
|
|
347
390
|
// 源 modGroupInfo.js:移除管理员走 del_admins 字段(mask 与 admins 同为 admins 位),不能写进 admins
|
|
348
391
|
set(1 << 7, 'del_admins', users);
|
|
349
392
|
}
|
|
350
393
|
if (args.creator) {
|
|
351
394
|
const users = await resolveOrFail(args.creator, '新群主');
|
|
352
395
|
if (!users.length)
|
|
353
|
-
throw
|
|
396
|
+
throw imValidationError('members_empty', '新群主解析失败');
|
|
354
397
|
set(1 << 6, 'creator', users[0]);
|
|
355
398
|
}
|
|
356
399
|
if (mask === 0)
|
|
357
|
-
throw
|
|
400
|
+
throw imValidationError('field_required', '至少修改一项(num/rsonoff/history/switchs/gtId/copyGroupids/admins/delAdmins/creator)');
|
|
358
401
|
body.mask = mask;
|
|
359
402
|
return {
|
|
360
403
|
summary: { action: '批量修改群属性', groupId, mask, changedFields: Object.keys(body).filter((k) => k !== 'group_id' && k !== 'mask') },
|
|
@@ -366,7 +409,7 @@ const groupModifyPair = makeWritePair({
|
|
|
366
409
|
method: 'POST',
|
|
367
410
|
path: '/api/em/msg/executeIm/group/batModGroupInfo',
|
|
368
411
|
body: plan.body,
|
|
369
|
-
operation: '
|
|
412
|
+
operation: 'im.group.modify',
|
|
370
413
|
});
|
|
371
414
|
assertImOk(res, '修改群属性');
|
|
372
415
|
return { groupId: plan.groupId };
|
|
@@ -374,31 +417,31 @@ const groupModifyPair = makeWritePair({
|
|
|
374
417
|
});
|
|
375
418
|
// ── 改群名/单字段(modifyGroupInfo)──
|
|
376
419
|
const groupRenamePair = makeWritePair({
|
|
377
|
-
action: '
|
|
420
|
+
action: 'im.group.rename',
|
|
378
421
|
async plan(args, env) {
|
|
379
422
|
const groupId = str(args.groupId);
|
|
380
423
|
if (!groupId || groupId === '0')
|
|
381
|
-
throw
|
|
424
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
382
425
|
const forbidden = ['announce', 'type', 'state'];
|
|
383
426
|
for (const key of forbidden) {
|
|
384
427
|
if (args[key] !== undefined)
|
|
385
|
-
throw
|
|
428
|
+
throw imValidationError('field_forbidden', `${key} 字段禁用:announce 请用公告操作,type/state 会触发高危行为`);
|
|
386
429
|
}
|
|
387
430
|
const fieldKeys = ['name', 'history', 'host', 'display', 'msgSetting', 'mark', 'gtid'].filter((k) => args[k] !== undefined);
|
|
388
431
|
if (fieldKeys.length === 0)
|
|
389
|
-
throw
|
|
432
|
+
throw imValidationError('field_required', '至少修改一项:name/history/host/display/msgSetting/mark/gtid');
|
|
390
433
|
if (fieldKeys.length > 1)
|
|
391
|
-
throw
|
|
434
|
+
throw imValidationError('field_conflict', '本接口一次只能改一项');
|
|
392
435
|
const field = fieldKeys[0];
|
|
393
436
|
const maskOrder = { name: 0, history: 8, host: 4, display: 5, msgSetting: 6, mark: 7, gtid: 9 };
|
|
394
437
|
let info = {};
|
|
395
438
|
if (field === 'host') {
|
|
396
439
|
const { users, failed } = await resolveMembers({ users: args.host }, env);
|
|
397
440
|
if (failed.length) {
|
|
398
|
-
throw
|
|
441
|
+
throw imValidationError('members_resolve_failed', `群主信息获取失败(cid/uid 缺失或为 0):${failed.map((f) => f.name ? `${f.name}(${f.uid})` : f.uid).join('、')}`, { details: failed });
|
|
399
442
|
}
|
|
400
443
|
if (!users.length)
|
|
401
|
-
throw
|
|
444
|
+
throw imValidationError('members_empty', '新群主解析失败');
|
|
402
445
|
info = { host: users[0] };
|
|
403
446
|
}
|
|
404
447
|
else if (field === 'history' || field === 'display' || field === 'msgSetting' || field === 'mark') {
|
|
@@ -425,7 +468,7 @@ const groupRenamePair = makeWritePair({
|
|
|
425
468
|
method: 'POST',
|
|
426
469
|
path: '/api/em/msg/executeIm/group/modifyGroupInfo',
|
|
427
470
|
body: { data: plan.data },
|
|
428
|
-
operation: '
|
|
471
|
+
operation: 'im.group.rename',
|
|
429
472
|
});
|
|
430
473
|
assertImOk(res, '修改群信息');
|
|
431
474
|
return { groupId: plan.groupId };
|
|
@@ -433,21 +476,21 @@ const groupRenamePair = makeWritePair({
|
|
|
433
476
|
});
|
|
434
477
|
// ── 群公告 增改删 ──
|
|
435
478
|
const groupAnnounceModifyPair = makeWritePair({
|
|
436
|
-
action: '
|
|
479
|
+
action: 'im.group.announce.modify',
|
|
437
480
|
async plan(args, env) {
|
|
438
481
|
const groupId = str(args.groupId);
|
|
439
482
|
if (!groupId || groupId === '0')
|
|
440
|
-
throw
|
|
483
|
+
throw imValidationError('id_invalid', 'groupId 必须非 0');
|
|
441
484
|
const isDel = args.del === true || String(args.del) === '1' || String(args.flag) === '1';
|
|
442
485
|
const aid = args.aid !== undefined && args.aid !== '' ? String(args.aid) : '0';
|
|
443
486
|
if (isDel && (!aid || aid === '0'))
|
|
444
|
-
throw
|
|
487
|
+
throw imValidationError('aid_required', '删除公告必须指定 aid');
|
|
445
488
|
if (!isDel) {
|
|
446
489
|
const annouce = args.annouce !== undefined ? String(args.annouce) : '';
|
|
447
490
|
if (!annouce)
|
|
448
|
-
throw
|
|
491
|
+
throw imValidationError('annouce_required', '新增/修改公告必须提供 annouce');
|
|
449
492
|
if (annouce.length > 5000)
|
|
450
|
-
throw
|
|
493
|
+
throw imValidationError('annouce_too_long', '公告内容过长');
|
|
451
494
|
}
|
|
452
495
|
const summary = { action: isDel ? '删除群公告' : (aid && aid !== '0' ? '修改群公告' : '新增群公告'), groupId, aid };
|
|
453
496
|
const snapshot = { groupId, flag: isDel ? 1 : 0, aid };
|
|
@@ -480,7 +523,7 @@ const groupAnnounceModifyPair = makeWritePair({
|
|
|
480
523
|
method: 'POST',
|
|
481
524
|
path: '/api/em/msg/executeIm/group/modifyAnnouce',
|
|
482
525
|
body,
|
|
483
|
-
operation: '
|
|
526
|
+
operation: 'im.group.announce.modify',
|
|
484
527
|
});
|
|
485
528
|
assertImOk(res, '修改群公告');
|
|
486
529
|
const responseAid = str(res?.data?.data?.aid ?? res?.data?.aid ?? '');
|
|
@@ -531,32 +574,36 @@ function buildWriteDefinition(name, method, summary, inputProps, pair) {
|
|
|
531
574
|
];
|
|
532
575
|
}
|
|
533
576
|
const groupWriteOps = [
|
|
534
|
-
...buildWriteDefinition('
|
|
577
|
+
...buildWriteDefinition('im.group.create', 'group.create', '创建普通群', {
|
|
535
578
|
users: { type: 'array', items: { type: 'string' }, description: '群成员 uid(逗号串或数组)' },
|
|
536
579
|
name: { type: 'string', description: '群名(缺省自动生成)' },
|
|
537
580
|
names: { type: 'array', items: { type: 'string' }, description: '与 users 顺序对应姓名(用于默认群名)' },
|
|
538
581
|
admins: { type: 'array', items: { type: 'string' }, description: '管理员 uid' },
|
|
539
582
|
}, groupCreatePair),
|
|
540
|
-
...buildWriteDefinition('
|
|
583
|
+
...buildWriteDefinition('im.group.invite', 'group.invite', '邀请成员入群', {
|
|
541
584
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
542
585
|
users: { type: 'array', items: { type: 'string' }, description: '被邀请 uid' },
|
|
543
586
|
}, groupInvitePair),
|
|
544
|
-
...buildWriteDefinition('
|
|
587
|
+
...buildWriteDefinition('im.group.kick', 'group.kick', '踢出群成员', {
|
|
545
588
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
546
589
|
users: { type: 'array', items: { type: 'string' }, description: '被踢 uid' },
|
|
547
590
|
}, groupKickPair),
|
|
548
|
-
...buildWriteDefinition('
|
|
591
|
+
...buildWriteDefinition('im.group.exit', 'group.exit', '退出群聊', {
|
|
549
592
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
593
|
+
acknowledgeOrphanRisk: {
|
|
594
|
+
type: 'boolean',
|
|
595
|
+
description: '群主或最后一个成员退群时必须显式传 true(退群后该群会变成无主空群,无法再 join/destroy)',
|
|
596
|
+
},
|
|
550
597
|
}, groupExitPair),
|
|
551
|
-
...buildWriteDefinition('
|
|
598
|
+
...buildWriteDefinition('im.group.destroy', 'group.destroy', '解散群聊(仅群主,不可恢复)', {
|
|
552
599
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
553
600
|
}, groupDestroyPair),
|
|
554
|
-
...buildWriteDefinition('
|
|
601
|
+
...buildWriteDefinition('im.group.join', 'group.join', '申请加入群聊', {
|
|
555
602
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id(token 二选一)' },
|
|
556
603
|
token: { type: 'string', description: '群 token/二维码加入' },
|
|
557
604
|
mark: { type: 'string', maxLength: 100, description: '申请理由' },
|
|
558
605
|
}, groupJoinPair),
|
|
559
|
-
...buildWriteDefinition('
|
|
606
|
+
...buildWriteDefinition('im.group.modify', 'group.modify', '批量修改群属性(batModGroupInfo)', {
|
|
560
607
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
561
608
|
num: { type: 'integer', description: '最大人数' },
|
|
562
609
|
switchs: { type: 'string', description: '群开关(flag:onoff 逗号分隔,如 1:1,8:0)' },
|
|
@@ -568,7 +615,7 @@ const groupWriteOps = [
|
|
|
568
615
|
copyGroupids: { type: 'array', items: { type: 'string' }, description: '拷贝群列表' },
|
|
569
616
|
gtId: { type: 'string', description: '关联群类型 id' },
|
|
570
617
|
}, groupModifyPair),
|
|
571
|
-
...buildWriteDefinition('
|
|
618
|
+
...buildWriteDefinition('im.group.rename', 'group.rename', '修改群信息(一次一项:群名/历史/群主/显示/打扰/标记/gtid)', {
|
|
572
619
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
573
620
|
name: { type: 'string', description: '改群名' },
|
|
574
621
|
history: { type: 'integer', description: '新人历史消息范围' },
|
|
@@ -578,7 +625,7 @@ const groupWriteOps = [
|
|
|
578
625
|
mark: { type: 'integer', description: '标记位' },
|
|
579
626
|
gtid: { type: 'string', description: 'GTID' },
|
|
580
627
|
}, groupRenamePair),
|
|
581
|
-
...buildWriteDefinition('
|
|
628
|
+
...buildWriteDefinition('im.group.announce.modify', 'group.announce.modify', '新增/修改/删除群公告', {
|
|
582
629
|
groupId: { type: 'string', pattern: '^\\d+$', description: '群 id' },
|
|
583
630
|
annouce: { type: 'string', description: '公告内容(删除时不需要)' },
|
|
584
631
|
aid: { type: 'string', description: '公告 id(0/不传=新增;非 0=修改;删除必填)' },
|