Metadata-Version: 2.4
Name: haozhuma
Version: 0.1.3
Summary: Python SDK for Haozhuma SMS receiving platform
Author: laozig
License-Expression: MIT
Project-URL: Homepage, https://github.com/laozig/haozhuma
Project-URL: Repository, https://github.com/laozig/haozhuma
Project-URL: Documentation, https://github.com/laozig/haozhuma#readme
Project-URL: Issues, https://github.com/laozig/haozhuma/issues
Keywords: haozhuma,sms,otp,verification-code
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: curl_cffi>=0.7.0
Dynamic: license-file

# haozhuma

`haozhuma` 是一个面向注册机、批量任务和验证码自动化流程的 **豪猪接码 Python SDK**。

项目地址：<https://github.com/laozig/haozhuma>

问题反馈：<https://github.com/laozig/haozhuma/issues>

## 特性

- 纯代码 SDK，适合直接嵌入注册机工程。
- [`login()`](src/haozhuma/client.py:385) 默认自动复用 [`TokenCache`](src/haozhuma/token_cache.py:14)。
- [`HaozhuClient.next_code()`](src/haozhuma/client.py:336) 一行完成取号和取码。
- 支持 `with` 上下文，自动关闭 HTTP 会话。
- 提供 [`SMSProvider`](src/haozhuma/provider.py:9) 协议，方便以后接入其他接码平台。

## 安装

### 从 PyPI 安装

```bash
pip install haozhuma
```

### 从源码安装

```bash
git clone https://github.com/laozig/haozhuma.git
cd haozhuma
python -m pip install -e .
```

## 最短用法

```python
from haozhuma import login

with login("your_user", "your_password", sid="92162") as sms:
    phone, code = sms.next_code()
    print(phone, code)
```

这段代码内部会完成：

- 登录豪猪平台。
- 复用或写入 `token` 缓存。
- 获取手机号。
- 轮询验证码。
- 成功后自动释放号码。

## 常见用法

### 1. 状态机写法

```python
from haozhuma import login

sms = login("your_user", "your_password", sid="92162")

phone = sms.get_phone()
code = sms.poll_code()

print(phone, code)
print(sms.phone, sms.code)

sms.release()
```

### 2. 批量任务写法

```python
from haozhuma import login

sms = login("your_user", "your_password", sid="92162")

for index in range(100):
    try:
        phone, code = sms.next_code()
        print(index, phone, code)
    except TimeoutError:
        print(index, "timeout")
```

### 3. 手动控制缓存

```python
from haozhuma import TokenCache, login

cache = TokenCache("./token_cache.json")
sms = login("your_user", "your_password", sid="92162", token_cache=cache)
```

### 4. 已有 token 时直接恢复

```python
from haozhuma import from_token

sms = from_token("your_token", sid="92162")
phone = sms.get_phone()
code = sms.poll_code()
```

### 5. 获取完整响应对象

```python
from haozhuma import login

sms = login("your_user", "your_password", sid="92162")

phone_info = sms.get_phone_info()
message = sms.poll_message()

print(phone_info.raw)
print(message.raw)
```

## 顶层入口

### [`login()`](src/haozhuma/client.py:385)

创建并返回一个已登录的 [`HaozhuClient`](src/haozhuma/client.py:22)。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `user` | `str` | 必填 | 豪猪 API 用户名 |
| `password` | `str` | 必填 | 豪猪 API 密码 |
| `sid` | `str` | `""` | 项目 ID |
| `server` | `str` | `api.haozhuma.com` | 豪猪服务器地址 |
| `author` | `str` | `adminzfz` | 开发者账号，用于分成参数 |
| `timeout` | `int` | `10` | HTTP 超时秒数 |
| `poll_interval` | `int` | `15` | 默认轮询间隔 |
| `wait_timeout` | `int` | `180` | 默认验证码等待总时长 |
| `impersonate` | `str` | `chrome120` | `curl_cffi` 指纹伪装参数 |
| `session` | `Any \| None` | `None` | 自定义 HTTP session |
| `use_cache` | `bool` | `True` | 是否优先使用 [`TokenCache`](src/haozhuma/token_cache.py:14) |
| `token_cache` | `TokenCache \| None` | `None` | 自定义 token 缓存实例 |
| `force` | `bool` | `False` | 是否强制重新登录，忽略缓存 |

