Login with Scriptable

Hey guys, I need your help. I‘m a seller on merch by Amazon and I try to build an widget for my iPhone that shows my daily sales! I looked up the threads on that site to find a solution but I couldn’t! My problem is, I can’t get the login working to get my data!

Here’s what I have until now:


var user = 'Eric'
var pass = '123'

var v = new WebView(user, pass);
await v.loadURL('https://merch.amazon.com/dashboard');

let viewPromise = v.present();

let js = document.getElementById('ap_email').value = user; document.getElementById('ap_password').value = pass; document.getElementById('ap-claim').value = user; document.getElementById('ap-credential-autofill-hint').value = user; document.getElementById('ap-credential-autofill-hint').dataset.claim = user; document.getElementById('signInSubmit').click();

await v.evaluateJavascript(js);
await v.waitForLoad();  // that can take some time, e.g. until the last image has loaded too

js = ""

let result = await v.evaluateJavascript(js);
// do whatever with the data

// end of script, wait until the WebView is closed
await viewPromise;

I don’t know what to do to get the informations in the field and press the buttons. I found that code in another thread about WebView and a guy said he used this code for an auto login for aliexpress.

I hope someone can help me to get the login done!

I appreciate your help guys. Thanks!

That guy was me :sweat_smile:

Maybe it’s just a typo, but you haven’t quoted your first javascript string that you use in the WebView (I think it’s line 10). With your provided script you essentially do nothing on the login page.

Oh hey :blush: Could you help me with that problem? You said that i should quote the JavaScript, right? So you mean this:


let js = "document.getElementById('ap_email').value = user; document.getElementById('ap_password').value = pass; document.getElementById('ap-claim').value = user; document.getElementById('ap-credential-autofill-hint').value = user; document.getElementById('ap-credential-autofill-hint').dataset.claim = user; document.getElementById('signInSubmit').click();"

I tried this but also nothing happens on the site :confused:

Hey guys, I really need help with that please. When I make the quotes, I got an error that the next line with the evaluateJavascript code is not a function! What did I wrong?

Isn’t the function evaluateJavaScript(), not evaluateJavascript()?

I mean this line. Therefore I got the following error:

2020-10-27 12:27:37: Error on line 9:27: TypeError: v.evaluateJavascript is not a function. (In ‘v.evaluateJavascript(js)’, ‘v.evaluateJavascript’ is undefined)

Ahh, thx for the Info. I change it to your function and the error is gone. No I got an error that JavaScript can’t find the variable user and pass! What did I wrong? I set the variable at the first line!

I suspect that you are now referencing the variables from the script in your web view where they are not defined. Pass the values into js, or define them within it rather than outside of it.

Here‘s my code now:


var user = 'Eric';
var pass = '123';

var v = new WebView();
await v.loadURL('https://merch.amazon.com/dashboard');

let js = "document.getElementById('ap_email').value = user; document.getElementById('ap_password').value = pass; document.getElementById('ap-claim').value = user; document.getElementById('ap-credential-autofill-hint').value = user; document.getElementById('ap-credential-autofill-hint').dataset.claim = user; document.getElementById('signInSubmit').click();"

await v.evaluateJavaScript(js);
await v.waitForLoad();  // that can take some time, e.g. until the last image has loaded too

js = ""

let result = await v.evaluateJavaScript(js);
// do whatever with the data

// end of script, wait until the WebView is closed
await viewPromise;

Where should the variables should be? Later I want to use the parameters from the widget!

Let us look at the first part of your js definition.

In it you are passing a string for the web view to evaluate. It is a string, so you are telling it evaluate as part of that a variable called user, but there is no definition in the evaluation string of a variable called user.

There are quite a few ways to deal with this, but probably the easiest to understand is if we change that fragment above to this…

let js = "document.getElementById('ap_email').value = 'Eric';

Now the string has the value, the web view should be able to evaluate it.

Ok I understand. Now at the end I got a inbrowser webview and the page is white, but when I have a look at the html code it‘s the right page. Can you explain to me how I can get some parts of the code to show in the widget?

I know in the code there is a second part of js to evaluate and I think I would use this code to get the part I want to know:


js = "var usd = document.getElementsByClassName('royalities')[0].getElementsByTagName('currency-summary-sold-USD
')[0].innerText"

How can i show this text now in the widget?

At first one tip for your login script: If you use a template literal instead of a normal string, you can embed local variables into the string like this:

let js = `let user = '${user}';
let pass = '${pass}';

document.getElementById('ap_email').value = user;
document.getElementById('ap_password').value = pass;
document.getElementById('ap-claim').value = user;
document.getElementById('ap-credential-autofill-hint').value = user;
document.getElementById('ap-credential-autofill-hint').dataset.claim = user;

document.getElementById('signInSubmit').click();`;

