ff14-mitigator/api/abilities.php
2026-05-21 15:54:25 +02:00

127 lines
4.1 KiB
PHP

<?php
ini_set('display_errors', '0');
require_once __DIR__ . '/../config.php';
session_start_safe();
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['error' => 'Method not allowed']); exit; }
if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; }
if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; }
$reportCode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['report_code'] ?? '');
$fightId = (int)($_POST['fight_id'] ?? 0);
$startTime = (float)($_POST['start_time'] ?? 0);
$endTime = (float)($_POST['end_time'] ?? 0);
$language = strtolower(trim($_POST['language'] ?? 'en'));
$language = in_array($language, ['en', 'de', 'fr', 'jp'], true) ? $language : 'en';
$translate = $language === 'en' ? 'false' : 'true';
if (!$reportCode || !$fightId || !$endTime) { http_response_code(400); echo json_encode(['error' => 'Missing params']); exit; }
$token = $_SESSION['access_token'];
function localized_graphql_uri(string $language): string {
$host = [
'de' => 'de.fflogs.com',
'fr' => 'fr.fflogs.com',
'jp' => 'ja.fflogs.com',
][$language] ?? 'www.fflogs.com';
return preg_replace('#https://[^/]+#', 'https://' . $host, GRAPHQL_URI);
}
function ab_gql(string $query): array {
global $token, $language;
$acceptLanguage = $language === 'jp' ? 'ja' : $language;
$ch = curl_init(localized_graphql_uri($language));
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['query' => $query]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token, 'Accept-Language: ' . $acceptLanguage],
CURLOPT_SSL_VERIFYPEER => !DEV_MODE,
]);
$body = curl_exec($ch);
curl_close($ch);
return json_decode($body, true) ?? [];
}
// Fetch ability names from masterData
$mdResult = ab_gql(<<<GQL
{
reportData {
report(code: "$reportCode") {
playerDetails(fightIDs: [$fightId])
masterData(translate: $translate) {
abilities { gameID name }
}
}
}
}
GQL);
$abilityNames = [];
foreach ($mdResult['data']['reportData']['report']['masterData']['abilities'] ?? [] as $ab) {
$abilityNames[(int)$ab['gameID']] = $ab['name'];
}
// Build sorted player list
$pdRaw = $mdResult['data']['reportData']['report']['playerDetails'] ?? null;
$pdParsed = is_string($pdRaw) ? json_decode($pdRaw, true) : $pdRaw;
$pdGroups = $pdParsed['data']['playerDetails'] ?? [];
$players = [];
$roleMap = ['tanks' => 'tank', 'healers' => 'healer', 'dps' => 'dps'];
foreach ($roleMap as $group => $role) {
foreach ($pdGroups[$group] ?? [] as $p) {
$players[] = ['name' => $p['name'], 'role' => $role];
}
}
usort($players, fn($a, $b) => strcmp($a['name'], $b['name']));
// Fetch DamageTaken events from NPC sources only (paginated)
$filterEsc = 'source.type = \\"NPC\\"';
$seenIds = [];
$nextPage = $startTime;
for ($page = 0; $page < 10; $page++) {
$evResult = ab_gql(<<<GQL
{
reportData {
report(code: "$reportCode") {
events(
fightIDs: [$fightId],
dataType: DamageTaken,
startTime: $nextPage,
endTime: $endTime,
filterExpression: "$filterEsc"
) {
data
nextPageTimestamp
}
}
}
}
GQL);
$ev = $evResult['data']['reportData']['report']['events'] ?? [];
foreach ($ev['data'] ?? [] as $event) {
$abId = (int)($event['abilityGameID'] ?? 0);
if ($abId > 7) $seenIds[$abId] = true;
}
$nextPage = $ev['nextPageTimestamp'] ?? null;
if ($nextPage === null || $nextPage >= $endTime) break;
}
// Build sorted ability list
$abilities = [];
foreach (array_keys($seenIds) as $id) {
$abilities[] = [
'id' => $id,
'name' => $abilityNames[$id] ?? ('Ability #' . $id),
];
}
usort($abilities, fn($a, $b) => strcmp($a['name'], $b['name']));
echo json_encode(['abilities' => $abilities, 'players' => $players]);