From a9bcd1715893126029fe709024ac2d13e7b02c99 Mon Sep 17 00:00:00 2001 From: YangXu <2945065490@qq.com> Date: Sat, 28 Sep 2024 21:52:50 +0800 Subject: [PATCH 1/5] feat(123&123share): Android Or TV --- drivers/123/driver.go | 20 ++++ drivers/123/meta.go | 7 +- drivers/123/util.go | 186 ++++++++++++++++++------------------ drivers/123_share/driver.go | 24 ++++- drivers/123_share/meta.go | 7 +- drivers/123_share/util.go | 61 ++++++++++-- 6 files changed, 200 insertions(+), 105 deletions(-) diff --git a/drivers/123/driver.go b/drivers/123/driver.go index aeda7fcf742..04190abd735 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 } diff --git a/drivers/123/meta.go b/drivers/123/meta.go index cb2cbc15ba0..4d67284cab0 100644 --- a/drivers/123/meta.go +++ b/drivers/123/meta.go @@ -11,7 +11,12 @@ type Addition struct { 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:"houji"` + OsVersion string `json:"osversion" default:"14"` + LoginUuid string `json:"loginuuid" default:"1fce20b2428d30899fd537f4cf231dfb"` } var config = driver.Config{ diff --git a/drivers/123/util.go b/drivers/123/util.go index 73c73b3b3b3..0253f34c96d 100644 --- a/drivers/123/util.go +++ b/drivers/123/util.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/google/uuid" "hash/crc32" "math" "math/rand" @@ -26,7 +27,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" @@ -45,6 +46,29 @@ const ( //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 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 +92,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 +104,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 } @@ -196,14 +173,27 @@ func (d *Pan123) login() error { 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,7 +205,8 @@ 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 } @@ -278,3 +269,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..664cbf254e6 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 { @@ -36,6 +37,25 @@ func (d *Pan123Share) GetAddition() driver.Additional { func (d *Pan123Share) Init(ctx context.Context) error { // TODO login / refresh token //op.MustSaveDriverStorage(d) + // 拼接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 + return nil } @@ -66,11 +86,13 @@ func (d *Pan123Share) Link(ctx context.Context, file model.Obj, args model.LinkA } } 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) { @@ -152,7 +174,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..48bd2357f10 100644 --- a/drivers/123_share/meta.go +++ b/drivers/123_share/meta.go @@ -11,7 +11,12 @@ type Addition struct { 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" required:"true"` + PlatformType string `json:"platformType" type:"select" options:"android,tv" default:"android" required:"true"` + DeviceName string `json:"devicename" default:"XiaoMi"` + DeiveType string `json:"devicetype" default:"houji"` + OsVersion string `json:"osversion" default:"14"` + LoginUuid string `json:"loginuuid" default:"1fce20b2428d30899fd537f4cf231dfb"` } var config = driver.Config{ diff --git a/drivers/123_share/util.go b/drivers/123_share/util.go index 80ea8f0ca46..a7e3e5045be 100644 --- a/drivers/123_share/util.go +++ b/drivers/123_share/util.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/google/uuid" "hash/crc32" "math" "math/rand" @@ -23,12 +24,35 @@ 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 FileList = MainApi + "/share/get" DownloadInfo = MainApi + "/share/download/info" //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 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,21 +79,35 @@ 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 } @@ -115,3 +153,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位 +} From 419a827b6569a3dc4200b04af373dc3f66582339 Mon Sep 17 00:00:00 2001 From: xrgzs Date: Fri, 4 Oct 2024 20:56:05 +0800 Subject: [PATCH 2/5] fix(123&123share): update default values for device-related fields - Update DeviceName to "Xiaomi" - Change DeiveType to "M1810E5A" - Update OsVersion to "Android_8.1.0" - Modify LoginUuid to "66a4c82756134af2a57d323fd7a5e58c" - Remove required attribute from AccessToken in 123_share/meta.go --- drivers/123/meta.go | 8 ++++---- drivers/123_share/meta.go | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/123/meta.go b/drivers/123/meta.go index 4d67284cab0..961497d4292 100644 --- a/drivers/123/meta.go +++ b/drivers/123/meta.go @@ -13,10 +13,10 @@ type Addition struct { //OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"` 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:"houji"` - OsVersion string `json:"osversion" default:"14"` - LoginUuid string `json:"loginuuid" default:"1fce20b2428d30899fd537f4cf231dfb"` + 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/meta.go b/drivers/123_share/meta.go index 48bd2357f10..bb7f259f631 100644 --- a/drivers/123_share/meta.go +++ b/drivers/123_share/meta.go @@ -11,12 +11,12 @@ type Addition struct { 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" required:"true"` + 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:"houji"` - OsVersion string `json:"osversion" default:"14"` - LoginUuid string `json:"loginuuid" default:"1fce20b2428d30899fd537f4cf231dfb"` + 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{ From e3ccec01abc61a490b88871f8885c9ad41274146 Mon Sep 17 00:00:00 2001 From: xrgzs Date: Fri, 4 Oct 2024 21:22:42 +0800 Subject: [PATCH 3/5] fix(123): adjust API rate limit to 800ms Increase the rate limit for Pan123 API calls from 700ms to 800ms to comply with the API's rate limit requirements. This adjustment ensures that the driver operates within the allowed limits and avoids potential service disruptions. --- drivers/123/driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/123/driver.go b/drivers/123/driver.go index 04190abd735..a97de28caf9 100644 --- a/drivers/123/driver.go +++ b/drivers/123/driver.go @@ -277,7 +277,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) From 406a963b212495bb6794a7c9e7e528c8d275bf14 Mon Sep 17 00:00:00 2001 From: xrgzs Date: Fri, 4 Oct 2024 21:54:16 +0800 Subject: [PATCH 4/5] fix(123&123share): remove X-Forwarded-For header --- drivers/123/driver.go | 9 +-------- drivers/123_share/driver.go | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/123/driver.go b/drivers/123/driver.go index a97de28caf9..7bfe14421c4 100644 --- a/drivers/123/driver.go +++ b/drivers/123/driver.go @@ -85,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, @@ -102,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 diff --git a/drivers/123_share/driver.go b/drivers/123_share/driver.go index 664cbf254e6..17f21093738 100644 --- a/drivers/123_share/driver.go +++ b/drivers/123_share/driver.go @@ -78,13 +78,6 @@ 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, @@ -96,7 +89,7 @@ func (d *Pan123Share) Link(ctx context.Context, file model.Obj, args model.LinkA "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 From 0ffbc36159dd0c00acfddc36782b02573e4ccc5f Mon Sep 17 00:00:00 2001 From: YangXu <2945065490@qq.com> Date: Mon, 7 Oct 2024 11:05:24 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat(123&123share):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drivers/123/meta.go | 6 +- drivers/123/types.go | 18 ++++ drivers/123/util.go | 78 +++++++++++++++- drivers/123_share/driver.go | 9 +- drivers/123_share/meta.go | 8 +- drivers/123_share/types.go | 18 ++++ drivers/123_share/util.go | 176 ++++++++++++++++++++++++++++++++++-- 7 files changed, 297 insertions(+), 16 deletions(-) diff --git a/drivers/123/meta.go b/drivers/123/meta.go index 961497d4292..4694f585776 100644 --- a/drivers/123/meta.go +++ b/drivers/123/meta.go @@ -6,8 +6,10 @@ 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"` 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 0253f34c96d..c494dcbf1ae 100644 --- a/drivers/123/util.go +++ b/drivers/123/util.go @@ -2,9 +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" @@ -44,6 +47,8 @@ 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 ( @@ -170,6 +175,69 @@ 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{ @@ -212,13 +280,19 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r } 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()) } diff --git a/drivers/123_share/driver.go b/drivers/123_share/driver.go index 17f21093738..7f8b2541fca 100644 --- a/drivers/123_share/driver.go +++ b/drivers/123_share/driver.go @@ -35,8 +35,7 @@ func (d *Pan123Share) GetAddition() driver.Additional { } func (d *Pan123Share) Init(ctx context.Context) error { - // TODO login / refresh token - //op.MustSaveDriverStorage(d) + // TODO refresh token // 拼接UserAgent if d.PlatformType == "android" { d.params.UserAgent = AndroidUserAgentPrefix + "(" + d.OsVersion + ";" + d.DeviceName + " " + d.DeiveType + ")" @@ -56,10 +55,14 @@ func (d *Pan123Share) Init(ctx context.Context) error { d.params.DeviceName = d.DeviceName d.params.DeviceType = d.DeiveType - return nil + _, 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 } diff --git a/drivers/123_share/meta.go b/drivers/123_share/meta.go index bb7f259f631..7da542395dd 100644 --- a/drivers/123_share/meta.go +++ b/drivers/123_share/meta.go @@ -6,8 +6,12 @@ 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"` 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 a7e3e5045be..87a6a89d846 100644 --- a/drivers/123_share/util.go +++ b/drivers/123_share/util.go @@ -2,9 +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" @@ -21,12 +24,17 @@ import ( ) const ( - Api = "https://www.123pan.com/api" - AApi = "https://www.123pan.com/a/api" - BApi = "https://www.123pan.com/b/api" - MainApi = Api - 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" ) @@ -53,6 +61,147 @@ type Params struct { 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())) @@ -113,7 +262,20 @@ func (d *Pan123Share) request(url string, method string, callback base.ReqCallba } 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