this script will allow you to copy the content from the room and save notes at the bottom right of the page
// ==UserScript==
// @license JazzMedo
// @name TryHackMe Copy to Clipboard and add notes
// @version 1.0.2
// @description this script will allow you to copy the content from the room and save notes at the bottom right of the page
// @author JazzMedo
// @match https://tryhackme.com/r/*
// @include https://tryhackme.com/r/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=tryhackme.com
// @grant none
// @namespace https://greasyfork.org/users/1420266
// ==/UserScript==
(function () {
'use strict';
setTimeout(() => {
(function () {
// Select all elements with data-sentry-component="AccordionDetails"
const accordionDetailsList = document.querySelectorAll('[data-sentry-component="AccordionDetails"]');
// Function to get text excluding images and handling links
const getTextExcludingImages = (element) => {
let textContent = '';
// Loop through the child nodes of the element
Array.from(element.childNodes).forEach((child) => {
// Skip if the child or its parent is inside a <pre> tag
const isInsidePre = (node) => {
while (node && node !== element) {
if (node.tagName === 'PRE' || node.tagName === 'pre') {
return true;
}
node = node.parentNode;
}
return false;
};
if (isInsidePre(child.nodeType === Node.TEXT_NODE ? child.parentNode : child)) {
return;
}
// Only consider text nodes or elements that aren't images
if (child.nodeType === Node.TEXT_NODE) {
let text = child.textContent.trim();
if (text) {
// Check if the parent element is a <p> tag
const parentElement = child.parentElement;
const isParagraph = parentElement && parentElement.tagName === 'P';
// Add newline if the text ends with a period in a <p> tag
if ((isParagraph && text.trim(" ").endsWith('.')) || text.endsWith('?') || text.endsWith(':')) {
textContent += text + '\n\n';
} else {
textContent += text;
}
}
}
else if (child.nodeType === Node.ELEMENT_NODE && child.tagName !== 'IMG') {
// Skip <pre> tags and their contents
if (child.tagName === 'PRE' || child.tagName === 'pre') {
return;
}
// If it's an element and not an image, recurse into it
if (child.tagName === 'A' || child.tagName === 'a') {
// If it's a link, add the text and a newline after it
textContent += ' ' + child.textContent.trim() + ' ';
}
else if (child.tagName === 'B' || child.tagName === 'b') {
// If the element is a <b> tag, add the text and a newline after the paragraph
textContent += child.textContent.trim() + '\n\n';
} else if (child.tagName === 'span' || child.tagName === 'SPAN') {
// If the element is a <b> tag, add the text and a newline after the paragraph
textContent += " " + child.textContent.trim() + ' ';
}
else if (child.tagName === 'UL' || child.tagName === 'ul') {
// Add newline after a <ul> tag
textContent += getTextExcludingImages(child) + '\n';
} else if (child.tagName === 'LI' || child.tagName === 'li') {
// Add newline after a <li> tag
textContent += child.textContent.trim() + '\n';
} else if (child.tagName === 'P' || child.tagName === 'p') {
// If <p> contains <b>, add \n\n before <p>
if (child.querySelector('b') || child.querySelector('B')) {
textContent += '\n\n' + getTextExcludingImages(child);
} else {
textContent += getTextExcludingImages(child) + '\n\n';
}
} else if (child.tagName === 'TABLE' || child.tagName === 'table') {
// Format the table content
textContent += formatTable(child) + '\n\n'; // Call a new function to format the table
} else if (child.tagName === 'DIV' || child.tagName === 'div') {
// Add double newlines before and after the <div> content
textContent += '\n\n' + getTextExcludingImages(child) + '\n\n';
} else if (child.tagName === 'H2' || child.tagName === 'h2') {
// Add newline after <h2> tag
textContent += child.textContent.trim() + '\n\n';
} else if (child.tagName === 'code' || child.tagName === 'CODE') {
// Add newline after <h2> tag
textContent += ' `' + child.textContent.trim() + '` ';
} else {
// Otherwise, continue recursively
textContent += getTextExcludingImages(child);
}
}
});
// Filter excessive newlines (replace \n\n\n with \n\n)
textContent = textContent.replace(/\n{3,}/g, '\n\n');
return textContent;
};
// New function to format the table
function formatTable(table) {
let formattedTable = '----------------------------\n';
const rows = table.querySelectorAll('tr');
rows.forEach((row) => {
const cells = row.querySelectorAll('th, td');
const rowContent = Array.from(cells).map(cell => cell.textContent.trim()).join(' | '); // Join cell content with a separator
formattedTable += '| ' + rowContent + ' |\n'; // Add a newline after each row
});
// Add a newline after each row for separation
formattedTable += '----------------------------\n'; // Ensure there's a newline after the last row
return formattedTable;
}
// Loop through all the matched elements
accordionDetailsList.forEach((accordionDetails) => {
// Create a button for each AccordionDetails element
const button = document.createElement('button');
// Replace text with SVG icon
button.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>`;
button.title = 'Copy Text Only';
button.style.position = 'absolute';
button.style.bottom = '10px';
button.style.right = '10px'; // Position the button in the top-right corner
// button.style.zIndex = '1000';
button.style.padding = '8px 8px 4px 8px'; // Make the button more square-shaped for the icon
button.style.borderRadius = '5px';
button.style.backgroundColor = '#47acee'; // Green background
button.style.color = 'white';
button.style.border = 'none';
button.style.cursor = 'pointer';
button.style.transition = 'all ease-in-out 0.3s';
// Position the region element relative to show the button correctly
accordionDetails.style.position = 'relative';
// Add the button to the accordion details element
accordionDetails.appendChild(button);
// Flag to track whether the button was already clicked
let isClicked = false;
// Set up button click event to copy the text
button.addEventListener('click', () => {
if (isClicked) return; // Prevent further clicks if already clicked
isClicked = true; // Mark as clicked
let textContent = getTextExcludingImages(accordionDetails);
// Use the Clipboard API to copy text
if (textContent) {
navigator.clipboard.writeText(textContent).then(() => {
// Change button icon to checkmark when copied
button.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>`;
button.style.backgroundColor = '#4CAF50';
setTimeout(() => {
// Reset to copy icon
button.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>`;
button.style.backgroundColor = '#47acee';
isClicked = false;
}, 1000);
}).catch((err) => {
console.error('Failed to copy text: ', err);
isClicked = false; // Reset flag in case of error
});
}
});
});
// Inject CSS into the document
const style = document.createElement('style');
style.innerHTML = `
pre {
position: relative; /* Ensure the button is positioned relative to the pre block */
}
.copy-button {
position: absolute;
bottom: 10px;
right: 10px;
background-color: #47acee; /* Dark yellow background */
color: white;
border: none;
padding: 8px 8px 4px;
cursor: pointer;
border-radius: 5px;
transition: all ease-in-out 0.2s; /* Animation for button press */
}
.copy-button:hover {
background-color: #8cd1ff; /* Darker green on hover */
}
.copy-button:active {
transform: scale(0.95); /* Slightly shrink the button on click */
}
`;
document.head.appendChild(style);
document.querySelectorAll('pre code').forEach(block => {
const copyButton = document.createElement('button');
copyButton.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>`;
copyButton.className = 'copy-button'; // Add class for styling
copyButton.title = 'Copy Commands Only';
block.parentElement.style.position = 'relative'; // Ensure the parent is positioned
block.parentElement.appendChild(copyButton);
copyButton.addEventListener('click', () => {
const lines = block.innerText.split('\n');
const commands = lines.map(line => {
if (line.includes('$ ')) {
const parts = line.split('$ ');
return parts.length > 1 ? parts[1] : '';
} else if (line.includes('>')) {
const parts = line.split('>');
return parts.length > 1 ? parts[1].trim() : '';
}
return '';
}).filter(command => command !== '').join('\n');
navigator.clipboard.writeText(commands).then(() => {
const originalText = copyButton.innerHTML;
copyButton.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>`;
copyButton.style.backgroundColor = '#4CAF50';
setTimeout(() => {
copyButton.innerHTML = originalText;
copyButton.style.backgroundColor = '#47acee';
}, 2000);
});
});
});
// تعديل مكان الزر العائم ليكون في الزاوية السفلى اليسرى// تعديل مكان الزر العائم ليكون في الزاوية السفلى اليسرى
function createCommandPopupUI() {
if (document.querySelector('#command-popup-ui')) return;
const floatingButton = document.createElement('button');
floatingButton.id = 'floating-button';
floatingButton.innerHTML = `+`;
floatingButton.style.position = 'fixed';
floatingButton.style.fontSize = '2rem';
floatingButton.style.bottom = '20px';
floatingButton.style.left = '20px'; // استخدام left بدلاً من right
floatingButton.style.width = '33px';
floatingButton.style.height = '33px';
floatingButton.style.borderRadius = '5px';
floatingButton.style.border = 'none';
floatingButton.style.backgroundColor = '#61dafb';
floatingButton.style.color = 'white';
floatingButton.style.fontSize = '24px';
floatingButton.style.cursor = 'pointer';
floatingButton.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
floatingButton.style.zIndex = '9999';
floatingButton.style.transition = 'all ease-in-out 0.3s';
const popup = document.createElement('div');
popup.id = 'command-popup-ui';
popup.style.position = 'fixed';
popup.style.bottom = '80px';
popup.style.left = '20px'; // تغيير الموضع إلى يسار الصفحة
popup.style.width = '400px';
popup.style.backgroundColor = '#282c34';
popup.style.color = 'white';
popup.style.padding = '15px';
popup.style.borderRadius = '8px';
popup.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
popup.style.display = 'none';
popup.style.zIndex = '9999';
const input = document.createElement('input');
input.type = 'text';
input.placeholder = 'Write you command here ...';
input.style.width = 'calc(100% - 50px)';
input.style.padding = '10px';
input.style.borderRadius = '4px';
input.style.border = 'none';
input.style.marginRight = '10px';
// Add keypress event listener for Enter key
input.addEventListener('keypress', (event) => {
if (event.key === 'Enter') {
const command = input.value.trim();
if (command) {
const originalContent = runButton.innerHTML;
runButton.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>`;
runButton.style.transform = 'scale(0.95)';
output.appendChild(createCommandElement(command));
saveCommand(command);
input.value = '';
setTimeout(() => {
runButton.innerHTML = originalContent;
runButton.style.transform = 'scale(1)';
}, 1000);
}
}
});
const inputContainer = document.createElement('div');
inputContainer.style.display = 'flex';
inputContainer.style.alignItems = 'center';
inputContainer.style.marginBottom = '10px';
const runButton = document.createElement('button');
runButton.innerText = "➕";
runButton.style.backgroundColor = '#61dafb';
runButton.style.color = '#282c34';
runButton.style.border = 'none';
runButton.style.padding = '10px';
runButton.style.borderRadius = '4px';
runButton.style.cursor = 'pointer';
runButton.style.width = '40px';
runButton.style.height = '38px';
const output = document.createElement('div');
output.style.marginTop = '10px';
output.style.fontSize = '14px';
output.style.overflowY = 'auto';
output.style.maxHeight = '200px';
output.style.transition = 'all 0.3s ease';
const savedCommands = JSON.parse(localStorage.getItem('commands')) || [];
function createCommandElement(command) {
const commandContainer = document.createElement('div');
commandContainer.style.display = 'flex';
commandContainer.style.alignItems = 'center';
commandContainer.style.marginBottom = '8px';
commandContainer.style.animation = 'slideIn 0.3s ease';
const commandText = document.createElement('div');
commandText.innerHTML = `$ ${command}`;
commandText.style.color = '#61dafb';
commandText.style.flex = '1';
const buttonsContainer = document.createElement('div');
buttonsContainer.style.display = 'flex';
buttonsContainer.style.gap = '5px';
const cmdCopyBtn = document.createElement('button');
cmdCopyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>`;
cmdCopyBtn.style.backgroundColor = '#98c379';
cmdCopyBtn.style.color = 'white';
cmdCopyBtn.style.border = 'none';
cmdCopyBtn.style.padding = '8px 8px 4px';
cmdCopyBtn.style.borderRadius = '4px';
cmdCopyBtn.style.cursor = 'pointer';
cmdCopyBtn.style.fontSize = '12px';
cmdCopyBtn.style.marginLeft = '10px';
cmdCopyBtn.style.transition = 'transform 0.2s ease';
const deleteBtn = document.createElement('button');
deleteBtn.innerText = '🗑';
deleteBtn.style.backgroundColor = '#e06c75';
deleteBtn.style.color = 'white';
deleteBtn.style.border = 'none';
deleteBtn.style.padding = '5px 10px';
deleteBtn.style.borderRadius = '4px';
deleteBtn.style.cursor = 'pointer';
deleteBtn.style.fontSize = '12px';
deleteBtn.style.transition = 'transform 0.2s ease';
cmdCopyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(command);
const originalSvg = cmdCopyBtn.innerHTML;
cmdCopyBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>`;
cmdCopyBtn.style.transform = 'scale(0.95)';
setTimeout(() => {
cmdCopyBtn.style.transform = 'scale(1)';
cmdCopyBtn.innerHTML = originalSvg;
}, 1000);
});
deleteBtn.addEventListener('click', () => {
const originalContent = commandContainer.innerHTML;
commandContainer.style.backgroundColor = '#ff000015';
commandContainer.innerHTML = `
<div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
<span style="color: #e06c75;">Are you sure you want to delete this command?</span>
<div style="display: flex; gap: 8px;">
<button id="confirm-delete" style="background: #e06c75; border: none; color: white; padding: 5px 10px; border-radius: 4px; cursor: pointer;">Yes</button>
<button id="cancel-delete" style="background: #98c379; border: none; color: white; padding: 5px 10px; border-radius: 4px; cursor: pointer;">No</button>
</div>
</div>
`;
const confirmBtn = commandContainer.querySelector('#confirm-delete');
const cancelBtn = commandContainer.querySelector('#cancel-delete');
confirmBtn.addEventListener('click', () => {
commandContainer.style.animation = 'slideOut 0.3s ease';
setTimeout(() => {
commandContainer.remove();
const index = savedCommands.indexOf(command);
if (index > -1) {
savedCommands.splice(index, 1);
localStorage.setItem('commands', JSON.stringify(savedCommands));
}
}, 280);
});
cancelBtn.addEventListener('click', () => {
commandContainer.style.backgroundColor = 'transparent';
commandContainer.innerHTML = originalContent;
});
});
buttonsContainer.appendChild(cmdCopyBtn);
buttonsContainer.appendChild(deleteBtn);
commandContainer.appendChild(commandText);
commandContainer.appendChild(buttonsContainer);
return commandContainer;
}
savedCommands.forEach(command => {
output.appendChild(createCommandElement(command));
});
function saveCommand(command) {
savedCommands.push(command);
localStorage.setItem('commands', JSON.stringify(savedCommands));
}
runButton.addEventListener('click', () => {
const command = input.value.trim();
if (command) {
const originalContent = runButton.innerHTML;
runButton.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>`;
runButton.style.transform = 'scale(0.95)';
output.appendChild(createCommandElement(command));
saveCommand(command);
input.value = '';
setTimeout(() => {
runButton.innerHTML = originalContent;
runButton.style.transform = 'scale(1)';
}, 1000);
}
});
floatingButton.addEventListener('click', () => {
if (popup.style.display === 'none') {
popup.style.display = 'block';
popup.style.opacity = '0';
popup.style.transform = 'scale(0.95) translateY(20px)';
floatingButton.innerHTML = `-`;
floatingButton.style.backgroundColor = '#ffcdd2';
setTimeout(() => {
popup.style.opacity = '1';
popup.style.transform = 'scale(1) translateY(0)';
}, 0);
} else {
popup.style.opacity = '0';
popup.style.transform = 'scale(0.95) translateY(20px)';
floatingButton.innerHTML = `+`;
floatingButton.style.backgroundColor = '#61dafb';
setTimeout(() => {
popup.style.display = 'none';
}, 300);
}
});
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideOut {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(-100%); }
}
@keyframes popIn {
0% { transform: scale(0) rotate(-180deg); opacity: 0; }
100% { transform: scale(1) rotate(0deg); opacity: 1; }
}
#floating-button {
transition: transform 0.3s ease;
animation: popIn 0.5s ease;
}
#floating-button:hover {
transform: scale(1.1);
}
#command-popup-ui {
transition: opacity 0.3s ease, transform 0.3s ease;
transform-origin: bottom left;
}
`;
document.head.appendChild(style);
inputContainer.appendChild(input);
inputContainer.appendChild(runButton);
popup.appendChild(inputContainer);
popup.appendChild(output);
document.body.appendChild(floatingButton);
document.body.appendChild(popup);
}
createCommandPopupUI();
})();
}, 2000); // Delay the execution by 2 seconds
// Your code here...
})();