// ==UserScript== // @name 哔哩哔哩评论检测 // @version 1 // @description 评论发送后会检测是否被shadowban // @author freedom-introvert & ChatGPT // @match https://*.bilibili.com/* // @run-at document-idle // @grant GM.xmlHttpRequest // @grant unsafeWindow // @connect api.bilibili.com // @license GPL // ==/UserScript== var BilibiliCommentAntiFraud=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=`:host,.ui-root[data-theme=light]{--ui-surface:#fff;--ui-surface-elevated:#fff;--ui-text:#18191c;--ui-text-secondary:#61666d;--ui-border:#e3e5e7;--ui-track:#e9eaec;--ui-brand:#fb7299;--ui-success:#23a55a;--ui-warning:#e58900;--ui-danger:#d92d20;--ui-focus-ring:#2f7fe8;--ui-shadow:0 12px 32px #18191c2e}:host([data-theme=dark]),.ui-root[data-theme=dark]{--ui-surface:#1f2024;--ui-surface-elevated:#292a2f;--ui-text:#f4f5f6;--ui-text-secondary:#b8bcc3;--ui-border:#45474d;--ui-track:#3b3d43;--ui-brand:#fb7299;--ui-success:#42bf73;--ui-warning:#f3a832;--ui-danger:#ff7262;--ui-focus-ring:#75aeff;--ui-shadow:0 14px 36px #0000006b}:host,.ui-root{color:var(--ui-text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.floating-panel{z-index:2147483647;box-sizing:border-box;border:1px solid var(--ui-border);background:var(--ui-surface);width:min(460px,100vw - 32px);box-shadow:var(--ui-shadow);color:var(--ui-text);border-radius:16px;align-items:flex-start;gap:12px;padding:16px;animation:.18s ease-out panel-enter;display:flex;position:fixed;inset-block-end:calc(20px + env(safe-area-inset-bottom));inset-inline-start:50%;transform:translate(-50%)}.floating-panel__content{flex:1;min-width:0}.floating-panel__close{width:32px;height:32px;color:var(--ui-text-secondary);cursor:pointer;background:0 0;border:0;border-radius:8px;flex:none;place-items:center;margin:-4px -4px 0 0;font-size:24px;line-height:1;display:grid}.floating-panel__close:hover{background:var(--ui-track);color:var(--ui-text)}.floating-panel__close:active{transform:scale(.95)}.floating-panel__close:focus-visible{outline:3px solid var(--ui-focus-ring);outline-offset:2px}.status-icon{background:var(--ui-brand);color:#fff;border-radius:50%;place-items:center;width:24px;height:24px;display:inline-grid}.status-icon svg{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.5px;width:100%;height:100%}.status-icon--success{background:var(--ui-success)}.status-icon--warning{background:var(--ui-warning)}.status-icon--danger,.status-icon--error{background:var(--ui-danger)}.progress-bar{background:var(--ui-track);width:100%;height:6px;accent-color:var(--ui-brand);border:0;border-radius:999px;overflow:hidden}.progress-bar::-webkit-progress-bar{background:var(--ui-track)}.progress-bar::-webkit-progress-value{background:var(--ui-brand)}.progress-bar::-moz-progress-bar{background:var(--ui-brand)}.progress-bar--indeterminate{position:relative}.progress-bar__indicator{border-radius:inherit;background:var(--ui-brand);width:38%;animation:1.25s ease-in-out infinite progress-indeterminate;position:absolute;inset-block:0;inset-inline-start:-38%}.progress-state,.result-state,.dynamic-warning{align-items:flex-start;gap:12px;display:flex}.progress-state h2,.result-state h2{margin:1px 0 4px;font-size:16px;line-height:1.4}.progress-state p,.result-state p{color:var(--ui-text-secondary);margin:0;font-size:14px;line-height:1.55}.progress-state>div,.result-state>div{flex:1;min-width:0}.progress-state .progress-bar{margin-top:12px;display:block}.progress-state__detail{margin-top:6px!important;font-size:12px!important}.comment-preview{-webkit-line-clamp:3;-webkit-user-select:text;user-select:text;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden;color:var(--ui-text)!important;margin-top:10px!important}.ui-button{background:var(--ui-brand);color:#18191c;cursor:pointer;font:inherit;border:0;border-radius:8px;margin-top:12px;padding:7px 12px;font-size:14px;font-weight:700;line-height:1.4}.ui-button:hover{filter:brightness(.96)}.ui-button:active{transform:scale(.98)}.ui-button:focus-visible{outline:3px solid var(--ui-focus-ring);outline-offset:2px}.dynamic-warning{box-sizing:border-box;border:1px solid var(--ui-danger);background:var(--ui-surface-elevated);color:var(--ui-text);border-radius:10px;margin:12px 0;padding:12px}.dynamic-warning p{color:var(--ui-text);margin:1px 0 0;font-size:14px;line-height:1.5}@keyframes panel-enter{0%{opacity:0;transform:translate(-50%)translateY(8px)}to{opacity:1;transform:translate(-50%)translateY(0)}}@keyframes progress-indeterminate{0%{inset-inline-start:-38%}to{inset-inline-start:100%}}@media (width<=520px){.floating-panel{width:calc(100vw - 16px);inset-block-end:calc(8px + env(safe-area-inset-bottom))}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}`,n=`https://api.bilibili.com/x/v2/reply/main`,r=`https://api.bilibili.com/x/v2/reply/reply`,i=`https://api.bilibili.com/x/polymer/web-dynamic/v1/detail`;function a(e=document.cookie){for(let t of e.split(`;`)){let e=t.trim();if(e.startsWith(`buvid3=`))return e}return null}function o(e,t=e=>GM.xmlHttpRequest(e)){return{async getMainCommentList(e,r,i,o,s,c){let l=`${n}?oid=${e}&type=${r}&next=${i}&mode=${o}`+(c?`&seek_rpid=${c}`:``),u=a(),d={url:l,anonymous:!s};u&&(d.headers={cookie:u});let f=await t(d),p=typeof f.responseText==`string`?f.responseText:typeof f.response==`string`?f.response:JSON.stringify(f.response);if(p===void 0)throw Error(`GM.xmlHttpRequest returned no text response`);let m=JSON.parse(p);return console.log(`获取主评论列表,携带cookie:${s}`,l,m),m},async fetchCommentReplies(t,n,i,a,o,s){let c=new URL(r);c.search=new URLSearchParams({oid:String(t),type:String(n),root:String(i),pn:String(a),sort:String(o)}).toString();let l=await e(c,{credentials:s?`include`:`omit`});if(!l.ok)throw Error(`HTTP error! status: ${l.status}`);return l.json()},async fetchDynamic(t,n){let r=new URL(i);r.search=new URLSearchParams({id:t}).toString();let a=await e(r,{credentials:n?`include`:`omit`});if(!a.ok)throw Error(`HTTP error! status: ${a.status}`);return a.json()}}}var s={normal:{title:`评论公开可见`,description:`未登录状态下也找到了这条评论。`,canCopy:!1,live:`polite`},"shadow-banned":{title:`评论仅自己可见`,description:`匿名访问无法找到,但登录后仍可访问。`,canCopy:!0,live:`assertive`},"quick-deleted":{title:`评论已被系统删除`,description:`登录与未登录状态下都无法找到这条评论。`,canCopy:!0,live:`assertive`},suspicious:{title:`评论状态可疑`,description:`评论区列表不可见,但回复接口仍能访问。`,canCopy:!0,live:`polite`},error:{title:`检测未完成`,description:`检查过程中发生错误,请稍后再试。`,canCopy:!1,live:`assertive`}};function c(e,t,n){let r=s[e];return{phase:`result`,kind:e,...r,description:n??r.description,comment:t}}var l=5e3;function u(e,t){for(let n of e??[])if(n.rpid===t)return n;return null}async function d(e){let t=l/10;for(let n=0;n<=t;n+=1)await e.sleep(10),e.emit({phase:`waiting`,elapsedMs:n*10,totalMs:l});e.emit({phase:`waiting`,elapsedMs:l,totalMs:l})}function f(e,t,n){e.logger?.error(t,n),e.emit(c(`error`,void 0,t))}async function p(e,t){let n=t.logger??console;await d(t);let{oid:r,type:i,rpid:a,root:o}=e;if(o===0){t.emit({phase:`checking`,step:1,totalSteps:3,message:`查找无账号评论区时间排序第一页`});let o=await t.getMainCommentList(r,i,0,2,!1);if(o.code!==0){f(t,`获取评论主列表时发生错误`,o);return}if(n.log(o),u(o.data?.replies??null,a)){t.emit(c(`normal`,e.content.message));return}if(t.emit({phase:`checking`,step:2,totalSteps:3,message:`有账号获取此评论的回复列表`}),o=await t.fetchCommentReplies(r,i,a,0,0,!0),o.code===12022){t.emit(c(`quick-deleted`,e.content.message));return}if(o.code!==0){f(t,`有账号获取评论回复列表时发生错误`,o);return}t.emit({phase:`checking`,step:3,totalSteps:3,message:`无账号获取此评论的回复列表`}),o=await t.fetchCommentReplies(r,i,a,0,0,!1),o.code===12022?t.emit(c(`shadow-banned`,e.content.message)):o.code===0?t.emit(c(`suspicious`,e.content.message)):f(t,`获取评论回复列表时发生错误`,o);return}for(let s=0;;s+=1){t.emit({phase:`checking`,step:1,totalSteps:2,message:`无账号查找评论回复第${s}页……`});let l=await t.fetchCommentReplies(r,i,o,s,0,!1),d=l.data?.replies??null;if(n.log(l),d===null||d.length===0){n.log(`已翻遍无账号下的评论回复页`);break}if(u(d,a)){t.emit(c(`normal`,e.content.message));return}}for(let s=0;;s+=1){t.emit({phase:`checking`,step:2,totalSteps:2,message:`有账号查找评论回复第${s}页……`});let l=await t.fetchCommentReplies(r,i,o,s,0,!0),d=l.data?.replies??null;if(n.log(l),d===null||d.length===0){n.log(`已翻遍有账号下的评论回复页`);break}if(u(d,a)){t.emit(c(`shadow-banned`,e.content.message));return}}t.emit(c(`quick-deleted`,e.content.message))}var m={[-352]:`检测到此动态被shadowBan,仅自己可见!(也可能是误判了,你可以在无痕模式去验证一下)`,4101131:`检测到此动态被shadowBan,仅自己可见!(可能你转发到动态的评论被ShadowBan)`,500:`检测到此动态被shadowBan,仅自己可见!(可能你转发到动态的评论疑似审核中)`};async function h(e,t){let n=t.logger??console,r=await t.fetchDynamic(e,!1);n.log(r);let i=m[r.code];i?(n.log(`检测到动态被shadowBan!`),t.warn(i)):r.code===0?n.log(`检查到此动态正常,没被shadowBan`):n.log(`动态检查出错:未知的响应码`,r)}function g(){let e=0,t,n={phase:`idle`},r=new Set,i=new Set,a=e=>{n=e,i.forEach(e=>e())};return{start(){let n=++e;return t=n,a({phase:`waiting`,elapsedMs:0,totalMs:5e3}),n},update(e,n){e!==t||r.has(e)||a(n)},close(e){e!==t||r.has(e)||(r.add(e),a({phase:`idle`}))},getState(){return n},subscribe(e){return i.add(e),()=>i.delete(e)}}}var _=`http://www.w3.org/2000/svg`,v={info:`提示`,success:`成功`,warning:`警告`,danger:`危险`,error:`错误`},y={normal:`success`,"shadow-banned":`danger`,"quick-deleted":`danger`,suspicious:`warning`,error:`error`};function b(e,t,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}function x(e){let t=b(`span`,`status-icon status-icon--${e}`);t.setAttribute(`aria-label`,v[e]);let n=document.createElementNS(_,`svg`);n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`aria-hidden`,`true`),n.setAttribute(`focusable`,`false`);let r=document.createElementNS(_,`circle`);r.setAttribute(`cx`,`12`),r.setAttribute(`cy`,`12`),r.setAttribute(`r`,`9`),n.append(r);let i=e===`success`?[`m7 12 3 3 7-7`]:e===`info`?[`M12 11v5`,`M12 8h.01`]:[`M12 7v6`,`M12 17h.01`];for(let e of i){let t=document.createElementNS(_,`path`);t.setAttribute(`d`,e),n.append(t)}return t.append(n),t}function S(e,t){if(e===void 0||t===void 0){let e=b(`div`,`progress-bar progress-bar--indeterminate`);return e.setAttribute(`role`,`progressbar`),e.setAttribute(`aria-label`,`检测进度`),e.append(b(`span`,`progress-bar__indicator`)),e}let n=b(`progress`,`progress-bar`);return n.value=e,n.max=t,n.setAttribute(`aria-label`,`检测进度`),n}function C(e){let t=b(`div`,`progress-state`),n=b(`div`);if(e.phase===`checking`)n.append(b(`h2`,void 0,`正在确认评论是否公开`),b(`p`,void 0,e.message),S(),b(`p`,`progress-state__detail`,`第 ${e.step} 步,共 ${e.totalSteps} 步`));else{let t=Math.ceil(e.elapsedMs/1e3),r=Math.ceil(e.totalMs/1e3),i=Math.max(0,Math.ceil((e.totalMs-e.elapsedMs)/1e3));n.append(b(`h2`,void 0,`等待评论同步`),b(`p`,void 0,`评论正在同步到公开列表。`),S(Math.min(e.elapsedMs,e.totalMs),e.totalMs),b(`p`,`progress-state__detail`,`已等待 ${t} 秒,共 ${r} 秒 · 预计剩余 ${i} 秒`))}return t.append(x(`info`),n),t}function w(e){return e.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&`host`in e&&typeof e.appendChild==`function`}async function T(e,t){if(navigator.clipboard?.writeText)try{return await navigator.clipboard.writeText(e),!0}catch{}let n=t.getRootNode(),r=b(`textarea`);r.value=e,r.setAttribute(`aria-hidden`,`true`),r.style.cssText=`position:fixed;opacity:0;pointer-events:none`,(w(n)?n:document.body).append(r);try{return r.select(),document.execCommand(`copy`)}finally{r.remove()}}function E(e){let t=b(`div`,`result-state result-state--${e.kind}`),n=b(`div`);if(n.append(b(`h2`,void 0,e.title),b(`p`,void 0,e.description)),e.comment&&n.append(b(`p`,`comment-preview`,e.comment)),e.canCopy&&e.comment){let t=b(`button`,`ui-button`,`复制评论`);t.type=`button`,t.addEventListener(`click`,()=>{T(e.comment,t).then(e=>{e&&(t.textContent=`已复制`,window.setTimeout(()=>{t.isConnected&&(t.textContent=`复制评论`)},1500))})}),n.append(t)}return t.append(x(y[e.kind]),n),t}function D(e,t,n,r){if(t.phase===`idle`){e.replaceChildren();return}let i=r??b(`section`,`floating-panel`),a=t.phase===`result`?t.live:`polite`;i.setAttribute(`role`,a===`assertive`?`alert`:`status`),i.setAttribute(`aria-live`,a);let o=i.querySelector(`.floating-panel__content`);if(!o){o=b(`div`,`floating-panel__content`);let t=b(`button`,`floating-panel__close`);t.type=`button`,t.setAttribute(`aria-label`,`关闭`);let r=b(`span`,void 0,`×`);r.setAttribute(`aria-hidden`,`true`),t.append(r),t.addEventListener(`click`,n),i.append(o,t),e.replaceChildren(i)}return o.replaceChildren(),o.append(t.phase===`result`?E(t):C(t)),i}function O(e,t){let n=t.autoDismissMs??5e3,r,i=n,a=0,o,s,c=()=>{r!==void 0&&window.clearTimeout(r),r=void 0},l=()=>{let e=t.currentSession();e!==void 0&&t.controller.close(e)},u=()=>{!o||r!==void 0||(a=Date.now(),r=window.setTimeout(l,Math.max(0,i)))},d=()=>{r!==void 0&&(i=Math.max(0,i-(Date.now()-a)),c())},f=()=>{let r=t.controller.getState(),a=r.phase===`result`&&r!==o;r.phase===`result`?a&&(o=r,c(),i=n):(o=void 0,c()),s=D(e,r,l,s);let f=s;r.phase===`result`&&f&&(f.addEventListener(`pointerenter`,d),f.addEventListener(`pointerleave`,u),f.addEventListener(`focusin`,d),f.addEventListener(`focusout`,e=>{let t=e.relatedTarget;(!(t instanceof Node)||!f.contains(t))&&u()}),u())},p=t.controller.subscribe(f);return f(),{dispose(){c(),p(),e.replaceChildren()}}}function k(e,t){let n=e.dataset.theme;if(n===`dark`||n===`light`)return n;if(e.classList.contains(`dark`))return`dark`;let r=e.style.colorScheme.trim().toLowerCase();return r.startsWith(`dark`)?`dark`:r.startsWith(`light`)?`light`:t?`dark`:`light`}function A(e){let t=document.documentElement,n=window.matchMedia(`(prefers-color-scheme: dark)`),r=()=>e(k(t,n.matches)),i=new MutationObserver(r),a=()=>r();return i.observe(t,{attributes:!0,attributeFilter:[`class`,`data-theme`,`style`]}),n.addEventListener(`change`,a),r(),()=>{i.disconnect(),n.removeEventListener(`change`,a)}}function j(e,t={}){let n=Math.min(t.timeoutMs??1e4,1e4),r,i,a,o,s=!1,c=()=>{i?.disconnect(),i=void 0,a!==void 0&&window.clearTimeout(a),a=void 0},l=()=>{if(s||r)return;if(document.querySelector(`[data-comment-anti-fraud-warning]`)){c();return}let n=document.querySelector(`.bili-dyn-content`);if(!n)return;r=document.createElement(`div`),r.dataset.commentAntiFraudWarning=``,r.className=`ui-root`,n.prepend(r);let i=r;if(t.styleText){let e=r.attachShadow({mode:`open`}),n=document.createElement(`style`);n.textContent=t.styleText,i=document.createElement(`div`),i.className=`ui-root`,e.append(n,i)}o=A(e=>r?.setAttribute(`data-theme`,e));let a=document.createElement(`aside`);a.className=`dynamic-warning`,a.setAttribute(`role`,`alert`);let l=document.createElement(`p`);l.textContent=e,a.append(x(`danger`),l),i.append(a),c()};return l(),!r&&!s&&(i=new MutationObserver(l),i.observe(document.body,{childList:!0,subtree:!0}),a=window.setTimeout(c,n)),()=>{s=!0,c(),o?.(),r?.remove(),r=void 0}}var M=new URL(`https://api.bilibili.com/x/v2/reply/add`);function N(e){if(e.hostname===`t.bilibili.com`)return e.pathname.split(`/`)[1]||null;if(e.hostname===`www.bilibili.com`){let t=e.pathname.split(`/`);if(t[1]===`opus`)return t[2]||null}return null}function P(e,t){try{let n;if(typeof e==`string`)n=e;else{let t=e.url;n=typeof t==`string`?t:String(e)}let r=new URL(n,t.location.href);return r.origin===M.origin&&r.pathname===M.pathname}catch{return!1}}function F(e={}){let n=e.pageWindow??unsafeWindow,r=n.fetch,i=e.api??o((e,t)=>r.call(n,e,t)),a=e.sleep??(e=>new Promise(t=>window.setTimeout(t,e))),s=e.mountWarning??j,l=g(),u,d,f=document.createElement(`div`);f.id=`bilibili-comment-anti-fraud-root`;let m=f.attachShadow({mode:`open`}),_=document.createElement(`style`);_.textContent=t;let v=document.createElement(`div`);v.className=`ui-root`,m.append(_,v),document.body.append(f);let y=A(e=>f.setAttribute(`data-theme`,e)),b=O(v,{controller:l,currentSession:()=>u,autoDismissMs:5e3}),x=async e=>{if(console.log(e.code),e.code!==0||!e.data?.reply)return;let{reply:t}=e.data;console.log(`${e.data.success_toast},准备检查评论`);let n=l.start();u=n;try{await p(t,{sleep:a,getMainCommentList:i.getMainCommentList,fetchCommentReplies:i.fetchCommentReplies,emit:e=>l.update(n,e)})}catch(e){console.error(`评论检查时发生错误`,e),l.update(n,c(`error`))}},S=async function(e,t){let i=await r.call(this,e,t);try{P(e,n)&&i.clone().text().then(e=>x(JSON.parse(e))).catch(e=>console.error(`解析评论发送响应失败`,e))}catch(e){console.error(`读取评论发送响应失败`,e)}return i};n.fetch=S;let C=()=>{let e=N(n.location);e&&(console.log(`Dynamic ID:`,e),h(e,{fetchDynamic:i.fetchDynamic,warn:e=>{d?.(),d=s(e,{styleText:t})}}).catch(e=>console.error(`动态检查失败`,e)))},w=document.readyState!==`complete`;return w?n.addEventListener(`load`,C,{once:!0}):C(),console.log(`反诈脚本已加载`),{controller:l,host:f,dispose(){w&&n.removeEventListener(`load`,C),n.fetch===S&&(n.fetch=r),y(),d?.(),d=void 0,b.dispose(),f.remove()}}}function I(e,t={}){if(!(!e.unsafeWindow||!document.body))return F({...t,pageWindow:e.unsafeWindow})}return typeof unsafeWindow<`u`&&I({unsafeWindow}),e.bootstrapUserscript=I,e.installUserscript=F,e})({});