Autohotkey Script Examples



EDIT: there are 3 timestamp lines. Modified the explanation accordingly.

I thought I’d post a few Autohotkey scripts on this thread to see if there’s any interest in perhaps posting more of them. LMK if you think this is useful or if there are specific things you want to see.

This first one is very simple and straightforward. It’s based on Brett Terpstra’s “What Was I Doing?” workflow. It:

  • responds to a keybinding
  • opens a small text box
  • Lets you type a status update
  • Appends that update to a text file with today’s date
  • Note that the text file “lived” in the same folder I pointed ResophNotes at. In Mac speak that’s “… same folder I pointed NVAlt at”.

A few more notes:

  • I called this script using the keybinding ctrl-alt-windows-space. The keybinding appears in a separate thread of Autohotkey, and this script file was called once the keybinding was invoked.
  • Line 5 … generates the input box with window title “Task Capture”. The two numbers give the size of the box (width and height).
  • Lines 6 - 8 … generates the timestamp values.
  • Line 9 … the heart of the script. FileAppend does exactly what its title says: it takes the text in the second argument %wdidt_date% %wdidt_time% %text% 'n and appends it to the file in the last argument. If the file exists, it uses that file. If the file does not exist, it creates a new one. So every month a new file is added.
    Line 11 … gives a little bubble at the system tray to let me know the operation executed properly
; WDIDT Entry Box

#SingleInstance force

  inputbox, text, Task Capture,,,350,100
  FormatTime, wdidt_ynm,, yyyy-MM-dd
  FormatTime, wdidt_date,, yyyy-MM-dd
  FormatTime, wdidt_time,, HH:mm
  fileappend, %wdidt_date% %wdidt_time% %text% `n, C:\{PATH TO RESOPHNOTES FOLDER} %wdidt_ynm% WDIDT.txt
  traytip,,Task Added,4,1


Yes. Please continue to share! :slight_smile:


This next one is an example of a script that moves and resizes a particular window. This one took the main Outlook window, moved it to Monitor 2 (Widows designates monitor numbers rather than Mac’s numbering from left to right), and resized it to fit. As with many of my scripts, this one was tied to a keybinding. I had a foreground thread of Autohotkey with the keybinding that called this script, and this script ran and then quit.

To break down the script:

  • Line 3 … #singleInstance force checks local memory for a process with the same name and kills it if one exists.
  • Lines 5-17 … clears variables. Variables don’t need to be separately defined.
  • Lines 22 … the SysGet command gets info from the system. In this case, it asks how many monitors are connected and stores the resulting integer in the variable num_monitors.
  • Line 23 … asks the system what are the dimensions of Monitor 2. It returns 4 values: LEFT, RIGHT, TOP, BOTTOM. Those values are stored in variables derived from the original variable as Mon2_Bound_right and so on. Note that SysGet counts in pixels from far left to far right. So if you have 2 monitors that are 1024 x 768, the right hand monitor’s left edge is pixel 1025. It also counts from top to bottom in pixels.
  • Lines 28-32 … activates and brings the Outlook window to the front. The SetTitleMatchMode = 2 commands relaxes the matching to “window title contains”. IfWinExist asks the system whether there’s a window who’s title contains “Outlook”. The first WinActivate will activate any existing window with “Outlook” in the title (the one closest to the top of the alt-tab stack if there are multiple windows). The second one will launch Outlook if no existing window is found (at least I think that’s how its supposed to work).
  • Line 38 … re-invoking SetTitleMatchMode is almost definitely not needed, but it always made me feel better. :slight_smile:
  • Line 39 … makes sure there are two monitors before attempting to move the window to monitor 2. On Windows, if you move an application window by one screen off the left or right side, it will stay there. At least it did when I last checked a year or so ago.
  • Line 40-41 … figure out the dimensions of monitor 2 and resize the Outlook window accordingly. This meant that I could have different monitors on my desk at work and home and use the same script for both.
  • Line 42 … calculate the x coordinate for the top left corner of the Outlook Window. Start at the left edge of the second monitor, add half of the horizontal available space of the monitor, then subtract half of the width of the Outlook window.
  • Line 43 … same, but for y coordinate.
  • Line 44 … Moves the window using the calculations from above. There’s more info in the documentation for WinMove, but the second argument is the title of the window to move (remember that SetTitleMatchMode from line 38 allows for fuzzy match). The empty argument is there for any words to exclude from the title match, so you could use “windows containing Outlook but without calendar”. Helpful if you want like to have multiple windows of the same app open.
  • Line 47 … ctrl-1 is the Outlook keyboard shortcut to switch to the Inbox.
  • Line 51 … ExitApp terminates the process.

A few other notes:

  • I had a series of these for most major apps: Outlook, OneNote, Notepad++, Chrome, Skype, etc. Each script was its own text file.
  • Just like with, these can be combined. For example, I had one script that moved Outlook to the main monitor, moved Chrome to the second monitor, switched Chrome to my Todoist tab, and then re-activated Outlook to make it the foreground program. With one keybinding I could set up my workspace to process emails. That was done by calling 4 scripts in series.
  • I had other combinations that set up my workspace for processing tasks, for working on tasks, doing PPT with Excel, etc.
  • I also had one that would return Skype to the top left corner of the right-hand monitor, which is where I prefer it to be.
; Move Outlook Window Monitor 2

#SingleInstance force

;  Clear Variables
num_monitors = 
Mon2_Bound_ = 
Mon2_Bound_left =
Mon2_Bound_right =
Mon2_Bound_top =
Mon2_Bound_bottom =
outlook_dual_x = 
outlook_dual_y = 
outlook_width = 
outlook_height = 

;  Get details on monitors
sysget, num_monitors, MonitorCount
SysGet, Mon2_Bound_, Monitor, 2

;  Activate Outlook Window
SetTitleMatchMode, 2
IfWinExist, Outlook
	WinActivate ;
	WinActivate ;

;  Move Outlook Window
settitlematchmode, 2
if (num_monitors = 2) {
    outlook_width := (Mon2_bound_right - mon2_bound_left) * 0.85
    outlook_height := mon2_bound_bottom * 0.92
    outlook_dual_x := mon2_bound_left + (Mon2_bound_right - mon2_bound_left)/2 - outlook_width/2
    outlook_dual_y := Mon2_bound_bottom/2 - outlook_height/2 
    winmove, Outlook, , outlook_dual_x, outlook_dual_y, outlook_width, outlook_height

sendinput ^1



I can’t wait to see what I can do with these when I have some time this weekend. Thanks for sharing these!!


Glad you like them. If there’s anything specific you want, happy to try to write it out for you. Ironically, I’m now all Mac all day, but hopefully I can remember enough to be helpful.


Back in my Windows days, I used Todoist as my task manager. Todoist has many great qualities, but when I was brainstorming and project planning, I wanted the flexibility that a text editor could give me. I made this script to transform a taskpaper-type syntax into a file that Todoist could use (it used to be that you could import from a text file using a specific syntax. I don’t know if that’s possible anymore.)

Breaking down the script:

  • Lines 3-5 … I’ve covered #singleinstance force and SetTitleMatchMode before. I use them in nearly every script.
  • Line 7 … WinActivate activates the window with “Notepad++” somewhere in the title.
  • Line 8 … pauses the script until Notepad++ is the foreground window. Since I’m sending generic copy/paste commands, I wanted to make sure I’m doing those on the right window.
  • Lines 10-12 … select and copy all.
  • Line 14 … take the contents of the clipboard and assign it to the variable SL_Input.
  • Line 15 … create a variable with the current timestamp, down to the second.
  • Line 17 … In taskpaper, a project is defined by a colon at the end of the line. I use this variable to test whether the line is a project or not.
  • Lines 19-26 … The heart of the script depends on a series of text replacements. Here I define the text elements the script looks for.
  • Lines 28-34 … these are the text elements that get substituted for their related ones above. They’re built on the syntax that Todoist used to use for template import.
  • Line 37 … replaces all carriage returns with that star-like character. The text parser in line 48 depends on a single character, and it could not recognize a line break. So I replace all line breaks (rn) with this character.
  • Line 38-45 … replace the text elements, changing the bulleted list into the ellipsis-delineated list. They’re done in reverse order because of the way the text replacement function works: a bullet with 8 spaces could be recognized as a bullet with 4 spaces and 4 empty spaces in front of it. I never figured out how to make the tolerance tighter without totally ruining the script.
  • Line 48 … this loop is how I write the new file in the proper format. the parse command tells the loop to cycle through the text in sl_input, breaking at every instance of the star character.
  • Line 50 … the StringRight command takes the first character from the right hand side of the string and puts it in the variable Project_Test. This tests whether the line defines a project. Note that if there’s a space at the end of the line, it will take that and miss a project definition.
  • Line 53 … if the line defines a project, it gets appended to the file with Markdown bold syntax. Its my preference to have project titles bolded, and Todoist parses some Markdown, so I used it. I explained the FileAppend command in the script above
  • Line 57 … if the line is not a project, it gets appended to the file as is.
  • Line 59 … adds a return between lines.

A few other notes:

  • I would sometimes save multiple files during the course of a day and then import them all at once. By having the filename be the timestamp down to the second, I could be confident that I would overwrite one file with another. I could have done it down to the minute, but then what fun would that be? :slight_smile:
  • I have another similar script that lets me clean up Kindle notes. Use case: go to the Kindle notes page on the web, copy and paste notes into a text editor, run the script to remove all the excess formatting .
  • I have another one that cleans up an email list for meeting notes. Use case was to hit reply-all to a meeting invite and copy the single line of multiple email addresses. The script would then turn that into a bulleted list with one name per line and put it on the clipboard. I could then paste a nicely formatted list of everyone on the invite into my notes. Yes, OneNote and the clipper will do that also, but where’s the fun in that? :slight_smile:
  • I had a pair that would remove star bullets and replace them with dashes. You know, because I’m civilized.
  • I had another pair that would remove bullets and replace bullets. Used this a lot less than I thought I would.
; Turn bulleted list into todoist

#SingleInstance force

settitlematchmode, 2

Winactivate, Notepad++
winwaitactive, Notepad++

Send ^a
sleep 10
Send ^c

SL_input = %clipboard%
FormatTime, TimeString,,yyyy-MM-dd_HH-mm-ss

Project_Char := ":"

SL_LCom1 := "//"
SL_LCom2 :="--"
SL_L0 := ""
SL_L1 := "- "
SL_L2 := "    - "
SL_L3 := "        - "
SL_L4 := "            - "
SL_L5 := "                - "

SL_RCom :="[[NOTE]]: "
SL_R0 := 
SL_R1 := "..."
SL_R2 := "......"
SL_R3 := "........."
SL_R4 := "............"
SL_R5 := "..............."

StringReplace, sl_input, sl_input, `r`n , ¤, All
StringReplace, SL_input, SL_input, %SL_LCom1%, %SL_RCom%, All
StringReplace, SL_input, SL_input, %SL_LCom2%, %SL_RCom%, All
StringReplace, SL_input, SL_input, %SL_L5%, %SL_R5%, All
StringReplace, SL_input, SL_input, %SL_L4%, %SL_R4%, All
StringReplace, SL_input, SL_input, %SL_L3%, %SL_R3%, All
StringReplace, SL_input, SL_input, %SL_L2%, %SL_R2%, All
StringReplace, SL_input, SL_input, %SL_L1%, %SL_R1%, All
StringReplace, SL_input, SL_input, %SL_L0%, %SL_R0%, All

Loop, parse, SL_input, ¤, 
    StringRight, Project_Test, A_Loopfield, 1
    If Project_Test = %Project_Char%
        FileAppend, **%A_Loopfield%**, C:\[PATH]\%TimeString%.txt
        FileAppend, %A_Loopfield%, C:\[PATH]\%TimeString%.txt
    FileAppend, `r, C:\[PATH]\%TimeString%.txt



