Скрипт для КФ (биографии)

by David_Goggins

当前为 2025-10-23 提交的版本,查看 最新版本

// ==UserScript==
// @name         Скрипт для КФ (биографии)
// @namespace    https://forum.blackrussia.online/
// @version      1.2.4
// @description  by David_Goggins 
// @author       David_Goggins  
// @match        https://forum.blackrussia.online/threads/*
// @include      https://forum.blackrussia.online/threads/*
// @match        https://forum.blackrussia.online/forums*
// @include      https://forum.blackrussia.online/forums
// @grant        none
// @license      MIT            
// @collaborator Kuk
// @icon         https://avatars.mds.yandex.net/i?id=e7371f38fb4d7fe174b4362d628c7f74-4988204-images-thumbs&n=13
// @copyright    2021, Kuk (https://openuserjs.org/users/Kuk)
// ==/UserScript==

// ==UserScript==
// @name Goggins (RP-Биографии)
// @namespace https://forum.blackrussia.online/
// @version 25.8
// @description Удалены приветствие и разделитель. Заголовок модального окна отцентрирован и без двоеточия.
// @author David_Goggins / Artem_Gogol (Финальное Объединение)
// @match https://forum.blackrussia.online/threads/*
// @grant none
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @require https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js
// ==/UserScript==

