sophhub 0.4.10 → 0.4.11
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/agents/intern-admin/.config.json +8 -0
- package/agents/intern-admin/scripts/init_workspace.sh +27 -0
- package/package.json +1 -1
- package/skills/claw-agent-get-send/skill.json +14 -3
- package/skills/claw-agent-get-send/src/SKILL.md +23 -52
- package/skills/claw-agent-get-send/src/pyproject.toml +1 -1
- package/skills/claw-agent-get-send/src/scripts/appia_claw.py +111 -19
- package/skills/claw-agent-get-send/src/reference-http.md +0 -149
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
"bot_api_enabled": false,
|
|
6
6
|
"workspace": "/home/node/.openclaw/workspace-intern",
|
|
7
7
|
"agent_dependencies": [],
|
|
8
|
+
"post_install": [
|
|
9
|
+
{
|
|
10
|
+
"name": "init-workspace",
|
|
11
|
+
"script": "scripts/init_workspace.sh",
|
|
12
|
+
"args": [],
|
|
13
|
+
"delete_on_success": true
|
|
14
|
+
}
|
|
15
|
+
],
|
|
8
16
|
"tools": {
|
|
9
17
|
"deny": [
|
|
10
18
|
"message",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# intern-admin workspace:创建 knowledge/ 树与 memory/;INDEX.md、FAQ.md 由 Agent 按需创建。
|
|
3
|
+
# 须先于 intern-qa 完成,以便其 setup_links.sh 将 knowledge/ 链到本工作区上级同名目录。
|
|
4
|
+
#
|
|
5
|
+
# 用法:安装器 post_install 调用;或:
|
|
6
|
+
# WORKSPACE=/path/to/workspace-intern ./scripts/init_workspace.sh
|
|
7
|
+
# OPENCLAW_WORKSPACE=... ./scripts/init_workspace.sh
|
|
8
|
+
# ./scripts/init_workspace.sh /path/to/workspace-intern
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
WS="${WORKSPACE:-${OPENCLAW_WORKSPACE:-${1:-}}}"
|
|
13
|
+
if [[ -z "$WS" ]]; then
|
|
14
|
+
echo "init_workspace: set WORKSPACE or OPENCLAW_WORKSPACE, or pass workspace root as first arg." >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
WS="$(cd "$WS" && pwd)"
|
|
19
|
+
|
|
20
|
+
# 一次 mkdir -p 建齐 BOOTSTRAP 约定路径(父目录会自动创建,不必逐条拆开写)
|
|
21
|
+
mkdir -p \
|
|
22
|
+
"$WS/knowledge/images/"{media,emf_raw} \
|
|
23
|
+
"$WS/knowledge/attachments" \
|
|
24
|
+
"$WS/knowledge/archive" \
|
|
25
|
+
"$WS/memory"
|
|
26
|
+
|
|
27
|
+
echo "init_workspace: knowledge/ + memory/ ready under ${WS}"
|
package/package.json
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claw-agent-get-send",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"types": ["store"],
|
|
5
5
|
"displayName": "Claw Agent Get/Send",
|
|
6
|
-
"description": "Appia claw.agent.groups.get / claw.agent.message.send
|
|
6
|
+
"description": "Appia(IM 即时通讯)侧 claw.agent.groups.get / claw.agent.message.send:查询机器人在哪些群聊、向群发纯文本或 Markdown;脚本封装 + HTTP/curl 参考(JWT、msg/md、错误码)",
|
|
7
7
|
"changelog": [
|
|
8
|
+
{
|
|
9
|
+
"version": "1.0.1",
|
|
10
|
+
"date": "2026-05-09",
|
|
11
|
+
"changes": [
|
|
12
|
+
"appia_claw.py:--cred-file / -c,JSON 凭证优先于环境变量;groups.get / message.send 必填 userId;默认 Base URL",
|
|
13
|
+
"reference-http.md 迁至技能根目录;仓库 .gitignore 忽略 **/.secrets/",
|
|
14
|
+
"SKILL.md:bot-secret 式分步用法;description 标明 IM 群聊场景与触发词",
|
|
15
|
+
"docs/claw-agent-get-send.md:凭证说明、常用示例、与 skill 一致的 IM 表述",
|
|
16
|
+
"skill.json store description 与 IM 语境对齐"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
8
19
|
{
|
|
9
20
|
"version": "1.0.0",
|
|
10
21
|
"date": "2026-04-28",
|
|
@@ -17,5 +28,5 @@
|
|
|
17
28
|
}
|
|
18
29
|
],
|
|
19
30
|
"createdAt": "2026-04-28",
|
|
20
|
-
"updatedAt": "2026-
|
|
31
|
+
"updatedAt": "2026-05-09"
|
|
21
32
|
}
|
|
@@ -1,72 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claw-agent-get-send
|
|
3
|
-
description:
|
|
4
|
-
Call Appia Claw HTTP APIs (agent.groups.get, agent.message.send) using env vars or curl.
|
|
5
|
-
Use when querying bot group memberships, verifying rid+group name, sending plaintext or Markdown AST to Appia rooms,
|
|
6
|
-
or when the user needs JWT auth rules, curl examples, or md payload shape for agent.message.send.
|
|
3
|
+
description: On Appia (an IM / team chat platform), list which group chats the bot agent is in (rid + name), or send plaintext or Markdown to a group via Appia Claw. Use when the user asks for group list / 群列表 / Appia 群聊 / rid, or to message a group / 向群发消息 / IM 通知;claw、agent.groups.get、agent.message.send。
|
|
7
4
|
---
|
|
8
5
|
|
|
9
|
-
#
|
|
6
|
+
# Appia 即时通讯(IM)· Claw 群列表与发消息
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
## 环境变量
|
|
14
|
-
|
|
15
|
-
| 变量 | 必填 | 说明 |
|
|
16
|
-
|------|------|------|
|
|
17
|
-
| `APPIA_BASE_URL` | 是 | 根地址,例如 `https://sophgo.appia.cn` |
|
|
18
|
-
| `CLAW_JWT` | 是 | `Authorization: Bearer` 的值 |
|
|
19
|
-
| `APP_AGENT_ID` | 是 | SophClaw 机器人 Agent ID(与 `AGENT_ID` 二选一) |
|
|
20
|
-
| `TARGET_RID` | send / verify | 目标群 `rid` |
|
|
21
|
-
| `TARGET_GROUP_NAME` | verify | 群展示名,`verify-target` 双因子校验用 |
|
|
22
|
-
| `TARGET_GROUP_MATCH` | 否 | `exact`(默认)或 `contains` |
|
|
23
|
-
|
|
24
|
-
| 可选 | 说明 |
|
|
25
|
-
|------|------|
|
|
26
|
-
| `APPIA_MSG_CHUNK_SIZE` | 单条 `msg` 最大字符数,超出则拆分多次发送(默认 3500) |
|
|
8
|
+
Appia 为 IM(即时通讯)工具中的群聊/会话场景。脚本 `{baseDir}/scripts/appia_claw.py` 通过 Claw HTTP API 拉取机器人所在群并发消息。`{baseDir}` 与本 `SKILL.md` 同级(安装根下一般有 `scripts/`)。
|
|
27
9
|
|
|
28
10
|
## 用法
|
|
29
11
|
|
|
30
|
-
`{baseDir}` 为与本 `SKILL.md` 同级的安装根(通常含 `scripts/`)。
|
|
31
|
-
|
|
32
|
-
### 查询群列表
|
|
33
|
-
|
|
34
12
|
```bash
|
|
35
|
-
|
|
36
|
-
```
|
|
13
|
+
# 子命令前可加:--cred-file /path/to.cred.json(或 -c)、--timeout 秒;凭证与路径示例见 docs/claw-agent-get-send.md
|
|
37
14
|
|
|
38
|
-
|
|
15
|
+
# 1. 列出当前 agent 已加入的群(rid、群名)
|
|
16
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json groups
|
|
39
17
|
|
|
40
|
-
|
|
41
|
-
uv run {baseDir}/scripts/appia_claw.py
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 发送纯文本(支持分段)
|
|
18
|
+
# 2. 同上,输出接口原始 JSON(便于复制 rid)
|
|
19
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json groups --json
|
|
45
20
|
|
|
46
|
-
|
|
47
|
-
uv run {baseDir}/scripts/appia_claw.py
|
|
48
|
-
uv run {baseDir}/scripts/appia_claw.py send --text "hello" --rid <rid>
|
|
49
|
-
```
|
|
21
|
+
# 3. 双因子校验:凭证或环境里已配置 TARGET_RID、TARGET_GROUP_NAME
|
|
22
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json verify-target
|
|
50
23
|
|
|
51
|
-
|
|
24
|
+
# 4. 向指定 rid 发纯文本(--rid 可换成本地凭证里的 target_rid)
|
|
25
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send --text "正文" --rid "<rid>"
|
|
52
26
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
uv run {baseDir}/scripts/appia_claw.py send-md --md-json '[{"type":"PARAGRAPH","value":[{"type":"PLAIN_TEXT","value":"**粗体** 文本"}]}]'
|
|
27
|
+
# 5. 长文本从 UTF-8 文件发送
|
|
28
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send --file /tmp/body.txt --rid "<rid>"
|
|
56
29
|
|
|
57
|
-
#
|
|
58
|
-
uv run {baseDir}/scripts/appia_claw.py
|
|
30
|
+
# 6. 发 Markdown AST:一段纯文本自动包成 md
|
|
31
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send-md --text "正文" --rid "<rid>"
|
|
59
32
|
|
|
60
|
-
#
|
|
61
|
-
uv run {baseDir}/scripts/appia_claw.py send-md --
|
|
33
|
+
# 7. 从 JSON 文件读 md 数组
|
|
34
|
+
uv run {baseDir}/scripts/appia_claw.py --cred-file /path/to.cred.json send-md --md-file /tmp/md.json --rid "<rid>"
|
|
62
35
|
```
|
|
63
36
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- `send` 命令使用 **`msg` 纯文本**;极长正文按 `APPIA_MSG_CHUNK_SIZE` **分多条**发送。
|
|
67
|
-
- `send-md` 命令使用 **`md` Markdown AST** 结构,支持 Rocket.Chat 富文本格式。
|
|
68
|
-
- 请避免与业务侧「同 Sender 并发冲突」同时使用(串行调用本脚本即可)。
|
|
37
|
+
不用凭证文件时,可在环境中设置 `CLAW_JWT`、`APP_AGENT_ID`、`CLAW_USER_ID`(及场景需要的 `TARGET_RID` 等),命令相同,省略 `--cred-file …` 即可。
|
|
69
38
|
|
|
70
|
-
##
|
|
39
|
+
## 注意事项
|
|
71
40
|
|
|
72
|
-
|
|
41
|
+
- 最小凭证字段(JSON 或环境变量):JWT、机器人 `agentId`、创建者 `userId`;勿将填好真实值的文件提交 Git。
|
|
42
|
+
- 未设置 `APPIA_BASE_URL` 时,脚本默认 `https://sophgo.appia.cn`。
|
|
43
|
+
- HTTP / curl / 错误码:技能根目录 **`../reference-http.md`**(相对本文件)。
|
|
@@ -11,6 +11,56 @@ from typing import Any
|
|
|
11
11
|
from urllib import error, parse, request
|
|
12
12
|
|
|
13
13
|
DEFAULT_CHUNK_SIZE = 3500
|
|
14
|
+
# 未配置 APPIA_BASE_URL / APPIA_HOST(凭证与环境均无)时使用。
|
|
15
|
+
DEFAULT_APPIA_BASE_URL = "https://sophgo.appia.cn"
|
|
16
|
+
|
|
17
|
+
# 凭证文件解析后的规范字段(键)→ 值;未使用凭证文件时为 None。
|
|
18
|
+
_CRED: dict[str, str] | None = None
|
|
19
|
+
|
|
20
|
+
# (规范 env 名, JSON / 文件中允许的别名)。文件中同一逻辑字段任一别名非空即可写入规范键。
|
|
21
|
+
_CRED_CANON_ALIASES: tuple[tuple[str, tuple[str, ...]], ...] = (
|
|
22
|
+
("APPIA_BASE_URL", ("APPIA_BASE_URL", "appia_base_url", "base_url", "APPIA_HOST", "appia_host")),
|
|
23
|
+
("CLAW_JWT", ("CLAW_JWT", "claw_jwt", "jwt", "APPIA_CLAW_JWT", "appia_claw_jwt")),
|
|
24
|
+
("APP_AGENT_ID", ("APP_AGENT_ID", "app_agent_id", "agent_id", "AGENT_ID")),
|
|
25
|
+
("CLAW_USER_ID", ("CLAW_USER_ID", "claw_user_id", "user_id", "USER_ID", "creator_user_id", "APPIA_USER_ID", "appia_user_id")),
|
|
26
|
+
("TARGET_RID", ("TARGET_RID", "target_rid")),
|
|
27
|
+
("TARGET_GROUP_NAME", ("TARGET_GROUP_NAME", "target_group_name")),
|
|
28
|
+
("TARGET_GROUP_MATCH", ("TARGET_GROUP_MATCH", "target_group_match")),
|
|
29
|
+
("APPIA_MSG_CHUNK_SIZE", ("APPIA_MSG_CHUNK_SIZE", "appia_msg_chunk_size")),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _scalar_from_raw(raw: dict[str, Any], aliases: tuple[str, ...]) -> str:
|
|
34
|
+
for key in aliases:
|
|
35
|
+
if key not in raw:
|
|
36
|
+
continue
|
|
37
|
+
val = raw[key]
|
|
38
|
+
if val is None:
|
|
39
|
+
continue
|
|
40
|
+
s = str(val).strip()
|
|
41
|
+
if s:
|
|
42
|
+
return s
|
|
43
|
+
return ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def load_cred_file(path: str) -> dict[str, str]:
|
|
47
|
+
exp = os.path.expanduser(path)
|
|
48
|
+
try:
|
|
49
|
+
with open(exp, encoding="utf-8") as f:
|
|
50
|
+
raw_any: Any = json.load(f)
|
|
51
|
+
except OSError as exc:
|
|
52
|
+
sys.exit(f"无法读取凭证文件 {exp}: {exc}")
|
|
53
|
+
except json.JSONDecodeError as exc:
|
|
54
|
+
sys.exit(f"凭证文件 JSON 无效 {exp}: {exc}")
|
|
55
|
+
if not isinstance(raw_any, dict):
|
|
56
|
+
sys.exit("凭证文件顶层必须是 JSON 对象")
|
|
57
|
+
raw = raw_any
|
|
58
|
+
out: dict[str, str] = {}
|
|
59
|
+
for canon, aliases in _CRED_CANON_ALIASES:
|
|
60
|
+
v = _scalar_from_raw(raw, aliases)
|
|
61
|
+
if v:
|
|
62
|
+
out[canon] = v
|
|
63
|
+
return out
|
|
14
64
|
|
|
15
65
|
|
|
16
66
|
def _env(name: str, *fallbacks: str) -> str:
|
|
@@ -21,31 +71,51 @@ def _env(name: str, *fallbacks: str) -> str:
|
|
|
21
71
|
return ""
|
|
22
72
|
|
|
23
73
|
|
|
74
|
+
def _cfg(canon: str, *env_fallbacks: str) -> str:
|
|
75
|
+
"""凭证文件中非空字段优先,否则回退到环境变量(与原先 _env 链一致)。"""
|
|
76
|
+
if _CRED:
|
|
77
|
+
v = _CRED.get(canon, "").strip()
|
|
78
|
+
if v:
|
|
79
|
+
return v
|
|
80
|
+
return _env(canon, *env_fallbacks)
|
|
81
|
+
|
|
82
|
+
|
|
24
83
|
def _base_url() -> str:
|
|
25
|
-
base =
|
|
84
|
+
base = _cfg("APPIA_BASE_URL", "APPIA_HOST").rstrip("/")
|
|
26
85
|
if not base:
|
|
27
|
-
|
|
86
|
+
base = DEFAULT_APPIA_BASE_URL.rstrip("/")
|
|
28
87
|
return base
|
|
29
88
|
|
|
30
89
|
|
|
31
90
|
def _jwt() -> str:
|
|
32
|
-
tok =
|
|
91
|
+
tok = _cfg("CLAW_JWT", "APPIA_CLAW_JWT")
|
|
33
92
|
if not tok:
|
|
34
|
-
sys.exit("
|
|
93
|
+
sys.exit("缺少配置:凭证文件或环境变量 CLAW_JWT(或 APPIA_CLAW_JWT)")
|
|
35
94
|
return tok
|
|
36
95
|
|
|
37
96
|
|
|
38
97
|
def _agent_id() -> str:
|
|
39
|
-
aid =
|
|
98
|
+
aid = _cfg("APP_AGENT_ID", "AGENT_ID")
|
|
40
99
|
if not aid:
|
|
41
|
-
sys.exit("
|
|
100
|
+
sys.exit("缺少配置:凭证文件或环境变量 APP_AGENT_ID(或 AGENT_ID)")
|
|
42
101
|
return aid
|
|
43
102
|
|
|
44
103
|
|
|
104
|
+
def _user_id() -> str:
|
|
105
|
+
"""创建者 userId:agent.groups.get(query)与 agent.message.send(body)必填。"""
|
|
106
|
+
uid = _cfg("CLAW_USER_ID", "USER_ID", "APPIA_USER_ID")
|
|
107
|
+
if not uid:
|
|
108
|
+
sys.exit(
|
|
109
|
+
"缺少配置:凭证文件或环境变量 CLAW_USER_ID(或 USER_ID、APPIA_USER_ID、creator_user_id)"
|
|
110
|
+
)
|
|
111
|
+
return uid
|
|
112
|
+
|
|
113
|
+
|
|
45
114
|
def _groups_get(timeout: float) -> dict[str, Any]:
|
|
46
115
|
base = _base_url()
|
|
47
|
-
|
|
48
|
-
|
|
116
|
+
aid = _agent_id()
|
|
117
|
+
user_id = _user_id()
|
|
118
|
+
q = parse.urlencode({"agentId": aid, "userId": user_id})
|
|
49
119
|
url = f"{base}/api/v1/claw/agent.groups.get?{q}"
|
|
50
120
|
req = request.Request(
|
|
51
121
|
url,
|
|
@@ -82,12 +152,15 @@ def cmd_groups(args: argparse.Namespace) -> int:
|
|
|
82
152
|
|
|
83
153
|
|
|
84
154
|
def cmd_verify_target(args: argparse.Namespace) -> int:
|
|
85
|
-
rid =
|
|
86
|
-
name =
|
|
155
|
+
rid = _cfg("TARGET_RID")
|
|
156
|
+
name = _cfg("TARGET_GROUP_NAME")
|
|
87
157
|
if not rid or not name:
|
|
88
|
-
print(
|
|
158
|
+
print(
|
|
159
|
+
"⚠️ verify-target 需要 TARGET_RID 与 TARGET_GROUP_NAME(凭证文件或环境变量)",
|
|
160
|
+
file=sys.stderr,
|
|
161
|
+
)
|
|
89
162
|
return 1
|
|
90
|
-
mode = (
|
|
163
|
+
mode = (_cfg("TARGET_GROUP_MATCH") or "exact").lower()
|
|
91
164
|
if mode not in ("exact", "contains"):
|
|
92
165
|
mode = "exact"
|
|
93
166
|
data = _groups_get(args.timeout)
|
|
@@ -146,15 +219,25 @@ def _send_request(body_obj: dict[str, Any], timeout: float) -> dict[str, Any]:
|
|
|
146
219
|
|
|
147
220
|
def _send_plain(rid: str, msg: str, timeout: float) -> dict[str, Any]:
|
|
148
221
|
"""发送纯文本消息。"""
|
|
149
|
-
|
|
150
|
-
body_obj: dict[str, Any] = {
|
|
222
|
+
aid = _agent_id()
|
|
223
|
+
body_obj: dict[str, Any] = {
|
|
224
|
+
"rid": rid,
|
|
225
|
+
"agentId": aid,
|
|
226
|
+
"userId": _user_id(),
|
|
227
|
+
"msg": msg,
|
|
228
|
+
}
|
|
151
229
|
return _send_request(body_obj, timeout)
|
|
152
230
|
|
|
153
231
|
|
|
154
232
|
def _send_md(rid: str, md: list[dict[str, Any]], timeout: float) -> dict[str, Any]:
|
|
155
233
|
"""发送 Markdown AST 消息。"""
|
|
156
|
-
|
|
157
|
-
body_obj: dict[str, Any] = {
|
|
234
|
+
aid = _agent_id()
|
|
235
|
+
body_obj: dict[str, Any] = {
|
|
236
|
+
"rid": rid,
|
|
237
|
+
"agentId": aid,
|
|
238
|
+
"userId": _user_id(),
|
|
239
|
+
"md": md,
|
|
240
|
+
}
|
|
158
241
|
return _send_request(body_obj, timeout)
|
|
159
242
|
|
|
160
243
|
|
|
@@ -168,7 +251,7 @@ def _chunk_text(text: str, size: int) -> list[str]:
|
|
|
168
251
|
|
|
169
252
|
def cmd_send(args: argparse.Namespace) -> int:
|
|
170
253
|
"""发送纯文本消息(支持分段)。"""
|
|
171
|
-
rid = args.rid or
|
|
254
|
+
rid = args.rid or _cfg("TARGET_RID")
|
|
172
255
|
if not rid:
|
|
173
256
|
print("⚠️ send 需要 TARGET_RID 或 --rid", file=sys.stderr)
|
|
174
257
|
return 1
|
|
@@ -182,7 +265,7 @@ def cmd_send(args: argparse.Namespace) -> int:
|
|
|
182
265
|
else:
|
|
183
266
|
print("⚠️ 需要 --file 或 --text", file=sys.stderr)
|
|
184
267
|
return 1
|
|
185
|
-
chunk_raw =
|
|
268
|
+
chunk_raw = _cfg("APPIA_MSG_CHUNK_SIZE")
|
|
186
269
|
chunk_size = int(chunk_raw) if chunk_raw else DEFAULT_CHUNK_SIZE
|
|
187
270
|
parts = _chunk_text(text, chunk_size)
|
|
188
271
|
for idx, part in enumerate(parts):
|
|
@@ -198,7 +281,7 @@ def cmd_send(args: argparse.Namespace) -> int:
|
|
|
198
281
|
|
|
199
282
|
def cmd_send_md(args: argparse.Namespace) -> int:
|
|
200
283
|
"""发送 Markdown AST 消息。"""
|
|
201
|
-
rid = args.rid or
|
|
284
|
+
rid = args.rid or _cfg("TARGET_RID")
|
|
202
285
|
if not rid:
|
|
203
286
|
print("⚠️ send-md 需要 TARGET_RID 或 --rid", file=sys.stderr)
|
|
204
287
|
return 1
|
|
@@ -246,8 +329,15 @@ def cmd_send_md(args: argparse.Namespace) -> int:
|
|
|
246
329
|
|
|
247
330
|
|
|
248
331
|
def main() -> int:
|
|
332
|
+
global _CRED
|
|
249
333
|
p = argparse.ArgumentParser(description="Appia Claw (groups.get / message.send)")
|
|
250
334
|
p.add_argument("--timeout", type=float, default=60.0)
|
|
335
|
+
p.add_argument(
|
|
336
|
+
"--cred-file",
|
|
337
|
+
"-c",
|
|
338
|
+
metavar="PATH",
|
|
339
|
+
help="JSON 凭证文件(含 Appia 地址、JWT、agentId 等);文件中非空字段优先于环境变量",
|
|
340
|
+
)
|
|
251
341
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
252
342
|
|
|
253
343
|
gp = sub.add_parser("groups", help="GET agent.groups.get")
|
|
@@ -273,6 +363,8 @@ def main() -> int:
|
|
|
273
363
|
smp.set_defaults(_run=cmd_send_md)
|
|
274
364
|
|
|
275
365
|
args = p.parse_args()
|
|
366
|
+
if args.cred_file:
|
|
367
|
+
_CRED = load_cred_file(args.cred_file)
|
|
276
368
|
runner = getattr(args, "_run", None)
|
|
277
369
|
if runner is None:
|
|
278
370
|
return 1
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
# Claw HTTP API(curl 参考)
|
|
2
|
-
|
|
3
|
-
与仓库根目录 `claw.agent.groups.get.curl.md`、`claw.agent.message.send.curl.md` 同源;集成在 skill 内便于离线查阅。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## `GET /api/v1/claw/agent.groups.get`
|
|
8
|
-
|
|
9
|
-
用于按 `agentId` 查询该机器人所在的所有群聊,返回 `rid` 和群名称。
|
|
10
|
-
|
|
11
|
-
### 鉴权方式
|
|
12
|
-
|
|
13
|
-
与 `mcpToDos` 一致:
|
|
14
|
-
|
|
15
|
-
- `authRequired: false`
|
|
16
|
-
- 通过请求头 `Authorization: Bearer <JWT>` 校验
|
|
17
|
-
- 校验规则受以下设置控制:
|
|
18
|
-
- `Appia_Antagent_JWT_Enable`
|
|
19
|
-
- `APPIA_JWT_SECRET`
|
|
20
|
-
|
|
21
|
-
### 请求示例
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
curl -sS -G 'https://YOUR_ROCKETCHAT_HOST/api/v1/claw/agent.groups.get' \
|
|
25
|
-
-H 'Authorization: Bearer YOUR_MCP_JWT_TOKEN' \
|
|
26
|
-
--data-urlencode 'agentId=AGENT_BOT_USER_ID'
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### 参数
|
|
30
|
-
|
|
31
|
-
- `agentId`(query,必填):OpenClaw 侧 Agent ID(会映射到机器人用户)
|
|
32
|
-
|
|
33
|
-
### 成功返回示例
|
|
34
|
-
|
|
35
|
-
```json
|
|
36
|
-
{
|
|
37
|
-
"success": true,
|
|
38
|
-
"data": {
|
|
39
|
-
"agentId": "AGENT_BOT_USER_ID",
|
|
40
|
-
"groups": [
|
|
41
|
-
{
|
|
42
|
-
"rid": "ROOM_ID_1",
|
|
43
|
-
"name": "群聊A"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"rid": "ROOM_ID_2",
|
|
47
|
-
"name": "群聊B"
|
|
48
|
-
}
|
|
49
|
-
],
|
|
50
|
-
"total": 2
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 常见失败
|
|
56
|
-
|
|
57
|
-
- `{"success":false,"message":"401"}`:JWT 无效或过期
|
|
58
|
-
- `{"success":false,"message":"agentId is required"}`:缺少 `agentId`
|
|
59
|
-
- `{"success":false,"message":"agent not found by agentId"}`:找不到对应 agentId 的机器人
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## `POST /api/v1/claw/agent.message.send`
|
|
64
|
-
|
|
65
|
-
用于根据 `rid + agentId` 向指定群聊发送消息。
|
|
66
|
-
|
|
67
|
-
接口会校验:
|
|
68
|
-
|
|
69
|
-
1. `rid` 对应房间存在
|
|
70
|
-
2. `agentId` 对应用户存在、为 bot、且 active
|
|
71
|
-
3. 该 `agentId` 确实在该 `rid` 的订阅列表里
|
|
72
|
-
|
|
73
|
-
满足后,走后端正常发消息逻辑(`executeSendMessage`)。
|
|
74
|
-
|
|
75
|
-
### 鉴权方式
|
|
76
|
-
|
|
77
|
-
与 `mcpToDos` 一致:
|
|
78
|
-
|
|
79
|
-
- `authRequired: false`
|
|
80
|
-
- 通过请求头 `Authorization: Bearer <JWT>` 校验
|
|
81
|
-
- 校验规则受以下设置控制:
|
|
82
|
-
- `Appia_Antagent_JWT_Enable`
|
|
83
|
-
- `APPIA_JWT_SECRET`
|
|
84
|
-
|
|
85
|
-
### 请求示例
|
|
86
|
-
|
|
87
|
-
#### 示例一:发送纯文本
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
curl -sS -X POST 'https://YOUR_ROCKETCHAT_HOST/api/v1/claw/agent.message.send' \
|
|
91
|
-
-H 'Content-Type: application/json' \
|
|
92
|
-
-H 'Authorization: Bearer YOUR_MCP_JWT_TOKEN' \
|
|
93
|
-
-d '{
|
|
94
|
-
"rid": "TARGET_ROOM_RID",
|
|
95
|
-
"agentId": "AGENT_BOT_USER_ID",
|
|
96
|
-
"msg": "这是一条由 Claw 机器人发送的消息"
|
|
97
|
-
}'
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
#### 示例二:发送 Markdown(`md`)
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
curl -sS -X POST 'https://YOUR_ROCKETCHAT_HOST/api/v1/claw/agent.message.send' \
|
|
104
|
-
-H 'Content-Type: application/json' \
|
|
105
|
-
-H 'Authorization: Bearer YOUR_MCP_JWT_TOKEN' \
|
|
106
|
-
-d '{
|
|
107
|
-
"rid": "TARGET_ROOM_RID",
|
|
108
|
-
"agentId": "OPENCLAW_AGENT_ID",
|
|
109
|
-
"md": [
|
|
110
|
-
{
|
|
111
|
-
"type": "PARAGRAPH",
|
|
112
|
-
"value": [
|
|
113
|
-
{ "type": "PLAIN_TEXT", "value": "这是一条 *Markdown* 消息" }
|
|
114
|
-
]
|
|
115
|
-
}
|
|
116
|
-
]
|
|
117
|
-
}'
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### 参数
|
|
121
|
-
|
|
122
|
-
- `rid`(body,必填):目标群聊房间 ID
|
|
123
|
-
- `agentId`(body,必填):OpenClaw 侧 Agent ID(会映射到机器人用户)
|
|
124
|
-
- `msg`(body,可选):消息文本
|
|
125
|
-
- `md`(body,可选):Markdown AST(Rocket.Chat `md` 结构)
|
|
126
|
-
- 约束:`msg` 和 `md` 至少提供一个
|
|
127
|
-
|
|
128
|
-
### 成功返回示例
|
|
129
|
-
|
|
130
|
-
```json
|
|
131
|
-
{
|
|
132
|
-
"success": true,
|
|
133
|
-
"data": {
|
|
134
|
-
"rid": "TARGET_ROOM_RID",
|
|
135
|
-
"agentId": "AGENT_BOT_USER_ID",
|
|
136
|
-
"status": "sent"
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### 常见失败
|
|
142
|
-
|
|
143
|
-
- `{"success":false,"message":"401"}`:JWT 无效或过期
|
|
144
|
-
- `{"success":false,"message":"rid and agentId are required, and either msg or md must be provided"}`:参数缺失
|
|
145
|
-
- `{"success":false,"message":"room not found"}`:房间不存在
|
|
146
|
-
- `{"success":false,"message":"agent not found by agentId"}`:找不到对应 agentId 的机器人
|
|
147
|
-
- `{"success":false,"message":"agent must be a bot user"}`:agent 不是 bot
|
|
148
|
-
- `{"success":false,"message":"agent is inactive"}`:机器人未激活
|
|
149
|
-
- `{"success":false,"message":"agent is not in this room"}`:机器人不在该群里
|