Загрузка...

UI Отслеживаем наше проведенное время на форуме

Тема в разделе Дополнения создана пользователем Yowori 6 апр 2025. 200 просмотров

Загрузка...
  1. Yowori
    Yowori Автор темы 6 апр 2025 Эльфографика грядёт ~ https://lolz.live/threads/7861550/ 14 808 3 июн 2019
    Это расширение для нашего любимого форума с блеском отслеживает, сколько времени ты проводишь на разных разделах форума.
    Впрочем, не стоит ожидать фокусов. Простой, но эффективный инструмент для тех, кто любит данные и статистику.


    [IMG]
    — Следит за временем: Подсчитывает, сколько времени ты проводишь на форуме, в темах, у пользователей или на странице профиля.
    — Показывает статистику: После сбора данных, ты можешь увидеть, сколько времени потратил на различные разделы или темы.
    — Cброс статистики: Если вдруг решишь, что накопленные данные не особо важны, есть опция для сброса (с подтверждением, чтоб не перепутать).
    — Переключение видов: Удобно переключаться между общей статистикой, детальными данными по разделам, темам и пользователям.
    [IMG]
    [IMG]
    [IMG]
    — Расширение ~ Chrome ~ Opera ~ Firefox
    Скрипт ~ https://greasyfork.org/ru/scripts/532007-lolz-time-tracker
    или
    Код
    // ==UserScript==
    // @name Lolz Time Tracker
    // @namespace http://tampermonkey.net/
    // @version 1.0
    // @description Track time spent on lolz.live with detailed statistics
    // @author Yowori
    // @match https://lolz.live/*
    // @match https://lolz.guru/*
    // @match https://zelenka.guru/*
    // @grant GM_getValue
    // @grant GM_setValue
    // @grant GM_addStyle
    // @grant GM_registerMenuCommand
    // ==/UserScript==

    (function() {
    'use strict';

    const SECTION_CONFIG = {
    forums: { pattern: /\/forums\//, name: 'Разделы ' },
    threads: { pattern: /\/threads\//, name: 'Темы ' },
    members: { pattern: /\/members\//, name: 'Пользователи ' },
    account: { pattern: /\/account\//, name: 'Профиль ' },
    other: { pattern: /.*/, name: 'Другое ' }
    };

    let trackerState = {
    currentSection: null,
    currentThread: null,
    currentUser: null,
    isPageActive: true,
    lastUpdateTime: Date.now(),
    totalSeconds: 0,
    sectionSeconds: {},
    threadSeconds: {},
    userSeconds: {}
    };

    const storageKey = 'lolzTimeTracker_v4';
    const uiStateKey = 'lolzTimeTrackerUIState';
    let updateInterval;
    let statsVisible = false;
    let detailedView = 'main';
    let confirmationOpen = false;

    GM_addStyle(`
    #time-tracker-container {
    position: fixed;
    left: 10px;
    bottom: 10px;
    z-index: 9999;
    font-family: Arial, sans-serif;
    }

    #time-tracker-toggle {
    background: #4CAF50;
    color: white;
    border: none;
    padding: 8px 12px;
    border-radius: 4px;
    cursor: pointer;
    font-weight: bold;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    transition: all 0.2s ease;
    margin-right: 8px;
    }

    #time-tracker-toggle:hover {
    background: #45a049;
    transform: translateY(-1px);
    }

    #time-tracker-reset {
    background: #f44336;
    color: white;
    border: none;
    padding: 8px 12px;
    border-radius: 4px;
    cursor: pointer;
    font-weight: bold;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    transition: all 0.2s ease;
    }

    #time-tracker-reset:hover {
    background: #d32f2f;
    transform: translateY(-1px);
    }

    #time-tracker-buttons {
    display: flex;
    }

    #time-tracker-widget {
    display: none;
    background: #272727;
    color: #fff;
    padding: 12px;
    border-radius: 6px;
    margin-top: 8px;
    width: 280px;
    backdrop-filter: blur(5px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.3);
    border: 1px solid #383838;
    max-height: 60vh;
    overflow-y: auto;
    }

    .time-tracker-header {
    font-size: 16px;
    font-weight: bold;
    margin-bottom: 10px;
    color: #4CAF50;
    border-bottom: 1px solid #444;
    padding-bottom: 5px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    }

    .time-tracker-back-btn {
    background: none;
    border: none;
    color: #4CAF50;
    cursor: pointer;
    font-size: 14px;
    }

    .time-tracker-confirm-reset {
    background: none;
    border: none;
    color: #f44336;
    cursor: pointer;
    font-size: 14px;
    margin-left: 5px;
    }

    .time-tracker-section {
    margin: 8px 0;
    display: flex;
    justify-content: space-between;
    }

    .time-tracker-section-name {
    color: #ddd;
    cursor: pointer;
    }

    .time-tracker-section-time {
    font-weight: bold;
    color: #fff;
    }

    .time-tracker-detail-item {
    margin: 6px 0;
    padding-left: 10px;
    border-left: 2px solid #444;
    }

    .time-tracker-nav-btn {
    background: none;
    border: none;
    color: #ddd;
    cursor: pointer;
    margin: 0 5px;
    padding: 2px 5px;
    }

    .time-tracker-nav-btn:hover {
    color: #4CAF50;
    }

    .time-tracker-nav-btn.active {
    color: #4CAF50;
    border-bottom: 1px solid #4CAF50;
    }
    `);

    function init() {
    loadData();
    loadUIState();
    createUI();
    setupVisibilityListener();
    setupPageAnalyzers();
    startTracking();
    GM_registerMenuCommand("Показать статистику Lolz.live", toggleStats);
    }

    function loadUIState() {
    const savedState = GM_getValue(uiStateKey, { statsVisible: false });
    statsVisible = savedState.statsVisible;
    }

    function saveUIState() {
    GM_setValue(uiStateKey, { statsVisible: statsVisible });
    }

    function createUI() {
    const container = document.createElement('div');
    container.id = 'time-tracker-container';

    const buttonsContainer = document.createElement('div');
    buttonsContainer.id = 'time-tracker-buttons';

    const toggleBtn = document.createElement('button');
    toggleBtn.id = 'time-tracker-toggle';
    toggleBtn.textContent = statsVisible ? ' Скрыть' : ' Статистика';
    toggleBtn.addEventListener('click', toggleStats);

    const resetBtn = document.createElement('button');
    resetBtn.id = 'time-tracker-reset';
    resetBtn.textContent = ' Сбросить';
    resetBtn.addEventListener('click', confirmResetStats);

    buttonsContainer.appendChild(toggleBtn);
    buttonsContainer.appendChild(resetBtn);

    const widget = document.createElement('div');
    widget.id = 'time-tracker-widget';
    widget.style.display = statsVisible ? 'block' : 'none';

    container.appendChild(buttonsContainer);
    container.appendChild(widget);
    document.body.appendChild(container);

    updateUI();
    }

    function confirmResetStats() {
    const widget = document.getElementById('time-tracker-widget');
    if (!widget) return;

    confirmationOpen = true;
    widget.innerHTML = `
    <div class="time-tracker-header">
    <span>Сброс статистики</span>
    </div>
    <div style="margin: 10px 0;">
    Вы уверены, что хотите сбросить статистику за сегодня?
    </div>
    <div style="display: flex; justify-content: flex-end;">
    <button id="time-tracker-cancel-reset" style="background: #4CAF50; color: white; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: all 0.2s ease; margin-right: 8px;">Отмена</button>
    <button id="time-tracker-confirm-reset" style="background: #f44336; color: white; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: all 0.2s ease;">Сбросить</button>
    </div>
    `;

    document.getElementById('time-tracker-cancel-reset').addEventListener('click', () => {
    confirmationOpen = false;
    updateUI();
    });

    document.getElementById('time-tracker-confirm-reset').addEventListener('click', resetStats);
    }

    function resetStats() {
    const today = getTodayKey();
    const savedData = GM_getValue(storageKey) || {};
    savedData[today] = {
    totalSeconds: 0,
    sectionSeconds: {},
    threadSeconds: {},
    userSeconds: {}
    };
    GM_setValue(storageKey, savedData);

    trackerState.totalSeconds = 0;
    trackerState.sectionSeconds = {};
    trackerState.threadSeconds = {};
    trackerState.userSeconds = {};


    for (const section in SECTION_CONFIG) {
    trackerState.sectionSeconds[section] = 0;
    }

    confirmationOpen = false;
    updateUI();
    }


    function toggleStats() {
    statsVisible = !statsVisible;
    const widget = document.getElementById('time-tracker-widget');
    if (widget) widget.style.display = statsVisible ? 'block' : 'none';

    const toggleBtn = document.getElementById('time-tracker-toggle');
    if (toggleBtn) toggleBtn.textContent = statsVisible ? ' Скрыть' : ' Статистика';

    saveUIState();

    if (statsVisible && !confirmationOpen) {
    updateUI();
    }
    }


    function loadData() {
    const today = getTodayKey();
    const savedData = GM_getValue(storageKey) || {};
    const todayData = savedData[today] || {
    totalSeconds: 0,
    sectionSeconds: {},
    threadSeconds: {},
    userSeconds: {}
    };

    trackerState = {
    ...trackerState,
    totalSeconds: todayData.totalSeconds || 0,
    sectionSeconds: todayData.sectionSeconds || {},
    threadSeconds: todayData.threadSeconds || {},
    userSeconds: todayData.userSeconds || {}
    };


    for (const section in SECTION_CONFIG) {
    if (!trackerState.sectionSeconds[section]) {
    trackerState.sectionSeconds[section] = 0;
    }
    }
    }


    function saveData() {
    const today = getTodayKey();
    const savedData = GM_getValue(storageKey) || {};
    savedData[today] = {
    totalSeconds: trackerState.totalSeconds,
    sectionSeconds: trackerState.sectionSeconds,
    threadSeconds: trackerState.threadSeconds,
    userSeconds: trackerState.userSeconds
    };
    GM_setValue(storageKey, savedData);
    }


    function detectSection() {
    const path = window.location.pathname;
    for (const [section, config] of Object.entries(SECTION_CONFIG)) {
    if (config.pattern.test(path)) {
    return section;
    }
    }
    return 'other';
    }


    function detectThread() {
    const threadMatch = window.location.pathname.match(/\/threads\/(\d+)/);
    if (threadMatch) {
    return threadMatch[1];
    }
    return null;
    }


    function detectUser() {
    const userMatch = window.location.pathname.match(/\/members\/(\d+)/);
    if (userMatch) {
    return userMatch[1];
    }
    const prettyUrlMatch = window.location.pathname.match(/^\/([^\/]+)\/?$/);
    if (prettyUrlMatch && !['forums', 'threads', 'account', 'members'].includes(prettyUrlMatch[1])) {
    return prettyUrlMatch[1];
    }
    return null;
    }


    function setupPageAnalyzers() {
    trackerState.currentSection = detectSection();
    trackerState.currentThread = detectThread();
    trackerState.currentUser = detectUser();
    }


    function startTracking() {
    if (updateInterval) clearInterval(updateInterval);

    updateInterval = setInterval(() => {
    if (!trackerState.isPageActive) return;

    const now = Date.now();
    const elapsedSeconds = Math.floor((now - trackerState.lastUpdateTime) / 1000);

    if (elapsedSeconds > 0) {
    trackerState.lastUpdateTime = now;

    setupPageAnalyzers();

    trackerState.totalSeconds += elapsedSeconds;

    trackerState.sectionSeconds[trackerState.currentSection] =
    (trackerState.sectionSeconds[trackerState.currentSection] || 0) + elapsedSeconds;

    if (trackerState.currentThread) {
    trackerState.threadSeconds[trackerState.currentThread] =
    (trackerState.threadSeconds[trackerState.currentThread] || 0) + elapsedSeconds;
    }

    if (trackerState.currentUser) {
    trackerState.userSeconds[trackerState.currentUser] =
    (trackerState.userSeconds[trackerState.currentUser] || 0) + elapsedSeconds;
    }

    saveData();

    if (statsVisible && !confirmationOpen) {
    updateUI();
    }
    }
    }, 1000);
    }

    function updateUI() {
    if (confirmationOpen) return;

    const widget = document.getElementById('time-tracker-widget');
    if (!widget) return;

    let html = '';

    switch (detailedView) {
    case 'main':
    html = getMainView();
    break;
    case 'sections':
    html = getSectionsDetailView();
    break;
    case 'threads':
    html = getThreadsDetailView();
    break;
    case 'users':
    html = getUsersDetailView();
    break;
    }

    widget.innerHTML = html;
    addEventHandlers();
    }

    function getMainView() {
    let html = `<div class="time-tracker-header">
    <span>Статистика за сегодня</span>
    </div>`;

    html += `<div class="time-tracker-section">
    <span class="time-tracker-section-name">Всего:</span>
    <span class="time-tracker-section-time">${formatTime(trackerState.totalSeconds)}</span>
    </div>`;

    html += `<div style="margin: 10px 0; display: flex; justify-content: space-around;">
    <button class="time-tracker-nav-btn ${detailedView === 'threads' ? 'active' : ''}" data-view="threads">Темы</button>
    <button class="time-tracker-nav-btn ${detailedView === 'users' ? 'active' : ''}" data-view="users">Пользователи</button>
    </div>`;

    const sortedSections = Object.entries(trackerState.sectionSeconds)
    .sort((a, b) => b[1] - a[1]);

    html += `<div style="margin-top: 10px; font-weight: bold; color: #ddd;">Общая информация :</div>`;
    for (const [sectionId, seconds] of sortedSections.slice(0, 5)) {
    if (seconds > 0) {
    const sectionName = SECTION_CONFIG[sectionId].name;
    html += `<div class="time-tracker-section">
    <span class="time-tracker-section-name">${sectionName}:</span>
    <span class="time-tracker-section-time">${formatTime(seconds)}</span>
    </div>`;
    }
    }

    return html;
    }

    function getSectionsDetailView() {
    let html = `<div class="time-tracker-header">
    <button class="time-tracker-back-btn" data-view="main">Назад</button>
    <span>Статистика по разделам</span>
    </div>`;

    const sortedSections = Object.entries(trackerState.sectionSeconds)
    .sort((a, b) => b[1] - a[1]);

    for (const [sectionId, seconds] of sortedSections) {
    if (seconds > 0) {
    const sectionName = SECTION_CONFIG[sectionId].name;
    html += `<div class="time-tracker-section">
    <span class="time-tracker-section-name">${sectionName}:</span>
    <span class="time-tracker-section-time">${formatTime(seconds)}</span>
    </div>`;
    }
    }

    return html;
    }

    function getThreadsDetailView() {
    let html = `<div class="time-tracker-header">
    <button class="time-tracker-back-btn" data-view="main">Назад</button>
    <span>Темы</span>
    </div>`;

    const sortedThreads = Object.entries(trackerState.threadSeconds)
    .sort((a, b) => b[1] - a[1]);

    for (const [threadId, seconds] of sortedThreads.slice(0, 20)) {
    if (seconds > 0) {
    html += `<div class="time-tracker-section">
    <span class="time-tracker-section-name" data-thread-id="${threadId}">Тема #${threadId}:</span>
    <span class="time-tracker-section-time">${formatTime(seconds)}</span>
    </div>`;
    }
    }

    return html;
    }

    function getUsersDetailView() {
    let html = `<div class="time-tracker-header">
    <button class="time-tracker-back-btn" data-view="main">Назад</button>
    <span>Пользователи</span>
    </div>`;

    const sortedUsers = Object.entries(trackerState.userSeconds)
    .sort((a, b) => b[1] - a[1]);

    for (const [userId, seconds] of sortedUsers.slice(0, 20)) {
    if (seconds > 0) {
    const isNumericId = /^\d+$/.test(userId);
    const userLink = isNumericId ? `/members/${userId}/` : `/${userId}/`;

    html += `<div class="time-tracker-section">
    <span class="time-tracker-section-name" data-user-id="${userId}" data-is-numeric="${isNumericId}">Пользователь ${isNumericId ? '#' + userId : userId}:</span>
    <span class="time-tracker-section-time">${formatTime(seconds)}</span>
    </div>`;
    }
    }

    return html;
    }

    function addEventHandlers() {
    document.querySelectorAll('.time-tracker-nav-btn').forEach(btn => {
    btn.addEventListener('click', () => {
    detailedView = btn.dataset.view;
    updateUI();
    });
    });

    document.querySelectorAll('.time-tracker-back-btn').forEach(btn => {
    btn.addEventListener('click', () => {
    detailedView = btn.dataset.view;
    updateUI();
    });
    });

    document.querySelectorAll('.time-tracker-section-name[data-thread-id]').forEach(el => {
    el.addEventListener('click', (e) => {
    e.preventDefault();
    const threadId = el.dataset.threadId;
    window.location.href = `/threads/${threadId}/`;
    });
    });

    document.querySelectorAll('.time-tracker-section-name[data-user-id]').forEach(el => {
    el.addEventListener('click', (e) => {
    e.preventDefault();
    const userId = el.dataset.userId;
    const isNumeric = el.dataset.isNumeric === 'true';

    if (isNumeric) {
    window.location.href = `/members/${userId}/`;
    } else {
    window.location.href = `/${userId}/`;
    }
    });
    });
    }

    function formatTime(totalSeconds) {
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const seconds = totalSeconds % 60;

    if (totalSeconds < 60) {
    return `${seconds} сек`;
    } else {
    return `${hours > 0 ? hours + ' ч ' : ''}${minutes} мин`;
    }
    }

    function setupVisibilityListener() {
    document.addEventListener('visibilitychange', () => {
    trackerState.isPageActive = !document.hidden;
    trackerState.lastUpdateTime = Date.now();
    });
    }

    function getTodayKey() {
    const now = new Date();
    return `${now.getFullYear()}-${now.getMonth()+1}-${now.getDate()}`;
    }

    window.addEventListener('load', init);
    document.addEventListener('DOMContentLoaded', init);
    })();

    Короче говоря, этот скрипт для тех, кто хочет знать, куда уходит время, и может позволить себе не забывать о нем даже после полудня на форуме.
     
    6 апр 2025 Изменено
  2. рикка
    рикка 6 апр 2025 丰 все суки в лондоне мёртвые 6062 4 апр 2019
    расширение чтобы ещё более отчётливо понять как быстро проходит жизнь в оффтопе
     
  3. unleash
    unleash 6 апр 2025 статус 3604 26 ноя 2020
    там таких цифр больших не будет
     
  4. ахаххахаххахахах
    ахаххахаххахахах 6 апр 2025 договорнячок хочу... :pwalk:
    страшно смотреть будет на это
     
    6 апр 2025 Изменено
  5. биллиногами
    биллиногами 6 апр 2025 Банки|Турция|Верификации|Биржи - lzt.lol/TUR :zerotwo:
    На ios есть вариант поставить ?
     
    6 апр 2025 Изменено
  6. Legitim
    Legitim 6 апр 2025 Мы живём для того, чтобы стать нефтью 12 245 10 фев 2019
    А время на маркете оно показывает?
     
    1. Посмотреть предыдущие комментарии (1)
    2. Yowori Автор темы
      Legitim, просто время на маркете или со статистикой по товарам на которых больше всего сидел?
      6 апр 2025 Изменено
    3. Legitim
      Yowori, Та просто время наверн
  7. WhatACat
    WhatACat 6 апр 2025 https://lolz.live/account/upgrades покупайте уники, ау, автобай 15 935 16 дек 2023
    Сбор информации и перенаправление к Yowori встроено :2011_like:
     
    1. Yowori Автор темы
      WhatACat, собственно говоря а где минусы?
Top