二维码已失效
18 | 19 |├── .gitignore ├── .vercelignore ├── LICENSE ├── api ├── check_link.ts ├── generate.ts ├── sign.ts └── state-query.ts ├── index.html ├── index.ts ├── package.json ├── public └── favicon.ico ├── readme.md ├── serve ├── index.ts └── local-serve.ts ├── tsconfig.json ├── tsconfig.node.json ├── vercel.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | node_modules 3 | .vercel 4 | .yarn 5 | package-lock.json 6 | .yarn.lock 7 | dist -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | .history 2 | node_modules 3 | .vercel 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 itxve 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /api/check_link.ts: -------------------------------------------------------------------------------- 1 | import type { VercelRequest, VercelResponse } from "@vercel/node"; 2 | 3 | const headers = { 4 | Referer: "https://www.aliyundrive.com/", 5 | "User-Agent": 6 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", 7 | }; 8 | 9 | async function matchShareId(link: string) { 10 | const aliRe = /www.aliyundrive.com\/s\/(.*)/; 11 | const aliResult = link.match(aliRe); 12 | 13 | if (!aliResult) { 14 | throw new Error("Link is error"); 15 | } 16 | return aliResult[1]; 17 | } 18 | 19 | async function linkCheck(link: string) { 20 | const aliRe = /www.aliyundrive.com\/s\/(.*)/; 21 | const aliResult = link.match(aliRe); 22 | 23 | if (!aliResult) { 24 | throw new Error("Link is error"); 25 | } 26 | const shareId = aliResult[1]; 27 | return fetch( 28 | "https://api.aliyundrive.com/adrive/v3/share_link/get_share_by_anonymous", 29 | { method: "post", body: JSON.stringify({ share_id: shareId }), headers } 30 | ); 31 | } 32 | 33 | export default async function (req: VercelRequest, res: VercelResponse) { 34 | res.setHeader("Access-Control-Allow-Origin", "*"); 35 | const { link } = req.query; 36 | 37 | if (!link) { 38 | res.status(200).send({ code: 500, msg: "link is empty", data: null }); 39 | return Promise.reject(); 40 | } 41 | 42 | return linkCheck(link as string) 43 | .then(async (r) => { 44 | if (r.status == 200) { 45 | res.status(200).send({ code: r.status, msg: "", data: await r.json() }); 46 | } else { 47 | res.status(200).send({ code: r.status, msg: "", data: await r.json() }); 48 | } 49 | }) 50 | .catch((e: Error) => { 51 | res.status(200).send({ code: 500, msg: e.message, data: null }); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /api/generate.ts: -------------------------------------------------------------------------------- 1 | import type { VercelRequest, VercelResponse } from "@vercel/node"; 2 | import QRCode from "qrcode"; 3 | import https from "https"; 4 | 5 | export default function (req: VercelRequest, res: VercelResponse) { 6 | res.setHeader("Access-Control-Allow-Origin", "*"); 7 | const { img } = req.query; 8 | https 9 | .get( 10 | { 11 | host: "passport.aliyundrive.com", 12 | path: 13 | "/newlogin/qrcode/generate.do?" + 14 | "appName=aliyun_drive" + 15 | "&fromSite=52" + 16 | "&appName=aliyun_drive" + 17 | "&appEntrance=web" + 18 | "&isMobile=false" + 19 | "&lang=zh_CN" + 20 | "&returnUrl=" + 21 | "&bizParams=" + 22 | "&_bx-v=2.0.31", 23 | timeout: 3000, 24 | }, 25 | (response) => { 26 | let data: any = ""; 27 | response.on("data", (d) => { 28 | data += d; 29 | }); 30 | response.on("end", async () => { 31 | const json = JSON.parse(data); 32 | const result = { 33 | codeContent: json.content.data.codeContent, 34 | ck: json.content.data.ck, 35 | t: json.content.data.t, 36 | }; 37 | if (img) { 38 | const image = await QRCode.toDataURL(result.codeContent); 39 | result.codeContent = image; 40 | } 41 | res.send(result); 42 | }); 43 | } 44 | ) 45 | .on("error", (e) => { 46 | res.status(500).send(e); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /api/sign.ts: -------------------------------------------------------------------------------- 1 | import type { VercelRequest, VercelResponse } from "@vercel/node"; 2 | 3 | export default async function (req: VercelRequest, res: VercelResponse) { 4 | res.setHeader("Access-Control-Allow-Origin", "*"); 5 | const { refreshToken } = req.query; 6 | 7 | try { 8 | const ali = new AliyundriveSign(); 9 | await ali.updateAccesssToken(refreshToken as string); 10 | await ali.sign_in(); 11 | res.status(200).send(ali.messages.join("\n")); 12 | } catch (e) { 13 | res.status(500).send(e); 14 | } 15 | } 16 | 17 | // 源代码 :https://github.com/mrabit/aliyundriveDailyCheck/blob/master/autoSignin.js 18 | export class AliyundriveSign { 19 | updateAccesssTokenURL = "https://auth.aliyundrive.com/v2/account/token"; 20 | signinURL = 21 | "https://member.aliyundrive.com/v1/activity/sign_in_list?_rx-s=mobile"; 22 | rewardURL = 23 | "https://member.aliyundrive.com/v1/activity/sign_in_reward?_rx-s=mobile"; 24 | 25 | authorization = { 26 | nick_name: "", 27 | refresh_token: "", 28 | access_token: "", 29 | }; 30 | messages: string[] = []; 31 | 32 | // 使用 refresh_token 更新 access_token 33 | async updateAccesssToken(refreshToken: string) { 34 | return fetch(this.updateAccesssTokenURL, { 35 | method: "POST", 36 | body: JSON.stringify({ 37 | grant_type: "refresh_token", 38 | refresh_token: refreshToken, 39 | }), 40 | headers: { "Content-Type": "application/json" }, 41 | }) 42 | .then((d) => d.json()) 43 | .then((d) => { 44 | const { code, message, nick_name, refresh_token, access_token } = d; 45 | if ( 46 | code === "RefreshTokenExpired" || 47 | code === "InvalidParameter.RefreshToken" 48 | ) { 49 | return Promise.reject(message); 50 | } 51 | this.authorization = { 52 | nick_name, 53 | refresh_token, 54 | access_token, 55 | }; 56 | }); 57 | } 58 | 59 | //签到 60 | async sign_in(access_token: string = "") { 61 | access_token = this.authorization.access_token || access_token; 62 | return fetch(this.signinURL, { 63 | method: "POST", 64 | body: JSON.stringify({ 65 | isReward: false, 66 | }), 67 | headers: { 68 | authorization: access_token, 69 | "Content-Type": "application/json", 70 | }, 71 | }) 72 | .then((res) => res.json()) 73 | .then(async (json) => { 74 | if (!json.success) { 75 | return Promise.reject(json.message); 76 | } 77 | const { signInLogs, signInCount } = json.result; 78 | const currentSignInfo = signInLogs[signInCount - 1]; // 当天签到信息 79 | 80 | this.messages.push(`本月累计签到 ${signInCount} 天`); 81 | 82 | // 未领取奖励列表 83 | const rewards = signInLogs.filter( 84 | (v: any) => v.status === "normal" && !v.isReward 85 | ); 86 | 87 | if (rewards.length) { 88 | for await (let reward of rewards) { 89 | const signInDay = reward.day; 90 | try { 91 | const rewardInfo = await this.getReward(access_token, signInDay); 92 | this.messages.push( 93 | `第${signInDay}天奖励领取成功: 获得${rewardInfo.name || ""}${ 94 | rewardInfo.description || "" 95 | }` 96 | ); 97 | } catch (e: any) { 98 | this.messages.push(`第${signInDay}天奖励领取失败:`, e); 99 | } 100 | } 101 | } else if (currentSignInfo.isReward) { 102 | this.messages.push( 103 | `今日签到获得${currentSignInfo.reward.name || ""}${ 104 | currentSignInfo.reward.description || "" 105 | }` 106 | ); 107 | } 108 | }); 109 | } 110 | 111 | // 领取奖励 112 | async getReward(access_token: string, signInDay: number) { 113 | return fetch(this.rewardURL, { 114 | method: "POST", 115 | body: JSON.stringify({ signInDay }), 116 | headers: { 117 | authorization: access_token, 118 | "Content-Type": "application/json", 119 | }, 120 | }) 121 | .then((d) => d.json()) 122 | .then((json) => { 123 | if (!json.success) { 124 | return Promise.reject(json.message); 125 | } 126 | return json.result; 127 | }); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /api/state-query.ts: -------------------------------------------------------------------------------- 1 | import type { VercelRequest, VercelResponse } from "@vercel/node"; 2 | import https from "https"; 3 | 4 | export default function (req: VercelRequest, res: VercelResponse) { 5 | res.setHeader("Access-Control-Allow-Origin", "*"); 6 | const { ck, t } = req.query; 7 | const params: any = { 8 | t, 9 | ck, 10 | appName: "aliyun_drive", 11 | appEntrance: "web", 12 | isMobile: "false", 13 | lang: "zh_CN", 14 | returnUrl: "", 15 | fromSite: "52", 16 | bizParams: "", 17 | navlanguage: "zh-CN", 18 | navPlatform: "MacIntel", 19 | }; 20 | 21 | let body = ""; 22 | Object.keys(params).forEach((key) => { 23 | body += "&" + key + "=" + params[key]; 24 | }); 25 | 26 | const status: any = { 27 | NEW: "请用阿里云盘 App 扫码", 28 | SCANED: "请在手机上确认", 29 | EXPIRED: "二维码已过期", 30 | CANCELED: "已取消", 31 | CONFIRMED: "已确认", 32 | }; 33 | https 34 | .request( 35 | { 36 | method: "POST", 37 | host: "passport.aliyundrive.com", 38 | path: 39 | "/newlogin/qrcode/query.do?appName=aliyun_drive&fromSite=52&_bx-v=2.0.31", 40 | headers: { 41 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 42 | }, 43 | timeout: 3000, 44 | }, 45 | (response) => { 46 | let data: any = ""; 47 | response.on("data", (d) => { 48 | data += d; 49 | }); 50 | response.on("end", () => { 51 | const rt = JSON.parse(data).content; 52 | if (rt.data.qrCodeStatus === "CONFIRMED") { 53 | const data = Buffer.from(rt.data.bizExt, "base64"); 54 | rt.data.bizExt = JSON.parse(String(data)); 55 | } 56 | //添加 一个tip 57 | rt.data.tip = status[rt.data.qrCodeStatus]; 58 | res.send(rt); 59 | }); 60 | } 61 | ) 62 | .on("error", (e) => { 63 | res.status(500).send(e); 64 | }) 65 | .end(body); 66 | } 67 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |二维码已失效
18 | 19 |