$emojiBase.$m, $mods); } } header('Content-Type: application/json; charset=utf-8'); $tier = detectTier(); // Allow client hint via header (extension may send X-Dewemoji-Plan: pro|free) $hint = strtolower(trim($_SERVER['HTTP_X_DEWEMOJI_PLAN'] ?? '')); // Optional license verify (server-side spot check) $acct = $_SERVER['HTTP_X_ACCOUNT_ID'] ?? ''; $key = $_SERVER['HTTP_X_LICENSE_KEY'] ?? ''; if ($acct && $key) { // probabilistic verify, e.g., 1 in 50 requests if (mt_rand(1,50) === 1) { $verify = @file_get_contents('https://emojilicense.dewe.pw/v1/license/verify', false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\n", 'content' => json_encode(['key'=>$key,'account_id'=>$acct]), 'timeout' => 3, ] ])); if ($verify) { $v = json_decode($verify,true); if (!empty($v['ok']) && !empty($v['plan'])) { $tier = strtolower($v['plan']); $isPro = ($tier==='pro'); } } } } if ($hint === 'pro' || $hint === 'free') { $tier = $hint; } $isPro = ($tier === 'pro'); $origin = $_SERVER['HTTP_ORIGIN'] ?? ''; $host = $_SERVER['HTTP_HOST'] ?? ''; $whitelist = ['emoji.dewe.pw']; $isWhitelisted = in_array($host, $whitelist, true) || in_array(parse_url($origin, PHP_URL_HOST) ?: '', $whitelist, true); // --- Free vs Pro gating --- $maxLimit = $isPro ? 50 : 20; $maxPages = $isPro ? 20 : 5; if ($isWhitelisted) { $maxLimit = 100; $maxPages = 1000; } // --- Helpers for slug <-> label normalization --- $CATEGORY_MAP = [ 'all' => 'all', 'smileys' => 'Smileys & Emotion', 'people' => 'People & Body', 'animals' => 'Animals & Nature', 'food' => 'Food & Drink', 'travel' => 'Travel & Places', 'activities'=> 'Activities', 'objects' => 'Objects', 'symbols' => 'Symbols', 'flags' => 'Flags', ]; function dw_slugify($s){ $s = strtolower(trim((string)$s)); $s = str_replace('&', 'and', $s); $s = preg_replace('/[^a-z0-9]+/','-',$s); return trim($s,'-'); } // Parse incoming filters $qParam = trim((string)($_GET['q'] ?? '')); $catParam = trim((string)($_GET['category'] ?? '')); $subParam = trim((string)($_GET['subcategory'] ?? '')); // Normalize category: accept either slug ("people") or label ("People & Body") $catLabel = ''; if ($catParam !== '' && strtolower($catParam) !== 'all') { $catLabel = $CATEGORY_MAP[strtolower($catParam)] ?? $catParam; // fallback to raw label } // Normalize subcategory to a slug for comparison $subSlug = $subParam !== '' ? dw_slugify($subParam) : ''; // Expose normalized vars for filter closure $q = $qParam; $cat = $catLabel; // label form (or '') $sub = $subSlug; // slug form (or '') // Parse query params with caps $limit = min(max((int)($_GET['limit'] ?? $maxLimit), 1), $maxLimit); $page = max((int)($_GET['page'] ?? 1), 1); if ($page > $maxPages) { header('Content-Type: application/json; charset=utf-8'); echo json_encode([ 'items' => [], 'page' => $page, 'limit' => $limit, 'total' => 0, 'plan' => $isPro ? 'pro' : 'free', 'message' => 'Page limit reached for your plan.' ], JSON_UNESCAPED_UNICODE); exit; } // Load data $data = json_decode(file_get_contents(__DIR__.'/../data/emojis.json'), true); $items = $data['emojis'] ?? []; // Server-side filtering (case-insensitive) $items = array_values(array_filter($items, function($e) use ($q,$cat,$sub) { // Category match: compare by slug to be robust if ($cat !== '') { $itemCatSlug = dw_slugify($e['category'] ?? ''); $wantCatSlug = dw_slugify($cat); if ($itemCatSlug !== $wantCatSlug) return false; } // Subcategory match: normalize item subcategory to slug and compare if ($sub !== '') { $itemSubSlug = dw_slugify($e['subcategory'] ?? ''); if ($itemSubSlug !== $sub) return false; } // Text search across common fields if ($q !== '') { $hay = strtolower(join(' ', [ $e['emoji'] ?? '', $e['name'] ?? '', $e['slug'] ?? '', $e['category'] ?? '', $e['subcategory'] ?? '', join(' ', $e['keywords_en'] ?? []), join(' ', $e['keywords_id'] ?? []), join(' ', $e['aliases'] ?? []), join(' ', $e['shortcodes'] ?? []), ])); foreach (preg_split('/\s+/', strtolower($q)) as $tok) { if ($tok === '') continue; if (strpos($hay, $tok) === false) return false; } } return true; })); $total = count($items); $offset = ($page - 1) * $limit; $items = array_slice($items, $offset, $limit); // Field filtering + augmentation $items = array_map(function($raw) use ($tier, $isPro) { $e = filterEmoji($raw, $tier, true); // add tone support & modifiers for clients $e['supports_skin_tone'] = dw_supports_skin_tone($raw) || dw_supports_skin_tone($e); if (!isset($e['skin_tone_modifiers'])) { $e['skin_tone_modifiers'] = dw_skin_tone_modifiers(); } if ($e['supports_skin_tone'] && !empty($e['emoji'])) { $base = dw_strip_tone($e['emoji']); if ($base !== '') { $e['emoji_base'] = $base; if ($isPro) { $e['variants'] = dw_build_tone_variants($base); } } } return $e; }, $items); // ETag for caching $etag = '"'.sha1(json_encode([$page,$limit,$q,$cat,$sub,$tier,$total,$items])).'"'; header('ETag: '.$etag); header('Cache-Control: public, max-age=300'); if (($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === $etag) { http_response_code(304); exit; } echo json_encode([ 'items' => $items, 'page' => $page, 'limit' => $limit, 'total' => $total, 'plan' => $isPro ? 'pro' : 'free', ], JSON_UNESCAPED_UNICODE);