Lagom / Strategic Attack Automator

// ==UserScript==
// @name         Strategic Attack Automator
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  This script automatically extracts and schedules attacks based (https://devilicious.dev/) on memo data in Tribal Wars. It requires the "Script Enviar Ataques" by DuMal Team to be installed for full functionality.
// @author       Lagom
// @email        lagomscripts@gmail.com
// @license      AGPL-3.0-or-later
// @match        https://*.tribalwars.com.pt/game.php?*screen=memo*
// @match        https://*.tribalwars.com.pt/game.php?*screen=place*
// @grant        none
// ==/UserScript==

(function () {
  "use strict";
  console.log(
    "Strategic Attack Automator v1.0 by Lagom © 2024. All rights reserved."
  );
  const timeZone = "Europe/Lisbon";

  const TROOPS = [
    {
      unit: "spear",
      wood: 50,
      clay: 30,
      iron: 10,
      population: 1,
      offensive_strength: 10,
      general_defense: 15,
      cavalry_defense: 45,
      archer_defense: 20,
      speed: 18,
      booty: 25,
    },
    {
      unit: "sword",
      wood: 30,
      clay: 30,
      iron: 70,
      population: 1,
      offensive_strength: 25,
      general_defense: 50,
      cavalry_defense: 15,
      archer_defense: 40,
      speed: 22,
      booty: 15,
    },
    {
      unit: "axe",
      wood: 60,
      clay: 30,
      iron: 40,
      population: 1,
      offensive_strength: 40,
      general_defense: 10,
      cavalry_defense: 5,
      archer_defense: 10,
      speed: 18,
      booty: 10,
    },
    {
      unit: "archer",
      wood: 100,
      clay: 30,
      iron: 60,
      population: 1,
      offensive_strength: 15,
      general_defense: 50,
      cavalry_defense: 40,
      archer_defense: 5,
      speed: 18,
      booty: 10,
    },
    {
      unit: "spy",
      wood: 50,
      clay: 50,
      iron: 20,
      population: 2,
      offensive_strength: 0,
      general_defense: 2,
      cavalry_defense: 1,
      archer_defense: 2,
      speed: 9,
      booty: 0,
    },
    {
      unit: "light",
      wood: 125,
      clay: 100,
      iron: 250,
      population: 4,
      offensive_strength: 130,
      general_defense: 30,
      cavalry_defense: 40,
      archer_defense: 30,
      speed: 10,
      booty: 80,
    },
    {
      unit: "mountedarcher",
      wood: 250,
      clay: 100,
      iron: 150,
      population: 5,
      offensive_strength: 120,
      general_defense: 40,
      cavalry_defense: 30,
      archer_defense: 50,
      speed: 10,
      booty: 50,
    },
    {
      unit: "heavy",
      wood: 200,
      clay: 150,
      iron: 600,
      population: 6,
      offensive_strength: 150,
      general_defense: 200,
      cavalry_defense: 80,
      archer_defense: 180,
      speed: 11,
      booty: 50,
    },
    {
      unit: "ram",
      wood: 300,
      clay: 200,
      iron: 200,
      population: 5,
      offensive_strength: 2,
      general_defense: 20,
      cavalry_defense: 50,
      archer_defense: 20,
      speed: 30,
      booty: 0,
    },
    {
      unit: "catapult",
      wood: 320,
      clay: 400,
      iron: 100,
      population: 8,
      offensive_strength: 100,
      general_defense: 100,
      cavalry_defense: 50,
      archer_defense: 100,
      speed: 30,
      booty: 0,
    },
    {
      unit: "knight",
      wood: 20,
      clay: 20,
      iron: 40,
      population: 10,
      offensive_strength: 150,
      general_defense: 250,
      cavalry_defense: 400,
      archer_defense: 150,
      speed: 10,
      booty: 100,
    },
    {
      unit: "snob",
      wood: 40000,
      clay: 50000,
      iron: 50000,
      population: 100,
      offensive_strength: 30,
      general_defense: 100,
      cavalry_defense: 50,
      archer_defense: 100,
      speed: 35,
      booty: 0,
    },
    {
      unit: "militia",
      wood: 0,
      clay: 0,
      iron: 0,
      population: 0,
      offensive_strength: 5,
      general_defense: 15,
      cavalry_defense: 45,
      archer_defense: 25,
      speed: 0.02,
      booty: 0,
    },
  ];

  const STRATEGY_CODE = {
    f1: {
      tabName: "f1",
      allowedTroops: ["axe", "light", "ram", "catapult"],
      message: "Full",
      attackStrategy: "Ataque com todas as tropas possiveis!",
    },
    f2: {
      tabName: "f2",
      allowedTroops: ["axe", "light", "ram", "catapult", "snob"],
      message: "1|Nobre",
      attackStrategy: "Ataque com todas as tropas possiveis!",
    },
    f3: {
      tabName: "f3",
      allowedTroops: ["axe", "light", "ram", "catapult", "snob"],
      message: "3|Nobre",
      attackStrategy:
        "3 quartos no primeiro ataque + arietes e catapultas full e o restante das tropas dividido em mais 3 nobres",
    },
    f4: {
      tabName: "f4",
      allowedTroops: ["axe", "light", "ram", "catapult", "snob"],
      message: "2|Nobre",
      attackStrategy:
        "Dividir em dois ataques de nobres, porém a catapulta e arietes vão todos no primeiro ataque",
    },
  };

  const ATTACK_STORE_NAME = "tribal-attacks-store";

  const memoIdsError = [];

  const tabNames = [];

  const attackQueue = [];

  let openingTab = false;

  function convertToTimeZone(dateString = null) {
    const date = new Date(dateString || Date.now());

    const formattedDate = new Intl.DateTimeFormat("en-US", {
      timeZone: timeZone,
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
      hour12: false,
    }).format(date);

    const [month, day, year, hour, minute, second] =
      formattedDate.match(/\d+/g);
    return new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}`);
  }

  function identifyTroopType(line) {
    if (!line) {
      return null;
    }

    const dataTitleMatch = line.match(/data-title="([^"]+)"/);
    if (dataTitleMatch) {
      const troopType = dataTitleMatch[1];
      if (
        TROOPS.some(
          (troop) => troop.unit.toLowerCase() === troopType.toLowerCase()
        )
      ) {
        return troopType;
      }
    }

    const imgSrcMatch = line.match(/<img src="([^"]+)"/);
    if (imgSrcMatch) {
      const imgSrc = imgSrcMatch[1];
      for (const troop of TROOPS) {
        if (imgSrc.includes(troop.unit.toLowerCase())) {
          return troop.unit;
        }
      }
    }

    return null;
  }

  function generateUUID() {
    return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
      /[xy]/g,
      function (c) {
        var r = (Math.random() * 16) | 0,
          v = c == "x" ? r : (r & 0x3) | 0x8;
        return v.toString(16);
      }
    );
  }

  function addToQueue(
    link,
    departureDateTime,
    attackUUID,
    strategyCode,
    attackerVillage,
    defenderVillage
  ) {
    attackQueue.push({
      link,
      departureDateTime,
      attackUUID,
      strategyCode,
      attackerVillage,
      defenderVillage,
    });
    attackQueue.sort((a, b) => a.departureDateTime - b.departureDateTime);
  }

  const processQueue = () => {
    const attackQueueLocal = [...attackQueue];

    if (attackQueueLocal.length === 0 || openingTab) {
      return;
    }

    const { link, departureDateTime, attackUUID } = attackQueueLocal.shift();

    const formattedLink = link + "&uuid=" + attackUUID;

    const currentTime = convertToTimeZone();

    const delay = departureDateTime.getTime() - currentTime.getTime() - 30000;

    const adjustedDelay = Math.max(delay, 1500);

    openingTab = true;

    setTimeout(() => {
      attackQueue.shift();
      window.open(formattedLink, "_blank");
      openingTab = false;
      processQueue();
    }, adjustedDelay);
  };

  function scheduleAttack(
    link,
    departureTimeString,
    attackUUID,
    strategyCode,
    attackerVillage,
    defenderVillage
  ) {
    if (!link || !departureTimeString) {
      console.error("Argumentos inválidos.");
      return;
    }

    const departureTime = new Date(departureTimeString);
    const currentTime = convertToTimeZone();

    if (departureTime < currentTime) {
      return;
    }

    addToQueue(
      link,
      departureTime,
      attackUUID,
      strategyCode,
      attackerVillage,
      defenderVillage
    );

    processQueue();
  }

  function extractAttacks(tabNames) {
    const contentValueElement = getContainerElement();
    const attacks = [];

    if (!contentValueElement) {
      console.log("Elemento content_value não encontrado");
      return attacks;
    }

    const memoAttackElements = contentValueElement.querySelectorAll(
      "div[id^='memo_'] form table.vis tbody tr.show_row td"
    );

    memoAttackElements.forEach((element) => {
      const parentDivId = element.closest("div[id^='memo_']").id;
      const memoId = parentDivId.substring(5); // Remove o prefixo "memo_"
      const strategyCode =
        tabNames.find((tab) => tab.id === memoId)?.code || "";
      if (memoIdsError.includes(memoId)) {
        return;
      }

      const attackLines = element.innerHTML.split("\n");
      attackLines.forEach((line) => {
        if (line.includes('<a style="color: #4040D0" href=')) {
          const linkMatch = line.match(/href="([^"]+)"[^>]*>Attack<\/a>/);
          const dataTime = extractDateTime(line);

          if (linkMatch && dataTime && dataTime.arrival) {
            const formattedLink = linkMatch[1]?.replace(/&amp;/g, "&") || "";
            const departureTime = dataTime.departure || new Date(0);
            const arrivalTime = dataTime.arrival || new Date(0);
            const troopType = identifyTroopType(line) || "Unknown";
            const troopDetails =
              TROOPS.find((troop) => troop.unit === troopType) || {};

            // Extração das coordenadas e links das aldeias
            const villageMatches = [
              ...line.matchAll(/<a[^>]+>([^<]+) \((\d+\|\d+)\) (K\d+)<\/a>/g),
            ];

            const attackerVillage = villageMatches[0] || [];
            const defenderVillage = villageMatches[1] || [];

            const attackerVillageName = attackerVillage[1]?.trim() || "Unknown";
            const attackerCoordinates = attackerVillage[2] || "Unknown";
            const attackerK = attackerVillage[3] || "Unknown";
            const attackerLink =
              attackerVillage[0]
                ?.match(/href="([^"]+)"/)?.[1]
                ?.replace(/&amp;/g, "&") || "";

            const defenderVillageName = defenderVillage[1]?.trim() || "Unknown";
            const defenderCoordinates = defenderVillage[2] || "Unknown";
            const defenderK = defenderVillage[3] || "Unknown";
            const defenderLink =
              defenderVillage[0]
                ?.match(/href="([^"]+)"/)?.[1]
                ?.replace(/&amp;/g, "&") || "";

            attacks.push({
              uuid: generateUUID(),
              link: formattedLink,
              memoID: memoId,
              departureTime: departureTime,
              arrivalTime: arrivalTime,
              troop: troopDetails,
              strategyCode: strategyCode,
              attackerVillage: {
                name: attackerVillageName,
                coordinates: attackerCoordinates,
                k: attackerK,
                link: attackerLink,
              },
              defenderVillage: {
                name: defenderVillageName,
                coordinates: defenderCoordinates,
                k: defenderK,
                link: defenderLink,
              },
            });
          }
        }
      });
    });

    return attacks;
  }

  function extractDateTime(line) {
    const dateTimeRegex =
      /(\d{4}-\d{2}-\d{2})\s<b>(\d{2}:\d{2}:\d{2}\.\d{3})<\/b>\s\|\s(\d{4}-\d{2}-\d{2})\s(\d{2}:\d{2}:\d{2}\.\d{3})/g;
    let match = dateTimeRegex.exec(line);

    if (match) {
      const departureDate = match[1];
      const departureTime = match[2];
      const arrivalDate = match[3];
      const arrivalTime = match[4];

      const departure = `${departureDate} ${departureTime}`;
      const arrival = `${arrivalDate} ${arrivalTime}`;

      return { departure, arrival };
    }

    return null;
  }

  function extractTabNames() {
    const contentValueElement = getContainerElement();

    if (!contentValueElement) {
      console.log("Elemento content_value não encontrado");
      return tabNames;
    }

    const tabBars = contentValueElement.querySelector("div#tab-bar");
    const erroredTabs = [];

    const tabLabels = tabBars.querySelectorAll(
      "div[id^='tab_']:not(#memo-add-tab-button) span.memo-tab-label strong, " +
        "div[id^='tab_']:not(#memo-add-tab-button) span.memo-tab-label a"
    );

    tabLabels.forEach((tabLabel) => {
      const tabName = tabLabel.textContent.trim();
      const parentDivId = tabLabel.parentNode.parentNode.id;
      const tabID = parentDivId.substring(4);
      if (tabName in STRATEGY_CODE) {
        tabNames.push({ id: tabID, code: tabName });
      } else {
        erroredTabs.push(tabLabel.parentElement);
        memoIdsError.push(tabID);
      }
    });

    if (erroredTabs.length > 0) {
      const errorElement = document.createElement("div");
      const errorText = `As seguintes abas contêm erros: ${erroredTabs
        .map((tab) => tab.textContent.trim())
        .join(", ")}`;
      errorElement.textContent = errorText;
      errorElement.style.color = "red";
      errorElement.style.fontWeight = "bold";
      contentValueElement.insertBefore(errorElement, tabBars);
    }

    return tabNames;
  }

  function getContainerElement() {
    const contentValueElement = document.getElementById("content_value");

    if (!contentValueElement) {
      return null;
    }

    return contentValueElement;
  }

  function getAttacksFromLocalStorage() {
    const storedAttacks = localStorage.getItem(ATTACK_STORE_NAME);
    return storedAttacks ? JSON.parse(storedAttacks) : [];
  }

  // function getEstrategyCodeFromLocalStorage(memoID) {
  //   const storedAttacks = getAttacksFromLocalStorage();
  //   const attack = storedAttacks.find((attack) => attack.memoID === memoID);
  //   return attack ? attack.strategyCode : null;
  // }

  function getAttackDetailsFromLocalStorage(UUID) {
    const storedAttacks = getAttacksFromLocalStorage();
    const attack = storedAttacks.find((attack) => attack.uuid === UUID);
    return attack;
  }

  function saveAttacksToLocalStorage(attacks) {
    const currentTime = convertToTimeZone();

    const filteredAttacks = attacks.filter((attack) => {
      const attackTime = new Date(attack.departureTime);
      return attackTime > currentTime;
    });

    localStorage.setItem(ATTACK_STORE_NAME, JSON.stringify(filteredAttacks));
  }

  function extractTroopData() {
    const troopData = {};

    const formData = document.querySelector("#command-data-form");

    const troopTables = formData.querySelectorAll("table");

    troopTables.forEach((table) => {
      const troopType = table.querySelector("th")?.textContent.trim();

      if (!troopType) return;

      troopData[troopType] = {};

      const unitsEntryAll = table.querySelectorAll(".units-entry-all");

      unitsEntryAll.forEach((entry) => {
        const unitName = entry.getAttribute("data-unit");

        const countText = entry.textContent.match(/\((\d+)\)/);
        const unitCount = countText ? parseInt(countText[1]) : 0;

        troopData[troopType][unitName] = unitCount;
      });
    });

    return troopData;
  }

  function addTroopsToInputs(troopData, attackUUID, strategyCode) {
    const formData = document.querySelector("#command-data-form");
    const actionUrl = formData.getAttribute("action");

    formData.setAttribute("action", `${actionUrl}&uuid=${attackUUID}`);

    const getTropsByEstrategy = allocateTroopsByStrategy(
      troopData,
      strategyCode
    );

    for (const troopName in getTropsByEstrategy) {
      if (getTropsByEstrategy.hasOwnProperty(troopName)) {
        const troopCount = getTropsByEstrategy[troopName];

        const inputField = formData.querySelector(`#unit_input_${troopName}`);

        if (inputField) {
          inputField.value = troopCount;
        }
      }
    }
  }

  function createUIAttacksAutomator() {
    if (!attackQueue || attackQueue.length === 0) return;

    const container = createUIContainer();

    addCountdownAndAttackCount(container);

    enableDrag(container);

    createUIContent(container);

    document.body.appendChild(container);
  }

  function createUIContainer() {
    const container = document.createElement("div");
    container.id = "attack-automator-container";
    container.style.position = "fixed";
    container.style.top = "12%";
    container.style.left = "1.5%";
    container.style.backgroundColor = "#2d2d2d";
    container.style.padding = "20px";
    container.style.paddingTop = "5px";
    container.style.borderRadius = "10px";
    container.style.boxShadow = "rgba(0, 0, 0, 0.8) -14px 19px 14px 4px";
    container.style.zIndex = "99998";
    container.style.cursor = "move";
    container.style.userSelect = "none";
    container.style.width = "450px";
    container.style.height = "500px";

    container.innerHTML += `
        <style>
            #attack-automator-container {
                display: flex;
                flex-direction: column;
                height: 100%;
            }

            #attack-automator-container h3 {
              margin-bottom: auto;
            }

            #attack-automator-container ul {
                list-style-type: none;
                padding: 0;
                flex-grow: 1;
                overflow-y: auto;
                max-height: 300px;
            }

            #attack-automator-container li {
                margin-bottom: 10px;
                font-size: 1rem;
                color: #ccc; // Cor do texto da lista
            }

            #attack-automator-container button {
                display: block;
                margin-top: 20px;
                padding: 10px 20px;
                background-color: #4CAF50;
                cursor: pointer;
                font-size: 1rem;
                font-weight: bold;
                margin-top: auto;
            }

            #attack-automator-container button:hover {
                background-color: #45a049;
            }
            /* Estilo do scrollbar */
            #attack-automator-container ul::-webkit-scrollbar {
                width: 8px; /* Largura do scrollbar */
            }

            /* Estilo do thumb do scrollbar */
            #attack-automator-container ul::-webkit-scrollbar-thumb {
                background-color: #888; /* Cor do thumb */
                border-radius: 4px; /* Arredondamento do thumb */
            }

            /* Estilo do thumb do scrollbar quando hover */
            #attack-automator-container ul::-webkit-scrollbar-thumb:hover {
                background-color: #555; /* Cor do thumb ao passar o mouse */
            }

            .village-link {
              color: white;
              font-size: 14px;
              text-decoration: none;
            }

            .village-link:hover {
                color: #ff69b4;
            }
        </style>
    `;
    return container;
  }

  function addCountdownAndAttackCount(container) {
    const countdownDisplay = document.createElement("p");
    countdownDisplay.id = "countdown-display";
    countdownDisplay.style.marginBottom = "10px";
    countdownDisplay.style.fontSize = "1rem";
    countdownDisplay.style.color = "#b3b3b3";
    container.appendChild(countdownDisplay);

    const attacksInfoContainer = document.createElement("div");
    attacksInfoContainer.style.display = "flex";
    attacksInfoContainer.style.justifyContent = "space-between";
    attacksInfoContainer.style.marginTop = "10px";

    const attacksCountDisplay = document.createElement("p");
    attacksCountDisplay.textContent = `Ataques na fila: ${attackQueue.length}`;
    attacksCountDisplay.style.fontSize = "1rem";
    attacksCountDisplay.style.color = "#b3b3b3";
    attacksCountDisplay.style.marginTop = "0px";
    attacksInfoContainer.appendChild(attacksCountDisplay);

    const sentAttacksContainer = document.createElement("div");
    sentAttacksContainer.style.display = "flex";
    sentAttacksContainer.style.alignItems = "center";

    const sentAttacksCount = getSentAttacksCount();
    const sentAttacksDisplay = document.createElement("p");
    sentAttacksDisplay.id = "sent-attacks-display";
    sentAttacksDisplay.style.fontSize = "1rem";
    sentAttacksDisplay.style.color = "#b3b3b3";
    sentAttacksDisplay.style.marginTop = "0px";
    sentAttacksDisplay.textContent = `Ataques enviados: ${sentAttacksCount}`;
    sentAttacksContainer.appendChild(sentAttacksDisplay);

    const resetSentAttacksButton = document.createElement("button");
    resetSentAttacksButton.id = "reset-sent-attacks-button";
    resetSentAttacksButton.className = "icon-button";
    resetSentAttacksButton.title = "Resetar Ataques Enviados";
    resetSentAttacksButton.style.marginTop = "-15px";
    resetSentAttacksButton.style.marginLeft = "4px";
    resetSentAttacksButton.style.borderRadius = "50%";
    resetSentAttacksButton.style.padding = "0px";
    resetSentAttacksButton.style.width = "20px";
    resetSentAttacksButton.style.height = "20px";
    resetSentAttacksButton.style.border = "none";
    resetSentAttacksButton.style.cursor = "pointer";
    resetSentAttacksButton.style.backgroundImage =
      "url('https://iili.io/JiYhECX.png')";
    resetSentAttacksButton.style.backgroundSize = "cover";
    resetSentAttacksButton.style.backgroundColor = "transparent";
    resetSentAttacksButton.addEventListener("click", resetSentAttacksCount);
    resetSentAttacksButton.addEventListener("mouseover", () => {
      resetSentAttacksButton.style.transform = "scale(1.1)";
    });
    resetSentAttacksButton.addEventListener("mouseout", () => {
      resetSentAttacksButton.style.transform = "scale(1)";
    });
    sentAttacksContainer.appendChild(resetSentAttacksButton);

    attacksInfoContainer.appendChild(sentAttacksContainer);
    container.appendChild(attacksInfoContainer);

    const timerZoneDisplay = document.createElement("span");
    timerZoneDisplay.id = "timer-zone-display";
    timerZoneDisplay.style.position = "absolute";
    timerZoneDisplay.style.fontSize = "10px";
    timerZoneDisplay.style.color = "#b3b3b3";
    timerZoneDisplay.textContent = `Time Zone: (${timeZone})`;
    timerZoneDisplay.style.top = "5px";
    timerZoneDisplay.style.right = "20px";
    container.appendChild(timerZoneDisplay);

    const intervalId = setInterval(updateCountdown, 1000);

    function updateCountdown() {
      const now = convertToTimeZone();
      const nextAttackTime = new Date(attackQueue[0]?.departureDateTime);
      if (!nextAttackTime || nextAttackTime < now) {
        clearInterval(intervalId);
        return;
      }

      const timeDifference = nextAttackTime - now;

      if (timeDifference > 0) {
        const seconds = Math.floor((timeDifference / 1000) % 60);
        const minutes = Math.floor((timeDifference / (1000 * 60)) % 60);
        const hours = Math.floor((timeDifference / (1000 * 60 * 60)) % 24);
        const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));

        countdownDisplay.textContent = `Próximo ataque: ${days}d ${hours}h ${minutes}m ${seconds}s`;
        attacksCountDisplay.textContent = `Ataques na fila: ${attackQueue.length}`;
        updateUIContent();
      } else {
        updateUIContent();
        countdownDisplay.textContent = "Não existe ataques!";
        attacksCountDisplay.textContent = `Ataques na fila: ${attackQueue.length}`;
      }
    }
  }

  function enableDrag(container) {
    let isDragging = false;
    let offsetX = 0,
      offsetY = 0;

    function startDrag(e) {
      e.preventDefault();
      isDragging = true;
      offsetX = e.clientX - container.getBoundingClientRect().left;
      offsetY = e.clientY - container.getBoundingClientRect().top;
    }

    function endDrag() {
      isDragging = false;
    }

    function drag(e) {
      if (isDragging) {
        const newX = e.clientX - offsetX;
        const newY = e.clientY - offsetY;
        container.style.left = `${newX}px`;
        container.style.top = `${newY}px`;
      }
    }

    container.addEventListener("mousedown", startDrag);
    document.addEventListener("mousemove", drag);
    document.addEventListener("mouseup", endDrag);
  }

  function createUIContent(container) {
    const title = document.createElement("h3");
    title.textContent = "Ataques Agendados";
    title.style.fontSize = "1.5rem";
    title.style.color = "rgb(179, 179, 179)";
    title.style.marginTop = "-1px";
    container.appendChild(title);

    if (attackQueue.length === 0) {
      const message = document.createElement("p");
      message.textContent = "Nenhum ataque agendado.";
      message.style.color = "#999";
      container.appendChild(message);
    } else {
      const list = document.createElement("ul");
      list.id = "attack-queue-list";
      list.style.listStyleType = "none";
      list.style.padding = "0";
      attackQueue.sort(
        (a, b) => new Date(a.departureTime) - new Date(b.departureTime)
      );

      updateUIContent();

      container.appendChild(list);
    }

    const closeButton = document.createElement("button");
    closeButton.textContent = "Fechar";
    closeButton.style.border = "none";
    closeButton.style.borderRadius = "5px";
    closeButton.addEventListener("click", () => {
      container.remove();
    });
    container.appendChild(closeButton);
  }

  function updateUIContent() {
    const sentAttacksCount = getSentAttacksCount();
    const attacksDisplay = document.getElementById("sent-attacks-display");
    if (attacksDisplay) {
      attacksDisplay.textContent = `Ataques enviados: ${sentAttacksCount}`;
    }

    const attackList = document.getElementById("attack-queue-list");
    if (!attackList) return;

    attackList.innerHTML = "";

    attackQueue.forEach((attack) => {
      const item = document.createElement("li");

      const date = new Date(attack.departureDateTime);
      const options = {
        day: "2-digit",
        month: "2-digit",
        year: "numeric",
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit",
        hour12: false,
      };
      const formattedDate = date
        .toLocaleDateString("pt-BR", options)
        .replace(/(\d{2})\/(\d{2})\/(\d{4}), /, "$1 - ");

      const attackerLink = document.createElement("a");
      attackerLink.className = "village-link";
      attackerLink.href = attack.attackerVillage.link;
      attackerLink.setAttribute("target", "_blank");
      attackerLink.textContent = `(${attack.attackerVillage.coordinates}):${attack.attackerVillage.k}`;
      attackerLink.title = attack.attackerVillage.name;

      const defenderLink = document.createElement("a");
      defenderLink.className = "village-link";
      defenderLink.href = attack.defenderVillage.link;
      defenderLink.setAttribute("target", "_blank");
      defenderLink.textContent = `(${attack.defenderVillage.coordinates}):${attack.defenderVillage.k}`;
      defenderLink.title = attack.defenderVillage.name;

      item.innerHTML = `${formattedDate} - `;
      item.appendChild(attackerLink);
      item.innerHTML += ` > `;
      item.appendChild(defenderLink);
      item.innerHTML += ` - ${STRATEGY_CODE[attack.strategyCode].message}`;

      item.style.marginBottom = "5px";
      item.style.fontSize = "13px";

      attackList.appendChild(item);
    });
  }

  function allocateTroopsByStrategy(troopData, strategyCode) {
    if (!strategyCode || !STRATEGY_CODE[strategyCode]) {
      console.error("Invalid strategy code.");
      return;
    }

    const strategy = STRATEGY_CODE[strategyCode];
    const allowedTroops = strategy.allowedTroops;

    const allocatedTroops = {};

    for (const category in troopData) {
      if (troopData.hasOwnProperty(category)) {
        const troops = troopData[category];

        for (const troopType in troops) {
          if (
            troops.hasOwnProperty(troopType) &&
            allowedTroops.includes(troopType)
          ) {
            allocatedTroops[troopType] = troops[troopType];
          }
        }
      }
    }

    return allocatedTroops;
  }

  function resetSentAttacksCount() {
    localStorage.setItem("sentAttacksCount", 0);
    document.getElementById(
      "sent-attacks-display"
    ).textContent = `Ataques enviados: 0`;
  }

  function handleMemoPage() {
    console.log("Strategic Attack Automator running on the screen memo");

    createUIScriptRunning();

    const tabNames = extractTabNames();
    const attacks = extractAttacks(tabNames);

    saveAttacksToLocalStorage(attacks);

    attacks.forEach((attack) => {
      const linkWithStrategyCode = `${attack.link}&strategy=${attack.strategyCode}`;
      scheduleAttack(
        linkWithStrategyCode,
        attack.departureTime,
        attack.uuid,
        attack.strategyCode,
        attack.attackerVillage,
        attack.defenderVillage
      );
    });

    createUIAttacksAutomator();
  }

  function createUIScriptRunning() {
    const container = document.createElement("div");
    container.id = "script-running-container";
    container.style.position = "fixed";
    container.style.top = "70px";
    container.style.right = "20px";
    container.style.backgroundColor = "rgba(0, 0, 0, 0.8)";
    container.style.color = "#fff";
    container.style.padding = "10px";
    container.style.borderRadius = "5px";
    container.style.zIndex = "99999";
    container.style.display = "flex";
    container.style.flexDirection = "column";
    container.style.alignItems = "center";
    container.style.width = "200px";

    const icon = document.createElement("img");
    icon.src = "https://iili.io/JiRdqP9.md.png";
    icon.alt = "Script Icon";
    icon.style.width = "100px";
    icon.style.height = "100px";
    icon.style.marginTop = "-15px";

    const label = document.createElement("p");
    label.textContent = "Lagom";
    label.style.margin = "-6px 0 0 0";
    label.style.fontSize = "12px";
    label.style.fontWeight = "bold";
    label.style.color = "#fff";

    const contact = document.createElement("p");
    contact.textContent = "lagomscripts@gmail.com";
    contact.style.bottom = "10px";
    contact.style.fontSize = "12px";
    contact.style.color = "#fff";

    const text = document.createElement("p");
    text.textContent = "Iniciando Script...";
    text.style.margin = "10px 0 0 0";
    text.style.color = "#fff";
    text.style.textAlign = "center";

    container.appendChild(contact);
    container.appendChild(icon);
    container.appendChild(label);
    container.appendChild(text);
    document.body.appendChild(container);

    const phrases = [
      "O inimigo está tão confuso que pensa que isso é um bug do jogo!",
      "Estamos invadindo mais terras do que um bando de coelhos em uma plantação de cenouras!",
      "Preparem-se para o ataque! Eu espero que todos tenham trazido biscoitos, porque a batalha vai ser longa.",
      "Vamos dominar o mundo... depois de uma rápida pausa para café!",
      "O inimigo está tão assustado que está pensando em mudar de mundo!",
      "Nossas tropas são como um buffet grátis: irresistíveis e em constante movimento!",
      "Os defensores estão tão confusos que estão perguntando se isso é uma festa de disfarces!",
      "Nossos ataques são como piadas ruins: você não pode escapar deles!",
      "Estamos atacando com tanta força que até os Barbaros estão se escondendo!",
      "Nossos ataques são mais agressivos do que um abraço de urso durante uma briga de bar!",
      "A única coisa que se espalha mais rápido do que nossas tropas são boatos sobre suas dividas.",
      "Os inimigos estão tão surpresos que estão pensando em mudar de profissão para palhaço de circo!",
      "Nossos planos são tão complexos que até o Google Maps está perdido!",
      "Atacamos mais vezes do que um telefone tocando durante um filme!",
      "Nossos inimigos estão tão desesperados que estão procurando por cheats no Google!",
      "Estamos fazendo mais barulho do que um grupo de cosplayers de Super Mario em um evento de jogos!",
      "Os defensores estão tão confusos que estão pedindo dicas no grupo da Familia!",
      "Nós invadimos mais vilas do que um político em período de eleições!",
      "Se eu ganhasse uma moeda de ouro para cada ataque, eu seria mais rico do que o Bill Gates!",
      "Aquecendo para o meu próximo ataque. Alerta de spoiler: sua vila perde.",
      "Se está lendo isso, parabéns! Você já encontrou o melhor aliado para dominar o mundo! Precisa de mais ajuda? Só chamar!",
      "Shhh... estou me escondendo do Lagom. Não diga a ele que estou saqueando sua vila.",
      "Este script é tão eficiente que até um esquilo poderia dominar sua vila... com uma katana.",
      "Me sentindo fofo, posso apagar suas fazendas mais tarde. Não sei, depende do meu humor.",
      "Não tenha ciúmes, mas minhas tropas são basicamente os X-Men... menos a coisa de salvar o mundo.",
      "Está gostando dos ataques? Imagino que sim! Quer maximizar ainda mais seu potencial? Estou aqui para ajudar!",
      "Executar este script é como colocar um novo par de meias manchadas de chimichanga.",
      "Minhas tropas estão tão famintas que até comeriam um unicórnio... se eu tivesse um. Mas só tenho você.",
      "Parece que você está se divertindo com meu script. Quer descobrir mais truques e segredos? Estou só a uma mensagem de distância!",
      "Relaxe, meu amigo, eu estou aqui para te ajudar... a se livrar de recursos desnecessários. Tipo, muitos recursos.",
      "Não se preocupe em defender sua vila, estou assumindo essa responsabilidade... para que você possa focar em coisas mais importantes, como comer chimichangas.",
      "Ataque preventivo? Que nada, é só uma forma educada de dizer 'estou te ajudando a reduzir seu estoque'.",
      "Seus recursos estão seguros comigo... até eu decidir o contrário.",
      "Não se preocupe em reabastecer seus recursos, eu já estou cuidando disso... na minha vila.",
    ];

    setTimeout(() => {
      const randomIndex = Math.floor(Math.random() * phrases.length);
      text.textContent = phrases[randomIndex];
      setInterval(() => {
        const randomIndex = Math.floor(Math.random() * phrases.length);
        text.textContent = phrases[randomIndex];
      }, 60000);
    }, 7000);
  }

  function handlePlaceConfirmPage() {
    console.log(
      "Strategic Attack Automator running on the screen confirm place"
    );

    setTimeout(() => {
      const urlParams = new URLSearchParams(window.location.search);
      const attackUUID = urlParams.get("uuid");

      if (attackUUID == null || attackUUID == "null") return;

      const attackDetails = getAttackDetailsFromLocalStorage(attackUUID);

      if (!attackDetails) {
        return console.log("Attack not found.");
      }

      const arrivalInput = document.getElementById("CStime");

      arrivalInput.value = attackDetails.arrivalTime;

      clickButton("CSbutton", 0);

      setTimeout(() => {
        incrementSentAttacksCount();
        window.close();
      }, 35000);
    }, 2000);
  }

  function incrementSentAttacksCount() {
    const sentAttacksCount =
      (parseInt(localStorage.getItem("sentAttacksCount"), 10) || 0) + 1;

    localStorage.setItem("sentAttacksCount", sentAttacksCount);
  }

  function getSentAttacksCount() {
    return parseInt(localStorage.getItem("sentAttacksCount"), 10) || 0;
  }

  function handlePlacePage() {
    console.log("Strategic Attack Automator running on the screen place");

    const urlParams = new URLSearchParams(window.location.search);
    const strategyCode = urlParams.get("strategy");
    const attackUUID = urlParams.get("uuid");
    const troopData = extractTroopData();

    if (attackUUID == null || attackUUID == "null") return;

    addTroopsToInputs(troopData, attackUUID, strategyCode);

    clickButton("target_attack");
  }

  function clickButton(id, timeout = 0) {
    const button = document.querySelector(`#${id}`);

    if (button) {
      setTimeout(() => {
        button.click();
      }, timeout);
    }
  }

  function init() {
    const url = window.location.href;

    const pageHandlers = {
      "screen=memo": handleMemoPage,
      "screen=place&try=confirm": handlePlaceConfirmPage,
      "screen=place": handlePlacePage,
    };

    const handler = Object.entries(pageHandlers).find(([key]) =>
      url.includes(key)
    );

    if (handler) {
      handler[1]();
    }
  }

  init();
})();