March 6, 2020

Working with Frames in Chrome and Edge

Filed under: Automation,Scripting,Web/Tech — Marcus Tettmar @ 1:28 pm

The new Chrome and Edge functions in Macro Scheduler 15 make it possible to locate frames and iframes and then manipulate elements within them.

Switching Frames

The ChromeSwitchFrame and EdgeSwitchFrame functions allow you to specify a frame or iframe element, by index or element reference, to switch the “browsing context” to.

What this means is that any subsequent interactions will take place against elements within that frame. So a subsequent ChromeFindElements call will attempt to locate the specific element within the frame, rather than the parent page, and ChromeElementAction will act against the given element within that frame.

//Find the frame using xpath
ChromeFindElements>sessionID,xpath,//iframe[@src='/contact-form/formpage.html'],elements

//switch browsing context to this frame
ChromeSwitchFrame>session_id,element,elements_1,res

If xpath doesn’t mean anything to you see my recent post Using Macro Scheduler 15’s Chrome Functions which includes an explanation of using Chrome’s Developer Tools to identify elements.

At some point you may need to switch the browsing context back to the parent frame. To do this you call ChromeSwitchFrame/EdgeSwitchFrame again with a null index. Subsequent calls to the Chrome/Edge functions will then act against the parent frame.

ChromeSwitchFrame>session_id,index,null,res

Traversing Frames

Since each time you switch frames you change the context to that frame, calling ChromeSwitchFrame or EdgeSwitchFrame again (on a valid frame element within) will switch the context down another level.

//Find the frame using xpath
ChromeFindElements>sessionID,xpath,//iframe[@src='/contact-form/formpage.html'],elements
ChromeSwitchFrame>session_id,element,elements_1,res

//Switch to the next frame down *within the current frame*
ChromeSwitchFrame>session_id,index,0,res

Differences between Edge and Chrome

The Edge and Chrome functions work in the same way and are almost identical. There’s one major difference when it comes to switching frames. When specifying an element (rather than index) ChromeSwitchFrame requires the element ID, whereas EdgeSwitchFrame requires the full element object. As well as an array of element IDs, EdgeFindElements returns a second array of the objects:

EdgeFindElements>sessionID,xpath,//iframe[@src='/contact-form/formpage.html'],FrameElements

This returns two arrays prefixed with the name passed as the return var (TheElements): FrameElements_1 … FrameElements_n and FrameElements_objects_1 … FrameElements_objects_n. For EdgeSwitchFrame use the second _objects array:

//switch to the frame
EdgeSwitchFrame>session_id,element,FrameElements_objects_1,res

More Help

View the Chrome Functions and Edge Functions topics in the Macro Scheduler Manual.

Full Example:

Let>CHROMEDRIVER_EXE=c:\chromedriver.exe

//start a Chrome session
ChromeStart>session_id

//navigate to google.com
ChromeNavigate>session_id,url,https://www.mjtnet.com/contact.htm

//Find the frame
ChromeFindElements>sessionID,xpath,//iframe[@src='/contact-form/formpage.html'],elements

//switch to the first frame (the one with the fields)
ChromeSwitchFrame>session_id,element,elements_1,res

//now anything we do is inside that frame so we should be able to get the name field and enter something
ChromeFindElements>session_id,id,name,inputs
ChromeSetElementValue>session_id,inputs_1,john doe

//Switch context back to parent
ChromeSwitchFrame>session_id,index,null,res

February 27, 2020

Using Macro Scheduler 15’s Chrome Automation Functions

Filed under: Automation,Scripting,Web/Tech — Marcus Tettmar @ 6:48 pm

Macro Scheduler 15 now has native Chrome and Edge functions. You may be wondering how they work. There are a couple of sample scripts included with v15 but if you’re not sure how to get things set up or how to identify elements in the page this article is for you.

I wanted an example that wasn’t too involved but juicy enough to offer a few hurdles and demonstrate some features like xpath. I’ve set upon using Yahoo Finance to pull out the value of a stock symbol. Bear in mind that websites have a habit of changing so I can’t guarantee that the code in this article will work 6 months from now. But that shouldn’t matter much – it’s the principles of how to do this that matters most.

