Metadata-Version: 2.4
Name: boxim-sdk
Version: 3.2.5
Summary: BoxIM SDK for Python - BoxIM 即时通讯平台 SDK
Author: nichengfuben
License: MIT
Project-URL: Homepage, https://www.boximchat.com
Project-URL: Repository, https://github.com/nichengfuben/boxim-sdk
Keywords: boxim,im,chat,sdk,websocket
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications :: Chat
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: aiohttp<3.13,>=3.9.0; python_version < "3.9"
Requires-Dist: aiohttp<3.14,>=3.9.0; python_version == "3.9"
Requires-Dist: aiohttp>=3.9.0; python_version >= "3.10"
Requires-Dist: requests>=2.31.0
Requires-Dist: urllib3>=1.26.0
Requires-Dist: websockets<14,>=12.0; python_version < "3.9"
Requires-Dist: websockets>=12.0; python_version >= "3.9"

# BoxIM SDK

Python SDK for [BoxIM](https://www.boximchat.com)（盒子 IM）。覆盖登录、社交、私聊/群聊消息、历史与离线拉取、贴纸、投诉收藏、WebRTC 信令，以及管理端 `AdminClient`。

当前版本：**3.2.5**

兼容：**Python 3.8–3.14**，**macOS / Linux / Windows**。默认校验证书；运行时不应产生 Python warnings（测试以 `-W error` 门禁）。

约定：绝大多数业务方法均有异步双胞胎，命名为在同步方法前加 `a`（如 `login` → `alogin`）。下文以同步签名为准，不再逐条重复 `await`。

---

## 安装与入口

### PyPI（推荐）

```bash
pip install boxim-sdk
pip install -U boxim-sdk          # 升级
python -c "import boxim; print(boxim.__version__)"
```

### 本地开发安装

```bash
git clone https://github.com/nichengfuben/boxim-sdk.git
cd boxim-sdk
python -m pip install -U pip
pip install -e ".[dev]" || pip install -e .
pip install pytest
python -m compileall -q boxim
python -c "import boxim; print(boxim.__version__)"
pytest -q
python achecker.py
```

### CI / GitHub / 发布

- CI：推送到 `main` 或开 PR 时跑 `.github/workflows/ci.yml`（macOS / Linux / Windows × Python 3.8–3.14：`compileall` / `python -W error` import / `pytest -W error` / `achecker`）
- 发版：合并到 `main` 后打 tag `vX.Y.Z` 并 push，触发 `.github/workflows/publish.yml` 构建并上传 PyPI
- 仓库：https://github.com/nichengfuben/boxim-sdk
- PyPI：https://pypi.org/project/boxim-sdk/

```bash
# 维护者发版示例（版本号与 pyproject.toml / boxim.__version__ 一致）
git tag v3.2.5
git push origin main --tags
```

TLS：默认 `ssl_verify=True`。若需关闭校验（如本地 MITM 代理）：

```bash
set BOXIM_SSL_VERIFY=false   # Windows cmd
export BOXIM_SSL_VERIFY=false
```

推荐用全局实例管理器：

```python
import boxim

im = boxim.init("username", "password")   # 登录并注册默认实例
im = boxim.get()                          # 取当前实例
```

也可用兼容入口或一次性客户端：

```python
from boxim.client import initialize_im_client, get_im_client
from boxim import BoxIM, quick_login

im = initialize_im_client("username", "password")
im = get_im_client()

im = quick_login("username", "password", base_url=None, debug=False)
im = BoxIM(base_url="https://www.boximchat.com/").login("username", "password")
```


多实例：

```python
import boxim

boxim.create("user_a", "pwd_a", name="a")
boxim.create("user_b", "pwd_b", name="b")
boxim.use("a")
im = boxim.get("b")
```

相关：`ainit` / `acreate` / `register` / `list_instances` / `has` / `remove` / `destroy` / `reset` / `is_initialized` / `current_name`。

---

## 认证

`im.login(username, password, terminal=TerminalType.WEB)` → `self`  
`im.register(mode, user_name=None, phone=None, email=None, code=None, password=None, confirm_password=None, nick_name=None)` → `Dict`  
`im.refresh_token()` → `self`  
`im.modify_password(old_password, new_password)` → `Dict`（`PUT /api/modifyPwd`）  
`im.reset_password(mode, phone=None, email=None, code=None, password=None, confirm_password=None)` → `Dict`  
`im.unregister()` → `Dict`  
`im.close()` — 释放资源

`mode` 使用 `RegistrationMode.USERNAME / PHONE / EMAIL`。

### 二维码登录

`im.generate_qr_login()` → `QRLoginInfo`  
`im.check_qr_login_status(qr_code)` → `Dict`  
`im.qr_login_wait(poll_interval=2.0, timeout=300.0)` → `self`  
`im.scan_qr_login(qr_code)` / `im.confirm_qr_login(qr_code)` / `im.cancel_qr_login(qr_code)` → `Dict`

---

## 用户与资料

`im.get_me()` / `im.me` → 当前用户  
`im.get_user(user_id)` → `User`  
`im.search_users(keyword)` → `List[User]`  
`im.update_profile(**kwargs)` → `self`（如 `nickName`、`sex`、`signature`、`headImage`）  
`im.get_online_friends()` → `List[Dict]`（`GET /api/friend/online`）  
`im.get_online_terminals(user_ids=None)` — 兼容旧名，转发 `get_online_friends`  
`im.bind_phone(phone, code)` / `im.bind_email(email, code)`  
`im.set_audio_tip(enabled)` / `im.set_manual_approve(enabled)`  
`im.get_realname_auth_info()` / `im.submit_realname_auth(real_name, id_card)`  
`im.set_locale(locale)` / `im.report_cid(cid)` / `im.remove_cid()`

---

## 好友 / 好友请求 / 黑名单

`im.get_friends()` / `im.friends`  
`im.get_friend_info(user_id)`  
`im.add_friend(user_id, remark=None)`（别名 `send_friend_request`）  
`im.delete_friend(user_id)`  
`im.set_friend_dnd(user_id, dnd)` / `im.set_friend_top(user_id, top)`  
`im.update_friend_remark(user_id, remark)`

`im.get_friend_requests()` / `im.friend_requests`  
`im.accept_friend_request(request_id)` / `im.reject_friend_request(request_id)` / `im.recall_friend_request(request_id)`

`im.add_to_blacklist(user_id)` / `im.remove_from_blacklist(user_id)`  
`im.get_blacklist(max_page_id=None)` → `List`（`GET /api/blacklist/page`）

---

## 群组

`im.get_groups()` / `im.groups`  
`im.create_group(name)` / `im.create_group_with_friends(user_ids)`  
`im.get_group_info(group_id)` / `im.modify_group(group_id, **kwargs)`  
`im.delete_group(group_id)` / `im.quit_group(group_id)`  
`im.get_group_members(group_id, version=0)` / `im.get_group_online_members(group_id)`  
`im.invite_to_group(group_id, user_ids)` / `im.remove_group_members(group_id, user_ids)`  
`im.join_group(group_id, token=None)`  
`im.get_group_card_token(group_id)` / `im.get_group_qrcode_token(group_id)`  
`im.set_group_dnd(group_id, dnd)` / `im.set_group_top(group_id, top)`  
`im.set_group_muted(group_id, muted)` / `im.set_group_member_muted(group_id, user_ids, muted)`  
`im.set_group_allow_invite(group_id, allow)` / `im.set_group_allow_share_card(group_id, allow)`  
`im.add_group_manager(group_id, user_ids)` / `im.remove_group_manager(group_id, user_ids)`  
`im.set_group_top_message(group_id, message_id)` / `im.remove_group_top_message(group_id)` / `im.hide_group_top_message(group_id)`

---

## 私聊消息

发送类方法返回 `self`（`send_private_raw` 返回服务端 `Dict`）。

`im.send_text(user_id, text)`  
`im.send_image(user_id, image_path)`  
`im.send_file(user_id, file_path)`  
`im.send_voice(user_id, voice_path, duration=3)`  
`im.send_video(user_id, video_path)`  
`im.send_sticker(user_id, sticker_id)`  
`im.send_user_card(user_id, target_user_id, target_nickname, target_head_image)`  
`im.send_group_card(user_id, group_id, group_name, group_head_image)`  
`im.send_private_raw(user_id, content, msg_type, quote_message_id=None)` → `Dict`  
`im.recall_private_message(message_id)`  
`im.mark_private_read(friend_id, message_id=None)`  
`im.get_max_read_private_message_id(friend_id)` → `int`  
`im.load_private_offline_message(min_id)` → `List[Dict]`  
`im.delete_private_messages(chat_id, message_ids)`  
`im.delete_private_chat(chat_id)`

### 私聊历史（重要）

`POST /api/message/private/history`，与官方 Web 端一致，**两种请求体互斥**：

1. 按会话序号区间补洞：

```python
msgs = im.get_private_message_history(
    friend_id=44090,
    min_seq_no=108,
    max_seq_no=137,
)
```

对应 JSON：

```json
{ "friendId": 44090, "minSeqNo": 108, "maxSeqNo": 137 }
```

2. 按本地消息 ID 查询（如发送中状态核对）：

```python
msgs = im.get_private_message_history(
    friend_id=44090,
    local_ids=["2084808148890869762"],
)
```

对应 JSON：

```json
{ "friendId": 44090, "localIds": ["2084808148890869762"] }
```

规则：

- 必须二选一；混传、都不传、或只传 `min_seq_no`/`max_seq_no` 其中一个会抛 `ValidationError`
- `local_ids` 元素为雪花字符串；传入 `int` 时会转为 `str`
- **没有** `page` / `size` 分页参数
- 序号区间无消息时返回空列表（`code=200`），不代表接口失败

---

## 群聊消息

`im.send_group_text(group_id, text, at_users=None)`  
`im.send_group_image(group_id, image_path, at_users=None)`  
`im.send_group_file(group_id, file_path, at_users=None)`  
`im.send_group_voice(group_id, voice_path, duration=3, at_users=None)`  
`im.send_group_video(group_id, video_path, at_users=None)`  
`im.send_group_sticker(group_id, sticker_id, at_users=None)`  
`im.send_group_raw(group_id, content, msg_type, at_users=None, receipt=False, quote_message_id=None)` → `Dict`  
`im.recall_group_message(message_id)`  
`im.mark_group_read(group_id, message_id=None)`  
`im.load_group_offline_message(min_id)` → `List[Dict]`  
`im.get_group_message_readers(group_id, message_id)` → `List[int]`  
`im.delete_group_messages(chat_id, message_ids)`  
`im.delete_group_chat(chat_id)`

### 群聊历史

`POST /api/message/group/history`，规则与私聊相同，仅将 `friendId` 换为 `groupId`：

```python
msgs = im.get_group_message_history(group_id=30700, min_seq_no=1700, max_seq_no=1730)
msgs = im.get_group_message_history(group_id=30700, local_ids=["2084806157064441858"])
```

---

## 系统消息

`im.load_system_offline_message(min_seq_no=0)` → `List[SystemMessage]`  
`im.mark_system_read(max_seq_no)`  
`im.get_system_message_content(message_id)`  
`im.delete_system_chat()`

---

## 贴纸

`im.get_sticker_albums()`  
`im.get_stickers(album_id)`  
`im.search_stickers(name)`  
`im.get_custom_stickers()`  
`im.add_custom_sticker(name, image_url, thumb_url, width, height)`  
`im.top_custom_sticker(sticker_id)`  
`im.delete_custom_sticker(sticker_id)`

---

## 验证码 / 投诉 / 收藏 / 系统配置

滑块：`im.get_slider_captcha()` / `im.check_slider_captcha(...)`  
兼容旧名：`get_captcha_img` / `verify_captcha_img`（转发滑块）  
短信/邮箱：`send_sms_captcha` / `verify_sms_captcha` / `send_email_captcha` / `verify_email_captcha`

投诉：`im.initiate_complaint(...)`（及 `submit_complaint`）  
`im.get_complaint_page(max_page_id=None, status=None)` / `im.get_complaint_info(complaint_id)`

收藏：`im.add_favorite(...)` / `im.get_favorites()` / `im.delete_favorite(favorite_id)`

配置：`im.get_system_config()` / `im.check_version(version)`

---

## 消息监听

```python
@im.on_event("private_message")
def on_pm(msg, is_group):
    ...

im.on_message(handler)      # handler(msg: dict, is_group: bool)
im.off_message(handler)
im.start_listening()        # 非阻塞
im.stop_listening()
im.listen()                 # 阻塞
im.listen_sync()            # 阻塞至 Ctrl+C
```

---

## WebRTC

私聊信令：`webrtc_setup` / `accept` / `reject` / `cancel` / `failed` / `handup` / `offer` / `answer` / `send_candidate` / `device` / `info` / `heartbeat`

群聊信令：`webrtc_group_setup` / `accept` / `reject` / `failed` / `join` / `invite` / `quit` / `cancel` / `offer` / `answer` / `send_candidate` / `device` / `info` / `heartbeat`

高级会话：`im.create_call(...)` / `im.create_incoming_call(...)` → `RTCCallSession`  
`im.active_calls` — 当前会话字典

SDP / candidate 按服务端约定以 JSON 字符串发送。

---

## AdminClient

```python
from boxim import AdminClient

admin = AdminClient(base_url="https://your-admin-host")
admin.login("admin", "password")
admin.im_user_list(page=1, limit=10)
admin.request("GET", "/im/group/list", params={...})
```

管理端 `/im/*` 薄封装见 `AdminImMixin`（用户/群/消息/敏感词/贴纸/系统消息/企业/投诉/实名等）。兼容别名：`list_im_users` / `list_im_groups` / `list_sensitive_words`。

---

## 常用类型与异常

枚举：`MessageType`、`TerminalType`、`RegistrationMode`、`FriendRequestStatus`、`ComplaintType`、`UserSex`、`ChatType`、`RTCMode`、`RTCState`、`QRLoginStatus`、`WebSocketCommand`

模型：`User`、`Friend`、`FriendRequest`、`Group`、`Message`、`SystemMessage`、`Sticker`、`StickerAlbum`、`QRLoginInfo`、`SystemConfig`、`TokenInfo`、`UploadResult` 等

异常：`BoxIMError`、`AuthError`、`NetworkError`、`ValidationError`、`RTCError`、`StreamError`、`ConfigError`、`TimeoutError`

属性：`.me` `.friends` `.groups` `.friend_requests` `.config` `.http` `.ws` `.uploader` `.container` `.token_store` `.active_calls`

---

## API 速查

| 分类 | 方法 |
|------|------|
| 实例 | `boxim.init/get/create/use/...`，`quick_login`，`BoxIM.login` |
| 认证 | `login` `register` `refresh_token` `modify_password` `reset_password` `unregister` + 扫码系列 |
| 用户 | `get_me` `get_user` `search_users` `update_profile` `get_online_friends` |
| 好友 | `get_friends` `add_friend` `delete_friend` `set_friend_dnd/top` `update_friend_remark` |
| 好友请求 | `get_friend_requests` `accept/reject/recall_friend_request` |
| 黑名单 | `add_to_blacklist` `remove_from_blacklist` `get_blacklist` |
| 群组 | `get_groups` `create_group` `invite_to_group` `set_group_*` `*_manager` `*_top_message` |
| 私聊 | `send_*` `recall_private_message` `mark_private_read` `load_private_offline_message` `get_private_message_history` |
| 群聊 | `send_group_*` `recall_group_message` `mark_group_read` `load_group_offline_message` `get_group_message_history` |
| 系统消息 | `load_system_offline_message` `mark_system_read` `get_system_message_content` `delete_system_chat` |
| 监听 | `on_message` `on_event` `listen` `start_listening` |
| WebRTC | `webrtc_*` / `webrtc_group_*` / `create_call` |
| Admin | `AdminClient.login` `request` `im_*` |

---

## 开发自检

```bash
pytest
python achecker.py
```

注释与文档分工见 `AGENTS.md`；详细设计在 `docs-src/`。
