Compare commits

..

No commits in common. "main" and "1.1" have entirely different histories.
main ... 1.1

10 changed files with 156 additions and 691 deletions

View file

@ -12,12 +12,11 @@
"THAIlux", "THAIlux",
"Reijn7000", "Reijn7000",
"McPikkie", "McPikkie",
"Brabo-Gamer",
"SquadKiller101", "SquadKiller101",
"HeteKip", "HeteKip",
"Pettie1972", "Pettie1972",
"Reijn7001", "Reijn7000"
"Ghost_0223",
"Bossoemang"
], ],
"alts": [ "alts": [

View file

@ -24,7 +24,7 @@ if ($fileContent -match "\`$apiKey\s*=\s*\'([^\']+)\'") {
$apiKey = $matches[1] $apiKey = $matches[1]
} }
else { else {
write-output "API Key not found" Write-Output "API Key not found"
} }
$headers = @{ $headers = @{
@ -38,17 +38,15 @@ $fileContent = Get-Content -Path "$scriptroot/../discord/config.php" -Raw
# Use regex to match the apiKey value # Use regex to match the apiKey value
if ($fileContent -match "\`$webhookurl\s*=\s*'([^']+)'") { if ($fileContent -match "\`$webhookurl\s*=\s*'([^']+)'") {
$webhookurl = $matches[1] $webhookurl = $matches[1]
} } else {
else { Write-Output "No web url found"
write-output "No web url found"
} }
# Use regex to match the losers webhook url # Use regex to match the losers webhook url
if ($fileContent -match "\`$webhookurl_losers\s*=\s*'([^']+)'") { if ($fileContent -match "\`$webhookurl_losers\s*=\s*'([^']+)'") {
$webhookurl_losers = $matches[1] $webhookurl_losers = $matches[1]
} } else {
else { Write-Output "No losers web url found"
write-output "No losers web url found"
} }
function send-discord { function send-discord {
@ -71,13 +69,7 @@ function send-discord-losers {
$payload = [PSCustomObject]@{ $payload = [PSCustomObject]@{
content = $content content = $content
} }
if ($payload.content -eq "") {
$payload = [PSCustomObject]@{
content = "Nothing to report"
}
}
Invoke-RestMethod -Uri $webhookurl_losers -Method Post -Body ($payload | ConvertTo-Json) -ContentType 'Application/Json' Invoke-RestMethod -Uri $webhookurl_losers -Method Post -Body ($payload | ConvertTo-Json) -ContentType 'Application/Json'
} }
$map_map = @{ $map_map = @{
@ -99,142 +91,142 @@ try {
$player_matches = get-content "$scriptroot/../data/player_matches.json" | convertfrom-json -Depth 100 $player_matches = get-content "$scriptroot/../data/player_matches.json" | convertfrom-json -Depth 100
} }
catch { catch {
write-output 'Unable to read file exitin' Write-Output 'Unable to read file exitin'
} }
write-output $player_matches Write-Output $player_matches
write-output $new_win_matches Write-Output $new_win_matches
$new_win_matches = $player_matches[-2].new_win_matches $new_win_matches = $player_matches[-1].new_win_matches
# Gebruik nu de lijst van nieuwe verloren matches uit het JSON-bestand # Gebruik nu de lijst van nieuwe verloren matches uit het JSON-bestand
$new_loss_matches = $player_matches[-1].new_loss_matches $new_loss_matches = $player_matches[-1].new_loss_matches
# Post verloren matches naar #losers kanaal # Post verloren matches naar #losers kanaal
# foreach ($lossid in $new_loss_matches) { foreach ($lossid in $new_loss_matches) {
# $lossmatch = $player_matches.player_matches | Where-Object { $_.id -eq $lossid } $lossmatch = $player_matches.player_matches | Where-Object { $_.id -eq $lossid }
# if ($null -eq $lossmatch) { continue } if ($null -eq $lossmatch) { continue }
# if ($lossmatch[0].gameMode -eq 'tdm') { continue } if ($lossmatch[0].gameMode -eq 'tdm') { continue }
# # Fetch detailed match stats and telemetry for the loss # Fetch detailed match stats and telemetry for the loss
# $loss_match_stats = $null $loss_match_stats = $null
# $loss_telemetry = $null $loss_telemetry = $null
# try { try {
# $loss_match_stats = Invoke-RestMethod -Uri "https://api.pubg.com/shards/steam/matches/$lossid" -Method GET -Headers $headers $loss_match_stats = Invoke-RestMethod -Uri "https://api.pubg.com/shards/steam/matches/$lossid" -Method GET -Headers $headers
# $loss_telemetry = (invoke-webrequest @($lossmatch.telemetry_url)[0]).content | convertfrom-json | where-object { ($_._T -eq 'LOGPLAYERTAKEDAMAGE') -or ($_._T -eq 'LOGPLAYERKILLV2') } $loss_telemetry = (invoke-webrequest @($lossmatch.telemetry_url)[0]).content | convertfrom-json | where-object { ($_._T -eq 'LOGPLAYERTAKEDAMAGE') -or ($_._T -eq 'LOGPLAYERKILLV2') }
# } catch { } catch {
# $errorMessage = $_.Exception.Message $errorMessage = $_.Exception.Message
# Write-Warning ("Failed to fetch API/telemetry data for loss match {0}: {1}" -f $lossid, $errorMessage) Write-Warning ("Failed to fetch API/telemetry data for loss match {0}: {1}" -f $lossid, $errorMessage)
# } }
# $loss_stats_table = @() $loss_stats_table = @()
# $loss_victims = @() # For team damage $loss_victims = @() # For team damage
# # Iterate through players found in the locally stored match data for this loss # Iterate through players found in the locally stored match data for this loss
# foreach ($player_stat in $lossmatch[0].stats) { foreach ($player_stat in $lossmatch[0].stats) {
# $player_name = $player_stat.name $player_name = $player_stat.name
# # Find the corresponding detailed stats from the API response # Find the corresponding detailed stats from the API response
# $detailed_player_stats = $null $detailed_player_stats = $null
# if ($null -ne $loss_match_stats) { if ($null -ne $loss_match_stats) {
# $detailed_player_stats = $loss_match_stats.included | Where-Object {$_.type -eq 'participant'} | ForEach-Object {$_.attributes.stats} | Where-Object { $_.name -eq $player_name } $detailed_player_stats = $loss_match_stats.included | Where-Object {$_.type -eq 'participant'} | ForEach-Object {$_.attributes.stats} | Where-Object { $_.name -eq $player_name }
# } }
# if ($null -eq $detailed_player_stats) { if ($null -eq $detailed_player_stats) {
# Write-Warning "Could not find detailed stats for player $player_name in loss match $lossid. Using basic stats." Write-Warning "Could not find detailed stats for player $player_name in loss match $lossid. Using basic stats."
# # Fallback to basic stats if detailed stats are missing # Fallback to basic stats if detailed stats are missing
# $loss_stats_table += [PSCustomObject]@{ $loss_stats_table += [PSCustomObject]@{
# Name = $player_name Name = $player_name
# 'Human dmg' = "N/A" 'Human dmg' = "N/A"
# 'Human Kills' = "N/A" 'Human Kills' = "N/A"
# 'Dmg' = "$([math]::Round($player_stat.damageDealt))" # Use basic stat 'Dmg' = "$([math]::Round($player_stat.damageDealt))" # Use basic stat
# 'Kills' = "$($player_stat.kills)" # Use basic stat 'Kills' = "$($player_stat.kills)" # Use basic stat
# 'alive' = "$([math]::Round(($player_stat.timeSurvived / 60)))" # Use basic stat 'alive' = "$([math]::Round(($player_stat.timeSurvived / 60)))" # Use basic stat
# } }
# continue # Skip telemetry processing if detailed stats failed continue # Skip telemetry processing if detailed stats failed
# } }
# # Calculate stats (similar to win stats calculation) # Calculate stats (similar to win stats calculation)
# $human_dmg = "N/A" $human_dmg = "N/A"
# $human_kills = "N/A" $human_kills = "N/A"
# if ($null -ne $loss_telemetry) { if ($null -ne $loss_telemetry) {
# try { try {
# $human_dmg = [math]::Round(($loss_telemetry | Where-Object { $_._T -eq 'LOGPLAYERTAKEDAMAGE' -and $_.attacker.name -eq $player_name -and $_.victim.accountId -notlike "ai.*" -and $_.victim.teamId -ne $_.attacker.teamId } | Measure-Object -Property damage -Sum).Sum) $human_dmg = [math]::Round(($loss_telemetry | Where-Object { $_._T -eq 'LOGPLAYERTAKEDAMAGE' -and $_.attacker.name -eq $player_name -and $_.victim.accountId -notlike "ai.*" -and $_.victim.teamId -ne $_.attacker.teamId } | Measure-Object -Property damage -Sum).Sum)
# $human_kills = ($loss_telemetry | Where-Object { $_._T -eq 'LOGPLAYERKILLV2' -and $_.killer.name -eq $player_name -and $_.victim.accountId -notlike "ai.*" }).count $human_kills = ($loss_telemetry | Where-Object { $_._T -eq 'LOGPLAYERKILLV2' -and $_.killer.name -eq $player_name -and $_.victim.accountId -notlike "ai.*" }).count
# } catch { } catch {
# $errorMessage = $_.Exception.Message $errorMessage = $_.Exception.Message
# Write-Warning ("Error processing telemetry stats for {0} in loss {1}: {2}" -f $player_name, $lossid, $errorMessage) Write-Warning ("Error processing telemetry stats for {0} in loss {1}: {2}" -f $player_name, $lossid, $errorMessage)
# } }
# } }
# $loss_stats_table += [PSCustomObject]@{ $loss_stats_table += [PSCustomObject]@{
# Name = $player_name Name = $player_name
# 'Human dmg' = "$human_dmg" 'Human dmg' = "$human_dmg"
# 'Human Kills' = "$human_kills" 'Human Kills' = "$human_kills"
# 'Dmg' = "$([math]::Round($detailed_player_stats.damageDealt))" 'Dmg' = "$([math]::Round($detailed_player_stats.damageDealt))"
# 'Kills' = "$($detailed_player_stats.kills)" 'Kills' = "$($detailed_player_stats.kills)"
# 'alive' = "$([math]::Round(($detailed_player_stats.timeSurvived / 60)))" 'alive' = "$([math]::Round(($detailed_player_stats.timeSurvived / 60)))"
# } }
# # Calculate team damage # Calculate team damage
# if ($null -ne $loss_telemetry) { if ($null -ne $loss_telemetry) {
# try { try {
# $teamdmg = $loss_telemetry | Where-Object { $teamdmg = $loss_telemetry | Where-Object {
# $_._T -eq 'LOGPLAYERTAKEDAMAGE' -and $_._T -eq 'LOGPLAYERTAKEDAMAGE' -and
# $_.victim.teamId -eq $_.attacker.teamId -and $_.victim.teamId -eq $_.attacker.teamId -and
# $_.victim.accountId -notlike "ai.*" -and $_.victim.accountId -notlike "ai.*" -and
# $_.victim.name -ne $_.attacker.name -and $_.victim.name -ne $_.attacker.name -and
# $_.attacker.name -eq $player_name $_.attacker.name -eq $player_name
# } }
# if ($teamdmg.count -ge 1) { if ($teamdmg.count -ge 1) {
# foreach ($victim_name in ($teamdmg.victim.name | Select-Object -Unique)) { foreach ($victim_name in ($teamdmg.victim.name | Select-Object -Unique)) {
# $loss_victims += [PSCustomObject]@{ $loss_victims += [PSCustomObject]@{
# attacker = $player_name attacker = $player_name
# victim = $victim_name victim = $victim_name
# Damage = "$([math]::Round((($teamdmg | Where-Object { $_.victim.name -eq $victim_name }).damage | Measure-Object -Sum).Sum))" Damage = "$([math]::Round((($teamdmg | Where-Object { $_.victim.name -eq $victim_name }).damage | Measure-Object -Sum).Sum))"
# } }
# } }
# } }
# } catch { } catch {
# $errorMessage = $_.Exception.Message $errorMessage = $_.Exception.Message
# Write-Warning ("Error processing team damage for {0} in loss {1}: {2}" -f $player_name, $lossid, $errorMessage) Write-Warning ("Error processing team damage for {0} in loss {1}: {2}" -f $player_name, $lossid, $errorMessage)
# } }
# } }
# } }
# # Format the stats table # Format the stats table
# $content_lossstats = "" $content_lossstats = ""
# if ($loss_stats_table.Count -gt 0) { if ($loss_stats_table.Count -gt 0) {
# $content_lossstats = '```' + ($loss_stats_table | Format-Table -AutoSize | Out-String) + '```' $content_lossstats = '```' + ($loss_stats_table | Format-Table -AutoSize | Out-String) + '```'
# } }
# # Format team damage table # Format team damage table
# $content_loss_victims = "" $content_loss_victims = ""
# if ($loss_victims.Count -gt 0) { if ($loss_victims.Count -gt 0) {
# $content_loss_victims = ":skull::skull: Team Damage :skull::skull:`n" + '```' + ($loss_victims | Format-Table -AutoSize | Out-String) + '```' $content_loss_victims = ":skull::skull: Team Damage :skull::skull:`n" + '```' + ($loss_victims | Format-Table -AutoSize | Out-String) + '```'
# } }
# # Original message construction variables # Original message construction variables
# $losers = $lossmatch[0].stats.name -join ', ' # Join names for display $losers = $lossmatch[0].stats.name -join ', ' # Join names for display
# $map = $map_map[$lossmatch[0].mapName] $map = $map_map[$lossmatch[0].mapName]
# $place = ($lossmatch[0].stats | Select-Object -First 1).winPlace # Get placement from the first player stat $place = ($lossmatch[0].stats | Select-Object -First 1).winPlace # Get placement from the first player stat
# $first_player_name = ($lossmatch[0].stats | Select-Object -First 1).name $first_player_name = ($lossmatch[0].stats | Select-Object -First 1).name
# $replay_url = $lossmatch[0].telemetry_url -replace 'https://telemetry-cdn.pubg.com/bluehole-pubg', 'https://chickendinner.gg' $replay_url = $lossmatch[0].telemetry_url -replace 'https://telemetry-cdn.pubg.com/bluehole-pubg', 'https://chickendinner.gg'
# $replay_url = $replay_url -replace '-telemetry.json', '' $replay_url = $replay_url -replace '-telemetry.json', ''
# $replay_url = $replay_url + "?follow=$first_player_name" # Follow the first player $replay_url = $replay_url + "?follow=$first_player_name" # Follow the first player
# $match_settings = @" $match_settings = @"
# `````` ``````
# match mode $($lossmatch[0].gameMode) match mode $($lossmatch[0].gameMode)
# match type $($lossmatch[0].matchType) match type $($lossmatch[0].matchType)
# map $($map_map[$lossmatch[0].mapName]) map $($map_map[$lossmatch[0].mapName])
# id $($lossmatch[0].id) id $($lossmatch[0].id)
# `````` ``````
# "@ "@
# send-discord-losers -content "We hebben een LOSERT! Geen Kip voor jou! :skull::skull:" send-discord-losers -content "We hebben een LOSERT! Geen Kip voor jou! :skull::skull:"
# send-discord-losers -content ":partying_face::partying_face::partying_face: Helaas, $($losers) :partying_face::partying_face::partying_face:" send-discord-losers -content ":partying_face::partying_face::partying_face: Helaas, $($losers) :partying_face::partying_face::partying_face:"
# send-discord-losers -content $match_settings send-discord-losers -content $match_settings
# send-discord-losers -content $content_lossstats send-discord-losers -content $content_lossstats
# send-discord-losers -content $content_loss_victims send-discord-losers -content $content_loss_victims
# send-discord-losers -content "[2D replay](<$replay_url>)" send-discord-losers -content "[2D replay](<$replay_url>)"
# send-discord-losers -content "Meer match details [DTCH_STATS](<https://dtch.online/matchinfo.php?matchid=$($lossmatch[0].id)>)" send-discord-losers -content "Meer match details [DTCH_STATS](<https://dtch.online/matchinfo.php?matchid=$($lossmatch[0].id)>)"
# } }
foreach ($winid in $new_win_matches) { foreach ($winid in $new_win_matches) {
@ -245,20 +237,13 @@ foreach ($winid in $new_win_matches) {
$winmatches = $player_matches.player_matches | Where-Object { $_.id -eq $winid } $winmatches = $player_matches.player_matches | Where-Object { $_.id -eq $winid }
$telemetry = (invoke-webrequest @($winmatches.telemetry_url)[0]).content | convertfrom-json | where-object { ($_._T -eq 'LOGPLAYERTAKEDAMAGE') -or ($_._T -eq 'LOGPLAYERKILLV2') } $telemetry = (invoke-webrequest @($winmatches.telemetry_url)[0]).content | convertfrom-json | where-object { ($_._T -eq 'LOGPLAYERTAKEDAMAGE') -or ($_._T -eq 'LOGPLAYERKILLV2') }
$winners = @(($winmatches | where-object { $_.stats.winPlace -eq 1 }).stats.name) $winners = @(($winmatches | where-object { $_.stats.winPlace -eq 1 }).stats.name)
# $2D_replay_url = @($winmatches.telemetry_url)[0] -replace 'https://telemetry-cdn.pubg.com/bluehole-pubg', 'https://chickendinner.gg' $2D_replay_url = @($winmatches.telemetry_url)[0] -replace 'https://telemetry-cdn.pubg.com/bluehole-pubg', 'https://chickendinner.gg'
# $2D_replay_url = $2D_replay_url -replace '-telemetry.json', '' $2D_replay_url = $2D_replay_url -replace '-telemetry.json', ''
# $2D_replay_url = $2D_replay_url + "?follow=$($winners[0])" $2D_replay_url = $2D_replay_url + "?follow=$($winners[0])"
$2D_replay_url = 'https://chickendinner.gg/' + $winid
$match_stats = Invoke-RestMethod -Uri "https://api.pubg.com/shards/steam/matches/$winid" -Method GET -Headers $headers $match_stats = Invoke-RestMethod -Uri "https://api.pubg.com/shards/steam/matches/$winid" -Method GET -Headers $headers
if ($winmatches[0].gameMode -eq 'tdm' ) { if ($winmatches[0].gameMode -eq 'tdm' ) {
continue continue
}
if (
($winmatches[0].matchtype -eq 'event' -and $winmatches[0].gameMode -ne 'ibr') -or
($winmatches[0].gameMode -eq 'tdm')
) {
Write-Output 'Skipping because of event or tdm'
continue
} #skip tdm matches } #skip tdm matches
if ($winmatches[0].matchType -eq 'custom') { if ($winmatches[0].matchType -eq 'custom') {
$players_to_report = $match_stats.included.attributes.stats | where-object { $_.playerId -notlike "ai.*" } $players_to_report = $match_stats.included.attributes.stats | where-object { $_.playerId -notlike "ai.*" }
@ -266,15 +251,11 @@ foreach ($winid in $new_win_matches) {
else { else {
$players_to_report = $match_stats.included.attributes.stats | where-object { $_.winplace -eq 1 } $players_to_report = $match_stats.included.attributes.stats | where-object { $_.winplace -eq 1 }
} }
$2D_replay_url = 'https://chickendinner.gg/' + $winid + "/" + $players_to_report[0].name
if ($new_win_matches.count -le 10) { if ($new_win_matches.count -le 10) {
#fail safe #fail safe
$winnerswithurl = @()
foreach ($winner in $winners) {
$winnerswithurl += "[$winner](<https://dtch.online/latestmatches.php?selected_player=$($winner)>)"
}
send-discord -content ":chicken: :chicken: **WINNER WINNER CHICKEN DINNER!!** :chicken: :chicken:" send-discord -content ":chicken: :chicken: **WINNER WINNER CHICKEN DINNER!!** :chicken: :chicken:"
send-discord -content ":partying_face::partying_face::partying_face: Gefeliciteerd $($winnerswithurl -join ', ') :partying_face::partying_face::partying_face:" send-discord -content ":partying_face::partying_face::partying_face: Gefeliciteerd $($winners -join ', ') :partying_face::partying_face::partying_face:"
$match_settings = @" $match_settings = @"
`````` ``````
match mode $($winmatches[0].gameMode) match mode $($winmatches[0].gameMode)
@ -355,8 +336,6 @@ k_t = Team eliminaties
foreach ($item in $player_matches) { foreach ($item in $player_matches) {
if ($item.PSObject.Properties.Name -contains "new_win_matches") { if ($item.PSObject.Properties.Name -contains "new_win_matches") {
$item.new_win_matches = $null $item.new_win_matches = $null
}
if ($item.PSObject.Properties.Name -contains "new_loss_matches") {
$item.new_loss_matches = $null $item.new_loss_matches = $null
} }
} }
@ -368,5 +347,4 @@ $newJson = $player_matches | ConvertTo-Json -Depth 100
$newJson | out-file "$scriptroot/../data/player_matches.json" $newJson | out-file "$scriptroot/../data/player_matches.json"
remove-lock remove-lock
Stop-Transcript Stop-Transcript

View file

@ -4,11 +4,9 @@ import discord
import random import random
import asyncio import asyncio
import re import re
from textwrap import dedent
from discord.ext import commands from discord.ext import commands
from openai import OpenAI
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
def get_token(): def get_token():
with open("config.php", "r") as file: with open("config.php", "r") as file:
content = file.read() content = file.read()
@ -31,13 +29,10 @@ intents.presences = True # Nodig als de bot presences moet zien
intents.members = True # Nodig om leden in een voice channel te zien intents.members = True # Nodig om leden in een voice channel te zien
bot = commands.Bot(command_prefix="!", intents=intents) bot = commands.Bot(command_prefix="!", intents=intents)
client = OpenAI(api_key=OPENAI_API_KEY)
@bot.event @bot.event
async def on_ready(): async def on_ready():
print(f'Bot is ingelogd als {bot.user}') print(f'Bot is ingelogd als {bot.user}')
channel = bot.get_channel(759006368832159745)
if channel:
await channel.send("Ben er weer!")
@bot.command() @bot.command()
async def test(ctx): async def test(ctx):
@ -130,19 +125,19 @@ async def teamify(ctx, *args):
await ctx.send(f"Kanaal {channel.name} is opgeruimd omdat het leeg was!") await ctx.send(f"Kanaal {channel.name} is opgeruimd omdat het leeg was!")
@bot.command() @bot.command()
async def whoisbest(ctx, category="Casual", matchesback=18, top=3): async def whoisbest(ctx, category="Casual", matchesback=18):
if category.lower() == "help": if category.lower() == "help":
help_message = ( help_message = (
"**Gebruik van het commando `whoisbest`:**\n" "**Gebruik van het commando `whoisbest`:**\n"
"`!whoisbest [category] [matchesback] [top]`\n\n" "`!whoisbest [category] [matchesback]`\n\n"
"**Parameters:**\n" "**Parameters:**\n"
"`category` - De categorie van de stats, bijv. 'Casual' of 'Ranked'. Niet hoofdlettergevoelig.\n" "`category` - De categorie van de stats, bijv. 'Casual' of 'Ranked'. Niet hoofdlettergevoelig.\n"
"`matchesback` - Het minimum aantal matches dat een speler gespeeld moet hebben om mee te tellen (standaard 18).\n\n" "`matchesback` - Het minimum aantal matches dat een speler gespeeld moet hebben om mee te tellen (standaard 18).\n\n"
"**Voorbeeld:**\n" "**Voorbeeld:**\n"
"`!whoisbest Casual 18`\n" "`!whoisbest Casual 18`\n"
"Laat de top 3 spelers zien op basis van winratio en gemiddelde damage in de Casual categorie met minimaal 18 matches.\n" "Laat de top 3 spelers zien op basis van winratio en gemiddelde damage in de Casual categorie met minimaal 18 matches.\n\n"
"Typ `!whoisbest help` om deze uitleg opnieuw te zien." "Typ `!whoisbest help` om deze uitleg opnieuw te zien."
) )
await ctx.send(help_message) await ctx.send(help_message)
@ -178,10 +173,10 @@ async def whoisbest(ctx, category="Casual", matchesback=18, top=3):
return return
# Sorteer spelers op winratio (aflopend) # Sorteer spelers op winratio (aflopend)
top_winratio = sorted(players, key=lambda x: x.get("winratio", 0), reverse=True)[:top] top_winratio = sorted(players, key=lambda x: x.get("winratio", 0), reverse=True)[:3]
# Sorteer spelers op gemiddelde damage (aflopend) # Sorteer spelers op gemiddelde damage (aflopend)
top_ahd = sorted(players, key=lambda x: x.get("ahd", 0), reverse=True)[:top] top_ahd = sorted(players, key=lambda x: x.get("ahd", 0), reverse=True)[:3]
# Bouw het bericht op # Bouw het bericht op
message = f"**\U0001F3C6 Top 3 Winratio ({actual_category})**\n" message = f"**\U0001F3C6 Top 3 Winratio ({actual_category})**\n"
@ -191,55 +186,11 @@ async def whoisbest(ctx, category="Casual", matchesback=18, top=3):
message += f"\n**\U0001F480 Top 3 AHD ({actual_category})**\n" message += f"\n**\U0001F480 Top 3 AHD ({actual_category})**\n"
for i, player in enumerate(top_ahd, start=1): for i, player in enumerate(top_ahd, start=1):
message += f"{i}. **{player['playername']}** - {player['ahd']:.2f}\n" message += f"{i}. **{player['playername']}** - {player['ahd']:.2f}\n"
##AI
system_prompt = dedent(f""" await ctx.send(message)
Je bent een Discord announcer-bot op de PUBG-server van DTCH.
Stijl: brutaal/competitief, licht denigrerend maar leesbaar.
AHD = Average Human Damage
Regels:
- Gebruik uitsluitend de meegeleverde stats-tekst.
- Output ALLEEN Discord-markdown (geen JSON, geen codeblokken).
- Structuur:
1) Titel met category en korte snedige ondertitel.
2) **🏆 Top {top} Winratio** en **💀 Top {top} AHD** (exact die koppen).
3) Per regel: 🥇/🥈/🥉 + **naam** + waarde (winratio met %).
4) De rest van de regels doe je zonder medaille gewoon een cijfer
5) Sluit af met 1 of 2 regels analyse van de stats, gebruik humor.
- Opgegeven parameters:
Categorie: {category}
Minimaal aantal matches: {matchesback}
Top: {top}
- verdere info
1) Als de aantal matches laag is (onder de 15) dan zijn cijfers niet echt meer representatief. Meld dat dan ook.
- Max ~1800 tekens.
""").strip()
user_prompt = dedent(f"""
Verpak onderstaande stats-tekst in één strakke Discord-post volgens de regels.
Wijzig geen waardes, haal alles uit de tekst tussen START/EINDE.
[STATS-TEKST START]
{message}
[STATS-TEKST EINDE]
""").strip()
response = client.chat.completions.create(
model="gpt-5-nano",
#temperature=0.6,
#presence_penalty=0.2,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
)
antwoord = response.choices[0].message.content
await ctx.send(f"{ctx.author.mention} {antwoord[:1900]}")
except Exception as e: except Exception as e:
await ctx.send(f"{ctx.author.mention} Er ging iets mis: {e}") await ctx.send(f"Fout bij het laden van de statistieken: {str(e)}")
##AIEND
@bot.event @bot.event
async def on_voice_state_update(member, before, after): async def on_voice_state_update(member, before, after):
@ -264,15 +215,7 @@ async def on_voice_state_update(member, before, after):
async def on_member_join(member): async def on_member_join(member):
logging_channel = discord.utils.get(member.guild.text_channels, name="raadhuisplein") logging_channel = discord.utils.get(member.guild.text_channels, name="raadhuisplein")
if logging_channel: if logging_channel:
welcome_message = ( await logging_channel.send(f"🎉 Welkom {member.mention} op de server! We hopen dat je een leuke tijd hebt!")
f"🎉 Welcome {member.mention} to **{member.guild.name}**!\n\n"
"👋 We're glad to have you here.\n"
"👉 Want to jump right in? Type `!iamgamer` in the chat and you'll get the **Tourists** role.\n"
"With that role you can join the fun and games with everyone else. 🎮\n"
"Use !dtch_help for more info\n\n"
"Enjoy your stay and have a great time! 🚀"
)
await logging_channel.send(welcome_message)
@bot.event @bot.event
async def on_member_remove(member): async def on_member_remove(member):
@ -308,203 +251,5 @@ async def moveall(ctx):
await ctx.send(f"{moved_members} speler(s) zijn verplaatst naar het teamify kanaal.") await ctx.send(f"{moved_members} speler(s) zijn verplaatst naar het teamify kanaal.")
else: else:
await ctx.send("Er waren geen spelers om te verplaatsen.") await ctx.send("Er waren geen spelers om te verplaatsen.")
@bot.command()
async def iamgamer(ctx):
role = discord.utils.get(ctx.guild.roles, name="Tourists")
if role is None:
await ctx.send("De rol **Tourists** bestaat niet!")
return
try:
await ctx.author.add_roles(role)
await ctx.send(f"{ctx.author.mention}, je bent nu een **Tourist**! Veel plezier! 🎮")
except Exception as e:
await ctx.send(f"Er is iets misgegaan bij het toekennen van de rol: {e}")
@bot.command(name="dtch_help", aliases=["commands"])
async def dtch_help_command(ctx):
embed = discord.Embed(
title="📖 DTCH Bot Command Help",
description="Heres a list of all available commands and how to use them:",
color=discord.Color.blue()
)
embed.add_field(
name="👉 !teamify",
value=(
"`!teamify` - Split players into random teams of max 4.\n"
"`!teamify <number_of_teams>` - Split players into a given number of teams.\n"
"`!teamify <number_of_teams> move` - Split & move players into temporary voice channels.\n"
"`!teamify move` - Auto split & move players.\n"
"🔹 Works only in the **#teamify** channel."
),
inline=False
)
embed.add_field(
name="👉 !moveall",
value="`!moveall` - Moves all players from other voice channels into the **teamify** channel.",
inline=False
)
embed.add_field(
name="👉 !whoisbest",
value=(
"`!whoisbest [category] [matchesback]`\n"
"Shows the top 3 players based on win ratio and average damage.\n"
"`category` = e.g. Casual, Ranked\n"
"`matchesback` = minimum matches required (default: 18)\n"
"Example: `!whoisbest Casual 18`"
),
inline=False
)
embed.add_field(
name="👉 !iamgamer",
value="`!iamgamer` - Gives you the **Tourists** role 🎮 so you can join games and unlock more fun.",
inline=False
)
embed.set_footer(text="✨ For advanced options, use: !teamify help or !whoisbest help")
await ctx.send(embed=embed)
@bot.command()
@commands.cooldown(1, 15, commands.BucketType.user)
async def ask(ctx, *, vraag: str):
## CLAN MEMBERS
file_path = os.path.join("..", "config", "clanmembers.json")
# Inlezen als string
with open(file_path, "r", encoding="utf-8") as f:
clanmembers_str = f.read()
#LIFETIME STATS
file_path_lifetimestats = os.path.join("..", "data", "player_lifetime_data.json")
with open(file_path_lifetimestats, "r", encoding="utf-8") as file:
data_lifetimestats = json.load(file)
squad_str = json.dumps(data_lifetimestats.get("squad", {}), indent=2)
#Last stats
file_path_laststats = os.path.join("..", "data", "player_last_stats.json")
with open(file_path_laststats, "r", encoding="utf-8") as file:
data_laststat = json.load(file)
casual_str = json.dumps(data_laststat.get("Casual", []), indent=2)
ranked_str = json.dumps(data_laststat.get("Ranked", []), indent=2)
custom_str = json.dumps(data_laststat.get("custom", []), indent=2)
# Online members
voice_members = [
member.display_name
for vc in ctx.guild.voice_channels
for member in vc.members
]
voice_str = ", ".join(voice_members) if voice_members else "Niemand zit in een voice kanaal"
"""Stuur een vraag naar OpenAI"""
try:
async with ctx.typing():
response = client.chat.completions.create(
model="gpt-5-nano",
messages=[
{
"role": "system",
"content": (
"Je bent een licht denigrerende chatbot in Discord, actief op de PUBG server van clan DTCH. "
f"De vraag is gesteld door {ctx.author.display_name}. "
# Context
f"Clanleden: {clanmembers_str}. "
f"Momenteel in voice: {voice_str}. "
f"Lifetime stats (squad): {squad_str}. "
f"Casual (spelen we toch wel het vaakst met elkaar, belangrijke stat dus) stats: {casual_str}. "
f"Ranked stats: {ranked_str}. "
f"Custom stats (clan games meestal tegen elkaar): {custom_str}. "
# Richtlijnen voor antwoord
"Houd je antwoorden kort en bondig, met maximaal 1600 tekens. "
"Kill Death Human (KD_H) en winrato zijn de belangrijkste stats omdat bots heel makkelijk te killen zijn vinden de Human Kill death het belangrijkste"
"Gebruik Discord markdown (``` of **vetgedrukt**) wanneer je stats of tabellen toont. "
"Wees een beetje sarcastisch, maar wel begrijpelijk. "
"Stel geen vragen want je hebt geen context bij de volgende prompt."
)
},
{"role": "user", "content": vraag},
],
)
antwoord = response.choices[0].message.content
await ctx.send(f"{ctx.author.mention} {antwoord[:1900]}")
except Exception as e:
await ctx.send(f"{ctx.author.mention} Er ging iets mis: {e}")
@ask.error
async def ask_error(ctx, error):
if isinstance(error, commands.CommandOnCooldown):
retry_after = int(error.retry_after + 0.999)
await ctx.reply(f"Rustig {ctx.author.display_name}, probeer het over {retry_after}s nog eens.")
else:
raise error
@bot.command()
async def loterij(ctx, *members: discord.Member):
"""
Gebruik:
!loterij @naam1 @naam2 @naam3
"""
# Alleen in #teamify? -> uncomment als je dat ook wilt
# if ctx.channel.name != "teamify":
# await ctx.send("Dit commando kan alleen worden gebruikt in het #teamify kanaal.")
# return
if not members or len(members) < 2:
await ctx.send("Gebruik: `!loterij @naam1 @naam2 ...` (minimaal 2 mensen, anders is het wel héél zielig).")
return
# Uniek maken (als iemand 2x getagd wordt telt 'ie maar 1x mee)
unique_members = []
seen_ids = set()
for m in members:
if m.id not in seen_ids:
unique_members.append(m)
seen_ids.add(m.id)
# Tekst “tussen A, B en C” netjes bouwen
mentions = [m.mention for m in unique_members]
if len(mentions) == 2:
between_text = f"{mentions[0]} en {mentions[1]}"
else:
between_text = f"{', '.join(mentions[:-1])} en {mentions[-1]}"
winnaar = random.choice(unique_members)
await ctx.send(f"En de loterij gaat tussen {between_text}.")
# Countdown
await asyncio.sleep(1)
await ctx.send("in 3")
await asyncio.sleep(1)
await ctx.send("2")
await asyncio.sleep(1)
await ctx.send("1")
await asyncio.sleep(1)
await ctx.send("trom gerofel 🥁")
await asyncio.sleep(1)
await ctx.send(f"De winnaar is {winnaar.mention} 🎆🎇")
@loterij.error
async def loterij_error(ctx, error):
if isinstance(error, commands.BadArgument):
await ctx.send(
"Ik snap er niks van 🤨\n"
"Gebruik het zo:\n"
"`!loterij @naam1 @naam2 @naam3`\n"
"Zorg dat je **echte Discord-mentions** gebruikt."
)
else:
raise error
bot.run(token) bot.run(token)

View file

@ -12,8 +12,7 @@ if ($host == 'dev.dtch.online') {
<a href="last_stats.php">Last quarter %</a> <a href="last_stats.php">Last quarter %</a>
<a href="latestmatches.php">Last Matches</a> <a href="latestmatches.php">Last Matches</a>
<a href="topstats.php">Top20</a> <a href="topstats.php">Top20</a>
<a href="user_stats.php">User Stats</a> <a href="user_stats.php">User Stats</a>
<a href="videos.php">Videos</a>
</div> </div>
<a href="javascript:void(0);" class="icon" onclick="myFunction()"> <a href="javascript:void(0);" class="icon" onclick="myFunction()">
<i class="fa fa-bars"></i> <i class="fa fa-bars"></i>

View file

@ -240,87 +240,3 @@ td, th {
background-color: #0f0f0f; background-color: #0f0f0f;
color: #e69109; color: #e69109;
} }
/* Videos page styles */
.videos-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: center;
padding: 1rem;
}
.video-item {
flex: 1 1 300px;
max-width: 100%;
}
.video-item video {
max-width: 100%;
max-height: 80vh;
width: auto;
height: auto;
object-fit: contain;
display: block;
margin: 0 auto;
}
/* Share and theatre button styles */
.video-controls {
display: flex;
justify-content: center;
align-items: center;
margin-top: 0.5rem;
gap: 0.5rem;
}
/* Theatre mode styles */
.video-item.theatre-mode {
flex: 1 1 100%;
max-width: 100%;
}
.video-item.theatre-mode video {
width: 100%;
height: auto;
max-height: none;
}
.video-title {
text-align: center;
font-size: 1.2em;
font-weight: bold;
margin-top: 10px;
margin-bottom: 10px;
}
.video-item.theatre-mode {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
box-sizing: border-box;
}
.video-item.theatre-mode video {
max-width: 90%;
max-height: 80vh;
width: auto;
height: auto;
}
.video-item.theatre-mode .video-title {
color: #fff;
order: -1; /* Display title above the video */
}
.video-item.theatre-mode .video-controls {
position: relative;
bottom: auto;
margin-top: 15px;
}

View file

@ -5,16 +5,6 @@ table {
td, th { td, th {
padding: 8px; padding: 8px;
} }
/* Theatre mode mobile styles */
.video-item.theatre-mode {
flex: 1 1 100%;
max-width: 100%;
}
.video-item.theatre-mode video {
width: 100%;
height: auto;
max-height: none;
}
.topnav { .topnav {
overflow: hidden; overflow: hidden;
@ -91,50 +81,4 @@ table {
section h2 { section h2 {
margin-top: 10px; margin-top: 10px;
}
/* Videos page responsive adjustments */
.videos-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: center;
padding: 1rem;
}
.video-item {
flex: 1 1 100%;
max-width: 100%;
}
.video-item video {
width: 100%;
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
}
/* Share and theatre button mobile styles */
.video-controls {
display: flex;
justify-content: center;
align-items: center;
margin-top: 0.5rem;
gap: 0.5rem;
}
.video-title {
text-align: center;
font-size: 1.1em;
font-weight: bold;
margin-top: 8px;
margin-bottom: 8px;
}
.video-item.theatre-mode {
padding: 10px;
}
.video-item.theatre-mode video {
max-width: 100%;
max-height: 70vh;
}
.video-item.theatre-mode .video-controls {
margin-top: 10px;
} }

View file

@ -157,15 +157,15 @@ $lastMatches = array_slice($allMatches, 0, 8);
if (substr($playerStats['playerId'], 0, 2) !== 'ai') { if (substr($playerStats['playerId'], 0, 2) !== 'ai') {
// Create links for each stat // Create links for each stat
echo "<tr>"; echo "<tr>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['name']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['name']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'> Human </a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'> Human </a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['kills']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['kills']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['HumanDmg']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['HumanDmg']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['timeSurvived']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['timeSurvived']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['winPlace']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['winPlace']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['revives']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['revives']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['DBNOs']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['DBNOs']) . "</a></td>";
echo "<td><a href='https://www.pubg-meta.com/player-stats/steam/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['assists']) . "</a></td>"; echo "<td><a href='https://pubg.op.gg/user/" . urlencode($playerStats['name']) . "' target='_blank'>" . htmlspecialchars($playerStats['assists']) . "</a></td>";
echo "</tr>"; echo "</tr>";
} else { } else {
// Display without link // Display without link

View file

@ -48,13 +48,7 @@ foreach ($player in $player_data) {
$stats.included = $sortedStats $stats.included = $sortedStats
$stats | ConvertTo-Json -Depth 100 | Out-File "$scriptroot/../data/matches/$match.json" $stats | ConvertTo-Json -Depth 100 | Out-File "$scriptroot/../data/matches/$match.json"
} }
if (
($stats.data.attributes.matchtype -eq 'event' -and $stats.data.attributes.gameMode -ne 'ibr') -or
($stats.data.attributes.gameMode -eq 'tdm')
) {
Write-Output 'Skipping because of event or tdm'
continue
}
$playermatches += [PSCustomObject]@{ $playermatches += [PSCustomObject]@{
stats = $stats.included.ATTRIBUTES.stats | where-object { $_.name -eq $player.attributes.name } stats = $stats.included.ATTRIBUTES.stats | where-object { $_.name -eq $player.attributes.name }
matchType = $stats.data.attributes.matchtype matchType = $stats.data.attributes.matchtype

View file

@ -161,14 +161,6 @@ $groupedGuids_clan_matches_gt_3 = $guids | Group-Object | Where-Object { $_.Coun
$last_month = (get-date).AddMonths($monthsback) $last_month = (get-date).AddMonths($monthsback)
foreach ($file in $matchfiles) { foreach ($file in $matchfiles) {
$json = get-content $file | ConvertFrom-Json $json = get-content $file | ConvertFrom-Json
if (
($stats.data.attributes.matchtype -eq 'event' -and $stats.data.attributes.gameMode -ne 'ibr') -or
($stats.data.attributes.gameMode -eq 'tdm')
) {
Write-Output 'Skipping because of event or tdm'
continue
}
if ($json.created -gt $last_month) { if ($json.created -gt $last_month) {
$killstats += $json $killstats += $json
if ($groupedGuids_clan_matches_gt_1.Name -contains $json.matchid) { if ($groupedGuids_clan_matches_gt_1.Name -contains $json.matchid) {

View file

@ -1,102 +0,0 @@
<?php
$ogDescription = "Bekijk alle video's";
// Scan the media/videos directory for mp4 files
$videosDir = __DIR__ . '/media/videos';
$allFiles = scandir($videosDir);
$videoFiles = array_filter($allFiles, function($file) use ($videosDir) {
return is_file($videosDir . '/' . $file) && strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'mp4';
});
// Build array with creation time and sort by creation date descending
$videoData = [];
foreach ($videoFiles as $file) {
$path = $videosDir . '/' . $file;
$videoData[] = [
'filename' => $file,
'ctime' => filectime($path)
];
}
usort($videoData, function($a, $b) {
return $b['ctime'] - $a['ctime'];
});
?>
<!DOCTYPE html>
<html lang="en">
<?php include './includes/head.php'; ?>
<body>
<?php include './includes/navigation.php'; ?>
<?php include './includes/header.php'; ?>
<main>
<section>
<h2>Videos</h2>
<?php if (!empty($videoData)): ?>
<div class="videos-container">
<?php foreach ($videoData as $video): ?>
<div class="video-item">
<video controls>
<source src="media/videos/<?php echo htmlspecialchars($video['filename']); ?>" type="video/mp4">
Your browser does not support the video tag.
</video>
<p class="video-title"><?php echo pathinfo($video['filename'], PATHINFO_FILENAME); ?></p>
<div class="video-controls">
<button class="btn share-btn">Delen</button>
<button class="btn theatre-btn">Theatermodus</button>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>No videos found.</p>
<?php endif; ?>
</section>
</main>
<?php include './includes/footer.php'; ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.share-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
var videoItem = btn.closest('.video-item');
var src = videoItem.querySelector('video source').src;
if (navigator.share) {
navigator.share({ title: videoItem.querySelector('p').innerText, url: src })
.catch(function(error) { console.error('Error sharing', error); });
} else if (navigator.clipboard) {
navigator.clipboard.writeText(src).then(function() {
alert('Videolink gekopieerd naar klembord');
}, function(err) {
alert('Kon link niet kopiëren: ' + err);
});
} else {
prompt('Kopieer deze link:', src);
}
});
});
document.querySelectorAll('.theatre-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
var clickedVideoItem = btn.closest('.video-item');
// Remove theatre mode from all other videos
document.querySelectorAll('.video-item.theatre-mode').forEach(function(item) {
if (item !== clickedVideoItem) {
item.classList.remove('theatre-mode');
item.querySelector('.theatre-btn').innerText = 'Theatermodus';
}
});
// Toggle theatre mode for the clicked video
var isActive = clickedVideoItem.classList.toggle('theatre-mode');
btn.innerText = isActive ? 'Sluit theatermodus' : 'Theatermodus';
});
});
});
</script>
</body>
</html>