Before we begin make sure you are running Macro Scheduler 15.0.06 or above.

ChromeDriver

Before we can use the new Chrome functions we need to download the win32 version of ChromeDriver from: https://chromedriver.chromium.org

The zip file contains a single file: chromedriver.exe. Save this somewhere.

We need to tell the script where chromedriver.exe is. In my examples below I’ve got it in the root of drive C. So the first line of my script needs to be:

Let>CHROMEDRIVER_EXE=c:\chromedriver.exe

Start a Chrome Session

Now, we can start a Chrome session. To do this we use the ChromeStart function and give it a variable name that we want the session ID stored in. I’m using session_id. We’ll need this for the rest of the commands:

ChromeStart>session_id

Run your script and if your script is correctly pointing at ChromeDriver.exe you should see an empty Chrome window appear.

Navigating

To navigate to Yahoo add the ChromeNavigate command, passing the session_id to it:

ChromeNavigate>session_id,url,https://finance.yahoo.com/

Close the Chrome browser window (you don’t have to but running your script is going to start another) and run your script again.  This time you should see it navigate to Yahoo and show something like this:

Using Developer Tools

So the first thing we need to do is get past that “I agree” button. This is where Chrome’s Developer Tools comes in to help us find the element and the attributes we can use to identify it.

Right click on the “I agree” button and select “Inspect” at the bottom of the popup menu.  Chrome’s Developer Tools should appear. Usually at the bottom, but if you’ve used it before you may have changed the layout. It should appear in ‘Elements’ view and the highlighted element should be the “I agree” button:

Sometimes, I’ve found that the first time I select “Inspect”, if Developer Tools wasn’t already open, the element I want isn’t the highlighted one. Usually right clicking on the element and selecting Inspect a second time does the trick.

Another way to identify the element once Developer Tools is already open is to click the element selector button which you see at top left in the screenshot above. Then move the mouse to the “I agree” button and click.

Once you’ve got the right element selected look for an attribute which we could use to identify it:

<button type="submit" class="btn primary" name="agree" value="agree">I agree</button>

Ideally we want something unique.That isn’t always possible and I’ll talk about what we can do in those cases later. Here we’re in luck. The element has a name – “agree”. We can use this to get a reference to the element so that we can then ‘click’ it:

ChromeFindElements>session_id,name,agree,agree_elements

Notice how we’re passing the session ID again (we want to make sure we’re referring to the Chrome window we started). The second parameter is the “Strategy” we want to use to identify the element. ‘Name’ is an available strategy – find the element by name. The third parameter is the strategy value. For ‘Name’ it’s the name itself which in this case is “agree”. Finally we give the command a variable name we’d like to store the located element IDs in.

Valid ‘strategies’ you can use are:

id, name, class name, css selector, link text, partial link text, tag name, xpath

Clicking on the Element

ChromeFindElements returns an array of matching elements. In this case the array should have only one item because there’s only one matching element. So the item we want is the first one: agree_elements_1.

Let’s use that to click on the button using the ChromeElementAction command:

ChromeElementAction>session_id,agree_elements_1,click

The script so far:

Let>CHROMEDRIVER_EXE=c:\chromedriver.exe
ChromeStart>session_id
ChromeNavigate>session_id,url,https://finance.yahoo.com/
ChromeFindElements>session_id,name,agree,agree_elements
ChromeElementAction>session_id,agree_elements_1,click

Close your browser window and re-run the script. Hopefully you’ll sail right through that I agree box.

ChromeDriver should wait for events to complete and pages to load before firing new events, but if you have any issues you may need to slow things down a touch. E.g. stick a Wait>1 before the click.

Sending the Stock Symbol

We now need to find that input box, enter something and submit it. Using the same ‘Identify’ technique as described above you should find that the input element has its “id” attribute set to “yfin-usr-qry”:

So we’ll use that:

ChromeFindElements>session_id,id,yfin-usr-qry,input_elements

