diff --git a/drivers/123/driver.go b/drivers/123/driver.go index aeda7fcf742..7bfe14421c4 100644 --- a/drivers/123/driver.go +++ b/drivers/123/driver.go @@ -30,6 +30,7 @@ type Pan123 struct { model.Storage Addition apiRateLimit sync.Map + params Params } func (d *Pan123) Config() driver.Config { @@ -41,6 +42,25 @@ func (d *Pan123) GetAddition() driver.Additional { } func (d *Pan123) Init(ctx context.Context) error { + // 拼接UserAgent + if d.PlatformType == "android" { + d.params.UserAgent = AndroidUserAgentPrefix + "(" + d.OsVersion + ";" + d.DeviceName + " " + d.DeiveType + ")" + d.params.Platform = AndroidPlatformParam + d.params.AppVersion = AndroidAppVer + d.params.XChannel = AndroidXChannel + d.params.XAppVersion = AndroidXAppVer + + } else if d.PlatformType == "tv" { + d.params.UserAgent = TVUserAgentPrefix + "(" + d.OsVersion + ";" + d.DeviceName + " " + d.DeiveType + ")" + d.params.Platform = TVPlatformParam + d.params.AppVersion = TVAndroidAppVer + } + + d.params.OsVersion = d.OsVersion + d.params.LoginUuid = d.LoginUuid + d.params.DeviceName = d.DeviceName + d.params.DeviceType = d.DeiveType + _, err := d.request(UserInfo, http.MethodGet, nil, nil) return err } @@ -65,13 +85,6 @@ func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) ( func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { if f, ok := file.(File); ok { //var resp DownResp - var headers map[string]string - if !utils.IsLocalIPAddr(args.IP) { - headers = map[string]string{ - //"X-Real-IP": "1.1.1.1", - "X-Forwarded-For": args.IP, - } - } data := base.Json{ "driveId": 0, "etag": f.Etag, @@ -82,7 +95,7 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) "type": f.Type, } resp, err := d.request(DownloadInfo, http.MethodPost, func(req *resty.Request) { - req.SetBody(data).SetHeaders(headers) + req.SetBody(data) }, nil) if err != nil { return nil, err @@ -257,7 +270,7 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr func (d *Pan123) APIRateLimit(ctx context.Context, api string) error { value, _ := d.apiRateLimit.LoadOrStore(api, - rate.NewLimiter(rate.Every(700*time.Millisecond), 1)) + rate.NewLimiter(rate.Every(800*time.Millisecond), 1)) limiter := value.(*rate.Limiter) return limiter.Wait(ctx) diff --git a/drivers/123/meta.go b/drivers/123/meta.go index cb2cbc15ba0..4694f585776 100644 --- a/drivers/123/meta.go +++ b/drivers/123/meta.go @@ -6,12 +6,19 @@ import ( ) type Addition struct { - Username string `json:"username" required:"true"` - Password string `json:"password" required:"true"` + Username string `json:"username"` + Password string `json:"password"` + UseQrCodeLogin bool `json:"use_qr_code_login"` + UniID string `json:"uni_id"` driver.RootID //OrderBy string `json:"order_by" type:"select" options:"file_id,file_name,size,update_at" default:"file_name"` //OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"` - AccessToken string + AccessToken string `json:"accesstoken" type:"text"` + PlatformType string `json:"platformType" type:"select" options:"android,tv" default:"android" required:"true"` + DeviceName string `json:"devicename" default:"Xiaomi"` + DeiveType string `json:"devicetype" default:"M1810E5A"` + OsVersion string `json:"osversion" default:"Android_8.1.0"` + LoginUuid string `json:"loginuuid" default:"66a4c82756134af2a57d323fd7a5e58c"` } var config = driver.Config{ diff --git a/drivers/123/types.go b/drivers/123/types.go index a8682c52fc9..81738676973 100644 --- a/drivers/123/types.go +++ b/drivers/123/types.go @@ -121,3 +121,21 @@ type S3PreSignedURLs struct { PreSignedUrls map[string]string `json:"presignedUrls"` } `json:"data"` } + +type QrCodeGenerateResp struct { + Data struct { + UniID string `json:"uniID"` + Url string `json:"url"` + } `json:"data"` +} + +type QrCodeResultResp struct { + Data struct { + Expire time.Time `json:"expire"` + LoginType int `json:"login_type"` + RefreshTokenExpireTime int `json:"refresh_token_expire_time"` + Token string `json:"token"` + LoginStatus int `json:"loginStatus"` + ScanPlatform int `json:"scanPlatform"` + } `json:"data"` +} diff --git a/drivers/123/util.go b/drivers/123/util.go index 73c73b3b3b3..c494dcbf1ae 100644 --- a/drivers/123/util.go +++ b/drivers/123/util.go @@ -2,8 +2,12 @@ package _123 import ( "context" + "encoding/base64" "errors" "fmt" + "github.com/alist-org/alist/v3/internal/op" + "github.com/google/uuid" + "github.com/skip2/go-qrcode" "hash/crc32" "math" "math/rand" @@ -26,7 +30,7 @@ const ( Api = "https://www.123pan.com/api" AApi = "https://www.123pan.com/a/api" BApi = "https://www.123pan.com/b/api" - MainApi = BApi + MainApi = Api SignIn = MainApi + "/user/sign_in" Logout = MainApi + "/user/logout" UserInfo = MainApi + "/user/info" @@ -43,8 +47,33 @@ const ( UploadCompleteV2 = MainApi + "/file/upload_complete/v2" S3Complete = MainApi + "/file/s3_complete_multipart_upload" //AuthKeySalt = "8-8D$sL8gPjom7bk#cY" + QrcodeGenerate = MainApi + "/user/qr-code/generate" + QrcodeResult = MainApi + "/user/qr-code/result" ) +const ( + AndroidUserAgentPrefix = "123pan/v2.4.7" // 123pan/v2.4.7(Android_14;XiaoMi) + AndroidPlatformParam = "android" + AndroidAppVer = "69" + AndroidXAppVer = "2.4.7" + AndroidXChannel = "1002" + TVUserAgentPrefix = "123pan_android_tv/1.0.0" // 123pan_android_tv/1.0.0(14;samsung SM-X800) + TVPlatformParam = "android_tv" + TVAndroidAppVer = "100" +) + +type Params struct { + UserAgent string + Platform string + AppVersion string + OsVersion string + LoginUuid string + DeviceType string + DeviceName string + XChannel string + XAppVersion string +} + func signPath(path string, os string, version string) (k string, v string) { table := []byte{'a', 'd', 'e', 'f', 'g', 'h', 'l', 'm', 'y', 'i', 'j', 'n', 'o', 'p', 'k', 'q', 'r', 's', 't', 'u', 'b', 'c', 'v', 'w', 's', 'z'} random := fmt.Sprintf("%.f", math.Round(1e7*rand.Float64())) @@ -68,81 +97,6 @@ func GetApi(rawUrl string) string { return u.String() } -//func GetApi(url string) string { -// vm := js.New() -// vm.Set("url", url[22:]) -// r, err := vm.RunString(` -// (function(e){ -// function A(t, e) { -// e = 1 < arguments.length && void 0 !== e ? e : 10; -// for (var n = function() { -// for (var t = [], e = 0; e < 256; e++) { -// for (var n = e, r = 0; r < 8; r++) -// n = 1 & n ? 3988292384 ^ n >>> 1 : n >>> 1; -// t[e] = n -// } -// return t -// }(), r = function(t) { -// t = t.replace(/\\r\\n/g, "\\n"); -// for (var e = "", n = 0; n < t.length; n++) { -// var r = t.charCodeAt(n); -// r < 128 ? e += String.fromCharCode(r) : e = 127 < r && r < 2048 ? (e += String.fromCharCode(r >> 6 | 192)) + String.fromCharCode(63 & r | 128) : (e = (e += String.fromCharCode(r >> 12 | 224)) + String.fromCharCode(r >> 6 & 63 | 128)) + String.fromCharCode(63 & r | 128) -// } -// return e -// }(t), a = -1, i = 0; i < r.length; i++) -// a = a >>> 8 ^ n[255 & (a ^ r.charCodeAt(i))]; -// return (a = (-1 ^ a) >>> 0).toString(e) -// } -// -// function v(t) { -// return (v = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(t) { -// return typeof t -// } -// : function(t) { -// return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t -// } -// )(t) -// } -// -// for (p in a = Math.round(1e7 * Math.random()), -// o = Math.round(((new Date).getTime() + 60 * (new Date).getTimezoneOffset() * 1e3 + 288e5) / 1e3).toString(), -// m = ["a", "d", "e", "f", "g", "h", "l", "m", "y", "i", "j", "n", "o", "p", "k", "q", "r", "s", "t", "u", "b", "c", "v", "w", "s", "z"], -// u = function(t, e, n) { -// var r; -// n = 2 < arguments.length && void 0 !== n ? n : 8; -// return 0 === arguments.length ? null : (r = "object" === v(t) ? t : (10 === "".concat(t).length && (t = 1e3 * Number.parseInt(t)), -// new Date(t)), -// t += 6e4 * new Date(t).getTimezoneOffset(), -// { -// y: (r = new Date(t + 36e5 * n)).getFullYear(), -// m: r.getMonth() + 1 < 10 ? "0".concat(r.getMonth() + 1) : r.getMonth() + 1, -// d: r.getDate() < 10 ? "0".concat(r.getDate()) : r.getDate(), -// h: r.getHours() < 10 ? "0".concat(r.getHours()) : r.getHours(), -// f: r.getMinutes() < 10 ? "0".concat(r.getMinutes()) : r.getMinutes() -// }) -// }(o), -// h = u.y, -// g = u.m, -// l = u.d, -// c = u.h, -// u = u.f, -// d = [h, g, l, c, u].join(""), -// f = [], -// d) -// f.push(m[Number(d[p])]); -// return h = A(f.join("")), -// g = A("".concat(o, "|").concat(a, "|").concat(e, "|").concat("web", "|").concat("3", "|").concat(h)), -// "".concat(h, "=").concat(o, "-").concat(a, "-").concat(g); -// })(url) -// `) -// if err != nil { -// fmt.Println(err) -// return url -// } -// v, _ := r.Export().(string) -// return url + "?" + v -//} - func (d *Pan123) login() error { var body base.Json if utils.IsEmailFormat(d.Username) { @@ -155,19 +109,47 @@ func (d *Pan123) login() error { body = base.Json{ "passport": d.Username, "password": d.Password, - "remember": true, + "type": 1, } } - res, err := base.RestyClient.R(). - SetHeaders(map[string]string{ - "origin": "https://www.123pan.com", - "referer": "https://www.123pan.com/", - "user-agent": "Dart/2.19(dart:io)-alist", - "platform": "web", - "app-version": "3", - //"user-agent": base.UserAgent, - }). - SetBody(body).Post(SignIn) + + req := base.RestyClient.R() + + req.SetHeaders(map[string]string{ + /* "origin": "https://www.123pan.com", + "referer": "https://www.123pan.com/",*/ + "user-agent": d.params.UserAgent, + "platform": d.params.Platform, + "app-version": d.params.AppVersion, + "osversion": d.params.OsVersion, + "devicetype": d.params.DeviceType, + "devicename": d.params.DeviceName, + "loginuuid": d.params.LoginUuid, + }) + + if d.params.XChannel != "" && d.params.XAppVersion != "" { + req.SetHeaders(map[string]string{ + "x-channel": d.params.XChannel, + "x-app-version": d.params.XAppVersion, + }) + } + + req.SetQueryParam("auth-key", generateAuthKey()) + + res, err := req.SetBody(body).Post(SignIn) + //res, err := base.RestyClient.R(). + // SetHeaders(map[string]string{ + // /* "origin": "https://www.123pan.com", + // "referer": "https://www.123pan.com/",*/ + // "user-agent": d.params.UserAgent, + // "platform": d.params.Platform, + // "app-version": d.params.AppVersion, + // "osversion": d.params.OsVersion, + // "devicetype": d.params.DeviceType, + // "devicename": d.params.DeviceName, + // //"user-agent": base.UserAgent, + // }). + // SetBody(body).Post(SignIn) if err != nil { return err } @@ -193,17 +175,93 @@ func (d *Pan123) login() error { // return &authKey, nil //} +func (d *Pan123) loginByQrCode() error { + if d.Addition.UniID == "" { + uniID, err := d.generateQrCode() + if uniID == "" && err != nil { + return err + } else { + // 保存 uniID 用于 二维码登录 + d.Addition.UniID = uniID + op.MustSaveDriverStorage(d) + return err + } + } else { + token, err := d.getTokenByUniID() + if token == "" && err != nil { + return err + } else { + d.Addition.AccessToken = token + op.MustSaveDriverStorage(d) + return err + } + } +} + +func (d *Pan123) generateQrCode() (string, error) { + var resp QrCodeGenerateResp + _, err := d.request(QrcodeGenerate, http.MethodGet, nil, &resp) + if err != nil { + return "", err + } + // 拼接二维码链接 + qrUrl := fmt.Sprintf(resp.Data.Url+"?uniID=%s", resp.Data.UniID+"&source=123pan&type=login") + // 生成二维码 + qrBytes, _ := qrcode.Encode(qrUrl, qrcode.Medium, 256) + base64Bytes := base64.StdEncoding.EncodeToString(qrBytes) + // 展示二维码 + qrTemplate := ` +
+ + Or Click Here + ` + qrPage := fmt.Sprintf(qrTemplate, base64Bytes, qrUrl) + return resp.Data.UniID, fmt.Errorf("need verify: \n%s", qrPage) +} + +func (d *Pan123) getTokenByUniID() (string, error) { + var resp QrCodeResultResp + _, err := d.request(QrcodeResult, http.MethodGet, func(req *resty.Request) { + req.SetQueryParam("uniID", d.Addition.UniID) + }, &resp) + if err != nil { + return "", err + } + + if resp.Data.LoginStatus == 4 { + return "", errors.New("uniID expired") + } else if resp.Data.Token == "" && resp.Data.LoginStatus == 0 { + return "", errors.New("wait for scan qrcode") + } + + return resp.Data.Token, nil + +} + func (d *Pan123) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := base.RestyClient.R() req.SetHeaders(map[string]string{ - "origin": "https://www.123pan.com", - "referer": "https://www.123pan.com/", + /* "origin": "https://www.123pan.com", + "referer": "https://www.123pan.com/",*/ "authorization": "Bearer " + d.AccessToken, - "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) alist-client", - "platform": "web", - "app-version": "3", - //"user-agent": base.UserAgent, + "user-agent": d.params.UserAgent, + "platform": d.params.Platform, + "app-version": d.params.AppVersion, + "osversion": d.params.OsVersion, + "devicetype": d.params.DeviceType, + "devicename": d.params.DeviceName, + "loginuuid": d.params.LoginUuid, }) + + if d.params.XChannel != "" && d.params.XAppVersion != "" { + req.SetHeaders(map[string]string{ + "x-channel": d.params.XChannel, + "x-app-version": d.params.XAppVersion, + }) + } + + req.SetQueryParam("auth-key", generateAuthKey()) + if callback != nil { callback(req) } @@ -215,19 +273,26 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r // return nil, err //} //req.SetQueryParam("auth-key", *authKey) - res, err := req.Execute(method, GetApi(url)) + //res, err := req.Execute(method, GetApi(url)) + res, err := req.Execute(method, url) if err != nil { return nil, err } body := res.Body() code := utils.Json.Get(body, "code").ToInt() - if code != 0 { - if code == 401 { + if code != 0 && code != 200 { + if code == 401 && d.Addition.UseQrCodeLogin == false { err := d.login() if err != nil { return nil, err } return d.request(url, method, callback, resp) + } else if code == 401 && d.Addition.UseQrCodeLogin == true { + err := d.loginByQrCode() + if err != nil { + return nil, err + } + return d.request(url, method, callback, resp) } return nil, errors.New(jsoniter.Get(body, "message").ToString()) } @@ -278,3 +343,10 @@ func (d *Pan123) getFiles(ctx context.Context, parentId string, name string) ([] } return res, nil } + +func generateAuthKey() string { + timestamp := time.Now().Unix() + randomInt := rand.Intn(1e9) // 生成9位的随机整数 + uuidStr := strings.ReplaceAll(uuid.New().String(), "-", "") // 去掉 UUID 中的所有 - + return fmt.Sprintf("%d-%09d-%s", timestamp, randomInt, uuidStr) // 确保随机整数是9位 +} diff --git a/drivers/123_share/driver.go b/drivers/123_share/driver.go index 9c1f3803710..7f8b2541fca 100644 --- a/drivers/123_share/driver.go +++ b/drivers/123_share/driver.go @@ -23,6 +23,7 @@ type Pan123Share struct { model.Storage Addition apiRateLimit sync.Map + params Params } func (d *Pan123Share) Config() driver.Config { @@ -34,12 +35,34 @@ func (d *Pan123Share) GetAddition() driver.Additional { } func (d *Pan123Share) Init(ctx context.Context) error { - // TODO login / refresh token - //op.MustSaveDriverStorage(d) - return nil + // TODO refresh token + // 拼接UserAgent + if d.PlatformType == "android" { + d.params.UserAgent = AndroidUserAgentPrefix + "(" + d.OsVersion + ";" + d.DeviceName + " " + d.DeiveType + ")" + d.params.Platform = AndroidPlatformParam + d.params.AppVersion = AndroidAppVer + d.params.XChannel = AndroidXChannel + d.params.XAppVersion = AndroidXAppVer + + } else if d.PlatformType == "tv" { + d.params.UserAgent = TVUserAgentPrefix + "(" + d.OsVersion + ";" + d.DeviceName + " " + d.DeiveType + ")" + d.params.Platform = TVPlatformParam + d.params.AppVersion = TVAndroidAppVer + } + + d.params.OsVersion = d.OsVersion + d.params.LoginUuid = d.LoginUuid + d.params.DeviceName = d.DeviceName + d.params.DeviceType = d.DeiveType + + _, err := d.request(UserInfo, http.MethodGet, nil, nil) + return err } func (d *Pan123Share) Drop(ctx context.Context) error { + _, _ = d.request(Logout, http.MethodPost, func(req *resty.Request) { + req.SetBody(base.Json{}) + }, nil) return nil } @@ -58,23 +81,18 @@ func (d *Pan123Share) Link(ctx context.Context, file model.Obj, args model.LinkA // TODO return link of file, required if f, ok := file.(File); ok { //var resp DownResp - var headers map[string]string - if !utils.IsLocalIPAddr(args.IP) { - headers = map[string]string{ - //"X-Real-IP": "1.1.1.1", - "X-Forwarded-For": args.IP, - } - } data := base.Json{ + "driveId": "0", "shareKey": d.ShareKey, "SharePwd": d.SharePwd, "etag": f.Etag, "fileId": f.FileId, "s3keyFlag": f.S3KeyFlag, + "FileName": f.FileName, "size": f.Size, } resp, err := d.request(DownloadInfo, http.MethodPost, func(req *resty.Request) { - req.SetBody(data).SetHeaders(headers) + req.SetBody(data) }, nil) if err != nil { return nil, err @@ -152,7 +170,7 @@ func (d *Pan123Share) Put(ctx context.Context, dstDir model.Obj, stream model.Fi func (d *Pan123Share) APIRateLimit(ctx context.Context, api string) error { value, _ := d.apiRateLimit.LoadOrStore(api, - rate.NewLimiter(rate.Every(700*time.Millisecond), 1)) + rate.NewLimiter(rate.Every(800*time.Millisecond), 1)) limiter := value.(*rate.Limiter) return limiter.Wait(ctx) diff --git a/drivers/123_share/meta.go b/drivers/123_share/meta.go index 7cbcba27724..7da542395dd 100644 --- a/drivers/123_share/meta.go +++ b/drivers/123_share/meta.go @@ -6,12 +6,21 @@ import ( ) type Addition struct { - ShareKey string `json:"sharekey" required:"true"` - SharePwd string `json:"sharepassword"` + Username string `json:"username"` + Password string `json:"password"` + UseQrCodeLogin bool `json:"use_qr_code_login"` + UniID string `json:"uni_id"` + ShareKey string `json:"sharekey" required:"true"` + SharePwd string `json:"sharepassword"` driver.RootID //OrderBy string `json:"order_by" type:"select" options:"file_name,size,update_at" default:"file_name"` //OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"` - AccessToken string `json:"accesstoken" type:"text"` + AccessToken string `json:"accesstoken" type:"text"` + PlatformType string `json:"platformType" type:"select" options:"android,tv" default:"android" required:"true"` + DeviceName string `json:"devicename" default:"Xiaomi"` + DeiveType string `json:"devicetype" default:"M1810E5A"` + OsVersion string `json:"osversion" default:"Android_8.1.0"` + LoginUuid string `json:"loginuuid" default:"66a4c82756134af2a57d323fd7a5e58c"` } var config = driver.Config{ diff --git a/drivers/123_share/types.go b/drivers/123_share/types.go index e8ca9e77440..af26c5d034f 100644 --- a/drivers/123_share/types.go +++ b/drivers/123_share/types.go @@ -97,3 +97,21 @@ type Files struct { // DownloadUrl string `json:"DownloadUrl"` // } `json:"data"` //} + +type QrCodeGenerateResp struct { + Data struct { + UniID string `json:"uniID"` + Url string `json:"url"` + } `json:"data"` +} + +type QrCodeResultResp struct { + Data struct { + Expire time.Time `json:"expire"` + LoginType int `json:"login_type"` + RefreshTokenExpireTime int `json:"refresh_token_expire_time"` + Token string `json:"token"` + LoginStatus int `json:"loginStatus"` + ScanPlatform int `json:"scanPlatform"` + } `json:"data"` +} diff --git a/drivers/123_share/util.go b/drivers/123_share/util.go index 80ea8f0ca46..87a6a89d846 100644 --- a/drivers/123_share/util.go +++ b/drivers/123_share/util.go @@ -2,8 +2,12 @@ package _123Share import ( "context" + "encoding/base64" "errors" "fmt" + "github.com/alist-org/alist/v3/internal/op" + "github.com/google/uuid" + "github.com/skip2/go-qrcode" "hash/crc32" "math" "math/rand" @@ -20,15 +24,184 @@ import ( ) const ( - Api = "https://www.123pan.com/api" - AApi = "https://www.123pan.com/a/api" - BApi = "https://www.123pan.com/b/api" - MainApi = BApi - FileList = MainApi + "/share/get" - DownloadInfo = MainApi + "/share/download/info" + Api = "https://www.123pan.com/api" + AApi = "https://www.123pan.com/a/api" + BApi = "https://www.123pan.com/b/api" + MainApi = Api + SignIn = MainApi + "/user/sign_in" + Logout = MainApi + "/user/logout" + FileList = MainApi + "/share/get" + DownloadInfo = MainApi + "/share/download/info" + UserInfo = MainApi + "/user/info" + QrcodeGenerate = MainApi + "/user/qr-code/generate" + QrcodeResult = MainApi + "/user/qr-code/result" //AuthKeySalt = "8-8D$sL8gPjom7bk#cY" ) +const ( + AndroidUserAgentPrefix = "123pan/v2.4.7" // 123pan/v2.4.7(Android_14;XiaoMi) + AndroidPlatformParam = "android" + AndroidAppVer = "69" + AndroidXAppVer = "2.4.7" + AndroidXChannel = "1002" + TVUserAgentPrefix = "123pan_android_tv/1.0.0" // 123pan_android_tv/1.0.0(14;samsung SM-X800) + TVPlatformParam = "android_tv" + TVAndroidAppVer = "100" +) + +type Params struct { + UserAgent string + Platform string + AppVersion string + OsVersion string + LoginUuid string + DeviceType string + DeviceName string + XChannel string + XAppVersion string +} + +func (d *Pan123Share) login() error { + var body base.Json + if utils.IsEmailFormat(d.Username) { + body = base.Json{ + "mail": d.Username, + "password": d.Password, + "type": 2, + } + } else { + body = base.Json{ + "passport": d.Username, + "password": d.Password, + "type": 1, + } + } + + req := base.RestyClient.R() + + req.SetHeaders(map[string]string{ + /* "origin": "https://www.123pan.com", + "referer": "https://www.123pan.com/",*/ + "user-agent": d.params.UserAgent, + "platform": d.params.Platform, + "app-version": d.params.AppVersion, + "osversion": d.params.OsVersion, + "devicetype": d.params.DeviceType, + "devicename": d.params.DeviceName, + "loginuuid": d.params.LoginUuid, + }) + + if d.params.XChannel != "" && d.params.XAppVersion != "" { + req.SetHeaders(map[string]string{ + "x-channel": d.params.XChannel, + "x-app-version": d.params.XAppVersion, + }) + } + + req.SetQueryParam("auth-key", generateAuthKey()) + + res, err := req.SetBody(body).Post(SignIn) + //res, err := base.RestyClient.R(). + // SetHeaders(map[string]string{ + // /* "origin": "https://www.123pan.com", + // "referer": "https://www.123pan.com/",*/ + // "user-agent": d.params.UserAgent, + // "platform": d.params.Platform, + // "app-version": d.params.AppVersion, + // "osversion": d.params.OsVersion, + // "devicetype": d.params.DeviceType, + // "devicename": d.params.DeviceName, + // //"user-agent": base.UserAgent, + // }). + // SetBody(body).Post(SignIn) + if err != nil { + return err + } + if utils.Json.Get(res.Body(), "code").ToInt() != 200 { + err = fmt.Errorf(utils.Json.Get(res.Body(), "message").ToString()) + } else { + d.AccessToken = utils.Json.Get(res.Body(), "data", "token").ToString() + } + return err +} + +//func authKey(reqUrl string) (*string, error) { +// reqURL, err := url.Parse(reqUrl) +// if err != nil { +// return nil, err +// } +// +// nowUnix := time.Now().Unix() +// random := rand.Intn(0x989680) +// +// p4 := fmt.Sprintf("%d|%d|%s|%s|%s|%s", nowUnix, random, reqURL.Path, "web", "3", AuthKeySalt) +// authKey := fmt.Sprintf("%d-%d-%x", nowUnix, random, md5.Sum([]byte(p4))) +// return &authKey, nil +//} + +func (d *Pan123Share) loginByQrCode() error { + if d.Addition.UniID == "" { + uniID, err := d.generateQrCode() + if uniID == "" && err != nil { + return err + } else { + // 保存 uniID 用于 二维码登录 + d.Addition.UniID = uniID + op.MustSaveDriverStorage(d) + return err + } + } else { + token, err := d.getTokenByUniID() + if token == "" && err != nil { + return err + } else { + d.Addition.AccessToken = token + op.MustSaveDriverStorage(d) + return err + } + } +} + +func (d *Pan123Share) generateQrCode() (string, error) { + var resp QrCodeGenerateResp + _, err := d.request(QrcodeGenerate, http.MethodGet, nil, &resp) + if err != nil { + return "", err + } + // 拼接二维码链接 + qrUrl := fmt.Sprintf(resp.Data.Url+"?uniID=%s", resp.Data.UniID+"&source=123pan&type=login") + // 生成二维码 + qrBytes, _ := qrcode.Encode(qrUrl, qrcode.Medium, 256) + base64Bytes := base64.StdEncoding.EncodeToString(qrBytes) + // 展示二维码 + qrTemplate := ` + + + Or Click Here + ` + qrPage := fmt.Sprintf(qrTemplate, base64Bytes, qrUrl) + return resp.Data.UniID, fmt.Errorf("need verify: \n%s", qrPage) +} + +func (d *Pan123Share) getTokenByUniID() (string, error) { + var resp QrCodeResultResp + _, err := d.request(QrcodeResult, http.MethodGet, func(req *resty.Request) { + req.SetQueryParam("uniID", d.Addition.UniID) + }, &resp) + if err != nil { + return "", err + } + + if resp.Data.LoginStatus == 4 { + return "", errors.New("uniID expired") + } else if resp.Data.Token == "" && resp.Data.LoginStatus == 0 { + return "", errors.New("wait for scan qrcode") + } + + return resp.Data.Token, nil + +} + func signPath(path string, os string, version string) (k string, v string) { table := []byte{'a', 'd', 'e', 'f', 'g', 'h', 'l', 'm', 'y', 'i', 'j', 'n', 'o', 'p', 'k', 'q', 'r', 's', 't', 'u', 'b', 'c', 'v', 'w', 's', 'z'} random := fmt.Sprintf("%.f", math.Round(1e7*rand.Float64())) @@ -55,27 +228,54 @@ func GetApi(rawUrl string) string { func (d *Pan123Share) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := base.RestyClient.R() req.SetHeaders(map[string]string{ - "origin": "https://www.123pan.com", - "referer": "https://www.123pan.com/", + /* "origin": "https://www.123pan.com", + "referer": "https://www.123pan.com/",*/ "authorization": "Bearer " + d.AccessToken, - "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) alist-client", - "platform": "web", - "app-version": "3", - //"user-agent": base.UserAgent, + "user-agent": d.params.UserAgent, + "platform": d.params.Platform, + "app-version": d.params.AppVersion, + "osversion": d.params.OsVersion, + "devicetype": d.params.DeviceType, + "devicename": d.params.DeviceName, + "loginuuid": d.params.LoginUuid, }) + + if d.params.XChannel != "" && d.params.XAppVersion != "" { + req.SetHeaders(map[string]string{ + "x-channel": d.params.XChannel, + "x-app-version": d.params.XAppVersion, + }) + } + + req.SetQueryParam("auth-key", generateAuthKey()) + if callback != nil { callback(req) } if resp != nil { req.SetResult(resp) } - res, err := req.Execute(method, GetApi(url)) + //res, err := req.Execute(method, GetApi(url)) + res, err := req.Execute(method, url) if err != nil { return nil, err } body := res.Body() code := utils.Json.Get(body, "code").ToInt() - if code != 0 { + if code != 0 && code != 200 { + if code == 401 && d.Addition.UseQrCodeLogin == false { + err := d.login() + if err != nil { + return nil, err + } + return d.request(url, method, callback, resp) + } else if code == 401 && d.Addition.UseQrCodeLogin == true { + err := d.loginByQrCode() + if err != nil { + return nil, err + } + return d.request(url, method, callback, resp) + } return nil, errors.New(jsoniter.Get(body, "message").ToString()) } return body, nil @@ -115,3 +315,10 @@ func (d *Pan123Share) getFiles(ctx context.Context, parentId string) ([]File, er } // do others that not defined in Driver interface + +func generateAuthKey() string { + timestamp := time.Now().Unix() + randomInt := rand.Intn(1e9) // 生成9位的随机整数 + uuidStr := strings.ReplaceAll(uuid.New().String(), "-", "") // 去掉 UUID 中的所有 - + return fmt.Sprintf("%d-%09d-%s", timestamp, randomInt, uuidStr) // 确保随机整数是9位 +}