您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
模拟观看直播获取粉丝勋章亲密度:每日每观看五分钟可获得300亲密度,最多可获得1500亲密度
// ==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/[email protected]/dist/jquery.js // @require https://unpkg.com/[email protected]/crypto-js.js // @grant unsafeWindow // @grant GM_xmlhttpRequest // @run-at document-idle // @license MIT // @namespace http://tampermonkey.net/ // ==/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 = $(`<div class="link-toast ${type} fixed" style="z-index:2001;text-align: left;"><span class="toast-text">${msg}</span></div>`)[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 = $( `<div data-v-6d89404b="" data-v-42ea937d="" title="" class="icon-ctnr live-skin-normal-a-text pointer" id = "blth_like_button" style="line-height: 16px;margin-left: 15px;"><i data-v-6d89404b="" class="v-middle icon-font icon-delete" style="font-size: 16px;"></i><span data-v-6d89404b="" class="action-text v-middle" style="font-size: 12px;margin-left: 5px;">去掉直播水印</span></div>` ); remove_button.click(() => { $('.web-player-icon-roomStatus').remove() }); const blanc_button = $( `<div data-v-6d89404b="" data-v-42ea937d="" title="" class="icon-ctnr live-skin-normal-a-text pointer" id = "blth_like_button" style="line-height: 16px;margin-left: 15px;"><i data-v-6d89404b="" class="v-middle icon-font icon-top" style="font-size: 16px;"></i><span data-v-6d89404b="" class="action-text v-middle" style="font-size: 12px;margin-left: 5px;">回到默认界面</span></div>` ); 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 class='xzsjzsdiv'>"); // 设置容器样式 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(` <div class="settings-header"> <h3 style="margin:0;color:#06aed5;text-align:center;">勋章升级设置</h3> </div> <fieldset class="settings-fieldset"> <legend class="settings-legend">用户信息</legend> <div id="user_info" class="user-info"> <img src="${Live_info.face_url}" width="50" height="50" class="user-avatar"> <div class="user-details"> <div><span class="detail-label">昵称:</span>${Live_info.uname}</div> <div><span class="detail-label">UID:</span>${Live_info.uid}</div> <div><span class="detail-label">直播消费:</span>${Live_info.cost}</div> <div><span class="detail-label">会员等级:</span>${Live_info.vipTypetext}</div> <div><span class="detail-label">主站等级:</span>Lv${Live_info.Blever}</div> <div><span class="detail-label">硬币数量:</span>${Live_info.coin}</div> </div> </div><br> <div class="setting-item" data-toggle="auto_coin_add"> <label class="checkbox-container"> <input type="checkbox"> <span class="checkmark"></span> <span class="setting-label">自动观看分享投币</span> </label> </div> </fieldset> <fieldset class="settings-fieldset"> <legend class="settings-legend">勋章升级参数设置</legend> <div class="setting-item" data-toggle="auto_medal_task"> <label class="checkbox-container"> <input type="checkbox"> <span class="checkmark"></span> <span class="setting-label">自动直播观看任务</span> </label> </div> <div class="setting-item" data-toggle="medal_level_pass"> <label class="checkbox-container"> <input type="checkbox"> <span class="checkmark"></span> <span class="setting-label">高级勋章任务跳过</span> </label> </div> <div class="setting-item" data-toggle="medal_level_pass_num"> <span class="setting-label">跳过</span> <input class="setting-input num" type="number" min="1"> <span class="setting-label">级及以上勋章</span> <button class="setting-button" data-action="save">保存</button> </div> <div class="setting-item" data-toggle="medal_pass_uid"> <div class="setting-label">不执行观看任务主播UID [逗号隔开]</div> <div class="input-group"> <input class="setting-input keyword" type="text" placeholder="输入UID,多个用逗号分隔"> <button class="setting-button" data-action="save">保存</button> </div> </div> <div class="setting-item" data-toggle="medal_first_uid"> <div class="setting-label">优先执行观看任务主播UID [逗号隔开]</div> <div class="input-group"> <input class="setting-input keyword" type="text" placeholder="输入UID,多个用逗号分隔"> <button class="setting-button" data-action="save">保存</button> </div> </div> <div class="setting-item" data-toggle="sort"> <label class="checkbox-container"> <input type="checkbox"> <span class="checkmark"></span> <span class="setting-label">按等级升序顺序执行</span> </label> </div> <div class="setting-item" data-toggle="hide_Toast"> <label class="checkbox-container"> <input type="checkbox"> <span class="checkmark"></span> <span class="setting-label">运行提示信息弹出显示</span> </label> </div> <div class="settings-note"> <ul> <p><strong>注意事项:</strong></p> <li>同一时间仅一个勋章可获得亲密度,故每日观看总亲密度有上限,可适当排序或过滤。部分设置需要刷新后生效。20级以上默认不执行观看任务。 </ul> </div> </fieldset> <div class="settings-footer" data-toggle="ui_hide"> <button class="setting-button toggle-button" data-action="save">隐藏设置面板</button> </div> `); // 添加CSS样式 $('head').append(` <style> .xzsjzsdiv { scrollbar-width: thin; scrollbar-color: #06aed5 rgba(0,0,0,0.1); } .xzsjzsdiv::-webkit-scrollbar { width: 6px; } .xzsjzsdiv::-webkit-scrollbar-thumb { background-color: #06aed5; border-radius: 3px; } .settings-fieldset { border: 1px solid #ddd; border-radius: 8px; margin-bottom: 5px; padding: 10px; } .settings-legend { color: #06aed5; font-weight: bold; padding: 0 8px; font-size: 14px; } .user-info { display: flex; gap: 10px; align-items: flex-start; } .user-avatar { border-radius: 50%; border: 2px solid #06aed5; } .user-details { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; font-size: 12px; } .detail-label { color: #666; font-weight: bold; } .setting-item { margin-bottom: 2px; padding: 0px 0; border-bottom: 1px dashed #eee; } .setting-item:last-child { border-bottom: none; } .checkbox-container { display: flex; align-items: center; cursor: pointer; position: relative; user-select: none; } .checkbox-container input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0; } .checkmark { position: relative; height: 18px; width: 18px; background-color: #fff; border: 1px solid #06aed5; border-radius: 4px; margin-right: 8px; } .checkbox-container input:checked ~ .checkmark { background-color: #06aed5; } .checkmark:after { content: ""; position: absolute; display: none; } .checkbox-container input:checked ~ .checkmark:after { display: block; } .checkbox-container .checkmark:after { left: 6px; top: 2px; width: 5px; height: 10px; border: solid white; border-width: 0 2px 2px 0; transform: rotate(45deg); } .setting-label { font-size: 14px; color: #333; } .setting-input { padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; margin: 0 5px; width: 50px; } .setting-input.keyword { width: 220px; margin-right: 8px; } .setting-button { background-color: #06aed5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 13px; transition: all 0.2s; } .setting-button:hover { background-color: #0589b0; } .input-group { display: flex; align-items: center; margin-top: 5px; } .settings-note { font-size: 12px; color: #666; margin-top: 15px; background: #f8f8f8; padding: 0px; border-radius: 6px; } .settings-note ul { margin: 5px 0 0 15px; padding: 0; } .settings-note li { margin-bottom: 3px; } .toggle-button { display: block; width: 100%; margin-top: 10px; background-color: #888; } .toggle-button:hover { background-color: #666; } </style> `); // 添加到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<medal_list_now.length;i++){ if(MY_API.CONFIG.medal_first_uid.indexOf(medal_list_now[i].medal.target_id) > -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<medal_list_now.length;i++){ if(MY_API.CONFIG.medal_pass_uid.indexOf(medal_list_now[i].medal.target_id) > -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<dotime;t++){ setTimeout(async() => { 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<need_coin;i++){ if(break_mark)break await BAPI.article_coin_add(oidlist[i].id, oidlist[i].author.mid, Live_info.csrf_token).then(async(response) => { //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 = $(`<img width=100 height=100 style="position: fixed; top: 155px; right: 60px;z-index:999;" src=${icon_pic} />`) $('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<list.length;i++){ if(list[i].title == "富可敌国"){ if(list[i].finished){ Live_info.cost = '10个W元以上' }else{ Live_info.cost = parseInt(list[i].progress.now/10) + '元' } break } } } }); } await get_cost() let showinfo = async function () { let info = document.getElementById('user_info'); info.innerHTML = ` <img src="${Live_info.face_url}" width="50" height="50" class="user-avatar"> <div class="user-details"> <div><span class="detail-label">昵称:</span>${Live_info.uname}</div> <div><span class="detail-label">UID:</span>${Live_info.uid}</div> <div><span class="detail-label">直播消费:</span>${Live_info.cost}</div> <div><span class="detail-label">会员等级:</span>${Live_info.vipTypetext}</div> <div><span class="detail-label">主站等级:</span>Lv${Live_info.Blever}</div> <div><span class="detail-label">硬币数量:</span>${Live_info.coin}</div> </div>` } 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) } }