I’ve used input_elements as my array name for the found elements. We now want to enter some text. Let’s search for Microsoft which is stock symbol “MSFT”. To do this we’ll use the ChromeSetElementValue command. There’s only one matching element again so as above we use the first match input_elements_1:

ChromeSetElementValue>session_id,input_elements_1,MSFT
Wait>1

Notice this time I have put a wait afterwards. The next step is to ‘submit’ the field. I found that submitting it without the wait didn’t allow enough time for the entry to ‘take’. There could be some background processing going on which needs time to finish. Remember, as with most automation, we’re automating something that was designed for a user, and Robots run faster than users, so sometimes need slowing down!

We will now use the ChromeElementAction command again, this time with the submit action:

ChromeElementAction>session_id,input_elements_1,submit

Instead of doing this we could have found the search button and issued a ‘click’ on that. But that would require another ChromeFindElements which in this case is unnecessary. We can ‘submit’ the input directly.

Extracting The Stock Value

Rerun the script and you should end up looking at the Microsoft stock info which we want to extract.

Tip: rerunning the script from the top every time isn’t ideal. We’ve got the Chrome window open so the session is still active. So what you could do if you just want to run and test a few commands is grab the session ID from the watch list and assign it with a Let statement placed just before the commands you want to test. E.g:

Let>session_id=b6976cfec0326dcce35ad3674c3ed90e
ChromeFindElements>session_id,id,yfin-usr-qry,input_elements
ChromeSetElementValue>session_id,input_elements_1,MSFT

This is where things get a little more interesting. Use the inspect element tool to locate the element as above. You’ll see it’s a span element. You might also notice the class keeps changing. This site is very dynamic, updating as the stock value changes. In this instance we aren’t lucky enough to have an id, or a name. But notice those custom ‘data-reactid’ attributes.  You’ll see that they all have different values, and the one we want is “34”.  Let’s see if we can use that.

Now, ‘data-reactid’ is a custom attribute. It’s not standard html, and there’s no ‘strategy’ called that. So we’re going to use something called ‘xpath’. xpath is incredibly powerful and can help us find pretty much anything. There’s a great tutorial on xpath here.

We need to find a ‘span’ element with attribute data-reactid which is set to “34”. In xpath we can do this with:

//span[@data-reactid='34']

The // means find nodes regardless of where they are – we don’t need to provide a path. The @ symbol is used to specify an attribute. And we’re giving it a value. We’re saying find a span element with attribute data-reactid set to value “34” and we don’t care where it is.

So we can use this in the ChromeFindElements command as follows:

ChromeFindElements>session_id,xpath,//span[@data-reactid='34'],info_elements

Now, before we continue we should confirm whether or not this is the ONLY span tag with this attribute and value. We could do that either by searching the source using Chrome’s element inspector, or, I think easier, just debug the code to see what we get back. Stick a breakpoint just after this line, run the script and then look at the watch list. You’ll see there are two:

Now, we’re going to need to extract them to see which is the right one. We’re going to use the ChromeGetElementData command:

ChromeGetElementData>session_id,info_elements_1,text,stockValue

If you step through this command and look at the value of stockValue in the watch list, you’ll notice that info_elements_1 is not the one we want. We want the second one:

ChromeGetElementData>session_id,info_elements_2,text,stockValue
MessageModal>Value is %stockValue%

So there we have it. We’ve started Chrome, navigated to Yahoo Finance, clicked ‘I agree’, searched for MSFT and extracted the stock value.  We might want to close the Chrome window at the end:

ChromeQuit>session_id

Here’s the full script:

Let>CHROMEDRIVER_EXE=c:\chromedriver.exe

ChromeStart>session_id
ChromeNavigate>session_id,url,https://finance.yahoo.com/

ChromeFindElements>session_id,name,agree,agree_elements
ChromeElementAction>session_id,agree_elements_1,click

ChromeFindElements>session_id,id,yfin-usr-qry,input_elements
ChromeSetElementValue>session_id,input_elements_1,MSFT
Wait>1

ChromeElementAction>session_id,input_elements_1,submit
Wait>1

