Class: WinToaster::Notifier
- Inherits:
-
Object
- Object
- WinToaster::Notifier
- Defined in:
- lib/win_toaster/notifier.rb
Overview
Builds a Windows toast notification and shows it by invoking Windows PowerShell (powershell.exe) from WSL. The toast XML and the PowerShell script are Base64-encoded so that arbitrary text (including Japanese and shell metacharacters) survives the WSL -> Windows boundary without any quoting or injection concerns.
Constant Summary collapse
- DEFAULT_APP_ID =
Default AppUserModelId. This is Windows PowerShell’s registered id, taken from the reference article. Toasts shown under it appear as coming from “Windows PowerShell”. Override via
app_idto use your own. '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'- POWERSHELL =
The Windows PowerShell executable, resolved from PATH inside WSL.
"powershell.exe"
Instance Attribute Summary collapse
-
#app_id ⇒ Object
readonly
Returns the value of attribute app_id.
-
#detail ⇒ Object
readonly
Returns the value of attribute detail.
-
#hero ⇒ Object
readonly
Returns the value of attribute hero.
-
#image ⇒ Object
readonly
Returns the value of attribute image.
-
#message ⇒ Object
readonly
Returns the value of attribute message.
-
#title ⇒ Object
readonly
Returns the value of attribute title.
Instance Method Summary collapse
-
#build_xml ⇒ Object
Builds the toast XML using the ToastGeneric template.
-
#deliver ⇒ Object
Builds and shows the toast.
-
#initialize(title:, message:, detail: nil, image: nil, hero: nil, app_id: DEFAULT_APP_ID) ⇒ Notifier
constructor
imageis shown as the small icon (appLogoOverride) andheroas the large banner image. -
#powershell_command ⇒ Object
The argv used to launch PowerShell with the script as an EncodedCommand.
-
#powershell_script ⇒ Object
The PowerShell script that decodes the XML and shows the toast.
Constructor Details
#initialize(title:, message:, detail: nil, image: nil, hero: nil, app_id: DEFAULT_APP_ID) ⇒ Notifier
image is shown as the small icon (appLogoOverride) and hero as the large banner image. Both take a file path; from WSL a Linux/WSL path (e.g. /mnt/c/… or /home/…) is accepted and converted to a Windows path automatically.
26 27 28 29 30 31 32 33 |
# File 'lib/win_toaster/notifier.rb', line 26 def initialize(title:, message:, detail: nil, image: nil, hero: nil, app_id: DEFAULT_APP_ID) @title = title @message = @detail = detail @image = image @hero = hero @app_id = app_id end |
Instance Attribute Details
#app_id ⇒ Object (readonly)
Returns the value of attribute app_id.
20 21 22 |
# File 'lib/win_toaster/notifier.rb', line 20 def app_id @app_id end |
#detail ⇒ Object (readonly)
Returns the value of attribute detail.
20 21 22 |
# File 'lib/win_toaster/notifier.rb', line 20 def detail @detail end |
#hero ⇒ Object (readonly)
Returns the value of attribute hero.
20 21 22 |
# File 'lib/win_toaster/notifier.rb', line 20 def hero @hero end |
#image ⇒ Object (readonly)
Returns the value of attribute image.
20 21 22 |
# File 'lib/win_toaster/notifier.rb', line 20 def image @image end |
#message ⇒ Object (readonly)
Returns the value of attribute message.
20 21 22 |
# File 'lib/win_toaster/notifier.rb', line 20 def @message end |
#title ⇒ Object (readonly)
Returns the value of attribute title.
20 21 22 |
# File 'lib/win_toaster/notifier.rb', line 20 def title @title end |
Instance Method Details
#build_xml ⇒ Object
Builds the toast XML using the ToastGeneric template. Text nodes are XML-escaped. The detail line and image nodes are omitted when nil.
37 38 39 40 41 42 |
# File 'lib/win_toaster/notifier.rb', line 37 def build_xml nodes = [title, , detail].compact.map { |t| "<text>#{escape_xml(t)}</text>" } nodes << image_node("appLogoOverride", image) if image nodes << image_node("hero", hero) if hero %(<toast><visual><binding template="ToastGeneric">#{nodes.join}</binding></visual></toast>) end |
#deliver ⇒ Object
Builds and shows the toast. Returns true on success and raises WinToaster::Error on any failure.
71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/win_toaster/notifier.rb', line 71 def deliver _stdout, stderr, status = Open3.capture3(*powershell_command) unless status.success? raise Error, "failed to show toast (exit #{status.exitstatus}): #{stderr.strip}" end true rescue Errno::ENOENT raise Error, "#{POWERSHELL} not found. win_toaster requires Windows PowerShell, " \ "so run it from WSL or from Ruby on Windows." end |
#powershell_command ⇒ Object
The argv used to launch PowerShell with the script as an EncodedCommand. Using -EncodedCommand avoids shell quoting issues and the UNC-path warning that -File triggers when run from a WSL working directory.
64 65 66 67 |
# File 'lib/win_toaster/notifier.rb', line 64 def powershell_command encoded = [powershell_script.encode("UTF-16LE")].pack("m0") [POWERSHELL, "-NoProfile", "-NonInteractive", "-EncodedCommand", encoded] end |
#powershell_script ⇒ Object
The PowerShell script that decodes the XML and shows the toast.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/win_toaster/notifier.rb', line 45 def powershell_script xml_b64 = [build_xml.encode("UTF-8")].pack("m0") app_id_literal = app_id.gsub("'", "''") <<~PS $ErrorActionPreference = 'Stop' [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, ContentType = WindowsRuntime] | Out-Null $xmlText = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('#{xml_b64}')) $xml = New-Object Windows.Data.Xml.Dom.XmlDocument $xml.LoadXml($xmlText) $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('#{app_id_literal}').Show($toast) PS end |