Перейти к содержимому
Zone of Games Forum

Рекомендованные сообщения

5 минут назад, adl сказал:

Ничего менять не надо. Была новость- утекли только номера телефонов, без какой-либо привязки к почте\логину. Не наводите панику.

 

Утёкшие данные не позволяют определить аккаунт, пароль, платёжную информацию и другие личные данные пользователей Steam по номеру телефона

https://store.steampowered.com/news/app/593110/view/533224478739530145

Человеку написавшему в поддержку(с его слов),посоветовали сменить пароли,и сказали что  возможно была попытка взлома.

Поделиться сообщением


Ссылка на сообщение
1 час назад, edifiei сказал:

Приветствую,офтоп,

Вы унас спец по макросам в стим,не ли макроса который позволит убрать все игры из блеклиста?)

  1. Заходим на страницу: https://store.steampowered.com/account/notinterested/
  2. Устанавливаем Tamper Monkey (если ещё нет). 
  3. Ставим скрипт:
Скрытый текст

// ==UserScript==
// @name         SIGA
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Постепенное удаление всех игр из списка "Не интересно" в Steam с возможностью паузы и отслеживанием прогресса.
// @author       0wn3df1x
// @match        https://store.steampowered.com/account/notinterested*
// @icon         https://store.steampowered.com/favicon.ico
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @connect      store.steampowered.com
// ==/UserScript==

