8 files changed, 56 insertions(+), 10 deletions(-)

M CHANGELOG.md
M README.md
M cmd/quasiauto/main.go
M exec.go
M exec_test.go
M go.mod
M go.sum
M sequence_test.go
M CHANGELOG.md +6 -0
@@ 13,6 13,12 @@ and this project adheres to [Semantic Ve
 > - **Fixed**: for any bug fixes.
 > - **Security**: in case of vulnerabilities.
 
+## [0.3.0]
+
+### Added
+
+- Implements BEEP support
+
 ## [0.2.0] 2021-08-20
 
 ### Added

          
M README.md +5 -5
@@ 71,7 71,7 @@ quasiauto can autotype on behalf of any 
 Limitations
 ================
 
-BEEP and VKEY are not implemented. BEEP might be, but it would pull in a heavy dependency and I'm holding off on that. VKEY is Windows-specific.
+VKEY is not implemented, as it's windows specific and I don't have access to a Windows computer.
 
 I haven't found a way to ensure that no modifier keys are being held when autotype starts. Consequently, there's a risk that whatever hotkey trigger is set up might mean a meta key is still being held when typing starts. This is Not Good™, and avoiding this means you may have to insert a small delay (500ms) before executing the program. In my case, the various delays introduced by forking, parsing input, and so on is enough that I don't have a problem if I don't linger on the modifier; however, YMMV. I'll add a safety measure, if I can find a way to read the keyboard state and ensure it's clear in a platform-independent way.
 

          
@@ 110,8 110,8 @@ OTP		1328765
 Metrics
 -------
 
+```24
+         Coverage     Parse benchmark 	Exec benchmark
+v0.2.0   94%          12734 		10801
+v0.1.0   78%           8035
 ```
-         Coverage     Benchmark
-v0.2.0   94%          40745 ns/op
-v0.1.0   78%
-```

          
M cmd/quasiauto/main.go +0 -1
@@ 11,7 11,6 @@ import (
 
 var Version string = "development"
 
-// TODO replace the hard-coded values with a CSV
 // TODO add notify-send entry title? https://github.com/keybase/go-notifier
 // TODO quasi-type mode -- just type & tab through fields
 // TODO add mouse mode -- click == type & advance

          
M exec.go +17 -3
@@ 2,6 2,7 @@ package quasiauto
 
 import (
 	"fmt"
+	"github.com/gen2brain/beeep"
 	"log"
 	"regexp"
 	"strconv"

          
@@ 100,9 101,12 @@ func handleCommand(t Typer, seq SeqEntry
 	}
 	switch seq.Token {
 	case "DELAY":
+		if len(seq.Args) != 1 {
+			return fmt.Errorf("DELAY takes 1 integer argument")
+		}
 		d, e := strconv.Atoi(seq.Args[0])
 		if e != nil {
-			return fmt.Errorf("DELAY takes integer arguments; got %s: %s", seq.Args[0], e)
+			return fmt.Errorf("bad argument %s for DELAY: %s", seq.Args[0], e)
 		}
 		time.Sleep(time.Duration(d) * time.Millisecond)
 	case "VKEY":

          
@@ 117,8 121,18 @@ func handleCommand(t Typer, seq SeqEntry
 			t.ActivePID(ps[0])
 		}
 	case "BEEP":
-		// FIXME implement BEEP -- github.com/gen2brain/beeep, but it imports javascript
-		return fmt.Errorf("BEEP is not yet implemented")
+		if len(seq.Args) != 2 {
+			return fmt.Errorf("BEEP takes two arguments, frequency (Hz) & duration (ms)")
+		}
+		freq, e := strconv.ParseFloat(seq.Args[0], 64)
+		if e != nil {
+			return fmt.Errorf("bad frequency argument %s for BEEP: %s", seq.Args[0], e)
+		}
+		duration, e := strconv.Atoi(seq.Args[1])
+		if e != nil {
+			return fmt.Errorf("bad duration argument %s for BEEP: %s", seq.Args[0], e)
+		}
+		beeep.Beep(freq, duration)
 	}
 	return nil
 }

          
M exec_test.go +11 -0
@@ 127,6 127,17 @@ func Test_handleCommand_APPACT(t *testin
 	}
 }
 
+func Benchmark_Exec(b *testing.B) {
+	input := "aa^+bcc%@dee^~ff~gg"
+	s := NewSequence()
+	s.Parse(input)
+	mt := New()
+	args := make(map[string]string)
+	for i := 0; i < b.N; i++ {
+		s.Exec(args, &mt)
+	}
+}
+
 type MockTyper struct {
 	Typed     []string
 	Tapped    []string

          
M go.mod +1 -0
@@ 3,6 3,7 @@ module ser1.net/quasiauto
 go 1.16
 
 require (
+	github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1 // indirect
 	github.com/go-vgo/robotgo v0.99.1
 	github.com/stretchr/testify v1.7.0 // indirect
 )

          
M go.sum +15 -0
@@ 5,14 5,26 @@ github.com/StackExchange/wmi v0.0.0-2021
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1 h1:Xh9mvwEmhbdXlRSsgn+N0zj/NqnKvpeqL08oKDHln2s=
+github.com/gen2brain/beeep v0.0.0-20210529141713-5586760f0cc1/go.mod h1:ElSskYZe3oM8kThaHGJ+kiN2yyUMVXMZ7WxF9QqLDS8=
 github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE=
+github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10=
 github.com/go-vgo/robotgo v0.99.0 h1:Lp6kvic1/LxkSShf6KNjcrcE21FCFlonrCguQN96Ay4=
 github.com/go-vgo/robotgo v0.99.0/go.mod h1:0+i2QWRmZtbIF02RwmiGfFj33Judprukd8ls5J6Eajg=
 github.com/go-vgo/robotgo v0.99.1 h1:bjw/ta8ejS9fxS+PSxAu7PNoSrvpeB0/FbDh+FX9Tdo=
 github.com/go-vgo/robotgo v0.99.1/go.mod h1:0+i2QWRmZtbIF02RwmiGfFj33Judprukd8ls5J6Eajg=
+github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
+github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
+github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ=
+github.com/gopherjs/gopherwasm v1.1.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
 github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
 github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
+github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
+github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
 github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
 github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
 github.com/otiai10/gosseract v2.2.1+incompatible h1:Ry5ltVdpdp4LAa2bMjsSJH34XHVOV7XMi41HtzL8X2I=

          
@@ 35,6 47,8 @@ github.com/stretchr/testify v1.4.0 h1:2E
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
+github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
 github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
 github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
 github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=

          
@@ 58,6 72,7 @@ golang.org/x/net v0.0.0-20201110031124-6
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

          
M sequence_test.go +1 -1
@@ 69,7 69,7 @@ func Test_ParseTokens(t *testing.T) {
 	}
 }
 
-func BenchmarkMap(b *testing.B) {
+func Benchmark_Parse(b *testing.B) {
 	input := "{BEEP}{PLUS}{USERNAME}{TAB}{F7}heyho{NUMPAD5}{TILDE}{PASSWORD}{ENTER}{[}{%}"
 	for i := 0; i < b.N; i++ {
 		seq := NewSequence()