您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
同步客户端关注功能
当前为
- // ==UserScript==
- // @name NGA Follow Support
- // @namespace https://gf.qytechs.cn/users/263018
- // @version 1.3.4
- // @author snyssss
- // @description 同步客户端关注功能
- // @match *://bbs.nga.cn/*
- // @match *://ngabbs.com/*
- // @match *://nga.178.com/*
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_addValueChangeListener
- // @grant GM_registerMenuCommand
- // @noframes
- // ==/UserScript==
- ((ui, self) => {
- if (!ui || !self) return;
- // 钩子
- const hookFunction = (object, functionName, callback) => {
- ((originalFunction) => {
- object[functionName] = function () {
- const returnValue = originalFunction.apply(this, arguments);
- callback.apply(this, [returnValue, originalFunction, arguments]);
- return returnValue;
- };
- })(object[functionName]);
- };
- // 用户信息
- class UserInfo {
- execute(task) {
- task().finally(() => {
- if (this.waitingQueue.length) {
- const next = this.waitingQueue.shift();
- this.execute(next);
- } else {
- this.isRunning = false;
- }
- });
- }
- enqueue(task) {
- if (this.isRunning) {
- this.waitingQueue.push(task);
- } else {
- this.isRunning = true;
- this.execute(task);
- }
- }
- rearrange() {
- if (this.data) {
- const list = Object.values(this.children);
- for (let i = 0; i < list.length; i++) {
- if (list[i].source === undefined) {
- list[i].create(this.data);
- }
- Object.entries(this.container).forEach((item) => {
- list[i].clone(this.data, item);
- });
- }
- }
- }
- reload() {
- this.enqueue(async () => {
- this.data = await get_user_info(this.uid);
- Object.values(this.children).forEach((item) => item.destroy());
- this.rearrange();
- });
- }
- constructor(id) {
- this.uid = id;
- this.waitingQueue = [];
- this.isRunning = false;
- this.container = {};
- this.children = {};
- this.reload();
- }
- }
- // 用户信息组件
- class UserInfoWidget {
- destroy() {
- if (this.source) {
- this.source = undefined;
- }
- if (this.target) {
- Object.values(this.target).forEach((item) => {
- if (item.parentNode) {
- item.parentNode.removeChild(item);
- }
- });
- }
- }
- clone(data, [argid, container]) {
- if (this.source) {
- if (this.target[argid] === undefined) {
- this.target[argid] = this.source.cloneNode(true);
- if (this.callback) {
- this.callback(data, this.target[argid]);
- }
- }
- const isSmall = container.classList.contains("posterInfoLine");
- if (isSmall) {
- const anchor = container.querySelector(".author ~ br");
- if (anchor) {
- anchor.parentNode.insertBefore(this.target[argid], anchor);
- }
- } else {
- container.appendChild(this.target[argid]);
- }
- }
- }
- constructor(func, callback) {
- this.create = (data) => {
- this.destroy();
- this.source = func(data);
- this.target = {};
- };
- this.callback = callback;
- }
- }
- ui.sn = ui.sn || {};
- ui.sn.userInfo = ui.sn.userInfo || {};
- ((info) => {
- // 扩展规则
- const extraData = (() => {
- const key = `EXTRA_DATA`;
- const data = GM_getValue(key) || {
- [0]: {
- time: 0,
- },
- };
- const save = () => {
- GM_setValue(key, data);
- };
- const setValue = (uid, value) => {
- data[uid] = value;
- save();
- };
- const getValue = (uid) => data[uid];
- const remove = (uid) => {
- delete data[uid];
- save();
- };
- const specialList = () => {
- const result = Object.entries(data).filter(
- ([key, value]) => value.level
- );
- if (Object.keys(result)) {
- return result;
- }
- return null;
- };
- GM_addValueChangeListener(key, function (_, prev, next) {
- Object.assign(data, next);
- });
- return {
- specialList,
- setValue,
- getValue,
- remove,
- };
- })();
- // 获取用户信息
- const get_user_info = (uid) => {
- const searchPair = (content, keyword, start = "{", end = "}") => {
- // 获取成对括号的位置
- const getLastIndex = (content, position, start = "{", end = "}") => {
- if (position >= 0) {
- let nextIndex = position + 1;
- while (nextIndex < content.length) {
- if (content[nextIndex] === end) {
- return nextIndex;
- }
- if (content[nextIndex] === start) {
- nextIndex = getLastIndex(content, nextIndex, start, end);
- if (nextIndex < 0) {
- break;
- }
- }
- nextIndex = nextIndex + 1;
- }
- }
- return -1;
- };
- // 起始位置
- const str = keyword + start;
- // 起始下标
- const index = content.indexOf(str) + str.length;
- // 结尾下标
- const lastIndex = getLastIndex(content, index, start, end);
- if (lastIndex >= 0) {
- return start + content.substring(index, lastIndex) + end;
- }
- return null;
- };
- return new Promise((resolve, reject) => {
- fetch(`/nuke.php?func=ucp&uid=${uid}`)
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = searchPair(reader.result, `__UCPUSER =`);
- if (text) {
- try {
- resolve(JSON.parse(text));
- return;
- } catch {
- reject();
- return;
- }
- }
- reject();
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- };
- // 获取用户发帖列表
- const get_user_topic_list = (uid) =>
- new Promise((resolve) => {
- fetch(`/thread.php?lite=js&authorid=${uid}`)
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data.__T);
- } else {
- resolve({});
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- resolve({});
- });
- });
- // 获取用户回帖列表
- const get_user_post_list = (uid) =>
- new Promise((resolve, reject) => {
- fetch(`/thread.php?lite=js&authorid=${uid}&searchpost=1`)
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data.__T);
- } else {
- resolve({});
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- resolve({});
- });
- });
- // 关注
- const follow = (uid) =>
- new Promise((resolve, reject) => {
- fetch(
- `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=1`,
- {
- method: "post",
- }
- )
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data[0]);
- } else {
- reject(result.error[0]);
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- // 取消关注
- const un_follow = (uid) =>
- new Promise((resolve, reject) => {
- fetch(
- `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=8`,
- {
- method: "post",
- }
- )
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data[0]);
- } else {
- reject(result.error[0]);
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- // 移除粉丝
- const un_follow_fans = (uid) =>
- new Promise((resolve, reject) => {
- fetch(
- `/nuke.php?lite=js&__lib=follow_v2&__act=follow&id=${uid}&type=256`,
- {
- method: "post",
- }
- )
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data[0]);
- } else {
- reject(result.error[0]);
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- // 获取关注列表
- const follow_list = (page) =>
- new Promise((resolve, reject) => {
- fetch(
- `/nuke.php?lite=js&__lib=follow_v2&__act=get_follow&page=${page}`,
- {
- method: "post",
- }
- )
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data[0]);
- } else {
- reject(result.error[0]);
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- // 获取粉丝列表
- const follow_by_list = (page) =>
- new Promise((resolve, reject) => {
- fetch(
- `/nuke.php?lite=js&__lib=follow_v2&__act=get_follow_by&page=${page}`,
- {
- method: "post",
- }
- )
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data[0]);
- } else {
- reject(result.error[0]);
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- // 获取关注动态
- const follow_dymanic_list = (page) =>
- new Promise((resolve, reject) => {
- fetch(
- `/nuke.php?lite=js&__lib=follow_v2&__act=get_push_list&page=${page}`,
- {
- method: "post",
- }
- )
- .then((res) => res.blob())
- .then((blob) => {
- const reader = new FileReader();
- reader.onload = () => {
- const text = reader.result;
- const result = eval(`
- (${text.replace("window.script_muti_get_var_store=", "")})
- `);
- if (result.data) {
- resolve(result.data);
- } else {
- reject(result.error[0]);
- }
- };
- reader.readAsText(blob, "GBK");
- })
- .catch(() => {
- reject();
- });
- });
- // 切换关注
- const handleSwitchFollow = (uid, isFollow) => {
- if (isFollow) {
- if (confirm("取消关注?")) {
- un_follow(uid).then(() => {
- info[uid]?.reload();
- u.refresh();
- });
- }
- } else {
- follow(uid).then(() => {
- info[uid]?.reload();
- u.refresh();
- });
- }
- };
- // 移除粉丝
- const handleRemoveFans = (uid) => {
- if (confirm("移除粉丝?")) {
- un_follow_fans(uid).then(() => {
- u.refresh();
- });
- }
- };
- // STYLE
- GM_addStyle(`
- .s-user-info-container:not(:hover) .ah {
- display: none !important;
- }
- .s-table-wrapper {
- height: calc((2em + 10px) * 11 + 3px);
- overflow-y: auto;
- }
- .s-table {
- margin: 0;
- }
- .s-table th,
- .s-table td {
- position: relative;
- white-space: nowrap;
- }
- .s-table th {
- position: sticky;
- top: 2px;
- z-index: 1;
- }
- .s-table input:not([type]), .s-table input[type="text"] {
- margin: 0;
- box-sizing: border-box;
- height: 100%;
- width: 100%;
- }
- .s-input-wrapper {
- position: absolute;
- top: 6px;
- right: 6px;
- bottom: 6px;
- left: 6px;
- }
- .s-text-ellipsis {
- display: flex;
- }
- .s-text-ellipsis > * {
- flex: 1;
- width: 1px;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .s-button-group {
- margin: -.1em -.2em;
- }
- `);
- // MENU
- const m = (() => {
- const container = document.createElement("DIV");
- container.className = `td`;
- container.innerHTML = `<a class="mmdefault" href="javascript: void(0);" style="white-space: nowrap;">关注</a>`;
- const content = container.querySelector("A");
- const create = (onclick) => {
- const anchor = document.querySelector("#mainmenu .td:last-child");
- anchor.before(container);
- content.onclick = onclick;
- };
- const update = (count) => {
- if (count) {
- content.innerHTML = `关注 <span class="small_colored_text_btn stxt block_txt_c0 vertmod">${count}</span>`;
- } else {
- content.innerHTML = `关注`;
- }
- };
- return {
- create,
- update,
- };
- })();
- // UI
- const u = (() => {
- const modules = {};
- const createView = () => {
- const tabContainer = (() => {
- const c = document.createElement("div");
- c.className = "w100";
- c.innerHTML = `
- <div class="right_" style="margin-bottom: 5px;">
- <table class="stdbtn" cellspacing="0">
- <tbody>
- <tr></tr>
- </tbody>
- </table>
- </div>
- <div class="clear"></div>
- `;
- return c;
- })();
- const tabPanelContainer = (() => {
- const c = document.createElement("div");
- c.style = "width: 800px;";
- return c;
- })();
- const content = (() => {
- const c = document.createElement("div");
- c.append(tabContainer);
- c.append(tabPanelContainer);
- return c;
- })();
- const addModule = (() => {
- const tc = tabContainer.getElementsByTagName("tr")[0];
- const cc = tabPanelContainer;
- return (module) => {
- const tabBox = document.createElement("td");
- tabBox.innerHTML = `<a href="javascript:void(0)" class="nobr silver">${module.name}</a>`;
- const tab = tabBox.childNodes[0];
- const toggle = () => {
- Object.values(modules).forEach((item) => {
- if (item.tab === tab) {
- item.tab.className = "nobr";
- item.content.style = "display: block";
- item.visible = true;
- } else {
- item.tab.className = "nobr silver";
- item.content.style = "display: none";
- item.visible = false;
- }
- });
- module.refresh();
- };
- tc.append(tabBox);
- cc.append(module.content);
- tab.onclick = (() => {
- let timeout;
- return () => {
- if (timeout > 0) {
- return;
- }
- timeout = setTimeout(() => {
- timeout = 0;
- }, 320);
- toggle();
- };
- })();
- modules[module.name] = {
- ...module,
- tab,
- toggle,
- visible: false,
- };
- return modules[module.name];
- };
- })();
- return {
- content,
- addModule,
- };
- };
- const refresh = () => {
- Object.values(modules)
- .find((item) => item.visible)
- ?.refresh();
- };
- return {
- modules,
- createView,
- refresh,
- };
- })();
- // 我的关注
- {
- const name = "我的关注";
- const content = (() => {
- const c = document.createElement("div");
- c.style.display = "none";
- c.innerHTML = `
- <div class="s-table-wrapper">
- <table class="s-table forumbox">
- <thead>
- <tr class="block_txt_c0">
- <th class="c1" width="1">用户</th>
- <th class="c2">过滤规则</th>
- <th class="c3" width="1">特别关注</th>
- <th class="c4" width="1">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- <div class="silver" style="margin-top: 5px;">特别关注功能需要占用额外的资源,请谨慎开启</div>
- `;
- return c;
- })();
- let page = 0;
- let hasNext = false;
- let isFetching = false;
- const box = content.querySelector("DIV");
- const list = content.querySelector("TBODY");
- const wrapper = content.querySelector(".s-table-wrapper");
- const fetchData = () => {
- isFetching = true;
- follow_list(page)
- .then((res) => {
- hasNext = Object.keys(res).length > 0;
- for (let i in res) {
- const { uid, username } = res[i];
- const data = extraData.getValue(uid) || {};
- if (list.querySelector(`[data-id="${uid}"]`)) {
- continue;
- }
- const item = document.createElement("TR");
- item.className = `row${
- (list.querySelectorAll("TR").length % 2) + 1
- }`;
- item.setAttribute("data-id", uid);
- item.innerHTML = `
- <td class="c1">
- <a href="/nuke.php?func=ucp&uid=${uid}" class="b nobr">${username}</a>
- </td>
- <td class="c2">
- <div class="s-input-wrapper">
- <input value="${data.rule || ""}" />
- </div>
- </td>
- <td class="c3">
- <div style="text-align: center;">
- <input type="checkbox" ${
- data.level ? `checked="checked"` : ""
- } />
- </div>
- </td>
- <td class="c4">
- <div class="s-button-group">
- <button>重置</button>
- <button>移除</button>
- </div>
- </td>
- `;
- const ruleElement = item.querySelector("INPUT");
- const levelElement = item.querySelector(`INPUT[type="checkbox"]`);
- const actions = item.querySelectorAll("BUTTON");
- const save = () => {
- extraData.setValue(uid, {
- rule: ruleElement.value,
- level: levelElement.checked ? 1 : 0,
- });
- };
- const clear = () => {
- ruleElement.value = "";
- levelElement.checked = false;
- save();
- };
- ruleElement.onchange = save;
- levelElement.onchange = save;
- actions[0].onclick = () => clear();
- actions[1].onclick = () => handleSwitchFollow(uid, 1);
- list.appendChild(item);
- }
- })
- .finally(() => {
- isFetching = false;
- });
- };
- box.onscroll = () => {
- if (isFetching || !hasNext) {
- return;
- }
- if (
- box.scrollHeight - box.scrollTop - box.clientHeight <=
- wrapper.clientHeight
- ) {
- page = page + 1;
- fetchData();
- }
- };
- const refresh = () => {
- list.innerHTML = "";
- page = 1;
- hasNext = false;
- fetchData();
- };
- hookFunction(u, "createView", (view) => {
- view.addModule({
- name,
- content,
- refresh,
- });
- });
- }
- // 我的粉丝
- {
- const name = "我的粉丝";
- const content = (() => {
- const c = document.createElement("div");
- c.style.display = "none";
- c.innerHTML = `
- <div class="s-table-wrapper">
- <table class="s-table forumbox">
- <thead>
- <tr class="block_txt_c0">
- <th class="c1">用户</th>
- <th class="c2" width="1">操作</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- `;
- return c;
- })();
- let page = 0;
- let hasNext = false;
- let isFetching = false;
- const box = content.querySelector("DIV");
- const list = content.querySelector("TBODY");
- const wrapper = content.querySelector(".s-table-wrapper");
- const fetchData = () => {
- isFetching = true;
- follow_by_list(page)
- .then((res) => {
- hasNext = Object.keys(res).length > 0;
- for (let i in res) {
- const { uid, username } = res[i];
- if (list.querySelector(`[data-id="${uid}"]`)) {
- continue;
- }
- const item = document.createElement("TR");
- item.className = `row${
- (list.querySelectorAll("TR").length % 2) + 1
- }`;
- item.setAttribute("data-id", uid);
- item.innerHTML = `
- <td class="c1">
- <a href="/nuke.php?func=ucp&uid=${uid}" class="b nobr">${username}</a>
- </td>
- <td class="c2">
- <div class="s-button-group">
- <button>移除</button>
- </div>
- </td>
- `;
- const action = item.querySelector("BUTTON");
- action.onclick = () => handleRemoveFans(uid);
- list.appendChild(item);
- }
- })
- .finally(() => {
- isFetching = false;
- });
- };
- box.onscroll = () => {
- if (isFetching || !hasNext) {
- return;
- }
- if (
- box.scrollHeight - box.scrollTop - box.clientHeight <=
- wrapper.clientHeight
- ) {
- page = page + 1;
- fetchData();
- }
- };
- const refresh = () => {
- list.innerHTML = "";
- page = 1;
- hasNext = false;
- fetchData();
- };
- hookFunction(u, "createView", (view) => {
- view.addModule({
- name,
- content,
- refresh,
- });
- });
- }
- // 关注动态
- {
- const name = "关注动态";
- const content = (() => {
- const c = document.createElement("div");
- c.style.display = "none";
- c.innerHTML = `
- <div class="s-table-wrapper">
- <table class="s-table forumbox">
- <thead>
- <tr class="block_txt_c0">
- <th class="c1" width="1">时间</th>
- <th class="c2">内容</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
- `;
- return c;
- })();
- let page = 0;
- let hasNext = false;
- let isFetching = false;
- const box = content.querySelector("DIV");
- const list = content.querySelector("TBODY");
- const wrapper = content.querySelector(".s-table-wrapper");
- const fetchData = () => {
- isFetching = true;
- follow_dymanic_list(page)
- .then((res) =>
- Promise.all(
- Object.keys(res[1]).map((uid) =>
- get_user_info(uid).then((item) => {
- if (item.follow) {
- const info = extraData.getValue(uid) || {
- rule: "",
- level: 0,
- };
- extraData.setValue(uid, {
- ...info,
- });
- } else {
- extraData.remove(uid);
- }
- })
- )
- ).then(() => {
- return res;
- })
- )
- .then((res) => {
- hasNext = res[2] > res[3];
- extraData.setValue(0, {
- time: Math.floor(new Date() / 1000),
- unread: 0,
- });
- return res;
- })
- .then((res) => {
- const filtered = Object.values(res[0])
- .map((item) => ({
- id: item[0],
- uid: item[2],
- info: item[4]
- ? res[4][`${item[3]}_${item[4]}`]
- : res[4][item[3]],
- time: item[6],
- summary: item.summary
- .replace(
- /\[uid=(\d+)\](.+)\[\/uid\]/,
- `<a href="/nuke.php?func=ucp&uid=${item[2]}" class="b nobr">$2</a>`
- )
- .replace(
- /\[pid=(\d+)\](.+)\[\/pid\](\s?)/,
- `<a href="/read.php?pid=${item[4]}" class="b nobr">回复</a>`
- )
- .replace(
- /\[tid=(\d+)\](.+)\[\/tid\]/,
- item[4] === 0
- ? `<a href="/read.php?tid=${item[3]}" title="$2" class="b nobr">$2</a>`
- : `<a href="/read.php?pid=${item[4]}&opt=128" title="$2" class="b nobr">$2</a>`
- ),
- }))
- .filter((item) => {
- const { uid, info } = item;
- const data = extraData.getValue(uid);
- if (data) {
- const { rule } = data;
- if (rule) {
- return (
- info.subject.search(rule) >= 0 ||
- info.content.search(rule) >= 0
- );
- }
- return true;
- }
- return false;
- });
- return filtered;
- })
- .then((res) => {
- for (let i in res) {
- const { id, time, summary } = res[i];
- if (list.querySelector(`[data-id="${id}"]`)) {
- continue;
- }
- const item = document.createElement("TR");
- item.className = `row${
- (list.querySelectorAll("TR").length % 2) + 1
- }`;
- item.setAttribute("data-id", id);
- item.setAttribute("data-time", time);
- item.innerHTML = `
- <td class="c1">
- <span class="nobr">${ui.time2dis(time)}</span>
- </td>
- <td class="c2">
- <div class="s-text-ellipsis">
- <span>${summary}</span>
- </div>
- </td>
- `;
- list.appendChild(item);
- }
- if (box.scrollHeight === box.clientHeight && hasNext) {
- page = page + 1;
- fetchData();
- }
- })
- .finally(() => {
- isFetching = false;
- });
- };
- box.onscroll = () => {
- if (isFetching || !hasNext) {
- return;
- }
- if (
- box.scrollHeight - box.scrollTop - box.clientHeight <=
- wrapper.clientHeight
- ) {
- page = page + 1;
- fetchData();
- }
- };
- const refresh = () => {
- list.innerHTML = "";
- page = 1;
- hasNext = false;
- fetchData();
- };
- hookFunction(u, "createView", (view) => {
- view.addModule({
- name,
- content,
- refresh,
- });
- });
- }
- // 打开菜单
- const handleCreateView = (() => {
- let view, window;
- return () => {
- if (view === undefined) {
- view = u.createView();
- }
- u.modules["关注动态"].toggle();
- m.update(0);
- if (window === undefined) {
- window = ui.createCommmonWindow();
- }
- window._.addContent(null);
- window._.addTitle(`关注`);
- window._.addContent(view.content);
- window._.show();
- };
- })();
- // 扩展用户信息
- (() => {
- const execute = (argid) => {
- const args = ui.postArg.data[argid];
- if (args.comment) return;
- const uid = +args.pAid;
- if (uid > 0) {
- if (info[uid] === undefined) {
- info[uid] = new UserInfo(uid);
- }
- if (document.contains(info[uid].container[argid]) === false) {
- info[uid].container[argid] =
- args.uInfoC.closest("tr").querySelector(".posterInfoLine") ||
- args.uInfoC.querySelector("div");
- }
- info[uid].enqueue(async () => {
- args.uInfoC.className =
- args.uInfoC.className + " s-user-info-container";
- if (info[uid].children[16]) {
- info[uid].children[16].destroy();
- }
- info[uid].children[16] = new UserInfoWidget(
- (data) => {
- const value = data.follow_by_num || 0;
- const element = document.createElement("SPAN");
- if (uid === self || data.follow) {
- element.className =
- "small_colored_text_btn stxt block_txt_c2 vertmod";
- } else {
- element.className =
- "small_colored_text_btn stxt block_txt_c2 vertmod ah";
- }
- element.style.cursor = "default";
- element.innerHTML = `<span class="white"><span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">★</span> ${value}</span>`;
- element.style.cursor = "pointer";
- return element;
- },
- (data, element) => {
- element.onclick = () => {
- if (data.uid === self) {
- handleCreateView();
- } else {
- handleSwitchFollow(data.uid, data.follow);
- }
- };
- }
- );
- info[uid].rearrange();
- });
- }
- };
- let initialized = false;
- if (ui.postArg) {
- Object.keys(ui.postArg.data).forEach((i) => execute(i));
- }
- hookFunction(ui, "eval", () => {
- if (initialized) return;
- if (ui.postDisp) {
- hookFunction(
- ui,
- "postDisp",
- (returnValue, originalFunction, arguments) => execute(arguments[0])
- );
- initialized = true;
- }
- });
- })();
- // 提醒关注
- (async () => {
- // 增加菜单项
- m.create(handleCreateView);
- // 获取动态
- (() => {
- const cache = extraData.getValue(0) || {
- time: 0,
- unread: 0,
- };
- const fetchData = async (page = 1, result = {}) =>
- new Promise((resolve) => {
- follow_dymanic_list(page).then(async (res) => {
- const list = Object.values(res[0]);
- const prefiltered = list
- .map((item) => ({
- id: item[0],
- uid: item[2],
- info: item[4]
- ? res[4][`${item[3]}_${item[4]}`]
- : res[4][item[3]],
- time: item[6],
- }))
- .filter((item) => item.time > (cache.time || 0))
- .filter((item) => {
- if (result[item.id]) {
- return false;
- }
- result[item.id] = item;
- return true;
- });
- if (prefiltered.length) {
- await Promise.all(
- Object.keys(res[1]).map((uid) =>
- get_user_info(uid).then((item) => {
- if (item.follow) {
- const info = extraData.getValue(uid) || {
- rule: "",
- level: 0,
- };
- extraData.setValue(uid, {
- ...info,
- });
- } else {
- extraData.remove(uid);
- }
- })
- )
- );
- const hasNext =
- prefiltered.length === list.length && res[2] > res[3];
- if (hasNext) {
- const withNext = await fetchData(page + 1, result);
- resolve(withNext);
- }
- }
- resolve(result);
- });
- });
- fetchData().then((res) => {
- const filtered = Object.values(res).filter((item) => {
- const { uid, info } = item;
- const data = extraData.getValue(uid);
- if (data) {
- const { rule } = data;
- if (rule) {
- return (
- info.subject.search(rule) >= 0 ||
- info.content.search(rule) >= 0
- );
- }
- return true;
- }
- return false;
- });
- const unread = (cache.unread || 0) + filtered.length;
- extraData.setValue(0, {
- time: Math.floor(new Date() / 1000),
- unread: unread,
- });
- m.update(unread);
- });
- })();
- // 特别关注
- {
- const fetchData = async (uid, value) => {
- // 请求用户信息
- const { username, follow, posts } = await get_user_info(uid);
- // 用户缓存
- const { rule, time, postNum } = value;
- // 已取消关注
- if (follow === 0) {
- extraData.remove(uid);
- return [];
- }
- // 判断是否有新活动
- if (posts <= (postNum || 0)) {
- return [];
- }
- // 是否匹配
- const isMatch = (text) => {
- if (rule) {
- return text.search(rule) >= 0;
- }
- return true;
- };
- // 请求发帖记录
- const ts = await get_user_topic_list(uid).then((res) =>
- Object.values(res)
- .filter(
- (item) => item.postdate > (time || 0) && isMatch(item.subject)
- )
- .map((item) => ({
- 0: 5,
- 1: item.authorid,
- 2: item.author,
- 5: item.subject,
- 6: item.tid,
- 9: item.postdate,
- 10: 1,
- }))
- );
- // 请求回帖记录
- const ps = await get_user_post_list(uid).then((res) =>
- Object.values(res)
- .filter(
- (item) =>
- item.__P.postdate > (time || 0) && isMatch(item.__P.content)
- )
- .map((item) => ({
- 0: 6,
- 1: uid,
- 2: username,
- 5: item.subject,
- 6: item.__P.tid,
- 7: item.__P.pid,
- 9: item.__P.postdate,
- 10: 1,
- }))
- );
- // 更新缓存
- extraData.setValue(uid, {
- ...value,
- time: Math.floor(new Date() / 1000),
- postNum: posts,
- });
- // 返回结果
- return [...ts, ...ps];
- };
- const data = (
- await Promise.all(
- extraData
- .specialList()
- .map(async ([key, value]) => await fetchData(key, value))
- )
- )
- .flat()
- .sort((a, b) => a[9] - b[9]);
- if (Object.keys(data).length) {
- const func = () => {
- // 修复 NGA 脚本错误
- TPL[KEY["_BIT_SYS"]][KEY["_TYPE_KEYWORD_WATCH_REPLY"]] = function (
- x
- ) {
- return x[KEY["_ABOUT_ID_4"]]
- ? "{_U} 在{_T1} {_R2} 中的 {_R5} 触发了关键词监视<br/>"
- : "{_U} 在主题 {_T} 中的 {_R5} 触发了关键词监视<br/>";
- };
- // 推送消息
- for (let i in data) {
- ui.notification._add(1, data[i], 1);
- }
- // 打开窗口
- ui.notification.openBox();
- };
- if (ui.notification) {
- func();
- } else {
- ui.loadNotiScript(() => {
- func();
- });
- }
- }
- }
- })();
- })(ui.sn.userInfo);
- })(commonui, __CURRENT_UID);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址