ChromeFindElements>session_id,xpath,//span[@data-reactid='34'],info_elements
ChromeGetElementData>session_id,info_elements_2,text,stockValue
MessageModal>stockValue

ChromeQuit>session_id

Using Edge instead of Chrome?

The Edge commands work in exactly the same way as Chrome. And finding elements in Edge is done in exactly the same way. So you should be able to adapt the above code to work with Edge very easily. The main difference is that Edge is a bit more picky when it comes to installing the correct version of Edge Driver.  And if you have an older ‘legacy’ version of Edge you will need to install Microsoft Web Driver instead of MS Edge Driver.  The manual explains how.

Conclusion

The Chrome and Edge functions are incredibly powerful and finding elements using Chrome’s, or Edge’s, Developers Tools is quick and easy. There’s also a lot more that you can do, such as grab the entire source of the page. I hope this has been useful. Let me know in the comments below how you are using these functions.

You’ll find more info in the manual. See Chrome Functions and Edge Functions.

If you have any questions find me on the forums or drop us a line.

January 9, 2019

PrettifyScp – Code Clean Up

Filed under: General,Scripting — Marcus Tettmar @ 2:10 pm

I often end up working on Macro Scheduler code that is hard to read because of a lack of indentation. Sometimes this is simply because the code has been chopped and changed so many times it’s become a bit messy and needs a bit of a clean up. And I’m not pointing fingers here either – some of this code is my own!

Indenting code inside code blocks makes it much easier to read. But cleaning up code and sorting out the indentation manually is tedious. So I wrote a little script to do it!

So here is my first PrettifyScp script. Just run it and browse to the .scp file you want to fix up. Wait a few seconds and it will pop up a message when it’s done.

//tokens to identify start and end of indented code
Let>incTokens=^IF.*|^SRT\>.*|^REPEAT\>.*|^WHILE\>.*|^ELSE|^ELSE>
Let>decTokens=^ENDIF|^END\>.*|^UNTIL\>.*|^ENDWHILE.*|^ELSE|^ELSE>

//tokens to identify blocks to ignore
Let>inIgnores=^DIALOG\>.*|^VBSTART|^/\*.*
Let>outIgnores=^ENDDIALOG\>.*|^VBEND|\*/.*

//set tab chars
Let>tabs={"  "}

Let>numTabs=0
Let>inIgnoreSection=0
Let>outText={""}

//ask user for input script file
Input>scp_file,Select script file

If>scp_file<>{""}

  //create backup file name
  ExtractFilePath>scp_file,path
  ExtractFileName>scp_file,scp_name
  ExtractFileName>scp_file,scp_name_no_ext,1
  Let>backupfile=%path%\%scp_name_no_ext%.BAK

  //read in script lines to loop through
  ReadFile>scp_file,scp_data
  //temporarily remove percent symbols to avoid unwanted variable resolution
  StringReplace>scp_data,%,~^PERCENT^~,scp_data
  //split to array of lines
  Separate>scp_data,CRLF,scp_lines

  //if there are lines, loop through them
  If>scp_lines_count>0

    Let>k=0
    Repeat>k

      Let>k=k+1
      Let>this_line=scp_lines_%k%

      //Message>To do: %this_line%

      //left trim the line
      LTrim>this_line,trimmed_line

      //are we going into a section to ignore?
      RegEx>inIgnores,trimmed_line,0,matches,pIgnore,0
      If>pIgnore>0
        Let>inIgnoreSection=1
      Endif

      If>inIgnoreSection=1
        //if we are in a section to ignore, are we coming out of it?
        RegEx>outIgnores,trimmed_line,0,matches,pIgnore,0
        If>pIgnore>0
          Let>inIgnoreSection=0
        Endif
      Else
        //otherwise we are not in an ignore section so see if we need to do anything

        //should we unindent?
        RegEx>decTokens,trimmed_line,0,matches,dec_nm,0
        If>dec_nm=1
          Let>numTabs=numTabs-1
          //capitalise first letter while we're here
          MidStr>trimmed_line,1,1,fChar
          Uppercase>fChar,fChar
          RegEx>(^.{1}),trimmed_line,0,matches,nm,1,fChar,trimmed_line
        Endif

        //create the indent chars
        Let>indent={""}
        If>numTabs>0
          Let>t=0
          Repeat>t
            Let>t=t+1
            Let>indent=%indent%%tabs%
          Until>t=numTabs
        Endif

        //insert indent chars to line to output
        Let>this_line=%indent%%trimmed_line%

        //should we indent further lines?
        RegEx>incTokens,trimmed_line,0,matches,inc_nm,0
        If>inc_nm=1
          Let>numTabs=numTabs+1
          //capitalise first letter
          MidStr>trimmed_line,1,1,fChar
          Uppercase>fChar,fChar
          RegEx>(^.{1}),trimmed_line,0,matches,nm,1,fChar,trimed_line
        Endif

      Endif

      //output the new line
      Let>outText=%outText%%this_line%
      If>k<scp_lines_count
        Let>outText=%outText%%CRLF%
      Endif

    Until>k=scp_lines_count

    //swap percent symbols back in ...
    StringReplace>outText,~^PERCENT^~,%,outText

    //backup the file
    DeleteFile>backupfile
    CopyFile>scp_file,backupfile

    //write output to original file name
    DeleteFile>scp_file
    Let>WLN_NOCRLF=1
    WriteLn>scp_file,res,outText

    MessageModal>Done! Backup saved to %backupfile%