This next one is fun because it was inspired by @David’s “Thank You / You’re Welcome” TextExpander group. I wrote this before TextExpander on Windows was a thing. It would have been a lot easier to write the script as a series of simple text replacements, but I used it as an excuse to learn about arrays and loops, because, you know, geek. :slight_smile:

To break down the script …

  • Lines 5-8 … zero-ing out a few variables
  • Line 10 … instantiates the array named TYYWArray
  • Lines 11-23 … adds the array elements. I defined this one as a 2D array with 4 rows for each column in the table. I can’t remember why I put the single letters in the first column, but it looks like I never use them in the rest of the script. After that is the name of the language, the text of “thank you”, and the text of “you’re welcome”.
  • Line 25 … defines a string that gets used in the pop-up selector box in line 27. Looking at this again, it would have been more elegant to do a Loop function to define the names.
  • Lines 27-29 … Defines the GUI pop-up box. Line 27 defines the overall box and assigns the output to the variable lang_choice. Note that the v is there to define it as a variable. Line 28 defines the buttons being pressed. Line 29 gives the window title.
  • Line 30 … Return is much like an output command. If you give no argument for Return, it simply terminates the script. To be honest, I’m not entirely sure why it is here, but I know the script doesn’t work without it.
  • Lines 32-35 … tells the script what ro do once the pop-up box is closed
    Line 37 … StringSplit takes 3 arguments: the variable into which the split content goes, the variable that has the content to be split, and the delimiter used to make the splits. %A_Space% is a built-in Autohotkey variable that is a space, so in this example the script takes the contents of the variable lang_choice from the picker box in line 27, splits it at every space, and puts the output into the variable tyywss. Autohotkey will add ordinal numbers to the end of tyywss for each of the split sub-strings (tyywss1 gets everything up to the first space, tyywss2 gets everything from the first to the second spaces, etc).
    Lines 39-44 … tests whether I wanted “Thank You” or “You’re welcome”. tyywss1 will always have 2 letters, either “TY” or “YW”. The nested if statements assign the column number for the array in the variable k. The last if statement is there as a check in case there was an error.
  • Lines 46-52 … goes into the array and pulls out the appropriate phrase. It cycles through the array and tries to match the language selected (which is in tyywss2) to the language in the array col 2. Once it matches, it pulls out the phrase wanted. Note the convention % variable is the same as %variable, namely that it gives the content of the variable instead of the variable as a thing to manipulate.
    Line 54 … SendInput gives the output by typing.

