Hack your torn (Kidding)
// ==UserScript==
// @name Hack your Torn War
// @namespace http://tampermonkey.net/
// @version 4
// @description Hack your torn (Kidding)
// @author ShAdOwCrEsT [3929345]
// @match https://www.torn.com/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @connect api.torn.com
// @license GPU AGPLv3
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
.glitter-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
overflow: hidden;
}
.glitter-particle {
position: absolute;
width: 10px;
height: 10px;
opacity: 0.9;
animation: fall linear infinite, twinkle ease-in-out infinite;
}
.glitter-star4 {
background: linear-gradient(45deg, transparent 40%, var(--sparkle-color) 40%, var(--sparkle-color) 60%, transparent 60%),
linear-gradient(-45deg, transparent 40%, var(--sparkle-color) 40%, var(--sparkle-color) 60%, transparent 60%);
}
.glitter-star6 {
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
background: linear-gradient(135deg, var(--sparkle-light) 0%, var(--sparkle-color) 50%, var(--sparkle-bright) 100%);
}
.glitter-diamond {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
background: linear-gradient(135deg, var(--sparkle-light) 0%, var(--sparkle-color) 50%, var(--sparkle-bright) 100%);
}
.color-white {
--sparkle-color: #c0c0c0;
--sparkle-light: #ffffff;
--sparkle-bright: #e8e8e8;
filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.8));
}
.color-yellow {
--sparkle-color: #ffd700;
--sparkle-light: #ffff99;
--sparkle-bright: #ffeb3b;
filter: drop-shadow(0 0 3px rgba(255, 215, 0, 0.8));
}
.color-blue {
--sparkle-color: #4da6ff;
--sparkle-light: #b3d9ff;
--sparkle-bright: #80bfff;
filter: drop-shadow(0 0 3px rgba(77, 166, 255, 0.8));
}
@keyframes fall {
0% {
transform: translateY(-20px) rotate(0deg) scale(1);
opacity: 0;
}
10% { opacity: 0.9; }
90% { opacity: 0.9; }
100% {
transform: translateY(100vh) rotate(360deg) scale(1);
opacity: 0;
}
}
@keyframes twinkle {
0%, 100% { opacity: 0.9; filter: brightness(1); }
50% { opacity: 0.5; filter: brightness(1.5); }
}
/* Settings button */
#torn-war-settings-btn {
position: fixed;
bottom: 16px;
right: 16px;
z-index: 10000;
width: 36px;
height: 36px;
border-radius: 50%;
background: #2a2a2a;
border: 2px solid #555;
color: #ccc;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.5);
transition: background 0.2s, border-color 0.2s;
}
#torn-war-settings-btn:hover {
background: #3a3a3a;
border-color: #ffd700;
color: #ffd700;
}
/* Settings modal */
#torn-war-settings-modal {
position: fixed;
bottom: 60px;
right: 16px;
z-index: 10001;
background: #1e1e1e;
border: 1px solid #444;
border-radius: 10px;
padding: 16px;
width: 240px;
box-shadow: 0 4px 20px rgba(0,0,0,0.7);
color: #ccc;
font-family: sans-serif;
font-size: 13px;
display: none;
}
#torn-war-settings-modal.open { display: block; }
#torn-war-settings-modal h3 {
margin: 0 0 12px;
color: #ffd700;
font-size: 14px;
}
#torn-war-settings-modal label {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
cursor: pointer;
}
#torn-war-settings-modal button {
margin-top: 10px;
width: 100%;
padding: 6px;
background: #333;
border: 1px solid #555;
border-radius: 6px;
color: #ccc;
cursor: pointer;
font-size: 12px;
}
#torn-war-settings-modal button:hover {
background: #444;
border-color: #ffd700;
color: #ffd700;
}
`);
// ─── Settings ────────────────────────────────────────────────────────────
const SETTINGS_INITIALIZED_KEY = 'settingsInitialized';
function showFirstTimeSetup() {
const soundChoice = confirm('Would you like to enable sound alerts?');
GM_setValue('alertSoundEnabled', soundChoice);
const glitterChoice = confirm('Would you like to enable glitter animation?');
GM_setValue('glitterEnabled', glitterChoice);
GM_setValue('alertTime', 100);
GM_setValue('minChain', 100);
GM_setValue(SETTINGS_INITIALIZED_KEY, true);
}
if (!GM_getValue(SETTINGS_INITIALIZED_KEY)) {
showFirstTimeSetup();
}
// Live-read settings so the modal changes take effect immediately
function getSoundEnabled() { return GM_getValue('alertSoundEnabled', true); }
function getGlitterEnabled() { return GM_getValue('glitterEnabled', true); }
// ─── Settings UI ─────────────────────────────────────────────────────────
function buildSettingsUI() {
const btn = document.createElement('button');
btn.id = 'torn-war-settings-btn';
btn.title = 'Torn War Script Settings';
btn.textContent = '⚙';
document.body.appendChild(btn);
const modal = document.createElement('div');
modal.id = 'torn-war-settings-modal';
modal.innerHTML = `
<h3>⚔ War Script Settings</h3>
<label>
<input type="checkbox" id="tws-sound" ${getSoundEnabled() ? 'checked' : ''}>
Sound alerts
</label>
<label>
<input type="checkbox" id="tws-glitter" ${getGlitterEnabled() ? 'checked' : ''}>
Glitter animation
</label>
<button id="tws-reset-api">🔑 Reset API Key</button>
<button id="tws-reset-all">🗑 Reset All Settings</button>
`;
document.body.appendChild(modal);
btn.addEventListener('click', () => modal.classList.toggle('open'));
document.addEventListener('click', (e) => {
if (!modal.contains(e.target) && e.target !== btn) {
modal.classList.remove('open');
}
});
modal.querySelector('#tws-sound').addEventListener('change', (e) => {
GM_setValue('alertSoundEnabled', e.target.checked);
});
modal.querySelector('#tws-glitter').addEventListener('change', (e) => {
GM_setValue('glitterEnabled', e.target.checked);
});
modal.querySelector('#tws-reset-api').addEventListener('click', () => {
GM_setValue(API_STORAGE_KEY, '');
apiKey = '';
modal.classList.remove('open');
requestApiKey();
});
modal.querySelector('#tws-reset-all').addEventListener('click', () => {
if (confirm('Reset all settings? You will be asked to set them up again.')) {
GM_deleteValue(SETTINGS_INITIALIZED_KEY);
GM_deleteValue('alertSoundEnabled');
GM_deleteValue('glitterEnabled');
GM_deleteValue('alertTime');
GM_deleteValue('minChain');
GM_deleteValue(API_STORAGE_KEY);
location.reload();
}
});
}
// ─── API Key ─────────────────────────────────────────────────────────────
const API_STORAGE_KEY = 'tornApiKey';
const API_URL = 'https://api.torn.com/faction/?selections=chain&key=';
let apiKey = GM_getValue(API_STORAGE_KEY, '');
const userAlertTime = GM_getValue('alertTime', 100);
function requestApiKey() {
const inputKey = prompt('Enter your Torn API key (only needed once):', '');
if (inputKey && inputKey.trim()) {
GM_setValue(API_STORAGE_KEY, inputKey.trim());
apiKey = inputKey.trim();
}
}
if (!apiKey) requestApiKey();
// ─── Audio (FIX: lazy init + user-gesture unlock) ────────────────────────
let audioContext = null;
let audioUnlocked = false;
let beepInterval = null;
let alertTriggered = false;
// One-time unlock on first user click anywhere on the page
function unlockAudio() {
if (audioUnlocked) return;
getAudioContext(); // creates context
if (audioContext && audioContext.state === 'suspended') {
audioContext.resume();
}
audioUnlocked = true;
}
document.addEventListener('click', unlockAudio, { once: true });
function getAudioContext() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
return audioContext;
}
function playBeep() {
if (!getSoundEnabled()) return;
const ctx = getAudioContext();
if (ctx.state === 'suspended') {
ctx.resume().then(() => _doBeep(ctx));
} else {
_doBeep(ctx);
}
}
function _doBeep(ctx) {
const oscillator = ctx.createOscillator();
const gainNode = ctx.createGain();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(800, ctx.currentTime);
gainNode.gain.setValueAtTime(0.3, ctx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
oscillator.connect(gainNode);
gainNode.connect(ctx.destination);
oscillator.start(ctx.currentTime);
oscillator.stop(ctx.currentTime + 0.3);
}
function startBeeping() {
if (beepInterval) return;
playBeep();
beepInterval = setInterval(playBeep, 5000);
}
function stopBeeping() {
if (beepInterval) {
clearInterval(beepInterval);
beepInterval = null;
}
}
// ─── Glitter ─────────────────────────────────────────────────────────────
function createGlitterParticle(container) {
const particle = document.createElement('div');
particle.className = 'glitter-particle';
const shapes = ['glitter-star4', 'glitter-star6', 'glitter-diamond'];
particle.classList.add(shapes[Math.floor(Math.random() * shapes.length)]);
const colors = ['color-white', 'color-yellow', 'color-blue'];
particle.classList.add(colors[Math.floor(Math.random() * colors.length)]);
const duration = 3 + Math.random() * 2;
const delay = Math.random() * 2;
const size = 6 + Math.random() * 8;
const twinkle = 0.5 + Math.random() * 1;
particle.style.left = (Math.random() * 100) + '%';
particle.style.width = size + 'px';
particle.style.height = size + 'px';
particle.style.animationDuration = `${duration}s, ${twinkle}s`;
particle.style.animationDelay = delay + 's';
container.appendChild(particle);
setTimeout(() => particle.parentNode && particle.remove(), (duration + delay) * 1000);
}
function createGlitterEffect() {
const container = document.createElement('div');
container.className = 'glitter-container';
document.body.appendChild(container);
for (let i = 0; i < 50; i++) {
setTimeout(() => {
if (document.querySelector('.glitter-container')) createGlitterParticle(container);
}, i * 100);
}
const id = setInterval(() => {
if (!document.querySelector('.glitter-container')) { clearInterval(id); return; }
createGlitterParticle(container);
}, 100);
container.dataset.regenerateInterval = id;
}
function removeGlitter() {
const container = document.querySelector('.glitter-container');
if (container) {
clearInterval(parseInt(container.dataset.regenerateInterval));
container.remove();
}
stopBeeping();
}
// ─── Chain polling ────────────────────────────────────────────────────────
function fetchChainData() {
if (!apiKey) return;
GM_xmlhttpRequest({
method: 'GET',
url: API_URL + apiKey,
onload(response) {
try {
const data = JSON.parse(response.responseText);
if (data.error) {
console.error('API Error:', data.error);
if (data.error.code === 2) {
GM_setValue(API_STORAGE_KEY, '');
apiKey = '';
requestApiKey();
}
return;
}
handleChainData(data.chain);
} catch (e) {
console.error('Failed to parse chain data:', e);
}
},
onerror(e) { console.error('API request failed:', e); }
});
}
function handleChainData(chain) {
const { current, cooldown, end } = chain;
const timeRemaining = end - Math.floor(Date.now() / 1000);
if (current > 0 && timeRemaining > 0 && cooldown === 0 && timeRemaining < userAlertTime) {
triggerAlert();
} else {
alertTriggered = false;
removeGlitter();
}
}
function triggerAlert() {
if (alertTriggered) return;
if (getGlitterEnabled()) createGlitterEffect();
startBeeping();
alertTriggered = true;
}
// ─── Init ─────────────────────────────────────────────────────────────────
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', buildSettingsUI);
} else {
buildSettingsUI();
}
setInterval(fetchChainData, 10000);
fetchChainData();
})();