Poshbot.Democracy.psm1
<# Module Mixed by BarTender A Framework for making PowerShell Modules Version: 6.1.22 Author: Adrian.Andersson Copyright: 2019 Domain Group Module Details: Module: Poshbot.Democracy Description: A poshbot based slack voting system Revision: 1.0.21.1 Author: Adrian.Andersson Company: Adrian.Andersson Check Manifest for more details #> function close-vote { <# .SYNOPSIS Close the active vote and get the results .DESCRIPTION Close the active vote and get the results .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] [PoshBot.BotCommand( CommandName = 'closevote' )] PARAM( ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" #$user = $global:PoshBotContext.CallingUserInfo.FirstName $voteData = get-voteData } process{ write-verbose 'Check the channel/user and the votedata exists' $channel = $global:PoshBotContext.OriginalMessage.RawMessage.channel if(!$channel) { new-poshbotCardResponse -type Error -text 'Invalid channel response' return } $activeVote = get-activeVote -channel $channel if($activeVote) { new-poshbotTextResponse -text '_closing vote_' $results = get-voteresult $activeVote if($results) { new-poshbotCardResponse -type normal -text $results -Title "Results | $($activeVote.title)" } $activeVote.isActive = $false $activeVote.closedDate = get-date #Need to save the file again somehow if($(save-activeVote $activeVote) -eq $true){ write-verbose 'succesful save' return }else{ write-verbose 'error save' new-poshbotCardResponse -type Warning -text 'Error saving vote' -Title 'Error Saving Vote' } }else{ new-poshbotCardResponse -type Warning -text 'This channel has no active votes (debug: na)' -Title 'No active votes' return } } } function get-activeVoteBotCommand { <# .SYNOPSIS Get the currently active vote for the channel .DESCRIPTION Get the currently active vote for the channel .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] [PoshBot.BotCommand( CommandName = 'getVote' )] PARAM( ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" #$user = $global:PoshBotContext.CallingUserInfo.FirstName } process{ write-verbose 'Check the channel/user and the votedata exists' $channel = $global:PoshBotContext.OriginalMessage.RawMessage.channel if(!$channel) { new-poshbotCardResponse -type Error -text 'Invalid channel response' return } $activeVote = get-activeVote -channel $channel if($activeVote) { $optionCount = $($activeVote.options|measure-object).count $i = 0 $optionRes = while($i -lt $optionCount) { [psCustomObject] @{ id = $i+1 option = $activeVote.options[$i] } $i++ } new-poshbotCardResponse -type normal -text $($optionRes|format-table|out-string) -Title "$title `nUse `!vote #id` to vote" return }else{ new-poshbotCardResponse -type Warning -text 'This channel has no active votes (debug: na)' -Title 'No active votes' return } } } function get-voteResultBotCommand { <# .SYNOPSIS Get the vote results without closing via a poshbot command .DESCRIPTION Save a users voteGet the vote results without closing via a poshbot command Invokes the get-voteResult function via Poshbot command . Will get the active vote for the current channel - .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script #> [CmdletBinding()] [PoshBot.BotCommand( CommandName = 'voteresult' )] PARAM( ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" #$user = $global:PoshBotContext.CallingUserInfo.FirstName } process{ write-verbose 'Check the channel/user and the votedata exists' $channel = $global:PoshBotContext.OriginalMessage.RawMessage.channel if(!$channel) { new-poshbotCardResponse -type Error -text 'Invalid channel response' return } $activeVote = get-activeVote -channel $channel if($activeVote) { $result = get-voteresult $($activeVote) if($result) { new-poshbotCardResponse -type normal -text $result -Title "Results | $($activeVote.title)" }else{ new-poshbotCardResponse -type Error -text 'Unable to get active vote results' -Title "Results | $($activeVote.title)" } return }else{ new-poshbotCardResponse -type Warning -text 'This channel has no active votes (debug: na)' -Title 'No active votes' return } } } function new-vote { <# .SYNOPSIS Create a new vote .DESCRIPTION For the current channel, if there are no active votes then Create a new vote for the current channel Set it to active .PARAMETER title What are we voting on .PARAMETER options Need at least two What are the voting options .PARAMETER closeAfter After this many votes, close it automatically 0 means manual close .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] [PoshBot.BotCommand( CommandName = 'newvote' )] PARAM( [Parameter(Mandatory=$true,Position=0)] [string]$title, [Parameter(Mandatory=$true,Position=1)] [array]$options, [Parameter(Mandatory=$false,Position=2)] [int]$closeAfter = 0 ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" #$user = $global:PoshBotContext.CallingUserInfo.FirstName $voteData = get-voteData } process{ write-verbose 'Check the channel and the votedata exists' $channel = $global:PoshBotContext.OriginalMessage.RawMessage.channel if(!$channel) { new-poshbotCardResponse -type Error -text 'Invalid channel response' return } if(!$voteData) { new-poshbotCardResponse -type Error -text 'Unable to load vote data' return } write-verbose 'Check we dont have active data already for this channel' if($votedata."$channel") { write-verbose 'Channel data found, checking for validity and active vote' if($votedata."$channel".getType().Name -ne 'Hashtable') { write-verbose 'Channel data exists but is not a hashtable, recreating it' $votedata."$channel" = @{} }else{ $keys = $voteData."$channel".keys foreach($key in $keys) { if($votedata."$channel"."$key".isActive -eq $true) { new-poshbotCardResponse -type Error -text "Please close the current active vote`n*$($votedata."$channel"."$key".isActive.title)*" -Title 'Vote already open' return } } } }else{ write-verbose 'Channel data not found, creating' $votedata."$channel" = @{} } $optionCount = $($options|measure-object).Count if($optionCount -le 1) { new-poshbotCardResponse -type Error -text "Not enough options" -Title 'Options' return }elseIf($optionCount -gt 9) { new-poshbotCardResponse -type Error -text "Too many options - currently limited to 9" -Title 'Options' return } $voteId = $voteData."$channel".count $voteDetails = @{ id = $voteId channel = $channel title = $title closeAfter = $closeAfter isActive = $true votes = @{} options = [array]$options createdDate = get-date closedDate = $null } write-verbose 'Getting voteId' write-verbose "Got Id of $voteId - Adding data" $voteData."$channel"."$voteId" = $voteDetails write-verbose 'Saving data' save-voteData $voteData $i = 0 $optionRes = while($i -lt $optionCount) { [psCustomObject] @{ id = $i+1 option = $options[$i] } $i++ } new-poshbotCardResponse -type normal -text $($optionRes|format-table|out-string) -Title "$title `nUse `!vote #id` to vote" } } function Save-vote { <# .SYNOPSIS Allow user to select a vote and save it .DESCRIPTION Allow user to select a vote and save it Will select the current active vote for the channel Will also get results if we go over the count .PARAMETER option What option they currently selected ------------ .EXAMPLE verb-noun param1 #### DESCRIPTION Line by line of what this example will do #### OUTPUT Copy of the output of this line .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] [PoshBot.BotCommand( CommandName = 'vote' )] PARAM( [Parameter(Mandatory=$true,Position=0)] [ValidateRange(1,9)] [int]$option ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" #$user = $global:PoshBotContext.CallingUserInfo.FirstName } process{ write-verbose 'Check the channel/user and the votedata exists' $channel = $global:PoshBotContext.OriginalMessage.RawMessage.channel $userId = $global:PoshBotContext.FromName if(!$channel) { new-poshbotCardResponse -type Error -text 'Invalid channel response' return } if(!$userId) { new-poshbotCardResponse -type Error -text 'Invalid user' return } $activeVote = get-activeVote $channel if($activeVote) { $optionAdjust = $option-1 $optionSelect = $activeVote.options[$optionAdjust] if($optionSelect) { write-verbose 'Checking if user has already voted' if($activeVote.votes."$userId") { #'1' write-verbose 'Active vote for user in play' $response = "$($global:PoshBotContext.CallingUserInfo.FirstName) changed their vote to $optionSelect" }else{ #'2' write-verbose 'New vote for user' $response = "$($global:PoshBotContext.CallingUserInfo.FirstName) voted for $optionSelect" } $activeVote.votes."$userId" = $optionAdjust new-poshbotTextResponse -text $response #Check whether we should close voting $close = get-voteCloseStatus $activeVote #$close if($close -eq 'shouldClose') { write-verbose 'We should close the vote' new-poshbotTextResponse -text '_That was the last required vote, closing and getting results_' $activeVote.isActive = $false #get the results $results = get-voteresult $activeVote if($results) { new-poshbotCardResponse -type normal -text $results -Title "Results | $($activeVote.title)" } } if($(save-activeVote $activeVote) -eq $true){ write-verbose 'succesful save' return }else{ write-verbose 'error save' new-poshbotCardResponse -type Warning -text 'Error saving vote' -Title 'Error Saving Vote' } }else{ new-poshbotTextResponse -DM -Text "Option $option is invalid" return } }else{ new-poshbotCardResponse -type Warning -text 'This channel has no active votes (debug: ac)' -Title 'No active votes' return } } } function get-activeVote { <# .SYNOPSIS Get the vote data and return the active vote .DESCRIPTION Get the vote data and return the active vote for the channel id provided .PARAMETER channelId What channel .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script #> [CmdletBinding()] PARAM( [Parameter(Mandatory=$true)] [string]$channel ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" } process{ $voteData = get-voteData write-verbose 'Channel data found, checking for validity and active vote' if($votedata."$channel".getType().Name -ne 'Hashtable') { write-verbose 'Channel data not a hashtable, probably corrupt, return $null' return } if(($voteData."$channel") -and ($votedata."$channel".getType().Name -eq 'Hashtable')) { write-verbose 'Channel available and valid' $keys = $voteData."$channel".keys $activeVotes = foreach($key in $keys) { if($votedata."$channel"."$key".isActive -eq $true) { $votedata."$channel"."$key" } } $activeVotesCount = $($activeVotes|measure-object).count if($activeVotesCount -gt 1) { write-verbose 'We have undesired multiple votes, return the most recent' return $($activeVotes|sort-object -property createdDate -Descending|Select-object -First 1) }elseIf($activeVotesCount -eq 1) { write-verbose 'We have the desired active vote' return $activeVotes }else{ write-verbose 'No Active votes for this channel' return $null } }else{ write-verbose 'Either no channel data or data is corrupt' return $null } $activeVotes = if($votedata."$channel") { else{ } }else{ write-verbose 'No data for channel' return } } } function get-voteCloseStatus { <# .SYNOPSIS Figure out what the close state should be I.E. if the closeAfter is set, should we close it .DESCRIPTION Figure out what the close state should be I.E. if the closeAfter is set, should we close it .PARAMETER ActiveVote An active vote hashtable .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script #> [CmdletBinding()] PARAM( [Parameter(Mandatory=$true,Position=0)] [hashtable]$activeVote ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" } process{ write-verbose 'Check we have valid voteData' if($activeVote) { if($activeVote.isActive -eq $true) { write-verbose 'check the number of votes' $count = $activeVote.votes.count if($count -ge $activeVote.closeAfter -and $activeVote.closeAfter -ne 0) { #we should close return 'shouldClose' }elseIf($activeVote.closeAfter -eq 0){ return 'manualClose' }else{ return 'active' } }else{ return 'closed' } }else{ return 'error' } } } function get-voteData { <# .SYNOPSIS Get the vote data .DESCRIPTION Get all the vote data from the vote file If there is none, pass back an empty hashtable .PARAMETER path Path to the xml file .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] PARAM( [Parameter()] [string]$Path = 'c:\poshbot\voteData\voteData.xml' ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" } process{ $folder = split-path $Path -Parent $file = split-path $Path -Leaf write-verbose 'Checking for directory' if(!$(test-path $folder)) { new-item -ItemType Directory -Path $folder -Force write-verbose "New directory created at $folder" }else{ write-verbose "Directory exists at $folder" } write-verbose 'Checking for file' if(!$(test-path $Path)) { write-verbose 'File not found, creating hash' $voteHash = @{} }else{ write-verbose 'File found, importing' $voteHash = import-clixml $path } return $voteHash } } function get-voteResult { <# .SYNOPSIS Get a preformated response for the active Vote .DESCRIPTION Get a preformated response for the active Vote Return a nice vote tally As well as a line-by-line of who chose what .PARAMETER activeVote An active vote hashtable .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script #> [CmdletBinding()] PARAM( [Parameter(Mandatory=$true,Position=0)] [hashtable]$activeVote ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" } process{ write-verbose 'Check we have valid voteData' if($activeVote) { $groupedResults = $activeVote.votes.values |group-object|select-object Count,Name $totalVotes = $($groupedResults.count|measure-object -Sum).sum $i = 0 $totalOptions = $($activeVote.options|measure-object).count $voteSummary = while($i -lt $totalOptions) { $option = $activeVote.options[$i] $votes = $($groupedResults|where-object{$_.name -eq $i}).count if(!$votes) { $votes = 0 } $voters = $activeVote.votes [psCustomObject] @{ Option = $option Votes = $votes Percent = $([math]::round($($votes/$totalVotes)*100,0)) voters = $($activevote.votes.GetEnumerator()|where-object{$_.value -eq $i}).name } $i++ } $top = "$($voteSummary | select-object Option,Votes,Percent|format-table|out-string)" $bottom = foreach($v in $voteSummary) { "*$($v.option)*`n--------------------------`n $($v.voters -join "`n ")`n`n`n" } $result = "$top`n$bottom" return $result }else{ return $null } } } function save-activeVote { <# .SYNOPSIS Take an active vote, match it to all the vote data, update it, then save it out to the file .DESCRIPTION Take an active vote, match it to all the vote data, update it, then save it out to the file .PARAMETER activeVote An active vote hashtable .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] PARAM( [Parameter(Mandatory=$true)] [hashtable]$activeVote ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" } process{ $voteData = get-voteData $channel = $activeVote.channel $voteId = $activeVote.id if($voteData."$channel"."$voteid") { $voteData."$channel"."$voteid" = $activeVote save-voteData $voteData return $true }else{ return $false } } } function save-voteData { <# .SYNOPSIS Save the vote data .DESCRIPTION Helper function to save all the votedata to file .PARAMETER voteData Hashtable of the voteData .PARAMETER path Path to the xml file .NOTES Author: Adrian Andersson Changelog: 2019-03-22 - AA - Initial Script .COMPONENT What cmdlet does this script live in #> [CmdletBinding()] PARAM( [Parameter()] [string]$Path = 'c:\poshbot\voteData\voteData.xml', [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)] [hashtable]$voteData ) begin{ #Return the script name when running verbose, makes it tidier write-verbose "===========Executing $($MyInvocation.InvocationName)===========" #Return the sent variables when running debug Write-Debug "BoundParams: $($MyInvocation.BoundParameters|Out-String)" } process{ $folder = split-path $Path -Parent write-verbose 'Checking for directory' if(!$(test-path $folder)) { write-warning 'Folder does not exist' new-item -ItemType Directory -Path $folder -Force write-verbose "New directory created at $folder" }else{ write-verbose "Directory exists at $folder" } $voteData | Export-Clixml -Path $path -Depth 6 } } |