v2 of Google "Did you mean..." tool? Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
kunkel321
Posts: 1062
Joined: 30 Nov 2015, 21:19

v2 of Google "Did you mean..." tool?

Post by kunkel321 » 06 Aug 2023, 10:46

I've been using this v1 function written by TheDewd viewtopic.php?f=6&t=83164#p364301. It is a tool to check spelling of a word (or, I guess, grammar of a phrase). A google search finds several different versions of this, but I can't find any that are written for AHK v2. I tried the v1 to v2 converter, but it doesn't work for this.

Do you guys know if this is already available online? Or... If anyone is up for the challenge...
ste(phen|ve) kunkel
User avatar
mikeyww
Posts: 26984
Joined: 09 Sep 2014, 18:38

Re: v2 of Google "Did you mean..." tool?  Topic is solved

Post by mikeyww » 06 Aug 2023, 12:08

Code: Select all

#Requires AutoHotkey v2.0

MsgBox GoogleAutoCorrect('Helllo Worlpd')

GoogleAutoCorrect(word) {
 objReq := ComObject('WinHttp.WinHttpRequest.5.1')
 objReq.Open('GET', 'https://www.google.com/search?q=' word)
 objReq.SetRequestHeader('User-Agent'
  , 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)')
 objReq.Send(), HTML := objReq.ResponseText
 RegExMatch(HTML, 'value="(.*?)"', &A)
 RegExMatch(HTML, ';spell=1.*?>(.*?)<\/a>', &B)
 Return B[1] || A[1]
}
User avatar
kunkel321
Posts: 1062
Joined: 30 Nov 2015, 21:19

Re: v2 of Google "Did you mean..." tool?

Post by kunkel321 » 06 Aug 2023, 12:15

Awesome! Thanks Mike! I'm going to use this in a project right now.
ste(phen|ve) kunkel
User avatar
kunkel321
Posts: 1062
Joined: 30 Nov 2015, 21:19

Re: v2 of Google "Did you mean..." tool?

Post by kunkel321 » 07 Aug 2023, 12:42

This v2 code mostly works, but I'm getting an odd error occasionally...
Error: This value of type "String" has no property named "__Item".

