// ==UserScript==
// @name Torn Pickpocket Module
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Enhanced pickpocketing with color coding and auto-picker - Framework Module
// @match https://www.torn.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant unsafeWindow
// @license MIT (With Credit)
// ==/UserScript==
(function() {
'use strict';
// Use unsafeWindow to match framework
const globalWindow = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window;
console.log('[PICKPOCKET] Module starting...');
// PICKPOCKETING CONFIG
const categoryColorMap = {
"Safe": "#37b24d",
"Moderately Unsafe": "#74b816",
"Unsafe": "#f59f00",
"Risky": "#f76707",
"Dangerous": "#f03e3e",
"Very Dangerous": "#7048e8",
};
const markGroups = {
"Safe": ["Drunk man", "Drunk woman", "Homeless person", "Junkie", "Elderly man", "Elderly woman"],
"Moderately Unsafe": ["Classy lady", "Laborer", "Postal worker", "Young man", "Young woman", "Student"],
"Unsafe": ["Rich kid", "Sex worker", "Thug"],
"Risky": ["Jogger", "Businessman", "Businesswoman", "Gang member", "Mobster"],
"Dangerous": ["Cyclist"],
"Very Dangerous": ["Police officer"],
};
const activityTypes = ["Walking", "Stumbling", "Loitering", "Listening to music", "Distracted", "Soliciting", "Running", "Jogging"];
// Settings
let ppSettings = {
coloringEnabled: GM_getValue('ppColoringEnabled', true),
autoPickerEnabled: GM_getValue('ppAutoPickerEnabled', false),
targetCategories: GM_getValue('ppTargetCategories', ["Safe"]),
targetActivities: GM_getValue('ppTargetActivities', ["Stumbling"]),
autoPickDelay: GM_getValue('ppAutoPickDelay', 3000),
maxNerve: GM_getValue('ppMaxNerve', 5),
randomDelay: GM_getValue('ppRandomDelay', true),
maxActions: GM_getValue('ppMaxActions', 50),
safetyMode: GM_getValue('ppSafetyMode', true)
};
// State
let processedTargets = new Set();
let autoPickerInterval = null;
let actionCount = 0;
let sessionStart = Date.now();
function saveSettings() {
Object.keys(ppSettings).forEach(key => {
GM_setValue(`pp${key.charAt(0).toUpperCase() + key.slice(1)}`, ppSettings[key]);
});
}
function log(msg, type = 'info') {
if (globalWindow.TornFramework && globalWindow.TornFramework.log) {
globalWindow.TornFramework.log(msg, type, 'PICKPOCKET');
} else {
console.log(`[PICKPOCKET] ${msg}`);
}
}
function addRandomDelay(baseDelay) {
if (!ppSettings.randomDelay) return baseDelay;
const variance = baseDelay * 0.3;
return baseDelay + (Math.random() * variance * 2 - variance);
}
function isRateLimited() {
if (!ppSettings.safetyMode) return false;
if (actionCount >= ppSettings.maxActions) {
return true;
}
return false;
}
function processCurrentTargets() {
if (!ppSettings.coloringEnabled) return;
try {
const targetElements = document.querySelectorAll('.titleAndProps___DdeVu > div');
targetElements.forEach((titleDiv) => {
const titleText = titleDiv.textContent.trim().split(' (')[0];
if (!titleText) return;
let category = null;
for (const cat in markGroups) {
if (markGroups[cat].includes(titleText)) {
category = cat;
break;
}
}
if (category) {
titleDiv.style.color = categoryColorMap[category];
titleDiv.style.fontWeight = 'bold';
titleDiv.style.textShadow = '0 1px 2px rgba(0,0,0,0.5)';
if (!titleDiv.textContent.includes(`(${category})`)) {
titleDiv.textContent = `${titleText} (${category})`;
}
}
});
} catch (error) {
log(`Target processing failed: ${error.message}`, 'error');
}
}
function getCurrentNerve() {
try {
const nerveElement = document.querySelector('.bar-value___NTdce');
if (nerveElement) {
const nerveText = nerveElement.textContent;
const match = nerveText.match(/(\d+)/);
return match ? parseInt(match[1]) : 0;
}
const altNerveElement = document.querySelector('[class*="nerve"] .bar-value, .nerve-bar .bar-value');
if (altNerveElement) {
const match = altNerveElement.textContent.match(/(\d+)/);
return match ? parseInt(match[1]) : 0;
}
return 100;
} catch (error) {
log(`Nerve reading failed: ${error.message}`, 'error');
return 0;
}
}
function getCategoryPriority(category) {
const priorities = {
"Safe": 1,
"Moderately Unsafe": 2,
"Unsafe": 3,
"Risky": 4,
"Dangerous": 5,
"Very Dangerous": 6
};
return priorities[category] || 999;
}
function findBestTarget() {
if (isRateLimited()) {
log('Rate limited - skipping target search', 'warning');
return null;
}
try {
const crimeOptions = document.querySelectorAll('.crime-option');
const currentNerve = getCurrentNerve();
if (currentNerve < 1) {
log('Insufficient nerve for pickpocketing', 'warning');
return null;
}
const validTargets = [];
for (const option of crimeOptions) {
const titleElement = option.querySelector('.titleAndProps___DdeVu > div');
const activityElement = option.querySelector('.activity___e7mdA');
const buttonElement = option.querySelector('.commit-button');
if (!titleElement || !buttonElement) continue;
const titleText = titleElement.textContent.trim().split(' (')[0];
const activityText = activityElement ? activityElement.textContent.trim().split('\n')[0] : '';
const ariaLabel = buttonElement.getAttribute('aria-label') || '';
const nerveMatch = ariaLabel.match(/(\d+)\s*nerve/i);
const nerveCost = nerveMatch ? parseInt(nerveMatch[1]) : 5;
if (currentNerve < nerveCost || nerveCost > ppSettings.maxNerve) continue;
if (buttonElement.getAttribute('aria-disabled') === 'true') continue;
let targetCategory = null;
for (const cat in markGroups) {
if (markGroups[cat].includes(titleText)) {
targetCategory = cat;
break;
}
}
if (!targetCategory || !ppSettings.targetCategories.includes(targetCategory)) continue;
if (ppSettings.targetActivities.length > 0) {
if (!ppSettings.targetActivities.some(activity => activityText.includes(activity))) continue;
}
validTargets.push({
element: buttonElement,
title: titleText,
activity: activityText,
category: targetCategory,
nerve: nerveCost,
priority: getCategoryPriority(targetCategory)
});
}
validTargets.sort((a, b) => {
if (a.priority !== b.priority) return a.priority - b.priority;
return a.nerve - b.nerve;
});
return validTargets[0] || null;
} catch (error) {
log(`Target search failed: ${error.message}`, 'error');
return null;
}
}
function performAutoPick(isTest = false) {
try {
const target = findBestTarget();
if (!target) {
const msg = isTest ? 'No valid targets available for test' : 'No targets match current criteria';
log(msg, 'warning');
return false;
}
if (isTest) {
log(`Test successful: Would pick ${target.title} (${target.category}) - ${target.activity} [${target.nerve} nerve]`, 'success');
return true;
}
if (ppSettings.safetyMode && isRateLimited()) {
log('Safety mode: Action blocked due to rate limiting', 'warning');
return false;
}
target.element.click();
actionCount++;
log(`Auto-picked: ${target.title} (${target.category}) [${target.nerve} nerve] - Activity: ${target.activity}`, 'success');
processedTargets.add(target.title + target.activity);
setTimeout(() => {
processedTargets.delete(target.title + target.activity);
}, 30000);
return true;
} catch (error) {
log(`Auto pick failed: ${error.message}`, 'error');
return false;
}
}
function startAutoPicker() {
if (autoPickerInterval) return;
try {
log('Auto picker starting...', 'success');
autoPickerInterval = setInterval(() => {
if (ppSettings.autoPickerEnabled && isOnPickpocketPage()) {
if (ppSettings.safetyMode && isRateLimited()) {
log('Auto picker paused - rate limit reached', 'warning');
return;
}
performAutoPick();
}
}, addRandomDelay(ppSettings.autoPickDelay));
} catch (error) {
log(`Auto picker start failed: ${error.message}`, 'error');
}
}
function stopAutoPicker() {
if (autoPickerInterval) {
clearInterval(autoPickerInterval);
autoPickerInterval = null;
log('Auto picker stopped', 'warning');
}
}
function isOnPickpocketPage() {
return window.location.href.includes('crimes') &&
(document.querySelector('.pickpocketing-root') !== null ||
document.querySelector('[class*="pickpocket"]') !== null ||
document.querySelector('.crime-option') !== null);
}
// Create menu section HTML
const menuSection = `
<h4 style="margin: 0 0 12px 0; color: #37b24d;">👤 Pickpocketing</h4>
<div style="display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center; margin-bottom: 12px;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="checkbox" id="ppColoringEnabled" ${ppSettings.coloringEnabled ? 'checked' : ''}>
<span style="margin-left: 8px;">Target Color Coding</span>
</label>
<div style="font-size: 10px; color: #999;">Visual aid</div>
</div>
<div style="display: flex; align-items: center; margin-bottom: 12px; padding: 8px; background: rgba(55,178,77,0.1); border-radius: 6px;">
<label style="display: flex; align-items: center; cursor: pointer; flex-grow: 1;">
<input type="checkbox" id="ppAutoPickerEnabled" ${ppSettings.autoPickerEnabled ? 'checked' : ''}>
<span style="margin-left: 8px; font-weight: bold; color: #37b24d;">🤖 Auto Picker</span>
</label>
<div id="ppAutoPickStatus" style="padding: 4px 8px; border-radius: 4px; font-size: 10px; font-weight: bold; background: ${ppSettings.autoPickerEnabled ? '#37b24d' : '#666'}; color: white;">
${ppSettings.autoPickerEnabled ? 'ACTIVE' : 'DISABLED'}
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px;">
<div>
<label style="display: block; margin-bottom: 4px; font-size: 10px;">Base Delay (ms):</label>
<input type="number" id="ppAutoPickDelay" value="${ppSettings.autoPickDelay}" min="1000" max="30000" step="500" style="width: 100%; padding: 6px; background: #333; color: white; border: 1px solid #555; border-radius: 4px; font-size: 11px;">
</div>
<div>
<label style="display: block; margin-bottom: 4px; font-size: 10px;">Max Nerve Cost:</label>
<input type="number" id="ppMaxNerve" value="${ppSettings.maxNerve}" min="1" max="50" style="width: 100%; padding: 6px; background: #333; color: white; border: 1px solid #555; border-radius: 4px; font-size: 11px;">
</div>
</div>
<div style="margin-bottom: 12px;">
<label style="display: block; margin-bottom: 6px; font-weight: bold; font-size: 11px;">🎯 Target Categories:</label>
<div id="ppCategoryCheckboxes" style="max-height: 120px; overflow-y: auto; border: 1px solid #555; padding: 8px; border-radius: 4px; background: rgba(0,0,0,0.3);">
${Object.keys(markGroups).map(category => `
<label style="display: block; margin-bottom: 4px; cursor: pointer; font-size: 11px; padding: 2px 4px; border-radius: 3px; transition: background 0.2s;">
<input type="checkbox" class="ppCategoryCheck" value="${category}" ${ppSettings.targetCategories.includes(category) ? 'checked' : ''}>
<span style="margin-left: 6px; color: ${categoryColorMap[category]}; font-weight: bold;">●</span>
<span style="margin-left: 4px;">${category}</span>
</label>
`).join('')}
</div>
</div>
<div style="margin-bottom: 12px;">
<label style="display: block; margin-bottom: 6px; font-weight: bold; font-size: 11px;">🚶 Target Activities:</label>
<div id="ppActivityCheckboxes" style="max-height: 100px; overflow-y: auto; border: 1px solid #555; padding: 8px; border-radius: 4px; background: rgba(0,0,0,0.3);">
${activityTypes.map(activity => `
<label style="display: block; margin-bottom: 3px; cursor: pointer; font-size: 10px; padding: 2px 4px; border-radius: 3px; transition: background 0.2s;">
<input type="checkbox" class="ppActivityCheck" value="${activity}" ${ppSettings.targetActivities.includes(activity) ? 'checked' : ''}>
<span style="margin-left: 6px;">${activity}</span>
</label>
`).join('')}
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 12px;">
<div>
<label style="display: block; margin-bottom: 4px; font-size: 10px;">Max Actions/Hour:</label>
<input type="number" id="ppMaxActions" value="${ppSettings.maxActions}" min="10" max="200" style="width: 100%; padding: 6px; background: #333; color: white; border: 1px solid #555; border-radius: 4px; font-size: 10px;">
</div>
<div style="display: flex; align-items: end;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input type="checkbox" id="ppSafetyMode" ${ppSettings.safetyMode ? 'checked' : ''}>
<span style="margin-left: 6px; font-size: 10px;">Safety Mode</span>
</label>
</div>
</div>
<button id="ppTestPick" style="width: 100%; padding: 8px; background: linear-gradient(45deg, #37b24d, #51cf66); border: none; color: white; border-radius: 6px; cursor: pointer; font-size: 11px; font-weight: bold; box-shadow: 0 2px 8px rgba(55,178,77,0.3); transition: all 0.2s;">🧪 Test Auto Pick</button>
`;
function setupEventHandlers() {
// Coloring toggle
const coloringToggle = document.getElementById('ppColoringEnabled');
if (coloringToggle) {
coloringToggle.onchange = function() {
ppSettings.coloringEnabled = this.checked;
saveSettings();
setTimeout(processCurrentTargets, 100);
log(`Color coding ${this.checked ? 'enabled' : 'disabled'}`, 'info');
};
}
// Auto picker toggle
const autoPickToggle = document.getElementById('ppAutoPickerEnabled');
if (autoPickToggle) {
autoPickToggle.onchange = function() {
ppSettings.autoPickerEnabled = this.checked;
const status = document.getElementById('ppAutoPickStatus');
if (status) {
status.textContent = ppSettings.autoPickerEnabled ? 'ACTIVE' : 'DISABLED';
status.style.background = ppSettings.autoPickerEnabled ? '#37b24d' : '#666';
}
if (ppSettings.autoPickerEnabled && isOnPickpocketPage() && !isRateLimited()) {
startAutoPicker();
} else {
stopAutoPicker();
}
saveSettings();
log(`Auto picker ${this.checked ? 'enabled' : 'disabled'}`, 'info');
};
}
// Number inputs
['ppAutoPickDelay', 'ppMaxNerve', 'ppMaxActions'].forEach(id => {
const input = document.getElementById(id);
if (input) {
input.onchange = function() {
const setting = id.replace('pp', '').charAt(0).toLowerCase() + id.replace('pp', '').slice(1);
ppSettings[setting] = parseInt(this.value);
saveSettings();
// Restart auto picker if running and delay changed
if (id === 'ppAutoPickDelay' && autoPickerInterval) {
stopAutoPicker();
startAutoPicker();
}
};
}
});
// Safety mode toggle
const safetyToggle = document.getElementById('ppSafetyMode');
if (safetyToggle) {
safetyToggle.onchange = function() {
ppSettings.safetyMode = this.checked;
saveSettings();
log(`Safety mode ${this.checked ? 'enabled' : 'disabled'}`, 'info');
};
}
// Category checkboxes
document.querySelectorAll('.ppCategoryCheck').forEach(checkbox => {
checkbox.onchange = () => {
ppSettings.targetCategories = Array.from(document.querySelectorAll('.ppCategoryCheck:checked')).map(cb => cb.value);
saveSettings();
log(`Target categories updated: ${ppSettings.targetCategories.join(', ')}`, 'info');
};
});
// Activity checkboxes
document.querySelectorAll('.ppActivityCheck').forEach(checkbox => {
checkbox.onchange = () => {
ppSettings.targetActivities = Array.from(document.querySelectorAll('.ppActivityCheck:checked')).map(cb => cb.value);
saveSettings();
log(`Target activities updated: ${ppSettings.targetActivities.join(', ')}`, 'info');
};
});
// Test button
const testBtn = document.getElementById('ppTestPick');
if (testBtn) {
testBtn.onclick = () => {
log('Testing auto pick functionality...', 'warning');
const result = performAutoPick(true);
if (!result) {
log('Test failed - check your target settings or visit crimes page', 'error');
}
};
}
}
// Module configuration
const moduleConfig = {
name: 'Pickpocket',
version: '1.1',
description: 'Enhanced pickpocketing with color coding and auto-picker',
menuSection: menuSection,
initialize: function() {
log('Pickpocket module initializing...', 'info');
setupEventHandlers();
// Start observer for target processing
const observer = new MutationObserver(() => {
if (ppSettings.coloringEnabled) {
clearTimeout(observer.debounceTimer);
observer.debounceTimer = setTimeout(processCurrentTargets, 300);
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Initial target processing
setTimeout(processCurrentTargets, 1000);
// Auto-start if enabled and on pickpocket page
if (ppSettings.autoPickerEnabled && isOnPickpocketPage()) {
setTimeout(startAutoPicker, 2000);
log('Auto picker will start in 2 seconds (on pickpocket page)', 'info');
}
log('Pickpocket module initialized successfully', 'success');
},
cleanup: function() {
stopAutoPicker();
log('Pickpocket module cleaned up', 'info');
},
isActive: function() {
return ppSettings.autoPickerEnabled && autoPickerInterval !== null && isOnPickpocketPage();
}
};
function initializePickpocketModule() {
log('Attempting to register pickpocket module with framework...', 'info');
// Register with framework
if (globalWindow.TornFramework.registerModule(moduleConfig)) {
log('Pickpocket module registered successfully', 'success');
// Page change detection
let currentUrl = window.location.href;
const urlObserver = new MutationObserver(() => {
if (window.location.href !== currentUrl) {
currentUrl = window.location.href;
if (!isOnPickpocketPage() && autoPickerInterval) {
stopAutoPicker();
log('Left pickpocket page - auto picker stopped', 'info');
} else if (isOnPickpocketPage() && ppSettings.autoPickerEnabled && !autoPickerInterval) {
setTimeout(startAutoPicker, 1000);
log('Entered pickpocket page - auto picker starting', 'info');
}
}
});
urlObserver.observe(document.body, { childList: true, subtree: true });
} else {
log('Failed to register pickpocket module', 'error');
}
}
// Wait for framework
function waitForFramework() {
console.log('[PICKPOCKET] Checking for framework...');
if (globalWindow.TornFramework && globalWindow.TornFramework.initialized) {
console.log('[PICKPOCKET] Framework found, initializing module');
initializePickpocketModule();
} else {
console.log('[PICKPOCKET] Framework not ready, waiting...');
setTimeout(waitForFramework, 500);
}
}
// Start initialization
waitForFramework();
})();