Prayer timer for Torn that tracks time since last prayer
// ==UserScript==
// @name Prayer Timer
// @namespace Randy's Tools
// @version 2.3
// @description Prayer timer for Torn that tracks time since last prayer
// @author Randy
// @match https://www.torn.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
const STORAGE_KEY = 'torn_prayer_timer';
const HOURS_48 = 48 * 60 * 60 * 1000; // 48 hours in milliseconds
const HOURS_36 = 36 * 60 * 60 * 1000; // 36 hours in milliseconds
const HOURS_24 = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
let timerElement;
let intervalId;
let initAttempts = 0;
const maxInitAttempts = 10;
// Create the timer button
function createTimerButton() {
// Remove existing timer if it exists
const existing = document.getElementById('prayer-timer-btn');
if (existing) {
existing.remove();
}
const button = document.createElement('div');
button.id = 'prayer-timer-btn';
button.style.cssText = `
position: fixed;
top: 10px;
left: 10px;
z-index: 10000;
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
color: white;
padding: 8px 12px;
border-radius: 8px;
font-family: Arial, sans-serif;
font-size: 12px;
font-weight: bold;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
border: 2px solid rgba(255,255,255,0.2);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
min-width: 140px;
text-align: center;
user-select: none;
cursor: pointer;
`;
// Add click handler to navigate to church
button.addEventListener('click', function() {
window.location.href = 'https://www.torn.com/church.php';
});
// Add hover effects
button.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.05)';
this.style.boxShadow = '0 6px 16px rgba(0,0,0,0.4)';
});
button.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
this.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
});
document.body.appendChild(button);
timerElement = button;
console.log('Prayer timer button created successfully');
return button;
}
// Get stored timer data
function getTimerData() {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
try {
return JSON.parse(stored);
} catch (e) {
console.error('Error parsing timer data:', e);
}
}
return null;
}
// Save timer data
function saveTimerData(data) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
// Reset the timer (start from 0)
function resetTimer() {
const now = Date.now();
saveTimerData({
startTime: now,
isActive: true
});
console.log('Prayer timer reset - starting from 0');
}
// Format time elapsed
function formatTimeElapsed(milliseconds) {
const hours = Math.floor(milliseconds / (1000 * 60 * 60));
const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
if (hours > 0) {
return `${hours}h ${minutes}m ${seconds}s`;
} else if (minutes > 0) {
return `${minutes}m ${seconds}s`;
} else {
return `${seconds}s`;
}
}
// Update timer display
function updateTimerDisplay() {
if (!timerElement) {
console.warn('Timer element not found, attempting to recreate...');
if (document.body) {
createTimerButton();
}
return;
}
const timerData = getTimerData();
if (!timerData || !timerData.isActive) {
// No timer data, start from 0
resetTimer();
timerElement.innerHTML = '⛪ 0s';
timerElement.style.background = 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)';
return;
}
const now = Date.now();
const timeElapsed = now - timerData.startTime;
if (timeElapsed >= HOURS_48) {
// Past 48 hours - shame message
timerElement.innerHTML = '💀 You suck, try again';
timerElement.style.background = 'linear-gradient(135deg, #424242 0%, #212121 100%)';
} else if (timeElapsed >= HOURS_36) {
// 36-48 hours - red
const formatted = formatTimeElapsed(timeElapsed);
timerElement.innerHTML = `🔴 ${formatted}`;
timerElement.style.background = 'linear-gradient(135deg, #f44336 0%, #d32f2f 100%)';
} else if (timeElapsed >= HOURS_24) {
// 24-36 hours - yellow
const formatted = formatTimeElapsed(timeElapsed);
timerElement.innerHTML = `🟡 ${formatted}`;
timerElement.style.background = 'linear-gradient(135deg, #ff9800 0%, #f57c00 100%)';
} else {
// 0-24 hours - green
const formatted = formatTimeElapsed(timeElapsed);
timerElement.innerHTML = `🟢 ${formatted}`;
timerElement.style.background = 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)';
}
}
// Monitor for pray button clicks ONLY
function monitorPrayButton() {
document.addEventListener('click', function(e) {
const target = e.target;
// Only check if we're on the church page
if (!window.location.href.includes('/church.php')) {
return;
}
// Check for exact "Pray" text or value
const text = target.textContent?.trim() || '';
const value = target.value?.trim() || '';
// Look for exact "Pray" match (case insensitive)
if (text.toLowerCase() === 'pray' || value.toLowerCase() === 'pray') {
console.log('Pray button clicked - resetting timer');
setTimeout(() => {
resetTimer();
updateTimerDisplay();
}, 100);
}
// Also check parent elements in case the text is in a span inside a button
let parent = target.parentElement;
while (parent && parent !== document.body) {
const parentText = parent.textContent?.trim() || '';
const parentValue = parent.value?.trim() || '';
if (parentText.toLowerCase() === 'pray' || parentValue.toLowerCase() === 'pray') {
console.log('Pray button (parent) clicked - resetting timer');
setTimeout(() => {
resetTimer();
updateTimerDisplay();
}, 100);
break;
}
parent = parent.parentElement;
}
});
}
// Check if timer is visible and recreate if needed
function ensureTimerVisible() {
const existing = document.getElementById('prayer-timer-btn');
if (!existing && document.body) {
console.log('Timer not found, recreating...');
createTimerButton();
updateTimerDisplay();
}
}
// Initialize the extension with retry mechanism
function init() {
initAttempts++;
// Wait for document body to be available
if (!document.body) {
if (initAttempts < maxInitAttempts) {
console.log(`Init attempt ${initAttempts}: waiting for document.body...`);
setTimeout(init, 100);
} else {
console.error('Failed to initialize: document.body not available');
}
return;
}
// Create timer button
createTimerButton();
// Start timer updates
updateTimerDisplay();
// Clear any existing interval
if (intervalId) {
clearInterval(intervalId);
}
intervalId = setInterval(updateTimerDisplay, 1000);
// Monitor for pray button clicks
monitorPrayButton();
// Set up visibility check interval
setInterval(ensureTimerVisible, 5000); // Check every 5 seconds
console.log('Torn Prayer Timer initialized successfully');
}
// Multiple initialization strategies
function startInitialization() {
// Strategy 1: Immediate if DOM is ready
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(init, 50);
}
// Strategy 2: DOM Content Loaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(init, 100);
});
}
// Strategy 3: Window load (fallback)
window.addEventListener('load', () => {
setTimeout(init, 200);
});
// Strategy 4: Immediate retry (for dynamic pages)
setTimeout(() => {
if (!document.getElementById('prayer-timer-btn')) {
init();
}
}, 500);
}
// Handle page navigation and dynamic content
let currentUrl = location.href;
const observer = new MutationObserver(() => {
if (location.href !== currentUrl) {
currentUrl = location.href;
// Ensure timer is still visible after navigation
setTimeout(ensureTimerVisible, 1000);
}
});
// Start observing when body is available
function startObserver() {
if (document.body) {
observer.observe(document.body, {
childList: true,
subtree: true
});
} else {
setTimeout(startObserver, 100);
}
}
// Start everything
startInitialization();
startObserver();
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
if (intervalId) {
clearInterval(intervalId);
}
observer.disconnect();
});
})();