Created
June 20, 2020 05:43
-
-
Save G33kDude/a1483a62c4a085fc1b441c92875d3197 to your computer and use it in GitHub Desktop.
Revisions
-
G33kDude created this gist
Jun 20, 2020 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,321 @@ #NoEnv SetBatchLines, -1 #Include <Socket> MATRIC_CONFIG_PATH := A_Desktop "\..\Documents\.matric\config.json" APP_NAME := "AutoHotkey" API_PORT := 50300 RESP_PORT := 50301 ; Assert that MATRIC is running if !WinExist("ahk_exe MatricServer.exe") throw Exception("Please start MATRIC") ; Assert the integration API is enabled if !GetMatricConfig().EnableIntegrationAPI throw Exception("Please enable the MATRIC integration API") ; Open the UDP socket listening on 50301 udpClient := new BetterUDP() udpClient.OnRecv := Func("OnRecv") udpClient.Bind(["127.0.0.1", RESP_PORT]) udpClient.Listen() ; Get the PIN from MATRIC if !(pin := GetPIN(MATRIC_CONFIG_PATH, APP_NAME)) throw Exception("Could not retrieve connection PIN") ; Issue a command to MATRIC UDPSend( ( JOIN ; ahk { "command": "GETCONNECTEDCLIENTS", "appName": APP_NAME, "appPIN": pin } )) return ; Read the MATRIC config to find the PIN for appName ; If one is not present, trigger Matric to generate one GetPIN(configPath, appName) { ; Look for the PIN for i, entry in GetMatricConfig().AuthorizedApps if (entry.appName == appName) return entry.appPin ; Get MATRIC to generate a PIN UDPSend({"command": "CONNECT", "appName": appName}) WinWait, Authorization required ahk_exe MatricServer.exe ControlSend, ahk_parent, {Enter} WinWaitClose ; Look for the PIN again for i, entry in GetMatricConfig().AuthorizedApps if (entry.appName == appName) return entry.appPin } ; Read and parse the MATRIC config file GetMatricConfig() { global MATRIC_CONFIG_PATH return Jxon_Load(FileOpen(MATRIC_CONFIG_PATH, "r").Read()) } ; Handle response data received from MATRIC OnRecv(sock) { MsgBox, % "Received message from MATRIC:`n`n" . Jxon_Dump(Jxon_Load(sock.RecvText()), 4) } ; Wrapper function to make it easier to send commands to MATRIC UDPSend(object) { global API_PORT, udpClient udpClient.SendTextTo(Jxon_Dump(object), ["127.0.0.1", API_PORT]) } ; Add some functions to the UDP class that really ought to be there class BetterUDP extends SocketUDP { SendTextTo(text, address, encoding:="UTF-8") { VarSetCapacity(buffer, StrPut(text, encoding) * (encoding="UTF-16" || encoding="cp1200" ? 2 : 1)) length := StrPut(text, &buffer, encoding) return this.SendTo(&buffer, length, address) } SendTo(pBuffer, length, address) { addr := DllCall("WS2_32\inet_addr", "AStr", address[1], "UInt") port := DllCall("WS2_32\htons", "UShort", address[2], "UShort") VarSetCapacity(sa_in, 16, 0) NumPut(2, sa_in, 0, "UShort") ; AF_INET NumPut(port, sa_in, 2, "UShort") NumPut(addr, sa_in, 4, "UInt") DllCall("Ws2_32\sendto", "UInt", this.Socket, "Ptr", pBuffer , "Int", length, "Int", 0, "Ptr", &sa_in, "Int", 16) } } ; --- Coco's JSON Library --- ; https://github.com/cocobelgica/AutoHotkey-JSON Jxon_Load(ByRef src, args*) { static q := Chr(34) key := "", is_key := false stack := [ tree := [] ] is_arr := { (tree): 1 } next := q . "{[01234567890-tfn" pos := 0 while ( (ch := SubStr(src, ++pos, 1)) != "" ) { if InStr(" `t`n`r", ch) continue if !InStr(next, ch, true) { ln := ObjLength(StrSplit(SubStr(src, 1, pos), "`n")) col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1)) msg := Format("{}: line {} col {} (char {})" , (next == "") ? ["Extra data", ch := SubStr(src, pos)][1] : (next == "'") ? "Unterminated string starting at" : (next == "\") ? "Invalid \escape" : (next == ":") ? "Expecting ':' delimiter" : (next == q) ? "Expecting object key enclosed in double quotes" : (next == q . "}") ? "Expecting object key enclosed in double quotes or object closing '}'" : (next == ",}") ? "Expecting ',' delimiter or object closing '}'" : (next == ",]") ? "Expecting ',' delimiter or array closing ']'" : [ "Expecting JSON value(string, number, [true, false, null], object or array)" , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1] , ln, col, pos) throw Exception(msg, -1, ch) } is_array := is_arr[obj := stack[1]] if i := InStr("{[", ch) { val := (proto := args[i]) ? new proto : {} is_array? ObjPush(obj, val) : obj[key] := val ObjInsertAt(stack, 1, val) is_arr[val] := !(is_key := ch == "{") next := q . (is_key ? "}" : "{[]0123456789-tfn") } else if InStr("}]", ch) { ObjRemoveAt(stack, 1) next := stack[1]==tree ? "" : is_arr[stack[1]] ? ",]" : ",}" } else if InStr(",:", ch) { is_key := (!is_array && ch == ",") next := is_key ? q : q . "{[0123456789-tfn" } else ; string | number | true | false | null { if (ch == q) ; string { i := pos while i := InStr(src, q,, i+1) { val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C") static end := A_AhkVersion<"2" ? 0 : -1 if (SubStr(val, end) != "\") break } if !i ? (pos--, next := "'") : 0 continue pos := i ; update pos val := StrReplace(val, "\/", "/") , val := StrReplace(val, "\" . q, q) , val := StrReplace(val, "\b", "`b") , val := StrReplace(val, "\f", "`f") , val := StrReplace(val, "\n", "`n") , val := StrReplace(val, "\r", "`r") , val := StrReplace(val, "\t", "`t") i := 0 while i := InStr(val, "\",, i+1) { if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0 continue 2 ; \uXXXX - JSON unicode escape sequence xxxx := Abs("0x" . SubStr(val, i+2, 4)) if (A_IsUnicode || xxxx < 0x100) val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6) } if is_key { key := val, next := ":" continue } } else ; number | true | false | null { val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos) ; For numerical values, numerify integers and keep floats as is. ; I'm not yet sure if I should numerify floats in v2.0-a ... static number := "number", integer := "integer" if val is %number% { if val is %integer% val += 0 } ; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo, ; SOMETIMES return strings due to certain optimizations. Since it ; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a else if (val == "true" || val == "false") val := %val% + 0 ; AHK_H has built-in null, can't do 'val := %value%' where value == "null" ; as it would raise an exception in AHK_H(overriding built-in var) else if (val == "null") val := "" ; any other values are invalid, continue to trigger error else if (pos--, next := "#") continue pos += i-1 } is_array? ObjPush(obj, val) : obj[key] := val next := obj==tree ? "" : is_array ? ",]" : ",}" } } return tree[1] } Jxon_Dump(obj, indent:="", lvl:=1) { static q := Chr(34) if IsObject(obj) { static Type := Func("Type") if Type ? (Type.Call(obj) != "Object") : (ObjGetCapacity(obj) == "") throw Exception("Object type not supported.", -1, Format("<Object at 0x{:p}>", &obj)) is_array := 0 for k in obj is_array := k == A_Index until !is_array static integer := "integer" if indent is %integer% { if (indent < 0) throw Exception("Indent parameter must be a postive integer.", -1, indent) spaces := indent, indent := "" Loop % spaces indent .= " " } indt := "" Loop, % indent ? lvl : 0 indt .= indent lvl += 1, out := "" ; Make #Warn happy for k, v in obj { if IsObject(k) || (k == "") throw Exception("Invalid object key.", -1, k ? Format("<Object at 0x{:p}>", &obj) : "<blank>") if !is_array out .= ( ObjGetCapacity([k], 1) ? Jxon_Dump(k) : q . k . q ) ;// key . ( indent ? ": " : ":" ) ; token + padding out .= Jxon_Dump(v, indent, lvl) ; value . ( indent ? ",`n" . indt : "," ) ; token + indent } if (out != "") { out := Trim(out, ",`n" . indent) if (indent != "") out := "`n" . indt . out . "`n" . SubStr(indt, StrLen(indent)+1) } return is_array ? "[" . out . "]" : "{" . out . "}" } ; Number else if (ObjGetCapacity([obj], 1) == "") return obj ; String (null -> not supported by AHK) if (obj != "") { obj := StrReplace(obj, "\", "\\") , obj := StrReplace(obj, "/", "\/") , obj := StrReplace(obj, q, "\" . q) , obj := StrReplace(obj, "`b", "\b") , obj := StrReplace(obj, "`f", "\f") , obj := StrReplace(obj, "`n", "\n") , obj := StrReplace(obj, "`r", "\r") , obj := StrReplace(obj, "`t", "\t") static needle := (A_AhkVersion<"2" ? "O)" : "") . "[^\x20-\x7e]" while RegExMatch(obj, needle, m) obj := StrReplace(obj, m[0], Format("\u{:04X}", Ord(m[0]))) } return q . obj . q }