// ==UserScript== // @name Bilibili直播间挂机助手3 // @namespace SeaLoong // @version 3.1.11 // @description B站直播间挂机用: 签到,领瓜子,移动端心跳,瓜子换硬币等 // @author SeaLoong // @homepageURL https://github.com/SeaLoong/BLRHH // @supportURL https://github.com/SeaLoong/BLRHH/issues // @updateURL https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/installer.github.user.js // @include /^https?:\/\/live\.bilibili\.com\/(blanc\/)?\d+.*$/ // @license MIT License // @resource EULA https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/html/eula.html // @resource NOTICE https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/html/notice.html // @resource tfjs https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.3.0/dist/tf.min.js // @resource Sign https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/sign.js // @resource Exchange https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/exchange.js // @resource TreasureBox https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/treasurebox.js // @resource TreasureBox/worker https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/treasurebox/worker.js // @resource Heartbeat https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/heartbeat.js // @resource DailyReward https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/dailyreward.js // @resource AvoidDetection https://raw.githubusercontent.com/SeaLoong/BLRHH/dist/modules/avoiddetection.js // @resource jquery https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js // @resource lodash https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.19/lodash.min.js // @resource toastr https://cdn.bootcdn.net/ajax/libs/toastr.js/2.1.4/toastr.min.js // @resource spark-md5 https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.0/spark-md5.min.js // @resource Toast https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/toast.js // @resource Util https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/util.js // @resource Dialog https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/dialog.js // @resource Page https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/page.js // @resource Logger https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/logger.js // @resource Config https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/config.js // @resource Request https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/request.js // @resource Worker https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/worker.js // @resource Worker/env https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/worker/env.js // @resource Worker/channel https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/worker/channel.js // @resource AppToken https://raw.githubusercontent.com/SeaLoong/BLUL/dist/modules/apptoken.js // @connect bilibili.com // @connect * // @grant unsafeWindow // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM.listValues // @grant GM.getResourceUrl // @grant GM.xmlHttpRequest // @grant GM.addStyle // @grant GM.getResourceText // @grant GM.registerMenuCommand // @grant GM.unregisterMenuCommand // @run-at document-start // @incompatible chrome 不支持内核低于80的版本 // @incompatible firefox 不支持内核低于72的版本 // ==/UserScript== 'use strict'; var BLUL; (function () { try { ((0, eval)('(null ?? 1) && (({a: 1})?.a)')); // eslint-disable-line no-eval } catch (error) { console.error('[BLUL]不支持当前浏览器,请使用内核不低于Chromium80或Firefox72的浏览器', error); return; } if (typeof unsafeWindow !== 'undefined') { const safeWindow = window; window = unsafeWindow; // eslint-disable-line no-global-assign window.safeWindow = safeWindow; } if (window.top.BLUL) { // console.warn('[BLUL]检测到BLUL已存在,本脚本将不再初始化BLUL,脚本行为可能会出现异常'); BLUL = new Proxy(window.top.BLUL, { has: function (target, property) { if (property === 'isChild') return true; return property in target; }, get: function (target, property, receiver) { if (property === 'isChild') return true; return target[property]; } }); return; } BLUL = { debug: () => {}, NAME: 'BLUL', GM: GM, ENVIRONMENT: GM.info.scriptHandler, ENVIRONMENT_VERSION: GM.info.version, VERSION: GM.info.script.version, RESOURCE: {}, BLUL_MODULE_NAMES: ['Toast', 'Util', 'Dialog', 'Page', 'Logger', 'Config', 'Request', 'Worker', 'Worker/env', 'Worker/channel', 'AppToken'], INFO: {}, TRACKED_LISTENERS: {} }; BLUL.lazyFn = function (...args) { let object = BLUL; let name; let promise = false; if (args.length >= 3) [object, name, promise] = args; else if (args.length === 2) [object, name] = args; else [name] = args; const list = []; let fn; Object.defineProperty(object, name, { configurable: true, get: () => fn ?? ((...args) => promise ? new Promise(resolve => list.push({ args, resolve })) : list.push({ args })), set: f => { fn = f; if (fn instanceof Function) { for (const { resolve, args } of list) { const v = fn.apply(object, args); if (resolve) resolve(v); } } } }); }; BLUL.getResourceUrl = async (name) => await GM.getResourceUrl(name) ?? BLUL.RESOURCE[name] ?? ((BLUL.BLUL_MODULE_NAMES.includes(name) ? BLUL.RESOURCE.BLULBase : BLUL.RESOURCE.base) + '/modules/' + name.toLowerCase() + '.js'); BLUL.getResourceText = async (name) => { let ret = await GM.getResourceText(name); if (ret === undefined || ret === null) { ret = (await window.fetch(await BLUL.getResourceUrl(name))).text(); } return ret; }; BLUL.createImportModuleFunc = function (context, keepContext = false) { /** * 如果需要上下文, Module 应当返回(export default)一个 Function/AsyncFunction, 其参数表示上下文, 且第一个参数是importModule * 在不需要上下文的情况下可以返回任意 */ const importUrlMap = new Map(); async function importModule (name, reImport = false) { try { if (!reImport && importUrlMap.has(name)) return importUrlMap.get(name); const url = await BLUL.getResourceUrl(name); try { let ret = await import(url); ret = ret?.default ?? ret; if (ret instanceof Function) ret = ret.apply(window, context); ret = await ret; importUrlMap.set(name, ret); return ret; } catch (error) { (BLUL.Logger ?? console).error(`模块 ${name} 导入失败,尝试使用script标签加载`, error); return new Promise((resolve, reject) => { const elem = document.createElement('script'); elem.onerror = reject; elem.onload = () => { importUrlMap.set(name, undefined); resolve(); }; document.body.appendChild(elem); elem.src = url; }); } } catch (error) { (BLUL.Logger ?? console).error(`使用script标签加载模块 ${name} 失败`, error); } } if (!keepContext) context.unshift(importModule); return importModule; }; BLUL.createImportModuleFromCodeFunc = function (context, keepContext = false) { /** * 如果需要上下文, Module 应当返回(const exports = )一个 Function/AsyncFunction, 其参数表示上下文, 且第一个参数是importModule * 在不需要上下文的情况下可以返回任意 * 这种方式不兼容 export 语法 */ const importCodeMap = new Map(); async function importModule (code, reImport = false) { try { if (!reImport && importCodeMap.has(code)) return importCodeMap.get(code); code = (await BLUL.getResourceText(code) ?? code); code = code.replace('export default', 'const exports =') + ';\nif (typeof exports !== "undefined") return exports;'; const fn = Function(code); // eslint-disable-line no-new-func let ret = fn.apply(window, context); if (ret instanceof Function) ret = ret.apply(window, context); ret = await ret; importCodeMap.set(code, ret); return ret; } catch (error) { (BLUL.Logger ?? console).error('模块导入失败', error, code); } } if (!keepContext) context.unshift(importModule); return importModule; }; BLUL.lazyFn('__addResourceConfig'); BLUL.lazyFn('setBase'); BLUL.lazyFn('importModule'); BLUL.lazyFn('onupgrade'); BLUL.lazyFn('onpreinit'); BLUL.lazyFn('oninit'); BLUL.lazyFn('onpostinit'); BLUL.lazyFn('onrun'); BLUL.addResource = async function (name, urls, displayName) { if (BLUL.RESOURCE[name] !== undefined) return; BLUL.RESOURCE[name] = urls instanceof Array ? urls[0] : urls; BLUL.__addResourceConfig(name, urls, displayName); if (await GM.getValue('resetResource')) return; const resource = (await GM.getValue('config'))?.resource; if (!resource) return; if (resource[name]?.__VALUE__) BLUL.RESOURCE[name] = resource[name]?.__VALUE__; }; const listenerFilters = {}; BLUL.addListenerFilter = (type, f) => { if (!(listenerFilters[type] instanceof Array)) listenerFilters[type] = []; listenerFilters[type].push(f); }; BLUL.removeListenerFilter = (type, f) => { if (listenerFilters[type] instanceof Array) { for (let i = 0; i < listenerFilters[type].length; i++) { if (listenerFilters[type][i] === f) { listenerFilters[type].splice(i, 1); break; } } } }; const addEventListener = EventTarget.prototype.addEventListener; EventTarget.prototype.addEventListener = function (type, listener, ...args) { if (listenerFilters[type] instanceof Array) { let allow = true; for (const f of listenerFilters[type]) { allow &= f.call(this, type, listener, ...args); if (!allow) return; } } if (!(BLUL.TRACKED_LISTENERS[type] instanceof Array)) BLUL.TRACKED_LISTENERS[type] = []; BLUL.TRACKED_LISTENERS[type].push({ target: this, listener, args }); addEventListener.call(this, type, listener, ...args); }; const removeEventListener = EventTarget.prototype.removeEventListener; EventTarget.prototype.removeEventListener = function (type, listener, ...args) { if (BLUL.TRACKED_LISTENERS[type] instanceof Array) { for (let i = 0; i < BLUL.TRACKED_LISTENERS[type].length; i++) { const o = BLUL.TRACKED_LISTENERS[type][i]; if (o.target === this && o.listener === listener) { BLUL.TRACKED_LISTENERS[type].splice(i, 1); break; } } } removeEventListener.call(this, type, listener, ...args); }; BLUL.removeAllListener = (type, rejectType = true) => { if (rejectType) { listenerFilters[type] = [t => type !== t]; } for (const o of BLUL.TRACKED_LISTENERS[type]) { removeEventListener.call(o.target, type, o.listener, ...o.args); } BLUL.TRACKED_LISTENERS[type] = null; }; BLUL.recover = () => { EventTarget.prototype.addEventListener = addEventListener; EventTarget.prototype.removeEventListener = removeEventListener; }; let hasRun = false; BLUL.run = async (options) => { if (hasRun) return 2; hasRun = true; const { debug, slient, unique, login, EULA, EULA_VERSION, NOTICE } = options ?? {}; if (debug) { BLUL.debug = console.debug; BLUL.debug(BLUL); } // 等待load事件 await new Promise(resolve => { window.addEventListener('load', resolve); }); // 特殊直播间页面,如 6 55 76 if (!document.getElementById('aside-area-vm')) return 1; const resetResourceMenuCmdId = await GM.registerMenuCommand?.('恢复默认源', async () => { await GM.setValue('resetResource', true); window.location.reload(true); }); const unregisterMenuCmd = async () => { BLUL.debug('unregisterMenuCmd'); if (await GM.getValue('resetResource')) { await BLUL.Config.reset('resource', true); await GM.deleteValue('resetResource'); } await GM.unregisterMenuCommand?.(resetResourceMenuCmdId); // eslint-disable-line no-unused-expressions }; await BLUL.addResource('BLULBase', ['https://cdn.jsdelivr.net/gh/SeaLoong/BLUL@dist'], 'BLUL根目录'); await BLUL.addResource('lodash', ['https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.19/lodash.min.js', 'https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js']); await BLUL.addResource('toastr', ['https://cdn.bootcdn.net/ajax/libs/toastr.js/2.1.4/toastr.min.js', 'https://cdn.jsdelivr.net/npm/toastr@2.1.4/toastr.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js']); await BLUL.addResource('jquery', ['https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js', 'https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js', 'https://code.jquery.com/jquery-3.5.1.min.js']); BLUL.onpostinit(unregisterMenuCmd); const importModule = BLUL.createImportModuleFromCodeFunc([BLUL, GM]); await importModule('jquery'); await importModule('Toast'); if (unique) { const mark = 'running'; // 检查重复运行 if (await (async () => { const running = parseInt(await GM.getValue(mark) ?? 0); const ts = Date.now(); return (ts - running >= 0 && ts - running <= 5e3); })()) { if (!slient) { BLUL.Toast.warn('已经有其他页面正在运行脚本了哟~'); } await unregisterMenuCmd(); return 2; } // 标记运行中 await GM.setValue(mark, Date.now()); const uniqueCheckInterval = setInterval(async () => { await GM.setValue(mark, Date.now()); }, 4e3); window.addEventListener('unload', async () => { clearInterval(uniqueCheckInterval); await GM.deleteValue(mark); }); } await importModule('lodash'); /* global _ */ const Util = BLUL.Util = await importModule('Util'); await Util.callUntilTrue(() => window.BilibiliLive?.ROOMID && window.BilibiliLive?.ANCHOR_UID && window.BilibiliLive?.SHORT_ROOMID && window.__statisObserver); if (login) { BLUL.INFO.CSRF = Util.getCookie('bili_jct'); if (!BLUL.INFO.CSRF) { if (!slient) { BLUL.Toast.warn('你还没有登录呢~'); } await unregisterMenuCmd(); return 3; } await Util.callUntilTrue(() => window.BilibiliLive?.UID); } await importModule('Dialog'); if (EULA) { if (Util.compareVersion(EULA_VERSION, await GM.getValue('eulaVersion')) > 0) { await GM.setValue('eula', false); } if (!await GM.getValue('eula')) { const dialog = new BLUL.Dialog(await Util.result(EULA), '最终用户许可协议'); dialog.addButton('我同意', () => dialog.close(true)); dialog.addButton('我拒绝', () => dialog.close(false), 1); if (!await dialog.show()) { await unregisterMenuCmd(); return 4; } await GM.setValue('eula', true); await GM.setValue('eulaVersion', EULA_VERSION); } } await importModule('Page'); await importModule('Logger'); await importModule('Config'); await importModule('Request'); await importModule('Worker'); await importModule('AppToken'); BLUL.Config.addItem('resource', '自定义源', false, { tag: 'input', help: '该设置项下的各设置项只在没有设置对应的 @resource 时���效。
此项直接影响脚本的加载,URL不正确或访问速度太慢均可能导致不能正常加载。
需要重置源可点击油猴图标再点击此脚本下的"恢复默认源"来重置。', attribute: { type: 'checkbox' } }); BLUL.__addResourceConfig = (name, urls, displayName = name) => { BLUL.Config.addItem(`resource.${name}`, displayName, BLUL.RESOURCE[name], { tag: 'input', list: urls instanceof Array ? urls : undefined, corrector: v => { const i = v.trim().search(/\/+$/); return i > -1 ? v.substring(0, i) : v; }, attribute: { type: 'url' } }); BLUL.Config.onload(() => { BLUL.RESOURCE[name] = BLUL.Config.get(`resource.${name}`); }); }; BLUL.setBase = _.once(urls => BLUL.addResource('base', urls, '根目录')); BLUL.importModule = importModule; BLUL.INFO.UID = window.BilibiliLive.UID; BLUL.INFO.ROOMID = window.BilibiliLive.ROOMID; BLUL.INFO.ANCHOR_UID = window.BilibiliLive.ANCHOR_UID; BLUL.INFO.SHORT_ROOMID = window.BilibiliLive.SHORT_ROOMID; BLUL.INFO.VISIT_ID = window.__statisObserver.__visitId ?? ''; BLUL.INFO.__NEPTUNE_IS_MY_WAIFU__ = window.__NEPTUNE_IS_MY_WAIFU__; // 包含B站自己请求返回的一些数据,当然也可以自行请求获取 const callHandler = f => { try { return f.apply(BLUL.load, [BLUL, GM]); } catch (error) { (BLUL.Logger ?? console).error(error); } }; if (Util.compareVersion(BLUL.VERSION, await GM.getValue('version')) > 0) { await GM.setValue('version', BLUL.VERSION); if (NOTICE) { const dialog = new BLUL.Dialog(await Util.result(NOTICE), '更新说明-' + BLUL.VERSION); dialog.addButton('知道了', () => dialog.close()); dialog.show(); } BLUL.onupgrade = callHandler; } BLUL.onpreinit = callHandler; BLUL.oninit = callHandler; BLUL.onpostinit = callHandler; BLUL.onrun = callHandler; window.top[BLUL.NAME] = window.top.BLUL = BLUL; return 0; }; })(); /* global BLUL */ 'use strict'; (async () => { const EULA = await GM.getResourceText('EULA'); const NOTICE = await GM.getResourceText('NOTICE'); BLUL.NAME = 'BLRHH'; const result = await BLUL.run({ debug: await GM.getValue('debug'), slient: false, unique: true, login: true, EULA: EULA, EULA_VERSION: EULA.match(/\[v(.+?)\]/)[1], NOTICE: NOTICE }); switch (result) { case 0: break; case 1: break; case 2: break; case 3: (BLUL.Logger ?? console).warn('脚本运行需要登录,当前未登录'); break; case 4: (BLUL.Logger ?? console).warn('未同意EULA,脚本将不会运行'); break; } if (result !== 0) { BLUL.recover(); return; } BLUL.Logger.info('脚本信息', `运行环境: ${BLUL.ENVIRONMENT} ${BLUL.ENVIRONMENT_VERSION}`, `版本: ${BLUL.VERSION}`); const { importModule } = BLUL; try { const r = await BLUL.Request.fetch('https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByUser?room_id=' + BLUL.INFO.ROOMID); const obj = await r.json(); BLUL.INFO.InfoByUser = obj.data; if (obj.code !== 0) { BLUL.Logger.warn(obj.message); } /* eslint-disable camelcase */ const color = Number(BLUL.INFO.InfoByUser?.property?.danmu?.color).toString(16).toUpperCase(); BLUL.Logger.info('用户信息', `uid: ${BLUL.INFO.UID} 用户名: ${BLUL.INFO.InfoByUser?.info?.uname}`, `手机绑定: ${BLUL.INFO.InfoByUser?.info?.mobile_verify ? '是' : '否'} 实名认证: ${BLUL.INFO.InfoByUser?.info?.identification ? '是' : '否'}`, `UL: ${BLUL.INFO.InfoByUser?.user_level?.level} 金瓜子: ${BLUL.INFO.InfoByUser?.wallet?.gold} 银瓜子: ${BLUL.INFO.InfoByUser?.wallet?.silver}`, `弹幕模式: ${BLUL.INFO.InfoByUser?.property?.danmu?.mode} 弹幕颜色: ${color} 弹幕长度: ${BLUL.INFO.InfoByUser?.property?.danmu?.length}`, `房间id: ${BLUL.INFO.ROOMID} 短id: ${BLUL.INFO.SHORT_ROOMID} 主播uid: ${BLUL.INFO.ANCHOR_UID}`); /* eslint-enable camelcase */ } catch (error) { BLUL.Logger.error('初始化用户信息失败', error); } BLUL.setBase('https://cdn.jsdelivr.net/gh/SeaLoong/BLRHH@dist'); await importModule('Sign'); await importModule('Exchange'); await importModule('TreasureBox'); await importModule('Heartbeat'); await importModule('DailyReward'); await importModule('AvoidDetection'); })();