In our company, we are using the firefox greasemonkey addon to fill in login credentials on a webpage to open it and login automatically.
This is what the script looks like:
// ==UserScript==
// #name logon
// #namespace https://www.example.com
// #include https://www.example.com
// #version 1
// #grant none
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
// ==/UserScript==
$(document).ready(function() {
$('input[name=username]').val('*****');
$('input[name=passcode]').val('*****');
$('input[name=send]').click();
});
Since upgrading to Firefox version 70, the script does not enter the login credentials anymore.
I am under the impression that this is due to the page loading to slowly and the script executing to fast.
I would therefore like to insert a timeout in this script. So that it will only execute after 5 seconds after opening the page.
I have tried several examples found online, but none seems to work.
Help would be greatly appreciated!
Thanks
If your theory about why it does not work is correct, the code below should work. Observe some other changes such as:
JQuery is not needed for trivial things like this. Or any things. Just don't. It adds a dependency on your code for little benefit.
You should not store password in plaintext script, but if you do at least put it in variable so that it's obvious to non-programmers where to change it.
Using someinput.form.submit() also submits the form, and removes one input on which existence you have to rely on (the script still works if the submit button is renamed).
// ==UserScript==
// #name logon
// #namespace https://www.example.com
// #include https://www.example.com
// #version 1
// #grant none
// #rut-at document-start
// ==/UserScript==
const TIMEOUT = 2000;
const NAME = "***";
const PASSWORD = "***";
document.addEventListener("load", function() {
setTimeout(function() {
document.querySelector("input[name=username]").value = NAME;
document.querySelector("input[name=passcode]").value = PASSWORD;
document.querySelector("input[name=passcode]").form.submit();
}, TIMEOUT);
});
But your original script may also have broken to other things than what you think, such as:
Input fields renamed. Use dev tools to inspect them and see if there's any name, class name or ID.
Framework is now used that does not read the values from fields, but only updates it's internal values when you type in them
Look in dev console to errors. Next time when asking, also demonstrate what have you tried and how it failed, otherwise you will not learn as much as you could have.
Related
I want to make a script that fills the registration form for Doulos webinars. e.g. https://register.gotowebinar.com/register/2965111702634965004
When I go to this link via Firefox it loads https://register.gotowebinar.com/pageNotFound
Only when I click back then the proper form comes in. It looks like tampermonkey is trying to run the script before it gets to the right form, it doesn't find the right fields there and in this situation once it loads the fields indicated in the script are not filled in.
Here an example if I wanted to fill the first name and email:
// ==UserScript==
// #name Duolos autofill registration form
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author blaz
// #match https://register.gotowebinar.com/register/*
// #icon https://www.google.com/s2/favicons?sz=64&domain=gotowebinar.com
// #grant none
// #run-at document-end
// ==/UserScript==
var formFirstName = document.getElementById('registrant.firstName')
var formEmail = document.getElementById('registrant.email')
formFirstName.value = 'My_first_name'
formEmail.value = 'my#email.com'
Important note: this is my first contact with JavaScript and Tampermonkey.
How to make such a script properly?
Filling works as I applied the suggested setTimeout() by #JeremyThille
I keep up with clicking on 'Undo' within 10 seconds and the form loads during that time.
setTimeout(function(){
console.log("10s later...");
var formFirstName = document.getElementById('registrant.firstName')
var formEmail = document.getElementById('registrant.email')
formFirstName.value = 'My_first_name'
formEmail.value = 'my#email.com'
}, 10000);
Trying to create a Greasemonkey script to auto-fill some forms, and I want the script to go to another URL after the form is submitted.
// ==UserScript==
// #name usersaime
// #description fills data form for user and pass
// #include https://tramites.saime.gob.ve/*
// #version 1.1
// #grant none
// ==/UserScript==
document.getElementById('LoginForm_username').value = "user";
document.getElementById('LoginForm_password').value = "1234";
setTimeout(function() {
document.getElementById('login_button').click();
}, 2000);
window.addEventListener('load', function(event) {
// all resources finished loading
window.location = 'https://tramites.saime.gob.ve/index/example/example';
});
The window.location is not working at all. I also tried window.location.href and window.location.replace, and a setTimeout function to the window.location. Nothing works.
There are no errors on the console.
Tried:
Firefox 59 + Greasemonkey 4.3
Chrome 66 + Tampermonkey 4.5
The login page/form is https://tramites.saime.gob.ve/index.php?r=site/login.
And on successful login it goes to https://tramites.saime.gob.ve/index.php?r=tramite/tramite/ -- which I want to redirect.
Simple, immediate answer:
The reason window.location is not working for you is because:
The window.location call is made inside a window load event handler.
That target page is one of those were the load event fires almost immediately after the DOMContentLoaded event.
By default userscripts trigger at DOMContentLoaded, but in this case, the load event has already fired by the time your script runs.
The real answer:
There are many problems with that question code:
It's not tracking and accounting for the state of the process. The script fires on many (kinds of) pages and must behave differently depending on state.
The page(s) is/are extensively AJAX-driven. The code is not properly accounting for that.
Load event is not particularly useful in this kind of scenario.
The aforementioned, script-firing versus load, race condition.
Hard-coding login credentials in a script like that means that you will get pwned, it's just a question of when and how much damage.
You need to track and differentiate between at least 3 different states, using a combination of the pages' URLs and/or key nodes on the pages and/or state variables that the script stores/passes.
The following userscript illustrates the process, although I can't test it on the site and the recommended authentication framework is omitted for simplicity. :
// ==UserScript==
// #name _Login and then redirect an AJAX-driven page
// #include https://tramites.saime.gob.ve/*
// #require https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// #require https://gist.github.com/raw/2625891/waitForKeyElements.js
// #grant GM_addStyle
// #grant GM.getValue
// ==/UserScript==
//- The #grant directives are needed to restore the proper sandbox.
//--- Different pages require different actions
if (location.search.includes ("site/login") ) { // initial login page
//-- Wait for page to initialize
waitForKeyElements ("#login_button:visible", loginWhenReady);
}
else if (location.search.includes ("tramite/tramite") ) { // successful login page
//-- Just redirect
location.assign ("https://tramites.saime.gob.ve/index/example/example");
}
else {
//-- All other pages, no action needed.
}
function loginWhenReady (jNode) {
//-- Demo purposes only! Use framework or password manager instead!
$("#LoginForm_username").val ("user");
$("#LoginForm_password").val ("1234");
clickNode (jNode); // Login button passed in by WFKE
}
function clickNode (jNode) {
var clickEvent = document.createEvent ('MouseEvents');
clickEvent.initEvent ('click', true, true);
jNode[0].dispatchEvent (clickEvent);
}
Possible quick and easy solution:
If-and-only-if (IIF) the post login page is always
https://tramites.saime.gob.ve/index.php?r=tramite/tramite/.
AND IIF that page is used for nothing else that you care about...
Then the following userscript may suffice for your needs:
// ==UserScript==
// #name _Quick and dirty post login redirect
// #include https://tramites.saime.gob.ve/index.php?r=tramite/tramite*
// #grant none
// #run-at document-start
// ==/UserScript==
location.replace ("https://tramites.saime.gob.ve/index/example/example");
document.querySelector('div.su-input-group:nth-child(2) > input:nth-child(2)').value = ('abc');
document.querySelector("#password").value = "abc";
document.querySelector("#dob").value = "abc";
document.querySelector(".button-orange").click();
So far this is the code that I was able to write and it does not work on https://kite3.zerodha.com/. The script fails after line number 3. BTW same code works for me on other sites.
The reason this code is not working on kite3 website is that controls that need to be filled are animated, and they are not available for selection until animation finishes.
Solution for this is usage of waitForKeyElements js library. When these elements become available values can be set, buttons clicked.
Only one criteria is used for wait just to prove the concept.
This code is working for login on that website, just replace username and password with correct values.
// ==UserScript==
// #name Login to Kite
// #namespace http://tampermonkey.net/
// #version 0.1
// #match *kite3.zerodha.com
// #require https://gist.github.com/raw/2625891/waitForKeyElements.js
// #require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// ==/UserScript==
waitForKeyElements(":password", login);
function login() {
document.querySelector("[type=text]").value = 'yourUserId';
document.querySelector("[type=password]").value = 'yourPassword';
document.querySelector("button").click();
}
The website is: lexin.nada.kth.se/lexin/#searchinfo=both,swe_gre,hej;
My script is:
function main(){
var links=document.getElementsByTagName("a");
alert("There are " + links.length + "links.");
}
main();
Running the script gives me two alert messages saying
There are 0 links.
Any ideas why I can't get the right amount of links from the document? And why do I get the alert twice?
The alert fires more than once because that page contains iFrames (with, de facto, the same URL as the main page). Greasemonkey treats iFrames as if they were standalone web pages. Use #noframes to stop that.
The script is not finding the links because they are added, by javascript, long after the page loads and the GM script fires. This is a common problem with scripts and AJAX. A simple, robust solution is use waitForKeyElements() (and jQuery).
Here is a complete sample script that avoids the iFrames and shows how to get dynamic links:
// ==UserScript==
// #name _Find elements added by AJAX
// #include http://YOUR_SERVER.COM/YOUR_PATH/*
// #match http://stackoverflow.com/questions/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// #require https://gist.github.com/raw/2625891/waitForKeyElements.js
// #noframes
// #grant GM_addStyle
// ==/UserScript==
/*- The #grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
var totalUsrLinks = 0;
waitForKeyElements ("a[href*='/users/']", listLinks);
function listLinks (jNode) {
var usrMtch = jNode.attr ("href").match (/^.*\/users\/(\d+)\/.*$/);
if (usrMtch && usrMtch.length > 1) {
totalUsrLinks++;
var usrId = usrMtch[1];
console.log ("Found link for user: ", usrId, "Total links = ", totalUsrLinks);
}
}
It's returning an HTMLcollection because of .getElementsByTagName and because of that, you will have to state the HTMLcollection with .getElementsByTagName and then find the length, and alert it. It will look like this...
(function main(){
var links = document.getElementsByTagName("a").length
alert("There are "+ links + " links.");
})()
I added an IIFE or an Immediately-Invoked-Function-Expression more on IIFEs here, so you don't have to call the function, and so the code is small and able to be "swallowed". lastly, it's alerting 2 alert boxes, because there's one[alert box] in the function and you're calling that function so it's going to do the same thing.
I frequent a forum that has a horrible way of ignoring users. If you place someone on ignore, it almost makes that users presence more prevalent.
So I wrote this to hide them completely:
// ==UserScript==
// #name Freddie
// #namespace http://tampermonkey.net/
// #version 0.1
// #description hide annoying forum users
// #author You
// #match http://www.scout.com/college/kansas/forums/*
// #grant none
// ==/UserScript==
/* jshint -W097 */
'use strict';
function checkForDiv() { // crappy workaround function to wait for AJAX content
if (!document.getElementById("wrapper")) {
setTimeout(checkForDiv, 300);
} else {
checkNames();
}
}
function checkNames() {
var mybannedList = ["Pyros", "GOHawksGators", "th30r3o"]; // add usernames here
var nms = document.body.querySelectorAll('a.authName'), i = 0, len = nms.length;
for (i; i < len; i++) {
if (mybannedList.indexOf(nms[i].innerHTML) != -1) {
nms[i].parentNode.parentNode.style.display = "none";
}
}
}
checkForDiv();
But when you go to the page with the ignored users they still appear, upon refreshing, the script runs and they disappear.
Please good sirs, what do I do?
The site uses AJAX for navigation so the page address changes without reloading, that's why Tampermonkey doesn't inject your script when you navigate from another page on that site.
The simplest solution would be to include the entire site: // #match http://www.scout.com/*
There are other more advanced methods of detecting page transitions based on MutationObserver or some DOM event or property change that occurs on navigation.
Also beware of #grant none with jQuery loaded via #require: it breaks sites that also load jQuery unless you use jQuery.noConflict. The simplest solution is to remove that line as you don't need to access the web page variables.
P.S. There's a known timer-based wrapper: waitForKeyElements.