Last active
June 5, 2018 03:51
-
-
Save heri16/2f335efefe76e6f69f22449cab78fd48 to your computer and use it in GitHub Desktop.
AutoHotKey Server to Automate Export/Import of XML data into CPSSoft RENE Admin via HTTP API.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Persistent | |
#NoEnv ; Avoids checking empty variables to see if they are environment variables | |
#KeyHistory 0 ; Disable key history | |
#SingleInstance force ; Skips the dialog box and replaces the old instance automatically | |
SetBatchLines, -1 ; Never sleep (i.e. have the script run at maximum speed) | |
SendMode, Input ; Use faster and more reliable send method | |
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory | |
CoordMode, Mouse, Screen ; Make 0,0 the top-left of screen | |
SetTitleMatchMode, 3 ; Allow only exact window title matches | |
FileEncoding, UTF-8 ; Use UTF-8 file encoding | |
; Enable Console-based Script | |
;LibConDebug := 1 | |
;FreeConsole() | |
;SmartStartConsole() | |
paths := {} | |
paths["/file/put"] := Func("PutFile") | |
paths["/menu/exim"] := Func("OpenImportExportMenu") | |
paths["/import/xml"] := Func("ImportXmlData") | |
paths["/import/logs"] := Func("WaitImportLogs") | |
paths["/"] := Func("HelloWorld") | |
paths["404"] := Func("NotFound") | |
paths["/logo"] := Func("Logo") | |
server := new HttpServer() | |
server.LoadMimes(A_ScriptDir . "/mime.types") | |
server.SetPaths(paths) | |
server.Serve(8001) | |
Puts("Ready.") | |
return | |
Logo(ByRef req, ByRef res, ByRef server) { | |
server.ServeFile(res, A_ScriptDir . "/logo.png") | |
res.status := 200 | |
} | |
NotFound(ByRef req, ByRef res) { | |
res.SetBodyText("Page not found") | |
} | |
HelloWorld(ByRef req, ByRef res) { | |
res.SetBodyText("<html><body><img src='./logo'/><br/>Hello World</body></html>") | |
res.status := 200 | |
} | |
WaitImportLogs(ByRef req, ByRef res) { | |
global winMainHwnd | |
global filePattern | |
Puts("WaitImportLogs:") | |
if winMainHwnd is not integer | |
{ | |
return HttpError(res, "Main ReneAdmin window is unknown. Please do /menu/exim first.") | |
} | |
WinGet, progPID, PID, ahk_id %winMainHwnd% | |
Print("WaitingImportCompleteDialog...") | |
Loop { | |
Print(".") | |
WinWait, ahk_class TfrmDialog ahk_pid %progPID%,, 1 | |
} Until ErrorLevel == 0 | |
importCompleteHwnd := WinExist("") | |
NewLine() | |
Puts("ClosingImportCompleteDialog...") | |
ControlClick, TPanel2, ahk_id %importCompleteHwnd% | |
Puts("CheckingOfflineForm...") | |
ControlGet, offlineFormHwnd, Hwnd,, TfrmExportHistory1, ahk_id %winMainHwnd% | |
if (!offlineFormHwnd) { | |
return HttpError(res, "ReneAdmin Accurate Offline Form could not be found") | |
} | |
Puts("SelectingOfflineFilters...") | |
ControlClick, TAdvOfficeCheckBox2, ahk_id %offlineFormHwnd% | |
ControlClick, TAdvOfficeCheckBox3, ahk_id %offlineFormHwnd% | |
ControlClick, TAdvOfficeCheckBox4, ahk_id %offlineFormHwnd% | |
ControlClick, TAdvOfficeCheckBox5, ahk_id %offlineFormHwnd% | |
Puts("ClickingRefreshToolbar..") | |
ControlGet, offlineToolBarHwnd, Hwnd,, TToolBar1, ahk_id %offlineFormHwnd% | |
ControlClick, X265 Y10, ahk_id %offlineToolBarHwnd% | |
Puts("SelectingFirstRow...") | |
Sleep, 2000 | |
ControlSend, TtiInternalVirtualTree1, {Down}, ahk_id %offlineFormHwnd% | |
Puts("ClickingShowLogToolbar...") | |
ControlClick, X180 Y10, ahk_id %offlineToolBarHwnd% | |
Puts("DeselectingOfflineFilters...") | |
ControlClick, TAdvOfficeCheckBox2, ahk_id %offlineFormHwnd% | |
ControlClick, TAdvOfficeCheckBox3, ahk_id %offlineFormHwnd% | |
ControlClick, TAdvOfficeCheckBox4, ahk_id %offlineFormHwnd% | |
ControlClick, TAdvOfficeCheckBox5, ahk_id %offlineFormHwnd% | |
Puts("WaitingShowLogWindow...") | |
WinWait, ahk_class TfrmExportLog ahk_pid %progPID%,, 5 | |
if (ErrorLevel) { | |
return HttpError(res, "ReneAdmin Offline SeeLog Window could not be found") | |
} | |
showLogWindow := WinExist("") | |
ControlGetText, logsInHistory, TMemo1, ahk_id %showLogWindow% | |
ControlSend, TAdvSmoothPanel1, {Alt Down}t{Alt Up}, ahk_id %showLogWindow% | |
Puts("ArchivingXmlFile...") | |
FileMove, %filePattern%, ..\Data\Archive | |
Puts("FetchedImportLogs.") | |
importLogs := logsInHistory | |
res.SetBodyText(logsInHistory) | |
res.status := 200 | |
} | |
OpenImportExportMenu(ByRef req, ByRef res) { | |
global winMainHwnd | |
Puts("OpenImportExportMenu:") | |
WriteReneSetupConfig({ PATHDATABASE: "3002.FDB" }) | |
startResult := StartWrappedProgram() | |
if startResult is not integer | |
{ | |
return HttpError(res, startResult) | |
} | |
progPID := startResult | |
DetectHiddenWindows, On | |
winMainHwnd := WinExist(Format("ahk_class TfrmMain ahk_pid {}", progPID)) | |
if (!winMainHwnd) { | |
Puts("WaitingLoginWindow...") | |
WinWait, ahk_class TfrmAdminLogin ahk_pid %progPID%, , 10 | |
if (!ErrorLevel) { | |
secretPin := DecryptSecretPin(".\pin.txt") | |
ControlSetText, TEdit1, %secretPin% | |
ControlSend, TEdit1, {Enter} | |
} | |
Puts("WaitingMainWindow...") | |
WinWait, ahk_class TfrmMain ahk_pid %progPID%, , 15 | |
if (ErrorLevel) { | |
return HttpError(res, "Main Window could not be found") | |
} | |
winMainHwnd := WinExist("") | |
} | |
DetectHiddenWindows, Off | |
WinShow, ahk_id %winMainHwnd% | |
WinMinimize, ahk_id %winMainHwnd% | |
retry := 50 | |
While (!toolBarPagerHwnd && retry > 0) | |
{ | |
Puts("WaitingToolBarPager...") | |
Sleep, 100 | |
ControlGet, toolBarPagerHwnd, Hwnd,, TAdvToolBarPager1, ahk_id %winMainHwnd% | |
retry -= 1 | |
} | |
if (!toolBarPagerHwnd) | |
{ | |
return HttpError(res, "ReneAdmin ToolBarPager Control could not be found") | |
} | |
retry := 30 | |
While (!isToolBarPagerVisible && retry > 0) | |
{ | |
Puts("WaitingToolBarPagerVisible...") | |
Sleep, 200 | |
ControlGet, isToolBarPagerVisible, Visible,,, ahk_id %toolBarPagerHwnd% | |
retry -= 1 | |
} | |
if (!isToolBarPagerVisible) { | |
return HttpError(res, "ReneAdmin ToolBarPager Control did not become visible") | |
} | |
; Move right to third tab of ToolBarPager | |
ControlFocus, , ahk_id %toolBarPagerHwnd% | |
ControlSend,, {Right}{Right}, ahk_id %toolBarPagerHwnd% | |
retry := 30 | |
While (!accurateBtnHwnd && retry > 0) | |
{ | |
Puts("WaitingAccurateButton...") | |
Sleep, 100 | |
ControlGet, accurateBtnHwnd, Hwnd,, ACCURATE 4, ahk_id %toolBarPagerHwnd% | |
retry -= 1 | |
} | |
if (!accurateBtnHwnd) { | |
return HttpError(res, "ReneAdmin AccurateButton Control was not found") | |
} | |
; Move back to first tab of ToolBarPager | |
ControlSend,, {Left}{Left}, ahk_id %toolBarPagerHwnd% | |
;ControlFocus,, ahk_id %accurateBtnHwnd% | |
;ControlSend,, {Space}, ahk_id %accurateBtnHwnd% | |
;Puts("OpeningOnlineForm...") | |
;ControlClick, X90 Y15, ahk_id %accurateBtnHwnd% | |
;WinWait, ahk_class #32768 ahk_pid %progPID%,, 5 | |
;if (ErrorLevel) { | |
; return HttpError(res, "DropDown could not be found") | |
;} | |
;ControlSend,, o | |
;retry := 50 | |
;While (!onlineFormHwnd && retry > 0) | |
;{ | |
; Puts("WaitingOnlineForm...") | |
; Sleep, 100 | |
; ControlGet, onlineFormHwnd, Hwnd,, TfrmAccurateAgentQueue1, ahk_id %winMainHwnd% | |
; retry -= 1 | |
;} | |
;if (!onlineFormHwnd) { | |
; return HttpError(res, "ReneAdmin Accurate Online Form could not open") | |
;} | |
Puts("OpeningOfflineForm...") | |
ControlClick, X90 Y15, ahk_id %accurateBtnHwnd% | |
WinWait, ahk_class #32768 ahk_pid %progPID%,, 5 | |
if (ErrorLevel) { | |
return HttpError(res, "DropDown could not be found") | |
} | |
ControlSend,, f | |
retry := 50 | |
While (!offlineFormHwnd && retry > 0) | |
{ | |
Puts("WaitingOfflineForm...") | |
Sleep, 100 | |
ControlGet, offlineFormHwnd, Hwnd,, TfrmExportHistory1, ahk_id %winMainHwnd% | |
retry -= 1 | |
} | |
if (!offlineFormHwnd) { | |
return HttpError(res, "ReneAdmin Accurate Offline Form could not open") | |
} | |
Puts("OpenedForms.") | |
res.SetBodyText("Opened") | |
res.status := 200 | |
} | |
ImportXmlData(ByRef req, ByRef res) { | |
global winMainHwnd | |
global filePattern | |
Puts("ImportXmlData:") | |
filenameUnsafe := req.queries["filename"] | |
SplitPath, filenameUnsafe, filename | |
if (!filename) { | |
res.SetBodyText("Invalid Input: filename required in querystring") | |
res.status := 400 | |
return | |
} | |
if winMainHwnd is not integer | |
{ | |
return HttpError(res, "Main ReneAdmin window is unknown. Please do /menu/exim first.") | |
} | |
WinGet, progPID, PID, ahk_id %winMainHwnd% | |
Puts("CheckingOfflineForm...") | |
ControlGet, offlineFormHwnd, Hwnd,, TfrmExportHistory1, ahk_id %winMainHwnd% | |
if (!offlineFormHwnd) { | |
return HttpError(res, "ReneAdmin Accurate Offline Form could not be found") | |
} | |
Puts("ClickingImportToolbar...") | |
ControlGet, offlineToolBarHwnd, Hwnd,, TToolBar1, ahk_id %offlineFormHwnd% | |
ControlClick, X100 Y10, ahk_id %offlineToolBarHwnd% | |
Puts("WaitingImportWindow...") | |
WinWait, ahk_class TfrmImportAcc ahk_pid %progPID%,, 10 | |
if (ErrorLevel) { | |
return HttpError(res, "Import Window could not be found") | |
} | |
importFormHwnd := WinExist("") | |
Puts("InputImportFilePath...") | |
filePattern := Format("..\Data\{}", filename) | |
Loop, Files, %filePattern% | |
{ | |
filePath := A_LoopFileLongPath | |
ControlSetText, TEdit1, %filePath%, ahk_id %importFormHwnd% | |
ControlSend, TEdit1, {Enter}, ahk_id %importFormHwnd% | |
Break | |
} | |
if (!filePath) { | |
return HttpError(res, Format("{} file could not be found", filename)) | |
} | |
retry := 40 | |
While (!importMemoHwnd && retry > 0) | |
{ | |
Puts("WaitingImportMemo...") | |
Sleep, 500 | |
ControlGet, importMemoHwnd, Hwnd,, TMemo1, ahk_id %importFormHwnd% | |
retry -= 1 | |
} | |
if (!importMemoHwnd) | |
{ | |
return HttpError(res, "ReneAdmin ImportMemo Control could not be found") | |
} | |
retry := 10 | |
While (!isImportMemoVisible && retry > 0) | |
{ | |
Puts("WaitingImportMemoVisible...") | |
Sleep, 100 | |
ControlGet, isImportMemoVisible, Visible,,, ahk_id %importMemoHwnd% | |
retry -= 1 | |
} | |
if (!isImportMemoVisible) { | |
return HttpError(res, "ReneAdmin ImportMemo Control did not become visible within timeout") | |
} | |
Puts("ClickingImportButton...") | |
ControlGetText, importMemoText,, ahk_id %importMemoHwnd% | |
ControlSend, TAdvSmoothPanel1, {Alt Down}i{Alt Up}, ahk_id %importFormHwnd% | |
Puts("StartedImport.") | |
res.SetBodyText(importMemoText) | |
res.status := 200 | |
} | |
PutFile(ByRef req, ByRef res) { | |
if (req.method != "PUT") { | |
res.headers["Allow"] := "PUT" | |
res.status := 405 | |
return | |
} | |
filenameUnsafe := req.queries["filename"] | |
SplitPath, filenameUnsafe, filename | |
if (!filename) { | |
filename := CreateUUID() | |
;res.SetBodyText("Invalid QueryString, filename required") | |
;res.status := 400 | |
;return | |
} | |
Puts(Format("File {}: {} bytes", filename, StrLen(req.body))) | |
file := FileOpen(Format("..\Data\{}", filename), "w") | |
file.Write(req.body) | |
file.Close() | |
res.SetBodyText(filename) | |
res.status := 200 | |
} | |
WriteReneSetupConfig(config) { | |
;Puts("WriteReneSetupConfig") | |
FileRead, configTemplate, .\hdsetup.xml.tpl | |
For key, value in config | |
{ | |
configTemplate := RegExReplace(configTemplate, Format("<{1}>.*</{1}>", key), Format("<{1}>{2}</{1}>", key, value)) | |
} | |
configFile := FileOpen(Format("{}\RENE2\hdsetup.xml", A_AppData), "w") | |
configFile.Write(configTemplate) | |
configFile.Close() | |
} | |
StartWrappedProgram() { | |
;Puts("StartWrappedProgram") | |
; Ensure reneadmin.exe is running under our current user session | |
Process, Exist, reneadmin.exe ; check to see if MTAutoDown.exe is running | |
existingPID := ErrorLevel | |
if (existingPID) | |
{ | |
;Puts("ReuseReneAdminStep") | |
DetectHiddenWindows, On | |
isMainWinExist := WinExist("ahk_exe reneadmin.exe") | |
DetectHiddenWindows, Off | |
if (isMainWinExist) | |
{ | |
progPID := existingPID | |
} | |
else | |
{ | |
;Process, Close, reneadmin.exe ; close all other running processes in other desktops | |
;isOk := ErrorLevel | |
;if (!isOk) { | |
; return "reneadmin.exe running but could not be closed" | |
;} | |
} | |
} | |
if (!progPID) | |
{ | |
;Puts("StartReneAdminStep") | |
IfExist, %A_ProgramsCommon%\CPSSoft\RENE2\RENE Administration.lnk | |
{ | |
Run, %A_ProgramsCommon%\CPSSoft\RENE2\RENE Administration.lnk, , UseErrorLevel, progPID | |
if (A_LastError > 0) { | |
return Format("RENE Administration.lnk could not be launched due to Error Code {}", A_LastError) | |
} | |
} | |
Else | |
{ | |
return "RENE Administration.lnk could not be found" | |
} | |
} | |
return progPID | |
} | |
DecryptSecretPin(pinFile) { | |
if (GetConsoleHandle()) | |
{ | |
restartConsoleWindow := true | |
} | |
shell := ComObjCreate("WScript.Shell") | |
exec := shell.Exec("powershell -WindowStyle Hidden -NonInteractive -NoLogo -Command -") | |
commands = | |
( | |
$secpass = Get-Content "%pinFile%" | ConvertTo-SecureString | |
$cred = New-Object System.Management.Automation.PSCredential ("pin", $secpass) | |
$cred.GetNetworkCredential().Password | |
exit | |
) | |
exec.StdIn.WriteLine(commands) | |
output := exec.StdOut.ReadAll() | |
if (restartConsoleWindow) { | |
SmartStartConsole() | |
} | |
return Trim(output, " `t`n`r") | |
} | |
HttpError(res, msg) { | |
Puts(msg) | |
res.SetBodyText(Format("Error: {}", msg)) | |
res.status := 500 | |
} | |
FileTruncate(filename) | |
{ | |
hFile := DllCall( "CreateFile", Str,filename, UInt,0x40000000, UInt,3, UInt,0, UInt,5, UInt,0, UInt,0 ) | |
DllCall( "CloseHandle", UInt,hFile ) ; Thanks tonne ! | |
return hFile > 0 ? 1 : hFile | |
} | |
CreateUUID() | |
{ | |
VarSetCapacity(UUID, 16, 0) | |
if (DllCall("rpcrt4.dll\UuidCreate", "ptr", &UUID) != 0) | |
return (ErrorLevel := 1) & 0 | |
if (DllCall("rpcrt4.dll\UuidToString", "ptr", &UUID, "uint*", suuid) != 0) | |
return (ErrorLevel := 2) & 0 | |
return StrGet(suuid), DllCall("rpcrt4.dll\RpcStringFree", "uint*", suuid) | |
} | |
UuidEqual(uuid1, uuid2) | |
{ | |
return DllCall("rpcrt4.dll\UuidEqual", "ptr", &uuid1, "ptr", &uuid2, "ptr", &RPC_S_OK) | |
} | |
#include %A_ScriptDir%\LibCon.ahk | |
#include %A_ScriptDir%\AHKhttp.ahk | |
#include %A_ScriptDir%\AHKsock.ahk |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment