gro / VK Cleaning Likes

// ==UserScript==
// @name         VK Cleaning Likes
// @version      0.1.1
// @description  Скрипт дает возможность очистить лайки от забаненных пользователей на своей странице
// @author       Pavel Gromadchuk
// @homepage     https://vk.com/@gro-vk-cleaning-likes
// @match        https://vk.com/*
// @license      MIT
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// ==/UserScript==

/* jshint esversion: 6 */

(() => {
    'use strict';

    GM_registerMenuCommand('Удалить лайки от забаненных', async () => {
        const link = location.href;

        if (link.match(/w=likes/)) {
            const userId = (await unsafeWindow.fetch('feed2.php').then(res => res.json())).user.id;
            const object = decodeURIComponent(location.search.slice(3));
            const ownerId = +object.match(/\/[a-z]+([-\d]+)/)[1];

            if (userId === ownerId) {
                const users = [];
                const params = {
                    act: 'show',
                    al: 1,
                    offset: 0,
                    w: object
                };

                const total = +document.querySelector('#likes_tab_likes span').innerText;

                do {
                    updateBox('Получаем лайкнувших', params.offset, total);

                    const data = await Request('wkview.php', params);
                    const part = Array.prototype.slice.call(data.getElementsByClassName('fans_fan_row'));

                    params.offset += part.length;
                    users.push(...part);
                } while (params.offset < total);

                const banned = users.filter((user) => {
                    const avatar = user.getElementsByClassName('fans_fan_img')[0].src;
                    
                    return avatar.includes('deactivated') && avatar.includes('.png');
                }).map(user => user.dataset.id);

                if (banned.length) {
                    const total = banned.length;

                    while (banned.length) {
                        const userId = +banned.shift();
                        const spamObject = object.split('/')[1];

                        updateBox('Баним пользователей', total - banned.length, total);

                        const data = await Request('like.php', {
                            act: 'spam',
                            al: 1,
                            mid: userId,
                            object: spamObject
                        });

                        const hash = data.match(/hash: '([\w]+)'/)[1];

                        await Request('like.php', {
                            act: 'do_spam',
                            al: 1,
                            hash,
                            mid: userId,
                            object: spamObject
                        });

                    }

                    updateBox('Готово!');
                } else {
                    updateBox('Не нашлось забаненных пользователей');
                }
            } else {
                Notification('Скрипт работает лишь на вашей личной странице.');
            }
        } else {
            Notification('Не найдет бокс с лайками.');
        }
    });
})();

const Request = async (path, params = {}) => {
    const codeIndex = (params.offset && params.offset > 0) ? 0 : 1 ;

    return new Promise((resolve) => {
        unsafeWindow.ajax.post(path, params, {
            onDone: (...data) => {
                if (path === 'wkview.php') {
                    resolve(textToHtml(data[codeIndex]));
                } else {
                    resolve(data[2]);
                }
            }
        });
    });
}

const Notification = (text, status = false, instruction = false) => {
    const params = {
        title: status ? 'Успешно' : 'Ошибка',
        text
    };

    if (!status) {
        params.text += '\r\nНажмите на уведомление для открытия инструкции.';
        params.onclick = () => window.open('https://vk.com/@gro-vk-cleaning-likes');
    }

    GM_notification(params);
}

const textToHtml = async (text) => {
    const html = document.createElement('div');

    html.innerHTML = text;

    return html;
}

const formatNumber = number => `${number}`.replace(/(\d)(?=(\d{3})+$)/g, '$1 ');

const updateBox = (text, count = 0, total = 0) => {
    const percent = Math.round(100 * count / total);

    let code = '<div style="padding: 50px; text-align: center;">';

    if (count || total) {
        text += ` (${ formatNumber(count) } / ${ formatNumber(total) })`;

        code += `
            <div class="page_attach_progress ui_progress" style="margin-bottom: 10px;">
                <div class="ui_progress_back"></div>
                <div class="ui_progress_bar" style="width: ${ percent }%;"></div>
            </div>
        `;
    }

    code += `<div>${ text }</div>`;
    code += `</div>`;

    document.getElementsByClassName('fans_box')[0].innerHTML = code;
}