Torn.com Enhanced Chat Buttons V2

Add customizable buttons to your chats in Torn to make your life easier

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Torn.com Enhanced Chat Buttons V2
// @namespace    http://tampermonkey.net/
// @version      2.62
// @description  Add customizable buttons to your chats in Torn to make your life easier
// @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 BAZAAR_CACHE_TTL = 10 * 60 * 1000; // 10 minutes

    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 => {
            // New chat version
            if (chatBox.id === 'public_trade') return true;
            // Old chat version
            const oldTitle = chatBox.querySelector('.chat-box-header__name___jIjjM');
            return oldTitle && oldTitle.textContent === 'Trade';
        },
        HospitalChat: chatBox => {
            // New chat version
            if (chatBox.id === 'public_hospital') return true;
            // Old chat version
            const oldTitle = chatBox.querySelector('.chat-box-header__name___jIjjM');
            return oldTitle && oldTitle.textContent === 'Hospital';
        },
        FactionChat: chatBox => {
            // New chat version
            if (chatBox.id && chatBox.id.startsWith('faction-')) return true;
            // Old chat version
            const oldTitle = chatBox.querySelector('.chat-box-header__name___jIjjM');
            return oldTitle && oldTitle.textContent === 'Faction';
        },
        CompanyChat: chatBox => {
            // New chat version
            if (chatBox.id && chatBox.id.startsWith('company-')) return true;
            // Old chat version
            const oldTitle = chatBox.querySelector('.chat-box-header__name___jIjjM');
            return oldTitle && oldTitle.textContent === 'Company';
        },
        GlobalChat: chatBox => {
            // New chat version
            if (chatBox.id === 'public_global') return true;
            // Old chat version
            const oldTitle = chatBox.querySelector('.chat-box-header__name___jIjjM');
            return oldTitle && oldTitle.textContent === 'Global';
        },
        UserChat: chatBox => {
            // New chat version
            if (chatBox.id && chatBox.id.startsWith('private-')) return true;
            // Old chat version
            return chatBox.querySelector('.chat-box-header__options___nTsMU') !== null;
        }
    };

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

    function saveBuyItems(items) {
        localStorage.setItem('buyItems', items);
        showNotification('Buy items saved.');
    }

    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 getBazaarData() {
        return loadCache('bazaarCache');
    }

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

    function loadBazaarCache() {
        const cacheData = JSON.parse(localStorage.getItem('bazaarCache'));
        if (cacheData && (Date.now() - cacheData.timestamp < BAZAAR_CACHE_TTL)) {
            return cacheData.data;
        }
        return null;
    }

    function formatBazaarItems(bazaarData, maxLength = 125) {
        if (!bazaarData || !bazaarData.bazaar) return 'No bazaar data available';

        const items = bazaarData.bazaar;
        const totalValue = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);

        const groupedItems = {};
        items.forEach(item => {
            let type = item.type;
            let name = item.name;

            let displayName;

            if (name === 'Donator Pack') {
                displayName = 'DPs';
            } else if (name === 'Erotic DVD') {
                displayName = 'eDVD';
            } else if (name === 'Xanax') {
                displayName = 'Xan';
            } else if (name === 'Feathery Hotel Coupon') {
                displayName = 'FHC';
            } else if (['Heavy Arms Cache', 'Armor Cache', 'Melee Cache', 'Small Arms Cache', 'Medium Arms Cache'].includes(name)) {
                displayName = 'RW cache';
            } else if (name === 'Six-Pack of Energy Drink') {
                displayName = '6pack edrinks';
            } else if (name === 'Six-Pack of Alcohol') {
                displayName = '6pack alcohol';
            } else {
                if (type === 'Flower' || type === 'Plushie') {
                    displayName = 'Flushies';
                } else if (type === 'Energy Drink') {
                    displayName = 'edrinks';
                } else if (['Melee', 'Primary', 'Secondary', 'Temporary', 'Clothing', 'Jewelry', 'Defensive', 'Special', 'Miscellaneous', 'Enhancer', 'Tools', 'Cars'].includes(type)) {
                    displayName = 'Misc';
                } else {
                    displayName = type;
                }
            }

            if (!groupedItems[displayName]) {
                groupedItems[displayName] = { value: 0 };
            }
            groupedItems[displayName].value += item.price * item.quantity;
        });

        const formattedItems = Object.entries(groupedItems)
            .map(([name, data]) => ({
                name,
                value: data.value,
                percentage: (data.value / totalValue) * 100,
                isMisc: name === 'Misc'
            }))
            .sort((a, b) => {
                if (a.isMisc && !b.isMisc) return 1;
                if (!a.isMisc && b.isMisc) return -1;
                return b.percentage - a.percentage;
            });

        const significantItems = formattedItems.filter(item => item.percentage >= 1);

        let currentLength = 0;
        let itemsToInclude = [];

        for (const item of significantItems) {
            const newLength = currentLength + (currentLength > 0 ? 2 : 0) + item.name.length;
            if (newLength <= maxLength) {
                itemsToInclude.push(item.name);
                currentLength = newLength;
            } else {
                break;
            }
        }

        if (itemsToInclude.length === 0) return 'Empty bazaar';

        return itemsToInclude.join(', ');
    }

    function formatBuyItems(maxLength = 125) {
        const buyItems = getBuyItems().split(',').map(item => item.trim()).filter(item => item);
        if (buyItems.length === 0) return 'No buy items set';

        // Get the last used index from localStorage
        let lastIndex = parseInt(localStorage.getItem('lastBuyItemIndex') || '0');

        let currentLength = 0;
        let itemsToInclude = [];
        let startIndex = lastIndex;

        // Try to fit items starting from the last used index
        for (let i = 0; i < buyItems.length; i++) {
            const index = (startIndex + i) % buyItems.length;
            const item = buyItems[index];
            const newLength = currentLength + (currentLength > 0 ? 2 : 0) + item.length;

            if (newLength <= maxLength) {
                itemsToInclude.push(item);
                currentLength = newLength;
            } else {
                break;
            }
        }

        // Update the last used index for next time
        if (itemsToInclude.length > 0) {
            lastIndex = (startIndex + itemsToInclude.length) % buyItems.length;
            localStorage.setItem('lastBuyItemIndex', lastIndex.toString());
        }

        return itemsToInclude.join(', ');
    }

    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, {bazaar} for bazaar info, {buy} for buy items."></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>

                <label for="buy-items">Items to Buy (comma separated)</label>
                <input type="text" id="buy-items" placeholder="e.g., xanax, BCT, energy drinks" value="${getBuyItems()}">
                <button id="save-buy-items-button">Save Buy Items</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);
        });
        document.getElementById('save-buy-items-button').addEventListener('click', () => {
            const items = document.getElementById('buy-items').value;
            saveBuyItems(items);
        });

        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 peopleButton = document.querySelector('#people_panel_button');
        if (!peopleButton || 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 = 'root___WHFbh root___J_YsG';

        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', '24');
        svg.setAttribute('width', '24');
        svg.classList.add('root___DYylw', 'icon___M_Izz');
        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)'));

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

    function applyButtonConfigurations() {
        const configs = getButtonConfigurations();
        // Query for both old and new chat window classes
        document.querySelectorAll('.root___FmdS_, .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 });
                        }
                    });

                    // Try both new and old input container selectors
                    const inputContainer = chatBox.querySelector('.root___WUd1h') || chatBox.querySelector('.chat-box-footer___YK914');
                    if (inputContainer) {
                        let buttonContainer = chatBox.querySelector('.button-container');
                        if (!buttonContainer) {
                            buttonContainer = document.createElement('div');
                            buttonContainer.className = 'button-container';
                            buttonContainer.style.display = 'flex';
                            buttonContainer.style.flexWrap = 'wrap';
                            inputContainer.insertAdjacentElement('beforebegin', buttonContainer);
                        }
                        buttonContainer.appendChild(button);
                    }
                }
            });
        });
    }

    async function addCustomText(chatBox, messageTemplate, event) {
        let name = 'User';
        // Try both new and old name selectors
        const titleElement = chatBox.querySelector('.title___Bmq5P') ||
                           chatBox.querySelector('.typography___Dc5WV') ||
                           chatBox.querySelector('.chat-box-header__name___jIjjM');
        if (titleElement) {
            name = titleElement.textContent.trim();
        }
        let message = messageTemplate.replace('{name}', name);

        if (message.includes('{buy}')) {
            const messageWithoutBuy = message.replace('{buy}', '');
            const availableSpace = 125 - messageWithoutBuy.length;
            const buyText = formatBuyItems(availableSpace);
            message = message.replace('{buy}', buyText);
        }

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

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

            // Calculate available space for bazaar list
            const messageWithoutBazaar = message.replace('{bazaar}', '');
            const availableSpace = 125 - messageWithoutBazaar.length;

            const bazaarText = formatBazaarItems(bazaarData, availableSpace);
            message = message.replace('{bazaar}', bazaarText);
        }

        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(() => {
            // Try both new and old textarea selectors
            const textArea = chatBox.querySelector('.textarea___V8HsV') || chatBox.querySelector('textarea');
            if (!textArea) return;
            textArea.focus();
            textArea.setRangeText(message, 0, textArea.value.length, 'end');
            textArea.dispatchEvent(new Event('input', { bubbles: true }));
            textArea.focus();
            textArea.selectionStart = textArea.selectionEnd = message.length;

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

            // Try both new and old title selectors
            const titleElement = chatBox.querySelector('.title___Bmq5P') ||
                               chatBox.querySelector('.chat-box-header__name___jIjjM');
            const chatBoxName = titleElement ? titleElement.textContent.trim() : '';
            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');
            });

            // Query for both old and new chat window classes
            document.querySelectorAll('.root___FmdS_, .chat-box___mHm01').forEach(chatBox => {
                // Try both new and old title selectors
                const titleElement = chatBox.querySelector('.title___Bmq5P') ||
                                   chatBox.querySelector('.chat-box-header__name___jIjjM');
                const chatBoxName = titleElement ? titleElement.textContent.trim() : '';
                if (chatBoxName === recentButtonInfo.chatBoxName) {
                    const button = chatBox.querySelector(`[data-button-text="${recentButtonInfo.buttonText}"]`);
                    if (button) {
                        button.classList.add('recent');
                    }
                }
            });
        }
    }

    function exportConfig() {
        const config = {
            ...getButtonConfigurations(),
            buyItems: getBuyItems()
        };
        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 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);
                        if (config.buyItems) {
                            saveBuyItems(config.buyItems);
                            document.getElementById('buy-items').value = config.buyItems;
                        }
                        populateButtonConfigs();
                        applyButtonConfigurations();
                        showNotification('Configuration imported from file.');
                    } else {
                        showNotification('Invalid configuration file.');
                    }
                } catch (err) {
                    showNotification('Error: Invalid JSON.');
                }
            };
            reader.readAsText(file);
        };
        input.click();
    }

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