plugins/CleverReach/Public/peoplestage/invoke-broadcast.ps1
function Invoke-Broadcast{ [CmdletBinding()] param ( [Parameter(Mandatory=$false)][Hashtable] $InputHashtable ) begin { #----------------------------------------------- # START TIMER #----------------------------------------------- $processStart = [datetime]::now #$inserts = 0 #----------------------------------------------- # VALUES FROM UPLOAD #----------------------------------------------- Set-ProcessId -Id $InputHashtable.ProcessId $tag = ( $InputHashtable.Tag -split ", " ) $groupId = $InputHashtable.GroupId #----------------------------------------------- # LOG #----------------------------------------------- $moduleName = "BROADCAST" # Start the log Write-Log -message $Script:logDivider Write-Log -message $moduleName -Severity INFO # Log the params, if existing Write-Log -message "INPUT:" if ( $InputHashtable ) { $InputHashtable.Keys | ForEach-Object { $param = $_ Write-Log -message " $( $param ) = '$( $InputHashtable[$param] )'" -writeToHostToo $false } } #----------------------------------------------- # PARSE MESSAGE #----------------------------------------------- $mailing = [Mailing]::new($InputHashtable.MessageName) Write-Log "Got chosen message entry with id '$( $mailing.mailingId )' and name '$( $mailing.mailingName )'" $templateId = $mailing.mailingId #----------------------------------------------- # CHECK CLEVERREACH CONNECTION #----------------------------------------------- try { Test-CleverReachConnection } catch { Write-Log -Message $_.Exception -Severity ERROR throw [System.IO.InvalidDataException] $msg exit 0 # TODO is exit needed here? } } process { try { #----------------------------------------------- # CHECK THE CURRENT SENDING MODE #----------------------------------------------- $doRelease = $false Switch ( $InputHashtable.mode ) { "taggingOnly" { # broadcast is not needed Write-Log -Message "Mode 'taggingOnly'. Triggering the broadcast script is not needed. Please change your settings to 'Upload Only'" -Severity WARNING exit 0 # leave the script now } "prepare" { # broadcast is not needed Write-Log -Message "Mode 'prepare'. Everything gets prepared, but not sending takes place" -Severity WARNING $doRelease = $false } Default { Write-Log -Message "No specific upload mode defined in the channel. Proceeding triggering a mailing." $doRelease = $true } } #----------------------------------------------- # GET GENERAL STATISTICS FOR LIST #----------------------------------------------- # Write-Log "Getting stats for group $( $groupId ):" # #$groupStats = Invoke-CR -Object "groups" -Path "/$( $groupId )/stats" -Method GET -Verbose # $groupStats = Get-GroupStatsByRuntime -GroupId $groupId #-IncludeMetrics -IncludeLastChanged -Verbose # <# # { # "total_count": 4, # "inactive_count": 0, # "active_count": 4, # "bounce_count": 0, # "avg_points": 69.5, # "quality": 3, # "time": 1685545449, # "order_count": 0 # } # #> # $groupStats.psobject.properties | ForEach-Object { # Write-Log " $( $_.Name ): $( $_.Value )" # } #----------------------------------------------- # GET STATISTICS FOR TAGS #----------------------------------------------- $tag | ForEach-Object { $t = $_ Write-Log "Getting tag stats for tag $( $t ) for group $( $groupId )" $tagQuery = [PSCustomObject]@{ "tag" = $t "group_id" = $groupId "active" = $true } $tagCount = Invoke-CR -Object "tags" -Path "/count" -Method GET -Verbose -Query $tagQuery Write-Log "Got $( $tagCount ) receivers for tag $( $t ) in group $( $groupId )" } #----------------------------------------------- # READ MAILING DETAILS #----------------------------------------------- # get details of mailing $templateSource = Invoke-CR -Object "mailings" -Path "/$( $templateId )" -Method GET -Verbose $newMailingName = "$( $templateSource.name ) - $( $processStart.ToString("yyyyMMddHHmmss") )" Write-Log -message "Looked up the mailing '$( $templateId )' with name '$( $templateSource.Name )'" Write-Log -message "New mailing name: '$( $newMailingName )'" #----------------------------------------------- # CREATE SEGMENT FOR THE BROADCAST #----------------------------------------------- Write-Log "Creating a new filter/segment on group '$( $groupId )'" $rules = [System.Collections.ArrayList]@() #[ArrayList]@() # Add the first part to the rules [void]$rules.add( [PSCustomObject]@{ "operator"= "" "field"= "(" "logic"= "" "condition"= "" } ) # Add all tag rules - they are combined together, so you need to have all tags in combination $tag | ForEach-Object { $t = $_ [void]$rules.add( [PSCustomObject]@{ "operator"= "AND" "field" = "tags" "logic" = "CONTAINS" "condition" = $t } ) } # End of the first part [void]$rules.add( [PSCustomObject]@{ "operator"= "" "field"= ")" "logic"= "" "condition"= "" } ) # Now add the rules, that are used always [void]$rules.addrange([array]@(, [PSCustomObject]@{ "operator"= "AND" "field"= "(" "logic"= "" "condition"= "" } [PSCustomObject]@{ "operator"= "AND" "field" = "activated" "logic" = "bg" "condition" = "1" } [PSCustomObject]@{ "operator"= "AND" "field" = "deactivated" "logic" = "eq" "condition" = "0" } [PSCustomObject]@{ "operator"= "AND" "field" = "bounced" "logic" = "eq" "condition" = "0" } [PSCustomObject]@{ "operator"= "" "field"= ")" "logic"= "" "condition"= "" } )) $filterBody = [PSCustomObject]@{ "name" = "$( $Script:settings.upload.tagSource ).$( $newMailingName )" #"operator" = "AND" "rules" = $rules } $segment = Invoke-CR -Object "groups" -Path "/$( $groupId )/filters" -Method POST -Verbose -Body $filterBody #$script:debug = $segment # We are gettting the 'id' and 'success' back If ( $segment.success -eq $true ) { Write-Log "Created a new filter/segment with id '$( $segment.id )' and name '$( $newMailingName )'" } else { throw "There was a problem creating the segment" } #----------------------------------------------- # COUNT SEGMENT #----------------------------------------------- $segmentCount = Invoke-CR -Object "groups" -Path "/$( $groupId )/filters/$( $segment.id )/count" -Method GET -Verbose If ( $segmentCount -gt 0 ) { Write-Log "Count of this segment: $( $segmentCount )" } else { throw "Not able to count segment '$( $segment.id )'. Stopping here!" } #----------------------------------------------- # ADDING A PREHEADER IF DEFINED #----------------------------------------------- # The cleverreach preheader looks like # <div id="CR-PRHEADER" style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;">...</div> # This needs to be the first element after the body of # $templateSource.body_html # So doing a replace after the body tag # This will just place a variable that needs to be defined in the receivers entry with "preheader" # TODO Think about putting this into the settings #$preheaderTemplate = '<div style="font-size:0px;line-height:1px;mso-line-height-rule:exactly;display:none;max-width:0px;max-height:0px;opacity:0;overflow:hidden;mso-hide:all;">[PREHEADER]</div>' $preheaderVariable = $Script:settings.broadcast.preheaderFieldname.ToUpper() $preheaderTemplate = @" <div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;">{$( $preheaderVariable )}‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ </div> "@ # Find the right place to insert after $bodyRegex = "<body[^\>]*>" # Regex to find the native cleverreach preheader $crPreheaderRegex = '<div.*id="CR-PRHEADER".*?/div>' <# $htmlText = @" <html> <header> </header> <body jay jay jay test="hello"> Hello world </body> </html> "@ $htmlText -replace $bodyRegex, "$( $matches[0] )$( $preheaderTemplate )" #> # This is the html of the mailing $htmlTemplate = $templateSource.body_html # Add the preheader, if wished If ( $Script:settings.broadcast.addPreheaderAfterBody -eq $true -and $InputHashtable.PreheaderIsSet -eq $true) { #$Script:plugindebug = $templateSource.body_html # Remove CR preheader, if needed $nativePreheaderRegexMatch = $htmlTemplate -match $crPreheaderRegex If ( $Script:settings.broadcast.removeNativePreheader -eq $true -and $nativePreheaderRegexMatch) { $html = $htmlTemplate -replace $crPreheaderRegex, "" Write-Log "Removed native CleverReach PreHeader" } else { $html = $htmlTemplate Write-Log "No native CleverReach PreHeader found" } # Replace the existing body tag with the body tag and the new preheader $regexMatch = $html -match $bodyRegex If ( $regexMatch -eq $true ) { $matchedBodyTag = $matches[0] $html = $html -replace $bodyRegex, "$( $matchedBodyTag )$( $preheaderTemplate )" Write-Log "Added custom PreHeader with {$( $preheaderVariable )} variable/field" } else { Write-Log "Body tag for inserting PreHeader not found" # Trigger an error, because there should be preheader tag found throw "There was a problem with the html. No body tag found to insert a PreHeader" } $Script:plugindebug = $html } else { Write-Log "No replacement of preheader" $html = $htmlTemplate } #----------------------------------------------- # COPY/DUPLICATE THE MAILING AND USE SEGMENT #----------------------------------------------- Write-Log -message "Creating a copy of the mailing" $mailingSettings = [PSCustomObject]@{ "name" = $newMailingName "subject" = $templateSource.subject "sender_name" = $templateSource.sender_name "sender_email" = $templateSource.sender_email "content" = [PSCustomObject]@{ "type" = $Script:settings.broadcast.defaultContentType "html" = $html "text" = $templateSource.body_text } "receivers" = [PSCustomObject]@{ #"groups" = [Array]@(, $groupId ) "filter" = $segment.id } "settings" = [PSCustomObject]@{ "editor" = $Script:settings.broadcast.defaultEditor #"open_tracking" = $settings.broadcast.defaultOpenTracking #"click_tracking" = $settings.broadcast.defaultClickTracking #"category_id" = $templateSource.category_id <# link_tracking_url = "27.wayne.cleverreach.com" link_tracking_type = "google" # google|intelliad|crconnect unsubscribe_form_id = "23" campaign_id = "52" #> } "tags" = [Array]@( $templateSource.tags ) #[Array]@(, ($templateSource.tags | Where-Object { $_ -notin [Array]@( "cr-mailing-envelope" ) })) } # Category If ( $templateSource.category_id -gt 0 ) { $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "category_id" -Value $templateSource.category_id } # Tracking If ( $templateSource.is_trackable -eq $true ) { $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "open_tracking" -Value $Script:settings.broadcast.defaultOpenTracking $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "click_tracking" -Value $Script:settings.broadcast.defaultClickTracking } If ( $Script:settings.broadcast.defaultLinkTrackingUrl -ne "" ) { $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "link_tracking_url" -Value $Script:settings.broadcast.defaultLinkTrackingUrl } If ( $Script:settings.broadcast.defaultLinkTrackingType -ne "" ) { $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "link_tracking_type" -Value $Script:settings.broadcast.defaultLinkTrackingType } If ( $Script:settings.broadcast.defaultGoogleCampaignName -ne "" ) { $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "google_campaign_name" -Value $Script:settings.broadcast.defaultGoogleCampaignName } # Unsubscribe If ( $templateSource.unsubscribe_form_id -gt 0 ) { $mailingSettings.settings | Add-Member -MemberType NoteProperty -Name "unsubscribe_form_id" -Value $templateSource.unsubscribe_form_id } $script:debug = $mailingSettings # put it all together $copiedMailing = Invoke-CR -Object "mailings" -Method POST -Verbose -body $mailingSettings Write-Log -message "Created a copy of the mailing with the new id $( $copiedMailing.id )" #----------------------------------------------- # BROADCAST MAILING (IF ALLOWED BY CLEVERREACH) #----------------------------------------------- If ( $doRelease -eq $true ) { $releaseTimestamp = (Get-Unixtime) + $Script:settings.broadcast.defaultReleaseOffset $releaseBody = [PSCustomObject]@{ time = [int]$releaseTimestamp } Invoke-CR -Object "mailings" -Path "/$( $copiedMailing.id )/release" -Method POST -Verbose -body $releaseBody Write-Log "Released mailing for unix timestamp at $( $releaseTimestamp )" # Wait until finished If ( $Script:settings.broadcast.waitUntilFinished -eq $true ) { $maxWaitTime = [int]$Script:settings.broadcast.defaultReleaseOffset + [int]$Script:settings.broadcast.maxWaitForFinishedAfterOffset $secondsToWait = 5 $i = 0 Do { # Wait and count Start-Sleep -Seconds $secondsToWait $i += $secondsToWait # Ask for the current status $mailingStatus = Invoke-CR -Object "mailings" -Path "/$( $copiedMailing.id )" -Method GET -Verbose } Until ( $i -gt $maxWaitTime -or $mailingStatus.state -eq "finished" ) # Log the exit of the loop If ( $mailingStatus.state -eq "finished" ) { # All good Write-Log "Mailing finished successfully after $( $i ) seconds (Offset: $( [int]$Script:settings.broadcast.defaultReleaseOffset ))" -Severity INFO } else { # Something went wrong Write-Log "Mailing exceeded the maximum wait time of $( $maxWaitTime ) seconds" -Severity WARNING } } } else { Write-Log "Mailing not released" } } catch { #$msg = "Error during writing data. Abort!" #Write-Log -Message $msg -Severity ERROR #Write-Log -Message $_.Exception -Severity ERROR #throw [System.IO.InvalidDataException] $msg $msg = "Error during broadcasting data. Abort!" Write-Log -Message $msg -Severity ERROR -WriteToHostToo $false Write-Log -Message $_.Exception -Severity ERROR throw $_.Exception } finally { } #----------------------------------------------- # STOP TIMER #----------------------------------------------- $processEnd = [datetime]::now $processDuration = New-TimeSpan -Start $processStart -End $processEnd Write-Log -Message "Needed $( [int]$processDuration.TotalSeconds ) seconds in total" #----------------------------------------------- # RETURN VALUES TO PEOPLESTAGE #----------------------------------------------- # count the number of successful upload rows $recipients = $tagCount # put in the source id as the listname $transactionId = $copiedMailing.id # return object $return = [Hashtable]@{ # Mandatory return values "Recipients"=$recipients "TransactionId"=$transactionId # General return value to identify this custom channel in the broadcasts detail tables "CustomProvider"= $Script:settings.providername "ProcessId" = $InputHashtable.ProcessId #$Script:processId # Some more information for the broadcasts script #"EmailFieldName"= $params.EmailFieldName #"Path"= $params.Path #"UrnFieldName"= $params.UrnFieldName #"TargetGroupId" = $targetGroup.targetGroupId # More information about the different status of the import #"RecipientsIgnored" = $status.report.total_ignored #"RecipientsQueued" = $recipients #"RecipientsSent" = $status.report.total_added + $status.report.total_updated } # log the return object Write-Log -message "RETURN:" $return.Keys | ForEach-Object { $param = $_ Write-Log -message " $( $param ) = '$( $return[$param] )'" -writeToHostToo $false } # return the results $return } end { } } |