mirror of
https://github.com/AynaLivePlayer/miaosic.git
synced 2025-12-13 16:28:13 +08:00
fix session cmd: save session every in every command add qq init fix init sequence update gitignore fix miaosic cmd add kugou album info in search update qq music api.
279 lines
7.5 KiB
Go
279 lines
7.5 KiB
Go
package qq
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/AynaLivePlayer/miaosic"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
QiMeiPublicKey = `-----BEGIN PUBLIC KEY-----
|
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9qaIuS0qzfR8gWkrkTZKM2iWHn2ajQpBRZjMSoSf6+KJGvar2ORhBfpDXyVtZCKpqLQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B9NMbHddGSAUmRTCrHQIDAQAB
|
|
-----END PUBLIC KEY-----`
|
|
QiMeiSecret = "ZdJqM15EeO2zWc08"
|
|
QiMeiAppKey = "0AND0HD6FE4HY80F"
|
|
)
|
|
|
|
type QimeiResult struct {
|
|
Q16 string `json:"q16"`
|
|
Q36 string `json:"q36"`
|
|
}
|
|
|
|
type qimeiReserved struct {
|
|
Harmony string `json:"harmony"`
|
|
Clone string `json:"clone"`
|
|
Containe string `json:"containe"`
|
|
Oz string `json:"oz"`
|
|
Oo string `json:"oo"`
|
|
Kelong string `json:"kelong"`
|
|
Uptimes string `json:"uptimes"`
|
|
MultiUser string `json:"multiUser"`
|
|
Bod string `json:"bod"`
|
|
Dv string `json:"dv"`
|
|
FirstLevel string `json:"firstLevel"`
|
|
Manufact string `json:"manufact"`
|
|
Name string `json:"name"`
|
|
Host string `json:"host"`
|
|
Kernel string `json:"kernel"`
|
|
}
|
|
|
|
type qimeiPayload struct {
|
|
AndroidID string `json:"androidId"`
|
|
PlatformID int `json:"platformId"`
|
|
AppKey string `json:"appKey"`
|
|
AppVersion string `json:"appVersion"`
|
|
BeaconIDSrc string `json:"beaconIdSrc"`
|
|
Brand string `json:"brand"`
|
|
ChannelID string `json:"channelId"`
|
|
Cid string `json:"cid"`
|
|
Imei string `json:"imei"`
|
|
Imsi string `json:"imsi"`
|
|
Mac string `json:"mac"`
|
|
Model string `json:"model"`
|
|
NetworkType string `json:"networkType"`
|
|
Oaid string `json:"oaid"`
|
|
OSVersion string `json:"osVersion"`
|
|
Qimei string `json:"qimei"`
|
|
Qimei36 string `json:"qimei36"`
|
|
SDKVersion string `json:"sdkVersion"`
|
|
TargetSDKVersion string `json:"targetSdkVersion"`
|
|
Audit string `json:"audit"`
|
|
UserID string `json:"userId"`
|
|
PackageID string `json:"packageId"`
|
|
DeviceType string `json:"deviceType"`
|
|
SDKName string `json:"sdkName"`
|
|
Reserved string `json:"reserved"`
|
|
}
|
|
|
|
type qimeiParams struct {
|
|
Key string `json:"key"`
|
|
Params string `json:"params"`
|
|
Time string `json:"time"`
|
|
Nonce string `json:"nonce"`
|
|
Sign string `json:"sign"`
|
|
Extra string `json:"extra"`
|
|
}
|
|
|
|
type qimeiRequest struct {
|
|
App int `json:"app"`
|
|
Os int `json:"os"`
|
|
QimeiParams qimeiParams `json:"qimeiParams"`
|
|
}
|
|
|
|
type qimeiResponse struct {
|
|
Data string `json:"data"`
|
|
}
|
|
|
|
type qimeiData struct {
|
|
Data struct {
|
|
Q16 string `json:"q16"`
|
|
Q36 string `json:"q36"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
func qimeiRandomBeaconID() string {
|
|
timeMonth := time.Now().Format("2006-01-") + "01"
|
|
rand1 := rng.Intn(900000) + 100000
|
|
rand2 := rng.Intn(900000000) + 100000000
|
|
|
|
var parts []string
|
|
for i := 1; i <= 40; i++ {
|
|
switch i {
|
|
case 1, 2, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30, 33, 34, 37, 38:
|
|
parts = append(parts, fmt.Sprintf("k%d:%s%d.%d", i, timeMonth, rand1, rand2))
|
|
case 3:
|
|
parts = append(parts, "k3:0000000000000000")
|
|
case 4:
|
|
// 生成16位随机十六进制
|
|
buf := make([]byte, 8)
|
|
rng.Read(buf)
|
|
parts = append(parts, fmt.Sprintf("k4:%x", buf))
|
|
default:
|
|
parts = append(parts, fmt.Sprintf("k%d:%d", i, rng.Intn(10000)))
|
|
}
|
|
}
|
|
return strings.Join(parts, ";")
|
|
}
|
|
|
|
func qimeiRandomPayloadByDevice(device *Device, version string) *qimeiPayload {
|
|
fixedRand := rng.Intn(14400)
|
|
uptime := time.Now().Add(-time.Duration(fixedRand) * time.Second).Format("2006-01-02 15:04:05")
|
|
|
|
reserved := &qimeiReserved{
|
|
Harmony: "0",
|
|
Clone: "0",
|
|
Containe: "",
|
|
Oz: "UhYmelwouA+V2nPWbOvLTgN2/m8jwGB+yUB5v9tysQg=",
|
|
Oo: "Xecjt+9S1+f8Pz2VLSxgpw==",
|
|
Kelong: "0",
|
|
Uptimes: uptime,
|
|
MultiUser: "0",
|
|
Bod: device.Brand,
|
|
Dv: device.Device,
|
|
FirstLevel: "",
|
|
Manufact: device.Brand,
|
|
Name: device.Model,
|
|
Host: "se.infra",
|
|
Kernel: device.ProcVersion,
|
|
}
|
|
|
|
reservedJSON, _ := json.Marshal(reserved)
|
|
|
|
return &qimeiPayload{
|
|
AndroidID: device.AndroidID,
|
|
PlatformID: 1,
|
|
AppKey: QiMeiAppKey,
|
|
AppVersion: version,
|
|
BeaconIDSrc: qimeiRandomBeaconID(),
|
|
Brand: device.Brand,
|
|
ChannelID: "10003505",
|
|
Cid: "",
|
|
Imei: device.IMEI,
|
|
Imsi: "",
|
|
Mac: "",
|
|
Model: device.Model,
|
|
NetworkType: "unknown",
|
|
Oaid: "",
|
|
OSVersion: fmt.Sprintf("Android %s,level %d", device.Version.Release, device.Version.Sdk),
|
|
Qimei: "",
|
|
Qimei36: "",
|
|
SDKVersion: version,
|
|
TargetSDKVersion: "33",
|
|
Audit: "",
|
|
UserID: "{}",
|
|
PackageID: "com.tencent.qqmusic",
|
|
DeviceType: "Phone",
|
|
SDKName: "",
|
|
Reserved: string(reservedJSON),
|
|
}
|
|
}
|
|
|
|
func getQimei(device *Device, version string) (*QimeiResult, error) {
|
|
result, err := fetchQimei(device, version)
|
|
if err == nil {
|
|
device.Qimei = result.Q36
|
|
return result, nil
|
|
}
|
|
if device.Qimei != "" {
|
|
return &QimeiResult{Q16: "", Q36: device.Qimei}, nil
|
|
}
|
|
return &QimeiResult{Q16: "", Q36: "6c9d3cd110abca9b16311cee10001e717614"}, nil
|
|
}
|
|
|
|
func qimeiRandomString(length int) string {
|
|
const charset = "abcdef0123456789"
|
|
result := make([]byte, length)
|
|
for i := range result {
|
|
result[i] = charset[rng.Intn(len(charset))]
|
|
}
|
|
return string(result)
|
|
}
|
|
|
|
// 从腾讯API获取QIMEI
|
|
func fetchQimei(device *Device, version string) (*QimeiResult, error) {
|
|
payload := qimeiRandomPayloadByDevice(device, version)
|
|
payloadJSON, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 生成加密密钥和随机数
|
|
cryptKey := qimeiRandomString(16)
|
|
nonce := qimeiRandomString(16)
|
|
ts := time.Now().UnixMilli()
|
|
|
|
// RSA加密密钥
|
|
rsaEncryptedKey, err := rsaEncrypt([]byte(cryptKey), QiMeiPublicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keyBase64 := base64.StdEncoding.EncodeToString(rsaEncryptedKey)
|
|
|
|
// AES加密payload
|
|
aesEncrypted, err := aesEncrypt([]byte(cryptKey), payloadJSON)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
paramsBase64 := base64.StdEncoding.EncodeToString(aesEncrypted)
|
|
|
|
// 生成签名
|
|
extra := fmt.Sprintf(`{"appKey":"%s"}`, QiMeiAppKey)
|
|
sign := calcMd5(keyBase64, paramsBase64, fmt.Sprintf("%d", ts), nonce, QiMeiSecret, extra)
|
|
|
|
// 构建请求
|
|
qimeiParams := qimeiParams{
|
|
Key: keyBase64,
|
|
Params: paramsBase64,
|
|
Time: fmt.Sprintf("%d", ts),
|
|
Nonce: nonce,
|
|
Sign: sign,
|
|
Extra: extra,
|
|
}
|
|
|
|
requestBody := qimeiRequest{
|
|
App: 0,
|
|
Os: 1,
|
|
QimeiParams: qimeiParams,
|
|
}
|
|
|
|
requestJSON, err := json.Marshal(requestBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := miaosic.Requester.Post(
|
|
"https://api.tencentmusic.com/tme/trpc/proxy",
|
|
map[string]string{
|
|
"Host": "api.tencentmusic.com",
|
|
"method": "GetQimei",
|
|
"service": "trpc.tme_datasvr.qimeiproxy.QimeiProxy",
|
|
"appid": "qimei_qq_android",
|
|
"sign": calcMd5("qimei_qq_androidpzAuCmaFAaFaHrdakPjLIEqKrGnSOOvH", fmt.Sprintf("%d", ts/1000)),
|
|
"user-agent": "QQMusic",
|
|
"timestamp": fmt.Sprintf("%d", ts/1000),
|
|
"Content-Type": "application/json",
|
|
}, requestJSON,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var qimeiResp qimeiResponse
|
|
if err := json.Unmarshal(resp.Body(), &qimeiResp); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var qimeiData qimeiData
|
|
if err := json.Unmarshal([]byte(qimeiResp.Data), &qimeiData); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &QimeiResult{
|
|
Q16: qimeiData.Data.Q16,
|
|
Q36: qimeiData.Data.Q36,
|
|
}, nil
|
|
}
|