go-toast

Send toast notifications in Windows
Log | Files | Refs | README | LICENSE

toast.go (7764B)


      1 // Package toast wraps the lower-level wintoast api and provides an easy way
      2 // to send and respond to toast notifications on Windows.
      3 //
      4 // First, setup your AppData vis SetAppData function. This will install your
      5 // application metadata into the Windows Registry.
      6 //
      7 // Then, if you want in-process callback to be invoked upon user interaction,
      8 // invoke SetActivationCallback.
      9 //
     10 // Finally, generate your notification by instantiation a toast.Notification
     11 // and pushing it with Push method.
     12 package toast
     13 
     14 import (
     15 	"bytes"
     16 
     17 	"git.sr.ht/~jackmordaunt/go-toast/v2/tmpl"
     18 	"git.sr.ht/~jackmordaunt/go-toast/v2/wintoast"
     19 )
     20 
     21 // Notification
     22 //
     23 // The toast notification data. The following fields are strongly recommended;
     24 //   - AppID
     25 //   - Title
     26 //
     27 // If no toastAudio is provided, then the toast notification will be silent.
     28 //
     29 // The AppID is shown beneath the toast message (in certain cases), and above the notification within the Action
     30 // Center - and is used to group your notifications together. It is recommended that you provide a "pretty"
     31 // name for your app, and not something like "com.example.MyApp". It can be ellided if the value has already
     32 // been set via SetAppData.
     33 //
     34 // If no Title is provided, but a Body is, the body will display as the toast notification's title -
     35 // which is a slightly different font style (heavier).
     36 //
     37 // The Icon should be an absolute path to the icon (as the toast is invoked from a temporary path on the user's
     38 // system, not the working directory).
     39 //
     40 // If you would like the toast to call an external process/open a webpage, then you can set ActivationArguments
     41 // to the uri you would like to trigger when the toast is clicked. For example: "https://google.com" would open
     42 // the Google homepage when the user clicks the toast notification.
     43 // By default, clicking the toast just hides/dismisses it.
     44 //
     45 // The following would show a notification to the user letting them know they received an email, and opens
     46 // gmail.com when they click the notification. It also makes the Windows 10 "mail" sound effect.
     47 //
     48 //	toast := toast.Notification{
     49 //	    AppID:               "Google Mail",
     50 //	    Title:               email.Subject,
     51 //	    Message:             email.Preview,
     52 //	    Icon:                "C:/Program Files/Google Mail/icons/logo.png",
     53 //	    ActivationArguments: "https://gmail.com",
     54 //	    Audio:               toast.Mail,
     55 //	}
     56 //
     57 //	err := toast.Push()
     58 type Notification struct {
     59 	// The name of your app. This value shows up in Windows Action Centre, so make it
     60 	// something readable for your users.
     61 	AppID string
     62 
     63 	// The main title/heading for the toast notification.
     64 	Title string
     65 
     66 	// The single/multi line message to display for the toast notification.
     67 	Body string
     68 
     69 	// An optional path to an image on the OS to display to the left of the title & message.
     70 	Icon string
     71 
     72 	// An optional crop style for the Icon.
     73 	IconCrop CropStyle
     74 
     75 	// An optional path to an image to display as a bold hero image.
     76 	HeroIcon string
     77 
     78 	// A color to show as the icon background.
     79 	IconBackgroundColor string
     80 
     81 	// Action to take when the notification is as a whole activated.
     82 	ActivationType ActivationType
     83 
     84 	// The activation/action arguments (invoked when the user clicks the notification).
     85 	// This is returned to the callback when activated.
     86 	ActivationArguments string
     87 
     88 	// Optional text input to display before the actions.
     89 	Inputs []Input
     90 
     91 	// Optional action buttons to display below the notification title & message.
     92 	Actions []Action
     93 
     94 	// The audio to play when displaying the toast
     95 	Audio toastAudio
     96 
     97 	// Whether to loop the audio (default false).
     98 	Loop bool
     99 
    100 	// How long the toast should show up for (short/long).
    101 	Duration toastDuration
    102 
    103 	// This is an absolute path to an executable that will launched by the
    104 	// Windows Runtime when the COM server is not running. This executable must be able
    105 	// to handle the -Embedding flag that Windows invokes it with.
    106 	ActivationExe string
    107 }
    108 
    109 // CropStyle specifies the hint-crop attribute for an image.
    110 type CropStyle = string
    111 
    112 const (
    113 	CropStyleEmpty  CropStyle = ""
    114 	CropStyleSquare CropStyle = "square"
    115 	CropStyleCircle CropStyle = "circle"
    116 )
    117 
    118 // UserData contains user supplied data from the notification, such as text input
    119 // or a selection.
    120 type UserData = wintoast.UserData
    121 
    122 // Input
    123 //
    124 // Defines an input element, generally a text input.
    125 // See  https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-input for more info.
    126 //
    127 // Inputs are by default textual, however if selections are supplied the input will be rendered
    128 // as a select input.
    129 type Input struct {
    130 	ID          string
    131 	Title       string
    132 	Placeholder string
    133 	Selections  []InputSelection
    134 }
    135 
    136 // InputSelection
    137 //
    138 // Defines an input selection for use with select inputs.
    139 // See https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-selection for more info.
    140 type InputSelection struct {
    141 	ID      string
    142 	Content string
    143 }
    144 
    145 // Action
    146 //
    147 // Defines an actionable button.
    148 // See https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-adaptive-interactive-toasts for more info.
    149 //
    150 //	toast.Action{toast.Protocol, "Open Maps", "bingmaps:?q=sushi"}
    151 //
    152 // TODO(jfm): we can likely support an activation callback directly in the Action.
    153 type Action struct {
    154 	Type      ActivationType
    155 	Content   string
    156 	Arguments string
    157 	InputID   string // optional ID of any related input, affects styling.
    158 }
    159 
    160 // Push the notification to the Windows Runtime via the COM API.
    161 // Ensure [SetAppData] has been called prior to pushing notifications.
    162 //
    163 //	notification := toast.Notification{
    164 //	    AppID: "Example App",
    165 //	    Title: "My notification",
    166 //	    Message: "Some message about how important something is...",
    167 //	    Icon: "go.png",
    168 //	    Actions: []toast.Action{
    169 //	        {"protocol", "I'm a button", ""},
    170 //	        {"protocol", "Me too!", ""},
    171 //	    },
    172 //	}
    173 //	err := notification.Push()
    174 //	if err != nil {
    175 //	    log.Fatalln(err)
    176 //	}
    177 func (n *Notification) Push() error {
    178 	n.applyDefaults()
    179 	xml, err := n.buildXML()
    180 	if err != nil {
    181 		return err
    182 	}
    183 	return wintoast.Push(n.AppID, xml, wintoast.PowershellFallback)
    184 }
    185 
    186 func (n *Notification) applyDefaults() {
    187 	if n.ActivationType == "" {
    188 		n.ActivationType = Foreground
    189 	}
    190 	if n.Duration == "" {
    191 		n.Duration = Short
    192 	}
    193 	if n.Audio == "" {
    194 		n.Audio = Default
    195 	}
    196 }
    197 
    198 func (n *Notification) buildXML() (string, error) {
    199 	var out bytes.Buffer
    200 	err := tmpl.XMLTemplate.Execute(&out, n)
    201 	if err != nil {
    202 		return "", err
    203 	}
    204 	return out.String(), nil
    205 }
    206 
    207 // SetActivationCallback sets the global activation callback.
    208 //
    209 // The first argument contains application defined data (embedded within the xml),
    210 // which is how the callback knows which part of the toast was activated.
    211 // Argument data is defined by `toast.Action.Arguments` on the notification.
    212 //
    213 // The second argument contains user defined data (input/selected by user).
    214 // All elements of user input will be supplied here, even if the value is empty.
    215 // User inputs correspond to all `toast.Input`s defined on the notification.
    216 //
    217 // This function will be invoked when a toast notification is interacted with.
    218 //
    219 // This will do nothing if the the powershell fallback is in-effect.
    220 func SetActivationCallback(cb func(args string, data []UserData)) {
    221 	wintoast.SetActivationCallback(func(appUserModelId, invokedArgs string, userData []wintoast.UserData) {
    222 		cb(invokedArgs, userData)
    223 	})
    224 }
    225 
    226 type AppData = wintoast.AppData
    227 
    228 // SetAppData sets application metadata in the Windows Registry.
    229 // This is required to display the application name, as well as any branding.
    230 // Registry is global state, hence it makes sense to set it global.
    231 func SetAppData(data AppData) error {
    232 	return wintoast.SetAppData(data)
    233 }