您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在微博网页端,为每个帖子创建一个收藏按钮,来试图解决帖子收藏的成本较高的问题。
当前为
// ==UserScript== // @name 微博帖子一键收藏、新窗口打开 // @namespace http://tampermonkey.net/ // @version 20240905 // @description 在微博网页端,为每个帖子创建一个收藏按钮,来试图解决帖子收藏的成本较高的问题。 // @author Fat Cabbage // @license MIT // @match https://www.weibo.com/* // @match https://weibo.com/* // @match https://s.weibo.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=weibo.com // @grant none // @require https://code.jquery.com/jquery-3.5.1.min.js // ==/UserScript== /* globals jQuery, $, waitForKeyElements */ let blockConfig = new Map(); let onScrollFlag = false; let needUpdateNodeList = []; const buttonClassName = 'button_a656'; const buttonFavoriteClassName = 'button_a656_favorite'; const buttonOpenNewTabClassName = 'button_a656_open_new_tab'; let settingButtonSelector; let rootNodeClass; let postNodeFullClass; let buttonLocateSelector; let timeNodeSector = ``; let forwardNodeStartClass; let forwardNodeSelector; let buttonClassList; let blogCaches = new Map(); let domain; const domainWeibo = 'weibo.com'; const domainSWeibo = 's.weibo.com'; const domain3WWeibo = 'www.weibo.com'; const CONST_ID = 'ID'; const CONST_BLOG_ID = 'blogID'; const CONST_IS_FAVORITE = 'isFavorites'; const CONST_LAST_UPDATED = 'lastUpdated'; const CONST_IS_LOADING = 'isLoading'; const CONST_RES_OK = 'ok'; const CONST_RES_CODE = 'code'; const promptTimeMs = 1000; (function () { 'use strict'; domain = location.hostname; settingButtonSelector = 'button[title="设置"]' if (domain === domainWeibo || domain === domain3WWeibo) { rootNodeClass = 'vue-recycle-scroller__item-wrapper'; rootNodeClass = 'Main_full'; postNodeFullClass = `Feed_wrap`; buttonLocateSelector = 'div[class*="head_main"]'; timeNodeSector = `a[class^="head-info_time"]`; forwardNodeStartClass = 'retweet Feed_retweet' forwardNodeSelector = 'div.retweet[class*="Feed_retweet"]' } else if (domain === domainSWeibo) { rootNodeClass = 'main-full'; postNodeFullClass = 'card'; // buttonLocateSelector = 'div.menu.s-fr > a'; buttonLocateSelector = 'div.from > a:last-child'; timeNodeSector = `div.from > a:first-child`; } else { return; } let settingButton = document.querySelector(settingButtonSelector) let classList = settingButton.classList buttonClassList = Array.from(classList).filter( className => className.startsWith('IconBox_') ); setTimeout(() => { document.addEventListener('DOMContentLoaded', function () { onScrollFlag = true; }); window.onscroll = () => { onScrollFlag = true; } updateFavoriteButton(); updateFavoriteButton2(); listenRootBlock(); }, 2000); })(); function updateFavoriteButton() { onScrollFlag = true; setInterval(() => { if (onScrollFlag) { for (let [articleNode, config] of blockConfig) { let isVisible = isInViewPortOfOne(articleNode) if (isVisible) { needUpdateNodeList.push(articleNode); } else { needUpdateNodeList = needUpdateNodeList.filter(item => item !== articleNode); } } onScrollFlag = false; } }, 100); } function updateFavoriteButton2() { setInterval(() => { let articleNode = needUpdateNodeList.pop(); if (articleNode) { updateFavoriteButton3(articleNode); let forwardNode = articleNode.querySelector(forwardNodeSelector); if (forwardNode != null) { updateFavoriteButton3(forwardNode); } } }, 100); } function updateFavoriteButton3(articleNode) { let blogID = getBlogID(articleNode); let isLoading = getBlogCacheValue(blogID, CONST_IS_LOADING); if (isLoading) { return; } let buttonNode = articleNode.querySelector(`button[class*="${buttonFavoriteClassName}"]`); if (blogCaches.has(blogID)) { if (domain === domainWeibo || domain === domain3WWeibo) { let lastUpdate = getBlogCacheValue(blogID, CONST_LAST_UPDATED); if (lastUpdate) { let time_diff = new Date() - new Date(lastUpdate); time_diff /= 1000; // Greater than 60 seconds // if (time_diff > 60) { if (time_diff > 1e50) { getFavoriteStatus(blogID).then(() => { updateButtonText(blogID, buttonNode) }); } else { updateButtonText(blogID, buttonNode); } } else { getFavoriteStatus(blogID).then(() => { updateButtonText(blogID, buttonNode) }); } } else if (domain === domainSWeibo) { // s.weibo.com do not update status, due to lack of API support updateButtonText(blogID, buttonNode); } } else { getFavoriteStatus(blogID).then(() => { updateButtonText(blogID, buttonNode) }); } } function listenRootBlock() { setInterval(() => { let rootNode = document.querySelector(`div[class*="${rootNodeClass}"]`); if (rootNode == null) { return; } let isLoadEvent = rootNode.getAttribute('data_a656_is_load_event'); if (isLoadEvent != null) { return; } rootNode.setAttribute('data_a656_is_load_event', true.toString()); if (domain === domainWeibo || domain === domain3WWeibo) { new MutationObserver((mutationsLi) => { for (let mutations of mutationsLi) { if (mutations.type === 'childList') { mutations.addedNodes.forEach(node => { if (node.nodeName === '#text' || node.nodeName === '#comment') { return; } let articleNode = getArticleNode(node, 'default'); if (articleNode == null) { return; } if (blockConfig.get(articleNode) == null) { blockConfig.set(articleNode, {}); } placeFavoriteButton(articleNode); }) } } }).observe(rootNode, {childList: true, subtree: true}); onScrollFlag = true; let postList = rootNode.querySelectorAll(`article[class*="Feed_wrap"]`); postList.forEach(articleNode => { if (!blockConfig.has(articleNode)) { blockConfig.set(articleNode, {}); } let blogID = getBlogID(articleNode); getFavoriteStatus(blogID); placeFavoriteButton(articleNode) }); } else if (domain === domainSWeibo) { let postList = rootNode.querySelectorAll(`div[class="${postNodeFullClass}"]`); onScrollFlag = true; postList.forEach(articleNode => { if (!blockConfig.has(articleNode)) { blockConfig.set(articleNode, {}); } let blogID = getBlogID(articleNode); let id = getBlogIDNum(articleNode); setBlogCaches(blogID, CONST_ID, id); getFavoriteStatus(blogID); placeFavoriteButton(articleNode) }); } }, 500); } function placeFavoriteButton(node) { if (node == null) { return; } if (node.getAttribute('data_a656_value1') === 'true') { return; } node.setAttribute('data_a656_value1', true.toString()); let favoriteButtonNode = createFavoriteButton(); let openButton = createOpenButton(); let targetNode = node.querySelector(buttonLocateSelector); if (domain === domainWeibo || domain === domain3WWeibo) { targetNode.parentNode.insertBefore(favoriteButtonNode, targetNode.nextSibling); targetNode.parentNode.insertBefore(openButton, targetNode.nextSibling); } else if (domain === domainSWeibo) { favoriteButtonNode.style.position = 'absolute'; favoriteButtonNode.style.top = '10px'; favoriteButtonNode.style.right = '40px'; openButton.style.position = 'absolute'; openButton.style.top = '10px'; openButton.style.right = '100px'; openButton.style.marginRight = '0px'; targetNode.parentNode.insertBefore(favoriteButtonNode, targetNode.nextSibling); targetNode.parentNode.insertBefore(openButton, targetNode.nextSibling); } let forwardNode = node.querySelector(forwardNodeSelector); if (forwardNode != null) { let favoriteButtonNode = createFavoriteButton('forward'); let openButton = createOpenButton('forward'); let targetNode = forwardNode.querySelector('a').parentNode; if (domain === domainWeibo || domain === domain3WWeibo) { openButton.style.marginLeft = 'auto'; favoriteButtonNode.style.marginRight = '26px'; targetNode.appendChild(openButton); targetNode.appendChild(favoriteButtonNode); } } } function createFavoriteButton(type = 'default') { let buttonNode = document.createElement('button'); buttonNode.classList.add(buttonClassName); buttonNode.classList.add(buttonFavoriteClassName); addBaseClass(buttonNode); buttonNode.style.width = '50px'; buttonNode.style.height = '28px'; buttonNode.style.marginLeft = '0px'; buttonNode.style.marginRight = '5px'; buttonNode.style.padding = '0'; buttonNode.addEventListener('click', ev => { let buttonNode = ev.target; let articleNode = getArticleNode(buttonNode, type); let blogID = getBlogID(articleNode); let ID = getBlogCacheValue(blogID, CONST_ID) let isFavorites = getBlogCacheValue(blogID, CONST_IS_FAVORITE) let error = false; if (isFavorites) { removeFavorite(ID).then(res => { if (res) { setBlogCaches(blogID, CONST_IS_FAVORITE, false); buttonNode.innerText = `已取消收藏`; setTimeout(() => { buttonNode.innerText = `收藏`; }, promptTimeMs); } else { buttonNode.innerText = `取消收藏失败`; error = true; } }); } else { setFavorite(ID).then(res => { if (res) { setBlogCaches(blogID, CONST_IS_FAVORITE, true); buttonNode.innerText = `已收藏`; setTimeout(() => { buttonNode.innerText = `取消收藏`; }, promptTimeMs); } else { buttonNode.innerText = `收藏失败`; error = true; } }) } }); return buttonNode; } function createOpenButton(type = 'default') { let buttonNode = document.createElement('button'); buttonNode.textContent = '新页面打开'; buttonNode.classList.add(buttonClassName); buttonNode.classList.add(buttonOpenNewTabClassName); addBaseClass(buttonNode); buttonNode.setAttribute('href', '#woo_svg_nav_sun'); buttonNode.style.width = '90px'; buttonNode.style.height = '28px'; buttonNode.style.marginRight = '5px'; buttonNode.style.padding = '0'; buttonNode.addEventListener('click', ev => { let buttonNode = ev.target; let articleNode = getArticleNode(buttonNode, type); let link = articleNode.querySelector(timeNodeSector).href; window.open(link); }); return buttonNode; } function addBaseClass(node) { if (buttonClassList == null) { return; } for (let cl of buttonClassList) { node.classList.add(cl); } } function getBlogID(article_node) { let time_a_node = article_node.querySelector(timeNodeSector); let url = time_a_node.href; let index = url.lastIndexOf('/'); return url.substring(index + 1); } function getBlogIDNum(article_node) { if (domain === domainSWeibo) { let node = article_node; while (node != null) { let actionType = node.getAttribute(`action-type`); if (actionType === `feed_list_item`) { return node.getAttribute(`mid`); } node = node.parentNode; } return null; } return null; } function getFavoriteStatus(blogID) { setBlogCaches(blogID, CONST_IS_LOADING, true); if (domain === domainWeibo || domain === domain3WWeibo) { let url; if (domain === domainWeibo) { url = `https://weibo.com/ajax/statuses/show?id=${blogID}`; } else { url = `https://www.weibo.com/ajax/statuses/show?id=${blogID}`; } return $.ajax({ url: url, type: 'GET', }).then(res => { setBlogCaches(blogID, CONST_ID, res.id); setBlogCaches(blogID, CONST_BLOG_ID, blogID); setBlogCaches(blogID, CONST_IS_FAVORITE, res.favorited); setBlogCaches(blogID, CONST_IS_LOADING, false); }); } else if (domain === domainSWeibo) { return new Promise(() => { setBlogCaches(blogID, CONST_BLOG_ID, blogID); setBlogCaches(blogID, CONST_IS_FAVORITE, false); setBlogCaches(blogID, CONST_IS_LOADING, false); }) } } function setFavorite(ID) { if (domain === domainWeibo || domain === domain3WWeibo) { let data = JSON.stringify({'id': `${ID}`}); let token = getCookie('XSRF-TOKEN'); let url; if (domain === domainWeibo) { url = `https://weibo.com/ajax/statuses/createFavorites`; } else { url = `https://www.weibo.com/ajax/statuses/createFavorites`; } return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(res => { if (typeof res === `string`) { return false; } return res[CONST_RES_OK] === 1; }); } else if (domain === domainSWeibo) { let data = { 'mid': `${ID}` }; return $.ajax({ url: `https://s.weibo.com/ajax_Mblog/favAdd`, type: 'POST', data: data }).then(res => { if (typeof res === `string`) { return false; } return res[CONST_RES_CODE] === `100000`; }); } } function removeFavorite(ID) { if (domain === domainWeibo || domain === domain3WWeibo) { let data = JSON.stringify({'id': `${ID}`}); let token = getCookie('XSRF-TOKEN'); let url; if (domain === domainWeibo) { url = `https://weibo.com/ajax/statuses/destoryFavorites`; } else { url = `https://www.weibo.com/ajax/statuses/destoryFavorites`; } return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(res => { if (typeof res === `string`) { return false; } return res[CONST_RES_OK] === 1; }); } else if (domain === domainSWeibo) { let data = { 'mid': `${ID}` }; return $.ajax({ url: `https://s.weibo.com/ajax_Mblog/favDel`, type: 'POST', data: data }).then(res => { if (typeof res === `string`) { return false; } return res[CONST_RES_CODE] === `100000`; }); } } function getBlogCacheValue(blogID, key) { if (!blogCaches.has(blogID)) { let blogCache = {}; blogCaches.set(blogID, blogCache); } return blogCaches.get(blogID)[key]; } function setBlogCaches(blogID, key, value) { let blogCache = blogCaches.get(blogID); if (blogCache == null) { blogCache = {}; } blogCache[key] = value; blogCache[CONST_LAST_UPDATED] = new Date(); blogCaches.set(blogID, blogCache); } function isInViewPortOfOne(el) { let viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight let screenTop = document.documentElement.scrollTop let screenBottom = screenTop + viewPortHeight let bounding = el.getBoundingClientRect(); let top = screenTop + bounding.top; let bottom = bounding.bottom; return screenTop <= top && top <= screenBottom } function getArticleNode(node, type = 'default') { let postList = node.querySelectorAll(`article[class*="Feed_wrap"]`); if (postList.length > 0) { return postList[0]; } while (node != null) { if (node.className == null) { return null; } if (type === 'default') { if (node.className.indexOf(postNodeFullClass) >= 0) { return node; } } else if (type === 'forward') { if (node.className.indexOf(forwardNodeStartClass) >= 0) { return node; } } node = node.parentNode; } return null; } function updateButtonText(blogID, buttonNode) { let text; if (getBlogCacheValue(blogID, CONST_IS_FAVORITE)) { text = '取消收藏' } else { text = '收藏' } buttonNode.innerText = text; } function getCookie(name) { let cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { let cookie = cookies[i].trim(); if (cookie.startsWith(name + '=')) { return cookie.substring(name.length + 1); } } return null; } function toast(msg, duration) { duration = isNaN(duration) ? 3000 : duration; let m = document.createElement('div'); m.innerHTML = msg; m.style.setProperty('font-size', '20px', 'important'); m.style.setProperty('color', 'rgb(255, 255, 255)', 'important'); m.style.setProperty('background-color', 'rgba(0,0,0,0.6)', 'important'); m.style.setProperty('border-style', 'solid', 'important'); m.style.setProperty('border-color', '#ffffff', 'important'); m.style.setProperty('z-index', '256', 'important'); m.style.cssText = 'font-size: 20px; ' + 'color: rgb(255, 255, 255); ' + 'background-color: rgba(0,0,0,0.6); ' + 'border-style: solid; ' + 'border-color: #ffffff; ' + 'z-index: 256; ' + 'padding: 10px 15px; ' + 'margin: 0 0 0 -60px; ' + 'border-radius: 4px; ' + 'position: fixed; ' + 'top: 50%; ' + 'left: 50%; ' + 'width: 130px; ' + 'text-align: center;'; document.body.appendChild(m); setTimeout(function () { var d = 0.5; m.style.opacity = '0'; setTimeout(function () { document.body.removeChild(m) }, d * 1000); }, duration); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址