// ==UserScript== // @name HIT Forker // @version 0.6.3 // @description Monitors mturk.com for HITs // @author ThisPoorGuy // @icon http://nopurpose.org/stuff/avatars/Lj21396.gif // @include https://worker.mturk.com/?finder_beta // @include https://worker.mturk.com/?hit_forker // @grant GM_log // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @connect turkerview.com // @require https://code.jquery.com/jquery-3.1.0.min.js // @namespace https://greasyfork.org/users/163167 // @downloadURL none // ==/UserScript== // Acknowledgements // The core of this script was forked/stolen/adapted from Kadauchi's Hit Finder Beta script. Coding assistance in spots // provided by Salem Beats and ChrisTurk. TurkerView was created by ChrisTurk. This script hooks into JR Panda Crazy // Changelog // 0.6.3 - Bringing PC functionality in house // 0.6.1 - Fixed panda button shading // 0.6.0 - Re-jiggered panda crazy integration code to use Salem's PC library. Now detects if panda crazy is actually running when you click. // 0.5.3 - Slight modifications to break integration with JR Panda Crazy, due to a memory leak issue. Will reach out to dev to see if we can clear it up and be happy together... // 0.5.2 - Code cleanup. // 0.5.1 - Fixed the panda buttons on the Found hits table. // 0.5.0 - Hitting the panda button now sends full HIT name, pay and requester name info to panda crazy too. Have you noticed these version number leaps are pretty arbitratry? // - Fixed a stupid typo in a variable name. // 0.4.3 - Switched TO request from a .get call to a .ajax call with a timeout to prevent the entire thing from exploding when TO's servers do. // 0.4.2 - Link to TV Requester profile in export. // 0.4.1 - Show/Hide HITs and Logged Hits settings are now saved across sessions // 0.4.0 - Blocking a HIT or a Requester will now remove that Hit or all hits from said requester from display in the Hit Log // 0.3.5 - Added a button to hide the new HITS table. Moved the button to hide the logged hits for consistancy. // 0.3.2 - Modified icon for desktop notifications. Added Requester TV score to hit export // 0.3.1 - Fix for amazon screwing with things. Thanks ChrisTurk! // 0.3.0 - Under the hood changes, removed code for running on www, added new launch URL, old ?finder_beta will be phased out eventually // 0.2.9 - Now acceptable to people with red/green color blindness! // 0.2.6 - Fixed a minor error which caused colors to not work properly. // 0.2.5 - Changed Coloration to respect TV reviews FIRST and then fall back on TO Values. Also changed colors. // 0.2.0 - Added TurkerView Hourly ratings to HIT results, fixed export links. // 0.1.5 - Some minor UI tweaks // 0.1.4 - Added some indication that you have already clicked a button to send HIT to PC. Only works in log currently. // 0.1.3 - Fixed issue with the Panda buttons in the HIT log not having the right GID // 0.1.2 - Fixed an issue with HITs that have double quotes in the title not working with the ignore hit by title button. I think. // 0.1.1 - Cleaned up the header, removed unused audio files // 0.1.0 - Made modifications to launch links with worker website. Added buttons to send information to PandaCrazy directly instead of copying link // TODO: // Remove www code // Clean up interface // Delete the above todos because they're already done. var worker = true; var _config = JSON.parse(localStorage.getItem('_finder')) || {}; var blocklist = JSON.parse(localStorage.getItem('_finder_bl')) || {}; var includelist = JSON.parse(localStorage.getItem('_finder_il')) || {}; // Compatability check if (_config.version !== '1.1') { _config = {}; } var config = { version : _config.version || '1.1', delay : _config.delay || '3', type : _config.type || 'LastUpdatedTime%3A1&pageSize=', size : _config.size || '25', rew : _config.rew || '0.00', avail : _config.avail || '0', mto : _config.mto || '0.00', alert : _config.alert || '0', qual : _config.hasOwnProperty('qual') ? _config.qual : true, new : _config.hasOwnProperty('new') ? _config.new : true, newaudio : _config.newaudio || 'beep', pb : _config.hasOwnProperty('pb') ? _config.pb : false, to : _config.hasOwnProperty('to') ? _config.to : true, nl : _config.hasOwnProperty('nl') ? _config.nl : false, bl : _config.hasOwnProperty('bl') ? _config.bl : false, m : _config.hasOwnProperty('m') ? _config.m : false, push : _config.push || 'access_token_here', theme : _config.theme || 'default', custom : _config.custom || {main: 'FFFFFF', primary: 'CCCCCC', secondary: '111111', text: '000000', link: '0000EE', visited: '551A8B', prop : false}, to_theme : _config.to_theme || '1', h_hidden : _config.h_hidden || '0', l_hidden : _config.l_hidden || '0' }; var themes = { 'default' : {main: 'FFFFFF', primary: 'CCCCCC', secondary: '111111', menu: '373b44', menutext: 'FFFFFF', text: '000000', link: '0000EE', visited: '551A8B', prop : true}, 'light' : {main: 'FFFFFF', primary: 'CCCCCC', secondary: '111111', menu: '373b44', menutext: 'FFFFFF', text: '000000', link: '0000EE', visited: '551A8B', prop : true}, 'dark' : {main: '404040', primary: '666666', secondary: 'FFFFFF', menu: '202020', menutext: 'FFFFFF', text: 'FFFFFF', link: 'FFFFFF', visited: 'B3B3B3', prop : true}, 'darker' : {main: '000000', primary: '262626', secondary: 'FFFFFF', menu: '373b44', menutext: 'FFFFFF', text: 'FFFFFF', link: 'FFFFFF', visited: 'B3B3B3', prop : true}, 'custom' : config.custom }; var turkerview = { }; var turkerview_update = 0; var searches = 0, logged = 0, hitlog = {}, noti_delay = [], push_delay = []; // General Configuration variables var url, upd, num, rew, minrew, searchqual, pandaurl; url = 'https://worker.mturk.com/?'; pandaurl = 'https://worker.mturk.com'; upd = '&sort=updated_desc&page_size='; num = '&sort=num_hits_desc&page_size='; rew = '&sort=reward_desc&page_size='; minrew = '&filters%5Bmin_reward%5D='; searchqual = '&filters%5Bqualified='; var PandaCrazy = (function createPandaCrazy() { let _self = this; let _lastSentPingTime; let _lastReceivedPongTime; let _onlineSinceLastPing; let _pcListener; const MAX_WAIT_FOR_PANDA_CRAZY_RESPONSE_MS = 1000; function ping() { _lastSentPingTime = Date.now(); localStorage.setItem("JR_message_ping_pandacrazy", `{"theTarget": "${Math.random()}"}`); } function hasIndicatedOnlineSinceLastPing() { if(_lastSentPingTime !== undefined && _lastReceivedPongTime !== undefined) { return _lastReceivedPongTime >= _lastSentPingTime; } else { return undefined; } } function online() { function respondToStorage(resolve, reject, e) { if(e.key.includes("JR_message_pong") && Boolean(e.newValue)) { _lastReceivedPongTime = Date.now(); let pongData = JSON.parse(e.newValue); let lag = Number(pongData.time) - Number(_lastReceivedPongTime); if(hasIndicatedOnlineSinceLastPing()) { resolve("online"); } } } let isOnlinePromise = new Promise((resolve, reject) => { setTimeout(() => {reject("timeout");}, MAX_WAIT_FOR_PANDA_CRAZY_RESPONSE_MS); if(_pcListener) {window.removeEventListener("storage", _pcListener);} _pcListener = respondToStorage.bind(window, resolve, reject); window.addEventListener("storage", _pcListener); /* window.addEventListener("storage", e => { // console.log("Storage Event", e); if(e.key.includes("JR_message_pong") && Boolean(e.newValue)) { _lastReceivedPongTime = Date.now(); let pongData = JSON.parse(e.newValue); let lag = Number(pongData.time) - Number(_lastReceivedPongTime); if(hasIndicatedOnlineSinceLastPing()) { resolve("online"); } } }); */ }); ping(); return isOnlinePromise; } function addJob(gid, once, metadata) { let commandString = once ? "addOnceJob" : "addJob"; localStorage.setItem("JR_message_pandacrazy", JSON.stringify({ time: Date.now(), command: commandString, data: { groupId: gid, title: (metadata ? metadata.hitTitle || metadata.title : undefined), requesterName: (metadata ? metadata.requesterName : undefined), requesterId: (metadata ? metadata.requesterID || metadata.requesterId || metadata.rid : undefined), pay: (metadata ? metadata.hitValue || metadata.pay : undefined), duration: (metadata ? metadata.duration : undefined), hitsAvailable: (metadata ? metadata.hitsAvailable : undefined) } })); } function startJob(gid) { localStorage.setItem("JR_message_pandacrazy", JSON.stringify({ time: Date.now(), command: "startcollect", data: { groupId: gid } })); } return { addJob, startJob, ping, online }; })(); $('head').html( 'HIT Forker' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '