// ==UserScript==
// @name 哔哩哔哩粉丝勋章勋章升级
// @version 1.02
// @description 模拟观看直播获取粉丝勋章亲密度:每日每观看五分钟可获得300亲密度,最多可获得1500亲密度
// @author 无夏不春风orz
// @iconURL https://www.bilibili.com/favicon.ico
// @match https://live.bilibili.com/*
// @match https://www.bilibili.com/blackboard/live/*
// @connect bilibili.com
// @require https://unpkg.com/jquery@3.7.1/dist/jquery.js
// @require https://unpkg.com/crypto-js@4.2.0/crypto-js.js
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @run-at document-idle
// @license MIT
// @namespace http://tampermonkey.net/
// @downloadURL https://update.greasyfork.icu/scripts/537954/%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%E7%B2%89%E4%B8%9D%E5%8B%8B%E7%AB%A0%E5%8B%8B%E7%AB%A0%E5%8D%87%E7%BA%A7.user.js
// @updateURL https://update.greasyfork.icu/scripts/537954/%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%E7%B2%89%E4%B8%9D%E5%8B%8B%E7%AB%A0%E5%8B%8B%E7%AB%A0%E5%8D%87%E7%BA%A7.meta.js
// ==/UserScript==
(function () {
var NAME
var BAPI
var Live_info = {
coin: undefined,
room_id: undefined,
uid: undefined,
csrf_token: undefined,
rnd: undefined,
ruid: undefined,
uname: undefined,
user_level: undefined,
Blever: undefined,
vipType: undefined,
face_url: undefined,
vipTypetext: undefined,
vipStatus: undefined,
cost: undefined,
regtime: undefined,
identification: undefined,
img_key:undefined,
sub_key:undefined,
buvid3:undefined,
};
const ts_ms = () => Date.now();
const ts_s = () => Math.round(ts_ms() / 1000);
const hour = () => new Date(ts_ms()).getHours();
const minute = () => new Date(ts_ms()).getMinutes();
function sleep(ms) {
return new Promise(resolve => setTimeout(() => resolve('sleep'), ms));
}
String.prototype.replaceAll = function (oldSubStr, newSubStr) {
return this.replace(new RegExp(oldSubStr, 'gm'), newSubStr)
}
const newWindow = {
init: () => {
return newWindow.Toast.init();
},
Toast: {
init: () => {
try {
const list = [];
window.toast = (msg, type = 'info', timeout = 5e3) => {
console.log(msg)
switch (type){
case 'success':
case 'info':
case 'error':
break;
default:
type = 'info';
}
const a = $(`
${msg}
`)[0];
document.body.appendChild(a);
a.style.top = (document.body.scrollTop + list.length * 40 + 10) + 'px';
a.style.left = 10 + 'px';
list.push(a);
setTimeout(() => {
a.className += ' out';
setTimeout(() => {
list.shift();
list.forEach((v) => {
v.style.top = (parseInt(v.style.top, 10) - 40) + 'px';
});
$(a).remove();
}, 200);
}, timeout);
};
return $.Deferred().resolve();
} catch (err){
return $.Deferred().reject();
}
}
}
}
const getCookie = (name) => {
let arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
if (arr != null)
return unescape(arr[2]);
return false;
}
$(function () { //DOM完毕,等待弹幕加载完成
if((typeof BilibiliLive) == "undefined"){
BilibiliLive = undefined;
}
let loadInfo = (delay) => {
setTimeout(async function () {
GM_xmlhttpRequest({
method: "GET",
url: "https://api.bilibili.com/x/web-interface/nav",
onload: async function(response) {
let data = JSON.parse(response.response);
if (!data.data.isLogin) {
loadInfo(5000);
window.toast('无账号登陆信息,请先登录或检查网络','error',8000);
} else {
if(BilibiliLive == undefined || BilibiliLive.ROOMID == undefined) return loadInfo(5000);
Live_info.room_id = BilibiliLive.ROOMID;
Live_info.ruid = BilibiliLive.ANCHOR_UID
Live_info.uid = data.data.mid
Live_info.coin = parseInt(data.data.money)
Live_info.Blever = data.data.level_info.current_level
Live_info.vipType = data.data.vipType
Live_info.vipStatus = data.data.vipStatus
Live_info.uname = data.data.uname
Live_info.face_url = data.data.face
Live_info.vipTypetext = data.data.vip_label.text
if(Live_info.vipTypetext=='')Live_info.vipTypetext = '普通用户'
let img_url = data.data.wbi_img.img_url
let sub_url = data.data.wbi_img.sub_url
let img_key = img_url.slice(img_url.lastIndexOf('/') + 1,img_url.lastIndexOf('.'))
let sub_key = sub_url.slice(sub_url.lastIndexOf('/') + 1,sub_url.lastIndexOf('.'))
Live_info.img_key = img_key
Live_info.sub_key = sub_key
NAME = 'BILI' + Live_info.uid
Live_info.csrf_token = getCookie('bili_jct');
Live_info.buvid3 = getCookie('buvid3');
window.toast('登陆信息获取成功','success');
init();
}
},
onerror : function(err){
loadInfo(5000);
window.toast('无账号登陆信息,请先登录或检查网络','error',8000);
}
});
}, delay);
};
newWindow.init();
loadInfo(5000);
});
async function init() { //API初始化
const right_ctnr = $('.right-ctnr');
const share = right_ctnr.find('.v-middle.icon-font.icon-share').parent();
const remove_button = $(
`去掉直播水印
`
);
remove_button.click(() => {
$('.web-player-icon-roomStatus').remove()
});
const blanc_button = $(
`回到默认界面
`
);
blanc_button.click(() => {
window.top.location.href = 'https://live.bilibili.com/blanc/'+ Live_info.room_id
})
if ($('.right-ctnr').length !== 0){
right_ctnr[0].insertBefore(remove_button[0], share[0]);
right_ctnr[0].insertBefore(blanc_button[0], remove_button[0], share[0]);
}
BAPI = BilibiliAPI;
const MY_API = {
CONFIG_DEFAULT: {
auto_medal_task: true,
medal_level_pass: true,
medal_pass_level: 21,
js_running_mark:0,
medal_pass_uid:[],
sort:true,
hide_Toast:true,
medal_first_uid:[],
auto_coin_add: true,
},
CONFIG: {},
loadConfig: async function () {
let p = $.Deferred();
try {
let config = JSON.parse(localStorage.getItem(`${NAME}_CONFIG`));
$.extend(true, MY_API.CONFIG, MY_API.CONFIG_DEFAULT);
for (let item in MY_API.CONFIG) {
if (!MY_API.CONFIG.hasOwnProperty(item))
continue;
if (config[item] !== undefined && config[item] !== null)
MY_API.CONFIG[item] = config[item];
}
p.resolve()
} catch (e) {
console.log('API载入配置失败,加载默认配置', e);
MY_API.CONFIG = MY_API.CONFIG_DEFAULT
MY_API.saveConfig()
p.reject()
}
return p
},
saveConfig: function () {
try {
localStorage.setItem(`${NAME}_CONFIG`, JSON.stringify(MY_API.CONFIG));
return true
} catch (e) {
console.log('API保存出错', e);
return false
}
},
creatSetBox: function () { // 创建设置框
// 获取最大宽度和高度
const widthmax = $('.web-player-ending-panel').width() - 50;
const heightmax = $('.chat-history-panel').height();
// 创建主容器
const div = $("");
// 设置容器样式
div.css({
'width': '400px',
'min-width': '400px',
'height': '560px',
'max-height': `${heightmax}px`,
'position': 'absolute',
'top': '10px',
'right': '10px',
'background': 'rgba(255,255,255,.95)',
'padding': '10px',
'z-index': '99',
'border-radius': '12px',
'box-shadow': '0 4px 12px rgba(0,0,0,0.15)',
'transition': 'all .3s ease',
'overflow': 'auto',
'line-height': '1.2',
'font-family': 'system-ui, -apple-system, sans-serif'
});
// 创建HTML内容
div.append(`
`);
// 添加CSS样式
$('head').append(`
`);
// 添加到DOM
$('.player-section.p-relative.border-box.none-select.z-player-section').append(div);
// 隐藏/显示按钮事件
div.find('div[data-toggle="ui_hide"] [data-action="save"]').click(function () {
$('.xzsjzsdiv').toggle();
$(this).text($('.xzsjzsdiv').is(':visible') ? '隐藏设置面板' : '显示设置面板');
});
// 初始化复选框状态
const initCheckbox = (toggle, configKey) => {
const checkbox = div.find(`div[data-toggle="${toggle}"] input`);
if (MY_API.CONFIG[configKey]) {
checkbox.prop('checked', true);
}
checkbox.change(function () {
MY_API.CONFIG[configKey] = $(this).prop('checked');
MY_API.saveConfig();
window.toast(`${configKey.replace(/_/g, ' ')} 设置: ${MY_API.CONFIG[configKey]}`);
if (configKey === 'auto_medal_task' && MY_API.CONFIG[configKey]) {
window.location.reload();
}
});
};
// 初始化所有复选框
initCheckbox('auto_medal_task', 'auto_medal_task');
initCheckbox('auto_coin_add', 'auto_coin_add');
initCheckbox('medal_level_pass', 'medal_level_pass');
initCheckbox('sort', 'sort');
initCheckbox('hide_Toast', 'hide_Toast');
// 初始化跳过等级输入
div.find('div[data-toggle="medal_level_pass_num"] .num').val(parseInt(MY_API.CONFIG.medal_pass_level.toString()));
div.find('div[data-toggle="medal_level_pass_num"] [data-action="save"]').click(function () {
const level = parseInt(div.find('div[data-toggle="medal_level_pass_num"] .num').val());
if (isNaN(level) || level < 1) {
window.toast('请输入1以上的有效数字');
return;
}
MY_API.CONFIG.medal_pass_level = level;
MY_API.saveConfig();
window.toast(`勋章跳过等级已设置为: ${MY_API.CONFIG.medal_pass_level}`);
});
// 处理UID列表保存
const handleUidSave = (toggle, configKey, message) => {
const input = div.find(`div[data-toggle="${toggle}"] .keyword`);
input.val(MY_API.CONFIG[configKey].join(', '));
div.find(`div[data-toggle="${toggle}"] [data-action="save"]`).click(function () {
let val = input.val().trim();
if (!val) {
MY_API.CONFIG[configKey] = [];
MY_API.saveConfig();
window.toast(`${message} 已清空`);
return;
}
val = val.replaceAll(',', ',');
const uids = val.split(",")
.map(uid => uid.trim())
.filter(uid => uid !== '')
.map(uid => Number(uid))
.filter(uid => !isNaN(uid));
const uniqueUids = [...new Set(uids)];
MY_API.CONFIG[configKey] = uniqueUids;
MY_API.saveConfig();
window.toast(`${message} 已设置: ${uniqueUids.join(', ')}`);
});
};
// 初始化UID列表
handleUidSave('medal_pass_uid', 'medal_pass_uid', '【观看跳过】主播UID');
handleUidSave('medal_first_uid', 'medal_first_uid', '【观看优先】主播UID');
},
getMedalList:async() => {
let medal_list_now = [];
let break_mark = false
for(let page=1;page<999;page++){
if(break_mark)break
await BAPI.fansMedal_panel(page).then(async (data) => {
medal_list_now = medal_list_now.concat(data.data.list);
if(data.data.special_list.length)medal_list_now = medal_list_now.concat(data.data.special_list);
window.toast(`【勋章升级】正在获取勋章数据:已获取${medal_list_now.length}个`,'success');
if (data.data.page_info.current_page >= data.data.page_info.total_page){
break_mark = true;
}else{
await sleep(2000)
}
}, () => {
break_mark = true;
});
}
return medal_list_now
},
auto_heartbert:async() => {
window.toast(`【勋章升级】开始获取勋章数据`);
let medal_list_now = await MY_API.getMedalList()
if(MY_API.CONFIG.sort && medal_list_now.length)medal_list_now.sort(function(a, b) { return a.medal.level - b.medal.level;});
if(MY_API.CONFIG.medal_first_uid.length && medal_list_now.length){
let new_medal_list_now = []
let first_medal_list = []
for(let i=0;i
-1){
first_medal_list.push(medal_list_now[i])
}else{
new_medal_list_now.push(medal_list_now[i])
}
}
medal_list_now = [].concat(first_medal_list).concat(new_medal_list_now)
}
let start_ts = ts_s()
let dotime = 26
if(medal_list_now.length){
for(let i=0;i -1){
if(MY_API.CONFIG.hide_Toast)window.toast(`【观看任务】[${medal_list_now[i].anchor_info.nick_name}][${medal_list_now[i].medal.target_id}] [${medal_list_now[i].medal.medal_name}] [${medal_list_now[i].medal.level}] [${medal_list_now[i].room_info.room_id}] 不执行名单跳过`)
continue
}
if(MY_API.CONFIG.medal_level_pass && medal_list_now[i].medal.level >= 21){
if(MY_API.CONFIG.hide_Toast)window.toast(`【观看任务】[${medal_list_now[i].anchor_info.nick_name}][${medal_list_now[i].medal.target_id}] [${medal_list_now[i].medal.medal_name}] [${medal_list_now[i].medal.level}] [${medal_list_now[i].room_info.room_id}] ${medal_list_now[i].medal.level}级勋章跳过`)
continue
}
if(MY_API.CONFIG.medal_level_pass && medal_list_now[i].medal.level >= MY_API.CONFIG.medal_pass_level){
if(MY_API.CONFIG.hide_Toast)window.toast(`【观看任务】[${medal_list_now[i].anchor_info.nick_name}][${medal_list_now[i].medal.target_id}] [${medal_list_now[i].medal.medal_name}] [${medal_list_now[i].medal.level}] [${medal_list_now[i].room_info.room_id}] ${medal_list_now[i].medal.level}级勋章跳过`)
continue
}
if(medal_list_now[i].medal.today_feed != undefined && medal_list_now[i].medal.today_feed >= 1500){
if(MY_API.CONFIG.hide_Toast)window.toast(`【观看任务】[${medal_list_now[i].anchor_info.nick_name}][${medal_list_now[i].medal.target_id}] [${medal_list_now[i].medal.medal_name}] [${medal_list_now[i].medal.level}] [${medal_list_now[i].room_info.room_id}] 今日经验已满`)
continue
}
if(medal_list_now[i].medal.today_feed != undefined){
if(MY_API.CONFIG.hide_Toast)window.toast(`【观看任务】[${medal_list_now[i].anchor_info.nick_name}][${medal_list_now[i].medal.target_id}] [${medal_list_now[i].medal.medal_name}] [${medal_list_now[i].medal.level}] [${medal_list_now[i].room_info.room_id}] 今日已获经验${medal_list_now[i].medal.today_feed}`)
dotime = ((1500 - medal_list_now[i].medal.today_feed)/300)*5
}
for(let t=0;t {
if(MY_API.CONFIG.hide_Toast)window.toast(`【观看任务】[${medal_list_now[i].anchor_info.nick_name}][${medal_list_now[i].medal.target_id}] [${medal_list_now[i].medal.medal_name}] [${medal_list_now[i].medal.level}] [${medal_list_now[i].room_info.room_id}] 进度${t}/${dotime}`,'info',60000)
},t* 60 * 1000)
}
let roomHeart = new RoomHeart(medal_list_now[i].room_info.room_id,dotime,medal_list_now[i].medal.target_id)
await roomHeart.start()
await sleep(dotime*60*1000)
}
}
if(ts_s() - start_ts < 10*60){
window.toast(`【观看任务】将在10分钟后重新运行...`)
await sleep(10*60*1000)
}
return MY_API.auto_heartbert()
},
experience_add:async() => {//主站经验大会员加速包
if(Live_info.vipType >= 1 && Live_info.vipStatus == 1){
await BAPI.experience_add(Live_info.csrf_token).then(async(re) => {
if(re.code == 0){
window.toast(`【大会员经验】主站经验大会员加速包领取成功`, 'success');
}else if(re.code == 69801){
window.toast(`【大会员经验】主站经验大会员加速包已领取`, 'error');
}else{
window.toast(`【大会员经验】主站经验大会员加速包:${re.message}`, 'error');
}
});
window.toast(`【大会员经验】将在12小时后重新运行...`)
await sleep(12*3600*1000)
return MY_API.experience_add()
}
},
score_task_sign:async() => {//大会员自动签到
if(Live_info.vipType >= 1 && Live_info.vipStatus == 1){
await BAPI.score_task_sign().then(async(re) => {
if(re.code == 0 && re.message == "success"){
window.toast(`【大会员签到】大会员签到成功`, 'success');
await BAPI.vip_point_task_combine().then((r) => {
if(r.code == 0){
window.toast(`【大会员签到】大会员积分:${r.data.point_info.point}`, 'success');
}
})
}
});
window.toast(`【大会员签到】将在12小时后重新运行...`)
await sleep(12*3600*1000)
return MY_API.score_task_sign()
}
},
watch_share:async() => {
await BAPI.top_rcmd().then(async (response) => {
if(response.code === 0 && response.data.item){
const obj =(response.data.item[0])
await BAPI.watch_heartbeat(obj.id, obj.cid, Live_info.uid, Live_info.csrf_token).then((response) => {
//console.log('每日观看', response)
if(response.code === 0){
window.toast(`【视频观看】视频观看完成(av=${obj.id})`, 'success');
}else{
window.toast(`【视频观看】视频观看${response.message}`, 'error');
}
})
await BAPI.aid_share(obj.id, Live_info.csrf_token).then((response) => {
//console.log('每日分享', response)
if(response.code === 0){
window.toast(`【视频分享】视频分享完成(av=${obj.id})`, 'success');
}else{
window.toast(`【视频分享】视频分享${response.message}`, 'error');
}
})
}else{
window.toast(`【观看分享】获取视频失败:${response.msg}`, 'error');
}
})
window.toast(`【观看分享】将在12小时后重新运行...`)
await sleep(12*3600*1000)
return MY_API.watch_share()
},
coin_add:async() => {
await BAPI.exp().then(async (response) => {
//console.log('今日投币已获经验exp',response.data)
if(response.code === 0){
let need_coin = (50 - response.data)/10
if(need_coin){
await BAPI.article_recommends().then(async(response) => {
let oidlist = response.data
let break_mark = false
for(let i = 0;i {
//console.log('每日专栏投币', response)
if(response.code === 0){
window.toast(`【投币任务】投币1个成功(av=${oidlist[i].id})`);
}else if(response.code == -104){
break_mark=true
window.toast(`【投币任务】投币${response.message}`);
}else{
break_mark=true
window.toast(`【投币任务】投币${response.message}`);
}
})
await sleep(5000)
}
})
}else{
window.toast(`【投币任务】投币任务已完成`);
}
}
})
window.toast(`【投币任务】将在12小时后重新运行...`)
await sleep(12*3600*1000)
return MY_API.coin_add()
},
};
MY_API.loadConfig()
try {
const promiseInit = $.Deferred();
const uniqueCheck = () => {
const t = Date.now();
if(t - MY_API.CONFIG.js_running_mark >= 0 && t - MY_API.CONFIG.js_running_mark <= 10e3){
// 其他脚本正在运行
window.toast('检测到脚本已经运行');
return promiseInit.reject();
}
// 没有其他脚本正在运行
return promiseInit.resolve();
};
uniqueCheck().then(() => {
let timer_unique;
const uniqueMark = () => {
timer_unique = setTimeout(uniqueMark, 2e3);
MY_API.CONFIG.js_running_mark = Date.now();
try {
localStorage.setItem(`${NAME}_CONFIG`, JSON.stringify(MY_API.CONFIG));
return true
} catch (e){
console.log('API保存出错', e);
return false
};
};
window.addEventListener('unload', () => {
if(timer_unique){
clearTimeout(timer_unique);
MY_API.CONFIG.js_running_mark = 0;
try {
localStorage.setItem(`${NAME}_CONFIG`, JSON.stringify(MY_API.CONFIG));
return true
} catch (e){
console.log('API保存出错', e);
return false
};
}
});
uniqueMark();
StartPlunder(MY_API);
})
} catch (e){
console.error('重复运行检测错误', e);
}
}
async function StartPlunder(API) {
await BAPI.room_init(Live_info.room_id).then(async function(data){
if(data.code == 0 && data.data.live_status == 1){
window.toast('当前直播间正在直播,由于B站限制仅能生效一个观看亲密度(包括各端),可能不能正常获取亲密度,请挂在未开播直播间','error',60000);
}
})
let icon_pic = 'https://s1.hdslb.com/bfs/live/e051dfd4557678f8edcac4993ed00a0935cbd9cc.png'
let ui_button = $(`
`)
$('html').append(ui_button);
ui_button.click(function () {
$('.xzsjzsdiv').toggle()
});
API.creatSetBox(); //创建设置框
$('.xzsjzsdiv').hide()
let get_cost = () => {
return BAPI.cost().then((re) => {
if(re.code == 0){
let list = re.data.info
for(let i=0;i
昵称:${Live_info.uname}
UID:${Live_info.uid}
直播消费:${Live_info.cost}
会员等级:${Live_info.vipTypetext}
主站等级:Lv${Live_info.Blever}
硬币数量:${Live_info.coin}
`
}
showinfo()
setTimeout(async() => {
if (API.CONFIG.auto_medal_task) {
API.auto_heartbert()
}
if (API.CONFIG.auto_coin_add) {
API.watch_share()
API.coin_add()
API.score_task_sign()
API.experience_add()
}
}, 5 * 1000);
}
})();
const BilibiliAPI = {
runUntilSucceed: (callback, delay = 0, period = 50) => {
setTimeout(() => {
if (!callback())
BilibiliAPI.runUntilSucceed(callback, period, period);
}, delay);
},
processing: 0,
ajax: (settings) => {
if (settings.xhrFields === undefined)
settings.xhrFields = {};
settings.xhrFields.withCredentials = true;
jQuery.extend(settings, {
url: settings.url,
method: settings.method || 'GET',
crossDomain: true,
dataType: settings.dataType || 'json'
});
const p = jQuery.Deferred();
BilibiliAPI.runUntilSucceed(() => {
if (BilibiliAPI.processing > 8)
return false;
++BilibiliAPI.processing;
return jQuery.ajax(settings).then((arg1, arg2, arg3) => {
--BilibiliAPI.processing;
p.resolve(arg1, arg2, arg3);
return true;
}, (arg1, arg2, arg3) => {
--BilibiliAPI.processing;
p.reject(arg1, arg2, arg3);
return true;
});
});
return p;
},
fansMedal_panel: (page,pageSize=50) => {//获取全部勋章数据
return BilibiliAPI.ajax({
url: "https://api.live.bilibili.com/xlive/app-ucenter/v1/fansMedal/panel",
method: "GET",
data:{
page:page,
page_size:pageSize
}
})
},
cost: () => {//花费适用于10w以下
return BilibiliAPI.ajax({
url: "https://api.live.bilibili.com/xlive/web-ucenter/v1/achievement/list?type=normal&status=0&category=all&keywords=&page=1&pageSize=100",
method: "GET",
})
},
room_init: (roomid) => {//花费适用于10w以下
return BilibiliAPI.ajax({
url: "https://api.live.bilibili.com/room/v1/Room/room_init?id="+roomid,
method: "GET",
})
},
top_rcmd: () => {//首页视频推荐
return BilibiliAPI.ajax({
url: "https://api.bilibili.com/x/web-interface/index/top/rcmd",
method: "GET",
})
},
watch_heartbeat: (i, e, a, csrf) => {//视频观看
return BilibiliAPI.ajax({
method: "POST",
url: "https://api.bilibili.com/x/report/web/heartbeat",
data: {
aid: i,
cid: e,
mid: a,
start_ts: Math.round(Date.now()/1000),
played_time: 0,
realtime: 0,
type: 3,
play_type: 1,
dt: 2,
csrf:csrf
}
})
},
aid_share: (i, csrf) => {//视频分享
return BilibiliAPI.ajax({
method: "POST",
url: "https://api.bilibili.com/x/web-interface/share/add",
data: {
aid: i,
csrf: csrf,
jsonp: "jsonp"
}
})
},
exp: () => {//投币经验
return BilibiliAPI.ajax({
url: "https://api.bilibili.com/x/web-interface/coin/today/exp",
})
},
article_recommends: () => {//获取最新专栏信息
return BilibiliAPI.ajax({
url: "https://api.bilibili.com/x/article/recommends",
method: "GET",
data:{
aid:'',
cid:3,
pn:1,
ps:20,
jsonp:'jsonp',
sort:1
}
})
},
article_coin_add: (oid, upid, csrf) => {//专栏投币
return BilibiliAPI.ajax({
url: "https://api.bilibili.com/x/web-interface/coin/add",
method: "POST",
data:{
aid:oid,
upid: upid,
multiply: 1,
avtype: 2,
csrf: csrf
}
})
},
experience_add: (csrf) => {//大会员经验包
return BilibiliAPI.ajax({
url: "https://api.bilibili.com/x/vip/experience/add",
method: "POST",
data:{
csrf: csrf
}
})
},
score_task_sign:() => {//大会员积分签到
return BilibiliAPI.ajax({
url: "https://api.bilibili.com/pgc/activity/score/task/sign",
method: "POST",
})
},
}
//https://github.com/lzghzr/TampermonkeyJS
class RoomHeart {
constructor(t,g,r) {
this.roomID = t,
this.Dotime = g,
this.ruid = r
}
areaID;
parentID;
seq = 0;
roomID;
Dotime;
get id() {
return [this.parentID, this.areaID, this.seq, this.roomID]
}
buvid = this.getItem("LIVE_BUVID");
uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (t => {
const e = 16 * Math.random() | 0;
return ("x" === t ? e : 3 & e | 8).toString(16)
}));
device = [this.buvid, this.uuid];
get ts() {
return Date.now()
}
_patchData = {};
get patchData() {
const t = [];
for (const[e, i]of Object.entries(this._patchData))
t.push(i);
return t
}
get isPatch() {
return 0 === this.patchData.length ? 0 : 1
}
W = "undefined" == typeof unsafeWindow ? window : unsafeWindow;
ua = this.W && this.W.navigator ? this.W.navigator.userAgent : "";
csrf = this.getItem("bili_jct") || "";
nextInterval = Math.floor(5) + Math.floor(55 * Math.random());
heartBeatInterval;
secretKey;
secretRule;
timestamp;
lastHeartbeatTimestamp = Date.now();
get watchTimeFromLastReport() {
const t = Math.ceil(((new Date).getTime() - this.lastHeartbeatTimestamp) / 1e3);
return t < 0 ? 0 : t > this.heartBeatInterval ? this.heartBeatInterval : t
}
start() {
return this.getInfoByRoom()
}
doneFunc = function () {};
async getInfoByRoom() {
if (0 === this.roomID)
return !1;
const t = await fetch(`//api.live.bilibili.com/room/v1/Room/get_info?room_id=${this.roomID}&from=room`, {
mode: "cors",
credentials: "include"
}).then((t => t.json()));
return 0 === t.code && (({
area_id: this.areaID,
parent_area_id: this.parentID,
room_id: this.roomID
} = t.data), 0 !== this.areaID && 0 !== this.parentID && (this.e(), !0))
}
async webHeartBeat() {
if (this.seq > this.Dotime)
return;
const t = `${this.nextInterval}|${this.roomID}|1|0`,
e = CryptoJS.enc.Utf8.parse(t),
i = CryptoJS.enc.Base64.stringify(e),
s = await fetch(`//live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat?hb=${encodeURIComponent(i)}&pf=web`, {
mode: "cors",
credentials: "include"
}).then((t => t.json()));
0 === s.code && (this.nextInterval = s.data.next_interval, setTimeout((() => this.webHeartBeat()), 1e3 * this.nextInterval))
}
async savePatchData() {
if (this.seq > this.Dotime)
return;
const t = {
id: JSON.stringify(this.id),
device: JSON.stringify(this.device),
ruid:this.ruid,
ets: this.timestamp,
benchmark: this.secretKey,
time: this.watchTimeFromLastReport > this.heartBeatInterval ? this.heartBeatInterval : this.watchTimeFromLastReport,
ts: this.ts,
ua: this.ua
},
e = this.sypder(JSON.stringify(t), this.secretRule),
i = Object.assign({
s: e
}, t);
this._patchData[this.roomID] = i,
setTimeout((() => this.savePatchData()), 15e3)
}
async e() {
const t = {
id: JSON.stringify(this.id),
device: JSON.stringify(this.device),
ruid:this.ruid,
ts: this.ts,
is_patch: 0,
heart_beat: "[]",
ua: this.ua
},
e = await fetch("//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/E", {
headers: {
"content-type": "application/x-www-form-urlencoded"
},
method: "POST",
body: `${this.json2str(t)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
mode: "cors",
credentials: "include"
}).then((t => t.json()));
0 === e.code && (this.seq += 1, ({
heartbeat_interval: this.heartBeatInterval,
secret_key: this.secretKey,
secret_rule: this.secretRule,
timestamp: this.timestamp
} = e.data), setTimeout((() => this.x()), 1e3 * this.heartBeatInterval))
}
async x() {
if (this.seq > this.Dotime)
return this.doneFunc();
const t = {
id: JSON.stringify(this.id),
device: JSON.stringify(this.device),
ruid:this.ruid,
ets: this.timestamp,
benchmark: this.secretKey,
time: this.heartBeatInterval,
ts: this.ts,
ua: this.ua
},
e = this.sypder(JSON.stringify(t), this.secretRule),
i = Object.assign({
s: e
}, t);
this._patchData[this.roomID] = i,
this.lastHeartbeatTimestamp = Date.now();
const s = await fetch("//live-trace.bilibili.com/xlive/data-interface/v1/x25Kn/X", {
headers: {
"content-type": "application/x-www-form-urlencoded"
},
method: "POST",
body: `${this.json2str(i)}&csrf_token=${this.csrf}&csrf=${this.csrf}&visit_id=`,
mode: "cors",
credentials: "include"
}).then((t => t.json()));
0 === s.code && (this.seq += 1, ({
heartbeat_interval: this.heartBeatInterval,
secret_key: this.secretKey,
secret_rule: this.secretRule,
timestamp: this.timestamp
} = s.data), setTimeout((() => this.x()), 1e3 * this.heartBeatInterval))
}
sypder(t, e) {
const i = JSON.parse(t),
[s, a, r, n] = JSON.parse(i.id),
[o, c] = JSON.parse(i.device),
h = i.benchmark,
m = {
platform: "web",
parent_id: s,
area_id: a,
seq_id: r,
room_id: n,
buvid: o,
uuid: c,
ets: i.ets,
time: i.time,
ts: i.ts
};
let d = JSON.stringify(m);
for (const t of e)
switch (t) {
case 0:
d = CryptoJS.HmacMD5(d, h).toString(CryptoJS.enc.Hex);
break;
case 1:
d = CryptoJS.HmacSHA1(d, h).toString(CryptoJS.enc.Hex);
break;
case 2:
d = CryptoJS.HmacSHA256(d, h).toString(CryptoJS.enc.Hex);
break;
case 3:
d = CryptoJS.HmacSHA224(d, h).toString(CryptoJS.enc.Hex);
break;
case 4:
d = CryptoJS.HmacSHA512(d, h).toString(CryptoJS.enc.Hex);
break;
case 5:
d = CryptoJS.HmacSHA384(d, h).toString(CryptoJS.enc.Hex);
break;
default:
break
}
return d
}
getItem(t) {
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(t).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || ""
}
json2str(t) {
let e = "";
for (const i in t)
e += `${i}=${encodeURIComponent(t[i])}&`;
return e.slice(0, -1)
}
}