// ==UserScript==
// @name 原神/明日方舟/王者荣耀玩家指示器(可扩展/全平台) - bilibili.com
// @namespace Violentmonkey Scripts
// @match https://*.bilibili.com/*
// @grant none
// @version 1.10
// @author fyb
// @description 2022/9/11 16:25:30
// @license MIT
// ==/UserScript==
(function () {
const setting = {
automatic: 0,//是否开启自动查询,开启后自动在用户名后显示成分,关闭后需要点击用户名后的按钮查询(0为关闭,1为开启)
matchingDynamic: 1, //在动态里查询关键词(0为关闭,1为开启) 可与其他兼容
matchingVideo: 0, //在视频标题简介里匹配关键词(0为关闭,1为开启)可与其他兼容
matchingFollow: 0, //在关注列表里匹配关键词(0为关闭,1为开启)可与其他兼容
matchingFollowPage: 2 //查询关注列表页数,一页50个,最多查询五页(1-5),需先开启matchingFollow
}
const match = [ //匹配规则,name为用户标签,color为显示标签的颜色,keyword为匹配关键词数组,follows为匹配关注列表数组
{
name: '[原神玩家]',
color: "red",
keyword: ['原神', '刻晴', '丘丘人', '雷电将军'],
follows: ['原神']
},
{
name: '[明日方舟玩家]',
color: "orange",
keyword: ['明日方舟'],
follows: ['明日方舟']
},
{
name: '[王者荣耀玩家]',
color: "blue",
keyword: ['王者荣耀'],
follows: ['哔哩哔哩王者荣耀赛事']
}
]
let myCss = `
.userComponentBtn{
display:none;
border:1px solid #fb7299;
color:#fb7299;
cursor:default;
font-size:12px;
line-height:16px;
margin-left:5px;
}
.myCursor{
cursor:pointer;
}
.toHover:hover .userComponentBtn{
display:inline-block;
}
`
let css = document.createElement("style");
css.innerHTML = myCss;
document.body.appendChild(css);
const bili_new = document.getElementsByClassName('comment-m-v1').length+document.getElementsByClassName('item goback').length != 0;
console.log('原神/明日方舟/王者荣耀玩家指示器(可扩展/全平台)插件加载成功')
const bili_dyn_url = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?&host_mid='
const bili_video_url = 'https://api.bilibili.com/x/space/arc/search?mid='
const bili_follow_url = 'https://api.bilibili.com/x/relation/followings?ps=50&pn='
const isOk = new Set()
const isAddBtn = new Set()
const isPlayer = new Set()
const bili_get_comment_list = () => {
if (bili_new) {
let lst = new Set()
for (let c of document.getElementsByClassName('user-name')) {
lst.add(c)
}
for (let c of document.getElementsByClassName('sub-user-name')) {
lst.add(c)
}
return lst
} else {
return document.getElementsByClassName('user')
}
}
const get_pid = (c) => {
if (bili_new) {
return c.dataset['userId']
} else {
return c.children[0]['href'].replace(/[^\d]/g, "")
}
}
const addTag = (c, m) => {
let toAppend = document.createElement("DIV");
toAppend.style.color = m['color'];
toAppend.style.display = 'inline-block'
toAppend.innerHTML = m['name'];
if (bili_new) {
c.append(toAppend);
} else {
c.children[0].append(toAppend);
}
}
const toMatchAll = (json, c) => {
if (!isPlayer.has(c)) {
match.forEach(m => {
for (let i = 0; i < m['keyword'].length; i++) {
if (json.includes(m['keyword'][i])) {
addTag(c, m)
isPlayer.add(c)
break;
}
}
})
}
}
const toMatchFollow = (json, c) => {
if (!isPlayer.has(c)) {
match.forEach(m => {
for (let i = 0; i < json['data']['list'].length; i++) {
let a = m['follows'].filter((v) => json['data']['list'][i]['uname'] == v)
if (a.length != 0) {
addTag(c, m)
isPlayer.add(c)
break;
}
}
})
}
}
const config = {
attributes: true,
childList: true,
subtree: true
};
const queryUser = (c, sync) => {
if (!isOk.has(c)) {
isOk.add(c)
let pid = get_pid(c);
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
let json = JSON.stringify(JSON.parse(xhr.responseText))
toMatchAll(json, c)
}
}
const xhrFollow = new XMLHttpRequest();
xhrFollow.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
let json = JSON.parse(xhrFollow.responseText)
if (json['data'] == null) {
return;
}
toMatchFollow(json, c)
}
}
if (setting.matchingDynamic == 1) {
xhr.open('GET', bili_dyn_url + pid, sync)
xhr.send()
}
if (setting.matchingVideo == 1) {
xhr.open('GET', bili_video_url + pid, sync)
xhr.send()
}
if (setting.matchingFollow == 1) {
if (setting.matchingFollowPage >= 1 && setting.matchingFollowPage <= 5) {
for (let i = 1; i <= setting.matchingFollowPage; i++) {
xhrFollow.open('GET', bili_follow_url + i + '&vmid=' + pid, sync)
xhrFollow.send("CONTENT-TYPE", "application/json")
}
}
}
}
return;
}
const addQueryBtn = (c) => {
if (!isAddBtn.has(c)) {
isAddBtn.add(c);
let toAppend = document.createElement("DIV");
toAppend.innerHTML = '查成分'
toAppend.className = 'userComponentBtn myCursor'
toAppend.addEventListener("click", function () {
let _this = this
new Promise(function (resolve, reject) {
_this.innerHTML = '查询中'
resolve(queryUser(c, false));
}).then(function () {
_this.innerHTML = '查询完毕'
_this.className = 'userComponentBtn'
})
})
if (bili_new) {
c.parentNode.parentNode.className += ' toHover';
c.parentNode.append(toAppend);
} else {
c.className += ' toHover';
c.insertBefore(toAppend,c.children[1]);
}
}
}
var bili_match = ['comment-list ', 'reply-box']
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
for (let q = 0; q < bili_match.length; q++) {
if (mutation.target.className.toString() == bili_match[q]) {
console.log("评论增加");
let bgcl = bili_get_comment_list()
if (setting.automatic == 0) {
bgcl.forEach(c => {
addQueryBtn(c);
});
}
if (setting.automatic == 1) {
bgcl.forEach(c => {
queryUser(c, true)
});
break;
}
}
}
}
}
}
const observer = new MutationObserver(callback);
if (window.location.pathname.indexOf('video') != -1) {
console.log("当前为视频页面")
if (!bili_new) {
observer.observe(document.body, config);
} else {
bili_match = ['reply-list', 'sub-reply-list'];
observer.observe(document.body, config);
}
}
if (window.location.hostname.indexOf('space') != -1 || window.location.hostname.indexOf('t.bilibili.com') != -1) {
console.log("当前为动态页面")
bili_match = ['comment-list has-limit', 'reply-box'];
observer.observe(document.body, config);
}
})();