Files
fanqie-go/internal/fqapi/device_register.go
T
chenxin 5bcc8a962e ♻️ refactor(handlers): status 与 review 页面模板化
- HTML 提取至 templates/status.html、templates/review.html(go:embed 内嵌)
- status.go 1022→271 行、review.go 1281→438 行,动态数据全部经 html/template 按上下文自动转义
- 评论循环/作者徽章/贴纸/图片条件渲染翻译为模板 range/if;emojiMap 以 template.JS 注入
- 与旧版渲染输出实测对比:22 条评论结构完全一致;差异仅为 JS 上下文安全转义(& 等)与 html/template 剥除 CSS 注释,均无功能影响
- 其余文件为 gofmt 顺手格式化
2026-09-30 00:25:09 +08:00

384 lines
10 KiB
Go

package fqapi
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fanqie/internal/common"
"fmt"
"math/big"
"net"
"os"
"runtime"
"sort"
"strconv"
"strings"
"time"
"github.com/google/uuid"
)
// 使用共享的设备配置
var (
androidVersions = DeviceConfig.AndroidVersions
brandModels = DeviceConfig.BrandModels
deviceBrands = DeviceConfig.DeviceBrands
)
// generateUUID 生成UUID4字符串
func generateUUID() string {
return uuid.New().String()
}
// currentMillis 返回当前毫秒级时间戳
func currentMillis() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
}
// getCustomBT 获取设备开机时间戳
// 模拟 Android 中的 m25453o() 方法,计算设备开机时间戳:
// custom_bt = current_millis - elapsed_ms_since_boot
func getCustomBT() string {
var uptimeSeconds float64
if runtime.GOOS == "linux" {
// 在Linux上读取/proc/uptime
data, err := os.ReadFile("/proc/uptime")
if err == nil {
// /proc/uptime格式: "系统运行时间(秒) 空闲时间(秒)"
parts := strings.Split(string(data), " ")
if len(parts) > 0 {
uptime, err := strconv.ParseFloat(strings.TrimSpace(parts[0]), 64)
if err == nil {
uptimeSeconds = uptime
}
}
}
// 如果读取失败,使用time.monotonic作为备选
if uptimeSeconds == 0 {
// Go没有直接的monotonic API,使用启动时间估算
uptimeSeconds = float64(time.Now().Unix() % 86400)
}
} else {
// 非Linux系统,使用估算值
// 这里可以使用进程启动时间作为近似
uptimeSeconds = float64(time.Now().Unix() % 86400)
}
return fmt.Sprintf("%d", currentMillis()-int64(uptimeSeconds*1000))
}
// generateDeviceInfo 随机生成设备及操作系统信息
func generateDeviceInfo() map[string]interface{} {
osVersion := androidVersions[randInt(len(androidVersions))]
// 随机选择品牌
deviceBrand := deviceBrands[randInt(len(deviceBrands))]
// 根据品牌选择对应的型号
models := brandModels[deviceBrand]
deviceModel := models[randInt(len(models))]
releaseBytes := make([]byte, 7)
rand.Read(releaseBytes)
releaseBuild := hex.EncodeToString(releaseBytes)
openUDIDBytes := make([]byte, 10)
rand.Read(openUDIDBytes)
return map[string]interface{}{
"os": "Android",
"os_version": osVersion[0],
"os_api": osVersion[1],
"device_model": deviceModel,
"device_brand": deviceBrand,
"device_manufacturer": deviceBrand,
"cpu_abi": "arm64-v8a",
"release_build": releaseBuild,
"cdid": generateUUID(),
"sig_hash": "a4a27c2633195374c15651ffc3c4a497",
"openudid": hex.EncodeToString(openUDIDBytes),
"clientudid": generateUUID(),
"req_id": generateUUID(),
}
}
// randInt 生成随机整数
func randInt(max int) int {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
return int(n.Int64())
}
// randomString 生成随机字符串
func randomString(length int) string {
const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
b := make([]byte, length)
for i := range b {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
b[i] = charset[n.Int64()]
}
return string(b)
}
// generateOptionalFields 生成ROM信息等
func generateOptionalFields() (map[string]interface{}, map[string]int64) {
rom := randomString(14)
part1 := randomString(4)
part2 := randomString(6)
part3 := randomString(8)
genTime := currentMillis()
return map[string]interface{}{
"rom": rom,
"rom_version": fmt.Sprintf("coloros__%s.%s.%s release-keys", part1, part2, part3),
"apk_first_install_time": genTime - int64(randInt(365))*86400000,
}, map[string]int64{
"_gen_time": genTime,
}
}
// generateIPv6AddressLinkLocal 生成链路本地IPv6地址
func generateIPv6AddressLinkLocal() string {
ifaces, err := net.Interfaces()
if err != nil {
return "::1"
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}
ip := ipNet.IP
if ip.To16() != nil && strings.HasPrefix(ip.String(), "fe80::") {
return strings.Split(ip.String(), "%")[0]
}
}
}
return "::1"
}
// generateIPv6AddressUniqueLocal 生成唯一本地IPv6地址
func generateIPv6AddressUniqueLocal() string {
b := make([]byte, 1)
rand.Read(b)
parts := []string{fmt.Sprintf("fd%02x", b[0])}
for i := 0; i < 7; i++ {
b := make([]byte, 2)
rand.Read(b)
parts = append(parts, fmt.Sprintf("%04x", uint16(b[0])<<8|uint16(b[1])))
}
return strings.Join(parts, ":")
}
// generateIPv6List 生成IPv6列表
func generateIPv6List() []map[string]string {
return []map[string]string{
{"type": "client_anpi", "value": generateIPv6AddressLinkLocal()},
{"type": "client_anpi", "value": generateIPv6AddressUniqueLocal()},
{"type": "client_anpi", "value": generateIPv6AddressUniqueLocal()},
}
}
// GenerateFullRequestBody 生成完整的请求体数据
func GenerateFullRequestBody() map[string]interface{} {
rawData := map[string]interface{}{
"magic_tag": "ss_app_log",
"header": map[string]interface{}{
"display_name": "番茄免费小说",
"aid": 1967,
"channel": "43536163a",
"package": "com.dragon.read",
"sdk_version": "3.7.0-rc.25-fanqie-xiaoshuo",
"sdk_target_version": 29,
"git_hash": "711d1a7",
"density_dpi": 240,
"display_density": "hdpi",
"resolution": "720x1280",
"language": "zh",
"timezone": 8,
"access": "wifi",
"not_request_sender": 0,
"carrier": "CHINA MOBILE",
"mcc_mnc": "46000",
"region": "CN",
"tz_name": "Asia/Shanghai",
"tz_offset": 28800,
"sim_region": "cn",
"sim_serial_number": []interface{}{},
"oaid_may_support": false,
"device_platform": "android",
"custom": map[string]interface{}{"host_bit": 32, "dragon_device_type": "pad"},
"pre_installed_channel": "",
"is_system_app": 0,
"sdk_flavor": "china",
"guest_mode": 0,
},
}
deviceInfo := generateDeviceInfo()
optionalFields, genTime := generateOptionalFields()
header := rawData["header"].(map[string]interface{})
for k, v := range deviceInfo {
header[k] = v
}
for k, v := range optionalFields {
header[k] = v
}
header["ipv6_list"] = generateIPv6List()
for k, v := range genTime {
rawData[k] = v
}
return rawData
}
// tryActivatePremium 尝试激活设备特权
func tryActivatePremium(header string) {
logger := common.NewLogger("device_register", "INFO", "")
// 构建查询参数
baseParams := []string{
"aid=1967",
"app_name=novelapp",
"channel=0",
"device_platform=android",
"os_version=0",
"version_code=58932",
"manifest_version_code=58932",
"update_version_code=58932",
header,
}
// 合并并排序参数
allParams := strings.Join(baseParams, "&")
paramList := strings.Split(allParams, "&")
sort.Strings(paramList)
sortedParams := strings.Join(paramList, "&")
requestBody := map[string]interface{}{
"add_count_daily": 0,
"amount": 2592000,
"privilege_id": 7210376203117531962,
"from": 8,
"unique_key": fmt.Sprintf("%d", time.Now().UnixNano()/1e6),
}
bodyJSON, _ := json.Marshal(requestBody)
url := "https://reading.snssdk.com/reading/user/privilege/add/v/?" + sortedParams
resp, err := GorgonRequestSync(url, map[string]interface{}{
"method": "post",
"headers": map[string]string{
"content-type": "application/json",
},
"body": bodyJSON,
})
if err != nil {
logger.Error("激活特权失败: " + err.Error())
return
}
var result map[string]interface{}
json.Unmarshal(resp.Content, &result)
if code, ok := result["code"].(float64); ok && code == 0 {
logger.Info("已成功激活设备特权")
} else {
logger.Warning("设备特权激活失败...")
}
}
// Register 设备注册
func Register() (string, error) {
logger := common.NewLogger("device_register", "INFO", "")
logger.Info("正在注册设备...")
// 生成完整请求体数据
data := GenerateFullRequestBody()
// 只记录关键设备信息
header := data["header"].(map[string]interface{})
logger.Info(fmt.Sprintf("生成设备信息 - 品牌: %s, 型号: %s, 系统: Android %v",
header["device_brand"],
header["device_model"],
header["os_version"]))
jsonData, err := json.Marshal(data)
if err != nil {
logger.Error("JSON序列化失败: " + err.Error())
return "", err
}
// 使用TTEncrypt加密
encrypted, err := TTEncrypt(jsonData)
if err != nil {
logger.Error("加密失败: " + err.Error())
return "", err
}
url := "https://i.snssdk.com/service/2/device_register/?tt_data=a"
headers := map[string]string{
"user-agent": "okhttp/4.10.0",
"content-type": "application/octet-stream; tt-data=a",
}
client := common.NewHTTPClient()
resp, err := client.Post(url, encrypted, headers)
if err != nil {
logger.Warning("设备注册失败!")
logger.Warning(err.Error())
return "", fmt.Errorf("设备注册错误")
}
var respData map[string]interface{}
err = json.Unmarshal(resp.Content, &respData)
if err != nil {
logger.Error("响应解析失败: " + err.Error())
return "", err
}
if resp.Status == 200 {
// 检查device_id
deviceID, ok := respData["device_id"]
if !ok {
logger.Warning("设备注册失败!响应中没有device_id")
return "", fmt.Errorf("设备注册失败")
}
// 转换device_id为float64并检查
deviceIDFloat, ok := deviceID.(float64)
if !ok || deviceIDFloat == 0 {
logger.Warning("设备注册失败!device_id无效")
return "", fmt.Errorf("设备注册失败")
}
deviceModel := data["header"].(map[string]interface{})["device_model"].(string)
header := fmt.Sprintf("device_id=%v&device_type=%s&iid=%v&version_name=5.8.9.32",
respData["device_id_str"],
deviceModel,
respData["install_id_str"])
logger.Info("设备注册成功!")
// 尝试激活特权
tryActivatePremium(header)
return header, nil
}
logger.Warning("设备注册失败!")
return "", fmt.Errorf("设备注册失败")
}