Raw Source
viza- / Magic Wand and Uspeli v2.0

// ==UserScript==
// @name         Magic Wand and Uspeli v2.0
// @namespace    http://tampermonkey.net/
// @icon         https://shitcopikabu.strangled.net/icon.png
// @version      2.0
// @description  Разудаление комментариев и вызов "Успели"
// @author       Melhov Anton & v1z
// @updateURL    https://openuserjs.org/install/shitcopikabu/Pikabu_Magic_wand_userscript_edition.user.js
// @homepage     https://vk.com/id59967447
// @homepage     https://vk.com/shitcopikabu
// @homepage     https://vk.com/klubnikapikabu
// @include      http://pikabu.ru/*
// @include      https://pikabu.ru/*
// @include      https://old.pikabu.ru/*
// @connect      https://shitcopikabu.strangled.net
// @connect      https://v25.name
// @license      MIT
// @grant        none
// ==/UserScript==

// ==OpenUserJS==
// @author shitcopikabu & v1z
// @collaborator AHTOH874
// ==/OpenUserJS==

(function() {
  "use strict";

  const VERSION = "2.0.0";
  const DELETED_SPAN_COLOR = 'rgb(136, 136, 136)';
  const TELEGRAM_BACKEND_URL = 'https://v25.name/';
  const UNDELETE_BACKEND_URL = 'https://shitcopikabu.strangled.net/';
  const JSON_HEADERS = {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }

  function getUsername() {
    const username = document.querySelector('.user__nick.user__nick_big');
    return username ? username.innerText : '';
  }

  function getUrl(parentBlock) {
    const link = parentBlock.querySelector('[data-role="link"]');
    return link ? link.href : '';
  }

  function getImages(parentBlock) {
    const foundImages = parentBlock.querySelectorAll('.comment-image__content > a > img');
    return Array.from(foundImages).map(image => image.dataset.largeImage);
  }

  function isNew(images) {
    const lsHistory = JSON.parse(localStorage.getItem('uspeli-images-sent')) || [];
    for (const image of images) {
      if (lsHistory.includes(image)) {
        return false;
      }
      lsHistory.push(image);
    }

    localStorage.setItem('uspeli-images-sent', JSON.stringify(lsHistory));
    return true;
  }

  function postApi(url, data) {
    const composedUrl = TELEGRAM_BACKEND_URL + url;

    const result = fetch(composedUrl, {
      method: 'post',
      headers: JSON_HEADERS,
      body: JSON.stringify(data)
    }).then(function(response) {
      if (response.status == 200) {
        return true;
      } else if (response.status == 400) {
        alert('Эта версия расширения не поддерживается. Требуется обновление.');
        return false;
      } else {
        alert('Не удалось - возможно, сервер сам не успевает.');
        return false;
      }
    });

    return result;
  }

  function postUndeleteApi(url, data) {
    const composedUrl = UNDELETE_BACKEND_URL + url;

    const result = fetch(composedUrl, {
      method: 'post',
      headers: JSON_HEADERS,
      body: JSON.stringify(data)
    }).then(function(response) {
      if (response.status == 200) {
        return response.json();
      } else {
        throw new Error("Server error " + response.status);
      }
    }).then(function(data) {
      return data;
    }).catch(function(e) {
      alert("Ошибка.\n" + e);
      return false;
    })
    return result;
  }


  function wandClick(button, data) {
    postUndeleteApi('ajax/undelete', data).then((response) => {
      if (response && response.comments && response.comments.length > 0) {
        const oldComment = button.closest(".comment__body").querySelector('.comment__content')
        oldComment.innerHTML = response.comments[0].content_html;
        button.remove();
      }
    });
  }

  function createUspeliButton() {
    const button = document.createElement('span');
    button.className = 'uspeli comment__tool hint';
    button.innerText = 'Успеть';
    button.style.color = 'gray';
    button.setAttribute('aria-label', 'Может быть, вы успеете?');
    return button;
  }

  function createWandButton() {
    const button = document.createElement('span');
    button.className = 'wand comment__tool hint';
    button.innerText = '✨';
    button.style.textDecoration = 'none';
    button.setAttribute('aria-label', 'Вернуть как было');
    return button;
  }

  function isRelated(element) {
    if (!element) {
      return false;
    }

    return element.classList.contains('comment-image') ||
           element.classList.contains('comment__content') ||
           element.classList.contains('comment__header') ||
           element.classList.contains('comment__body') ||
           element.classList.contains('comment__controls');
  }

  function uspeliSuccess(uspeliButton) {
    const newButton = document.createElement('span');
    newButton.innerText = "Вы успели!";
    uspeliButton.replaceWith(newButton);
  }

  function checkImages(commentBody) {
    const images = getImages(commentBody);
    if (images.length > 0) {
      const nearest = commentBody.querySelector('.comment__tool[data-role="link"]');
      const uspeliButton = nearest.parentElement.insertBefore(createUspeliButton(), nearest);

      const data = {
        images: images,
        commentUrl: getUrl(commentBody),
        username: getUsername(commentBody),
        version: VERSION
      }

      uspeliButton.addEventListener('click', () => {
        if (!isNew(images)) {
          return uspeliSuccess(uspeliButton);
        }

        postApi('pikabu/accept', data).then(function() {
          return uspeliSuccess(uspeliButton)
        });
      });
    }
  }

  function checkDeletedContent(commentBody) {
    const deletedCommentContent = commentBody.querySelector('.comment__content > span');
    if (deletedCommentContent && deletedCommentContent.style.color == DELETED_SPAN_COLOR) {
      const nearest = commentBody.querySelector('.comment__tool[data-role="link"]');
      const wandButton = nearest.parentElement.appendChild(createWandButton());
      const data = {
          CommentID: parseInt(commentBody.parentElement.dataset.id)
      }
      wandButton.addEventListener('click', (button) => wandClick(button.target, data))
    }
  }

  document.addEventListener('mouseover', (event) => {
    if (isRelated(event.target) || isRelated(event.relatedTarget)) {
      const commentBody = event.target.closest('.comment__body');

      if (!commentBody || commentBody.dataset.parsed) {
        return false;
      }

      checkDeletedContent(commentBody);
      checkImages(commentBody);

      commentBody.dataset.parsed = true;
      return false;
    }
  }, false);
  })();