// ==UserScript==
// @name torn-crime-crack-helper
// @namespace nodelore.torn.crack-helper
// @version 1.0.3
// @description Utilize password database to crack torn cracking crime.
// @author nodelore[2786679]
// @match https://www.torn.com/loader.php?sid=crimes*
// @grant GM_getValue
// @grant GM.getValue
// @grant GM_setValue
// @grant GM.setValue
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant unsafeWindow
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// Avoid duplicate injection
if (window.CRACK_HELPER_INJECTED) {
return;
}
// ========================= Configuration==============================================================================================================================
const CRACKER_SEL = "1m";
const LIMIT = 10;
// add custom password list here, and set CRACKER_SEL to the one you want to choose
const PASSWORD_DATABASE = {
"1m": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-1M.txt",
"1m_alter": "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt",
"100k": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-100K.txt",
"100k_alter": "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-100000.txt",
"10k": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-10K.txt",
"1k": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-1K.txt",
}
// =====================================================================================================================================================================
window.CRACK_HELPER_INJECTED = true;
console.log('Userscript crime cracker starts');
if (GM) {
window.GM_getValue = GM.getValue;
window.GM_setValue = GM.setValue;
}
if(!PASSWORD_DATABASE[CRACKER_SEL]){
console.log("Fail to fetch cracker password");
return;
}
const CRACKER_HELPER_KEY = "CRACKER_HELPER_STORAGE";
let cracker_helper = {
"source": "",
"data": [],
}
let crackerObserver, titleInterval, updateInterval;
const setCrackTitle = (title)=>{
if(titleInterval){
clearInterval(titleInterval);
}
titleInterval = setInterval(()=>{
if($('div[class*=crimeHeading] div[class*=title]').length > 0){
$('div[class*=crimeHeading] div[class*=title]').text(`CRACKING(${title})`)
clearInterval(titleInterval);
titleInterval = undefined;
}
}, 1000)
}
const addStyle = ()=>{
const styles = `
.cracker-detail-panel{
display: flex;
flex-flow: row nowrap;
position: absolute;
width: 310px;
background: #F2F2F2;
box-sizing: border-box;
padding: 5px 0;
z-index: 1000001;
border-radius: 6px;
border: 1px solid rgba(1, 1, 1, .15);
pointer: default;
}
.cracker-detail-panel-minimize{
width: 205px;
}
.cracker-detail-panel-minimize .cracker-current-info{
display: none;
}
.cracker-current-info, .cracker-current-status{
display: flow;
flex-flow: column nowrap;
flex: 1;
border-right: 1px solid rgba(1, 1, 1, .15);
box-sizing: border-box;
padding: 5px;
}
.cracker-button-set{
display: flow;
flex-flow: row nowrap;
width: 100px;
justify-content: space-around;
align-items: center;
box-sizing: border-box;
padding: 5px;
}
.cracker-button-set div{
width: 40px;
height: 40px;
box-shadow: 0 0 6px 2px rgba(1, 1, 1, .08);
border-radius: 50%;
background: #FFF;
font: 24px/normal Arial,Helvetica,sans-serif;
line-height: 40px;
text-align: center;
vertical-align: middle;
font-weight: bold;
}
.cracker-current-info div, .cracker-current-status div{
width: 100%;
color: #000;
font: 12px/normal Arial,Helvetica,sans-serif;
}
`;
const isTampermonkeyEnabled = typeof unsafeWindow !== 'undefined';
if (isTampermonkeyEnabled){
GM_addStyle(styles);
} else {
let style = document.createElement("style");
style.type = "text/css";
style.innerHTML = styles;
document.head.appendChild(style);
}
}
const cracker_record = {};
const handleCrime = (item)=>{
const index = item.index().toString();
let target = "";
item.find("div[class*=charSlot_]").each(function(){
const val = $(this).text().trim();
if(val == ""){
target += "."
}else{
target += val;
}
});
const targetRegex = new RegExp(`^${target}$`);
// console.log(`target Regex is ${targetRegex}`);
setCrackTitle("Calculating");
const result = cracker_helper.data.filter(item => targetRegex.test(item)).slice(0, LIMIT);
cracker_record[index] = result;
setCrackTitle("Done");
const found = $('.cracker-detail-panel').filter(function() {
return $(this).attr('data-attr') === index;
});
if(found.length < 1){
const detailPanel = $(`div.
<div class="cracker-detail-panel" data-attr=${index}>
<div class="cracker-current-info">
<div class="cracker-info-label" style="margin-bottom: 5px;"><b>Current pattern:</b></div>
<div class="cracker-info-text">${targetRegex}</div>
</div>
<div class="cracker-current-status">
<div class="cracker-status-label" style="margin-bottom: 5px;"><b>Total ${result.length} results:</b></div>
<div class="cracker-status-text">${result[0]}</div>
</div>
<div class="cracker-button-set" style="display: flex; flex-flow: row nowrap" data-index="0">
<div title="previous one" data-attr=${index} class="cracker-button-prev" data-action="prev" >🔙</div>
<div title="next one" data-attr=${index} class="cracker-button-next" data-action="next">🔜</div>
</div>
</div>
`)
if(window.innerWidth < 1800){
detailPanel.addClass("cracker-detail-panel-minimize");
}
detailPanel.css({
left: item.offset().left + item.width() + 10,
top: item.offset().top,
height: item.height(),
})
detailPanel.find(".cracker-button-set div").click(function(){
const action = $(this).attr("data-action");
const action_index = $(this).attr("data-attr");
let current_index = parseInt($(this).parent().attr("data-index"));
const action_record = cracker_record[action_index];
if(action_record){
const record_length = action_record.length;
if(action === "next"){
if(current_index < record_length - 1){
current_index += 1;
}
} else if(action === "prev"){
if(current_index > 0){
current_index -= 1;
}
}
$(this).parent().attr("data-index", current_index);
$(this).parent().parent().find(".cracker-status-text").text(action_record[current_index]);
} else{
console.error("Fail to fetch record detail")
}
});
$(body).append(detailPanel);
} else{
found.find('.cracker-info-text').text(targetRegex);
found.find('.cracker-status-label b').text(`Total ${result.length} results:`)
found.find('.cracker-status-text').text(result[0])
found.find('.cracker-button-set').attr("data-index", "0");
}
}
const handlePage = (crimes)=>{
crimes.each(function(){
handleCrime($(this));
})
crackerObserver = new MutationObserver((mutationList)=>{
for(const mut of mutationList){
for(const addedNode of mut.addedNodes){
if(addedNode.tagName === "SPAN" && addedNode.classList.length > 0 && addedNode.classList[0].startsWith("discoveredChar")){
const parent = $(addedNode).parent().parent().parent().parent();
handleCrime(parent);
} else if(addedNode.tagName === "BUTTON" && $(addedNode).attr("aria-label") === "You have already cracked this password"){
const parent = $(addedNode).parent().parent().parent();
if(parent){
const parentIndex = parent.index().toString();
const found = $('.cracker-detail-panel').filter(function() {
return $(this).attr('data-attr') === parentIndex;
});
if(found){
delete cracker_record[parentIndex];
let i = parseInt(parentIndex) + 1;
while(cracker_record[i]){
cracker_record[i-1] = cracker_record[i];
i += 1;
}
found.remove();
}
}
}
}
}
})
crackerObserver.observe($("div.crimes-app")[0], {subtree: true, childList: true})
}
const updatePage = ()=>{
if(location.href.endsWith("cracking")){
const crimes = $('.crime-option');
if(crimes.length < 1){
if(!updateInterval){
updateInterval = setInterval(()=>{
if($('.crime-option').length > 0 && cracker_helper.data.length > 0){
handlePage($('.crime-option'));
clearInterval(updateInterval);
updateInterval = undefined;
}
}, 1000);
}
} else{
handlePage(crimes);
}
} else{
$('.cracker-detail-panel').each(function(){
$(this).remove();
})
}
}
addStyle();
setCrackTitle("Loading")
GM.getValue(CRACKER_HELPER_KEY, cracker_helper).then((cracker)=>{
cracker_helper = cracker;
if(cracker_helper.source == CRACKER_SEL){
setCrackTitle("Loaded")
updatePage();
console.log('load cracker_helper from cache:')
console.log(cracker_helper)
}
else{
console.log(`try to fetch ${PASSWORD_DATABASE[CRACKER_SEL]}`);
setCrackTitle("Loading from network")
GM_xmlhttpRequest({
method: 'GET',
url: PASSWORD_DATABASE[CRACKER_SEL],
timeout: 30000,
onload: (res)=>{
const text = res.responseText;
cracker_helper.data = [];
text.split('\n').forEach((pwd)=>{
cracker_helper.data.push(pwd.trim().replace('\n', ''));
});
cracker_helper.source = CRACKER_SEL;
GM_setValue(CRACKER_HELPER_KEY, cracker_helper);
setCrackTitle("Loaded")
updatePage();
console.log('load cracker_helper from network:')
console.log(cracker_helper)
},
onerror: (res)=>{
console.error(`error: ${res}`);
},
ontimeout: ()=>{
console.error('timeout')
}
})
}
}).catch((err)=>{
console.error(err);
})
window.onhashchange = ()=>{
if(crackerObserver){
crackerObserver.disconnect();
}
updatePage();
}
})();