mchat-client 0.1.0__tar.gz

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.
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: mchat-client
3
+ Version: 0.1.0
4
+ Summary: MChat Python 客户端 SDK
5
+ Classifier: Development Status :: 4 - Beta
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Communications :: Chat
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: paho-mqtt>=2.0.0
16
+ Provides-Extra: dev
17
+
18
+ # MChat Python 客户端
19
+
20
+ Python 版 MChat 客户端 SDK,与《技术设计方案》及《消息交互接口与示例》一致。封装 MQTT 连接、请求-响应、收件箱/群消息订阅与事件。
21
+
22
+ ## 要求
23
+
24
+ - Python >= 3.10
25
+ - 依赖:paho-mqtt >= 2.0.0
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ cd client/python
31
+ pip install -e .
32
+ # 或从项目外:pip install -e /path/to/MChat/client/python
33
+ ```
34
+
35
+ ## 连接参数
36
+
37
+ 与 `employee.create` 返回的 `mqtt_connection` 对应,构造 `MChatClient` 时传入:
38
+
39
+ - `broker_host` / `broker_port` / `use_tls`
40
+ - `username`(如 employee_id)/ `password`
41
+ - `employee_id`:当前员工 ID,用于 auth.bind、收件箱订阅、在线状态
42
+ - 可选:`client_id`、`device_id`、`request_timeout_ms`、`skip_auth_bind`
43
+
44
+ ## 使用示例
45
+
46
+ ```python
47
+ from mchat_client import (
48
+ MChatClient,
49
+ send_private_message,
50
+ get_org_tree,
51
+ get_storage_config,
52
+ get_agent_capability_list,
53
+ )
54
+
55
+ client = MChatClient(
56
+ broker_host="broker.example.com",
57
+ broker_port=1883,
58
+ use_tls=False,
59
+ username="emp_zhangsan_001",
60
+ password="your_mqtt_password",
61
+ employee_id="emp_zhangsan_001",
62
+ )
63
+
64
+ client.connect()
65
+
66
+ client.on("inbox", lambda payload: print("收件箱:", payload))
67
+ client.on("group", lambda group_id, payload: print("群消息", group_id, payload))
68
+
69
+ # 发单聊
70
+ send_private_message(client, "emp_lisi_002", "你好")
71
+
72
+ # 获取组织树
73
+ tree = get_org_tree(client)
74
+ print(tree.get("data", {}).get("employees"))
75
+
76
+ # 订阅某群(需已知 group_id)
77
+ client.subscribe_group("grp_xxx")
78
+
79
+ client.disconnect()
80
+ ```
81
+
82
+ ## API 概览
83
+
84
+ - **MChatClient**
85
+ - `connect()` / `disconnect()`
86
+ - `request(action, params)`:通用请求,成功返回完整响应体(含 code、message、data),失败抛异常
87
+ - `subscribe_group(group_id)` / `unsubscribe_group(group_id)`
88
+ - `on("inbox" | "group" | "connect" | "offline" | "error", callback)`
89
+ - **便捷方法**(见 `api.py`):`send_private_message`、`send_group_message`、`get_org_tree`、`get_storage_config`、`get_agent_capability_list`
90
+
91
+ ## 示例
92
+
93
+ 同目录下 **example/** 为可运行示例(连接、拉取组织架构与 Agent、收件箱/群消息、可选发测试消息)。详见 [example/README.md](example/README.md)。
94
+
95
+ ## 发布到 PyPI
96
+
97
+ 在 `client/python` 目录下构建并上传(需先安装 `build`、`twine`):
98
+
99
+ ```bash
100
+ cd client/python
101
+ pip install build twine
102
+ python -m build
103
+ twine upload dist/*
104
+ ```
105
+
106
+ 发布前请将 `pyproject.toml` 中的 `version` 更新为待发布版本号。
@@ -0,0 +1,89 @@
1
+ # MChat Python 客户端
2
+
3
+ Python 版 MChat 客户端 SDK,与《技术设计方案》及《消息交互接口与示例》一致。封装 MQTT 连接、请求-响应、收件箱/群消息订阅与事件。
4
+
5
+ ## 要求
6
+
7
+ - Python >= 3.10
8
+ - 依赖:paho-mqtt >= 2.0.0
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ cd client/python
14
+ pip install -e .
15
+ # 或从项目外:pip install -e /path/to/MChat/client/python
16
+ ```
17
+
18
+ ## 连接参数
19
+
20
+ 与 `employee.create` 返回的 `mqtt_connection` 对应,构造 `MChatClient` 时传入:
21
+
22
+ - `broker_host` / `broker_port` / `use_tls`
23
+ - `username`(如 employee_id)/ `password`
24
+ - `employee_id`:当前员工 ID,用于 auth.bind、收件箱订阅、在线状态
25
+ - 可选:`client_id`、`device_id`、`request_timeout_ms`、`skip_auth_bind`
26
+
27
+ ## 使用示例
28
+
29
+ ```python
30
+ from mchat_client import (
31
+ MChatClient,
32
+ send_private_message,
33
+ get_org_tree,
34
+ get_storage_config,
35
+ get_agent_capability_list,
36
+ )
37
+
38
+ client = MChatClient(
39
+ broker_host="broker.example.com",
40
+ broker_port=1883,
41
+ use_tls=False,
42
+ username="emp_zhangsan_001",
43
+ password="your_mqtt_password",
44
+ employee_id="emp_zhangsan_001",
45
+ )
46
+
47
+ client.connect()
48
+
49
+ client.on("inbox", lambda payload: print("收件箱:", payload))
50
+ client.on("group", lambda group_id, payload: print("群消息", group_id, payload))
51
+
52
+ # 发单聊
53
+ send_private_message(client, "emp_lisi_002", "你好")
54
+
55
+ # 获取组织树
56
+ tree = get_org_tree(client)
57
+ print(tree.get("data", {}).get("employees"))
58
+
59
+ # 订阅某群(需已知 group_id)
60
+ client.subscribe_group("grp_xxx")
61
+
62
+ client.disconnect()
63
+ ```
64
+
65
+ ## API 概览
66
+
67
+ - **MChatClient**
68
+ - `connect()` / `disconnect()`
69
+ - `request(action, params)`:通用请求,成功返回完整响应体(含 code、message、data),失败抛异常
70
+ - `subscribe_group(group_id)` / `unsubscribe_group(group_id)`
71
+ - `on("inbox" | "group" | "connect" | "offline" | "error", callback)`
72
+ - **便捷方法**(见 `api.py`):`send_private_message`、`send_group_message`、`get_org_tree`、`get_storage_config`、`get_agent_capability_list`
73
+
74
+ ## 示例
75
+
76
+ 同目录下 **example/** 为可运行示例(连接、拉取组织架构与 Agent、收件箱/群消息、可选发测试消息)。详见 [example/README.md](example/README.md)。
77
+
78
+ ## 发布到 PyPI
79
+
80
+ 在 `client/python` 目录下构建并上传(需先安装 `build`、`twine`):
81
+
82
+ ```bash
83
+ cd client/python
84
+ pip install build twine
85
+ python -m build
86
+ twine upload dist/*
87
+ ```
88
+
89
+ 发布前请将 `pyproject.toml` 中的 `version` 更新为待发布版本号。
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mchat-client"
7
+ version = "0.1.0"
8
+ description = "MChat Python 客户端 SDK"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "paho-mqtt>=2.0.0",
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Communications :: Chat",
23
+ ]
24
+ [project.optional-dependencies]
25
+ dev = []
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,21 @@
1
+ """
2
+ MChat Python 客户端 SDK
3
+ """
4
+
5
+ from mchat_client.client import MChatClient
6
+ from mchat_client.api import (
7
+ send_private_message,
8
+ send_group_message,
9
+ get_org_tree,
10
+ get_storage_config,
11
+ get_agent_capability_list,
12
+ )
13
+
14
+ __all__ = [
15
+ "MChatClient",
16
+ "send_private_message",
17
+ "send_group_message",
18
+ "get_org_tree",
19
+ "get_storage_config",
20
+ "get_agent_capability_list",
21
+ ]
@@ -0,0 +1,54 @@
1
+ """
2
+ 便捷 API:基于 request 封装的常用方法
3
+ """
4
+
5
+ from typing import Any, Dict, Optional, Union
6
+
7
+ from mchat_client.client import MChatClient
8
+
9
+
10
+ def send_private_message(
11
+ client: MChatClient,
12
+ to_employee_id: str,
13
+ content: Union[str, Dict[str, Any]],
14
+ quote_msg_id: Optional[str] = None,
15
+ ) -> Dict[str, Any]:
16
+ """发送私聊消息。"""
17
+ params: Dict[str, Any] = {"to_employee_id": to_employee_id, "content": content}
18
+ if quote_msg_id is not None:
19
+ params["quote_msg_id"] = quote_msg_id
20
+ return client.request("msg.send_private", params)
21
+
22
+
23
+ def send_group_message(
24
+ client: MChatClient,
25
+ group_id: str,
26
+ content: Union[str, Dict[str, Any]],
27
+ quote_msg_id: Optional[str] = None,
28
+ ) -> Dict[str, Any]:
29
+ """发送群消息。"""
30
+ params: Dict[str, Any] = {"group_id": group_id, "content": content}
31
+ if quote_msg_id is not None:
32
+ params["quote_msg_id"] = quote_msg_id
33
+ return client.request("msg.send_group", params)
34
+
35
+
36
+ def get_org_tree(client: MChatClient) -> Dict[str, Any]:
37
+ """获取组织架构(部门、员工)。"""
38
+ return client.request("org.tree")
39
+
40
+
41
+ def get_storage_config(client: MChatClient) -> Dict[str, Any]:
42
+ """获取存储配置。"""
43
+ return client.request("config.storage")
44
+
45
+
46
+ def get_agent_capability_list(
47
+ client: MChatClient,
48
+ skill: Optional[str] = None,
49
+ ) -> Dict[str, Any]:
50
+ """获取 Agent 能力列表。"""
51
+ params: Dict[str, Any] = {}
52
+ if skill is not None:
53
+ params["skill"] = skill
54
+ return client.request("agent.capability_list", params)
@@ -0,0 +1,258 @@
1
+ """
2
+ MChat Python 客户端:MQTT 连接、请求-响应、收件箱/群订阅与事件
3
+ """
4
+
5
+ import json
6
+ import random
7
+ import string
8
+ import threading
9
+ import time
10
+ from typing import Any, Callable, Dict, List, Optional
11
+
12
+ import paho.mqtt.client as mqtt
13
+
14
+ REQ_PREFIX = "mchat/msg/req/"
15
+ RESP_PREFIX = "mchat/msg/resp/"
16
+ INBOX_PREFIX = "mchat/inbox/"
17
+ GROUP_PREFIX = "mchat/group/"
18
+ STATUS_PREFIX = "mchat/status/"
19
+
20
+
21
+ def _gen_seq_id() -> str:
22
+ return "seq_" + "".join(random.choices(string.ascii_lowercase + string.digits, k=10)) + "_" + str(int(time.time() * 1000))
23
+
24
+
25
+ def _gen_client_id(employee_id: str, device_id: Optional[str] = None) -> str:
26
+ dev = device_id or "py"
27
+ uid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
28
+ return f"{employee_id}_{dev}_{uid}"
29
+
30
+
31
+ class MChatClient:
32
+ """MChat 客户端,与《消息交互接口与示例》一致。"""
33
+
34
+ def __init__(
35
+ self,
36
+ broker_host: str,
37
+ broker_port: int,
38
+ username: str,
39
+ password: str,
40
+ employee_id: str,
41
+ use_tls: bool = False,
42
+ client_id: Optional[str] = None,
43
+ device_id: Optional[str] = None,
44
+ request_timeout_ms: int = 30000,
45
+ skip_auth_bind: bool = False,
46
+ ):
47
+ self._broker_host = broker_host
48
+ self._broker_port = broker_port
49
+ self._use_tls = use_tls
50
+ self._username = username
51
+ self._password = password
52
+ self._employee_id = employee_id
53
+ self._client_id = (client_id or "").strip() or _gen_client_id(employee_id, device_id)
54
+ self._request_timeout_ms = request_timeout_ms
55
+ self._skip_auth_bind = skip_auth_bind
56
+
57
+ self._client: Optional[mqtt.Client] = None
58
+ self._pending: Dict[str, Dict[str, Any]] = {}
59
+ self._pending_lock = threading.Lock()
60
+ self._connect_event = threading.Event()
61
+ self._connect_error: Optional[Exception] = None
62
+
63
+ self._listeners: Dict[str, List[Callable[..., None]]] = {
64
+ "inbox": [],
65
+ "group": [],
66
+ "connect": [],
67
+ "offline": [],
68
+ "error": [],
69
+ }
70
+
71
+ @property
72
+ def connected(self) -> bool:
73
+ return self._client is not None and self._client.is_connected()
74
+
75
+ def get_client_id(self) -> str:
76
+ return self._client_id
77
+
78
+ def connect(self) -> None:
79
+ """连接 Broker,订阅 resp/inbox,可选 auth.bind,发布 online。"""
80
+ self._connect_event.clear()
81
+ self._connect_error = None
82
+
83
+ try:
84
+ client = mqtt.Client(
85
+ callback_api_version=getattr(mqtt.CallbackAPIVersion, "VERSION1", None) or mqtt.CallbackAPIVersion.VERSION1,
86
+ client_id=self._client_id,
87
+ protocol=mqtt.MQTTv311,
88
+ )
89
+ client.username_pw_set(self._username, self._password)
90
+
91
+ will_payload = json.dumps({"status": "offline", "updated_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())})
92
+ client.will_set(
93
+ f"{STATUS_PREFIX}{self._employee_id}",
94
+ will_payload,
95
+ qos=1,
96
+ retain=True,
97
+ )
98
+
99
+ client.on_connect = self._on_connect
100
+ client.on_message = self._on_message
101
+ client.on_disconnect = self._on_disconnect
102
+ if hasattr(client, "on_connect_fail"):
103
+ client.on_connect_fail = self._on_connect_fail
104
+
105
+ if self._use_tls:
106
+ client.tls_set()
107
+
108
+ client.connect(self._broker_host, self._broker_port, 60)
109
+ self._client = client
110
+ client.loop_start()
111
+
112
+ if not self._connect_event.wait(timeout=15):
113
+ raise TimeoutError("Connection timeout")
114
+ if self._connect_error:
115
+ raise self._connect_error
116
+
117
+ except Exception:
118
+ self._client = None
119
+ raise
120
+
121
+ def _on_connect(self, client: mqtt.Client, userdata: Any, flags: Any, rc: int) -> None:
122
+ if rc != 0:
123
+ self._connect_error = ConnectionError(f"Connect failed: {rc}")
124
+ self._connect_event.set()
125
+ return
126
+ try:
127
+ client.subscribe(f"{RESP_PREFIX}{self._client_id}/+", qos=1)
128
+ client.subscribe(f"{INBOX_PREFIX}{self._employee_id}", qos=1)
129
+ online_payload = json.dumps({"status": "online", "updated_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())})
130
+ client.publish(f"{STATUS_PREFIX}{self._employee_id}", online_payload, qos=1, retain=True)
131
+ if not self._skip_auth_bind:
132
+ try:
133
+ r = self._request_impl(client, "auth.bind", {"employee_id": self._employee_id})
134
+ if r.get("code") != 0:
135
+ print(f"[mchat] auth.bind failed: {r.get('message')}")
136
+ except Exception as e:
137
+ print(f"[mchat] auth.bind error: {e}")
138
+ for fn in self._listeners["connect"]:
139
+ try:
140
+ fn()
141
+ except Exception:
142
+ pass
143
+ except Exception as e:
144
+ self._connect_error = e
145
+ finally:
146
+ self._connect_event.set()
147
+
148
+ def _on_message(self, client: mqtt.Client, userdata: Any, msg: Any) -> None:
149
+ topic = msg.topic
150
+ try:
151
+ payload_str = msg.payload.decode("utf-8") if isinstance(msg.payload, bytes) else msg.payload
152
+ except Exception:
153
+ return
154
+ if topic.startswith(RESP_PREFIX + self._client_id + "/"):
155
+ seq_id = topic[len(RESP_PREFIX + self._client_id + "/") :]
156
+ with self._pending_lock:
157
+ p = self._pending.pop(seq_id, None)
158
+ if p:
159
+ try:
160
+ body = json.loads(payload_str)
161
+ p["event"].set()
162
+ p["result"] = body
163
+ except Exception:
164
+ p["event"].set()
165
+ p["error"] = ValueError("Invalid response JSON")
166
+ return
167
+ if topic == f"{INBOX_PREFIX}{self._employee_id}":
168
+ try:
169
+ body = json.loads(payload_str)
170
+ for fn in self._listeners["inbox"]:
171
+ try:
172
+ fn(body)
173
+ except Exception:
174
+ pass
175
+ except Exception:
176
+ pass
177
+ return
178
+ if topic.startswith(GROUP_PREFIX):
179
+ group_id = topic[len(GROUP_PREFIX) :]
180
+ try:
181
+ body = json.loads(payload_str)
182
+ for fn in self._listeners["group"]:
183
+ try:
184
+ fn(group_id, body)
185
+ except Exception:
186
+ pass
187
+ except Exception:
188
+ pass
189
+
190
+ def _on_disconnect(self, client: mqtt.Client, userdata: Any, rc: int) -> None:
191
+ for fn in self._listeners["offline"]:
192
+ try:
193
+ fn()
194
+ except Exception:
195
+ pass
196
+
197
+ def _on_connect_fail(self, client: mqtt.Client, userdata: Any) -> None:
198
+ self._connect_error = ConnectionError("Connect failed")
199
+ self._connect_event.set()
200
+
201
+ def _request_impl(self, client: mqtt.Client, action: str, params: Dict[str, Any]) -> Dict[str, Any]:
202
+ seq_id = _gen_seq_id()
203
+ topic = f"{REQ_PREFIX}{self._client_id}/{seq_id}"
204
+ payload = json.dumps({"action": action, **params})
205
+ event = threading.Event()
206
+ with self._pending_lock:
207
+ self._pending[seq_id] = {"event": event, "result": None, "error": None}
208
+ client.publish(topic, payload, qos=1)
209
+ timeout_s = self._request_timeout_ms / 1000.0
210
+ if not event.wait(timeout=timeout_s):
211
+ with self._pending_lock:
212
+ self._pending.pop(seq_id, None)
213
+ raise TimeoutError("Request timeout")
214
+ with self._pending_lock:
215
+ p = self._pending.get(seq_id)
216
+ if p and p.get("error"):
217
+ raise p["error"]
218
+ if p and p.get("result") is not None:
219
+ return p["result"]
220
+ raise RuntimeError("No response")
221
+
222
+ def request(self, action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
223
+ """发起一次请求,成功时返回 data 在其中的响应体;code!=0 或异常时抛错。"""
224
+ if not self._client or not self._client.is_connected():
225
+ raise RuntimeError("Not connected")
226
+ params = params or {}
227
+ r = self._request_impl(self._client, action, params)
228
+ if r.get("code") != 0:
229
+ raise RuntimeError(r.get("message") or f"code {r.get('code')}")
230
+ return r
231
+
232
+ def subscribe_group(self, group_id: str) -> None:
233
+ """订阅群消息。"""
234
+ if not self._client or not self._client.is_connected():
235
+ raise RuntimeError("Not connected")
236
+ self._client.subscribe(f"{GROUP_PREFIX}{group_id}", qos=1)
237
+
238
+ def unsubscribe_group(self, group_id: str) -> None:
239
+ """取消订阅群。"""
240
+ if self._client:
241
+ self._client.unsubscribe(f"{GROUP_PREFIX}{group_id}")
242
+
243
+ def on(self, event: str, fn: Callable[..., None]) -> None:
244
+ """注册事件:inbox, group, connect, offline, error。"""
245
+ if event in self._listeners:
246
+ self._listeners[event].append(fn)
247
+
248
+ def disconnect(self) -> None:
249
+ """断开连接。"""
250
+ if self._client:
251
+ with self._pending_lock:
252
+ for p in self._pending.values():
253
+ p["event"].set()
254
+ p["error"] = RuntimeError("Disconnected")
255
+ self._pending.clear()
256
+ self._client.loop_stop()
257
+ self._client.disconnect()
258
+ self._client = None
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: mchat-client
3
+ Version: 0.1.0
4
+ Summary: MChat Python 客户端 SDK
5
+ Classifier: Development Status :: 4 - Beta
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Communications :: Chat
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: paho-mqtt>=2.0.0
16
+ Provides-Extra: dev
17
+
18
+ # MChat Python 客户端
19
+
20
+ Python 版 MChat 客户端 SDK,与《技术设计方案》及《消息交互接口与示例》一致。封装 MQTT 连接、请求-响应、收件箱/群消息订阅与事件。
21
+
22
+ ## 要求
23
+
24
+ - Python >= 3.10
25
+ - 依赖:paho-mqtt >= 2.0.0
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ cd client/python
31
+ pip install -e .
32
+ # 或从项目外:pip install -e /path/to/MChat/client/python
33
+ ```
34
+
35
+ ## 连接参数
36
+
37
+ 与 `employee.create` 返回的 `mqtt_connection` 对应,构造 `MChatClient` 时传入:
38
+
39
+ - `broker_host` / `broker_port` / `use_tls`
40
+ - `username`(如 employee_id)/ `password`
41
+ - `employee_id`:当前员工 ID,用于 auth.bind、收件箱订阅、在线状态
42
+ - 可选:`client_id`、`device_id`、`request_timeout_ms`、`skip_auth_bind`
43
+
44
+ ## 使用示例
45
+
46
+ ```python
47
+ from mchat_client import (
48
+ MChatClient,
49
+ send_private_message,
50
+ get_org_tree,
51
+ get_storage_config,
52
+ get_agent_capability_list,
53
+ )
54
+
55
+ client = MChatClient(
56
+ broker_host="broker.example.com",
57
+ broker_port=1883,
58
+ use_tls=False,
59
+ username="emp_zhangsan_001",
60
+ password="your_mqtt_password",
61
+ employee_id="emp_zhangsan_001",
62
+ )
63
+
64
+ client.connect()
65
+
66
+ client.on("inbox", lambda payload: print("收件箱:", payload))
67
+ client.on("group", lambda group_id, payload: print("群消息", group_id, payload))
68
+
69
+ # 发单聊
70
+ send_private_message(client, "emp_lisi_002", "你好")
71
+
72
+ # 获取组织树
73
+ tree = get_org_tree(client)
74
+ print(tree.get("data", {}).get("employees"))
75
+
76
+ # 订阅某群(需已知 group_id)
77
+ client.subscribe_group("grp_xxx")
78
+
79
+ client.disconnect()
80
+ ```
81
+
82
+ ## API 概览
83
+
84
+ - **MChatClient**
85
+ - `connect()` / `disconnect()`
86
+ - `request(action, params)`:通用请求,成功返回完整响应体(含 code、message、data),失败抛异常
87
+ - `subscribe_group(group_id)` / `unsubscribe_group(group_id)`
88
+ - `on("inbox" | "group" | "connect" | "offline" | "error", callback)`
89
+ - **便捷方法**(见 `api.py`):`send_private_message`、`send_group_message`、`get_org_tree`、`get_storage_config`、`get_agent_capability_list`
90
+
91
+ ## 示例
92
+
93
+ 同目录下 **example/** 为可运行示例(连接、拉取组织架构与 Agent、收件箱/群消息、可选发测试消息)。详见 [example/README.md](example/README.md)。
94
+
95
+ ## 发布到 PyPI
96
+
97
+ 在 `client/python` 目录下构建并上传(需先安装 `build`、`twine`):
98
+
99
+ ```bash
100
+ cd client/python
101
+ pip install build twine
102
+ python -m build
103
+ twine upload dist/*
104
+ ```
105
+
106
+ 发布前请将 `pyproject.toml` 中的 `version` 更新为待发布版本号。
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/mchat_client/__init__.py
4
+ src/mchat_client/api.py
5
+ src/mchat_client/client.py
6
+ src/mchat_client.egg-info/PKG-INFO
7
+ src/mchat_client.egg-info/SOURCES.txt
8
+ src/mchat_client.egg-info/dependency_links.txt
9
+ src/mchat_client.egg-info/requires.txt
10
+ src/mchat_client.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ paho-mqtt>=2.0.0
2
+
3
+ [dev]
@@ -0,0 +1 @@
1
+ mchat_client