您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
bilibili 共同关注一键查询(自主查询版)
当前为
// ==UserScript== // @name bilibili 成分查询 // @namespace https://github.com/sparanoid/userscript // @supportURL https://github.com/sparanoid/userscript/issues // @version 0.1.1 // @description bilibili 共同关注一键查询(自主查询版) // @author Sparanoid // @match https://*.bilibili.com/* // @icon https://experiments.sparanoid.net/favicons/v2/www.bilibili.com.ico // @grant none // @run-at document-start // ==/UserScript== window.addEventListener('load', () => { const DEBUG = true; const NAMESPACE = 'bilibili-social-check'; const apiBase = 'https://api.bilibili.com'; const feedbackUrl = 'https://t.bilibili.com/545085157213602473'; console.log(`${NAMESPACE} loaded`); async function fetchResult(url = '', data = {}) { const response = await fetch(url, { credentials: 'include', }); return response.json(); } function debug(description = '', msg = '', force = false) { if (DEBUG || force) { console.log(`${NAMESPACE}: ${description}`, msg) } } function formatDate(timestamp) { let date = timestamp.toString().length === 10 ? new Date(+timestamp * 1000) : new Date(+timestamp); return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; } function rateColor(percent) { return `hsl(${100 - percent}, 70%, 45%)`; } function percentDisplay(num) { return num.toFixed(2).replace('.00', ''); } function attachElwww(item) { let injectWrap = item.querySelector('.con .info'); // .text - comment content // .text-con - reply content let content = item.querySelector('.con .text') || item.querySelector('.reply-con .text-con'); let id = item.dataset.id; // Simple way to attach element on replies initially loaded with comment // which wouldn't trigger mutation inside observeComments let replies = item.querySelectorAll('.con .reply-box .reply-item'); if (replies.length > 0) { [...replies].map(reply => { attachElwww(reply); }); } if (injectWrap.querySelector('.asoulcnki')) { debug('already loaded for this comment'); } else { // Insert asoulcnki check button let asoulcnkiEl = document.createElement('span'); asoulcnkiEl.classList.add('asoulcnki', 'btn-hover', 'btn-highlight'); asoulcnkiEl.innerHTML = '狠狠地查'; asoulcnkiEl.addEventListener('click', e => { let contentPrepared = ''; // Copy meme icons alt text for (let node of content.childNodes.values()) { if (node.nodeType === 3) { contentPrepared += node.textContent; } else if (node.nodeName === 'IMG' && node.nodeType === 1) { contentPrepared += node.alt; } else if (node.nodeName === 'BR' && node.nodeType === 1) { contentPrepared += '\n'; } else if (node.nodeName === 'A' && node.nodeType === 1 && node.classList.contains('comment-jump-url')) { contentPrepared += node.href.replace(/https?:\/\/www\.bilibili\.com\/video\//, ''); } else { contentPrepared += node.innerText; } } // Need regex to stripe `回复 @username :` let contentProcessed = contentPrepared.replace(/回复 @.*:/, ''); debug('content processed', contentProcessed); // ask to confirm if words count not enough if (contentProcessed.length < 10 && !confirm('内容过短(少于 10 字),可能无法得到正确结果,是否继续查询?')) return; fetchResult(`${apiBase}/v1/api/check`, { text: contentProcessed }) .then(data => { debug('data returned', data); let resultContent = ''; if (data.code !== 0) { resultContent = `返回结果错误,可能是文本内容过短,或请访问 <a href="${apiBase}/" target="_blank">枝网</a> 查看服务是否正常`; } else { let result = data.data; let startTime = result.start_time; let endTime = result.end_time; let rate = result.rate * 100; let relatedItems = result.related; resultContent = `<a href="${apiBase}" target="_blank">枝网</a>文本复制检测报告(Chrome 脚本版/<a href="${feedbackUrl}" target="_blank">反馈</a>) 查重时间:${formatDate(Date.now())} 数据范围:${formatDate(startTime)} - ${formatDate(endTime)} 总文字复制比:<b style="color: ${rateColor(rate)}">${percentDisplay(rate)}%</b>\n`; if (relatedItems.length === 0) { resultContent += `一眼原创,再偷必究(查重结果仅作娱乐参考)`; } else { let selfOriginal = +relatedItems[0][1].rpid === +id ? `(<span style="color: blue;">本文原创,已收录</span>)` : ''; resultContent += `重复次数:${relatedItems.length}${selfOriginal}\n`; relatedItems.map((item, idx) => { let rate = item[0] * 100; resultContent += `#${idx + 1} <span style="color: ${rateColor(rate)}">${percentDisplay(rate)}%</span> <a href="${item[2].trim()}" title="${item[1].content}" target="_blank">${item[2].trim()}</a> 发布于:${formatDate(item[1].ctime)} 作者:@${item[1].m_name} (UID <a href="https://space.bilibili.com/${item[1].mid}" target="_blank">${item[1].mid}</a>)\n\n`; }); resultContent += `查重结果仅作娱乐参考,请注意辨别是否为原创`; } } // Insert result let resultWrap = document.createElement('div'); resultWrap.style.padding = '.5rem'; resultWrap.style.margin = '.5rem 0'; resultWrap.style.background = 'hsla(0, 0%, 50%, .1)'; resultWrap.style.borderRadius = '4px'; resultWrap.style.whiteSpace = 'pre'; resultWrap.classList.add('asoulcnki-result'); resultWrap.innerHTML = resultContent; // Remove previous result if exists if (injectWrap.querySelector('.asoulcnki-result')) { injectWrap.querySelector('.asoulcnki-result').remove(); } injectWrap.append(resultWrap); }); }, false); injectWrap.append(asoulcnkiEl); // Insert comment ID link let idLink = document.createElement('a'); idLink.innerHTML = '#'; idLink.setAttribute('title', '当前评论 ID: ' + id); idLink.setAttribute('href', '#reply' + id); idLink.style.marginRight = '.25em'; injectWrap.prepend(idLink); } } function attachEl(wrapper, output) { let content = document.createElement('div'); content.innerText = output; wrapper.append(content); } function processFollowings(wrapper, id, output, iteration) { let outputlist = ''; fetchResult(`${apiBase}/x/relation/same/followings?vmid=${id}&pn=${iteration}`).then(data => { debug('data returned', data); if (data.code !== 0) { outputlist = data.message; } else { let result = data.data; let total = result.total; let items = result.list; items.map(item => { outputlist += item.uname + '\n'; }); if (items.length > 0) { debug('try next page', iteration + 1); setTimeout(() => { processFollowings(wrapper, id, output, iteration + 1); }, 200); } else { debug('loop finished'); } attachEl(wrapper, outputlist); } }); } function observeCard(wrapper) { let iteration = 1; let resultContent = ''; let id = wrapper.querySelector('.info .user .name')?.href.split('/').slice(-1)[0]; // ensure user id exists debug('current uid', id); if (id) { // Create output wrapper and limit height let injectWrap = wrapper; let contentWrap = document.createElement('div'); contentWrap.style.overflowY = 'auto'; contentWrap.style.maxHeight = '300px'; contentWrap.style.paddingTop = '1rem'; contentWrap.style.marginTop = '1rem'; contentWrap.style.borderTop = '1px solid #eee'; contentWrap.classList.add(`${NAMESPACE}-wrap`); injectWrap.append(contentWrap); processFollowings(contentWrap, id, resultContent, iteration); } } // .user-card loads dynamcially. So observe it first const wrapperObserver = new MutationObserver((mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { [...mutation.addedNodes].map(item => { debug('mutation wrapper added', item); let classNames = [ 'user-card', // normal card, global, comments avatar, comment mentions, and etc. 'userinfo-wrapper', // card in dongtai mentions ] if (classNames.some(className => item.classList?.contains(className))) { debug('mutation wrapper added (found target)', item); observeCard(item); } }) } } }); wrapperObserver.observe(document.body, { attributes: false, childList: true, subtree: true }); }, false);