#### 返回值

- `HaozhuClient`

#### 异常

- [`HaozhuResponseError`](src/haozhuma/exceptions.py:21)
- [`HaozhuAPIError`](src/haozhuma/exceptions.py:10)
- `ValueError`

### [`from_token()`](src/haozhuma/client.py:441)

用现有 `token` 直接恢复客户端，不再触发登录。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `token` | `str` | 必填 | 已有的豪猪 token |
| `sid` | `str` | `""` | 项目 ID |
| `server` | `str` | `api.haozhuma.com` | 豪猪服务器地址 |
| `author` | `str` | `adminzfz` | 开发者账号 |
| `timeout` | `int` | `10` | HTTP 超时秒数 |
| `poll_interval` | `int` | `15` | 默认轮询间隔 |
| `wait_timeout` | `int` | `180` | 默认验证码等待总时长 |
| `impersonate` | `str` | `chrome120` | `curl_cffi` 指纹伪装参数 |
| `session` | `Any \| None` | `None` | 自定义 HTTP session |

#### 返回值

- `HaozhuClient`

## `HaozhuClient` API

### 构造函数 [`HaozhuClient()`](src/haozhuma/client.py:22)

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `server` | `str` | `api.haozhuma.com` | 豪猪服务器地址 |
| `username` | `str` | `""` | 账号名 |
| `password` | `str` | `""` | 密码 |
| `token` | `str` | `""` | 已登录 token |
| `project_id` | `str` | `""` | 默认项目 ID |
| `author` | `str` | `adminzfz` | 开发者账号 |
| `timeout` | `int` | `10` | HTTP 超时秒数 |
| `poll_interval` | `int` | `15` | 默认轮询间隔 |
| `wait_timeout` | `int` | `180` | 默认验证码等待时长 |
| `impersonate` | `str` | `chrome120` | `curl_cffi` 指纹伪装参数 |
| `session` | `Any \| None` | `None` | 外部传入的 session |

### 属性

| 属性 | 类型 | 说明 |
|---|---|---|
| `token` | `str` | 当前登录 token，见 [`token`](src/haozhuma/client.py:75) |
| `phone` | `str` | 最近一次取到的手机号，见 [`phone`](src/haozhuma/client.py:79) |
| `phone_info` | `PhoneResult \| None` | 最近一次取号完整结果，见 [`phone_info`](src/haozhuma/client.py:85) |
| `message` | `MessageResult \| None` | 最近一次短信结果，见 [`message`](src/haozhuma/client.py:89) |
| `code` | `str` | 最近一次验证码，见 [`code`](src/haozhuma/client.py:93) |

### [`from_env()`](src/haozhuma/client.py:51)

从环境变量构造客户端。

#### 环境变量

| 变量名 | 说明 |
|---|---|
| `HAOZHUMA_SERVER` | 服务器地址 |
| `HAOZHUMA_USER` | 用户名 |
| `HAOZHUMA_PASSWORD` | 密码 |
| `HAOZHUMA_TOKEN` | token |
| `HAOZHUMA_SID` | 项目 ID |
| `HAOZHUMA_AUTHOR` | 开发者账号 |
| `HAOZHUMA_POLL_INTERVAL` | 轮询间隔 |
| `HAOZHUMA_WAIT_TIMEOUT` | 验证码等待时长 |
| `HAOZHUMA_IMPERSONATE` | 指纹伪装参数 |

#### 返回值

- `HaozhuClient`

### [`normalize_server()`](src/haozhuma/client.py:68)

规范化服务器地址。

#### 参数

| 参数 | 类型 | 说明 |
|---|---|---|
| `server` | `str` | 原始服务器地址 |

#### 返回值

