futami/public/index.php

272 lines
8 KiB
PHP

<?php
define('FTM_ROOT', __DIR__ . '/..');
define('FTM_PATH_PUB', FTM_ROOT . '/public');
define('FTM_PATH_PRV', FTM_ROOT . '/private');
define('FTM_LEGACY_SOUND_TYPE', [
'opus' => 'audio/ogg',
'ogg' => 'audio/ogg',
'mp3' => 'audio/mpeg',
'caf' => 'audio/x-caf',
'wav' => 'audio/wav',
]);
header('X-Powered-By: Futami');
header('Cache-Control: max-age=86400, must-revalidate');
header('Access-Control-Allow-Origin: *');
$reqMethod = (string)filter_input(INPUT_SERVER, 'REQUEST_METHOD');
if($reqMethod === 'OPTIONS') {
http_response_code(204);
header('Access-Control-Allow-Methods: OPTIONS, GET');
header('Access-Control-Allow-Headers: Cache-Control');
return;
}
if($reqMethod !== 'HEAD' && $reqMethod !== 'GET') {
http_response_code(405);
return;
}
function json_out($data): void {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);
exit;
}
function match_etag($eTag): void {
if(filter_input(INPUT_SERVER, 'HTTP_IF_NONE_MATCH') === $eTag) {
http_response_code(304);
exit;
}
}
function gen_etag(string $user, string $data): string {
return sprintf('W/"%s-%s"', hash('sha1', $data), $user);
}
function gen_etag_file(string $user, string $path): string {
return gen_etag($user, hash_file('sha1', $path));
}
function etag(string $etag): void {
match_etag($etag);
header('ETag: ' . $etag);
}
function etag_data(string $user, string $data): void {
etag(gen_etag($user, $data));
}
function etag_file(string $user, string $path): void {
etag(gen_etag_file($user, $path));
}
$reqPath = '/' . trim(parse_url((string)filter_input(INPUT_SERVER, 'REQUEST_URI'), PHP_URL_PATH), '/');
if($reqPath === '/common.json') {
$commonPath = FTM_PATH_PRV . '/common.ini';
etag_file('common', $commonPath);
$common = parse_ini_file($commonPath, false, INI_SCANNER_TYPED);
$colours = $common['colours'];
$common['colours'] = [];
foreach($colours as $n => $c)
$common['colours'][] = compact('n', 'c');
json_out($common);
}
if($reqPath === '/sounds2.json') {
$sndLibData = file_get_contents(FTM_PATH_PRV . '/sound-library.ini');
$sndPackData = file_get_contents(FTM_PATH_PRV . '/sound-packs.ini');
etag_data('sounds2', $sndLibData . $sndPackData);
$sndLib = parse_ini_string($sndLibData, true, INI_SCANNER_TYPED);
$sndPacks = parse_ini_string($sndPackData, true, INI_SCANNER_TYPED);
$library = [];
foreach($sndLib as $name => $info)
$library[] = [
'name' => $name,
'title' => $info['title'],
'sources' => $info['sources'],
];
$packs = [];
foreach($sndPacks as $name => $info)
$packs[] = [
'name' => $name,
'title' => $info['title'],
'events' => $info['events'],
];
json_out(compact('library', 'packs'));
}
if($reqPath === '/sounds.json') {
$sndLibData = file_get_contents(FTM_PATH_PRV . '/sound-library.ini');
$sndPackData = file_get_contents(FTM_PATH_PRV . '/sound-packs.ini');
etag_data('sounds', $sndLibData . $sndPackData);
$sndLib = parse_ini_string($sndLibData, true, INI_SCANNER_TYPED);
$sndPacks = parse_ini_string($sndPackData, true, INI_SCANNER_TYPED);
$library = [];
foreach($sndLib as $name => $info) {
$sources = [];
foreach($info['sources'] as $type => $path) {
$sources[] = [
'format' => FTM_LEGACY_SOUND_TYPE[$type],
'url' => $path,
];
}
$library[] = [
'id' => $name,
'name' => $info['title'],
'sources' => $sources,
];
}
$packs = [];
foreach($sndPacks as $name => $info)
$packs[] = [
'id' => $name,
'name' => $info['title'],
'events' => $info['events'],
];
json_out(compact('library', 'packs'));
}
if($reqPath === '/texttriggers.json') {
$textTriggersPath = FTM_PATH_PRV . '/text-triggers.ini';
etag_file('texttriggers', $textTriggersPath);
$triggers = parse_ini_file($textTriggersPath, true, INI_SCANNER_TYPED);
if($triggers === false)
$triggers = [];
else
$triggers = array_values($triggers);
json_out($triggers);
}
if($reqPath === '/soundtriggers.json') {
$textTriggersPath = FTM_PATH_PRV . '/text-triggers.ini';
etag_file('soundtriggers', $textTriggersPath);
$textTriggers = parse_ini_file($textTriggersPath, true, INI_SCANNER_TYPED);
$sndLib = parse_ini_file(FTM_PATH_PRV . '/sound-library.ini', true, INI_SCANNER_TYPED);
$soundTrigs = [];
foreach($textTriggers as $triggerInfo) {
if($triggerInfo['type'] !== 'sound' && $triggerInfo['type'] !== 'alias')
continue;
$soundTrig = [];
if($triggerInfo['type'] === 'sound') {
$sounds = [];
foreach($triggerInfo['sounds'] as $soundName) {
if(!isset($sndLib[$soundName]))
continue;
$sound = [];
$libSound = $sndLib[$soundName];
if(isset($libSound['sources']['mp3']))
$sound['m'] = $libSound['sources']['mp3'];
if(isset($libSound['sources']['ogg']))
$sound['o'] = $libSound['sources']['ogg'];
if(isset($libSound['sources']['opus']))
$sound['o'] = $libSound['sources']['opus'];
if(isset($libSound['sources']['caf']))
$sound['c'] = $libSound['sources']['caf'];
if(empty($sound))
continue;
if(isset($triggerInfo['volume'])) {
$sound['v'] = ceil(($triggerInfo['volume'] - 1) * 100);
$sound['v2'] = $triggerInfo['volume'];
}
if(isset($triggerInfo['rate']))
$sound['r'] = $triggerInfo['rate'];
$sounds[] = $sound;
}
$soundTrig['s'] = $sounds;
} elseif($triggerInfo['type'] === 'alias') {
$soundTrig['f'] = $triggerInfo['for'];
}
$matches = [];
foreach($triggerInfo['match'] as $match) {
$filters = [];
$value = null;
$notValue = null;
$parts = explode(';', $match);
foreach($parts as $part) {
$part = explode(':', trim($part));
switch($part[0]) {
case 'lc':
$filters[] = 'lower';
break;
case 'is':
$filters[] = 'exact';
$value = trim($part[1]);
break;
case 'starts':
$filters[] = 'starts';
$value = trim($part[1]);
break;
case 'has':
$filters[] = 'contains';
$value = trim($part[1]);
break;
case 'hasnot':
$notValue = trim($part[1]);
break;
default:
$filters[] = 'missing:' . $part[0];
break;
}
}
$matchNew = ['t' => implode(':', $filters)];
if($value !== null)
$matchNew['m'] = $value;
if($notValue !== null)
$matchNew['n'] = $notValue;
$matches[] = $matchNew;
}
$soundTrig['t'] = $matches;
$soundTrigs[] = $soundTrig;
}
json_out([
'meta' => [
'baseUrl' => '',
],
'triggers' => $soundTrigs,
]);
}
if($reqPath === '/' || $reqPath === '/index.html' || $reqPath === '/index.php') {
header('Content-Type: text/html; charset=utf-8');
echo <<<HTML
<!doctype html>
Data and settings shared between both chat clients is stored on this subdomain.
HTML;
return;
}
http_response_code(404);