com.start-automating.scriptdeck.sdPlugin/WatchPath.@KeyDown,WillAppear.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
$invokeError = $null
$settings = $event.MessageData.payload.settings
if (-not $settings.Path) { return }


$eventName         = $settings.Path
$fileWatcherFilter = $settings.PathFilter
$eventSubscribers  = Get-EventSubscriber
$separator         = [IO.Path]::DirectorySeparatorChar
$eventName         = $ExecutionContext.SessionState.InvokeCommand.ExpandString($eventName)
# If the event name was not a path,
if (-not "$eventName".Contains("$separator")) {
    return # we are done.
}

# Split the path into parts
$pathParts = @($eventName.Split("$separator"))
# If the last part contained wildcards, it's a filter
$fileWatcherPath, $fileWatcherFilter = 
    if ($pathParts[-1] -match '[\*\?]') {
        ($pathParts[0..($pathParts.Length - 2)] -join $separator)
        $pathParts[-1]
    }
    elseif ($pathParts[-1] -match '\.') {
        ($pathParts[0..($pathParts.Length - 2)] -join $separator)
        $pathParts[-1]
    }
    else {
        $pathParts -join $separator
        $fileWatcherFilter
    }

# Find any previous event subscriptions from this button.
$toRemove = $eventSubscribers | 
    Where-Object {
        $_.SourceObject -is [IO.FileSystemWatcher] -and 
        $_.SourceObject.SourceEvent.MessageData.Context -eq $event.MessageData.Context
    }

# And remove them.
$toRemove |
    Unregister-Event

# Create a FileSystemWatcher
$fileSystemWatcher = [IO.FileSystemWatcher]::new()
$fileSystemWatcher | Add-Member SourceEvent $event -Force
$fileSystemWatcher.Path = $fileWatcherPath
# set the filter,
if ($fileWatcherFilter) {
    $fileSystemWatcher.Filter = $fileWatcherFilter
}
# (recurse if they said they want to),
if ($settings.Recurse) {
    $fileSystemWatcher.IncludeSubdirectories = $true
}
# set up the notification files,
$NotifyFilter = $settings.FileChangeEvent -as [IO.NotifyFilters]
if (-not $NotifyFilter) { $NotifyFilter = 'LastWrite'}
$fileSystemWatcher.NotifyFilter = $NotifyFilter
# predetermine the event id
$fileChangedSourceId = "FileChanged_$($event.MessageData.Context)"
# and create a handler to propagate the event.
$propagateFileEvent = "
    `$fileChangedContext = '$($event.MessageData.Context)'
    `$fileChangedSourceId = '$fileChangedSourceId'
"
 + {
    # Why not subscribe directly?
    # FileSystemWatcher often sends multiple events,
    # so we'll guard against that by propagating only the first in a timeframe.
    if ($script:LastFileEvent) {
        if (($event.TimeGenerated - $script:LastFileEvent.TimeGenerated) -gt '00:00:00.25') {
            $script:LastFileEvent = $null
        }
    }

    # If the last event was from the same context, return
    if ($script:LastFileEvent -and $event.SourceArgs[1].FullPath -eq $script:LastFileEvent.SourceArgs[1].FullPath -and
        ($script:LastFileEvent.MessageData.Context -eq $event.MessageData.Context)) {
        return
    }
    
    $script:LastFileEvent = $event

    # Create an object for the file change
    $eventMessageData = [Ordered]@{
        FilePath = $event.SourceArgs[1].FullPath
    }
    # and get the subscriber.
    $sourceSubscriber = Get-EventSubscriber | 
        Where-Object { $_.SourceObject.SourceEvent.MessageData.Context -eq $fileChangedContext }            
        
    # The subscriber has the source event, which provides additional context
    foreach ($prop in $sourceSubscriber.SourceObject.SourceEvent.MessageData.psobject.properties) {
        # so add that to the message.
        $eventMessageData[$prop.Name] = $prop.Value
    }

    $eventMessageData = [PSCustomObject]$eventMessageData
    # and generate the file event.
    New-Event -SourceIdentifier $fileChangedSourceId -MessageData $eventMessageData
}

# Now, we need to unregister all of the events subscribers related to this button.
Get-EventSubscriber -SourceIdentifier $fileChangedSourceId -ErrorAction Ignore | Unregister-Event

# and register our changed event.
Register-ObjectEvent -EventName Changed -Action (
    [scriptblock]::Create($propagateFileEvent)
) -InputObject $fileSystemWatcher

# If there was a specific handler
if ($settings.Handler) {
    try {
        # Register for it
        $createdHandler = [ScriptBlock]::Create($settings.Handler)
        Register-EngineEvent -SourceIdentifier $fileChangedSourceId -Action $createdHandler
    } catch {
        $ex = $_
        Send-StreamDeck -LogMessage "WatchEvent@ScriptDeck: Could not create handler: $($ex | Out-String)"
        Send-StreamDeck -ShowAlert -Context $event.MessageData.Context
    }
}

# If they want to automatically set images
if ($settings.AutoSetImage) {
    # register a handler that
    Register-EngineEvent -SourceIdentifier $fileChangedSourceId -Action {
        $eventFile = $event.MessageData.FilePath -as [IO.FileInfo]
        # will set the image if the event file is an image file.
        if ($eventFile.Extension -in '.gif', '.svg' ,'.png', '.jpg', '.jpeg', '.bmp') {
            Send-StreamDeck -ImagePath $eventFile.Fullname -Context $event.MessageData.Context
        }
    }
}

# If they want to automatically set the title,
if ($settings.AutoSetTitle) {
    # register a handler
    Register-EngineEvent -SourceIdentifier $fileChangedSourceId -Action {
        $eventFile = $event.MessageData.FilePath -as [IO.FileInfo]
        # that sets the title to the name of the file.
        Send-StreamDeck -Title $eventFile.Name -Context $event.MessageData.Context                
    }
}

# If they want to send a message
if ($settings.SendMessage) {
    # write this event to disk
    $context = $event.MessageData.context
    $messageDataJson = $event.MessageData | ConvertTo-Json -Depth 10
    $messagePath = 
        Join-Path $fileWatcherPath "$(@($event.SourceIdentifier -split '\.' -ne '')[-1]).$context.json"
    $messageDataJson | Set-Content $messagePath
}

# If they want to RunScripts
if ($settings.RunScripts) {
    # create a handler that will run any .ps1
    Register-EngineEvent -SourceIdentifier $fileChangedSourceId -Action {
        $eventFile = $event.MessageData.FilePath -as [IO.FileInfo]        
        if ($eventFile.Extension -ne '.ps1') { return }
        . $eventFile.FullName
    }
}
    
if ($invokeError) {
    Send-StreamDeck -ShowAlert -Context $event.MessageData.Context
} else {
    Send-StreamDeck -ShowOK -Context $event.MessageData.Context
}