- `str`

### [`request()`](src/haozhuma/client.py:120)

统一请求底层方法。一般业务代码不直接调用，但可用于调试未封装接口。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `api` | `str` | 必填 | 豪猪接口名 |
| `allow_wait` | `bool` | `False` | 是否允许 `-1` 且包含“等待”消息时不报错 |
| `**params` | `Any` | - | 透传给接口的查询参数 |

#### 返回值

- `dict[str, Any]`

#### 异常

- [`HaozhuAPIError`](src/haozhuma/exceptions.py:10)
- [`HaozhuResponseError`](src/haozhuma/exceptions.py:21)

### [`login()`](src/haozhuma/client.py:147)

登录豪猪平台并把 token 保存到当前实例。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `username` | `str` | `""` | 可覆盖实例内用户名 |
| `password` | `str` | `""` | 可覆盖实例内密码 |
| `force` | `bool` | `False` | 若已有 token，是否强制重新登录 |

#### 返回值

- `str`：token

### [`ensure_token()`](src/haozhuma/client.py:163)

确保当前实例有 token，没有就自动登录。

#### 返回值

- `str`：token

### [`get_summary()`](src/haozhuma/client.py:168)

获取账号余额与账号信息。

#### 返回值

- [`AccountSummary`](src/haozhuma/models.py:8)

### [`get_phone_raw()`](src/haozhuma/client.py:172)

原始取号接口封装。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `sid` | `str` | `""` | 项目 ID |
| `phone` | `str` | `""` | 指定号码，占用模式时使用 |
| `isp` | `str \| int` | `""` | 运营商参数 |
| `province` | `str \| int` | `""` | 省份代码，对应接口参数 `Province` |
| `ascription` | `str \| int` | `""` | 号码类型 |
| `paragraph` | `str` | `""` | 只取指定号段 |
| `exclude` | `str` | `""` | 排除指定号段 |
| `uid` | `str` | `""` | 指定对接码 UID |
| `author` | `str` | `""` | 开发者账号，空则使用实例默认值 |

#### 返回值

- `dict[str, Any]`

### [`get_phone_info()`](src/haozhuma/client.py:198)

获取号码并返回完整响应对象。

#### 参数

- 与 [`get_phone_raw()`](src/haozhuma/client.py:172) 一致，但通常通过 `**filters` 传入。

#### 返回值

- [`PhoneResult`](src/haozhuma/models.py:28)

### [`get_phone()`](src/haozhuma/client.py:202)

获取号码并直接返回手机号字符串。

#### 参数

- 与 [`get_phone_info()`](src/haozhuma/client.py:198) 一致。

#### 返回值

- `str`

### [`occupy_phone_info()`](src/haozhuma/client.py:205)

指定号码占用，并返回完整响应对象。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `phone` | `str` | 必填 | 要占用的手机号 |
| `sid` | `str` | `""` | 项目 ID |
| `author` | `str` | `""` | 开发者账号 |

#### 返回值

- [`PhoneResult`](src/haozhuma/models.py:28)

### [`occupy_phone()`](src/haozhuma/client.py:215)

指定号码占用，并直接返回手机号字符串。

#### 返回值

- `str`

### [`get_message_raw()`](src/haozhuma/client.py:218)

原始读短信接口封装。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `phone` | `str` | `""` | 手机号，留空时默认使用当前状态手机号 |
| `sid` | `str` | `""` | 项目 ID |
| `allow_wait` | `bool` | `False` | 是否允许等待状态返回 |

#### 返回值

- `dict[str, Any]`

### [`get_message()`](src/haozhuma/client.py:228)

读取一次短信内容并返回完整消息对象。

#### 返回值

- [`MessageResult`](src/haozhuma/models.py:58)

### [`release_phone()`](src/haozhuma/client.py:240)

释放指定号码。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `phone` | `str` | `""` | 手机号，留空时默认使用当前状态手机号 |
| `sid` | `str` | `""` | 项目 ID |

#### 返回值

- `dict[str, Any]`

