Created
April 13, 2025 14:32
-
-
Save PaleNeutron/d15e3a83050473b4b33f066d12be734a to your computer and use it in GitHub Desktop.
AutoHotkey Recorder with exact timestamp
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
; EDITED from https://github.com/raeleus/AHK-Macro-Recorder | |
#Requires AutoHotkey v2.0+ | |
;#NoTrayIcon | |
#SingleInstance Off | |
Thread("NoTimers") | |
CoordMode("ToolTip") | |
SetTitleMatchMode(2) | |
DetectHiddenWindows(true) | |
;-------------------------- | |
if (A_Args.Length < 1) { | |
A_Args.Push("~Record1.ahk") | |
} | |
if (A_Args.Length < 2) { | |
A_Args.Push("F1") | |
} | |
LogFile := ".\" A_Args[1] | |
UpdateSettings | |
Recording := false | |
Playing := false | |
ActionKey := A_Args[2] | |
Hotkey(ActionKey, KeyAction) | |
return | |
ShowTip(s := "", pos := "y35", color := "Red|00FFFF") { | |
static bak := "", idx := 0, ShowTip := Gui(), RecordingControl | |
if (bak = color "," pos "," s) | |
return | |
bak := color "," pos "," s | |
SetTimer(ShowTip_ChangeColor, 0) | |
ShowTip.Destroy() | |
if (s = "") | |
return | |
ShowTip := Gui("+LastFound +AlwaysOnTop +ToolWindow -Caption +E0x08000020", "ShowTip") | |
WinSetTransColor("FFFFF0 150") | |
ShowTip.BackColor := "cFFFFF0" | |
ShowTip.MarginX := 10 | |
ShowTip.MarginY := 5 | |
ShowTip.SetFont("q3 s20 bold cRed") | |
RecordingControl := ShowTip.Add("Text", , s) | |
ShowTip.Show("NA " . pos) | |
SetTimer(ShowTip_ChangeColor, 1000) | |
ShowTip_ChangeColor() { | |
r := StrSplit(SubStr(bak, 1, InStr(bak, ",") - 1), "|") | |
RecordingControl.SetFont("q3 c" r[idx := Mod(Round(idx), r.Length) + 1]) | |
return | |
} | |
} | |
;============ Hotkey ============= | |
KeyAction(HotkeyName) { | |
if (Recording) { | |
Stop | |
return | |
} | |
KeyDown := A_TickCount | |
loop { | |
Duration := A_TickCount - KeyDown | |
if (Duration < 400) { | |
ShowTip | |
if (!GetKeyState(ActionKey)) { | |
ShowTip | |
PlayKeyAction | |
break | |
} | |
} else if (Duration < 1400) { | |
ShowTip("RECORD") | |
if (!GetKeyState(ActionKey)) { | |
ShowTip | |
RecordKeyAction | |
break | |
} | |
} else { | |
ShowTip("SHOW SOURCE") | |
if (!GetKeyState(ActionKey)) { | |
ShowTip | |
EditKeyAction | |
break | |
} | |
} | |
} | |
} | |
RecordKeyAction() { | |
if (Recording) { | |
Stop() | |
return | |
} | |
#SuspendExempt | |
RecordScreen() | |
} | |
RecordScreen() { | |
global LogArr := [] | |
global oldid := "" | |
global Recording := false | |
global RelativeX, RelativeY | |
if (Recording || Playing) | |
return | |
UpdateSettings() | |
LogArr := [] | |
oldid := "" | |
Log() | |
Recording := true | |
SetHotkey(1) | |
CoordMode("Mouse", "Screen") | |
MouseGetPos(&RelativeX, &RelativeY) | |
ShowTip("Recording") | |
return | |
} | |
UpdateSettings() { | |
global MouseMode, RecordSleep | |
if (FileExist(LogFile)) { | |
LogFileObject := FileOpen(LogFile, "r") | |
Loop 3 { | |
LogFileObject.ReadLine() | |
} | |
MouseMode := RegExReplace(LogFileObject.ReadLine(), ".*=") | |
LogFileObject.ReadLine() | |
RecordSleep := RegExReplace(LogFileObject.ReadLine(), ".*=") | |
LogFileObject.Close() | |
} else { | |
MouseMode := "screen" | |
RecordSleep := "true" | |
} | |
if (MouseMode != "screen" && MouseMode != "window" && MouseMode != "relative") | |
MouseMode := "screen" | |
if (RecordSleep != "true" && RecordSleep != "false") | |
RecordSleep := "false" | |
} | |
Stop() { | |
global LogArr, Recording, isPaused | |
#SuspendExempt | |
if (Recording) { | |
if (LogArr.Length > 0) { | |
UpdateSettings() | |
s := ";Press " ActionKey " to play. Hold to record. Long hold to edit`n;#####SETTINGS#####`n;What is the preferred method of recording mouse coordinates (screen,window,relative)`n;MouseMode=" MouseMode "`n;Record sleep between input actions (true,false)`n;RecordSleep=" RecordSleep "`nLoop(1)`n{`n`nStartingValue := 0`ni := RegRead(`"HKEY_CURRENT_USER\SOFTWARE\`" A_ScriptName, `"i`", StartingValue)`nRegWrite(i + 1, `"REG_DWORD`", `"HKEY_CURRENT_USER\SOFTWARE\`" A_ScriptName, `"i`")`n`nSetKeyDelay(30)`nSendMode(`"Event`")`nSetTitleMatchMode(2)" | |
if (MouseMode == "window") { | |
s .= "`n;CoordMode(`"Mouse`", `"Screen`")`nCoordMode(`"Mouse`", `"Window`")`n" | |
} else { | |
s .= "`nCoordMode(`"Mouse`", `"Screen`")`n;CoordMode(`"Mouse`", `"Window`")`n" | |
} | |
For k, v in LogArr | |
s .= "`n" v "`n" | |
s .= "`n`n}`nExitApp()`n`n" ActionKey "::ExitApp()`n" | |
s := RegExReplace(s, "\R", "`n") | |
if (FileExist(LogFile)) | |
FileDelete(LogFile) | |
FileAppend(s, LogFile, "UTF-16") | |
s := "" | |
} | |
Recording := 0 | |
LogArr := "" | |
SetHotkey(0) | |
} | |
ShowTip() | |
Suspend(false) | |
Pause(false) | |
isPaused := false | |
return | |
} | |
PlayKeyAction() { | |
#SuspendExempt | |
if (Recording || Playing) | |
Stop() | |
ahk := A_AhkPath | |
if (!FileExist(ahk)) | |
{ | |
MsgBox("Can't Find " ahk " !", "Error", 4096) | |
Exit() | |
} | |
if (A_IsCompiled) { | |
Run(ahk " /script /restart `"" LogFile "`"") | |
} else { | |
Run(ahk " /restart `"" LogFile "`"") | |
} | |
return | |
} | |
EditKeyAction() { | |
#SuspendExempt | |
Stop() | |
SplitPath(LogFile, &LogFileName) | |
try { | |
RegDelete("HKEY_CURRENT_USER\SOFTWARE\" LogFileName, "i") | |
} catch OSError as err { | |
} | |
Run("`"" EnvGet("LocalAppData") "\Programs\Microsoft VS Code\Code.exe`" `"" LogFile "`"") | |
return | |
} | |
;============ Functions ============= | |
SetHotkey(f := false) { | |
f := f ? "On" : "Off" | |
Loop 254 | |
{ | |
k := GetKeyName(vk := Format("vk{:X}", A_Index)) | |
if (!(k ~= "^(?i:|Control|Alt|Shift)$")) | |
Hotkey("~*" vk, LogKey, f) | |
} | |
For i, k in StrSplit("NumpadEnter|Home|End|PgUp" . "|PgDn|Left|Right|Up|Down|Delete|Insert", "|") | |
{ | |
sc := Format("sc{:03X}", GetKeySC(k)) | |
if (!(k ~= "^(?i:|Control|Alt|Shift)$")) | |
Hotkey("~*" sc, LogKey, f) | |
} | |
if (f = "On") { | |
SetTimer(LogWindow) | |
LogWindow() | |
} else | |
SetTimer(LogWindow, 0) | |
} | |
LogKey(HotkeyName) { | |
Critical() | |
k := GetKeyName(vksc := SubStr(A_ThisHotkey, 3)) | |
k := StrReplace(k, "Control", "Ctrl"), r := SubStr(k, 2) | |
if (r ~= "^(?i:Alt|Ctrl|Shift|Win)$") | |
LogKey_Control(k) | |
else if (k ~= "^(?i:LButton|RButton|MButton)$") | |
LogKey_Mouse(k) | |
else { | |
if (k = "NumpadLeft" || k = "NumpadRight") && !GetKeyState(k, "P") | |
return | |
k := StrLen(k) > 1 ? "{" k "}" : k ~= "\w" ? k : "{" vksc "}" | |
Log(k, 1) | |
} | |
} | |
LogKey_Control(key) { | |
global LogArr | |
k := InStr(key, "Win") ? key : SubStr(key, 2) | |
Log("{" k " Down}", 1) | |
Critical("Off") | |
ErrorLevel := !KeyWait(key) | |
Critical() | |
Log("{" k " Up}", 1) | |
} | |
LogKey_Mouse(key) { | |
global LogArr, RelativeX, RelativeY | |
k := SubStr(key, 1, 1) | |
;screen | |
CoordMode("Mouse", "Screen") | |
MouseGetPos(&X, &Y, &id) | |
Log((MouseMode == "window" || MouseMode == "relative" ? ";" : "") "MouseClick(`"" k "`", " X ", " Y ",,, `"D`") `;screen") | |
;window | |
CoordMode("Mouse", "Window") | |
MouseGetPos(&WindowX, &WindowY, &id) | |
Log((MouseMode != "window" ? ";" : "") "MouseClick(`"" k "`", " WindowX ", " WindowY ",,, `"D`") `;window") | |
;relative | |
CoordMode("Mouse", "Screen") | |
MouseGetPos(&tempRelativeX, &tempRelativeY, &id) | |
Log((MouseMode != "relative" ? ";" : "") "MouseClick(`"" k "`", " (tempRelativeX - RelativeX) ", " (tempRelativeY - RelativeY) ",,, `"D`", `"R`") `;relative") | |
RelativeX := tempRelativeX | |
RelativeY := tempRelativeY | |
;get dif | |
CoordMode("Mouse", "Screen") | |
MouseGetPos(&X1, &Y1) | |
t1 := A_TickCount | |
Critical("Off") | |
ErrorLevel := !KeyWait(key) | |
Critical() | |
t2 := A_TickCount | |
if (t2 - t1 <= 200) | |
X2 := X1, Y2 := Y1 | |
else | |
MouseGetPos(&X2, &Y2) | |
;log screen | |
i := LogArr.Length - 2, r := LogArr[i] | |
if (InStr(r, ",,, `"D`")") && Abs(X2 - X1) + Abs(Y2 - Y1) < 5) | |
LogArr[i] := SubStr(r, 1, -16) ") `;screen", Log() | |
else | |
Log((MouseMode == "window" || MouseMode == "relative" ? ";" : "") "MouseClick(`"" k "`", " (X + X2 - X1) ", " (Y + Y2 - Y1) ",,, `"U`") `;screen") | |
;log window | |
i := LogArr.Length - 1, r := LogArr[i] | |
if (InStr(r, ",,, `"D`")") && Abs(X2 - X1) + Abs(Y2 - Y1) < 5) | |
LogArr[i] := SubStr(r, 1, -16) ") `;window", Log() | |
else | |
Log((MouseMode != "window" ? ";" : "") "MouseClick(`"" k "`", " (WindowX + X2 - X1) ", " (WindowY + Y2 - Y1) ",,, `"U`") `;window") | |
;log relative | |
i := LogArr.Length, r := LogArr[i] | |
if (InStr(r, ",,, `"D`", `"R`")") && Abs(X2 - X1) + Abs(Y2 - Y1) < 5) | |
LogArr[i] := SubStr(r, 1, -23) ",,,, `"R`") `;relative", Log() | |
else | |
Log((MouseMode != "relative" ? ";" : "") "MouseClick(`"" k "`", " (X2 - X1) ", " (Y2 - Y1) ",,, `"U`", `"R`") `;relative") | |
} | |
LogWindow() { | |
global oldid, LogArr, MouseMode | |
static oldtitle | |
id := WinExist("A") | |
title := WinGetTitle() | |
class := WinGetClass() | |
if (title = "" && class = "") | |
return | |
if (id = oldid && title = oldtitle) | |
return | |
oldid := id, oldtitle := title | |
title := SubStr(title, 1, 50) | |
title .= class ? " ahk_class " class : "" | |
title := RegExReplace(Trim(title), "[``%;]", "``$0") | |
CommentString := "" | |
if (MouseMode != "window") | |
CommentString := ";" | |
s := CommentString "tt := `"" title "`"`n" CommentString "WinWait(tt)" . "`n" CommentString "if (!WinActive(tt))`n" CommentString " WinActivate(tt)" | |
i := LogArr.Length | |
r := i = 0 ? "" : LogArr[i] | |
if (InStr(r, "tt = ") = 1) | |
LogArr[i] := s, Log() | |
else | |
Log(s) | |
} | |
Log(str := "", Keyboard := false) { | |
global LogArr, RecordSleep | |
static LastTime := 0 | |
t := A_TickCount | |
Delay := (LastTime ? t - LastTime : 0) | |
LastTime := t | |
if (str = "") | |
return | |
i := LogArr.Length | |
r := i = 0 ? "" : LogArr[i] | |
; if (Keyboard && InStr(r, "Send") && Delay < 1000) { | |
; LogArr[i] := SubStr(r, 1, -1) . str "`"" | |
; return | |
; } | |
LogArr.Push((RecordSleep == "false" ? ";" : "") "Sleep(" (Delay) ")") | |
LogArr.Push(Keyboard ? "Send `"{Blind}" str "`"" : str) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment