Grizzway Tools

Epic fixes and visual mods for fishtank.live

// ==UserScript==
// @name         Grizzway Tools
// @namespace    http://tampermonkey.net/
// @version      4.8
// @description  Epic fixes and visual mods for fishtank.live
// @author       Grizzway
// @match        *://*.fishtank.live/*
// @grant        GM_addStyle
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // User-editable color for stream title
    const streamTitleColor = 'limegreen';
    const streamTitleFilter = 'drop-shadow(0 0 1px #00ff00) drop-shadow(0 0 2px #00ff00)'
        // Custom Ping Sound
    const customPingSound = "https://files.catbox.moe/1qbutz.mp3"; // Replace with your own link PLEASE
    // Sound blocking functionality
    const blockedSounds = [
        'https://cdn.fishtank.live/sounds/suicidebomb.mp3',
        'https://cdn.fishtank.live/sounds/suicidebomb-1.mp3',
        'https://cdn.fishtank.live/sounds/suicidebomb-2.mp3',
        'https://cdn.fishtank.live/sounds/suicidebomb-3.mp3',
        'https://cdn.fishtank.live/sounds/suicidebomb-4.mp3',
        'https://cdn.fishtank.live/sounds/nuke-1.mp3',
        'https://cdn.fishtank.live/sounds/nuke-2.mp3',
        'https://cdn.fishtank.live/sounds/nuke-3.mp3',
        'https://cdn.fishtank.live/sounds/nuke-4.mp3',
        'https://cdn.fishtank.live/sounds/nuke-5.mp3',
        'https://cdn.fishtank.live/sounds/horn.mp3'
    ];
    // List of unwanted classes to remove
    const unwantedClasses = [
        'mirror',
        'live-stream-player_blur__7BhBE',
        'live-stream-player_upside-down__YvkE4',
        'chat-message-default_shrink-ray__nGvpr'
    ];

    // Save original Audio class
    const OriginalAudio = window.Audio;

    // Create a unified Audio wrapper
    window.Audio = function(src) {
        console.log("[Tampermonkey] Attempting to play sound with src:", src); // Log the source URL

        // Check if the sound is a blocked one
        if (src && typeof src === "string" && blockedSounds.some(sound => src.includes(sound))) {
            console.log('Blocked sound (Audio wrapper):', src);
            return new OriginalAudio(); // Prevent playing blocked sound
        }

        // Check for mention.mp3 and replace with custom ping sound
        if (src && typeof src === "string" && src.toLowerCase().includes("mention")) {
            console.log("[Tampermonkey] Replacing mention sound with custom sound!", src);
            return new OriginalAudio(customPingSound); // Play custom ping sound instead
        }

        // Log all other sounds for debugging
        console.log("[Tampermonkey] Playing sound without modification:", src);

        // Default behavior for all other sounds
        return new OriginalAudio(src);
    };

    // Keep Audio.prototype chain intact
    window.Audio.prototype = OriginalAudio.prototype;

    // Intercept fetch requests and ensure the cache doesn't block our custom sound
    const originalFetch = window.fetch;
    window.fetch = async function(resource, init) {
        if (typeof resource === 'string') {
            // Log all resources being fetched to ensure we catch the mention sound
            console.log("[Tampermonkey] Fetching resource:", resource);

            if (resource.includes('mention.mp3')) {
                console.log("[Tampermonkey] Replacing mention sound via fetch:", resource);
                return new Response(await fetch(customPingSound), {
                    status: 200
                });
            }

            if (blockedSounds.some(sound => resource.includes(sound))) {
                console.log('Blocked sound (fetch):', resource);
                return new Response(null, {
                    status: 403
                });
            }
        }
        return originalFetch(resource, init);
    };

    // Intercept XMLHttpRequests and modify response for mention.mp3
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url) {
        console.log("[Tampermonkey] XMLHttpRequest opened with URL:", url);
        if (url.includes("mention.mp3")) {
            console.log("[Tampermonkey] Replacing mention sound via XHR:", url);
            // Override the request and prevent the original
            this.abort(); // Cancel the original request
            const customAudioRequest = new XMLHttpRequest();
            customAudioRequest.open(method, customPingSound, true);
            customAudioRequest.send();
            return;
        }

        if (blockedSounds.some(sound => url.includes(sound))) {
            console.log('Blocked sound (XHR):', url);
            this.abort();
        } else {
            originalOpen.apply(this, arguments);
        }
    };

    // Block audio elements directly (HTMLAudioElement)
    const originalAudioPlay = HTMLAudioElement.prototype.play;
    HTMLAudioElement.prototype.play = function() {
        console.log("[Tampermonkey] Attempting to play audio:", this.src);
        if (this.src.includes("mention.mp3")) {
            console.log("[Tampermonkey] Replacing mention sound with custom sound in HTMLAudioElement.");
            this.src = customPingSound;
        }

        if (blockedSounds.some(sound => this.src.includes(sound))) {
            console.log('Blocked sound (HTMLAudioElement):', this.src);
            return Promise.reject('Blocked sound');
        }
        return originalAudioPlay.apply(this, arguments);
    };

    setInterval(() => {
        const body = document.querySelector('body');
        if (body) {
            unwantedClasses.forEach(className => {
                if (body.classList.contains(className)) {
                    body.classList.remove(className);
                }
            });
        }

        // Check if the video player is open
        const videoPlayer = document.querySelector('.live-stream-player_live-stream-player__4CHjG');
        if (videoPlayer) {
            unwantedClasses.forEach(className => {
                const elementsWithClass = videoPlayer.querySelectorAll(`.${className}`);
                elementsWithClass.forEach(el => el.classList.remove(className));

                if (videoPlayer.classList.contains(className)) {
                    videoPlayer.classList.remove(className);
                }
            });
        }

        // Check chat messages container and its children
        const chatContainer = document.querySelector('#chat-messages.chat-messages_chat-messages__UeL0a');
        if (chatContainer) {
            unwantedClasses.forEach(className => {
                const chatElements = chatContainer.querySelectorAll(`.${className}`);
                chatElements.forEach(el => el.classList.remove(className));
            });
        }

        // Ensure stream title color stays updated
        const streamTitles = document.querySelectorAll('.live-stream_name__ngU04');
        streamTitles.forEach(title => {
            title.style.color = streamTitleColor;
            title.style.filter = streamTitleFilter;
        });

    }, 1000);


    function profileFix() {
        const profileModal = document.querySelector('.profile-modal_profile-modal__4mjE7');
        if (!profileModal || profileModal.offsetParent === null) return; // Ensure modal is visible

        setTimeout(() => {
            const bio = profileModal.querySelector('.user-profile_bio__nzdwR');
            const clanActions = profileModal.querySelector('.user-profile_clan-actions__aS32x');
            const header = profileModal.querySelector('.user-profile_header__zP1kv');

            if (!bio || !clanActions || !header) return; // Ensure required elements exist

            // Prevent duplicates: Check if already moved
            if (header.contains(bio) && header.nextElementSibling === clanActions) return;

            // Move bio into header, right after .user-profile_info__eFefT
            const userInfo = header.querySelector('.user-profile_info__eFefT');
            if (userInfo && !header.contains(bio)) {
                bio.remove();
                userInfo.insertAdjacentElement('afterend', bio);
            }

            // Move clan actions **just below** the header
            if (header.nextElementSibling !== clanActions) {
                clanActions.remove();
                header.insertAdjacentElement('afterend', clanActions);
            }
        }, 100);
    }

    // Observer to detect when the profile modal appears
    const profileObserver = new MutationObserver(() => {
        const modal = document.querySelector('.profile-modal_profile-modal__4mjE7');
        if (modal && modal.offsetParent !== null) {
            profileFix();
        }
    });

    profileObserver.observe(document.body, {
        childList: true,
        subtree: true
    });

    // Extract logged-in username
    function getLoggedInUsername() {
        const userElement = document.querySelector('.top-bar-user_display-name__bzlpw');
        return userElement ? userElement.textContent.trim() : null;
    }


    // Remove text-shadow and change font-weight for the logged-in user
    function styleLoggedInUserMessages() {
        const username = getLoggedInUsername();
        if (!username) return;

        const messages = document.querySelectorAll('.chat-message-default_user__uVNvH');
        messages.forEach(message => {
            if (message.textContent.includes(username)) {
                const userElement = message;
                if (userElement) {
                    userElement.style.textShadow = 'none';
                    userElement.style.fontWeight = '1000';
                }

                const messageTextElement = message.closest('.chat-message-default_chat-message-default__JtJQL').querySelector('.chat-message-default_body__iFlH4');
                if (messageTextElement) {
                    messageTextElement.style.textShadow = 'none';
                    messageTextElement.style.fontWeight = '1000';
                }
            }
        });
    }

    function addLinkButton(messageElement, url) {
        // Safety check: don't add another button if one already exists for this URL
        const existingButton = messageElement.querySelector(`.custom-link-button[href="${url}"]`);
        if (existingButton) return; // Skip if button already exists for this URL

        const button = document.createElement('a');
        button.href = url.startsWith('http') ? url : 'https://' + url;
        button.target = '_blank';
        button.rel = 'noopener noreferrer';
        button.className = 'custom-link-button';
        button.style.marginLeft = '8px';
        button.style.width = '20px';
        button.style.height = '20px';
        button.style.display = 'inline-flex';
        button.style.alignItems = 'center';
        button.style.justifyContent = 'center';
        button.style.backgroundColor = '#4EA1FF';
        button.style.color = 'white';
        button.style.borderRadius = '4px';
        button.style.fontSize = '14px';
        button.style.textDecoration = 'none';
        button.style.verticalAlign = 'middle';
        button.textContent = '🔗';

        // Insert the button at the end of the message content
        const messageContent = messageElement.querySelector('.chat-message-default_message__milmT');
        if (messageContent) {
            messageContent.appendChild(button); // Append the button to the message
        } else {
            console.warn('Unable to insert button: No message content found.');
        }
    }

    function detectLinksAndButton(messageElement) {
        const contentElement = messageElement.querySelector('.chat-message-default_message__milmT');
        if (!contentElement) return;

        const textContent = contentElement.innerText || contentElement.textContent || '';
        const urlRegex = /(\b(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:\/[^\s]*)?\b)/g; // global flag for multiple matches
        const matches = textContent.match(urlRegex);

        if (matches) {
            // Loop through all the found URLs and add a button for each one
            matches.forEach(url => {
                addLinkButton(messageElement, url);
            });
        }
    }

    function observeMessages(chatContainer) {
        const observer = new MutationObserver((mutationsList) => {
            mutationsList.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1 && node.classList.contains('chat-message-default_chat-message-default__JtJQL')) {
                        detectLinksAndButton(node);
                    }
                });
            });
        });

        observer.observe(chatContainer, {
            childList: true,
            subtree: true
        });
    }

    function waitForChatContainer() {
        const chatContainer = document.querySelector('.chat-messages_chat-messages__UeL0a');
        if (chatContainer) {
            observeMessages(chatContainer);
        } else {
            setTimeout(waitForChatContainer, 1000);
        }
    }

    window.onload = function() {
        waitForChatContainer();
    };

    let isElementVisible = false; // Track visibility state

    // Function to change the medal size
    function changeMedalSize(isVisible) {
        const buttons = document.querySelectorAll('.medal_medal__Hqowf.medal_md__RvQMV button');
        buttons.forEach(button => {
            if (isVisible) {
                button.style.setProperty('--medal-size', '32px');
            } else {
                button.style.setProperty('--medal-size', '64px');
            }
        });
    }

    // Function to check if the medal selector is visible so it can fix the sizing
    function checkElement() {
        const medalSelector = document.querySelector('.medal-selector_medals__sk3PN');

        if (medalSelector) {
            // Check if it's visible
            const rect = medalSelector.getBoundingClientRect();
            const isVisible = rect.width > 0 && rect.height > 0;

            // Only change the size if visibility has changed
            if (isVisible !== isElementVisible) {
                isElementVisible = isVisible;
                changeMedalSize(isElementVisible);
            }
        } else {
            // If the selector disappears completely
            if (isElementVisible) {
                isElementVisible = false;
                changeMedalSize(false);
            }
        }
    }

    // check every 100ms
    setInterval(checkElement, 100);

    // Highlight messages and add glowing username for the logged-in user
    function highlightUserMessages() {
        const username = getLoggedInUsername();
        if (!username) return;

        const messages = document.querySelectorAll('.chat-message-default_user__uVNvH');
        messages.forEach(message => {
            if (message.textContent.includes(username)) {
                const chatMessage = message.closest('.chat-message-default_chat-message-default__JtJQL');

                if (chatMessage && !chatMessage.classList.contains('highlighted-message')) {
                    chatMessage.classList.add('highlighted-message');
                }

                if (!message.classList.contains('glowing-username')) {
                    message.classList.add('glowing-username');
                }
            }
        });
    }

    // Re-check messages every second and apply the styles
    setInterval(() => {
        highlightUserMessages();
        styleLoggedInUserMessages();
    }, 1000);


    function createToggle() {
        const userTab = document.querySelector('.top-bar_links__4FJwt');
        if (!userTab) {
            console.log("User tab not found, retrying...");
            setTimeout(createToggle, 500); // Retry until it's available
            return;
        }

        // Create a container for the toggle
        const container = document.createElement('div');
        container.style.display = 'flex';
        container.style.alignItems = 'center';
        container.style.gap = '5px';
        container.style.marginTop = '10px';

        // Create the checkbox
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.id = 'stylingToggle';
        checkbox.checked = true; // Checked by default

        // Create the label
        const label = document.createElement('label');
        label.htmlFor = 'stylingToggle';
        label.textContent = 'Styling';
        label.style.color = 'white'; // or whatever color fits your site

        // Append checkbox and label to the container
        container.appendChild(checkbox);
        container.appendChild(label);

        // Append container to user tab
        userTab.appendChild(container);

        // Listen to checkbox changes
        checkbox.addEventListener('change', function() {
            if (this.checked) {
                applyOptionalStyles();
            } else {
                removeOptionalStyles();
            }
        });
    }

function applyOptionalStyles() {
        if (document.getElementById('optionalStyles')) return;
        const style = document.createElement('style');
        style.id = 'optionalStyles';
        style.textContent = `
            /* This is for the site background */
            body, .layout_layout__5rz87, .select_options__t1ibN {
                background-image: url('https://images.gamebanana.com/img/ss/mods/5f681fd055666.jpg') !important;
                background-size: cover !important;
                background-position: center !important;
                background-repeat: no-repeat !important;
            }
            /* This is for the chat background */
            .chat_chat__2rdNg {
                background-image: url('https://i.imgur.com/UVjYx1I.gif') !important;
                background-size: cover !important;
                background-position: center !important;
                background-repeat: no-repeat !important;
            }
            /* Your text highlight color */
            .highlighted-message {
                background-color: rgba(125, 5, 5, 0.4) !important;
            }
            /* The color your name glows in chat */
            .glowing-username {
                -webkit-text-stroke: 1px rgba(0, 255, 0, 0.2);
                filter: drop-shadow(0 0 1px #00ff00) drop-shadow(0 0 2px #00ff00);
            }
            /* Header Colors */
            .maejok-input-invalid, .maejok-context-message, .maejok-tts-warning-text,
            .chat_header__8kNPS, .top-bar_top-bar__X4p2n, .panel_body__O5yBA, .inventory_slots__D4IrC {
                background-color: limegreen !important;
                border-color: limegreen !important;
            }
            /* More header stuff */
            .panel_header__T2yFW {
                background-color: darkgreen !important;
            }
            /* Removing dropshadow on certain text to make it look nicer */
            .top-bar_top-bar__X4p2n {
                box-shadow: none !important;
            }
            .maejok-context-message svg path {
                fill: black !important;
            }
            /* Video player Opacity */
            .hls-stream-player_hls-stream-player__BJiGl, .live-stream-player_container__A4sNR, .layout_center__Vsd3b {
                opacity: 1;
            }
            /* Makes the Ads go away! */
            .ads_ads__Z1cPk {
                opacity: 0;
            }
            /* Poll bottom color aka the part next to the vote button */
            .poll_footer__rALdX {
                background-color: limegreen;
            }
            /* Poll Vote Button Disabled */
            .poll_vote__b_NE0 button:disabled:hover {
                background-color: red !important;
                color: white !important;
                border-color: black !important;
            }
            /* Poll Vote Button Enabled */
            .poll_vote__b_NE0 button:hover {
                background-color: red !important;
                color: white !important;
                border-color: black !important;
            }
            /* TTS & SFX Chat Messages */
            .chat-message-tts_chat-message-tts__2Jlxi, .chat-message-sfx_chat-message-sfx__OGv6q {
                border-color: #00ff00 !important;
                background-color: darkgreen !important;
                filter: drop-shadow(0 0 2px #00ff00) drop-shadow(0 0 2px #00ff00);
            }
            /* Interior Text for TTS & SFX, Currently Glows */
            .chat-message-tts_message__sWVCc, .chat-message-sfx_message__d2Rei {
                -webkit-text-stroke: 2px rgba(0, 255, 0, 0.6);
                filter: drop-shadow(0 0 2px #00ff00) drop-shadow(0 0 2px #00ff00);
            }
            /* TTS & SFX Footer Timestamp */
            .chat-message-tts_timestamp__pIVv0, .chat-message-sfx_timestamp__ilYfg {
                 color: black !important;
                 font-weight: bold !important;
                 text-shadow: none !important;
            }
        `;
        document.head.appendChild(style);
    }

    function removeOptionalStyles() {
        const style = document.getElementById('optionalStyles');
        if (style) {
            style.remove();
        }
    }

    createToggle();
    applyOptionalStyles();

    // Custom style for the user's messages
    GM_addStyle(`
        /* IMPORTANT: THIS IS EVERY MODAL. DONT TOUCH THE SIZE OR ALIGNMENT */
        .modal_modal__MS70U {
             height:1000px !important;
             width: 1500px !important;
             align-self: anchor-center !important;
             justify-self: anchor-center !important;
             overflow: scroll !important;
             background-color: #191d21bb !important; /* The last two characters are hex code for opacity. 00 for 0%, ff for 100% */
        }
        /* Modal Dim Background, 0% Opacity means no dimming */
        .modal_backdrop__94Bu6 {
             opacity: 0;
        }
        /* FULL SCREEN SETTINGS MENU & ITEM DEX DO NOT TOUCH */
        .settings-modal_body__qdvDm, .profile-modal_profile-modal__4mjE7 {
             display: flex;
             flex-direction: column;
             gap: 8px;
             align-items: center;
             margin: auto;
             width: 100%;
             padding: 16px;
             background-color: rgba(0, 0, 0, .25);
             border: 1px solid #505050;
             border-radius: 4px;
        }
        /* FULL SCREEN SETTINGS MENU & ITEM DEX DO NOT TOUCH */
        .user-profile-tabs_tab__2bsiR {
             display: flex;
             flex-wrap: wrap;
             background-color: rgba(0, 0, 0, .5);
             padding: 8px;
             border: 1px solid #505050;
             border-bottom-left-radius: 4px;
             border-bottom-right-radius: 4px;
             border-top: 0;
             gap: 4px;
             width: 1400px;
             height: 900px;
             justify-self: center;
        }
        /* FULL SCREEN SETTINGS MENU & ITEM DEX DO NOT TOUCH */
        .user-profile-items_user-profile-items__rl_CV {
             display: flex;
             width: 1400px;
             height: 900px;
        }
        /* FULL SCREEN SETTINGS MENU & ITEM DEX DO NOT TOUCH */
        .user-profile-items_items__zuulV {
             display: flex;
             flex-wrap: wrap;
             gap: 4px;
             padding: 8px;
             width: 100%;
             height: 100%;
             justify-content: space-evenly;
             max-height: 900px !important;
        }
        .user-profile-tts-sfx_messages__w4_Ew {
             max-height: 93vh !important; /* DO NOT TOUCH UNLESS YOU WANT YOUR SFX AND TTS HISTORY WINDOW BROKEN */
        }
        .user-profile_profile__g_tBc {
             width: 1400px !important; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
             max-width: 1400px !important; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
         .user-profile_body__hk8ZS {
             width: 1400px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
             align-content: center; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .profile-modal_profile-modal__4mjE7 {
             align-items: unset; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .user-profile_bio__nzdwR {
             padding-left: 80px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
             width: 1000px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .input_input__Zwrui textarea {
             height: 100px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .user-profile_header__zP1kv {
             width: 1400px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .medal_medal__Hqowf.medal_md__RvQMV button {
             --medal-size: 64px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .user-update-profile_actions__vbfIX button {
            width: 800px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .user-update-profile_user-update-profile__sa20I {
            width: 1400px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .user-update-profile_details__7bBRy {
            width: 350px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .user-update-profile_bio__DaV4N {
            width: 800px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
            justify-self: flex-end; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
            height: 200px; /* DO NOT TOUCH UNLESS YOU WANT YOUR PROFILE AND/OR SETTINGS WINDOWS BROKEN */
        }
        .modal_body__j3Bav {
            align-self: anchor-center;
            height:100%;
            overflow: visible !important;
        }
        .modal_header__O0ebJ {
            padding-top:50px;
            font-size:xxx-large;
        }
        .item-generator-modal_items__Xs78_, .item-generator-modal_generator__vmam0 {
            background-color: rgba(14, 16, 18, 1) !important;
        }
        .logs_logs__YL0uF {
            width: 1400px;
            align-self: center;
        }
        .logs_body__lqe_U {
            height: 700px !important;
        }
        .logs_message__p9V2r {
            font-size: medium;
        }
         .sfx-modal_sfx-modal__i_ppy, .tts-modal_tts-modal__rxY0z {
             width: 1200px;
             padding-top: 150px;
         }
         .select_value__yPLpn {
             background-color: rgba(43, 45, 46, 1) !important;
         }
         .input_input-wrapper__xvMLO input{
             background-color: rgba(43, 45, 46, 1) !important;
         }
         .user-update-profile_user-update-profile__sa20I .user-update-profile_bio__DaV4N {
             grid-column: 1 / 3;
             grid-row: 1 / 4;
         }
         .input_input-wrapper__xvMLO textarea {
             height: 165px;
         }
         .user-update-profile_footer__kONMY {
             grid-row: 2 / 6 !important;
             height:60px;
         }
         .user-profile-tabs_user-profile-tabs__7SFh7 {
             padding-top: 75px;
         }
        `);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址