542 lines
No EOL
29 KiB
PowerShell
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
|
|
}
|
|
} |