### [`release_all()`](src/haozhuma/client.py:248)

释放当前账号占用的全部号码。

#### 返回值

- `dict[str, Any]`

### [`blacklist_phone()`](src/haozhuma/client.py:251)

拉黑指定号码。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `phone` | `str` | `""` | 手机号，留空时默认使用当前状态手机号 |
| `sid` | `str` | `""` | 项目 ID |

#### 返回值

- `dict[str, Any]`

### [`block_phone()`](src/haozhuma/client.py:259)

[`blacklist_phone()`](src/haozhuma/client.py:251) 的别名。

### [`extract_code()`](src/haozhuma/client.py:262)

从原始短信内容中提取验证码。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `payload` | `Mapping[str, Any] \| str` | 必填 | 原始消息对象或原始短信文本 |
| `fallback_code` | `str` | `""` | 备用验证码文本 |

#### 返回值

- `str`

### [`poll_message()`](src/haozhuma/client.py:278)

轮询短信，直到拿到验证码或超时。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `phone` | `str` | `""` | 手机号，留空时默认使用当前状态手机号 |
| `sid` | `str` | `""` | 项目 ID |
| `timeout` | `int \| None` | `None` | 本次轮询总等待时长，秒 |
| `interval` | `int \| None` | `None` | 本次轮询间隔，秒 |
| `auto_blacklist` | `bool` | `False` | 超时后是否自动拉黑当前号码 |
| `on_attempt` | `Callable[[int, MessageResult], None] \| None` | `None` | 每次轮询回调，参数为尝试次数和消息对象 |

#### 返回值

- [`MessageResult`](src/haozhuma/models.py:58)

#### 异常

- [`HaozhuTimeoutError`](src/haozhuma/exceptions.py:25)

### [`poll_code()`](src/haozhuma/client.py:318)

轮询短信，但只返回验证码字符串。

#### 参数

- 与 [`poll_message()`](src/haozhuma/client.py:278) 一致。

#### 返回值

- `str`

### [`next_code()`](src/haozhuma/client.py:336)

一行完成取号和取码，适合注册机主循环。

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `sid` | `str` | `""` | 项目 ID |
| `timeout` | `int \| None` | `None` | 本次轮询总等待时长 |
| `interval` | `int \| None` | `None` | 本次轮询间隔 |
| `auto_release` | `bool` | `True` | 成功后自动释放号码 |
| `auto_blacklist` | `bool` | `True` | 超时后自动拉黑号码 |
| `on_attempt` | `Callable[[int, MessageResult], None] \| None` | `None` | 轮询回调 |
| `**filters` | `Any` | - | 透传给 [`get_phone()`](src/haozhuma/client.py:202) 的筛选参数，如 `isp`、`province`、`paragraph` 等 |

#### 返回值

- `tuple[str, str]`：`(phone, code)`

#### 异常

- [`HaozhuTimeoutError`](src/haozhuma/exceptions.py:25)

### [`release()`](src/haozhuma/client.py:364)

[`release_phone()`](src/haozhuma/client.py:240) 的快捷别名。

### [`blacklist()`](src/haozhuma/client.py:367)

[`blacklist_phone()`](src/haozhuma/client.py:251) 的快捷别名。

### [`block()`](src/haozhuma/client.py:370)

[`blacklist_phone()`](src/haozhuma/client.py:251) 的快捷别名。

### [`close()`](src/haozhuma/client.py:373)

主动关闭 HTTP 会话。

### 上下文管理

[`HaozhuClient`](src/haozhuma/client.py:22) 支持：

```python
from haozhuma import login

with login("your_user", "your_password", sid="92162") as sms:
    phone, code = sms.next_code()
```

退出 `with` 时会自动调用 [`close()`](src/haozhuma/client.py:373)。

## `TokenCache` API

### [`TokenCache()`](src/haozhuma/token_cache.py:14)

#### 参数

| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `path` | `str \| Path \| None` | `None` | 缓存文件路径 |

### [`default_path()`](src/haozhuma/token_cache.py:18)