A few other notes:

  • This method works well for anything that you could represent as a multi-column table from which you want to look up values. Its particularly nice because you only have to define the output items in one place, and the rest of the script is pretty much reusable.
  • Some examples of other scripts I have include an emoji picker, a symbol picker, contexts for task mangement systems, email addresses, often used mailing addresses, an even for my phone numbers.
  • I also have one that gives me multiple options for how to format the current date.

; Thank You / You're Welcome Picker

#SingleInstance force

  j = 0
  k = 0
  lang_choice = 
  lang_options = 
  TYYWArray := Object()
  TYYWArray.Insert(["c","Chinese","Xiexie","Bu Keqi"])
  TYYWArray.Insert(["a","Farsi","Mamnunam","Khahesh mikonam"])
  TYYWArray.Insert(["f","French","Merci beaucoup","De rien"])
  TYYWArray.Insert(["g","German","ich danke Ihnen sehr","Bitte sehr"])
  TYYWArray.Insert(["h","Hindi","dhanyavad","koi bat nahin"])
  TYYWArray.Insert(["i","Italian","grazie mille","prego"])
  TYYWArray.Insert(["j","Japanese","domo arigato gozaimasu","do itashimashitev"])
  TYYWArray.Insert(["p","Portuguese","muito obrigado","Nao ha de que"])
  TYYWArray.Insert(["r","Russian","Spasibo bolshoe","Dobro pozhalovat"])
  TYYWArray.Insert(["s","Spanish","muchas gracias","de nada"])
  TYYWArray.Insert(["t","Turkish","Tesekkur ederim","Bir sey degil"])
  TYYWArray.Insert(["y","Yiddish","A sheynem dank","Nishto farvos"])
  Lang_options = TY Chinese|TY Farsi|TY French|TY German|TY Hindi|TY Italian|TY Japanese|TY Korean|TY Portuguese|TY Russian|TY Spanish|TY Turkish|TY Yiddish|YW Chinese|YW Farsi|YW French|YW German|YW Hindi|YW Italian|YW Japanese|YW Korean|YW Portuguese|YW Russian|YW Spanish|YW Turkish|YW Yiddish|
  Gui, Add, ListBox, vlang_choice h100, %lang_options%
  Gui, Add, Button, hidden default, OK ; Add an Ok Button
  Gui, Show, Center, Thank You / You're Welcome Picker ; The window's title
  Gui, Submit ; Save the input from the user selection
  Gui, destroy
  StringSplit, tyywss, lang_choice, %A_Space%
  if tyywss1 = TY
    k = 3
  else if tyywss1 = YW
    k = 4
    k = 2
  for j in TYYWArray
      if tyywss2 = % TYYWArray[j,2]
          output_greeting = % TYYWArray[j,k]
  Sendinput %output_greeting%    


