// ==UserScript== // @name AutoPerfReports // @namespace http://tampermonkey.net/ // @version 0.6 // @description Automatically pull data out of Riverbed for the previous day. // @author joe.tretter@kcc.com // @match https://ttw.gws.kcc.com/* // @grant window.close // ==/UserScript== (function() { 'use strict'; console.log("Start"); let today = new Date(); let simulateDate = new Date(); // simulation is using yesterday's date. simulateDate.setDate(today.getDate() - 1); // To overwrite the simulate date uncomment the below and change the date (remember that the month starts at 0! //simulateDate = new Date(2019,1,17); simulateDate = new Date(simulateDate.getFullYear(), simulateDate.getMonth(), simulateDate.getDate()); let timestamp = simulateDate.getTime() / 1000; let rivUrl = "https://ttw.gws.kcc.com/#search:time=" + timestamp + "+1435&search=Hello&server=ustca089.kcc.com&serverTableKey=ustca089.kcc.com"; window.location.href = rivUrl; // no cleartext password function encryptXor(text, key) { return Array.from( text, (c, i) => String.fromCharCode(c.charCodeAt() ^ key.charCodeAt(i % key.length)) ).join(''); } // if the login dialog pops up -> log in. function loginThread(credentials) { let myInterval; myInterval= window.setInterval(()=>{ if (document.getElementById("usernameField") !== null) { // If we don't have credentials saved we ask for credentials. // If the login dialog comes up again in less than 10 seconds we assume the password changed and don't retry automatically to prevent account locking. if ((credentials === null) || ((localStorage.getItem("LastLoginDate") !== null) && ((new Date().getTime() - localStorage.getItem("LastLoginDate")) / 1000 < 10 ))) { clearInterval(myInterval); window.document.title="[RiverRobot] Login unsuccessful."; console.log("Login unsuccessful."); document.getElementById("loginSubmitBtn").value="RiverRobot - Save Credentials + Login"; // change the Form tag to a div tag so that we can run some javascript rather than submit the form. var theForm = document.getElementsByTagName('form')[0]; var theContainer = document.createElement('div'); theContainer.classList="module"; theContainer.innerHTML = theForm.innerHTML; theForm.parentNode.replaceChild(theContainer, theForm); // Credentials are stored encrypted in the Browsen's localstorage (yes, it's NOT safe, but it's a hurdle) document.getElementById("loginSubmitBtn").onclick=function(){ credentials={username:document.getElementById("usernameField").value,passwordCrypt:encryptXor(document.getElementById("passwordField").value, '5346245634634765377')}; localStorage.setItem("riverRobotCredentials",JSON.stringify(credentials)); alert('Credentials Saved.'); window.location.reload(); }; document.getElementById("loginSubmitBtn").onkeydown=onclick; } else { window.document.title="[RiverRobot] Logging In"; localStorage.setItem("LastLoginDate",new Date().getTime()) clearInterval(myInterval); document.getElementById("usernameField").value=credentials.username; document.getElementById("passwordField").value=encryptXor(credentials.passwordCrypt, '5346245634634765377'); document.getElementById("loginSubmitBtn").click(); } } },1000); } // I have not found a reliable way to see if the page is ready other than polling... function execQuery(theQuery) { let myInterval; return new Promise((resolve, reject) => { myInterval = window.setInterval(() => { if (document.getElementsByClassName("gwt-SuggestBox OT335KC-ub-f").length === 1) { clearInterval(myInterval); // even if the screen is loaded, if we are too fast it will be too fast for the javascript of the page... window.setTimeout(function(){ window.document.title="[RiverRobot] Executing Query " + theQuery; console.log("ExecQuery ",theQuery); let theSearchField = document.getElementsByClassName("gwt-SuggestBox OT335KC-ub-f")[0]; theSearchField.value = theQuery; let theNumResultsField = document.getElementsByClassName("gwt-TextBox OT335KC-vb-f")[0]; theNumResultsField.value = 99999; let theSearchButton = document.getElementsByClassName("gwt-Button OT335KC-e-e")[0]; theSearchButton.click(); resolve(); },15000); // that's a bit lazy - I just assume it's ready in 15 sec... otherwise I had to poll the search button till it's clickable. } else { window.document.title="[RiverRobot] Waiting for Query field " + theQuery; console.log("Waiting for Query field",theQuery); } }, 1000); }); }; // I have not found a reliable way to see if the data is ready than polling... function downloadResultsCsv(theName) { let myInterval; return new Promise((resolve, reject) => { myInterval = window.setInterval(() => { let clsErrorEle = document.getElementsByClassName("pluginErrorStringBlack"); if (clsErrorEle.length > 0) { clearInterval(myInterval); let errText=clsErrorEle[0].innerText ; console.log("While waiting for results of " + theName + " Page shows error: " + errText + " -> Proceeding to next"); resolve(errText); } let pTag = document.getElementsByTagName("P"); if (pTag.length > 0) { console.log("Ready", theName, pTag); window.document.title="[RiverRobot] Downloading Results " + theName; clearInterval(myInterval); let theCsvUrl = pTag[0].firstChild.href; var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { console.log("Length of response ",this.responseText.length); // to be able to give the file a custom name I load it into a blob first, otherwise it will always save with the generic name. let blob = new Blob([this.responseText], { type: 'text/plain' }); let anchor = document.createElement('a'); anchor.download = theName + "-" + simulateDate.getFullYear() + "-" + ("0"+(1+simulateDate.getMonth())).slice(-2) + "-" + ("0"+simulateDate.getDate()).slice(-2) + ".csv"; anchor.href = window.URL.createObjectURL(blob); anchor.dataset.downloadurl = ['text/plain', anchor.download, anchor.href].join(':'); anchor.click(); resolve(); } }; xhttp.open("GET", theCsvUrl, true); xhttp.send(); } else { window.document.title="[RiverRobot] Waiting for Results " + theName; console.log("Waiting for Results",theName); } }, 5000); }); } let allQueriesAndNames = [ { name: 'ClcCalculationEngineBo.UpdateSummaryForPeriod', query: "class.method = 'Cas.BusinessLogic.Clc.ClcCalculationEngineBo.UpdateSummaryForPeriod' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Clc.ClcCalculationEngineBo.UpdateSummaryForPeriod -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmTacticView.GetView', query: "class.method = 'Cas.BusinessLogic.Prm.PrmTacticView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmTacticView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmTacticView.AddViewItem', query: "class.method = 'Cas.BusinessLogic.Prm.PrmTacticView.AddViewItem' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmTacticView.AddViewItem -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmProductAllView.GetView', query: "class.method = 'Cas.BusinessLogic.Prm.PrmProductAllView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmProductAllView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmProductAllView.AddViewItem', query: "class.method = 'Cas.BusinessLogic.Prm.PrmProductAllView.AddViewItem' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmProductAllView.AddViewItem -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'BpaMyPaymentMassCreationView.GetView', query: "class.method = 'Cas.BusinessLogic.Bpa.BpaMyPaymentMassCreationView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Bpa.BpaMyPaymentMassCreationView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PayMainBo.Save', query: "class.method = 'Cas.BusinessLogic.Pay.PayMainBo.Save' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Pay.PayMainBo.Save -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmMainBo.Copy', query: "class.method = 'Cas.BusinessLogic.Prm.PrmMainBo.Copy' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmMainBo.Copy -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmTacticProductAllView.GetView', query: "class.method = 'Cas.BusinessLogic.Prm.PrmTacticProductAllView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmTacticProductAllView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmMainView.GetView', query: "class.method = 'Cas.BusinessLogic.Prm.PrmMainView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmMainView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmMyTacticHistoryView.GetView', query: "class.method = 'Cas.BusinessLogic.Prm.PrmMyTacticHistoryView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmMyTacticHistoryView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmFundingSourcesView.GetView', query: "class.method = 'Cas.BusinessLogic.Prm.PrmFundingSourcesView.GetView' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmFundingSourcesView.GetView -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmMainBo.AddTacticByCopy', query: "class.method = 'Cas.BusinessLogic.Prm.PrmMainBo.AddTacticByCopy' AND server = 'ustca089.kcc.com' | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmMainBo.AddTacticByCopy -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'ClcCalculationBo.Calculation', query: "class.method = 'Cas.BusinessLogic.Clc.ClcCalculationEngineBo.Calculation' AND server = 'ustca089.kcc.com' AND transactiontype = 'accenture_businesslogic' | calls -type class.method -class.method Cas.BusinessLogic.Clc.ClcCalculationEngineBo.Calculation -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" }, { name: 'PrmMainBo.Save', query: " ! ( class.method = 'Cas.BusinessLogic.Clc.ClcCalculationEngineBo.Activate' ) and ! ( class.method = 'Cas.BusinessLogic.Prm.PrmMainBo.Copy' ) and ( class.method = 'Cas.BusinessLogic.Prm.PrmMainBo.Save' ) | calls -type class.method -class.method Cas.BusinessLogic.Prm.PrmMainBo.Save -include_call_count -visualization transactions -limit 100000 -sort_by total_duration" } ]; // Password is encrypted with XOR to preent "accidental" revelation let credentials=localStorage.getItem("riverRobotCredentials"); if (credentials!==null) { credentials=JSON.parse(credentials); } loginThread(credentials); let allDone = new Promise((allResolve,allReject)=>{ // Loop over all queries one by one considering the promise resolution: have to execute them in sequence! for (let i=0,p=Promise.resolve();i new Promise(resolve => {let theQueryAndName=allQueriesAndNames[i]; console.log("Executing", theQueryAndName); execQuery(theQueryAndName.query).then(() => { downloadResultsCsv(theQueryAndName.name).then(() => { console.log("Done ", theQueryAndName,i,allQueriesAndNames.length) resolve(); if (i===(allQueriesAndNames.length-1)) { console.log("All Done."); allResolve(); } }); }) })) };} ); // if we are all finished then we can close the window. allDone.then(()=>{ window.document.title="[RiverRobot] DONE"; console.log("Closing window"); // this doesn't work, Chrome doesn't allow me to close the last window, worked around that by killing the task in the scheduling... window.close(); }); })();