From 34b89035a0658cf88f80ad843f20a6d167f23f99 Mon Sep 17 00:00:00 2001 From: Abdullah Gheith Date: Thu, 26 Mar 2026 21:25:50 +0100 Subject: [PATCH 1/8] ignore invalid certs --- pkg/common/config.go | 1 + pkg/executor/executor.go | 7 ++++++- resources/pi/pi.html | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/common/config.go b/pkg/common/config.go index fe3baef..451ff78 100644 --- a/pkg/common/config.go +++ b/pkg/common/config.go @@ -11,6 +11,7 @@ type Config struct { TitlePrefix string `json:"titlePrefix"` BodyScript string `json:"bodyScript"` ShowSuccessNotification bool `json:"showSuccessNotification"` + InsecureSkipVerify bool `json:"insecureSkipVerify"` MethodType string `json:"methodType"` Body string `json:"body"` } diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index 49a8ba1..e2f770f 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -2,6 +2,7 @@ package executor import ( "context" + "crypto/tls" "strings" "github.com/cockroachdb/errors" @@ -28,7 +29,11 @@ func (e *Executor) Execute( ctx context.Context, executeReq ExecuteRequest, ) (*ExecuteResponse, error) { - httpReq := req.C().NewRequest() + client := req.C() + if executeReq.Config.InsecureSkipVerify { + client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) + } + httpReq := client.NewRequest() httpReq = httpReq.SetContext(ctx) httpReq.Method = executeReq.Config.MethodType diff --git a/resources/pi/pi.html b/resources/pi/pi.html index 16698bd..272da8a 100644 --- a/resources/pi/pi.html +++ b/resources/pi/pi.html @@ -65,9 +65,10 @@ - -
- +
+ + +

From 992b67f0b39eccb26e80891e221c184498f7107a Mon Sep 17 00:00:00 2001 From: AbdullahGheith <64132433+AbdullahGheith@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:27:29 +0100 Subject: [PATCH 2/8] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cb8e7f7..b018fe8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ **ApiMonkey** is a powerful StreamDeck plugin designed for developers, IT professionals, and enthusiasts who require a seamless way to send HTTP/HTTPS requests directly from their StreamDeck. With its advanced features and customization options, ApiMonkey stands out by providing enhanced functionality for automated workflows and interactions with web services. + ## Features ApiMonkey goes beyond simple HTTP/HTTPS request functionalities, offering a range of advanced features that set it apart from analogs: @@ -142,4 +143,4 @@ return totalCount - [streamdeck-sdk-go](https://github.com/tystuyfzand/streamdeck-sdk-go) - StreamDeck SDK for Go - [streamdeck-easypi](https://github.com/BarRaider/streamdeck-easypi) - EasyPI for StreamDeck - [gopher-lua](https://github.com/yuin/gopher-lua) - Lua VM in Go -- [gjson](https://github.com/tidwall/gjson) - JSON parser for Go \ No newline at end of file +- [gjson](https://github.com/tidwall/gjson) - JSON parser for Go From 5bc9cdade96685e8ad23deff46a9760a92465b44 Mon Sep 17 00:00:00 2001 From: AbdullahGheith <64132433+AbdullahGheith@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:32:55 +0100 Subject: [PATCH 3/8] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b018fe8..92878f3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ **ApiMonkey** is a powerful StreamDeck plugin designed for developers, IT professionals, and enthusiasts who require a seamless way to send HTTP/HTTPS requests directly from their StreamDeck. With its advanced features and customization options, ApiMonkey stands out by providing enhanced functionality for automated workflows and interactions with web services. - ## Features ApiMonkey goes beyond simple HTTP/HTTPS request functionalities, offering a range of advanced features that set it apart from analogs: From 1e4e7fbd77ec4ca19d82631ad210a861a90bc0d1 Mon Sep 17 00:00:00 2001 From: Abdullah Gheith Date: Thu, 26 Mar 2026 21:49:45 +0100 Subject: [PATCH 4/8] dont open browser if browser url not entered --- pkg/instance/instance.go | 3 ++- resources/pi/pi.html | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index 6fc7e09..cd934b9 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -223,7 +223,8 @@ func (i *DefaultInstance) stopWithoutLock() { func (i *DefaultInstance) KeyPressed() error { targetUrl := i.cfg.BrowserUrl if targetUrl == "" { - targetUrl = i.cfg.ApiUrl + i.ExecuteSingleRequest(i.ctx) + return nil } targetUrl, err := utils.ExecuteTemplate(targetUrl, i.cfg.TemplateParameters) diff --git a/resources/pi/pi.html b/resources/pi/pi.html index 272da8a..ba6197d 100644 --- a/resources/pi/pi.html +++ b/resources/pi/pi.html @@ -65,9 +65,14 @@ + + +
+
Invalid certs
+
- +
From aaaad7acdb2ca7153e27f8f880acdd8997640624 Mon Sep 17 00:00:00 2001 From: Abdullah Gheith Date: Thu, 26 Mar 2026 22:03:56 +0100 Subject: [PATCH 5/8] multiple calls going out --- main.go | 5 +++ pkg/instance/instance.go | 77 ++++++++++++++++++++++++++++++++-------- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index dd96a08..9665f39 100644 --- a/main.go +++ b/main.go @@ -88,6 +88,11 @@ func main() { lg.Err(err).Send() return } + + if err := manager.StartAsync(event.Context); err != nil { + lg.Err(err).Send() + return + } }) sdk.AddHandler(func(event *sdk.KeyDownEvent) { diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index cd934b9..4e06245 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -28,6 +28,7 @@ type DefaultInstance struct { ctxCancel context.CancelFunc executor Executor sdk SDK + lastReq time.Time } func NewInstance( @@ -91,13 +92,19 @@ func (i *DefaultInstance) StartAsync() { } func (i *DefaultInstance) run() { - ctx := i.ctx + for { + i.mut.Lock() + ctx := i.ctx + if ctx == nil || ctx.Err() != nil { + i.mut.Unlock() + return + } - for ctx.Err() == nil { interval := 30 - if i.cfg.IntervalSeconds > 0 { + if i.cfg != nil && i.cfg.IntervalSeconds > 0 { interval = i.cfg.IntervalSeconds } + i.mut.Unlock() newLogger := log.With(). Str("id", uuid.NewString()). @@ -110,15 +117,38 @@ func (i *DefaultInstance) run() { i.ExecuteSingleRequest(innerCtx) innerCancel() - time.Sleep(time.Duration(interval) * time.Second) + select { + case <-ctx.Done(): + return + case <-time.After(time.Duration(interval) * time.Second): + } } } func (i *DefaultInstance) ExecuteSingleRequest( ctx context.Context, ) { + if ctx == nil { + return + } + + i.mut.Lock() + if i.cfg == nil { + i.mut.Unlock() + return + } + + if time.Since(i.lastReq) < 1*time.Second { + i.mut.Unlock() + return + } + + i.lastReq = time.Now() + cfg := *i.cfg + i.mut.Unlock() + resp, err := i.executor.Execute(ctx, executor.ExecuteRequest{ - Config: *i.cfg, + Config: cfg, }) if err != nil { zerolog.Ctx(ctx).Err(err).Msg("error executing request") @@ -126,23 +156,24 @@ func (i *DefaultInstance) ExecuteSingleRequest( return } - if handleErr := i.HandleResponse(ctx, resp); handleErr != nil { + if handleErr := i.HandleResponse(ctx, cfg, resp); handleErr != nil { zerolog.Ctx(ctx).Err(handleErr).Msg("error handling response") i.ShowAlert() return } - if i.cfg.ShowSuccessNotification { + if cfg.ShowSuccessNotification { i.ShowOk() } } func (i *DefaultInstance) HandleResponse( ctx context.Context, + cfg common.Config, response *executor.ExecuteResponse, ) error { var sb strings.Builder - prefix, err := utils.ExecuteTemplate(i.cfg.TitlePrefix, i.cfg.TemplateParameters) + prefix, err := utils.ExecuteTemplate(cfg.TitlePrefix, cfg.TemplateParameters) if err != nil { return errors.Wrap(err, "failed to execute template on prefix") } @@ -151,7 +182,7 @@ func (i *DefaultInstance) HandleResponse( sb.WriteString(strings.ReplaceAll(prefix, "\\n", "\n") + "\n") } - if len(i.cfg.ResponseMapper) == 0 { + if len(cfg.ResponseMapper) == 0 { sb.WriteString(response.Response) i.sdk.SetTitle(i.ctxID, sb.String(), 0) @@ -160,8 +191,8 @@ func (i *DefaultInstance) HandleResponse( return nil } - def, defaultOk := i.cfg.ResponseMapper["*"] - mapped, ok := i.cfg.ResponseMapper[response.Response] + def, defaultOk := cfg.ResponseMapper["*"] + mapped, ok := cfg.ResponseMapper[response.Response] if !ok && defaultOk { mapped = def @@ -221,22 +252,38 @@ func (i *DefaultInstance) stopWithoutLock() { } func (i *DefaultInstance) KeyPressed() error { - targetUrl := i.cfg.BrowserUrl + i.mut.Lock() + if i.cfg == nil { + i.mut.Unlock() + return nil + } + cfg := *i.cfg + ctx := i.ctx + i.mut.Unlock() + + targetUrl := cfg.BrowserUrl if targetUrl == "" { - i.ExecuteSingleRequest(i.ctx) + i.ExecuteSingleRequest(ctx) return nil } - targetUrl, err := utils.ExecuteTemplate(targetUrl, i.cfg.TemplateParameters) + templatedBrowserUrl, err := utils.ExecuteTemplate(targetUrl, cfg.TemplateParameters) if err != nil { i.ShowAlert() return errors.Wrap(err, "failed to execute template") } - if err = utils.OpenBrowser(targetUrl); err != nil { + if err = utils.OpenBrowser(templatedBrowserUrl); err != nil { i.ShowAlert() return err } + templatedApiUrl, err := utils.ExecuteTemplate(cfg.ApiUrl, cfg.TemplateParameters) + if err == nil && templatedApiUrl != templatedBrowserUrl { + i.ExecuteSingleRequest(ctx) + } else if err != nil { + // fallback to execute if template fails? probably not, but at least we don't block. + } + return nil } From ccd5f85889d9ea3f94cd3e159a7b10ea1cf3735b Mon Sep 17 00:00:00 2001 From: Abdullah Gheith Date: Thu, 26 Mar 2026 22:15:56 +0100 Subject: [PATCH 6/8] Revert "multiple calls going out" This reverts commit aaaad7ac --- pkg/instance/instance.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index 6fc7e09..cd934b9 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -223,7 +223,8 @@ func (i *DefaultInstance) stopWithoutLock() { func (i *DefaultInstance) KeyPressed() error { targetUrl := i.cfg.BrowserUrl if targetUrl == "" { - targetUrl = i.cfg.ApiUrl + i.ExecuteSingleRequest(i.ctx) + return nil } targetUrl, err := utils.ExecuteTemplate(targetUrl, i.cfg.TemplateParameters) From c1cb11a39dc60ede7779f700602980ec06ac157f Mon Sep 17 00:00:00 2001 From: Abdullah Gheith Date: Thu, 26 Mar 2026 22:24:34 +0100 Subject: [PATCH 7/8] interval work --- main.go | 6 +++++- pkg/instance/instance.go | 38 ++++++++++++++++++++++++++---------- pkg/instance/interfaces.go | 2 +- pkg/instance/manager.go | 4 ++-- pkg/instance/manager_test.go | 6 +++--- resources/pi/pi.html | 2 +- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index dd96a08..ef27086 100644 --- a/main.go +++ b/main.go @@ -68,7 +68,7 @@ func main() { return } - if err = manager.StartAsync(event.Context); err != nil { + if err = manager.StartAsync(event.Context, true); err != nil { lg.Err(err).Send() return } @@ -88,6 +88,10 @@ func main() { lg.Err(err).Send() return } + if err := manager.StartAsync(event.Context, false); err != nil { + lg.Err(err).Send() + return + } }) sdk.AddHandler(func(event *sdk.KeyDownEvent) { diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index cd934b9..ecaa69a 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -77,7 +77,7 @@ func (i *DefaultInstance) ShowOk() { i.sdk.ShowOk(i.ctxID) } -func (i *DefaultInstance) StartAsync() { +func (i *DefaultInstance) StartAsync(immediate bool) { i.mut.Lock() defer i.mut.Unlock() @@ -87,18 +87,13 @@ func (i *DefaultInstance) StartAsync() { i.ctx = ctx i.ctxCancel = cancel - go i.run() + go i.run(immediate) } -func (i *DefaultInstance) run() { +func (i *DefaultInstance) run(immediate bool) { ctx := i.ctx - for ctx.Err() == nil { - interval := 30 - if i.cfg.IntervalSeconds > 0 { - interval = i.cfg.IntervalSeconds - } - + if immediate { newLogger := log.With(). Str("id", uuid.NewString()). Str("ctxID", i.ctxID). @@ -109,8 +104,31 @@ func (i *DefaultInstance) run() { i.ExecuteSingleRequest(innerCtx) innerCancel() + } + + if i.cfg.IntervalSeconds <= 0 { + return + } + + ticker := time.NewTicker(time.Duration(i.cfg.IntervalSeconds) * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + newLogger := log.With(). + Str("id", uuid.NewString()). + Str("ctxID", i.ctxID). + Logger() - time.Sleep(time.Duration(interval) * time.Second) + innerCtx, innerCancel := context.WithCancel(ctx) + innerCtx = newLogger.WithContext(innerCtx) + + i.ExecuteSingleRequest(innerCtx) + innerCancel() + } } } diff --git a/pkg/instance/interfaces.go b/pkg/instance/interfaces.go index dcbcd6d..d8a9fac 100644 --- a/pkg/instance/interfaces.go +++ b/pkg/instance/interfaces.go @@ -30,7 +30,7 @@ type Factory interface { type Instance interface { SetConfig(payload *fastjson.Value) error - StartAsync() + StartAsync(immediate bool) Stop() KeyPressed() error } diff --git a/pkg/instance/manager.go b/pkg/instance/manager.go index afe1175..5b94c91 100644 --- a/pkg/instance/manager.go +++ b/pkg/instance/manager.go @@ -36,7 +36,7 @@ func (m *Manager) InitInstance(ctxId string) (Instance, error) { return instance, nil } -func (m *Manager) StartAsync(ctxId string) error { +func (m *Manager) StartAsync(ctxId string, immediate bool) error { m.mut.Lock() defer m.mut.Unlock() instance, ok := m.instances[ctxId] @@ -45,7 +45,7 @@ func (m *Manager) StartAsync(ctxId string) error { return errors.New("instance not found") } - instance.StartAsync() + instance.StartAsync(immediate) return nil } diff --git a/pkg/instance/manager_test.go b/pkg/instance/manager_test.go index 03e203d..b438ce6 100644 --- a/pkg/instance/manager_test.go +++ b/pkg/instance/manager_test.go @@ -20,14 +20,14 @@ func TestNewManager(t *testing.T) { mockInstance := NewMockInstance(gomock.NewController(t)) factory.EXPECT().Create(ctxID).Return(mockInstance) - mockInstance.EXPECT().StartAsync() + mockInstance.EXPECT().StartAsync(gomock.Any()) mockInstance.EXPECT().Stop() mockInstance.EXPECT().KeyPressed() _, err := mgr.InitInstance(ctxID) assert.Nil(t, err) - assert.NoError(t, mgr.StartAsync(ctxID)) + assert.NoError(t, mgr.StartAsync(ctxID, true)) assert.NoError(t, mgr.KeyPressed(ctxID)) js := &fastjson.Value{} @@ -43,7 +43,7 @@ func TestNotExist(t *testing.T) { ctxID := "1231231" - assert.ErrorContains(t, mgr.StartAsync(ctxID), "instance not found") + assert.ErrorContains(t, mgr.StartAsync(ctxID, true), "instance not found") assert.ErrorContains(t, mgr.KeyPressed(ctxID), "instance not found") assert.ErrorContains(t, mgr.SetInstanceConfig(ctxID, nil), "instance not found") assert.NoError(t, mgr.Stop(ctxID)) diff --git a/resources/pi/pi.html b/resources/pi/pi.html index 16698bd..5764a32 100644 --- a/resources/pi/pi.html +++ b/resources/pi/pi.html @@ -56,7 +56,7 @@
Interval (Sec)
- +
Indicator
From 2dcaccdae107cb07354185c90068f0a3ded23567 Mon Sep 17 00:00:00 2001 From: Abdullah Gheith Date: Thu, 26 Mar 2026 22:31:44 +0100 Subject: [PATCH 8/8] fix --- main.go | 3 +++ pkg/instance/instance.go | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index ef27086..3fb7438 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,7 @@ func main() { } }) sdk.AddHandler(func(event *sdk.WillDisappearEvent) { + defer recoverPanic() if event.Payload == nil { return } @@ -84,6 +85,7 @@ func main() { }) sdk.AddHandler(func(event *sdk.ReceiveSettingsEvent) { + defer recoverPanic() if err := manager.SetInstanceConfig(event.Context, event.Settings); err != nil { lg.Err(err).Send() return @@ -95,6 +97,7 @@ func main() { }) sdk.AddHandler(func(event *sdk.KeyDownEvent) { + defer recoverPanic() if err := manager.KeyPressed(event.Context); err != nil { lg.Err(err).Send() return diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index ecaa69a..7309ee0 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -91,6 +91,11 @@ func (i *DefaultInstance) StartAsync(immediate bool) { } func (i *DefaultInstance) run(immediate bool) { + defer func() { + if rec := recover(); rec != nil { + log.Error().Msgf("panic in run: %v", rec) + } + }() ctx := i.ctx if immediate { @@ -106,7 +111,7 @@ func (i *DefaultInstance) run(immediate bool) { innerCancel() } - if i.cfg.IntervalSeconds <= 0 { + if i.cfg == nil || i.cfg.IntervalSeconds <= 0 { return } @@ -135,6 +140,12 @@ func (i *DefaultInstance) run(immediate bool) { func (i *DefaultInstance) ExecuteSingleRequest( ctx context.Context, ) { + if i.cfg == nil { + zerolog.Ctx(ctx).Error().Msg("config is nil, cannot execute request") + i.ShowAlert() + return + } + resp, err := i.executor.Execute(ctx, executor.ExecuteRequest{ Config: *i.cfg, }) @@ -239,9 +250,31 @@ func (i *DefaultInstance) stopWithoutLock() { } func (i *DefaultInstance) KeyPressed() error { + i.mut.Lock() + defer i.mut.Unlock() + + if i.cfg == nil { + i.ShowAlert() + return errors.New("instance config is nil") + } + targetUrl := i.cfg.BrowserUrl if targetUrl == "" { - i.ExecuteSingleRequest(i.ctx) + ctx := i.ctx + if ctx == nil { + ctx = context.Background() + } + + newLogger := log.With(). + Str("id", uuid.NewString()). + Str("ctxID", i.ctxID). + Logger() + + innerCtx, innerCancel := context.WithCancel(ctx) + defer innerCancel() + innerCtx = newLogger.WithContext(innerCtx) + + i.ExecuteSingleRequest(innerCtx) return nil }