To explain that a little: ` (a backtick) starts and ends the template literal. It can contain line breaks, which are not removed from the resulting string. Using line breaks makes the whole thing easier to read in this case and doesn’t do any harm.
You can also “embed” other things in it with the expression ${expression} where expression can be any valid JavaScript.
As an example, you could do something like this:

let world = "world";
console.log(`Hello ${world}!`);

which would write Hello world! to the console.

In the example of writing JavaScript into a string, we need to make sure that it is valid javascript.
If you write

let user = "Eric";
let js = `let user = ${user}`

and evaluate that with a WebView, it won’t work, because the JavaScript you pass to the WebView is let user = Eric;. The script searches for a variable called Eric which of course doesn’t exist in the scope of the webpage. To fix this we need to wrap the embedded value in a string like (notice the ' around ${user})

let user = "Eric";
let js = `let user = '${user}'`

Now to your last question: to return somthing from a script inside a WebView you can use any one line as the last line in your script:

usd;
return usd;

If you run the script asynchronous (evaluated with v.evaluateJavaScript(js, true) (the second parameter is true)), you actually have to pass the value to the completion() function: completion(usd). This is all done inside the script you pass to the WebView.

Note that you can only return strings, numbers and booleans from the WebView. If you want to return an object, you can use one of the lines

JSON.stringify(usd);
return JSON.stringify(usd);
completion(JSON.stringify(usd)); // when the script is executed asynchronously

as the last line in your script. To get back the object in Scriptable you would use let result = JSON.parse(await v.evaluateJavaScript(js));


So a complete example would be:

let user = 'Eric';
let pass = '123';

let v = new WebView();
await v.loadURL('<your URL>');

let js = `let user = '${user}';
let pass = '${pass}';

document.getElementById('ap_email').value = user;
document.getElementById('ap_password').value = pass;
document.getElementById('ap-claim').value = user;
document.getElementById('ap-credential-autofill-hint').value = user;
document.getElementById('ap-credential-autofill-hint').dataset.claim = user;

document.getElementById('signInSubmit').click();`;
await v.evaluateJavaScript(js);
await v.waitForLoad();

js = `
let usd = document.getElementsByClassName('royalities')[0]
    .getElementsByTagName('currency-summary-sold-USD')[0]
    .innerText;
return usd;`;
const result = await v.evaluateJavaScript(js);

// use result in the widget

If you want to use this script in a widget, don’t forget to remove the code that presents the WebView because that won’t work in the widget. It will throw an error and therefore prevent all other code from running.

Hey schl3ck,

thanks for the code! When I try your complete example I got the following error:

2020-10-27 16:41:35: Error: Failed evaluating JavaScript with error on line 4: TypeError: null is not an object (evaluating ‘document.getElementById(‘ap_email’).value = user’)

Why it doesn’t work? :confused:

Maybe I can write you a private message to explain what I try to do with the widget?

This error looks like it couldn’t find the ap_email input on the page. When you add v.present(); before evaluating the javascript, does it display completely normal? (do not use await this time).

Let’s keep the conversation here so others may find it helpful or have better ideas than me :wink:

Ok, than let me explain what I want to do with the widget! I‘m a merch seller on Amazon and I want to have a widget that shows me my daily sales! Therefore I have to login to Amazon and than grab some data to calculate my sales! You already saw the link to the March dashboard! When I once logged in to Merch than I only have to insert the password!

I don’t know how to check if I only have to insert the password and not the username.

And if I logged in I want to grab the data!

Ok, I see your current problem now. When you were already logged in once, it doesn’t ask for the username, but your script doesn’t check for this.

I assume that the password fields id stays the same.

To fix this problem we need to check if the username field exists before trying to insert any data there. You can do this like this:

let js = `let user = '${user}';
let pass = '${pass}';

let input = document.getElementById('ap_email');
if (input) {
  input.value = user;
  // assume that the other fields are then also there
  document.getElementById('ap-claim').value = user;
  document.getElementById('ap-credential-autofill-hint').value = user;
  document.getElementById('ap-credential-autofill-hint').dataset.claim = user;
}
document.getElementById('ap_password').value = pass;


document.getElementById('signInSubmit').click();`;

Yeah that’s my problem. When I try your code I got the following error:

2020-10-27 22:26:14: Error: Failed evaluating JavaScript with error on line 0: SyntaxError: Can’t create duplicate variable that shadows a global property: ‘input’

Ah, dammit!

Try renaming the variable input I just introduced in my last script.

Here we go, I changed it to logi and added it also for the pass and the submit button. It seems to work but the problem now is that it is loading and loading and loading and never stops of loading. When I add the v.present(); it only shows me an empty page with no content. I‘d try to share the html of the blank page and saw that it‘s the right page i want to have. How I have to use the returned usd for example to show in the widget? It’s included in the const result, but how I access the result of the usd right now?

And how it will stop again? How do I have to change the code?

Nice that the login is now working!

I don’t know what usd contains, so this following script simply displays it:

let result = ...

let w = new ListWidget();
let text = w.addText(result);
// let it shrink to 1/10th of its original size to fit more
text.minimumScaleFactor = 0.1;
Script.setWidget(w);

I’ve not tested it, because I don’t have iOS 14 and therefore no widgets, but according to the Scriptable documentation it should work.
You have to style it yourself, because as I’ve said I don’t know what usd contains.