Greasy Fork is available in English.

IdlePixel Activity Log Tweaks

Adds a new activity log to the top next to player count and introduces a new Activity Log modal.

// ==UserScript==
// @name         IdlePixel Activity Log Tweaks
// @namespace    godofnades.idlepixel
// @version      0.7.18
// @description  Adds a new activity log to the top next to player count and introduces a new Activity Log modal.
// @author       GodofNades
// @match        *://idle-pixel.com/login/play*
// @grant        none
// @require      https://greasyfork.org/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
// @license      MIT
// ==/UserScript==

(function () {
	"use strict";

	let player, storeName, activityLog;
	const dbName = "ActivityLogDB";

	function initDB() {
		return new Promise((resolve, reject) => {
			const request = indexedDB.open(dbName, 1);
			request.onerror = (event) => reject(event.target.error);
			request.onsuccess = (event) => resolve(event.target.result);
			request.onupgradeneeded = (event) => {
				const db = event.target.result;
				db.createObjectStore(storeName, { keyPath: "id", autoIncrement: true });
			};
		});
	}

	async function saveNewData(newData) {
		const db = await initDB();
		const transaction = db.transaction([storeName], "readwrite");
		const store = transaction.objectStore(storeName);
		store.add({newData});
	}

	class UITweaksPlugin extends IdlePixelPlusPlugin {
		constructor() {
			super("actlogtweaks", {
				about: {
					name: `IdlePixel Activity Log Tweaks (ver: 0.7.18)`,
					version: `0.7.18`,
					author: `GodofNades`,
					description: `Adds a new activity log to the top next to player count and introduces a new Activity Log modal.`,
				},
				/*config: [
                    {
                        label: "------------------------------",
                        type: "label"
                    },
                    {
                        label: "General Stuff",
                        type: "label"
                    },
                    {
                        label: "------------------------------",
                        type: "label"
                    },
                    {
                        id: "font",
                        label: "Primary Font",
                        type: "select",
                        options: FONTS,
                        default: FONT_DEFAULT
                    }
                ]*/
			});
			this.activeFilters = new Set([
				"Achievement",
				"Brewing",
				"Combat",
				"Cooking",
				"Crafting",
				"Criptoe",
				"Economy",
				"Farming",
				"Fishing",
				"Gems",
				"Geodes",
				"New Loot",
				"SD Watch",
				"Woodcutting",
				"XP",
			]);
		}

		initStyles() {
			const css = `
          div#al-table-div {
            height: 100%;
            width: 100%;
            overflow-y: scroll;
            max-height: 100%;
            display: block;
            margin:auto;
            border: 1px solid #708090; /* Border around the outside of the table */
          }

          table#al-table-table {
            border-collapse: collapse; /* Changed to collapse for combined borders */
            width: 100%;
            background-color: #000; /* Table background */
          }

          th.al-table-header {
            border: 1px solid #708090; /* Border for th */
            color: white;
            text-align: center;
            padding: 0.5em 1em;
            background-color: black;
          }

          td.al-table-cell {
            border: 1px solid #708090; /* Border for td */
            color: white;
            text-align: center;
            padding: 0.5em 1em;
          }

          th.al-table-header:nth-child(2), td.al-table-cell:nth-child(2) {
            border-left: 1px solid #708090;
            border-right: 1px solid #708090;
          }

          #al-table-table tbody tr:nth-child(odd) {
            background-color: #222;
          }

          #al-table-table tbody tr:nth-child(even) {
            background-color: #333;
          }

          tr.al-table-header-row {
            position: sticky;
            top: 0;
            z-index: 1;
          }
          p.activity-subheader {
            -webkit-text-stroke: 1px #00f7ff;
            font-size: 20pt;
            font-weight: bold;
            text-align: center;
            //background-color: #000;
          }

          td.activity-cell {
            text-align: left;
            width: 50%;
          }

          #filter-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            background-color: black;
            padding: 1px;
            border: 1px solid #708090;
            color: white;
            margin-top: 10px;
            margin-bottom: 10px;
          }

          #filter-title {
            font-size: 1.5em;
            font-weight: bold;
            margin-bottom: 10px;
          }

          #filter-box {
            display: grid;
            grid-template-columns: 1fr 1px 1fr;
            grid-gap: 10px;
            padding-left: 10px;
            padding-right: 10px;
            padding-top: 10px;
            text-align: left;
            width: 100%;
            border-top-width: 1px;
            border-top-style: solid;
            border-top-color: #708090;
          }

          .filter-column {
            display: flex;
            flex-direction: column;
          }

          .filter-column label {
            margin-bottom: 5px;
          }

          .vertical-line {
            background-color: #708090;
            width: 1px;
            height: 100%;
          }

          .filter-label {
            display: flex;
            align-items: center; /* Aligns the checkbox and label text vertically */
            margin-right: 5px;
          }

          .category-filter {
            margin-right: 5px; /* Space between the checkbox and the label text */
          }

          .al-button {
            padding: 10px 20px;
            margin: 0 5px;
            border: 1px solid #708090;
            background-color: #333;
            color: white;
            cursor: pointer;
          }

          .al-button:hover {
            background-color: #444;
          }
              `;
			const styleSheet = document.createElement("style");
			styleSheet.innerHTML = css;
			document.head.appendChild(styleSheet);
		}

		createPanel() {
			let alModalHTML = `
        <div id="modal-style-al" style="display: none">
            <div style="position: absolute; top: 0px; left: 0px; width: 98vw; height: 100vh;">
                <div id="al-modal-base_window" style="position: absolute; top: 10vh; left: 25vw; width: 30vw; height: 85vh; text-align: center; border: 1px solid grey; background-color: rgb(0, 0, 0); border-radius: 20px; padding: 20px; z-index: 10000; display: flex; align-items: center; flex-direction: column;">
                    <div id="close-button-al" style="background-color: red; width: 30px; height: 30px; position: absolute; top: 10px; right: 10px; border-radius: 50%; cursor: pointer;">
                        <p style="color: white; font-size: 20px; font-weight: bold; text-align: center; line-height: 30px;">X</p>
                    </div>
                    <br/>
                    <p id="activity-subheader" class="activity-subheader">Your Recent Activity Log <span id="row-count">(0)</span></p>
                    <div id="al-table-div" class="al-table-div">
                      <table id="al-table-table">
                        <thead>
                          <tr class="al-table-header-row">
                            <th class="al-table-header" style="width: 25%">Time</th>
                            <th class="al-table-header" style="width: 25%">Category</th>
                            <th class="al-table-header" style="width: 50%">Activity</th>
                          </tr>
                        </thead>
                        <tbody>
                        <!-- Data rows will be inserted here -->
                        </tbody>
                      </table>
                    </div>
                  </div>
                  </br>
                </div>
            </div>
    `;

			const contentDiv = document.getElementById("content");
			const modalContainer = document.createElement("div");
			modalContainer.innerHTML = alModalHTML;
			contentDiv.appendChild(modalContainer);

			const onlineCount = document.querySelector(
				".game-top-bar .gold:not(#game-top-bar-admin-link)"
			);
			const linkElement = document.createElement("a");
			linkElement.href = "#";
			linkElement.className = "hover float-end link-no-decoration";
			linkElement.title = "Activity Log";
			linkElement.textContent = "Activity" + "\u00A0\u00A0\u00A0";

			onlineCount.insertAdjacentElement("beforebegin", linkElement);

			const modalStyleAL = document.getElementById("modal-style-al");
			const closeButton = document.getElementById("close-button-al");

			linkElement.addEventListener("click", function (event) {
				event.preventDefault();
				modalStyleAL.style.display = "block";
			});

			closeButton.addEventListener("click", function () {
				modalStyleAL.style.display = "none";
			});

			document.addEventListener("keydown", function (event) {
				var chat_focused = $("#chat-area-input").is(":focus");
				if (!chat_focused) {
					// Use the key code for backtick, which is 192
					/*                     if (event.keyCode === 192) {
                        if (modalStyleAL.style.display === 'block') {
                            // Close the modal
                            modalStyleAL.style.display = 'none';
                        } else {
                            // Open the modal
                            modalStyleAL.style.display = 'block';
                        }
                    } */
				}
			});

			// Add an event listener to the modal
			modalStyleAL.addEventListener("click", function (event) {
				// Check if the click happened outside the modal window
				const isClickInside = document
					.getElementById("al-modal-base_window")
					.contains(event.target);

				if (!isClickInside) {
					// If the click is outside, hide the modal
					modalStyleAL.style.display = "none";
				}
			});

			// Stop propagation of the click event from the modal window to the modal background
			document
				.getElementById("al-modal-base_window")
				.addEventListener("click", function (event) {
					event.stopPropagation();
				});

			const colorElements = [
				{
					id: "mainBoxColor",
					label: "Primary Box (Background)",
					default: "#000000",
				},
				{
					id: "boxHeaderColor",
					label: "Header (Text)",
					default: "#00f7ff",
				},
				{
					id: "filterTextClass",
					label: "Checkbox Labels (Text)",
					default: "#ffffff",
				},
			];

			/*colorElements.forEach(colorElement => {
                const colorPicker = this.createColorPicker(colorElement.id, colorElement.label, colorElement.default);
                document.getElementById('al-modal-base_window').appendChild(colorPicker);
            });*/
		}

		/*createColorPicker(id, label, defaultColor) {
            const wrapper = document.createElement('div');
            const pickerLabel = document.createElement('label');
            pickerLabel.setAttribute('for', id);
            pickerLabel.textContent = label;

            const pickerInput = document.createElement('input');
            pickerInput.type = 'color';
            pickerInput.id = id;

            // Load existing color settings or initialize if not present
            const storedColors = localStorage.getItem('activitylog.colors');
            const colors = storedColors ? JSON.parse(storedColors) : {};

            pickerInput.value = colors[id] || defaultColor;

            pickerInput.addEventListener('input', function(event) {
                // Retrieve the latest colors object
                const storedColors = localStorage.getItem('activitylog.colors');
                const currentColors = storedColors ? JSON.parse(storedColors) : {};

                // Update the current color in the colors object
                currentColors[event.target.id] = event.target.value;

                // Apply color changes immediately
                IdlePixelPlus.plugins.actlogtweaks.applyLiveColorPreferences(event.target.id, event.target.value);

                // Save the updated colors object to localStorage
                localStorage.setItem('activitylog.colors', JSON.stringify(currentColors));
            });

            wrapper.appendChild(pickerLabel);
            wrapper.appendChild(pickerInput);
            return wrapper;
        }

        applyColorPreferences() {
            // Load existing color settings or initialize if not present
            const colors = JSON.parse(localStorage.getItem('activitylog.colors')) || {};

            // Apply all color preferences based on the colors object
            for (const id in colors) {
                const color = colors[id];
                // Determine which element the color should be applied to based on the id
                switch (id) {
                    case 'mainBoxColor':
                        document.getElementById('al-modal-base_window').style.backgroundColor = color;
                        break;
                    case 'boxHeaderColor':
                        document.querySelectorAll('.activity-subheader').forEach(element => {
                            element.style.webkitTextStroke = `1px ${color}`;
                        });
                        break;
                    case 'filterTextClass':
                        document.querySelectorAll('.filter-label').forEach(element => {
                            element.style.color = color;
                        });
                        // Add cases for other color IDs as necessary
                }
            }
        }

        applyLiveColorPreferences(id, color) {
            // Apply color preferences based on the passed id and color
            switch (id) {
                case 'mainBoxColor':
                    document.getElementById('al-modal-base_window').style.backgroundColor = color;
                    break;
                case 'boxHeaderColor':
                    document.querySelectorAll('.activity-subheader').forEach(element => {
                        element.style.webkitTextStroke = `1px ${color}`;
                    });
                    break;
                case 'filterTextClass':
                    document.querySelectorAll('.filter-label').forEach(element => {
                        element.style.color = color;
                    });
                    break;
                    // Add cases for other color IDs as necessary
            }
        }*/

		initFilters() {
			const filterContainer = document.createElement("div");
			filterContainer.id = "filter-container";

			const title = document.createElement("div");
			title.id = "filter-title";
			title.textContent = "Filters";
			filterContainer.appendChild(title);

			const filterBox = document.createElement("div");
			filterBox.id = "filter-box";
			filterBox.style.display = "grid";
			filterBox.style.gridTemplateColumns = "repeat(3, 1fr)";
			filterBox.style.gridGap = "10px";
			filterBox.style.padding = "10px";
			filterBox.style.alignItems = "start";
			filterContainer.appendChild(filterBox);

			// Original categories array without "Uncategorized"
			let categories = [
				"Achievement",
				"Brewing",
				"Combat",
				"Cooking",
				"Crafting",
				"Criptoe",
				"Economy",
				"Farming",
				"Fishing",
				"Gems",
				"Geodes",
				"New Loot",
				"SD Watch",
				"Woodcutting",
				"XP",
			];

			// Sort categories alphabetically
			categories.sort();

			// Add "Uncategorized" at the end
			categories.push("Uncategorized");

			// Calculate the number of categories per column
			const perColumn = Math.ceil(categories.length / 3);

			// Create three columns and distribute the sorted categories into them
			for (let i = 0; i < 3; i++) {
				const columnDiv = document.createElement("div");
				columnDiv.className = "filter-column";
				// Apply borders to the middle column
				if (i === 1) {
					columnDiv.style.borderLeft = "1px solid #708090";
					columnDiv.style.borderRight = "1px solid #708090";
					columnDiv.style.paddingLeft = "10px";
				}
				filterBox.appendChild(columnDiv);

				// Slice the sorted categories array to get the categories for this column
				const columnCategories = categories.slice(
					i * perColumn,
					(i + 1) * perColumn
				);
				columnCategories.forEach((category) => {
					const label = document.createElement("label");
					label.className = "filter-label";

					const checkbox = document.createElement("input");
					checkbox.type = "checkbox";
					checkbox.className = "category-filter";
					checkbox.checked = this.activeFilters.has(category);
					checkbox.value = category;

					const labelText = document.createTextNode(category);
					label.appendChild(checkbox);
					label.appendChild(labelText);

					columnDiv.appendChild(label);
				});
			}

			const activitySubheader = document.querySelector(".activity-subheader");
			activitySubheader.parentNode.insertBefore(
				filterContainer,
				activitySubheader.nextSibling
			);

			// Add event listeners to checkboxes
			document.querySelectorAll(".category-filter").forEach((checkbox) => {
				checkbox.addEventListener("change", this.handleFilterChange.bind(this));
			});

			// Create buttons container
			const buttonsContainer = document.createElement("div");
			buttonsContainer.id = "buttons-container";
			buttonsContainer.style.display = "flex";
			buttonsContainer.style.justifyContent = "center";
			buttonsContainer.style.gap = "30px";
			buttonsContainer.style.marginTop = "10px"; // Adjust as needed for spacing
			buttonsContainer.style.marginBottom = "10px"; // Adjust as needed for spacing

			// Create the "All" button
			const allButton = document.createElement("button");
			allButton.textContent = "All";
			allButton.className = "al-button";
			allButton.onclick = () => {
				this.setAllFilters(true);
			};

			// Create the "None" button
			const noneButton = document.createElement("button");
			noneButton.textContent = "None";
			noneButton.className = "al-button";
			noneButton.onclick = () => {
				this.setAllFilters(false);
			};

			// Append buttons to the buttons container
			buttonsContainer.appendChild(allButton);
			buttonsContainer.appendChild(noneButton);

			// Append buttons container after the filterContainer
			filterContainer.after(buttonsContainer);
		}

		setAllFilters(check) {
			// This will either check or uncheck all checkboxes based on the 'check' boolean
			const checkboxes = document.querySelectorAll(".category-filter");
			checkboxes.forEach((checkbox) => {
				checkbox.checked = check;
				// Update activeFilters set based on the 'check' boolean
				if (check) {
					this.activeFilters.add(checkbox.value);
				} else {
					this.activeFilters.delete(checkbox.value);
				}
			});
			// After changing the checkboxes, apply the filters
			this.applyFilters();
		}

		handleFilterChange(event) {
			const category = event.target.value;
			if (event.target.checked) {
				this.activeFilters.add(category);
			} else {
				this.activeFilters.delete(category);
			}
			this.applyFilters();
		}

		applyFilters() {
			const rows = document.querySelectorAll("#al-table-table tbody tr");
			rows.forEach((row, index) => {
				// Reset the display and remove any inline background-color style
				row.style.display = "";
				row.style.backgroundColor = ""; // Clear any inline style

				// Get the category from the cell
				const categoryCellText = row.cells[1].textContent;
				if (
					this.activeFilters.has(categoryCellText) ||
					this.activeFilters.size === 0
				) {
					// Show the row
					row.style.display = "";
				} else {
					// Hide the row
					row.style.display = "none";
				}
			});

			// Reapply the zebra striping
			this.zebraStripeRows();
			this.updateRowCount();
		}

		zebraStripeRows() {
			const visibleRows = document.querySelectorAll(
				'#al-table-table tbody tr:not([style*="display: none"])'
			);
			visibleRows.forEach((row, index) => {
				if (index % 2 === 0) {
					row.style.backgroundColor = "#333"; // Lighter for even rows
				} else {
					row.style.backgroundColor = "#222"; // Darker for odd rows
				}
			});
		}

		updateRowCount() {
			const visibleRows = document.querySelectorAll(
				'#al-table-table tbody tr:not([style*="display: none"])'
			);
			const rowCount = visibleRows.length;
			const rowCountSpan = document.getElementById("row-count");

			// Format the count with commas based on the user's locale
			const formattedCount = rowCount.toLocaleString();
			rowCountSpan.textContent = ` (${formattedCount})`;
		}

		onLogin() {
			websocket.send(ActivityLogger.hide());
			websocket.send((ActivityLogger.forceHide = true));
			this.initStyles();
			this.createPanel();
			this.initFilters();
			player = IdlePixelPlus.getVarOrDefault("username");
			storeName = `${player}.activityLog`;
			const oldData = this.fetchOldData(player);
			const updatedData = this.updateData(oldData);
			this.saveNewData(player, updatedData);
			activityLog =
				JSON.parse(localStorage.getItem(player + ".activityLog")) || [];
			this.displayActivityLog();
			//this.applyColorPreferences()
		}

		determineCategory(message) {
			const categoryPatterns = {
				Achievement: /^You completed the achievement/,
				Brewing: /^(You brew|Not Consumed!)/,
				Combat:
					/^(You (defeated a|died to a)|Tsunami Triggered in Beach|FP REFUNDED|ENERGY REFUNDED)/,
				Cooking: /^You (burnt|successfully cooked)/,
				Crafting: /^You craft/,
				Criptoe: /^Research Points gained:/,
				Economy: /^You sell/,
				Farming: /^((You|Your) (harvest|tool finds a seed)|Instantly grows)/,
				Fishing:
					/^(You just caught a|You caught a|Found 1 Super Bait using Rotten Potion)/,
				Gathering: /^You found a unique gathering item/,
				Gems: /^You find a/,
				Geodes: /^You found a [a-z_]+ geode/,
				"New Loot": /^You looted a new item/,
				"SD Watch": /^SD Watch Charge Used/,
				Woodcutting: /^You chop down/,
				XP: /^(You gain a [a-z_]+ level|You unlocked a new skill|You gained)/,
			};

			for (const [category, pattern] of Object.entries(categoryPatterns)) {
				if (pattern.test(message)) {
					//console.log(message);
					return category;
				}
			}
			//console.log(message);
			return "Uncategorized";
		}

		formatDate() {
			const now = new Date();
			return now.getTime(); // Returns the Unix timestamp
		}

		isUnixTimestamp(timestamp) {
			const numericTimestamp = Number(timestamp);
			return !isNaN(numericTimestamp) && numericTimestamp > 1000000000;
		}

		fetchOldData(player) {
			const oldDataRaw = localStorage.getItem(player + ".activityLog");
			return oldDataRaw ? JSON.parse(oldDataRaw) : [];
		}

		convertToUnix(originalDateString) {
			const date = new Date(originalDateString);
			return date.getTime();
		}

		updateData(data) {
			return data.map((entry) => {
				// Create a new object excluding the category field
				const { category, ...entryWithoutCategory } = entry;

				// Check and convert timestamp if necessary
				if (!this.isUnixTimestamp(entry.timestamp)) {
					const unixTimestamp = this.convertToUnix(entry.timestamp);
					return { ...entryWithoutCategory, timestamp: unixTimestamp };
				}

				return entryWithoutCategory;
			});
		}

		saveNewData(player, newData) {
			localStorage.setItem(player + ".activityLog", JSON.stringify(newData));
		}

		convertUnixToReadable(unixTimestamp) {
			const date = new Date(unixTimestamp);
			const yearFormatter = new Intl.DateTimeFormat(undefined, {
				year: "numeric",
			});
			const monthFormatter = new Intl.DateTimeFormat(undefined, {
				month: "2-digit",
			});
			const dayFormatter = new Intl.DateTimeFormat(undefined, {
				day: "2-digit",
			});
			const hourFormatter = new Intl.DateTimeFormat(undefined, {
				hour: "2-digit",
				hour12: false,
			});
			const minuteFormatter = new Intl.DateTimeFormat(undefined, {
				minute: "2-digit",
			});
			const secondFormatter = new Intl.DateTimeFormat(undefined, {
				second: "2-digit",
			});

			const year = yearFormatter.format(date);
			const month = monthFormatter.format(date);
			const day = dayFormatter.format(date);
			let hour = hourFormatter.format(date);
			let minute = minuteFormatter.format(date);
			let second = secondFormatter.format(date);

			hour = hour.padStart(2, "0");
			minute = minute.padStart(2, "0");
			second = second.padStart(2, "0");

			const formattedDate = `${year}/${month}/${day} ${hour}:${minute}:${second}`;
			return formattedDate;
		}

		buildActivityLog(time, category, message, color) {
			const tr = document.createElement("tr");
			const tdTimestamp = document.createElement("td");
			tdTimestamp.className = "al-table-cell";
			tdTimestamp.style.color = color;
			tdTimestamp.textContent = this.convertUnixToReadable(time);

			const tdCategory = document.createElement("td");
			tdCategory.className = "al-table-cell";
			tdCategory.style.color = color;
			tdCategory.textContent = category;

			const tdMessage = document.createElement("td");
			tdMessage.className = "al-table-cell activity-cell";
			tdMessage.style.color = color;
			tdMessage.style.fontWeight = "bold";
			tdMessage.textContent = message;

			tr.appendChild(tdTimestamp);
			tr.appendChild(tdCategory);
			tr.appendChild(tdMessage);

			const tableBody = document.querySelector("#al-table-table tbody");

			// Insert the new row at the top of the table body
			if (tableBody.firstChild) {
				tableBody.insertBefore(tr, tableBody.firstChild);
			} else {
				tableBody.appendChild(tr);
			}

			// Construct the new log entry
			activityLog.unshift({
				timestamp: time,
				message: message,
				color: color,
			});
			// Trim the activityLog array to 1000 entries if necessary
			if (activityLog.length > 250) {
				activityLog.length = 250;
			}
			// Store the updated activityLog array in localStorage
			localStorage.setItem(
				player + ".activityLog",
				JSON.stringify(activityLog)
			);
			// Update the table with the new log entry
			//this.displayActivityLog();

			const newLogEntry = {
				timestamp: time,
				category: category,
				message: message,
				color: color,
			};

			saveNewData(newLogEntry);

			this.applyFilters();
		}

		displayActivityLog() {
			// Retrieve the stored log
			let activityLog =
				JSON.parse(localStorage.getItem(player + ".activityLog")) || [];

			// Get the table body where logs should be displayed
			const tableBody = document.querySelector("#al-table-table tbody");

			// Clear any existing rows in the table body
			tableBody.innerHTML = "";

			// Loop through each log entry and add it to the table
			activityLog.forEach((logEntry) => {
				let category = this.determineCategory(logEntry.message);
				const tr = document.createElement("tr");

				const tdTimestamp = document.createElement("td");
				tdTimestamp.className = "al-table-cell";
				tdTimestamp.style.color = logEntry.color;
				tdTimestamp.textContent = this.convertUnixToReadable(
					logEntry.timestamp
				);

				const tdCategory = document.createElement("td");
				tdCategory.className = "al-table-cell";
				tdCategory.style.color = logEntry.color;
				tdCategory.textContent = category;

				const tdMessage = document.createElement("td");
				tdMessage.className = "al-table-cell activity-cell";
				tdMessage.style.color = logEntry.color;
				tdMessage.textContent = logEntry.message;

				tr.appendChild(tdTimestamp);
				tr.appendChild(tdCategory);
				tr.appendChild(tdMessage);

				// Add the new row to the table body
				tableBody.appendChild(tr);
			});
			this.updateRowCount();
		}

		onVariableSet(key, valueBefore, valueAfter) {
			if (
				key === "researcher_points" &&
				valueBefore &&
				valueBefore != valueAfter
			) {
				let timestamp = this.formatDate();
				this.buildActivityLog(
					timestamp,
					"Criptoe",
					`Research Points gained: ${(
						valueAfter - valueBefore
					).toLocaleString()}`,
					"green"
				);
			}
		}

		onMessageReceived(data) {
			if (data.startsWith("ACTIVITY_LOG=")) {
				data = data.replaceAll("ACTIVITY_LOG=", "");
				let messageSplit = data.split("~");
				let message = messageSplit[0];
				let color = messageSplit[1];
				const timestamp = this.formatDate();
				let category = this.determineCategory(message);

				this.buildActivityLog(timestamp, category, message, color);
			}
			if (data.startsWith("SCROLL_TOAST")) {
				// SCROLL_TOAST=images/badge_10_percent_fp.png~yellow~FP REFUNDED
				// SCROLL_TOAST=images/badge_10_percent_energy.png~red~ENERGY REFUNDED
				let messageSplit = data.split("~");
				let message = messageSplit[2];
				if (message == "1") {
					message = "Found 1 Super Bait using Rotten Potion";
				}
				if (message == "Completed!") {
					message = "SD Watch Charge Used!";
				}
				let color = "white";
				const timestamp = this.formatDate();
				let category = this.determineCategory(message);

				if (message != "UNSET CHAT TAG" && message != "UNSET SIGIL") {
					this.buildActivityLog(timestamp, category, message, color);
				}
			}
			//TSUNAMI_ANIMATE
			if (data == "TSUNAMI_ANIMATE") {
				let message = "Tsunami Triggered in Beach";
				let color = "white";
				const timestamp = this.formatDate();
				let category = this.determineCategory(message);

				this.buildActivityLog(timestamp, category, message, color);
			}
		}
	}

	const plugin = new UITweaksPlugin();
	IdlePixelPlus.registerPlugin(plugin);
})();