// ==UserScript== // @name BilibiliExp // @namespace BilibiliExp // @match *://www.bilibili.com/video/* // @match *://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png* // @version 1.4.0 // @author Dreace // @license GPL-3.0 // @description B 站经验助手,自动投币视频、模拟移动端分享、经验获取统计、升级时间估计 // @grant GM.xmlHttpRequest // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @grant unsafeWindow // @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js // @require https://cdn.bootcss.com/blueimp-md5/1.1.0/js/md5.min.js // @downloadURL https://update.greasyfork.icu/scripts/393096/BilibiliExp.user.js // @updateURL https://update.greasyfork.icu/scripts/393096/BilibiliExp.meta.js // ==/UserScript== "use strict"; if ( location.href.match( "www.mcbbs.net/template/mcbbs/image/special_photo_bg.png" ) && location.href.match("access_key") && window.opener ) { window.stop(); document.children[0].innerHTML = 'BilibiliExp - 获取 Access Key正在跳转……'; window.opener.postMessage("get_access_key: " + location.href, "*"); return; } var coinUrl = "https://api.bilibili.com/x/web-interface/nav?build=0&mobi_app=web"; var addCoinUrl = "https://api.bilibili.com/x/web-interface/coin/add"; var shareUrl = "https://app.bilibili.com/x/v2/view/share/complete"; var shareUrlPre = "https://app.bilibili.com/x/v2/view/share/click"; var rewardUrl = "https://account.bilibili.com/home/reward"; var aid = null; var expToday = 0; var bili_jct = getCookie("bili_jct"); try { aid = unsafeWindow.vd ? unsafeWindow.vd.aid : unsafeWindow.aid; } catch (error) { console.error("[BilibiliExp] aid 获取失败"); return; } var access_key = GM.getValue("access_key"); if (access_key) { checkKeyStatus(access_key); } else { getKey(); } var first = true; (function (open) { XMLHttpRequest.prototype.open = function () { this.addEventListener( "readystatechange", function () { if ( this.responseURL.indexOf("api.bilibili.com/x/v2/dm/web/view") >= 0 && first ) { first = false; main(); } }, false ); open.apply(this, arguments); }; })(XMLHttpRequest.prototype.open); async function main() { while (document.querySelector(".dm").innerHTML.indexOf("-") !== -1) { await wait(1000); } if (!aid) { return; } var res = await gmAjax({ url: rewardUrl, method: "GET", }); if (res.code === 0) { if (res.data.level_info.current_level === 6) { console.log("[BilibiliExp] 已达到六级"); injectHTML(); return; } expToday = 50 - res.data.coins_av; // 暂时移除 if (false) { var shareData = { access_key: access_key.key, actionKey: "appkey", aid: aid, build: "10300", device: "phone", epid: "", from: "64", mobi_app: "iphone", platform: "ios", s_locale: "zh-Hans_CN", share_channel: "wechat", share_trace_id: md5(new Date()), type: "av", statistics: "statistics=%7B%22appId%22%3A1%2C%22version%22%3A%226.10.0%22%2C%22abtest%22%3A%22%22%2C%22platform%22%3A1%7D", }; var signed = get_sign(shareData, "c2ed53a74eeefe3cf99fbd01d8c9c375"); res = await gmAjax({ method: "POST", url: shareUrlPre, data: signed.data + "&sign=" + signed.sign, headers: { "Content-Type": "application/x-www-form-urlencoded" }, }); res = await gmAjax({ method: "POST", url: shareUrl, data: signed.data + "&sign=" + signed.sign, headers: { "Content-Type": "application/x-www-form-urlencoded", }, }); console.log("[BilibiliExp] " + res.data.toast); } } else { console.error("[BilibiliExp] 等级信息获取失败"); return; } res = await biliAjax({ url: coinUrl, type: "GET", dataType: "json", }); var totalCoin = res.data.money; console.log("[BilibiliExp] 当前硬币 " + totalCoin + " 个"); if (totalCoin < 50) { console.log("[BilibiliExp] 硬币小于 50,暂不投币"); return; } else { if (expToday === 0) { console.log("[BilibiliExp] 今日已获取全部经验"); return; } } console.log("[BilibiliExp] 准备投币"); res = await addCoin(); if (res && res.code === 0) { console.log("[BilibiliExp] 投了一个币"); expToday -= 10; } if (expToday > 0) { res = await addCoin(); if (res && res.code === 0) { console.log("[BilibiliExp] 投了一个币"); expToday -= 10; } } await wait(3000); injectHTML(); } async function injectHTML() { var res = await gmAjax({ url: rewardUrl, method: "GET", }); if (res.code === 0) { var rewardInfo = res.data; var link = document.createElement("link"); link.rel = "stylesheet"; link.href = "//at.alicdn.com/t/font_1537779_4srood2g1uk.css"; document.body.appendChild(link); var style = "color: inherit;display: inline-block;line-height: 1;"; var spansData = []; var total = 0; total += rewardInfo.login ? 5 : 0; spansData.push({ ok: rewardInfo.login, name: "每日登录", text: (rewardInfo.login ? 5 : 0) + "/5", className: "icon-login", }); total += rewardInfo.share_av ? 5 : 0; spansData.push({ ok: rewardInfo.share_av, name: "分享视频", text: (rewardInfo.share_av ? 5 : 0) + "/5", className: "icon-share", }); total += rewardInfo.watch_av ? 5 : 0; spansData.push({ ok: rewardInfo.watch_av, name: "观看视频", text: (rewardInfo.watch_av ? 5 : 0) + "/5", className: "icon-play", }); total += rewardInfo.coins_av; spansData.push({ ok: rewardInfo.coins_av === 50, name: "视频投币", text: rewardInfo.coins_av + "/50", className: "icon-coin", }); spansData.push({ ok: total === 65, name: "总计", text: total + "/65", className: "icon-total", }); if (res.data.level_info.current_level === 6) { spansData.push({ ok: true, name: "一个成熟的六级大佬", text: "六级辣", className: "icon-day", }); } else { spansData.push({ ok: false, name: "最快到 " + (rewardInfo.level_info.current_level + 1) + " 级剩余天数", text: Math.ceil( (rewardInfo.level_info.next_exp - rewardInfo.level_info.current_exp) / 65 ) + " 天", className: "icon-day", }); } var bar = document.getElementById("arc_toolbar_report"); bar.style.height = "60px"; var ops = document.createElement("div"); ops.className = "ops"; spansData.forEach(function (item) { var span = document.createElement("span"); if (item.ok) { span.style = "color:rgb(251, 114, 153);"; } else { span.style = "color:rgb(80, 80, 80);"; } span.title = item.name; span.innerHTML = '' + item.text; ops.appendChild(span.cloneNode(true)); }); ops.style.marginTop = "10px"; bar.appendChild(ops); } } function addCoin() { return biliAjax({ url: addCoinUrl, type: "POST", dataType: "json", data: { aid: aid, multiply: "1", select_like: 0, cross_domain: true, csrf: bili_jct, }, }); } function gmAjax(opt) { return new Promise(function (resolve, reject) { GM.xmlHttpRequest({ method: opt.method, url: opt.url, data: opt.data ? opt.data : "", headers: opt.headers ? opt.headers : "", onload: function (response) { var res = JSON.parse(response.responseText); resolve(res); }, onError: function (error) { reject(error); }, }); }); } function wait(n) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(); }, n); }); } function empty() { return new Promise(function (resolve, reject) { resolve(); }); } function request(opt) { return $.ajax(opt); } function createPromise() { return $.Deferred(); } function biliAjax(opt) { var req; opt.xhrFields = { withCredentials: true }; opt.crossDomain = true; var defer = createPromise(); req = request(opt); req.done(function (d) { defer.resolve(d); }); req.fail(function (d) { console.log(d); defer.reject(d); }); return defer; } function get_sign(params, key) { params.appkey = "27eb53fc9058f8c3"; params.ts = Date.now(); var s_keys = []; for (var i in params) { s_keys.push(i); } s_keys.sort(); var data = ""; for (var i = 0; i < s_keys.length; i++) { data += (data ? "&" : "") + s_keys[i] + "=" + params[s_keys[i]]; } var sign = md5(data + key); return { sign: sign, data: data, signedData: data + "&sign=" + sign, }; } function getCookie(cname) { var name = cname + "="; var ca = document.cookie.split(";"); for (var i = 0; i < ca.length; i++) { var c = ca[i].trim(); if (c.indexOf(name) == 0) return c.substring(name.length, c.length); } return ""; } function checkKeyStatus(access_key) { if (Date.now() - access_key.time > 25 * 24 * 3600000) { console.log("[BilibiliExp] access_key 即将过期"); getKey(); } } window.addEventListener("message", function (e) { if (typeof e.data == "string" && e.data.split(":")[0] == "get_access_key") { access_key_window.close(); var url = e.data.split(": ")[1]; var key = url.match(/access_key=([a-f0-9]{32})/); if (key) { var access_key = { key: key[1], time: Date.now(), }; GM.setValue("access_key", access_key); console.log("[BilibiliExp] 成功获取 access_key: " + access_key.key); } } }); function getKey() { var access_key_window = window.open("about:blank"); access_key_window.document.title = "BilibiliExp - 获取 Access Key"; access_key_window.document.body.innerHTML = '[BilibiliExp] 正在获取 Access Key'; window.access_key_window = access_key_window; biliAjax({ url: "https://passport.bilibili.com/login/app/third", type: "GET", dataType: "json", data: { appkey: "27eb53fc9058f8c3", api: "https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png", sign: "04224646d1fea004e79606d3b038c84a", }, }).then(function (res) { if (res.data.has_login) { access_key_window.document.body.innerHTML = '[BilibiliExp] 正在跳转'; access_key_window.location.href = res.data.confirm_uri; } else { access_key_window.close(); console.error("[BilibiliExp] 必须登录 B 站才能获取 access_key"); } }); }