Torn Room Manager UI

Torn War Tool UI

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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 Room Manager UI
// @namespace    http://tampermonkey.net/
// @version      22.0
// @match        https://www.torn.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      script.google.com
// @connect      script.googleusercontent.com
// @connect      api.torn.com
// @description Torn War Tool UI
// ==/UserScript==

(async function () {
    const API_URL = "https://script.google.com/macros/s/AKfycbzpRX2Lbh599fjORYW1V2AoApCMzk3FXnNCppn6SJM6vpwI1p0xSs2PwmZyWGpKOIv2/exec"; // Replace
    const POLL_INTERVAL = 10000;

    let localApiData = {
        name: null,
        id: null
    };

    async function getUserData(apiKey) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://api.torn.com/user/?selections=profile&key=${apiKey}`,
                onload: r => {
                    try {
                        const data = JSON.parse(r.responseText);
                        if (data.error) {
                            reject(data.error.error);
                        } else {
                            resolve({ name: data.name, id: data.player_id });
                        }
                    } catch (e) {
                        console.error(e);
                        reject(e);
                    }
                },
                onerror: e => reject(e)
            });
        });
    }

    async function getApiKey() {
        if (localApiData.name && localApiData.id) {
            return GM_getValue("TornAPIKey");
        }
        let key = GM_getValue("TornAPIKey");
        if (!key) {
            key = prompt("Enter your Torn API Key:");
        }
        if (key) {
            try {
                const userData = await getUserData(key);
                localApiData = userData;
                GM_setValue("TornAPIKey", key);
                return key;
            } catch (e) {
                alert(`Error with API key: ${e}. Please try again.`);
                GM_setValue("TornAPIKey", null);
                return null;
            }
        }
        return null;
    }

    function apiGet() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: API_URL,
                onload: r => {
                    try {
                        resolve(JSON.parse(r.responseText));
                    }
                    catch (e) {
                        console.error(e);
                        reject(e);
                    }
                },
                onerror: e => reject(e)
            });
        });
    }

    function apiPost(data) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "POST",
                url: API_URL,
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify(data),
                onload: r => {
                    try {
                        resolve(JSON.parse(r.responseText));
                    }
                    catch {
                        resolve(r.responseText);
                    }
                },
                onerror: e => reject(e)
            });
        });
    }

    async function getMemberStatuses(members, apiKey) {
        const statuses = {};
        const requests = members.map(m => {
            if (m.userId) {
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: `https://api.torn.com/user/${m.userId}?selections=profile&key=${apiKey}`,
                        onload: r => {
                            try {
                                const data = JSON.parse(r.responseText);
                                if (data.status) {
                                    statuses[m.userId] = data.status.state;
                                }
                                resolve();
                            } catch (e) {
                                console.error(e);
                                resolve(); // Resolve even on error to not block other requests
                            }
                        },
                        onerror: e => {
                            console.error(e);
                            resolve(); // Resolve even on error
                        }
                    });
                });
            }
            return Promise.resolve();
        });
        await Promise.all(requests);
        return statuses;
    }

    async function refreshUI() {
        const apiKey = await getApiKey();
        if (!apiKey) return;

        const data = await apiGet();
        if (!data.rooms || data.rooms.length === 0) data.rooms = [{ id: 1, name: "Waiting Room", target: "" }];

        let container = document.getElementById("roomUI");
        if (!container) {
            container = document.createElement("div");
            container.id = "roomUI";
            container.style.position = "fixed";
            container.style.top = "80px";
            container.style.right = "10px";
            container.style.background = "#1e1e2f";
            container.style.color = "#fff";
            container.style.padding = "15px";
            container.style.zIndex = "9999";
            container.style.maxHeight = "90vh";
            container.style.overflowY = "auto";
            container.style.borderRadius = "10px";
            container.style.fontFamily = "'Segoe UI', sans-serif";
            container.style.boxShadow = "0 4px 12px rgba(0,0,0,0.4)";
            document.body.appendChild(container);
        }
        container.innerHTML = "<h2 style='text-align:center;'>Torn Room Manager</h2>";

        // Get status for all members in the current rooms
        const memberStatuses = await getMemberStatuses(data.members, apiKey);

        data.rooms.forEach(room => {
            const roomDiv = document.createElement("div");
            roomDiv.style.border = "1px solid #444";
            roomDiv.style.margin = "5px 0";
            roomDiv.style.padding = "10px";
            roomDiv.style.borderRadius = "8px";
            roomDiv.style.background = "#282c34";

            roomDiv.innerHTML = `<b>${room.name}</b> (Target: ${room.target || "None"})`;

            const membersInRoom = data.members.filter(m => m.roomId == room.id);
            membersInRoom.forEach(m => {
                const p = document.createElement("p");
                const status = memberStatuses[m.userId] || "Unknown";
                p.textContent = `${m.userName} (${m.userId}) - Status: ${status}`;
                p.style.margin = "2px 0";
                roomDiv.appendChild(p);
            });

            // Waiting Room join/leave
            if (room.name === "Waiting Room") {
                const isInRoom = membersInRoom.some(m => m.apiKey === apiKey);
                const btn = document.createElement("button");
                btn.textContent = isInRoom ? "Leave Room" : "Join Room";
                btn.style.marginTop = "5px";
                btn.style.padding = "4px 10px";
                btn.onclick = async () => {
                    if (isInRoom) {
                        await apiPost({ type: "leaveRoom", roomId: room.id, apiKey });
                    } else {
                        await apiPost({
                            type: "joinRoom",
                            roomId: room.id,
                            userName: localApiData.name,
                            userId: localApiData.id,
                            apiKey
                        });
                    }
                    refreshUI();
                };
                roomDiv.appendChild(btn);
            }

            // Admin buttons
            const admin = data.admins.find(a => a.apiKey === apiKey);
            if (admin) {
                const createBtn = document.createElement("button");
                createBtn.textContent = "Create Room";
                createBtn.style.margin = "5px 5px 0 0";
                createBtn.onclick = async () => {
                    const name = prompt("New room name:");
                    if (!name) return;
                    await apiPost({ type: "createRoom", roomName: name, apiKey });
                    refreshUI();
                };

                const deleteBtn = document.createElement("button");
                deleteBtn.textContent = "Delete Room";
                deleteBtn.style.margin = "0 5px 0 0";
                deleteBtn.onclick = async () => {
                    if (room.id === 1) return alert("Cannot delete Waiting Room");
                    if (!confirm(`Delete ${room.name}?`)) return;
                    await apiPost({ type: "deleteRoom", roomId: room.id, apiKey });
                    refreshUI();
                };

                const targetBtn = document.createElement("button");
                targetBtn.textContent = "Set Target";
                targetBtn.style.margin = "0 5px 0 0";
                targetBtn.onclick = async () => {
                    const t = prompt("Enter target for room:", room.target || "");
                    if (t !== null) {
                        await apiPost({ type: "setTarget", roomId: room.id, target: t, apiKey });
                        refreshUI();
                    }
                };
                roomDiv.appendChild(createBtn);
                roomDiv.appendChild(deleteBtn);
                roomDiv.appendChild(targetBtn);
            }

            container.appendChild(roomDiv);
        });
    }

    refreshUI();
    setInterval(refreshUI, POLL_INTERVAL);
})();