(function() {
    'use strict';

    const $ = window.jQuery;

    let state = {
        allIgnoredApps: [],
        removedInSessionAppIds: [],
        currentIndex: 0,
        totalToRemove: 0,
        isPaused: false,
        isCleaning: false,
        sessionID: null,
        removeInterval: 3500,
        currentAppRetries: 0,
        uiContainer: null,
        mainActionButton: null,
        getRemovedButton: null,
        progressBarFill: null,
        progressTextElement: null,
        etaTextElement: null,
        statusMessageElement: null,
        copyFeedbackElement: null
    };

    const RATE_LIMIT_WAIT_MS = 3 * 60 * 1000;
    const MAX_APP_RETRIES = 3;

    let cssStyles = `
        #steamIgnoredCleanerUI {
            background: #151f2c;
            padding: 20px;
            margin: 25px 0;
            border-radius: 5px;
            color: #c6d4df;
            border: 1px solid #2a3f5a;
            box-shadow: 0 0 15px rgba(0,0,0,0.3);
            font-family: "Motiva Sans", Arial, Helvetica, sans-serif;
        }
        .cleaner-header h2 {
            color: #67c1f5;
            margin-top: 0;
            margin-bottom: 15px;
            font-weight: 500;
            border-bottom: 1px solid #2a3f5a;
            padding-bottom: 10px;
        }
        .cleaner-main-layout {
            display: flex;
            gap: 20px;
        }
        .cleaner-controls {
            flex-grow: 1;
        }
        .cleaner-side-panel {
            flex-basis: 200px;
            flex-shrink: 0;
        }
        .steam-cleaner-button {
            background: linear-gradient(to bottom, #799905 5%, #536904 95%);
            border: 1px solid #1b2838;
            padding: 8px 15px;
            color: #fff;
            cursor: pointer;
            border-radius: 3px;
            margin-bottom: 15px;
            font-size: 14px;
            transition: background 0.2s ease;
        }
        .steam-cleaner-button:hover {
            background: linear-gradient(to bottom, #8ab807 5%, #647d05 95%);
        }
        .steam-cleaner-button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
        .steam-cleaner-button.secondary {
            background: linear-gradient(to bottom, #4b6077 5%, #3a4b5d 95%);
        }
        .steam-cleaner-button.secondary:hover {
            background: linear-gradient(to bottom, #5c738e 5%, #45586b 95%);
        }
        .progress-section {
            margin-top: 15px;
        }
        .progress-bar-container {
            width: 100%;
            background: #101822;
            height: 22px;
            border-radius: 3px;
            overflow: hidden;
            border: 1px solid #2a3f5a;
        }
        .progress-bar-fill {
            height: 100%;
            background: #67c1f5;
            width: 0%;
            transition: width 0.3s ease-out;
            text-align: center;
            line-height: 22px;
            color: #fff;
            font-size: 12px;
        }
        .progress-info, .status-output-message {
            color: #a8b2ba;
            margin: 8px 0;
            font-size: 13px;
        }
        .status-output-message.error {
            color: #ff6b6b;
            font-weight: bold;
        }
        .status-output-message.success {
            color: #799905;
            font-weight: bold;
        }
        .copy-feedback-message {
            display: block;
            font-size: 12px;
            color: #67c1f5;
            margin-top: 5px;
            min-height: 15px;
        }
        .initial-info {
            color: #a8b2ba;
            margin-bottom: 15px;
            font-size: 14px;
        }
    `;
    GM_addStyle(cssStyles.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, ''));


    function createUI() {
        const container = $(`
            <div id="steamIgnoredCleanerUI">
                <div class="cleaner-header">
                    <h2>Steam Очиститель Списка "Не интересно"</h2>
                </div>
                <div class="cleaner-main-layout">
                    <div class="cleaner-controls">
                        <div class="initial-info">
                            Этот скрипт поможет вам удалить <b>ВСЕ</b> игры из вашего списка "Не интересно" в Steam.
                            Процесс происходит постепенно, чтобы избежать блокировок со стороны Steam.
                        </div>
                        <button id="cleanerMainActionButton" class="steam-cleaner-button">Начать очистку</button>
                        <div class="progress-section">
                            <div class="progress-bar-container">
                                <div id="cleanerProgressBarFill" class="progress-bar-fill">0%</div>
                            </div>
                            <div id="cleanerProgressText" class="progress-info">Ожидание начала...</div>
                            <div id="cleanerEtaText" class="progress-info">ETA: N/A</div>
                        </div>
                        <div id="cleanerStatusMessage" class="status-output-message"></div>
                    </div>
                    <div class="cleaner-side-panel">
                        <button id="cleanerGetRemovedButton" class="steam-cleaner-button secondary" disabled>
                            Копировать список удаленных
                        </button>
                        <span id="cleanerCopyFeedback" class="copy-feedback-message"></span>
                    </div>
                </div>
            </div>
        `).insertAfter('.pageheader');

        state.uiContainer = container;
        state.mainActionButton = $('#cleanerMainActionButton');
        state.getRemovedButton = $('#cleanerGetRemovedButton');
        state.progressBarFill = $('#cleanerProgressBarFill');
        state.progressTextElement = $('#cleanerProgressText');
        state.etaTextElement = $('#cleanerEtaText');
        state.statusMessageElement = $('#cleanerStatusMessage');
        state.copyFeedbackElement = $('#cleanerCopyFeedback');

        state.mainActionButton.click(handleMainAction);
        state.getRemovedButton.click(copyRemovedListToClipboard);
    }

    function updateStatus(message, type = 'info') {
        state.statusMessageElement.text(message)
            .removeClass('error success')
            .addClass(type === 'error' ? 'error' : (type === 'success' ? 'success' : ''));
        console.log((type === 'error' ? 'X ' : (type === 'success' ? 'V ' : 'i ')) + message);
    }

    function updateProgressDisplay() {
        if (state.totalToRemove === 0 && !state.isCleaning) {
            state.progressBarFill.css('width', '0%').text('0%');
            state.progressTextElement.text('Ожидание начала...');
            state.etaTextElement.text('ETA: N/A');
            return;
        }
         if (state.totalToRemove === 0 && state.isCleaning) {
            state.progressBarFill.css('width', '0%').text('0%');
            state.progressTextElement.text('Нет игр для удаления.');
            state.etaTextElement.text('ETA: N/A');
            return;
        }


        const processedCount = state.currentIndex;
        const percent = state.totalToRemove > 0 ? (processedCount / state.totalToRemove) * 100 : 0;
        state.progressBarFill.css('width', `${percent.toFixed(1)}%`).text(`${percent.toFixed(0)}%`);

        if (state.isCleaning && !state.isPaused && processedCount < state.totalToRemove) {
            state.progressTextElement.text(`Обработано: ${processedCount} из ${state.totalToRemove}`);
            const etaSeconds = (state.totalToRemove - processedCount) * (state.removeInterval / 1000);
            state.etaTextElement.text(`ETA: ${formatTime(etaSeconds)}`);
        } else if (processedCount === state.totalToRemove && state.totalToRemove > 0) {
             state.progressTextElement.text(`Завершено: ${processedCount} из ${state.totalToRemove}`);
             state.etaTextElement.text('Готово!');
        } else if (state.isPaused) {
            state.progressTextElement.text(`Пауза. Обработано: ${processedCount} из ${state.totalToRemove}`);
             const etaSecondsPaused = (state.totalToRemove - processedCount) * (state.removeInterval / 1000);
            state.etaTextElement.text(`ETA (на паузе): ${formatTime(etaSecondsPaused)}`);
        }
    }

    function formatTime(totalSeconds) {
        if (isNaN(totalSeconds) || totalSeconds < 0) return 'N/A';
        const hours = Math.floor(totalSeconds / 3600);
        const minutes = Math.floor((totalSeconds % 3600) / 60);
        const seconds = Math.floor(totalSeconds % 60);
        let timeString = '';
        if (hours > 0) timeString += `${hours `;
        if (minutes > 0 || hours > 0) timeString += `${minutes `;
        timeString += `${seconds}с`;
        return timeString.trim() || '0с';
    }

    async function fetchIgnoredApps() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://store.steampowered.com/dynamicstore/userdata/',
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.rgIgnoredApps) {
                            resolve(Object.keys(data.rgIgnoredApps).map(Number));
                        } else {
                            resolve([]);
                        }
                    } catch (e) {
                        console.error("X Ошибка парсинга userdata:", e);
                        reject("Не удалось разобрать данные пользователя.");
                    }
                },
                onerror: function(err) {
                    console.error("X Ошибка загрузки userdata:", err);
                    reject("Ошибка сети при получении списка игнорируемых игр.");
                }
            });
        });
    }

    function removeNextApp() {
        if (state.isPaused || !state.isCleaning) {
            if (state.isPaused) updateStatus('Процесс на паузе.', 'info');
            return;
        }

        if (state.currentIndex >= state.totalToRemove) {
            updateStatus('* Очистка завершена! Все игры из списка обработаны.', 'success');
            state.mainActionButton.text('Начать очистку').prop('disabled', false);
            state.getRemovedButton.prop('disabled', state.removedInSessionAppIds.length === 0);
            state.isCleaning = false;
            updateProgressDisplay();
            return;
        }

        const appId = state.allIgnoredApps[state.currentIndex];
        updateStatus(`Удаление appID: ${appId} (${state.currentIndex + 1} из ${state.totalToRemove})...`);
        updateProgressDisplay();

        $J.post('https://store.steampowered.com/recommended/ignorerecommendation/', {
            sessionid: state.sessionID,
            appid: appId,
            snr: '1_account_notinterested_',
            remove: 1
        })
        .done(function(response) {
            state.removedInSessionAppIds.push(appId);
            state.getRemovedButton.prop('disabled', false);

            $J('#ignored_app_' + appId).fadeOut(function() { $(this).remove(); });
            if (typeof GDynamicStore !== 'undefined' && GDynamicStore.InvalidateCache) {
                GDynamicStore.InvalidateCache();
            }

            state.currentIndex++;
            state.currentAppRetries = 0;
            setTimeout(removeNextApp, state.removeInterval);
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
            if (jqXHR.status === 429) {
                updateStatus(`! Ошибка 429 (Too Many Requests) для appID: ${appId}. Ожидание ${formatTime(RATE_LIMIT_WAIT_MS / 1000)}...`, 'error');
                setTimeout(removeNextApp, RATE_LIMIT_WAIT_MS);
            } else {
                state.currentAppRetries++;
                if (state.currentAppRetries < MAX_APP_RETRIES) {
                    const retryDelay = state.removeInterval * (state.currentAppRetries + 1);
                    updateStatus(`X Ошибка удаления ${appId} (попытка ${state.currentAppRetries}/${MAX_APP_RETRIES}): ${jqXHR.statusText || errorThrown}. Повтор через ${formatTime(retryDelay/1000)}.`, 'error');
                    setTimeout(removeNextApp, retryDelay);
                } else {
                    updateStatus(`X Пропуск appID: ${appId} после ${MAX_APP_RETRIES} неудачных попыток. Ошибка: ${jqXHR.statusText || errorThrown}`, 'error');
                    state.currentIndex++;
                    state.currentAppRetries = 0;
                    setTimeout(removeNextApp, state.removeInterval);
                }
            }
        });
    }

    async function handleMainAction() {
        if (state.isCleaning && !state.isPaused) {
            state.isPaused = true;
            state.mainActionButton.text('Продолжить очистку');
            updateStatus('Очистка приостановлена.', 'info');
            updateProgressDisplay();
        } else if (state.isCleaning && state.isPaused) {
            state.isPaused = false;
            state.mainActionButton.text('Пауза');
            updateStatus('Возобновление очистки...', 'info');
            removeNextApp();
        } else {
            state.mainActionButton.prop('disabled', true).text('Загрузка списка...');
            updateStatus('Получение списка игнорируемых игр...', 'info');
            state.isCleaning = true;
            state.isPaused = false;

            try {
                state.allIgnoredApps = await fetchIgnoredApps();
                state.totalToRemove = state.allIgnoredApps.length;
                state.currentIndex = 0;
                state.removedInSessionAppIds = [];
                state.currentAppRetries = 0;

                if (state.totalToRemove === 0) {
                    updateStatus('V В вашем списке "Не интересно" нет игр для удаления.', 'success');
                    state.mainActionButton.prop('disabled', false).text('Начать очистку');
                    state.isCleaning = false;
                    updateProgressDisplay();
                    return;
                }

                updateStatus(`Найдено ${state.totalToRemove} игр. Начинаем очистку...`, 'info');
                state.mainActionButton.prop('disabled', false).text('Пауза');
                state.getRemovedButton.prop('disabled', true);
                state.copyFeedbackElement.text('');
                updateProgressDisplay();
                removeNextApp();

            } catch (errorMsg) {
                updateStatus(`X Ошибка: ${errorMsg}`, 'error');
                state.mainActionButton.prop('disabled', false).text('Начать очистку');
                state.isCleaning = false;
            }
        }
    }

    function copyRemovedListToClipboard() {
        if (state.removedInSessionAppIds.length === 0) {
            state.copyFeedbackElement.text('Список удаленных пуст.');
            setTimeout(() => state.copyFeedbackElement.text(''), 3000);
            return;
        }

        const listString = state.removedInSessionAppIds.join('\n');
        navigator.clipboard.writeText(listString).then(() => {
            state.copyFeedbackElement.text(`Скопировано ${state.removedInSessionAppIds.length} AppID!`);
            setTimeout(() => state.copyFeedbackElement.text(''), 3000);
        }).catch(err => {
            state.copyFeedbackElement.text('X Ошибка копирования.');
            console.error('X Ошибка копирования в буфер обмена:', err);
            setTimeout(() => state.copyFeedbackElement.text(''), 3000);
        });
    }

    $(function() {
        if (typeof g_sessionID === 'undefined' || g_sessionID === null) {
            const pageContent = $('#account_notinterested_area .pagecontent');
            if (pageContent.length) {
                 pageContent.first().prepend('<div style="color: red; font-weight: bold; padding: 10px; background: #330000; border: 1px solid red; margin-bottom:15px;">Steam Session ID не найден. Убедитесь, что вы авторизованы в Steam. Скрипт не будет работать.</div>');
            } else {
                alert('Steam Session ID не найден. Убедитесь, что вы авторизованы в Steam. Скрипт не будет работать.');
            }
            console.error("X Steam Ignored Apps Cleaner: g_sessionID is not available.");
            return;
        }
        state.sessionID = g_sessionID;

        if (typeof $J === 'undefined') {
             window.$J = $;
             console.warn("i Steam Ignored Apps Cleaner: $J is not defined by Steam, using Tampermonkey's jQuery.");
        }

        createUI();
        updateStatus('Скрипт готов к работе. Нажмите "Начать очистку".', 'info');
        updateProgressDisplay();
    });

})();

 


