Greasy Fork is available in English.

Torn.com Enhanced Chat Buttons V2

Add customizable buttons to Torn.com chat with enhanced UI and features

// ==UserScript==
// @name         Torn.com Enhanced Chat Buttons V2
// @namespace    http://tampermonkey.net/
// @version      2.40
// @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 CACHE_TTL = 24 * 60 * 60 * 1000;

    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;
            border: none;
        }

        .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;
        }

        .tt-chat-filter {
            display: flex;
            padding: 4px;
            align-items: center;
            background-color: var(--chat-box-bg);
            color: var(--chat-box-label-info);
            border-bottom: 1px solid var(--chat-box-input-border);
            margin-bottom: 8px;
        }

        .tt-chat-filter input {
            margin-left: 4px;
            margin-right: 4px;
            border-radius: 5px;
            width: -webkit-fill-available;
            width: -moz-available;
            border: 1px solid var(--chat-box-input-border);
            background-color: var(--chat-box-bg);
            color: var(--chat-box-label-info);
        }

        .notification {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            padding: 10px;
            border-radius: 5px;
            z-index: 9999999999;
            opacity: 0;
            transition: opacity 0.5s ease;
        }

        .notification.show {
            opacity: 1;
        }

        .button-config-card {
            border: 1px solid #ccc;
            background-color: #fff;
            padding: 10px;
            margin: 10px 0;
            border-radius: 5px;
        }

        .button-config-message {
            white-space: pre-wrap;
            background: #f9f9f9;
            padding: 5px;
            border-radius: 5px;
            border: 1px solid #ddd;
            margin: 5px 0;
        }
    `;

    const conditions = {
        TradeChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Trade',
        HospitalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Hospital',
        FactionChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Faction',
        CompanyChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Company',
        GlobalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Global',
        UserChat: chatBox => chatBox.querySelector('.chat-box-header__options___nTsMU')
    };

    const companyTypes = {
        1: "Hair Salon",
        2: "Law Firm",
        3: "Flower Shop",
        4: "Car Dealership",
        5: "Clothing Store",
        6: "Gun Shop",
        7: "Game Shop",
        8: "Candle Shop",
        9: "Toy Shop",
        10: "Adult Novelties",
        11: "Cyber Cafe",
        12: "Grocery Store",
        13: "Theater",
        14: "Sweet Shop",
        15: "Cruise Line",
        16: "Television Network",
        18: "Zoo",
        19: "Firework Stand",
        20: "Property Broker",
        21: "Furniture Store",
        22: "Gas Station",
        23: "Music Store",
        24: "Nightclub",
        25: "Pub",
        26: "Gents Strip Club",
        27: "Restaurant",
        28: "Oil Rig",
        29: "Fitness Center",
        30: "Mechanic Shop",
        31: "Amusement Park",
        32: "Lingerie Store",
        33: "Meat Warehouse",
        34: "Farm",
        35: "Software Corporation",
        36: "Ladies Strip Club",
        37: "Private Security Firm",
        38: "Mining Corporation",
        39: "Detective Agency",
        40: "Logistics Management",
    };

    function addCSS(cssString) {
        const style = document.createElement('style');
        style.textContent = cssString;
        document.head.append(style);
    }

    function showNotification(message) {
        const notification = document.createElement('div');
        notification.className = 'notification';
        notification.innerText = message;
        document.body.appendChild(notification);
        requestAnimationFrame(() => {
            notification.classList.add('show');
        });
        setTimeout(() => {
            notification.classList.remove('show');
            setTimeout(() => {
                notification.remove();
            }, 500);
        }, 2000);
    }

    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 getAPIKey() {
        return localStorage.getItem('apiKey') || '';
    }

    function saveAPIKey(key) {
        localStorage.setItem('apiKey', key);
        showNotification('API key saved.');
    }

    function saveCache(key, data) {
        const cacheData = {
            timestamp: Date.now(),
            data
        };
        localStorage.setItem(key, JSON.stringify(cacheData));
    }

    function loadCache(key) {
        const cacheData = JSON.parse(localStorage.getItem(key));
        if (cacheData && (Date.now() - cacheData.timestamp < CACHE_TTL)) {
            return cacheData.data;
        }
        return null;
    }

    function clearCache() {
        localStorage.removeItem('companyCache');
        localStorage.removeItem('factionCache');
        showNotification('API cache cleared.');
    }

    function createUIPanel() {
        if (document.querySelector('.custom-ui-panel')) {
            return;
        }

        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="Enter your message here. Use {name} for chatter's name, {company} for company info, {faction} for faction info."></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">
                <label for="api-key">API Key</label>
                <input type="text" id="api-key" placeholder="Enter your API key" value="${getAPIKey()}">
                <button id="save-api-key-button">Save API Key</button>
                <button id="import-button">Import Config (File)</button>
                <button id="export-button">Export Config (File)</button>
                <button id="clear-cache-button">Clear API Cache</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('clear-cache-button').addEventListener('click', clearCache);
        document.getElementById('button-text-content').addEventListener('input', updateCharCounter);
        document.getElementById('search-input').addEventListener('input', filterButtonConfigs);
        document.getElementById('save-api-key-button').addEventListener('click', () => {
            const key = document.getElementById('api-key').value;
            saveAPIKey(key);
        });

        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 = 'button-config-card draggable';
            configDiv.dataset.index = index;

            const textDiv = document.createElement('div');
            textDiv.innerHTML = `<strong>Text:</strong> ${buttonConfig.buttonText}`;
            configDiv.appendChild(textDiv);

            const colorDiv = document.createElement('div');
            colorDiv.innerHTML = `<strong>Color:</strong> ${buttonConfig.backgroundColor}`;
            configDiv.appendChild(colorDiv);

            const conditionDiv = document.createElement('div');
            conditionDiv.innerHTML = `<strong>Condition:</strong> ${buttonConfig.condition}`;
            configDiv.appendChild(conditionDiv);

            const messageDiv = document.createElement('div');
            messageDiv.className = 'button-config-message';
            messageDiv.innerText = buttonConfig.text;
            configDiv.appendChild(messageDiv);

            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 = 'button-config-card draggable';
            configDiv.dataset.index = index;

            const textDiv = document.createElement('div');
            textDiv.innerHTML = `<strong>Text:</strong> ${buttonConfig.buttonText}`;
            configDiv.appendChild(textDiv);

            const colorDiv = document.createElement('div');
            colorDiv.innerHTML = `<strong>Color:</strong> ${buttonConfig.backgroundColor}`;
            configDiv.appendChild(colorDiv);

            const conditionDiv = document.createElement('div');
            conditionDiv.innerHTML = `<strong>Condition:</strong> ${buttonConfig.condition}`;
            configDiv.appendChild(conditionDiv);

            const messageDiv = document.createElement('div');
            messageDiv.className = 'button-config-message';
            messageDiv.innerText = buttonConfig.text;
            configDiv.appendChild(messageDiv);

            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);
        updateCharCounter();
    }

    function deleteButtonConfig(index) {
        const config = getButtonConfigurations();
        config.buttons.splice(index, 1);
        saveButtonConfigurations(config);
        populateButtonConfigs();
        showNotification('Button deleted.');
    }

    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();
            showNotification('Button moved.');
        }
    }

    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();
        showNotification('Button added.');
    }

    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();
        showNotification('Button edited.');
    }

    function clearInputFields() {
        document.getElementById('button-text').value = '';
        document.getElementById('button-text-content').value = '';
        document.getElementById('button-color').value = '';
        updateCharCounter();
    }

    function closeUI() {
        const panel = document.querySelector('.custom-ui-panel');
        if (panel) {
            panel.remove();
        }
    }

function createConfigButton() {
    const noteButton = document.querySelector('.chat-note-button___oaIVx');
    if (!noteButton || document.querySelector('#chat-config-button')) return;

    const button = document.createElement('button');
    button.id = 'chat-config-button';
    button.type = 'button';
    button.title = 'Edit Chat Buttons';
    button.className = 'chat-list-button___d1Olw';

    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svg.setAttribute('viewBox', '0 0 512 512');
    svg.setAttribute('height', '14');
    svg.setAttribute('width', '14');
    svg.innerHTML = `
        <path d="M312 201.8c0-17.4 9.2-33.2 19.9-47C344.5 138.5 352 118.1 352 96c0-53-43-96-96-96s-96 43-96 96c0 22.1 7.5 42.5 20.1 58.8c10.7 13.8 19.9 29.6 19.9 47c0 29.9-24.3 54.2-54.2 54.2L112 256C50.1 256 0 306.1 0 368c0 20.9 13.4 38.7 32 45.3L32 464c0 26.5 21.5 48 48 48l352 0c26.5 0 48-21.5 48-48l0-50.7c18.6-6.6 32-24.4 32-45.3c0-61.9-50.1-112-112-112l-33.8 0c-29.9 0-54.2-24.3-54.2-54.2zM416 416l0 32L96 448l0-32 320 0z" fill="url(#config-default-blue)"/>
        <defs>
            <linearGradient id="config-default-blue" x1="0.5" x2="0.5" y2="1">
                <stop offset="0" stop-color="#8faeb4"/>
                <stop offset="1" stop-color="#638c94"/>
            </linearGradient>
            <linearGradient id="config-hover-blue" x1="0.5" x2="0.5" y2="1">
                <stop offset="0" stop-color="#eaf0f1"/>
                <stop offset="1" stop-color="#7b9fa6"/>
            </linearGradient>
        </defs>
    `;

    button.appendChild(svg);
    button.addEventListener('click', createUIPanel);

    const path = svg.querySelector('path');
    button.addEventListener('mouseenter', () => path.setAttribute('fill', 'url(#config-hover-blue)'));
    button.addEventListener('mouseleave', () => path.setAttribute('fill', 'url(#config-default-blue)'));

    noteButton.insertAdjacentElement('afterend', button);
}


    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) {
                            let timer;
                            const delay = 1000;
                            timer = setTimeout(() => {
                                button.classList.remove('recent');
                                clearRecentButtonInfo();
                            }, delay);
                            button.addEventListener('mouseup', () => {
                                clearTimeout(timer);
                            }, { once: true });
                            button.addEventListener('mouseleave', () => {
                                clearTimeout(timer);
                            }, { once: true });
                        }
                    });

                    const footerContainer = chatBox.querySelector('.chat-box-footer___YK914');
                    if (footerContainer) {
                        let buttonContainer = footerContainer.parentElement.querySelector('.button-container');
                        if (!buttonContainer) {
                            buttonContainer = document.createElement('div');
                            buttonContainer.className = 'button-container';
                            buttonContainer.style.display = 'flex';
                            buttonContainer.style.flexWrap = 'wrap';
                            footerContainer.insertAdjacentElement('beforebegin', buttonContainer);
                        }
                        buttonContainer.appendChild(button);
                    }
                }
            });
        });
    }

    async function addCustomText(chatBox, messageTemplate, event) {
        const nameElement = chatBox.querySelector('.typography___Dc5WV');
        const name = nameElement ? nameElement.textContent.trim() : 'Trader';
        let message = messageTemplate.replace('{name}', name);

        if (message.includes('{company}')) {
            const apiKey = getAPIKey();
            if (!apiKey) {
                alert('API key not set. Please set the API key in the settings tab.');
                return;
            }

            let companyInfo = loadCache('companyCache');
            if (!companyInfo) {
                const apiUrl = `https://api.torn.com/company/?selections=profile&key=${apiKey}`;
                try {
                    const response = await fetch(apiUrl);
                    const data = await response.json();
                    if (!data.error && data.company) {
                        companyInfo = data.company;
                        saveCache('companyCache', companyInfo);
                    } else {
                        alert('Failed to retrieve company information. Check your API key.');
                        return;
                    }
                } catch (error) {
                    alert('Error fetching company information:', error);
                    return;
                }
            }

            const companyType = companyTypes[companyInfo.company_type] || 'Unknown';
            const companyDetails = `${companyInfo.rating}* ${companyType}`;
            message = message.replace('{company}', companyDetails);
        }

        if (message.includes('{faction}')) {
            const apiKey = getAPIKey();
            if (!apiKey) {
                alert('API key not set. Please set the API key in the settings tab.');
                return;
            }

            let factionInfo = loadCache('factionCache');
            if (!factionInfo) {
                const apiUrl = `https://api.torn.com/faction/?selections=basic&key=${apiKey}`;
                try {
                    const response = await fetch(apiUrl);
                    const data = await response.json();
                    if (!data.error && data.respect && data.name && data.rank) {
                        factionInfo = data;
                        saveCache('factionCache', factionInfo);
                    } else {
                        alert('Failed to retrieve faction information. Check your API key.');
                        return;
                    }
                } catch (error) {
                    alert('Error fetching faction information:', error);
                    return;
                }
            }

            const respectFormatted = factionInfo.respect >= 1000000 ? (factionInfo.respect / 1000000).toFixed(1) + 'm' : (factionInfo.respect / 1000).toFixed(1) + 'k';
            const factionDetails = `${factionInfo.name}, ${factionInfo.rank.name} Ranked ${respectFormatted} Respect`;
            message = message.replace('{faction}', factionDetails);
        }

        insertMessage(chatBox, message, event.target);
    }

    function insertMessage(chatBox, message, targetButton) {
        navigator.clipboard.writeText(message).then(() => {
            const textArea = chatBox.querySelector('textarea');
            if (!textArea) return;
            textArea.focus();
            textArea.value = '';
            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;

            document.querySelectorAll('.custom-chat-button').forEach(btn => {
                btn.classList.remove('recent');
            });
            targetButton.classList.add('recent');

            const chatBoxNameElem = chatBox.querySelector('.chat-box-header__name___jIjjM');
            const chatBoxName = chatBoxNameElem ? chatBoxNameElem.textContent : '';
            saveRecentButtonInfo(targetButton.getAttribute('data-button-text'), chatBoxName);
        });
    }

    function applyRecentButtonClass() {
        const recentButtonInfo = JSON.parse(localStorage.getItem('recentButtonInfo'));
        if (recentButtonInfo) {
            document.querySelectorAll('.custom-chat-button').forEach(btn => {
                btn.classList.remove('recent');
            });

            document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => {
                const chatBoxNameElem = chatBox.querySelector('.chat-box-header__name___jIjjM');
                const chatBoxName = chatBoxNameElem ? chatBoxNameElem.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 = async (event) => {
            const file = event.target.files[0];
            if (!file) {
                showNotification('No file selected.');
                return;
            }
            const reader = new FileReader();
            reader.onload = (e) => {
                try {
                    const config = JSON.parse(e.target.result);
                    if (config && config.buttons) {
                        saveButtonConfigurations(config);
                        populateButtonConfigs();
                        applyButtonConfigurations();
                        showNotification('Configuration imported from file.');
                    } else {
                        showNotification('Invalid configuration file.');
                    }
                } catch (err) {
                    showNotification('Error: Invalid JSON.');
                }
            };
            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);
        showNotification('Configuration exported to file.');
    }

    function updateCharCounter() {
        const textArea = document.getElementById('button-text-content');
        if (!textArea) return;
        const counter = document.getElementById('char-counter');
        if (!counter) return;
        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() {
        createConfigButton();
        applyButtonConfigurations();
        applyRecentButtonClass();
    });

    const chatContainer = document.querySelector('#chatRoot');
    if (chatContainer) {
        chatContainerObserver.observe(chatContainer, { childList: true, subtree: true });
    }

    applyButtonConfigurations();
    applyRecentButtonClass();
})();