// ==UserScript==
// @name NodeSeek+
// @namespace http://tampermonkey.net/
// @version 0.2
// @description load post detail information is automatically loaded when the button is clicked
// @author tsd
// @match https://www.nodeseek.com/*
// @match https://www.nodeseek.com/*
// @icon https://www.nodeseek.com/static/image/favicon/android-chrome-192x192.png
// @license GPLv3
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
// ==/UserScript==
(function () {
"use strict";
console.log("script");
//收藏列表数组
let allCollectionData = [];
// 检查是否登陆
let loginStatus = false;
if (document.querySelector("#nsk-right-panel-container>.user-card")) {
loginStatus = true;
}
if (loginStatus) {
// autoSignIn();
//维护一个全部的收藏id列表
loadUntilEmpty();
}
//TODO 自动签到 or 手动签到
function autoSignIn() {
let timeNow =
new Date().getFullYear() +
"/" +
(new Date().getMonth() + 1) +
"/" +
new Date().getDate(),
timeOld = GM_getValue("menu_signInTime");
if (!timeOld || timeOld != timeNow) {
// 写入签到时间以供后续比较
GM_setValue("menu_signInTime", timeNow);
GM_xmlhttpRequest({
url: "/api/attendance?random=true",
method: "POST",
timeout: 4000,
});
console.log(`[NodeSeek] 签到完成`);
GM_notification({ text: "签到完成!", timeout: 3500 });
}
}
//查看POST中的回复消息
initializePage();
// 定义一个函数来发送GET请求
async function loadData(page) {
const url = `https://www.nodeseek.com/api/statistics/list-collection?page=${page}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error:", error);
return null;
}
}
async function loadUntilEmpty(page = 1) {
while (true) {
const data = await loadData(page);
data.collections.forEach((item) => {
// 将获取到的数据加到数组中
allCollectionData.push(item.post_id);
});
// 如果没有获取到数据或获取到的数据为空,停止加载
if (!data || data.collections.length === 0) {
break;
}
page++;
}
}
function initializePage() {
let lists = document.querySelectorAll(".post-list");
lists.forEach((list) => {
let items = list.childNodes;
items.forEach((element) => {
setupPostItem(element);
});
});
}
function setupPostItem(element) {
let post_item = element.querySelector(".post-title>a");
let new_div = document.createElement("span");
new_div.className = "info-triganle";
new_div.innerHTML = '<span class="triangle">▼</span>';
element.querySelector(".post-info").append(new_div);
setupCursorStyle(new_div);
new_div.onclick = function () {
togglePostContent(post_item, element, new_div);
};
}
function togglePostContent(post_item, element, new_div) {
let id = post_item.href.replace("https://www.nodeseek.com", "");
let content = document.getElementById(id);
if (content) {
toggleDisplay(content, new_div);
} else {
new_div.firstElementChild.innerText = "加载中";
document.body.style.cursor = "wait";
new_div.firstElementChild.className = "content-loaded";
fetchContent(post_item.href, element, (contents, targetEle) => {
insertContentAfter(contents, targetEle);
loadNextPage(contents, targetEle, 1);
new_div.firstElementChild.innerText = "▲";
document.body.style.cursor = "auto";
});
}
}
//显隐箭头
function toggleDisplay(content, new_div) {
if (content.style.display === "none") {
content.style.display = "block";
new_div.firstElementChild.innerText = "▲";
} else {
content.style.display = "none";
new_div.firstElementChild.innerText = "▼";
}
}
//获取div框中的内容TODO返回值判断
function fetchContent(url, targetEle, callback) {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function () {
if (xhr.status === 200) {
const tempContainer = document.createElement("div");
tempContainer.innerHTML = xhr.responseText;
let contents = document.createElement("div");
contents.id = url.replace("https://www.nodeseek.com", "");
contents.className = "content-div";
//content
let post_contents = tempContainer.querySelectorAll(".post-content");
contents.innerHTML += '<div class="post-content-box"></div>';
//menu
let colloct = contents.firstChild;
colloct.innerHTML +=
//TODO:免费鸡腿和like处理
'<div data-v-372de460="" class="comment-menu">' +
// +'<div data-v-372de460="" title="加鸡腿" class="menu-item"><svg data-v-372de460="" class="iconpark-icon"><use data-v-372de460="" href="#chicken-leg"></use></svg><span data-v-372de460="">0</span></div>'
// +'<div data-v-372de460="" title="反对" class="menu-item"><svg data-v-372de460="" class="iconpark-icon"><use data-v-372de460="" href="#bad-one"></use></svg><span data-v-372de460="">0</span></div> '
'<div data-v-372de460="" title="收藏" class="menu-item"><svg data-v-372de460="" class="iconpark-icon"><use data-v-372de460="" href="#star-6negdgdk"></use></svg></div>' +
// +'<div data-v-372de460="" class="menu-item"><svg data-v-372de460="" class="iconpark-icon"><use data-v-372de460="" href="#quote"></use></svg><span data-v-372de460="">引用</span></div> <!----> '
// +'<div data-v-372de460="" class="menu-item"><svg data-v-372de460="" class="iconpark-icon"><use data-v-372de460="" href="#back"></use></svg><span data-v-372de460="">回复</span></div> <!---->
"</div>";
//找收藏的id
let regex = /\/post-(\d+)-1/;
let match = contents.id.match(regex);
let post_id = match[1];
post_id = parseInt(post_id);
let is_collected = allCollectionData.some((item) => item === post_id);
let icon = colloct.firstElementChild.querySelector(".menu-item");
//判断是否已收藏
if (is_collected) {
icon.style.color = "red";
}
setupCursorStyle(colloct);
colloct.onclick = function () {
colloctContent(post_id, colloct);
};
post_contents.forEach((e) => {
contents.firstChild.appendChild(e.parentElement);
});
if (callback && typeof callback === "function") {
callback(contents, targetEle);
}
} else {
console.info("加载完毕");
}
};
xhr.onerror = function () {
console.error("Network error occurred while loading content.");
};
xhr.send();
}
//帖子的收藏处理
function colloctContent(post_id, colloct) {
let icon = colloct.firstElementChild.querySelector(".menu-item");
if (icon.style.color === "red") {
//取消收藏处理
let result = confirm("您确定要取消收藏吗?");
if (result) {
collection_del("remove", post_id).then((success) => {
if (success === true) {
icon.style.color = "";
}
});
} else {
alert("操作被取消了!");
}
} else {
//收藏帖子
collection_add("add", post_id).then((success) => {
if (success === true) {
icon.style.color = "red";
}
});
}
}
//收藏方法
async function collection_add(action_type, post_id) {
const url = "https://www.nodeseek.com/api/statistics/collection";
const data = {
action: action_type,
postId: post_id,
};
try {
const responseData = await postData(url, data);
if (responseData && responseData.success === true) {
alert("收藏成功!");
}
// else if (responseData && responseData.success === false) {
// alert("你已经收藏过了!");
// }
return responseData ? responseData.success : null;
} catch (error) {
console.error("Error in collection_add:", error);
return null;
}
}
//取消收藏方法
async function collection_del(action_type, post_id) {
const url = "https://www.nodeseek.com/api/statistics/collection";
const data = {
action: action_type,
postId: post_id,
};
try {
const responseData = await postData(url, data);
if (responseData && responseData.success === true) {
alert("取消收藏成功!");
}
return responseData ? responseData.success : null;
} catch (error) {
console.error("Error in collection_add:", error);
return null;
}
}
//POST请求
async function postData(url = "", data = {}) {
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const responseData = await response.json();
return responseData;
} catch (error) {
console.error("Error in postData:", error);
}
}
function insertContentAfter(content, targetEle) {
let ul = targetEle.parentNode;
ul.insertBefore(content, targetEle.nextSibling);
}
function loadNextPage(contentDiv, targetEle, currentPage) {
let nextPage = currentPage + 1;
let nextPageUrl = targetEle
.querySelector(".post-title>a")
.href.replace(/(\d+)$/, nextPage);
fetchContent(nextPageUrl, targetEle, (nextContents, targetEle) => {
let postContentBox = contentDiv.querySelector(".post-content-box");
if (nextContents.querySelector(".post-content")) {
let nextPostContents = nextContents.querySelectorAll(".post-content");
nextPostContents.forEach((e) => {
postContentBox.appendChild(e.parentElement);
});
// 递归调用以加载后续页面,延迟1秒
setTimeout(() => {
loadNextPage(contentDiv, targetEle, nextPage);
}, 1000);
}
});
}
function setupCursorStyle(element) {
element.addEventListener("mouseover", function () {
document.body.style.cursor = "pointer";
});
element.addEventListener("mouseout", function () {
document.body.style.cursor = "auto";
});
}
let css = `
.content-div {
height: 600px;
padding: 20px;
margin: 10px auto;
border: 1px solid gray;
border-radius: 10px;
overflow: scroll;
}
.post-content-box {
border-bottom: 2px dashed gray;
padding-bottom: 10px;
margin-bottom: 10px;
}
.triangle {
font-size: medium;
color: gray;
}
.info-triganle{
position: absolute;
right: 54px;
}
.content-loaded {
font-size: medium;
color: red;
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
border-radius: 3px;
background: rgba(0,0,0,0.06);
-webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.08);
}
::-webkit-scrollbar-thumb {
border-radius: 3px;
background: rgba(0,0,0,0.12);
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
}
`;
GM_addStyle(css);
})();