- 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 顺手格式化
384 lines
10 KiB
Go
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("设备注册失败")
|
|
}
|