342: RegExMatch(HTML, 'value="(.*?)"', &A)
343: RegExMatch(HTML, ';spell=1.*?>(.*?)<\/a>', &B)
▶ 344: Return B[1] || A[1]
345: }
348: {

Call stack:
S:\AutoHotkey\MasterScript\@HotStrHelpML for v2\HH - Multi For v2.ahk (344) : [GoogleAutoCorrect] Return B[1] || A[1]
S:\AutoHotkey\MasterScript\@HotStrHelpML for v2\HH - Multi For v2.ahk (318) : [hhButtonSpell] googleSugg := GoogleAutoCorrect(tRepStr)
It's always the same string, "__Item". I don't know if it's a problem with the function, or a glitch with Google. The full code is below, but the relevant part is just lines 344 - 352. The function that calls the google function appears directly above it. My tool is a "hotstring helper" so the idea is to select some text then hit the hotkey (!#h). The google function works fine under that normal condition--however: Run the script, don't select any text, press hotkey. Then type a misspelled word into the 'replacment text' edit box. Then press the Spell button. (The dialogs might appear behind the main gui.) The error doesn't seem to appear every time either--not reliably reproducible....

Anyone have thoughts on a solution?

Code: Select all

#SingleInstance
#Requires AutoHotkey v2.0

;------------------------------------------------------------------------------
;   Hotstring Helper - Mulitline
;------------------------------------------------------------------------------
; By Kunkel321, with much help from forum members and others. Version 8-7-2023
; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=114688
; A version of Hotstring Helper that will support block multi-line replacements.
; Customization options are present throughout, and are flagged as such.
; Needs AHK v2. Partly auto-converted from v1, partly rewritten.
; Please get a copy of AutoHotkey.exe (v2) and rename it to match the name of this
; script file, so that the .exe and the .ahk have the same name, in the same folder.
; DO NOT COMPILE, or the Append command won't work.  Keep at bottom of AutoExecute
; section of your script. The Gui stays in RAM, but gets repopulated upon
; hotkey press. Remove these comments as desired.

;==Change=colors=as=desired========================
GuiColor := "F0F8FF" ; "F0F8FF" is light blue
FontColor := "003366" ; "003366" is dark blue
;==================================================

Global hFactor := 0 ; Don't change size here.  Change in TogSize() function, below.
Global wFactor := 0 ; Don't change here.  Change in TogSize() function.
FormName := "Hotstring Helper -- Muli-Line" ; Change here, if desired.

hh := Gui('', FormName)
hh.Opt("-MinimizeBox +alwaysOnTop")
hh.BackColor := GuiColor
hh.SetFont("s11 c" . FontColor)
; -----  Trigger string parts
hh.AddText('y4 w30', 'Options')
hh.AddText('vTrigStrLbl x+20 w250', 'Trigger String')
hh.AddEdit('vMyDefaultOpts yp+20 xm+10 w30 h24')
DefHotStr := hh.AddEdit('vDefHotStr x+28 w' . wFactor + 250, '')
; ----- Replacement string parts
hh.AddText('xm', 'Enter Replacement String')
hh.SetFont('s9')
hh.AddButton('vSizeTog x+5 yp-5 h8 +notab', 'Make Bigger').OnEvent("Click", TogSize)
hh.AddButton('vSymTog x+5 h8 +notab', '+ Symbols').OnEvent("Click", TogSym)
hh.SetFont('s11')
;RepStr := hh.AddEdit('vRepStr +Wrap yp+25 xs h' . hFactor + 100 . ' w' . wFactor + 320, '')
RepStr := hh.AddEdit('vRepStr +Wrap y+1 xs h' . hFactor + 100 . ' w' . wFactor + 320, '')
ComLbl := hh.AddText('xm y' . hFactor + 182, 'Enter Comment')
ComStr := hh.AddEdit('vComStr xs y' . hFactor + 200 . ' w' . wFactor + 315)
; ---- Buttons
(ButApp := hh.AddButton('xm y' . hFactor + 234, '&Append')).OnEvent("Click", hhButtonAppend)
(ButVal := hh.AddButton('+notab x+5 y' . hFactor + 234, '&Validate')).OnEvent("Click", hhButtonValidate)
(ButSpell := hh.AddButton('+notab x+5 y' . hFactor + 234, '&Spell')).OnEvent("Click", hhButtonSpell)
(ButOpen := hh.AddButton('+notab x+5 y' . hFactor + 234, '&Open')).OnEvent("Click", hhButtonOpen)
(ButCancel := hh.AddButton('+notab x+5 y' . hFactor + 234, '&Cancel')).OnEvent("Click", hhButtonCancel)

; The below hotkey also marks the end of the AutoExecution Section of this script file.
!#h::   ; HotString Helper activation hotkey-combo (not string) is Win+h. Change if desired.
{ MyDefaultOpts := ""
  DefaultHotStr := ""
  DefHotStr := ""
  LBLhotstring := ""
  SizeTog := ""
  addFirstLetters := ""
  tooSmallLen := ""
  Global myPrefix := ""
  Global mySuffix := ""
  initials := ""
  HotStrSug := ""
  Global tMyDefaultOpts := ""
  Global tDefHotStr := ""
  Global tRepStr := ""
  Global tComStr := ""
  Global ClipboardOld := ClipboardAll() ; Save and put back later.
  A_Clipboard := ""  ; Must start off blank for detection to work.
  Send("^c") ; Copy selected text.
  Errorlevel := !ClipWait(0.3) ; Wait for clipboard to contain text.

  If !InStr(A_Clipboard, "`n") ; Only trim NON multi line text strings.
    A_Clipboard := Trim(A_Clipboard) ; Because MS Word keeps leaving spaces.

  ; If white space present in selected text, probably not an Autocorrect entry.
  If (InStr(A_Clipboard, " ") || InStr(A_Clipboard, "`n"))
  {
   ;=======Change=options=for=MULTI=word=entry=options=and=trigger=strings=as=desired==============
   MyDefaultOpts := ""    ; PreEnter these multi-word hotstring options; "*" = end char not needed, etc.
   myPrefix := ";"        ; Optional character that you want suggested at the beginning of each hotstring.
   addFirstLetters := 5   ; Add first letter of this many words. (5 recommended; 0 = don't use feature.)
    tooSmallLen := 2      ; Only first letters from words longer than this. (Moot if addFirstLetters = 0)
   mySuffix := ""         ; An empty string "" means don't use feature.
  ;===========================================================one=more=below=======================
    If (addFirstLetters > 0)
    { LBLhotstring := "Edit trigger string as needed"
      initials := "" ; Initials will be the first letter of each word as a hotstring suggestion.
      HotStrSug := StrReplace(A_Clipboard, "`n", " ") ; Unwrap, but only for hotstr suggestion.
      Loop Parse, HotStrSug, A_Space
      { If (Strlen(A_LoopField) > tooSmallLen) ; Check length of each word, ignore if N letters.
           initials :=initials . SubStr(A_LoopField, ("1")<1 ? ("1")-1 : ("1"), "1")
        If (StrLen(initials) = addFirstLetters) ; stop looping if hotstring is N chars long.
           break
      }
      initials := StrLower(initials)
      DefaultHotStr := myPrefix . initials . mySuffix ; Append preferred prefix or suffix, as defined above, to initials.
    }
    else
    {LBLhotstring := "Add a trigger string"
     DefaultHotStr := myPrefix . mySuffix ; Use prefix and/or suffix as needed, but no initials.
    }
  }
  Else If (A_Clipboard = "")
      LBLhotstring := "Add a trigger string"
  else
  { LBLhotstring := "Add misspelled word"
    DefaultHotStr := A_Clipboard ; No spaces found so assume it's a mispelling autocorrect entry: no pre/suffix.
    ;===============Change=options=AUTOCORRECT=words=as=desired======================================
    myDefaultOpts := ""    ; PreEnter these (single-word) autocorrect options; "T" = raw text mode, etc.
    ;================================================================================================
  }
  hh['MyDefaultOpts'].value := MyDefaultOpts
  hh['TrigStrLbl'].value := LBLhotstring
  hh['DefHotStr'].value := DefaultHotStr
  hh['RepStr'].value := A_Clipboard
  hh['RepStr'].Opt("-Readonly")
  ButApp.Enabled := true
  hh.Show('Autosize')
} ; bottom of hotkey function

TogSize(*)
{   If (hh['SizeTog'].text = "Make Bigger") {
    hh['SizeTog'].text := "Make Smaller"
    ; ======Change=size=of=GUI=when="Make Bigger"=is=envoked========
    hFactor := 200 ; Height of Replacement box, Y pos of things below it.
    wFactor := 200 ; Width of 3 of the edit boxes.
    ;===============================================================
    SubTogSize(hFactor, wFactor)
    hh.Show('Autosize Center')
    return
  }
  If (hh['SizeTog'].text = "Make Smaller") {
    hh['SizeTog'].text := "Make Bigger"
    SubTogSize(0, 0)
    hh.Show('Autosize')
    return
  }
  SubTogSize(hFactor, wFactor)
  {
    DefHotStr.Move(,, wFactor + 250,)
    RepStr.Move(,, wFactor + 320, hFactor + 100)
    ComLbl.Move(, hFactor + 182,,)
    ComStr.move(, hFactor + 200, wFactor + 315,)
    ButApp.Move(, hFactor + 234,,)
    ButVal.Move(, hFactor + 234,,)
    ButSpell.Move(, hFactor + 234,,)
    ButOpen.Move(, hFactor + 234,,)
    ButCancel.Move(, hFactor + 234,,)
  }
}

TogSym(*)
{ ;====assign=symbolss=for="show symb"=button=================================
  myPilcrow := "¶"    ; Okay to change symb here if desired.
  myDot := "• "       ; adding a space allows more natural wrapping.
  myTab := " ? "
  ;===========================================================================
  If (hh['SymTog'].text = "+ Symbols") {
     hh['SymTog'].text := "- Symbols"
     RepStr := hh['RepStr'].text
     RepStr := StrReplace(StrReplace(RepStr, "`r`n", "`n"), "`n", myPilcrow . "`n") ; Pilcrow for Enter
     RepStr := StrReplace(RepStr, A_Space, myDot) ; middle dot for Space
     RepStr := StrReplace(RepStr, A_Tab, myTab) ; space arrow space for Tab
     hh['RepStr'].value := RepStr
     hh['RepStr'].Opt("+Readonly")
     ButApp.Enabled := false
     hh.Show('Autosize')
     return
  }
  If (hh['SymTog'].text = "- Symbols") {
    hh['SymTog'].text := "+ Symbols"
    RepStr := hh['RepStr'].text
    RepStr := StrReplace(RepStr, myPilcrow . "`r", "`r") ; Have to use `r ... weird.
	RepStr := StrReplace(RepStr, myDot, A_Space)
	RepStr := StrReplace(RepStr, myTab, A_Tab)
    hh['RepStr'].value := RepStr
    hh['RepStr'].Opt("-Readonly")
    ButApp.Enabled := true
    hh.Show('Autosize')
    return
  }
}

#HotIf WinActive(FormName, ) ; Allows window-specific behavior.=============================
{
$Enter:: ; When Enter is pressed, but only in this GUI. "$" prevents accidental Enter key loop.
  {
    If (hh['SymTog'].text = "Hide Symb")
      return
    Else if RepStr.Focused {
      Send("{Enter}") ; Just normal typing; Enter yields Enter key press.
      Return
    }
    Else {
      hhButtonAppend() ; Replacement box not focused, so press Append button.
      return
    }
  }
  Esc::
  {
    hh.Hide()
    A_Clipboard := ClipboardOld
  }
}
#HotIf ; Turn off window-specific behavior.======================

hhButtonAppend(*)
{ tMyDefaultOpts := hh['MyDefaultOpts'].text
  tDefHotStr := hh['DefHotStr'].text
  tRepStr := hh['RepStr'].text
  ValidationFunction(tMyDefaultOpts, tDefHotStr, tRepStr)
  If Not InStr(CombinedValidMsg, "-Okay.",,, 3)
  {    ; Msg doesn't have three occurrences of "-Okay."
    msgResult := MsgBox(CombinedValidMsg "`n`n####################`nContinue Anyway?", "VALIDATION", "OC 4096" )
    if (msgResult = "OK") {
       Appendit(tMyDefaultOpts, tDefHotStr, tRepStr) ; not valid, but user chose to continue anyway
       return
     }
    else
       return ; not valid, and user cancelled
  }
  else { ; no validation problems found
    Appendit(tMyDefaultOpts, tDefHotStr, tRepStr)
    return
 }
}

hhButtonValidate(*)
{ tMyDefaultOpts := hh['MyDefaultOpts'].text
  tDefHotStr := hh['DefHotStr'].text
  tRepStr := hh['RepStr'].text
  ValidationFunction(tMyDefaultOpts, tDefHotStr, tRepStr)
  MsgBox("Validation Results`n#################`n" . CombinedValidMsg,, 4096)
  Return
}

ValidationFunction(tMyDefaultOpts, tDefHotStr, tRepStr)
{ Global CombinedValidMsg
  ThisFile := Fileread(A_ScriptName) ; Save these contents to variable 'ThisFile'.
 ; ThisFile := Fileread("S:\AutoHotkey\MasterScript\MasterScript.ahk") ; <---- CHANGE later
  If (tMyDefaultOpts = "") ; If options box is empty, skip regxex check.
    validOpts := "Okay."
  else { ;===== Make sure hotstring options are valid ========
   NeedleRegEx := "(\*|B0|\?|SI|C|K[0-9]{1,3}|SE|X|SP|O|R|T)" ; These are in the AHK docs I swear!!!
   WithNeedlesRemoved := RegExReplace(tMyDefaultOpts, NeedleRegEx, "") ; Remove all valid options from var.

  If(WithNeedlesRemoved = "") ; If they were all removed...
     validOpts := "Okay."
   else { ; Some characters from the Options box were not recognized.
     OptTips := " ; Just a block text assignement to var
       (
  Don't include the colons.
  ..from AHK v1 docs...
   * - ending char not needed
   ? - trigger inside other words
   B0 - no backspacing
   SI - send input mode
   C - case-sensitive
   K(n) - set key delay
   SE - send event mode
   X - execute command
   SP - send play mode
   O - omit end char
   R - send raw
   T - super raw
      )"
     validOpts := "Invalid Hotsring Options found.`n---> " . WithNeedlesRemoved . "`n`n`tTips:`n" . OptTips
   }
  }
    ;==== Make sure hotstring box content is valid ========
  validHot := "" ; Reset to empty each time.
  If (tDefHotStr = "") || (tDefHotStr = myPrefix) || (tDefHotStr = mySuffix) || InStr(tDefHotStr, ":")
      validHot := "HotString box should not be empty.`n-Don't include colons."
  else ; No colons, and not empty. Good. Now check for duplicates.
     Loop Parse, ThisFile, "`n", "`r" ; Check line-by-line.
      If instr(A_LoopField, ":" . tDefHotStr . "::") { ; If line contains tDefHotStr...
           validHot := "DUPLICATE FOUND`nAt Line " . A_Index . ":`n " . A_LoopField
           break
         }
   If (validHot = "") ; If variable didn't get set in loop, then no duplicates found
       validHot := "Okay."
  ;==== Make sure replacement string box content is valid ===========
  If (tRepStr = "") || (SubStr(tRepStr, ("1")<1 ? ("1")-1 : ("1"), "1")==":") ; If Replacement box empty, or first char is ":"
      validRep := "Replacement string box should not be empty.`n-Don't include the colons."
  else
      validRep := "Okay."
  ; Concatenate the three above validity checks.
  CombinedValidMsg := "OPTIONS BOX `n-" . validOpts . "`n`nHOTSTRING BOX `n-" . validHot . "`n`nREPLACEMENT BOX `n-" . validRep
  Return CombinedValidMsg ; return result for use is Append or Validation functions.
} ; end of validation func

Appendit(tMyDefaultOpts, tDefHotStr, tRepStr)
{ WholeStr := ""
  tMyDefaultOpts := hh['MyDefaultOpts'].text
  tDefHotStr := hh['DefHotStr'].text
  tRepStr := hh['RepStr'].text
  tComStr := hh['ComStr'].text
  If (tComStr != "")
    tComStr := " `; " . tComStr
  If InStr(tRepStr, "`n") {
    WholeStr :=  ":" . tMyDefaultOpts . ":" . tDefHotStr . "::" . tComStr . "`n(`n" . tRepStr . "`n)"
  }
  Else {
    WholeStr :=  ":" . tMyDefaultOpts . ":" . tDefHotStr . "::" . tRepStr . tComStr
  }
  FileAppend("`n" WholeStr, A_ScriptFullPath) ; 'n makes sure it goes on a new line.
  Reload() ; relaod the script so the new hotstring will be ready for use.
}

hhButtonSpell(*) ; Called is "Spell" because "Spell Check" is too long.
{ tRepStr := hh['RepStr'].text
  If (tRepStr = "")
    MsgBox("Replacement Text not found.",, 4096)
  else {
    googleSugg := GoogleAutoCorrect(tRepStr) ; Calls below function
    If (tRepStr = googleSugg)
        MsgBox("No suggestions found.",, 4096)
    Else {
      msgResult := MsgBox(googleSugg "`n`n######################`nChange Replacement Text?", "Google Suggestion", "OC 4096")
      if (msgResult = "OK")
        hh['RepStr'].value := googleSugg
      else
      return
    }
  }
}

GoogleAutoCorrect(word)
{ ; Original by TheDewd, converted to v2 by Mikeyww.
  ; autohotkey.com/boards/viewtopic.php?f=82&t=120143
 objReq := ComObject('WinHttp.WinHttpRequest.5.1')
 objReq.Open('GET', 'https://www.google.com/search?q=' word)
 objReq.SetRequestHeader('User-Agent'
  , 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)')
 objReq.Send(), HTML := objReq.ResponseText
 RegExMatch(HTML, 'value="(.*?)"', &A)
 RegExMatch(HTML, ';spell=1.*?>(.*?)<\/a>', &B)
 Return B[1] || A[1]
}

hhButtonOpen(*)
{  ; Open this file and go to the bottom so you can see your Hotstrings.
  hh.Hide()
  A_Clipboard := ClipboardOld  ; Restore previous contents of clipboard.
  Edit()
  WinWaitActive(A_ScriptName) ; Wait for the script to be open in text editor.
  Sleep(250)
  Send("{Ctrl Down}{End}{Ctrl Up}{Home}") ; Navigate to the bottom.
}

hhButtonCancel(*)
{ hh.Hide()
  A_Clipboard := ClipboardOld  ; Restore previous contents of clipboard.
}

; HotStrings will be appended (added) by the script at the bottom.
;HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH


::;ottff:: ; just a test...
(
one two
three four
five	six
seven	eight
nine               ten.
)
ste(phen|ve) kunkel
User avatar
mikeyww
Posts: 26984
Joined: 09 Sep 2014, 18:38

Re: v2 of Google "Did you mean..." tool?

Post by mikeyww » 07 Aug 2023, 13:34

Regarding the error, you can see this if your string introduces a problem for RegExMatch.

Code: Select all

#Requires AutoHotkey v2.0
RegExMatch('', '(.)', &m)
MsgBox m[1]
Of course, one can readily check for such a condition.

Code: Select all

#Requires AutoHotkey v2.0
If RegExMatch('', '(.)', &m)
 MsgBox m[1]
User avatar
kunkel321
Posts: 1062
Joined: 30 Nov 2015, 21:19

Re: v2 of Google "Did you mean..." tool?

Post by kunkel321 » 07 Aug 2023, 14:03

mikeyww wrote:
07 Aug 2023, 13:34
Regarding the error, you can see this if your string introduces a problem for RegExMatch.
Yep... That's the one! If I'm reading this correctly, RegExMatch doesn't like having a blank haystack. LOL.

In the google function, it is the returned HTML code that is used for the haystack... So I'm wondering it this is getting caused by a bad internet connection.

I'll experiment. Thanks Mike!
ste(phen|ve) kunkel
User avatar
mikeyww
Posts: 26984
Joined: 09 Sep 2014, 18:38

Re: v2 of Google "Did you mean..." tool?

Post by mikeyww » 07 Aug 2023, 14:08

Yes, if HTML is null (e.g., nothing returned), you may see this.
User avatar
kunkel321
Posts: 1062
Joined: 30 Nov 2015, 21:19

Re: v2 of Google "Did you mean..." tool?

Post by kunkel321 » 07 Aug 2023, 14:27

Yeah, I thought it was caused by typing a misspelled word into the gui after .show()ing it, but it wasn't that. It is that, if the searched-for string is so short that google doesn't have any "did you mean..." recommendations, then there is an error. Putting If in front of the two RegExMatches causes the function to return a blank string and prevents the error.
ste(phen|ve) kunkel
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: v2 of Google "Did you mean..." tool?

Post by hasantr » 07 Aug 2023, 16:18

I've never seen this before. It works great for my language too. Thanks.
Post Reply

Return to “Ask for Help (v2)”