Чем больше игр скрыто — тем дольше будет удалять. Как видно с моего скрина, для удаления моих 52916 скрытых понадобится ждать 51 час. Можно поставить на удаление и держать вкладку открытой пару часов. Затем продолжить в другой день. 

hUKjDyg.png

  • Спасибо (+1) 1

Поделиться сообщением


Ссылка на сообщение
2 минуты назад, 0wn3df1x сказал:
  1. Заходим на страницу: https://store.steampowered.com/account/notinterested/
  2. Устанавливаем Tamper Monkey (если ещё нет). 
  3. Ставим скрипт:
  Скрипт (Показать содержимое)


// ==UserScript==
// @name         SIGA
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Постепенное удаление всех игр из списка "Не интересно" в Steam с возможностью паузы и отслеживанием прогресса.
// @author       0wn3df1x
// @match        https://store.steampowered.com/account/notinterested*
// @icon         https://store.steampowered.com/favicon.ico
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @connect      store.steampowered.com
// ==/UserScript==

(function() {
    'use strict';

    const $ = window.jQuery;

    let state = {
        allIgnoredApps: [],
        removedInSessionAppIds: [],
        currentIndex: 0,
        totalToRemove: 0,
        isPaused: false,
        isCleaning: false,
        sessionID: null,
        removeInterval: 3500,
        currentAppRetries: 0,
        uiContainer: null,
        mainActionButton: null,
        getRemovedButton: null,
        progressBarFill: null,
        progressTextElement: null,
        etaTextElement: null,
        statusMessageElement: null,
        copyFeedbackElement: null
    };

    const RATE_LIMIT_WAIT_MS = 3 * 60 * 1000;
    const MAX_APP_RETRIES = 3;

    let cssStyles = `
        #steamIgnoredCleanerUI {
            background: #151f2c;
            padding: 20px;
            margin: 25px 0;
            border-radius: 5px;
            color: #c6d4df;
            border: 1px solid #2a3f5a;
            box-shadow: 0 0 15px rgba(0,0,0,0.3);
            font-family: "Motiva Sans", Arial, Helvetica, sans-serif;
        }
        .cleaner-header h2 {
            color: #67c1f5;
            margin-top: 0;
            margin-bottom: 15px;
            font-weight: 500;
            border-bottom: 1px solid #2a3f5a;
            padding-bottom: 10px;
        }
        .cleaner-main-layout {
            display: flex;
            gap: 20px;
        }
        .cleaner-controls {
            flex-grow: 1;
        }
        .cleaner-side-panel {
            flex-basis: 200px;
            flex-shrink: 0;
        }
        .steam-cleaner-button {
            background: linear-gradient(to bottom, #799905 5%, #536904 95%);
            border: 1px solid #1b2838;
            padding: 8px 15px;
            color: #fff;
            cursor: pointer;
            border-radius: 3px;
            margin-bottom: 15px;
            font-size: 14px;
            transition: background 0.2s ease;
        }
        .steam-cleaner-button:hover {
            background: linear-gradient(to bottom, #8ab807 5%, #647d05 95%);
        }
        .steam-cleaner-button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
        .steam-cleaner-button.secondary {
            background: linear-gradient(to bottom, #4b6077 5%, #3a4b5d 95%);
        }
        .steam-cleaner-button.secondary:hover {
            background: linear-gradient(to bottom, #5c738e 5%, #45586b 95%);
        }
        .progress-section {
            margin-top: 15px;
        }
        .progress-bar-container {
            width: 100%;
            background: #101822;
            height: 22px;
            border-radius: 3px;
            overflow: hidden;
            border: 1px solid #2a3f5a;
        }
        .progress-bar-fill {
            height: 100%;
            background: #67c1f5;
            width: 0%;
            transition: width 0.3s ease-out;
            text-align: center;
            line-height: 22px;
            color: #fff;
            font-size: 12px;
        }
        .progress-info, .status-output-message {
            color: #a8b2ba;
            margin: 8px 0;
            font-size: 13px;
        }
        .status-output-message.error {
            color: #ff6b6b;
            font-weight: bold;
        }
        .status-output-message.success {
            color: #799905;
            font-weight: bold;
        }
        .copy-feedback-message {
            display: block;
            font-size: 12px;
            color: #67c1f5;
            margin-top: 5px;
            min-height: 15px;
        }
        .initial-info {
            color: #a8b2ba;
            margin-bottom: 15px;
            font-size: 14px;
        }
    `;
    GM_addStyle(cssStyles.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, ''));


    function createUI() {
        const container = $(`
            <div id="steamIgnoredCleanerUI">
                <div class="cleaner-header">
                    <h2>Steam Очиститель Списка "Не интересно"</h2>
                </div>
                <div class="cleaner-main-layout">
                    <div class="cleaner-controls">
                        <div class="initial-info">
                            Этот скрипт поможет вам удалить <b>ВСЕ</b> игры из вашего списка "Не интересно" в Steam.
                            Процесс происходит постепенно, чтобы избежать блокировок со стороны Steam.
                        </div>
                        <button id="cleanerMainActionButton" class="steam-cleaner-button">Начать очистку</button>
                        <div class="progress-section">
                            <div class="progress-bar-container">
                                <div id="cleanerProgressBarFill" class="progress-bar-fill">0%</div>
                            </div>
                            <div id="cleanerProgressText" class="progress-info">Ожидание начала...</div>
                            <div id="cleanerEtaText" class="progress-info">ETA: N/A</div>
                        </div>
                        <div id="cleanerStatusMessage" class="status-output-message"></div>
                    </div>
                    <div class="cleaner-side-panel">
                        <button id="cleanerGetRemovedButton" class="steam-cleaner-button secondary" disabled>
                            Копировать список удаленных
                        </button>
                        <span id="cleanerCopyFeedback" class="copy-feedback-message"></span>
                    </div>
                </div>
            </div>
        `).insertAfter('.pageheader');

        state.uiContainer = container;
        state.mainActionButton = $('#cleanerMainActionButton');
        state.getRemovedButton = $('#cleanerGetRemovedButton');
        state.progressBarFill = $('#cleanerProgressBarFill');
        state.progressTextElement = $('#cleanerProgressText');
        state.etaTextElement = $('#cleanerEtaText');
        state.statusMessageElement = $('#cleanerStatusMessage');
        state.copyFeedbackElement = $('#cleanerCopyFeedback');

        state.mainActionButton.click(handleMainAction);
        state.getRemovedButton.click(copyRemovedListToClipboard);
    }

    function updateStatus(message, type = 'info') {
        state.statusMessageElement.text(message)
            .removeClass('error success')
            .addClass(type === 'error' ? 'error' : (type === 'success' ? 'success' : ''));
        console.log((type === 'error' ? 'X ' : (type === 'success' ? 'V ' : 'i ')) + message);
    }

    function updateProgressDisplay() {
        if (state.totalToRemove === 0 && !state.isCleaning) {
            state.progressBarFill.css('width', '0%').text('0%');
            state.progressTextElement.text('Ожидание начала...');
            state.etaTextElement.text('ETA: N/A');
            return;
        }
         if (state.totalToRemove === 0 && state.isCleaning) {
            state.progressBarFill.css('width', '0%').text('0%');
            state.progressTextElement.text('Нет игр для удаления.');
            state.etaTextElement.text('ETA: N/A');
            return;
        }


        const processedCount = state.currentIndex;
        const percent = state.totalToRemove > 0 ? (processedCount / state.totalToRemove) * 100 : 0;
        state.progressBarFill.css('width', `${percent.toFixed(1)}%`).text(`${percent.toFixed(0)}%`);

        if (state.isCleaning && !state.isPaused && processedCount < state.totalToRemove) {
            state.progressTextElement.text(`Обработано: ${processedCount} из ${state.totalToRemove}`);
            const etaSeconds = (state.totalToRemove - processedCount) * (state.removeInterval / 1000);
            state.etaTextElement.text(`ETA: ${formatTime(etaSeconds)}`);
        } else if (processedCount === state.totalToRemove && state.totalToRemove > 0) {
             state.progressTextElement.text(`Завершено: ${processedCount} из ${state.totalToRemove}`);
             state.etaTextElement.text('Готово!');
        } else if (state.isPaused) {
            state.progressTextElement.text(`Пауза. Обработано: ${processedCount} из ${state.totalToRemove}`);
             const etaSecondsPaused = (state.totalToRemove - processedCount) * (state.removeInterval / 1000);
            state.etaTextElement.text(`ETA (на паузе): ${formatTime(etaSecondsPaused)}`);
        }
    }

    function formatTime(totalSeconds) {
        if (isNaN(totalSeconds) || totalSeconds < 0) return 'N/A';
        const hours = Math.floor(totalSeconds / 3600);
        const minutes = Math.floor((totalSeconds % 3600) / 60);
        const seconds = Math.floor(totalSeconds % 60);
        let timeString = '';
        if (hours > 0) timeString += `${hours `;
        if (minutes > 0 || hours > 0) timeString += `${minutes `;
        timeString += `${seconds}с`;
        return timeString.trim() || '0с';
    }

    async function fetchIgnoredApps() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://store.steampowered.com/dynamicstore/userdata/',
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.rgIgnoredApps) {
                            resolve(Object.keys(data.rgIgnoredApps).map(Number));
                        } else {
                            resolve([]);
                        }
                    } catch (e) {
                        console.error("X Ошибка парсинга userdata:", e);
                        reject("Не удалось разобрать данные пользователя.");
                    }
                },
                onerror: function(err) {
                    console.error("X Ошибка загрузки userdata:", err);
                    reject("Ошибка сети при получении списка игнорируемых игр.");
                }
            });
        });
    }

    function removeNextApp() {
        if (state.isPaused || !state.isCleaning) {
            if (state.isPaused) updateStatus('Процесс на паузе.', 'info');
            return;
        }

        if (state.currentIndex >= state.totalToRemove) {
            updateStatus('* Очистка завершена! Все игры из списка обработаны.', 'success');
            state.mainActionButton.text('Начать очистку').prop('disabled', false);
            state.getRemovedButton.prop('disabled', state.removedInSessionAppIds.length === 0);
            state.isCleaning = false;
            updateProgressDisplay();
            return;
        }

        const appId = state.allIgnoredApps[state.currentIndex];
        updateStatus(`Удаление appID: ${appId} (${state.currentIndex + 1} из ${state.totalToRemove})...`);
        updateProgressDisplay();

        $J.post('https://store.steampowered.com/recommended/ignorerecommendation/', {
            sessionid: state.sessionID,
            appid: appId,
            snr: '1_account_notinterested_',
            remove: 1
        })
        .done(function(response) {
            state.removedInSessionAppIds.push(appId);
            state.getRemovedButton.prop('disabled', false);

            $J('#ignored_app_' + appId).fadeOut(function() { $(this).remove(); });
            if (typeof GDynamicStore !== 'undefined' && GDynamicStore.InvalidateCache) {
                GDynamicStore.InvalidateCache();
            }

            state.currentIndex++;
            state.currentAppRetries = 0;
            setTimeout(removeNextApp, state.removeInterval);
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
            if (jqXHR.status === 429) {
                updateStatus(`! Ошибка 429 (Too Many Requests) для appID: ${appId}. Ожидание ${formatTime(RATE_LIMIT_WAIT_MS / 1000)}...`, 'error');
                setTimeout(removeNextApp, RATE_LIMIT_WAIT_MS);
            } else {
                state.currentAppRetries++;
                if (state.currentAppRetries < MAX_APP_RETRIES) {
                    const retryDelay = state.removeInterval * (state.currentAppRetries + 1);
                    updateStatus(`X Ошибка удаления ${appId} (попытка ${state.currentAppRetries}/${MAX_APP_RETRIES}): ${jqXHR.statusText || errorThrown}. Повтор через ${formatTime(retryDelay/1000)}.`, 'error');
                    setTimeout(removeNextApp, retryDelay);
                } else {
                    updateStatus(`X Пропуск appID: ${appId} после ${MAX_APP_RETRIES} неудачных попыток. Ошибка: ${jqXHR.statusText || errorThrown}`, 'error');
                    state.currentIndex++;
                    state.currentAppRetries = 0;
                    setTimeout(removeNextApp, state.removeInterval);
                }
            }
        });
    }

    async function handleMainAction() {
        if (state.isCleaning && !state.isPaused) {
            state.isPaused = true;
            state.mainActionButton.text('Продолжить очистку');
            updateStatus('Очистка приостановлена.', 'info');
            updateProgressDisplay();
        } else if (state.isCleaning && state.isPaused) {
            state.isPaused = false;
            state.mainActionButton.text('Пауза');
            updateStatus('Возобновление очистки...', 'info');
            removeNextApp();
        } else {
            state.mainActionButton.prop('disabled', true).text('Загрузка списка...');
            updateStatus('Получение списка игнорируемых игр...', 'info');
            state.isCleaning = true;
            state.isPaused = false;

            try {
                state.allIgnoredApps = await fetchIgnoredApps();
                state.totalToRemove = state.allIgnoredApps.length;
                state.currentIndex = 0;
                state.removedInSessionAppIds = [];
                state.currentAppRetries = 0;

                if (state.totalToRemove === 0) {
                    updateStatus('V В вашем списке "Не интересно" нет игр для удаления.', 'success');
                    state.mainActionButton.prop('disabled', false).text('Начать очистку');
                    state.isCleaning = false;
                    updateProgressDisplay();
                    return;
                }

                updateStatus(`Найдено ${state.totalToRemove} игр. Начинаем очистку...`, 'info');
                state.mainActionButton.prop('disabled', false).text('Пауза');
                state.getRemovedButton.prop('disabled', true);
                state.copyFeedbackElement.text('');
                updateProgressDisplay();
                removeNextApp();

            } catch (errorMsg) {
                updateStatus(`X Ошибка: ${errorMsg}`, 'error');
                state.mainActionButton.prop('disabled', false).text('Начать очистку');
                state.isCleaning = false;
            }
        }
    }

    function copyRemovedListToClipboard() {
        if (state.removedInSessionAppIds.length === 0) {
            state.copyFeedbackElement.text('Список удаленных пуст.');
            setTimeout(() => state.copyFeedbackElement.text(''), 3000);
            return;
        }

        const listString = state.removedInSessionAppIds.join('\n');
        navigator.clipboard.writeText(listString).then(() => {
            state.copyFeedbackElement.text(`Скопировано ${state.removedInSessionAppIds.length} AppID!`);
            setTimeout(() => state.copyFeedbackElement.text(''), 3000);
        }).catch(err => {
            state.copyFeedbackElement.text('X Ошибка копирования.');
            console.error('X Ошибка копирования в буфер обмена:', err);
            setTimeout(() => state.copyFeedbackElement.text(''), 3000);
        });
    }

    $(function() {
        if (typeof g_sessionID === 'undefined' || g_sessionID === null) {
            const pageContent = $('#account_notinterested_area .pagecontent');
            if (pageContent.length) {
                 pageContent.first().prepend('<div style="color: red; font-weight: bold; padding: 10px; background: #330000; border: 1px solid red; margin-bottom:15px;">Steam Session ID не найден. Убедитесь, что вы авторизованы в Steam. Скрипт не будет работать.</div>');
            } else {
                alert('Steam Session ID не найден. Убедитесь, что вы авторизованы в Steam. Скрипт не будет работать.');
            }
            console.error("X Steam Ignored Apps Cleaner: g_sessionID is not available.");
            return;
        }
        state.sessionID = g_sessionID;

        if (typeof $J === 'undefined') {
             window.$J = $;
             console.warn("i Steam Ignored Apps Cleaner: $J is not defined by Steam, using Tampermonkey's jQuery.");
        }

        createUI();
        updateStatus('Скрипт готов к работе. Нажмите "Начать очистку".', 'info');
        updateProgressDisplay();
    });

})();

 


