diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f92250..cfad24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # 2.0.4 +* 优化B站风控相关,新增bili_tiket参数 * fix Repeated Instantiation puppeteer * 优化获取B站登录ck * 添加截图列队,优化配置文件注释 diff --git a/models/bilibili/bilibili.get.web.data.ts b/models/bilibili/bilibili.get.web.data.ts index 628af0f..f505377 100644 --- a/models/bilibili/bilibili.get.web.data.ts +++ b/models/bilibili/bilibili.get.web.data.ts @@ -1,7 +1,7 @@ import axios from "axios"; import lodash from "lodash"; import { BiliApi } from '@/models/bilibili/bilibili.api'; -import { readSavedCookieItems, readSavedCookieOtherItems, readSyncCookie } from '@/models/bilibili/bilibili.models'; +import { cookieWithBiliTicket, readSavedCookieItems, readSavedCookieOtherItems, readSyncCookie } from '@/models/bilibili/bilibili.models'; import { getWbiSign } from '@/models/bilibili/bilibili.wbi'; export class BiliGetWebData { @@ -12,6 +12,7 @@ export class BiliGetWebData { async getBiliDynamicListDataByUid(uid: any) { const url = BiliApi.BILIBIL_API.biliDynamicInfoList; let { cookie } = await readSyncCookie(); + cookie = await cookieWithBiliTicket(cookie); const data = { offset: '', @@ -43,6 +44,7 @@ export class BiliGetWebData { async getBilibiUserInfoByUid(uid: any) { const url = BiliApi.BILIBIL_API.biliSpaceUserInfoWbi; let { cookie } = await readSyncCookie(); + cookie = await cookieWithBiliTicket(cookie); const data = { mid: uid, @@ -67,6 +69,7 @@ export class BiliGetWebData { async searchBiliUserInfoByKeyword(keyword: string) { const url = BiliApi.BILIBIL_API.biliSearchUpWbi; let { cookie } = await readSyncCookie(); + cookie = await cookieWithBiliTicket(cookie); const data = { keyword: keyword, diff --git a/models/bilibili/bilibili.models.ts b/models/bilibili/bilibili.models.ts index 047d17a..a041e81 100644 --- a/models/bilibili/bilibili.models.ts +++ b/models/bilibili/bilibili.models.ts @@ -12,6 +12,7 @@ import { _paths } from '@/utils/paths'; import { ScreenshotOptions } from '@/utils/puppeteer.render'; import { BiliApi } from '@/models/bilibili/bilibili.api'; import { gen_buvid_fp } from '@/models/bilibili/bilibili.buid.fp'; +import { getBiliTicket } from '@/models/bilibili/bilibili.ticket'; declare const logger: any, Bot: any, redis: any, segment: any; @@ -426,11 +427,11 @@ export async function getNewTempCk() { const result = await postGateway(newTempCk); - const { code, data } = await result.data; // 解析校验结果 + const data = await result.data; // 解析校验结果 - if (code !== 0) { - logger?.mark(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`); - } else if (code === 0) { + if (data?.code !== 0) { + logger?.error(`优纪插件:tempCK,Gateway校验失败:${JSON.stringify(data)}`); + } else if (data?.code === 0) { logger?.mark(`优纪插件:tempCK,Gateway校验成功:${JSON.stringify(data)}`); } } @@ -593,21 +594,22 @@ async function getPayload(cookie: string) { * @returns 返回POST请求的结果 */ export async function postGateway(cookie: string) { - const payload = getPayload(cookie); + const data = { payload: await getPayload(cookie) }; const requestUrl = 'https://api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi'; - const headers = lodash.merge({}, BiliApi.BILIBILI_HEADERS, { - 'Cookie': cookie, - 'Content-type': 'Application/json', - 'Charset': 'UTF-8', - }, { - 'Host': 'api.bilibili.com', - 'Origin': 'https://www.bilibili.com', - 'Referer': 'https://www.bilibili.com/', - }); + const config = { + headers: lodash.merge({}, BiliApi.BILIBILI_HEADERS, { + 'Cookie': cookie, + 'Content-type': 'application/json;charset=UTF-8', + }, { + 'Host': 'api.bilibili.com', + 'Origin': 'https://www.bilibili.com', + 'Referer': 'https://www.bilibili.com/', + }) + } try { - const res = await axios.post(requestUrl, { payload }, { headers }); + const res = await axios.post(requestUrl, data, config); return res; } catch (error) { logger.error('Error making POST request:', error); @@ -624,3 +626,27 @@ export async function get_buvid_fp(cookie: string) { let buvidFp = gen_buvid_fp(uuid, seedget); return `buvid_fp=${buvidFp};`; } + +/** + * 获取有效bili_ticket并添加到cookie + * @param {string} cookie + * @returns {Promise<{ cookie: string; }>} 返回包含最新有效的bili_ticket的cookie + */ +export async function cookieWithBiliTicket(cookie: string): Promise { + const BiliJctKey = "Yz:yuki:bili:bili_ticket" + cookie = await readSavedCookieItems(cookie, ['bili_ticket'], true); + const biliTicket = await redis.get(BiliJctKey); + if (!biliTicket) { + try { + const csrf = await readSavedCookieItems(cookie, ['bili_jct'], false); + const { ticket, ttl } = await getBiliTicket(csrf); + await redis.set(BiliJctKey, ticket, { EX: ttl }); + return cookie + `;bili_ticket=${ticket};`; + } catch (error) { + logger?.error(`${error}`); + return cookie; + } + } else { + return cookie + `;bili_ticket=${biliTicket};`; + } +} \ No newline at end of file diff --git a/models/bilibili/bilibili.ticket.ts b/models/bilibili/bilibili.ticket.ts new file mode 100644 index 0000000..2e9c537 --- /dev/null +++ b/models/bilibili/bilibili.ticket.ts @@ -0,0 +1,64 @@ +import { createHmac } from 'crypto'; +import { BiliApi } from '@/models/bilibili/bilibili.api'; + +/** + * Generate HMAC-SHA256 signature + * @param {string} key The key string to use for the HMAC-SHA256 hash + * @param {string} message The message string to hash + * @returns {string} The HMAC-SHA256 signature as a hex string + */ +function hmacSha256(key: string, message: string): string { + return createHmac('sha256', key).update(message).digest('hex'); +} + +/** + * Get Bilibili web ticket + * @param {string | null} csrf CSRF token, can be empty or null, or the cookie's bili_jct value + * @returns {Promise<{ code: number, ticket: string, created_at: number, ttl: number }>} + * Promise that resolves to an object containing code, ticket, created_at, and ttl values + */ +export async function getBiliTicket(csrf: string | null): Promise<{ code?: number; ticket?: string; created_at?: number; ttl?: number }> { + const ts = Math.floor(Date.now() / 1000); + const hexSign = hmacSha256('XgwSnGZ1p', `ts${ts}`); + const url = 'https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket'; + + const params = new URLSearchParams({ + key_id: 'ec02', + hexsign: hexSign, + 'context[ts]': String(ts), + csrf: csrf ?? '' // 使用空字符串代替null + }); + + try { + const response = await fetch(`${url}?${params}`, { + method: 'POST', + headers: { + 'User-Agent': BiliApi.BILIBILI_HEADERS['User-Agent'] + } + }); + + if (!response.ok) { + throw new Error(`get bili_jct HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + if (data.code !== 0) { + if (data.code === 400) { + throw new Error(`get bili_jct Parameter error! ${data.message}`); + } + throw new Error(`Failed to retrieve bili ticket: ${data.message}`); + } + + // 返回所需的对象结构 + return { + code: data.code, + ticket: data.data?.ticket, + created_at: data.data?.created_at, + ttl: data.data?.ttl + }; + + } catch (error) { + throw new Error(`Failed to fetch Bilibili ticket: ${error instanceof Error ? error.message : String(error)}`); + } +} diff --git a/package.json b/package.json index 6b53372..5d58429 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yuki-plugin", - "version": "2.0.4-3", + "version": "2.0.4-4", "author": "snowtafir", "description": "优纪插件,yunzai-V4 关于 微博推送、B站推送 等功能的拓展插件", "main": "./index",