poshbotio / PoshBot

Powershell-based bot framework
MIT License
536 stars 108 forks source link

Help with Middleware PreResponse Hook #157

Closed mgeorgebrown89 closed 5 years ago

mgeorgebrown89 commented 5 years ago

I'm trying to get the approve needed message in slack to also tag users Slack names via a preresponse middleware hook. This seems the most appropriate place to do this. I've tried a few different approaches, but all I've been able to do so far is make the bot not send any response at all except for the checkmark. I'm trying to regex match the response text and then append the users id with an @ symbol (something I've done with other functions) but I doesn't seem to be doing anything, and I'm not sure how to debug this. The commented out code was my attempt to debug and get the group names to match against the groups.psd1 file. It worked this way, but the approval message sent by the bot must reside somewhere else.

param(
    $Context,
    $Bot
)

Import-LocalizedData -BaseDirectory 'C:\Repositories\PoshBot' -FileName groups.psd1 -BindingVariable groups

<#$Text = "Approval is needed to run [newWs Vegas 'Axiom Constructors IMCO' vs-axiom01] from someone in the approval group(s) [admin, Cloud Ops Engineers].
To approve, say '!approve 60ec9079'.
To deny, say '!deny 60ec9079'.
To list pending approvals, say '!pending'."
$Response = @{
    Text = $Text
}
$Context = @{
    Response = $Response
}#>

if($Context.Response.Text -match ("Approval is needed")){
    $approvalGroups = (((($Context.Response.Text -split "]")[1]) -split '\[') -split ",").Trim()
    foreach($approvalGroup in $approvalGroups){
        $groupUsers = $groups."$approvalGroup".Users
        foreach($user in $groupUsers){
            $Context.Response.Text += "<@$user> "
        }
    }
}

$Context
devblackops commented 5 years ago

@mgeorgebrown89 The command approval message is sent "out of band" so you won't have access to modify the message directly.

However, since the middleware hooks have the main bot instance sent as a parameter you can send your own message and have access to call all the internal bot methods if needed.

Try something like this as a preexecute hook. Pre/Post response hooks won't work as pending commands get put in a separate queue and won't be executed until they are approved and response hooks are post command execution.

PreResponse.ps1

param(
    $Context,
    $Bot
)

$commandName = $Context.Command.Name
$Bot.LogInfo("Starting preexecute hook for [$commandName] command")

Import-LocalizedData -BaseDirectory $Bot.Configuration.ConfigurationDirectory -FileName groups.psd1 -BindingVariable groups

if ($Bot.Executor.ApprovalNeeded($Context)) {

    $Bot.LogInfo("[PreExecute] hook approval needed. Forming custom response")

    $commandString = $Context.ParsedCommand.CommandString
    $from = $Context.Message.From

    $msg = '[<@{0}>] needs approval to run the following: {1}{2}' -f $from, $commandString, [System.Environment]::NewLine
    $msg += 'Available approvers: '

    $approvalGroups = $Bot.Executor.GetApprovalGroups($Context)
    foreach($approvalGroup in $approvalGroups){
        $groupUsers = $groups.$approvalGroup.Users
        foreach($user in $groupUsers){
            $msg += "<@$user> "
        }
    }

    $Bot.LogInfo("Sending approval notification: [$msg]")

    $Context.Response.To              = $Context.Message.To
    $Context.Response.MessageFrom     = $Context.Message.From
    $Context.Response.OriginalMessage = $Context.Message
    $Context.Response.Data            = New-PoshBotCardResponse -Type Warning -Title "Approval Notification for: [$commandString]" -Text $msg
    $Bot.SendMessage($Context.Response)
    $Bot.LogInfo("Ending [PreExecute] hook")
}

$Context
mgeorgebrown89 commented 5 years ago

This is great, thank you! I tweaked it a bit to eliminate the command executor from the list of available approvers and any duplicate users (if they're in more than one group of approvers).

param(
    $Context,
    $Bot
)

$commandName = $Context.Command.Name
$Bot.LogInfo("Starting preexecute hook for [$commandName] command")

Import-LocalizedData -BaseDirectory $Bot.Configuration.ConfigurationDirectory -FileName groups.psd1 -BindingVariable groups
Import-Module 'C:\Repositories\PoshBot\plugins\PoshBot.Personality\PoshBot.Personality.psm1'

if ($Bot.Executor.ApprovalNeeded($Context)) {

    $Bot.LogInfo("[PreExecute] hook approval needed. Forming custom response")

    $commandString = $Context.ParsedCommand.CommandString
    $from = $Context.Message.From

    $Greeting = Get-VectoriaResponse -type Greeting
    $gre = $Greeting.response

    $msg = "$gre, "
    $approvalGroups = $Bot.Executor.GetApprovalGroups($Context)
    $approvalUsers = @()
    foreach ($approvalGroup in $approvalGroups) {
        $groupUsers = $groups.$approvalGroup.Users
        foreach ($user in $groupUsers) {
            if ($user -ne $from) {
                $approvalUsers += "$user"
            }
        }
    }
    $approvalUsers = $approvalUsers |Sort-Object | Select-Object -Unique
    if ($approvalUsers.Count -eq 1) {
        $msg += "<@$approvalUsers>."
    }
    elseif ($approvalUsers.Count -eq 2) {
        $user1 = $approvalUsers[0]
        $user2 = $approvalUsers[1]
        $msg += "<@$user1> and <@$user2>."
    }
    elseif ($approvalUsers.Count -ge 3) {
        for ($i = 0; $i -lt ($approvalUsers.Count - 1); $i++) {
            $user = $approvalUsers[$i]
            $msg += "<@$user>, "
        }
        $lastUser = $approvalUsers[$approvalUsers.Count -1]
        $msg += "and <@$lastUser>."
    }
    #$msg = ($msg.Substring(0,$msg.Length-2)) + "."
    $msg += [System.Environment]::NewLine
    $msg += '<@{0}> needs approval to run the following: {1}{2}' -f $from, $commandString, [System.Environment]::NewLine

    $Bot.LogInfo("Sending approval notification: [$msg]")

    $Context.Response.To = $Context.Message.To
    $Context.Response.MessageFrom = $Context.Message.From
    $Context.Response.OriginalMessage = $Context.Message
    $Context.Response.Data = New-PoshBotTextResponse -Text $msg
    $Bot.SendMessage($Context.Response)
    $Bot.LogInfo("Ending [PreExecute] hook")
}

$Context