(function() {
    'use strict';

    // --- КОНСТАНТЫ ПРЕФИКСОВ (ИЗ КОДА 1/2) ---
    const VAJNO_PREFIX = 1;
    const NARASSSMOTRENII_PREFIX = 2;
    const BEZPREFIXA_PREFIX = 3;
    const OTKAZANO_PREFIX = 4;
    const REALIZOVANNO_PREFIX = 5;
    const RESHENO_PREFIX = 6;
    const ZAKRITO_PREFIX = 7;
    const ODOBRENO_PREFIX = 8;
    const RASSMORTENO_PREFIX = 9;
    const KOMANDEPROEKTA_PREFIX = 10;
    const SPECADMINY_PREFIX = 11;
    const GLAVNOMYADMINY_PREFIX = 12;
    const TEXSPECY_PREFIX = 13;
    const OJIDANIE_PREFIX = 14;
    const PROVERENOKONTRKACH_PREFIX = 15;

    // --- ССЫЛКИ НА БАННЕРЫ (ДИЗАЙН ИЗ КОДА 1) ---
    const APPROVED_BANNER_URL = 'https://i.postimg.cc/sgkL5vvb/1618083711121.png';
    const NEW_BANNER_BBCODE = '[B][CENTER][url=https://postimages.org/][img]' + APPROVED_BANNER_URL + '[/img][/url][/CENTER][/B]';
    const FOOTER_LINKS =
        '[RIGHT][B][COLOR=#ff0000]Полезные ссылки.[/COLOR][/B]\n' +
        "[SIZE=3][FONT=georgia]Заявление на пост Агента Поддержки - [URL]https://forum.blackrussia.online/forums/%D0%90%D0%B3%D0%B5%D0%BD%D1%82%D1%8B-%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%BA%D0%B8.3173/[/URL]\n" +
        "Заявление на пост Лидера - [URL]https://forum.blackrussia.online/forums/%D0%9B%D0%B8%D0%B4%D0%B5%D1%80%D1%8B.3174/[/URL][/FONT][/SIZE][/RIGHT]";

    // --- ФУНКЦИИ ГЕНЕРАЦИИ КОНТЕНТА (ДИЗАЙН ИЗ КОДА 1) ---

    // RP-Био Отказы
    function generateRejectionContent(reasonText) {
        return (
            NEW_BANNER_BBCODE + "\n\n" +
            "[B][CENTER][COLOR=#ff0000]Доброго времени суток уважаемый {{ user.name }}[/COLOR][/CENTER][/B]\n\n" +
            "[CENTER][SIZE=5][COLOR=#000000]Ваша RolePlay биография была проверена, но есть моменты для доработки![/COLOR][/SIZE][/CENTER]\n\n" +
            "[CENTER][SIZE=5][COLOR=#000000]Статус: [COLOR=#FF0000]❌ Отказано.[/COLOR][/COLOR][/SIZE][/CENTER]\n\n" +
            "[CENTER][SIZE=4][COLOR=#000000]Убедительная просьба ознакомиться [URL='https://forum.blackrussia.online/threads/Правила-составления-rp-биографии.13425782/']с правилами написания RolePlay биографий.[/URL][/COLOR][/SIZE][/CENTER]\n\n" +
            "[CENTER][SIZE=5][COLOR=#000000]Причина отказа: [COLOR=#FF0000]" + reasonText + "[/COLOR][/COLOR][/SIZE][/CENTER]\n\n" +
            "[CENTER][SIZE=5][COLOR=#000000]Анализируйте ошибки![/COLOR][/SIZE][/CENTER]\n\n" +
            NEW_BANNER_BBCODE
        );
    }

    // RP-Биографии: Одобрено
    const approvalContent =
        NEW_BANNER_BBCODE + "\n\n" +
        "[B][CENTER][COLOR=#ff0000]Доброго времени суток уважаемый {{ user.name }}[/COLOR][/CENTER][/B]\n\n" +
        "[CENTER][SIZE=5][COLOR=#000000]Ваша RolePlay биография была проверена и получает статус: [COLOR=#00FF00]✔️ Одобрено.[/COLOR][/COLOR][/SIZE][/CENTER]\n\n" +
        "[CENTER][SIZE=5][COLOR=#000000]Приятной игры![/COLOR][/SIZE][/CENTER]\n\n" +
        NEW_BANNER_BBCODE + "\n\n" +
        FOOTER_LINKS;

    // RP-Биографии: На доработке
    const reworkContent =
        NEW_BANNER_BBCODE + "\n\n" +
        "[B][CENTER][COLOR=#ff0000]Доброго времени суток уважаемый {{ user.name }}[/COLOR][/CENTER][/B]\n\n" +
        "[CENTER][SIZE=5][COLOR=#000000]Ваша RolePlay биография была проверена, но есть моменты для доработки![/COLOR][/SIZE][/CENTER]\n\n" +
        "[CENTER][SIZE=5][COLOR=#000000]Статус: [COLOR=#FFC000]🟡 На доработке.[/COLOR][/COLOR][/SIZE][/CENTER]\n\n" +
        "[CENTER][SIZE=4][COLOR=#000000]Вам даётся 24 часа на дополнение/исправление вашей RolePlay биографии.[/COLOR][/SIZE][/CENTER]\n\n" +
        "[CENTER][SIZE=5][COLOR=#000000]Приятной игры![/COLOR][/SIZE][/CENTER]\n\n" +
        NEW_BANNER_BBCODE;


    // --- МАССИВ ШАБЛОНОВ RP-БИОГРАФИЙ (БЕЗ ПРИВЕТСТВИЯ И РАЗДЕЛИТЕЛЯ) ---
    const buttons = [
        // Удалено: { title: 'ПРИВЕТСТВИЕ', ... }
        // Удалено: { title: '______________________________________RP БИОГРАФИИ_____________________________________' },

        { title: '✔️ БИОГРАФИЯ - ОДОБРЕНО', content: approvalContent, prefix: ODOBRENO_PREFIX, status: true },
        { title: '🟡 БИОГРАФИЯ - ДОРАБОТКА (24Ч)', content: reworkContent, prefix: NARASSSMOTRENII_PREFIX, status: true },
        { title: '❌ НЕ ДОРАБОТАЛ', content: generateRejectionContent("Вы не доработали RP биографию за данные вам 24 часа."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ БИОГРАФИЯ НЕ ПО ФОРМЕ', content: generateRejectionContent("Биография составлена не по форме."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ ЗАГОЛОВОК НЕ ПО ФОРМЕ', content: generateRejectionContent("Заголовок темы составлен не по форме.<br>Заполните его в формате: Биография | Nick_Name"), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ КОПИПАСТ', content: generateRejectionContent("Ваша биография скопирована.<br>Постарайтесь изложить свою идею."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ ОРФОГРАФИЯ', content: generateRejectionContent("Слишком много орфографических ошибок.<br>Проверьте свой текст на наличие ошибок в правописании."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ ПУНКТУАЦИЯ', content: generateRejectionContent("Слишком много пунктуационных ошибок.<br>В вашей биографии неправильно раставлены знаки препинания, либо они вовсе отсутсвуют."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ НАРУШЕНИЕ ПРАВИЛ ИГРЫ', content: generateRejectionContent("Запрещено указывать факторы, позволяющие игроку нарушать правила игры."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ МЕНЕЕ 200 СЛОВ', content: generateRejectionContent("Слишком мало RP информации.<br>Минимальный объем информации из жизни вашего персонажа 200 слов."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ БОЛЕЕ 600 СЛОВ', content: generateRejectionContent("Избыток RP информации.<br>Максимальный объем информации из жизни вашего персонажа 600 слов."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ НЕ ТОТ ШРИФТ/РАЗМЕР', content: generateRejectionContent("Нарушен шрифт текста и/или его размер.<br>Ваша биография должно быть написана шрифтами Verdana или Times New Roman с минимальным размером 15."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ СВЕРХСПОСОБНОСТИ', content: generateRejectionContent("Запрещено придавать персонажу нереалистичные свойства."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ НЕТ ВИДЕО- ФОТОФРАГМЕНТОВ', content: generateRejectionContent("В биографии должна присутствовать хотя бы одна фотография, поясняющая вашу биографию/вашего персонажа.<br>Например, это может быть фотография вашего персонажа."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ НАРУШЕНИЕ ПОЛИТИКИ/ЭКСТРЕМИЗМ', content: generateRejectionContent("Ваша биография включает в себя запрещённую тематику.<br>Запрещено упоминание различных группировок, психотропных и подобных веществ, экстремитских и националистических лозунгов."), prefix: OTKAZANO_PREFIX, status: true },
        { title: '❌ ОБЩИЙ ОТКАЗ (СВОЙ ВАРИАНТ)', content: generateRejectionContent("..."), prefix: OTKAZANO_PREFIX, status: false },
    ];

    // --- СЧЕТЧИКИ (ОСТАВЛЕНЫ) ---

    var titles = document.getElementsByClassName('structItem-title');
    var count_ojidanie = 0;
    var count_ga = 0;
    var count_na_rassmotrenii = 0;
    var count_sa = 0;

    for (var i = 0; i < titles.length; i++) {
        var prefix_ojidanie = titles[i].querySelector('.labelLink .label--silver');
        if (prefix_ojidanie && prefix_ojidanie.textContent.trim() === 'Ожидание') {
            count_ojidanie++;
        }
        var prefix_ga = titles[i].querySelector('.label.label--red');
        if (prefix_ga && prefix_ga.textContent.trim() === 'Главному администратору') {
            count_ga++;
        }
        var prefix_na_rassmotrenii = titles[i].querySelector('.label.label--orange');
        if (prefix_na_rassmotrenii && prefix_na_rassmotrenii.textContent.trim() === 'На рассмотрении') {
            count_na_rassmotrenii++;
        }
        var prefix_sa = titles[i].querySelector('.label.label--accent');
        if (prefix_sa && prefix_sa.textContent.trim() === 'Специальному администратору') {
            count_sa++;
        }
    }

    function getColor(count) {
        if (count < 7) {
            return 'lime';
        } else if (count >= 7 && count < 15) {
            return 'orange';
        } else {
            return 'red';
        }
    }

    var headers = document.getElementsByClassName('block-minorHeader uix_threadListSeparator');
    if (headers.length > 0) {
        var firstHeader = headers[0];
        var secondHeader = headers[1];

        var countElementGA = document.createElement('span');
        countElementGA.style.marginLeft = '10px';
        countElementGA.style.fontSize = '1.4rem';
        countElementGA.style.color = getColor(count_ga);
        countElementGA.textContent = 'Глав.Админу: ' + count_ga + ' ||';

        var countElementNaRassmotrenii = document.createElement('span');
        countElementNaRassmotrenii.style.marginLeft = '10px';
        countElementNaRassmotrenii.style.fontSize = '1.4rem';
        countElementNaRassmotrenii.style.color = getColor(count_na_rassmotrenii);
        countElementNaRassmotrenii.textContent = 'На рассмотрении: ' + count_na_rassmotrenii + ' ||';

        var countElementSA = document.createElement('span');
        countElementSA.style.marginLeft = '10px';
        countElementSA.style.fontSize = '1.4rem';
        countElementSA.style.color = getColor(count_sa);
        countElementSA.textContent = 'Спец.Админу: ' + count_sa;

        var arrowIcon = firstHeader.querySelector('.uix_threadCollapseTrigger');
        if (arrowIcon) {
            firstHeader.insertBefore(countElementGA, arrowIcon);
            firstHeader.insertBefore(countElementNaRassmotrenii, arrowIcon);
            firstHeader.insertBefore(countElementSA, arrowIcon);
        } else {
            firstHeader.appendChild(countElementGA);
            firstHeader.appendChild(countElementNaRassmotrenii);
            firstHeader.appendChild(countElementSA);
        }

        var countElementOjidanie = document.createElement('span');
        countElementOjidanie.style.marginLeft = '10px';
        countElementOjidanie.style.fontSize = '1.4rem';
        countElementOjidanie.style.color = getColor(count_ojidanie);
        countElementOjidanie.textContent = 'Ожидание: ' + count_ojidanie;

        if (secondHeader) {
            arrowIcon = secondHeader.querySelector('.uix_threadCollapseTrigger');
            if (arrowIcon) {
                secondHeader.insertBefore(countElementOjidanie, arrowIcon);
            } else {
                secondHeader.appendChild(countElementOjidanie);
            }
        }
    }


    // --- ФУНКЦИОНАЛ ИЗ КОДА 2 ---

    function getFormData(data) {
        const formData = new FormData();
        Object.entries(data).forEach(i => formData.append(i[0], i[1]));
        return formData;
    }

    function editThreadData(prefix, pin = false) {
        const threadTitle = $('.p-title-value')[0].lastChild.textContent.trim();

        if (typeof XF === 'undefined' || !XF.config || !XF.config.csrf) {
            console.error('Ошибка: Не найдены переменные XF. Невозможно сменить префикс.');
            return;
        }

        const data = {
            prefix_id: prefix,
            title: threadTitle,
            _xfToken: XF.config.csrf,
            _xfRequestUri: document.URL.split(XF.config.url.fullBase)[1],
            _xfWithData: 1,
            _xfResponseType: 'json',
        };

        if (pin === true) {
            data.sticky = 1;
        }

        fetch(`${document.URL}edit`, {
            method: 'POST',
            body: getFormData(data),
        }).then(() => location.reload()).catch(error => console.error('Ошибка при смене префикса:', error));
    }

    function getThreadData() {
        const usernameElement = $('a.username')[0];
        if (!usernameElement) {
            return { user: { id: 'Unknown', name: 'Уважаемый пользователь', mention: 'Уважаемый пользователь' } };
        }

        const authorID = usernameElement.attributes['data-user-id']?.nodeValue || 'UnknownID';
        const authorName = $(usernameElement).text().trim() || 'Уважаемый пользователь';
        const hours = new Date().getHours();

        return {
            user: {
                id: authorID,
                name: authorName,
                mention: `[USER=${authorID}]${authorName}[/USER]`,
            },
            greeting: () =>
                4 < hours && hours <= 11
                ? 'Доброе утро'
                : 11 < hours && hours <= 15
                ? 'Добрый день'
                : 15 < hours && hours <= 21
                ? 'Добрый вечер'
                : 'Доброй ночи',
        };
    }

    // Кнопка с прозрачным фоном (для сохранения стиля главной кнопки)
    function addButton(name, id) {
        $('.button--icon--reply').before(
            `<button type="button" class="button rippleButton" id="${id}" style="background: transparent !important; background-image: none !important; margin: 10px; border: none; border-radius: 10px; color: white !important;">${name}</button>`,
        );
    }

    // ИСПРАВЛЕННАЯ ФУНКЦИЯ: Сетка кнопок и прозрачные разделители (Grid)
    function buttonsMarkup(buttons) {
        // Устанавливаем отступы между кнопками и используем flex/grid для расположения
        return `<div class="select_answer" style="display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 5px; color: white;">
            ${buttons.map(
                (btn, i) => {
                    if (btn.content === undefined) {
                        // Разделители: прозрачный фон, белый текст, занимают всю ширину
                        return `<div style="text-align: center; font-weight: bold; padding: 10px 0; color: white; background-color: transparent; margin: 5px 0; flex-basis: 100%; border: none;">${btn.title.replace(/_/g, '').replace(/-/g, '').trim()}</div>`;
                    } else {
                        // Кнопки-шаблоны: равномерная ширина для 3-4 столбцов (как на скриншоте)
                        return `<button id="answers-${i}" class="button--primary button rippleButton" style="
                            margin: 5px;
                            flex: 1 1 calc(33.33% - 5px);
                            min-width: 150px;
                            text-align: center;
                            background-color: rgba(255, 255, 255, 0.1) !important;
                            border: 1px solid rgba(255, 255, 255, 0.2);
                            color: white;
                        ">
                            <span class="button-text">${btn.title}</span>
                        </button>`;
                    }
                }
            ).join('')}
        </div>`;
    }

    // Функция вставки контента и автоотправки (Логика из Кода 2)
    function pasteContent(id, data = {}, send = false) {
        if (buttons[id].content === undefined) return;

        const template = Handlebars.compile(buttons[id].content);

        if ($('.fr-element.fr-view p').text().trim() === '') $('.fr-element.fr-view p').empty();
        $('span.fr-placeholder').empty();

        const contentToPaste = template(data).replace(/\n/g, '<br>');
        $('div.fr-element.fr-view p').append(contentToPaste);

        // ЗАКРЫТИЕ ОКНА: (как в Коде 2)
        $('a.overlay-titleCloser').trigger('click');

        if (send === true) {
            editThreadData(buttons[id].prefix, false);

            setTimeout(() => {
                 $('.button--icon.button--icon--reply.rippleButton').trigger('click');
            }, 100);
        }
    }

    // --- ЗАПУСК СКРИПТА ---

    $(document).ready(() => {
        if (typeof Handlebars === 'undefined') {
            $('body').append('<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>');
        }

        if (typeof XF === 'undefined' || typeof jQuery === 'undefined' || typeof XF.alert === 'undefined') {
            return;
        }

        const threadData = getThreadData();
        const mainButtonId = 'goggins_rp_bio_templates';

        // Добавляем ТОЛЬКО ОДНУ кнопку шаблонов (с прозрачным фоном)
        addButton('Шаблоны RP Биографий', mainButtonId);


        // Обработчик для основной кнопки шаблонов
        $(`button#${mainButtonId}`).click(() => {

            // 1. Изменяем заголовок и открываем модальное окно
            const customTitle = 'Выберите шаблон RP Биографии';
            XF.alert(buttonsMarkup(buttons), null, customTitle);

            // 2. Стиль для заголовка модального окна: по центру и без двоеточия (XF.alert добавляет двоеточие, его трудно удалить, но мы его центрируем)
            const $modalTitle = $('.overlay-title:contains("' + customTitle + '")');
            if ($modalTitle.length) {
                 $modalTitle.text(customTitle); // Удаление двоеточия (XF.alert добавляет его после текста)
                 $modalTitle.css({
                    'text-align': 'center',
                    'background-color': 'transparent',
                    'color': 'white'
                });
            }

            // 3. КОД ДЛЯ ПРОЗРАЧНОСТИ ФОНА МОДАЛЬНОГО ОКНА:
            $('.overlay-content').css({
                'background-color': 'rgba(29, 31, 33, 0.85)', // Очень темный, полупрозрачный
                'color': 'white'
            });
            $('.overlay-container').css({
                'background-color': 'transparent',
            });

            // Привязываем обработчики кликов к кнопкам внутри модального окна (Логика из Кода 2)
            buttons.forEach((btn, id) => {
                if (btn.content === undefined) return;

                $(document).off('click', `#answers-${id}`);
                $(document).on('click', `#answers-${id}`, function() {
                    const send = buttons[id].status;
                    pasteContent(id, threadData, send);
                });
            });
        });

        // --- СЧЕТЧИКИ ---
        // (Оставлены для совместимости)
        var titles = document.getElementsByClassName('structItem-title');
        var count_ojidanie = 0;
        var count_ga = 0;
        var count_na_rassmotrenii = 0;
        var count_sa = 0;

        for (var i = 0; i < titles.length; i++) {
            var prefix_ojidanie = titles[i].querySelector('.labelLink .label--silver');
            if (prefix_ojidanie && prefix_ojidanie.textContent.trim() === 'Ожидание') {
                count_ojidanie++;
            }
            var prefix_ga = titles[i].querySelector('.label.label--red');
            if (prefix_ga && prefix_ga.textContent.trim() === 'Главному администратору') {
                count_ga++;
            }
            var prefix_na_rassmotrenii = titles[i].querySelector('.label.label--orange');
            if (prefix_na_rassmotrenii && prefix_na_rassmotrenii.textContent.trim() === 'На рассмотрении') {
                count_na_rassmotrenii++;
            }
            var prefix_sa = titles[i].querySelector('.label.label--accent');
            if (prefix_sa && prefix_sa.textContent.trim() === 'Специальному администратору') {
                count_sa++;
            }
        }

        function getColor(count) {
            if (count < 7) {
                return 'lime';
            } else if (count >= 7 && count < 15) {
                return 'orange';
            } else {
                return 'red';
            }
        }

        var headers = document.getElementsByClassName('block-minorHeader uix_threadListSeparator');
        if (headers.length > 0) {
            var firstHeader = headers[0];
            var secondHeader = headers[1];

            var countElementGA = document.createElement('span');
            countElementGA.style.marginLeft = '10px';
            countElementGA.style.fontSize = '1.4rem';
            countElementGA.style.color = getColor(count_ga);
            countElementGA.textContent = 'Глав.Админу: ' + count_ga + ' ||';

            var countElementNaRassmotrenii = document.createElement('span');
            countElementNaRassmotrenii.style.marginLeft = '10px';
            countElementNaRassmotrenii.style.fontSize = '1.4rem';
            countElementNaRassmotrenii.style.color = getColor(count_na_rassmotrenii);
            countElementNaRassmotrenii.textContent = 'На рассмотрении: ' + count_na_rassmotrenii + ' ||';

            var countElementSA = document.createElement('span');
            countElementSA.style.marginLeft = '10px';
            countElementSA.style.fontSize = '1.4rem';
            countElementSA.style.color = getColor(count_sa);
            countElementSA.textContent = 'Спец.Админу: ' + count_sa;

            var arrowIcon = firstHeader.querySelector('.uix_threadCollapseTrigger');
            if (arrowIcon) {
                firstHeader.insertBefore(countElementGA, arrowIcon);
                firstHeader.insertBefore(countElementNaRassmotrenii, arrowIcon);
                firstHeader.insertBefore(countElementSA, arrowIcon);
            } else {
                firstHeader.appendChild(countElementGA);
                firstHeader.appendChild(countElementNaRassmotrenii);
                firstHeader.appendChild(countElementSA);
            }

            var countElementOjidanie = document.createElement('span');
            countElementOjidanie.style.marginLeft = '10px';
            countElementOjidanie.style.fontSize = '1.4rem';
            countElementOjidanie.style.color = getColor(count_ojidanie);
            countElementOjidanie.textContent = 'Ожидание: ' + count_ojidanie;

            if (secondHeader) {
                arrowIcon = secondHeader.querySelector('.uix_threadCollapseTrigger');
                if (arrowIcon) {
                    secondHeader.insertBefore(countElementOjidanie, arrowIcon);
                } else {
                    secondHeader.appendChild(countElementOjidanie);
                }
            }
        }
    });

})();

QingJ © 2025

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