├── .github ├── CONTRIBUTING.md └── FUNDING.yml ├── .gitignore ├── LICENSE ├── Media ├── Fields.png ├── FindMessage.png ├── FindMessageSelect.png ├── MultiAttachments.png ├── RichMessage.png └── SimpleMessage.png ├── PSSlack ├── PSSlack.Format.ps1xml ├── PSSlack.psd1 ├── PSSlack.psm1 ├── Private │ ├── Add-ObjectDetail.ps1 │ ├── Color-ToNumber.ps1 │ ├── ConvertFrom-UnixTime.ps1 │ ├── Get-PSSlackConfigPath.ps1 │ ├── Get-PropertyOrder.ps1 │ ├── Get-SlackUserFromID.ps1 │ ├── Get-UnixTime.ps1 │ ├── Parse-SlackAuth.ps1 │ ├── Parse-SlackChannel.ps1 │ ├── Parse-SlackError.ps1 │ ├── Parse-SlackFile.ps1 │ ├── Parse-SlackGroup.ps1 │ ├── Parse-SlackMessage.ps1 │ ├── Parse-SlackReminder.ps1 │ ├── Parse-SlackTeam.ps1 │ ├── Parse-SlackUser.ps1 │ ├── Parse-SlackUserGroup.ps1 │ ├── Remove-SensitiveData.ps1 │ └── Test-IsWindows.ps1 ├── Public │ ├── Find-SlackMessage.ps1 │ ├── Get-PSSlackConfig.ps1 │ ├── Get-SlackAuth.ps1 │ ├── Get-SlackChannel.ps1 │ ├── Get-SlackFileInfo.ps1 │ ├── Get-SlackGroup.ps1 │ ├── Get-SlackGroupHistory.ps1 │ ├── Get-SlackHistory.ps1 │ ├── Get-SlackReminder.ps1 │ ├── Get-SlackTeam.ps1 │ ├── Get-SlackUser.ps1 │ ├── Get-SlackUserGroup.ps1 │ ├── Get-SlackUserMap.ps1 │ ├── New-SlackAction.ps1 │ ├── New-SlackActionConfirmation.ps1 │ ├── New-SlackActionOption.ps1 │ ├── New-SlackField.ps1 │ ├── New-SlackMessage.ps1 │ ├── New-SlackMessageAttachment.ps1 │ ├── New-SlackReminder.ps1 │ ├── Remove-SlackFile.ps1 │ ├── Remove-SlackMessage.ps1 │ ├── Remove-SlackReminder.ps1 │ ├── Send-SlackAPI.ps1 │ ├── Send-SlackFile.ps1 │ ├── Send-SlackMessage.ps1 │ ├── Set-PSSlackConfig.ps1 │ ├── Set-SlackReminderComplete.ps1 │ ├── Test-SlackApi.ps1 │ └── Test-SlackAuth.ps1 └── en-US │ └── about_PSSlack.help.txt ├── README.md ├── Send-SlackChannelInvite.ps1 ├── Tests └── PSSlack.Tests.ps1 ├── appveyor.yml ├── build.ps1 ├── deploy.psdeploy.ps1 └── psake.ps1 /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Contributions to PSSlack would be awesome! Below are some guidelines to get you started 4 | 5 | ## Getting Started 6 | 7 | * Make sure you have a [GitHub account](https://github.com/signup/free) 8 | * Submit a new issue, assuming one does not already exist 9 | * Clearly describe the issue including steps to reproduce when it is a bug 10 | * Make sure you fill in the earliest version that you know has the issue 11 | * Fork the repository on GitHub 12 | 13 | ## Suggesting Enhancements 14 | 15 | Is anything missing from PSSlack? Is there anything that could be done to improve it, including documentation? 16 | 17 | Helpful things to include: 18 | 19 | * A brief description of what could be changed, and why this might be helpful 20 | * If possible, a brief mock-up showing how the enhancement would look (e.g. command and output) 21 | 22 | ## Making Changes 23 | 24 | * From your fork of the repository, create a topic branch where work on your change will take place 25 | * To quickly create a topic branch based on master; `git checkout -b my_contribution master`. Please avoid working directly on the `master` branch 26 | * Make commits of logical units 27 | * Check for unnecessary whitespace with `git diff --check` before committing 28 | * Please follow the prevailing code conventions in the repository. Differences in style make the code harder to understand for everyone 29 | * Try to follow [commit messages like these](https://chris.beams.io/posts/git-commit/) 30 | * Try to include all the necessary Pester tests for your changes 31 | * Run _all_ PESTER tests in the module to assure nothing else was accidentally broken (AppVeyor does this automatically, but running locally can save you time) 32 | 33 | ### PSSlack-specific Contribution Tips 34 | 35 | #### Adding a new command 36 | 37 | Depend on what command you add, there are a few files you might consider changing: 38 | 39 | * Add your command. E.g. `PSSlack\Public\Get-SlackSomething.ps1`. [Team example](https://github.com/RamblingCookieMonster/PSSlack/blob/master/PSSlack/Public/Get-SlackTeam.ps1) 40 | * Write a private command to parse the output. E.g. `PSSlack\Private\Parse-SlackSomething.ps1`. [Team example](https://github.com/RamblingCookieMonster/PSSlack/blob/master/PSSlack/Private/Parse-SlackTeam.ps1) 41 | * Add a type definition to make the output more readable by default. `PSSlack\PSSlack.Format.ps1xml`. Include this type name in your `Parse-SlackSomething` output ([Team example](https://github.com/RamblingCookieMonster/PSSlack/blob/master/PSSlack/Private/Parse-SlackTeam.ps1#L9)) 42 | * Add your function name to `PSSlack\PSSlack.psd1` 43 | 44 | Thanks to [Kanji](https://github.com/kanjibates) for the `SlackTeam` examples above! You can see his pull request [here](https://github.com/RamblingCookieMonster/PSSlack/pull/39). 45 | 46 | ## Documentation 47 | 48 | Chances are the documentation is out of date, and missing important bits - pull requests welcome! 49 | 50 | ## Submitting Changes 51 | 52 | * Push your changes to a topic branch in your fork of the repository (e.g. `git push origin my_contribution`) 53 | * Submit a pull request to the main repository 54 | * Once the pull request has been reviewed and accepted, it will be merged with the master branch. 55 | * Celebrate 56 | 57 | ## Additional Resources 58 | 59 | * [General GitHub documentation](https://help.github.com/) 60 | * [GitHub forking documentation](https://guides.github.com/activities/forking/) 61 | * [GitHub pull request documentation](https://help.github.com/send-pull-requests/) 62 | * [GitHub Flow guide](https://guides.github.com/introduction/flow/) 63 | * [GitHub's guide to contributing to open source projects](https://guides.github.com/activities/contributing-to-open-source/) 64 | 65 | Thanks for Brandon Olin for [most of this guide](https://github.com/poshbotio/PoshBot/blob/master/.github/CONTRIBUTING.md)! -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ramblingcookiemonster] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Manifest.xml 2 | **PSSlack.xml 3 | **Send-SlackUpdate.ps1 4 | PSSlack/X11ColorList.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Warren F. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Media/Fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamblingCookieMonster/PSSlack/1c9d6c0b810803fbf9a1c666f299ccd90e3f5bee/Media/Fields.png -------------------------------------------------------------------------------- /Media/FindMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamblingCookieMonster/PSSlack/1c9d6c0b810803fbf9a1c666f299ccd90e3f5bee/Media/FindMessage.png -------------------------------------------------------------------------------- /Media/FindMessageSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamblingCookieMonster/PSSlack/1c9d6c0b810803fbf9a1c666f299ccd90e3f5bee/Media/FindMessageSelect.png -------------------------------------------------------------------------------- /Media/MultiAttachments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamblingCookieMonster/PSSlack/1c9d6c0b810803fbf9a1c666f299ccd90e3f5bee/Media/MultiAttachments.png -------------------------------------------------------------------------------- /Media/RichMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamblingCookieMonster/PSSlack/1c9d6c0b810803fbf9a1c666f299ccd90e3f5bee/Media/RichMessage.png -------------------------------------------------------------------------------- /Media/SimpleMessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamblingCookieMonster/PSSlack/1c9d6c0b810803fbf9a1c666f299ccd90e3f5bee/Media/SimpleMessage.png -------------------------------------------------------------------------------- /PSSlack/PSSlack.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | 3 | # Script module or binary module file associated with this manifest. 4 | RootModule = 'PSSlack.psm1' 5 | 6 | # Version number of this module. 7 | ModuleVersion = '1.0.0' 8 | 9 | # ID used to uniquely identify this module 10 | GUID = 'fb0a1f73-e16c-4829-b2a7-4fc8d7bed545' 11 | 12 | # Author of this module 13 | Author = 'Warren Frame' 14 | 15 | # Copyright statement for this module 16 | Copyright = '(c) 2018 Warren F. All rights reserved.' 17 | 18 | # Description of the functionality provided by this module 19 | Description = 'PowerShell module for the Slack API' 20 | 21 | # Minimum version of the Windows PowerShell engine required by this module 22 | PowerShellVersion = '3.0' 23 | 24 | # Format files (.ps1xml) to be loaded when importing this module 25 | FormatsToProcess = 'PSSlack.Format.ps1xml' 26 | 27 | # Functions to export from this module 28 | FunctionsToExport = '*' 29 | 30 | # Cmdlets to export from this module 31 | CmdletsToExport = '*' 32 | 33 | # Variables to export from this module 34 | VariablesToExport = '_PSSlackColorMap' 35 | 36 | # Aliases to export from this module 37 | AliasesToExport = '*' 38 | 39 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 40 | PrivateData = @{ 41 | 42 | PSData = @{ 43 | 44 | # Tags applied to this module. These help with module discovery in online galleries. 45 | Tags = @('Slack', 'Chat', 'Message', 'Messaging', 'ChatOps') 46 | 47 | # A URL to the license for this module. 48 | LicenseUri = 'https://github.com/RamblingCookieMonster/PSSlack/blob/master/LICENSE' 49 | 50 | # A URL to the main website for this project. 51 | ProjectUri = 'https://github.com/RamblingCookieMonster/PSSlack/' 52 | 53 | # ReleaseNotes of this module 54 | ReleaseNotes = 'Switched from channels.list to conversations.list, thanks to @DWOF!' 55 | 56 | } # End of PSData hashtable 57 | 58 | } # End of PrivateData hashtable 59 | } 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /PSSlack/PSSlack.psm1: -------------------------------------------------------------------------------- 1 | #Get public and private function definition files. 2 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) 3 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) 4 | $ModuleRoot = $PSScriptRoot 5 | 6 | # Import System.Drawing Assembly 7 | Try { 8 | Add-Type -Assembly System.Drawing 9 | } 10 | Catch { 11 | Write-Error -Message "Failed to import System.Drawing assembly: $_" 12 | } 13 | 14 | #Dot source the files 15 | Foreach($import in @($Public + $Private)) 16 | { 17 | Try 18 | { 19 | . $import.fullname 20 | } 21 | Catch 22 | { 23 | Write-Error -Message "Failed to import function $($import.fullname): $_" 24 | } 25 | } 26 | 27 | #Create / Read config 28 | $script:_PSSlackXmlpath = Get-PSSlackConfigPath 29 | if(-not (Test-Path -Path $script:_PSSlackXmlpath -ErrorAction SilentlyContinue)) 30 | { 31 | Try 32 | { 33 | Write-Warning "Did not find config file $($script:_PSSlackXmlpath), attempting to create" 34 | [pscustomobject]@{ 35 | Uri = $null 36 | Token = $null 37 | ArchiveUri = $null 38 | Proxy = $null 39 | MapUser = $null 40 | ForceVerbose = $False 41 | } | Export-Clixml -Path $($script:_PSSlackXmlpath) -Force -ErrorAction Stop 42 | } 43 | Catch 44 | { 45 | Write-Warning "Failed to create config file $($script:_PSSlackXmlpath): $_" 46 | } 47 | } 48 | 49 | #Initialize the config variable. I know, I know... 50 | Try 51 | { 52 | #Import the config 53 | $PSSlack = $null 54 | $PSSlack = Get-PSSlackConfig -Source PSSlack.xml -ErrorAction Stop 55 | } 56 | Catch 57 | { 58 | Write-Warning "Error importing PSSlack config: $_" 59 | } 60 | 61 | $_PSSlackUserMap = @{} 62 | if($PSSlack.MapUser){ 63 | $_PSSlackUserMap = Get-SlackUserMap -Update 64 | } 65 | 66 | # Create a hashtable for use with the "leaky bucket" rate-limiting algorithm. (Some of Slack's API calls will fail if you request them too quickly.) 67 | # https://en.wikipedia.org/wiki/Leaky_bucket 68 | $Script:APIRateBuckets = @{} 69 | 70 | # Import some color defs. 71 | $_PSSlackColorMap = @{ 72 | aliceblue = "#F0F8FF" 73 | antiquewhite = "#FAEBD7" 74 | aqua = "#00FFFF" 75 | aquamarine = "#7FFFD4" 76 | azure = "#F0FFFF" 77 | beige = "#F5F5DC" 78 | bisque = "#FFE4C4" 79 | black = "#000000" 80 | blanchedalmond = "#FFEBCD" 81 | blue = "#0000FF" 82 | blueviolet = "#8A2BE2" 83 | brown = "#A52A2A" 84 | burlywood = "#DEB887" 85 | cadetblue = "#5F9EA0" 86 | chartreuse = "#7FFF00" 87 | chocolate = "#D2691E" 88 | coral = "#FF7F50" 89 | cornflowerblue = "#6495ED" 90 | cornsilk = "#FFF8DC" 91 | crimson = "#DC143C" 92 | darkblue = "#00008B" 93 | darkcyan = "#008B8B" 94 | darkgoldenrod = "#B8860B" 95 | darkgray = "#A9A9A9" 96 | darkgreen = "#006400" 97 | darkkhaki = "#BDB76B" 98 | darkmagenta = "#8B008B" 99 | darkolivegreen = "#556B2F" 100 | darkorange = "#FF8C00" 101 | darkorchid = "#9932CC" 102 | darkred = "#8B0000" 103 | darksalmon = "#E9967A" 104 | darkseagreen = "#8FBC8F" 105 | darkslateblue = "#483D8B" 106 | darkslategray = "#2F4F4F" 107 | darkturquoise = "#00CED1" 108 | darkviolet = "#9400D3" 109 | deeppink = "#FF1493" 110 | deepskyblue = "#00BFFF" 111 | dimgray = "#696969" 112 | dodgerblue = "#1E90FF" 113 | firebrick = "#B22222" 114 | floralwhite = "#FFFAF0" 115 | forestgreen = "#228B22" 116 | fuchsia = "#FF00FF" 117 | gainsboro = "#DCDCDC" 118 | ghostwhite = "#F8F8FF" 119 | gold = "#FFD700" 120 | goldenrod = "#DAA520" 121 | gray = "#808080" 122 | green = "#008000" 123 | greenyellow = "#ADFF2F" 124 | honeydew = "#F0FFF0" 125 | hotpink = "#FF69B4" 126 | indianred = "#CD5C5C" 127 | indigo = "#4B0082" 128 | ivory = "#FFFFF0" 129 | khaki = "#F0E68C" 130 | lavender = "#E6E6FA" 131 | lavenderblush = "#FFF0F5" 132 | lawngreen = "#7CFC00" 133 | lemonchiffon = "#FFFACD" 134 | lightblue = "#ADD8E6" 135 | lightcoral = "#F08080" 136 | lightcyan = "#E0FFFF" 137 | lightgoldenrodyellow = "#FAFAD2" 138 | lightgreen = "#90EE90" 139 | lightgrey = "#D3D3D3" 140 | lightpink = "#FFB6C1" 141 | lightsalmon = "#FFA07A" 142 | lightseagreen = "#20B2AA" 143 | lightskyblue = "#87CEFA" 144 | lightslategray = "#778899" 145 | lightsteelblue = "#B0C4DE" 146 | lightyellow = "#FFFFE0" 147 | lime = "#00FF00" 148 | limegreen = "#32CD32" 149 | linen = "#FAF0E6" 150 | maroon = "#800000" 151 | mediumaquamarine = "#66CDAA" 152 | mediumblue = "#0000CD" 153 | mediumorchid = "#BA55D3" 154 | mediumpurple = "#9370DB" 155 | mediumseagreen = "#3CB371" 156 | mediumslateblue = "#7B68EE" 157 | mediumspringgreen = "#00FA9A" 158 | mediumturquoise = "#48D1CC" 159 | mediumvioletred = "#C71585" 160 | midnightblue = "#191970" 161 | mintcream = "#F5FFFA" 162 | mistyrose = "#FFE4E1" 163 | moccasin = "#FFE4B5" 164 | navajowhite = "#FFDEAD" 165 | navy = "#000080" 166 | oldlace = "#FDF5E6" 167 | olive = "#808000" 168 | olivedrab = "#6B8E23" 169 | orange = "#FFA500" 170 | orangered = "#FF4500" 171 | orchid = "#DA70D6" 172 | palegoldenrod = "#EEE8AA" 173 | palegreen = "#98FB98" 174 | paleturquoise = "#AFEEEE" 175 | palevioletred = "#DB7093" 176 | papayawhip = "#FFEFD5" 177 | peachpuff = "#FFDAB9" 178 | peru = "#CD853F" 179 | pink = "#FFC0CB" 180 | plum = "#DDA0DD" 181 | powderblue = "#B0E0E6" 182 | purple = "#800080" 183 | red = "#FF0000" 184 | rosybrown = "#BC8F8F" 185 | royalblue = "#4169E1" 186 | saddlebrown = "#8B4513" 187 | salmon = "#FA8072" 188 | sandybrown = "#F4A460" 189 | seagreen = "#2E8B57" 190 | seashell = "#FFF5EE" 191 | sienna = "#A0522D" 192 | silver = "#C0C0C0" 193 | skyblue = "#87CEEB" 194 | slateblue = "#6A5ACD" 195 | slategray = "#708090" 196 | snow = "#FFFAFA" 197 | springgreen = "#00FF7F" 198 | steelblue = "#4682B4" 199 | tan = "#D2B48C" 200 | teal = "#008080" 201 | thistle = "#D8BFD8" 202 | tomato = "#FF6347" 203 | turquoise = "#40E0D0" 204 | violet = "#EE82EE" 205 | wheat = "#F5DEB3" 206 | white = "#FFFFFF" 207 | whitesmoke = "#F5F5F5" 208 | yellow = "#FFFF00" 209 | yellowgreen = "#9ACD32" 210 | } 211 | 212 | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 213 | Export-ModuleMember -Function $Public.Basename -Variable _PSSlackColorMap 214 | -------------------------------------------------------------------------------- /PSSlack/Private/Add-ObjectDetail.ps1: -------------------------------------------------------------------------------- 1 | function Add-ObjectDetail 2 | { 3 | <# 4 | .SYNOPSIS 5 | Decorate an object with 6 | - A TypeName 7 | - New properties 8 | - Default parameters 9 | 10 | .DESCRIPTION 11 | Helper function to decorate an object with 12 | - A TypeName 13 | - New properties 14 | - Default parameters 15 | 16 | .PARAMETER InputObject 17 | Object to decorate. Accepts pipeline input. 18 | 19 | .PARAMETER TypeName 20 | Typename to insert. 21 | 22 | This will show up when you use Get-Member against the resulting object. 23 | 24 | .PARAMETER PropertyToAdd 25 | Add these noteproperties. 26 | 27 | Format is a hashtable with Key (Property Name) = Value (Property Value). 28 | 29 | Example to add a One and Date property: 30 | 31 | -PropertyToAdd @{ 32 | One = 1 33 | Date = (Get-Date) 34 | } 35 | 36 | .PARAMETER DefaultProperties 37 | Change the default properties that show up 38 | 39 | .PARAMETER Passthru 40 | Whether to pass the resulting object on. Defaults to true 41 | 42 | .EXAMPLE 43 | # 44 | # Create an object to work with 45 | $Object = [PSCustomObject]@{ 46 | First = 'Cookie' 47 | Last = 'Monster' 48 | Account = 'CMonster' 49 | } 50 | 51 | #Add a type name and a random property 52 | Add-ObjectDetail -InputObject $Object -TypeName 'ApplicationX.Account' -PropertyToAdd @{ AnotherProperty = 5 } 53 | 54 | # First Last Account AnotherProperty 55 | # ----- ---- ------- --------------- 56 | # Cookie Monster CMonster 5 57 | 58 | #Verify that get-member shows us the right type 59 | $Object | Get-Member 60 | 61 | # TypeName: ApplicationX.Account ... 62 | 63 | .EXAMPLE 64 | # 65 | # Create an object to work with 66 | $Object = [PSCustomObject]@{ 67 | First = 'Cookie' 68 | Last = 'Monster' 69 | Account = 'CMonster' 70 | } 71 | 72 | #Add a random property, set a default property set so we only see two props by default 73 | Add-ObjectDetail -InputObject $Object -PropertyToAdd @{ AnotherProperty = 5 } -DefaultProperties Account, AnotherProperty 74 | 75 | # Account AnotherProperty 76 | # ------- --------------- 77 | # CMonster 5 78 | 79 | #Verify that the other properties are around 80 | $Object | Select -Property * 81 | 82 | # First Last Account AnotherProperty 83 | # ----- ---- ------- --------------- 84 | # Cookie Monster CMonster 5 85 | 86 | .NOTES 87 | This breaks the 'do one thing' rule from certain perspectives... 88 | The goal is to decorate an object all in one shot 89 | 90 | This abstraction simplifies decorating an object, with a slight trade-off in performance. For example: 91 | 92 | 10,000 objects, add a property and typename: 93 | Add-ObjectDetail: ~4.6 seconds 94 | Add-Member + PSObject.TypeNames.Insert: ~3 seconds 95 | 96 | Initial code borrowed from Shay Levy: 97 | http://blogs.microsoft.co.il/scriptfanatic/2012/04/13/custom-objects-default-display-in-powershell-30/ 98 | 99 | .LINK 100 | http://ramblingcookiemonster.github.io/Decorating-Objects/ 101 | 102 | .FUNCTIONALITY 103 | PowerShell Language 104 | #> 105 | [CmdletBinding()] 106 | param( 107 | [Parameter( Mandatory = $true, 108 | Position=0, 109 | ValueFromPipeline=$true )] 110 | [ValidateNotNullOrEmpty()] 111 | [psobject[]]$InputObject, 112 | 113 | [Parameter( Mandatory = $false, 114 | Position=1)] 115 | [string]$TypeName, 116 | 117 | [Parameter( Mandatory = $false, 118 | Position=2)] 119 | [System.Collections.Hashtable]$PropertyToAdd, 120 | 121 | [Parameter( Mandatory = $false, 122 | Position=3)] 123 | [ValidateNotNullOrEmpty()] 124 | [Alias('dp')] 125 | [System.String[]]$DefaultProperties, 126 | 127 | [boolean]$Passthru = $True 128 | ) 129 | 130 | Begin 131 | { 132 | if($PSBoundParameters.ContainsKey('DefaultProperties')) 133 | { 134 | # define a subset of properties 135 | $ddps = New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet,$DefaultProperties 136 | $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps 137 | } 138 | } 139 | Process 140 | { 141 | foreach($Object in $InputObject) 142 | { 143 | switch ($PSBoundParameters.Keys) 144 | { 145 | 'PropertyToAdd' 146 | { 147 | foreach($Key in $PropertyToAdd.Keys) 148 | { 149 | #Add some noteproperties. Slightly faster than Add-Member. 150 | $Object.PSObject.Properties.Add( ( New-Object System.Management.Automation.PSNoteProperty($Key, $PropertyToAdd[$Key]) ) ) 151 | } 152 | } 153 | 'TypeName' 154 | { 155 | #Add specified type 156 | [void]$Object.PSObject.TypeNames.Insert(0,$TypeName) 157 | } 158 | 'DefaultProperties' 159 | { 160 | # Attach default display property set 161 | Add-Member -InputObject $Object -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers 162 | } 163 | } 164 | if($Passthru) 165 | { 166 | $Object 167 | } 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /PSSlack/Private/Color-ToNumber.ps1: -------------------------------------------------------------------------------- 1 | Function Color-ToNumber { 2 | [cmdletbinding()] 3 | param( 4 | [object]$Color 5 | ) 6 | if(-not $IsCoreCLR) 7 | { 8 | [System.Drawing.Color] $Color = $Color 9 | '#{0:X2}{1:X2}{2:X2}' -f $Color.R, 10 | $Color.G, 11 | $Color.B 12 | } 13 | } -------------------------------------------------------------------------------- /PSSlack/Private/ConvertFrom-UnixTime.ps1: -------------------------------------------------------------------------------- 1 | #From http://powershell.com/cs/blogs/tips/archive/2012/03/09/converting-unix-time.aspx - Thanks! 2 | function ConvertFrom-UnixTime { 3 | param( 4 | [Parameter(Mandatory=$true, 5 | ValueFromPipeline=$true)] 6 | [Int32] 7 | $UnixTime 8 | ) 9 | begin { 10 | $startdate = Get-Date –Date '01/01/1970' 11 | } 12 | process { 13 | $timespan = New-Timespan -Seconds $UnixTime 14 | $startdate + $timespan 15 | } 16 | } -------------------------------------------------------------------------------- /PSSlack/Private/Get-PSSlackConfigPath.ps1: -------------------------------------------------------------------------------- 1 | function Get-PSSlackConfigPath 2 | { 3 | [CmdletBinding()] 4 | param() 5 | 6 | end 7 | { 8 | if (Test-IsWindows) 9 | { 10 | Join-Path -Path $env:TEMP -ChildPath "$env:USERNAME-$env:COMPUTERNAME-PSSlack.xml" 11 | } 12 | else 13 | { 14 | Join-Path -Path $env:HOME -ChildPath '.psslack' # Leading . and no file extension to be Unixy. 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /PSSlack/Private/Get-PropertyOrder.ps1: -------------------------------------------------------------------------------- 1 | #function to extract properties 2 | Function Get-PropertyOrder { 3 | <# 4 | .SYNOPSIS 5 | Gets property order for specified object 6 | 7 | .DESCRIPTION 8 | Gets property order for specified object 9 | 10 | .PARAMETER InputObject 11 | A single object to convert to an array of property value pairs. 12 | 13 | .PARAMETER Membertype 14 | Membertypes to include 15 | 16 | .PARAMETER ExcludeProperty 17 | Specific properties to exclude 18 | 19 | .FUNCTIONALITY 20 | PowerShell Language 21 | #> 22 | [cmdletbinding()] 23 | param( 24 | [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromRemainingArguments=$false)] 25 | [PSObject]$InputObject, 26 | 27 | [validateset("AliasProperty", "CodeProperty", "Property", "NoteProperty", "ScriptProperty", 28 | "Properties", "PropertySet", "Method", "CodeMethod", "ScriptMethod", "Methods", 29 | "ParameterizedProperty", "MemberSet", "Event", "Dynamic", "All")] 30 | [string[]]$MemberType = @( "NoteProperty", "Property", "ScriptProperty" ), 31 | 32 | [string[]]$ExcludeProperty = $null 33 | ) 34 | 35 | begin { 36 | 37 | if($PSBoundParameters.ContainsKey('inputObject')) { 38 | $firstObject = $InputObject[0] 39 | } 40 | } 41 | process{ 42 | 43 | #we only care about one object... 44 | $firstObject = $InputObject 45 | } 46 | end{ 47 | 48 | #Get properties that meet specified parameters 49 | $firstObject.psobject.properties | 50 | Where-Object { $memberType -contains $_.memberType } | 51 | Microsoft.PowerShell.Utility\Select-Object -ExpandProperty Name | 52 | Where-Object{ -not $excludeProperty -or $excludeProperty -notcontains $_ } 53 | } 54 | } #Get-PropertyOrder -------------------------------------------------------------------------------- /PSSlack/Private/Get-SlackUserFromID.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackUserFromID { 2 | [cmdletbinding()] 3 | param( 4 | [string[]]$Id, 5 | $UserMap 6 | ) 7 | begin { 8 | if(-not $PSBoundParameters.ContainsKey('UserMap')){ 9 | $UserMap = $Script:_PSSlackUserMap 10 | if($UserMap.Keys.Count -like 0) { 11 | Write-Verbose "No Slack User Map found. Please run Get-SlackUserMap -Update" 12 | } 13 | } 14 | $Map = @{} 15 | foreach($Key in $UserMap.Keys) { 16 | $Map.add($UserMap[$Key], $Key) 17 | } 18 | } 19 | process { 20 | if(-not $Map.Keys.count) { 21 | return $Id 22 | } 23 | foreach($UserID in $Id) { 24 | if($Map.ContainsKey($UserID)) { 25 | $Map[$UserID] 26 | } 27 | else { 28 | $UserID 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /PSSlack/Private/Get-UnixTime.ps1: -------------------------------------------------------------------------------- 1 |  function Get-UnixTime { 2 | param($Date) 3 | $unixEpochStart = New-Object DateTime 1970,1,1,0,0,0,([DateTimeKind]::Utc) 4 | [int]($Date.ToUniversalTime() - $unixEpochStart).TotalSeconds 5 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackAuth.ps1: -------------------------------------------------------------------------------- 1 | # Parse auth.test 2 | Function Parse-SlackAuth { 3 | [CmdletBinding()] 4 | param( $InputObject ) 5 | 6 | foreach($Auth in $InputObject) 7 | { 8 | [PSCustomObject] @{ 9 | PSTypeName = 'PSSlack.AuthInfo' 10 | 11 | IsAuthenticated = [bool] $Auth.ok 12 | Error = if ($Auth.Error) { $Auth.Error } else { } 13 | 14 | Url = $Auth.url 15 | 16 | UserID = $Auth.user_id 17 | User = $Auth.user 18 | 19 | TeamID = $Auth.team_id 20 | Team = $Auth.team 21 | 22 | Raw = $Auth 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackChannel.ps1: -------------------------------------------------------------------------------- 1 | # Parse channels 2 | Function Parse-SlackChannel { 3 | [cmdletbinding()] 4 | param( $InputObject ) 5 | 6 | foreach($Channel in $InputObject) 7 | { 8 | $TopicSet = $null 9 | $PurposeSet = $null 10 | if($Channel.Purpose.last_set) 11 | { 12 | $PurposeSet = ConvertFrom-UnixTime $Channel.Purpose.last_set 13 | } 14 | if($Channel.topic.last_set) 15 | { 16 | $TopicSet = ConvertFrom-UnixTime $Channel.topic.last_set 17 | } 18 | [pscustomobject]@{ 19 | PSTypeName = 'PSSlack.Channel' 20 | ID = $Channel.id 21 | Name = $Channel.name 22 | Created = ConvertFrom-UnixTime $Channel.created 23 | Creator = $Channel.creator 24 | IsGeneral = $Channel.is_general 25 | IsArchived = $Channel.is_archived 26 | Members = $Channel.members 27 | Topic = $Channel.Topic.value 28 | TopicSet = $TopicSet 29 | Purpose = $Channel.Purpose.value 30 | PurposeSet = $PurposeSet 31 | MemberCount = $Channel.num_members 32 | Raw = $Channel 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackError.ps1: -------------------------------------------------------------------------------- 1 | function Parse-SlackError { 2 | [CmdletBinding()] 3 | param ( 4 | # The response object from Slack's API. 5 | [Parameter( 6 | Mandatory = $true, 7 | ValueFromPipeline = $true 8 | )] 9 | [Object]$ResponseObject, 10 | 11 | # The exception from Invoke-RestMethod, if available. 12 | [Exception]$Exception 13 | ) 14 | 15 | Begin { 16 | $SlackErrorData = @{ 17 | # Messages are adapted from Slack API documentation 18 | 19 | missing_scope = @{ 20 | Message = "The method requires a scope that your token is missing. For example, a user might need to be in the admin scope" 21 | RecommendedAction = "Review the API method documentation (https://api.slack.com/methods), these generally list valid scopes (https://api.slack.com/scopes)" 22 | } 23 | 24 | invalid_arg_name = @{ 25 | Message = "The method was passed an argument whose name falls outside the bounds of accepted or expected values. This includes very long names and names with non-alphanumeric characters other than _." 26 | RecommendedAction = "Verify API call is well-formed." 27 | } 28 | 29 | invalid_array_arg = @{ 30 | Message = "The method was passed a PHP-style array argument (e.g. with a name like foo[7]). These are never valid with the Slack API." 31 | RecommendedAction = "Rename or remove the argument" 32 | } 33 | 34 | invalid_charset = @{ 35 | Message = "The method was called via a POST request, but the charset specified in the Content-Type header was invalid. Valid charset names are: utf-8 iso-8859-1." 36 | } 37 | 38 | invalid_form_data = @{ 39 | Message = "The method was called via a POST request with Content-Type application/x-www-form-urlencoded or multipart/form-data, but the form data was either missing or syntactically invalid." 40 | } 41 | 42 | invalid_post_type = @{ 43 | Message = "The method was called via a POST request, but the specified Content-Type was invalid. Valid types are: application/x-www-form-urlencoded multipart/form-data text/plain." 44 | } 45 | 46 | missing_post_type = @{ 47 | Message = "The method was called via a POST request and included a data payload, but the request did not include a Content-Type header." 48 | } 49 | 50 | team_added_to_org = @{ 51 | Message = "The workspace associated with your request is currently undergoing migration to an Enterprise Organization. Web API and other platform operations will be intermittently unavailable until the transition is complete." 52 | RecommendedAction = "Wait until migration is complete, then try the request again." 53 | } 54 | 55 | request_timeout = @{ 56 | Message = "The method was called via a POST request, but the POST data was either missing or truncated." 57 | } 58 | 59 | fatal_error = @{ 60 | Message = "The server could not complete your operation(s) without encountering a catastrophic error. Some aspect of the operation may have succeeded before the error was raised." 61 | } 62 | 63 | not_authed = @{ 64 | Message = "No authentication token provided." 65 | RecommendedAction = "Specify an authentication token via the -Token or -URI parameters, then try again." 66 | } 67 | 68 | invalid_auth = @{ 69 | Message = "Some aspect of authentication cannot be validated. Either the provided token is invalid or the request originates from an IP address disallowed from making the request." 70 | } 71 | 72 | account_inactive = @{ 73 | Message = "Authentication token is for a deleted user or workspace." 74 | } 75 | 76 | no_permission = @{ 77 | Message = "The workspace token used in this request does not have the permissions necessary to complete the request." 78 | 79 | } 80 | 81 | ratelimited = @{ 82 | Message = "Slack API rate-limit exceeded." 83 | RecommendedAction = "Try again in a few moments." 84 | } 85 | } 86 | } 87 | 88 | process { 89 | If ($ResponseObject.ok) { 90 | # We weren't actually given an error in this case 91 | Write-Debug "Parse-SlackError: Received non-error response, skipping." 92 | return 93 | } 94 | 95 | $ErrorParams = $SlackErrorData[$ResponseObject.error] 96 | 97 | If ($ErrorParams -eq $null) { 98 | $ErrorParams = @{ 99 | Message = "Unknown error $($ResponseObject.error) received from Slack API." 100 | } 101 | } 102 | If ($Exception) { 103 | $ErrorParams.Exception = $Exception 104 | } 105 | 106 | Write-Error -ErrorId $ResponseObject.error @ErrorParams 107 | } 108 | 109 | end { 110 | } 111 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackFile.ps1: -------------------------------------------------------------------------------- 1 | # Parse output from search.messages 2 | Function Parse-SlackFile { 3 | [cmdletbinding()] 4 | param( 5 | $InputObject, 6 | [switch]$Match 7 | ) 8 | $Files = $InputObject.files 9 | $pstypename = 'PSSlack.File' 10 | foreach($File in $Files) 11 | { 12 | $UserName = $null 13 | $Map = @{} 14 | foreach($Key in $Script:_PSSlackUserMap.Keys) { 15 | $Map.add($Script:_PSSlackUserMap[$Key], $Key) 16 | } 17 | if($Map.ContainsKey($File.user)) 18 | { 19 | $UserName = $Map[$File.user] 20 | } 21 | if($Script:_PSSlackUserMap.Keys.Count -like 0) { 22 | Write-Verbose "No Slack User Map found. Please run Get-SlackUserMap -Update" 23 | } 24 | [pscustomobject]@{ 25 | PSTypeName = $pstypename 26 | ID = $File.id 27 | Name = $File.name 28 | Created = ConvertFrom-UnixTime $File.created 29 | Title = $File.title 30 | MimeType = $File.mimetype 31 | FileType = $File.filetype 32 | Type = $File.pretty_type 33 | UserName = $UserName 34 | UserID = $File.user 35 | Size = $File.size 36 | IsPublic = $File.is_public 37 | PermalinkPublic = $File.permalink_public 38 | Permalink = $File.permalink 39 | UrlPrivateDownload = $File.url_private_download 40 | Lines = $File.lines 41 | Channels = $File.channels 42 | Groups = $File.groups 43 | Ims = $File.ims 44 | Raw = $File 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackGroup.ps1: -------------------------------------------------------------------------------- 1 | Function Parse-SlackGroup { 2 | [cmdletbinding()] 3 | param( $InputObject ) 4 | 5 | foreach($Group in $InputObject) 6 | { 7 | $TopicSet = $null 8 | $PurposeSet = $null 9 | if($Group.Purpose.last_set) 10 | { 11 | $PurposeSet = ConvertFrom-UnixTime $Group.Purpose.last_set 12 | } 13 | if($Group.topic.last_set) 14 | { 15 | $TopicSet = ConvertFrom-UnixTime $Group.topic.last_set 16 | } 17 | [pscustomobject]@{ 18 | PSTypeName = 'PSSlack.Group' 19 | ID = $Group.id 20 | Name = $Group.name 21 | Created = ConvertFrom-UnixTime $Group.created 22 | Creator = $Group.creator 23 | IsArchived = $Group.is_archived 24 | Members = $Group.members 25 | Topic = $Group.Topic.value 26 | TopicSet = $TopicSet 27 | Purpose = $Group.Purpose.value 28 | PurposeSet = $PurposeSet 29 | MemberCount = ($Group.members).Count 30 | Raw = $Group 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackMessage.ps1: -------------------------------------------------------------------------------- 1 | # Parse output from search.messages 2 | Function Parse-SlackMessage { 3 | [cmdletbinding()] 4 | param( 5 | $InputObject, 6 | [switch]$Match 7 | ) 8 | 9 | function Extract-Previous { 10 | param($Message) 11 | if($Message.username -or $Message.Text) 12 | { 13 | "@{0}: {1}" -f $Message.Username, $Message.Text 14 | } 15 | else 16 | { 17 | $null 18 | } 19 | } 20 | 21 | if($Match) 22 | { 23 | $Messages = $InputObject.messages.matches 24 | $pstypename = 'PSSlack.SearchResult' 25 | 26 | foreach($Message in $Messages) 27 | { 28 | [pscustomobject]@{ 29 | PSTypeName = $pstypename 30 | Username = $Message.username 31 | User = $Message.User 32 | Channel = $Message.channel.name 33 | Text = $Message.text 34 | Attachments = $Message.Attachments 35 | Reactions = $Message.Reactions 36 | File = $Message.File 37 | Type = $Message.Type 38 | SubType = $Message.subtype 39 | Timestamp = ConvertFrom-UnixTime $Message.ts 40 | Permalink = $Message.permalink 41 | Previous = Extract-Previous $Message.Previous 42 | Previous_2 = Extract-Previous $Message.Previous_2 43 | Next = Extract-Previous $Message.Next 44 | Next_2 = Extract-Previous $Message.Next_2 45 | Raw = $Message 46 | } 47 | } 48 | } 49 | else 50 | { 51 | $Messages = $InputObject.messages 52 | $pstypename = 'PSSlack.History' 53 | 54 | foreach($Message in $Messages) 55 | { 56 | [pscustomobject]@{ 57 | PSTypeName = $pstypename 58 | Username = $Message.username 59 | User = $Message.User 60 | Text = $Message.text 61 | Attachments = $Message.Attachments 62 | Reactions = $Message.Reactions 63 | File = $Message.File 64 | Type = $Message.Type 65 | SubType = $Message.subtype 66 | Timestamp = ConvertFrom-UnixTime $Message.ts 67 | Raw = $Message 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackReminder.ps1: -------------------------------------------------------------------------------- 1 | # Parse output from reminders.list 2 | Function Parse-SlackReminder { 3 | [cmdletbinding()] 4 | param( 5 | $InputObject 6 | ) 7 | $Reminders = $InputObject.reminders 8 | $pstypename = 'PSSlack.Reminder' 9 | foreach($Reminder in $Reminders) 10 | { 11 | $UserName = $null 12 | $CreatorName = $null 13 | $CompleteTime = $null 14 | $Map = @{} 15 | foreach($Key in $Script:_PSSlackUserMap.Keys) { 16 | $Map.add($Script:_PSSlackUserMap[$Key], $Key) 17 | } 18 | if($Map.ContainsKey($Reminder.user)) 19 | { 20 | $UserName = $Map[$Reminder.user] 21 | } 22 | if($Map.ContainsKey($Reminder.creator)) 23 | { 24 | $CreatorName = $Map[$Reminder.creator] 25 | } 26 | if($Reminder.complete_ts -ne 0){ 27 | $CompleteTime = ConvertFrom-UnixTime $Reminder.complete_ts 28 | } 29 | if($Script:_PSSlackUserMap.Keys.Count -like 0) { 30 | Write-Verbose "No Slack User Map found. Please run Get-SlackUserMap -Update" 31 | } 32 | [pscustomobject]@{ 33 | PSTypeName = $pstypename 34 | ID = $Reminder.id 35 | Creator = $Reminder.creator 36 | CreatorName = $CreatorName 37 | User = $Reminder.user 38 | UserName = $UserName 39 | Text = $Reminder.text 40 | Recurring = $Reminder.recurring 41 | Timestamp = $Reminder.time 42 | Date = ConvertFrom-UnixTime $Reminder.time 43 | CompleteTS = $Reminder.complete_ts 44 | DateComplete = $CompleteTime 45 | Raw = $Reminder 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackTeam.ps1: -------------------------------------------------------------------------------- 1 | # Parse teams 2 | Function Parse-SlackTeam { 3 | [CmdletBinding()] 4 | param( $InputObject ) 5 | 6 | foreach($Team in $InputObject) 7 | { 8 | [PSCustomObject] @{ 9 | PSTypeName = 'PSSlack.Team' 10 | ID = $Team.id 11 | Name = $Team.name 12 | Domain = $Team.domain 13 | EmailDomain = ($Team.email_domain -split ',') 14 | Icon = [Ordered] @{ 15 | Image34 = $Team.icon.image_34 16 | Image44 = $Team.icon.image_44 17 | Image68 = $Team.icon.image_68 18 | Image88 = $Team.icon.image_88 19 | Image102 = $Team.icon.image_102 20 | Image132 = $Team.icon.image_132 21 | Image230 = $Team.icon.image_230 22 | ImageDefault = $Team.icon.image_default 23 | } 24 | Raw = $Team 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackUser.ps1: -------------------------------------------------------------------------------- 1 | # Parse users 2 | Function Parse-SlackUser { 3 | [cmdletbinding()] 4 | param( $InputObject ) 5 | 6 | foreach($User in $InputObject) 7 | { 8 | [pscustomobject]@{ 9 | PSTypeName = 'PSSlack.User' 10 | ID = $User.id 11 | Name = $User.name 12 | DisplayName = $User.Profile.Display_Name 13 | RealName = $User.Profile.Real_Name 14 | FirstName = $User.Profile.First_Name 15 | Last_Name = $User.Profile.Last_Name 16 | Email = $User.Profile.email 17 | Phone = $User.Profile.Phone 18 | Skype = $User.Profile.Skype 19 | IsBot = $User.Is_Bot 20 | IsAdmin = $User.Is_Admin 21 | IsOwner = $User.Is_Owner 22 | IsPrimaryOwner = $User.Is_Primary_Owner 23 | IsRestricted = $User.Is_Restricted 24 | IsUltraRestricted = $User.Is_Ultra_Restricted 25 | StatusText = $User.Profile.Status_Text 26 | StatusEmoji = $User.Profile.Status_Emoji 27 | TimeZoneLabel = $User.tz_label 28 | TimeZone = $User.tz 29 | Presence = $User.Presence 30 | BillingActive = $User.BillingActive 31 | Updated = ConvertFrom-UnixTime $User.updated 32 | Deleted = $User.Deleted 33 | Raw = $User 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /PSSlack/Private/Parse-SlackUserGroup.ps1: -------------------------------------------------------------------------------- 1 | Function Parse-SlackUserGroup { 2 | [cmdletbinding()] 3 | param( $InputObject ) 4 | 5 | foreach($Group in $InputObject) 6 | { 7 | $Users = $null 8 | if($Group.users.count -gt 0) { 9 | $Users = Get-SlackUserFromID -Id $Group.users 10 | $UserCount = $Users.count 11 | } 12 | else { 13 | $UserCount = $null 14 | } 15 | [pscustomobject]@{ 16 | PSTypeName = 'PSSlack.UserGroup' 17 | ID = $Group.id 18 | Name = $Group.name 19 | Handle = $Group.handle 20 | Description = $Group.description 21 | Created = ConvertFrom-UnixTime $Group.'date_create' 22 | Updated = ConvertFrom-UnixTime $Group.'date_update' 23 | CreatedBy = Get-SlackUserFromID -Id $Group.'created_by' 24 | UpdatedBy = Get-SlackUserFromID -Id $Group.'updated_by' 25 | Users = $Users 26 | UserCount = $UserCount 27 | Raw = $Group 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /PSSlack/Private/Remove-SensitiveData.ps1: -------------------------------------------------------------------------------- 1 | function Remove-SensitiveData { 2 | param ( 3 | [parameter(ValueFromPipeline = $True)] 4 | $InputObject, 5 | $SensitiveProperties = @('Uri', 'Token'), 6 | $ForceVerbose = $Script:PSSlack.ForceVerbose 7 | ) 8 | process { 9 | if($ForceVerbose) { 10 | return $InputObject 11 | } 12 | if($InputObject -is [hashtable] -or ($InputObject.Keys.Count -gt 0 -and $InputObject.Values.Count -gt 0)) { 13 | $Output = [hashtable]$($InputObject.PSObject.Copy()) 14 | foreach($Prop in $SensitiveProperties) { 15 | if($InputObject.ContainsKey($Prop)) { 16 | $Output[$Prop] = 'REDACTED' 17 | } 18 | } 19 | $Output 20 | } 21 | else { 22 | $InputObject | Microsoft.PowerShell.Utility\Select-Object -Property * -ExcludeProperty $SensitiveProperties 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /PSSlack/Private/Test-IsWindows.ps1: -------------------------------------------------------------------------------- 1 | function Test-IsWindows 2 | { 3 | [CmdletBinding()] 4 | [OutputType([bool])] 5 | param() 6 | 7 | end 8 | { 9 | !(Test-Path -Path Variable:\IsWindows) -or $IsWindows 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PSSlack/Public/Find-SlackMessage.ps1: -------------------------------------------------------------------------------- 1 | function Find-SlackMessage { 2 | <# 3 | .SYNOPSIS 4 | Search a Slack team for a message 5 | 6 | .DESCRIPTION 7 | Search a Slack team for a message 8 | 9 | Output will include details on the matching message, along with 'previous' and 'next' for context. 10 | 11 | .PARAMETER Query 12 | Search query. May contains booleans 13 | 14 | Messages are searched primarily inside the message text themselves, 15 | with a lower priority on the messages immediately before and after. 16 | 17 | If more than one search term is provided, user and channel are also matched at a lower priority. 18 | 19 | To specifically search within a channel, group, or DM, add 20 | in:channel_name, 21 | in:group_name, or 22 | in:username. 23 | 24 | To search for messages from a specific speaker, add 25 | from:username or 26 | from:botname. 27 | 28 | See https://api.slack.com/methods/search.messages for more information 29 | 30 | .PARAMETER Token 31 | Specify a token for authorization. 32 | 33 | See 'Authentication' section here for more information: https://api.slack.com/web 34 | Test tokens are a simple way to use this 35 | 36 | .PARAMETER SortDirection 37 | Sort asc[ending] or desc[ending]. Defaults to desc 38 | 39 | .PARAMETER SortBy 40 | Sort by score (relevance) or timestamp (date). Defaults to score 41 | 42 | .PARAMETER Count 43 | Return this many results per page. Defaults to 20 44 | 45 | .PARAMETER Page 46 | Which page to return. Defaults to 1 47 | 48 | .PARAMETER MaxPages 49 | Stop requesting search results once you hit this many pages. Defaults to the maximum value of int 50 | 51 | .PARAMETER Raw 52 | If specified, we provide raw output and do not parse any responses 53 | 54 | .FUNCTIONALITY 55 | Slack 56 | #> 57 | [cmdletbinding()] 58 | param ( 59 | [string]$Query, 60 | $Token = $Script:PSSlack.Token, 61 | [validateset('asc','desc')] 62 | $SortDirection = 'desc', 63 | [validateset('score','timestamp')] 64 | $SortBy = 'score', 65 | [int]$Count = 20, 66 | [int]$Page = 1, 67 | [int]$MaxPages = [int]::MaxValue, 68 | [switch]$Raw 69 | ) 70 | end 71 | { 72 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 73 | 74 | #Initial args 75 | $body = @{ 76 | query = $Query 77 | sort_dir = $SortDirection 78 | sort = $SortBy 79 | count = $Count 80 | page = $Page 81 | } 82 | $params = @{ 83 | Body = $Body 84 | Method = 'search.messages' 85 | } 86 | if($Token) 87 | { 88 | $Params.Add('Token',$token) 89 | } 90 | 91 | #Pagination and parsing 92 | do 93 | { 94 | Write-Verbose "SendSlackApi -Params $($Params | Remove-SensitiveData | Format-List | Out-String)" 95 | $response = Send-SlackApi @params 96 | if ($response.ok) 97 | { 98 | $link = "$($Script:PSSlack.ArchiveUri)/$($response.channel)/p$($response.ts -replace '\.')" 99 | $response | Add-Member -MemberType NoteProperty -Name link -Value $link 100 | $ResponsePage = $response.messages.paging.page 101 | $ResponsePageCount = $response.messages.paging.pages 102 | if($ResponsePage -lt $ResponsePageCount) 103 | { 104 | $Page++ 105 | $Params.Body.page = $Page 106 | } 107 | 108 | if($Raw) 109 | { 110 | $link = "$($Script:PSSlack.ArchiveUri)/$($response.channel)/p$($response.ts -replace '\.')" 111 | $response | Add-Member -MemberType NoteProperty -Name link -Value $link 112 | $response 113 | } 114 | else 115 | { 116 | Parse-SlackMessage -InputObject $Response -Match 117 | } 118 | } 119 | else { 120 | $response 121 | break 122 | } 123 | 124 | } until ( $ResponsePage -eq $ResponsePageCount -or 125 | $ResponsePage -eq $MaxPages -or 126 | $ResponsePageCount -eq 0) 127 | } 128 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-PSSlackConfig.ps1: -------------------------------------------------------------------------------- 1 | Function Get-PSSlackConfig { 2 | <# 3 | .SYNOPSIS 4 | Get PSSlack module configuration. 5 | 6 | .DESCRIPTION 7 | Get PSSlack module configuration 8 | 9 | .PARAMETER Source 10 | Get the config data from either... 11 | 12 | PSSlack: the live module variable used for command defaults 13 | PSSlack.xml: the serialized PSSlack.xml that loads when importing the module 14 | 15 | Defaults to PSSlack 16 | 17 | .PARAMETER Path 18 | If specified, read config from this XML file. 19 | 20 | Defaults to PSSlack.xml in the user temp folder on Windows, or .psslack in the user's home directory on Linux/macOS. 21 | 22 | .FUNCTIONALITY 23 | Slack 24 | #> 25 | [cmdletbinding(DefaultParameterSetName = 'source')] 26 | param( 27 | [parameter(ParameterSetName='source')] 28 | [ValidateSet("PSSlack","PSSlack.xml")] 29 | $Source = "PSSlack", 30 | 31 | [parameter(ParameterSetName='path')] 32 | [parameter(ParameterSetName='source')] 33 | $Path = $script:_PSSlackXmlpath 34 | ) 35 | 36 | if($PSCmdlet.ParameterSetName -eq 'source' -and $Source -eq "PSSlack" -and -not $PSBoundParameters.ContainsKey('Path')) 37 | { 38 | $Script:PSSlack 39 | } 40 | else 41 | { 42 | function Decrypt { 43 | param($String) 44 | if($String -is [System.Security.SecureString]) 45 | { 46 | [System.Runtime.InteropServices.marshal]::PtrToStringAuto( 47 | [System.Runtime.InteropServices.marshal]::SecureStringToBSTR( 48 | $string)) 49 | } 50 | } 51 | Import-Clixml -Path $Path | 52 | Microsoft.PowerShell.Utility\Select-Object -Property ArchiveUri, 53 | @{l='Uri';e={Decrypt $_.Uri}}, 54 | @{l='Token';e={Decrypt $_.Token}}, 55 | Proxy, 56 | MapUser, 57 | ForceVerbose 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackAuth.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackAuth { 2 | <# 3 | .SYNOPSIS 4 | Checks authentication and tells you who you are. 5 | 6 | .DESCRIPTION 7 | Checks authentication and tells you who you are. 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API. 11 | 12 | Default value is the value set by Set-PSSlackConfig. 13 | 14 | .PARAMETER Raw 15 | Return raw output. 16 | 17 | .EXAMPLE 18 | Get-SlackAuth 19 | 20 | # Checks authentication and retrieves the information of the default user specified by Get-PSSlackConfig. 21 | 22 | .EXAMPLE 23 | Get-SlackAuth -Token $Token 24 | 25 | # Checks authentication and retrieves the information of the user specified by $Token. 26 | .EXAMPLE 27 | Get-SlackAuth -Raw 28 | 29 | # Checks authentication and retrieves the information of the default user specified by Get-PSSlackConfig. 30 | # Returns raw output. 31 | 32 | .EXAMPLE 33 | Get-SlackAuth -Raw -Token $Token 34 | 35 | # Checks authentication and retrieves the information of the user specified by $Token. 36 | # Returns raw output. 37 | 38 | .FUNCTIONALITY 39 | Slack 40 | 41 | .LINK 42 | https://api.slack.com/methods/auth.test 43 | #> 44 | 45 | [CmdletBinding()] 46 | param ( 47 | [string]$Token = $Script:PSSlack.Token, 48 | [switch]$Raw 49 | ) 50 | end 51 | { 52 | $params = @{ 53 | Token = $Token 54 | Method = 'auth.test' 55 | } 56 | 57 | $RawAuth = Send-SlackApi @params 58 | 59 | if($Raw) 60 | { 61 | $RawAuth 62 | } 63 | else 64 | { 65 | Parse-SlackAuth -InputObject $RawAuth 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackChannel.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackChannel { 2 | <# 3 | .SYNOPSIS 4 | Get information about Slack channels 5 | 6 | .DESCRIPTION 7 | Get information about Slack channels 8 | 9 | .PARAMETER Token 10 | Specify a token for authorization. 11 | 12 | See 'Authentication' section here for more information: https://api.slack.com/web 13 | Test tokens are a simple way to use this 14 | 15 | .PARAMETER Name 16 | One or more channel names to return. Defaults to all. Accepts wildcards. 17 | 18 | .PARAMETER Types 19 | Mix and match channel types by providing array of any combination of public_channel, private_channel, mpim, im 20 | 21 | .PARAMETER ExcludeArchived 22 | Whether to exclude archived channels. Default is to include all. 23 | 24 | .PARAMETER Raw 25 | If specified, we provide raw output and do not parse any responses 26 | 27 | .PARAMETER Paging 28 | If specified, and more data is available when a paging cursor is returned, continue querying Slack until 29 | we have retrieved all the data available. 30 | 31 | .PARAMETER MaxQueries 32 | Limit the count of API queries to this number. Only used if you enable -Paging 33 | 34 | .FUNCTIONALITY 35 | Slack 36 | #> 37 | [cmdletbinding()] 38 | param ( 39 | $Token = $Script:PSSlack.Token, 40 | [string[]]$Name, 41 | [ValidateSet('public_channel', 'private_channel', 'mpim', 'im')] 42 | [string[]]$Types, 43 | [switch]$ExcludeArchived, 44 | [switch]$Raw, 45 | [switch]$Paging, 46 | [int]$MaxQueries 47 | ) 48 | end 49 | { 50 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 51 | $body = @{ 52 | limit = 200 53 | } 54 | if ($ExcludeArchived) 55 | { 56 | $body.Add("exclude_archived",1) 57 | } 58 | else 59 | { 60 | $body.Add("exclude_archived",0) 61 | } 62 | 63 | if ($Types) 64 | { 65 | $body.add("types",$($Types -join ",")) 66 | } 67 | else 68 | { 69 | $body.add("types","public_channel,private_channel") 70 | } 71 | 72 | $RawChannels = @() 73 | $has_more = $false 74 | $Queries = 0 75 | do { 76 | $params = @{ 77 | Body = $body 78 | Token = $Token 79 | Method = 'conversations.list' 80 | } 81 | $response = Send-SlackApi @params 82 | $Queries++ 83 | if (-not [string]::IsNullOrEmpty($response.response_metadata.next_cursor)) 84 | { 85 | $has_more = $true 86 | $body['cursor'] = $response.response_metadata.next_cursor 87 | } 88 | else 89 | { 90 | $has_more = $false 91 | } 92 | $RawChannels += $response 93 | } until ( 94 | -not $Paging -or 95 | -not $has_more -or 96 | ($MaxQueries -and $Queries -ge $MaxQueries) 97 | ) 98 | 99 | $HasWildCard = $False 100 | foreach($Item in $Name) 101 | { 102 | if($Item -match '\*') 103 | { 104 | $HasWildCard = $true 105 | break 106 | } 107 | } 108 | 109 | if($Name -and -not $HasWildCard) 110 | { 111 | # torn between independent queries, or filtering channels.list 112 | # submit a PR if this isn't performant enough or doesn't make sense. 113 | $Channels = $RawChannels.channels | 114 | Where-Object {$Name -Contains $_.name} 115 | } 116 | elseif ($Name -and$HasWildCard) 117 | { 118 | $AllChannels = $RawChannels.Channels 119 | 120 | # allow like operator on each channel requested in the param, avoid dupes 121 | $ChannelHash = [ordered]@{} 122 | foreach($SlackChannel in $AllChannels) 123 | { 124 | foreach($Chan in $Name) 125 | { 126 | if($SlackChannel.Name -like $Chan -and -not $ChannelHash.Contains($SlackChannel.id)) 127 | { 128 | $ChannelHash.Add($SlackChannel.Id, $SlackChannel) 129 | } 130 | } 131 | } 132 | $Channels = $ChannelHash.Values 133 | } 134 | else # nothing specified 135 | { 136 | $Channels = $RawChannels.channels 137 | } 138 | 139 | if($Raw) 140 | { 141 | $RawChannels 142 | } 143 | else 144 | { 145 | Parse-SlackChannel -InputObject $Channels 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackFileInfo.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackFileInfo { 2 | <# 3 | .SYNOPSIS 4 | Get Slack file info 5 | .DESCRIPTION 6 | Get Slack file info 7 | 8 | We query the first 100 files unless you specify -Paging 9 | .PARAMETER Token 10 | Token to use for the Slack API 11 | Default value is the value set by Set-PSSlackConfig 12 | .PARAMETER Channel 13 | If specified, search for files in this channel (ID) 14 | .PARAMETER Before 15 | If specified, search for files created before this date 16 | .PARAMETER After 17 | If specified, search for files created after this date 18 | .PARAMETER Types 19 | If specified, search for files of this type: 20 | all - All files 21 | spaces - Posts 22 | snippets - Snippets 23 | images - Image files 24 | videos - Video files 25 | gdocs - Google docs 26 | zips - Zip files 27 | pdfs - PDF files 28 | .PARAMETER User 29 | If specified, search for files by this user 30 | .Parameter Raw 31 | Return raw output. If specified, Name parameter is ignored 32 | .PARAMETER Paging 33 | If specified, and more data is available, continue querying Slack until we have retrieved all the data available. 34 | .PARAMETER Count 35 | Number of messages to return per query. Defaults to 100 36 | .PARAMETER MaxQueries 37 | Limit the count of API queries to this number. Only used if you enable -Paging 38 | .EXAMPLE 39 | Get-SlackFileInfo 40 | # Lists up to 100 files 41 | .EXAMPLE 42 | Get-SlackFileInfo -Paging 43 | # Lists all files, querying 100 at a time 44 | .EXAMPLE 45 | Get-SlackFileInfo -User wframe -Type csv -Paging 46 | # Lists all CSV files uploaded by wframe 47 | .EXAMPLE 48 | Get-SlackFileInfo -User wframe -Channel C58AHBEPJ 49 | # Lists up to 100 files from channel C58AHBEPJ 50 | .EXAMPLE 51 | Get-SlackFileInfo -Before (Get-Date).AddDays(-7) -After (Get-Date).AddDays(-14) -Paging 52 | # Get all files from a week ago 53 | .FUNCTIONALITY 54 | Slack 55 | #> 56 | [cmdletbinding()] 57 | param ( 58 | [string]$Token = $Script:PSSlack.Token, 59 | [string]$Channel, 60 | [datetime]$Before, 61 | [datetime]$After, 62 | [validateset('all','spaces','snippets','images','videos','gdocs','zips','pdfs')] 63 | [string[]]$Types, 64 | [string]$User, 65 | [switch]$Paging, 66 | [ValidateRange(1,1000)] 67 | [int]$Count = 100, 68 | [switch]$Raw, 69 | [int]$MaxQueries 70 | ) 71 | begin 72 | { 73 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 74 | $body = @{ 75 | count = $count 76 | } 77 | if($User) 78 | { 79 | $u = $null 80 | if($Script:_PSSlackUserMap.ContainsKey($User)){ 81 | $u = $Script:_PSSlackUserMap[$User] 82 | } 83 | else { 84 | $map = Get-SlackUserMap -Update -Token $Token 85 | if($map.ContainsKey($User)) 86 | { 87 | $u = $map[$User] 88 | } 89 | else 90 | { 91 | Write-Warning "Could not find user [$User]. Check Get-SlackUserMap for valid names" 92 | } 93 | } 94 | if($u) 95 | { 96 | $body.add('user', $u) 97 | } 98 | } 99 | if($PSBoundParameters.ContainsKey('Channel')) 100 | { 101 | $body.add('channel', $Channel) 102 | } 103 | if($PSBoundParameters.ContainsKey('Types')) 104 | { 105 | $body.add('types', $($Types -join ',')) 106 | } 107 | $BeforeTS = $null 108 | $AfterTS = $null 109 | if($PSBoundParameters.ContainsKey('Before')) 110 | { 111 | $BeforeTS = Get-UnixTime -Date $Before 112 | $body.add('ts_to', $BeforeTS) 113 | } 114 | if($PSBoundParameters.ContainsKey('After')) 115 | { 116 | $AfterTS = Get-UnixTime -Date $After 117 | $body.add('ts_from', $AfterTS) 118 | } 119 | $params = @{ 120 | Token = $Token 121 | Method = 'files.list' 122 | Body = $body 123 | } 124 | $Queries = 1 125 | $has_more = $false 126 | do 127 | { 128 | $response = Send-SlackApi @params 129 | Write-Debug "$($Response | Format-List -Property * | Out-String)" 130 | if ($response.ok) 131 | { 132 | if($response.psobject.properties.name -contains 'paging' -and $response.paging.page -lt $response.paging.pages) 133 | { 134 | Write-Debug 'Paging engaged!' 135 | $has_more = $true 136 | $Params.body.page = 1 + $response.paging.page 137 | } 138 | elseif ($response.psobject.properties.name -contains 'paging' -and $response.paging.page -like $response.paging.pages) 139 | { 140 | $has_more = $false 141 | } 142 | else { 143 | # Might need this case later - is paging always included? Is this an error? 144 | $has_more = $false 145 | } 146 | if($Raw) 147 | { 148 | $response 149 | } 150 | else 151 | { 152 | Parse-SlackFile -InputObject $Response 153 | } 154 | } 155 | else 156 | { 157 | $response 158 | } 159 | $Queries++ 160 | } 161 | until ( 162 | -not $Paging -or 163 | -not $has_more -or 164 | ($MaxQueries -and $Queries -gt $MaxQueries) 165 | ) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackGroup.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackGroup { 2 | <# 3 | .SYNOPSIS 4 | Get information about Slack groups 5 | 6 | .DESCRIPTION 7 | Get information about Slack groups 8 | 9 | .PARAMETER Token 10 | Specify a token for authorization. 11 | 12 | See 'Authentication' section here for more information: https://api.slack.com/web 13 | Test tokens are a simple way to use this 14 | 15 | .PARAMETER Name 16 | One or more group names to return. Defaults to all. Accepts wildcards. 17 | 18 | .PARAMETER ExcludeArchived 19 | Whether to exclude archived groups. Default is to include all. 20 | 21 | .PARAMETER Raw 22 | If specified, we provide raw output and do not parse any responses 23 | 24 | .FUNCTIONALITY 25 | Slack 26 | #> 27 | [cmdletbinding()] 28 | param ( 29 | $Token = $Script:PSSlack.Token, 30 | [string[]]$Name, 31 | [switch]$ExcludeArchived, 32 | [switch]$Raw 33 | ) 34 | end 35 | { 36 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 37 | 38 | if($ExcludeArchived) 39 | { 40 | $body = @{ exclude_archived = 1 } 41 | } 42 | else 43 | { 44 | $body = @{ exclude_archived = 0 } 45 | } 46 | $params = @{ 47 | Body = $body 48 | Token = $Token 49 | Method = 'groups.list' 50 | } 51 | $RawGroups = Send-SlackApi @params 52 | 53 | $HasWildCard = $False 54 | foreach($Item in $Name) 55 | { 56 | if($Item -match '\*') 57 | { 58 | $HasWildCard = $true 59 | break 60 | } 61 | } 62 | 63 | if($Name -and -not $HasWildCard) 64 | { 65 | # torn between independent queries, or filtering groups.list 66 | # submit a PR if this isn't performant enough or doesn't make sense. 67 | $Groups = $RawGroups.groups | 68 | Where-Object {$Name -Contains $_.name} 69 | } 70 | elseif ($Name -and$HasWildCard) 71 | { 72 | $AllGroups = $RawGroups.groups 73 | 74 | # allow like operator on each group requested in the param, avoid dupes 75 | $GroupHash = [ordered]@{} 76 | foreach($SlackGroup in $AllGroups) 77 | { 78 | foreach($Chan in $Name) 79 | { 80 | if($SlackGroup.Name -like $Chan -and -not $GroupHash.Contains($SlackGroup.id)) 81 | { 82 | $GroupHash.Add($SlackGroup.Id, $SlackGroup) 83 | } 84 | } 85 | } 86 | $Groups = $GroupHash.Values 87 | } 88 | else # nothing specified 89 | { 90 | $Groups = $RawGroups.groups 91 | } 92 | 93 | if($Raw) 94 | { 95 | $RawGroups 96 | } 97 | else 98 | { 99 | Parse-SlackGroup -InputObject $Groups 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackGroupHistory.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackGroupHistory { 2 | <# 3 | .SYNOPSIS 4 | Get history from a Slack group 5 | 6 | .DESCRIPTION 7 | Get history from a Slack group 8 | 9 | .PARAMETER Token 10 | Specify a token for authorization. 11 | 12 | See 'Authentication' section here for more information: https://api.slack.com/web 13 | Test tokens are a simple way to use this 14 | 15 | .PARAMETER Id 16 | One or more group IDs to extract history from. 17 | 18 | .PARAMETER Before 19 | Return history from before this date 20 | 21 | .PARAMETER After 22 | Return history from after this date 23 | 24 | .PARAMETER Inclusive 25 | If specified, include history from the date specified in Before and/or After parameters 26 | 27 | .PARAMETER Count 28 | Number of messages to return per query. Defaults to 100. Max 1000 29 | 30 | .PARAMETER Paging 31 | If specified, and more data is available with a given 'Count', continue querying Slack until 32 | we have retrieved all the data available. 33 | 34 | WARNING: This parameter is experimental 35 | 36 | .PARAMETER MaxQueries 37 | Limit the count of API queries to this number. Only used if you enable -Paging 38 | 39 | .PARAMETER Raw 40 | If specified, we provide raw output and do not parse any responses 41 | 42 | .FUNCTIONALITY 43 | Slack 44 | #> 45 | [cmdletbinding()] 46 | param ( 47 | $Token = $Script:PSSlack.Token, 48 | [parameter( ValueFromPipelineByPropertyName = $True)] # a bit iffy... ID is common... 49 | [Alias('ID')] 50 | [string[]]$GroupID, 51 | 52 | [ValidateRange(1,1000)] 53 | [int]$Count = 100, 54 | [switch]$Inclusive, 55 | [datetime]$Before, 56 | [datetime]$After, 57 | [switch]$Paging, 58 | [int]$MaxQueries, 59 | [switch]$Raw 60 | 61 | ) 62 | begin 63 | { 64 | function Get-UnixTime { 65 | param($Date) 66 | $unixEpochStart = new-object DateTime 1970,1,1,0,0,0,([DateTimeKind]::Utc) 67 | [int]($Date.ToUniversalTime() - $unixEpochStart).TotalSeconds 68 | } 69 | 70 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 71 | 72 | $body = @{ 73 | channel = $null 74 | count = $count 75 | } 76 | if($Paging) 77 | { 78 | $PageDirection = 'Backward' 79 | } 80 | $BeforeTS = $null 81 | $AfterTS = $null 82 | if($PSBoundParameters.ContainsKey('Before')) 83 | { 84 | $BeforeTS = Get-UnixTime -Date $Before 85 | $body.add('latest', $BeforeTS) 86 | } 87 | if($PSBoundParameters.ContainsKey('After')) 88 | { 89 | $AfterTS = Get-UnixTime -Date $After 90 | $body.add('oldest', $AfterTS) 91 | if(-not $PSBoundParameters.ContainsKey('Before') -and $Paging) 92 | { 93 | $PageDirection = 'Forward' 94 | } 95 | } 96 | if($Inclusive) 97 | { 98 | $body.add('inclusive', 1) 99 | } 100 | $params = @{ 101 | Token = $Token 102 | Method = 'groups.history' 103 | Body = $body 104 | } 105 | $Queries = 1 106 | 107 | } 108 | process 109 | { 110 | foreach($ID in $GroupID) 111 | { 112 | $has_more = $false 113 | $Params.body.channel = $ID 114 | do 115 | { 116 | if($has_more) 117 | { 118 | if($Params.Body.oldest) 119 | { 120 | [void]$Params.Body.remove('oldest') 121 | } 122 | if($Params.Body.latest) 123 | { 124 | [void]$Params.Body.remove('latest') 125 | } 126 | if($PageDirection -eq 'Forward') 127 | { 128 | 129 | $ts = $response.messages.ts | Sort-Object | 130 | Microsoft.PowerShell.Utility\Select-Object -last 1 131 | $Params.body.oldest = $ts 132 | Write-Debug "Paging Forward.`n$( 133 | [pscustomobject]@{ 134 | After = $After 135 | Before = $Before 136 | LastTS = $response.messages[-1].ts 137 | SortLast = $response.messages.ts | Sort-Object | 138 | Microsoft.PowerShell.Utility\Select-Object -last 1 139 | SortFirst = $response.messages.ts | Sort-Object | 140 | Microsoft.PowerShell.Utility\Select-Object -first 1 141 | ts = $ts 142 | } | Out-String 143 | )" 144 | } 145 | elseif($PageDirection -eq 'Backward') 146 | { 147 | $ts = $response.messages[-1].ts 148 | if($AfterTS -and $ts -lt $AfterTS) 149 | { 150 | Write-Debug "TS is less than AfterTS, breaking!" 151 | break 152 | } 153 | $Params.body.latest = $ts 154 | Write-Debug "Paging Forward.`n$( 155 | [pscustomobject]@{ 156 | After = $After 157 | Before = $Before 158 | LastTS = $response.messages[-1].ts 159 | SortLast = $response.messages.ts | Sort-Object | 160 | Microsoft.PowerShell.Utility\Select-Object -last 1 161 | SortFirst = $response.messages.ts | Sort-Object | 162 | Microsoft.PowerShell.Utility\Select-Object -first 1 163 | ts = $ts 164 | } | Out-String 165 | )" 166 | } 167 | 168 | $has_more = $false 169 | Write-Debug "Body is now:$($params.body | out-string)" 170 | } 171 | $response = Send-SlackApi @params 172 | 173 | Write-Debug "$($Response | Format-List -Property * | Out-String)" 174 | 175 | if ($response.ok) 176 | { 177 | 178 | if($response.psobject.properties.name -contains 'has_more' -and $response.has_more) 179 | { 180 | Write-Debug 'Paging engaged!' 181 | $has_more = $true 182 | } 183 | 184 | if($Raw) 185 | { 186 | $link = "$($Script:PSSlack.ArchiveUri)/$($response.group)/p$($response.ts -replace '\.')" 187 | $response | Add-Member -MemberType NoteProperty -Name link -Value $link 188 | $response 189 | } 190 | else 191 | { 192 | #Order our messages appropriately according to page direction 193 | if($Paging -and $PageDirection -eq 'Forward') 194 | { 195 | Parse-SlackMessage -InputObject $Response | Sort-Object TimeStamp 196 | } 197 | else 198 | { 199 | Parse-SlackMessage -InputObject $Response 200 | } 201 | } 202 | } 203 | else 204 | { 205 | $response 206 | } 207 | $Queries++ 208 | } 209 | until ( 210 | -not $Paging -or 211 | -not $has_more -or 212 | ($MaxQueries -and $Queries -gt $MaxQueries) 213 | ) 214 | } 215 | } 216 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackHistory.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackHistory { 2 | <# 3 | .SYNOPSIS 4 | Get history from a Slack channel 5 | 6 | .DESCRIPTION 7 | Get history from a Slack channel 8 | 9 | .PARAMETER Token 10 | Specify a token for authorization. 11 | 12 | See 'Authentication' section here for more information: https://api.slack.com/web 13 | Test tokens are a simple way to use this 14 | 15 | .PARAMETER Id 16 | One or more channel IDs to extract history from. 17 | 18 | .PARAMETER Before 19 | Return history from before this date 20 | 21 | .PARAMETER After 22 | Return history from after this date 23 | 24 | .PARAMETER Inclusive 25 | If specified, include history from the date specified in Before and/or After parameters 26 | 27 | .PARAMETER Count 28 | Number of messages to return per query. Defaults to 100. Max 1000 29 | 30 | .PARAMETER Paging 31 | If specified, and more data is available with a given 'Count', continue querying Slack until 32 | we have retrieved all the data available. 33 | 34 | WARNING: This parameter is experimental 35 | 36 | .PARAMETER MaxQueries 37 | Limit the count of API queries to this number. Only used if you enable -Paging 38 | 39 | .PARAMETER Raw 40 | If specified, we provide raw output and do not parse any responses 41 | 42 | .FUNCTIONALITY 43 | Slack 44 | #> 45 | [cmdletbinding()] 46 | param ( 47 | $Token = $Script:PSSlack.Token, 48 | [parameter( ValueFromPipelineByPropertyName = $True)] # a bit iffy... ID is common... 49 | [Alias('ID')] 50 | [string[]]$ChannelID, 51 | 52 | [ValidateRange(1,1000)] 53 | [int]$Count = 100, 54 | [switch]$Inclusive, 55 | [datetime]$Before, 56 | [datetime]$After, 57 | [switch]$Paging, 58 | [int]$MaxQueries, 59 | [switch]$Raw 60 | 61 | ) 62 | begin 63 | { 64 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 65 | $body = @{ 66 | channel = $null 67 | count = $count 68 | } 69 | if($Paging) 70 | { 71 | $PageDirection = 'Backward' 72 | } 73 | $BeforeTS = $null 74 | $AfterTS = $null 75 | if($PSBoundParameters.ContainsKey('Before')) 76 | { 77 | $BeforeTS = Get-UnixTime -Date $Before 78 | $body.add('latest', $BeforeTS) 79 | } 80 | if($PSBoundParameters.ContainsKey('After')) 81 | { 82 | $AfterTS = Get-UnixTime -Date $After 83 | $body.add('oldest', $AfterTS) 84 | if(-not $PSBoundParameters.ContainsKey('Before') -and $Paging) 85 | { 86 | $PageDirection = 'Forward' 87 | } 88 | } 89 | if($Inclusive) 90 | { 91 | $body.add('inclusive', 1) 92 | } 93 | $params = @{ 94 | Token = $Token 95 | Method = 'channels.history' 96 | Body = $body 97 | } 98 | $Queries = 1 99 | 100 | } 101 | process 102 | { 103 | foreach($ID in $ChannelID) 104 | { 105 | $has_more = $false 106 | $Params.body.channel = $ID 107 | do 108 | { 109 | if($has_more) 110 | { 111 | if($Params.Body.oldest) 112 | { 113 | [void]$Params.Body.remove('oldest') 114 | } 115 | if($Params.Body.latest) 116 | { 117 | [void]$Params.Body.remove('latest') 118 | } 119 | if($PageDirection -eq 'Forward') 120 | { 121 | 122 | $ts = $response.messages.ts | Sort-Object | 123 | Microsoft.PowerShell.Utility\Select-Object -last 1 124 | $Params.body.oldest = $ts 125 | Write-Debug "Paging Forward.`n$( 126 | [pscustomobject]@{ 127 | After = $After 128 | Before = $Before 129 | LastTS = $response.messages[-1].ts 130 | SortLast = $response.messages.ts | Sort-Object | 131 | Microsoft.PowerShell.Utility\Select-Object -last 1 132 | SortFirst = $response.messages.ts | Sort-Object | 133 | Microsoft.PowerShell.Utility\Select-Object -first 1 134 | ts = $ts 135 | } | Out-String 136 | )" 137 | } 138 | elseif($PageDirection -eq 'Backward') 139 | { 140 | $ts = $response.messages[-1].ts 141 | if($AfterTS -and $ts -lt $AfterTS) 142 | { 143 | Write-Debug "TS is less than AfterTS, breaking!" 144 | break 145 | } 146 | $Params.body.latest = $ts 147 | Write-Debug "Paging Forward.`n$( 148 | [pscustomobject]@{ 149 | After = $After 150 | Before = $Before 151 | LastTS = $response.messages[-1].ts 152 | SortLast = $response.messages.ts | Sort-Object | 153 | Microsoft.PowerShell.Utility\Select-Object -last 1 154 | SortFirst = $response.messages.ts | Sort-Object | 155 | Microsoft.PowerShell.Utility\Select-Object -first 1 156 | ts = $ts 157 | } | Out-String 158 | )" 159 | } 160 | 161 | $has_more = $false 162 | Write-Debug "Body is now:$($params.body | out-string)" 163 | } 164 | $response = Send-SlackApi @params 165 | 166 | Write-Debug "$($Response | Format-List -Property * | Out-String)" 167 | 168 | if ($response.ok) 169 | { 170 | 171 | if($response.psobject.properties.name -contains 'has_more' -and $response.has_more) 172 | { 173 | Write-Debug 'Paging engaged!' 174 | $has_more = $true 175 | } 176 | 177 | if($Raw) 178 | { 179 | $link = "$($Script:PSSlack.ArchiveUri)/$($response.channel)/p$($response.ts -replace '\.')" 180 | $response | Add-Member -MemberType NoteProperty -Name link -Value $link 181 | $response 182 | } 183 | else 184 | { 185 | #Order our messages appropriately according to page direction 186 | if($Paging -and $PageDirection -eq 'Forward') 187 | { 188 | Parse-SlackMessage -InputObject $Response | Sort-Object TimeStamp 189 | } 190 | else 191 | { 192 | Parse-SlackMessage -InputObject $Response 193 | } 194 | } 195 | } 196 | else 197 | { 198 | $response 199 | } 200 | $Queries++ 201 | } 202 | until ( 203 | -not $Paging -or 204 | -not $has_more -or 205 | ($MaxQueries -and $Queries -gt $MaxQueries) 206 | ) 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackReminder.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackReminder 2 | { 3 | <# 4 | .SYNOPSIS 5 | Gets a list Slack reminders for the current user or information about 6 | the specified Slack reminder using the Id parameter. 7 | 8 | .DESCRIPTION 9 | Gets a list Slack reminders for the current user or information about 10 | the specified Slack reminder using the Id parameter. 11 | 12 | .PARAMETER Token 13 | Token to use for the Slack API 14 | 15 | Default value is the value set by Set-PSSlackConfig 16 | 17 | This takes precedence over Uri 18 | 19 | .PARAMETER Proxy 20 | Proxy server to use 21 | 22 | Default value is the value set by Set-PSSlackConfig 23 | 24 | .PARAMETER ReminderId 25 | The ID of the reminder 26 | 27 | .EXAMPLE 28 | # This is a simple example on how to get a list of all reminders 29 | 30 | Get-SlackReminder 31 | 32 | # Gets a list of reminders for the current user based off of the Token 33 | 34 | .EXAMPLE 35 | # This is a simple example on how to get information on a specific reminder 36 | 37 | Get-SlackReminder -Id "RmAZPB1FBP" 38 | 39 | # Gets the information for the reminder specified by the Reminder parameter 40 | 41 | .LINK 42 | https://api.slack.com/methods/reminders.info 43 | 44 | .LINK 45 | https://api.slack.com/methods/reminders.list 46 | 47 | .FUNCTIONALITY 48 | Slack 49 | #> 50 | 51 | param ( 52 | [Parameter()] 53 | [ValidateNotNullOrEmpty()] 54 | [string]$Token = $Script:PSSlack.Token, 55 | [string]$ReminderId 56 | ) 57 | begin 58 | { 59 | $Params = @{} 60 | if ($Proxy) 61 | { 62 | $Params.Proxy = $Proxy 63 | } 64 | } 65 | process 66 | { 67 | $body = @{ } 68 | 69 | if ($ReminderId) 70 | { 71 | $body.reminder = $ReminderId 72 | $Params.add('Body', $Body) 73 | $method = "reminders.info" 74 | } 75 | else 76 | { 77 | $method = "reminders.list" 78 | } 79 | } 80 | end 81 | { 82 | Write-Verbose "Send-SlackApi" 83 | $response = Send-SlackApi @Params -Method $method -Token $Token -ForceVerbose:$ForceVerbose 84 | Parse-SlackReminder -InputObject $response 85 | } 86 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackTeam.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackTeam { 2 | <# 3 | .SYNOPSIS 4 | Get info about the current Slack team. 5 | 6 | .DESCRIPTION 7 | Get info about the current Slack team. 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API. 11 | 12 | Default value is the value set by Set-PSSlackConfig. 13 | 14 | .PARAMETER Raw 15 | Return raw output. 16 | 17 | .EXAMPLE 18 | Get-SlackTeam 19 | 20 | # Gets the default Slack team specified by Get-PSSlackConfig. 21 | 22 | .EXAMPLE 23 | Get-SlackTeam -Token $Token 24 | 25 | # Gets the Slack team specified by $Token. 26 | .EXAMPLE 27 | Get-SlackTeam -Raw 28 | 29 | # Gets the default Slack team specified by Get-PSSlackConfig. 30 | # Returns raw output. 31 | 32 | .EXAMPLE 33 | Get-SlackTeam -Raw -Token $Token 34 | 35 | # Gets the Slack team specified by $Token. 36 | # Returns raw output. 37 | 38 | .FUNCTIONALITY 39 | Slack 40 | 41 | .LINK 42 | https://api.slack.com/methods/team.info 43 | #> 44 | 45 | [CmdletBinding()] 46 | param ( 47 | [string]$Token = $Script:PSSlack.Token, 48 | [switch]$Raw 49 | ) 50 | end 51 | { 52 | $params = @{ 53 | Token = $Token 54 | Method = 'team.info' 55 | } 56 | 57 | $RawTeam = Send-SlackApi @params 58 | 59 | if($Raw) 60 | { 61 | $RawTeam 62 | } 63 | else 64 | { 65 | Parse-SlackTeam -InputObject $RawTeam.team 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackUser.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackUser { 2 | <# 3 | .SYNOPSIS 4 | Get info on a Slack user 5 | 6 | .DESCRIPTION 7 | Get info on a Slack user 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API 11 | 12 | Default value is the value set by Set-PSSlackConfig 13 | 14 | .PARAMETER Presence 15 | Whether to include presence information 16 | 17 | .PARAMETER Billing 18 | Whether to include billing info 19 | 20 | .Parameter Name 21 | Optional. One or more names to search for. Accepts wildcards. 22 | 23 | .Parameter Raw 24 | Return raw output. If specified, Name parameter is ignored 25 | 26 | .EXAMPLE 27 | Get-SlackUser -Token $Token ` 28 | -Name ps* 29 | 30 | # Get users with name starting 'ps' 31 | 32 | .EXAMPLE 33 | Get-SlackUser -Token $Token -Presence -Billing 34 | 35 | # Get all users in the team, including bots, as well as presence and billing info 36 | 37 | .FUNCTIONALITY 38 | Slack 39 | #> 40 | 41 | [cmdletbinding(DefaultParameterSetName = 'Content')] 42 | param ( 43 | [string]$Token = $Script:PSSlack.Token, 44 | [string[]]$Name, 45 | [switch]$Presence, 46 | [switch]$Billing, 47 | [switch]$ExcludeBots, 48 | [switch]$Raw 49 | ) 50 | begin 51 | { 52 | $body = @{} 53 | if($Presence) 54 | { 55 | $body.add('presence', 1) 56 | } 57 | 58 | $params = @{ 59 | Token = $Token 60 | Method = 'users.list' 61 | } 62 | if($body.keys.count -gt 0) 63 | { 64 | $params.add('body', $Body) 65 | } 66 | $RawUsers = Send-SlackApi @params 67 | 68 | $HasWildCard = $False 69 | foreach($Item in $Name) 70 | { 71 | if($Item -match '\*') 72 | { 73 | $HasWildCard = $true 74 | break 75 | } 76 | } 77 | 78 | if($Billing) 79 | { 80 | $BillingInfo = Send-SlackApi -Token $Token -Method team.billableInfo 81 | $UserIDs = $BillingInfo.billable_info.psobject.properties.name 82 | foreach($User in $RawUsers.members) 83 | { 84 | $UserId = $User.Id 85 | if($UserIDs -contains $UserId) 86 | { 87 | Add-Member -InputObject $User -MemberType NoteProperty -Name BillingActive -Value $BillingInfo.billable_info.$UserId.billing_active -Force 88 | } 89 | } 90 | } 91 | 92 | if($Name -and -not $HasWildCard) 93 | { 94 | # torn between independent queries, or filtering users.list 95 | # submit a PR if this isn't performant enough or doesn't make sense. 96 | $Users = $RawUsers.members | 97 | Where-Object {$Name -Contains $_.name} 98 | } 99 | elseif ($Name -and $HasWildCard) 100 | { 101 | $AllUsers = $RawUsers.members 102 | 103 | # allow like operator on each channel requested in the param, avoid dupes 104 | $UserHash = [ordered]@{} 105 | foreach($SlackUser in $AllUsers) 106 | { 107 | foreach($Username in $Name) 108 | { 109 | if($SlackUser.Name -like $Username -and -not $UserHash.Contains($SlackUser.id)) 110 | { 111 | $UserHash.Add($SlackUser.Id, $SlackUser) 112 | } 113 | } 114 | } 115 | $Users = $UserHash.Values 116 | } 117 | else # nothing specified 118 | { 119 | $Users = $RawUsers.members 120 | } 121 | 122 | if($Raw) 123 | { 124 | $RawUsers 125 | } 126 | else 127 | { 128 | Parse-SlackUser -InputObject $Users 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackUserGroup.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackUserGroup { 2 | <# 3 | .SYNOPSIS 4 | Get Slack user groups 5 | .DESCRIPTION 6 | Get Slack user groups 7 | .PARAMETER Token 8 | Token to use for the Slack API 9 | Default value is the value set by Set-PSSlackConfig 10 | .PARAMETER IncludeUsers 11 | If specified, include users 12 | 13 | If you update the user map ahead of time, we parse user IDs to user names: 14 | Get-SlackUserMap -Update ahead of team 15 | .PARAMETER IncludeDisabled 16 | If specified, include disabled users 17 | .Parameter Raw 18 | Return raw output. If specified, Name parameter is ignored 19 | .EXAMPLE 20 | Get-SlackUserGroup 21 | # Get slack user group info 22 | .EXAMPLE 23 | $null = Get-SlackUserMap -Update 24 | Get-SlackUserGroup -IncludeUsers 25 | 26 | # Get user id to name map, pull user groups and their members 27 | .FUNCTIONALITY 28 | Slack 29 | #> 30 | [cmdletbinding()] 31 | param ( 32 | [string]$Token = $Script:PSSlack.Token, 33 | [switch]$IncludeUsers, 34 | [switch]$IncludeDisabled, 35 | [switch]$Raw 36 | ) 37 | begin 38 | { 39 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 40 | $body = @{} 41 | if($IncludeUsers) { 42 | $body.add('include_users',$true) 43 | } 44 | if($IncludeDisabled) { 45 | $body.add('include_disabled',$true) 46 | } 47 | 48 | $params = @{ 49 | Token = $Token 50 | Method = 'usergroups.list' 51 | } 52 | if($body.keys.count -gt 0) { 53 | $params.add('body',$body) 54 | } 55 | 56 | $RawGroups = Send-SlackApi @params 57 | if($Raw) { 58 | $RawGroups 59 | } 60 | else { 61 | Parse-SlackUserGroup -InputObject $RawGroups.usergroups 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /PSSlack/Public/Get-SlackUserMap.ps1: -------------------------------------------------------------------------------- 1 | function Get-SlackUserMap { 2 | <# 3 | .SYNOPSIS 4 | Get a map of Slack IDs to friendly names 5 | 6 | .DESCRIPTION 7 | Get a map of Slack IDs to friendly names 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API 11 | 12 | Default value is the value set by Set-PSSlackConfig 13 | 14 | .Parameter Update 15 | If specified, update PSSlack's cached map of names and IDs 16 | 17 | .Parameter Raw 18 | Return raw output. If specified, Name parameter is ignored 19 | 20 | .EXAMPLE 21 | Get-SlackUserMap 22 | # Get map of names to IDs from cached PSSlack data 23 | 24 | .EXAMPLE 25 | Get-SlackUserMap -Update 26 | # Get map of names to IDs from Slack, and update cached PSSlack data 27 | 28 | .FUNCTIONALITY 29 | Slack 30 | #> 31 | [cmdletbinding(DefaultParameterSetName = 'Content')] 32 | param ( 33 | [string]$Token = $Script:PSSlack.Token, 34 | [switch]$Raw, 35 | [switch]$Update 36 | ) 37 | begin 38 | { 39 | if(-not $Update) 40 | { 41 | return $Script:_PSSlackUserMap 42 | } 43 | $params = @{ 44 | Token = $Token 45 | Method = 'users.list' 46 | } 47 | if($body.keys.count -gt 0) 48 | { 49 | $params.add('body', $Body) 50 | } 51 | $RawUsers = Send-SlackApi @params 52 | $AllUsers = $RawUsers.members 53 | foreach($SlackUser in $RawUsers.members) 54 | { 55 | $UID = $SlackUser.id 56 | $Name = $SlackUser.name 57 | if($Script:_PSSlackUserMap.ContainsKey($Name)) 58 | { 59 | $Script:_PSSlackUserMap[$Name] = $UID 60 | } 61 | else 62 | { 63 | $Script:_PSSlackUserMap.add($Name, $UID) 64 | } 65 | } 66 | $Script:_PSSlackUserMap 67 | } 68 | } -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackAction.ps1: -------------------------------------------------------------------------------- 1 | 2 | function New-SlackAction 3 | { 4 | <# 5 | .SYNOPSIS 6 | Creates an action to use in a Slack message attachment. 7 | 8 | .DESCRIPTION 9 | Creates an action to use in a Slack message attachment. 10 | 11 | Used to create actions for Slack message attachments. 12 | 13 | .PARAMETER Name 14 | Provide a string to give this specific action a name. 15 | The name will be returned to your Action URL along with the message's callback_id when this action is invoked. 16 | Use it to identify this particular response path. 17 | If multiple actions share the same name, only one of them can be in a triggered state. 18 | 19 | .PARAMETER Text 20 | The user-facing label for the message button or menu representing this action. 21 | Cannot contain markup. Best to keep these short and decisive. 22 | Use a maximum of 30 characters or so for best results across form factors. 23 | 24 | .PARAMETER Type 25 | Provide button when this action is a message button or provide select when the action is a message menu. 26 | 27 | .PARAMETER Value 28 | Provide a string identifying this specific action. It will be sent to your Action URL along with the name and attachment's callback_id. 29 | If providing multiple actions with the same name, value can be strategically used to differentiate intent. 30 | Your value may contain up to 2000 characters. 31 | 32 | .PARAMETER Confirmation 33 | If you provide a JSON hash of confirmation fields, your button or menu will pop up a dialog with your 34 | indicated text and choices, giving them one last chance to avoid a destructive action or other undesired outcome. 35 | 36 | .PARAMETER Style 37 | Used only with message buttons, this decorates buttons with extra visual importance, 38 | which is especially useful when providing logical default action or highlighting a destructive activity. 39 | default — Yes, it's the default. Buttons will look simple. 40 | primary — Use this sparingly, when the button represents a key action to accomplish. You should probably only ever have one primary button within a set. 41 | danger — Use this when the consequence of the button click will result in the destruction of something, like a piece of data stored on your servers. Use even more sparingly than primary. 42 | 43 | .PARAMETER Options 44 | Used only with message menus. The individual options to appear in this menu, provided as an array of option fields. 45 | Required when data_source is static or otherwise unspecified. A maximum of 100 options can be provided in each menu. 46 | 47 | Options can be created using the New-SlackActionOption command 48 | 49 | .PARAMETER ExistingAction 50 | One or more actions to add this action to. 51 | 52 | Allows you to chain calls to this function: 53 | New-SlackAction ... | New-SlackAction ... 54 | 55 | .EXAMPLE 56 | # This is a simple example illustrating some common options 57 | # when constructing an action 58 | 59 | $WebhookUri = "https://hooks.slack.com/services/SomeUniqueId" 60 | 61 | $actions = New-SlackAction -Name Acknowledge ` 62 | -Text Acknowledge ` 63 | -Type button | 64 | New-SlackAction -Name Dismiss ` 65 | -Text Dismiss ` 66 | -Type button 67 | 68 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 69 | -Title 'Failed to process account' ` 70 | -Actions $actions ` 71 | -Fallback 'Your client is bad' | 72 | New-SlackMessage | 73 | Send-SlackMessage -Uri $WebhookUri 74 | 75 | # We create an action object with an 'Acknowledge' button and a 76 | # Dismiss button. 77 | # Creates an attachment with the button created in the action object 78 | # Creates a message from that attachment and sents it with a uri 79 | 80 | .LINK 81 | https://github.com/RamblingCookieMonster/PSSlack 82 | 83 | .LINK 84 | https://api.slack.com/docs/interactive-message-field-guide#action_fields 85 | #> 86 | [OutputType([System.Collections.Hashtable])] 87 | Param 88 | ( 89 | [Parameter(ValueFromPipeline = $True)] 90 | [PSTypeName('PSSlack.Action')] 91 | $ExistingAction, 92 | 93 | [Parameter(Mandatory = $true, 94 | Position = 0)] 95 | [String]$Name, 96 | 97 | [Parameter(Mandatory = $true, 98 | Position = 1)] 99 | [ValidateLength(1, 30)] 100 | [String]$Text, 101 | 102 | [Parameter(Mandatory = $true, 103 | Position = 2)] 104 | [ValidateSet("button", 105 | "select")] 106 | [String]$Type, 107 | 108 | [ValidateLength(0, 3000)] 109 | [String]$Value, 110 | 111 | [PSTypeName('PSSlack.ActionConfirmation')] 112 | $Confirmation, 113 | 114 | [ValidateSet("default", 115 | "primary", 116 | "danger")] 117 | [String]$Style, 118 | 119 | [PSTypeName('PSSlack.ActionOption')] 120 | $Options 121 | ) 122 | 123 | Process 124 | { 125 | $Action = @{} 126 | switch ($PSBoundParameters.Keys) 127 | { 128 | 'name' {$Action.name = $Name} 129 | 'text' {$Action.text = $Text} 130 | 'type' {$Action.type = $Type} 131 | 'value' {$Action.value = $Value} 132 | 'Confirmation' {$Action.confirm = $Confirmation} 133 | 'style' { $Action.style = $Style} 134 | 'options' { $Action.options = $Options} 135 | } 136 | 137 | Add-ObjectDetail -InputObject $Action -TypeName 'PSSlack.Action' -Passthru $False 138 | 139 | if ($ExistingAction) 140 | { 141 | @($ExistingAction) + $Action 142 | } 143 | else 144 | { 145 | $Action 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackActionConfirmation.ps1: -------------------------------------------------------------------------------- 1 | 2 | function New-SlackActionConfirmation 3 | { 4 | <# 5 | .SYNOPSIS 6 | Creates an action confirmation to use in a Slack action. 7 | 8 | .DESCRIPTION 9 | Creates an action confirmation to use in a Slack action. 10 | 11 | .PARAMETER Text 12 | Describe in detail the consequences of the action and contextualize your button text choices. 13 | Use a maximum of 30 characters or so for best results across form factors. 14 | 15 | .PARAMETER Title 16 | Title the pop up window. Please be brief. 17 | 18 | .PARAMETER OkText 19 | The text label for the button to continue with an action. Keep it short. Defaults to Okay. 20 | 21 | .PARAMETER DismissText 22 | The text label for the button to cancel the action. Keep it short. Defaults to Cancel. 23 | 24 | .EXAMPLE 25 | 26 | # This example illustrates a pattern where you might 27 | # want to add an action (button or menu) to your Slack attachment. 28 | # This can be useful when working with Slack bots or integrations that 29 | # make calls out with the action data. Activating the button pops up a 30 | # confirmation dialog. 31 | 32 | # Create an action using the New-SlackAction command 33 | $confirmation = New-SlackActionConfirmation -Text "Are you sure?" 34 | 35 | $action = New-SlackAction -Name Acknowledge ` 36 | -Text Acknowledge ` 37 | -Type button ` 38 | -Confirmation $confirmation 39 | 40 | $WebhookUri = "https://hooks.slack.com/services/SomeUniqueId" 41 | 42 | # Construct and send the message! 43 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 44 | -Title 'Failed to process account' ` 45 | -Actions $action ` 46 | -Fallback 'Your client is bad' | 47 | New-SlackMessage | 48 | Send-SlackMessage -Uri $WebhookUri 49 | 50 | # We create an action object with an 'Acknowledge' button 51 | # Creates an attachment with the button created in the action object 52 | # Creates a message from that attachment and sents it with a uri 53 | 54 | .LINK 55 | https://github.com/RamblingCookieMonster/PSSlack 56 | 57 | .LINK 58 | https://api.slack.com/docs/interactive-message-field-guide#confirmation_fields 59 | #> 60 | [OutputType([System.Collections.Hashtable])] 61 | Param 62 | ( 63 | [Parameter(Mandatory = $true, 64 | Position = 0)] 65 | [ValidateLength(1, 30)] 66 | [String]$Text, 67 | 68 | [String]$Title, 69 | 70 | [String]$OkText, 71 | 72 | [String]$DismissText 73 | ) 74 | 75 | Process 76 | { 77 | $ActionConfirmation = @{} 78 | switch ($PSBoundParameters.Keys) 79 | { 80 | 'text' {$ActionConfirmation.text = $Text} 81 | 'title' {$ActionConfirmation.title = $Title} 82 | 'oktext' {$ActionConfirmation.ok_text = $OkText} 83 | 'dismissText' {$ActionConfirmation.dismiss_text = $DismissText} 84 | } 85 | 86 | Add-ObjectDetail -InputObject $ActionConfirmation -TypeName 'PSSlack.ActionConfirmation' -Passthru $False 87 | 88 | $ActionConfirmation 89 | } 90 | } -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackActionOption.ps1: -------------------------------------------------------------------------------- 1 | 2 | function New-SlackActionOption 3 | { 4 | <# 5 | .SYNOPSIS 6 | Creates an action option to use in a Slack action 7 | 8 | .DESCRIPTION 9 | Creates an action option to use in a Slack action 10 | 11 | .PARAMETER Text 12 | A short, user-facing string to label this option to users. 13 | Use a maximum of 30 characters or so for best results across, you guessed it, form factors. 14 | 15 | .PARAMETER Value 16 | A short string that identifies this particular option to your application. 17 | It will be sent to your Action URL when this option is selected. 18 | While there's no limit to the value of your Slack app, this value may contain up to only 2000 characters. 19 | 20 | .PARAMETER Description 21 | A user-facing string that provides more details about this option. Also should contain up to 30 characters. 22 | 23 | .PARAMETER ExistingActionOption 24 | One or more action options to add this action option to. 25 | 26 | Allows you to chain calls to this function: 27 | New-SlackActionOption ... | New-SlackActionOption ... 28 | 29 | .EXAMPLE 30 | # This is a simple example illustrating some common options 31 | # when constructing an action 32 | 33 | $WebhookUri = "https://hooks.slack.com/services/SomeUniqueId" 34 | 35 | $Options = New-SlackActionOption -Text "Option 1" ` 36 | -Value "option1" | 37 | New-SlackActionOption -Text "Option 2" ` 38 | -Value "option2" 39 | 40 | $actions = New-SlackAction -Name Acknowledge ` 41 | -Text Menu ` 42 | -Type select ` 43 | -Options $Options 44 | 45 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 46 | -Title 'Failed to process account' ` 47 | -Actions $actions ` 48 | -Fallback 'Your client is bad' | 49 | New-SlackMessage | 50 | Send-SlackMessage -Uri $WebhookUri 51 | 52 | # We create an action object with an menu that contains two options 53 | # Creates an attachment with the button created in the action object 54 | # Creates a message from that attachment and sents it with a uri 55 | 56 | .LINK 57 | https://github.com/RamblingCookieMonster/PSSlack 58 | 59 | .LINK 60 | https://api.slack.com/docs/interactive-message-field-guide#option_fields 61 | #> 62 | Param 63 | ( 64 | [Parameter(ValueFromPipeline = $True)] 65 | [PSTypeName('PSSlack.ActionOption')] 66 | $ExistingActionOption, 67 | 68 | [Parameter(Mandatory = $true, 69 | Position = 0)] 70 | [ValidateLength(1, 30)] 71 | [String]$Text, 72 | 73 | [Parameter(Mandatory = $true, 74 | Position = 1)] 75 | [ValidateLength(1, 2000)] 76 | [String]$Value, 77 | 78 | [ValidateLength(1, 30)] 79 | [String]$Description 80 | ) 81 | 82 | Process 83 | { 84 | $ActionOption = @{} 85 | switch ($PSBoundParameters.Keys) 86 | { 87 | 'text' {$ActionOption.text = $Text} 88 | 'value' {$ActionOption.value = $Value} 89 | 'description' {$ActionOption.description = $Description} 90 | } 91 | 92 | Add-ObjectDetail -InputObject $ActionOption -TypeName 'PSSlack.ActionOption' -Passthru $False 93 | 94 | if ($ExistingActionOption) 95 | { 96 | @($ExistingActionOption) + $ActionOption 97 | } 98 | else 99 | { 100 | $ActionOption 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackField.ps1: -------------------------------------------------------------------------------- 1 | function New-SlackField 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates an array of Slack message attachment fields from an arbitrary PowerShell object 6 | 7 | .DESCRIPTION 8 | Creates an array of Slack message attachment fields from an arbitrary PowerShell object 9 | 10 | By default, retrieves all properties. You can use parameters to restrict this. 11 | 12 | .PARAMETER InputObject 13 | A plain-text summary of the attachment. This text will be used in clients that don't show formatted text (eg. IRC, mobile notifications) and should not contain any markup. 14 | 15 | .PARAMETER Short 16 | Whether to try to fit the field into a table, rather than a list 17 | 18 | .PARAMETER IncludeProperty 19 | If specified, include only these properties from the InputObject's properties 20 | 21 | .PARAMETER ExcludeProperty 22 | If specified, exclude these properties from the InputObject's properties 23 | 24 | .PARAMETER MemberType 25 | If specified, restrict the properties we discover from InputObject to these member types. 26 | 27 | Defaults to NoteProperty, Property, ScriptProperty 28 | 29 | .EXAMPLE 30 | [pscustomobject]@{ 31 | One = 1 32 | Two = 2 33 | Five = 5 34 | } | New-SlackField 35 | 36 | # Simple illustration: pipe anything that produces an object into New-SlackField. 37 | 38 | .EXAMPLE 39 | $Fields = [pscustomobject]@{ 40 | AlertName = 'The System Is Down' 41 | Severity = 11 42 | ImpactedDepartment = 'All' 43 | URL = 'https://www.youtube.com/watch?v=TmpRs7xN06Q' 44 | } | New-SlackField -Short 45 | 46 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 47 | -Fields $Fields ` 48 | -Fallback 'Your client is bad' | 49 | New-SlackMessage -Channel '@wframe' ` 50 | -IconEmoji :bomb: ` 51 | -AsUser ` 52 | -Username 'SCOM Bot' | 53 | Send-SlackMessage -Token $Token 54 | 55 | # Build an imaginary SCOM alert, send it through New-SlackField, short mode 56 | # Send a Slack message with that field in an attachment 57 | 58 | .LINK 59 | https://github.com/RamblingCookieMonster/PSSlack 60 | 61 | .LINK 62 | https://api.slack.com/docs/attachments 63 | 64 | .LINK 65 | https://api.slack.com/methods/chat.postMessage 66 | #> 67 | [CmdletBinding(DefaultParameterSetName='InputObject')] 68 | Param 69 | ( 70 | [Parameter(Mandatory = $true, 71 | ParameterSetName = 'InputObject', 72 | ValueFromPipeline = $True)] 73 | [Object[]] 74 | $InputObject, 75 | 76 | [switch]$Short, 77 | 78 | [string[]]$IncludeProperty, 79 | 80 | [string[]]$ExcludeProperty, 81 | 82 | [string[]]$MemberType 83 | ) 84 | 85 | Process 86 | { 87 | foreach($Object in $InputObject) 88 | { 89 | $Params = @{} 90 | if($ExcludeProperty) 91 | { 92 | $Params.add('ExcludeProperty', $ExcludeProperty) 93 | } 94 | if($MemberType) 95 | { 96 | $Params.add('MemberType', $MemberType) 97 | } 98 | $Properties = Get-PropertyOrder @params -InputObject $Object 99 | 100 | if($IncludeProperty) 101 | { 102 | $Properties = $Properties | Where-Object {$IncludeProperty -contains $_} 103 | } 104 | 105 | foreach($Property in $Properties) 106 | { 107 | $Field = @{ 108 | title = $Property 109 | value = $Object.$Property 110 | } 111 | if($Short) 112 | { 113 | $Field.add('short',$true) 114 | } 115 | Add-ObjectDetail -InputObject $Field -TypeName 'PSSlack.Field' 116 | } 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackMessage.ps1: -------------------------------------------------------------------------------- 1 | function New-SlackMessage 2 | { 3 | <# 4 | .SYNOPSIS 5 | Construct a new Slack message 6 | 7 | .DESCRIPTION 8 | Construct a new Slack message 9 | 10 | Note that this does not send a message 11 | It produces a message to send with Send-SlackMessage 12 | 13 | .PARAMETER Channel 14 | Channel, private group, or IM channel to send message to. Can be an encoded ID, or a name. 15 | 16 | .PARAMETER Text 17 | Text of the message to send 18 | 19 | See formatting spec for more information. https://api.slack.com/docs/formatting 20 | 21 | .PARAMETER Username 22 | Set your bot's user name. Must be used in conjunction with as_user set to false, otherwise ignored 23 | 24 | See authorship details: https://api.slack.com/methods/chat.postMessage#authorship 25 | 26 | .PARAMETER IconUrl 27 | URL to an image to use as the icon for this message. 28 | 29 | If using a token, must be used in conjunction with as_user set to false, otherwise ignored. 30 | 31 | See authorship details: https://api.slack.com/methods/chat.postMessage#authorship 32 | 33 | .PARAMETER IconEmoji 34 | Emoji to use as the icon for this message. 35 | Overrides icon_url. 36 | 37 | If using a token, must be used in conjunction with as_user set to false, otherwise ignored 38 | 39 | .PARAMETER AsUser 40 | Use true to post the message as the authed user, instead of as a bot. Defaults to false. 41 | 42 | Only used when authorizing with a token 43 | 44 | See authorship details: https://api.slack.com/methods/chat.postMessage#authorship 45 | 46 | .PARAMETER LinkNames 47 | Find and link channel names and usernames. 48 | 49 | .PARAMETER Thread 50 | Optional thread where file is sent. Needs to be the parent thread id which is either the ts or thread_ts. 51 | 52 | Can find a ts by querying https://api.slack.com/methods/conversations.history 53 | 54 | .PARAMETER Parse 55 | Change how messages are treated. Defaults to none 56 | 57 | If set to full, channels like #general and usernames like @bob will be linkified. 58 | 59 | More details here: https://api.slack.com/docs/formatting#linking_to_channels_and_users 60 | 61 | .PARAMETER UnfurlLinks 62 | Use true to enable unfurling of primarily text-based content. 63 | 64 | .PARAMETER UnfurlMedia 65 | Use false to disable unfurling of media content. 66 | 67 | .PARAMETER Attachments 68 | Optional rich structured message attachments. 69 | 70 | Provide one or more hash tables created using New-SlackMessageAttachment 71 | 72 | See attachments spec https://api.slack.com/docs/attachments 73 | 74 | .EXAMPLE 75 | # This is a simple example illustrating some common options 76 | # when constructing a message attachment 77 | # giving you a richer message 78 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 79 | 80 | New-SlackMessageAttachment -Color $_PSSlackColorMap.red ` 81 | -Title 'The System Is Down' ` 82 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 83 | -Text 'Please Do The Needful' ` 84 | -Pretext 'Everything is broken' ` 85 | -AuthorName 'SCOM Bot' ` 86 | -AuthorIcon 'http://ramblingcookiemonster.github.io/images/tools/wrench.png' ` 87 | -Fallback 'Your client is bad' | 88 | New-SlackMessage -Channel '@wframe' ` 89 | -IconEmoji :bomb: | 90 | Send-SlackMessage -Token $Token 91 | 92 | # Create a message attachment with details about an alert 93 | # Attach this to a slack message sending to the devnull channel 94 | # Send the newly created message using a token 95 | 96 | .EXAMPLE 97 | # This example demonstrates that you can chain new attachments 98 | # together to form a multi-attachment message 99 | 100 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 101 | 102 | New-SlackMessageAttachment -Color $_PSSlackColorMap.red ` 103 | -Title 'The System Is Down' ` 104 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 105 | -Text 'Everybody panic!' ` 106 | -Pretext 'Everything is broken' ` 107 | -Fallback 'Your client is bad' | 108 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 109 | -Title 'The Other System Is Down' ` 110 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 111 | -Text 'Please Do The Needful' ` 112 | -Fallback 'Your client is bad' | 113 | New-SlackMessage -Channel '@wframe' ` 114 | -IconEmoji :bomb: ` 115 | -AsUser ` 116 | -Username 'SCOM Bot' | 117 | Send-SlackMessage -Token $Token 118 | 119 | # Create an attachment, create another attachment, 120 | # add these to a message, 121 | # and send with a token 122 | 123 | .EXAMPLE 124 | 125 | # This example illustrates a pattern where you might 126 | # want to send output from a script; you might 127 | # include errors, successful items, or other output 128 | 129 | # Pretend we're in a script, and caught an exception of some sort 130 | $Fail = [pscustomobject]@{ 131 | samaccountname = 'bob' 132 | operation = 'Remove privileges' 133 | status = "An error message" 134 | timestamp = (Get-Date).ToString() 135 | } 136 | 137 | # Create an array from the properties in our fail object 138 | $Fields = @() 139 | foreach($Prop in $Fail.psobject.Properties.Name) 140 | { 141 | $Fields += @{ 142 | title = $Prop 143 | value = $Fail.$Prop 144 | short = $true 145 | } 146 | } 147 | 148 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 149 | 150 | # Construct and send the message! 151 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 152 | -Title 'Failed to process account' ` 153 | -Fields $Fields ` 154 | -Fallback 'Your client is bad' | 155 | New-SlackMessage -Channel 'devnull' | 156 | Send-SlackMessage -Uri $uri 157 | 158 | # We build up a pretend error object, and send each property to a 'Fields' array 159 | # Creates an attachment with the fields from our error 160 | # Creates a message fromthat attachment and sents it with a uri 161 | 162 | .FUNCTIONALITY 163 | Slack 164 | #> 165 | [CmdletBinding()] 166 | [OutputType([System.Collections.Hashtable],[String])] 167 | Param 168 | ( 169 | [string]$Channel, 170 | [string]$Text, 171 | [string]$Username, 172 | [string]$IconUrl, 173 | [string]$IconEmoji, 174 | [switch]$AsUser, 175 | [switch]$LinkNames, 176 | [string]$Thread, 177 | 178 | [validateset('full','none')] 179 | [string]$Parse, 180 | 181 | [validateset($True, $False)] 182 | [bool]$UnfurlLinks, 183 | 184 | [validateset($True, $False)] 185 | [bool]$UnfurlMedia, 186 | 187 | [Parameter(Mandatory=$true, 188 | ValueFromPipeline = $true, 189 | Position=1)] 190 | [PSTypeName('PSSlack.MessageAttachment')] 191 | [System.Collections.Hashtable[]] 192 | $Attachments 193 | ) 194 | Begin 195 | { 196 | $AllAttachments = @() 197 | } 198 | Process 199 | { 200 | foreach($Attachment in $Attachments) 201 | { 202 | $AllAttachments += $Attachment 203 | } 204 | } 205 | End 206 | { 207 | $body = @{} 208 | 209 | switch ($psboundparameters.keys) { 210 | 'channel' { $body.channel = $Channel} 211 | 'text' { $body.text = $text} 212 | 'username' { $body.username = $username} 213 | 'asuser' { $body.as_user = $AsUser} 214 | 'iconurl' { $body.icon_url = $iconurl} 215 | 'iconemoji' { $body.icon_emoji = $iconemoji} 216 | 'linknames' { $body.link_names = 1} 217 | 'thread' {$body.thread_ts = $Thread} 218 | 'Parse' { $body.Parse = $Parse} 219 | 'UnfurlLinks' { $body.Unfurl_Links = $UnfurlLinks} 220 | 'UnfurlMedia' { $body.Unfurl_Media = $UnfurlMedia} 221 | 'iconurl' { $body.icon_url = $iconurl} 222 | 'attachments' { $body.attachments = @($AllAttachments)} 223 | } 224 | 225 | Add-ObjectDetail -InputObject $body -TypeName PSSlack.Message 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackMessageAttachment.ps1: -------------------------------------------------------------------------------- 1 | #Borrowed from https://github.com/jgigler/Powershell.Slack - thanks @jgigler et al! 2 | function New-SlackMessageAttachment 3 | { 4 | <# 5 | .SYNOPSIS 6 | Creates a rich notification (Attachment) to use in a Slack message. 7 | 8 | .DESCRIPTION 9 | Creates a rich notification (Attachment) to use in a Slack message. 10 | 11 | Used to create Atachment message payloads for Slack. 12 | Attachemnts are a way of crafting richly-formatted messages in Slack. 13 | They can be as simple as a single plain text message, 14 | to as complex as a multi-line message with pictures, links and tables. 15 | 16 | .PARAMETER Fallback 17 | A plain-text summary of the attachment. This text will be used in clients that don't show formatted text (eg. IRC, mobile notifications) and should not contain any markup. 18 | 19 | .PARAMETER Severity 20 | This value is used to color the border along the left side of the message attachment. This parameter cannot be used in conjunction with the "Color" parameter. 21 | 22 | Only good, bad and warning are accepted by this parameter. 23 | 24 | .PARAMETER Color 25 | This value is used to color the border along the left side of the message attachment. 26 | 27 | There are two options for this value: 28 | 29 | Use Hex Web Colors to define the color. e.g. -Color #FF0000 30 | Use $_PSSlackColorMap. e.g. -Color $_PSSlackColorMap.orange 31 | 32 | See $_PSSlackColorMap for a full list of colors. 33 | 34 | This parameter cannot be used in conjuction with the Severity Parameter. 35 | 36 | .PARAMETER Pretext 37 | This is optional text that appears above the message attachment block. 38 | 39 | .PARAMETER AuthorName 40 | Small text used to display the author's name. 41 | 42 | .PARAMETER AuthorLink 43 | A valid URL that will hyperlink the AuthorName text mentioned above. 44 | 45 | Will only work if AuthorName is present. 46 | 47 | .PARAMETER AuthorIcon 48 | A valid URL that displays a small 16x16px image to the left of the AuthorName text. 49 | 50 | Will only work if AuthorName is present. 51 | 52 | .PARAMETER Title 53 | The title is displayed as larger, bold text near the top of the message attachment. 54 | 55 | .PARAMETER TitleLink 56 | If the title link is specified then it turns the Title into a hyperlink that the user can click. 57 | 58 | .PARAMETER Text 59 | This is the main text in a message attachment, and can contain standard message markup. 60 | Not to be confused with Pretext which would appear above this. 61 | 62 | .PARAMETER ImageURL 63 | A valid URL to an image file that will be displayed inside a message attachment. 64 | 65 | .PARAMETER ThumbURL 66 | A valid URL to an image file that will be displayed as a thumbnail on the right side of a message attachment. 67 | 68 | .PARAMETER Fields 69 | One or more hashtables contained provided here will be displayed in a table inside the message attachment. 70 | 71 | Each hashtable provided must contain a "title" key and a "value" key. 72 | Optionally it may also contain "Short" which is a boolean parameter. 73 | 74 | .PARAMETER Actions 75 | A collection of actions (buttons or menus) to include in the attachment. 76 | Required when using message buttons or message menus. A maximum of 5 actions per attachment may be provided. 77 | Actions can be created with the New-SlackAction command 78 | 79 | .PARAMETER CallbackId 80 | The provided string will act as a unique identifier for the collection of buttons within the attachment. 81 | It will be sent back to your message button action URL with each invoked action. 82 | This field is required when the attachment contains message buttons. 83 | It is key to identifying the interaction you're working with. 84 | 85 | .PARAMETER MarkDownFields 86 | One or more fields (text, pretext, fields) to enable markdown-esque formatting in. 87 | 88 | The formatting is described here: https://get.slack.help/hc/en-us/articles/202288908-How-can-I-add-formatting-to-my-messages- 89 | 90 | .PARAMETER ExistingAttachment 91 | One or more attachments to add this attachment to. 92 | 93 | Allows you to chain calls to this function: 94 | New-SlackMessageAttachment ... | New-SlackMessageAttachment ... 95 | 96 | .EXAMPLE 97 | # This is a simple example illustrating some common options 98 | # when constructing a message attachment 99 | # giving you a richer message 100 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 101 | 102 | New-SlackMessageAttachment -Color $_PSSlackColorMap.red ` 103 | -Title 'The System Is Down' ` 104 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 105 | -Text 'Please Do The Needful' ` 106 | -Pretext 'Everything is broken' ` 107 | -AuthorName 'SCOM Bot' ` 108 | -AuthorIcon 'http://ramblingcookiemonster.github.io/images/tools/wrench.png' ` 109 | -Fallback 'Your client is bad' | 110 | New-SlackMessage -Channel '@wframe' ` 111 | -IconEmoji :bomb: | 112 | Send-SlackMessage -Token $Token 113 | 114 | # Create a message attachment with details about an alert 115 | # Attach this to a slack message sending to the devnull channel 116 | # Send the newly created message using a token 117 | 118 | .EXAMPLE 119 | # This example demonstrates that you can chain new attachments 120 | # together to form a multi-attachment message 121 | 122 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 123 | 124 | New-SlackMessageAttachment -Color $_PSSlackColorMap.red ` 125 | -Title 'The System Is Down' ` 126 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 127 | -Text 'Everybody panic!' ` 128 | -Pretext 'Everything is broken' ` 129 | -Fallback 'Your client is bad' | 130 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 131 | -Title 'The Other System Is Down' ` 132 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 133 | -Text 'Please Do The Needful' ` 134 | -Fallback 'Your client is bad' | 135 | New-SlackMessage -Channel '@wframe' ` 136 | -IconEmoji :bomb: ` 137 | -AsUser ` 138 | -Username 'SCOM Bot' | 139 | Send-SlackMessage -Token $Token 140 | 141 | # Create an attachment, create another attachment, 142 | # add these to a message, 143 | # and send with a token 144 | 145 | .EXAMPLE 146 | 147 | # This example illustrates a pattern where you might 148 | # want to send output from a script; you might 149 | # include errors, successful items, or other output 150 | 151 | # Pretend we're in a script, and caught an exception of some sort 152 | $Fail = [pscustomobject]@{ 153 | samaccountname = 'bob' 154 | operation = 'Remove privileges' 155 | status = "An error message" 156 | timestamp = (Get-Date).ToString() 157 | } 158 | 159 | # Create an array from the properties in our fail object 160 | $Fields = @() 161 | foreach($Prop in $Fail.psobject.Properties.Name) 162 | { 163 | $Fields += @{ 164 | title = $Prop 165 | value = $Fail.$Prop 166 | short = $true 167 | } 168 | } 169 | 170 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 171 | 172 | # Construct and send the message! 173 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 174 | -Title 'Failed to process account' ` 175 | -Fields $Fields ` 176 | -Fallback 'Your client is bad' | 177 | New-SlackMessage -Channel 'devnull' | 178 | Send-SlackMessage -Token $Token 179 | 180 | # We build up a pretend error object, and send each property to a 'Fields' array 181 | # Creates an attachment with the fields from our error 182 | # Creates a message fromthat attachment and sents it with a uri 183 | 184 | .EXAMPLE 185 | 186 | # This example illustrates a pattern where you might 187 | # want to add an action (button or menu) to your Slack attachment. 188 | # This can be useful when working with Slack bots or integrations that 189 | # make calls out with the action data 190 | 191 | # Create an action using the New-SlackAction command 192 | $action = New-SlackAction -Name Acknowledge -Text Acknowledge -Type button 193 | 194 | $WebhookUri = "https://hooks.slack.com/services/SomeUniqueId" 195 | 196 | # Construct and send the message! 197 | New-SlackMessageAttachment -Color $_PSSlackColorMap.orange ` 198 | -Title 'Failed to process account' ` 199 | -Actions $action ` 200 | -Fallback 'Your client is bad' | 201 | New-SlackMessage | 202 | Send-SlackMessage -Uri $WebhookUri 203 | 204 | # We create an action object with an 'Acknowledge' button 205 | # Creates an attachment with the button created in the action object 206 | # Creates a message from that attachment and sents it with a uri 207 | 208 | .LINK 209 | https://github.com/RamblingCookieMonster/PSSlack 210 | 211 | .LINK 212 | https://api.slack.com/docs/attachments 213 | 214 | .LINK 215 | https://api.slack.com/docs/interactive-message-field-guide 216 | 217 | .LINK 218 | https://api.slack.com/methods/chat.postMessage 219 | #> 220 | [CmdletBinding(DefaultParameterSetName='Severity')] 221 | [OutputType([System.Collections.Hashtable])] 222 | Param 223 | ( 224 | [Parameter(ValueFromPipeline = $True)] 225 | [PSTypeName('PSSlack.MessageAttachment')] 226 | [object[]] 227 | $ExistingAttachment, 228 | 229 | [Parameter(Mandatory=$true, 230 | Position=0)] 231 | [String]$Fallback, 232 | 233 | [Parameter(Mandatory=$false, 234 | ParameterSetName='Severity')] 235 | [ValidateSet("good", 236 | "warning", 237 | "danger")] 238 | [String]$Severity, 239 | 240 | [Parameter(Mandatory=$false, 241 | ParameterSetName='Color')] 242 | [Alias("Colour")] 243 | $Color, 244 | 245 | [String]$AuthorName, 246 | [String]$Pretext, 247 | [String]$AuthorLink, 248 | [String]$AuthorIcon, 249 | [String]$Title, 250 | [String]$TitleLink, 251 | [Parameter(Position=1)] 252 | [String]$Text, 253 | [String]$ImageURL, 254 | [String]$ThumbURL, 255 | [validatescript({ 256 | foreach($key in $_.keys){ 257 | if('title', 'short', 'value' -notcontains $key) 258 | { 259 | throw "$Key is invalid, must be 'title', 'value', or 'short'" 260 | } 261 | } 262 | $true 263 | })] 264 | [System.Collections.Hashtable[]]$Fields, 265 | [System.Collections.Hashtable[]]$Actions, 266 | [string]$CallBackId, 267 | [validateset('text','pretext','fields')] 268 | [string[]]$MarkDownFields # https://get.slack.help/hc/en-us/articles/202288908-How-can-I-add-formatting-to-my-messages- 269 | ) 270 | 271 | Begin 272 | { 273 | if(-not $Actions -and $CallBackId) 274 | { 275 | throw "The Actions parameter is required when the CallbackId parameter is used" 276 | } 277 | elseif(-not $CallBackId -and $Actions) 278 | { 279 | throw "The CallBackId parameter is required when the Actions parameter is used" 280 | } 281 | 282 | #consolidate the colour and severity parameters for the API. 283 | if($PSCmdlet.ParameterSetName -like 'Severity') 284 | { 285 | $Color = $Severity 286 | } 287 | 288 | $Attachment = @{} 289 | switch($PSBoundParameters.Keys) 290 | { 291 | 'fallback' {$Attachment.fallback = $Fallback} 292 | 'color' {$Attachment.color = $Color} 293 | 'pretext'{$Attachment.pretext = $Pretext} 294 | 'AuthorName'{$Attachment.author_name = $AuthorName} 295 | 'AuthorLink' {$Attachment.author_link = $AuthorLink} 296 | 'AuthorIcon' { $Attachment.author_icon = $AuthorIcon} 297 | 'Title' { $Attachment.title = $Title} 298 | 'TitleLink' { $Attachment.title_link = $TitleLink } 299 | 'Text' {$Attachment.text = $Text} 300 | 'fields' { $Attachment.fields = $Fields } #Fields are defined by the user as an Array of HashTables. 301 | 'actions' { $Attachment.actions = $Actions } #Actions are defined by the user as an Array of HashTables. 302 | 'CallbackId' { $Attachment.callback_id = $CallbackId } 303 | 'ImageUrl' {$Attachment.image_url = $ImageURL} 304 | 'ThumbUrl' {$Attachment.thumb_url = $ThumbURL} 305 | 'MarkDownFields' {$Attachment.mrkdwn_in = @($MarkDownFields)} 306 | } 307 | 308 | Add-ObjectDetail -InputObject $Attachment -TypeName 'PSSlack.MessageAttachment' -Passthru $False 309 | $ReturnObject = @() 310 | } 311 | Process 312 | { 313 | foreach($a in $ExistingAttachment) 314 | { 315 | $ReturnObject += $a 316 | } 317 | 318 | If($ExistingAttachment) 319 | { 320 | Write-Verbose "Existing Attachemnt: $($ExistingAttachment | Convertto-Json -compress)" 321 | } 322 | } 323 | End { 324 | $ReturnObject += $Attachment 325 | $ReturnObject 326 | } 327 | } -------------------------------------------------------------------------------- /PSSlack/Public/New-SlackReminder.ps1: -------------------------------------------------------------------------------- 1 | function New-SlackReminder 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a Slack reminder 6 | 7 | .DESCRIPTION 8 | Creates a Slack reminder 9 | 10 | .PARAMETER Token 11 | Token to use for the Slack API 12 | 13 | Default value is the value set by Set-PSSlackConfig 14 | 15 | This takes precedence over Uri 16 | 17 | .PARAMETER Proxy 18 | Proxy server to use 19 | 20 | Default value is the value set by Set-PSSlackConfig 21 | 22 | .PARAMETER Text 23 | The content of the reminder 24 | 25 | .PARAMETER Time 26 | When this reminder should happen. Must be passed in a valid datetime format. 27 | 28 | .PARAMETER User 29 | The user who will receive the reminder. If no user is specified, 30 | the reminder will go to user who created it. 31 | 32 | Note: This is not the user's name in Slack but rather the unique user identifier. 33 | 34 | .EXAMPLE 35 | # This is a simple example on how to create a reminder 36 | 37 | New-SlackReminder -Text "Do some things" -Time (Get-Date).AddMinutes(1) 38 | 39 | # Creates a reminder with the name "Do some things" set for 1 minute from now 40 | 41 | .LINK 42 | https://api.slack.com/methods/reminders.add 43 | 44 | .FUNCTIONALITY 45 | Slack 46 | #> 47 | 48 | param ( 49 | 50 | [Parameter()] 51 | [ValidateNotNullOrEmpty()] 52 | [string]$Token = $Script:PSSlack.Token, 53 | 54 | [Parameter()] 55 | [ValidateNotNullOrEmpty()] 56 | [string]$Proxy = $Script:PSSlack.Proxy, 57 | 58 | [Parameter(Mandatory = $true, 59 | Position = 0)] 60 | [String]$Text, 61 | 62 | [Parameter(Mandatory = $true, 63 | Position = 1)] 64 | [validatescript( {$_ -gt (Get-Date) -and $_ -le (Get-Date).AddYears(5)})] 65 | [datetime]$Time, 66 | 67 | [string]$User 68 | 69 | ) 70 | begin 71 | { 72 | $ProxyParam = @{} 73 | if ($Proxy) 74 | { 75 | $ProxyParam.Proxy = $Proxy 76 | } 77 | } 78 | process 79 | { 80 | $body = @{ } 81 | 82 | switch ($psboundparameters.keys) 83 | { 84 | 'text' {$body.text = $text} 85 | 'time' {$body.time = [Math]::Floor([decimal](Get-Date($time).ToUniversalTime()-uformat "%s"))} 86 | 'user' {$body.user = $User} 87 | } 88 | } 89 | end 90 | { 91 | Write-Verbose "Send-SlackApi -Body $body" 92 | $response = Send-SlackApi @ProxyParam -Method reminders.add -Body $body -Token $Token -ForceVerbose:$ForceVerbose 93 | 94 | Add-ObjectDetail -InputObject $response.reminder -TypeName 'PSSlack.Reminder' 95 | } 96 | } -------------------------------------------------------------------------------- /PSSlack/Public/Remove-SlackFile.ps1: -------------------------------------------------------------------------------- 1 | function Remove-SlackFile { 2 | <# 3 | .SYNOPSIS 4 | Remove Slack files 5 | .DESCRIPTION 6 | Remove Slack files 7 | .PARAMETER Token 8 | Token to use for the Slack API 9 | 10 | Default value is the value set by Set-PSSlackConfig 11 | .PARAMETER ID 12 | Remove file with this ID 13 | .PARAMETER Force 14 | If specified, override all prompts and delete file 15 | .PARAMETER Raw 16 | Return raw output 17 | .EXAMPLE 18 | Remove-SlackFile -id F18UVDLR3 -Force 19 | # Remove a specific file without prompts 20 | .EXAMPLE 21 | Get-SlackFileInfo -Before (Get-Date).AddYears(-1) | Remove-SlackFile 22 | # Remove files created over a year ago 23 | .FUNCTIONALITY 24 | Slack 25 | #> 26 | [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="High")] 27 | param ( 28 | [parameter( ValueFromPipelineByPropertyName = $True)] 29 | [string]$Id, 30 | [string]$Token = $Script:PSSlack.Token, 31 | [switch]$Force, 32 | [switch]$Raw 33 | ) 34 | begin 35 | { 36 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 37 | $RejectAll = $false 38 | $ConfirmAll = $false 39 | } 40 | process 41 | { 42 | foreach($FileID in $Id) 43 | { 44 | $body = @{ 45 | file = $FileID 46 | } 47 | $params = @{ 48 | Token = $Token 49 | Method = 'files.delete' 50 | Body = $body 51 | } 52 | if( ($Force -and -not $WhatIfPreference) -or $PSCmdlet.ShouldProcess( "Removed the file [$FileID]", 53 | "Remove the file [$FileID]?", 54 | "Removing Files" )) { 55 | if( ($Force -and -not $WhatIfPreference) -or $PSCmdlet.ShouldContinue("Are you REALLY sure you want to remove [$FileID]?", 56 | "Removing [$FileID]", 57 | [ref]$ConfirmAll, 58 | [ref]$RejectAll)) { 59 | $Response = $null 60 | $Response = Send-SlackApi @params 61 | if($Raw) 62 | { 63 | return $Response 64 | } 65 | if($Response.ok) 66 | { 67 | [pscustomobject]@{ 68 | Id = $FileID 69 | ok = $true 70 | Raw = $Response 71 | } 72 | } 73 | else 74 | { 75 | [pscustomobject]@{ 76 | Id = $FileID 77 | ok = $false 78 | Error = $Response.error 79 | Raw = $Response 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /PSSlack/Public/Remove-SlackMessage.ps1: -------------------------------------------------------------------------------- 1 | function Remove-SlackMessage { 2 | <# 3 | .SYNOPSIS 4 | Deletes Slack messages 5 | .DESCRIPTION 6 | This cmdlet invokes the "chat.delete" Slack API method to delete messages from a given Slack channel. 7 | .EXAMPLE 8 | # Remove a message sent to a channel with ID C1W2X3Y4Z at Saturday, August 5, 2017 8:19:05 PM 9 | PS> Remove-SlackMessage -ChannelID "C1W2X3Y4Z" -TimeStamp 1501964345.000481 10 | 11 | # Using a pipeline, Remove all messages sent to a channel by a specific bot/user 12 | # (Multilined for clarity) 13 | PS> Get-SlackChannel -name "TargetChannel" | 14 | Get-SlackHistory -Count 1000 | 15 | Where-Object Username -match "MalfunctioningBot" | 16 | Remove-SlackMessage -ChannelID "C5H8XBUMV" 17 | .INPUTS 18 | The message(s) to delete. These can be specified individually using their timestamps, or piped in from Get-SlackHistory or Find-SlackMessage. 19 | .OUTPUTS 20 | The object returned by the Slack API. The "ok" field indicates whether or not the operation was successful. 21 | .PARAMETER ChannelID 22 | The ID of the channel where the target message is to be deleted from. This must be specified as the channel's ID, not its name. 23 | .PARAMETER TimeStamp 24 | The timestamp (message ID) of the message(s) to be deleted. This is a Unix epoch timestamp with microsecond resolution (6 decimal places). 25 | .PARAMETER HistoryObject 26 | A PSSlack.History object returned from Get-SlackHistory. This is intended for use in pipelined scenarios. 27 | .PARAMETER SearchResultObject 28 | A PSSlack.SearchResult object returned from Find-SlackMessage. This is intended for use in pipelined scenarios. 29 | .PARAMETER AsUser 30 | Delete the message as the authed user associated with this request's token, using the "chat:write:user" scope. Bot users in this context are considered authed users. 31 | 32 | If not specified, the message will be deleted with the "chat:write:bot" scope. 33 | .PARAMETER Token 34 | The Slack API Token to use for authorizing this request. 35 | .PARAMETER Force 36 | Skip confirmation prompts for deleting messages. 37 | .NOTES 38 | When used with a typical user or bot user token, this cmdlet may only delete messages posted by that user. 39 | 40 | When used with an admin user's user token, this cmdlet may delete most messages posted in a channel/workspace. Use with caution. 41 | 42 | For information on the chat.delete method, visit the Slack API documentation page: https://api.slack.com/methods/chat.delete 43 | .FUNCTIONALITY 44 | Slack 45 | .LINK 46 | https://api.slack.com/methods/chat.delete 47 | #> 48 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] 49 | param ( 50 | # The ID of the channel where the target message is to be deleted from. 51 | [Parameter( 52 | ValueFromPipelineByPropertyName = $true, 53 | Mandatory = $true 54 | )] 55 | [ValidateNotNullOrEmpty()] 56 | [Alias( 57 | "Channel" 58 | )] 59 | [string]$ChannelID, 60 | 61 | # The timestamp of the message to be deleted. 62 | [Parameter( 63 | ValueFromPipelineByPropertyName = $true, 64 | Mandatory = $true, 65 | ParameterSetName = "ByParameter" 66 | 67 | )] 68 | [ValidateNotNullOrEmpty()] 69 | [Alias( 70 | "ts" 71 | )] 72 | [string]$TimeStamp, 73 | 74 | # The history item (from Get-SlackHistory) referencing the message to be deleted. 75 | [Parameter( 76 | ValueFromPipeline = $true, 77 | Mandatory = $true, 78 | ParameterSetName = "ByObject-History" 79 | )] 80 | [ValidateNotNullOrEmpty()] 81 | [PSTypeName("PSSlack.History")] 82 | $HistoryObject, 83 | 84 | # The message search result (from Find-SlackMessage) referencing the message to be deleted. 85 | [Parameter( 86 | ValueFromPipeline = $true, 87 | Mandatory = $true, 88 | ParameterSetName = "ByObject-SearchResult" 89 | )] 90 | [ValidateNotNullOrEmpty()] 91 | [PSTypeName("PSSlack.SearchResult")] 92 | $SearchResultObject, 93 | 94 | # A switch to delete the message using the currently auth'd user (via the chat:write:user scope) 95 | # See https://api.slack.com/methods/chat.delete for more info 96 | [switch]$AsUser, 97 | 98 | # Disable confirmation prompts when deleting messages. 99 | [Switch]$Force, 100 | 101 | [string]$Token = $Script:PSSlack.Token 102 | ) 103 | 104 | begin { 105 | Write-Verbose "$($PSBoundParameters | Remove-SensitiveData | Out-String)" 106 | $RejectAll = $false 107 | $ConfirmAll = $false 108 | } 109 | 110 | process { 111 | switch ($PSCmdlet.ParameterSetName) { 112 | "ByParameter" { $PrimaryIterator = $TimeStamp } 113 | "ByObject-History" { $PrimaryIterator = $HistoryObject } 114 | "ByObject-SearchResult" { $PrimaryIterator = $SearchResultObject } 115 | } 116 | 117 | # Get generic for a brief moment so we can use one loop for all three cases. 118 | foreach ($item in $PrimaryIterator) { 119 | $Body = @{ 120 | as_user = $AsUser 121 | channel = $ChannelID 122 | } 123 | switch ($PSCmdlet.ParameterSetName) { 124 | "ByParameter" { 125 | $Body.ts = $TimeStamp 126 | } 127 | "ByObject-History" { 128 | $Body.ts = $Item.raw.ts 129 | } 130 | "ByObject-SearchResult" { 131 | $Body.channel = $Item.raw.channel.id 132 | $Body.ts = $Item.raw.ts 133 | } 134 | } 135 | $Params = @{ 136 | Body = $Body 137 | Method = "chat.delete" 138 | Token = $Token 139 | } 140 | If (($Force -and -not $WhatIfPreference) -or 141 | $PSCmdlet.ShouldProcess( 142 | "Removed the message [$($Body.ts)] from channel $($Body.channel)", 143 | "Remove the message [$($Body.ts)] from channel $($Body.channel)?", 144 | "Removing messages" 145 | )) { 146 | If (($Force -and -not $WhatIfPreference) -or 147 | $PSCmdlet.ShouldContinue( 148 | "Are you sure you want to remove message [$($Body.ts)] from channel $($Body.channel)?", 149 | "Removing Slack message", 150 | $true, 151 | [ref]$ConfirmAll, 152 | [ref]$RejectAll 153 | )) { 154 | Send-SlackApi @Params 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /PSSlack/Public/Remove-SlackReminder.ps1: -------------------------------------------------------------------------------- 1 | function Remove-SlackReminder 2 | { 3 | <# 4 | .SYNOPSIS 5 | Deletes a Slack reminder 6 | 7 | .DESCRIPTION 8 | Deletes a Slack reminder based on the reminder object or reminder id passed to the function 9 | 10 | .PARAMETER Token 11 | Token to use for the Slack API 12 | 13 | Default value is the value set by Set-PSSlackConfig 14 | 15 | This takes precedence over Uri 16 | 17 | .PARAMETER Proxy 18 | Proxy server to use 19 | 20 | Default value is the value set by Set-PSSlackConfig 21 | 22 | .PARAMETER ReminderId 23 | The ID of the reminder that will be deleted. 24 | 25 | .PARAMETER ReminderObject 26 | One or more objects of type PSSlack.Reminder. This is intended for use in pipelined scenarios. 27 | 28 | .EXAMPLE 29 | # This is a simple example on how to remove a reminder by passing its ID 30 | 31 | Remove-SlackReminder -ReminderId "RmAZPB1FBP" 32 | 33 | # Removes a reminder with an ID of "RmAZPB1FBP" 34 | 35 | .EXAMPLE 36 | # This is a simple example on how to remove a reminder by searching and filtering the existing reminders 37 | # The returned reminder object is then passed to Remove-SlackReminder 38 | 39 | New-SlackReminder -Text "you should delete me soon" -Time (get-date).AddMinutes(10) 40 | 41 | Get-SlackReminder | 42 | Where-Object {$_.Text -like "*delete me*"} | 43 | Remove-SlackReminder 44 | 45 | # We create a reminder 46 | # We then search for that reminder and delete it by piping it into Remove-SlackReminder 47 | 48 | .EXAMPLE 49 | # This is a simple example on how to remove multiple reminders by searching and filtering the existing reminders 50 | # The returned reminder object array is then passed to Remove-SlackReminder 51 | 52 | New-SlackReminder -Text "you should delete me soon" -Time (get-date).AddMinutes(10) 53 | New-SlackReminder -Text "you should delete me soon too" -Time (get-date).AddMinutes(10) 54 | 55 | Get-SlackReminder | 56 | Where-Object {$_.Text -like "*delete me*"} | 57 | Remove-SlackReminder 58 | 59 | # We create two reminders 60 | # We then search for the reminders and delete them by piping them into Remove-SlackReminder 61 | 62 | .LINK 63 | https://api.slack.com/methods/reminders.delete 64 | 65 | .FUNCTIONALITY 66 | Slack 67 | #> 68 | 69 | [CmdletBinding(DefaultParameterSetName = 'ByParameter')] 70 | param ( 71 | [Parameter()] 72 | [ValidateNotNullOrEmpty()] 73 | [string]$Token = $Script:PSSlack.Token, 74 | 75 | [Parameter()] 76 | [ValidateNotNullOrEmpty()] 77 | [string]$Proxy = $Script:PSSlack.Proxy, 78 | 79 | [Parameter(ValueFromPipeline = $True)] 80 | [Parameter(ParameterSetName = 'ByObject')] 81 | [PSTypeName('PSSlack.Reminder')] 82 | $ReminderObject, 83 | 84 | [Parameter(ParameterSetName = 'ByParameter')] 85 | [Alias("Reminder")] 86 | [string]$ReminderId 87 | ) 88 | begin 89 | { 90 | $ProxyParam = @{} 91 | if ($Proxy) 92 | { 93 | $ProxyParam.Proxy = $Proxy 94 | } 95 | } 96 | process 97 | { 98 | if ($ReminderObject) 99 | { 100 | foreach ($object in $ReminderObject) 101 | { 102 | $body = @{ } 103 | $body.reminder = $object.ID 104 | Write-Verbose "Send-SlackApi" 105 | Send-SlackApi @ProxyParam -Method reminders.delete -Body $body -Token $Token -ForceVerbose:$ForceVerbose 106 | } 107 | } 108 | else 109 | { 110 | $body = @{ } 111 | $body.reminder = $ReminderId 112 | Write-Verbose "Send-SlackApi" 113 | Send-SlackApi @ProxyParam -Method reminders.delete -Body $body -Token $Token -ForceVerbose:$ForceVerbose 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /PSSlack/Public/Send-SlackAPI.ps1: -------------------------------------------------------------------------------- 1 | function Send-SlackApi 2 | { 3 | <# 4 | .SYNOPSIS 5 | Send a message to the Slack API endpoint 6 | 7 | .DESCRIPTION 8 | Send a message to the Slack API endpoint 9 | 10 | This function is used by other PSSlack functions. 11 | It's a simple wrapper you could use for calls to the Slack API 12 | 13 | .PARAMETER Method 14 | Slack API method to call. 15 | 16 | Reference: https://api.slack.com/methods 17 | 18 | .PARAMETER Body 19 | Hash table of arguments to send to the Slack API. 20 | 21 | .PARAMETER Token 22 | Slack token to use 23 | 24 | .PARAMETER Proxy 25 | Proxy server to use 26 | 27 | .PARAMETER ForceVerbose 28 | If specified, don't explicitly remove verbose output from Invoke-RestMethod 29 | 30 | *** WARNING *** 31 | This will expose your token in verbose output 32 | 33 | .FUNCTIONALITY 34 | Slack 35 | #> 36 | [OutputType([String])] 37 | [cmdletbinding()] 38 | param ( 39 | [Parameter(Mandatory)] 40 | [ValidateNotNullOrEmpty()] 41 | [string]$Method, 42 | 43 | [Parameter()] 44 | [ValidateNotNullOrEmpty()] 45 | [hashtable]$Body = @{ }, 46 | 47 | [ValidateNotNullOrEmpty()] 48 | [ValidateScript({ 49 | if (-not $_ -and -not $Script:PSSlack.Token) 50 | { 51 | throw 'Please supply a Slack Api Token with Set-SlackApiToken.' 52 | } 53 | else 54 | { 55 | $true 56 | } 57 | })] 58 | [string]$Token = $Script:PSSlack.Token, 59 | 60 | [string]$Proxy = $Script:PSSlack.Proxy, 61 | 62 | [switch]$ForceVerbose = $Script:PSSlack.ForceVerbose 63 | ) 64 | $Params = @{ 65 | Uri = "https://slack.com/api/$Method" 66 | ErrorAction = 'Stop' 67 | } 68 | if($Proxy) { 69 | $Params['Proxy'] = $Proxy 70 | } 71 | if(-not $ForceVerbose) { 72 | $Params.Add('Verbose', $False) 73 | } 74 | if($ForceVerbose) { 75 | $Params.Add('Verbose', $true) 76 | } 77 | $Body.token = $Token 78 | 79 | try { 80 | $Response = $null 81 | $Response = Invoke-RestMethod @Params -Body $Body 82 | } 83 | catch { 84 | # (HTTP 429 is "Too Many Requests") 85 | if ($_.Exception.Response.StatusCode -eq 429) { 86 | 87 | # Get the time before we can try again. 88 | if( $_.Exception.Response.Headers -and $_.Exception.Response.Headers.Contains('Retry-After') ) { 89 | $RetryPeriod = $_.Exception.Response.Headers.GetValues('Retry-After') 90 | if($RetryPeriod -is [string[]]) { 91 | $RetryPeriod = [int]$RetryPeriod[0] 92 | } 93 | } 94 | else { 95 | $RetryPeriod = 2 96 | } 97 | Write-Verbose "Sleeping [$RetryPeriod] seconds due to Slack 429 response" 98 | Start-Sleep -Seconds $RetryPeriod 99 | Send-SlackApi @PSBoundParameters 100 | 101 | } 102 | elseif ($_.ErrorDetails.Message -ne $null) { 103 | # Convert the error-message to an object. (Invoke-RestMethod will not return data by-default if a 4xx/5xx status code is generated.) 104 | $_.ErrorDetails.Message | ConvertFrom-Json | Parse-SlackError -Exception $_.Exception -ErrorAction Stop 105 | 106 | } 107 | else { 108 | Write-Error -Exception $_.Exception -Message "Slack API call failed: $_" 109 | } 110 | } 111 | 112 | # Check to see if we have confirmation that our API call failed. 113 | # (Responses with exception-generating status codes are handled in the "catch" block above - this one is for errors that don't generate exceptions) 114 | if ($Response -ne $null -and $Response.ok -eq $False) { 115 | $Response | Parse-SlackError 116 | } 117 | elseif($Response) { 118 | Write-Output $Response 119 | } 120 | else { 121 | Write-Verbose "Something went wrong. `$Response is `$null" 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /PSSlack/Public/Send-SlackFile.ps1: -------------------------------------------------------------------------------- 1 | function Send-SlackFile { 2 | <# 3 | .SYNOPSIS 4 | Send a Slack file 5 | 6 | .DESCRIPTION 7 | Send a Slack file 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API 11 | 12 | Default value is the value set by Set-PSSlackConfig 13 | 14 | .PARAMETER Content 15 | Content of the file to send. Text is editable after sending. 16 | 17 | .Parameter FileType 18 | If specified, override the FileType determined by the filename. 19 | 20 | List of types: https://api.slack.com/types/file#file_types 21 | 22 | .PARAMETER Channel 23 | Optional channel, private group, or IM channel to send file to. Can be an encoded ID, or a name. 24 | 25 | .PARAMETER Thread 26 | Optional thread where file is sent. Needs to be the parent thread id which is either the ts or thread_ts. 27 | 28 | Can find a ts by querying https://api.slack.com/methods/conversations.history 29 | 30 | .PARAMETER FileName 31 | Required filename for this file. Used to determine syntax highlighting and other functionality. 32 | 33 | .PARAMETER Title 34 | Optional title of the file 35 | 36 | .PARAMETER Comment 37 | Optional initial comment for the file 38 | 39 | .PARAMETER ForceVerbose 40 | If specified, don't explicitly remove verbose output from Invoke-RestMethod 41 | 42 | *** WARNING *** 43 | This will expose your token in verbose output 44 | 45 | .EXAMPLE 46 | Send-SlackFile -Token $Token ` 47 | -Channel general ` 48 | -Content 'get-help about_* | get-random' ` 49 | -FileType perl ` 50 | -filename example.ps1 51 | 52 | # Send a slack file that turns into a snippet. 53 | # Use perl, because PowerShell syntax highlighting is sad 54 | 55 | .EXAMPLE 56 | Send-SlackFile -Channel '@wframe' ` 57 | -path C:\homer.gif ` 58 | -title homer 59 | 60 | # Send a gif to @wframe using a previously-stored token (Set-PSSlackConfig) 61 | 62 | .FUNCTIONALITY 63 | Slack 64 | #> 65 | 66 | [cmdletbinding(DefaultParameterSetName = 'Content')] 67 | param ( 68 | [string]$Token = $Script:PSSlack.Token, 69 | 70 | [parameter(ParameterSetName = 'Content', 71 | Mandatory = $True)] 72 | [string]$Content, 73 | 74 | [validatescript({Test-Path -PathType Leaf -Path $_})] 75 | [parameter(ParameterSetName = 'File', 76 | Mandatory = $True)] 77 | [string]$Path, 78 | 79 | [parameter(ParameterSetName = 'Content')] 80 | [string]$FileType, 81 | 82 | [string[]]$Channel, 83 | [string]$Thread, 84 | [string]$FileName, 85 | [String]$Title, 86 | [String]$Comment, 87 | 88 | [switch]$ForceVerbose = $Script:PSSlack.ForceVerbose 89 | ) 90 | process 91 | { 92 | if ($Content) { 93 | $body = @{} 94 | switch ($psboundparameters.keys) { 95 | 'Content' {$body.content = $content} 96 | 'Channel' {$body.channels = $Channel -join ", " } 97 | 'Thread' {$body.thread_ts = $Thread} 98 | 'FileName' {$body.filename = $FileName} 99 | 'Title' {$body.Title = $Title} 100 | 'Comment' {$body.initial_comment = $Comment} 101 | 'FileType' {$body.filetype = $FileType} 102 | } 103 | Write-Verbose "Send-SlackApi -Body $($body | Format-List | Out-String)" 104 | $Params = @{ 105 | Method = 'files.upload' 106 | Body = $Body 107 | Token = $Token 108 | ForceVerbose = $ForceVerbose 109 | } 110 | $response = Send-SlackApi @Params 111 | } else { 112 | 113 | $fileName = (Split-Path -Path $Path -Leaf) 114 | $uri = 'https://slack.com/api/files.upload' 115 | 116 | if ($IsCoreCLR) { 117 | # PowerShell Core implementation 118 | 119 | $multipartContent = [System.Net.Http.MultipartFormDataContent]::new() 120 | 121 | # Add file contents 122 | $fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 123 | $fileHeader.Name = 'file' 124 | $fileHeader.FileName = $FileName 125 | $fileStream = [System.IO.FileStream]::new($Path, [System.IO.FileMode]::Open) 126 | $fileContent = [System.Net.Http.StreamContent]::new($fileStream) 127 | $fileContent.Headers.ContentDisposition = $fileHeader 128 | $fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('multipart/form-data') 129 | $multipartContent.Add($fileContent) 130 | 131 | # Add token 132 | $tokenHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 133 | $tokenHeader.Name = 'token' 134 | $tokenContent = [System.Net.Http.StringContent]::new($token) 135 | $tokenContent.Headers.ContentDisposition = $tokenHeader 136 | $multipartContent.Add($tokenContent) 137 | 138 | switch ($psboundparameters.keys) { 139 | 'Channel' { 140 | # Add channel 141 | $channelHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 142 | $channelHeader.Name = 'channels' 143 | $channelContent = [System.Net.Http.StringContent]::new(($Channel -join ', ')) 144 | $channelContent.Headers.ContentDisposition = $channelHeader 145 | $multipartContent.Add($channelContent) 146 | } 147 | 'Thread' { 148 | # Add Thread 149 | $threadHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 150 | $threadHeader.Name = 'thread_ts' 151 | $threadContent = [System.Net.Http.StringContent]::new($Thread) 152 | $threadContent.Headers.ContentDisposition = $threadHeader 153 | $multipartContent.Add($threadContent) 154 | } 155 | 'FileName' { 156 | # Add file name 157 | $filenameHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 158 | $filenameHeader.Name = 'filename' 159 | $filenameContent = [System.Net.Http.StringContent]::new($FileName) 160 | $filenameContent.Headers.ContentDisposition = $filenameHeader 161 | $multipartContent.Add($filenameContent) 162 | } 163 | 'Title' { 164 | # Add title 165 | $titleHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 166 | $titleHeader.Name = 'title' 167 | $titleContent = [System.Net.Http.StringContent]::new($Title) 168 | $titleContent.Headers.ContentDisposition = $titleHeader 169 | $multipartContent.Add($titleContent) 170 | } 171 | 'Comment' { 172 | # Add comment 173 | $commentHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new('form-data') 174 | $commentHeader.Name = 'initial_comment' 175 | $commentContent = [System.Net.Http.StringContent]::new($Comment) 176 | $commentContent.Headers.ContentDisposition = $commentHeader 177 | $multipartContent.Add($commentContent) 178 | } 179 | } 180 | 181 | try { 182 | $response = Invoke-RestMethod -Uri $uri -Method Post -Body $multipartContent 183 | } 184 | catch [System.Net.WebException] { 185 | Write-Error( "Rest call failed for $uri`: $_" ) 186 | throw $_ 187 | } 188 | finally { 189 | $fileStream.Close() 190 | } 191 | } 192 | else { 193 | # Legacy Windows PowerShell implementation 194 | 195 | $LF = "`r`n" 196 | $readFile = [System.IO.File]::ReadAllBytes($Path) 197 | $enc = [System.Text.Encoding]::GetEncoding("iso-8859-1") 198 | $fileEnc = $enc.GetString($readFile) 199 | $boundary = [System.Guid]::NewGuid().ToString() 200 | 201 | $bodyLines = 202 | "--$boundary$LF" + 203 | "Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"$LF" + 204 | "Content-Type: 'multipart/form-data'$LF$LF" + 205 | "$fileEnc$LF" + 206 | "--$boundary$LF" + 207 | "Content-Disposition: form-data; name=`"token`"$LF" + 208 | "Content-Type: 'multipart/form-data'$LF$LF" + 209 | "$token$LF" 210 | 211 | 212 | switch ($psboundparameters.keys) { 213 | 'Channel' {$bodyLines += 214 | ("--$boundary$LF" + 215 | "Content-Disposition: form-data; name=`"channels`"$LF" + 216 | "Content-Type: multipart/form-data$LF$LF" + 217 | ($Channel -join ", ") + $LF)} 218 | 'Thread' {$bodyLines += 219 | ("--$boundary$LF" + 220 | "Content-Disposition: form-data; name=`"thread_ts`"$LF" + 221 | "Content-Type: multipart/form-data$LF$LF" + 222 | "$Thread$LF")} 223 | 'FileName' {$bodyLines += 224 | ("--$boundary$LF" + 225 | "Content-Disposition: form-data; name=`"filename`"$LF" + 226 | "Content-Type: multipart/form-data$LF$LF" + 227 | "$FileName$LF")} 228 | 'Title' {$bodyLines += 229 | ("--$boundary$LF" + 230 | "Content-Disposition: form-data; name=`"title`"$LF" + 231 | "Content-Type: multipart/form-data$LF$LF" + 232 | "$Title$LF")} 233 | 'Comment' {$bodyLines += 234 | ("--$boundary$LF" + 235 | "Content-Disposition: form-data; name=`"initial_comment`"$LF" + 236 | "Content-Type: multipart/form-data$LF$LF" + 237 | "$Comment$LF")} 238 | } 239 | $bodyLines += "--$boundary--$LF" 240 | try { 241 | $Params = @{ 242 | Uri = $uri 243 | Method = 'Post' 244 | ContentType = "multipart/form-data; boundary=`"$boundary`"" 245 | Body = $bodyLines 246 | } 247 | if(-not $ForceVerbose) { 248 | $Params.Add('Verbose', $False) 249 | } 250 | if($ForceVerbose) { 251 | $Params.Add('Verbose', $true) 252 | } 253 | $response = Invoke-RestMethod @Params 254 | } 255 | catch [System.Net.WebException] { 256 | Write-Error( "Rest call failed for $uri`: $_" ) 257 | throw $_ 258 | } 259 | } 260 | } 261 | $response 262 | } 263 | } -------------------------------------------------------------------------------- /PSSlack/Public/Set-PSSlackConfig.ps1: -------------------------------------------------------------------------------- 1 | function Set-PSSlackConfig { 2 | <# 3 | .SYNOPSIS 4 | Set PSSlack module configuration. 5 | 6 | .DESCRIPTION 7 | Set PSSlack module configuration, and $PSSlack module variable. 8 | 9 | This data is used as the default Token and Uri for most commands. 10 | 11 | If a command takes either a token or a uri, tokens take precedence. 12 | 13 | WARNING: Use this to store the token or uri on a filesystem at your own risk 14 | Only supported on Windows systems, via the DPAPI 15 | 16 | .PARAMETER Token 17 | Specify a Token to use 18 | 19 | Only serialized to disk on Windows, via DPAPI 20 | 21 | .PARAMETER Uri 22 | Specify a Uri to use 23 | 24 | Only serialized to disk on Windows, via DPAPI 25 | 26 | .PARAMETER ArchiveUri 27 | Archive URI. Generally, https://.slack.com/archives/ 28 | 29 | Used to generate a link to a specific archive URI, where appropriate 30 | 31 | .PARAMETER Proxy 32 | Proxy to use with Invoke-RESTMethod 33 | 34 | .PARAMETER MapUser 35 | Whether to generate a map of Slack user ID to name on module load, for use in Slack File commands 36 | 37 | .PARAMETER ForceVerbose 38 | If set to true, we allow verbose output that may include sensitive data 39 | 40 | *** WARNING *** 41 | If you set this to true, your Slack token will be visible as plain text in verbose output 42 | 43 | .PARAMETER Path 44 | If specified, save config file to this file path. Defaults to PSSlack.xml in the user temp folder on Windows, or .psslack in the user's home directory on Linux/macOS. 45 | 46 | .FUNCTIONALITY 47 | Slack 48 | #> 49 | [cmdletbinding()] 50 | param( 51 | [string]$Uri, 52 | [string]$Token, 53 | [string]$ArchiveUri, 54 | [string]$Proxy, 55 | [bool]$MapUser, 56 | [bool]$ForceVerbose, 57 | [string]$Path = $script:_PSSlackXmlpath 58 | ) 59 | 60 | Switch ($PSBoundParameters.Keys) 61 | { 62 | 'Uri' { $Script:PSSlack.Uri = $Uri } 63 | 'Token' { $Script:PSSlack.Token = $Token } 64 | 'ArchiveUri' { $Script:PSSlack.ArchiveUri = $ArchiveUri } 65 | 'Proxy' { $Script:PSSlack.Proxy = $Proxy } 66 | 'MapUser' { $Script:PSSlack.MapUser = $MapUser } 67 | 'ForceVerbose' { $Script:PSSlack.ForceVerbose = $ForceVerbose } 68 | } 69 | 70 | Function Encrypt { 71 | param([string]$string) 72 | if($String -notlike '' -and (Test-IsWindows)) 73 | { 74 | ConvertTo-SecureString -String $string -AsPlainText -Force 75 | } 76 | } 77 | 78 | #Write the global variable and the xml 79 | $Script:PSSlack | 80 | Microsoft.PowerShell.Utility\Select-Object -Property ArchiveUri, 81 | @{l='Uri';e={Encrypt $_.Uri}}, 82 | @{l='Token';e={Encrypt $_.Token}}, 83 | Proxy, 84 | MapUser, 85 | ForceVerbose | 86 | Export-Clixml -Path $Path -force 87 | 88 | } 89 | -------------------------------------------------------------------------------- /PSSlack/Public/Set-SlackReminderComplete.ps1: -------------------------------------------------------------------------------- 1 | function Set-SlackReminderComplete 2 | { 3 | <# 4 | .SYNOPSIS 5 | Sets the status of a Slack reminder to complete 6 | 7 | .DESCRIPTION 8 | Sets the status of a Slack reminder to complete based on the reminder object or reminder id passed to the function 9 | 10 | .PARAMETER Token 11 | Token to use for the Slack API 12 | 13 | Default value is the value set by Set-PSSlackConfig 14 | 15 | This takes precedence over Uri 16 | 17 | .PARAMETER Proxy 18 | Proxy server to use 19 | 20 | Default value is the value set by Set-PSSlackConfig 21 | 22 | .PARAMETER ReminderId 23 | The ID of the reminder that will be deleted. 24 | 25 | .PARAMETER ReminderObject 26 | One or more objects of type PSSlack.Reminder. This is intended for use in pipelined scenarios. 27 | 28 | .EXAMPLE 29 | # This is a simple example on how to set the status to complete on a Slack reminder 30 | 31 | Set-SlackReminderComplete -ReminderId "RmAZPB1FBP" 32 | 33 | # Sets a Slack reminder with an ID of "RmAZPB1FBP" to complete status 34 | 35 | .EXAMPLE 36 | # This is a simple example on how to set the status to complete on a reminder by searching 37 | # and filtering the existing reminders 38 | # The returned reminder object is then passed to Set-SlackReminder 39 | 40 | New-SlackReminder -Text "you should complete me soon" -Time (get-date).AddMinutes(10) 41 | 42 | Get-SlackReminder | 43 | Where-Object {$_.Text -like "*complete me*"} | 44 | Set-SlackReminderComplete 45 | 46 | # We create a reminder 47 | # We then search for that reminder and set the status on it to complete by piping it into Set-SlackReminder 48 | 49 | .EXAMPLE 50 | # This is a simple example on how to set the status to complete on multiple reminders 51 | # by searching and filtering the existing reminders 52 | # The returned reminder object array is then passed to Set-SlackReminder 53 | 54 | New-SlackReminder -Text "you should complete me soon" -Time (get-date).AddMinutes(10) 55 | New-SlackReminder -Text "you should complete me soon too" -Time (get-date).AddMinutes(10) 56 | 57 | Get-SlackReminder | 58 | Where-Object {$_.Text -like "*complete me*"} | 59 | Set-SlackReminderComplete 60 | 61 | # We create two reminders 62 | # We then search for the reminders and set the status on them to complete by piping them into Set-SlackReminder 63 | 64 | .LINK 65 | https://api.slack.com/methods/reminders.complete 66 | 67 | .FUNCTIONALITY 68 | Slack 69 | #> 70 | 71 | [CmdletBinding(DefaultParameterSetName = 'ByParameter')] 72 | param ( 73 | [Parameter()] 74 | [ValidateNotNullOrEmpty()] 75 | [string]$Token = $Script:PSSlack.Token, 76 | 77 | [Parameter()] 78 | [ValidateNotNullOrEmpty()] 79 | [string]$Proxy = $Script:PSSlack.Proxy, 80 | 81 | [Parameter(ValueFromPipeline = $True)] 82 | [Parameter(ParameterSetName = 'ByObject')] 83 | [PSTypeName('PSSlack.Reminder')] 84 | $ReminderObject, 85 | 86 | [Parameter(ParameterSetName = 'ByParameter')] 87 | [Alias("Reminder")] 88 | [string]$ReminderId 89 | ) 90 | begin 91 | { 92 | $ProxyParam = @{} 93 | if ($Proxy) 94 | { 95 | $ProxyParam.Proxy = $Proxy 96 | } 97 | } 98 | process 99 | { 100 | if ($ReminderObject) 101 | { 102 | foreach ($object in $ReminderObject) 103 | { 104 | $body = @{ } 105 | $body.reminder = $object.ID 106 | Write-Verbose "Send-SlackApi" 107 | Send-SlackApi @ProxyParam -Method reminders.complete -Body $body -Token $Token -ForceVerbose:$ForceVerbose 108 | } 109 | } 110 | else 111 | { 112 | $body = @{ } 113 | $body.reminder = $ReminderId 114 | Write-Verbose "Send-SlackApi" 115 | Send-SlackApi @ProxyParam -Method reminders.complete -Body $body -Token $Token -ForceVerbose:$ForceVerbose 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /PSSlack/Public/Test-SlackApi.ps1: -------------------------------------------------------------------------------- 1 | function Test-SlackApi { 2 | <# 3 | .SYNOPSIS 4 | Tests connectivity to the Slack API. 5 | .DESCRIPTION 6 | This cmdlet calls the Slack API.test method to ensure PSSlack is able to connect to (and invoke) API methods. 7 | .EXAMPLE 8 | # Test connectivity without a token 9 | PS C:\> Test-SlackApi 10 | 11 | # Test connectivity using a specified token 12 | PS C:\> Test-SlackApi -Token "xoxp-111-222-333-456789abcdef" 13 | .INPUTS 14 | A Slack API token to use, if desired. 15 | .OUTPUTS 16 | A Boolean value indicating whether the call passed/failed, or the response object from Slack's API. 17 | .PARAMETER Token 18 | The API token to use when communicating with the Slack API. 19 | .PARAMETER Raw 20 | Return the raw response object from the Slack API, versus parsing it and returning a boolean. 21 | .NOTES 22 | Test-SlackApi will not, by default, use the preconfigured Slack API token (as the api.test method does not require authorization). 23 | .FUNCTIONALITY 24 | Slack 25 | .LINK 26 | https://api.slack.com/methods/api.test 27 | #> 28 | [CmdletBinding()] 29 | param ( 30 | [ValidateNotNullOrEmpty()] 31 | [String]$Token, 32 | 33 | [Switch]$Raw 34 | ) 35 | 36 | process { 37 | Write-Verbose "Testing Slack API." 38 | $RequestID = New-Guid # Generate a unique value for us to use when checking requests coming back from Slack's API 39 | Write-Debug "Unique ID: $RequestID" 40 | 41 | $Params = @{ 42 | Body = @{ 43 | PSSlackRequestID = $RequestID 44 | } 45 | Method = "api.test" 46 | } 47 | If ($Token) { 48 | Write-Verbose "Adding token to request." 49 | $Params.Token = $Token 50 | } 51 | Write-Verbose "Calling Slack API..." 52 | $Response = Send-SlackApi @Params -ErrorAction Stop 53 | 54 | If ($Raw) { 55 | Return $Response 56 | } Elseif ($Response.ok) { 57 | Write-Verbose "Received response from Slack api.test call." 58 | If ($Response.args.PSSlackRequestID -eq $RequestID) { 59 | Write-Verbose "Unique ID matches." 60 | Return $true 61 | } else { 62 | Write-Error "Slack API call succeeded, but responded with incorrect value." 63 | } 64 | } else { 65 | Return $False 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /PSSlack/Public/Test-SlackAuth.ps1: -------------------------------------------------------------------------------- 1 | function Test-SlackAuth { 2 | <# 3 | .SYNOPSIS 4 | Checks if you're authenticated. 5 | 6 | .DESCRIPTION 7 | Checks if you're authenticated. 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API. 11 | 12 | Default value is the value set by Set-PSSlackConfig. 13 | 14 | .PARAMETER Raw 15 | Return raw output. 16 | 17 | .EXAMPLE 18 | Test-SlackAuth 19 | 20 | # Checks if the default user specified by Get-PSSlackConfig is authenticated. 21 | 22 | .EXAMPLE 23 | Test-SlackAuth -Token $Token 24 | 25 | # Checks if the user specified by $Token is authenticated. 26 | .EXAMPLE 27 | Test-SlackAuth -Raw 28 | 29 | # Checks if the default user specified by Get-PSSlackConfig is authenticated. 30 | # Returns raw output. 31 | 32 | .EXAMPLE 33 | Test-SlackAuth -Raw -Token $Token 34 | 35 | # Checks if the default user specified by $Token is authenticated. 36 | # Returns raw output. 37 | 38 | .FUNCTIONALITY 39 | Slack 40 | 41 | .LINK 42 | https://api.slack.com/methods/auth.test 43 | #> 44 | 45 | [CmdletBinding()] 46 | param ( 47 | [string]$Token = $Script:PSSlack.Token, 48 | [switch]$Raw 49 | ) 50 | end 51 | { 52 | $RawAuth = Get-SlackAuth @PSBoundParameters 53 | 54 | if($Raw) 55 | { 56 | $RawAuth 57 | } 58 | else 59 | { 60 | $RawAuth.IsAuthenticated 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /PSSlack/en-US/about_PSSlack.help.txt: -------------------------------------------------------------------------------- 1 | PSTOPIC 2 | about_PSSlack 3 | 4 | SHORT DESCRIPTION 5 | PSSlack allows you to work with the Slack API through PowerShell functions 6 | 7 | LONG DESCRIPTION 8 | PSSlack allows you to work with the Slack API through PowerShell functions 9 | 10 | You can find API documentation here: 11 | https://api.slack.com/web 12 | https://api.slack.com/methods 13 | 14 | DETAILED DESCRIPTION 15 | 16 | Using PSSlack requires that you have a token or incoming webhook URI from Slack. 17 | 18 | Details on authentication via tokens in Slack: 19 | https://api.slack.com/docs/oauth 20 | 21 | Obtain a simple test token for Slack: 22 | https://api.slack.com/docs/oauth-test-tokens 23 | 24 | Set up an Incoming Webhook for your team: 25 | https://api.slack.com/incoming-webhooks 26 | https://my.slack.com/services/new/incoming-webhook/ 27 | (note: pick the right team in the URL or the top right) 28 | 29 | Once you have a token or Uri, you are ready to roll! 30 | 31 | If you want to save time, you can store your token, Uri, and other data using Set-PSSlackConfig 32 | These will be used for default values in several commands. 33 | 34 | AUTHORIZATION 35 | 36 | We don't get fancy with parametersets. Here's a breakdown of how we pick Uri or Token: 37 | 38 | Parameters are used before Set-PSSlackConfig settings 39 | Tokens are used before Uri 40 | 41 | Examples: 42 | Uri parameter specified, token exists in Set-PSSlackConfig: Uri is used 43 | Uri and token exist in Set-PSSlackConfig: token is used 44 | Token and Uri parameters are specified: Token is used 45 | 46 | OUTPUT 47 | 48 | The Slack API and Incoming Webhook alter the output Send-SlackMessage will provide. 49 | 50 | If you use a Uri for an Incoming Webhook, Slack will return a string: 51 | "ok" if the call succeeded 52 | An error string if something went wrong 53 | 54 | If you use a token, we get a bit more detail back: 55 | ok : True 56 | channel : D0ST7FE6Q 57 | ts : 1463254594.000027 58 | message : @{text=; username=Slack API Tester; icons=; attachments=System.Object[]; type=message;... 59 | link : ArchiveUri.From.Set-PSSlackConfig/D0ST7FE6Q/p1463254594000027 60 | 61 | If you use a token and things don't go so well, the OK field will not be true: 62 | ok error 63 | -- ----- 64 | False channel_not_found -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/kuxiy9m0g19g04o0?svg=true)](https://ci.appveyor.com/project/RamblingCookieMonster/psslack) 2 | 3 | PSSlack 4 | ============= 5 | 6 | This is a quick and dirty module to interact with the Slack API. 7 | 8 | Pull requests and other contributions would be welcome! 9 | 10 | # Instructions 11 | 12 | ```powershell 13 | # One time setup 14 | # Download the repository 15 | # Unblock the zip 16 | # Extract the PSSlack folder to a module path (e.g. $env:USERPROFILE\Documents\WindowsPowerShell\Modules\) 17 | # Or, with PowerShell 5 or later or PowerShellGet: 18 | Install-Module PSSlack 19 | 20 | # Import the module. 21 | Import-Module PSSlack #Alternatively, Import-Module \\Path\To\PSSlack 22 | 23 | # Get commands in the module 24 | Get-Command -Module PSSlack 25 | 26 | # Get help 27 | Get-Help Send-SlackMessage -Full 28 | Get-Help about_PSSlack 29 | ``` 30 | 31 | ### Prerequisites 32 | 33 | * PowerShell 3 or later 34 | * A valid token or incoming webhook uri from Slack. 35 | * [Grab a test token](https://api.slack.com/docs/oauth-test-tokens) 36 | * [Register a Slack app, grab a token](https://api.slack.com/docs/oauth) - we'll try wrapping this in the module later 37 | * [Add an incoming webhook to your team, grab the Uri](https://my.slack.com/services/new/incoming-webhook/) 38 | 39 | # Examples 40 | 41 | ### Send a Simple Slack Message 42 | 43 | ```powershell 44 | # This example shows a crudely crafted message without any attachments, 45 | # using parameters from Send-SlackMessage to construct the message. 46 | 47 | #Previously set up Uri from https://.slack.com/apps/A0F7XDUAZ 48 | $Uri = "Some incoming webhook uri from Slack" 49 | 50 | Send-SlackMessage -Uri $Uri ` 51 | -Channel '@wframe' ` 52 | -Parse full ` 53 | -Text 'Hello @wframe, join me in #devnull!' 54 | 55 | # Send a message to @wframe (not a channel), parsing the text to linkify usernames and channels 56 | ``` 57 | 58 |       ![Simple Send-SlackMessage](/Media/SimpleMessage.png) 59 | 60 | ### Search for a Slack Message 61 | 62 | ```powershell 63 | # Search for a message containing PowerShell, sorting results by timestamp 64 | 65 | Find-SlackMessage -Token $Token ` 66 | -Query 'PowerShell' ` 67 | -SortBy timestamp 68 | ``` 69 | 70 |       ![Find Message](/Media/FindMessage.png) 71 | 72 | ```powershell 73 | # Search for a message containing PowerShell 74 | # Results are sorted by best match by default 75 | # Notice the extra properties and previous/next messages 76 | 77 | Find-SlackMessage -Token $Token ` 78 | -Query 'PowerShell' | 79 | Select-Object -Property * 80 | ``` 81 | 82 |       ![Find Message Select All](/Media/FindMessageSelect.png) 83 | 84 | You could use this simply to search Slack from the CLI, or in an automated solution that might avoid posting if certain content is already found in Slack. 85 | 86 | ### Send a Richer Slack Message 87 | 88 | ```powershell 89 | # This is a simple example illustrating some common options 90 | # when constructing a message attachment 91 | # giving you a richer message 92 | 93 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 94 | 95 | New-SlackMessageAttachment -Color $([System.Drawing.Color]::red) ` 96 | -Title 'The System Is Down' ` 97 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 98 | -Text 'Please Do The Needful' ` 99 | -Pretext 'Everything is broken' ` 100 | -AuthorName 'SCOM Bot' ` 101 | -AuthorIcon 'http://ramblingcookiemonster.github.io/images/tools/wrench.png' ` 102 | -Fallback 'Your client is bad' | 103 | New-SlackMessage -Channel '@wframe' ` 104 | -IconEmoji :bomb: | 105 | Send-SlackMessage -Token $Token 106 | ``` 107 | 108 |       ![Rich messages](/Media/RichMessage.png) 109 | 110 | Notice that the title is clickable. You might link to... 111 | 112 | * The alert in question 113 | * A logging solution query 114 | * A dashboard 115 | * Some other contextual link 116 | * Strongbad 117 | 118 | ### Send Multiple Slack Attachments 119 | 120 | ```powershell 121 | # This example demonstrates that you can chain new attachments 122 | # together to form a multi-attachment message 123 | 124 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 125 | 126 | New-SlackMessageAttachment -Color $_PSSlackColorMap.red ` 127 | -Title 'The System Is Down' ` 128 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 129 | -Text 'Everybody panic!' ` 130 | -Pretext 'Everything is broken' ` 131 | -Fallback 'Your client is bad' | 132 | New-SlackMessageAttachment -Color $([System.Drawing.Color]::Orange) ` 133 | -Title 'The Other System Is Down' ` 134 | -TitleLink https://www.youtube.com/watch?v=TmpRs7xN06Q ` 135 | -Text 'Please Do The Needful' ` 136 | -Fallback 'Your client is bad' | 137 | New-SlackMessage -Channel '@wframe' ` 138 | -IconEmoji :bomb: ` 139 | -AsUser ` 140 | -Username 'SCOM Bot' | 141 | Send-SlackMessage -Token $Token 142 | ``` 143 | 144 |       ![Multiple Attachments](/Media/MultiAttachments.png) 145 | 146 | Notice that we can chain multiple New-SlackMessageAttachments together. 147 | 148 | ### Send a Table of Key Value Pairs 149 | 150 | ```powershell 151 | # This example illustrates a pattern where you might 152 | # want to send output from a script; you might 153 | # include errors, successful items, or other output 154 | 155 | # Pretend we're in a script, and caught an exception of some sort 156 | $Fail = [pscustomobject]@{ 157 | samaccountname = 'bob' 158 | operation = 'Remove privileges' 159 | status = "An error message" 160 | timestamp = (Get-Date).ToString() 161 | } 162 | 163 | # Create an array from the properties in our fail object 164 | $Fields = @() 165 | foreach($Prop in $Fail.psobject.Properties.Name) 166 | { 167 | $Fields += @{ 168 | title = $Prop 169 | value = $Fail.$Prop 170 | short = $true 171 | } 172 | } 173 | 174 | $Token = 'A token. maybe from https://api.slack.com/docs/oauth-test-tokens' 175 | 176 | # Construct and send the message! 177 | New-SlackMessageAttachment -Color $([System.Drawing.Color]::Orange) ` 178 | -Title 'Failed to process account' ` 179 | -Fields $Fields ` 180 | -Fallback 'Your client is bad' | 181 | New-SlackMessage -Channel 'devnull' | 182 | Send-SlackMessage -Uri $uri 183 | 184 | # We build up a pretend error object, and send each property to a 'Fields' array 185 | # Creates an attachment with the fields from our error 186 | # Creates a message fromthat attachment and sents it with a uri 187 | ``` 188 | 189 |       ![Fields](/Media/Fields.png) 190 | 191 | ### Store and Retrieve Configs 192 | 193 | To save time and typing, you can save a token or uri to a config file (protected via DPAPI) and a module variable. 194 | 195 | This is used as the default for commands, and is reloaded if you open a new PowerShell session. 196 | 197 | ```powershell 198 | # Save a Uri and Token. 199 | # If both are specified, token takes precedence. 200 | Set-PSSlackConfig -Uri 'SomeSlackUri' -Token 'SomeSlackToken' 201 | 202 | # Read the current cofig 203 | Get-PSSlackConfig 204 | ``` 205 | 206 | # Notes 207 | 208 | Currently evaluating .NET Core / Cross-platform functionality. The following will not work initially: 209 | 210 | * Serialization of URIs and tokens via Set-PSSlackConfig. Set these values per-session if needed 211 | * [System.Drawing.Color]::SomeColor shortcut. Use the provided $_PSSlackColorMap hash to simplify this. E.g. $_PSSlackColorMap.red 212 | 213 | There are a good number of Slack functions out there, including jgigler's [PowerShell.Slack](https://github.com/jgigler/Powershell.Slack) and Steven Murawski's [Slack](https://github.com/smurawski/Slack). We borrowed some ideas and code from these - thank you! 214 | 215 | If you want to go beyond interacting with the Slack API, you might consider [using a bot](http://ramblingcookiemonster.github.io/PoshBot/#references) 216 | -------------------------------------------------------------------------------- /Send-SlackChannelInvite.ps1: -------------------------------------------------------------------------------- 1 | function Send-SlackChannelInvite { 2 | <# 3 | .SYNOPSIS 4 | Send a Slack channel invite 5 | 6 | .DESCRIPTION 7 | Send a Slack channel invite 8 | 9 | .PARAMETER Token 10 | Token to use for the Slack API 11 | 12 | Default value is the value set by Set-PSSlackConfig 13 | 14 | .PARAMETER Channel 15 | ID of the Channel, private group, or IM channel to send file to. 16 | 17 | .PARAMETER User 18 | One or more user IDs to invite to the channel 19 | 20 | .PARAMETER ForceVerbose 21 | If specified, don't explicitly remove verbose output from Invoke-RestMethod 22 | 23 | *** WARNING *** 24 | This will expose your token in verbose output 25 | 26 | .EXAMPLE 27 | Send-SlackChannelInvite -Token $Token ` 28 | -Channel ABC123ID ` 29 | -User XYZ890ID 30 | 31 | .FUNCTIONALITY 32 | Slack 33 | #> 34 | 35 | param ( 36 | [string]$Token = $Script:PSSlack.Token, 37 | [string]$Channel, 38 | [string[]]$User, 39 | [switch]$ForceVerbose = $Script:PSSlack.ForceVerbose 40 | ) 41 | process 42 | { 43 | $body = @{} 44 | switch ($psboundparameters.keys) { 45 | 'Channel' { $body.channel = $Channel } 46 | 'User' { $body.users = $User } 47 | } 48 | Write-Verbose "Send-SlackApi -Body $($body | Format-List | Out-String)" 49 | $Params = @{ 50 | Method = 'conversations.invite' 51 | Body = $Body 52 | Token = $Token 53 | ForceVerbose = $ForceVerbose 54 | } 55 | $response = Send-SlackApi @Params 56 | 57 | $response 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Tests/PSSlack.Tests.ps1: -------------------------------------------------------------------------------- 1 | $PSVersion = $PSVersionTable.PSVersion.Major 2 | $ModuleName = $ENV:BHProjectName 3 | $ModulePath = Join-Path $ENV:BHProjectPath $ModuleName 4 | 5 | # Verbose output for non-master builds on appveyor 6 | # Handy for troubleshooting. 7 | # Splat @Verbose against commands as needed (here or in pester tests) 8 | $Verbose = @{} 9 | if($ENV:BHBranchName -notlike "master" -or $env:BHCommitMessage -match "!verbose") 10 | { 11 | $Verbose.add("Verbose",$True) 12 | } 13 | 14 | Import-Module $ModulePath -Force 15 | 16 | $TestUri = 'TestUri' 17 | $TestToken = 'TestToken' 18 | $TestArchive = 'TestArchive' 19 | $TestProxy = 'TestProxy' 20 | 21 | $AlternativePath = 'TestDrive:\ThisSlackXml.xml' 22 | 23 | Describe "PSSlack Module PS$PSVersion" { 24 | Context 'Strict mode' { 25 | 26 | Set-StrictMode -Version latest 27 | 28 | It 'Should load' { 29 | $Module = Get-Module $ModuleName 30 | $Module.Name | Should Be $ModuleName 31 | $Commands = $Module.ExportedCommands.Keys 32 | $Commands -contains 'Find-SlackMessage' | Should Be $True 33 | $Commands -contains 'Get-PSSlackConfig' | Should Be $True 34 | $Commands -contains 'Set-PSSlackConfig' | Should Be $True 35 | $Commands -contains 'New-SlackMessage' | Should Be $True 36 | $Commands -contains 'New-SlackMessageAttachment' | Should Be $True 37 | $Commands -contains 'Send-SlackMessage' | Should Be $True 38 | } 39 | 40 | It 'Should have empty values in PSSlack.xml' { 41 | $Config = Import-Clixml "$env:TEMP\$env:USERNAME-$env:COMPUTERNAME-PSSlack.xml" 42 | $Props = $Config.PSObject.Properties.Name 43 | #Loop is faster but less clear in failed tests. 44 | $Props -contains 'Uri' | Should Be $True 45 | $Props -contains 'Token' | Should Be $True 46 | $Props -contains 'ArchiveUri' | Should Be $True 47 | $Props -contains 'Proxy' | Should Be $True 48 | 49 | $Config.Uri | Should BeNullOrEmpty 50 | $Config.Token | Should BeNullOrEmpty 51 | $Config.ArchiveUri | Should BeNullOrEmpty 52 | $Config.Proxy | Should BeNullOrEmpty 53 | } 54 | } 55 | } 56 | 57 | Describe "Set-PSSlackConfig PS$PSVersion" { 58 | Context 'Strict mode' { 59 | 60 | Set-StrictMode -Version latest 61 | 62 | It 'Should set PSSlack.xml' { 63 | $Params = @{ 64 | Uri= $TestUri 65 | Token = $TestToken 66 | ArchiveUri = $TestArchive 67 | Proxy = $TestProxy 68 | } 69 | Set-PSSlackConfig @params 70 | $Config = Import-Clixml "$env:TEMP\$env:USERNAME-$env:COMPUTERNAME-PSSlack.xml" 71 | 72 | $Config.Uri | Should BeOfType System.Security.SecureString 73 | $Config.Token | Should BeOfType System.Security.SecureString 74 | $Config.ArchiveUri | Should Be 'TestArchive' 75 | $Config.Proxy | Should Be 'TestProxy' 76 | } 77 | 78 | It 'Should set a user-specified file' { 79 | $Params = @{ 80 | Uri= $TestUri 81 | Token = $TestToken 82 | ArchiveUri = "$TestArchive`x" 83 | Proxy = "$TestProxy`x" 84 | Path = $AlternativePath 85 | } 86 | Set-PSSlackConfig @params 87 | $Config = Import-Clixml $AlternativePath 88 | 89 | $Config.Uri | Should BeOfType System.Security.SecureString 90 | $Config.Token | Should BeOfType System.Security.SecureString 91 | $Config.ArchiveUri | Should Be 'TestArchivex' 92 | $Config.Proxy | Should Be 'TestProxyx' 93 | } 94 | } 95 | } 96 | 97 | Describe "Get-PSSlackConfig PS$PSVersion" { 98 | Context 'Strict mode' { 99 | 100 | Set-StrictMode -Version latest 101 | 102 | It 'Should read PSSlack.xml' { 103 | $Config = Get-PSSlackConfig -Source PSSlack.xml 104 | 105 | $Config.Uri | Should Be 'TestUri' 106 | $Config.Token | Should Be 'TestToken' 107 | $Config.ArchiveUri | Should Be 'TestArchive' 108 | $Config.Proxy | Should Be 'TestProxy' 109 | } 110 | 111 | It 'Should read PSSlack variable' { 112 | $Config = Get-PSSlackConfig -Source PSSlack 113 | 114 | $Config.Uri | Should Be 'TestUri' 115 | $Config.Token | Should Be 'TestToken' 116 | $Config.ArchiveUri | Should Be 'TestArchivex' #From running alternate path test before... 117 | $Config.Proxy | Should Be 'TestProxyx' #From running alternate path test before... 118 | } 119 | 120 | It 'Should read a user-specified file' { 121 | # We've tested set... use it here. 122 | $Params = @{ 123 | Uri= $TestUri 124 | Token = $TestToken 125 | ArchiveUri = "$TestArchive`x" 126 | Proxy = "$TestProxy`x" 127 | Path = $AlternativePath 128 | } 129 | Set-PSSlackConfig @params 130 | 131 | $Config = Get-PSSlackConfig -Path $AlternativePath 132 | 133 | $Config.Uri | Should Be 'TestUri' 134 | $Config.Token | Should Be 'TestToken' 135 | $Config.ArchiveUri | Should Be 'TestArchivex' 136 | $Config.Proxy | Should Be 'TestProxyx' 137 | } 138 | } 139 | } 140 | 141 | # Tests have passed, rely on set-psslackconfig... 142 | Set-PSSlackConfig -Uri $null -Token $null -ArchiveUri $null -Proxy $null 143 | 144 | 145 | Describe "Send-SlackMessage PS$PSVersion" { 146 | InModuleScope $ModuleName { 147 | 148 | Mock -ModuleName PSSlack -CommandName Send-SlackApi { 149 | [pscustomobject]@{ 150 | PSB = $PSBoundParameters 151 | Arg = $Args 152 | } 153 | } 154 | Mock -ModuleName PSSlack -CommandName Invoke-RestMethod { 155 | [pscustomobject]@{ 156 | PSB = $PSBoundParameters 157 | Arg = $Args 158 | } 159 | } 160 | 161 | It 'Should call Send-SlackApi for token auth' { 162 | $x = Send-SlackMessage -Token Token -Text 'Hi' 163 | Assert-MockCalled -ModuleName PSSlack -CommandName Send-SlackApi -Scope Describe 164 | } 165 | 166 | It 'Should call Invoke-RESTMethod for Uri auth' { 167 | $x = Send-SlackMessage -Uri Uri -Text 'Hi' 168 | Assert-MockCalled -ModuleName PSSlack -CommandName Invoke-RestMethod -Scope Describe 169 | } 170 | 171 | It 'Should not pass parameters if not specified' { 172 | $x = Send-SlackMessage -Token Token -Text 'Hi' 173 | # 6 we see here, 2 are from ForceVerbose resulting in Verbose $False... 174 | $x.arg.count | Should Be 8 175 | $x.arg -contains '-Body:' | Should Be $True 176 | $x.arg -contains '-Method:' | Should Be $True 177 | $x.arg -contains '-Token:' | Should Be $True 178 | $x.arg -contains 'Token' | Should Be $True 179 | $x.arg -contains 'chat.postMessage' | Should Be $True 180 | } 181 | } 182 | } 183 | 184 | Describe "Test-SlackApi" { 185 | Context "Strict Mode" { 186 | Set-StrictMode -Version latest 187 | It "Should receive API response" { 188 | $x = Test-SlackApi 189 | $x | Should Be $true 190 | } 191 | It "Should fail with invalid API keys" { 192 | {Test-SlackApi -Token "PSSlack_InvalidAPIToken"} | Should -Throw 193 | } 194 | } 195 | 196 | } 197 | 198 | Remove-Item $env:TEMP\$env:USERNAME-$env:COMPUTERNAME-PSSlack.xml -force -Confirm:$False 199 | 200 | Describe 'New-SlackMessageAttachment' { 201 | It 'Should be a hashtable when not on pipeline' { 202 | $message = New-SlackMessageAttachment -Text "Test" -Fallback "test" 203 | $message | Should -BeOfType Hashtable 204 | } 205 | 206 | It 'Should Be an Array with Three Members, when Piped Three Times' { 207 | $message = New-SlackMessageAttachment -text "test1" -Fallback 'Test1' | 208 | New-SlackMessageAttachment -text "test2" -Fallback "test2" | 209 | New-SlackMessageAttachment -text "test3" -FallBack "test3" 210 | $message.Count | Should -BeExactly 3 211 | } 212 | It 'Should Be an Array with Five Members, when Piped Five Times' { 213 | $message = New-SlackMessageAttachment -text "test1" -Fallback 'Test1' | 214 | New-SlackMessageAttachment -text "test2" -Fallback "test2" | 215 | New-SlackMessageAttachment -text "test3" -FallBack "test3" | 216 | New-SlackMessageAttachment -text "test4" -Fallback "test4" | 217 | New-SlackMessageAttachment -text "test5" -Fallback "test5" 218 | $message.Count | Should -BeExactly 5 219 | } 220 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # See http://www.appveyor.com/docs/appveyor-yml for many more options 2 | 3 | #Publish to PowerShell Gallery with this key 4 | environment: 5 | NuGetApiKey: 6 | secure: oqMFzG8F65K5l572V7VzlZIWU7xnSYDLtSXECJAAURrXe8M2+BAp9vHLT+1h1lR0 7 | 8 | # Allow WMF5 (i.e. PowerShellGallery functionality) 9 | os: WMF 5 10 | 11 | # Skip on updates to the readme. 12 | # We can force this by adding [skip ci] or [ci skip] anywhere in commit message 13 | skip_commits: 14 | message: /updated readme.*|update readme.*s/ 15 | 16 | build: false 17 | 18 | #Kick off the CI/CD pipeline 19 | test_script: 20 | - ps: . .\build.ps1 -Task 'Deploy' -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | param($Task = 'Default') 2 | 3 | # Grab nuget bits, install modules, set build variables, start build. 4 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 5 | Install-Module Pester -RequiredVersion 4.10.1 -Force -AllowClobber -SkipPublisherCheck -Scope CurrentUser 6 | Install-Module Psake, PSDeploy, BuildHelpers -force 7 | Import-Module Psake, BuildHelpers 8 | 9 | Set-BuildEnvironment 10 | 11 | Invoke-psake .\psake.ps1 -taskList $Task -nologo 12 | exit ( [int]( -not $psake.build_success ) ) -------------------------------------------------------------------------------- /deploy.psdeploy.ps1: -------------------------------------------------------------------------------- 1 | # Generic module deployment. 2 | # This stuff should be moved to psake for a cleaner deployment view 3 | 4 | # ASSUMPTIONS: 5 | 6 | # folder structure of: 7 | # - RepoFolder 8 | # - This PSDeploy file 9 | # - ModuleName 10 | # - ModuleName.psd1 11 | 12 | # Nuget key in $ENV:NugetApiKey 13 | 14 | # Set-BuildEnvironment from BuildHelpers module has populated ENV:BHProjectName 15 | 16 | # Publish to gallery with a few restrictions 17 | if( 18 | $env:BHProjectName -and $env:BHProjectName.Count -eq 1 -and 19 | $env:BHBuildSystem -ne 'Unknown' -and 20 | $env:BHBranchName -eq "master" -and 21 | $env:BHCommitMessage -match '!deploy' 22 | ) 23 | { 24 | Deploy Module { 25 | By PSGalleryModule { 26 | FromSource $ENV:BHProjectName 27 | To PSGallery 28 | WithOptions @{ 29 | ApiKey = $ENV:NugetApiKey 30 | } 31 | } 32 | } 33 | } 34 | else 35 | { 36 | "Skipping deployment: To deploy, ensure that...`n" + 37 | "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + 38 | "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + 39 | "`t* Your commit message includes !deploy (Current: $ENV:BHCommitMessage)" | 40 | Write-Host 41 | } 42 | 43 | # Publish to AppVeyor if we're in AppVeyor 44 | if( 45 | $env:BHProjectName -and $ENV:BHProjectName.Count -eq 1 -and 46 | $env:BHBuildSystem -eq 'AppVeyor' 47 | ) 48 | { 49 | Deploy DeveloperBuild { 50 | By AppVeyorModule { 51 | FromSource $ENV:BHProjectName 52 | To AppVeyor 53 | WithOptions @{ 54 | Version = $env:APPVEYOR_BUILD_VERSION 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /psake.ps1: -------------------------------------------------------------------------------- 1 | # PSake makes variables declared here available in other scriptblocks 2 | # Init some things 3 | Properties { 4 | # Find the build folder based on build system 5 | $ProjectRoot = $ENV:BHProjectPath 6 | if(-not $ProjectRoot) 7 | { 8 | $ProjectRoot = $PSScriptRoot 9 | } 10 | 11 | $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" 12 | $PSVersion = $PSVersionTable.PSVersion.Major 13 | $TestFile = "TestResults_PS$PSVersion`_$TimeStamp.xml" 14 | $lines = '----------------------------------------------------------------------' 15 | 16 | $Verbose = @{} 17 | if($ENV:BHCommitMessage -match "!verbose") 18 | { 19 | $Verbose = @{Verbose = $True} 20 | } 21 | } 22 | 23 | Task Default -Depends Test 24 | 25 | Task Init { 26 | $lines 27 | Set-Location $ProjectRoot 28 | "Build System Details:" 29 | Get-Item ENV:BH* 30 | "`n" 31 | } 32 | 33 | Task Test -Depends Init { 34 | $lines 35 | "`n`tSTATUS: Testing with PowerShell $PSVersion" 36 | 37 | # Gather test results. Store them in a variable and file 38 | $TestResults = Invoke-Pester -Path $ProjectRoot\Tests -PassThru -OutputFormat NUnitXml -OutputFile "$ProjectRoot\$TestFile" 39 | 40 | # In Appveyor? Upload our tests! #Abstract this into a function? 41 | If($ENV:BHBuildSystem -eq 'AppVeyor') 42 | { 43 | (New-Object 'System.Net.WebClient').UploadFile( 44 | "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", 45 | "$ProjectRoot\$TestFile" ) 46 | } 47 | 48 | Remove-Item "$ProjectRoot\$TestFile" -Force -ErrorAction SilentlyContinue 49 | 50 | # Failed tests? 51 | # Need to tell psake or it will proceed to the deployment. Danger! 52 | if($TestResults.FailedCount -gt 0) 53 | { 54 | Write-Error "Failed '$($TestResults.FailedCount)' tests, build failed" 55 | } 56 | "`n" 57 | } 58 | 59 | Task Build -Depends Test { 60 | $lines 61 | 62 | # Load the module, read the exported functions, update the psd1 FunctionsToExport 63 | Set-ModuleFunctions @Verbose 64 | 65 | # Bump the module version if we didn't manually bump it 66 | Try 67 | { 68 | $GalleryVersion = Get-NextNugetPackageVersion -Name $env:BHProjectName -ErrorAction Stop 69 | $GithubVersion = Get-MetaData -Path $env:BHPSModuleManifest -PropertyName ModuleVersion -ErrorAction Stop 70 | if($GalleryVersion -ge $GithubVersion) { 71 | Update-Metadata -Path $env:BHPSModuleManifest -PropertyName ModuleVersion -Value $GalleryVersion -ErrorAction stop 72 | } 73 | } 74 | Catch 75 | { 76 | "Failed to update version for '$env:BHProjectName': $_.`nContinuing with existing version" 77 | } 78 | } 79 | 80 | Task Deploy -Depends Build { 81 | $lines 82 | 83 | $Params = @{ 84 | Path = $ProjectRoot 85 | Force = $true 86 | Recurse = $false # We keep psdeploy artifacts, avoid deploying those : ) 87 | } 88 | Invoke-PSDeploy @Verbose @Params 89 | } 90 | --------------------------------------------------------------------------------