🔧 chore: 初始化番茄小说 API 项目结构
新增项目基础框架,包含核心 API 实现、算法模块、HTTP 处理器等完整功能模块
This commit is contained in:
commit
6981148727
107 files changed
+12463
No files matched your search
@@ -0,0 +1,384 @@
|
||||
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("设备注册失败")
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user