_lib.scan.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. class QRCodeHandler {
  2. // 状态常量
  3. static STATUS_NEW = "NEW"; // 待扫描
  4. static STATUS_SCANED = "SCANED"; // 已扫描
  5. static STATUS_CONFIRMED = "CONFIRMED"; // 已确认
  6. static STATUS_CANCELED = "CANCELED"; // 已取消
  7. static STATUS_EXPIRED = "EXPIRED"; // 已过期
  8. // 平台常量
  9. static PLATFORM_QUARK = "quark"; // 夸克
  10. static PLATFORM_ALI = "ali"; // 阿里云盘
  11. static PLATFORM_UC = "uc"; // UC
  12. static PLATFORM_BILI = "bili"; // 哔哩哔哩
  13. // 通用请求头
  14. static HEADERS = {
  15. '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',
  16. 'Content-Type': 'application/x-www-form-urlencoded',
  17. 'Accept': 'application/json, text/plain, */*'
  18. };
  19. constructor() {
  20. this.platformStates = {
  21. [QRCodeHandler.PLATFORM_QUARK]: null,
  22. [QRCodeHandler.PLATFORM_ALI]: null,
  23. [QRCodeHandler.PLATFORM_UC]: null,
  24. [QRCodeHandler.PLATFORM_BILI]: null
  25. };
  26. }
  27. }
  28. const qrcode = new QRCodeHandler();
  29. function formatCookiesToList(cookieString) {
  30. const result = [];
  31. let currentCookie = '';
  32. let inExpires = false;
  33. for (let i = 0; i < cookieString.length; i++) {
  34. const char = cookieString[i];
  35. // 判断是否进入或退出 `expires` 属性
  36. if (cookieString.slice(i, i + 8).toLowerCase() === 'expires=') {
  37. inExpires = true;
  38. }
  39. if (inExpires && char === ';') {
  40. inExpires = false;
  41. }
  42. // 检测到逗号分隔符并且不在 `expires` 属性中,表示一个 Cookie 条目结束
  43. if (char === ',' && !inExpires) {
  44. result.push(currentCookie.trim());
  45. currentCookie = '';
  46. } else {
  47. currentCookie += char;
  48. }
  49. }
  50. // 添加最后一个 Cookie 条目
  51. if (currentCookie.trim()) {
  52. result.push(currentCookie.trim());
  53. }
  54. return result;
  55. }
  56. function formatCookie(cookies) {
  57. if (!Array.isArray(cookies)) cookies = [cookies];
  58. if (cookies.length === 0) return '';
  59. let mainCookies = [];
  60. for (const cookie of cookies) {
  61. if (cookie && typeof cookie === 'string' && cookie.trim()) {
  62. mainCookies.push(cookie.split('; ')[0]);
  63. }
  64. }
  65. return mainCookies.join(';');
  66. }
  67. async function _checkQuarkStatus(state, httpUrl) {
  68. try {
  69. const res = await axios({
  70. url: httpUrl,
  71. method: "POST",
  72. data: {
  73. url: "https://uop.quark.cn/cas/ajax/getServiceTicketByQrcodeToken",
  74. headers: {
  75. ...QRCodeHandler.HEADERS
  76. },
  77. params: {
  78. request_id: state.request_id,
  79. client_id: "532",
  80. v: "1.2",
  81. token: state.token
  82. }
  83. }
  84. });
  85. const resData = res.data;
  86. if (resData.data.status === 2000000) { // 扫码成功
  87. const serviceTicket = resData.data.data.members.service_ticket;
  88. const cookieRes = await axios({
  89. url: httpUrl,
  90. method: "POST",
  91. data: {
  92. url: "https://pan.quark.cn/account/info",
  93. headers: {
  94. ...QRCodeHandler.HEADERS
  95. },
  96. params: {
  97. st: serviceTicket,
  98. lw: "scan"
  99. }
  100. }
  101. });
  102. log('扫码成功,开始获取cookie');
  103. const cookieResData = cookieRes.data;
  104. const cookies = Array.isArray(cookieResData.headers['set-cookie']) ? cookieResData.headers['set-cookie'].join('; ') : cookieResData.headers['set-cookie'];
  105. const cookies2array = formatCookiesToList(cookies);
  106. let mainCookies = formatCookie(cookies2array);
  107. const cookieSelfRes = await axios({
  108. url: httpUrl,
  109. method: "POST",
  110. data: {
  111. 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",
  112. headers: {
  113. ...QRCodeHandler.HEADERS,
  114. Origin: 'https://pan.quark.cn',
  115. Referer: 'https://pan.quark.cn/',
  116. Cookie: mainCookies
  117. }
  118. }
  119. });
  120. const cookieResDataSelf = cookieSelfRes.data;
  121. const cookiesSelf = Array.isArray(cookieResDataSelf.headers['set-cookie']) ? cookieResDataSelf.headers['set-cookie'].join('; ') : cookieResDataSelf.headers['set-cookie'];
  122. const cookies2arraySelf = formatCookiesToList(cookiesSelf);
  123. const mainCookiesSelf = formatCookie(cookies2arraySelf);
  124. if (mainCookiesSelf) mainCookies += ';' + mainCookiesSelf;
  125. return {
  126. status: QRCodeHandler.STATUS_CONFIRMED,
  127. cookie: mainCookies
  128. };
  129. } else if (resData.data.status === 50004002) { // token过期
  130. return {status: 'EXPIRED'};
  131. } else {
  132. return {status: 'NEW'};
  133. }
  134. } catch (e) {
  135. console.error(e);
  136. log(`[_checkQuarkStatus] error:${e.message}`);
  137. throw new Error(e.response.data.message || e.message);
  138. }
  139. }
  140. async function _checkUCStatus(state, httpUrl) {
  141. try {
  142. const res = await axios({
  143. url: httpUrl,
  144. method: "POST",
  145. data: {
  146. url: "https://api.open.uc.cn/cas/ajax/getServiceTicketByQrcodeToken",
  147. method: "POST",
  148. headers: {
  149. ...QRCodeHandler.HEADERS
  150. },
  151. params: {
  152. __t: Date.now()
  153. },
  154. data: {
  155. v: "1.2",
  156. request_id: state.request_id,
  157. client_id: "381",
  158. token: state.token
  159. }
  160. }
  161. });
  162. const resData = res.data;
  163. if (resData.data.status === 2000000) { // 扫码成功
  164. const serviceTicket = resData.data.data.members.service_ticket;
  165. const cookieRes = await axios({
  166. url: httpUrl,
  167. method: "POST",
  168. data: {
  169. url: "https://drive.uc.cn/account/info",
  170. headers: {
  171. ...QRCodeHandler.HEADERS
  172. },
  173. params: {
  174. st: serviceTicket
  175. },
  176. }
  177. });
  178. log('扫码成功,开始获取cookie');
  179. const cookieResData = cookieRes.data;
  180. const cookies = cookieResData.headers['set-cookie'];
  181. const cookies2array = formatCookiesToList(cookies);
  182. let mainCookies = formatCookie(cookies2array);
  183. const cookieSelfRes = await axios({
  184. url: httpUrl,
  185. method: "POST",
  186. data: {
  187. url: "https://pc-api.uc.cn/1/clouddrive/config?pr=UCBrowser&fr=pc",
  188. headers: {
  189. ...QRCodeHandler.HEADERS,
  190. Origin: 'https://drive.uc.cn',
  191. Referer: 'https://drive.uc.cn/',
  192. Cookie: mainCookies
  193. }
  194. }
  195. });
  196. const cookieResDataSelf = cookieSelfRes.data;
  197. const cookiesSelf = Array.isArray(cookieResDataSelf.headers['set-cookie']) ? cookieResDataSelf.headers['set-cookie'].join('; ') : cookieResDataSelf.headers['set-cookie'];
  198. const cookies2arraySelf = formatCookiesToList(cookiesSelf);
  199. const mainCookiesSelf = formatCookie(cookies2arraySelf);
  200. if (mainCookiesSelf) mainCookies += ';' + mainCookiesSelf;
  201. return {
  202. status: QRCodeHandler.STATUS_CONFIRMED,
  203. cookie: mainCookies
  204. };
  205. } else if (resData.data.status === 50004002) { // token过期
  206. return {status: 'EXPIRED'};
  207. } else {
  208. return {status: 'NEW'};
  209. }
  210. } catch (e) {
  211. console.error(e);
  212. log(`[_checkUCStatus] error:${e.message}`);
  213. throw new Error(e.response.data.message || e.message);
  214. }
  215. }
  216. async function _checkAliStatus(state, httpUrl) {
  217. try {
  218. const res = await axios({
  219. url: httpUrl,
  220. method: "POST",
  221. data: {
  222. url: "https://passport.aliyundrive.com/newlogin/qrcode/query.do",
  223. method: "POST",
  224. headers: {
  225. ...QRCodeHandler.HEADERS
  226. },
  227. params: {
  228. appName: "aliyun_drive",
  229. fromSite: "52",
  230. _bx_v: "2.2.3"
  231. },
  232. data: {
  233. ck: state.ck,
  234. t: state.t,
  235. appName: "aliyun_drive",
  236. appEntrance: "web",
  237. isMobile: "false",
  238. lang: "zh_CN",
  239. returnUrl: "",
  240. navlanguage: "zh-CN",
  241. bizParams: ""
  242. }
  243. }
  244. });
  245. const resData = res.data;
  246. if (!resData.data.content || !resData.data.content.data) {
  247. return {status: QRCodeHandler.STATUS_EXPIRED};
  248. }
  249. const status = resData.data.content.data.qrCodeStatus;
  250. if (status === "CONFIRMED") {
  251. if (resData.data.content.data.bizExt) {
  252. const bizExt = JSON.parse(atob(resData.data.content.data.bizExt));
  253. console.log('[_lib.scan.js]阿里扫码结果:', bizExt.pds_login_result);
  254. return {
  255. status: QRCodeHandler.STATUS_CONFIRMED,
  256. token: bizExt.pds_login_result.refreshToken
  257. };
  258. }
  259. return {status: QRCodeHandler.STATUS_EXPIRED};
  260. } else if (status === "SCANED") {
  261. return {status: QRCodeHandler.STATUS_SCANED};
  262. } else if (status === "CANCELED") {
  263. this.platformStates[QRCodeHandler.PLATFORM_ALI] = null;
  264. return {status: QRCodeHandler.STATUS_CANCELED};
  265. } else if (status === "NEW") {
  266. return {status: QRCodeHandler.STATUS_NEW};
  267. } else {
  268. return {status: QRCodeHandler.STATUS_EXPIRED};
  269. }
  270. } catch (e) {
  271. console.error(e);
  272. log(`[_checkAliStatus] error:${e.message}`);
  273. throw new Error(e.response.data.message || e.message);
  274. }
  275. }
  276. async function _checkBiliStatus(state, httpUrl) {
  277. try {
  278. const res = await axios({
  279. url: httpUrl,
  280. method: "POST",
  281. data: {
  282. url: "https://passport.bilibili.com/x/passport-login/web/qrcode/poll",
  283. headers: {
  284. ...QRCodeHandler.HEADERS
  285. },
  286. params: {
  287. qrcode_key: state.qrcode_key,
  288. source: "main-mini"
  289. }
  290. }
  291. });
  292. const resData = res.data;
  293. if (resData.data.code !== 0) {
  294. throw new Error(resData.data.message);
  295. }
  296. if (resData.data.data.code === 86101) { // 未扫码
  297. return {status: QRCodeHandler.STATUS_NEW};
  298. } else if (resData.data.data.code === 86090) { // 已扫码未确认
  299. return {status: QRCodeHandler.STATUS_SCANED};
  300. } else if (resData.data.data.code === 0) { // 已确认
  301. const url = resData.data.data.url;
  302. let cookie = "";
  303. if (url) {
  304. const search = new URL(url).search;
  305. cookie = search.slice(1);
  306. cookie = decodeURIComponent(cookie);
  307. }
  308. return {
  309. status: QRCodeHandler.STATUS_CONFIRMED,
  310. cookie: cookie
  311. };
  312. } else { // 二维码过期
  313. this.platformStates[QRCodeHandler.PLATFORM_BILI] = null;
  314. return {status: QRCodeHandler.STATUS_EXPIRED};
  315. }
  316. } catch (e) {
  317. console.error(e);
  318. log(`[_checkBiliStatus] error:${e.message}`);
  319. throw new Error(e.response.data.message || e.message);
  320. }
  321. }
  322. $.exports = {
  323. QRCodeHandler,
  324. qrcode,
  325. formatCookiesToList,
  326. formatCookie,
  327. _checkQuarkStatus,
  328. _checkUCStatus,
  329. _checkAliStatus,
  330. _checkBiliStatus,
  331. }