'use strict' /** * Страница настроек ВКонтакте */ class killer { constructor() { /** * Обработка группы сообщений * * Иницилизионная функция запускающая процессы обработки сообщений * * @param {string} group Группа для обработки * * @return {bool} Статус выполнения * * @todo 1. Добавить режим в котором проверяется не каждая группа сообщений, а * сначала читаются все найденные группы сообщений и только потом обрабатываются все вместе */ this.tribunal = async function (group = this.last(), list = this.list()) { if (await this.search(await this.account(group), await list)) { // Найдена цель на удаление // Инициализация типа действия с группой сообщений заблокированного аккаунта const action = await settings.read('killer_action'); // Обработка найденной цели (сортировано по предполагаемой востребованности) if (action === 'clear') return this.clear(group); else if (action === 'delete') return this.delete(group); else if (action === 'hide') return this.hide(group); else { // Не скрывать группу сообщений // Размытие if (await settings.read('killer_action_blur_status') ?? true) return this.blur(group); // Пометка цветом if (await settings.read('killer_action_highlight_status') ?? false) return this.highlight(group); return true; } } } /** * Обработка всех видимых групп сообщений * * Все сообщения загруженные в документе будут обработаны * * @return {number} Количество обработанных групп сообщений */ this.genocide = async function () { // Инициализация списка сообщений const wrap = this.wrap(); // Инициализация счётчика обработканных групп сообщений let handled = 0; for (let i = 0, l = wrap.children.length; i < l; ++i) { // Перебор групп сообщений // Обработка this.tribunal(wrap.children[i]); // Запись в счётчик ++handled; } return handled; } /** * Найти элемент-оболочку групп сообщений * * Элемент хранит в себе все группы сообщений (окно чата) * * @return {Element} Оболочка групп сообщений */ this.wrap = function () { return document.getElementsByClassName('_im_peer_history im-page-chat-contain')[0]; } /** * Найти последнюю группу сообщений * * Группа сообщений - элемент-оболочка в котором хранятся * сообщения от одного и того же пользователя. Группу сообщений * закрывает сообщение от другого пользователя или прохождение * некоторого времени, таким образом будет создана отдельная группа. * * Определить группы можно по аватарам пользователей (у каждой группы * аватар отправителя отображается только 1 раз) * * @return {Element} Группа сообщений, если найдена */ this.last = function () { // Инициализация списка сообщений const wrap = this.wrap(); // Инициализация последней группы сообщений const group = wrap.children[wrap.children.length - 1]; return group; } /** * Найти и прочитать пользователя группы сообщений * * @param {string} group Группа для обработки (иначе самая последняя, новая) * * @return {number} Идентификатор страницы ВКонтакте (только цифры) */ this.account = function (group = this.last()) { // Инициализация идентификатора отправителя const id = +group.getAttribute('data-peer'); return id; } /** * Прочитать список аккаунтов на удаление из реестра * * Реестр очищается через определённое в настройках время * он должен быть маленьким, чтобы не проводить длительные поиски аккаунта, * но при этом он должен быть большим, чтобы проверять каждого пользователя * в чате слишком часто, что ещё хуже, посколько выполняет отдельный запрос * и ищет данные о нахождении в чёрном списке. * * Реестр может хранить в себе данные об аккаунтах записанных туда непосредственно, * то есть аккаунты не обязательно находятся в чёрном списке * * @return {object} Список аккаунтов на удаление */ this.list = async function () { // Инициализация списка аккаунтов на удаление return await settings.read('killer_targets'); } /** * Поиск аккаунта в списке на удаление * * @param {number} account Аккаунт для обработки * * @return {boolean} Статус наличия аккаунта в списке на удаление * * @todo 1. Возможно оно может асинхронно перезаписывать само себя когда сохраняет нового * пользователя в реестр и сразу же после этого сохраняет реестр с другим пользователем без первого * из-за того что оба процесса были запущены одновременно */ this.search = async function (account = this.account(), targets = this.list()) { if (account === this.self()) { // Обрабатывается свой аккаунт // Инициализация статуса разрешения блокировки самого себя return await settings.read('killer_self') ?? false; } if (typeof await targets === 'object') { // Пройдена проверка на тип if (Object.keys(targets).length >= await settings.read('killer_targets_limit') ?? 500) { // Достигло ограничения количество аккаунтов в реестре // Реинициализация реестра this.purge(); } else { // Не достигло ограничения rоличество аккаунтов в ресстре // Перебор искомых целей для удаления for (const target in targets) if (+target === +account) return true; } } else { // Не пройдена проверка на тип и пустоту // Инициализация реестра this.purge(); } if (await this.blocked(account)) { // Пройдена проверка (заблокирован) // Инициализация буфера записи в хранилище let buffer = targets; // Запись аккаунта в буфер записи в хранилище buffer[account] = true; // Запись в реестр в хранилище settings.write('killer_targets', buffer); return true; } return false; } /** * Удаление группы сообщений (из HTML-документа) * * Для удаления на аккаунте со стороны ВКонтакте использовать this.delete() * * @param {string} group Группа для обработки * * @return {bool} Статус выполнения */ this.clear = async function (group = this.last()) { // Удаление group.remove(); return true; } /** * Скрытие группы сообщений (из HTML-документа) * * Для удаления из документа использовать this.clear() * * @param {string} group Группа для обработки * * @return {bool} Статус выполнения */ this.hide = async function (group = this.last()) { // Скрытие group.style.display = 'none'; return true; } /** * Пометка цветом группы сообщений (в HTML-документе) * * @param {string} group Группа для обработки * * @return {boolean} Статус выполнения */ this.highlight = async function (group = this.last()) { // Инициализация цвета для фона let color = await settings.read('killer_action_highlight_color'); // Проверка полученных значений if (typeof color !== 'string') color = '#aeafd0'; // Запись цвета group.style.background = color; return true; } /** * Размытие группы сообщений (в HTML-документе) * * @param {string} group Группа для обработки * * @return {boolean} Статус выполнения */ this.blur = async function (group = this.last()) { // Инициализация значения степени размытия let degree = await settings.read('killer_action_blur_degree'); // Проверка полученных значений if (typeof degree !== 'number') degree = 3; // Запись размытия group.style.filter = 'blur(' + degree + 'px)'; return true; } /** * Удаление группы сообщений на стороне ВКонтакте * * @param {string} group Группа для обработки * * @return {bool} Статус выполнения */ this.delete = async function (group = this.last()) { return false; } /** * Проверка аккаунта на наличие в списке заблокированных ВКонтакте * * @param {number} account Аккаунт для обработки * * @return {boolean} Статус аккаунта (true - заблокирован) */ this.blocked = async function (account = this.account()) { // Запрос на чтение страницы const response = await fetch('https://vk.com/id' + account); // Инициализация полученных данных const data = new TextDecoder('windows-1251').decode(new DataView(await response.arrayBuffer())); // Инициализация парсера const parser = new DOMParser(); // Инициализация HTML-страницы в понятном JS формате const page = parser.parseFromString(data, "text/html"); // Инициализация кнопки "Заблокировать пользователя" const button = page.body.querySelector('a[data-task-click="ProfileAction/toggle_blacklist"]'); // Возвращает true если заблокирован пользователь (подразумевается наличие текста "Разблокировать {пользователь}") return (button.innerText.trim()[0] === 'Р') ?? false; } /** * Прощение * * Удаляет аккаунт из реестра заблокированных * * @return {boolean} Статус выполнения * * @todo Доделать */ this.forgive = async function (account, targets) { if (typeof account !== 'undefined' && typeof account !== 'null') { // Пройдена проверка полученных значений аргументов // Инициализация буфера записи в хранилище let buffer = {}; for (const target in targets) { // Перебор заблокированных аккаунтов // Проверка на совпадение if (+target === +account) continue; // Запись в буфер записи в хранилище buffer.push(account); } // Запись в реестр в хранилище return settings.write('killer_targets', buffer); } return false; } /** * Очистка реестра аккаунтов * * Полностью удаляет все аккаунты из реестра * * @return {boolean} Статус выполнения */ this.purge = async function () { alert('попа'); return await settings.write('killer_targets', {}); } /** * Прочитать свой идентификатор аккаунта * * Ищет кнопку "Выход из аккаунта" и через неё узнает идентификатор * * @return {number} Идентификатор аккаунта, если найден */ this.self = function () { // Поиск идентификатора const id = +document.getElementById('top_logout_link').getAttribute('onclick').match(/[^_]*(?='\);\slogout\(\);)/)[0]; // Проверка найденного идентификатора if (typeof id === 'number') return id; return null; } } }