Another quick one to post. This one gives a bunch of different formatting options for the current date. It, too, uses a pop-up box to select the date format.

To break down the script …

  • Lines 5-10 … defines the different formats for the current date and time. The formats follow the typical UNIX conventions. LongDate is Friday, August 3, 2018.
  • Line 12 … defines a variable that is used to populate the choices in the pop-up box. Note that I wrapped each variable in % so the picker box displays the actual date and time rather than the names of the variables.
  • Lines 14-22 … defines the picker box and what happens when the buttons are pressed. Same code block as in the previous script.
  • Line 24 … sends the selected format by typing.

A few other notes

  • This is a handy method if you want to make a selection picker box without too much work.
  • You could put a list of links to launch, or apps to launch (setting the target to the full path of the app and using the run command, or even iTunes Playlists (copy the link to the playlist from the ellipsis menu, set that as the target of the choice, and use the run command. The first time you do it your default browser will ask whether you want to run iTunes. If you say “yes” it will open up the playlist in iTunes from then on).

; Time/Date Stamp Picker

#SingleInstance force

  FormatTime, bdatetime,,yyyy-MM-dd
  ForMatTime, ddatetime,,LongDate
  FormatTime, fdatetime,,yyyy-MM-dd_HH-mm-ss
  FormatTime, ttimetime,,HH:mm, dddd MMMM d, yyyy
  FormatTime, gdatetime,,yyyy-MM-dd HH:mm
  FormatTime, ctimetime,, HH:mm 
  Time_Options = %ddatetime%|%ttimetime%|%fdatetime%|%gdatetime%|%bdatetime%|%ctimetime%
  Gui, Add, ListBox, vtime_choice h100 w200, %time_options%
  Gui, Add, Button, hidden default, OK ; Add an Ok Button
  Gui, Show, Center, Time and Date Picker ; The window's title
  Gui, Submit ; Save the input from the user selection
  Gui, destroy
  SendInput %time_choice%


EDIT: Changed Chrome to Todoist in the description of line 6. Sorry for the mix-up.

Another one from my Todoist days. I never really got into the Todoist desktop client, preferring to use it in a web browser instead. I tried keeping multiple Todoist tabs open, but it never quite worked well, so instead I devised a series of scripts that helped me switch between Todoist views with a keybinding. This one switches to a project view, but you can substitute the name of a tag, a saved search, or the inbox in line 20.

To break down the script:

  • Lines 1-3 … I’ve covered #SingleInstanceForce and SetTitleMatchMode before.
  • Line 4 … WinActivate brings the topmost (in the alt-tab stack) Chrome tab to the front.
  • Line 6 … Loop tells the script to run through this loop and stop after 15 iterations. I’m not usually one to have more than 3-4 tabs open at any one time, so 15 is more than enough for me. If there isn’t a tab with Todoist in the title, this limit prevents the script from going on forever.
  • Line 8-12 … gets the title of the Active window (the final argument A is the designator for the active window). The InStr command looks for any string that has the sub-string Todoist, and if it finds it, the loop terminates.
  • Line 13 … if the tab title does not contain Todoist then switch to the next tab and try again.
  • Line 14 … sleep pauses the script for the given number of miliseconds. This was just enough to give the browser enough time to switch the tab and register the name before testing again.
  • Lines 17-24 … once the Todoist tab is found, it uses the keyboard shortcut / to jump to the search box, type the name of the project, select it, and press return. The Send command gives keystrokes by typing rather than pasting.

A few other notes:

  • At least when I was writing these scripts regularly, Autohotkey demanded the braces be as they are here rather than OTB format. Its been a few years and they might have changed the requirement.
  • I had a series of these for my most used perspectives (aka saved searches), labels, etc.
  • You can use this for pretty much any web-based tool, so long as there is some sort of keyboard-y way to get to things.
  • I can’t remember whether Autohotkey has a way to click at an image or click at a specific screen location, but that would be a way around not having a keyboard shortcut to use.

#SingleInstance force

SetTitleMatchMode, 2
WinActivate, - Google Chrome

Loop, 15
   WinGetTitle, Title, A  ;get active window title
   if(InStr(Title, "Todoist")>0)
      break ; Terminate the loop
   Send ^{Tab}
   Sleep, 50

Sleep, 50
Send {/}
Sleep, 20
Sleep, 20
Send {down}
Sleep, 20
Send {return}



A couple of odds and ends that don’t warrant individual posts.

Just a word on the notation. Autohotkey uses the following notations for modifier keys:

^ = control
! = alt
+ = shift
# = windows key
xxx :: xxx = the double colon signifies remapping the thing before to the thing after

I had a bunch of keybindings that launched specific programs. While I could do that with Launchy (the closest thing Windows has to Quicksilver/Alfred/Launchbar/etc), I found the keybindings a bit faster. I had these for all the Office programs, Windows Explorer, specific folders, websites. I even had one that opened a specific file that I used multiple times a day.


Autohotkey lets you set the transparency of a window. I found I used this most when i was on the laptop screen only and wanted to refer to an excel while I was making a powerpoint. Using the pair of commands below lets you hold down control shift windows space to make the foreground window transparent and then reset to full opaque when you release the keys. The second to last argument is the % transparency (100 is fully transparent). The last argument A designates the topmost active window.

^+#Space::WinSet, Transparent, 50, A
^+#Space UP::WinSet, Transparent, OFF, A

I spent a lot of time in Excel (still do, for that matter). In Windows Excel, Control Page Up/Down lets you switch between tabs. I used these keybindings to remap those to Windows F1/F2 which was much easier to press with my left hand (rather than needing both hands).


I remapped the volume key commands as below. I can’t remember whether the number is a % or something else, but 2 was an OK increment for me.

+F12::Send {Volume_Up 2}
+F11::Send {Volume_Down 2}
+F10::send {Volume_mute}

In PPT, alt v d will toggle between slide sorter mode (showing all slides in the main window) and slide editing mode (showing one slide the the main window and a column of slides to the left). I sped that up by remapping it. Note that Autohotkey behaves a little weirdly here. I could have enclosed the two Send commands in braces, but I didn’t need to. The empty Return statement is sufficient to terminate this portion of the script. If you were to use ExitApp here, the entire process would terminate and unload from memory.

send !v
send d

Make a new folder in Windows Explorer. Note that Windows Explorer needs to already be the topmost active window. AppsKey simulates a right click (some keyboards have a physical key on the lower right side).

send {AppsKey}
send w
send {enter}

Finally, a series of date snippets. They use what i think is the UNIX standard date formatting conventions. Each has the format in a comment on the right side of the first line. The ones that start with a number as either in the future or the last, the math for which you can see in the third line of each script. As before, the empty Return function is enough to terminate the code without exiting the entire process out of memory.

::ttime:: ; 12:25, Friday November 11, 2016
FormatTime, TimeString,,HH:mm, dddd MMMM d, yyyy
send %timestring%

::bdate:: ; 2016-11-11
FormatTime, TimeString,,yyyy-MM-dd
send %timestring%

Tomorrowdate = 
Tomorrowdate += 1, Days
FormatTime, Tomorrowdate, % Tomorrowdate, yyyy-MM-dd
SendInput %TomorrowDate% 

Tomorrowdate = 
Tomorrowdate += -1, Days
FormatTime, Tomorrowdate, % Tomorrowdate, yyyy-MM-dd
SendInput %TomorrowDate% 

::ddate:: ; Friday, November 11, 2016
ForMatTime, TimeString,,LongDate
send %TimeString%


In Windows Outlook, you’re able to define macros called “Quick Steps”. These let you chain together commands like “Mark as read and file in a folder”, “mark as read and create a task”, “set a follow up flag and set a category”, etc. The macros could get quite complicated, but the nice thing was that you could assign keyboard shortcuts to them and trust that they would fire off the same way every time.

For quite some time I’ve used a variation of Gina Trapani’s Trusted Trio folder for email processing. I have folders for Archive, @Action, @Waiting, and @Approvals. For each of these folders, I defined a Quick Step in Outlook that would mark the email as read and move it to the specified folder. Archive was mapped to control shift 9 and Approvals [1] was mapped to control shift 6. For @Action (control shift 8) and @Waiting (control shift 7) emails, I would flag the email for follow up, set a category, and file it away. This would put the email on the Outlook task list with a context. Later, when I switched to using Todoist, I would write the follow up task to a text file in the proper import format (see post above). I’d usually type those manually since I wanted to phrase the task in a certain way.

So how does this relate to Autohotkey? I used AHK to remap the extended Function Keys on my desktop keyboard [2] to the Quick Steps in Outlook. So I could be in Outlook, rest 4 fingers of my right hand on F16-F19 above the keypad, and process email really quickly. I usually rested my right thumb on the arrow keys, which let me navigate through my inbox even faster. And I could move my thumb up a little and reach the Delete key. It made email processing much faster.

Now that I’m Mac at work, I’ve used Keyboard Maestro to recreate this functionality to let me use the same keybindings on Mac Outlook [3].

The Autohotkey code is very straightforward. It assumes that Outlook is the foreground app when the keys are pressed, which I don’t think should be much of an issue for most people since the F16-F19 keys are “all the way over there.” :slight_smile:


[1]: Approvals was a folder I used to collect all of those “click here to approve” emails (purchase orders, time off requests, etc). I’d batch them up and go through them every day or two.

[2]: I love love love the Apple Wired Extended Keyboard A1243. Do to The Unfortunate Soda Mishap of 2016, I lost my work one. If anyone has a working one they’re willing to part with, please get in touch!

[3]: Yes, Mac Outlook. No, not by choice.


A couple of scripts for window management. There are comments above each one to describe what each one does. To summarize the keybindings I used:

Shift Windows Right Arrow ... move one screen to the right
Shift Windows Left Arrow ... move one screen to the left
Shift Windows Up Arrow ... maximize the window
Shift Windows Down Arrow ... minimize the window
Shift Windows forward slash ... restore window to last non-maximized state
Control H ... minimize window (same as + # ↓, but to match the Mac convention)
Control Shift H ... hide all windows except active one (again, to match the Mac convention)

A few notes on the scripts:

  • I borrowed these scripts from the AUtohotkey forums and only slightly modified them for my own use. I can’t remember where I got them or I’d give attribution.
  • Each of the scripts have a WinGet statement. It finds the minimized/maximized state of the active window. Later in the script is a statement if (mm = 1) which restores the maximized state of the window after the move.
  • some of the scripts end with send {ctrl up} {shift up} {win up}. Those are to make sure that the system doesn’t leave the keys pressed.
; ===== Move window one screen right
WinGet, mm, MinMax, A
WinRestore, A
WinGetPos, X, Y,,,A
WinMove, A,, X+A_ScreenWidth, Y
if(mm = 1){
    WinMaximize, A
send {ctrl up}{shift up}{win up}

; ===== Move window one screen left
WinGet, mm, MinMax, A
WinRestore, A
WinGetPos, X, Y,,,A
WinMove, A,, X-A_ScreenWidth, Y
if(mm = 1){
    WinMaximize, A
send {ctrl up}{shift up}{win up}

; ===== Maximize window
winrestore, a
winmaximize, a

; ===== Minimize window
WinGetActiveTitle, Title
WinMinimize, %Title%

; ===== Restore Window
WinGetActiveTitle, Title
WinRestore, %Title%

; ===== Hide active window
WinGetActiveTitle, Title
WinMinimize, %Title%

; ===== Hide all other windows
WinGetActiveTitle, Title
WinRestore, %Title%


The last scripts I have to share are a group that reassigned iTunes functions to the F keys on my desktop keyboard. In addition to rewind, play/pause, and forward, I added “activate iTunes” to F6.

A few notes

  • the DetectHiddenWindows statement lets the script look at windows that are minimized to the system tray (which don’t always appear in the Alt-Tab list).
  • I used window classes instead of window titles here so I could send the controls to iTunes without having to bring the window to the front first. That way I could be working on something and skip to the next song without the screen flickering. The WinGetClass statement gets the classID of the iTunes window. ControlSend sends the command to the window with the iTunes classID.
  • I added Shift-F6 to activate iTunes. If its running, it comes to the front. If its not running, it launches.
settitlematchmode 2
DetectHiddenWindows , On
IfWinExist, iTunes
	WinActivate ;
	run C:\Program Files\iTunes\iTunes.exe
DetectHiddenWindows , Off

settitlematchmode 2
DetectHiddenWindows , On
wingetclass, itunes_class, iTunes
ControlSend , ahk_parent, {left}, iTunes ahk_class iTunes
DetectHiddenWindows , Off

settitlematchmode 2
DetectHiddenWindows , On
wingetclass, itunes_class, iTunes
ControlSend , ahk_parent, {space}, iTunes ahk_class iTunes
DetectHiddenWindows , Off

settitlematchmode 2
DetectHiddenWindows , On
wingetclass, itunes_class, iTunes
ControlSend , ahk_parent, {right}, iTunes ahk_class iTunes
DetectHiddenWindows , Off


I am a fellow AHKer. I would love to chime in here with scripts, advice and answers. I’m currently mobile and not able to on my phone but I will when I get home.

My current setup is Windows 10 with 4 keyboards (primary full, two more full and one Belkin game pad). I have most of the keys bound to do some sort of automation for me. I would love to share more on this of others are interested.

Excited to listen more of the show! Great work you two!


I think for my first example, I’m going to post my server automation script explanation. I can get into code if anyone finds it deeply interesting. So, here’s what I have going.

At my home office, I have a Windows Server running (well, it’s Windows 10 posing as a server). This server does a lot for me. It serves as my Minecraft server host machine, it runs plex, it has a little application that takes images off my canon camera and saves them to my Google Drive uploader and a small web server VM running Windows Server 2012 that hosts a little website I made.

I needed a way to start/stop these various applications when I’m not on my own network. Enter Workflows on my iOS device, Dropbox, and AHK.

Here’s the breakdown:

  • Deletes the “communication file” from my Dropbox folder
  • Presents me with a list of commands I’d like to do
    • Start/Stop the minecraft server
    • Start/Stop the Canon utility
    • Start/Stop the web server
    • Start/Stop TeamViewer
    • Start/Stop Plex
  • From the selection it writes the associated command to a new text file in Dropbox
  • The script on the server picks up that file and reads the desired command and carries it out

It’s pretty simple, but I love it and it has saved me loads of headaches! I’m particularly fond of “text file communication”. So, it’s what I lean towards when I want to transfer automation between devices.

I’ll post more on my multi-keyboard setup soon!



Here a a few of my “staples” that are always with me (in AHK script code)

These are what AHK call “Hotstrings” (think of text expander)
The setup is – :*:when_i_type_this::ItIsReplacedWithThis
Example: :*:btw::By the way – So, typing btw would instantly be replaced with By the way

:*:@address::1234 8th street S
:*:qq::quick question for you

Some html/jquery maniuplation
This one is necessary because I never remember the proper syntax for a quick setTimeout function in javascript.

:*:jstimeout::setTimeout(function(){{} alert("Hello"); {}}, 3000);

Type in jstimeout and it spits out the below:

setTimeout(function(){ alert("Hello"); }, 3000);

Yup. I’m lazy.

:*?:br/::<br />

These two just type out different console.log lines (with and without quotes in it) and sends the cursor in between the parentheses.

:*:c.l1::console.log();{left 2}
:*:c.l2::console.log('');{left 3}

Similar to the above two, these also add whatever is in your clipboard at the time so you can inspect the variable in the console.

SendInput console.log('%Clipboard% : ' {+} %Clipboard%);

SendInput console.log('%Clipboard% : ' {+} Object.keys(%Clipboard%));

Just a few to test the waters and see if anyone likes these. Let me know if you would like to explain more and/or post more. I have plenty!

Thanks everyone!


For creating a new folder, you can just use the CTRL+SHIFT+N shortcut that works natively in Windows Explorer (Since Windows 7 I believe). If you wanted to keep your existing mapping you could use:



Open Windows PowerShell from Windows Explorer
This script creates a key binding (I use Windows + T) that opens Windows PowerShell in the directory you’re currently viewing in Windows Explorer by sending the key combination Alt + F to open the File menu and then sending R to open Windows PowerShell.

Send !f
Send r

If you prefer opening PowerShell as administrator then just send A instead of R:

Send !f
Send a

Edit1: Forgot to mention that I only tested this on Windows 10.
Edit2: Removed { and } as fischgeek suggested.


Why not just Run, powershell.exe

Edit1: Nope. Read that too fast. Current directory was your intention. my bad.
Edit2: the “{” and “}” aren’t needed in your example :wink:


Here’s one that creates a new text file if you’re in Windows Explorer and your cursor is positioned inside a directory. I found it on another forum:

Click, Right, 1
Sleep, 10
SendRaw, wt

It right clicks (that’s why your cursor should be positioned inside a directory in Windows Explorer) and then sends the keys w and t to the context menu to create a new text file.