Recently had a project where i needed to do full duplex synchronization between our companys internal calendar DB, and Google calendar - so ive spent some time with the calendar API - figure someone may find the functions useful.
You must enable API access for this to work, and fill the following in on the script:
Code: Select all
Global clientid = "************************"
Global calname = "**********************"
Global ClientSecret = "*************"
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
Global GGL_Refresh_Url = "https://accounts.google.com/o/oauth2/token"
Global authURL = "https://accounts.google.com/o/oauth2/auth?"
Global Stringredirect = "urn:ietf:wg:oauth:2.0:oob"
Global scope = "https://www.googleapis.com/auth/calendar"
Global clientid =
Global calname =
Global ClientSecret =
Global GGL_Refresh_Headers = {"Content-Type":"application/x-www-form-urlencoded"}
Global GGL_Token_Request_URLParams = {"redirect_uri":Stringredirect,"response_type":"code","client_id":Clientid,"scope":scope}
Global Token_Request = {1:{"code":""},2:{"client_id":clientid},3:{"client_secret":clientsecret},4:{"redirect_uri":stringredirect},5:{"grant_type":"authorization_code"}}
Global GGL_Request_Headers = {"Gdata-Version":"3.0","Content-Type":"application/json","Authorization":GGL_API_Key}
gui,Add,Edit,w400 vToken,[Paste Text from Window Here]
gui,Add,Button,gButton,Submit
for k,v in GGL_Token_Request_URLParams
Params .= k "=" v "&"
authurl .= Params
ie := ComObjCreate("InternetExplorer.Application")
ie.Navigate(authurl)
ie.visible := true
Gui,show,autosize
return
Button:
gui,submit
if(!Token)
return
Token_Request[1,"code"] := Token
Results := ListGoogleEvents("New")
a(Results)
return
exitapp
;Make HTTP POST/GET/PATCH/DELETE Requests with Params and headers
sendstring(type, url, body="", Headers="", URLParams="")
{
pwhr := comobjcreate("MSXML2.ServerXMLHTTP")
for k,v in URLParams
Params .= k "=" v "&"
url := (isobject(URLParams)) ? url "?" substr(params,1,-1) : url
pwhr.open(type, url, false)
for k,v in Headers
pwhr.setrequestheader(k,v)
if isobject(body)
{
for k,v in body
{
for k1,v1 in v
body .= k1 "=" v1 "&"
}
body:=substr(body,1,-1)
}
pwhr.Send(body)
Response := pwhr.Responsetext
return Response
}
;List all Updated and Cancelled Events from Google Calendar
ListGoogleEvents(RetType)
{
tkn := JSON1.Decode(Sendstring("POST",GGL_refresh_url,Token_Request,GGL_Refresh_Headers,"")).access_token
GGL_Request_Headers["Authorization"] := "Bearer " tkn
url = https://www.googleapis.com/calendar/v3/calendars/%calname%/events?syncToken=%syncTok%&key={YOUR_API_KEY}
x := JSON1.Decode(sendstring("GET", Url,"",GGL_Request_Headers))
Events:=[]
EventsCancelled:=[]
if(x.nextSyncToken <> ""){
Events := Append(Events,x.items,"confirmed")
EventsCancelled := Append(EventsCancelled,x.items,"cancelled")
}
while (x.nextSyncToken = "")
{
pageToken := x.nextPageToken
url = https://www.googleapis.com/calendar/v3/calendars/%calname%/events?pageToken=%pageToken%&syncToken=%syncTok%&key={YOUR_API_KEY}
x:=JSON1.Decode(sendstring(`"GET`", url,"",GGL_Request_Headers))
if(x.syncToken <> ""){
Events := Append(Events,x.items,"confirmed")
EventsCancelled := Append(EventsCancelled,x.items,"cancelled")
}else{
Events := Append(Events,x.items,"confirmed")
EventsCancelled := Append(EventsCancelled,x.items,"cancelled")
}
}
RetObj := (RetType="New") ? Events : EventsCancelled
return RetObj
}
;Helper Function to Create Single List of Calendar Events From Google Queries
Append(Arr1,Arr2,Status)
{
for each, item in Arr2
if (item.status = Status)
Arr1.Push(item)
else if(Status = "*")
Arr1.Push(item)
Return Arr1
}
A(Array, Parent="")
{
static
global GuiArrayTree, GuiArrayTreeX, GuiArrayTreeY
if Array_IsCircle(Array)
{
MsgBox, 16, GuiArray, Error: Circular refrence
return "Error: Circular refrence"
}
if !Parent
{
Gui, +HwndDefault
Gui, GuiArray:New, +HwndGuiArray +LabelGuiArray +Resize
Gui, Add, TreeView, vGuiArrayTree
Parent := "P1"
%Parent% := TV_Add("Array", 0, "+Expand")
A(Array, Parent)
GuiControlGet, GuiArrayTree, Pos
Gui, Show,, GuiArray
Gui, %Default%:Default
WinWaitActive, ahk_id%GuiArray%
WinWaitClose, ahk_id%GuiArray%
return
}
For Key, Value in Array
{
%Parent%C%A_Index% := TV_Add(Key, %Parent%)
KeyParent := Parent "C" A_Index
if (IsObject(Value))
A(Value, KeyParent)
else
%KeyParent%C1 := TV_Add(Value, %KeyParent%)
}
return
GuiArrayClose:
Gui, Destroy
return
GuiArraySize:
if !(A_GuiWidth || A_GuiHeight) ; Minimized
return
GuiControl, Move, GuiArrayTree, % "w" A_GuiWidth - (GuiArrayTreeX * 2) " h" A_GuiHeight - (GuiArrayTreeY * 2)
return
}
Array_IsCircle(Obj, Objs=0)
{
if !Objs
Objs := {}
For Key, Val in Obj
if (IsObject(Val)&&(Objs[&Val]||Array_IsCircle(Val,(Objs,Objs[&Val]:=1))))
return 1
return 0
}
class JSON1
{
Decode(jsonStr){
; Create the COM object we'll use to decode the string
SC := ComObjCreate("ScriptControl")
SC.Language := "JScript"
ComObjError(false)
; This next part is a JScript (similar to JavaScript), not AHK, just FYI
; This does the actual parsing
jsCode =
(
function arrangeForAhkTraversing(obj){
if(obj instanceof Array){
for(var i=0 ; i<obj.length ; ++i)
obj[i] = arrangeForAhkTraversing(obj[i]) ;
return ['array',obj] ;
}else if(obj instanceof Object){
var keys = [], values = [] ;
for(var key in obj){
keys.push(key) ;
values.push(arrangeForAhkTraversing(obj[key])) ;
}
return ['object',[keys,values]] ;
}else
return [typeof obj,obj] ;
}
)
; Decode the JSON into an array (call the JS function above) using the COM object, then return an AHK object
SC.ExecuteStatement(jsCode "; obj=" jsonStr)
return this.convertJScriptObjToAhks( SC.Eval("arrangeForAhkTraversing(obj)") )
}
convertJScriptObjToAhks(jsObj){
if(jsObj[0]="object"){
obj := {}, keys := jsObj[1][0], values := jsObj[1][1]
loop % keys.length
obj[keys[A_INDEX-1]] := this.convertJScriptObjToAhks( values[A_INDEX-1] )
return obj
}else if(jsObj[0]="array"){
array := []
loop % jsObj[1].length
array.insert(this.convertJScriptObjToAhks( jsObj[1][A_INDEX-1] ))
return array
}else
return jsObj[1]
}
}
ListGoogleEvents("New") - Returns current events on calendar with no filter
ListGoogleEvents("Deleted") or just "" returns deleted calendar events
If anyone finds this useful - i can provide function examples for updating/adding/deleting events as well.
Thanks!
Treas