Torn Faction Last Action Display

Display faction members' last action times on the faction page

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Torn Faction Last Action Display
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Display faction members' last action times on the faction page
// @author       ShAdOwCrEsT [3929345]
// @match        https://www.torn.com/factions.php*
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    const API_KEY_STORAGE = 'torn_faction_api_key';
    const FACTION_ID_STORAGE = 'torn_faction_id';

    function getApiKey() {
        let apiKey = GM_getValue(API_KEY_STORAGE, '');
        if (!apiKey) {
            apiKey = prompt('Please enter your Torn API key:');
            if (apiKey) {
                GM_setValue(API_KEY_STORAGE, apiKey);
            }
        }
        return apiKey;
    }

    async function fetchUserFaction(apiKey) {
        try {
            const response = await fetch(`https://api.torn.com/v2/user/faction?key=${apiKey}`);
            const data = await response.json();

            if (data.error) {
                console.error('API Error:', data.error);
                if (data.error.code === 2) {
                    GM_setValue(API_KEY_STORAGE, '');
                    alert('Invalid API key. Please refresh and enter a valid key.');
                }
                return null;
            }

            if (data.faction && data.faction.id) {
                GM_setValue(FACTION_ID_STORAGE, data.faction.id.toString());
                console.log('Faction ID stored:', data.faction.id);
                return data.faction.id;
            }

            return null;
        } catch (error) {
            console.error('Error fetching user faction:', error);
            return null;
        }
    }

    async function getFactionId(apiKey) {
        let factionId = GM_getValue(FACTION_ID_STORAGE, '');
        if (!factionId) {
            factionId = await fetchUserFaction(apiKey);
        }
        return factionId;
    }

    function getFactionIdFromUrl() {
        const urlParams = new URLSearchParams(window.location.search);
        const factionId = urlParams.get('ID');
        return factionId;
    }

    async function fetchFactionMembers(factionId, apiKey) {
        try {
            const response = await fetch(`https://api.torn.com/v2/faction/${factionId}/members?striptags=true&key=${apiKey}`);
            const data = await response.json();

            if (data.error) {
                console.error('API Error:', data.error);
                if (data.error.code === 2) {
                    GM_setValue(API_KEY_STORAGE, '');
                    GM_setValue(FACTION_ID_STORAGE, '');
                    alert('Invalid API key. Please refresh and enter a valid key.');
                }
                return null;
            }

            return data.members;
        } catch (error) {
            console.error('Error fetching faction data:', error);
            return null;
        }
    }

    function addLastActionColumn(members) {
        const observer = new MutationObserver((mutations, obs) => {
            const tableRows = document.querySelectorAll('ul.table-body li.table-row');

            if (tableRows.length > 0) {
                obs.disconnect();

                const memberMap = {};
                members.forEach(member => {
                    memberMap[member.id] = member;
                });

                addTableHeader();

                const inactiveMembers = [];

                tableRows.forEach(row => {
                    const profileLink = row.querySelector('a[href*="/profiles.php?XID="]');
                    if (profileLink) {
                        const href = profileLink.getAttribute('href');
                        const memberId = href.match(/XID=(\d+)/)?.[1];

                        if (memberId && memberMap[memberId]) {
                            const member = memberMap[memberId];
                            addLastActionCell(row, member.last_action.relative);

                            if (isInactiveForThreeDays(member.last_action.relative)) {
                                inactiveMembers.push({
                                    name: member.name,
                                    lastAction: member.last_action.relative
                                });
                            }
                        }
                    }
                });

                if (inactiveMembers.length > 0) {
                    const memberList = inactiveMembers.map(m => `${m.name} (${m.lastAction})`).join('\n');
                    alert(`Inactive members (3+ days):\n\n${memberList}\n\nTotal: ${inactiveMembers.length} member(s)`);
                }
            }
        });

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

    function addTableHeader() {
        const headerRow = document.querySelector('ul.table-header');
        if (headerRow && !document.querySelector('.last-action-header')) {
            const statusHeader = headerRow.querySelector('.table-cell.status');
            if (statusHeader) {
                const lastActionHeader = document.createElement('div');
                lastActionHeader.className = 'table-cell last-action-header';
                lastActionHeader.textContent = 'LA';
                lastActionHeader.title = 'Last Action';
                lastActionHeader.style.cssText = 'font-weight: bold; padding: 5px; text-align: center;';
                statusHeader.parentNode.insertBefore(lastActionHeader, statusHeader);
            }
        }
    }

    function formatTimeShort(relativeTime) {
        const match = relativeTime.match(/(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago/i);
        if (match) {
            const value = match[1];
            const unit = match[2].toLowerCase()[0];
            return `${value}${unit}`;
        }

        if (relativeTime.includes('0 minutes') || relativeTime.toLowerCase() === 'now') {
            return 'now';
        }

        return relativeTime;
    }

    function isInactiveForThreeDays(relativeTime) {
        const match = relativeTime.match(/(\d+)\s+(day|week|month|year)s?\s+ago/i);
        if (match) {
            const value = parseInt(match[1]);
            const unit = match[2].toLowerCase();

            if (unit === 'day' && value >= 3) {
                return true;
            }
            if (unit === 'week' || unit === 'month' || unit === 'year') {
                return true;
            }
        }
        return false;
    }

    function isInactiveForOneDay(relativeTime) {
        const match = relativeTime.match(/(\d+)\s+(day)s?\s+ago/i);
        if (match) {
            const value = parseInt(match[1]);
            const unit = match[2].toLowerCase();

            if (unit === 'day' && value >= 1 && value < 3) {
                return true;
            }
        }
        return false;
    }

    function addLastActionCell(row, lastAction) {
        if (row.querySelector('.last-action-cell')) {
            return;
        }

        const statusCell = row.querySelector('.table-cell.status');
        if (statusCell) {
            const lastActionCell = document.createElement('div');
            lastActionCell.className = 'table-cell last-action-cell';
            lastActionCell.textContent = formatTimeShort(lastAction);
            lastActionCell.title = lastAction;

            let cellStyle = 'padding: 5px; text-align: center; white-space: nowrap;';

            if (isInactiveForThreeDays(lastAction)) {
                cellStyle += ' background-color: rgba(255, 0, 0, 0.3);';
            }
            else if (isInactiveForOneDay(lastAction)) {
                cellStyle += ' background-color: rgba(255, 255, 0, 0.3);';
            }

            lastActionCell.style.cssText = cellStyle;
            statusCell.parentNode.insertBefore(lastActionCell, statusCell);
        }
    }

    async function init() {
        const isYourFactionPage = window.location.href.includes('factions.php?step=your');
        const isProfilePage = window.location.href.includes('factions.php?step=profile');

        if (!isYourFactionPage && !isProfilePage) {
            return;
        }

        const apiKey = getApiKey();
        if (!apiKey) {
            console.error('No API key provided');
            return;
        }

        let factionId;

        if (isProfilePage) {
            factionId = getFactionIdFromUrl();
            console.log('Faction ID from URL:', factionId);

            if (!factionId) {
                factionId = await getFactionId(apiKey);
                console.log('Faction ID from storage:', factionId);
            }
        } else {
            console.log('Getting faction ID...');
            factionId = await getFactionId(apiKey);
        }

        if (!factionId) {
            console.error('Could not retrieve faction ID');
            return;
        }

        console.log('Fetching faction members data for faction:', factionId);
        const members = await fetchFactionMembers(factionId, apiKey);

        if (members) {
            console.log('Members data received:', members.length, 'members');
            addLastActionColumn(members);
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();