AutoHotkey Webinar- Intro to API calls with AutoHotkey

Intro to API calls with AutoHotkeyIn this AutoHotkey Webinar we cover an Intro to API calls with AutoHotkey.

Video Hour 1: High-level:

  • What is a Webservice API call
  • Web Scraping vs. WebServices / API
  • List of some “fun” WebService / APIs as well as links to resources to tens-of-thousands!
  • Differences between a Get vs. Post request
  • High-level look at oAuth1 vs. oAuth2
  • Resources
    • HTTP: Intro to HyperText Protocol, Types of Requests, oAuth, Parsing XML, XPath, JSON
    • AutoHotkey resource: Syntax writer, winHTTPrequest, Msxml2 vs. WinHTTPrequest, Parsing XML & JSON
  • Basic examples of API requests (compared using browser and winHTTP request)

Video Hour 2: Coding and Q&A

  • Played with several APIs
  • Discussed & demonstrated reverse-engineering API calls from a website
  • Passing key-value pairs (and using a function to keep it organized)
  • Reviewed additional APIs
  • Q&A

Script Highlight: Select text and “pretty” hyperlink text

The below script demonstrates how you can use AHK to automate highlighting text on a page and then, using the winClip library, constructing a “pretty” html link.  Here are links to the GetActiveBrowserURL and WinClipAPI / WinClip

Intro to API calls with AutoHotkey


What is a Webservice API? (Application Programming Interface?)

API calls with AutoHotkey


Examples of Webservice / API calls with AutoHotkey

  • Application / Software querying products for sale on Amazon.com
  • App on your phone getting latest Weather
  • Database pulling updated sales report
  • Using your Tablet to Select movies to watch on Netflix
  • DropBox application syncing files between your computer & cloud
  • Google places search

APIs are becoming increasingly available!

…Since 2005, we’ve seen APIs grow from a curiosity, to a trend, and now to the point where APIs are core to many businesses. APIs have provided tremendous value to countless organizations and developers, which is reflected in their continued growth.   Source: Programmable Web

API calls with AutoHotkey

 

 


Main Differences between WebService / API call & Web Browser

API calls with AutoHotkey


When to use API verse Web Scraping?

API calls with AutoHotkey


Some Webservices / APIs

Some of the Amazing APIs out there…

Programmable Web – list of thousands of APIs as well as a great, six-part, article reviewing APIs

Short-list of APIs open to public on Wikipedia,   Google APIs– Over 100 APIs @ Google; Yahoo APIs

Social Sites : Twitter, Pinterest, Google+, YouTube, Facebook, LinkedIn, reddit, StumbleUpon, WordPress, Instagram

Contact / Business Lookup: ClearBit, Pipl, FullContact, Dunn & Bradstreet , Foursquare, Google Places, Yelp, GeoNames

Weather: Weather Underground, Yahoo Weather, World Air

Finance: Google Finance, Yahoo Finance, PayPal, Stripe, XigniteRealTime, Ally Invest

Government: A bunch of Government APIs, Cloud.Gov, US Census, 18F.gsa.gov

Additional: Wikipedia, Amazon Product, email to text, SmartSheet,SurveyGizmo, Bitly, SnapOCR, PasteBin, DropBox, Zoho, Zillow, imugr, Mailgun, MailChimp,  Microsoft: Graph, MS Office: Excel  Word  SharePoint  OneNote Outlook Yammer PowerPoint


Break-down of a REST API request via COM object

  • Create COM object
  • Open Endpoint (w/ parameters & Authentication if GET request)
  • Set RequestHeader(s)
  • Send (w/”payload & Authentication” for POST requests)
  • Get response (body or text)

Main differences of oAuth1 verse oAuth2

Unfortunately there is no “standard” implementation of oAuth1 or oAuth2 however, at a high-level, here are some of the main conceptual differences:

oAuth1:

  • Need a Key & Token from Webservice API (typically different than your username and password)
  • You use your Key & Token in your API call
  • oAuth1 is less secure and, generally, being phased out
  • While being phased out often the “developers” (us) can use oAuth1 for development of the “app”

