diff --git a/cmd/humanlog/main.go b/cmd/humanlog/main.go index 7cfda5b1..5afe529f 100644 --- a/cmd/humanlog/main.go +++ b/cmd/humanlog/main.go @@ -3,6 +3,7 @@ package main import ( "context" "crypto/tls" + "errors" "fmt" "log" "log/slog" @@ -15,10 +16,12 @@ import ( "strings" "time" + "connectrpc.com/connect" "github.com/99designs/keyring" "github.com/aybabtme/rgbterm" "github.com/blang/semver" "github.com/charmbracelet/huh" + "github.com/gen2brain/beeep" types "github.com/humanlogio/api/go/types/v1" "github.com/humanlogio/humanlog" "github.com/humanlogio/humanlog/internal/pkg/config" @@ -452,11 +455,27 @@ func newApp() *cli.App { // TODO(antoine): remove this codepath, it's redundant with the localhost port path ll := getLogger(cctx) apiURL := getAPIUrl(cctx) - notifyUnableToIngest := func(err error) { // TODO: notify using system notification? logerror("configured to ingest, but unable to do so: %v", err) - os.Exit(1) + msg := "Your logs are not being sent!" + var cerr *connect.Error + if errors.As(err, &cerr) { + if cerr.Code() == connect.CodeResourceExhausted { + msg += "\n\n- " + cerr.Message() + } else { + msg += "\n\n- " + cerr.Error() + } + } else { + msg += "\n\n" + "An unexpected error occured while trying to ingest your logs, see your terminal for details." + logerror("err=%T", err) + } + + if err := beeep.Alert("humanlog has problems!", msg, ""); err != nil { + logerror("couldn't send desktop notification: %v", err) + beeep.Beep(3000, 1) + os.Exit(1) + } } flushTimeout := 300 * time.Millisecond @@ -477,7 +496,7 @@ func newApp() *cli.App { if err := remotesink.Flush(ctx); err != nil { ll.ErrorContext(ctx, "couldn't flush buffered log", slog.Any("err", err)) } else { - ll.InfoContext(ctx, "done sending all logs") + ll.DebugContext(ctx, "done sending all logs") } }() loginfo("saving to %s", apiURL) @@ -514,7 +533,7 @@ func newApp() *cli.App { if err := done(ctx); err != nil { ll.ErrorContext(ctx, "couldn't flush buffered log (localhost)", slog.Any("err", err)) } else { - ll.InfoContext(ctx, "done sending all logs") + ll.DebugContext(ctx, "done sending all logs") } }() } diff --git a/go.mod b/go.mod index 0b0a3b82..12a600ec 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/charmbracelet/x/term v0.2.0 github.com/cli/safeexec v1.0.1 github.com/fatih/color v1.16.0 + github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 github.com/go-logfmt/logfmt v0.5.1 github.com/google/uuid v1.6.0 github.com/humanlogio/api/go v0.0.0-20241011070935-7bb04da206c8 @@ -49,7 +50,9 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -58,9 +61,11 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.18.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index a5ed90a6..eef97fbc 100644 --- a/go.sum +++ b/go.sum @@ -56,10 +56,16 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 h1:ygs9POGDQpQGLJPlq4+0LBUmMBNox1N4JSpw+OETcvI= +github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +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/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -98,6 +104,8 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -119,6 +127,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= diff --git a/vendor/github.com/gen2brain/beeep/LICENSE b/vendor/github.com/gen2brain/beeep/LICENSE new file mode 100644 index 00000000..9d4a9221 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2017, Milan Nikolic +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gen2brain/beeep/README.md b/vendor/github.com/gen2brain/beeep/README.md new file mode 100644 index 00000000..68b878af --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/README.md @@ -0,0 +1,37 @@ +## beeep +[![Build Status](https://github.com/gen2brain/beeep/actions/workflows/build.yml/badge.svg)](https://github.com/gen2brain/beeep/actions) +[![Go Reference](https://pkg.go.dev/badge/github.com/gen2brain/beeep.svg)](https://pkg.go.dev/github.com/gen2brain/beeep) +[![Go Report Card](https://goreportcard.com/badge/github.com/gen2brain/beeep?branch=master)](https://goreportcard.com/report/github.com/gen2brain/beeep) + +`beeep` provides a cross-platform library for sending desktop notifications, alerts and beeps. + +### Installation + + go get -u github.com/gen2brain/beeep + +### Build tags + +* `nodbus` - disable `godbus/dbus` and use only `notify-send` + +### Examples + +```go +err := beeep.Beep(beeep.DefaultFreq, beeep.DefaultDuration) +if err != nil { + panic(err) +} +``` + +```go +err := beeep.Notify("Title", "Message body", "assets/information.png") +if err != nil { + panic(err) +} +``` + +```go +err := beeep.Alert("Title", "Message body", "assets/warning.png") +if err != nil { + panic(err) +} +``` diff --git a/vendor/github.com/gen2brain/beeep/alert_darwin.go b/vendor/github.com/gen2brain/beeep/alert_darwin.go new file mode 100644 index 00000000..328785e3 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/alert_darwin.go @@ -0,0 +1,21 @@ +//go:build darwin && !linux && !freebsd && !netbsd && !openbsd && !windows && !js +// +build darwin,!linux,!freebsd,!netbsd,!openbsd,!windows,!js + +package beeep + +import ( + "fmt" + "os/exec" +) + +// Alert displays a desktop notification and plays a default system sound. +func Alert(title, message, appIcon string) error { + osa, err := exec.LookPath("osascript") + if err != nil { + return err + } + + script := fmt.Sprintf("display notification %q with title %q sound name \"default\"", message, title) + cmd := exec.Command(osa, "-e", script) + return cmd.Run() +} diff --git a/vendor/github.com/gen2brain/beeep/alert_js.go b/vendor/github.com/gen2brain/beeep/alert_js.go new file mode 100644 index 00000000..fdb55565 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/alert_js.go @@ -0,0 +1,12 @@ +//go:build js +// +build js + +package beeep + +// Alert displays a desktop notification and plays a beep. +func Alert(title, message, appIcon string) error { + if err := Notify(title, message, appIcon); err != nil { + return err + } + return Beep(DefaultFreq, DefaultDuration) +} diff --git a/vendor/github.com/gen2brain/beeep/alert_unix.go b/vendor/github.com/gen2brain/beeep/alert_unix.go new file mode 100644 index 00000000..0406bbd9 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/alert_unix.go @@ -0,0 +1,12 @@ +//go:build linux || freebsd || netbsd || openbsd || illumos +// +build linux freebsd netbsd openbsd illumos + +package beeep + +// Alert displays a desktop notification and plays a beep. +func Alert(title, message, appIcon string) error { + if err := Notify(title, message, appIcon); err != nil { + return err + } + return Beep(DefaultFreq, DefaultDuration) +} diff --git a/vendor/github.com/gen2brain/beeep/alert_unsupported.go b/vendor/github.com/gen2brain/beeep/alert_unsupported.go new file mode 100644 index 00000000..12c1e055 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/alert_unsupported.go @@ -0,0 +1,9 @@ +//go:build !linux && !freebsd && !netbsd && !openbsd && !windows && !darwin && !illumos && !js +// +build !linux,!freebsd,!netbsd,!openbsd,!windows,!darwin,!illumos,!js + +package beeep + +// Alert displays a desktop notification and plays a beep. +func Alert(title, message, appIcon string) error { + return ErrUnsupported +} diff --git a/vendor/github.com/gen2brain/beeep/alert_windows.go b/vendor/github.com/gen2brain/beeep/alert_windows.go new file mode 100644 index 00000000..825b147e --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/alert_windows.go @@ -0,0 +1,22 @@ +//go:build windows && !linux && !freebsd && !netbsd && !openbsd && !darwin && !js +// +build windows,!linux,!freebsd,!netbsd,!openbsd,!darwin,!js + +package beeep + +import ( + toast "github.com/go-toast/toast" +) + +// Alert displays a desktop notification and plays a default system sound. +func Alert(title, message, appIcon string) error { + if isWindows10 { + note := toastNotification(title, message, pathAbs(appIcon)) + note.Audio = toast.Default + return note.Push() + } + + if err := Notify(title, message, appIcon); err != nil { + return err + } + return Beep(DefaultFreq, DefaultDuration) +} diff --git a/vendor/github.com/gen2brain/beeep/beeep.go b/vendor/github.com/gen2brain/beeep/beeep.go new file mode 100644 index 00000000..b0e66d46 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/beeep.go @@ -0,0 +1,27 @@ +// Package beeep provides a cross-platform library for sending desktop notifications and beeps. +package beeep + +import ( + "errors" + "path/filepath" + "runtime" +) + +var ( + // ErrUnsupported is returned when operating system is not supported. + ErrUnsupported = errors.New("beeep: unsupported operating system: " + runtime.GOOS) +) + +func pathAbs(path string) string { + var err error + var abs string + + if path != "" { + abs, err = filepath.Abs(path) + if err != nil { + abs = path + } + } + + return abs +} diff --git a/vendor/github.com/gen2brain/beeep/beep_darwin.go b/vendor/github.com/gen2brain/beeep/beep_darwin.go new file mode 100644 index 00000000..93cf608e --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/beep_darwin.go @@ -0,0 +1,29 @@ +//go:build darwin && !linux && !freebsd && !netbsd && !openbsd && !windows && !js +// +build darwin,!linux,!freebsd,!netbsd,!openbsd,!windows,!js + +package beeep + +import ( + "os" + "os/exec" +) + +var ( + // DefaultFreq - frequency, in Hz, middle A + DefaultFreq = 0.0 + // DefaultDuration - duration in milliseconds + DefaultDuration = 0 +) + +// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker). +func Beep(freq float64, duration int) error { + osa, err := exec.LookPath("osascript") + if err != nil { + // Output the only beep we can + _, err = os.Stdout.Write([]byte{7}) + return err + } + + cmd := exec.Command(osa, "-e", `beep`) + return cmd.Run() +} diff --git a/vendor/github.com/gen2brain/beeep/beep_js.go b/vendor/github.com/gen2brain/beeep/beep_js.go new file mode 100644 index 00000000..690f196a --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/beep_js.go @@ -0,0 +1,38 @@ +//go:build js +// +build js + +package beeep + +import ( + "syscall/js" +) + +var ( + // DefaultFreq - frequency, in Hz, middle A + DefaultFreq = 0.0 + // DefaultDuration - duration in milliseconds + DefaultDuration = 0 +) + +// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker). +func Beep(freq float64, duration int) (err error) { + defer func() { + e := recover() + + if e == nil { + return + } + + if e, ok := e.(*js.Error); ok { + err = e + } else { + panic(e) + } + }() + + a := js.Global().Get("document").Call("createElement", "audio") + a.Set("src", `data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=`) + a.Call("play") + + return +} diff --git a/vendor/github.com/gen2brain/beeep/beep_unix.go b/vendor/github.com/gen2brain/beeep/beep_unix.go new file mode 100644 index 00000000..8a496be9 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/beep_unix.go @@ -0,0 +1,139 @@ +//go:build linux || freebsd || netbsd || openbsd || illumos +// +build linux freebsd netbsd openbsd illumos + +package beeep + +import ( + "errors" + "os" + "syscall" + "time" + "unsafe" +) + +// Constants +const ( + // This number represents the fixed frequency of the original PC XT's timer chip, which is approximately 1.193 MHz. This number + // is divided with the desired frequency to obtain a counter value, that is subsequently fed into the timer chip, tied to the PC speaker. + clockTickRate = 1193180 + + // linux/kd.h, start sound generation (0 for off) + kiocsound = 0x4B2F + + // linux/input-event-codes.h + evSnd = 0x12 // Event type + sndTone = 0x02 // Sound +) + +var ( + // DefaultFreq - frequency, in Hz, middle A + DefaultFreq = 440.0 + // DefaultDuration - duration in milliseconds + DefaultDuration = 200 +) + +// inputEvent represents linux/input.h event structure. +type inputEvent struct { + Time syscall.Timeval // time in seconds since epoch at which event occurred + Type uint16 // event type + Code uint16 // event code related to the event type + Value int32 // event value related to the event type +} + +// ioctl system call manipulates the underlying device parameters of special files. +func ioctl(fd, name, data uintptr) error { + _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, name, data) + if e != 0 { + return e + } + + return nil +} + +// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker). +// +// On Linux it needs permission to access `/dev/tty0` or `/dev/input/by-path/platform-pcspkr-event-spkr` files for writing, +// and `pcspkr` module must be loaded. User must be in correct groups, usually `input` and/or `tty`. +// +// If it can not open device files, it will fallback to sending Bell character (https://en.wikipedia.org/wiki/Bell_character). +// For bell character in X11 terminals you can enable bell with `xset b on`. For console check `setterm` and `--blength` or `--bfreq` options. +// +// On macOS this just sends bell character. Enable `Audible bell` in Terminal --> Preferences --> Settings --> Advanced. +// +// On Windows it uses Beep function via syscall. +// +// On Web it plays hard coded beep sound. +func Beep(freq float64, duration int) error { + if freq == 0 { + freq = DefaultFreq + } else if freq > 20000 { + freq = 20000 + } else if freq < 0 { + freq = DefaultFreq + } + + if duration == 0 { + duration = DefaultDuration + } + + period := int(float64(clockTickRate) / freq) + + var evdev bool + + f, err := os.OpenFile("/dev/tty0", os.O_WRONLY, 0644) + if err != nil { + e := err + f, err = os.OpenFile("/dev/input/by-path/platform-pcspkr-event-spkr", os.O_WRONLY, 0644) + if err != nil { + e = errors.New("beeep: " + e.Error() + "; " + err.Error()) + + // Output the only beep we can + _, err = os.Stdout.Write([]byte{7}) + if err != nil { + return errors.New(e.Error() + "; " + err.Error()) + } + + return nil + } + + evdev = true + } + + defer f.Close() + + if evdev { // Use Linux evdev API + ev := inputEvent{} + ev.Type = evSnd + ev.Code = sndTone + ev.Value = int32(freq) + + d := *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev)) + + // Start beep + f.Write(d[:]) + + time.Sleep(time.Duration(duration) * time.Millisecond) + + ev.Value = 0 + d = *(*[unsafe.Sizeof(ev)]byte)(unsafe.Pointer(&ev)) + + // Stop beep + f.Write(d[:]) + } else { // Use ioctl + // Start beep + err = ioctl(f.Fd(), kiocsound, uintptr(period)) + if err != nil { + return err + } + + time.Sleep(time.Duration(duration) * time.Millisecond) + + // Stop beep + err = ioctl(f.Fd(), kiocsound, uintptr(0)) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/gen2brain/beeep/beep_unsupported.go b/vendor/github.com/gen2brain/beeep/beep_unsupported.go new file mode 100644 index 00000000..6d432722 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/beep_unsupported.go @@ -0,0 +1,16 @@ +//go:build !linux && !freebsd && !netbsd && !openbsd && !windows && !darwin && !illumos && !js +// +build !linux,!freebsd,!netbsd,!openbsd,!windows,!darwin,!illumos,!js + +package beeep + +var ( + // DefaultFreq - frequency, in Hz, middle A + DefaultFreq = 0.0 + // DefaultDuration - duration in milliseconds + DefaultDuration = 0 +) + +// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker). +func Beep(freq float64, duration int) error { + return ErrUnsupported +} diff --git a/vendor/github.com/gen2brain/beeep/beep_windows.go b/vendor/github.com/gen2brain/beeep/beep_windows.go new file mode 100644 index 00000000..ad213458 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/beep_windows.go @@ -0,0 +1,42 @@ +//go:build windows && !linux && !freebsd && !netbsd && !openbsd && !darwin && !js +// +build windows,!linux,!freebsd,!netbsd,!openbsd,!darwin,!js + +package beeep + +import ( + "syscall" +) + +var ( + // DefaultFreq - frequency, in Hz, middle A + DefaultFreq = 587.0 + // DefaultDuration - duration in milliseconds + DefaultDuration = 500 +) + +// Beep beeps the PC speaker (https://en.wikipedia.org/wiki/PC_speaker). +func Beep(freq float64, duration int) error { + if freq == 0 { + freq = DefaultFreq + } else if freq > 32767 { + freq = 32767 + } else if freq < 37 { + freq = DefaultFreq + } + + if duration == 0 { + duration = DefaultDuration + } + + kernel32, _ := syscall.LoadLibrary("kernel32.dll") + beep32, _ := syscall.GetProcAddress(kernel32, "Beep") + + defer syscall.FreeLibrary(kernel32) + + _, _, e := syscall.Syscall(uintptr(beep32), uintptr(2), uintptr(int(freq)), uintptr(duration), 0) + if e != 0 { + return e + } + + return nil +} diff --git a/vendor/github.com/gen2brain/beeep/notify_darwin.go b/vendor/github.com/gen2brain/beeep/notify_darwin.go new file mode 100644 index 00000000..b510af28 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/notify_darwin.go @@ -0,0 +1,23 @@ +//go:build darwin && !linux && !freebsd && !netbsd && !openbsd && !windows && !js +// +build darwin,!linux,!freebsd,!netbsd,!openbsd,!windows,!js + +package beeep + +import ( + "fmt" + "os/exec" +) + +// Notify sends desktop notification. +// +// On macOS this executes AppleScript with `osascript` binary. +func Notify(title, message, appIcon string) error { + osa, err := exec.LookPath("osascript") + if err != nil { + return err + } + + script := fmt.Sprintf("display notification %q with title %q", message, title) + cmd := exec.Command(osa, "-e", script) + return cmd.Run() +} diff --git a/vendor/github.com/gen2brain/beeep/notify_js.go b/vendor/github.com/gen2brain/beeep/notify_js.go new file mode 100644 index 00000000..f88d7fcc --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/notify_js.go @@ -0,0 +1,51 @@ +//go:build js +// +build js + +package beeep + +import ( + "syscall/js" +) + +// Notify sends desktop notification. +// +// On Web, in Firefox it just works, in Chrome you must call it from some "user gesture" like `onclick`, +// and you must use TLS certificate, it doesn't work with plain http. +func Notify(title, message, appIcon string) (err error) { + defer func() { + e := recover() + + if e == nil { + return + } + + if e, ok := e.(*js.Error); ok { + err = e + } else { + panic(e) + } + }() + + n := js.Global().Get("Notification") + + opts := js.Global().Get("Object").Invoke() + opts.Set("body", message) + opts.Set("icon", pathAbs(appIcon)) + + if n.Get("permission").String() == "granted" { + n.New(js.ValueOf(title), opts) + } else { + var f js.Func + f = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + if args[0].String() == "granted" { + n.New(js.ValueOf(title), opts) + } + f.Release() + return nil + }) + + n.Call("requestPermission", f) + } + + return +} diff --git a/vendor/github.com/gen2brain/beeep/notify_unix.go b/vendor/github.com/gen2brain/beeep/notify_unix.go new file mode 100644 index 00000000..04dffd6d --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/notify_unix.go @@ -0,0 +1,59 @@ +//go:build (linux && !nodbus) || (freebsd && !nodbus) || (netbsd && !nodbus) || (openbsd && !nodbus) +// +build linux,!nodbus freebsd,!nodbus netbsd,!nodbus openbsd,!nodbus + +package beeep + +import ( + "errors" + "os/exec" + + "github.com/godbus/dbus/v5" +) + +// Notify sends desktop notification. +// +// On Linux it tries to send notification via D-Bus and it will fallback to `notify-send` binary. +func Notify(title, message, appIcon string) error { + appIcon = pathAbs(appIcon) + + cmd := func() error { + send, err := exec.LookPath("sw-notify-send") + if err != nil { + send, err = exec.LookPath("notify-send") + if err != nil { + return err + } + } + + c := exec.Command(send, title, message, "-i", appIcon) + return c.Run() + } + + knotify := func() error { + send, err := exec.LookPath("kdialog") + if err != nil { + return err + } + c := exec.Command(send, "--title", title, "--passivepopup", message, "10", "--icon", appIcon) + return c.Run() + } + + conn, err := dbus.SessionBus() + if err != nil { + return cmd() + } + obj := conn.Object("org.freedesktop.Notifications", dbus.ObjectPath("/org/freedesktop/Notifications")) + + call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0), appIcon, title, message, []string{}, map[string]dbus.Variant{}, int32(-1)) + if call.Err != nil { + e := cmd() + if e != nil { + e := knotify() + if e != nil { + return errors.New("beeep: " + call.Err.Error() + "; " + e.Error()) + } + } + } + + return nil +} diff --git a/vendor/github.com/gen2brain/beeep/notify_unix_nodbus.go b/vendor/github.com/gen2brain/beeep/notify_unix_nodbus.go new file mode 100644 index 00000000..8daff291 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/notify_unix_nodbus.go @@ -0,0 +1,46 @@ +//go:build (linux && nodbus) || (freebsd && nodbus) || (netbsd && nodbus) || (openbsd && nodbus) || illumos +// +build linux,nodbus freebsd,nodbus netbsd,nodbus openbsd,nodbus illumos + +package beeep + +import ( + "errors" + "os/exec" +) + +// Notify sends desktop notification. +func Notify(title, message, appIcon string) error { + appIcon = pathAbs(appIcon) + + cmd := func() error { + send, err := exec.LookPath("sw-notify-send") + if err != nil { + send, err = exec.LookPath("notify-send") + if err != nil { + return err + } + } + + c := exec.Command(send, title, message, "-i", appIcon) + return c.Run() + } + + knotify := func() error { + send, err := exec.LookPath("kdialog") + if err != nil { + return err + } + c := exec.Command(send, "--title", title, "--passivepopup", message, "10", "--icon", appIcon) + return c.Run() + } + + err := cmd() + if err != nil { + e := knotify() + if e != nil { + return errors.New("beeep: " + err.Error() + "; " + e.Error()) + } + } + + return nil +} diff --git a/vendor/github.com/gen2brain/beeep/notify_unsupported.go b/vendor/github.com/gen2brain/beeep/notify_unsupported.go new file mode 100644 index 00000000..6ad401b6 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/notify_unsupported.go @@ -0,0 +1,9 @@ +//go:build !linux && !freebsd && !netbsd && !openbsd && !windows && !darwin && !illumos && !js +// +build !linux,!freebsd,!netbsd,!openbsd,!windows,!darwin,!illumos,!js + +package beeep + +// Notify sends desktop notification. +func Notify(title, message, appIcon string) error { + return ErrUnsupported +} diff --git a/vendor/github.com/gen2brain/beeep/notify_windows.go b/vendor/github.com/gen2brain/beeep/notify_windows.go new file mode 100644 index 00000000..fa145840 --- /dev/null +++ b/vendor/github.com/gen2brain/beeep/notify_windows.go @@ -0,0 +1,127 @@ +//go:build windows && !linux && !freebsd && !netbsd && !openbsd && !darwin && !js +// +build windows,!linux,!freebsd,!netbsd,!openbsd,!darwin,!js + +package beeep + +import ( + "bufio" + "bytes" + "errors" + "os/exec" + "strings" + "syscall" + "time" + + toast "github.com/go-toast/toast" + "github.com/tadvi/systray" + "golang.org/x/sys/windows/registry" +) + +var isWindows10 bool +var applicationID string + +func init() { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + if err != nil { + return + } + defer k.Close() + + maj, _, err := k.GetIntegerValue("CurrentMajorVersionNumber") + if err != nil { + return + } + + isWindows10 = maj == 10 + + if isWindows10 { + applicationID = appID() + } +} + +// Notify sends desktop notification. +func Notify(title, message, appIcon string) error { + if isWindows10 { + return toastNotify(title, message, appIcon) + } + + err := baloonNotify(title, message, appIcon, false) + if err != nil { + e := msgNotify(title, message) + if e != nil { + return errors.New("beeep: " + err.Error() + "; " + e.Error()) + } + } + + return nil + +} + +func msgNotify(title, message string) error { + msg, err := exec.LookPath("msg") + if err != nil { + return err + } + cmd := exec.Command(msg, "*", "/TIME:3", title+"\n\n"+message) + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + return cmd.Run() +} + +func baloonNotify(title, message, appIcon string, bigIcon bool) error { + tray, err := systray.New() + if err != nil { + return err + } + + err = tray.ShowCustom(pathAbs(appIcon), title) + if err != nil { + return err + } + + go func() { + go func() { + _ = tray.Run() + }() + time.Sleep(3 * time.Second) + _ = tray.Stop() + }() + + return tray.ShowMessage(title, message, bigIcon) +} + +func toastNotify(title, message, appIcon string) error { + notification := toastNotification(title, message, pathAbs(appIcon)) + return notification.Push() +} + +func toastNotification(title, message, appIcon string) toast.Notification { + return toast.Notification{ + AppID: applicationID, + Title: title, + Message: message, + Icon: appIcon, + } +} + +func appID() string { + defID := "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe" + cmd := exec.Command("powershell", "-NoProfile", "Get-StartApps") + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + out, err := cmd.Output() + if err != nil { + return defID + } + + scanner := bufio.NewScanner(bytes.NewReader(out)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.Contains(line, "powershell.exe") { + sp := strings.Split(line, " ") + if len(sp) > 0 { + return sp[len(sp)-1] + } + } + } + + return defID +} diff --git a/vendor/github.com/go-toast/toast/.gitignore b/vendor/github.com/go-toast/toast/.gitignore new file mode 100644 index 00000000..ecdc9e24 --- /dev/null +++ b/vendor/github.com/go-toast/toast/.gitignore @@ -0,0 +1,3 @@ +.idea/ +vendor/* +!vendor/vendor.json diff --git a/vendor/github.com/go-toast/toast/LICENSE b/vendor/github.com/go-toast/toast/LICENSE new file mode 100644 index 00000000..68b7294f --- /dev/null +++ b/vendor/github.com/go-toast/toast/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2016 Jacob Marshall + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/go-toast/toast/readme.md b/vendor/github.com/go-toast/toast/readme.md new file mode 100644 index 00000000..4dbc2074 --- /dev/null +++ b/vendor/github.com/go-toast/toast/readme.md @@ -0,0 +1,61 @@ +# Toast + +A go package for Windows 10 toast notifications. + +As seen in [jacobmarshall/pokevision-cli](https://github.com/jacobmarshall/pokevision-cli). + +## CLI + +As well as using go-toast within your Go projects, you can also utilise the CLI - for any of your projects. + +Download [64bit](https://go-toast-downloads.s3.amazonaws.com/v1/toast64.exe) or [32bit](https://go-toast-downloads.s3.amazonaws.com/v1/toast32.exe) + +```cmd +C:\Users\Example\Downloads\toast64.exe \ + --app-id "Example App" \ + --title "Hello World" \ + --message "Lorem ipsum dolor sit amet, consectetur adipiscing elit." \ + --icon "C:\Users\Example\Pictures\icon.png" \ + --audio "default" --loop \ + --duration "long" \ + --activation-arg "https://google.com" \ + --action "Open maps" --action-arg "bingmaps:?q=sushi" \ + --action "Open browser" --action-arg "http://..." +``` + +![CLI](./screenshot-cli.png) + +## Example + +```go +package main + +import ( + "log" + + "gopkg.in/toast.v1" +) + +func main() { + notification := toast.Notification{ + AppID: "Example App", + Title: "My notification", + Message: "Some message about how important something is...", + Icon: "go.png", // This file must exist (remove this line if it doesn't) + Actions: []toast.Action{ + {"protocol", "I'm a button", ""}, + {"protocol", "Me too!", ""}, + }, + } + err := notification.Push() + if err != nil { + log.Fatalln(err) + } +} +``` + +## Screenshots + +![Toast](./screenshot-toast.png) + +![Action centre](./screenshot-action-centre.png) diff --git a/vendor/github.com/go-toast/toast/screenshot-action-centre.png b/vendor/github.com/go-toast/toast/screenshot-action-centre.png new file mode 100644 index 00000000..e63917b2 Binary files /dev/null and b/vendor/github.com/go-toast/toast/screenshot-action-centre.png differ diff --git a/vendor/github.com/go-toast/toast/screenshot-cli.png b/vendor/github.com/go-toast/toast/screenshot-cli.png new file mode 100644 index 00000000..fc03c37e Binary files /dev/null and b/vendor/github.com/go-toast/toast/screenshot-cli.png differ diff --git a/vendor/github.com/go-toast/toast/screenshot-toast.png b/vendor/github.com/go-toast/toast/screenshot-toast.png new file mode 100644 index 00000000..93904061 Binary files /dev/null and b/vendor/github.com/go-toast/toast/screenshot-toast.png differ diff --git a/vendor/github.com/go-toast/toast/toast.go b/vendor/github.com/go-toast/toast/toast.go new file mode 100644 index 00000000..1bcba4bf --- /dev/null +++ b/vendor/github.com/go-toast/toast/toast.go @@ -0,0 +1,359 @@ +package toast + +import ( + "bytes" + "errors" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" + + "github.com/nu7hatch/gouuid" + "syscall" +) + +var toastTemplate *template.Template + +var ( + ErrorInvalidAudio error = errors.New("toast: invalid audio") + ErrorInvalidDuration = errors.New("toast: invalid duration") +) + +type toastAudio string + +const ( + Default toastAudio = "ms-winsoundevent:Notification.Default" + IM = "ms-winsoundevent:Notification.IM" + Mail = "ms-winsoundevent:Notification.Mail" + Reminder = "ms-winsoundevent:Notification.Reminder" + SMS = "ms-winsoundevent:Notification.SMS" + LoopingAlarm = "ms-winsoundevent:Notification.Looping.Alarm" + LoopingAlarm2 = "ms-winsoundevent:Notification.Looping.Alarm2" + LoopingAlarm3 = "ms-winsoundevent:Notification.Looping.Alarm3" + LoopingAlarm4 = "ms-winsoundevent:Notification.Looping.Alarm4" + LoopingAlarm5 = "ms-winsoundevent:Notification.Looping.Alarm5" + LoopingAlarm6 = "ms-winsoundevent:Notification.Looping.Alarm6" + LoopingAlarm7 = "ms-winsoundevent:Notification.Looping.Alarm7" + LoopingAlarm8 = "ms-winsoundevent:Notification.Looping.Alarm8" + LoopingAlarm9 = "ms-winsoundevent:Notification.Looping.Alarm9" + LoopingAlarm10 = "ms-winsoundevent:Notification.Looping.Alarm10" + LoopingCall = "ms-winsoundevent:Notification.Looping.Call" + LoopingCall2 = "ms-winsoundevent:Notification.Looping.Call2" + LoopingCall3 = "ms-winsoundevent:Notification.Looping.Call3" + LoopingCall4 = "ms-winsoundevent:Notification.Looping.Call4" + LoopingCall5 = "ms-winsoundevent:Notification.Looping.Call5" + LoopingCall6 = "ms-winsoundevent:Notification.Looping.Call6" + LoopingCall7 = "ms-winsoundevent:Notification.Looping.Call7" + LoopingCall8 = "ms-winsoundevent:Notification.Looping.Call8" + LoopingCall9 = "ms-winsoundevent:Notification.Looping.Call9" + LoopingCall10 = "ms-winsoundevent:Notification.Looping.Call10" + Silent = "silent" +) + +type toastDuration string + +const ( + Short toastDuration = "short" + Long = "long" +) + +func init() { + toastTemplate = template.New("toast") + toastTemplate.Parse(` +[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null +[Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null +[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null + +$APP_ID = '{{if .AppID}}{{.AppID}}{{else}}Windows App{{end}}' + +$template = @" + + + + {{if .Icon}} + + {{end}} + {{if .Title}} + + {{end}} + {{if .Message}} + + {{end}} + + + {{if ne .Audio "silent"}} + +"@ + +$xml = New-Object Windows.Data.Xml.Dom.XmlDocument +$xml.LoadXml($template) +$toast = New-Object Windows.UI.Notifications.ToastNotification $xml +[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($APP_ID).Show($toast) + `) +} + +// Notification +// +// The toast notification data. The following fields are strongly recommended; +// - AppID +// - Title +// +// If no toastAudio is provided, then the toast notification will be silent. +// You can set the toast to have a default audio by setting "Audio" to "toast.Default", or if your go app takes +// user-provided input for audio, call the "toast.Audio(name)" func. +// +// The AppID is shown beneath the toast message (in certain cases), and above the notification within the Action +// Center - and is used to group your notifications together. It is recommended that you provide a "pretty" +// name for your app, and not something like "com.example.MyApp". +// +// If no Title is provided, but a Message is, the message will display as the toast notification's title - +// which is a slightly different font style (heavier). +// +// The Icon should be an absolute path to the icon (as the toast is invoked from a temporary path on the user's +// system, not the working directory). +// +// If you would like the toast to call an external process/open a webpage, then you can set ActivationArguments +// to the uri you would like to trigger when the toast is clicked. For example: "https://google.com" would open +// the Google homepage when the user clicks the toast notification. +// By default, clicking the toast just hides/dismisses it. +// +// The following would show a notification to the user letting them know they received an email, and opens +// gmail.com when they click the notification. It also makes the Windows 10 "mail" sound effect. +// +// toast := toast.Notification{ +// AppID: "Google Mail", +// Title: email.Subject, +// Message: email.Preview, +// Icon: "C:/Program Files/Google Mail/icons/logo.png", +// ActivationArguments: "https://gmail.com", +// Audio: toast.Mail, +// } +// +// err := toast.Push() +type Notification struct { + // The name of your app. This value shows up in Windows 10's Action Centre, so make it + // something readable for your users. It can contain spaces, however special characters + // (eg. é) are not supported. + AppID string + + // The main title/heading for the toast notification. + Title string + + // The single/multi line message to display for the toast notification. + Message string + + // An optional path to an image on the OS to display to the left of the title & message. + Icon string + + // The type of notification level action (like toast.Action) + ActivationType string + + // The activation/action arguments (invoked when the user clicks the notification) + ActivationArguments string + + // Optional action buttons to display below the notification title & message. + Actions []Action + + // The audio to play when displaying the toast + Audio toastAudio + + // Whether to loop the audio (default false) + Loop bool + + // How long the toast should show up for (short/long) + Duration toastDuration +} + +// Action +// +// Defines an actionable button. +// See https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-adaptive-interactive-toasts for more info. +// +// Only protocol type action buttons are actually useful, as there's no way of receiving feedback from the +// user's choice. Examples of protocol type action buttons include: "bingmaps:?q=sushi" to open up Windows 10's +// maps app with a pre-populated search field set to "sushi". +// +// toast.Action{"protocol", "Open Maps", "bingmaps:?q=sushi"} +type Action struct { + Type string + Label string + Arguments string +} + +func (n *Notification) applyDefaults() { + if n.ActivationType == "" { + n.ActivationType = "protocol" + } + if n.Duration == "" { + n.Duration = Short + } + if n.Audio == "" { + n.Audio = Default + } +} + +func (n *Notification) buildXML() (string, error) { + var out bytes.Buffer + err := toastTemplate.Execute(&out, n) + if err != nil { + return "", err + } + return out.String(), nil +} + +// Builds the Windows PowerShell script & invokes it, causing the toast to display. +// +// Note: Running the PowerShell script is by far the slowest process here, and can take a few +// seconds in some cases. +// +// notification := toast.Notification{ +// AppID: "Example App", +// Title: "My notification", +// Message: "Some message about how important something is...", +// Icon: "go.png", +// Actions: []toast.Action{ +// {"protocol", "I'm a button", ""}, +// {"protocol", "Me too!", ""}, +// }, +// } +// err := notification.Push() +// if err != nil { +// log.Fatalln(err) +// } +func (n *Notification) Push() error { + n.applyDefaults() + xml, err := n.buildXML() + if err != nil { + return err + } + return invokeTemporaryScript(xml) +} + +// Returns a toastAudio given a user-provided input (useful for cli apps). +// +// If the "name" doesn't match, then the default toastAudio is returned, along with ErrorInvalidAudio. +// +// The following names are valid; +// - default +// - im +// - mail +// - reminder +// - sms +// - loopingalarm +// - loopimgalarm[2-10] +// - loopingcall +// - loopingcall[2-10] +// - silent +// +// Handle the error appropriately according to how your app should work. +func Audio(name string) (toastAudio, error) { + switch strings.ToLower(name) { + case "default": + return Default, nil + case "im": + return IM, nil + case "mail": + return Mail, nil + case "reminder": + return Reminder, nil + case "sms": + return SMS, nil + case "loopingalarm": + return LoopingAlarm, nil + case "loopingalarm2": + return LoopingAlarm2, nil + case "loopingalarm3": + return LoopingAlarm3, nil + case "loopingalarm4": + return LoopingAlarm4, nil + case "loopingalarm5": + return LoopingAlarm5, nil + case "loopingalarm6": + return LoopingAlarm6, nil + case "loopingalarm7": + return LoopingAlarm7, nil + case "loopingalarm8": + return LoopingAlarm8, nil + case "loopingalarm9": + return LoopingAlarm9, nil + case "loopingalarm10": + return LoopingAlarm10, nil + case "loopingcall": + return LoopingCall, nil + case "loopingcall2": + return LoopingCall2, nil + case "loopingcall3": + return LoopingCall3, nil + case "loopingcall4": + return LoopingCall4, nil + case "loopingcall5": + return LoopingCall5, nil + case "loopingcall6": + return LoopingCall6, nil + case "loopingcall7": + return LoopingCall7, nil + case "loopingcall8": + return LoopingCall8, nil + case "loopingcall9": + return LoopingCall9, nil + case "loopingcall10": + return LoopingCall10, nil + case "silent": + return Silent, nil + default: + return Default, ErrorInvalidAudio + } +} + +// Returns a toastDuration given a user-provided input (useful for cli apps). +// +// The default duration is short. If the "name" doesn't match, then the default toastDuration is returned, +// along with ErrorInvalidDuration. Most of the time "short" is the most appropriate for a toast notification, +// and Microsoft recommend not using "long", but it can be useful for important dialogs or looping sound toasts. +// +// The following names are valid; +// - short +// - long +// +// Handle the error appropriately according to how your app should work. +func Duration(name string) (toastDuration, error) { + switch strings.ToLower(name) { + case "short": + return Short, nil + case "long": + return Long, nil + default: + return Short, ErrorInvalidDuration + } +} + +func invokeTemporaryScript(content string) error { + id, _ := uuid.NewV4() + file := filepath.Join(os.TempDir(), id.String()+".ps1") + defer os.Remove(file) + bomUtf8 := []byte{0xEF, 0xBB, 0xBF} + out := append(bomUtf8, []byte(content)...) + err := ioutil.WriteFile(file, out, 0600) + if err != nil { + return err + } + cmd := exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file) + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + if err = cmd.Run(); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md b/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md new file mode 100644 index 00000000..c88f9b2b --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# How to Contribute + +## Getting Started + +- Fork the repository on GitHub +- Read the [README](README.markdown) for build and test instructions +- Play with the project, submit bugs, submit patches! + +## Contribution Flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where you want to base your work (usually master). +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Make sure the tests pass, and add any new tests as appropriate. +- Submit a pull request to the original repository. + +Thanks for your contributions! + +### Format of the Commit Message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that you can easily kill and +start for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +: + + + +