Чем больше игр скрыто — тем дольше будет удалять. Как видно с моего скрина, для удаления моих 52916 скрытых понадобится ждать 51 час. Можно поставить на удаление и держать вкладку открытой пару часов. Затем продолжить в другой день. 

hUKjDyg.png

Спасибо,а то ручками очень долго)

Поделиться сообщением


Ссылка на сообщение
12 минут назад, 0wn3df1x сказал:
  1. . Как видно с моего скрина, для удаления моих 52916 скрытых понадобится ждать 51 час.

     

Это у тебя столько сткрытых игр было?!:ohmy:  Это где ты столько хентая умудрился накопать?  В стиме столько нету вроде бы.

  • Хаха (+1) 2

Поделиться сообщением


Ссылка на сообщение
10 минут назад, piton4 сказал:

Это у тебя столько сткрытых игр было?!:ohmy:  Это где ты столько хентая умудрился накопать?  В стиме столько нету вроде бы.

Они у меня и сейчас есть. Я их не удалял, просто скрипт сделал по запросу.
Я использую скрытие для формирования более точного списка рекомендаций, а также для отслеживания новых будущих игр.

  • +1 1

Поделиться сообщением


Ссылка на сообщение
6 минут назад, 0wn3df1x сказал:

Они у меня и сейчас есть. Я их не удалял

