Skip to content

Commit 9444148

Browse files
authored
添加qq扫码登陆功能,修复global关键词bug (#90)
1 parent 715a941 commit 9444148

15 files changed

+223
-38
lines changed

app.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ const cookie = require('./util/cookie');
1313
require('./util/colors');
1414
const userInfo = require('./config/user-info');
1515
const package = require('./package.json');
16-
global = Object.assign({}, userInfo);
16+
global.userInfo = Object.assign({}, userInfo);
1717

1818
console.log(chalk.green('\n🥳🎉 We had supported config the user cookies. \n'));
1919

20-
if (!(global.loginUin || global.uin)) {
20+
if (!(global.userInfo.loginUin || global.userInfo.uin)) {
2121
console.log(chalk.yellow(`😔 The configuration ${chalk.red('loginUin')} or your ${chalk.red('cookie')} in file ${chalk.green('config/user-info')} has not configured. \n`));
2222
}
2323

24-
if (!global.cookie) {
24+
if (!global.userInfo.cookie) {
2525
console.log(chalk.yellow(`😔 The configuration ${chalk.red('cookie')} in file ${chalk.green('config/user-info')} has not configured. \n`));
2626
}
2727

config/user-info.js

+33-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,42 @@
1-
const userInfo = {
2-
loginUin: '',
3-
cookie: '',
4-
};
1+
const fs = require('fs')
2+
const path = require('path')
53

6-
const cookieList = userInfo.cookie.split('; ').map(_ => _.trim());
4+
let userInfo, cookieList, cookieObject;
5+
const infoPath = path.join(__dirname, './user-info.json');
6+
fs.existsSync(infoPath) || fs.writeFileSync(infoPath, '{}', 'utf-8');
77

8-
const cookieObject = {};
9-
cookieList.filter(Boolean).forEach(_ => {
10-
if (_) {
11-
const [key, value = ''] = _.split('=');
8+
const initData = () => {
9+
10+
userInfo = { loginUin: '', cookie: '', ...JSON.parse(fs.readFileSync(infoPath, 'utf-8')) };
11+
cookieList = userInfo.cookie.split('; ').map(_ => _.trim());
1212

13-
cookieObject[key] = value;
14-
}
15-
});
13+
cookieObject = {};
14+
cookieList.filter(Boolean).forEach(_ => {
15+
if (_) {
16+
const [key, value = ''] = _.split('=');
17+
18+
cookieObject[key] = value;
19+
}
20+
});
21+
};
22+
23+
const refreshData = cookie => {
24+
const uin = cookie.match(/ uin=([^;]+)/)[1];
25+
fs.writeFileSync(infoPath, JSON.stringify({ loginUin: uin, cookie: cookie }), 'utf-8');
26+
initData();
27+
return {
28+
...userInfo,
29+
uin: userInfo.loginUin || cookieObject.uin,
30+
cookieList,
31+
cookieObject
32+
};
33+
};
34+
35+
initData();
1636

1737
module.exports = Object.assign(userInfo, {
1838
uin: userInfo.loginUin || cookieObject.uin,
1939
cookieList,
2040
cookieObject,
41+
refreshData
2142
});

module/apis/user/checkQQLoginQr.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const { getGtk, getGuid } = require('../../../util/loginUtils');
2+
const { refreshData } = require('../../../config/user-info');
3+
4+
module.exports = async ({ method = 'get', params = {}, option = {} }) => {
5+
const { ptqrtoken, qrsig } = params;
6+
if (!ptqrtoken || !qrsig) return { body: '参数错误' };
7+
const url = `https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump&ptqrtoken=${ptqrtoken}&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1711022193435&js_ver=23111510&js_type=1&login_sig=du-YS1h8*0GqVqcrru0pXkpwVg2DYw-DtbFulJ62IgPf6vfiJe*4ONVrYc5hMUNE&pt_uistyle=40&aid=716027609&daid=383&pt_3rd_aid=100497308&&o1vId=3674fc47871e9c407d8838690b355408&pt_js_version=v1.48.1`;
8+
const response = await fetch(url, { headers: { Cookie: `qrsig=${qrsig}` }});
9+
const { data = '' } = await response.text();
10+
let allCookie = [];
11+
const setCookie = cookies => {
12+
allCookie = [...allCookie, ...cookies.map(i => i.split(';')[0]).filter(i => i.split('=')[1])];
13+
};
14+
const refresh = data.includes('已失效')
15+
if (!data.includes('登录成功')) return { status: 200, isOk: false, refresh, message: refresh && '二维码已失效' || '未扫描二维码' };
16+
17+
// 获取p_skey 与gtk
18+
const checkSigUrl = data.match(/(?:'((?:https?|ftp):\/\/[^\s/$.?#].[^\s]*)')/g)[0].replaceAll('\'', '');
19+
const checkSigRes = await fetch(checkSigUrl, { redirect: 'manual', headers: { Cookie: allCookie.join('; ') } });
20+
const p_skey = checkSigRes.headers.get('Set-Cookie').match(/p_skey=([^;]+)/)[1];
21+
const gtk = getGtk(p_skey);
22+
setCookie(checkSigRes.headers.get('Set-Cookie').split(';, '));
23+
24+
// authorize
25+
const authorizeUrl = 'https://graph.qq.com/oauth2.0/authorize';
26+
const getAuthorizeData = (gtk) => {
27+
let data = new FormData()
28+
data.append('response_type', 'code')
29+
data.append('client_id', 100497308)
30+
data.append('redirect_uri', 'https://y.qq.com/portal/wx_redirect.html?login_type=1&surl=https://y.qq.com/')
31+
data.append('scope', 'get_user_info,get_app_friends')
32+
data.append('state', 'state')
33+
data.append('switch', '')
34+
data.append('from_ptlogin', 1)
35+
data.append('src', 1)
36+
data.append('update_auth', 1)
37+
data.append('openapi', '1010_1030')
38+
data.append('g_tk', gtk)
39+
data.append('auth_time', new Date)
40+
data.append('ui', getGuid())
41+
return data
42+
};
43+
44+
const authorizeRes = await fetch(authorizeUrl, {
45+
redirect: 'manual',
46+
method: 'POST',
47+
body: getAuthorizeData(gtk),
48+
headers: {
49+
Cookie: allCookie.join('; ')
50+
}
51+
});
52+
const code = authorizeRes.headers.get('Location').match(/[?&]code=([^&]+)/)[1];
53+
54+
// login
55+
const getFcgReqData = (g_tk, code) => {
56+
const data = {
57+
comm: {
58+
"g_tk":g_tk,
59+
"platform":"yqq",
60+
"ct":24,
61+
"cv":0
62+
}, req: {
63+
"module":"QQConnectLogin.LoginServer",
64+
"method":"QQLogin",
65+
"param": {
66+
"code":code
67+
}
68+
}
69+
}
70+
return JSON.stringify(data)
71+
};
72+
73+
const loginUrl = 'https://u.y.qq.com/cgi-bin/musicu.fcg';
74+
const loginRes = await fetch(loginUrl, {
75+
method: 'POST',
76+
body: getFcgReqData(gtk, code),
77+
headers: {
78+
'Content-Type': 'application/x-www-form-urlencoded',
79+
Cookie: allCookie.join('; ')
80+
}
81+
});
82+
setCookie(loginRes.headers.get('Set-Cookie').split(';, '));
83+
global.userInfo = { ...refreshData(allCookie.join('; ')) }
84+
85+
return { status: 200, isOk: true, message: '登录成功' };
86+
};

module/apis/user/getQQLoginQr.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const { hash33 } = require('../../../util/loginUtils');
2+
3+
module.exports = async ({ method = 'get', params = {}, option = {} }) => {
4+
const url = 'https://ssl.ptlogin2.qq.com/ptqrshow?appid=716027609&e=2&l=M&s=3&d=72&v=4&t=0.9698127522807933&daid=383&pt_3rd_aid=100497308&u1=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump';
5+
const response = await fetch(url, { responseType: 'arraybuffer' });
6+
const data = await response.arrayBuffer();
7+
const img = "data:image/png;base64," + (data && Buffer.from(data).toString('base64'));
8+
const qrsig = response.headers.get('Set-Cookie')?.match(/qrsig=([^;]+)/)[1];
9+
10+
return {status: 200, body: { img, ptqrtoken: hash33(qrsig), qrsig } };
11+
};

module/config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const commonParams = {
22
g_tk: 1124214810,
3-
loginUin: global.uin || '0',
3+
loginUin: global.userInfo?.uin || '0',
44
hostUin: 0,
55
inCharset: 'utf8',
66
outCharset: 'utf-8',

module/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ const UCommon = require('./apis/UCommon/UCommon');
3939
// getTopLists
4040
const getTopLists = require('./apis/rank/getTopLists');
4141

42+
// getQQLoginQr
43+
const getQQLoginQr = require('./apis/user/getQQLoginQr');
44+
45+
// checkQQLoginQr
46+
const checkQQLoginQr = require('./apis/user/checkQQLoginQr');
47+
4248
module.exports = {
4349
downloadQQMusic,
4450
// search
@@ -70,4 +76,7 @@ module.exports = {
7076
UCommon,
7177
// getTopLists
7278
getTopLists,
79+
// login
80+
getQQLoginQr,
81+
checkQQLoginQr
7382
};

routers/context/checkQQLoginQr.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { checkQQLoginQr } = require('../../module');
2+
3+
module.exports = async (ctx, next) => {
4+
const { ptqrtoken, qrsig } = ctx.request.body;
5+
6+
const params = { ptqrtoken, qrsig };
7+
8+
const props = {
9+
method: 'get',
10+
option: {},
11+
params,
12+
};
13+
const data = await checkQQLoginQr(props);
14+
Object.assign(ctx, { body: data });
15+
};

routers/context/cookies.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ module.exports = {
1010
ctx.body = {
1111
data: {
1212
code: 200,
13-
cookie: global.cookie,
14-
cookieList: global.cookieList,
15-
cookieObject: global.cookieObject,
13+
cookie: global.userInfo.cookie,
14+
cookieList: global.userInfo.cookieList,
15+
cookieObject: global.userInfo.cookieObject,
1616
},
1717
};
1818

1919
await next();
2020
},
2121
set: async (ctx, next) => {
22-
ctx.request.cookies = global.cookie;
22+
ctx.request.cookies = global.userInfo.cookie;
2323
ctx.request.header['Access-Control-Allow-Origin'] = 'https://y.qq.com';
2424
ctx.request.header['Access-Control-Allow-Methods'] = 'GET,PUT,POST,DELETE';
2525
ctx.request.header['Access-Control-Allow-Headers'] = 'Content-Type';

routers/context/getMusicPlay.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const get = require('lodash.get');
55
// songmid=003rJSwm3TechU
66
// songmid=001yNIo41SJjuC,001wPuVc4ZiMhj
77
module.exports = async (ctx, next) => {
8-
const uin = global.uin || '0';
8+
const uin = global.userInfo.uin || '0';
99
const songmid = ctx.query.songmid + '';
1010
// response data only need play url value (all play)
1111
const justPlayUrl = (ctx.query.resType || 'play') === 'play';

routers/context/getQQLoginQr.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const { getQQLoginQr } = require('../../module');
2+
3+
module.exports = async (ctx, next) => {
4+
const props = {
5+
method: 'get',
6+
};
7+
const { status, body } = await getQQLoginQr(props);
8+
Object.assign(ctx, {
9+
status,
10+
body,
11+
});
12+
};

routers/context/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ const getTopLists = require('./getTopLists');
3030
const getRanks = require('./getRanks');
3131
const getTicketInfo = require('./getTicketInfo');
3232
const getImageUrl = require('./getImageUrl');
33+
const getQQLoginQr = require('./getQQLoginQr');
34+
const checkQQLoginQr = require('./checkQQLoginQr');
3335
const {get: getCookie, set: setCookie} = require('./cookies');
3436

3537
module.exports = {
@@ -67,4 +69,6 @@ module.exports = {
6769
getRanks,
6870
getTicketInfo,
6971
getImageUrl,
72+
getQQLoginQr,
73+
checkQQLoginQr
7074
};

routers/router.js

+4
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,8 @@ router.get('/getTicketInfo', context.getTicketInfo);
105105
// getImageUrl
106106
router.get('/getImageUrl', context.getImageUrl);
107107

108+
// getQQLoginQr
109+
router.get('/user/getQQLoginQr', context.getQQLoginQr);
110+
router.post('/user/checkQQLoginQr', context.checkQQLoginQr);
111+
108112
module.exports = router;

util/cookie.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
module.exports = () => async (ctx, next) => {
2-
if (global.cookie) {
3-
ctx.request.cookie = global.cookie;
2+
if (global.userInfo.cookie) {
3+
ctx.request.cookie = global.userInfo.cookie;
44
}
55

66
const cookieHeader = ctx.request.headers;
77

88
if (cookieHeader) {
9-
global.cookieList.forEach(cookie => {
9+
global.userInfo.cookieList.forEach(cookie => {
1010
const [key, value = ''] = cookie.split('=');
1111

1212
if (value) {

util/loginUtils.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const hash33 = (t) => {
2+
for (var e = 0, n = 0, o = t.length; n < o; ++n)
3+
e += (e << 5) + t.charCodeAt(n);
4+
return 2147483647 & e
5+
};
6+
7+
const getGtk = (p_skey) => {
8+
var str = p_skey, hash = 5381;
9+
for (var i = 0, len = str.length; i < len; ++i) {
10+
hash += (hash << 5) + str.charCodeAt(i);
11+
}
12+
return hash & 0x7fffffff;
13+
};
14+
15+
const getGuid = () => {
16+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
17+
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
18+
return v.toString(16);
19+
}).toUpperCase();
20+
};
21+
22+
23+
module.exports = { hash33, getGtk, getGuid };

util/request.js

+14-14
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,29 @@ axios.defaults.responseType = 'json;text/plain;charset=utf-8;';
1010
const setHeaders = headers => {
1111
return {
1212
...headers,
13-
cookies: global.cookie
13+
cookies: global.userInfo.cookie
1414
};
1515
};
1616

1717
let yURL = 'https://y.qq.com';
1818
let cURL = 'https://c.y.qq.com';
1919
// let uURL = 'https:/u.y.qq.com/cgi-bin/musicu.fcg';
2020

21-
function request(url, method, options = {}, isUUrl = 'c') {
21+
function request(url, method = 'GET', options = {}, isUUrl = 'c') {
2222
let baseURL = '';
2323
switch (isUUrl) {
24-
case 'y':
25-
baseURL = yURL + url;
26-
break;
27-
case 'u':
28-
baseURL = url;
29-
break;
30-
case 'c':
31-
baseURL = cURL + url;
32-
break;
33-
default:
34-
baseURL = cURL + url;
35-
break;
24+
case 'y':
25+
baseURL = yURL + url;
26+
break;
27+
case 'u':
28+
baseURL = url;
29+
break;
30+
case 'c':
31+
baseURL = cURL + url;
32+
break;
33+
default:
34+
baseURL = cURL + url;
35+
break;
3636
}
3737

3838
options = Object.assign(options, {

0 commit comments

Comments
 (0)