oAuth2:

  • Need a Key and Token (same as oAuth1) however you use the Key & Token and some other parameters to perform a “handshake” which returns a secure token which typically times-out in seconds / minutes/hours
  • Your token is restricted to the level of your account (or what has been authorized)
  • The secure token is what is shared with your actual endpoint. (this allows other Social sites (like LinkedIn, Facebook, etc.) to assist your login but not have your username/password to the endpoint

HTTP and AutoHotkey Resources

HTTP Protocol & General Tutorials

API calls with AutoHotkeyAutoHotkey specific



Let’s make some API calls with AutoHotkey!

Weather Underground

  1. Dallas forecast in XML
  2. Dallas forecast in JSON
  3. Using WinHttpRequest
  4. Example with browser

Yahoo business

  1. Pizza restaurants in zip code 75019 in XML
  2. Pizza restaurants in zip code 75019 in JSON
  3. Using WinHTTPRequest

Intro to API calls with AutoHotkey

XML Parsing is super easy with this AutoHotkey class

XML ParsingI’ve been using APIs for several years however I was never great at parsing the returned data.  In the below video maestrith, author of AHK Studio, walked me through using the XML class he wrote for parsing XML.  It leverages using XPath and is a life saver!

Here is the video where he walks me through using it

And this is the file we reviewed when doing the XML parsing

;               | Root Element XML files can have only 1 root element, this creates that.
;			 |		       | Optional Path/Filename if you want to use an xml file outside of the scripts directory
;			 |		       |
MyXML:=new XML("RootElement","lib\MyXML.xml") ;Creates or Loads the XML if the file exists
;| Stored object into the name Node
;|		          | Path of the new Element you wish to create
;|		          | 				| Associative Array of Attributes that you want to be in the new element
;|		          | 				|							| A text string that is appended to <node>HERE!</node> but be careful
;|		          | 				|							| If you want to have nodes below this current node do not give it a text value
;|		          | 				|							| 	   | Duplicate, This allows for the same named Element to be at the same level 1=Allow Duplicate
;|		          | 				|							| 	   |
Node:=MyXML.Add("My/New/Path",{Attribute:"Value",Another:"Attribute"},"Text Value",0) ;Create a new Node Element
loop, 5
  Node:=MyXML.Add("My/New/Path",{Attribute:"Value",index:A_Index,Another:"Attribute"},"hello" A_Index+10,1) ;Create a new Node Element

Top:=MyXML.Add("Just_A_Node") ;Create a node on its own


;            | The node object that you created previously
;            | 	| The name for the new node
;            | 	|			| Like above an Associative Array for the Attributes
;            | 	|			|			| Text Value for the Node
;            | 	|			|			|
MyXML.Under(Top,"Node_Name",{Attribute:"Value"},"Text Value")

MyXML.Transform() ;Makes the XML Pretty
MyXML.Transform() ;Sometimes needed.

;~list:=MyXML.SN("//Path") ;Select NODES: build list which has all nodes with the nodename of Path  ;// means look everywhere
;~node:=myxml.SSN("//My/New")
;~list:=SN(node,"Path")
;~while(item:=list.item[a_index-1],obj:=xml.EA(item))
;~m(item.xml,obj.Attribute,obj.index)
;~m(MyXML.SSN("//My/New").xml)
;~m(SSN(Top,"Node_Name").xml) ;first item is ahk object.  then pass what we want from that object
;~m(SSN(Top,"Node_Name").xml) ;first item is ahk object.  then pass what we want from that object

;			     @=Attribute
;~m(MyXML.SSN("//Path[@index='4']").xml) ;//Path gets first.  If want multiple ones (and want specific one) use brackets and @index
;~m(MyXML.SSN("//Path[text()='15']").xml) ;//Path gets first.  If want multiple ones (and want specific one) use brackets and @index
;                   text() =looking for exact text values
;~m(MyXml.SSN("//Path[contains(text(),'lo11')]").xml)
;                   does matching across all path nodes thtat contain text value
                    ;~swap contains(text(),'lol1')  to contains(@Attribute,'" variable "') ;use inside double outside single quotes to pass variable



m(MyXML[]) ;Shows a MsgBox with the contents of the new node
;          1 is to "Make the XML pretty"
MyXML.Save(1) ;Saves the xml
ExitApp

Class XML{
  keep:=[]
  __New(param*){
    if(!FileExist(A_ScriptDir "\lib"))
      FileCreateDir,%A_ScriptDir%\lib
    root:=param.1,file:=param.2,file:=file?file:root ".xml",temp:=ComObjCreate("MSXML2.DOMDocument"),temp.setProperty("SelectionLanguage","XPath"),this.xml:=temp,this.file:=file,xml.keep[root]:=this
    ;temp.preserveWhiteSpace:=1
    if(FileExist(file)){
      FileRead,info,%file%
      if(info=""){
        this.xml:=this.CreateElement(temp,root)
        FileDelete,%file%
      }else
        temp.LoadXML(info),this.xml:=temp
    }else
      this.xml:=this.CreateElement(temp,root)
  }CreateElement(doc,root){
    return doc.AppendChild(this.xml.CreateElement(root)).ParentNode
  }Add(path,att:="",text:="",dup:=0){
    p:="/",add:=(next:=this.SSN("//" path))?1:0,last:=SubStr(path,InStr(path,"/",0,0)+1)
    if(!next.xml){
      next:=this.SSN("//*")
      for a,b in StrSplit(path,"/")
        p.="/" b,next:=(x:=this.SSN(p))?x:next.AppendChild(this.xml.CreateElement(b))
    }if(dup&&add)
      next:=next.ParentNode.AppendChild(this.xml.CreateElement(last))
    for a,b in att
      next.SetAttribute(a,b)
    next.text:=text
    return next
  }
  Find(info*){
    static last:=[]
    doc:=info.1.NodeName?info.1:this.xml
    if(info.1.NodeName)
      node:=info.2,find:=info.3,return:=info.4!=""?"SelectNodes":"SelectSingleNode",search:=info.4
    else
      node:=info.1,find:=info.2,return:=info.3!=""?"SelectNodes":"SelectSingleNode",search:=info.3
    if(InStr(info.2,"descendant"))
      last.1:=info.1,last.2:=info.2,last.3:=info.3,last.4:=info.4
    if(InStr(find,"'"))
      return doc[return](node "[.=concat('" RegExReplace(find,"'","'," Chr(34) "'" Chr(34) ",'") "')]/.." (search?"/" search:""))
    else
      return doc[return](node "[.='" find "']/.." (search?"/" search:""))
  }
  Under(under,node,att:="",text:="",list:=""){
    new:=under.AppendChild(this.xml.CreateElement(node)),new.text:=text
    for a,b in att
      new.SetAttribute(a,b)
    for a,b in StrSplit(list,",")
      new.SetAttribute(b,att[b])
    return new
  }ReCreate(path,new){
    rem:=this.SSN(path),rem.ParentNode.RemoveChild(rem),new:=this.Add(new)
    return new
  }SSN(path){
    return this.xml.SelectSingleNode(path)
  }SN(path){
    return this.xml.SelectNodes(path)
  }__Get(x=""){
    return this.xml.xml
  }Get(Path,Default){
    text:=this.SSN(path).text
    return text?text:Default
  }Transform(){
    static
    if(!IsObject(xsl))
      xsl:=ComObjCreate("MSXML2.DOMDocument"),xsl.loadXML("<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform""><xsl:output method=""xml"" indent=""yes"" encoding=""UTF-8""/><xsl:template match=""@*|node()""><xsl:copy>`n<xsl:apply-templates select=""@*|node()""/><xsl:for-each select=""@*""><xsl:text></xsl:text></xsl:for-each></xsl:copy>`n</xsl:template>`n</xsl:stylesheet>"),style:=null
    this.xml.transformNodeToObject(xsl,this.xml)
  }Save(x*){
    if(x.1=1)
      this.Transform()
    if(this.xml.SelectSingleNode("*").xml="")
      return m("Errors happened while trying to save " this.file ". Reverting to old version of the XML")
    filename:=this.file?this.file:x.1.1,ff:=FileOpen(filename,0),text:=ff.Read(ff.length),ff.Close()
    if(!this[])
      return m("Error saving the " this.file " xml.  Please get in touch with maestrith if this happens often")
    if(text!=this[])
      file:=FileOpen(filename,"rw"),file.seek(0),file.write(this[]),file.length(file.position)
  }EA(path,att:=""){
    list:=[]
    if(att)
      return path.NodeName?SSN(path,"@" att).text:this.SSN(path "/@" att).text
    nodes:=path.NodeName?path.SelectNodes("@*"):nodes:=this.SN(path "/@*")
    while,n:=nodes.item(A_Index-1)
      list[n.NodeName]:=n.text
    return list
}}
SSN(node,path){
  return node.SelectSingleNode(path)
}
SN(node,path){
  return node.SelectNodes(path)
}
m(x*){
  active:=WinActive("A")
  ControlGetFocus,Focus,A
  ControlGet,hwnd,hwnd,,%Focus%,ahk_id%active%
  static list:={btn:{oc:1,ari:2,ync:3,yn:4,rc:5,ctc:6},ico:{"x":16,"?":32,"!":48,"i":64}},msg:=[],msgbox
  list.title:="AHK Studio",list.def:=0,list.time:=0,value:=0,msgbox:=1,txt:=""
  for a,b in x
    obj:=StrSplit(b,":"),(vv:=List[obj.1,obj.2])?(value+=vv):(list[obj.1]!="")?(List[obj.1]:=obj.2):txt.=b "`n"
  msg:={option:value+262144+(list.def?(list.def-1)*256:0),title:list.title,time:list.time,txt:txt}
  Sleep,120
  MsgBox,% msg.option,% msg.title,% msg.txt,% msg.time
  msgbox:=0
  for a,b in {OK:value?"OK":"",Yes:"YES",No:"NO",Cancel:"CANCEL",Retry:"RETRY"}
    IfMsgBox,%a%
    {
      WinActivate,ahk_id%active%
      ControlFocus,%Focus%,ahk_id%active%
      return b
    }
}
t(x*){
  for a,b in x{
    if((obj:=StrSplit(b,":")).1="time"){
      SetTimer,killtip,% "-" obj.2*1000
      Continue
    }
    list.=b "`n"
  }
  Tooltip,% list
  return
  killtip:
  ToolTip
  return
}