您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A real-time comment checker, Fuck YouTube’s opaque comment censorship
// ==UserScript== // @name youtube-comment-censor-detector // @name:zh-CN YouTube发评反诈 // @name:zh-TW YouTube發評反詐 // @namespace npm/vite-plugin-monkey // @version 2.3.5 // @author freedom-introvert // @description A real-time comment checker, Fuck YouTube’s opaque comment censorship // @description:zh-CN Fuck YouTube版“阿瓦隆系统”,实时检查评论状态,防止评论被儿童偷偷误食你还被蒙在鼓里 // @description:zh-TW Fuck YouTube版“阿瓦隆系統”,即時檢查評論狀態,防止評論被兒童偷偷誤食你還被蒙在鼓裡 // @license GPL // @icon https://raw.githubusercontent.com/freedom-introvert/youtube-comment-censor-detector/refs/heads/main/logo/logo_256x256.avif // @match *://*.youtube.com/* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js // @require https://unpkg.com/vue-demi@latest/lib/index.iife.js // @require data:application/javascript,window.Vue%3DVue%3B // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/index.full.min.js // @resource element-plus/dist/index.css https://cdn.jsdelivr.net/npm/[email protected]/dist/index.css // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_registerMenuCommand // @grant unsafeWindow // @run-at document-start // ==/UserScript== (a=>{if(typeof GM_addStyle=="function"){GM_addStyle(a);return}const e=document.createElement("style");e.textContent=a,document.head.append(e)})(" .el-message,.is-message-box{z-index:9999!important}.comment-checker[data-v-684910ce]{font-size:12px}.container[data-v-684910ce]{width:80%;margin:0 auto}.comment-checker[data-v-684910ce]{padding:15px 15px 11px;border-radius:8px;transition:background-color .3s}.title[data-v-684910ce]{font-weight:700;margin-bottom:6px}.message[data-v-684910ce]{margin-bottom:10px}.el-progress[data-v-684910ce]{margin-bottom:4px}.buttons[data-v-684910ce]>[data-v-684910ce]{display:inline-flex;align-items:center;padding:4px 8px;margin-left:-8px;color:#4b5e9d;border-radius:4px;transition:background-color .2s,color .2s;-webkit-user-select:none;user-select:none;margin-right:10px}.buttons[data-v-684910ce]>[data-v-684910ce]:hover{background-color:#0000000d}.buttons[data-v-684910ce]>[data-v-684910ce]:active{background-color:#0000001a}.comment-checker.not-check[data-v-684910ce]{background-color:#00f3}.comment-checker.normal[data-v-684910ce]{background-color:#0f03}.comment-checker.deleted[data-v-684910ce]{background-color:#f003}.comment-checker.shadow-ban[data-v-684910ce]{background-color:#ff03}.hot-ban-checker[data-v-e5341f8d]{background-color:#007bff1a;border:1px solid rgba(0,60,136,.4);border-radius:6px;padding:1rem;margin:10px 0}.title[data-v-e5341f8d]{font-weight:700;margin-bottom:6px}.message[data-v-e5341f8d]{margin-bottom:10px}.actions[data-v-e5341f8d]{margin-top:10px}.buttons[data-v-e5341f8d]>[data-v-e5341f8d]{display:inline-flex;align-items:center;padding:4px 8px;margin-left:-8px;color:#4b5e9d;border-radius:4px;transition:background-color .2s,color .2s;-webkit-user-select:none;user-select:none;margin-right:10px}.buttons[data-v-e5341f8d]>[data-v-e5341f8d]:hover{background-color:#0000000d}.buttons[data-v-e5341f8d]>[data-v-e5341f8d]:active{background-color:#0000001a}.pagination[data-v-80f074da]{margin-top:6px}.detail[data-v-80f074da]{margin-left:10px}.info-table td[data-v-80f074da]:nth-child(1){white-space:nowrap;vertical-align:top}.info-table td[data-v-80f074da]:nth-child(2){padding-left:16px}.comment-content[data-v-80f074da]{white-space:break-spaces}summary[data-v-80f074da]{cursor:pointer;margin-top:2px;-webkit-user-select:none;user-select:none}.locate-link[data-v-80f074da]{width:100%}[data-v-80f074da] .locate-link>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.post-locate-link[data-v-80f074da]{font-size:10px}.youtube-comment[data-v-2a518d4b]{display:flex;align-items:flex-start;padding:16px;gap:16px;font-family:Roboto,sans-serif}.avatar-container[data-v-2a518d4b]{flex-shrink:0}.avatar[data-v-2a518d4b]{width:40px;height:40px;border-radius:50%;object-fit:cover}.comment-content[data-v-2a518d4b]{flex:1;display:flex;flex-direction:column;gap:4px}.comment-header[data-v-2a518d4b]{display:flex;align-items:center;gap:8px}.author-name[data-v-2a518d4b]{font-size:13px;font-weight:500;color:#000;text-decoration:none;transition:color .2s ease}.published-at[data-v-2a518d4b]{font-size:12px;color:#aaa}.comment-text[data-v-2a518d4b]{font-size:14px;line-height:1.5;white-space:pre-wrap;word-break:break-word;margin:0}.comment-actions[data-v-2a518d4b]{display:flex;align-items:center;gap:8px;margin-top:8px}.button[data-v-2a518d4b]{display:flex;align-items:center;gap:4px;background-color:transparent;border:none;cursor:pointer;color:#aaa;font-size:12px;transition:color .2s ease;text-decoration:none;padding:1px 6px}.button[data-v-2a518d4b]:hover{color:#f1f1f1}.icon[data-v-2a518d4b]{width:16px;height:16px;fill:currentColor}.action-text[data-v-2a518d4b]{line-height:100%}.hot-ban-comment-searcher[data-v-b5a0c87e],.not-comment-section[data-v-b5a0c87e]{height:100%}.warning[data-v-b5a0c87e]{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%}.search-progress[data-v-b5a0c87e]{display:flex;height:100%;flex-direction:column;justify-content:center;align-items:center;gap:15px}.warning-title[data-v-b5a0c87e]{margin-bottom:4px}.warning-text[data-v-b5a0c87e]{text-align:center;max-width:500px}.warning-divider[data-v-b5a0c87e]{max-width:800px}.el-progress[data-v-b5a0c87e]{width:300px}.footer[data-v-b5a0c87e]{display:flex;flex-direction:column;align-items:center}[data-v-1d7cd9ea] .dialog-body{height:calc(100% - 40px);display:flex;flex-direction:column} "); (function (vue, ElementPlus) { 'use strict'; function sleep(time) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, time); }); } function urlSafeBase64ToStandard(urlSafeBase64) { let standardBase64 = urlSafeBase64.replace(/%3D/g, "=").replace(/-/g, "+").replace(/_/g, "/"); return standardBase64; } function standardBase64ToUrlSafe(standardBase64) { let urlSafeBase64 = standardBase64.replace(/=/g, "%3D").replace(/\+/g, "-").replace(/\//g, "_"); return urlSafeBase64; } function createUrl(path) { if (path) { return new URL(new URL(window.location.href).origin + path); } return new URL(window.location.href); } function formatSecondsToMMSS(seconds) { const sec = parseInt(seconds, 10); const minutes = Math.floor(sec / 60); const remainingSeconds = sec % 60; const formattedMinutes = String(minutes).padStart(2, "0"); const formattedSeconds = String(remainingSeconds).padStart(2, "0"); return `${formattedMinutes}:${formattedSeconds}`; } function formatTimestamp(timestamp) { if (!timestamp) { return "--:--:--"; } const date = new Date(timestamp); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); const hours = String(date.getHours()).padStart(2, "0"); const minutes = String(date.getMinutes()).padStart(2, "0"); const seconds = String(date.getSeconds()).padStart(2, "0"); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } function translateState(state) { switch (state) { case "NORMAL": return "正常"; case "DELETED": return "已删除"; case "SHADOW_BAN": return "仅自己可见"; case "NOT_CHECK": return "还未检查"; } } function findValueInSingleEntryArray(data, key) { for (const item of data) { if (item.hasOwnProperty(key)) { return item[key]; } } return void 0; } const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _hoisted_1$4 = { class: "title" }; const _hoisted_2$4 = { class: "message" }; const _hoisted_3$4 = { key: 0 }; const _hoisted_4$4 = { class: "message" }; const _hoisted_5$3 = { class: "buttons" }; const maxTimeSec = 120; const _sfc_main$5 = { __name: "CommentChecker", setup(__props) { const check2 = vue.inject("check"); const hotBanCheck2 = vue.inject("hotBanCheck"); const commentRecord = vue.inject("commentRecord"); const onClose = vue.inject("onClose"); const onUnblock = vue.inject("onUnblock"); const interval = vue.inject("interval"); const showCancelButton = vue.ref(true); const showConfirmButton = vue.ref(false); const showCloseButton = vue.ref(false); const showHotBanCheckButton = vue.ref(false); const showStopHotBanCheckButton = vue.ref(false); const showLetMeAccessButtton = vue.ref(false); const showHotBanChecker = vue.ref(false); const stateClass = vue.ref("not-check"); const title = vue.ref("等待检查中……"); const message = vue.ref(""); const messageByHotCheck = vue.ref("等待检查中……"); let completed = false; let netErr = false; let hotBanCheckerController = { isCancelled: false }; let skipHotBanCheckWait = false; const stripedFlow = vue.ref(false); const currentTimeSec = vue.ref(0); const percentage = vue.computed(() => { if (currentTimeSec.value < 0 || currentTimeSec.value > maxTimeSec) { return 100; } else { return currentTimeSec.value / maxTimeSec * 100; } }); function format() { if (currentTimeSec.value < 0) { return `--:-- / ${formatSecondsToMMSS(maxTimeSec)}`; } else { return `${formatSecondsToMMSS(currentTimeSec.value)} / ${formatSecondsToMMSS(maxTimeSec)}`; } } let shown = null; let startTime = Date.now() / 1e3; async function startCheck() { while (currentTimeSec.value < maxTimeSec || netErr) { if (!netErr) { for (let i = interval; i > 0; i--) { message.value = "等待 " + i + "s 后检查评论状态"; await sleep(1e3); currentTimeSec.value = Date.now() / 1e3 - startTime; if (completed) { onStateCheckComplete(); return; } } } else { currentTimeSec.value = Date.now() / 1e3 - startTime; if (completed) { onStateCheckComplete(); return; } } message.value = "检查评论状态中……"; stripedFlow.value = true; try { await check2(commentRecord); } catch (err) { netErr = true; title.value = "网络错误,获取当前状态失败"; showConfirmButton.value = false; showCloseButton.value = true; stripedFlow.value = false; console.error(err); continue; } netErr = false; showCancelButton.value = false; showCloseButton.value = false; stripedFlow.value = false; if (commentRecord.currentState == "NORMAL") { title.value = "当前状态:正常"; stateClass.value = "normal"; shown = commentRecord.currentState; } else if (commentRecord.currentState == "SHADOW_BAN") { title.value = "当前状态:仅自己可见"; stateClass.value = "shadow-ban"; shown = commentRecord.currentState; } else if (commentRecord.currentState == "DELETED") { title.value = "当前状态:已被删除"; stateClass.value = "deleted"; if (shown) { completed = true; message.value = `不用等了,你的评论检查到的状态先从『${shown == "NORMAL" ? "正常" : "仅自己可见"}』再到删除,系统偷偷删了无疑。如果不信,你可以尝试编辑评论或添加回复来求证`; onStateCheckComplete(); return; } } showConfirmButton.value = true; } onStateCheckComplete(); completed = true; message.value = "观察时间已足够,当前状态可信,检查完毕"; buttonText.value = "关闭"; } function onStateCheckComplete() { showConfirmButton.value = false; showCloseButton.value = true; if (commentRecord.currentState == "NORMAL") { showHotBanCheckButton.value = true; } else if (commentRecord.currentState == "DELETED") { onUnblock(commentRecord); } } startCheck(); function cancelCheck() { completed = true; onClose(commentRecord); } function confirmCurrentState() { completed = true; message.value = "您已确认当前状态,检查完毕"; } function close() { onClose(commentRecord); } async function checkHotBan() { if (commentRecord.commentId.indexOf(".") == -1) { try { await ElementPlus.ElMessageBox.confirm( "确认检查吗?该检查需要遍历热门评论区,请注意评论区的评论数量(总数大于3000的评论区慎重考虑)!数量太多将导致漫长的检查过程,同时频繁调用API可能会引发不可预料的后果!", "警告", { confirmButtonText: "确定", cancelButtonText: "取消" } ); } catch (err) { return; } } showCloseButton.value = false; showHotBanCheckButton.value = false; showHotBanChecker.value = true; showStopHotBanCheckButton.value = true; while (currentTimeSec.value < maxTimeSec && !hotBanCheckerController.isCancelled) { messageByHotCheck.value = `为避免检查误判,检查需要等待至状态可信任时开始,剩余 ${Math.floor(maxTimeSec - currentTimeSec.value)}s`; if (!skipHotBanCheckWait && currentTimeSec.value > 50) { showLetMeAccessButtton.value = true; } if (skipHotBanCheckWait) { break; } await sleep(1e3); if (hotBanCheckerController.isCancelled) { return; } currentTimeSec.value = Date.now() / 1e3 - startTime; } showLetMeAccessButtton.value = false; messageByHotCheck.value = "正在重新检查评论状态……"; await check2(commentRecord); if (commentRecord.currentState != "NORMAL") { if (commentRecord.currentState == "SHADOW_BAN") { title.value = "当前状态:仅自己可见"; stateClass.value = "shadow-ban"; messageByHotCheck.value = "评论已被ShadowBan,热门的屏蔽的检查已取消"; } else if (commentRecord.currentState == "DELETED") { title.value = "当前状态:已被删除"; stateClass.value = "deleted"; messageByHotCheck.value = "评论已被删除,热门的屏蔽的检查已取消"; } showStopHotBanCheckButton.value = false; showCloseButton.value = true; return; } messageByHotCheck.value = "评论状态正常,准备检查中……"; let observer = { onCountChange(c, p) { messageByHotCheck.value = `正在搜索热门列表,已搜寻至:第${c}个 第${p}页`; } }; if (await hotBanCheck2(commentRecord, observer, hotBanCheckerController)) { if (commentRecord.hotBan) { messageByHotCheck.value = "⚠ 你的评论未在热门列表找到,已被热门屏蔽,检查完成"; } else { messageByHotCheck.value = "✔ 你的评论已在热门列表找到,没有被热门屏蔽,检查完成"; } } showStopHotBanCheckButton.value = false; showCloseButton.value = true; } function stopHotBanCheck() { hotBanCheckerController.isCancelled = true; messageByHotCheck.value = "你已终止热门屏蔽的检查"; showStopHotBanCheckButton.value = false; showCloseButton.value = true; } function letMeAccess() { skipHotBanCheckWait = true; showLetMeAccessButtton.value = false; } return (_ctx, _cache) => { const _component_el_progress = vue.resolveComponent("el-progress"); return vue.openBlock(), vue.createElementBlock("div", { class: vue.normalizeClass(["comment-checker", stateClass.value]) }, [ vue.createElementVNode("div", _hoisted_1$4, vue.toDisplayString(title.value), 1), vue.createElementVNode("div", _hoisted_2$4, vue.toDisplayString(message.value), 1), vue.createVNode(_component_el_progress, { percentage: percentage.value, striped: "", format, "striped-flow": stripedFlow.value }, null, 8, ["percentage", "striped-flow"]), showHotBanChecker.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$4, [ _cache[0] || (_cache[0] = vue.createElementVNode("div", { class: "title" }, "热门屏蔽检查", -1)), vue.createElementVNode("div", _hoisted_4$4, vue.toDisplayString(messageByHotCheck.value), 1) ])) : vue.createCommentVNode("", true), vue.createElementVNode("div", _hoisted_5$3, [ showCancelButton.value ? (vue.openBlock(), vue.createElementBlock("span", { key: 0, onClick: cancelCheck }, "取消")) : vue.createCommentVNode("", true), showConfirmButton.value ? (vue.openBlock(), vue.createElementBlock("span", { key: 1, onClick: confirmCurrentState }, "确认当前状态")) : vue.createCommentVNode("", true), showCloseButton.value ? (vue.openBlock(), vue.createElementBlock("span", { key: 2, onClick: close }, "关闭")) : vue.createCommentVNode("", true), showHotBanCheckButton.value ? (vue.openBlock(), vue.createElementBlock("span", { key: 3, onClick: checkHotBan }, "热门屏蔽检查")) : vue.createCommentVNode("", true), showStopHotBanCheckButton.value ? (vue.openBlock(), vue.createElementBlock("span", { key: 4, onClick: stopHotBanCheck }, "终止检查")) : vue.createCommentVNode("", true), showLetMeAccessButtton.value ? (vue.openBlock(), vue.createElementBlock("span", { key: 5, onClick: letMeAccess }, "让我检查!")) : vue.createCommentVNode("", true) ]) ], 2); }; } }; const CommentChecker = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-684910ce"]]); var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; var indexMinimal = {}; var minimal$1 = {}; var aspromise; var hasRequiredAspromise; function requireAspromise() { if (hasRequiredAspromise) return aspromise; hasRequiredAspromise = 1; aspromise = asPromise; function asPromise(fn, ctx) { var params = new Array(arguments.length - 1), offset = 0, index = 2, pending = true; while (index < arguments.length) params[offset++] = arguments[index++]; return new Promise(function executor(resolve, reject) { params[offset] = function callback(err) { if (pending) { pending = false; if (err) reject(err); else { var params2 = new Array(arguments.length - 1), offset2 = 0; while (offset2 < params2.length) params2[offset2++] = arguments[offset2]; resolve.apply(null, params2); } } }; try { fn.apply(ctx || null, params); } catch (err) { if (pending) { pending = false; reject(err); } } }); } return aspromise; } var base64 = {}; var hasRequiredBase64; function requireBase64() { if (hasRequiredBase64) return base64; hasRequiredBase64 = 1; (function(exports) { var base642 = exports; base642.length = function length(string) { var p = string.length; if (!p) return 0; var n = 0; while (--p % 4 > 1 && string.charAt(p) === "=") ++n; return Math.ceil(string.length * 3) / 4 - n; }; var b64 = new Array(64); var s64 = new Array(123); for (var i = 0; i < 64; ) s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++; base642.encode = function encode(buffer, start, end) { var parts = null, chunk = []; var i2 = 0, j = 0, t; while (start < end) { var b = buffer[start++]; switch (j) { case 0: chunk[i2++] = b64[b >> 2]; t = (b & 3) << 4; j = 1; break; case 1: chunk[i2++] = b64[t | b >> 4]; t = (b & 15) << 2; j = 2; break; case 2: chunk[i2++] = b64[t | b >> 6]; chunk[i2++] = b64[b & 63]; j = 0; break; } if (i2 > 8191) { (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); i2 = 0; } } if (j) { chunk[i2++] = b64[t]; chunk[i2++] = 61; if (j === 1) chunk[i2++] = 61; } if (parts) { if (i2) parts.push(String.fromCharCode.apply(String, chunk.slice(0, i2))); return parts.join(""); } return String.fromCharCode.apply(String, chunk.slice(0, i2)); }; var invalidEncoding = "invalid encoding"; base642.decode = function decode(string, buffer, offset) { var start = offset; var j = 0, t; for (var i2 = 0; i2 < string.length; ) { var c = string.charCodeAt(i2++); if (c === 61 && j > 1) break; if ((c = s64[c]) === void 0) throw Error(invalidEncoding); switch (j) { case 0: t = c; j = 1; break; case 1: buffer[offset++] = t << 2 | (c & 48) >> 4; t = c; j = 2; break; case 2: buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2; t = c; j = 3; break; case 3: buffer[offset++] = (t & 3) << 6 | c; j = 0; break; } } if (j === 1) throw Error(invalidEncoding); return offset - start; }; base642.test = function test(string) { return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(string); }; })(base64); return base64; } var eventemitter; var hasRequiredEventemitter; function requireEventemitter() { if (hasRequiredEventemitter) return eventemitter; hasRequiredEventemitter = 1; eventemitter = EventEmitter; function EventEmitter() { this._listeners = {}; } EventEmitter.prototype.on = function on(evt, fn, ctx) { (this._listeners[evt] || (this._listeners[evt] = [])).push({ fn, ctx: ctx || this }); return this; }; EventEmitter.prototype.off = function off(evt, fn) { if (evt === void 0) this._listeners = {}; else { if (fn === void 0) this._listeners[evt] = []; else { var listeners = this._listeners[evt]; for (var i = 0; i < listeners.length; ) if (listeners[i].fn === fn) listeners.splice(i, 1); else ++i; } } return this; }; EventEmitter.prototype.emit = function emit(evt) { var listeners = this._listeners[evt]; if (listeners) { var args = [], i = 1; for (; i < arguments.length; ) args.push(arguments[i++]); for (i = 0; i < listeners.length; ) listeners[i].fn.apply(listeners[i++].ctx, args); } return this; }; return eventemitter; } var float; var hasRequiredFloat; function requireFloat() { if (hasRequiredFloat) return float; hasRequiredFloat = 1; float = factory(factory); function factory(exports) { if (typeof Float32Array !== "undefined") (function() { var f32 = new Float32Array([-0]), f8b = new Uint8Array(f32.buffer), le = f8b[3] === 128; function writeFloat_f32_cpy(val, buf, pos) { f32[0] = val; buf[pos] = f8b[0]; buf[pos + 1] = f8b[1]; buf[pos + 2] = f8b[2]; buf[pos + 3] = f8b[3]; } function writeFloat_f32_rev(val, buf, pos) { f32[0] = val; buf[pos] = f8b[3]; buf[pos + 1] = f8b[2]; buf[pos + 2] = f8b[1]; buf[pos + 3] = f8b[0]; } exports.writeFloatLE = le ? writeFloat_f32_cpy : writeFloat_f32_rev; exports.writeFloatBE = le ? writeFloat_f32_rev : writeFloat_f32_cpy; function readFloat_f32_cpy(buf, pos) { f8b[0] = buf[pos]; f8b[1] = buf[pos + 1]; f8b[2] = buf[pos + 2]; f8b[3] = buf[pos + 3]; return f32[0]; } function readFloat_f32_rev(buf, pos) { f8b[3] = buf[pos]; f8b[2] = buf[pos + 1]; f8b[1] = buf[pos + 2]; f8b[0] = buf[pos + 3]; return f32[0]; } exports.readFloatLE = le ? readFloat_f32_cpy : readFloat_f32_rev; exports.readFloatBE = le ? readFloat_f32_rev : readFloat_f32_cpy; })(); else (function() { function writeFloat_ieee754(writeUint, val, buf, pos) { var sign = val < 0 ? 1 : 0; if (sign) val = -val; if (val === 0) writeUint(1 / val > 0 ? ( /* positive */ 0 ) : ( /* negative 0 */ 2147483648 ), buf, pos); else if (isNaN(val)) writeUint(2143289344, buf, pos); else if (val > 34028234663852886e22) writeUint((sign << 31 | 2139095040) >>> 0, buf, pos); else if (val < 11754943508222875e-54) writeUint((sign << 31 | Math.round(val / 1401298464324817e-60)) >>> 0, buf, pos); else { var exponent = Math.floor(Math.log(val) / Math.LN2), mantissa = Math.round(val * Math.pow(2, -exponent) * 8388608) & 8388607; writeUint((sign << 31 | exponent + 127 << 23 | mantissa) >>> 0, buf, pos); } } exports.writeFloatLE = writeFloat_ieee754.bind(null, writeUintLE); exports.writeFloatBE = writeFloat_ieee754.bind(null, writeUintBE); function readFloat_ieee754(readUint, buf, pos) { var uint = readUint(buf, pos), sign = (uint >> 31) * 2 + 1, exponent = uint >>> 23 & 255, mantissa = uint & 8388607; return exponent === 255 ? mantissa ? NaN : sign * Infinity : exponent === 0 ? sign * 1401298464324817e-60 * mantissa : sign * Math.pow(2, exponent - 150) * (mantissa + 8388608); } exports.readFloatLE = readFloat_ieee754.bind(null, readUintLE); exports.readFloatBE = readFloat_ieee754.bind(null, readUintBE); })(); if (typeof Float64Array !== "undefined") (function() { var f64 = new Float64Array([-0]), f8b = new Uint8Array(f64.buffer), le = f8b[7] === 128; function writeDouble_f64_cpy(val, buf, pos) { f64[0] = val; buf[pos] = f8b[0]; buf[pos + 1] = f8b[1]; buf[pos + 2] = f8b[2]; buf[pos + 3] = f8b[3]; buf[pos + 4] = f8b[4]; buf[pos + 5] = f8b[5]; buf[pos + 6] = f8b[6]; buf[pos + 7] = f8b[7]; } function writeDouble_f64_rev(val, buf, pos) { f64[0] = val; buf[pos] = f8b[7]; buf[pos + 1] = f8b[6]; buf[pos + 2] = f8b[5]; buf[pos + 3] = f8b[4]; buf[pos + 4] = f8b[3]; buf[pos + 5] = f8b[2]; buf[pos + 6] = f8b[1]; buf[pos + 7] = f8b[0]; } exports.writeDoubleLE = le ? writeDouble_f64_cpy : writeDouble_f64_rev; exports.writeDoubleBE = le ? writeDouble_f64_rev : writeDouble_f64_cpy; function readDouble_f64_cpy(buf, pos) { f8b[0] = buf[pos]; f8b[1] = buf[pos + 1]; f8b[2] = buf[pos + 2]; f8b[3] = buf[pos + 3]; f8b[4] = buf[pos + 4]; f8b[5] = buf[pos + 5]; f8b[6] = buf[pos + 6]; f8b[7] = buf[pos + 7]; return f64[0]; } function readDouble_f64_rev(buf, pos) { f8b[7] = buf[pos]; f8b[6] = buf[pos + 1]; f8b[5] = buf[pos + 2]; f8b[4] = buf[pos + 3]; f8b[3] = buf[pos + 4]; f8b[2] = buf[pos + 5]; f8b[1] = buf[pos + 6]; f8b[0] = buf[pos + 7]; return f64[0]; } exports.readDoubleLE = le ? readDouble_f64_cpy : readDouble_f64_rev; exports.readDoubleBE = le ? readDouble_f64_rev : readDouble_f64_cpy; })(); else (function() { function writeDouble_ieee754(writeUint, off0, off1, val, buf, pos) { var sign = val < 0 ? 1 : 0; if (sign) val = -val; if (val === 0) { writeUint(0, buf, pos + off0); writeUint(1 / val > 0 ? ( /* positive */ 0 ) : ( /* negative 0 */ 2147483648 ), buf, pos + off1); } else if (isNaN(val)) { writeUint(0, buf, pos + off0); writeUint(2146959360, buf, pos + off1); } else if (val > 17976931348623157e292) { writeUint(0, buf, pos + off0); writeUint((sign << 31 | 2146435072) >>> 0, buf, pos + off1); } else { var mantissa; if (val < 22250738585072014e-324) { mantissa = val / 5e-324; writeUint(mantissa >>> 0, buf, pos + off0); writeUint((sign << 31 | mantissa / 4294967296) >>> 0, buf, pos + off1); } else { var exponent = Math.floor(Math.log(val) / Math.LN2); if (exponent === 1024) exponent = 1023; mantissa = val * Math.pow(2, -exponent); writeUint(mantissa * 4503599627370496 >>> 0, buf, pos + off0); writeUint((sign << 31 | exponent + 1023 << 20 | mantissa * 1048576 & 1048575) >>> 0, buf, pos + off1); } } } exports.writeDoubleLE = writeDouble_ieee754.bind(null, writeUintLE, 0, 4); exports.writeDoubleBE = writeDouble_ieee754.bind(null, writeUintBE, 4, 0); function readDouble_ieee754(readUint, off0, off1, buf, pos) { var lo = readUint(buf, pos + off0), hi = readUint(buf, pos + off1); var sign = (hi >> 31) * 2 + 1, exponent = hi >>> 20 & 2047, mantissa = 4294967296 * (hi & 1048575) + lo; return exponent === 2047 ? mantissa ? NaN : sign * Infinity : exponent === 0 ? sign * 5e-324 * mantissa : sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496); } exports.readDoubleLE = readDouble_ieee754.bind(null, readUintLE, 0, 4); exports.readDoubleBE = readDouble_ieee754.bind(null, readUintBE, 4, 0); })(); return exports; } function writeUintLE(val, buf, pos) { buf[pos] = val & 255; buf[pos + 1] = val >>> 8 & 255; buf[pos + 2] = val >>> 16 & 255; buf[pos + 3] = val >>> 24; } function writeUintBE(val, buf, pos) { buf[pos] = val >>> 24; buf[pos + 1] = val >>> 16 & 255; buf[pos + 2] = val >>> 8 & 255; buf[pos + 3] = val & 255; } function readUintLE(buf, pos) { return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16 | buf[pos + 3] << 24) >>> 0; } function readUintBE(buf, pos) { return (buf[pos] << 24 | buf[pos + 1] << 16 | buf[pos + 2] << 8 | buf[pos + 3]) >>> 0; } return float; } var inquire_1; var hasRequiredInquire; function requireInquire() { if (hasRequiredInquire) return inquire_1; hasRequiredInquire = 1; inquire_1 = inquire; function inquire(moduleName) { try { var mod = eval("quire".replace(/^/, "re"))(moduleName); if (mod && (mod.length || Object.keys(mod).length)) return mod; } catch (e) { } return null; } return inquire_1; } var utf8 = {}; var hasRequiredUtf8; function requireUtf8() { if (hasRequiredUtf8) return utf8; hasRequiredUtf8 = 1; (function(exports) { var utf82 = exports; utf82.length = function utf8_length(string) { var len = 0, c = 0; for (var i = 0; i < string.length; ++i) { c = string.charCodeAt(i); if (c < 128) len += 1; else if (c < 2048) len += 2; else if ((c & 64512) === 55296 && (string.charCodeAt(i + 1) & 64512) === 56320) { ++i; len += 4; } else len += 3; } return len; }; utf82.read = function utf8_read(buffer, start, end) { var len = end - start; if (len < 1) return ""; var parts = null, chunk = [], i = 0, t; while (start < end) { t = buffer[start++]; if (t < 128) chunk[i++] = t; else if (t > 191 && t < 224) chunk[i++] = (t & 31) << 6 | buffer[start++] & 63; else if (t > 239 && t < 365) { t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 65536; chunk[i++] = 55296 + (t >> 10); chunk[i++] = 56320 + (t & 1023); } else chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63; if (i > 8191) { (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); i = 0; } } if (parts) { if (i) parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); return parts.join(""); } return String.fromCharCode.apply(String, chunk.slice(0, i)); }; utf82.write = function utf8_write(string, buffer, offset) { var start = offset, c1, c2; for (var i = 0; i < string.length; ++i) { c1 = string.charCodeAt(i); if (c1 < 128) { buffer[offset++] = c1; } else if (c1 < 2048) { buffer[offset++] = c1 >> 6 | 192; buffer[offset++] = c1 & 63 | 128; } else if ((c1 & 64512) === 55296 && ((c2 = string.charCodeAt(i + 1)) & 64512) === 56320) { c1 = 65536 + ((c1 & 1023) << 10) + (c2 & 1023); ++i; buffer[offset++] = c1 >> 18 | 240; buffer[offset++] = c1 >> 12 & 63 | 128; buffer[offset++] = c1 >> 6 & 63 | 128; buffer[offset++] = c1 & 63 | 128; } else { buffer[offset++] = c1 >> 12 | 224; buffer[offset++] = c1 >> 6 & 63 | 128; buffer[offset++] = c1 & 63 | 128; } } return offset - start; }; })(utf8); return utf8; } var pool_1; var hasRequiredPool; function requirePool() { if (hasRequiredPool) return pool_1; hasRequiredPool = 1; pool_1 = pool; function pool(alloc, slice, size) { var SIZE = size || 8192; var MAX = SIZE >>> 1; var slab = null; var offset = SIZE; return function pool_alloc(size2) { if (size2 < 1 || size2 > MAX) return alloc(size2); if (offset + size2 > SIZE) { slab = alloc(SIZE); offset = 0; } var buf = slice.call(slab, offset, offset += size2); if (offset & 7) offset = (offset | 7) + 1; return buf; }; } return pool_1; } var longbits; var hasRequiredLongbits; function requireLongbits() { if (hasRequiredLongbits) return longbits; hasRequiredLongbits = 1; longbits = LongBits; var util = requireMinimal$1(); function LongBits(lo, hi) { this.lo = lo >>> 0; this.hi = hi >>> 0; } var zero = LongBits.zero = new LongBits(0, 0); zero.toNumber = function() { return 0; }; zero.zzEncode = zero.zzDecode = function() { return this; }; zero.length = function() { return 1; }; var zeroHash = LongBits.zeroHash = "\0\0\0\0\0\0\0\0"; LongBits.fromNumber = function fromNumber(value) { if (value === 0) return zero; var sign = value < 0; if (sign) value = -value; var lo = value >>> 0, hi = (value - lo) / 4294967296 >>> 0; if (sign) { hi = ~hi >>> 0; lo = ~lo >>> 0; if (++lo > 4294967295) { lo = 0; if (++hi > 4294967295) hi = 0; } } return new LongBits(lo, hi); }; LongBits.from = function from(value) { if (typeof value === "number") return LongBits.fromNumber(value); if (util.isString(value)) { if (util.Long) value = util.Long.fromString(value); else return LongBits.fromNumber(parseInt(value, 10)); } return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : zero; }; LongBits.prototype.toNumber = function toNumber(unsigned) { if (!unsigned && this.hi >>> 31) { var lo = ~this.lo + 1 >>> 0, hi = ~this.hi >>> 0; if (!lo) hi = hi + 1 >>> 0; return -(lo + hi * 4294967296); } return this.lo + this.hi * 4294967296; }; LongBits.prototype.toLong = function toLong(unsigned) { return util.Long ? new util.Long(this.lo | 0, this.hi | 0, Boolean(unsigned)) : { low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned) }; }; var charCodeAt = String.prototype.charCodeAt; LongBits.fromHash = function fromHash(hash) { if (hash === zeroHash) return zero; return new LongBits( (charCodeAt.call(hash, 0) | charCodeAt.call(hash, 1) << 8 | charCodeAt.call(hash, 2) << 16 | charCodeAt.call(hash, 3) << 24) >>> 0, (charCodeAt.call(hash, 4) | charCodeAt.call(hash, 5) << 8 | charCodeAt.call(hash, 6) << 16 | charCodeAt.call(hash, 7) << 24) >>> 0 ); }; LongBits.prototype.toHash = function toHash() { return String.fromCharCode( this.lo & 255, this.lo >>> 8 & 255, this.lo >>> 16 & 255, this.lo >>> 24, this.hi & 255, this.hi >>> 8 & 255, this.hi >>> 16 & 255, this.hi >>> 24 ); }; LongBits.prototype.zzEncode = function zzEncode() { var mask = this.hi >> 31; this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0; this.lo = (this.lo << 1 ^ mask) >>> 0; return this; }; LongBits.prototype.zzDecode = function zzDecode() { var mask = -(this.lo & 1); this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0; this.hi = (this.hi >>> 1 ^ mask) >>> 0; return this; }; LongBits.prototype.length = function length() { var part0 = this.lo, part1 = (this.lo >>> 28 | this.hi << 4) >>> 0, part2 = this.hi >>> 24; return part2 === 0 ? part1 === 0 ? part0 < 16384 ? part0 < 128 ? 1 : 2 : part0 < 2097152 ? 3 : 4 : part1 < 16384 ? part1 < 128 ? 5 : 6 : part1 < 2097152 ? 7 : 8 : part2 < 128 ? 9 : 10; }; return longbits; } var hasRequiredMinimal$1; function requireMinimal$1() { if (hasRequiredMinimal$1) return minimal$1; hasRequiredMinimal$1 = 1; (function(exports) { var util = exports; util.asPromise = requireAspromise(); util.base64 = requireBase64(); util.EventEmitter = requireEventemitter(); util.float = requireFloat(); util.inquire = requireInquire(); util.utf8 = requireUtf8(); util.pool = requirePool(); util.LongBits = requireLongbits(); util.isNode = Boolean(typeof commonjsGlobal !== "undefined" && commonjsGlobal && commonjsGlobal.process && commonjsGlobal.process.versions && commonjsGlobal.process.versions.node); util.global = util.isNode && commonjsGlobal || typeof window !== "undefined" && window || typeof self !== "undefined" && self || minimal$1; util.emptyArray = Object.freeze ? Object.freeze([]) : ( /* istanbul ignore next */ [] ); util.emptyObject = Object.freeze ? Object.freeze({}) : ( /* istanbul ignore next */ {} ); util.isInteger = Number.isInteger || /* istanbul ignore next */ function isInteger(value) { return typeof value === "number" && isFinite(value) && Math.floor(value) === value; }; util.isString = function isString(value) { return typeof value === "string" || value instanceof String; }; util.isObject = function isObject(value) { return value && typeof value === "object"; }; util.isset = /** * Checks if a property on a message is considered to be present. * @param {Object} obj Plain object or message instance * @param {string} prop Property name * @returns {boolean} `true` if considered to be present, otherwise `false` */ util.isSet = function isSet(obj, prop) { var value = obj[prop]; if (value != null && obj.hasOwnProperty(prop)) return typeof value !== "object" || (Array.isArray(value) ? value.length : Object.keys(value).length) > 0; return false; }; util.Buffer = function() { try { var Buffer = util.inquire("buffer").Buffer; return Buffer.prototype.utf8Write ? Buffer : ( /* istanbul ignore next */ null ); } catch (e) { return null; } }(); util._Buffer_from = null; util._Buffer_allocUnsafe = null; util.newBuffer = function newBuffer(sizeOrArray) { return typeof sizeOrArray === "number" ? util.Buffer ? util._Buffer_allocUnsafe(sizeOrArray) : new util.Array(sizeOrArray) : util.Buffer ? util._Buffer_from(sizeOrArray) : typeof Uint8Array === "undefined" ? sizeOrArray : new Uint8Array(sizeOrArray); }; util.Array = typeof Uint8Array !== "undefined" ? Uint8Array : Array; util.Long = /* istanbul ignore next */ util.global.dcodeIO && /* istanbul ignore next */ util.global.dcodeIO.Long || /* istanbul ignore next */ util.global.Long || util.inquire("long"); util.key2Re = /^true|false|0|1$/; util.key32Re = /^-?(?:0|[1-9][0-9]*)$/; util.key64Re = /^(?:[\\x00-\\xff]{8}|-?(?:0|[1-9][0-9]*))$/; util.longToHash = function longToHash(value) { return value ? util.LongBits.from(value).toHash() : util.LongBits.zeroHash; }; util.longFromHash = function longFromHash(hash, unsigned) { var bits = util.LongBits.fromHash(hash); if (util.Long) return util.Long.fromBits(bits.lo, bits.hi, unsigned); return bits.toNumber(Boolean(unsigned)); }; function merge(dst, src, ifNotSet) { for (var keys = Object.keys(src), i = 0; i < keys.length; ++i) if (dst[keys[i]] === void 0 || !ifNotSet) dst[keys[i]] = src[keys[i]]; return dst; } util.merge = merge; util.lcFirst = function lcFirst(str) { return str.charAt(0).toLowerCase() + str.substring(1); }; function newError(name) { function CustomError(message, properties) { if (!(this instanceof CustomError)) return new CustomError(message, properties); Object.defineProperty(this, "message", { get: function() { return message; } }); if (Error.captureStackTrace) Error.captureStackTrace(this, CustomError); else Object.defineProperty(this, "stack", { value: new Error().stack || "" }); if (properties) merge(this, properties); } CustomError.prototype = Object.create(Error.prototype, { constructor: { value: CustomError, writable: true, enumerable: false, configurable: true }, name: { get: function get() { return name; }, set: void 0, enumerable: false, // configurable: false would accurately preserve the behavior of // the original, but I'm guessing that was not intentional. // For an actual error subclass, this property would // be configurable. configurable: true }, toString: { value: function value() { return this.name + ": " + this.message; }, writable: true, enumerable: false, configurable: true } }); return CustomError; } util.newError = newError; util.ProtocolError = newError("ProtocolError"); util.oneOfGetter = function getOneOf(fieldNames) { var fieldMap = {}; for (var i = 0; i < fieldNames.length; ++i) fieldMap[fieldNames[i]] = 1; return function() { for (var keys = Object.keys(this), i2 = keys.length - 1; i2 > -1; --i2) if (fieldMap[keys[i2]] === 1 && this[keys[i2]] !== void 0 && this[keys[i2]] !== null) return keys[i2]; }; }; util.oneOfSetter = function setOneOf(fieldNames) { return function(name) { for (var i = 0; i < fieldNames.length; ++i) if (fieldNames[i] !== name) delete this[fieldNames[i]]; }; }; util.toJSONOptions = { longs: String, enums: String, bytes: String, json: true }; util._configure = function() { var Buffer = util.Buffer; if (!Buffer) { util._Buffer_from = util._Buffer_allocUnsafe = null; return; } util._Buffer_from = Buffer.from !== Uint8Array.from && Buffer.from || /* istanbul ignore next */ function Buffer_from(value, encoding) { return new Buffer(value, encoding); }; util._Buffer_allocUnsafe = Buffer.allocUnsafe || /* istanbul ignore next */ function Buffer_allocUnsafe(size) { return new Buffer(size); }; }; })(minimal$1); return minimal$1; } var writer; var hasRequiredWriter; function requireWriter() { if (hasRequiredWriter) return writer; hasRequiredWriter = 1; writer = Writer; var util = requireMinimal$1(); var BufferWriter; var LongBits = util.LongBits, base642 = util.base64, utf82 = util.utf8; function Op(fn, len, val) { this.fn = fn; this.len = len; this.next = void 0; this.val = val; } function noop() { } function State(writer2) { this.head = writer2.head; this.tail = writer2.tail; this.len = writer2.len; this.next = writer2.states; } function Writer() { this.len = 0; this.head = new Op(noop, 0, 0); this.tail = this.head; this.states = null; } var create = function create2() { return util.Buffer ? function create_buffer_setup() { return (Writer.create = function create_buffer() { return new BufferWriter(); })(); } : function create_array() { return new Writer(); }; }; Writer.create = create(); Writer.alloc = function alloc(size) { return new util.Array(size); }; if (util.Array !== Array) Writer.alloc = util.pool(Writer.alloc, util.Array.prototype.subarray); Writer.prototype._push = function push(fn, len, val) { this.tail = this.tail.next = new Op(fn, len, val); this.len += len; return this; }; function writeByte(val, buf, pos) { buf[pos] = val & 255; } function writeVarint32(val, buf, pos) { while (val > 127) { buf[pos++] = val & 127 | 128; val >>>= 7; } buf[pos] = val; } function VarintOp(len, val) { this.len = len; this.next = void 0; this.val = val; } VarintOp.prototype = Object.create(Op.prototype); VarintOp.prototype.fn = writeVarint32; Writer.prototype.uint32 = function write_uint32(value) { this.len += (this.tail = this.tail.next = new VarintOp( (value = value >>> 0) < 128 ? 1 : value < 16384 ? 2 : value < 2097152 ? 3 : value < 268435456 ? 4 : 5, value )).len; return this; }; Writer.prototype.int32 = function write_int32(value) { return value < 0 ? this._push(writeVarint64, 10, LongBits.fromNumber(value)) : this.uint32(value); }; Writer.prototype.sint32 = function write_sint32(value) { return this.uint32((value << 1 ^ value >> 31) >>> 0); }; function writeVarint64(val, buf, pos) { while (val.hi) { buf[pos++] = val.lo & 127 | 128; val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0; val.hi >>>= 7; } while (val.lo > 127) { buf[pos++] = val.lo & 127 | 128; val.lo = val.lo >>> 7; } buf[pos++] = val.lo; } Writer.prototype.uint64 = function write_uint64(value) { var bits = LongBits.from(value); return this._push(writeVarint64, bits.length(), bits); }; Writer.prototype.int64 = Writer.prototype.uint64; Writer.prototype.sint64 = function write_sint64(value) { var bits = LongBits.from(value).zzEncode(); return this._push(writeVarint64, bits.length(), bits); }; Writer.prototype.bool = function write_bool(value) { return this._push(writeByte, 1, value ? 1 : 0); }; function writeFixed32(val, buf, pos) { buf[pos] = val & 255; buf[pos + 1] = val >>> 8 & 255; buf[pos + 2] = val >>> 16 & 255; buf[pos + 3] = val >>> 24; } Writer.prototype.fixed32 = function write_fixed32(value) { return this._push(writeFixed32, 4, value >>> 0); }; Writer.prototype.sfixed32 = Writer.prototype.fixed32; Writer.prototype.fixed64 = function write_fixed64(value) { var bits = LongBits.from(value); return this._push(writeFixed32, 4, bits.lo)._push(writeFixed32, 4, bits.hi); }; Writer.prototype.sfixed64 = Writer.prototype.fixed64; Writer.prototype.float = function write_float(value) { return this._push(util.float.writeFloatLE, 4, value); }; Writer.prototype.double = function write_double(value) { return this._push(util.float.writeDoubleLE, 8, value); }; var writeBytes = util.Array.prototype.set ? function writeBytes_set(val, buf, pos) { buf.set(val, pos); } : function writeBytes_for(val, buf, pos) { for (var i = 0; i < val.length; ++i) buf[pos + i] = val[i]; }; Writer.prototype.bytes = function write_bytes(value) { var len = value.length >>> 0; if (!len) return this._push(writeByte, 1, 0); if (util.isString(value)) { var buf = Writer.alloc(len = base642.length(value)); base642.decode(value, buf, 0); value = buf; } return this.uint32(len)._push(writeBytes, len, value); }; Writer.prototype.string = function write_string(value) { var len = utf82.length(value); return len ? this.uint32(len)._push(utf82.write, len, value) : this._push(writeByte, 1, 0); }; Writer.prototype.fork = function fork() { this.states = new State(this); this.head = this.tail = new Op(noop, 0, 0); this.len = 0; return this; }; Writer.prototype.reset = function reset() { if (this.states) { this.head = this.states.head; this.tail = this.states.tail; this.len = this.states.len; this.states = this.states.next; } else { this.head = this.tail = new Op(noop, 0, 0); this.len = 0; } return this; }; Writer.prototype.ldelim = function ldelim() { var head = this.head, tail = this.tail, len = this.len; this.reset().uint32(len); if (len) { this.tail.next = head.next; this.tail = tail; this.len += len; } return this; }; Writer.prototype.finish = function finish() { var head = this.head.next, buf = this.constructor.alloc(this.len), pos = 0; while (head) { head.fn(head.val, buf, pos); pos += head.len; head = head.next; } return buf; }; Writer._configure = function(BufferWriter_) { BufferWriter = BufferWriter_; Writer.create = create(); BufferWriter._configure(); }; return writer; } var writer_buffer; var hasRequiredWriter_buffer; function requireWriter_buffer() { if (hasRequiredWriter_buffer) return writer_buffer; hasRequiredWriter_buffer = 1; writer_buffer = BufferWriter; var Writer = requireWriter(); (BufferWriter.prototype = Object.create(Writer.prototype)).constructor = BufferWriter; var util = requireMinimal$1(); function BufferWriter() { Writer.call(this); } BufferWriter._configure = function() { BufferWriter.alloc = util._Buffer_allocUnsafe; BufferWriter.writeBytesBuffer = util.Buffer && util.Buffer.prototype instanceof Uint8Array && util.Buffer.prototype.set.name === "set" ? function writeBytesBuffer_set(val, buf, pos) { buf.set(val, pos); } : function writeBytesBuffer_copy(val, buf, pos) { if (val.copy) val.copy(buf, pos, 0, val.length); else for (var i = 0; i < val.length; ) buf[pos++] = val[i++]; }; }; BufferWriter.prototype.bytes = function write_bytes_buffer(value) { if (util.isString(value)) value = util._Buffer_from(value, "base64"); var len = value.length >>> 0; this.uint32(len); if (len) this._push(BufferWriter.writeBytesBuffer, len, value); return this; }; function writeStringBuffer(val, buf, pos) { if (val.length < 40) util.utf8.write(val, buf, pos); else if (buf.utf8Write) buf.utf8Write(val, pos); else buf.write(val, pos); } BufferWriter.prototype.string = function write_string_buffer(value) { var len = util.Buffer.byteLength(value); this.uint32(len); if (len) this._push(writeStringBuffer, len, value); return this; }; BufferWriter._configure(); return writer_buffer; } var reader; var hasRequiredReader; function requireReader() { if (hasRequiredReader) return reader; hasRequiredReader = 1; reader = Reader; var util = requireMinimal$1(); var BufferReader; var LongBits = util.LongBits, utf82 = util.utf8; function indexOutOfRange(reader2, writeLength) { return RangeError("index out of range: " + reader2.pos + " + " + (writeLength || 1) + " > " + reader2.len); } function Reader(buffer) { this.buf = buffer; this.pos = 0; this.len = buffer.length; } var create_array = typeof Uint8Array !== "undefined" ? function create_typed_array(buffer) { if (buffer instanceof Uint8Array || Array.isArray(buffer)) return new Reader(buffer); throw Error("illegal buffer"); } : function create_array2(buffer) { if (Array.isArray(buffer)) return new Reader(buffer); throw Error("illegal buffer"); }; var create = function create2() { return util.Buffer ? function create_buffer_setup(buffer) { return (Reader.create = function create_buffer(buffer2) { return util.Buffer.isBuffer(buffer2) ? new BufferReader(buffer2) : create_array(buffer2); })(buffer); } : create_array; }; Reader.create = create(); Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice; Reader.prototype.uint32 = /* @__PURE__ */ function read_uint32_setup() { var value = 4294967295; return function read_uint32() { value = (this.buf[this.pos] & 127) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value; if ((this.pos += 5) > this.len) { this.pos = this.len; throw indexOutOfRange(this, 10); } return value; }; }(); Reader.prototype.int32 = function read_int32() { return this.uint32() | 0; }; Reader.prototype.sint32 = function read_sint32() { var value = this.uint32(); return value >>> 1 ^ -(value & 1) | 0; }; function readLongVarint() { var bits = new LongBits(0, 0); var i = 0; if (this.len - this.pos > 4) { for (; i < 4; ++i) { bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; if (this.buf[this.pos++] < 128) return bits; } bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0; bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0; if (this.buf[this.pos++] < 128) return bits; i = 0; } else { for (; i < 3; ++i) { if (this.pos >= this.len) throw indexOutOfRange(this); bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; if (this.buf[this.pos++] < 128) return bits; } bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0; return bits; } if (this.len - this.pos > 4) { for (; i < 5; ++i) { bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; if (this.buf[this.pos++] < 128) return bits; } } else { for (; i < 5; ++i) { if (this.pos >= this.len) throw indexOutOfRange(this); bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; if (this.buf[this.pos++] < 128) return bits; } } throw Error("invalid varint encoding"); } Reader.prototype.bool = function read_bool() { return this.uint32() !== 0; }; function readFixed32_end(buf, end) { return (buf[end - 4] | buf[end - 3] << 8 | buf[end - 2] << 16 | buf[end - 1] << 24) >>> 0; } Reader.prototype.fixed32 = function read_fixed32() { if (this.pos + 4 > this.len) throw indexOutOfRange(this, 4); return readFixed32_end(this.buf, this.pos += 4); }; Reader.prototype.sfixed32 = function read_sfixed32() { if (this.pos + 4 > this.len) throw indexOutOfRange(this, 4); return readFixed32_end(this.buf, this.pos += 4) | 0; }; function readFixed64() { if (this.pos + 8 > this.len) throw indexOutOfRange(this, 8); return new LongBits(readFixed32_end(this.buf, this.pos += 4), readFixed32_end(this.buf, this.pos += 4)); } Reader.prototype.float = function read_float() { if (this.pos + 4 > this.len) throw indexOutOfRange(this, 4); var value = util.float.readFloatLE(this.buf, this.pos); this.pos += 4; return value; }; Reader.prototype.double = function read_double() { if (this.pos + 8 > this.len) throw indexOutOfRange(this, 4); var value = util.float.readDoubleLE(this.buf, this.pos); this.pos += 8; return value; }; Reader.prototype.bytes = function read_bytes() { var length = this.uint32(), start = this.pos, end = this.pos + length; if (end > this.len) throw indexOutOfRange(this, length); this.pos += length; if (Array.isArray(this.buf)) return this.buf.slice(start, end); if (start === end) { var nativeBuffer = util.Buffer; return nativeBuffer ? nativeBuffer.alloc(0) : new this.buf.constructor(0); } return this._slice.call(this.buf, start, end); }; Reader.prototype.string = function read_string() { var bytes = this.bytes(); return utf82.read(bytes, 0, bytes.length); }; Reader.prototype.skip = function skip(length) { if (typeof length === "number") { if (this.pos + length > this.len) throw indexOutOfRange(this, length); this.pos += length; } else { do { if (this.pos >= this.len) throw indexOutOfRange(this); } while (this.buf[this.pos++] & 128); } return this; }; Reader.prototype.skipType = function(wireType) { switch (wireType) { case 0: this.skip(); break; case 1: this.skip(8); break; case 2: this.skip(this.uint32()); break; case 3: while ((wireType = this.uint32() & 7) !== 4) { this.skipType(wireType); } break; case 5: this.skip(4); break; /* istanbul ignore next */ default: throw Error("invalid wire type " + wireType + " at offset " + this.pos); } return this; }; Reader._configure = function(BufferReader_) { BufferReader = BufferReader_; Reader.create = create(); BufferReader._configure(); var fn = util.Long ? "toLong" : ( /* istanbul ignore next */ "toNumber" ); util.merge(Reader.prototype, { int64: function read_int64() { return readLongVarint.call(this)[fn](false); }, uint64: function read_uint64() { return readLongVarint.call(this)[fn](true); }, sint64: function read_sint64() { return readLongVarint.call(this).zzDecode()[fn](false); }, fixed64: function read_fixed64() { return readFixed64.call(this)[fn](true); }, sfixed64: function read_sfixed64() { return readFixed64.call(this)[fn](false); } }); }; return reader; } var reader_buffer; var hasRequiredReader_buffer; function requireReader_buffer() { if (hasRequiredReader_buffer) return reader_buffer; hasRequiredReader_buffer = 1; reader_buffer = BufferReader; var Reader = requireReader(); (BufferReader.prototype = Object.create(Reader.prototype)).constructor = BufferReader; var util = requireMinimal$1(); function BufferReader(buffer) { Reader.call(this, buffer); } BufferReader._configure = function() { if (util.Buffer) BufferReader.prototype._slice = util.Buffer.prototype.slice; }; BufferReader.prototype.string = function read_string_buffer() { var len = this.uint32(); return this.buf.utf8Slice ? this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len)) : this.buf.toString("utf-8", this.pos, this.pos = Math.min(this.pos + len, this.len)); }; BufferReader._configure(); return reader_buffer; } var rpc = {}; var service; var hasRequiredService; function requireService() { if (hasRequiredService) return service; hasRequiredService = 1; service = Service; var util = requireMinimal$1(); (Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service; function Service(rpcImpl, requestDelimited, responseDelimited) { if (typeof rpcImpl !== "function") throw TypeError("rpcImpl must be a function"); util.EventEmitter.call(this); this.rpcImpl = rpcImpl; this.requestDelimited = Boolean(requestDelimited); this.responseDelimited = Boolean(responseDelimited); } Service.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) { if (!request) throw TypeError("request must be specified"); var self2 = this; if (!callback) return util.asPromise(rpcCall, self2, method, requestCtor, responseCtor, request); if (!self2.rpcImpl) { setTimeout(function() { callback(Error("already ended")); }, 0); return void 0; } try { return self2.rpcImpl( method, requestCtor[self2.requestDelimited ? "encodeDelimited" : "encode"](request).finish(), function rpcCallback(err, response) { if (err) { self2.emit("error", err, method); return callback(err); } if (response === null) { self2.end( /* endedByRPC */ true ); return void 0; } if (!(response instanceof responseCtor)) { try { response = responseCtor[self2.responseDelimited ? "decodeDelimited" : "decode"](response); } catch (err2) { self2.emit("error", err2, method); return callback(err2); } } self2.emit("data", response, method); return callback(null, response); } ); } catch (err) { self2.emit("error", err, method); setTimeout(function() { callback(err); }, 0); return void 0; } }; Service.prototype.end = function end(endedByRPC) { if (this.rpcImpl) { if (!endedByRPC) this.rpcImpl(null, null, null); this.rpcImpl = null; this.emit("end").off(); } return this; }; return service; } var hasRequiredRpc; function requireRpc() { if (hasRequiredRpc) return rpc; hasRequiredRpc = 1; (function(exports) { var rpc2 = exports; rpc2.Service = requireService(); })(rpc); return rpc; } var roots; var hasRequiredRoots; function requireRoots() { if (hasRequiredRoots) return roots; hasRequiredRoots = 1; roots = {}; return roots; } var hasRequiredIndexMinimal; function requireIndexMinimal() { if (hasRequiredIndexMinimal) return indexMinimal; hasRequiredIndexMinimal = 1; (function(exports) { var protobuf = exports; protobuf.build = "minimal"; protobuf.Writer = requireWriter(); protobuf.BufferWriter = requireWriter_buffer(); protobuf.Reader = requireReader(); protobuf.BufferReader = requireReader_buffer(); protobuf.util = requireMinimal$1(); protobuf.rpc = requireRpc(); protobuf.roots = requireRoots(); protobuf.configure = configure; function configure() { protobuf.util._configure(); protobuf.Writer._configure(protobuf.BufferWriter); protobuf.Reader._configure(protobuf.BufferReader); } configure(); })(indexMinimal); return indexMinimal; } var minimal; var hasRequiredMinimal; function requireMinimal() { if (hasRequiredMinimal) return minimal; hasRequiredMinimal = 1; minimal = requireIndexMinimal(); return minimal; } var minimalExports = requireMinimal(); const $Writer = minimalExports.Writer; const $root$2 = minimalExports.roots["default"] || (minimalExports.roots["default"] = {}); const NextContinuation = $root$2.NextContinuation = (() => { function NextContinuation2(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } NextContinuation2.prototype.commentAreaWrapper = null; NextContinuation2.prototype.uField3 = 0; NextContinuation2.prototype.mainCommentRequest = null; NextContinuation2.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.commentAreaWrapper != null && Object.hasOwnProperty.call(m, "commentAreaWrapper")) $root$2.CommentAreaWrapper.encode(m.commentAreaWrapper, w.uint32(18).fork()).ldelim(); if (m.uField3 != null && Object.hasOwnProperty.call(m, "uField3")) w.uint32(24).int32(m.uField3); if (m.mainCommentRequest != null && Object.hasOwnProperty.call(m, "mainCommentRequest")) $root$2.MainCommentRequest.encode(m.mainCommentRequest, w.uint32(50).fork()).ldelim(); return w; }; return NextContinuation2; })(); $root$2.CommentAreaWrapper = (() => { function CommentAreaWrapper(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } CommentAreaWrapper.prototype.videoId = ""; CommentAreaWrapper.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.videoId != null && Object.hasOwnProperty.call(m, "videoId")) w.uint32(18).string(m.videoId); return w; }; return CommentAreaWrapper; })(); $root$2.MainCommentRequest = (() => { function MainCommentRequest(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } MainCommentRequest.prototype.commentParameters = null; MainCommentRequest.prototype.commentReplyParameters = null; MainCommentRequest.prototype.sectionIdentifier = ""; MainCommentRequest.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.commentReplyParameters != null && Object.hasOwnProperty.call(m, "commentReplyParameters")) $root$2.CommentReplyParameters.encode(m.commentReplyParameters, w.uint32(26).fork()).ldelim(); if (m.commentParameters != null && Object.hasOwnProperty.call(m, "commentParameters")) $root$2.CommentParameters.encode(m.commentParameters, w.uint32(34).fork()).ldelim(); if (m.sectionIdentifier != null && Object.hasOwnProperty.call(m, "sectionIdentifier")) w.uint32(66).string(m.sectionIdentifier); return w; }; return MainCommentRequest; })(); $root$2.CommentParameters = (() => { function CommentParameters(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } CommentParameters.prototype.videoId = ""; CommentParameters.prototype.postId = ""; CommentParameters.prototype.channelId = ""; CommentParameters.prototype.sortType = 0; CommentParameters.prototype.targetCommentId = ""; CommentParameters.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.videoId != null && Object.hasOwnProperty.call(m, "videoId")) w.uint32(34).string(m.videoId); if (m.sortType != null && Object.hasOwnProperty.call(m, "sortType")) w.uint32(48).int32(m.sortType); if (m.targetCommentId != null && Object.hasOwnProperty.call(m, "targetCommentId")) w.uint32(130).string(m.targetCommentId); if (m.postId != null && Object.hasOwnProperty.call(m, "postId")) w.uint32(234).string(m.postId); if (m.channelId != null && Object.hasOwnProperty.call(m, "channelId")) w.uint32(242).string(m.channelId); return w; }; CommentParameters.SortType = function() { const valuesById = {}, values = Object.create(valuesById); values[valuesById[0] = "HOT"] = 0; values[valuesById[1] = "LATEST"] = 1; return values; }(); return CommentParameters; })(); $root$2.CommentReplyParameters = (() => { function CommentReplyParameters(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } CommentReplyParameters.prototype.rootCommentId = ""; CommentReplyParameters.prototype.channelId = ""; CommentReplyParameters.prototype.videoId = ""; CommentReplyParameters.prototype.postId = ""; CommentReplyParameters.prototype.pageSize = 0; CommentReplyParameters.prototype.sortParam = null; CommentReplyParameters.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.rootCommentId != null && Object.hasOwnProperty.call(m, "rootCommentId")) w.uint32(18).string(m.rootCommentId); if (m.channelId != null && Object.hasOwnProperty.call(m, "channelId")) w.uint32(42).string(m.channelId); if (m.videoId != null && Object.hasOwnProperty.call(m, "videoId")) w.uint32(50).string(m.videoId); if (m.pageSize != null && Object.hasOwnProperty.call(m, "pageSize")) w.uint32(72).int32(m.pageSize); if (m.postId != null && Object.hasOwnProperty.call(m, "postId")) w.uint32(122).string(m.postId); if (m.sortParam != null && Object.hasOwnProperty.call(m, "sortParam")) $root$2.CommentReplyParameters.SortParam.encode(m.sortParam, w.uint32(130).fork()).ldelim(); return w; }; CommentReplyParameters.SortParam = function() { function SortParam(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } SortParam.prototype.sortType = 0; SortParam.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.sortType != null && Object.hasOwnProperty.call(m, "sortType")) w.uint32(8).int32(m.sortType); return w; }; SortParam.SortType = function() { const valuesById = {}, values = Object.create(valuesById); values[valuesById[0] = "DEFAULT"] = 0; values[valuesById[1] = "HOT"] = 1; values[valuesById[2] = "LATEST"] = 2; return values; }(); return SortParam; }(); return CommentReplyParameters; })(); const BrowserContinuation = $root$2.BrowserContinuation = (() => { function BrowserContinuation2(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } BrowserContinuation2.prototype.request = null; BrowserContinuation2.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.request != null && Object.hasOwnProperty.call(m, "request")) $root$2.BrowserContinuation.Request.encode(m.request, w.uint32(641815778).fork()).ldelim(); return w; }; BrowserContinuation2.Request = function() { function Request(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } Request.prototype.description = ""; Request.prototype.continuationBase64 = ""; Request.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.description != null && Object.hasOwnProperty.call(m, "description")) w.uint32(18).string(m.description); if (m.continuationBase64 != null && Object.hasOwnProperty.call(m, "continuationBase64")) w.uint32(26).string(m.continuationBase64); return w; }; return Request; }(); return BrowserContinuation2; })(); const BrowserCommentListContinuation = $root$2.BrowserCommentListContinuation = (() => { function BrowserCommentListContinuation2(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } BrowserCommentListContinuation2.prototype.description = ""; BrowserCommentListContinuation2.prototype.mainCommentRequest = null; BrowserCommentListContinuation2.encode = function encode(m, w) { if (!w) w = $Writer.create(); if (m.description != null && Object.hasOwnProperty.call(m, "description")) w.uint32(18).string(m.description); if (m.mainCommentRequest != null && Object.hasOwnProperty.call(m, "mainCommentRequest")) $root$2.MainCommentRequest.encode(m.mainCommentRequest, w.uint32(426).fork()).ldelim(); return w; }; return BrowserCommentListContinuation2; })(); const $Reader$1 = minimalExports.Reader; const $root$1 = minimalExports.roots["default"] || (minimalExports.roots["default"] = {}); const CommentAction = $root$1.CommentAction = (() => { function CommentAction2(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } CommentAction2.prototype.action = 0; CommentAction2.prototype.commentId = ""; CommentAction2.decode = function decode(r, l, e) { if (!(r instanceof $Reader$1)) r = $Reader$1.create(r); var c = l === void 0 ? r.len : r.pos + l, m = new $root$1.CommentAction(); while (r.pos < c) { var t = r.uint32(); if (t === e) break; switch (t >>> 3) { case 1: { m.action = r.int32(); break; } case 3: { m.commentId = r.string(); break; } default: r.skipType(t & 7); break; } } return m; }; return CommentAction2; })(); $root$1.Action = (() => { const valuesById = {}, values = Object.create(valuesById); values[valuesById[0] = "DEFAULT"] = 0; values[valuesById[5] = "LIKE"] = 5; values[valuesById[6] = "DELETE"] = 6; return values; })(); const $Reader = minimalExports.Reader; const $root = minimalExports.roots["default"] || (minimalExports.roots["default"] = {}); const UpdateCommentParams = $root.UpdateCommentParams = (() => { function UpdateCommentParams2(p) { if (p) { for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) if (p[ks[i]] != null) this[ks[i]] = p[ks[i]]; } } UpdateCommentParams2.prototype.commentId = ""; UpdateCommentParams2.decode = function decode(r, l, e) { if (!(r instanceof $Reader)) r = $Reader.create(r); var c = l === void 0 ? r.len : r.pos + l, m = new $root.UpdateCommentParams(); while (r.pos < c) { var t = r.uint32(); if (t === e) break; switch (t >>> 3) { case 1: { m.commentId = r.string(); break; } default: r.skipType(t & 7); break; } } return m; }; return UpdateCommentParams2; })(); var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)(); var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)(); const cssLoader = (e) => { const t = GM_getResourceText(e); return GM_addStyle(t), t; }; cssLoader("element-plus/dist/index.css"); const _hoisted_1$3 = { key: 0, class: "hot-ban-checker" }; const _hoisted_2$3 = { class: "message" }; const _hoisted_3$3 = { class: "buttons" }; const _hoisted_4$3 = { class: "actions" }; const _sfc_main$4 = { __name: "CommentActions", props: ["comment"], emits: ["delete", "checkHotBan"], setup(__props, { emit: __emit }) { const props = __props; const emit = __emit; const comment = props.comment; const check2 = vue.inject("check"); const hotBanCheck2 = vue.inject("hotBanCheck"); const updating = vue.ref(false); const showHotBanChecker = vue.ref(false); const hotBanCheckerMessage = vue.ref("等待检查中……"); let hotBanCheckerController = vue.reactive({ isCancelled: false }); function updateState() { updating.value = true; check2(comment).then(() => { updating.value = false; ElementPlus.ElMessage({ type: comment.currentState == "NORMAL" ? "success" : "warning", message: "更新成功,当前状态:" + translateState(comment.currentState) }); }).catch((err) => { updating.value = false; let msg = err.message; if (msg == "COMMENT_AREA_CLOSED") { msg = "评论区已关闭"; } ElementPlus.ElMessage.error("更新失败,因为:" + msg); }); } function copyComment(commentText) { if (navigator.clipboard) { navigator.clipboard.writeText(commentText).then(() => { ElementPlus.ElMessage({ message: "评论已复制到剪贴板", type: "success" }); }).catch((err) => { ElementPlus.ElMessage.error("无法复制文本,因为: " + err); }); } else { const textArea = document.createElement("textarea"); textArea.value = commentText; document.body.appendChild(textArea); textArea.select(); try { document.execCommand("copy"); ElementPlus.ElMessage({ message: "评论已复制到剪贴板", type: "success" }); } catch (err) { ElementPlus.ElMessage.error("无法复制文本,因为: " + err); } document.body.removeChild(textArea); } } function askDelete() { ElementPlus.ElMessageBox.confirm("确定要删除这条记录吗(这不会删除你在YouTube上发布的评论)?删除操作无法撤销!").then(() => { emit("delete"); }).catch(() => { }); } async function toHotBanCheck() { if (Date.now() - comment.recordedTime < 120 * 1e3) { ElementPlus.ElMessage.warning(`当前时间距评论记录时间不足2分钟,状态不可信,请到 ${formatTimestamp(comment.recordedTime + 120 * 1e3)} 来检查`); return; } if (comment.commentId.indexOf(".") == -1) { try { await ElementPlus.ElMessageBox.confirm( "确认检查吗?该检查需要遍历热门评论区,请注意评论区的评论数量(总数大于3000的评论区慎重考虑)!数量太多将导致漫长的检查过程,同时频繁调用API可能会引发不可预料的后果!", "警告", { confirmButtonText: "确定", cancelButtonText: "取消" } ); } catch (err) { return; } } hotBanCheckerMessage.value = "正在重新检查评论状态……"; hotBanCheckerController.isCancelled = false; showHotBanChecker.value = true; try { await check2(comment); if (comment.currentState != "NORMAL") { ElementPlus.ElMessage.error(`评论状态重新检查后为${translateState(comment.currentState)},无法继续进行检查`); showHotBanChecker.value = false; return; } } catch (err) { let msg = err.message; if (msg == "COMMENT_AREA_CLOSED") { msg = "评论区已关闭"; } ElementPlus.ElMessage.error("检查失败,因为" + msg); showHotBanChecker.value = false; return; } let observer = { onCountChange(c, p) { hotBanCheckerMessage.value = `正在搜索热门列表,已搜寻至:第${c}个 第${p}页`; } }; let notCancelled; try { notCancelled = await hotBanCheck2(comment, observer, hotBanCheckerController); } catch (err) { showHotBanChecker.value = false; ElementPlus.ElMessage.error(err.message); return; } if (notCancelled) { if (comment.hotBan) { ElementPlus.ElMessage.warning("你的评论未在热门列表找到,已被热门屏蔽,检查完成"); } else { ElementPlus.ElMessage.success("你的评论已在热门列表找到,没有被热门屏蔽,检查完成"); } } showHotBanChecker.value = false; } return (_ctx, _cache) => { const _component_el_button = vue.resolveComponent("el-button"); return vue.openBlock(), vue.createElementBlock("div", null, [ showHotBanChecker.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$3, [ _cache[2] || (_cache[2] = vue.createElementVNode("div", { class: "title" }, "热门屏蔽检查", -1)), vue.createElementVNode("div", _hoisted_2$3, vue.toDisplayString(hotBanCheckerMessage.value), 1), vue.createElementVNode("div", _hoisted_3$3, [ vue.createElementVNode("span", { onClick: _cache[0] || (_cache[0] = ($event) => vue.unref(hotBanCheckerController).isCancelled = true) }, "终止检查") ]) ])) : vue.createCommentVNode("", true), vue.createElementVNode("div", _hoisted_4$3, [ vue.unref(comment).isUserDelete == false ? (vue.openBlock(), vue.createBlock(_component_el_button, { key: 0, type: "primary", plain: "", onClick: updateState, loading: updating.value }, { default: vue.withCtx(() => _cache[3] || (_cache[3] = [ vue.createTextVNode("更新状态") ])), _: 1, __: [3] }, 8, ["loading"])) : vue.createCommentVNode("", true), vue.unref(comment).currentState == "NORMAL" ? (vue.openBlock(), vue.createBlock(_component_el_button, { key: 1, type: "primary", plain: "", onClick: toHotBanCheck }, { default: vue.withCtx(() => _cache[4] || (_cache[4] = [ vue.createTextVNode("热门屏蔽检查") ])), _: 1, __: [4] })) : vue.createCommentVNode("", true), vue.createVNode(_component_el_button, { type: "primary", plain: "", onClick: _cache[1] || (_cache[1] = ($event) => copyComment(vue.unref(comment).content)) }, { default: vue.withCtx(() => _cache[5] || (_cache[5] = [ vue.createTextVNode("复制") ])), _: 1, __: [5] }), vue.createVNode(_component_el_button, { type: "danger", plain: "", onClick: askDelete }, { default: vue.withCtx(() => _cache[6] || (_cache[6] = [ vue.createTextVNode("删除记录") ])), _: 1, __: [6] }) ]) ]); }; } }; const CommentActions = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-e5341f8d"]]); const _hoisted_1$2 = { class: "detail" }; const _hoisted_2$2 = { class: "info-table" }; const _hoisted_3$2 = { class: "comment-content" }; const _hoisted_4$2 = { key: 0 }; const _hoisted_5$2 = { key: 3 }; const _hoisted_6$2 = { class: "comment-content" }; const _hoisted_7$1 = { class: "pagination" }; const pageSize = 20; const _sfc_main$3 = { __name: "CommentHistories", setup(__props) { const deleteComment2 = vue.inject("deleteComment"); const db2 = vue.inject("db"); const comments = vue.reactive([]); const loadingComments = vue.ref(false); const prevTime = vue.ref(null); const nextTime = vue.ref(null); var prevStack = [null]; function loadComments(direction = "next") { loadingComments.value = true; comments.length = 0; let time = null; if (direction == "next") { time = nextTime.value; prevStack.push(time ? time : -1); prevTime.value = prevStack[prevStack.length - 2]; } else if (direction == "prev") { time = prevStack[prevStack.length - 2]; time = time == -1 ? null : time; prevStack.pop(); prevTime.value = prevStack[prevStack.length - 2]; } db2.transaction("comments").objectStore("comments").index("recordedTime").openCursor(time ? IDBKeyRange.upperBound(time) : null, "prev").onsuccess = (event) => { var cursor = event.target.result; if (cursor) { if (comments.length < pageSize) { comments.push(cursor.value); cursor.continue(); } else { nextTime.value = cursor.value.recordedTime; loadingComments.value = false; } } else { nextTime.value = null; loadingComments.value = false; } }; } loadComments(); function formatStateDesc(comment) { switch (comment.currentState) { case "NORMAL": if (comment.hotBan === true) { return "热门屏蔽"; } else if (comment.hotBan === false) { return "完全正常"; } else { return "正常"; } case "DELETED": if (comment.isUserDelete) { return "用户删除"; } else { return "已删除"; } case "SHADOW_BAN": return "仅自己可见"; case "NOT_CHECK": return "还未检查"; } } function formatCommentArea(comment, needEmojiHead) { var commentAreaInfo = comment.commentAreaInfo; switch (comment.webPageType) { case "WEB_PAGE_TYPE_WATCH": return "📺 " + commentAreaInfo.videoId; case "WEB_PAGE_TYPE_BROWSE": return "📰" + commentAreaInfo.postId; } } function formatHotBan(hotBan) { if (hotBan == null) { return "未检查"; } return hotBan ? "是" : "否"; } function deleteCommentItem(comment) { deleteComment2(comment.commentId).then(() => { const index = comments.findIndex((item) => item.commentId == comment.commentId); if (index !== -1) { comments.splice(index, 1); ElementPlus.ElMessage.success("评论删除成功"); } }).catch((err) => { ElementPlus.ElMessage.error("评论删除失败"); console.error("delete comment from database failed", err); }); } return (_ctx, _cache) => { const _component_el_table_column = vue.resolveComponent("el-table-column"); const _component_el_link = vue.resolveComponent("el-link"); const _component_el_table = vue.resolveComponent("el-table"); const _component_el_button = vue.resolveComponent("el-button"); return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [ vue.createVNode(_component_el_table, { data: comments, "row-key": "commentId", height: "100%", class: "comment-list" }, { default: vue.withCtx(() => [ vue.createVNode(_component_el_table_column, { prop: "content", label: "评论内容", align: "left", "show-overflow-tooltip": "" }), vue.createVNode(_component_el_table_column, { prop: "state", label: "当前状态", align: "center", width: "136", formatter: formatStateDesc }), vue.createVNode(_component_el_table_column, { prop: "recordedTime", label: "记录时间", align: "center", width: "160", formatter: (comment) => vue.unref(formatTimestamp)(comment.recordedTime) }, null, 8, ["formatter"]), vue.createVNode(_component_el_table_column, { prop: "area", label: "所在评论区", align: "center", width: "240" }, { default: vue.withCtx(({ row }) => [ vue.createElementVNode("div", null, [ vue.createVNode(_component_el_link, { type: "primary", href: row.url, class: vue.normalizeClass(["locate-link", { "post-locate-link": row.webPageType == "WEB_PAGE_TYPE_BROWSE" }]) }, { default: vue.withCtx(() => [ vue.createTextVNode(vue.toDisplayString(formatCommentArea(row)), 1) ]), _: 2 }, 1032, ["href", "class"]) ]) ]), _: 1 }), vue.createVNode(_component_el_table_column, { type: "expand" }, { default: vue.withCtx(({ row }) => [ vue.createElementVNode("div", _hoisted_1$2, [ vue.createElementVNode("table", _hoisted_2$2, [ vue.createElementVNode("tbody", null, [ vue.createElementVNode("tr", null, [ _cache[2] || (_cache[2] = vue.createElementVNode("td", null, "评论内容", -1)), vue.createElementVNode("td", _hoisted_3$2, vue.toDisplayString(row.content), 1) ]), vue.createElementVNode("tr", null, [ _cache[3] || (_cache[3] = vue.createElementVNode("td", null, "当前状态", -1)), vue.createElementVNode("td", null, vue.toDisplayString(vue.unref(translateState)(row.currentState)), 1) ]), row.currentState == "DELETED" ? (vue.openBlock(), vue.createElementBlock("tr", _hoisted_4$2, [ _cache[4] || (_cache[4] = vue.createElementVNode("td", null, "用户删除", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.isUserDelete ? "是" : "否"), 1) ])) : vue.createCommentVNode("", true), vue.createElementVNode("tr", null, [ _cache[5] || (_cache[5] = vue.createElementVNode("td", null, "热门屏蔽", -1)), vue.createElementVNode("td", null, vue.toDisplayString(formatHotBan(row.hotBan)), 1) ]), vue.createElementVNode("tr", null, [ _cache[6] || (_cache[6] = vue.createElementVNode("td", null, "发送者", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.displayName), 1) ]), vue.createElementVNode("tr", null, [ _cache[7] || (_cache[7] = vue.createElementVNode("td", null, "评论ID", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.commentId), 1) ]), row.webPageType == "WEB_PAGE_TYPE_WATCH" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [ _cache[9] || (_cache[9] = vue.createElementVNode("tr", null, [ vue.createElementVNode("td", null, "评论区类型"), vue.createElementVNode("td", null, "视频") ], -1)), vue.createElementVNode("tr", null, [ _cache[8] || (_cache[8] = vue.createElementVNode("td", null, "视频ID", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.commentAreaInfo.videoId), 1) ]) ], 64)) : row.webPageType == "WEB_PAGE_TYPE_BROWSE" ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 2 }, [ _cache[12] || (_cache[12] = vue.createElementVNode("tr", null, [ vue.createElementVNode("td", null, "评论区类型"), vue.createElementVNode("td", null, "帖子") ], -1)), vue.createElementVNode("tr", null, [ _cache[10] || (_cache[10] = vue.createElementVNode("td", null, "帖子所属频道ID", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.commentAreaInfo.channelId), 1) ]), vue.createElementVNode("tr", null, [ _cache[11] || (_cache[11] = vue.createElementVNode("td", null, "帖子ID", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.commentAreaInfo.postId), 1) ]) ], 64)) : vue.createCommentVNode("", true), vue.createElementVNode("tr", null, [ _cache[13] || (_cache[13] = vue.createElementVNode("td", null, "点赞数", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.likeCount), 1) ]), row.commentId.indexOf(".") == -1 ? (vue.openBlock(), vue.createElementBlock("tr", _hoisted_5$2, [ _cache[14] || (_cache[14] = vue.createElementVNode("td", null, "回复数", -1)), vue.createElementVNode("td", null, vue.toDisplayString(row.replyCount), 1) ])) : vue.createCommentVNode("", true), vue.createElementVNode("tr", null, [ _cache[15] || (_cache[15] = vue.createElementVNode("td", null, "记录时间", -1)), vue.createElementVNode("td", null, vue.toDisplayString(vue.unref(formatTimestamp)(row.recordedTime)), 1) ]), vue.createElementVNode("tr", null, [ _cache[16] || (_cache[16] = vue.createElementVNode("td", null, "更新时间", -1)), vue.createElementVNode("td", null, vue.toDisplayString(vue.unref(formatTimestamp)(row.updatedTime)), 1) ]) ]) ]), vue.createElementVNode("details", null, [ _cache[17] || (_cache[17] = vue.createElementVNode("summary", null, "历史检查记录", -1)), vue.createVNode(_component_el_table, { data: row.histories, style: { "width": "100%" } }, { default: vue.withCtx(() => [ vue.createVNode(_component_el_table_column, { prop: "time", label: "时间戳", width: "160", formatter: (history) => vue.unref(formatTimestamp)(history.time) }, null, 8, ["formatter"]), vue.createVNode(_component_el_table_column, { prop: "state", label: "状态", width: "136", formatter: (history) => vue.unref(translateState)(history.state) }, null, 8, ["formatter"]), vue.createVNode(_component_el_table_column, { prop: "hotBan", label: "热门屏蔽", width: "120", formatter: (history) => formatHotBan(history.hotBan) }, null, 8, ["formatter"]), vue.createVNode(_component_el_table_column, { prop: "content", label: "评论内容", "show-overflow-tooltip": "" }), vue.createVNode(_component_el_table_column, { type: "expand" }, { default: vue.withCtx(({ row: row2 }) => [ vue.createElementVNode("div", _hoisted_6$2, vue.toDisplayString(row2.content), 1) ]), _: 2 }, 1024) ]), _: 2 }, 1032, ["data"]) ]), vue.createVNode(CommentActions, { comment: row, onDelete: ($event) => deleteCommentItem(row) }, null, 8, ["comment", "onDelete"]) ]) ]), _: 1 }) ]), _: 1 }, 8, ["data"]), vue.createElementVNode("div", _hoisted_7$1, [ prevTime.value ? (vue.openBlock(), vue.createBlock(_component_el_button, { key: 0, onClick: _cache[0] || (_cache[0] = ($event) => loadComments("prev")), disabled: loadingComments.value }, { default: vue.withCtx(() => [ vue.createTextVNode("< " + vue.toDisplayString(prevTime.value == -1 ? "NOW" : vue.unref(formatTimestamp)(prevTime.value)), 1) ]), _: 1 }, 8, ["disabled"])) : vue.createCommentVNode("", true), nextTime.value ? (vue.openBlock(), vue.createBlock(_component_el_button, { key: 1, onClick: _cache[1] || (_cache[1] = ($event) => loadComments("next")), disabled: loadingComments.value }, { default: vue.withCtx(() => [ vue.createTextVNode(vue.toDisplayString(vue.unref(formatTimestamp)(nextTime.value)) + " >", 1) ]), _: 1 }, 8, ["disabled"])) : vue.createCommentVNode("", true) ]) ], 64); }; } }; const CommentHistories = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-80f074da"]]); function createVideoRootCommentListContinuation(channelId, videoId, isLatestSort) { let payload = { uField3: 6, commentAreaWrapper: { videoId }, mainCommentRequest: { sectionIdentifier: "comments-section", commentParameters: { videoId, sortType: isLatestSort ? 1 : 0 } } }; let encoded = NextContinuation.encode(payload); let buffer = encoded.finish(); let continuation = btoa(String.fromCharCode(...buffer)); return standardBase64ToUrlSafe(continuation); } function createVideoReplyCommentListContinuation(channelId, videoId, rootCommentId, isLatestSort) { let payload = { uField3: 6, commentAreaWrapper: { videoId }, mainCommentRequest: { sectionIdentifier: `comment-replies-item-${rootCommentId}`, commentReplyParameters: { rootCommentId, channelId, videoId, pageSize: 10, sortParam: { sortType: 1 } } } }; let encoded = NextContinuation.encode(payload); let buffer = encoded.finish(); let continuation = btoa(String.fromCharCode(...buffer)); return standardBase64ToUrlSafe(continuation); } function createPostRootCommentListContinuation(channelId, postId, isLatestSort) { let payload = { description: "community", mainCommentRequest: { sectionIdentifier: "comments-section", commentParameters: { channelId, postId, sortType: isLatestSort ? 1 : 0 } } }; let encoded = BrowserCommentListContinuation.encode(payload); let buffer = encoded.finish(); let continuation = btoa(String.fromCharCode(...buffer)); continuation = standardBase64ToUrlSafe(continuation); payload = { request: { description: "FEcomment_post_detail_page_web_top_level", continuationBase64: continuation } }; encoded = BrowserContinuation.encode(payload); buffer = encoded.finish(); continuation = btoa(String.fromCharCode(...buffer)); return standardBase64ToUrlSafe(continuation); } function createPostReplyCommentListContinuation(channelId, postId, rootCommentId, isLatestSort) { let payload = { description: "community", mainCommentRequest: { sectionIdentifier: `comment-replies-item-${rootCommentId}`, commentReplyParameters: { rootCommentId, channelId, postId, pageSize: 10, sortParam: { sortType: 1 } } } }; let encoded = BrowserCommentListContinuation.encode(payload); let buffer = encoded.finish(); continuation = btoa(String.fromCharCode(...buffer)); continuation = standardBase64ToUrlSafe(continuation); payload = { request: { description: "FEcomment_post_detail_page_web_replies_page", continuationBase64: continuation } }; encoded = BrowserContinuation.encode(payload); buffer = encoded.finish(); let continuation = btoa(String.fromCharCode(...buffer)); return standardBase64ToUrlSafe(continuation); } const _hoisted_1$1 = { class: "youtube-comment" }; const _hoisted_2$1 = { class: "avatar-container" }; const _hoisted_3$1 = ["href"]; const _hoisted_4$1 = ["src", "alt"]; const _hoisted_5$1 = { class: "comment-content" }; const _hoisted_6$1 = { class: "comment-header" }; const _hoisted_7 = ["href"]; const _hoisted_8 = { class: "published-at" }; const _hoisted_9 = { class: "comment-text" }; const _hoisted_10 = { class: "comment-actions" }; const _hoisted_11 = { class: "action-text" }; const _hoisted_12 = { class: "action-text" }; const _hoisted_13 = ["href"]; const _sfc_main$2 = { __name: "YouTubeComment", props: ["comment"], setup(__props) { return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, [ vue.createElementVNode("div", _hoisted_2$1, [ vue.createElementVNode("a", { href: "https://www.youtube.com/" + __props.comment.userDisplayName, target: "_blank", rel: "noopener noreferrer" }, [ vue.createElementVNode("img", { src: __props.comment.userProfileImageUrl, alt: __props.comment.authorDisplayName, class: "avatar" }, null, 8, _hoisted_4$1) ], 8, _hoisted_3$1) ]), vue.createElementVNode("div", _hoisted_5$1, [ vue.createElementVNode("div", _hoisted_6$1, [ vue.createElementVNode("a", { href: "https://www.youtube.com/" + __props.comment.userDisplayName, target: "_blank", rel: "noopener noreferrer", class: "author-name" }, vue.toDisplayString(__props.comment.userDisplayName), 9, _hoisted_7), vue.createElementVNode("span", _hoisted_8, vue.toDisplayString(__props.comment.publishedTime), 1) ]), vue.createElementVNode("p", _hoisted_9, vue.toDisplayString(__props.comment.contentText), 1), vue.createElementVNode("div", _hoisted_10, [ vue.createElementVNode("button", { class: "button", onClick: _cache[0] || (_cache[0] = ($event) => vue.unref(ElementPlus.ElMessage).info("需要点赞评论请定位至评论")) }, [ _cache[2] || (_cache[2] = vue.createElementVNode("svg", { class: "icon", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" }, [ vue.createElementVNode("path", { d: "M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.31C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3z" }) ], -1)), vue.createElementVNode("span", _hoisted_11, vue.toDisplayString(__props.comment.likeCount), 1) ]), vue.createElementVNode("button", { class: "button", onClick: _cache[1] || (_cache[1] = ($event) => vue.unref(ElementPlus.ElMessage).info("需要查看回复请定位评论查看")) }, [ _cache[3] || (_cache[3] = vue.createElementVNode("svg", { t: "1756005965374", class: "icon", viewBox: "0 0 1024 1024", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "5216", width: "200", height: "200" }, [ vue.createElementVNode("path", { d: "M1024 640l-3.5 41.6c-6.5 78.1-27.7 154-62.2 223.9-7.5 15.2-30.4 10-30.4-7V800c0-61.9-25.1-117.9-65.6-158.4C821.9 601.1 765.9 576 704 576H552c-4.4 0-8 3.6-8 8v179.6c0 13.9-16.5 21.2-26.8 11.8L160 447.9 516.5 76.7c10-10.4 27.5-3.3 27.5 11.1V280c0 4.4 3.6 8 8 8h120c97.2 0 185.2 39.4 248.9 103.1S1024 542.8 1024 640z", "p-id": "5217" }), vue.createElementVNode("path", { d: "M356.7 95.1L0 453.1l357.3 326.1c10.3 9.4 26.8 2.1 26.8-11.8 0-32.1-13.5-62.7-37.2-84.4L92.5 450.9l258.2-259.1c21.3-21.4 33.3-50.4 33.3-80.6v-4.8c0-14.3-17.3-21.4-27.3-11.3z", "p-id": "5218" }) ], -1)), vue.createElementVNode("span", _hoisted_12, vue.toDisplayString(__props.comment.replyCount), 1) ]), vue.createElementVNode("a", { class: "button", href: __props.comment.url, target: "_blank" }, _cache[4] || (_cache[4] = [ vue.createElementVNode("svg", { t: "1756007306700", class: "icon", viewBox: "0 0 1024 1024", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "7302", width: "200", height: "200" }, [ vue.createElementVNode("path", { d: "M511.36599 512.177066m-201.557982 0a201.557983 201.557983 0 1 0 403.115965 0 201.557983 201.557983 0 1 0-403.115965 0Z", "p-id": "7303" }), vue.createElementVNode("path", { d: "M990.163299 477.654949h-78.434431a34.350763 34.350763 0 0 0-5.883154 0.514062c-16.278629-191.288167-168.532358-343.770368-359.706288-360.357433a33.619652 33.619652 0 0 0 0.376979-5.060655V34.270797a34.270797 34.270797 0 0 0-68.541595 0v78.434432a35.059026 35.059026 0 0 0 0.354132 4.923571c-191.790805 15.844532-344.798492 168.372427-361.385558 359.957608a34.556387 34.556387 0 0 0-4.226732-0.274167H34.270797a34.270797 34.270797 0 1 0 0 68.541595h78.445855a32.762882 32.762882 0 0 0 4.12392-0.262743c16.02731 192.076395 169.217774 345.17547 361.328439 361.134239a34.853401 34.853401 0 0 0-0.308437 4.569439v78.434432a34.270797 34.270797 0 0 0 68.541595 0v-78.480126a34.670623 34.670623 0 0 0-0.319861-4.649405c191.368132-16.621337 343.747521-169.377704 359.843372-360.905766a35.001908 35.001908 0 0 0 5.837459 0.502638h78.434431a34.270797 34.270797 0 0 0 34.270798-34.270797 34.270797 34.270797 0 0 0-34.305069-34.270798z m-177.065786 161.940941a328.656946 328.656946 0 1 1 25.703098-127.418824 326.440768 326.440768 0 0 1-25.748792 127.418824z", "p-id": "7304" }) ], -1), vue.createElementVNode("span", { class: "action-text" }, "定位", -1) ]), 8, _hoisted_13) ]), vue.renderSlot(_ctx.$slots, "default", {}, void 0, true) ]) ]); }; } }; const YouTubeComment = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-2a518d4b"]]); const _hoisted_1 = { class: "hot-ban-comment-searcher" }; const _hoisted_2 = { key: 1, class: "warning" }; const _hoisted_3 = { key: 2, class: "search-progress" }; const _hoisted_4 = { "infinite-scroll-distance": 500 }; const _hoisted_5 = { class: "footer" }; const _hoisted_6 = { key: 1 }; const _sfc_main$1 = { __name: "HotBanCommentSearcher", setup(__props) { const originalFetch2 = vue.inject("originalFetch"); const getAuthorization2 = vue.inject("getAuthorization"); const getContext2 = vue.inject("getContext"); const showNcs = vue.ref(true); const showWarning = vue.ref(false); const showSearchProgress = vue.ref(false); const showResults = vue.ref(false); const progressMsg = vue.ref("加载中..."); const comments = vue.reactive([]); const hasNext = vue.ref(true); const loading = vue.ref(false); const progStatus = vue.ref(""); let nextContinuation; let cancelled = false; let hotCommentIdSet = /* @__PURE__ */ new Set(); let url = createUrl(); let api; let channelId; let TYPE = null; if (url.searchParams.has("v")) { TYPE = "WEB_PAGE_TYPE_WATCH"; api = "https://www.youtube.com/youtubei/v1/next?prettyPrint=false"; } else if (url.pathname.split("/").includes("post")) { TYPE = "WEB_PAGE_TYPE_BROWSE"; api = "https://www.youtube.com/youtubei/v1/browse?prettyPrint=false"; } if (TYPE) { showNcs.value = false; showWarning.value = true; } async function onSearch() { var _a, _b, _c, _d, _e, _f, _g; showWarning.value = false; showSearchProgress.value = true; let continuation = null; if (TYPE === "WEB_PAGE_TYPE_WATCH") { continuation = createVideoRootCommentListContinuation(null, url.searchParams.get("v"), false); nextContinuation = createVideoRootCommentListContinuation(null, url.searchParams.get("v"), true); } else if (TYPE === "WEB_PAGE_TYPE_BROWSE") { channelId = (_b = (_a = ytInitialData.metadata) == null ? void 0 : _a.channelMetadataRenderer) == null ? void 0 : _b.externalId; if (!channelId) { progressMsg.value = "无法获取频道ID,请尝试刷新页面后重试!"; } continuation = createPostRootCommentListContinuation(channelId, url.pathname.split("/")[2], false); nextContinuation = createPostRootCommentListContinuation(channelId, url.pathname.split("/")[2], true); } let count = 0; while (continuation) { let data = { context: getContext2(), continuation }; let options = { method: "POST", body: JSON.stringify(data), headers: { //因为我搜索的是别人的热门屏蔽评论,所以带上认证信息也没啥(还能减轻风控?) authorization: getAuthorization2() } }; if (cancelled) { return; } let response; try { response = await (await originalFetch2(api, options)).json(); } catch (err) { progressMsg.value = "获取热门列表失败,您可以关闭后重试!"; return; } let mutations = response.frameworkUpdates.entityBatchUpdate.mutations; for (let mutation of mutations) { if (mutation.payload.commentEntityPayload) { hotCommentIdSet.add(mutation.payload.commentEntityPayload.properties.commentId); count++; progressMsg.value = `正在获取热门评论(已获取 ${count} 个)……`; } } continuation = null; for (const endpoint of response.onResponseReceivedEndpoints) { const items = ((_c = endpoint.appendContinuationItemsAction) == null ? void 0 : _c.continuationItems) || ((_d = endpoint.reloadContinuationItemsCommand) == null ? void 0 : _d.continuationItems); if (!items) continue; for (const item of items) { const token = (_g = (_f = (_e = item.continuationItemRenderer) == null ? void 0 : _e.continuationEndpoint) == null ? void 0 : _f.continuationCommand) == null ? void 0 : _g.token; if (token) { continuation = token; break; } } if (continuation) break; } } next(); } async function next() { var _a, _b, _c, _d, _e; if (!hasNext.value || loading.value || cancelled) { return; } loading.value = true; showSearchProgress.value = false; showResults.value = true; let count = 0; while (count < 20) { let data = { context: getContext2(), continuation: nextContinuation }; let options = { method: "POST", body: JSON.stringify(data), headers: { authorization: getAuthorization2() } }; if (cancelled) { return; } let response; try { response = await (await originalFetch2(api, options)).json(); } catch (err) { progStatus.value = "exception"; return; } nextContinuation = null; for (const endpoint of response.onResponseReceivedEndpoints) { const items = ((_a = endpoint.appendContinuationItemsAction) == null ? void 0 : _a.continuationItems) || ((_b = endpoint.reloadContinuationItemsCommand) == null ? void 0 : _b.continuationItems); if (!items) continue; for (const item of items) { const token = (_e = (_d = (_c = item.continuationItemRenderer) == null ? void 0 : _c.continuationEndpoint) == null ? void 0 : _d.continuationCommand) == null ? void 0 : _e.token; if (token) { nextContinuation = token; break; } } if (nextContinuation) break; } let mutations = response.frameworkUpdates.entityBatchUpdate.mutations; for (let i = 0; i < mutations.length; i++) { let mutation = mutations[i]; if (mutation.payload.commentEntityPayload) { let entity = mutation.payload.commentEntityPayload; let commentId = entity.properties.commentId; if (!hotCommentIdSet.has(commentId)) { let likeCount = parseInt(entity.toolbar.likeCountNotliked); likeCount = likeCount ? likeCount : 0; let replyCount = parseInt(entity.toolbar.replyCount); replyCount = replyCount ? replyCount : 0; let url2 = mutations[i + 1].payload.commentSurfaceEntityPayload.publishedTimeCommand.innertubeCommand.commandMetadata.webCommandMetadata.url; count++; comments.push({ commentId, url: url2, userDisplayName: entity.author.displayName, userProfileImageUrl: entity.avatar.image.sources[0].url, likeCount, replyCount, publishedTime: entity.properties.publishedTime, contentText: entity.properties.content.content }); } } } if (!nextContinuation) { hasNext.value = false; break; } } loading.value = false; } vue.onUnmounted(() => { cancelled = true; }); return (_ctx, _cache) => { const _component_el_empty = vue.resolveComponent("el-empty"); const _component_el_divider = vue.resolveComponent("el-divider"); const _component_el_button = vue.resolveComponent("el-button"); const _component_el_progress = vue.resolveComponent("el-progress"); const _component_el_scrollbar = vue.resolveComponent("el-scrollbar"); const _directive_infinite_scroll = vue.resolveDirective("infinite-scroll"); return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [ showNcs.value ? (vue.openBlock(), vue.createBlock(_component_el_empty, { key: 0, description: "请在有评论区(视频、帖子)的页面使用此功能", class: "not-comment-section" })) : vue.createCommentVNode("", true), showWarning.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, [ _cache[1] || (_cache[1] = vue.createElementVNode("svg", { t: "1756953289758", class: "warn-icon", viewBox: "0 0 1024 1024", version: "1.1", xmlns: "http://www.w3.org/2000/svg", "p-id": "4518", width: "64", height: "64" }, [ vue.createElementVNode("path", { d: "M1001.661867 796.544c48.896 84.906667 7.68 157.013333-87.552 157.013333H110.781867c-97.834667 0-139.050667-69.504-90.112-157.013333l401.664-666.88c48.896-87.552 128.725333-87.552 177.664 0l401.664 666.88zM479.165867 296.533333v341.333334a32 32 0 1 0 64 0v-341.333334a32 32 0 1 0-64 0z m0 469.333334v42.666666a32 32 0 1 0 64 0v-42.666666a32 32 0 1 0-64 0z", fill: "#FAAD14", "p-id": "4519" }) ], -1)), _cache[2] || (_cache[2] = vue.createElementVNode("h2", { class: "warning-title" }, "警告", -1)), _cache[3] || (_cache[3] = vue.createElementVNode("p", { class: "warning-text" }, [ vue.createTextVNode("将会获取所有热门评论,之后懒加载按时间评论"), vue.createElementVNode("br"), vue.createTextVNode(" 评论区数量过多(例如超过 5000 条),获取热门评论的过程将非常漫长,且过多的API调用可能导致不可预料的后果,请酌情使用! ") ], -1)), vue.createVNode(_component_el_divider, { class: "warning-divider" }), vue.createVNode(_component_el_button, { type: "primary", onClick: onSearch }, { default: vue.withCtx(() => _cache[0] || (_cache[0] = [ vue.createTextVNode("搜索") ])), _: 1, __: [0] }) ])) : vue.createCommentVNode("", true), showSearchProgress.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [ vue.createElementVNode("p", null, vue.toDisplayString(progressMsg.value), 1), vue.createVNode(_component_el_progress, { percentage: 50, indeterminate: true, "show-text": false }) ])) : vue.createCommentVNode("", true), showResults.value ? (vue.openBlock(), vue.createBlock(_component_el_scrollbar, { key: 3, class: "search-results" }, { default: vue.withCtx(() => [ vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", _hoisted_4, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(comments, (comment) => { return vue.openBlock(), vue.createBlock(YouTubeComment, { key: comment.commentId, comment }, null, 8, ["comment"]); }), 128)), vue.createElementVNode("div", _hoisted_5, [ loading.value ? (vue.openBlock(), vue.createBlock(_component_el_progress, { key: 0, percentage: 50, indeterminate: true, "show-text": false, status: progStatus.value, duration: 1 }, null, 8, ["status"])) : vue.createCommentVNode("", true), !hasNext.value ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_6, "- PAGE END -")) : vue.createCommentVNode("", true) ]) ])), [ [_directive_infinite_scroll, next] ]) ]), _: 1 })) : vue.createCommentVNode("", true) ]); }; } }; const HotBanCommentSearcher = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-b5a0c87e"]]); const _sfc_main = { __name: "App", setup(__props) { let historiesVisible = vue.ref(false); let searchHotBanVisible = vue.ref(false); const menuListener = vue.inject("menuListener"); menuListener.onOpenHistory = () => { historiesVisible.value = true; }; menuListener.onSearchHotBan = () => { searchHotBanVisible.value = true; }; return (_ctx, _cache) => { const _component_el_dialog = vue.resolveComponent("el-dialog"); return vue.openBlock(), vue.createElementBlock("div", null, [ vue.createVNode(_component_el_dialog, { modelValue: vue.unref(historiesVisible), "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.isRef(historiesVisible) ? historiesVisible.value = $event : historiesVisible = $event), "z-index": 3e3, title: "历史评论列表", width: "80%", style: { "height": "92vh" }, "body-class": "dialog-body", "align-center": "", "destroy-on-close": "" }, { default: vue.withCtx(() => [ vue.createVNode(CommentHistories) ]), _: 1 }, 8, ["modelValue"]), vue.createVNode(_component_el_dialog, { modelValue: vue.unref(searchHotBanVisible), "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => vue.isRef(searchHotBanVisible) ? searchHotBanVisible.value = $event : searchHotBanVisible = $event), "z-index": 3e3, title: "热门屏蔽评论搜索", width: "80%", style: { "height": "92vh" }, "body-class": "dialog-body", "align-center": "", "destroy-on-close": "" }, { default: vue.withCtx(() => [ vue.createVNode(HotBanCommentSearcher) ]), _: 1 }, 8, ["modelValue"]) ]); }; } }; const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-1d7cd9ea"]]); const originalFetch = _unsafeWindow.fetch; var authorizationCache = null; var contextCache = null; var trueLoaded = false; var db = null; const checkingCommentIdSet = /* @__PURE__ */ new Set(); function getAuthorization() { return authorizationCache; } function getContext() { return contextCache; } function waitForElement(observeSelector, targetSelector) { return new Promise((resolve) => { const parent = document.querySelector(observeSelector); if (!parent) return; const found = parent.querySelector(targetSelector); if (found) { resolve(found); return; } const observer = new MutationObserver(() => { const el = parent.querySelector(targetSelector); if (el) { observer.disconnect(); resolve(el); } }); observer.observe(parent, { childList: true, subtree: true }); }); } async function findComment(commentRecord, isLogin = true) { let continuation; let requestUrl; if (commentRecord.webPageType == "WEB_PAGE_TYPE_WATCH") { let payload = { uField3: 6, commentAreaWrapper: { videoId: commentRecord.commentAreaInfo.videoId }, mainCommentRequest: { sectionIdentifier: "comments-section", commentParameters: { videoId: commentRecord.commentAreaInfo.videoId, targetCommentId: commentRecord.commentId } } }; let encoded = NextContinuation.encode(payload); let buffer = encoded.finish(); continuation = btoa(String.fromCharCode(...buffer)); continuation = standardBase64ToUrlSafe(continuation); requestUrl = "https://www.youtube.com/youtubei/v1/next?prettyPrint=false"; } else if (commentRecord.webPageType == "WEB_PAGE_TYPE_BROWSE") { let payload = { description: "community", mainCommentRequest: { sectionIdentifier: "comments-section", commentParameters: { channelId: commentRecord.commentAreaInfo.channelId, postId: commentRecord.commentAreaInfo.postId, targetCommentId: commentRecord.commentId } } }; let encoded = BrowserCommentListContinuation.encode(payload); let buffer = encoded.finish(); continuation = btoa(String.fromCharCode(...buffer)); continuation = standardBase64ToUrlSafe(continuation); payload = { request: { description: "FEcomment_post_detail_page_web_top_level", continuationBase64: continuation } }; encoded = BrowserContinuation.encode(payload); buffer = encoded.finish(); continuation = btoa(String.fromCharCode(...buffer)); continuation = standardBase64ToUrlSafe(continuation); requestUrl = "https://www.youtube.com/youtubei/v1/browse?prettyPrint=false"; } else { throw new Error("Unsupported webPageType : " + commentRecord.webPageType); } let data = { context: contextCache, continuation }; let headers = {}; if (isLogin) { headers.authorization = authorizationCache; } let options = { method: "POST", body: JSON.stringify(data), headers }; let response = await (await originalFetch(requestUrl, options)).json(); let loggedOut = response.responseContext.mainAppWebResponseContext.loggedOut; if (loggedOut == isLogin) { console.warn("登录状态不符,需要的:" + isLogin + " API返回的:" + !loggedOut); } if (!response.frameworkUpdates) { throw new Error("COMMENT_AREA_CLOSED"); } let mutations = response.frameworkUpdates.entityBatchUpdate.mutations; for (let i = 0; i < mutations.length; i++) { let mutation = mutations[i]; if (mutation.payload.commentEntityPayload) { let entity = mutation.payload.commentEntityPayload; let commentId = entity.properties.commentId; if (commentId == commentRecord.commentId) { let likeCount = parseInt(entity.toolbar.likeCountNotliked); likeCount = likeCount ? likeCount : 0; let replyCount = parseInt(entity.toolbar.replyCount); replyCount = replyCount ? replyCount : 0; return { content: entity.properties.content.content, commentId, likeCount, replyCount }; } } } } async function insertComment() { } async function updateComment() { } async function selectComment() { } async function deleteComment() { } function appendHistory(commentRecord) { let histories = commentRecord.histories; let needPush = false; if (histories.length == 0) { needPush = true; } else { let lastHistory = histories[histories.length - 1]; needPush = lastHistory.state != commentRecord.currentState || lastHistory.content != commentRecord.content || lastHistory.hotBan != commentRecord.hotBan; } if (needPush) { histories.push({ time: commentRecord.updatedTime, content: commentRecord.content, state: commentRecord.currentState, hotBan: commentRecord.hotBan }); } } function updateRecord(commentRecord, state, result) { commentRecord.updatedTime = Date.now(); if (state) { commentRecord.currentState = state; } if (result) { commentRecord.likeCount = result.likeCount; commentRecord.replyCount = result.replyCount; commentRecord.content = result.content; } appendHistory(commentRecord); updateComment(commentRecord); } async function check(commentRecord) { let loggedOutResult = await findComment(commentRecord, false); if (loggedOutResult) { updateRecord(commentRecord, "NORMAL", loggedOutResult); return; } let loggedInResult = await findComment(commentRecord, true); if (loggedInResult) { updateRecord(commentRecord, "SHADOW_BAN", loggedInResult); } else { updateRecord(commentRecord, "DELETED"); } } async function toCheck(commentRecord) { checkingCommentIdSet.add(commentRecord.commentId); let selector; let pathname = window.location.pathname; if (pathname.startsWith("/post") || pathname.startsWith("/channel")) { selector = "ytd-item-section-renderer#sections"; } else { selector = "#comments"; } let element = (await waitForElement(selector, `a[href='${commentRecord.url}']`)).parentNode.parentNode.parentNode.parentNode; let div = document.createElement("div"); div.style.marginTop = "8px"; div.id = "checker"; element.append(div); let app = vue.createApp(CommentChecker); app.use(ElementPlus); app.provide("check", check); app.provide("hotBanCheck", hotBanCheck); app.provide("commentRecord", commentRecord); app.provide("interval", 5); app.provide("onUnblock", (commentRecord2) => { checkingCommentIdSet.delete(commentRecord2.commentId); }); app.provide("onClose", (commentRecord2) => { checkingCommentIdSet.delete(commentRecord2.commentId); console.log("评论检查完成", commentRecord2); div.remove(); }); app.mount(div); } function createCommentListRequest(commentRecord, isLatestSort) { let api; let continuation; if (commentRecord.webPageType == "WEB_PAGE_TYPE_WATCH") { api = "https://www.youtube.com/youtubei/v1/next?prettyPrint=false"; if (commentRecord.commentId.indexOf(".") != -1) { let rootCommentId = commentRecord.commentId.split(".")[0]; continuation = createVideoReplyCommentListContinuation( commentRecord.commentAreaInfo.channelId, commentRecord.commentAreaInfo.videoId, rootCommentId ); } else { continuation = createVideoRootCommentListContinuation( commentRecord.commentAreaInfo.channelId, commentRecord.commentAreaInfo.videoId, isLatestSort ); } } else if (commentRecord.webPageType == "WEB_PAGE_TYPE_BROWSE") { api = "https://www.youtube.com/youtubei/v1/browse?prettyPrint=false"; if (commentRecord.commentId.indexOf(".") != -1) { let rootCommentId = commentRecord.commentId.split(".")[0]; continuation = createPostReplyCommentListContinuation( commentRecord.commentAreaInfo.channelId, commentRecord.commentAreaInfo.postId, rootCommentId ); } else { continuation = createPostRootCommentListContinuation( commentRecord.commentAreaInfo.channelId, commentRecord.commentAreaInfo.postId, isLatestSort ); } } return { api, continuation }; } async function hotBanCheck(commentRecord, observer, controller) { var _a, _b, _c, _d, _e; if (!observer) { observer = { onCountChange(c, p) { } }; } if (!controller) { controller = { isCancelled: false }; } let pageCpunt = 0; let commentCount = 0; let { api, continuation } = createCommentListRequest(commentRecord); while (continuation) { if (controller.isCancelled) { return false; } let data = { context: contextCache, continuation }; let options = { method: "POST", body: JSON.stringify(data) }; let response = await (await originalFetch(api, options)).json(); pageCpunt++; if (!response.frameworkUpdates) { commentRecord.hotBan = true; updateRecord(commentRecord); return true; } for (let mutation of response.frameworkUpdates.entityBatchUpdate.mutations) { let entity = mutation.payload.commentEntityPayload; if (entity) { let commentId = entity.properties.commentId; commentCount++; observer.onCountChange(commentCount, pageCpunt); if (commentId == commentRecord.commentId) { commentRecord.hotBan = false; updateRecord(commentRecord); return true; } } } continuation = null; for (const endpoint of response.onResponseReceivedEndpoints) { const items = ((_a = endpoint.appendContinuationItemsAction) == null ? void 0 : _a.continuationItems) || ((_b = endpoint.reloadContinuationItemsCommand) == null ? void 0 : _b.continuationItems); if (!items) continue; for (const item of items) { const token = (_e = (_d = (_c = item.continuationItemRenderer) == null ? void 0 : _c.continuationEndpoint) == null ? void 0 : _d.continuationCommand) == null ? void 0 : _e.token; if (token) { continuation = token; break; } } if (continuation) break; } } commentRecord.hotBan = true; updateRecord(commentRecord); return true; } async function handlerYoutubei(request) { let requsetClone = request.clone(); let requestBody = await requsetClone.json(); if (requestBody && requestBody.context) { contextCache = requestBody.context; if (!trueLoaded) { console.log("fetch已成功劫持"); _GM_registerMenuCommand("✅ 脚本已完全加载"); trueLoaded = true; } } if (request.url.startsWith("https://www.youtube.com/youtubei/v1/comment/create_comment")) { let response = await originalFetch(request); if (response.status != 200) { return response; } let responseClone = response.clone(); try { let json = await responseClone.json(); if (json.frameworkUpdates.entityBatchUpdate.mutations.length == 1) { return response; } let entity = json.frameworkUpdates.entityBatchUpdate.mutations[0].payload.commentEntityPayload; let innertubeCommand = json.frameworkUpdates.entityBatchUpdate.mutations[1].payload.commentSurfaceEntityPayload.publishedTimeCommand.innertubeCommand; let webCommandMetadata = innertubeCommand.commandMetadata.webCommandMetadata; let webPageType = webCommandMetadata.webPageType; let url = webCommandMetadata.url; let commentAreaInfo = {}; commentAreaInfo.channelId = findValueInSingleEntryArray(json.actions[0].runAttestationCommand.ids, "externalChannelId"); if (webPageType == "WEB_PAGE_TYPE_WATCH") { commentAreaInfo.videoId = innertubeCommand.watchEndpoint.videoId; } else if (webPageType == "WEB_PAGE_TYPE_BROWSE") { commentAreaInfo.postId = createUrl(url).pathname.split("/")[2]; } let author = entity.author; let properties = entity.properties; let content = properties.content.content; let recordedTime = Date.now(); let commentRecord = { //评论ID commentId: properties.commentId, //@发送者 displayName: author.displayName, //频道ID,类似UID channelId: author.channelId, //评论内容 content, //webPageType 评论区类型 视频 or 帖子 webPageType, //URL 点击可跳转“所要查看的评论” 例如 /watch?v=${视频ID}&lc=${评论ID} url, //评论区信息,视频{视频ID},帖子{频道ID,帖子ID} commentAreaInfo, //当前状态 默认从SHADOW_BAN开始,到NORMAL或DELETED currentState: "NOT_CHECK", //是否在热门排序中被禁止显示(搜索整个热门评论区来检查),前提条件currentState = "NORMAL",值:null | false | true //此状态不会因为修改评论内容而解除,但会因为修改评论内容而赋予 hotBan: null, //历史记录,时间 内容 状态 是否热门屏蔽 histories: [], //{ time: recordedTime, state: "SHADOW_BAN", content, hotBan: null } //点赞与回复数,不记录历史 likeCount: 0, replyCount: 0, //记录的时间,用的是系统当前时间,约等于评论的发布时间,API里的publishedTime距离发布时间戳多久的Shit不是时间戳(PS:YouTube开放API可查询具体发布时间戳) recordedTime, //更新时间 updatedTime: recordedTime, //是否是用户自己执行的删除?用于区分是被系统删的还是自己删除。state为"DELETED"时该属性为才有意义。(劫持删除评论请求时记录) isUserDelete: false }; console.log(commentRecord); insertComment(commentRecord); console.log(createUrl(url).href); toCheck(commentRecord); } catch (err) { console.error(err); throw err; } return response; } else if (request.url.startsWith("https://www.youtube.com/youtubei/v1/comment/perform_comment_action")) { let actionBase64 = urlSafeBase64ToStandard(requestBody.actions[0]); let actionInfo = CommentAction.decode(Uint8Array.from(atob(actionBase64), (c) => c.charCodeAt(0))); if (actionInfo.action == 6) { if (checkingCommentIdSet.has(actionInfo.commentId)) { alert("现在不能删除该评论,因为评论还未完成检查,请先完成检查!"); const responseBody = { "error": { "code": 403, "message": "Can't delete comment now", "errors": [ { "message": "Can't delete comment now", "domain": "global", "reason": "forbidden" } ], "status": "FORBIDDEN" } }; return new Response(JSON.stringify(responseBody), { status: 403, headers: { "Content-Type": "application/json" } }); } else { let response = await originalFetch(request); let responseBody = await response.clone().json(); if (responseBody.actions && responseBody.actions[0].removeCommentAction.actionResult.status == "STATUS_SUCCEEDED") { let commentRecord = await selectComment(actionInfo.commentId); if (commentRecord) { commentRecord.isUserDelete = true; updateRecord(commentRecord, "DELETED"); } } return response; } } } else if (request.url.startsWith("https://www.youtube.com/youtubei/v1/comment/update_comment")) { let updateCommentParams = urlSafeBase64ToStandard(requestBody.updateCommentParams); let decodedParams = UpdateCommentParams.decode(Uint8Array.from(atob(updateCommentParams), (c) => c.charCodeAt(0))); if (checkingCommentIdSet.has(decodedParams.commentId)) { alert("现在不能修改该评论,因为评论还未完成检查,请先完成检查!"); const responseBody2 = { "error": { "code": 403, "message": "Can't edit comment now", "errors": [ { "message": "Can't edit comment now", "domain": "global", "reason": "forbidden" } ], "status": "FORBIDDEN" } }; return new Response(JSON.stringify(responseBody2), { status: 403, headers: { "Content-Type": "application/json" } }); } let response = await originalFetch(request); let responseBody = await response.clone().json(); if (responseBody.actions && responseBody.actions[0].updateCommentAction.actionResult.status == "STATUS_SUCCEEDED") { let commentRecord = await selectComment(decodedParams.commentId); if (commentRecord) { commentRecord.content = requestBody.commentText; commentRecord.currentState = "NOT_CHECK"; commentRecord.hotBan = null; updateRecord(commentRecord); } } return response; } return await originalFetch(request); } const fetchProxy = function(resource, options) { if (typeof resource == "string") { return originalFetch(resource, options); } if (!resource.url.startsWith("https://www.youtube.com/youtubei/")) { return originalFetch(resource, options); } let auth = resource.headers.get("Authorization"); if (auth) { authorizationCache = auth; if (resource.method != "POST") { return originalFetch(resource); } else { return handlerYoutubei(resource); } } return originalFetch(resource, options); }; try { _unsafeWindow.fetch = fetchProxy; } catch (err) { console.warn("替换 unsafeWindow.fetch 失败!相关信息:", err, Object.getOwnPropertyDescriptor(_unsafeWindow, "fetch")); if (confirm("fetch已被提前锁定,替换失败,YouTube发评反诈可能无法正常工作。\n你可以安装本项目的 Define property blocker 插件来反制锁定。\n\n点击“确定”前往项目地址,点击“取消”忽略。")) { window.location.href = "https://github.com/freedom-introvert/youtube-comment-censor-detector"; } } const _createElement = Document.prototype.createElement; Document.prototype.createElement = function(tagName, ...args) { const el = _createElement.call(this, tagName, ...args); if (tagName.toLowerCase() === "iframe") { el.addEventListener("load", () => { var _a; try { const fetchFromIframe = (_a = el.contentWindow) == null ? void 0 : _a.fetch; if (fetchFromIframe) { el.contentWindow.fetch = fetchProxy; console.log("已替换iframe window的fetch", el); } } catch (e) { console.log("未替换该iframe的fetch", el, e); } }); } return el; }; function openDB() { return new Promise((resolve, reject) => { let request = indexedDB.open("YT-CCD", 1); request.onerror = (event) => { reject(event); }; request.onsuccess = (event) => { resolve(event.target.result); }; request.onupgradeneeded = (event) => { let db2 = event.target.result; let objectStore = db2.createObjectStore("comments", { keyPath: "commentId" }); objectStore.createIndex("recordedTime", "recordedTime", { unique: false }); }; }); } async function init() { try { db = await openDB(); insertComment = function(comment) { return new Promise((resolve, reject) => { let request = db.transaction("comments", "readwrite").objectStore("comments").add(comment); request.onsuccess = (event) => { resolve(event); }; request.onerror = (event) => { reject(event); }; }); }; updateComment = function(comment) { return new Promise((resolve, reject) => { let request = db.transaction("comments", "readwrite").objectStore("comments").put(vue.toRaw(comment)); request.onsuccess = (event) => { resolve(event); }; request.onerror = (event) => { reject(event); }; }); }; selectComment = function(commentId) { return new Promise((resolve, reject) => { let request = db.transaction("comments").objectStore("comments").get(commentId); request.onsuccess = (event) => { resolve(request.result); }; request.onerror = (event) => { reject(event); }; }); }; deleteComment = function(commentId) { return new Promise((resolve, reject) => { let request = db.transaction("comments", "readwrite").objectStore("comments").delete(commentId); request.onsuccess = (event) => { resolve(request.result); }; request.onerror = (event) => { reject(event); }; }); }; } catch (err) { console.log("indexedDB数据库打开失败,评论历史记录相关功能已禁用,错误信息:", err); } const menuListener = { onOpenHistory: () => { alert("脚本正在初始化,请稍后……"); } }; _GM_registerMenuCommand("🧾 历史评论记录", () => { menuListener.onOpenHistory(); }); _GM_registerMenuCommand("🔍 搜索热门屏蔽评论", () => { menuListener.onSearchHotBan(); }); const div = document.createElement("div"); div.id = "yt-ccd"; div.style.position = "absolute"; document.body.append(div); let app = vue.createApp(App); app.use(ElementPlus); app.provide("menuListener", menuListener); app.provide("db", db); app.provide("check", check); app.provide("hotBanCheck", hotBanCheck); app.provide("deleteComment", deleteComment); app.provide("getAuthorization", getAuthorization); app.provide("getContext", getContext); app.provide("originalFetch", originalFetch); app.mount(div); } window.addEventListener("load", () => { init().then(() => { console.log("YouTube反诈加载完成"); }).catch((err) => { console.error("YouTube反诈加载失败", err); }); }); })(Vue, ElementPlus);