Have we ever talked before? - MAL

Quickly know if you have ever received Private Messages or profile comments from specific users by opening the user's profile page or by hovering over the user image/name on any page on MAL.

Install this script?
Author's suggested script

You may also like Who Defriended ME!? - MAL.

Install this script
// ==UserScript==
// @name         Have we ever talked before? - MAL
// @namespace    MALCuriosity
// @version      13
// @description  Quickly know if you have ever received Private Messages or profile comments from specific users by opening the user's profile page or by hovering over the user image/name on any page on MAL.
// @author       hacker09
// @match        https://myanimelist.net/*
// @icon         https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64
// @run-at       document-end
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        window.close
// @grant        GM_listValues
// @grant        GM.xmlHttpRequest
// ==/UserScript==

(async function() {
  'use strict';
  var newDocument, ProfileID; //Creates a new global variable
  var HoveredUserName = ''; //Creates a new global variable

  if (GM_getValue('ScriptUserName') === undefined) //If the variable ScriptUserName doesn't exist yet
  { //Starts the if condition
    GM_setValue('ScriptUserName', document.querySelector("a.header-profile-link").innerText); //Save the User Name to the variable ScriptUserName
  } //Finishes the if condition

  async function GetComments() //Creates a function to get the user id and Starts the function
  { //Starts the function
    if (location.href.split('/')[3] === 'profile' && location.href.split('/')[4] !== GM_getValue('ScriptUserName') && HoveredUserName === '') //If the opened page is a profile and if the profile username isn't of the account owner profile, and if the variable HoveredUserName is equal nothing
    { //Starts the if condition
      ProfileID = document.querySelector("input[name*=profileMemId]") !== null ? document.querySelector("input[name*=profileMemId]").value : document.querySelector(".mr0").href.match(/\d+/g)[0]; //Save the user id to the variable ProfileID
    } //Finishes the if condition
    else //If the opened page isn't a profile page
    { //Starts the else condition
      const response1 = await (await fetch('https://api.jikan.moe/v4/users/' + HoveredUserName)).json(); //Fetch
      ProfileID = response1.data.mal_id; //Saves the user ID to the variable ProfileID
    } //Finishes the else condition

    const response = await (await fetch('https://myanimelist.net/comtocom.php?id1=' + ProfileID + '&id2=' + GM_getValue("ScriptUserID"))).text(); //Fetch
    newDocument = new DOMParser().parseFromString(response, 'text/html'); //Parses the fetch response
  } //Finishes the async function

  document.querySelectorAll("a[href*='/profile/']").forEach(Element => Element.addEventListener("mouseover", async function() { //Get all the profile link elements and add an event listener to the link element

    if (this.title.match('comments') === null) //If the element doesn't have the word comments on its title attribute
    { //Starts the if condition
      HoveredUserName = this.href.split('/')[4]; //Save the hovered username to a variable
      this.title = "Hover again to see the updated comments and PMs message."; //Add the text "comments" to the username/image, so that even if the element is hovered again too fast the fetch request won't happen again
      await GetComments(); //Starts the function

      if (newDocument.body.innerText.search("No comments found") > -1) //If the text "No comments found" exists
      { //Starts the if condition
        this.title = "❌ There are no comments between you and this user!"; //Add a text to the username/image
      } //Finishes the if condition
      else //If the text "No comments found" doesn't exist
      { //Starts the else condition
        this.title = "✅ There are comments between you and this user!"; //Add a text to the username/image
      } //Finishes the else condition

      if (GM_listValues().includes(HoveredUserName)) //If the current HoveredUserName is on the user PMed list
      { //Starts the if condition
        this.title = this.title + "\n✅ There are PMs between you and this user!"; //Add a text to the username/image
      } //Finishes the if condition
      else //If the current HoveredUserName isn't on the user PMed list
      { //Starts the else condition
        this.title = this.title + "\n❌ There are no PMs between you and this user!"; //Add a text to the username/image
      } //Finishes the else condition
    } //Finishes the if condition
  })); //Finishes the forEach

  if (location.href === 'https://myanimelist.net/mymessages.php' || (GM_getValue('ScriptUserID') === undefined && location.href.split('/')[4] === GM_getValue('ScriptUserName'))) //If the opened profile username is the account owner profile and if the variable ScriptUserID doesn't exist yet or if the opened URL is = 'https://myanimelist.net/mymessages.php'
  { //Starts the if condition

    var array = []; //Creates a new global array
    var nextpagenum = 0; //Creates a new variable
    if (GM_getValue('ScriptUserID') === undefined && location.href.split('/')[4] === GM_getValue('ScriptUserName')) //If the opened profile username is the account owner profile and if the variable ScriptUserID doesn't exist yet
    { //Starts the if condition
      const response1 = await (await fetch('https://api.jikan.moe/v4/users/' + location.href.split('/')[4])).json(); //Fetch
      GM_setValue('ScriptUserID', response1.data.mal_id); //Save the user id to the variable ScriptUserID
    } //Finishes the if condition

    while (true) { //While the fetched page contains User Names
      const url = 'https://myanimelist.net/mymessages.php?go=&show=' + nextpagenum; //Creates a variable to fetch the PM pages
      var response = await (await fetch(url)).text(); //Fetches the PM pages and converts the fetched pages to text
      const newDocument = new DOMParser().parseFromString(response, 'text/html'); //Parses the fetch response
      nextpagenum += 20; //Increase the next page number by 20

      if (location.href === 'https://myanimelist.net/mymessages.php') //If the opened URL is = 'https://myanimelist.net/mymessages.php'
      { //Starts the if condition
        var DisplayedTotalPMs = 999999999; //Creates a new variable to make the script fetch only the 1-page
      } //Finishes the if condition
      else //If the opened URL isn't = 'https://myanimelist.net/mymessages.php'
      { //Starts the else condition
        DisplayedTotalPMs = parseInt(newDocument.querySelector("div.di-ib").innerText.match(/\d+/g)[2]); //Creates a new variable
      } //Finishes the else condition

      for (const UserNames of newDocument.querySelectorAll("div.mym.mym_user")) { //For every single User Name that sent a PM to the user
        array.push(UserNames.innerText); //Get and save the all mal User Names that sent PMs to the script user
      } //Finishes the for condition

      if (DisplayedTotalPMs >= parseInt(newDocument.querySelector("div.di-ib").innerText.match(/\d+/g)[0])) { //If the fetched page displayed messages total number is greater or equal the User total inbox messages number
        array = [...new Set(array)]; //Remove the duplicated usernames of the array
        array = array.filter(d => !GM_listValues().includes(d)); //Remove the duplicated usernames of the array comparing the usernames that the array has and tampermonkey is missing
        array.forEach(name => GM_setValue(name, 'PMed MAL User Name')) //Get and save the current PMed MAL User Name

        if (location.href !== 'https://myanimelist.net/mymessages.php') //If the opened URL isn't = 'https://myanimelist.net/mymessages.php'
        { //Starts the if condition
          close(); //Close the current tab
        } //Finishes the if condition
        return; //Make the while condition false and stop fetching
      } //Finishes the if condition
      await new Promise(resolve => setTimeout(resolve, 600)); //Timeout to start the next fetch request
    } //Finishes the while condition
  } //Finishes the if condition

  if (location.href.match(/https:\/\/myanimelist\.net\/profile\/[^\/]+(\/)?$/) !== null && location.href.split('/')[4] !== GM_getValue('ScriptUserName')) //If the opened page is a profile page and the profile username isn't of the script user profile
  { //Starts the if condition
    const HasCommented = document.createElement("a"); //Creates an a element
    HasCommented.setAttribute("id", "HasCommented"); //Adds the id HasCommented to the a element
    HasCommented.setAttribute("style", "cursor: pointer; margin-right: 15%;"); //Set the CSS for the button

    await GetComments(); //Starts the function

    if (newDocument.body.innerText.search("No comments found") > -1) //If the text "No comments found" is found
    { //Starts the if condition
      HasCommented.innerHTML = "❌"; //Add the text ❌ to the button
    } //Finishes the if condition
    else //If the text "No comments found" isn't found
    { //Starts the else condition
      HasCommented.innerHTML = "✅"; //Add a text to the button
      HasCommented.onclick = function() { //Detects the mouse click on the '✅' button
        open(`https://myanimelist.net/comtocom.php?id1=${ProfileID}&id2=${GM_getValue("ScriptUserID")}`, '_self'); //Open the user comments page on the same tab
      }; //Finishes the onclick event listener
    } //Finishes the else condition

    document.querySelector("#comment").parentElement.appendChild(HasCommented); //Shows the button

    setTimeout(function() { //Starts the setTimeout function
      document.querySelector("div.mt8 > input").parentElement.appendChild(document.querySelector("#HasCommented").cloneNode(true)); //Clone and append the HasCommented button
      document.querySelectorAll("#HasCommented")[1].onclick = function() { //Detects the mouse click on the second HasCommented button
        document.querySelector("#HasCommented").click(); //Click on the first HasCommented element
      }; //Finishes the onclick event listener
    }, 3000); //Finishes the setTimeout function

    const HasPMed = document.createElement("a"); //Creates an a element
    HasPMed.setAttribute("style", "cursor: pointer; margin-right: 47%;"); //Set the css for the button

    HasPMed.innerHTML = GM_listValues().includes(location.href.split('/')[4]) === true ? "✅" : "❌"; //If the current opened profile User Name is on the user PMed list Add a text to the button

    document.querySelector("#comment").parentElement.appendChild(HasPMed); //Shows the button
  } //Finishes the if condition
})();