Public/Support/CallQueue/Set-TeamsCallQueueTrigger.ps1

# Module: TeamsFunctions
# Function: CallQueue
# Author: David Eberhardt
# Updated: 07-AUG-2023
# Status: Alpha




function Set-TeamsCallQueueTrigger {
  <#
  .SYNOPSIS
    Setting one of the Exception Handling Triggers for a Call Queue
  .DESCRIPTION
    Helper function to set one of the Exception Handling Triggers for a Call Queue
  .PARAMETER Name
    Required. Name or Id of the Call Queue. Used only to display proper output message.
  .PARAMETER Overflow
    Required to define an Overflow Trigger. All other parameters are applied to this trigger. Allows for setting Threshold
  .PARAMETER Timeout
    Required to define an Timeout Trigger. All other parameters are applied to this trigger. Allows for setting Threshold
  .PARAMETER NoAgent
    Required to define an NoAgent Trigger. All other parameters are applied to this trigger. Allows for setting NoAgentApplyTo
  .PARAMETER Threshold
    Overflow: Optional. Number of people waiting in the queue before the Action to trigger
    Default: 10, Microsoft Default: 50 (See Parameter UseMicrosoftDefaults)
    Timeout: Optional. Time in Seconds for the Action to trigger
    Default: 30s, Microsoft Default: 50s (See Parameter UseMicrosoftDefaults)
  .PARAMETER NoAgentApplyTo
    Optional. Instructs that the setting applies to AllCalls or only NewCalls
  .PARAMETER Prompt
    Optional. Prompt to play when is reaching threshold but before Action is taken
    String as a Path for a Recording or a Greeting (Text-to-Voice)
    Aliases for backward compatibility: SharedVoicemailTextToSpeechPrompt, SharedVoicemailAudioFile
  .PARAMETER Action
    Required. Action to be taken if the Queue size limit (Threshold) is reached
    Forward requires specification of Target
    Default: DisconnectWithBusy, Values: DisconnectWithBusy, Forward, VoiceMail, SharedVoiceMail
  .PARAMETER Target
    Situational. Required only if Action is not the default. UserPrincipalName of the Target
    For Overflow, default is DisconnectWithBusy, for Timeout it is Default, for NoAgent it is Queue
  .PARAMETER SetPromptForAllTypes
    Situational. Boolean Switch. This switch will apply any provided prompt to all valid parameters
    If a Prompt is used setting a Text-to-Voice string value this switch will apply the same value to TextToSpeechPrompt-
    parameters of the queue. If not provided, it will only set the prompt for the Action provided.
    This affects Disconnect, RedirectPerson, RedirectVoiceApp, RedirectPhoneNumber, RedirectVoicemail & SharedVoicemail
    Prompts that are not provided will not be set (in the example above, any Timeout parameters would not be affected).
  .PARAMETER EnableTranscription
    Situational. Boolean Switch. Requires specification of LanguageId
    Enables a transcription of the Voicemail message to be sent to the Group mailbox
  .PARAMETER EnableSystemPromptSuppression
    Situational. Boolean Switch. Requires specification of LanguageId
    Enables a transcription of the Voicemail message to be sent to the Group mailbox
  .PARAMETER LanguageId
    Optional Language Identifier indicating the language that is used to play shared voicemail prompts.
    This parameter becomes a required parameter If the Action is set to SharedVoicemail and the language is not yet set.
  .EXAMPLE
    Set-TeamsCallQueueTrigger -Trigger Overflow -Action Forward -Target 'User@domain.com'
    Creates a new Overflow trigger that is set to Redirect to a Person in the Organisation.
  .EXAMPLE
    Set-TeamsCallQueueTrigger -Trigger Timeout -Action SharedVoicemail -Target "My Group" -EnableTranscription -EnableSystemPromptSuppression
    Creates a new Timeout trigger that is set to SharedVoicemail forwarding to "My Group" and enabling Transcription
    It also enables the suppression of the System Prompt before forwarding to the Target Group
  .EXAMPLE
    Set-TeamsCallQueueTrigger -Trigger NoAgent -Action Disconnect -Prompt "Sorry, we are closed" -SetPromptForAllTypes -LanguageId en-gb
    Creates a new NoAgent trigger that is set to Disconnect with a Text-to-Speech Prompt and Language set to English (UK)
  .EXAMPLE
    Set-TeamsCallQueueTrigger -Trigger Overflow -Action Disconnect -Prompt "Sorry, we are busy" -SetPromptForAllTypes
    Creates a new Overflow trigger that is set to Disconnect with a Text-to-Speech Prompt.
    The Switch SetPromptForAllTypes will set the Prompt for all types, not only for Disconnect, in case it is to be switched later.
    NOTE: As this is a Text-to-Voice prompt, it requires a Language to be set. Parameter LanguageId must be set in conjunction
    with the output of this Cmdlet, the creation of the Call Queue will fail
  .INPUTS
    System.String
  .OUTPUTS
    System.Object
  .NOTES
    Audio Files, if not found will result in the prompt not being configured.
  .COMPONENT
    TeamsCallQueue
  .FUNCTIONALITY
    Sets one of the Exception Handling triggers for a Call Queue
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/Set-TeamsCallQueueTrigger.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/about_TeamsFunctions.md
  .LINK
    https://github.com/DEberhardt/TeamsFunctions/tree/master/docs/
  #>


  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
  [Alias('Set-TeamsCQT')]
  # [OutputType([PSCustomObject])]
  param (
    [Parameter(Mandatory, Position = 0, HelpMessage = 'Name or ID of the Call Queue')]
    [string]$Name,

    [Parameter(Mandatory, HelpMessage = 'Name or ID of the Call Queue')]
    [Validateset('Overflow', 'Timeout', 'NoAgent')]
    [ArgumentCompleter({ param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters )
        $possibleValues = @{
          Overflow = @('DisconnectWithBusy', 'Forward', 'Voicemail', 'SharedVoicemail')
          Timeout  = @('Disconnect', 'Forward', 'Voicemail', 'SharedVoicemail')
          NoAgent  = @('Queue', 'Forward', 'Voicemail', 'SharedVoicemail')
        }
        if ($fakeBoundParameters.ContainsKey('Trigger')) {
          $possibleValues[$fakeBoundParameters.Trigger] | Where-Object { $_ -like "$wordToComplete*" }
        }
        else {
          $possibleValues.Values | ForEach-Object { $_ }
        }
      })]
    [string]$Trigger,

    [Parameter(HelpMessage = 'Overflow: # people in the Queue; Timeout: Time in seconds (0-2700s)')]
    [int16]$Threshold,

    [Parameter(HelpMessage = 'Prompt for trigger. Text-to-Voice will require the LanguageId Parameter')]
    [string]$Prompt,

    [Parameter(Mandatory, HelpMessage = 'Action to be taken')]
    [Validateset('Disconnect', 'Forward', 'Voicemail', 'SharedVoicemail', 'Queue')]
    [string]$Action,

    # if Action is not DisconnectWithBusy, this is required
    [Parameter(HelpMessage = 'TEL URI, UPN or Group Display Name that is targeted upon trigger, only valid for forwarded calls')]
    [string]$Target,

    [Parameter(HelpMessage = 'Applies any provided prompt to all types')]
    [switch]$SetPromptForAllTypes,

    [Parameter(HelpMessage = 'Using this Parameter will make a Transcription of the Voicemail message available in the Mailbox')]
    [bool]$EnableTranscription,

    [Parameter(HelpMessage = 'Using this Parameter will make a Transcription of the Voicemail message available in the Mailbox')]
    [bool]$EnableSystemPromptSuppression,

    [Parameter(HelpMessage = 'Language Identifier from Get-CsAutoAttendantSupportedLanguage.')]
    [ValidateScript( {
        if ($_ -in $(Get-OrbitAcSbCsAutoAttendantSupportedLanguage @args) ) { $True } else {
          throw [System.Management.Automation.ValidationMetadataException] 'Value must be a supported Langauge Id. Use Intellisense for options'
        } })]
    # [ArgumentCompleter({ Get-OrbitAcSbCsAutoAttendantSupportedLanguage @args })]
    [string]$LanguageId

  )

  begin {
    Show-FunctionStatus -Level Alpha
    Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)"

    # Asserting Graph Connection
    if ( -not (Test-GraphConnection) ) { throw 'Connection to Microsoft Graph not established. Please validate connection' }

    # Asserting MicrosoftTeams Connection
    if ( -not (Assert-MicrosoftTeamsConnection) ) { throw 'Connection to Microsoft Teams not established. Please validate connection' }

    # Setting Preference Variables according to Upstream settings
    if (-not $PSBoundParameters['Verbose']) { $VerbosePreference = $PSCmdlet.SessionState.PSVariable.GetValue('VerbosePreference') }
    if (-not $PSBoundParameters['Confirm']) { $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference') }
    if (-not $PSBoundParameters['WhatIf']) { $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference') }
    $DebugPreference = if (-not $PSBoundParameters['Debug']) { $PSCmdlet.SessionState.PSVariable.GetValue('DebugPreference') } else { 'Continue' }
    $InformationPreference = if ( $PSBoundParameters['InformationAction']) { $PSCmdlet.SessionState.PSVariable.GetValue('InformationAction') } else { 'Continue' }

    $Stack = Get-PSCallStack
    $Called = ($stack.length -ge 3)

    #Initialising Counters
    $private:StepsID0, $private:StepsID1 = Get-WriteBetterProgressSteps -Code $($MyInvocation.MyCommand.Definition) -MaxId 1
    $private:ActivityID0 = $($MyInvocation.MyCommand.Name)
    [int] $private:CountID0 = [int] $private:CountID1 = 1

    # Validation
    $StatusID0 = 'Validating Input'
    # Language has to be normalised as the Id is case sensitive
    $CurrentOperationID0 = 'LanguageId'
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ($PSBoundParameters['LanguageId']) {
      $LanguageId = $($LanguageId.Split('-')[0]).ToLower() + '-' + $($LanguageId.Split('-')[1]).ToUpper()
      Write-Verbose "'$Name' LanguageId normalised to '$LanguageId'"
      $VRSupported = ((Get-CsAutoAttendantSupportedLanguage -Id $LanguageId).VoiceResponseSupported)
      Write-Information "INFO: LanguageId '$LanguageId' - Voice Responses are $(if ( $VRSupported ) { 'not' }) supported"
    }

    if ( $PSBoundParameters['Prompt'] -and $PSBoundParameters['SetPromptForAllTypes'] ) {
      Write-Warning -Message "The configuration parameters for all Prompts are currently only available in PowerShell `
      and do not appear in Teams admin center. Saving a call queue configuration through Teams admin center will remove `
      any of these configured items"

    }

  } #begin

  process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"

    # re-Initialising counters for Progress bars (for Pipeline processing)
    [int] $private:CountID0 = 1

    #region PREPARATION
    $StatusID0 = 'Querying Object'
    # preparing Splatting Object
    $Parameters = @{}

    #region Query Unique Element
    $CurrentOperationID0 = "Finding unique result for provided Name '$Name'"
    Write-BetterProgress -Id 0 -Activity $ActivityID0 -Status $StatusID0 -CurrentOperation $CurrentOperationID0 -Step ($private:CountID0++) -Of $private:StepsID0
    if ( $script:OrbitRegexGuid.isMatch($Name) ) {
      #Identity or ObjectId
      Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - ID - '$Name'"
      $CallQueue = Get-CsCallQueue -Identity "$Name" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
    }
    else {
      #Name
      Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand) - Name - '$Name'"
      # Initial Query to determine unique result (single object)
      $CallQueue = Get-CsCallQueue -NameFilter "$Name" -WarningAction SilentlyContinue
      $CallQueue = $CallQueue | Where-Object Name -EQ "$Name"
    }

    if ($null -eq $CallQueue) {
      $Message = "'$Name' No Object found - Please check 'Name' provided"
      Write-Error -Message $Message -Category ParserError -RecommendedAction "Please check 'Name' provided" -ErrorAction Stop
    }
    elseif ($CallQueue.GetType().BaseType.Name -eq 'Array') {
      $Message = "'$Name' Multiple Results found! Cannot determine unique result. - Please use Set-CsCallQueue with the -Identity switch!"
      Write-Error -Message $Message -Category ParserError -RecommendedAction 'Please use Set-CsCallQueue with the -Identity switch!' -ErrorAction Stop
    }
    else {
      $ID = $CallQueue.Identity
      Write-Information "INFO: Call Queue '$Name' Call Queue found: Identity: $ID"
      $Parameters.Identity = $ID
    }
    #endregion

    # Language
    $Message = "'$Name' Language"
    $AudioFileMatch = '.(wav|wma|mp3)$'
    if ( $LanguageId ) {
      Write-Verbose -Message "$Message`: Language is provided - '$LanguageId' is used" -Verbose
    }
    elseif ( $CallQueue.LanguageId ) {
      $LanguageId = $CallQueue.LanguageId
      Write-Verbose -Message "$Message`: Language already set - '$LanguageId' is used" -Verbose
    }
    else {
      # Language not provided and not set - Checking for Parameters which would require LanguageId
      if (( $PSBoundParameters['EnableTranscription'] -and $EnableTranscription ) -or `
        ( $PSBoundParameters['Prompt'] -and $Prompt -notmatch $AudioFileMatch )) {
        Write-Error -Message "$Message`: Prompt or Transcription require a Language set selection. Please provide Parameter LanguageId" -ErrorAction Stop -RecommendedAction 'Add Parameter LanguageId'
      }
    }
    #endregion


    # Creating New-TeamsCallQueueTrigger for further use
    $Parameters = New-TeamsCallQueueTrigger @PSBoundParameters

    # Adding additional parameters
    $Parameters.Identity = $CallQueue.Identity
    if ( $LanguageId ) { $Parameters.LanguageId = $LanguageId }

    # Applying Call Queue settings
    if ($PSCmdlet.ShouldProcess("$Name", 'Set-CsCallQueue')) {
      try {
        $null = (Set-CsCallQueue @Parameters)
        if ($Called) {
          Write-Verbose -Message "'$Name' Applying Settings"
        }
      }
      catch {
        Write-Error -Message "Error action unsuccessful : $($_.Exception.Message)" -Category InvalidResult
      }
    }

  } #process

  end {
    Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)"
  } #end
} #Set-TeamsCallQueueTrigger