Auto Hot Key Tutorial

Here is a AHK script I came up with a while back that does exactly what the original poster was looking for. It’s scripted for 2 keys, “1” and “2”, but you only need to modify 2 lines of code to make it work for whichever 2 keys you want it to toggle spamming on and off.
To have the script use keys on the number pad, you would change “1” and “2” to “numpad1” and “numpad2” or whichever 2 keys you wanted to use.

Edited script June 23rd: fixed an issue with Numpad keys not being sent correctly by Sendinput

;   Title: WoWscripts - Spamming macro hotkeys that Toggle themselves ON and OFF
;  author: David J Rosenheim
;    date: July 16, 2016
; purpose: Reposition and resize World of Warcraft window as soon as it's opened
;           then setup multiple hotkeys capable of toggling themselves on and off.

/* ChangeLog
	
	0.9.1.0 Beta   Date: 06/22/20  11:36 PM
	- rewrote script to simplify defining of hotkeys and to make logic a bit more straight forward
	- added logic to detect when special keys are used as hotkeys (i.e. Numpad0 or F1) and to then enclose 
		hotkey name in curly braces "{}" so that SendInput command will send the correct key.
	
	0.9.0.2 Beta   Date: 06/03/20  5:51 PM
	- Fixed issue with hotkey continuing to fire repeatedly while Active window is NOT same window loop started in
	- added a line that can be uncommented to allow testing hotkeys using windows program Notepad.exe
	- added hotkey Ctrl+Alt+R to allow user to quickly Reload this script (needed when chgs made while running)
	- added hotkey Ctrl+Win+X to allow user to Immediately terminate this script and unload it from memory

	0.9.0.1 Beta   Date: 05/15/20  7:09 PM
	- removed WinMaximize and chgd back to WinMove as maximized WoW windows were causing issues when alt-tabbing to other windows
	- script defines 2 winProcesses - 1st for World of Warcraft Retail, 2nd for World of Warcraft Classic
	- created _winGroup and modified script so that hot keys are context-sensitive to only windows in the Group
	
	0.8.0.1  Beta	Date: 9/03/19  1:09:10 PM
	- Changed WinMove to WinMaximize as game window extends beyond left/right side of screen when full height
	
	0.8.0.0  Beta	Date: 9/09/18  3:36:41 PM	
	- Added logic that detects if the current Hotkey is the same as previous hotkey
	- Changed new behaviour that causing currently running spamming loop to switch to a new hotkey character without toggling OFF first.
	- Added _swapHotKeys boolen variable to allow user to turn new "swapping hotkey character" behaviour ON or OFF.
	
	0.7.1.0  Beta	Date: 9/09/18  12:54:14 PM	
	- small number of updates to some comments to make logic more clear
	
	0.7.0.0  Beta	Date: 8/11/16  11:26:26 PM
	- Moved main code to SpamHotKey label so that multiple hotkeys can be processed using the same code
	- changed method of defining hotkeys from double-colon to Hotkey command
	- changed method of specifing context-sensitivity for hotkeys from "#If WinActive(" to "Hotkey, IfWinActive"
	- #MaxThreadsPerHotkey is now 2 instead of 1 and is only defined under the SpamHotKey label
	- Now parsing A_ThisHotkey to get the keystroke for the SendInput command.  Parsing removes "$", if first character.
		Since a hotkey's modifiers (ctrl,alt,shift) are included in its name, parsing A_ThisHotkey ensures that the
		modifiers will be included as part of the keystroke sent by the SendInput command.
	- Suspend hotkey is now working correctly
	- all hotkeys (including Suspend hotkey) are correctly context-sensitive and only working in the _winProcess window. 
*/ 

#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%  ; Ensure a consistent working directory by making script unconditionally use its own folder

_keepFiring := False ; whether to keep spamming hotkey or not
_prevHK     := ""	 ; Previous hotkey (needs to be global so 2nd thread sees what hotkey 1st thread is spamming)
_swapHKs    := True  ; true  = Press same hotkey to toggle OFF; otherwise, switch to new hotkey and keep spamming
					 ; false = Pressing any hotkey toggles OFF current spamming hotkey

_notepad := "ahk_exe notepad.exe"
;GroupAdd, _winGrp, % _notepad             ; uncomment for testing and debugging using Notepad
GroupAdd, _winGrp, ahk_exe Wow.exe        ; add this process to Group
GroupAdd, _winGrp, ahk_exe WowClassic.exe ; add this process to Group

; Only continue after any window in Group becomes Active
WinWaitActive, ahk_group _winGrp
; move and resize the active window
if WinActive(_notepad)
	WinMove, ,, 150, 150, 1000,  650 ; chg x,y,width,height to match preference for Notepad ;
else
	WinMove, ,,  -7,  -9, 1933, 1049 ; chg x,y,width,height to match preference for WoW game client 

#MaxThreadsPerHotkey 2     ; 2 threads are needed so that hotkey loop can be interrupted 
#MaxThreadsBuffer On       ; while loop running, allows 2nd hotkey to be buffered instead of ignored
#IfWinActive, ahk_group _winGrp   ; make following hotkeys context-sensitive (i.e. only work in Group windows)
^!s::Suspend   ; Ctrl+Alt+S - Toggles ON/OFF the Suspend function for all hotkeys
^#x::ExitApp   ; Ctrl+Win+X - Immediately terminate this script and unload it from memory
$1::
$2::
$Numpad5::
Gosub, SpamHotKey  ; rapidfire hotkeys  ("$" in front prevents SendInput cmd from triggering hotkey again)

return

SpamHotKey: 
_thisHK := A_ThisHotkey

If  SubStr(_thisHK,1,1) = "$" 
	_thisHK := SubStr(_thisHK,2)   ; trim "$" char from beginning of hotkey name
if  StrLen(_thisHK) > 1            ; If length of hotkey name is greater than 1, then
	_thisHK := "{" . _thisHK . "}" ;  hotkey is special keyname and must be enclosed in curly braces

; If this is 1st thread, start loop that sends hotkey until toggled OFF
If NOT _keepFiring {  
	_keepFiring := true
	_prevHK := _thisHK
	_thisID := WinActive("A")  ; get Active Window's unique ID nbr (HWND)
	Loop {   ; this loop keeps running until 2nd thread signals to stop spamming any hotkey
		if  NOT WinActive("ahk_id" . _thisID)  ; if NOT same window as when this loop started, then...
			WinWaitActive, ahk_id _thisID      ; pause loop here and wait for original window to become Active again
		sendinput, %_thisHK%    ; send the hotkey pressed
		sleep 59    ; speed of clicks... smaller=faster, larger=slower
		if not _keepFiring   ; If 2nd thread signaled for this loop to stop spamming hotkey, then...
			break   ; Break out of this loop
	}
} 
Else { ; ... this is 2nd thread and 1st thread is already running its loop
	If  (_thisHK = _prevHK or _swapHKs = False) ; if hotkey same as previous hotkey OR no swapping hotkeys, then...
		_keepFiring := false  ; Signal 1st thread to stop looping
	else  ; otherwise, keep loop running and now spam new hotkey
		_prevHK := _thisHK
}

return  ; Ends thread. If this is 2nd thread, 1st thread will resume looping and see any changes made
1 Like