Понимаю :wink:

Поделиться сообщением


Ссылка на сообщение

Заприметил тут в одной новости анонс шикарной пошаговой PSX-Style RPG Never’s End.

header.jpg?t=1748985272

Выглядят как по мне просто пушка-гонка. Русский из коробки. Занесу в хотелки.

  • Спасибо (+1) 2

Поделиться сообщением


Ссылка на сообщение
10 минут назад, lordik555 сказал:

Заприметил тут в одной новости анонс шикарной пошаговой PSX-Style RPG Never’s End.

блин побежал сразу в стим пробовать, думал демка есть, а ее к сожалению нет, а так да вроде годная вещь, тоже добавил.

Изменено пользователем \miroslav\
  • +1 1

Поделиться сообщением


Ссылка на сообщение
1 минуту назад, \miroslav\ сказал:

блин побежал сразу в стим пробовать, думал демка есть, а ее к сожалению нет, атак да вроде годная вещь, тоже добавил.

Мне вот стилистически очень такие нравятся, приглушенные пастельные тона — кайф. Напомнило кстати игру Eternal Eyes с PS1. Проходил последнюю в том году.

Поделиться сообщением


Ссылка на сообщение
1 минуту назад, lordik555 сказал:

Мне вот стилистически очень такие нравятся

А я с фф тактикс полюбил такой жанр игр, серия игр Дисгайа тоже нравится. 

Поделиться сообщением


Ссылка на сообщение
7 минут назад, \miroslav\ сказал:

А я с фф тактикс полюбил такой жанр игр, серия игр Дисгайа тоже нравится. 

https://store.steampowered.com/app/699170/Fell_Seal_Arbiters_Mark/

Вот взял,очень советовали. Говорили “настоящее” продолжение  ФФТ на выходные поиграю

 

Изменено пользователем edifiei
  • Лайк (+1) 1

Поделиться сообщением


Ссылка на сообщение
13 минут назад, edifiei сказал:

https://store.steampowered.com/app/699170/Fell_Seal_Arbiters_Mark/

Вот взял,очень советовали. Говорили “настоящее” продолжение  ФФТ на выходные поиграю

А вот эта игрушка (установочный пакет) у меня уже давно ждет своего часа только вот руки никак до нее не доходят.)

Поделиться сообщением


Ссылка на сообщение

Новелла чисто на часик с небольшим — Чел я просто хочу домой.

header.jpg?t=1745482318

Из интересного: Paint-стилистика, вайбовая музыка (конечно не уровня Anger Foot, но на уровне), 13 концовок, которые постепенно раскрывают всю картину происходящего.

Разработчики наши ребята.

Изменено пользователем lordik555
  • +1 1

Поделиться сообщением


Ссылка на сообщение

6-го июня выкатят демку Tormented Souls 2

  • Лайк (+1) 1

Поделиться сообщением


Ссылка на сообщение

К летней распродаже обновил SteamDB - Sales; Ultimate Enhancer.

Обновил скриншоты с примерами разных случаев применения.

Скрытый текст

Игры, доступные в Казахстане и недоступные в России, с русским текстовым переводом, скидки на которые появились не раньше 02.06.2025

BNZCExN.png

 


Игры, доступные в русском регионе, цена на которые ниже рекомендуемых региональных Valve цен, с русской озвучкой и ценой, которая соответствует прошлому историческому минимуму

hcu3kck.png

 


Интерфейс калькулятора желаемого

s53CiaE.png

Из самого последнего — добавил возможность посчитать, сколько вам будет стоить закупка всех игр из списка желаемого по полной цене и по самым низким известным ценам.

Также добавил возможность фильтровать игры по минимуму не только в абсолютных числах, но и в процентах, т.к. оригинально в SteamDB этот момент упущен. Там показываются только абсолютные исторические минимумы.

 


Объясняю, почему это важно, на примере гипотетической игры "Звездный Странник":

  1. Изначальная ситуация (до корректировки региональных цен):
    • Базовая цена игры "Звездный Странник" составляла 500 рублей.
    • Разработчик устраивал распродажу со скидкой 75%.
    • Таким образом, исторический минимум цены игры составлял 125 рублей (500 - 75% = 125). Этот минимум был зафиксирован в SteamDB.
  2. Корректировка региональных цен Valve:
    • Произошла корректировка региональных цен, и базовая стоимость многих игр была пересмотрена в сторону увеличения.
    • Новая базовая цена "Звездного Странника" теперь составляет 1800 рублей.
  3. Текущая распродажа (после корректировки цен):
    • Разработчик решает сделать очень щедрую распродажу и устанавливает скидку в 90% на "Звездного Странника".
    • Новая цена со скидкой составляет 180 рублей (1800 - 90% = 180).
  4. Проблема старого подхода (только абсолютный минимум):
    • Если ориентироваться только на абсолютный исторический минимум из SteamDB (который был 125 рублей), то текущая цена в 180 рублей не будет отображаться как самая выгодная, потому что она выше прежнего абсолютного минимума. SteamDB покажет, что игра когда-то стоила дешевле. Но данный исторический минимум исходил из другой базовой цены, которой уже не существует.
  5. Преимущество нового подхода (учет скидки в процентах и текущей базовой цены):
    • Новый инструмент, который позволяет фильтровать по процентам от текущей базовой цены, покажет, что скидка в 90% - это чрезвычайно выгодное предложение в текущих реалиях.
    • Несмотря на то, что 180 рублей > 125 рублей (если бы мы сравнивали с нерелевантным старым абсолютным минимумом), скидка в 90% от новой базовой цены (1800 рублей) является гораздо более значимой и выгодной, чем, например, если бы сейчас была скидка 75% от 1800 рублей (что составило бы 450 рублей).
  • +1 3

