// ==UserScript==
// @name Bilibili直播间挂机助手
// @namespace SeaLoong
// @version 1.1.1
// @description Bilibili直播间自动签到,领瓜子,参加抽奖,完成任务,送礼等
// @author SeaLoong
// @include /https?:\/\/live\.bilibili\.com\/\d+/
// @grant none
// @run-at document-end
// @license MIT License
// ==/UserScript==
(function() {
'use strict';
/* 可修改以下内容用以自定义功能 */
var CONFIG = {
USE_SIGN: true, // 自动签到: true:启用, false:不启用
USE_AWARD: true, // 自动领取瓜子: true:启用, false:不启用
USE_LOTTERY: true, // 自动参加抽奖: true:启用, false:不启用
USE_TASK: true, // 自动完成任务: true:启用, false:不启用
USE_GIFT: true, // 自动送礼物: true:启用, false:不启用
GIFT_CONFIG: { // 若启用自动送礼物,则需要设置以下项
SHORT_ROOMID: 0, // 指定直播间ID(即地址中live.bilibili.com/后面的数字), 设置为0则表示自动检查是否有当前直播间头衔,有则切换并送礼
USE_SILVER: false, // 消耗银瓜子来送礼物: true:允许, false:不允许
ONLY_AFTER_23: true // 只允许在23:00后消耗银瓜子送礼物
}
};
/* 可修改以上内容用以自定义功能 */
var NAME = 'Bilibili-LiveRoom-HangHelper';
var TaskAward_Running = false;
var ToastElement,
ToastCount = 0;
var room_id_list = [];
var lottery_list_last = [],
lottery_check_time = 20;
var gift_list = null;
/*
1:辣条:亲密度+1
3:B坷垃:亲密度+99
4:喵娘:亲密度+52
6:亿元:亲密度+10
7:666:亲密度+?
8:233:亲密度+?
25:小电视:亲密度+12450?
39:节奏风暴:亲密度+1000?
105:火力票:亲密度+20
106:哔哩星:亲密度+20
*/
var Info = {
short_id: null,
uid: null,
ruid: null,
roomid: null,
rnd: null,
area_id: null, // area_v2_id
area_parent_id: null,
old_area_id: null, // areaId
csrf_token: function() {
return getCookie('bili_jct');
},
today_feed: null,
day_limit: null,
silver: null,
gold: null,
mobile_verified: null,
medal_list: null,
medal_target_id: null,
task_list: null,
bag_list: null
};
var DEBUG = function(arg) {
// console.debug(arg);
};
var API = {
ajax: function(settings) {
// DEBUG('API.ajax: settings:' + JSON.stringify(settings));
if (settings.xhrFields) {
$.extend(settings.xhrFields, {
withCredentials: true
});
} else {
settings.xhrFields = {
withCredentials: true
};
}
$.extend(settings, {
url: (settings.url.substr(0, 2) == '//' ? '' : '//api.live.bilibili.com/') + settings.url,
type: settings.type || 'GET',
crossDomain: true,
dataType: settings.dataType || 'json'
});
return $.ajax(settings);
},
ajaxGetCaptchaKey: function() {
return API.ajax({
url: '//www.bilibili.com/plus/widget/ajaxGetCaptchaKey.php?js'
});
},
msg: function(roomid, csrf_token) {
return API.ajax({
type: 'POST',
url: 'ajax/msg',
data: {
roomid: roomid,
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
},
ajaxCapsule: function(id, ts, platform, player_type) {
return API.ajax({
url: 'api/ajaxCapsule'
});
},
player: function(id, ts, platform, player_type) { //获取直播流相关信息
return API.ajax({
url: 'api/player',
data: {
id: id,
ts: ts, // HEX
platform: platform || 'pc',
player_type: player_type || 'web'
},
dataType: 'text'
});
},
create: function(width, height) { // 生成一个验证码
return API.ajax({
url: 'captcha/v1/Captcha/create',
data: {
width: width,
height: height,
_: Date.now()
}
});
},
topList: function(roomid, page, ruid) {
return API.ajax({
url: 'guard/topList',
data: {
roomid: roomid,
page: page,
ruid: ruid
}
});
},
getSuser: function() {
return API.ajax({
url: 'msg/getSuser'
});
},
refresh: function() {
return API.ajax({
url: 'index/refresh?area=all'
});
},
get_ip_addr: function() {
return API.ajax({
url: 'ip_service/v1/ip_service/get_ip_addr'
});
},
ajaxGetMyMedalList: function() {
return API.ajax({
url: '//live.bilibili.com/i/ajaxGetMyMedalList'
});
},
getuserinfo: function() {
return API.ajax({
url: '//live.bilibili.com/user/getuserinfo'
});
},
MyInfo: function() {
return API.ajax({
url: '//space.bilibili.com/ajax/member/MyInfo'
});
},
activity: {
mobileActivity: function() {
return API.ajax({
url: 'activity/v1/Common/mobileActivity'
});
},
roomInfo: function(roomid, ruid) {
return API.ajax({
url: 'activity/v1/Common/roomInfo',
data: {
roomid: roomid,
ruid: ruid
}
});
},
welcomeInfo: function(roomid) {
return API.ajax({
url: 'activity/v1/Common/welcomeInfo?roomid=' + roomid
});
},
master_invite_task: function() {
return API.ajax({
url: 'activity/v1/invite/master_invite_task'
});
},
check: function(roomid) {
return API.ajax({
url: 'activity/v1/Raffle/check?roomid=' + roomid
});
},
join: function(roomid, raffleId) {
return API.ajax({
url: 'activity/v1/Raffle/join',
data: {
roomid: roomid,
raffleId: raffleId
}
});
},
notice: function(roomid, raffleId) {
return API.ajax({
url: 'activity/v1/Raffle/notice',
data: {
roomid: roomid,
raffleId: raffleId
}
});
},
master_limit_tasks: function() {
return API.ajax({
url: 'activity/v1/task/master_limit_tasks'
});
},
receive_award: function(task_id, csrf_token) {
return API.ajax({
type: 'POST',
url: 'activity/v1/task/receive_award',
data: {
task_id: task_id,
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
},
user_limit_tasks: function() {
return API.ajax({
url: 'activity/v1/task/user_limit_tasks'
});
}
},
feed: {
getList: function(page, page_size) {
return API.ajax({
url: 'feed/v1/feed/getList',
data: {
page: page,
page_size: page_size,
_: Date.now()
}
});
},
heartBeat: function(_cb) {
return API.ajax({
url: 'feed/v1/feed/heartBeat',
data: {
_cb: _cb
}
});
},
GetUserFc: function(follow) { // follow: 主播uid==ruid
return API.ajax({
url: 'feed/v1/Feed/GetUserFc?follow=' + follow
});
},
IsUserFollow: function(follow) { // follow: 主播uid==ruid
return API.ajax({
url: 'feed/v1/Feed/IsUserFollow?follow=' + follow
});
},
},
feed_svr: {
notice: function(csrf_token) {
return API.ajax({
type: 'POST',
url: 'feed_svr/v1/feed_svr/notice',
data: {
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
},
my: function(page_size, csrf_token, live_status, type, offset) {
return API.ajax({
type: 'POST',
url: 'feed_svr/v1/feed_svr/my',
data: {
live_status: live_status || 0,
type: type || 0,
page_size: page_size,
offset: offset || 0,
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
}
},
FreeSilver: {
getSurplus: function() {
return API.ajax({
url: 'FreeSilver/getSurplus'
});
},
getAward: function(time_start, end_time, captcha) {
return API.ajax({
url: 'FreeSilver/getAward',
data: {
time_start: time_start,
end_time: end_time,
captcha: captcha
}
});
},
getCurrentTask: function() {
return API.ajax({
url: 'FreeSilver/getCurrentTask'
});
},
getCaptcha: function(ts, callback) {
getBlobDataURL("//api.live.bilibili.com/freeSilver/getCaptcha?ts=" + ts, callback);
}
},
gift: {
bag_list: function() {
return API.ajax({
url: 'gift/v2/gift/bag_list'
});
},
send: function(uid, gift_id, ruid, gift_num, coin_type, biz_id, rnd, csrf_token, platform, biz_code, storm_beat_id) {
return API.ajax({
type: 'POST',
url: 'gift/v2/gift/send',
data: {
uid: uid,
gift_id: gift_id,
ruid: ruid,
gift_num: gift_num,
coin_type: coin_type,
bag_id: 0,
platform: platform || 'pc',
biz_code: biz_code || 'live',
biz_id: biz_id, //roomid
rnd: rnd,
storm_beat_id: storm_beat_id || 0,
// metadata: metadata,
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
},
bag_send: function(uid, gift_id, ruid, gift_num, bag_id, biz_id, rnd, csrf_token, platform, biz_code, storm_beat_id) {
return API.ajax({
type: 'POST',
url: 'gift/v2/live/bag_send',
data: {
uid: uid,
gift_id: gift_id,
ruid: ruid,
gift_num: gift_num,
bag_id: bag_id,
platform: platform || 'pc',
biz_code: biz_code || 'live',
biz_id: biz_id, //roomid
rnd: rnd,
storm_beat_id: storm_beat_id || 0,
// metadata: metadata,
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
},
heart_gift_receive: function(roomid, area_v2_id) {
return API.ajax({
url: 'gift/v2/live/heart_gift_receive',
data: {
roomid: roomid,
area_v2_id: area_v2_id
}
});
},
heart_gift_status: function(roomid, area_v2_id) {
return API.ajax({
url: 'gift/v2/live/heart_gift_status',
data: {
roomid: roomid,
area_v2_id: area_v2_id
}
});
},
receive_daily_bag: function() {
return API.ajax({
url: 'gift/v2/live/receive_daily_bag'
});
},
room_gift_list: function(roomid, area_v2_id) {
return API.ajax({
url: 'gift/v2/live/room_gift_list',
data: {
roomid: roomid,
area_v2_id: area_v2_id
}
});
},
smalltv: {
check: function(roomid) {
return API.ajax({
url: 'gift/v2/smalltv/check',
data: {
roomid: roomid
}
});
},
join: function(roomid, raffleId) {
return API.ajax({
url: 'gift/v2/smalltv/join',
data: {
roomid: roomid,
raffleId: raffleId
}
});
},
notice: function(roomid, raffleId) {
return API.ajax({
url: 'gift/v2/smalltv/notice',
data: {
roomid: roomid,
raffleId: raffleId
}
});
}
}
},
giftBag: {
getSendGift: function() {
return API.ajax({
url: 'giftBag/getSendGift'
});
},
sendDaily: function() {
return API.ajax({
url: 'giftBag/sendDaily'
});
}
},
i: {
ajaxGetAchieve: function(page, pageSize, type, status, category, keywords) {
return API.ajax({
url: 'i/api/ajaxGetAchieve',
data: {
type: type || 'normal', // or'legend'
status: status || 0,
category: category || 'all',
keywords: keywords,
page: page,
pageSize: pageSize || 6
}
});
},
ajaxCancelWear: function() {
return API.ajax({
url: 'i/ajaxCancelWear'
});
},
ajaxWearFansMedal: function(medal_id) {
return API.ajax({
url: 'i/ajaxWearFansMedal?medal_id=' + medal_id
});
},
following: function(page, pageSize) {
return API.ajax({
url: 'i/api/following',
data: {
page: page,
pageSize: pageSize
}
});
},
guard: function(page, pageSize) {
return API.ajax({
url: 'i/api/guard',
data: {
page: page,
pageSize: pageSize
}
});
},
liveinfo: function() {
return API.ajax({
url: 'i/api/liveinfo'
});
},
medal: function(page, pageSize) {
return API.ajax({
url: 'i/api/medal',
data: {
page: page,
pageSize: pageSize
}
});
},
operation: function(page) {
return API.ajax({
url: 'i/api/operation?page=' + page
});
},
taskInfo: function() {
return API.ajax({
url: 'i/api/taskInfo'
});
}
},
live: {
getRoomKanBanModel: function(roomid) {
return API.ajax({
url: 'live/getRoomKanBanModel?roomid' + roomid
});
},
rankTab: function(roomid) {
return API.ajax({
url: 'live/rankTab?roomid=' + roomid
});
},
roomAdList: function() {
return API.ajax({
url: 'live/roomAdList'
});
}
},
live_user: {
get_anchor_in_room: function(roomid) {
return API.ajax({
url: 'live_user/v1/UserInfo/get_anchor_in_room?roomid=' + roomid
});
},
get_info_in_room: function(roomid) {
return API.ajax({
url: 'live_user/v1/UserInfo/get_info_in_room?roomid=' + roomid
});
},
get_weared_medal: function(uid, target_id, csrf_token, source) {
return API.ajax({
type: 'POST',
url: 'live_user/v1/UserInfo/get_weared_medal',
data: {
source: source || 1,
uid: uid,
target_id: target_id, // roomid
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
}
},
lottery: {
getRoomActivityByRoomid: function(roomid) {
return API.ajax({
url: 'lottery/v1/box/getRoomActivityByRoomid?roomid=' + roomid
});
},
check: function(roomid) {
return API.ajax({
url: 'lottery/v1/Storm/check?roomid=' + roomid
});
},
join: function(id, color, captcha_token, captcha_phrase, csrf_token) { // 加入节奏风暴
return API.ajax({
type: 'POST',
url: 'lottery/v1/Storm/join',
data: {
id: id,
color: color,
captcha_token: captcha_token,
captcha_phrase: captcha_phrase,
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
}
},
rankdb: {
roomInfo: function(ruid, roomid, areaId) {
return API.ajax({
url: 'rankdb/v1/Common/roomInfo',
data: {
ruid: ruid,
roomid: roomid,
areaId: areaId
}
});
}
},
room: {
get_info: function(room_id, from) {
return API.ajax({
url: 'room/v1/Room/get_info',
data: {
room_id: room_id,
from: from || 'room'
}
});
},
playUrl: function(cid, quality, platform) {
return API.ajax({
url: 'room/v1/Room/playUrl',
data: {
cid: cid, // roomid
quality: quality || '0',
platform: platform || 'web'
}
});
},
room_entry_action: function(room_id, csrf_token, platform) {
return API.ajax({
type: 'POST',
url: 'room/v1/Room/room_entry_action',
data: {
room_id: room_id,
platform: platform || 'pc',
csrf_token: typeof csrf_token == 'function' ? csrf_token() : csrf_token
}
});
},
room_init: function(id) {
return API.ajax({
url: 'room/v1/Room/room_init?id=' + id
});
}
},
sign: {
doSign: function() {
return API.ajax({
url: 'sign/doSign'
});
},
GetSignInfo: function() {
return API.ajax({
url: 'sign/GetSignInfo'
});
},
getLastMonthSignDays: function() {
return API.ajax({
url: 'sign/getLastMonthSignDays'
});
}
},
user: {
getWear: function(uid) {
return API.ajax({
url: 'user/v1/user_title/getWear?uid=' + uid
});
},
userOnlineHeart: function() {
return API.ajax({
type: 'POST',
url: 'User/userOnlineHeart'
});
},
getUserInfo: function(ts) { // ms
return API.ajax({
url: 'User/getUserInfo?ts=' + ts
});
}
},
YearWelfare: {
checkFirstCharge: function() {
return API.ajax({
url: 'YearWelfare/checkFirstCharge'
});
},
inviteUserList: function() {
return API.ajax({
url: 'YearWelfare/inviteUserList/1'
});
}
}
};
function ts_s() {
return Math.floor(Date.now() / 1000);
}
function ts_ms() {
return Date.now();
}
function getCookie(name) {
var arr, reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
if ((arr = document.cookie.match(reg))) {
return unescape(arr[2]);
} else {
return null;
}
}
function setCookie(name, value, seconds) {
seconds = seconds || 0;
var expires = '';
if (parseInt(seconds) !== 0) {
var date = new Date();
date.setTime(date.getTime() + (seconds * 1000));
expires = '; expires=' + date.toGMTString();
}
document.cookie = name + '=' + escape(value) + expires + '; path=/';
}
function getBlobDataURL(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.withCredentials = true;
xhr.onload = function() {
var reader = new FileReader();
reader.onload = function() {
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
xhr.send();
}
/*
验证码识别算法来自互联网,作者未知
该算法已被简单修改
*/
function getChar(t) {
if (t.sum <= 50) return '-';
if (t.sum > 120 && t.sum < 135) return '+';
if (t.sum > 155 && t.sum < 162) return 1;
if (t.sum > 189 && t.sum < 195) return 7;
if (t.sum > 228 && t.sum < 237) return 4;
if (t.sum > 250 && t.sum < 260) return 2;
if (t.sum > 286 && t.sum < 296) return 3;
if (t.sum > 303 && t.sum < 313) return 5;
if (t.sum > 335 && t.sum < 342) return 8;
if (t.sum > 343 && t.sum < 350) {
if (t.first > 24 && t.last > 24) return 0;
if (t.first < 24 && t.last > 24) return 9;
if (t.first > 24 && t.last < 24) return 6;
}
}
function calcImg() {
/*
* 1.验证码图片->二维点阵
* 2.二维点阵->横向一维压缩
* 3.分析并计算
*/
var ctx = $('#' + NAME + '_canvas')[0].getContext("2d");
var img = $('#' + NAME + '_img')[0];
ctx.drawImage(img, 0, 0, 120, 40);
var pixels = ctx.getImageData(0, 0, 120, 40).data;
var pix = [];
var i = 0;
var j = 0;
var n = 0;
for (i = 1; i <= 40; i++) {
pix[i] = [];
for (j = 1; j <= 120; j++) {
var c = 1;
if (pixels[n] - (-pixels[n + 1]) - (-pixels[n + 2]) > 200) { //判断RGB之和是否大于200(接近白色)
c = 0;
}
n += 4;
pix[i][j] = c;
}
}
//二维点阵pix[40][120]
var line = [];
line[0] = 0;
for (i = 1; i <= 120; i++) {
line[i] = 0;
for (j = 1; j <= 40; j++) {
line[i] += pix[j][i];
}
}
//一维line[120]
var temp = [];
n = 0;
for (i = 1; i <= 120; i++) {
if (line[i] > 0 && line[i - 1] === 0) {
n++;
temp[n] = {};
temp[n].first = line[i];
temp[n].sum = 0;
}
if (line[i] > 0) {
temp[n].sum += line[i];
}
if (line[i - 1] > 0 && line[i] === 0) {
temp[n].last = line[i - 1];
}
}
if (n == 4) {
var result = 0;
var a = getChar(temp[1]) * 10 - (-getChar(temp[2]));
var b = getChar(temp[4]);
if (getChar(temp[3]) == '+') {
result = a - (-b);
} else {
result = a - b;
}
DEBUG('TaskAward: calcImg: 识别验证码: ' + getChar(temp[1]) + getChar(temp[2]) + ' ' + getChar(temp[3]) + ' ' + getChar(temp[4]) + ' = ' + result);
return result;
} else {
return null;
}
}
var liveQuickLogin = function() {
if (!getCookie('DedeUserID') && !getCookie('LIVE_LOGIN_DATA')) {
try {
if (!window.biliQuickLogin) {
$.getScript('https://static-s.bilibili.com/account/bili_quick_login.js', function(response, status) {
if (status === 'success') login();
});
} else {
login();
}
} catch (tryErr) {
throw new Error(tryErr);
}
}
function login() {
if (window.biliQuickLogin) {
window.biliQuickLogin(function() {
window.location.reload();
});
throw 'Bilibili Live: 您还未登陆.';
} else {
throw 'Bilibili Live: 快速登录脚本加载失败.';
}
}
};
function giftIDtoFeed(gift_id) {
for (var i = gift_list.length - 1; i >= 0; i--) {
if (gift_list[i].id === gift_id) {
return Math.floor(gift_list[i].price / 100);
}
}
return 0;
}
function toast(t, e, n, r) {
if ('boolean' == typeof n) n = 'info';
var o = document.createDocumentFragment(),
a = document.createElement('div');
if ('success' !== (n = n || 'info') && 'caution' !== n && 'error' !== n && 'info' !== n)
throw new Error(i + ' 在使用 Link Toast 时必须指定正确的类型: success || caution || error || info');
if (a.innerHTML = '<span class="toast-text">' + e + '</span>',
a.className = 'link-toast ' + n + ' ' + (r ? 'fixed' : ''), !t.className && !t.attributes)
throw new Error(i + ' 传入 element 不是有效节点.');
var c = t.getBoundingClientRect(),
s = c.left,
u = c.top,
l = c.width,
f = c.height,
p = document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft;
a.style.left = s + l + p + 'px';
var d = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop;
a.style.top = u + f + d + ToastCount * 40 + 'px';
setTimeout((function() {
a.className += ' out';
setTimeout((function() { // TODO
ToastCount = 0;
a.parentNode.removeChild(a);
}), 350);
}), 3e3);
o.appendChild(a);
document.body.appendChild(o);
var h = document.body.offsetWidth,
v = a.getBoundingClientRect().left,
m = a.offsetWidth;
if (h - m - v < 0) a.style.left = h - m - 10 + p + 'px';
ToastCount += 1;
}
/*
window.BilibiliLive.ANCHOR_UID
window.BilibiliLive.COLORFUL_LOGGER
window.BilibiliLive.INIT_TIME
window.BilibiliLive.RND === window.DANMU_RND
window.BilibiliLive.ROOMID
window.BilibiliLive.SHORT_ROOMID
window.BilibiliLive.UID
window.captcha_key
window.Yb
*/
function Init() {
// liveQuickLogin();
var _interval = setInterval(function() {
if (window.BilibiliLive) {
DEBUG('Init: BilibiliLive: ' + JSON.stringify(window.BilibiliLive));
if (parseInt(window.BilibiliLive.UID) !== 0) {
Info.short_id = window.BilibiliLive.SHORT_ROOMID;
Info.uid = window.BilibiliLive.UID;
Info.roomid = window.BilibiliLive.ROOMID;
Info.ruid = window.BilibiliLive.ANCHOR_UID;
Info.rnd = window.BilibiliLive.RND;
// API.MyInfo().done(function(response) {
// if (response.status === true) {
// Info.mobile_verified = response.data.mobile_verified;
// }
// });
room_id_list[Info.short_id] = Info.roomid;
if (CONFIG.USE_AWARD) {
var _interval_treasure_box = setInterval(function() {
var _treasure_box = $('div.treasure-box.p-relative[id!=' + NAME + '_div_box]');
if (_treasure_box[0]) {
_treasure_box.attr('style', "display:none");
var div = $('<div id="' + NAME + '_div_box" class="treasure-box p-relative"></div>');
var b_box = $('<div id="' + NAME + '_div_b_box"class="t-center b-box none-select">自动<br>领取中</div>');
b_box.css({
'max-width': '40px',
'padding': '2px 3px',
'margin-top': '3px',
'font-size': '12px',
'color': '#fff',
'background-color': 'rgba(0,0,0,.5)',
'border-radius': '10px'
});
var img = $('<img id="' + NAME + '_img" style="display:none">');
var canvas = $('<canvas id="' + NAME + '_canvas" style="display:none" height="40" width="120"></canvas>');
div.append(b_box);
div.append(img);
div.append(canvas);
$('div.vertical-middle.dp-table.section.left-part div.dp-table-cell.v-middle').append(div);
clearInterval(_interval_treasure_box);
}
}, 200);
}
if (CONFIG.USE_GIFT && (CONFIG.GIFT_CONFIG.SHORT_ROOMID === 0 || CONFIG.GIFT_CONFIG.SHORT_ROOMID == Info.short_id)) {
API.live_user.get_weared_medal(Info.uid, Info.roomid, Info.csrf_token).done(function(response) {
DEBUG('Init: get_weared_medal: ' + JSON.stringify(response));