Skip to content

Instantly share code, notes, and snippets.

@LesterCovax
Forked from G33kDude/MATRIC.ahk
Created October 6, 2022 16:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LesterCovax/e06a95e771347d91491e1d0306089e00 to your computer and use it in GitHub Desktop.
Save LesterCovax/e06a95e771347d91491e1d0306089e00 to your computer and use it in GitHub Desktop.
#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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment