- // ==UserScript==
- // @name Torn.com Enhanced Chat Buttons V2
- // @namespace http://tampermonkey.net/
- // @version 2.0
- // @description Add customizable buttons to Torn.com chat with enhanced UI and features
- // @author Created by Callz [2188704], updated by Weav3r [1853324]
- // @match https://www.torn.com/*
- // @grant GM_setClipboard
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- const buttonCSS = `
- .custom-chat-button {
- background-color: #007BFF;
- color: white;
- padding: 2px 7px;
- text-align: center;
- text-decoration: none;
- display: inline-block;
- font-size: 14px;
- margin: 4px 6px;
- cursor: pointer;
- border-radius: 5px;
- border: none;
- transition: transform 0.1s ease, box-shadow 0.1s ease;
- min-width: 80px;
- overflow: hidden;
- white-space: nowrap;
- }
-
- .custom-chat-button:active {
- transform: scale(0.95);
- box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
- }
-
- .custom-chat-button.recent {
- border: 2px solid #FFD700;
- box-shadow: 0 0 5px rgba(255, 215, 0, 0.8);
- }
-
- .custom-ui-panel {
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background-color: #f5f5f5;
- padding: 10px;
- color: black;
- border-radius: 10px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
- z-index: 9999999999;
- width: 90%;
- max-width: 500px;
- box-sizing: border-box;
- max-height: 90vh;
- overflow: auto;
- }
-
- .custom-ui-panel h3 {
- font-size: 20px;
- margin-bottom: 10px;
- text-align: center;
- }
-
- .custom-ui-panel label {
- font-size: 14px;
- margin-bottom: 5px;
- display: block;
- }
-
- .custom-ui-panel input[type="text"],
- .custom-ui-panel select,
- .custom-ui-panel textarea {
- width: calc(100% - 12px);
- padding: 5px;
- margin-bottom: 10px;
- border: 1px solid #ccc;
- border-radius: 5px;
- font-size: 14px;
- }
-
- .custom-ui-panel input[type="color"] {
- padding: 0;
- margin-top: 5px;
- }
-
- .custom-ui-panel button {
- background-color: #007BFF;
- color: white;
- border: none;
- padding: 8px 12px;
- border-radius: 5px;
- cursor: pointer;
- margin: 5px;
- font-size: 14px;
- transition: background-color 0.3s ease;
- }
-
- .custom-ui-panel button#close-ui {
- background-color: #ccc;
- }
-
- .custom-ui-panel button#close-ui:hover {
- background-color: #999;
- }
-
- .custom-ui-panel textarea {
- height: 60px;
- resize: vertical;
- position: relative;
- }
-
- .custom-ui-panel hr {
- margin: 10px 0;
- border: 0;
- border-top: 1px solid #ccc;
- }
-
- .char-counter {
- position: absolute;
- bottom: 10px;
- right: 10px;
- font-size: 12px;
- color: #999;
- }
-
- #chat-config-button {
- color: green;
- }
-
- #button-configs {
- max-height: 400px;
- overflow-y: auto;
- margin-bottom: 10px;
- }
-
- @media (max-width: 600px) {
- .custom-ui-panel {
- width: 95%;
- max-width: none;
- padding: 8px;
- }
-
- .custom-ui-panel h3 {
- font-size: 18px;
- }
-
- .custom-ui-panel label,
- .custom-ui-panel button {
- font-size: 12px;
- }
-
- .custom-ui-panel input[type="text"],
- .custom-ui-panel select,
- .custom-ui-panel textarea {
- font-size: 12px;
- }
-
- .custom-ui-panel button {
- padding: 6px 10px;
- }
-
- .char-counter {
- font-size: 10px;
- }
- }
-
- .tabs {
- display: flex;
- margin-bottom: 10px;
- }
-
- .tab {
- flex: 1;
- padding: 10px;
- cursor: pointer;
- text-align: center;
- background-color: #e9e9e9;
- border: 1px solid #ccc;
- border-bottom: none;
- border-radius: 10px 10px 0 0;
- }
-
- .tab.settings-tab {
- flex: 0.2;
- padding: 10px;
- cursor: pointer;
- text-align: center;
- background-color: #e9e9e9;
- border: 1px solid #ccc;
- border-bottom: none;
- border-radius: 10px 10px 0 0;
- }
-
- .tab.active {
- background-color: #fff;
- border-bottom: 1px solid #fff;
- }
-
- .tab-content {
- display: none;
- }
-
- .tab-content.active {
- display: block;
- }
-
- .custom-ui-panel.config-list-tab-active {
- max-height: 80vh;
- }
-
- .search-container {
- display: flex;
- margin-bottom: 10px;
- }
-
- .search-container input[type="text"] {
- flex: 3;
- padding: 5px;
- margin-right: 5px;
- }
-
- .search-container select {
- flex: 1;
- padding: 5px;
- }
-
- .highlight {
- background-color: yellow;
- }
- `;
-
- const conditions = {
- TradeChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Trade',
- HospitalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Hospital',
- FactionChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Faction',
- CompanyChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Company',
- GlobalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Global',
- UserChat: chatBox => chatBox.querySelector('.chat-box-header__options___nTsMU'),
- };
-
- function addCSS(cssString) {
- const style = document.createElement('style');
- style.textContent = cssString;
- document.head.append(style);
- }
-
- function saveRecentButtonInfo(buttonText, chatBoxName) {
- localStorage.setItem('recentButtonInfo', JSON.stringify({ buttonText, chatBoxName }));
- }
-
- function clearRecentButtonInfo() {
- localStorage.removeItem('recentButtonInfo');
- }
-
- function getButtonConfigurations() {
- return JSON.parse(localStorage.getItem('chatButtonConfig')) || { buttons: [] };
- }
-
- function saveButtonConfigurations(config) {
- localStorage.setItem('chatButtonConfig', JSON.stringify(config));
- }
-
- function createUIPanel() {
- const panel = document.createElement('div');
- panel.className = 'custom-ui-panel';
- panel.innerHTML = `
- <div class="tabs">
- <div class="tab active" data-tab="config-list-tab">Configured Buttons</div>
- <div class="tab" data-tab="config-edit-tab">Create/Edit Button</div>
- <div class="tab settings-tab" data-tab="config-settings-tab">⚙️</div>
- </div>
- <div id="config-list-tab" class="tab-content active">
- <div class="search-container">
- <input type="text" id="search-input" placeholder="Search...">
- <select id="search-select">
- <option value="buttonText">Text</option>
- <option value="condition">Condition</option>
- <option value="text">Message</option>
- </select>
- </div>
- <div id="button-configs"></div>
- </div>
- <div id="config-edit-tab" class="tab-content">
- <div>
- <label for="button-text">Button Text</label>
- <input type="text" id="button-text" placeholder="Button Text">
-
- <label for="button-color">Background Color</label>
- <input type="color" id="button-color">
-
- <label for="button-condition">Condition</label>
- <select id="button-condition">
- <option value="TradeChat">Trade Chat</option>
- <option value="HospitalChat">Hospital Chat</option>
- <option value="FactionChat">Faction Chat</option>
- <option value="CompanyChat">Company Chat</option>
- <option value="GlobalChat">Global Chat</option>
- <option value="UserChat">User Chat</option>
- </select>
-
- <label for="button-text-content">Message</label>
- <textarea id="button-text-content" placeholder="Message content"></textarea>
- <div class="char-counter" id="char-counter">0</div>
-
- <button id="add-button">Add Button</button>
- <button id="edit-button" style="display: none;">Save Button</button>
- </div>
- </div>
- <div id="config-settings-tab" class="tab-content">
- <button id="import-button">Import Config</button>
- <button id="export-button">Export Config</button>
- </div>
- <button id="close-ui">Close</button>
- `;
- document.body.appendChild(panel);
-
- document.querySelectorAll('.tab').forEach(tab => {
- tab.addEventListener('click', () => {
- switchTab(tab.dataset.tab);
- });
- });
-
- document.getElementById('add-button').addEventListener('click', addNewButtonConfig);
- document.getElementById('edit-button').addEventListener('click', editButtonConfig);
- document.getElementById('close-ui').addEventListener('click', closeUI);
- document.getElementById('import-button').addEventListener('click', importConfig);
- document.getElementById('export-button').addEventListener('click', exportConfig);
- document.getElementById('button-text-content').addEventListener('input', updateCharCounter);
- document.getElementById('search-input').addEventListener('input', filterButtonConfigs);
- populateButtonConfigs();
- }
-
- function switchTab(tabId) {
- document.querySelectorAll('.tab, .tab-content').forEach(el => {
- el.classList.remove('active');
- });
- document.querySelector(`[data-tab="${tabId}"]`).classList.add('active');
- document.getElementById(tabId).classList.add('active');
-
- const panel = document.querySelector('.custom-ui-panel');
- if (tabId === 'config-list-tab') {
- panel.classList.add('config-list-tab-active');
- } else {
- panel.classList.remove('config-list-tab-active');
- }
- }
-
- function populateButtonConfigs() {
- const configsContainer = document.getElementById('button-configs');
- configsContainer.innerHTML = '';
- const configs = getButtonConfigurations();
-
- configs.buttons.forEach((buttonConfig, index) => {
- const configDiv = document.createElement('div');
- configDiv.className = 'draggable';
- configDiv.dataset.index = index;
- configDiv.innerHTML = `
- <div><strong>Text:</strong> ${buttonConfig.buttonText}</div>
- <div><strong>Color:</strong> ${buttonConfig.backgroundColor}</div>
- <div><strong>Condition:</strong> ${buttonConfig.condition}</div>
- <div><strong>Message:</strong> ${buttonConfig.text}</div>
- `;
-
- const editButton = document.createElement('button');
- editButton.textContent = 'Edit';
- editButton.addEventListener('click', () => {
- selectForEdit(index);
- switchTab('config-edit-tab');
- });
- configDiv.appendChild(editButton);
-
- const deleteButton = document.createElement('button');
- deleteButton.textContent = 'Delete';
- deleteButton.addEventListener('click', () => deleteButtonConfig(index));
- configDiv.appendChild(deleteButton);
-
- const moveUpButton = document.createElement('button');
- moveUpButton.textContent = 'Up';
- moveUpButton.addEventListener('click', () => moveButtonConfig(index, -1));
- configDiv.appendChild(moveUpButton);
-
- const moveDownButton = document.createElement('button');
- moveDownButton.textContent = 'Down';
- moveDownButton.addEventListener('click', () => moveButtonConfig(index, 1));
- configDiv.appendChild(moveDownButton);
-
- configsContainer.appendChild(configDiv);
- });
- }
-
- function filterButtonConfigs() {
- const searchInput = document.getElementById('search-input').value.toLowerCase();
- const searchBy = document.getElementById('search-select').value;
- const configs = getButtonConfigurations();
-
- const filteredConfigs = configs.buttons.filter(buttonConfig => {
- const fieldValue = buttonConfig[searchBy].toLowerCase();
- return fieldValue.includes(searchInput);
- });
-
- const configsContainer = document.getElementById('button-configs');
- configsContainer.innerHTML = '';
-
- filteredConfigs.forEach((buttonConfig, index) => {
- const configDiv = document.createElement('div');
- configDiv.className = 'draggable';
- configDiv.dataset.index = index;
- configDiv.innerHTML = `
- <div><strong>Text:</strong> ${buttonConfig.buttonText}</div>
- <div><strong>Color:</strong> ${buttonConfig.backgroundColor}</div>
- <div><strong>Condition:</strong> ${buttonConfig.condition}</div>
- <div><strong>Message:</strong> ${buttonConfig.text}</div>
- `;
-
- const editButton = document.createElement('button');
- editButton.textContent = 'Edit';
- editButton.addEventListener('click', () => {
- selectForEdit(index);
- switchTab('config-edit-tab');
- });
- configDiv.appendChild(editButton);
-
- const deleteButton = document.createElement('button');
- deleteButton.textContent = 'Delete';
- deleteButton.addEventListener('click', () => deleteButtonConfig(index));
- configDiv.appendChild(deleteButton);
-
- const moveUpButton = document.createElement('button');
- moveUpButton.textContent = 'Up';
- moveUpButton.addEventListener('click', () => moveButtonConfig(index, -1));
- configDiv.appendChild(moveUpButton);
-
- const moveDownButton = document.createElement('button');
- moveDownButton.textContent = 'Down';
- moveDownButton.addEventListener('click', () => moveButtonConfig(index, 1));
- configDiv.appendChild(moveDownButton);
-
- configsContainer.appendChild(configDiv);
- });
- }
-
- function selectForEdit(index) {
- const config = getButtonConfigurations().buttons[index];
- document.getElementById('button-text').value = config.buttonText;
- document.getElementById('button-color').value = config.backgroundColor;
- document.getElementById('button-condition').value = config.condition;
- document.getElementById('button-text-content').value = config.text;
-
- document.getElementById('add-button').style.display = 'block';
- document.getElementById('edit-button').style.display = 'block';
- document.getElementById('edit-button').setAttribute('data-edit-index', index);
- }
-
- function deleteButtonConfig(index) {
- const config = getButtonConfigurations();
- config.buttons.splice(index, 1);
- saveButtonConfigurations(config);
- populateButtonConfigs();
- }
-
- function moveButtonConfig(index, direction) {
- const config = getButtonConfigurations();
- const newIndex = index + direction;
-
- if (newIndex >= 0 && newIndex < config.buttons.length) {
- const buttonConfig = config.buttons.splice(index, 1)[0];
- config.buttons.splice(newIndex, 0, buttonConfig);
- saveButtonConfigurations(config);
- populateButtonConfigs();
- }
- }
-
- function addNewButtonConfig() {
- const buttonText = document.getElementById('button-text').value;
- const backgroundColor = document.getElementById('button-color').value;
- const condition = document.getElementById('button-condition').value;
- const text = document.getElementById('button-text-content').value;
-
- const config = getButtonConfigurations();
- config.buttons.push({ buttonText, backgroundColor, condition, text });
- saveButtonConfigurations(config);
- populateButtonConfigs();
- highlightButton(config.buttons.length - 1);
- switchTab('config-list-tab');
-
- clearInputFields();
- }
-
- function editButtonConfig() {
- const index = parseInt(document.getElementById('edit-button').getAttribute('data-edit-index'), 10);
- const buttonText = document.getElementById('button-text').value;
- const backgroundColor = document.getElementById('button-color').value;
- const condition = document.getElementById('button-condition').value;
- const text = document.getElementById('button-text-content').value;
-
- const config = getButtonConfigurations();
- config.buttons[index] = { buttonText, backgroundColor, condition, text };
- saveButtonConfigurations(config);
- populateButtonConfigs();
- highlightButton(index);
- switchTab('config-list-tab');
-
- document.getElementById('add-button').style.display = 'block';
- document.getElementById('edit-button').style.display = 'none';
-
- clearInputFields();
- }
-
- function clearInputFields() {
- document.getElementById('button-text').value = '';
- document.getElementById('button-text-content').value = '';
- }
-
- function closeUI() {
- document.querySelector('.custom-ui-panel').remove();
- }
-
- function createConfigButton() {
- const settingsPanel = document.querySelector('.settings-panel___IZSDs');
- if (settingsPanel && !document.querySelector('#chat-config-button')) {
- const configButton = document.createElement('button');
- configButton.id = 'chat-config-button';
- configButton.textContent = 'Edit Chat Buttons';
- configButton.addEventListener('click', createUIPanel);
- settingsPanel.appendChild(configButton);
- }
- }
-
- function applyButtonConfigurations() {
- const configs = getButtonConfigurations();
- document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => {
- configs.buttons.forEach(buttonConfig => {
- const conditionFunc = conditions[buttonConfig.condition];
- if (conditionFunc && conditionFunc(chatBox) && !chatBox.querySelector(`[data-button-text="${buttonConfig.buttonText}"]`)) {
- const button = document.createElement('button');
- button.className = 'custom-chat-button';
- button.innerText = buttonConfig.buttonText;
- button.style.backgroundColor = buttonConfig.backgroundColor;
- button.setAttribute('data-button-text', buttonConfig.buttonText);
- button.addEventListener('click', (event) => addCustomText(chatBox, buttonConfig.text, event));
- button.addEventListener('mousedown', (event) => {
- if (event.button === 0) { // Left mouse button
- let timer;
- const delay = 1000; // 1 second
-
- timer = setTimeout(() => {
- button.classList.remove('recent');
- clearRecentButtonInfo();
- }, delay);
-
- button.addEventListener('mouseup', () => {
- clearTimeout(timer);
- }, { once: true });
-
- button.addEventListener('mouseleave', () => {
- clearTimeout(timer);
- }, { once: true });
- }
- });
-
- const filterContainer = chatBox.querySelector('.tt-chat-filter');
- filterContainer.insertBefore(button, filterContainer.firstChild);
- }
- });
- });
- }
-
- function addCustomText(chatBox, messageTemplate, event) {
- const nameElement = chatBox.querySelector('.typography___Dc5WV');
- const name = nameElement ? nameElement.textContent.trim() : 'Trader';
- const message = messageTemplate.replace('{name}', name);
-
- navigator.clipboard.writeText(message).then(() => {
- const textArea = chatBox.querySelector('textarea');
- textArea.focus();
- textArea.value = ''; // Clear the existing text
- const startPos = textArea.selectionStart;
- const endPos = textArea.selectionEnd;
- textArea.setRangeText(message, startPos, endPos, 'end');
- textArea.dispatchEvent(new Event('input', { bubbles: true }));
- textArea.focus();
- textArea.selectionStart = textArea.selectionEnd = startPos + message.length;
-
- // Remove the 'recent' class from all buttons
- chatBox.querySelectorAll('.custom-chat-button').forEach(btn => {
- btn.classList.remove('recent');
- });
-
- // Add the 'recent' class to the clicked button
- event.target.classList.add('recent');
-
- // Save the recently clicked button information
- const chatBoxName = chatBox.querySelector('.chat-box-header__name___jIjjM').textContent;
- saveRecentButtonInfo(event.target.getAttribute('data-button-text'), chatBoxName);
- });
- }
-
- function applyRecentButtonClass() {
- const recentButtonInfo = JSON.parse(localStorage.getItem('recentButtonInfo'));
- if (recentButtonInfo) {
- document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => {
- const chatBoxName = chatBox.querySelector('.chat-box-header__name___jIjjM').textContent;
- if (chatBoxName === recentButtonInfo.chatBoxName) {
- const button = chatBox.querySelector(`[data-button-text="${recentButtonInfo.buttonText}"]`);
- if (button) {
- button.classList.add('recent');
- }
- }
- });
- }
- }
-
- function importConfig() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
- input.onchange = (event) => {
- const file = event.target.files[0];
- const reader = new FileReader();
- reader.onload = (e) => {
- const config = JSON.parse(e.target.result);
- saveButtonConfigurations(config);
- populateButtonConfigs();
- applyButtonConfigurations();
- };
- reader.readAsText(file);
- };
- input.click();
- }
-
- function exportConfig() {
- const config = getButtonConfigurations();
- const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'chatButtonConfig.json';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- }
-
- function updateCharCounter() {
- const textArea = document.getElementById('button-text-content');
- const counter = document.getElementById('char-counter');
- counter.textContent = textArea.value.length;
- }
-
- function highlightButton(index) {
- const configsContainer = document.getElementById('button-configs');
- const buttonDiv = configsContainer.querySelector(`.draggable[data-index="${index}"]`);
- if (buttonDiv) {
- buttonDiv.classList.add('highlight');
- buttonDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
- setTimeout(() => {
- buttonDiv.classList.remove('highlight');
- }, 2000);
- }
- }
-
- addCSS(buttonCSS);
-
- const chatContainerObserver = new MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- createConfigButton();
- applyButtonConfigurations();
- applyRecentButtonClass();
- });
- });
-
- const chatContainer = document.querySelector('#chatRoot');
- if (chatContainer) {
- chatContainerObserver.observe(chatContainer, { childList: true, subtree: true });
- }
-
- applyButtonConfigurations();
- applyRecentButtonClass();
- })();