Whisper 接入文档
生产域名:https://commu.fun/ API 前缀:/api/v2/(推荐)或 /api/(兼容)
🚀 快速开始(5 分钟接入)
三步完成首次对接:
第一步:获取参数(桌面端管理工具 → 实例管理)
| 参数 | 说明 |
|---|---|
instance_id |
实例 ID,纯数字,如 42601234 |
secret_key |
通信密钥,用于签名和解密,不可泄露 |
第二步:生成卡密(桌面端 → 卡密管理 → 批量生成)
第三步:最小可运行示例(Python,legacy 签名,无需额外依赖)
import hashlib, requests
API = "https://commu.fun"
INST = "YOUR_INSTANCE_ID"
KEY = "YOUR_SECRET_KEY"
def sign(params):
items = sorted((k, v) for k, v in params.items() if k != "sign")
raw = "&".join(f"{k}={v}" for k, v in items) + KEY
return hashlib.md5(raw.encode()).hexdigest()
params = {"key": "CARD-XXXX", "hwid": "MY-DEVICE-001", "instance_id": INST}
params["sign"] = sign(params)
r = requests.get(f"{API}/api/check", params=params)
print(r.json()) # {"code":0,"msg":"success","data":{...}}
卡密首次调用
/check自动激活,hwid建议用硬件特征拼接,不能为空。
签名算法
v2(推荐):HMAC-SHA256 + 防重放
请求头(三件套,必填):
| 请求头 | 格式 |
|---|---|
X-Timestamp |
当前秒级时间戳字符串,允许 ±300 s 偏差 |
X-Nonce |
随机字符串,12~128 位,每次请求必须不同(防重放) |
X-Signature |
HMAC-SHA256(secret_key, message) 十六进制小写 |
签名消息串(\n 换行分隔):
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY_SHA256
METHOD:大写,如POSTPATH:完整路径含/api前缀,如/api/v2/check,不含域名和 queryBODY_SHA256:sha256(原始 JSON 字节).hexdigest(),Body 用紧凑格式
Python 实现:
import hashlib, hmac, time, os, json, requests
def make_v2_headers(secret_key, method, path, body_bytes):
ts = str(int(time.time()))
nonce = os.urandom(16).hex()
bsha = hashlib.sha256(body_bytes).hexdigest()
msg = "\n".join([method.upper(), path, ts, nonce, bsha])
sig = hmac.new(secret_key.encode(), msg.encode(), hashlib.sha256).hexdigest()
return {"X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
"Content-Type": "application/json"}
def v2_post(api_base, path, body_dict, secret_key):
body = json.dumps(body_dict, separators=(",", ":"), ensure_ascii=False).encode()
headers = make_v2_headers(secret_key, "POST", path, body)
return requests.post(api_base + path, data=body, headers=headers)
legacy(兼容):MD5
- 取所有 query 参数(排除
sign),按参数名 ASCII 升序排序 - 拼接:
key1=value1&key2=value2... - 末尾追加
secret_key sign = md5(拼接结果).hexdigest()
import hashlib
def sign(params, secret_key):
items = sorted((k, v) for k, v in params.items() if k != "sign")
raw = "&".join(f"{k}={v}" for k, v in items) + secret_key
return hashlib.md5(raw.encode()).hexdigest()
通用返回格式
{"code": 0, "msg": "success", "data": {...}}
| 字段 | 说明 |
|---|---|
code |
0 = 成功,其余见错误码表 |
msg |
提示文本,错误时为具体原因 |
data |
业务数据(成功时) |
encrypted_data |
可选,开启加密返回时替代 data;enc_ver=2 表示 AES-GCM |
验证卡密 / 激活
POST /api/v2/check(推荐) GET /api/check(legacy)
首次调用自动激活卡密并绑定机器码;之后调用验证状态。
请求参数
| 参数 | 说明 |
|---|---|
key |
卡密字符串 |
hwid |
机器码,不能为空 |
instance_id |
软件实例 ID |
v2 JSON Body:{"key":"CARD-XXXX","hwid":"MY-DEVICE-001","instance_id":"12345678"}
返回示例
{"code":0,"msg":"success","data":{"status":"valid","expire_date":"2026-12-31 23:59:59","hwid":"MY-DEVICE-001","announcement":{"title":"系统公告","content":"欢迎!","time":"2026-01-01 12:00:00"}}}
永久卡密 expire_date 为 "永久"。
注意事项
unused→ 首次调用自动激活并计算到期时间- hwid 不同 → 若未用尽换绑次数,自动重绑;否则返回
code=6 announcement字段在/check、/unbind、/info、/cloudvar、/log均携带(可能为null)
心跳检测(卡密状态轮询)
POST /api/v2/heartbeat(推荐) GET /api/heartbeat(legacy)
适合高频轮询,不激活、不绑定 hwid、不写事件日志,仅校验状态。
请求参数
同 /check:key + hwid + instance_id
返回示例
{"code":0,"msg":"success","data":{"status":"valid","expire_date":"2026-12-31 23:59:59","remaining_seconds":86400,"announcement":null}}
与 /check 的区别
| 特性 | /check |
/heartbeat |
|---|---|---|
| 激活未使用卡密 | ✅ | ❌ 返回 code=7 |
| HWID 绑定/换绑 | ✅ | ❌ 仅校验 |
| 写入事件日志 | ✅ | ❌ |
| 返回剩余秒数 | ❌ | ✅ remaining_seconds |
解绑卡密
POST /api/v2/unbind(推荐) GET /api/unbind(legacy)
请求参数
| 参数 | 说明 |
|---|---|
key |
卡密字符串 |
hwid |
当前绑定的机器码 |
instance_id |
软件实例 ID |
返回示例
{"code":0,"msg":"success","data":{"status":"unbound_success"}}
注意事项
- hwid 不匹配 →
code=3 - 每张卡密同时只绑定一个设备;解绑次数上限由后台设置,用尽后返回
code=6,需管理员手动解绑
获取软件信息
POST /api/v2/info(推荐) GET /api/info(legacy)
请求参数
| 参数 | 说明 |
|---|---|
instance_id |
软件实例 ID |
返回示例
{"code":0,"msg":"success","data":{"version":"1.0.2","update_content":"1. 修复BUG\n2. 优化性能"}}
维护模式下
/info不受影响,仍可正常获取。
获取云变量
GET /api/cloudvar
请求参数
| 参数 | 说明 |
|---|---|
key |
变量名 |
instance_id |
软件实例 ID |
返回示例
{"code":0,"msg":"success","data":{"value":"云变量的值","announcement":null}}
发送事件日志
GET /api/log(legacy) POST /api/v2/log(推荐)
请求参数
| 参数 | 说明 |
|---|---|
message |
日志内容,≤2000 字符,legacy 需 URL 编码 |
instance_id |
软件实例 ID |
返回示例
{"code":0,"msg":"success","data":{"status":"logged","announcement":null}}
错误码速查
| code | 说明 |
|---|---|
| 0 | 成功 |
| 1 | 验证失败 / 卡密不存在 |
| 2 | 卡密不属于该实例 |
| 3 | 机器码不匹配(主动解绑) |
| 4 | 卡密已过期 |
| 5 | 卡密已封禁 |
| 6 | 已绑定且无可换绑次数(需人工处理) |
| 7 | 卡密未激活(心跳接口专用) |
维护模式
维护模式按实例独立,每个 instance_id 有自己的开关和公告。
开启入口:网页后台「实例管理」或桌面端「系统设置 → 维护模式」。
| 开启后返回 503 | 不受影响(仍可用) |
|---|---|
/check、/v2/check |
/info、/v2/info |
/unbind、/v2/unbind |
/api/check-update |
/heartbeat、/v2/heartbeat |
/cloudvar |
/api/v2/cloud/fetch |
/log、/v2/log |
503 响应体:{"detail": "管理员设置的维护公告"}
客户端处理:在 raise_for_status() 之前判断 status_code == 503,弹出公告并暂停业务,不要走通用错误处理、不要无限重试。
res = requests.post(url, json=body, headers=headers, timeout=10)
if res.status_code == 503:
show_maintenance_notice(res.json().get("detail", "系统维护中"))
return
res.raise_for_status()
云文件下发(进阶)
安全下发 DLL/TXT/JSON 等任意文件,密钥通过 HKDF 双端派生(不走网络),AES-256-GCM 加密。详细解密算法见下载区「AI 对接文档」。
POST /api/v2/cloud/fetch(需 v2 签名头)
核心参数:HKDF salt = X-Nonce + hwid、info = "wonckami_cloud_fetch_v1"、32 字节 key;payload = base64(nonce12 + tag16 + ciphertext);AAD = "slot|version|checksum"。
模块下载(Python SDK)
下载:/static/docs/whisper_module.zip,含 whisper_client.py + 配置示例。
from 模块 import get_default_client
client = get_default_client(verify_ssl=True)
res = client.check("CARD-XXXX", "MY-DEVICE-001")
print(res.ok, res.message, res.payload)
依赖(如需解密):pip install pycryptodome certifi
需要 AI 一键生成对接代码?下载区「AI 对接文档」可整段复制给 AI 工具,自动产出可运行的客户端。
安全文件下发
Whisper 提供了一套高安全性的文件下发机制,支持 RSA+AES 双重加密传输、一次性 Token 与内存流式解密,有效防御抓包、重放与中间人攻击。
核心特性
- 双重加密:请求体使用 AES 加密,AES 密钥使用 RSA 封装,确保传输安全。
- 一次性令牌:下载链接绑定 IP 与一次性 Token,防止链接泄露与盗链。
- 流式解密:文件内容通过 AES-GCM 流式加密传输,客户端在内存中解密,不落地明文文件。
Python 客户端示例
以下代码展示了如何进行安全握手、验证卡密并流式下载解密文件。
import requests
import base64
import json
import time
import os
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Hash import SHA256
SERVER_URL = "http://localhost:8000"
# 请替换为您的真实卡密
CARD_KEY = "YOUR_CARD_KEY"
GCM_NONCE_SIZE = 12
GCM_TAG_SIZE = 16
def get_server_public_key():
resp = requests.get(f"{SERVER_URL}/api/v2/secure/public_key")
resp.raise_for_status()
return RSA.import_key(resp.json()["public_key"])
def encrypt_aes_gcm(key, plaintext):
cipher = AES.new(key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
return ciphertext, cipher.nonce, tag
def decrypt_aes_gcm(key, ciphertext, nonce, tag):
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
return cipher.decrypt_and_verify(ciphertext, tag)
def main():
# 1. 获取公钥
pub_key = get_server_public_key()
# 2. 准备加密请求
aes_key = get_random_bytes(32)
payload = {
"card_key": CARD_KEY,
"timestamp": time.time(),
"nonce": os.urandom(8).hex()
}
# AES 加密数据
data_bytes = json.dumps(payload).encode()
ciphertext, nonce, tag = encrypt_aes_gcm(aes_key, data_bytes)
encrypted_data = base64.b64encode(nonce + tag + ciphertext).decode()
# RSA 加密 AES 密钥
cipher_rsa = PKCS1_OAEP.new(pub_key, hashAlgo=SHA256)
encrypted_key = base64.b64encode(cipher_rsa.encrypt(aes_key)).decode()
# 3. 发送验证请求
resp = requests.post(f"{SERVER_URL}/api/v2/secure/verify", json={
"encrypted_key": encrypted_key,
"encrypted_data": encrypted_data
})
if resp.status_code != 200:
print(f"[-] 验证失败: {resp.text}")
return
# 4. 解密响应
resp_json = resp.json()
enc_payload = base64.b64decode(resp_json["payload"])
nonce_resp = enc_payload[:GCM_NONCE_SIZE]
tag_resp = enc_payload[GCM_NONCE_SIZE:GCM_NONCE_SIZE+GCM_TAG_SIZE]
ciphertext_resp = enc_payload[GCM_NONCE_SIZE+GCM_TAG_SIZE:]
plaintext_resp = decrypt_aes_gcm(aes_key, ciphertext_resp, nonce_resp, tag_resp)
data = json.loads(plaintext_resp)
print(f"[+] 验证成功,文件版本: {data['file_info']['version']}")
# 5. 下载文件
token = data["token"]
file_key = bytes.fromhex(data["file_key"])
download_url = f"{SERVER_URL}{data['download_url'].split('?')[0]}"
print("[*] 开始安全下载...")
with requests.get(download_url, params={"token": token}, stream=True) as r:
# 流式解密逻辑...
pass
Whisper