class QRCodeHandler { // 状态常量 static STATUS_NEW = "NEW"; // 待扫描 static STATUS_SCANED = "SCANED"; // 已扫描 static STATUS_CONFIRMED = "CONFIRMED"; // 已确认 static STATUS_CANCELED = "CANCELED"; // 已取消 static STATUS_EXPIRED = "EXPIRED"; // 已过期 // 平台常量 static PLATFORM_QUARK = "quark"; // 夸克 static PLATFORM_ALI = "ali"; // 阿里云盘 static PLATFORM_UC = "uc"; // UC static PLATFORM_BILI = "bili"; // 哔哩哔哩 // 通用请求头 static HEADERS = { 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36', 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json, text/plain, */*' }; constructor() { this.platformStates = { [QRCodeHandler.PLATFORM_QUARK]: null, [QRCodeHandler.PLATFORM_ALI]: null, [QRCodeHandler.PLATFORM_UC]: null, [QRCodeHandler.PLATFORM_BILI]: null }; } static generateUUID() { if (crypto && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } else if (crypto && typeof crypto.getRandomValues === 'function') { return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16) ); } else { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } } async _generateQRCode(url) { return new Promise((resolve, reject) => { QRCode.toDataURL(url, function (err, res) { if (err) reject(err); resolve(res); }); }); } formatCookiesToList(cookieString) { const result = []; let currentCookie = ''; let inExpires = false; for (let i = 0; i < cookieString.length; i++) { const char = cookieString[i]; // 判断是否进入或退出 `expires` 属性 if (cookieString.slice(i, i + 8).toLowerCase() === 'expires=') { inExpires = true; } if (inExpires && char === ';') { inExpires = false; } // 检测到逗号分隔符并且不在 `expires` 属性中,表示一个 Cookie 条目结束 if (char === ',' && !inExpires) { result.push(currentCookie.trim()); currentCookie = ''; } else { currentCookie += char; } } // 添加最后一个 Cookie 条目 if (currentCookie.trim()) { result.push(currentCookie.trim()); } return result; }; formatCookie(cookies) { if (!Array.isArray(cookies)) cookies = [cookies]; if (cookies.length === 0) return ''; let mainCookies = []; for (const cookie of cookies) { if (cookie && typeof cookie === 'string' && cookie.trim()) { mainCookies.push(cookie.split('; ')[0]); } } return mainCookies.join(';'); } async startScan(platform) { switch (platform) { case QRCodeHandler.PLATFORM_QUARK: return await this._startQuarkScan(); case QRCodeHandler.PLATFORM_ALI: return await this._startAliScan(); case QRCodeHandler.PLATFORM_UC: return await this._startUCScan(); case QRCodeHandler.PLATFORM_BILI: return await this._startBiliScan(); default: throw new Error("Unsupported platform"); } } async checkStatus(platform) { switch (platform) { case QRCodeHandler.PLATFORM_QUARK: return await this._checkQuarkStatus(); case QRCodeHandler.PLATFORM_ALI: return await this._checkAliStatus(); case QRCodeHandler.PLATFORM_UC: return await this._checkUCStatus(); case QRCodeHandler.PLATFORM_BILI: return await this._checkBiliStatus(); default: throw new Error("Unsupported platform"); } } // 夸克平台相关方法 async _startQuarkScan() { try { const requestId = QRCodeHandler.generateUUID(); const res = await axios({ url: "/http", method: "POST", data: { url: "https://uop.quark.cn/cas/ajax/getTokenForQrcodeLogin", headers: { ...QRCodeHandler.HEADERS }, params: { request_id: requestId, client_id: "532", v: "1.2" }, } }); const resData = res.data; const qcToken = resData.data.data.members.token; const qrUrl = `https://su.quark.cn/4_eMHBJ?token=${qcToken}&client_id=532&ssb=weblogin&uc_param_str=&uc_biz_str=S%3Acustom%7COPT%3ASAREA%400%7COPT%3AIMMERSIVE%401%7COPT%3ABACK_BTN_STYLE%400`; this.platformStates[QRCodeHandler.PLATFORM_QUARK] = { token: qcToken, request_id: requestId }; const qrCode = await this._generateQRCode(qrUrl); return { qrcode: qrCode, status: QRCodeHandler.STATUS_NEW }; } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_QUARK] = null; throw e; } } async _checkQuarkStatus() { const state = this.platformStates[QRCodeHandler.PLATFORM_QUARK]; if (!state) { return {status: QRCodeHandler.STATUS_EXPIRED}; } try { const res = await axios({ url: "/http", method: "POST", data: { url: "https://uop.quark.cn/cas/ajax/getServiceTicketByQrcodeToken", headers: { ...QRCodeHandler.HEADERS }, params: { request_id: state.request_id, client_id: "532", v: "1.2", token: state.token } } }); const resData = res.data; if (resData.data.status === 2000000) { // 扫码成功 const serviceTicket = resData.data.data.members.service_ticket; const cookieRes = await axios({ url: "/http", method: "POST", data: { url: "https://pan.quark.cn/account/info", headers: { ...QRCodeHandler.HEADERS }, params: { st: serviceTicket, lw: "scan" } } }); const cookieResData = cookieRes.data; const cookies = Array.isArray(cookieResData.headers['set-cookie']) ? cookieResData.headers['set-cookie'].join('; ') : cookieResData.headers['set-cookie']; const cookies2array = this.formatCookiesToList(cookies); let mainCookies = this.formatCookie(cookies2array); const cookieSelfRes = await axios({ url: "/http", method: "POST", data: { url: "https://drive-pc.quark.cn/1/clouddrive/file/sort?pr=ucpro&fr=pc&uc_param_str=&pdir_fid=0&_page=1&_size=50&_fetch_total=1&_fetch_sub_dirs=0&_sort=file_type:asc,updated_at:desc", headers: { ...QRCodeHandler.HEADERS, Origin: 'https://pan.quark.cn', Referer: 'https://pan.quark.cn/', Cookie: mainCookies } } }); const cookieResDataSelf = cookieSelfRes.data; const cookiesSelf = Array.isArray(cookieResDataSelf.headers['set-cookie']) ? cookieResDataSelf.headers['set-cookie'].join('; ') : cookieResDataSelf.headers['set-cookie']; const cookies2arraySelf = this.formatCookiesToList(cookiesSelf); const mainCookiesSelf = this.formatCookie(cookies2arraySelf); if (mainCookiesSelf) mainCookies += ';' + mainCookiesSelf; return { status: QRCodeHandler.STATUS_CONFIRMED, cookie: mainCookies }; } else if (resData.data.status === 50004002) { // token过期 this.platformStates[QRCodeHandler.PLATFORM_QUARK] = null; return {status: QRCodeHandler.STATUS_EXPIRED}; } else { return {status: QRCodeHandler.STATUS_NEW}; } } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_QUARK] = null; throw new Error(e.response.data.message || e.message); } } // 阿里云盘平台相关方法 async _startAliScan() { try { const res = await axios({ url: "/http", method: "POST", data: { url: "https://passport.aliyundrive.com/newlogin/qrcode/generate.do", headers: { ...QRCodeHandler.HEADERS }, params: { appName: "aliyun_drive", fromSite: "52", appEntrance: "web", isMobile: "false", lang: "zh_CN", returnUrl: "", bizParams: "", _bx_v: "2.2.3" } } }); const resData = res.data; const contentData = resData.data.content.data; this.platformStates[QRCodeHandler.PLATFORM_ALI] = { ck: contentData.ck, t: contentData.t }; const qrCode = await this._generateQRCode(contentData.codeContent); return { qrcode: qrCode, status: QRCodeHandler.STATUS_NEW }; } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_ALI] = null; throw e; } } async _checkAliStatus() { const state = this.platformStates[QRCodeHandler.PLATFORM_ALI]; if (!state) { return {status: QRCodeHandler.STATUS_EXPIRED}; } try { const res = await axios({ url: "/http", method: "POST", data: { url: "https://passport.aliyundrive.com/newlogin/qrcode/query.do", method: "POST", headers: { ...QRCodeHandler.HEADERS }, params: { appName: "aliyun_drive", fromSite: "52", _bx_v: "2.2.3" }, data: { ck: state.ck, t: state.t, appName: "aliyun_drive", appEntrance: "web", isMobile: "false", lang: "zh_CN", returnUrl: "", navlanguage: "zh-CN", bizParams: "" } } }); const resData = res.data; if (!resData.data.content || !resData.data.content.data) { return {status: QRCodeHandler.STATUS_EXPIRED}; } const status = resData.data.content.data.qrCodeStatus; if (status === "CONFIRMED") { if (resData.data.content.data.bizExt) { const bizExt = JSON.parse(atob(resData.data.content.data.bizExt)); console.log(bizExt.pds_login_result); return { status: QRCodeHandler.STATUS_CONFIRMED, token: bizExt.pds_login_result.refreshToken }; } return {status: QRCodeHandler.STATUS_EXPIRED}; } else if (status === "SCANED") { return {status: QRCodeHandler.STATUS_SCANED}; } else if (status === "CANCELED") { this.platformStates[QRCodeHandler.PLATFORM_ALI] = null; return {status: QRCodeHandler.STATUS_CANCELED}; } else if (status === "NEW") { return {status: QRCodeHandler.STATUS_NEW}; } else { return {status: QRCodeHandler.STATUS_EXPIRED}; } } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_ALI] = null; throw new Error(e.message); } } // UC平台相关方法 async _startUCScan() { try { const requestId = QRCodeHandler.generateUUID(); const res = await axios({ url: "/http", method: "POST", data: { url: "https://api.open.uc.cn/cas/ajax/getTokenForQrcodeLogin", headers: { ...QRCodeHandler.HEADERS }, params: { __t: Date.now() }, data: { v: "1.2", request_id: requestId, client_id: "381" } } }); const resData = res.data; const token = resData.data.data.members.token; const qrUrl = `https://su.uc.cn/1_n0ZCv?token=${token}&client_id=381&uc_param_str=&uc_biz_str=S%3Acustom%7CC%3Atitlebar_fix`; this.platformStates[QRCodeHandler.PLATFORM_UC] = { token: token, request_id: requestId }; const qrCode = await this._generateQRCode(qrUrl); return { qrcode: qrCode, status: QRCodeHandler.STATUS_NEW }; } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_UC] = null; throw e; } } async _checkUCStatus() { const state = this.platformStates[QRCodeHandler.PLATFORM_UC]; if (!state) { return {status: QRCodeHandler.STATUS_EXPIRED}; } try { const res = await axios({ url: "/http", method: "POST", data: { url: "https://api.open.uc.cn/cas/ajax/getServiceTicketByQrcodeToken", method: "POST", headers: { ...QRCodeHandler.HEADERS }, params: { __t: Date.now() }, data: { v: "1.2", request_id: state.request_id, client_id: "381", token: state.token } } }); const resData = res.data; if (resData.data.status === 2000000) { // 扫码成功 const serviceTicket = resData.data.data.members.service_ticket; const cookieRes = await axios({ url: "/http", method: "POST", data: { url: "https://drive.uc.cn/account/info", headers: { ...QRCodeHandler.HEADERS }, params: { st: serviceTicket }, } }); const cookieResData = cookieRes.data; const cookies = cookieResData.headers['set-cookie']; const cookies2array = this.formatCookiesToList(cookies); let mainCookies = this.formatCookie(cookies2array); const cookieSelfRes = await axios({ url: "/http", method: "POST", data: { url: "https://pc-api.uc.cn/1/clouddrive/config?pr=UCBrowser&fr=pc", headers: { ...QRCodeHandler.HEADERS, Origin: 'https://drive.uc.cn', Referer: 'https://drive.uc.cn/', Cookie: mainCookies } } }); const cookieResDataSelf = cookieSelfRes.data; const cookiesSelf = Array.isArray(cookieResDataSelf.headers['set-cookie']) ? cookieResDataSelf.headers['set-cookie'].join('; ') : cookieResDataSelf.headers['set-cookie']; const cookies2arraySelf = this.formatCookiesToList(cookiesSelf); const mainCookiesSelf = this.formatCookie(cookies2arraySelf); if (mainCookiesSelf) mainCookies += ';' + mainCookiesSelf; this.platformStates[QRCodeHandler.PLATFORM_UC] = null; return { status: QRCodeHandler.STATUS_CONFIRMED, cookie: mainCookies }; } else if (resData.data.status === 50004002) { // token过期 this.platformStates[QRCodeHandler.PLATFORM_UC] = null; return {status: QRCodeHandler.STATUS_EXPIRED}; } else { return {status: QRCodeHandler.STATUS_NEW}; } } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_UC] = null; throw new Error(e.message); } } // 哔哩哔哩平台相关方法 async _startBiliScan() { try { const res = await axios({ url: "/http", method: "POST", data: { url: "https://passport.bilibili.com/x/passport-login/web/qrcode/generate", headers: { ...QRCodeHandler.HEADERS }, params: { source: "main-mini" } } }); const resData = res.data; console.log(resData) if (resData.data.code !== 0) { throw new Error(resData.data.message); } const qrcodeData = resData.data.data; this.platformStates[QRCodeHandler.PLATFORM_BILI] = { qrcode_key: qrcodeData.qrcode_key }; const qrCode = await this._generateQRCode(qrcodeData.url); return { qrcode: qrCode, status: QRCodeHandler.STATUS_NEW }; } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_BILI] = null; throw new Error(e.message); } } async _checkBiliStatus() { const state = this.platformStates[QRCodeHandler.PLATFORM_BILI]; if (!state) { return {status: QRCodeHandler.STATUS_EXPIRED}; } try { const res = await axios({ url: "/http", method: "POST", data: { url: "https://passport.bilibili.com/x/passport-login/web/qrcode/poll", headers: { ...QRCodeHandler.HEADERS }, params: { qrcode_key: state.qrcode_key, source: "main-mini" } } }); const resData = res.data; if (resData.data.code !== 0) { throw new Error(resData.data.message); } if (resData.data.data.code === 86101) { // 未扫码 return {status: QRCodeHandler.STATUS_NEW}; } else if (resData.data.data.code === 86090) { // 已扫码未确认 return {status: QRCodeHandler.STATUS_SCANED}; } else if (resData.data.data.code === 0) { // 已确认 const url = resData.data.data.url; let cookie = ""; if (url) { const search = new URL(url).search; cookie = search.slice(1); cookie = decodeURIComponent(cookie); } return { status: QRCodeHandler.STATUS_CONFIRMED, cookie: cookie }; } else { // 二维码过期 this.platformStates[QRCodeHandler.PLATFORM_BILI] = null; return {status: QRCodeHandler.STATUS_EXPIRED}; } } catch (e) { this.platformStates[QRCodeHandler.PLATFORM_BILI] = null; throw new Error(e.message); } } }