mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
feat: 更新BiliFunction模块以支持WBI签名和密钥管理
- 在useBiliFunction.ts中重构WBI密钥获取逻辑,添加缓存机制 - 新增encWbi函数用于生成带WBI签名的请求参数 - 在utils.ts中更新getRoomKey函数以使用新的WBI签名逻辑
This commit is contained in:
@@ -3,6 +3,7 @@ import { useBiliCookie } from '../store/useBiliCookie';
|
||||
import { QueryPostAPI } from '@/api/query';
|
||||
import { OPEN_LIVE_API_URL } from '@/data/constants';
|
||||
import { error } from '@tauri-apps/plugin-log';
|
||||
import { useBiliFunction } from '../store/useBiliFunction';
|
||||
|
||||
export async function QueryBiliAPI(url: string, method: string = 'GET', cookie: string = '', useCookie: boolean = true) {
|
||||
const u = new URL(url);
|
||||
@@ -28,8 +29,12 @@ export async function QueryBiliAPI(url: string, method: string = 'GET', cookie:
|
||||
|
||||
export async function getRoomKey(roomId: number, cookie: string) {
|
||||
try {
|
||||
const wbiKeys = await useBiliFunction().encWbi({
|
||||
id: roomId,
|
||||
type: 0
|
||||
});
|
||||
const result = await QueryBiliAPI(
|
||||
'https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=' + roomId
|
||||
'https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?' + wbiKeys
|
||||
);
|
||||
const json = await result.json();
|
||||
if (json.code === 0) return json.data.token;
|
||||
|
||||
@@ -21,63 +21,6 @@ const mixinKeyEncTab = [
|
||||
const getMixinKey = (orig: string): string =>
|
||||
mixinKeyEncTab.map(n => orig[n]).join('').slice(0, 32);
|
||||
|
||||
// 为请求参数进行 wbi 签名
|
||||
function encWbi(
|
||||
params: { [key: string]: string | number },
|
||||
img_key: string,
|
||||
sub_key: string
|
||||
): string {
|
||||
const mixin_key = getMixinKey(img_key + sub_key);
|
||||
const curr_time = Math.round(Date.now() / 1000);
|
||||
const chr_filter = /[!'()*]/g;
|
||||
|
||||
Object.assign(params, { wts: curr_time.toString() }); // 添加 wts 字段
|
||||
|
||||
// 按照 key 重排参数
|
||||
const query = Object.keys(params)
|
||||
.sort()
|
||||
.map(key => {
|
||||
// 过滤 value 中的 "!'()*" 字符
|
||||
const value = params[key].toString().replace(chr_filter, '');
|
||||
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
||||
})
|
||||
.join('&');
|
||||
|
||||
const wbi_sign = md5(query + mixin_key); // 计算 w_rid
|
||||
return query + '&w_rid=' + wbi_sign;
|
||||
}
|
||||
|
||||
// 获取最新的 img_key 和 sub_key
|
||||
async function getWbiKeys(cookie: string): Promise<{ img_key: string, sub_key: string }> {
|
||||
try {
|
||||
const response = await QueryBiliAPI('https://api.bilibili.com/x/web-interface/nav');
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("获取WBI密钥失败:", response.status);
|
||||
throw new Error("获取WBI密钥失败");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const { wbi_img } = result.data;
|
||||
|
||||
console.log(`获取WBI秘钥: img_key: ${wbi_img.img_url}, sub_key: ${wbi_img.sub_url}`);
|
||||
|
||||
return {
|
||||
img_key: wbi_img.img_url.slice(
|
||||
wbi_img.img_url.lastIndexOf('/') + 1,
|
||||
wbi_img.img_url.lastIndexOf('.')
|
||||
),
|
||||
sub_key: wbi_img.sub_url.slice(
|
||||
wbi_img.sub_url.lastIndexOf('/') + 1,
|
||||
wbi_img.sub_url.lastIndexOf('.')
|
||||
)
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("获取WBI密钥时发生错误:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export const useBiliFunction = defineStore('biliFunction', () => {
|
||||
const biliCookieStore = useBiliCookie();
|
||||
const account = useAccount();
|
||||
@@ -86,6 +29,7 @@ export const useBiliFunction = defineStore('biliFunction', () => {
|
||||
const uid = computed(() => account.value.biliId);
|
||||
// 存储WBI密钥
|
||||
const wbiKeys = ref<{ img_key: string, sub_key: string } | null>(null);
|
||||
const wbiKeysTimestamp = ref<number | null>(null);
|
||||
|
||||
// 队列相关状态
|
||||
const danmakuQueue = ref<{ roomId: number, message: string, color?: string, fontsize?: number, mode?: number }[]>([]);
|
||||
@@ -257,6 +201,77 @@ export const useBiliFunction = defineStore('biliFunction', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 为请求参数进行 wbi 签名
|
||||
async function encWbi(
|
||||
params: { [key: string]: string | number },
|
||||
): Promise<string> {
|
||||
const keys = await getWbiKeys();
|
||||
const { img_key, sub_key } = keys;
|
||||
const mixin_key = getMixinKey(img_key + sub_key);
|
||||
const curr_time = Math.round(Date.now() / 1000);
|
||||
const chr_filter = /[!'()*]/g;
|
||||
|
||||
Object.assign(params, { wts: curr_time.toString() }); // 添加 wts 字段
|
||||
|
||||
// 按照 key 重排参数
|
||||
const query = Object.keys(params)
|
||||
.sort()
|
||||
.map(key => {
|
||||
// 过滤 value 中的 "!'()*" 字符
|
||||
const value = params[key].toString().replace(chr_filter, '');
|
||||
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
||||
})
|
||||
.join('&');
|
||||
|
||||
const wbi_sign = md5(query + mixin_key); // 计算 w_rid
|
||||
return query + '&w_rid=' + wbi_sign;
|
||||
}
|
||||
|
||||
// 获取最新的 img_key 和 sub_key
|
||||
async function _fetchWbiKeys(): Promise<{ img_key: string, sub_key: string }> {
|
||||
try {
|
||||
const response = await QueryBiliAPI('https://api.bilibili.com/x/web-interface/nav');
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("获取WBI密钥失败:", response.status);
|
||||
throw new Error("获取WBI密钥失败");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const { wbi_img } = result.data;
|
||||
|
||||
console.log(`获取WBI秘钥: img_key: ${wbi_img.img_url}, sub_key: ${wbi_img.sub_url}`);
|
||||
|
||||
return {
|
||||
img_key: wbi_img.img_url.slice(
|
||||
wbi_img.img_url.lastIndexOf('/') + 1,
|
||||
wbi_img.img_url.lastIndexOf('.')
|
||||
),
|
||||
sub_key: wbi_img.sub_url.slice(
|
||||
wbi_img.sub_url.lastIndexOf('/') + 1,
|
||||
wbi_img.sub_url.lastIndexOf('.')
|
||||
)
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("获取WBI密钥时发生错误:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function getWbiKeys(): Promise<{ img_key: string, sub_key: string }> {
|
||||
const now = Date.now();
|
||||
if (wbiKeys.value && wbiKeysTimestamp.value && (now - wbiKeysTimestamp.value < 10 * 60 * 1000)) {
|
||||
console.log("使用缓存的WBI密钥");
|
||||
return wbiKeys.value;
|
||||
}
|
||||
|
||||
console.log("缓存不存在或已过期,获取新的WBI密钥");
|
||||
const newKeys = await _fetchWbiKeys();
|
||||
wbiKeys.value = newKeys;
|
||||
wbiKeysTimestamp.value = now;
|
||||
return newKeys;
|
||||
}
|
||||
|
||||
// 原始发送私信方法(重命名为_sendPrivateMessage)
|
||||
async function _sendPrivateMessage(receiverId: number, message: string): Promise<boolean> {
|
||||
if (!csrf.value || !cookie.value || !uid.value) {
|
||||
@@ -288,17 +303,6 @@ export const useBiliFunction = defineStore('biliFunction', () => {
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取WBI密钥(如果还没有)
|
||||
if (!wbiKeys.value) {
|
||||
wbiKeys.value = await getWbiKeys(cookie.value);
|
||||
}
|
||||
if (!wbiKeys.value) {
|
||||
const error = "获取WBI密钥失败,无法发送私信";
|
||||
console.error(error);
|
||||
onSendPrivateMessageFailed(receiverId, message, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const dev_id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16).toUpperCase();
|
||||
@@ -315,11 +319,7 @@ export const useBiliFunction = defineStore('biliFunction', () => {
|
||||
};
|
||||
|
||||
// 生成带WBI签名的URL查询字符串
|
||||
const signedQuery = encWbi(
|
||||
urlParams,
|
||||
wbiKeys.value.img_key,
|
||||
wbiKeys.value.sub_key
|
||||
);
|
||||
const signedQuery = await encWbi(urlParams);
|
||||
|
||||
// 构建最终URL
|
||||
const url = `https://api.vc.bilibili.com/web_im/v1/web_im/send_msg?${signedQuery}`;
|
||||
@@ -457,6 +457,7 @@ export const useBiliFunction = defineStore('biliFunction', () => {
|
||||
pmInterval,
|
||||
setDanmakuInterval,
|
||||
setPmInterval,
|
||||
encWbi
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user