Endif


The old script is renamed to a .BAK file so that your original script is backed up. So if there’s something I haven’t thought of in this first stab and something gets messed up, you can’t lose anything, but hopefully you are backing up your code already!

I’m ignoring code inside Dialogs and VBScript blocks as well as comment blocks. Dialogs shouldn’t be messed with and the indentation is handled by the dialog designer. VBScript could be cleaned up but would need a different parser and there are already VBScript code beautifiers out there so you could copy and paste those. So in this version I haven’t touched VBS blocks.

Hope this is useful. Any feedback or suggestions, or if you notice a bug, please post in the comments below.

July 20, 2018

Cross Language 128 and 256 Bit AES Encryption

Filed under: Scripting,Web/Tech — Marcus Tettmar @ 10:47 am

With Macro Scheduler 14.4.10 we have added new 128 and 256 bit AES Encryption methods which interoperate well with other languages.

Cross-platform/cross-language encryption/decryption can be tricky. Different text encodings, different padding formats and understanding how different algorithms derive keys and initialisation vectors can make encrypting and decrypting between one language and another a bit of a pain!

Macro Scheduler’s AES function originally worked only on Unicode strings as this is the standard string format in Macro Scheduler.  Having had some issues trying to make this work between NodeJS and PHP we decided to create some new UTF8 based AES options (available in Macro Scheduler 14.4.10) which we have verified work well with PHP/OpenSSL and NodeJS.  

These new implementations provide 128 and 256 bit AES encryption.  They use CBC chaining method and use a SHA256 password. If using AES 128 the SHA256 password is truncated to the 32 byte key length.  By default the initialisation vector is set to “0000000000000000” but can be set to whatever you want (but must be 16 characters long). Padding is PKCS#5.

Here is an example of AES_256:

Let>AES_ALG=AES_256_CBC
AESEncrypt>hello world,mypassword,ENCRYPT,result
AESEncrypt>result,mypassword,DECRYPT,original


Here is a compatible PHP example:

data = "hello world";

$method = 'AES-256-CBC';

// simple password hash
$password = 'mypassword';
$key = hex2bin(substr(hash('sha256', $password),0,64));

echo "Method: " . $method . "\n";
$encrypted = encrypt($data, $key, $method);
echo "Encrypted: ". $encrypted . "\n";
$decrypted = decrypt($encrypted, $key, $method);
echo "Decrypted: ". $decrypted . "\n"; // plain text

function encrypt(string $data, string $key, string $method): string
{
  $iv = "0000000000000000";
  $encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
  $encrypted = base64_encode($encrypted);
  return $encrypted;
}

function decrypt(string $data, string $key, string $method): string
{
  $data = base64_decode($data);
  $iv = "0000000000000000";
  $data = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA,$iv);
  return $data;
}

