Documentation

接入文档

API 地址、签名/加密、卡密校验与运营能力,快速完成集成。

文档下载

Whisper 对接文档 (推荐 v2)
大小 8.79 KB · 下载 2544
下载
API接口文档.txt
大小 9.76 KB · 下载 81
下载
Ai对接文档.txt
大小 17.84 KB · 下载 287
下载

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:大写,如 POST
  • PATH:完整路径含 /api 前缀,如 /api/v2/check,不含域名和 query
  • BODY_SHA256sha256(原始 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

  1. 取所有 query 参数(排除 sign),按参数名 ASCII 升序排序
  2. 拼接:key1=value1&key2=value2...
  3. 末尾追加 secret_key
  4. 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 可选,开启加密返回时替代 dataenc_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、不写事件日志,仅校验状态。

请求参数

/checkkey + 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 + hwidinfo = "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
Full size preview