// ==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);
})();