Try this PHP Code Here

And here’s the Javascript equivalent:

let data = 'hello world';
let password = 'mypassword';
let iv = '0000000000000000';

let password_hash = crypto.createHash('sha256').update(password,'utf8').digest('hex');

let key = hex2bin(password_hash);
password_hash = Buffer.alloc(32,key,"binary");

let cipher = crypto.createCipheriv('aes-256-cbc', password_hash, iv);

let encryptedData = cipher.update(data, 'utf8', 'base64') + cipher.final('base64');

console.log('Base64 Encrypted:', encryptedData);

let decipher = crypto.createDecipheriv('aes-256-cbc', password_hash, iv);

let decryptedText = decipher.update(encryptedData, 'base64', 'utf8') + decipher.final('utf8');

console.log('Decrypted Text:', decryptedText)

function hex2bin(hex)
{
  var bytes = [], str;

  for(var i=0; i< hex.length-1; i+=2)
    bytes.push(parseInt(hex.substr(i, 2), 16));

  return String.fromCharCode.apply(String, bytes);
}

Try this Javascript code here.

You can try the Javscript and PHP examples at repl.it:

https://repl.it/@MarcusTettmar/phpaes256cbc1
https://repl.it/@MarcusTettmar/jsaes267cbc1

April 4, 2018

Running Powershell Code From Inside Your Macros

Filed under: Automation,Scripting — Marcus Tettmar @ 4:25 pm

A while back I wrote this blog post which shows you how you can have a PowerShell script run Macro Scheduler commands via the MacroScript SDK.

However, what if you want to do it the other way around and run PowerShell code from inside a Macro Scheduler macro instead?

One way would simply be to run a PowerShell script on the command line, like this:

Run>powershell.exe -file "c:\myfolder\myscript.ps1"

But what if you didn’t have a PowerShell script file?  Well, while you could always have your macro create one using the WriteLn command (and delete it after), you don’t need to as you can pass a sequence of commands to PowerShell on the command line:

powershell.exe -executionPolicy bypass -command "Write-Host 'Hello World'; exit 123;"

As you can see all we’re really doing is passing the content of the script instead of a file.

We can also return the exit code of the PowerShell script to a variable in your macro.  Here’s an example:

//This line ensures the powershell script finishes before the macro continues
Let>RP_WAIT=1
//Uncomment this line to HIDE the powershell window when ready to go live
//Let>RP_WINDOWMODE=0

Let>localVar=Hello World
LabelToVar>MyPowershellScript,theScript
Run>powershell.exe -executionPolicy bypass -command "%theScript%"

//display the exit code
MDL>RP_RESULT

/*
MyPowershellScript:

$var = '%localVar%';
Write-Host $var;
Start-Sleep -s 10;
exit 555;
*/

What we’ve done here is store our PowerShell code in a label. We get that code into a variable and then pass it to PowerShell on the command line.

Notice how we can embed Macro Scheduler variables in the code too. We use PowerShell’s exit statement to set a return value which Macro Scheduler gets in the RP_RESULT variable.

Setting RP_WAIT to 1 makes sure the macro waits for PowerShell to finish before proceeding with the rest of the script. You may also want to hide the PowerShell window by setting RP_WINDOWMODE to 0, but for testing purposes I’ve left that commented out so that you can actually see it working. That’s also the reason for the 10 second delay at the end of the PowerShell script – apart from showing how we can add multiple lines of code it also gives us a chance to see things happen!

Get Date of Last Monday

Filed under: Scripting — Marcus Tettmar @ 2:59 pm

Someone asked me the other day how to get the date of the most recent Monday.  I came up with two solutions.

Get Date of Last Monday with VBScript

This version works by looping the date backwards one day at a time until the weekday of that date is a Monday.

//Put this VBSTART ... VBEND block at the top of your code ONCE
VBSTART
  Function LastMonday
    LastMonday = Date()
    Do While WeekDay(LastMonday) <> 2
      LastMonday = LastMonday -1
    Loop
  End Function
VBEND