Поделиться сообщением


Ссылка на сообщение

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас

  • Похожие публикации

    • Автор: SerGEAnt

      Для нативного отслеживания появившихся в Steam русских локализаций подписывайтесь на куратора «Русские переводы».
      @0wn3df1x продолжает следить за Steam-играми, в которых появилась русская локализация.
      Список за последние пять недель:
      NEBULOUS: Fleet Command — космическая тактика. Дата выхода в ранний доступ: 11 февраля 2022 года, 4163 обзора, 83% положительные.
      Endoparasitic — изометрический хоррор. Вышел 24 октября 2022 года, 3008 обзоров, 94% положительные.
      Different Strokes — онлайн-игра про рисование. Вышла 10 ноября 2022 года, 1545 обзоров, 93% положительные.
      Zed Zone — изометрическая зомби-выживалка. Дата выхода в ранний доступ: 30 марта 2023 года, 3579 обзоров, 80% положительные.
      Witchfire — фэнтези-шутер от авторов Painkiller. Дата выхода в ранний доступ: 23 сентября 2024 года, 14776 обзоров, 92% положительные.
      MindsEye — прошлогодний провальный сюжетный шутер. Вышел 10 июня 2025 года, 2825 обзоров, 47% положительные.
      Tower Wizard — магическая idle-игра. Вышла 19 июня 2025 года, 8455 обзоров, 97% положительные.
      Berry Bury Berry — idle-игра про выращивание ягод. Вышла 29 января 2026 года, 7311 обзоров, 98% положительные.
      The Karters 2: Turbo Charged — гоночная игра в духе Crash Team Racing. В раннем доступе с 17 июля 2025 года, 1031 обзор, 92% положительные.
      and Roger — визуальная новелла, о которой лучше ничего не говорить, чтобы не испортить эффект от знакомства. Вышла 23 июля 2025 года, 2346 обзоров, 95% положительные.
      Where Winds Meet — бесплатная китайская action/RPG. Вышла 15 ноября 2025 года, 110812 обзоров, 87% положительные.
      Rotwood — битемап от Klei Entertainment. Вышел 3 марта 2026 года, 8070 обзоров, 75% положительные.
      Samson — немного погремевший городской экшен. Вышел 8 апреля 2026 года, 3022 обзора, 55% положительные.
      Lost Castle 2 — динамичный скроллер. Дата выхода из раннего доступа: 11 июня 2026 года, 11891 обзор, 79% положительные.
      Melody's Escape 2 — музыкальная ритм-игра. В раннем доступе с 30 сентября 2022 года, 328 обзоров, 92% положительные.
      Bingle Bingle — карточный (или даже рулеточный) рогалик. В раннем доступе с 18 марта 2024 года, 599 обзоров, 83% положительные.
      Horse Racing Manager — симулятор управления конюшней. В раннем доступе с 24 октября 2025 года, 160 обзоров, 87% положительные.
      Outside the Blocks — игра про создание диорам. Вышла 4 ноября 2025 года, 186 обзоров, 89% положительные.
      Abandoned Archive — классический изометрический рогалик. Вышла 24 ноября 2025 года, 753 обзора, 93% положительные.
      Dogpile — карточный рогалик. Вышел 10 декабря 2025 года, 719 обзоров, 96% положительные.
      Tearscape — изометрический соулс-лайк. Вышел 2 февраля 2026 года, 305 обзоров, 92% положительные.
      Above the Snow — игра про строительство горнолыжного курорта. Вышла 23 апреля 2026 года, 394 обзора, 79% положительные.
    • Автор: SerGEAnt

      В Steam можно до 14 июня забрать на аккаунт платформер Gravity Circuit.
      В Steam можно до 14 июня забрать на аккаунт платформер Gravity Circuit.




×