返回默认缓存路径。

### [`make_key()`](src/haozhuma/token_cache.py:30)

生成缓存键。

#### 参数

| 参数 | 类型 | 说明 |
|---|---|---|
| `server` | `str` | 服务器地址 |
| `user` | `str` | 用户名 |
| `sid` | `str` | 项目 ID |

### [`load()`](src/haozhuma/token_cache.py:37)

读取缓存字典。

### [`save()`](src/haozhuma/token_cache.py:48)

保存缓存字典到文件。

### [`get()`](src/haozhuma/token_cache.py:56)

读取指定 `(server, user, sid)` 对应的 token。

### [`set()`](src/haozhuma/token_cache.py:61)

写入指定 token。

### [`delete()`](src/haozhuma/token_cache.py:73)

删除指定缓存项。

### [`clear()`](src/haozhuma/token_cache.py:82)

清空整个缓存文件。

## 数据模型

### [`AccountSummary`](src/haozhuma/models.py:8)

| 字段 | 类型 | 说明 |
|---|---|---|
| `money` | `str` | 账户余额 |
| `num` | `int` | 最大区号数量 |
| `raw` | `dict[str, Any]` | 原始响应 |

### [`PhoneResult`](src/haozhuma/models.py:28)

| 字段 | 类型 | 说明 |
|---|---|---|
| `phone` | `str` | 手机号 |
| `sid` | `str` | 项目 ID |
| `shop_name` | `str` | 项目名称 |
| `country_name` | `str` | 国家名称 |
| `country_code` | `str` | 国家代码 |
| `country_qu` | `str` | 国家区号 |
| `uid` | `str` | 对接码 UID |
| `sp` | `str` | 运营商 |
| `phone_gsd` | `str` | 归属地 |
| `raw` | `dict[str, Any]` | 原始响应 |

### [`MessageResult`](src/haozhuma/models.py:58)

| 字段 | 类型 | 说明 |
|---|---|---|
| `phone` | `str` | 手机号 |
| `sid` | `str` | 项目 ID |
| `sms` | `str` | 完整短信文本 |
| `sms_code` | `str` | 识别出的验证码 |
| `api_code` | `str` | 接口返回状态码 |
| `msg` | `str` | 接口描述 |
| `raw` | `dict[str, Any]` | 原始响应 |
| `ok` | `bool` | 是否成功拿到验证码 |

## 异常

| 异常 | 说明 |
|---|---|
| [`HaozhuError`](src/haozhuma/exceptions.py:6) | 基础异常 |
| [`HaozhuAPIError`](src/haozhuma/exceptions.py:10) | 接口返回非成功状态 |
| [`HaozhuResponseError`](src/haozhuma/exceptions.py:21) | HTTP 返回不是合法 JSON 或结构异常 |
| [`HaozhuTimeoutError`](src/haozhuma/exceptions.py:25) | 轮询验证码超时 |

## 默认行为

| 参数 | 默认值 | 作用 |
|---|---|---|
| `use_cache` | `True` | 登录时优先读 `TokenCache` |
| `auto_release` | `True` | `next_code()` 成功后自动释放号码 |
| `auto_blacklist` | `True` | `next_code()` 超时后自动拉黑号码 |
| `DEFAULT_SERVER` | `api.haozhuma.com` | 默认服务器地址 |
| `DEFAULT_AUTHOR` | `adminzfz` | 默认开发者账号 |
| `DEFAULT_POLL_INTERVAL` | `15` | 默认轮询间隔 |
| `DEFAULT_WAIT_TIMEOUT` | `180` | 默认等待时长 |

## 开发与发布

本地构建：

```bash
python -m build
python -m twine check dist/*
```

发布到 PyPI：

```bash
python -m twine upload dist/*
```

## License

本项目使用 MIT License，见 [`LICENSE`](LICENSE)。

## 免责声明

本模块仅用于已授权项目的验证码接收、测试环境验证和自动化流程开发。使用者需自行确认账号、项目和接口调用均符合平台规则与当地法律。