//use this line whenever you want to get the date of last monday
VBEval>LastMonday,dateOfLastMonday

To make it work for a different day just change the number it compares the WeekDay to in the Do line. 1 is Sunday, 2 is Monday and so on.

Get Date of Last Monday with MacroScript

This version works out how many days we need to deduct from the current date to get to Monday based on what the current day of the week is. If it’s a Sunday we just deduct 6. If it’s a Monday or onward we deduct the current day number.

//DayOfWeek starts with 1 as Sunday, so Monday is 2.
If>curDayOfWeek>1
  //today must be Monday or later ... 
  //So diff we want is 2 - current day of week
  Let>diffToMonday=2-curDayOfWeek
  //e.g. if today Monday result would be 0 difference, Tuesday would yield -1 difference, Wednesday, -3 etc
Else
  //curDayOfWeek is 1, so ...
  //today must be sunday, so we just want 6 days ago
  Let>diffToMonday=-6
Endif

//This should give us number of days to subtract from current day 
//to get Monday's date
DateAdd>today,D,%diffToMonday%,mbeg

October 29, 2017

Sending Email via Office 365

Filed under: Scripting — Marcus Tettmar @ 6:23 pm

We’ve had a few questions lately about how to send email using the SMTPSendMail function via the Office 365 SMTP server.

Until Macro Scheduler 14.4.02 which we have released today this wasn’t possible as explicit TLS was required and SMTPSendMail did not support it. We have now added this. So as long as you have updated to Macro Scheduler 14.4.02 (and you have a valid Office 365 account) here’s the code you need to send an email via the Office 365 mail server:

Let>server=smtp.office365.com
Let>SMTP_AUTH=1
Let>SMTP_USERID=YOUR_OFFICE365_USERID
Let>SMTP_PASSWORD=YOUR_OFFICE365_PASSWORD
Let>SMTP_PORT=587
Let>SMTP_SSL=1
Let>SMTP_USETLS=3
SMTPSendMail>RECIPIENT_EMAIL,server,YOUR_OFFICE365_EMAIL,YOUR_NAME,SUBJECT HERE,BODY TEXT,

See also:
http://help.mjtnet.com/article/4-sending-retrieving-emails-via-gmail-ssl

October 3, 2017

Regular Expressions (RegEx) In Window Commands

Filed under: Scripting — Marcus Tettmar @ 8:04 am

Did you know you can use Regular Expressions inside any of Macro Scheduler‘s window functions (e.g. SetFocus, WaitWindowOpen, WaitWindowClosed) so that you can match a window title using RegEx?

As an example someone recently asked us how they would match a window from an old piece of software where the window title was always the letter “b” followed by any 8 digits, e.g. b23425461. Macro Scheduler uses the PCRE syntax, so to match a window title like this we could use this expression:

^b\d{8}

If you’re new to RegEx this means: match from the beginning of the string (^), then match a “b” character followed by any digit (\d) 8 times. {8} means match the proceeding token 8 times. So:

Let>WIN_USEREGEX=1
SetFocus>^b\d{8}

I’ve posted a little about Regular Expressions before. This post links to some useful resources if you are new to RegEx.

Regular Expressions for Dummies
My Most Used RegEx
Remove Tags From HTML with RegEx

September 19, 2017

OCR Functions Anyone?

Filed under: Announcements,Scripting — Marcus Tettmar @ 11:29 am

Note: Macro Scheduler 14.4 with OCR was released in October 2017. This post was written prior to that.

A sneaky peak of some new Screen OCR functions we have in development:

Please note that this feature is currently in development and therefore the syntax and final implementation may differ on release.

July 26, 2017

Parsing XML with XMLParse and XPath

Filed under: Announcements,Scripting,Tutorials — Marcus Tettmar @ 10:22 am

Prompted by a recent forum post from Phil Pendlebury the other day I have written a new article on using XMLParse in the knowledge base:

Parsing XML with XMLParse and XPath

Hope it’s helpful. I’m sure you’ll have more questions when you start using it in anger. So when you do, be sure to head over to the forums and ask for help. 🙂

Older Posts »