160 lines
5.5 KiB
Python
160 lines
5.5 KiB
Python
"""
|
|
Admin balance fetchers for Kimi (Moonshot), DeepSeek, and Qwen (Aliyun BSS).
|
|
|
|
Credentials (read from environment):
|
|
- MOONSHOT_API_KEY : Bearer token for Kimi
|
|
- DEEPSEEK_API_KEY : Bearer token for DeepSeek
|
|
- ALIYUN_ACCESS_KEY_ID : AccessKey ID for Aliyun (Qwen billing)
|
|
- ALIYUN_ACCESS_KEY_SECRET : AccessKey Secret for Aliyun (Qwen billing)
|
|
- USD_CNY_RATE : Override USD->CNY rate (default 7.1)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
import os
|
|
import time
|
|
import uuid
|
|
from typing import Any, Dict, Tuple
|
|
from urllib import parse, request, error
|
|
|
|
|
|
USD_CNY_RATE = float(os.environ.get("USD_CNY_RATE", "7.1"))
|
|
|
|
|
|
def _http_get(url: str, headers: Dict[str, str] | None = None, timeout: int = 8) -> Tuple[Dict[str, Any] | None, str | None]:
|
|
"""Perform a simple GET request, return (json, error_message)."""
|
|
req = request.Request(url, headers=headers or {})
|
|
try:
|
|
with request.urlopen(req, timeout=timeout) as resp:
|
|
data = resp.read()
|
|
return json.loads(data.decode("utf-8")), None
|
|
except Exception as exc: # broad: network/parsing errors
|
|
return None, str(exc)
|
|
|
|
|
|
# -------- Kimi (Moonshot) --------
|
|
def fetch_kimi_balance() -> Dict[str, Any]:
|
|
api_key = os.environ.get("MOONSHOT_API_KEY")
|
|
if not api_key:
|
|
return {"success": False, "error": "MOONSHOT_API_KEY 未设置"}
|
|
|
|
url = "https://api.moonshot.ai/v1/users/me/balance"
|
|
payload, err = _http_get(url, headers={"Authorization": f"Bearer {api_key}"})
|
|
if err:
|
|
return {"success": False, "error": err}
|
|
|
|
try:
|
|
data = payload.get("data") or {}
|
|
available = float(data.get("available_balance", 0))
|
|
voucher = float(data.get("voucher_balance", 0))
|
|
cash = float(data.get("cash_balance", 0))
|
|
return {
|
|
"success": True,
|
|
"currency": "USD",
|
|
"available": available,
|
|
"available_cny": round(available * USD_CNY_RATE, 2),
|
|
"voucher": voucher,
|
|
"cash": cash,
|
|
"rate": USD_CNY_RATE,
|
|
"raw": payload,
|
|
}
|
|
except Exception as exc: # pragma: no cover
|
|
return {"success": False, "error": f"解析失败: {exc}", "raw": payload}
|
|
|
|
|
|
# -------- DeepSeek --------
|
|
def fetch_deepseek_balance() -> Dict[str, Any]:
|
|
api_key = os.environ.get("DEEPSEEK_API_KEY")
|
|
if not api_key:
|
|
return {"success": False, "error": "DEEPSEEK_API_KEY 未设置"}
|
|
|
|
url = "https://api.deepseek.com/user/balance"
|
|
payload, err = _http_get(url, headers={"Authorization": f"Bearer {api_key}"})
|
|
if err:
|
|
return {"success": False, "error": err}
|
|
|
|
try:
|
|
infos = payload.get("balance_infos") or []
|
|
primary = infos[0] if infos else {}
|
|
total = float(primary.get("total_balance") or 0)
|
|
granted = float(primary.get("granted_balance") or 0)
|
|
topped_up = float(primary.get("topped_up_balance") or 0)
|
|
return {
|
|
"success": True,
|
|
"currency": primary.get("currency", "CNY"),
|
|
"available": total,
|
|
"granted": granted,
|
|
"topped_up": topped_up,
|
|
"raw": payload,
|
|
}
|
|
except Exception as exc: # pragma: no cover
|
|
return {"success": False, "error": f"解析失败: {exc}", "raw": payload}
|
|
|
|
|
|
# -------- Qwen (Aliyun BSS QueryAccountBalance) --------
|
|
def _percent_encode(val: str) -> str:
|
|
res = parse.quote(str(val), safe="~")
|
|
return res.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
|
|
|
|
|
|
def _sign(params: Dict[str, Any], secret: str) -> str:
|
|
sorted_params = sorted(params.items(), key=lambda x: x[0])
|
|
canonicalized = "&".join(f"{_percent_encode(k)}={_percent_encode(v)}" for k, v in sorted_params)
|
|
string_to_sign = f"GET&%2F&{_percent_encode(canonicalized)}"
|
|
h = hmac.new((secret + "&").encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1)
|
|
return base64.b64encode(h.digest()).decode("utf-8")
|
|
|
|
|
|
def fetch_qwen_balance() -> Dict[str, Any]:
|
|
ak = os.environ.get("ALIYUN_ACCESS_KEY_ID")
|
|
sk = os.environ.get("ALIYUN_ACCESS_KEY_SECRET")
|
|
if not ak or not sk:
|
|
return {"success": False, "error": "缺少 ALIYUN_ACCESS_KEY_ID / ALIYUN_ACCESS_KEY_SECRET"}
|
|
|
|
params: Dict[str, Any] = {
|
|
"Format": "JSON",
|
|
"Version": "2017-12-14",
|
|
"AccessKeyId": ak,
|
|
"SignatureMethod": "HMAC-SHA1",
|
|
"Timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
"SignatureVersion": "1.0",
|
|
"SignatureNonce": str(uuid.uuid4()),
|
|
"Action": "QueryAccountBalance",
|
|
"RegionId": "cn-hangzhou",
|
|
}
|
|
signature = _sign(params, sk)
|
|
params["Signature"] = signature
|
|
url = "https://bss.aliyuncs.com/?" + parse.urlencode(params)
|
|
|
|
payload, err = _http_get(url)
|
|
if err:
|
|
return {"success": False, "error": err}
|
|
|
|
try:
|
|
data = payload.get("Data") or {}
|
|
amount = float(data.get("AvailableAmount") or 0)
|
|
currency = data.get("Currency", "CNY")
|
|
return {
|
|
"success": True,
|
|
"currency": currency,
|
|
"available": amount,
|
|
"cash": float(data.get("AvailableCashAmount") or 0),
|
|
"raw": payload,
|
|
}
|
|
except Exception as exc: # pragma: no cover
|
|
return {"success": False, "error": f"解析失败: {exc}", "raw": payload}
|
|
|
|
|
|
def fetch_all_balances() -> Dict[str, Any]:
|
|
return {
|
|
"rate_usd_cny": USD_CNY_RATE,
|
|
"kimi": fetch_kimi_balance(),
|
|
"deepseek": fetch_deepseek_balance(),
|
|
"qwen": fetch_qwen_balance(),
|
|
}
|
|
|