微博方便收藏的小脚本(A656)

在微博网页端,为每个帖子创建一个收藏按钮,来试图解决帖子收藏的成本较高的问题。

当前为 2023-03-09 提交的版本,查看 最新版本

// ==UserScript==
// @name         微博方便收藏的小脚本(A656)
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  在微博网页端,为每个帖子创建一个收藏按钮,来试图解决帖子收藏的成本较高的问题。
// @author       Fat Cabbage
// @license      MIT
// @match        https://weibo.com/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=weibo.com
// @grant        none
// ==/UserScript==

let blockConfig = new Map();
let onScrollFlag = false;

(function () {
    'use strict';

    setTimeout(() => {
        let node = document.querySelector('div[class="vue-recycle-scroller__item-wrapper"]');

        node.addEventListener('DOMNodeInserted', ev => {
            let target = ev.target;
            while (target.className !== 'vue-recycle-scroller__item-view') {
                target = target.parentNode;
            }

            if (blockConfig.get(target) == null) {
                blockConfig.set(target, {});
            }

            checkView(node);
        });

        window.onscroll = () => {
            onScrollFlag = true;
        }

        checkView(node);
        updateVisibleBlock();

    }, 2000)
})();

function checkView(el) {
    el.childNodes.forEach(node => {
        let value = node.getAttribute('data_a656_value1');
        if (value == null || value === false) {
            node.setAttribute('data_a656_value1', true);

            let buttonClassName = 'favorite_button_a656';
            let buttonNode = document.createElement('button');
            buttonNode.className = buttonClassName;
            buttonNode.addEventListener('click', ev => {
                let node = ev.target;
                let topNode = node.nextSibling;

                setTimeout(() => {
                    topNode.querySelector('i[class*="morepop_action"]').click();

                    setTimeout(() => {
                        let info;
                        topNode.getElementsByClassName('woo-box-flex woo-box-alignCenter woo-pop-item-main').forEach(ev => {
                            if (ev.innerText === '收藏' || ev.innerText === '取消收藏') {
                                ev.click();

                                info = ev.innerText;
                                node.innerText = `点击${info}`;
                                toast(`已${info}`, 500);
                            }
                        });
                    }, 10);
                }, 10);
            });

            let targetNode = node.querySelector('div[class^="Feed_body"]').querySelector('div[class*="head_main"]');
            targetNode.parentNode.insertBefore(buttonNode, targetNode.nextSibling);
        }
    });
}

function updateVisibleBlock() {
    onScrollFlag = true;
    setInterval(() => {
        if (onScrollFlag) {
            let list = [];

            for (let [node, config] of blockConfig) {
                let isVisible = isInViewPortOfOne(node)
                blockConfig.get(node)['isVisible'] = isVisible;

                if (isVisible) {
                    if (!config['isUpdated']) {
                        blockConfig.get(node)['isUpdated'] = true;
                        list.push(node);
                    }

                } else {
                    blockConfig.get(node)['isUpdated'] = false;
                }
            }

            let i = 0;
            let timer2 = setInterval(() => {
                if (i < list.length) {
                    let node = list[i];
                    getFavoriteStatus(node).then(value => {
                        node.querySelector('button[class="favorite_button_a656"]').innerText = '点击' + value.toString();
                        node.click();
                    });
                    i++;
                } else {
                    onScrollFlag = false;
                    clearInterval(timer2);
                }
            }, 100);
        }
    }, 100);
}

function getFavoriteStatus(el) {
    return new Promise(resolve => {
        // topNode.querySelector('i[class*="morepop_action"]');
        el.querySelector('i[class*="morepop_action"]').click();

        setTimeout(() => {
            el.getElementsByClassName('woo-box-flex woo-box-alignCenter woo-pop-item-main').forEach(ev => {
                if (ev.innerText === '收藏' || ev.innerText === '取消收藏') {
                    resolve(ev.innerText);
                }
            });
            resolve('收藏状态未知');
        }, 50);
    });
}

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);
}

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
}