pubg/discord/report_new_matches.ps1
2025-04-15 17:12:33 +02:00

542 lines
No EOL
29 KiB
PowerShell

# --- Script Setup ---
$logPrefix = Get-Date -Format "yyyyMMdd_HHmmss" # Use standard sortable format
# Determine script root directory reliably
if ($PSScriptRoot) {
$scriptRoot = $PSScriptRoot
} else {
$scriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
Write-Warning "PSScriptRoot not defined, using calculated path: $scriptRoot"
}
Write-Output "Script root identified as: $scriptRoot"
# Define paths using Join-Path
$includesPath = Join-Path -Path $scriptRoot -ChildPath "..\includes\ps1"
$configPath = Join-Path -Path $scriptRoot -ChildPath "..\config"
$discordConfigPath = Join-Path -Path $scriptRoot -ChildPath "config.php" # Config is in the same dir
$dataPath = Join-Path -Path $scriptRoot -ChildPath "..\data"
$logDir = Join-Path -Path $scriptRoot -ChildPath "..\logs" # Assuming logs dir relative to scriptroot parent
$playerMatchesJsonPath = Join-Path -Path $dataPath -ChildPath "player_matches.json"
# Ensure Log directory exists
if (-not (Test-Path -Path $logDir -PathType Container)) {
Write-Warning "Log directory not found at '$logDir'. Attempting to create."
try {
New-Item -Path $logDir -ItemType Directory -Force -ErrorAction Stop | Out-Null
Write-Output "Successfully created log directory."
} catch {
# Log to console if transcript path fails
Write-Error "Failed to create log directory '$logDir'. Please check permissions. Error: $($_.Exception.Message)"
# Continue without transcript if log dir fails? Or exit? For now, continue.
}
}
# Start Transcript (use calculated $logDir)
$transcriptPath = Join-Path -Path $logDir -ChildPath "report_new_matches_$logPrefix.log"
try {
Start-Transcript -Path $transcriptPath -Append -ErrorAction Stop
Write-Output "Starting report_new_matches script at $(Get-Date)"
Write-Output "Running from: $(Get-Location)"
Write-Output "Transcript logging to: $transcriptPath"
} catch {
Write-Error "Failed to start transcript at '$transcriptPath'. Error: $($_.Exception.Message)"
# Exit if transcript is critical? For now, continue.
}
# --- Locking ---
$lockFilePath = Join-Path -Path $includesPath -ChildPath "lockfile.ps1"
if (-not (Test-Path -Path $lockFilePath -PathType Leaf)) {
Write-Error "Lockfile script not found at '$lockFilePath'. Cannot proceed."
if ($transcriptPath) { Stop-Transcript }
exit 1
}
. $lockFilePath
New-Lock -by "report_new_matches" -ErrorAction Stop # Stop if locking fails
# --- Main Logic in Try/Finally for Lock Removal ---
try {
# --- Configuration Loading ---
$apiKey = $null
$webhookUrl = $null
$webhookUrlLosers = $null
# Load API Key from main config.php
$phpConfigPath = Join-Path -Path $configPath -ChildPath "config.php"
if (Test-Path -Path $phpConfigPath -PathType Leaf) {
try {
$fileContent = Get-Content -Path $phpConfigPath -Raw -ErrorAction Stop
if ($fileContent -match '^\s*\$apiKey\s*=\s*''([^'']+)''') { $apiKey = $matches[1]; Write-Verbose "API Key loaded." }
else { Write-Warning "API Key pattern not found in '$phpConfigPath'." }
} catch { Write-Warning "Failed to read '$phpConfigPath': $($_.Exception.Message)" }
} else { Write-Warning "Main config file not found at '$phpConfigPath'." }
# Load Webhook URLs from discord/config.php
if (Test-Path -Path $discordConfigPath -PathType Leaf) {
try {
$discordFileContent = Get-Content -Path $discordConfigPath -Raw -ErrorAction Stop
if ($discordFileContent -match '^\s*\$webhookurl\s*=\s*''([^'']+)''') { $webhookUrl = $matches[1]; Write-Verbose "Main webhook URL loaded." }
else { Write-Warning "Main webhook URL pattern not found in '$discordConfigPath'." }
if ($discordFileContent -match '^\s*\$webhookurl_losers\s*=\s*''([^'']+)''') { $webhookUrlLosers = $matches[1]; Write-Verbose "Losers webhook URL loaded." }
else { Write-Warning "Losers webhook URL pattern not found in '$discordConfigPath'." }
} catch { Write-Warning "Failed to read '$discordConfigPath': $($_.Exception.Message)" }
} else { Write-Warning "Discord config file not found at '$discordConfigPath'." }
# Validate required config
if (-not $apiKey) { Write-Error "API Key missing."; throw "Missing API Key" }
if (-not $webhookUrl) { Write-Error "Main Discord webhook URL missing."; throw "Missing Webhook URL" }
if (-not $webhookUrlLosers) { Write-Error "Losers Discord webhook URL missing."; throw "Missing Losers Webhook URL" }
# --- API Headers ---
$apiHeaders = @{
'accept' = 'application/vnd.api+json'
'Authorization' = "Bearer $apiKey"
}
# --- Helper Function for API Calls (Copied from update_clan_members.ps1) ---
function Invoke-PubgApi {
param(
[Parameter(Mandatory=$true)][string]$Uri, [Parameter(Mandatory=$true)][hashtable]$Headers,
[int]$RetryCount = 1, [int]$RetryDelaySeconds = 61
)
for ($attempt = 1; $attempt -le ($RetryCount + 1); $attempt++) {
try {
Write-Verbose "API call (Attempt $($attempt)): $Uri"
$response = Invoke-RestMethod -Uri $Uri -Method GET -Headers $Headers -ErrorAction Stop
if ($null -ne $response) { Write-Verbose "API call successful."; return $response }
else { Write-Warning "API call to $Uri returned null."; return $null }
} catch {
$statusCode = $_.Exception.Response.StatusCode.value__
$errorMessage = $_.Exception.Message
Write-Warning "API call failed (Attempt $($attempt)). Status: $statusCode. Error: $errorMessage"
if ($attempt -le $RetryCount -and $statusCode -eq 429) {
Write-Warning "Rate limit hit. Sleeping $RetryDelaySeconds sec..."; Start-Sleep -Seconds $RetryDelaySeconds
} elseif ($attempt -gt $RetryCount) { Write-Error "API call failed after $($attempt) attempts. URI: $Uri. Last Error: $errorMessage"; return $null }
else { Write-Error "Non-retryable API error. URI: $Uri. Error: $errorMessage"; return $null }
}
}
return $null
}
# --- Helper Function for Telemetry Download/Parse ---
function Get-TelemetryData {
param([string]$TelemetryUrl)
if (-not $TelemetryUrl) { Write-Warning "Get-TelemetryData: No Telemetry URL provided."; return $null }
$telemetryFileName = $TelemetryUrl.Split('/')[-1]
$telemetryCacheFilePath = Join-Path -Path $telemetryCachePath -ChildPath $telemetryFileName
# Try cache first
if (Test-Path -Path $telemetryCacheFilePath -PathType Leaf) {
Write-Verbose "Loading telemetry from cache: $telemetryCacheFilePath"
try {
$telemetry = Get-Content -Path $telemetryCacheFilePath | ConvertFrom-Json -ErrorAction Stop
if ($null -ne $telemetry) { return $telemetry }
else { Write-Warning "Failed to parse cached telemetry: $telemetryCacheFilePath" }
} catch { Write-Warning "Error reading cached telemetry '$telemetryCacheFilePath': $($_.Exception.Message)" }
}
# Download if not cached or cache failed
Write-Output "Downloading telemetry: $TelemetryUrl"
try {
$webHeaders = @{ 'Accept-Encoding' = 'gzip' }
$response = Invoke-WebRequest -Uri $TelemetryUrl -Headers $webHeaders -UseBasicParsing -ErrorAction Stop
$telemetryContent = $response.Content
$telemetryContent | Out-File -FilePath $telemetryCacheFilePath -Encoding UTF8 -ErrorAction SilentlyContinue
$telemetry = $telemetryContent | ConvertFrom-Json -ErrorAction Stop
if ($null -eq $telemetry) { Write-Warning "Failed to parse downloaded telemetry from $TelemetryUrl." }
return $telemetry
} catch {
$errorMessage = $_.Exception.Message
Write-Warning "Failed to download/save telemetry from $TelemetryUrl. Error: $errorMessage"
return $null
}
}
# --- Discord Sending Functions (with Error Handling) ---
function Send-DiscordMessage {
param([string]$Webhook, [string]$Content)
if (-not $Webhook -or -not $Content) { Write-Warning "Send-DiscordMessage: Missing Webhook or Content."; return }
$payload = @{ content = $Content } | ConvertTo-Json -Depth 3
try {
Invoke-RestMethod -Uri $Webhook -Method Post -Body $payload -ContentType 'application/json' -ErrorAction Stop
Write-Verbose "Successfully sent message to Discord."
} catch {
$errorMessage = $_.Exception.Message
Write-Error "Failed to send message to Discord ($Webhook). Error: $errorMessage"
}
}
function Send-DiscordWin($Content) { Send-DiscordMessage -Webhook $webhookUrl -Content $Content }
function Send-DiscordLoss($Content) { Send-DiscordMessage -Webhook $webhookUrlLosers -Content $Content }
# --- Map Definitions ---
$mapNameLookup = @{ # Renamed from $map_map
"Baltic_Main" = "Erangel"
"Chimera_Main" = "Paramo"
"Desert_Main" = "Miramar"
"DihorOtok_Main" = "Vikendi"
"Erangel_Main" = "Erangel" # Duplicate key, might be intentional?
"Heaven_Main" = "Haven"
"Kiki_Main" = "Deston"
"Range_Main" = "Camp Jackal"
"Savage_Main" = "Sanhok"
"Summerland_Main" = "Karakin"
"Tiger_Main" = "Taego"
"Neon_Main" = "Rondo"
}
# --- Load Player Matches Data ---
$playerMatchesData = $null
if (Test-Path -Path $playerMatchesJsonPath -PathType Leaf) {
try {
$playerMatchesData = Get-Content -Path $playerMatchesJsonPath | ConvertFrom-Json -Depth 100 -ErrorAction Stop
if ($null -eq $playerMatchesData -or -not ($playerMatchesData -is [array])) {
Write-Error "Invalid structure in '$playerMatchesJsonPath'. Expected array. Cannot proceed."
throw "Invalid player matches data."
}
Write-Output "Successfully loaded player matches data."
} catch {
Write-Error "Error reading '$playerMatchesJsonPath': $($_.Exception.Message). Cannot proceed."
throw $_
}
} else {
Write-Error "Player matches file not found at '$playerMatchesJsonPath'. Cannot proceed."
throw "Missing player matches file."
}
# --- Extract New Wins and Losses ---
# Find the special entries added by get_matches.ps1
$newWinEntry = $playerMatchesData | Where-Object { $_.PSObject.Properties.Name -eq 'new_win_matches' } | Select-Object -First 1
$newLossEntry = $playerMatchesData | Where-Object { $_.PSObject.Properties.Name -eq 'new_loss_matches' } | Select-Object -First 1
$newWinMatchIds = if ($null -ne $newWinEntry -and $newWinEntry.new_win_matches -is [array]) { $newWinEntry.new_win_matches } else { @() }
$newLossMatchIds = if ($null -ne $newLossEntry -and $newLossEntry.new_loss_matches -is [array]) { $newLossEntry.new_loss_matches } else { @() }
Write-Output "Found $($newWinMatchIds.Count) new win match IDs and $($newLossMatchIds.Count) new loss match IDs to report."
# Extract actual match data (excluding the special entries)
$actualMatchEntries = $playerMatchesData | Where-Object { $_.PSObject.Properties.Name -ne 'new_win_matches' -and $_.PSObject.Properties.Name -ne 'new_loss_matches' }
# --- Process and Report New Losses ---
Write-Output "Processing $($newLossMatchIds.Count) new losses..."
foreach ($lossId in $newLossMatchIds) {
if (-not $lossId) { continue }
Write-Output "Processing loss match ID: $lossId"
# Find all player entries related to this loss match ID in the loaded data
$lossMatchPlayerEntries = $actualMatchEntries.player_matches | Where-Object { $_.id -eq $lossId }
if ($null -eq $lossMatchPlayerEntries -or $lossMatchPlayerEntries.Count -eq 0) {
Write-Warning "Could not find match details for loss ID $lossId in player_matches.json data."
continue
}
# Use the first entry for common match details (assuming they are consistent)
$firstLossEntry = $lossMatchPlayerEntries[0]
$lossGameMode = $firstLossEntry.gameMode
$lossMatchType = $firstLossEntry.matchType
$lossMapNameRaw = $firstLossEntry.mapName
$lossTelemetryUrl = $firstLossEntry.telemetry_url
# Skip TDM matches
if ($lossGameMode -eq 'tdm') { Write-Output "Skipping TDM loss match $lossId."; continue }
# Fetch Full Match Stats from API (needed for all participants' details)
$lossMatchApiStats = Invoke-PubgApi -Uri "https://api.pubg.com/shards/steam/matches/$lossId" -Headers $apiHeaders
# Get Telemetry Data
$lossTelemetryEvents = Get-TelemetryData -TelemetryUrl $lossTelemetryUrl
$relevantLossTelemetry = $null
if ($lossTelemetryEvents -is [array]) {
$relevantLossTelemetry = $lossTelemetryEvents | Where-Object { $_._T -eq 'LogPlayerTakeDamage' -or $_._T -eq 'LogPlayerKillV2' }
} else { Write-Warning "Invalid or missing telemetry data for loss match $lossId." }
# Prepare data tables
$lossStatsTable = @()
$lossTeamDamageVictims = @()
# Iterate through players involved in this loss from our data
$involvedPlayerNames = $lossMatchPlayerEntries.stats.name | Select-Object -Unique
foreach ($playerName in $involvedPlayerNames) {
$playerLossStats = $lossMatchPlayerEntries | Where-Object { $_.stats.name -eq $playerName } | Select-Object -First 1 -ExpandProperty stats
# Try to get more detailed stats from the full API response if available
$detailedPlayerStats = $null
if ($null -ne $lossMatchApiStats -and $lossMatchApiStats.included -is [array]) {
$detailedPlayerStats = $lossMatchApiStats.included |
Where-Object { $_.type -eq 'participant' -and $_.attributes.stats.name -eq $playerName } |
Select-Object -First 1 -ExpandProperty attributes | Select-Object -ExpandProperty stats
}
# Use detailed stats if found, otherwise fallback to basic stats from player_matches.json
$statsToUse = if ($null -ne $detailedPlayerStats) { $detailedPlayerStats } else { $playerLossStats }
if ($null -eq $statsToUse) { Write-Warning "Could not find any stats for $playerName in loss $lossId."; continue }
# Calculate Human Kills/Damage from Telemetry
$humanDmg = "N/A"
$humanKills = "N/A"
if ($null -ne $relevantLossTelemetry) {
try {
$humanDmgEvents = $relevantLossTelemetry | Where-Object { $_._T -eq 'LogPlayerTakeDamage' -and $_.attacker.name -eq $playerName -and $_.victim.accountId -notlike "ai.*" -and $_.victim.teamId -ne $_.attacker.teamId }
if ($humanDmgEvents) { $humanDmg = [math]::Round(($humanDmgEvents | Measure-Object -Property damage -Sum).Sum) }
$humanKillEvents = $relevantLossTelemetry | Where-Object { $_._T -eq 'LogPlayerKillV2' -and $_.killer.name -eq $playerName -and $_.victim.accountId -notlike "ai.*" }
$humanKills = $humanKillEvents.Count
} catch { Write-Warning ("Error processing telemetry stats for {0} in loss {1}: {2}" -f $playerName, $lossId, $_.Exception.Message) }
}
# Add to stats table
$lossStatsTable += [PSCustomObject]@{
Name = $playerName
'Human dmg' = "$humanDmg"
'Human Kills' = "$humanKills"
'Dmg' = "$([math]::Round($statsToUse.damageDealt))"
'Kills' = "$($statsToUse.kills)"
'alive (min)' = "$([math]::Round(($statsToUse.timeSurvived / 60)))" # Clarify unit
}
# Calculate team damage from Telemetry
if ($null -ne $relevantLossTelemetry) {
try {
$teamDmgEvents = $relevantLossTelemetry | Where-Object {
$_._T -eq 'LogPlayerTakeDamage' -and $_.victim.teamId -eq $_.attacker.teamId -and
$_.victim.accountId -notlike "ai.*" -and $_.victim.name -ne $_.attacker.name -and
$_.attacker.name -eq $playerName
}
if ($teamDmgEvents) {
foreach ($victimEntry in ($teamDmgEvents | Group-Object victim.name)) {
$lossTeamDamageVictims += [PSCustomObject]@{
attacker = $playerName
victim = $victimEntry.Name
Damage = "$([math]::Round(($victimEntry.Group.damage | Measure-Object -Sum).Sum))"
}
}
}
} catch { Write-Warning ("Error processing team damage for {0} in loss {1}: {2}" -f $playerName, $lossId, $_.Exception.Message) }
}
} # End foreach playerName in loss
# Format tables for Discord message
$contentLossStats = if ($lossStatsTable.Count -gt 0) { '```' + ($lossStatsTable | Format-Table -AutoSize | Out-String) + '```' } else { "" }
$contentLossVictims = if ($lossTeamDamageVictims.Count -gt 0) { ":skull::skull: Team Damage :skull::skull:`n" + '```' + ($lossTeamDamageVictims | Format-Table -AutoSize | Out-String) + '```' } else { "" }
# Construct and Send Loss Message
$losersString = $involvedPlayerNames -join ', '
$mapDisplayName = if ($mapNameLookup.ContainsKey($lossMapNameRaw)) { $mapNameLookup[$lossMapNameRaw] } else { $lossMapNameRaw }
$firstPlayerName = $involvedPlayerNames[0] # Use first player for replay link
$replayUrl = $lossTelemetryUrl -replace 'https://telemetry-cdn.pubg.com/bluehole-pubg', 'https://chickendinner.gg' -replace '-telemetry.json', ''
$replayUrl += "?follow=$firstPlayerName"
$matchSettings = @"
``````
Match Mode : $lossGameMode
Match Type : $lossMatchType
Map : $mapDisplayName
Match ID : $lossId
``````
"@
Send-DiscordLoss -Content "We hebben een LOSERT! Geen Kip voor jou! :skull::skull:"
Send-DiscordLoss -Content ":partying_face::partying_face: Helaas, **$($losersString)** :partying_face::partying_face:"
Send-DiscordLoss -Content $matchSettings
if ($contentLossStats) { Send-DiscordLoss -Content $contentLossStats }
if ($contentLossVictims) { Send-DiscordLoss -Content $contentLossVictims }
Send-DiscordLoss -Content "[2D Replay](<$replayUrl>)"
Send-DiscordLoss -Content "Meer match details [DTCH_STATS](<https://dtch.online/matchinfo.php?matchid=$lossId>)"
Write-Output "Sent loss report for match $lossId."
Start-Sleep -Seconds 2 # Small delay between messages
} # End foreach lossId
# --- Process and Report New Wins ---
Write-Output "Processing $($newWinMatchIds.Count) new wins..."
foreach ($winId in $newWinMatchIds) {
if (-not $winId) { continue }
Write-Output "Processing win match ID: $winId"
# Find all player entries related to this win match ID
$winMatchPlayerEntries = $actualMatchEntries.player_matches | Where-Object { $_.id -eq $winId }
if ($null -eq $winMatchPlayerEntries -or $winMatchPlayerEntries.Count -eq 0) {
Write-Warning "Could not find match details for win ID $winId in player_matches.json data."
continue
}
# Use the first entry for common details
$firstWinEntry = $winMatchPlayerEntries[0]
$winGameMode = $firstWinEntry.gameMode
$winMatchType = $firstWinEntry.matchType
$winMapNameRaw = $firstWinEntry.mapName
$winTelemetryUrl = $firstWinEntry.telemetry_url
# Skip TDM
if ($winGameMode -eq 'tdm') { Write-Output "Skipping TDM win match $winId."; continue }
# Get Telemetry
$winTelemetryEvents = Get-TelemetryData -TelemetryUrl $winTelemetryUrl
$relevantWinTelemetry = $null
if ($winTelemetryEvents -is [array]) {
$relevantWinTelemetry = $winTelemetryEvents | Where-Object { $_._T -eq 'LogPlayerTakeDamage' -or $_._T -eq 'LogPlayerKillV2' }
} else { Write-Warning "Invalid or missing telemetry data for win match $winId." }
# Get Full Match Stats from API
$winMatchApiStats = Invoke-PubgApi -Uri "https://api.pubg.com/shards/steam/matches/$winId" -Headers $apiHeaders
# Determine players to report (winners or all non-AI in custom)
$playersToReportStats = @()
if ($null -ne $winMatchApiStats -and $winMatchApiStats.included -is [array]) {
if ($winMatchType -eq 'custom') {
$playersToReportStats = $winMatchApiStats.included | Where-Object { $_.type -eq 'participant' -and $_.attributes.stats.playerId -notlike "ai.*" } | Select-Object -ExpandProperty attributes | Select-Object -ExpandProperty stats
} else {
$playersToReportStats = $winMatchApiStats.included | Where-Object { $_.type -eq 'participant' -and $_.attributes.stats.winPlace -eq 1 } | Select-Object -ExpandProperty attributes | Select-Object -ExpandProperty stats
}
} else {
Write-Warning "Could not get full match stats from API for win $winId. Reporting might be incomplete."
# Fallback: Use players from our loaded data who won
$playersToReportStats = $winMatchPlayerEntries | Where-Object { $_.stats.winPlace -eq 1 } | Select-Object -ExpandProperty stats
}
if ($playersToReportStats.Count -eq 0) { Write-Warning "No winning players found to report for match $winId."; continue }
$winnerNames = $playersToReportStats.name
$winnersString = $winnerNames -join ', '
# Prepare data tables
$winStatsTable = @()
$winTeamDamageVictims = @()
# Fail-safe check (from original script) - Limit number of reports if too many new wins detected at once?
# This might indicate an issue with the comparison logic or first run.
if ($newWinMatchIds.Count -gt 10) {
Write-Warning "More than 10 new win matches detected ($($newWinMatchIds.Count)). This might indicate an issue. Reporting only the first 10."
# Optionally break or limit the loop here if desired
# For now, just log the warning.
}
# Send initial win messages
Send-DiscordWin -Content ":chicken::chicken: **WINNER WINNER CHICKEN DINNER!!** :chicken::chicken:"
Send-DiscordWin -Content ":partying_face::partying_face: Gefeliciteerd **$($winnersString)** :partying_face::partying_face:"
$mapDisplayName = if ($mapNameLookup.ContainsKey($winMapNameRaw)) { $mapNameLookup[$winMapNameRaw] } else { $winMapNameRaw }
$matchSettings = @"
``````
Match Mode : $winGameMode
Match Type : $winMatchType
Map : $mapDisplayName
Match ID : $winId
``````
"@
Send-DiscordWin -Content $matchSettings
# Calculate stats for each reported player
foreach ($playerStat in $playersToReportStats) {
$playerName = $playerStat.name
if (-not $playerName) { continue }
Write-Verbose "Creating stats table entry for winner $playerName in match $winId"
# Calculate Human Kills/Damage from Telemetry
$humanDmg = "N/A"
$humanKills = "N/A"
if ($null -ne $relevantWinTelemetry) {
try {
$humanDmgEvents = $relevantWinTelemetry | Where-Object { $_._T -eq 'LogPlayerTakeDamage' -and $_.attacker.name -eq $playerName -and $_.victim.accountId -notlike "ai.*" -and $_.victim.teamId -ne $_.attacker.teamId }
if ($humanDmgEvents) { $humanDmg = [math]::Round(($humanDmgEvents | Measure-Object -Property damage -Sum).Sum) }
$humanKillEvents = $relevantWinTelemetry | Where-Object { $_._T -eq 'LogPlayerKillV2' -and $_.killer.name -eq $playerName -and $_.victim.accountId -notlike "ai.*" }
$humanKills = $humanKillEvents.Count
} catch { Write-Warning ("Error processing telemetry stats for {0} in win {1}: {2}" -f $playerName, $winId, $_.Exception.Message) }
}
# Add to stats table
$winStatsTable += [PSCustomObject]@{
Name = $playerName
'Human dmg' = "$humanDmg"
'Human Kills' = "$humanKills"
'Dmg' = "$([math]::Round($playerStat.damageDealt))"
'Kills' = "$($playerStat.kills)"
'alive (min)' = "$([math]::Round(($playerStat.timeSurvived / 60)))"
}
# Calculate team damage from Telemetry
if ($null -ne $relevantWinTelemetry) {
try {
$teamDmgEvents = $relevantWinTelemetry | Where-Object {
$_._T -eq 'LogPlayerTakeDamage' -and $_.victim.teamId -eq $_.attacker.teamId -and
$_.victim.accountId -notlike "ai.*" -and $_.victim.name -ne $_.attacker.name -and
$_.attacker.name -eq $playerName
}
if ($teamDmgEvents) {
foreach ($victimEntry in ($teamDmgEvents | Group-Object victim.name)) {
$winTeamDamageVictims += [PSCustomObject]@{
attacker = $playerName
victim = $victimEntry.Name
Damage = "$([math]::Round(($victimEntry.Group.damage | Measure-Object -Sum).Sum))"
}
}
}
} catch { Write-Warning ("Error processing team damage for {0} in win {1}: {2}" -f $playerName, $winId, $_.Exception.Message) }
}
} # End foreach playerStat
# Format and send stats tables
$contentWinStats = if ($winStatsTable.Count -gt 0) { '```' + ($winStatsTable | Format-Table -AutoSize | Out-String) + '```' } else { "" }
if ($contentWinStats) { Send-DiscordWin -Content $contentWinStats }
$contentWinVictims = if ($winTeamDamageVictims.Count -gt 0) { ":skull::skull: Team Damage Report :skull::skull:`n" + '```' + ($winTeamDamageVictims | Format-Table -AutoSize | Out-String) + '```' } else { "" }
if ($contentWinVictims) { Send-DiscordWin -Content $contentWinVictims }
# Send Replay and Details Links
$firstWinnerName = $winnerNames[0]
$replayUrl = $winTelemetryUrl -replace 'https://telemetry-cdn.pubg.com/bluehole-pubg', 'https://chickendinner.gg' -replace '-telemetry.json', ''
$replayUrl += "?follow=$firstWinnerName"
Send-DiscordWin -Content "[2D Replay](<$replayUrl>)"
Send-DiscordWin -Content "More match details [DTCH_STATS](<https://dtch.online/matchinfo.php?matchid=$winId>)"
Write-Output "Sent win report for match $winId."
Start-Sleep -Seconds 2 # Small delay between messages
} # End foreach winId
# --- Clear New Match Lists in Data File ---
Write-Output "Clearing new win/loss lists in player matches data file..."
$updatedPlayerMatchesData = $playerMatchesData # Start with the loaded data
$winListCleared = $false
$lossListCleared = $false
# Iterate through the array to find and modify the special entries
for ($i = 0; $i -lt $updatedPlayerMatchesData.Count; $i++) {
$item = $updatedPlayerMatchesData[$i]
if ($item -is [PSCustomObject]) {
if ($item.PSObject.Properties.Name -eq 'new_win_matches') {
$item.new_win_matches = @() # Clear the list
$winListCleared = $true
}
if ($item.PSObject.Properties.Name -eq 'new_loss_matches') {
$item.new_loss_matches = @() # Clear the list
$lossListCleared = $true
}
}
# Stop if both found and cleared
if ($winListCleared -and $lossListCleared) { break }
}
if ($winListCleared -or $lossListCleared) {
try {
$updatedPlayerMatchesData | ConvertTo-Json -Depth 100 | Out-File -FilePath $playerMatchesJsonPath -Encoding UTF8 -ErrorAction Stop
Write-Output "Successfully cleared new match lists in '$playerMatchesJsonPath'."
} catch {
Write-Error "Failed to save player matches data after clearing lists. '$playerMatchesJsonPath'. Error: $($_.Exception.Message)"
}
} else {
Write-Warning "Could not find 'new_win_matches' or 'new_loss_matches' entries to clear in '$playerMatchesJsonPath'."
}
} # End Main Try Block
finally {
# --- Cleanup ---
Write-Output "Script finished at $(Get-Date)."
Remove-Lock # Ensure lock is always removed
if ($transcriptPath -and (Get-Transcript | Select-Object -ExpandProperty Path) -eq $transcriptPath) {
Stop-Transcript
}
}