-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
92 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ class AppClient { | |
// bilibili 客户端 | ||
private static readonly __secretKey: string = '560c52ccd288fed045859ed18bffd973' | ||
public static readonly appKey: string = '1d8b6e7d45233436' | ||
public static readonly build: string = '521200' | ||
public static readonly build: string = '5220000' | ||
public static readonly mobiApp: string = 'android' | ||
// bilibili 国际版 | ||
// private static readonly __secretKey: string = '36efcfed79309338ced0380abd824ac1' | ||
|
@@ -85,17 +85,19 @@ class AppClient { | |
*/ | ||
public static get baseQuery(): string { | ||
return `actionKey=${this.actionKey}&appkey=${this.appKey}&build=${this.build}\ | ||
&mobi_app=${this.mobiApp}&platform=${this.platform}&ts=${this.TS}` | ||
&mobi_app=${this.mobiApp}&platform=${this.platform}` | ||
} | ||
/** | ||
* 对参数签名 | ||
* | ||
* @static | ||
* @param {string} params | ||
* @returns {string} | ||
* @param {string} params | ||
* @param {boolean} [ts=true] | ||
* @returns {string} | ||
* @memberof AppClient | ||
*/ | ||
public static signQuery(params: string): string { | ||
public static signQuery(params: string, ts = true): string { | ||
if (ts) params = `${params}&ts=${this.TS}` | ||
const paramsSecret = params + this.__secretKey | ||
const paramsHash = tools.Hash('md5', paramsSecret) | ||
return `${params}&sign=${paramsHash}` | ||
|
@@ -176,9 +178,9 @@ class AppClient { | |
* @memberof AppClient | ||
*/ | ||
public headers: request.Headers = { | ||
'Buvid': '7A4C4919-20A6-4012-BBA4-6FAA1561542845107infoc', | ||
'Connection': 'Keep-Alive', | ||
'User-Agent': 'Mozilla/5.0 BiliDroid/5.21.0 ([email protected])' | ||
'Device-ID': 'Pwc3BzUCYwJjUWAGegZ6', | ||
'User-Agent': 'Mozilla/5.0 BiliDroid/5.22.0 ([email protected])' | ||
} | ||
/** | ||
* cookieJar | ||
|
@@ -240,7 +242,7 @@ class AppClient { | |
const auth: request.Options = { | ||
method: 'POST', | ||
uri: 'https://passport.bilibili.com/api/v2/oauth2/login', | ||
body: AppClient.signQuery(authQuery), | ||
body: AppClient.signQuery(authQuery, false), | ||
jar: this.__jar, | ||
json: true, | ||
headers: this.headers | ||
|
@@ -272,9 +274,22 @@ class AppClient { | |
* @memberof AppClient | ||
*/ | ||
public async init() { | ||
const buvid = await tools.XHR<string>({ uri: 'http://data.bilibili.com/gv/' }, 'Android') | ||
if (buvid !== undefined && buvid.response.statusCode === 200 && buvid.body.length === 46) | ||
// 设置 Buvid | ||
const buvid = await tools.XHR<string>({ | ||
uri: 'http://data.bilibili.com/gv/', | ||
headers: this.headers | ||
}, 'Android') | ||
if (buvid !== undefined && buvid.response.statusCode === 200 && buvid.body.endsWith('infoc')) | ||
this.headers['Buvid'] = buvid.body | ||
// 设置 Display-ID | ||
const displayid = await tools.XHR<{ code: number, data: { id: string } }>({ | ||
uri: 'http://app.bilibili.com/x/v2/display/id?' + AppClient.signQueryBase(), | ||
json: true, | ||
headers: this.headers | ||
}, 'Android') | ||
if (displayid !== undefined && displayid.response.statusCode === 200 | ||
&& displayid.body.code === 0 && displayid.body.data.id.length > 20) | ||
this.headers['Display-ID'] = displayid.body.data.id | ||
} | ||
/** | ||
* 获取验证码 | ||
|
@@ -316,6 +331,28 @@ class AppClient { | |
} | ||
return { status: status.httpError, data: getKeyResponse } | ||
} | ||
/** | ||
* 客户端登出 | ||
* | ||
* @returns {Promise<logoutResponse>} | ||
* @memberof AppClient | ||
*/ | ||
public async logout(): Promise<logoutResponse> { | ||
const revokeQuery = `${this.cookieString.replace(/; */g, '&')}&access_token=${this.accessToken}` | ||
const revoke: request.Options = { | ||
method: 'POST', | ||
uri: 'https://passport.bilibili.com/api/v2/oauth2/revoke', | ||
body: AppClient.signQueryBase(revokeQuery), | ||
json: true, | ||
headers: this.headers | ||
} | ||
const revokeResponse = await tools.XHR<revokeResponse>(revoke, 'Android') | ||
if (revokeResponse !== undefined && revokeResponse.response.statusCode === 200) { | ||
if (revokeResponse.body.code === 0) return { status: status.success, data: revokeResponse.body } | ||
return { status: status.error, data: revokeResponse.body } | ||
} | ||
return { status: status.httpError, data: revokeResponse } | ||
} | ||
/** | ||
* 更新access_token | ||
* | ||
|
@@ -324,7 +361,7 @@ class AppClient { | |
*/ | ||
public async refresh(): Promise<loginResponse> { | ||
const refreshQuery = `access_token=${this.accessToken}&appkey=${AppClient.appKey}&build=${AppClient.build}\ | ||
&mobi_app=${AppClient.mobiApp}&platform=${AppClient.platform}&refresh_token=${this.refreshToken}&ts=${AppClient.TS}` | ||
&mobi_app=${AppClient.mobiApp}&platform=${AppClient.platform}&refresh_token=${this.refreshToken}` | ||
const refresh: request.Options = { | ||
method: 'POST', | ||
uri: 'https://passport.bilibili.com/api/v2/oauth2/refresh_token', | ||
|
@@ -388,6 +425,16 @@ interface authResponseTokeninfo { | |
refresh_token: string | ||
expires_in: number | ||
} | ||
/** | ||
* 注销返回 | ||
* | ||
* @interface revokeResponse | ||
*/ | ||
interface revokeResponse { | ||
message: string | ||
ts: number | ||
code: number | ||
} | ||
/** | ||
* 登录返回信息 | ||
*/ | ||
|
@@ -408,6 +455,22 @@ interface loginResponseHttp { | |
status: status.httpError | ||
data: tools.response<getKeyResponse> | tools.response<authResponse> | undefined | ||
} | ||
/** | ||
* 登出返回信息 | ||
*/ | ||
type logoutResponse = revokeResponseSuccess | revokeResponseError | revokeResponseHttp | ||
interface revokeResponseSuccess { | ||
status: status.success | ||
data: revokeResponse | ||
} | ||
interface revokeResponseError { | ||
status: status.error | ||
data: revokeResponse | ||
} | ||
interface revokeResponseHttp { | ||
status: status.httpError | ||
data: tools.response<revokeResponse> | undefined | ||
} | ||
/** | ||
* 验证码返回信息 | ||
*/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,4 @@ | ||
import * as fs from 'fs' | ||
import * as dns from 'dns' | ||
import * as net from 'net' | ||
import * as http from 'http' | ||
import * as util from 'util' | ||
import * as crypto from 'crypto' | ||
import * as request from 'request' | ||
|
@@ -18,7 +15,7 @@ function getHeaders(platform: string): request.Headers { | |
case 'Android': | ||
return { | ||
'Connection': 'Keep-Alive', | ||
'User-Agent': 'Mozilla/5.0 BiliDroid/5.21.0 ([email protected])' | ||
'User-Agent': 'Mozilla/5.0 BiliDroid/5.22.0 ([email protected])' | ||
} | ||
case 'WebView': | ||
return { | ||
|
@@ -27,7 +24,7 @@ function getHeaders(platform: string): request.Headers { | |
'Connection': 'keep-alive', | ||
'Cookie': 'l=v', | ||
'Origin': liveOrigin, | ||
'User-Agent': 'Mozilla/5.0 (Linux; Android 7.1.1; E6883 Build/32.4.A.1.54; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36 BiliApp/1', | ||
'User-Agent': 'Mozilla/5.0 (Linux; Android 7.1.1; E6883 Build/32.4.A.1.54; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.119 Mobile Safari/537.36 BiliApp/5220000', | ||
'X-Requested-With': 'tv.danmaku.bili' | ||
} | ||
default: | ||
|
@@ -38,7 +35,7 @@ function getHeaders(platform: string): request.Headers { | |
'Cookie': 'l=v', | ||
'DNT': '1', | ||
'Origin': liveOrigin, | ||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' | ||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36' | ||
} | ||
} | ||
} | ||
|
@@ -49,20 +46,9 @@ function getHeaders(platform: string): request.Headers { | |
*/ | ||
class IP { | ||
constructor() { | ||
// @ts-ignore 此处为d.ts错误 | ||
this.httpAgent.createConnection = (options: net.NetConnectOpts, callback: Function): net.Socket => { | ||
// @ts-ignore ts对于联合类型的推断还在讨论中 | ||
options.lookup = (hostname, options, callback) => { | ||
const ip = this.ip | ||
if (ip === '') return dns.lookup(hostname, options, callback) | ||
return callback(null, ip, 4) | ||
} | ||
return net.createConnection(options, callback) | ||
} | ||
} | ||
public IPs: Set<string> = new Set() | ||
private __IPiterator: IterableIterator<string> = this.IPs.values() | ||
public httpAgent = new http.Agent() | ||
public get ip(): string { | ||
if (this.IPs.size === 0) return '' | ||
const ip = this.__IPiterator.next() | ||
|
@@ -78,8 +64,9 @@ const api = new IP() | |
* 测试可用ip | ||
* | ||
* @param {string[]} apiIPs | ||
* @returns {Promise<number>} | ||
*/ | ||
async function testIP(apiIPs: string[]) { | ||
async function testIP(apiIPs: string[]): Promise<number> { | ||
const test: Promise<undefined>[] = [] | ||
apiIPs.forEach(ip => { | ||
const headers = getHeaders('PC') | ||
|
@@ -98,7 +85,9 @@ async function testIP(apiIPs: string[]) { | |
})) | ||
}) | ||
await Promise.all(test) | ||
Log('可用ip数量为', api.IPs.size) | ||
const num = api.IPs.size | ||
Log('可用ip数量为', num) | ||
return num | ||
} | ||
const shortRoomID = new Map<number, number>() | ||
const longRoomID = new Map<number, number>() | ||
|
@@ -133,7 +122,13 @@ function XHR<T>(options: request.OptionsWithUri, platform: 'PC' | 'Android' | 'W | |
return new Promise<response<T> | undefined>(resolve => { | ||
options.gzip = true | ||
// 添加用户代理 | ||
if (typeof options.uri === 'string' && options.uri.startsWith(apiLiveOrigin)) options.agent = api.httpAgent | ||
if (typeof options.uri === 'string' && options.uri.startsWith(apiLiveOrigin)) { | ||
const ip = api.ip | ||
if (ip !== '') { | ||
options.proxy = `http://${ip}/` | ||
options.tunnel = false | ||
} | ||
} | ||
// 添加头信息 | ||
const headers = getHeaders(platform) | ||
options.headers = options.headers === undefined ? headers : Object.assign(headers, options.headers) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters