From db28a6c70b66746a1fa38b742b1ad8953fd46c45 Mon Sep 17 00:00:00 2001 From: snowtafir Date: Thu, 31 Oct 2024 21:54:15 +0800 Subject: [PATCH] optimize: use ck --- CHANGELOG.md | 1 + apps/bilibili.ts | 29 +- models/bilibili/bilibili.main.api.ts | 279 +++++++++++++ models/bilibili/bilibili.main.get.web.data.ts | 1 - models/bilibili/bilibili.mian.models.ts | 258 ++----------- models/bilibili/bilibili.risk.buid.fp.ts | 365 ++++++++++++++---- models/bilibili/bilibili.risk.wbi.ts | 78 +++- package.json | 2 +- 8 files changed, 688 insertions(+), 325 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2570a4b..ed8d349 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # 2.0.5 +* 优化ck * 新增 User-Agent 配置项 * 优化获取动态数据 * 新增获取B站up数据的随机延迟配置项 diff --git a/apps/bilibili.ts b/apps/bilibili.ts index 4ae6a08..221f72b 100644 --- a/apps/bilibili.ts +++ b/apps/bilibili.ts @@ -17,7 +17,7 @@ import { readSyncCookie, saveLocalBiliCk, saveLoginCookie, - readTempCk + saveTempCk } from '@/models/bilibili/bilibili.mian.models'; declare const logger: any; @@ -278,12 +278,6 @@ export default class YukiBili extends Plugin { let biliLoginCk = await pollLoginQRCode(this.e, token); - //let _uuid = readSavedCookieItems(biliLoginCk, ['_uuid']) - - //const buvid_fp = await get_buvid_fp(_uuid); - - //biliLoginCk = buvid_fp + biliLoginCk; - if (lodash.trim(biliLoginCk).length != 0) { await saveLoginCookie(this.e, biliLoginCk); this.e.reply(`get bilibili LoginCk:成功!`); @@ -373,14 +367,10 @@ export default class YukiBili extends Plugin { //筛选ck localBiliCookie = await readSavedCookieItems( localBiliCookie, - ['buvid3', 'buvid4', '_uuid', 'SESSDATA', 'DedeUserID', 'DedeUserID__ckMd5', 'bili_jct', 'b_nut', 'b_lsid'], + ['buvid3', 'buvid4', '_uuid', 'SESSDATA', 'DedeUserID', 'DedeUserID__ckMd5', 'bili_jct', 'b_nut', 'b_lsid', 'buvid_fp', 'sid'], false ); - //const buvid_fp = await get_buvid_fp(param._uuid) - - //localBiliCookie = buvid_fp + localBiliCookie; //添加buvid_fp值 - await saveLocalBiliCk(localBiliCookie); logger.mark(`${this.e.logFnc} 保存B站cookie成功 [UID:${param.DedeUserID}]`); @@ -445,9 +435,18 @@ export default class YukiBili extends Plugin { /** 删除并刷新redis缓存的临时B站ck */ async reflashTempCk() { try { - await getNewTempCk(); - let newTempCk = await readTempCk(); - if (newTempCk !== null && newTempCk !== undefined && newTempCk.length !== 0 && newTempCk !== '') { + const newTempCk = await getNewTempCk(); + if (newTempCk) { + await saveTempCk(newTempCk); + const result = await postGateway(newTempCk); + + const data = await result.data; // 解析校验结果 + + if (data?.code !== 0) { + logger?.error(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`); + } else if (data?.code === 0) { + logger?.mark(`优纪插件:tempCK,Gateway校验成功:${JSON.stringify(data)}`); + } this.e.reply( `~yuki-plugin:\n临时b站ck刷新成功~❤~\n接下来如果获取动态失败,请重启bot(手动或发送指令 #重启)刷新状态~\n如果重启续仍不可用,请考虑 #优纪添加b站登录 吧~` ); diff --git a/models/bilibili/bilibili.main.api.ts b/models/bilibili/bilibili.main.api.ts index 49401be..41f21d2 100644 --- a/models/bilibili/bilibili.main.api.ts +++ b/models/bilibili/bilibili.main.api.ts @@ -26,6 +26,8 @@ class BiliApi { // 将静态常量赋值给实例属性 get BILIBIL_API() { return { + //获取服务器端RTC时间戳 + biliServerTimeStamp: 'https://api.live.bilibili.com/xlive/open-interface/v1/rtc/getTimestamp', //获取动态资源列表 wbi/无wbi parama = { host_mid: uid, timezone_offset: -480, platform: 'web', features: 'itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote', web_location: "333.999", ...getDmImg(), "x-bili-device-req-json": { "platform": "web", "device": "pc" }, "x-bili-web-req-json": { "spm_id": "333.999" }, w_rid, wts } biliDynamicInfoList: `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space`, @@ -131,6 +133,283 @@ class BiliApi { 'User-Agent': this.USER_AGENT }; } + + /**返回GatWay payload */ + async BILIBILI_BROWSER_DATA(_uuid: string) { + return { + '3064': 1, // ptype, mobile => 2, others => 1 + '5062': `${Date.now()}`, // timestamp + '03bf': 'https://www.bilibili.com/', // url accessed + '39c8': '333.999.fp.risk', + '34f1': '', // target_url, default empty now + 'd402': '', // screenx, default empty + '654a': '', // screeny, default empty + '6e7c': '878x1066', // browser_resolution, window.innerWidth || document.body && document.body.clientWidth + "x" + window.innerHeight || document.body && document.body.clientHeight + '3c43': { + // 3c43 => msg + '2673': 0, // hasLiedResolution, window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight + '5766': 24, // colorDepth, window.screen.colorDepth + '6527': 0, // addBehavior, !!window.HTMLElement.prototype.addBehavior, html5 api + '7003': 1, // indexedDb, !!window.indexedDB, html5 api + '807e': 1, // cookieEnabled, navigator.cookieEnabled + 'b8ce': this.USER_AGENT, // ua "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0", + '641c': 0, // webdriver, navigator.webdriver, like Selenium + '07a4': 'zh-CN', // language + '1c57': 'not available', // deviceMemory in GB, navigator.deviceMemory + '0bd0': 16, // hardwareConcurrency, navigator.hardwareConcurrency + '748e': [1920, 1200], // window.screen.width window.screen.height + 'd61f': [1920, 1152], // window.screen.availWidth window.screen.availHeight + 'fc9d': -480, // timezoneOffset, (new Date).getTimezoneOffset() + '6aa9': 'Asia/Shanghai', //Intl.DateTimeFormat().resolvedOptions().timeZone, // timezone, (new window.Intl.DateTimeFormat).resolvedOptions().timeZone + '75b8': 1, // sessionStorage, window.sessionStorage, html5 api + '3b21': 1, // localStorage, window.localStorage, html5 api + '8a1c': 0, // openDatabase, window.openDatabase, html5 api + 'd52f': 'not available', // cpuClass, navigator.cpuClass + 'adca': this.USER_AGENT.includes('Windows') ? 'Win32' : 'Linux', // platform, navigator.platform + '80c9': [ + [ + 'PDF Viewer', + 'Portable Document Format', + [ + ['application/pdf', 'pdf'], + ['text/pdf', 'pdf'] + ] + ], + [ + 'Chrome PDF Viewer', + 'Portable Document Format', + [ + ['application/pdf', 'pdf'], + ['text/pdf', 'pdf'] + ] + ], + [ + 'Chromium PDF Viewer', + 'Portable Document Format', + [ + ['application/pdf', 'pdf'], + ['text/pdf', 'pdf'] + ] + ], + [ + 'Microsoft Edge PDF Viewer', + 'Portable Document Format', + [ + ['application/pdf', 'pdf'], + ['text/pdf', 'pdf'] + ] + ], + [ + 'WebKit built-in PDF', + 'Portable Document Format', + [ + ['application/pdf', 'pdf'], + ['text/pdf', 'pdf'] + ] + ] + ], // plugins + '13ab': 'f3YAAAAASUVORK5CYII=', // canvas fingerprint + 'bfe9': 'kABYpRAGAVYzWJooB9Bf4P+UortSvxRY0AAAAASUVORK5CYII=', // webgl_str + 'a3c1': [ + 'extensions:ANGLE_instanced_arrays;EXT_blend_minmax;EXT_color_buffer_half_float;EXT_float_blend;EXT_frag_depth;EXT_shader_texture_lod;EXT_sRGB;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_provoking_vertex', + 'webgl aliased line width range:[1, 1]', + 'webgl aliased point size range:[1, 1024]', + 'webgl alpha bits:8', + 'webgl antialiasing:yes', + 'webgl blue bits:8', + 'webgl depth bits:24', + 'webgl green bits:8', + 'webgl max anisotropy:16', + 'webgl max combined texture image units:32', + 'webgl max cube map texture size:16384', + 'webgl max fragment uniform vectors:1024', + 'webgl max render buffer size:16384', + 'webgl max texture image units:16', + 'webgl max texture size:16384', + 'webgl max varying vectors:30', + 'webgl max vertex attribs:16', + 'webgl max vertex texture image units:16', + 'webgl max vertex uniform vectors:4096', + 'webgl max viewport dims:[32767, 32767]', + 'webgl red bits:8', + 'webgl renderer:ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', + 'webgl shading language version:WebGL GLSL ES 1.0', + 'webgl stencil bits:0', + 'webgl vendor:Mozilla', + 'webgl version:WebGL 1.0', + 'webgl unmasked vendor:Google Inc. (Intel)', + 'webgl unmasked renderer:ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', + 'webgl vertex shader high float precision:23', + 'webgl vertex shader high float precision rangeMin:127', + 'webgl vertex shader high float precision rangeMax:127', + 'webgl vertex shader medium float precision:23', + 'webgl vertex shader medium float precision rangeMin:127', + 'webgl vertex shader medium float precision rangeMax:127', + 'webgl vertex shader low float precision:23', + 'webgl vertex shader low float precision rangeMin:127', + 'webgl vertex shader low float precision rangeMax:127', + 'webgl fragment shader high float precision:23', + 'webgl fragment shader high float precision rangeMin:127', + 'webgl fragment shader high float precision rangeMax:127', + 'webgl fragment shader medium float precision:23', + 'webgl fragment shader medium float precision rangeMin:127', + 'webgl fragment shader medium float precision rangeMax:127', + 'webgl fragment shader low float precision:23', + 'webgl fragment shader low float precision rangeMin:127', + 'webgl fragment shader low float precision rangeMax:127', + 'webgl vertex shader high int precision:0', + 'webgl vertex shader high int precision rangeMin:31', + 'webgl vertex shader high int precision rangeMax:30', + 'webgl vertex shader medium int precision:0', + 'webgl vertex shader medium int precision rangeMin:31', + 'webgl vertex shader medium int precision rangeMax:30', + 'webgl vertex shader low int precision:0', + 'webgl vertex shader low int precision rangeMin:31', + 'webgl vertex shader low int precision rangeMax:30', + 'webgl fragment shader high int precision:0', + 'webgl fragment shader high int precision rangeMin:31', + 'webgl fragment shader high int precision rangeMax:30', + 'webgl fragment shader medium int precision:0', + 'webgl fragment shader medium int precision rangeMin:31', + 'webgl fragment shader medium int precision rangeMax:30', + 'webgl fragment shader low int precision:0', + 'webgl fragment shader low int precision rangeMin:31', + 'webgl fragment shader low int precision rangeMax:30' + ], // webgl_params, cab be set to [] if webgl is not supported + '6bc5': 'Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', // webglVendorAndRenderer + 'ed31': 0, + '72bd': 0, + '097b': 0, + '52cd': [0, 0, 0], + 'a658': [ + 'Arial', + 'Arial Black', + 'Calibri', + 'Cambria', + 'Cambria Math', + 'Comic Sans MS', + 'Consolas', + 'Courier', + 'Courier New', + 'Georgia', + 'Helvetica', + 'Impact', + 'Lucida Console', + 'Lucida Sans Unicode', + 'Microsoft Sans Serif', + 'MS Gothic', + 'MS PGothic', + 'MS Sans Serif', + 'MS Serif', + 'Palatino Linotype', + 'Segoe Print', + 'Segoe Script', + 'Segoe UI', + 'Segoe UI Light', + 'Segoe UI Symbol', + 'Tahoma', + 'Times', + 'Times New Roman', + 'Trebuchet MS', + 'Verdana', + 'Wingdings' + ], + 'd02f': '35.749972093850374' + }, + '54ef': { + 'in_new_ab ': true, + 'ab_version ': { + 'waterfall_article ': 'SHOW ' + }, + 'ab_split_num ': { + 'waterfall_article ': 0 + } + }, + '8b94': '', + 'df35': `${_uuid}`, // _uuid, set from cookie, generated by client side(algorithm remains unknown) + '07a4': 'zh-CN', + '5f45': null, + 'db46': 0 + }; + } + + /**返回Bilibili Fingerprint data */ + BILIBILI_FINGERPRINT_DATA(_uuid: string) { + return { + userAgent: this.USER_AGENT, // 用户代理 + webdriver: false, // 是否是 WebDriver(例如 Selenium) + language: 'zh-CN', // 浏览器语言 + colorDepth: 24, // 屏幕颜色深度 + deviceMemory: 'not available', // 设备内存(GB) + pixelRatio: 2, // 设备像素比 + hardwareConcurrency: 8, // 处理器核心数量 + screenResolution: '1920x1200', // 屏幕分辨率 + availableScreenResolution: '1920x1152', // 可用屏幕分辨率 + timezoneOffset: -480, // 时区偏移,单位为分钟 + timezone: 'Asia/Shanghai', // 时区 + sessionStorage: true, // 是否支持 sessionStorage + localStorage: true, // 是否支持 localStorage + indexedDb: true, // 是否支持 IndexedDB + addBehavior: false, // 是否支持 addBehavior + openDatabase: false, // 是否支持 openDatabase + cpuClass: 'not available', // CPU 类型amd 或 intel 或 not available + platform: this.USER_AGENT.includes('Windows') ? 'Win32' : 'Linux', // 操作系统平台,例如 Windows,Linux,Mac + doNotTrack: null, // DNT 设置,通常是 `navigator.doNotTrack` + plugins: [ + { name: 'PDF Viewer', description: 'Portable Document Format', mimeTypes: [['application/pdf', 'pdf']] }, + { name: 'Chrome PDF Viewer', description: 'Portable Document Format', mimeTypes: [['application/pdf', 'pdf']] }, + { name: 'Chromium PDF Viewer', description: 'Portable Document Format', mimeTypes: [['application/pdf', 'pdf']] }, + { name: 'Microsoft Edge PDF Viewer', description: 'Portable Document Format', mimeTypes: [['application/pdf', 'pdf']] }, + { name: 'WebKit built-in PDF', description: 'Portable Document Format', mimeTypes: [['application/pdf', 'pdf']] } + ], + canvas: 'f3YAAAAASUVORK5CYII=', // Canvas 指纹 + webgl: 'kABYpRAGAVYzWJooB9Bf4P+UortSvxRY0AAAAASUVORK5CYII=', // WebGL 参数 + webglVendorAndRenderer: 'Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', // WebGL 的厂商和渲染器信息 + adBlock: false, // 是否检测到广告拦截器 + hasLiedLanguages: false, // 是否谎称语言 + hasLiedResolution: false, // 是否谎称分辨率 + hasLiedOs: false, // 是否谎称操作系统 + hasLiedBrowser: false, // 是否谎称浏览器 + touchSupport: 0, // 支持的触摸点数 + fonts: [ + 'Arial', + 'Arial Black', + 'Calibri', + 'Cambria', + 'Cambria Math', + 'Comic Sans MS', + 'Consolas', + 'Courier', + 'Courier New', + 'Georgia', + 'Helvetica', + 'Impact', + 'Lucida Console', + 'Lucida Sans Unicode', + 'Microsoft Sans Serif', + 'MS Gothic', + 'MS PGothic', + 'MS Sans Serif', + 'MS Serif', + 'Palatino Linotype', + 'Segoe Print', + 'Segoe Script', + 'Segoe UI', + 'Segoe UI Light', + 'Segoe UI Symbol', + 'Tahoma', + 'Times', + 'Times New Roman', + 'Trebuchet MS', + 'Verdana', + 'Wingdings' + ], + fontsFlash: false, // Flash 插件是否安装 + audio: '35.749972093850374', // 音频指纹 + enumerateDevices: [`id=${_uuid};gid=groupId1;kind=videoinput;label=Camera1`, `id=${_uuid};gid=groupId2;kind=audioinput;label=Microphone1`] + // 枚举设备指纹,Unreliable on Windows, see https://github.com/fingerprintjs/fingerprintjs/issues/375 + }; + } } export default new BiliApi(); diff --git a/models/bilibili/bilibili.main.get.web.data.ts b/models/bilibili/bilibili.main.get.web.data.ts index 0518ad4..a9bc84c 100644 --- a/models/bilibili/bilibili.main.get.web.data.ts +++ b/models/bilibili/bilibili.main.get.web.data.ts @@ -67,7 +67,6 @@ export class BiliGetWebData { const { w_rid, time_stamp } = await getWbiSign(data, BiliApi.BILIBILI_HEADERS, signCookie); const params = { ...data, - w_webid: w_webid, w_rid: w_rid, wts: time_stamp }; diff --git a/models/bilibili/bilibili.mian.models.ts b/models/bilibili/bilibili.mian.models.ts index 88aa4ae..8438386 100644 --- a/models/bilibili/bilibili.mian.models.ts +++ b/models/bilibili/bilibili.mian.models.ts @@ -75,8 +75,8 @@ export async function applyLoginQRCode(e: EventType) { return qrcodeKey; } else { - e.reply(`获取B站登录二维码失败: ${res.data?.message}`); - throw new Error(`获取B站登录二维码失败: ${res.data?.message}`); + e.reply(`获取B站登录二维码失败: ${JSON.stringify(res.data)}`); + throw new Error(`获取B站登录二维码失败: ${JSON.stringify(res.data)}`); } } @@ -110,8 +110,16 @@ export async function pollLoginQRCode(e: EventType, qrcodeKey: string) { if (data.data.code === 0) { // 登录成功,获取 cookie const LoginCookie = response.headers.get('set-cookie'); + let loginCk: string = ''; + try { + const nomalCk = await getNewTempCk(); + loginCk = `${nomalCk}${LoginCookie}`; + } catch (error) { + loginCk = LoginCookie; + logger.debug(`优纪插件: 获取B站登录ck缺失部分: ${error}`); + } e.reply(`~B站登陆成功~`); - return LoginCookie; + return loginCk; } else if (data.data.code === 86101) { // 未扫码 // 继续轮询 @@ -146,7 +154,7 @@ export async function checkBiliLogin(e: EventType) { redirect: 'follow' }); const resData: any = await res.json(); - Bot.logger?.debug(`B站验证登陆状态:${JSON.stringify(resData)}`); + Bot.logger?.debug(`B站验证登录状态:${JSON.stringify(resData)}`); if (resData.code === 0) { let uname = resData.data?.uname; @@ -290,7 +298,23 @@ export async function saveLocalBiliCk(data: any) { export async function readTempCk() { const CK_KEY = 'Yz:yuki:bili:tempCookie'; const tempCk = await Redis.get(CK_KEY); - return tempCk ?? ''; + if (!tempCk) { + const newTempCk = await getNewTempCk(); + await saveTempCk(newTempCk); + + const result = await postGateway(newTempCk); + + const data = await result.data; // 解析校验结果 + + if (data?.code !== 0) { + logger?.error(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`); + } else if (data?.code === 0) { + logger?.mark(`优纪插件:tempCK,Gateway校验成功:${JSON.stringify(data)}`); + } + return newTempCk; + } else { + return tempCk; + } } /**保存tempCK*/ @@ -430,23 +454,12 @@ async function getBuvid3_4(uuid: string): Promise { /**获取新的tempCK*/ export async function getNewTempCk() { const uuid = await genUUID(); + const b_nut = `b_nut=${Date.now() / 1000};`; const buvid3_buvid4 = await getBuvid3_4(uuid); const b_lsid = await gen_b_lsid(); - //const buvid_fp = await get_buvid_fp(uuid); - - let newTempCk = `${uuid}${buvid3_buvid4}${b_lsid}`; //${buvid_fp}`; + const buvid_fp = await get_buvid_fp(uuid); - await saveTempCk(newTempCk); - - const result = await postGateway(newTempCk); - - const data = await result.data; // 解析校验结果 - - if (data?.code !== 0) { - logger?.error(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`); - } else if (data?.code === 0) { - logger?.mark(`优纪插件:tempCK,Gateway校验成功:${JSON.stringify(data)}`); - } + return `${uuid}${buvid3_buvid4}${b_lsid}${buvid_fp}${b_nut}`; } /** @@ -454,205 +467,6 @@ export async function getNewTempCk() { * 风控相关函数 * ******************************************************************* */ -/**获取GatWay payload */ -async function getPayload(cookie: string) { - const payloadOriginData = { - '3064': 1, // ptype, mobile => 2, others => 1 - '5062': `${Date.now()}`, // timestamp - '03bf': 'https://www.bilibili.com/', // url accessed - '39c8': '333.999.fp.risk', - '34f1': '', // target_url, default empty now - 'd402': '', // screenx, default empty - '654a': '', // screeny, default empty - '6e7c': '878x1066', // browser_resolution, window.innerWidth || document.body && document.body.clientWidth + "x" + window.innerHeight || document.body && document.body.clientHeight - '3c43': { - // 3c43 => msg - '2673': 0, // hasLiedResolution, window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight - '5766': 24, // colorDepth, window.screen.colorDepth - '6527': 0, // addBehavior, !!window.HTMLElement.prototype.addBehavior, html5 api - '7003': 1, // indexedDb, !!window.indexedDB, html5 api - '807e': 1, // cookieEnabled, navigator.cookieEnabled - 'b8ce': BiliApi.BILIBILI_HEADERS['User-Agent'], // ua "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0", - '641c': 0, // webdriver, navigator.webdriver, like Selenium - '07a4': 'zh-CN', // language - '1c57': 'not available', // deviceMemory in GB, navigator.deviceMemory - '0bd0': 16, // hardwareConcurrency, navigator.hardwareConcurrency - '748e': [1920, 1200], // window.screen.width window.screen.height - 'd61f': [1920, 1152], // window.screen.availWidth window.screen.availHeight - 'fc9d': -480, // timezoneOffset, (new Date).getTimezoneOffset() - '6aa9': 'Asia/Shanghai', //Intl.DateTimeFormat().resolvedOptions().timeZone, // timezone, (new window.Intl.DateTimeFormat).resolvedOptions().timeZone - '75b8': 1, // sessionStorage, window.sessionStorage, html5 api - '3b21': 1, // localStorage, window.localStorage, html5 api - '8a1c': 0, // openDatabase, window.openDatabase, html5 api - 'd52f': 'not available', // cpuClass, navigator.cpuClass - 'adca': BiliApi.BILIBILI_HEADERS['User-Agent'].includes('Windows') ? 'Win32' : 'Linux', // platform, navigator.platform - '80c9': [ - [ - 'PDF Viewer', - 'Portable Document Format', - [ - ['application/pdf', 'pdf'], - ['text/pdf', 'pdf'] - ] - ], - [ - 'Chrome PDF Viewer', - 'Portable Document Format', - [ - ['application/pdf', 'pdf'], - ['text/pdf', 'pdf'] - ] - ], - [ - 'Chromium PDF Viewer', - 'Portable Document Format', - [ - ['application/pdf', 'pdf'], - ['text/pdf', 'pdf'] - ] - ], - [ - 'Microsoft Edge PDF Viewer', - 'Portable Document Format', - [ - ['application/pdf', 'pdf'], - ['text/pdf', 'pdf'] - ] - ], - [ - 'WebKit built-in PDF', - 'Portable Document Format', - [ - ['application/pdf', 'pdf'], - ['text/pdf', 'pdf'] - ] - ] - ], // plugins - '13ab': 'f3YAAAAASUVORK5CYII=', // canvas fingerprint - 'bfe9': 'kABYpRAGAVYzWJooB9Bf4P+UortSvxRY0AAAAASUVORK5CYII=', // webgl_str - 'a3c1': [ - 'extensions:ANGLE_instanced_arrays;EXT_blend_minmax;EXT_color_buffer_half_float;EXT_float_blend;EXT_frag_depth;EXT_shader_texture_lod;EXT_sRGB;EXT_texture_compression_bptc;EXT_texture_compression_rgtc;EXT_texture_filter_anisotropic;OES_element_index_uint;OES_fbo_render_mipmap;OES_standard_derivatives;OES_texture_float;OES_texture_float_linear;OES_texture_half_float;OES_texture_half_float_linear;OES_vertex_array_object;WEBGL_color_buffer_float;WEBGL_compressed_texture_s3tc;WEBGL_compressed_texture_s3tc_srgb;WEBGL_debug_renderer_info;WEBGL_debug_shaders;WEBGL_depth_texture;WEBGL_draw_buffers;WEBGL_lose_context;WEBGL_provoking_vertex', - 'webgl aliased line width range:[1, 1]', - 'webgl aliased point size range:[1, 1024]', - 'webgl alpha bits:8', - 'webgl antialiasing:yes', - 'webgl blue bits:8', - 'webgl depth bits:24', - 'webgl green bits:8', - 'webgl max anisotropy:16', - 'webgl max combined texture image units:32', - 'webgl max cube map texture size:16384', - 'webgl max fragment uniform vectors:1024', - 'webgl max render buffer size:16384', - 'webgl max texture image units:16', - 'webgl max texture size:16384', - 'webgl max varying vectors:30', - 'webgl max vertex attribs:16', - 'webgl max vertex texture image units:16', - 'webgl max vertex uniform vectors:4096', - 'webgl max viewport dims:[32767, 32767]', - 'webgl red bits:8', - 'webgl renderer:ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', - 'webgl shading language version:WebGL GLSL ES 1.0', - 'webgl stencil bits:0', - 'webgl vendor:Mozilla', - 'webgl version:WebGL 1.0', - 'webgl unmasked vendor:Google Inc. (Intel)', - 'webgl unmasked renderer:ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', - 'webgl vertex shader high float precision:23', - 'webgl vertex shader high float precision rangeMin:127', - 'webgl vertex shader high float precision rangeMax:127', - 'webgl vertex shader medium float precision:23', - 'webgl vertex shader medium float precision rangeMin:127', - 'webgl vertex shader medium float precision rangeMax:127', - 'webgl vertex shader low float precision:23', - 'webgl vertex shader low float precision rangeMin:127', - 'webgl vertex shader low float precision rangeMax:127', - 'webgl fragment shader high float precision:23', - 'webgl fragment shader high float precision rangeMin:127', - 'webgl fragment shader high float precision rangeMax:127', - 'webgl fragment shader medium float precision:23', - 'webgl fragment shader medium float precision rangeMin:127', - 'webgl fragment shader medium float precision rangeMax:127', - 'webgl fragment shader low float precision:23', - 'webgl fragment shader low float precision rangeMin:127', - 'webgl fragment shader low float precision rangeMax:127', - 'webgl vertex shader high int precision:0', - 'webgl vertex shader high int precision rangeMin:31', - 'webgl vertex shader high int precision rangeMax:30', - 'webgl vertex shader medium int precision:0', - 'webgl vertex shader medium int precision rangeMin:31', - 'webgl vertex shader medium int precision rangeMax:30', - 'webgl vertex shader low int precision:0', - 'webgl vertex shader low int precision rangeMin:31', - 'webgl vertex shader low int precision rangeMax:30', - 'webgl fragment shader high int precision:0', - 'webgl fragment shader high int precision rangeMin:31', - 'webgl fragment shader high int precision rangeMax:30', - 'webgl fragment shader medium int precision:0', - 'webgl fragment shader medium int precision rangeMin:31', - 'webgl fragment shader medium int precision rangeMax:30', - 'webgl fragment shader low int precision:0', - 'webgl fragment shader low int precision rangeMin:31', - 'webgl fragment shader low int precision rangeMax:30' - ], // webgl_params, cab be set to [] if webgl is not supported - '6bc5': 'Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar', // webglVendorAndRenderer - 'ed31': 0, - '72bd': 0, - '097b': 0, - '52cd': [0, 0, 0], - 'a658': [ - 'Arial', - 'Arial Black', - 'Calibri', - 'Cambria', - 'Cambria Math', - 'Comic Sans MS', - 'Consolas', - 'Courier', - 'Courier New', - 'Georgia', - 'Helvetica', - 'Impact', - 'Lucida Console', - 'Lucida Sans Unicode', - 'Microsoft Sans Serif', - 'MS Gothic', - 'MS PGothic', - 'MS Sans Serif', - 'MS Serif', - 'Palatino Linotype', - 'Segoe Print', - 'Segoe Script', - 'Segoe UI', - 'Segoe UI Light', - 'Segoe UI Symbol', - 'Tahoma', - 'Times', - 'Times New Roman', - 'Trebuchet MS', - 'Verdana', - 'Wingdings' - ], - 'd02f': '35.749972093850374' - }, - '54ef': { - 'in_new_ab ': true, - 'ab_version ': { - 'waterfall_article ': 'SHOW ' - }, - 'ab_split_num ': { - 'waterfall_article ': 0 - } - }, - '8b94': '', - 'df35': `${await readSavedCookieItems(cookie, ['_uuid'], false)}`, // _uuid, set from cookie, generated by client side(algorithm remains unknown) - '07a4': 'zh-CN', - '5f45': null, - 'db46': 0 - }; - return JSON.stringify(payloadOriginData); -} /** * 请求参数POST接口(ExClimbWuzhi)过校验 @@ -660,7 +474,9 @@ async function getPayload(cookie: string) { * @returns 返回POST请求的结果 */ export async function postGateway(cookie: string) { - const data = { payload: await getPayload(cookie) }; + const _uuid = await readSavedCookieItems(cookie, ['_uuid'], false); + const payloadJsonData = await BiliApi.BILIBILI_BROWSER_DATA(_uuid); + const data = { payload: JSON.stringify(payloadJsonData) }; const requestUrl = 'https://api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi'; const config = { @@ -693,8 +509,8 @@ export async function postGateway(cookie: string) { */ export async function get_buvid_fp(cookie: string) { const uuid = await readSavedCookieItems(cookie, ['_uuid'], false); - const seedget = Math.floor(Math.random() * (60 - 1 + 1) + 1); - let buvidFp = gen_buvid_fp(uuid, seedget); + const fingerprintData = BiliApi.BILIBILI_FINGERPRINT_DATA(uuid); + const buvidFp = gen_buvid_fp(fingerprintData); return `buvid_fp=${buvidFp};`; } diff --git a/models/bilibili/bilibili.risk.buid.fp.ts b/models/bilibili/bilibili.risk.buid.fp.ts index 7ab2a95..af3116f 100644 --- a/models/bilibili/bilibili.risk.buid.fp.ts +++ b/models/bilibili/bilibili.risk.buid.fp.ts @@ -1,82 +1,297 @@ -class Murmur3 { - static MOD = 1n << 64n; - static C1 = 0x87c37b91114253d5n; - static C2 = 0x4cf5ad432745937fn; - static C3 = 0x52dce729n; - static C4 = 0x38495ab5n; - static R1 = 27n; - static R2 = 31n; - static R3 = 33n; - static M = 5n; - - static hash(source, seed) { - let h1 = BigInt(seed); - let h2 = BigInt(seed); - let processed = 0; - - for (let i = 0; i < source.length; i += 16) { - const chunk = source.slice(i, i + 16); - processed += chunk.length; - if (chunk.length === 16) { - const k1 = BigInt(chunk.slice(0, 8).reduce((acc, val, idx) => acc | (BigInt(val) << BigInt(8 * idx)), 0n)); - const k2 = BigInt(chunk.slice(8).reduce((acc, val, idx) => acc | (BigInt(val) << BigInt(8 * idx)), 0n)); - h1 ^= (Murmur3.rotateLeft((k1 * Murmur3.C1) % Murmur3.MOD, Murmur3.R2) * Murmur3.C2) % Murmur3.MOD; - h1 = ((Murmur3.rotateLeft(h1, Murmur3.R1) + h2) * Murmur3.M + Murmur3.C3) % Murmur3.MOD; - h2 ^= (Murmur3.rotateLeft((k2 * Murmur3.C2) % Murmur3.MOD, Murmur3.R3) * Murmur3.C1) % Murmur3.MOD; - h2 = ((Murmur3.rotateLeft(h2, Murmur3.R2) + h1) * Murmur3.M + Murmur3.C4) % Murmur3.MOD; - } else { - let k1 = 0n; - let k2 = 0n; - for (let j = 0; j < chunk.length; j++) { - const byteVal = BigInt(chunk[j]); - if (j < 8) { - k1 |= byteVal << BigInt(8 * j); - } else { - k2 |= byteVal << BigInt(8 * (j - 8)); - } - } - k1 = (Murmur3.rotateLeft((k1 * Murmur3.C1) % Murmur3.MOD, Murmur3.R2) * Murmur3.C2) % Murmur3.MOD; - h1 ^= k1; - h2 ^= (Murmur3.rotateLeft((k2 * Murmur3.C2) % Murmur3.MOD, Murmur3.R3) * Murmur3.C1) % Murmur3.MOD; - } +/* + * Contains code from open-source projects: + * MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js) + */ +class MurmurHash3 { + // x64Add 函数:将两个64位整数相加 + // + // Given two 64bit ints (as an array of two 32bit ints) returns the two + // added together as a 64bit int (as an array of two 32bit ints). + // + static x64Add = function (m, n) { + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; + var o = [0, 0, 0, 0]; + o[3] += m[3] + n[3]; + o[2] += o[3] >>> 16; + o[3] &= 0xffff; + o[2] += m[2] + n[2]; + o[1] += o[2] >>> 16; + o[2] &= 0xffff; + o[1] += m[1] + n[1]; + o[0] += o[1] >>> 16; + o[1] &= 0xffff; + o[0] += m[0] + n[0]; + o[0] &= 0xffff; + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; + }; + + // x64Multiply 函数:将两个64位整数相乘 + // + // Given two 64bit ints (as an array of two 32bit ints) returns the two + // multiplied together as a 64bit int (as an array of two 32bit ints). + // + static x64Multiply = function (m, n) { + m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; + n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; + var o = [0, 0, 0, 0]; + o[3] += m[3] * n[3]; + o[2] += o[3] >>> 16; + o[3] &= 0xffff; + o[2] += m[2] * n[3]; + o[1] += o[2] >>> 16; + o[2] &= 0xffff; + o[2] += m[3] * n[2]; + o[1] += o[2] >>> 16; + o[2] &= 0xffff; + o[1] += m[1] * n[3]; + o[0] += o[1] >>> 16; + o[1] &= 0xffff; + o[1] += m[2] * n[2]; + o[0] += o[1] >>> 16; + o[1] &= 0xffff; + o[1] += m[3] * n[1]; + o[0] += o[1] >>> 16; + o[1] &= 0xffff; + o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0]; + o[0] &= 0xffff; + return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; + }; + + // x64Rotl 函数:将给定64位整数左移 + // + // Given a 64bit int (as an array of two 32bit ints) and an int + // representing a number of bit positions, returns the 64bit int (as an + // array of two 32bit ints) rotated left by that number of positions. + // + static x64Rotl = function (m, n) { + n %= 64; + if (n === 32) { + return [m[1], m[0]]; + } else if (n < 32) { + return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; + } else { + n -= 32; + return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; + } + }; + + // x64LeftShift 函数:将64位整数左移 + // + // Given a 64bit int (as an array of two 32bit ints) and an int + // representing a number of bit positions, returns the 64bit int (as an + // array of two 32bit ints) shifted left by that number of positions. + // + static x64LeftShift = function (m, n) { + n %= 64; + if (n === 0) { + return m; + } else if (n < 32) { + return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; + } else { + return [m[1] << (n - 32), 0]; + } + }; + + // x64Xor 函数:返回两个64位整数的异或结果 + // + // Given two 64bit ints (as an array of two 32bit ints) returns the two + // xored together as a 64bit int (as an array of two 32bit ints). + // + static x64Xor = function (m, n) { + return [m[0] ^ n[0], m[1] ^ n[1]]; + }; + + // x64Fmix 函数:MurmurHash3的最终混合步骤 + // + // Given a block, returns murmurHash3's final x64 mix of that block. + // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the + // only place where we need to right shift 64bit ints.) + // + static x64Fmix = function (h) { + h = MurmurHash3.x64Xor(h, [0, h[0] >>> 1]); + h = MurmurHash3.x64Multiply(h, [0xff51afd7, 0xed558ccd]); + h = MurmurHash3.x64Xor(h, [0, h[0] >>> 1]); + h = MurmurHash3.x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); + h = MurmurHash3.x64Xor(h, [0, h[0] >>> 1]); + return h; + }; + + // x64hash128 函数:生成128位哈希 + // + // Given a string and an optional seed as an int, returns a 128 bit + // hash using the x64 flavor of MurmurHash3, as an unsigned hex. + // + static x64hash128 = function (key, seed) { + key = key || ''; + seed = seed || 0; + var remainder = key.length % 16; + var bytes = key.length - remainder; + var h1 = [0, seed]; + var h2 = [0, seed]; + var k1 = [0, 0]; + var k2 = [0, 0]; + var c1 = [0x87c37b91, 0x114253d5]; + var c2 = [0x4cf5ad43, 0x2745937f]; + + for (var i = 0; i < bytes; i += 16) { + k1 = [ + (key.charCodeAt(i + 4) & 0xff) | + ((key.charCodeAt(i + 5) & 0xff) << 8) | + ((key.charCodeAt(i + 6) & 0xff) << 16) | + ((key.charCodeAt(i + 7) & 0xff) << 24), + (key.charCodeAt(i) & 0xff) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24) + ]; + + k2 = [ + (key.charCodeAt(i + 12) & 0xff) | + ((key.charCodeAt(i + 13) & 0xff) << 8) | + ((key.charCodeAt(i + 14) & 0xff) << 16) | + ((key.charCodeAt(i + 15) & 0xff) << 24), + (key.charCodeAt(i + 8) & 0xff) | + ((key.charCodeAt(i + 9) & 0xff) << 8) | + ((key.charCodeAt(i + 10) & 0xff) << 16) | + ((key.charCodeAt(i + 11) & 0xff) << 24) + ]; + + // 处理 k1 和 k2 + k1 = MurmurHash3.x64Multiply(k1, c1); + k1 = MurmurHash3.x64Rotl(k1, 31); + k1 = MurmurHash3.x64Multiply(k1, c2); + h1 = MurmurHash3.x64Xor(h1, k1); + h1 = MurmurHash3.x64Rotl(h1, 27); + h1 = MurmurHash3.x64Add(h1, h2); + h1 = MurmurHash3.x64Add(MurmurHash3.x64Multiply(h1, [0, 5]), [0, 0x52dce729]); + + k2 = MurmurHash3.x64Multiply(k2, c2); + k2 = MurmurHash3.x64Rotl(k2, 33); + k2 = MurmurHash3.x64Multiply(k2, c1); + h2 = MurmurHash3.x64Xor(h2, k2); + h2 = MurmurHash3.x64Rotl(h2, 31); + h2 = MurmurHash3.x64Add(h2, h1); + h2 = MurmurHash3.x64Add(MurmurHash3.x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); } - h1 ^= BigInt(processed); - h2 ^= BigInt(processed); - h1 = (h1 + h2) % Murmur3.MOD; - h2 = (h2 + h1) % Murmur3.MOD; - h1 = Murmur3.fmix64(h1); - h2 = Murmur3.fmix64(h2); - h1 = (h1 + h2) % Murmur3.MOD; - h2 = (h2 + h1) % Murmur3.MOD; - - return (h2 << BigInt(64)) | h1; - } - - static rotateLeft(x, k) { - const index = Number(k); - const binStr = x.toString(2).padStart(64, '0'); - return BigInt(`0b${binStr.slice(index)}${binStr.slice(0, index)}`); - } - - static fmix64(k) { - const C1 = 0xff51afd7ed558ccdn; - const C2 = 0xc4ceb9fe1a85ec53n; - const R = 33; - let tmp = k; - tmp ^= tmp >> BigInt(R); - tmp = (tmp * C1) % Murmur3.MOD; - tmp ^= tmp >> BigInt(R); - tmp = (tmp * C2) % Murmur3.MOD; - tmp ^= tmp >> BigInt(R); - return tmp; - } + k1 = [0, 0]; + k2 = [0, 0]; + + switch (remainder) { + case 15: + k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, String(key).charCodeAt(i + 14)], 48)); + // fallthrough + case 14: + k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 13)], 40)); + // fallthrough + case 13: + k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 12)], 32)); + // fallthrough + case 12: + k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 11)], 24)); + // fallthrough + case 11: + k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 10)], 16)); + // fallthrough + case 10: + k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 9)], 8)); + // fallthrough + case 9: + k2 = MurmurHash3.x64Xor(k2, [0, key.charCodeAt(i + 8)]); + k2 = MurmurHash3.x64Multiply(k2, c2); + k2 = MurmurHash3.x64Rotl(k2, 33); + k2 = MurmurHash3.x64Multiply(k2, c1); + h2 = MurmurHash3.x64Xor(h2, k2); + // fallthrough + case 8: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 7)], 56)); + // fallthrough + case 7: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 6)], 48)); + // fallthrough + case 6: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 5)], 40)); + // fallthrough + case 5: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 4)], 32)); + // fallthrough + case 4: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 3)], 24)); + // fallthrough + case 3: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 2)], 16)); + // fallthrough + case 2: + k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 1)], 8)); + // fallthrough + case 1: + k1 = MurmurHash3.x64Xor(k1, [0, key.charCodeAt(i)]); + k1 = MurmurHash3.x64Multiply(k1, c1); + k1 = MurmurHash3.x64Rotl(k1, 31); + k1 = MurmurHash3.x64Multiply(k1, c2); + h1 = MurmurHash3.x64Xor(h1, k1); + // fallthrough + } + + h1 = MurmurHash3.x64Xor(h1, [0, key.length]); + h2 = MurmurHash3.x64Xor(h2, [0, key.length]); + h1 = MurmurHash3.x64Add(h1, h2); + h2 = MurmurHash3.x64Add(h2, h1); + h1 = MurmurHash3.x64Fmix(h1); + h2 = MurmurHash3.x64Fmix(h2); + h1 = MurmurHash3.x64Add(h1, h2); + h2 = MurmurHash3.x64Add(h2, h1); + + return ( + ('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) + + ('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) + + ('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) + + ('00000000' + (h2[1] >>> 0).toString(16)).slice(-8) + ); + }; } -function gen_buvid_fp(uuid: any, seed: any) { - const source = new TextEncoder().encode(uuid); - const m = Murmur3.hash(source, seed); - return `${(m & (Murmur3.MOD - 1n)).toString(16)}${(m >> 64n).toString(16)}`; +function gen_buvid_fp(browserData: any) { + // 将所有自定义数据整合 + const components = [ + { key: 'userAgent', value: browserData.userAgent }, + { key: 'webdriver', value: browserData.webdriver }, // WebDriver 信息 + { key: 'language', value: browserData.language }, + { key: 'colorDepth', value: browserData.colorDepth }, + { key: 'deviceMemory', value: browserData.deviceMemory }, + { key: 'pixelRatio', value: browserData.pixelRatio }, // 设备像素比 + { key: 'hardwareConcurrency', value: browserData.hardwareConcurrency }, + { key: 'screenResolution', value: browserData.screenResolution }, // 屏幕分辨率 + { key: 'availableScreenResolution', value: browserData.availableScreenResolution }, // 可用屏幕分辨率 + { key: 'timezoneOffset', value: browserData.timezoneOffset }, // 时区偏移 + { key: 'timezone', value: browserData.timezone }, + { key: 'sessionStorage', value: browserData.sessionStorage ? 1 : 0 }, // sessionStorage 支持 + { key: 'localStorage', value: browserData.localStorage ? 1 : 0 }, // localStorage 支持 + { key: 'indexedDb', value: browserData.indexedDb ? 1 : 0 }, // IndexedDB 支持 + { key: 'addBehavior', value: browserData.addBehavior ? 1 : 0 }, // addBehavior 支持 + { key: 'openDatabase', value: browserData.openDatabase ? 1 : 0 }, // openDatabase 支持 + { key: 'cpuClass', value: browserData.cpuClass }, + { key: 'platform', value: browserData.platform }, + { key: 'doNotTrack', value: browserData.doNotTrack }, + { key: 'plugins', value: browserData.plugins.map(p => p.name).join(',') }, // 插件名称 + { key: 'canvas', value: browserData.canvas }, + { key: 'webgl', value: browserData.webgl }, + { key: 'webglVendorAndRenderer', value: browserData.webglVendorAndRenderer }, + { key: 'adBlock', value: browserData.adBlock ? 1 : 0 }, // 是否存在广告拦截器 + { key: 'hasLiedLanguages', value: browserData.hasLiedLanguages ? 1 : 0 }, + { key: 'hasLiedResolution', value: browserData.hasLiedResolution ? 1 : 0 }, + { key: 'hasLiedOs', value: browserData.hasLiedOs ? 1 : 0 }, + { key: 'hasLiedBrowser', value: browserData.hasLiedBrowser ? 1 : 0 }, + { key: 'touchSupport', value: browserData.touchSupport }, // 支持的触摸点数 + { key: 'fonts', value: browserData.fonts.map(f => f.replace(/\s+/g, '')).join(',') }, // 字体列表 + { key: 'fontsFlash', value: browserData.hasLiedOs ? browserData.fonts.map(f => f.replace(/\s+/g, '')).join(',') : 'flash not installed' }, + { key: 'audio', value: browserData.audio }, // 音频指纹 + { key: 'enumerateDevices', value: browserData.enumerateDevices.map(f => f.replace(/\s+/g, '')).join(',') } + ]; + const values = components.map(component => component.value).join('~~~'); + + // 使用 MurmurHash3 计算指纹 + const fingerprint = MurmurHash3.x64hash128(values, 31); // 调用之前定义的 x64hash128 函数 + + return fingerprint; } export { gen_buvid_fp }; diff --git a/models/bilibili/bilibili.risk.wbi.ts b/models/bilibili/bilibili.risk.wbi.ts index fff0fd5..3466edd 100644 --- a/models/bilibili/bilibili.risk.wbi.ts +++ b/models/bilibili/bilibili.risk.wbi.ts @@ -1,5 +1,7 @@ import md5 from 'md5'; import fetch from 'node-fetch'; +import { Redis } from 'yunzaijs'; +import BiliApi from '@/models/bilibili/bilibili.main.api'; const mixinKeyEncTab = [ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, @@ -41,27 +43,79 @@ function encWbi(params: { [key: string]: string | number | object }, img_key: st } // 获取最新的 img_key 和 sub_key async function getWbiKeys(headers, cookie) { - const res = await fetch('https://api.bilibili.com/x/web-interface/nav', { + const IMG_SUB_KEY = 'Yz:yuki:bili:wbi_img_key'; + const wbi_img_data: string = await Redis.get(IMG_SUB_KEY); + if (wbi_img_data) { + const wbi_img_data_json = JSON.parse(wbi_img_data); + return { + img_key: wbi_img_data_json.img_key, + sub_key: wbi_img_data_json.sub_key + }; + } else { + const res = await fetch('https://api.bilibili.com/x/web-interface/nav', { + headers: { + // SESSDATA 字段 + 'Cookie': cookie, + 'User-Agent': headers['User-Agent'], + 'Referer': 'https://www.bilibili.com/' //对于直接浏览器调用可能不适用 + } + }); + const { + data: { + wbi_img: { img_url, sub_url } + } + } = (await res.json()) as { + data: { + wbi_img: { img_url: string; sub_url: string }; + }; + }; + const wbi_img_data = { + img_key: img_url.slice(img_url.lastIndexOf('/') + 1, img_url.lastIndexOf('.')), + sub_key: sub_url.slice(sub_url.lastIndexOf('/') + 1, sub_url.lastIndexOf('.')) + }; + + const { microtime } = await getTimeStamp(); // 获取的 microtime 已经是北京时间(毫秒) + + // 创建当前北京时间的 Date 对象 + const current_zh_cn_Time = new Date(microtime); + + // 打印当前北京时间 + console.log(`当前北京时间: ${current_zh_cn_Time}`); + + // 创建明天0点的时间对象 + const tomorrow = new Date(current_zh_cn_Time); + tomorrow.setHours(0, 0, 0, 0); // 设置明天的0点 + tomorrow.setDate(tomorrow.getDate() + 1); // 日期加1,表示明天 + + // 计算剩余秒数 + const secondsUntilTomorrow = Math.floor((tomorrow.getTime() - current_zh_cn_Time.getTime()) / 1000); + + await Redis.set(IMG_SUB_KEY, JSON.stringify(wbi_img_data), { EX: secondsUntilTomorrow - 2 }); // 设置缓存,过期时间为第二天0点前2秒 + + return wbi_img_data; + } +} + +/**获取适用于 RTC 的时间戳*/ +async function getTimeStamp() { + const res = await fetch(BiliApi.BILIBIL_API.biliServerTimeStamp, { + method: 'GET', headers: { - // SESSDATA 字段 - 'Cookie': cookie, - 'User-Agent': headers['User-Agent'], - 'Referer': 'https://www.bilibili.com/' //对于直接浏览器调用可能不适用 + 'Host': 'api.live.bilibili.com', + 'User-Agent': `${BiliApi.USER_AGENT}` } }); const { - data: { - wbi_img: { img_url, sub_url } - } + data: { timestamp, microtime } } = (await res.json()) as { data: { - wbi_img: { img_url: string; sub_url: string }; + timestamp: number; + microtime: number; }; }; - return { - img_key: img_url.slice(img_url.lastIndexOf('/') + 1, img_url.lastIndexOf('.')), - sub_key: sub_url.slice(sub_url.lastIndexOf('/') + 1, sub_url.lastIndexOf('.')) + timestamp: timestamp, + microtime: microtime }; } diff --git a/package.json b/package.json index 35f225c..9c6cd92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yuki-plugin", - "version": "2.0.5-12", + "version": "2.0.5-13", "author": "snowtafir", "description": "优纪插件,yunzai-V4 关于 微博推送、B站推送 等功能的拓展插件", "main": "./index",