first from cpanel commit
This commit is contained in:
82
api/emoji.php
Normal file
82
api/emoji.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
// api/emoji.php (route: /api/emoji?slug=... or rewrite to /api/emoji/{slug})
|
||||
require_once __DIR__.'/../helpers/auth.php';
|
||||
require_once __DIR__.'/../helpers/filters.php';
|
||||
|
||||
// ---- Skin tone helpers (Fitzpatrick modifiers) ----
|
||||
if (!function_exists('dw_supports_skin_tone')) {
|
||||
function dw_supports_skin_tone(array $emoji): bool {
|
||||
// Respect explicit flag if present in source data
|
||||
if (isset($emoji['supports_skin_tone'])) {
|
||||
return (bool)$emoji['supports_skin_tone'];
|
||||
}
|
||||
$cat = strtolower(trim($emoji['category'] ?? ''));
|
||||
$sub = strtolower(trim($emoji['subcategory'] ?? ''));
|
||||
$name = strtolower(trim($emoji['name'] ?? ''));
|
||||
// 1) category must be People & Body
|
||||
if ($cat !== 'people & body') return false;
|
||||
// 2) Exclude faces: many face emojis are not tone-modifiable
|
||||
if (preg_match('~\bface\b~', $name) || preg_match('~\bface\b~', $sub)) return false;
|
||||
// 3) Allowlist of tone-capable subcategories (based on Unicode charts)
|
||||
$allowSubs = [
|
||||
'hand-fingers-open','hand-fingers-partial','hand-fingers-closed','hand-single-finger',
|
||||
'hands','hand-prop','handshake','body-parts','person','person-gesture','person-activity','person-resting'
|
||||
];
|
||||
if (in_array($sub, $allowSubs, true) || str_starts_with($sub, 'hand')) return true;
|
||||
// Fallback by keywords mentioning body parts / gestures
|
||||
$keys = [];
|
||||
foreach (['keywords','keywords_en','keywords_id'] as $k) {
|
||||
if (!empty($emoji[$k]) && is_array($emoji[$k])) $keys = array_merge($keys, $emoji[$k]);
|
||||
}
|
||||
$hay = strtolower(' '.implode(' ', $keys).' ');
|
||||
return (bool)preg_match('~\b(hand|hands|wave|clap|thumb|finger|palm|point|ear|nose|leg|foot|person|man|woman|boy|girl|salute)\b~', $hay);
|
||||
}
|
||||
}
|
||||
if (!function_exists('dw_skin_tone_modifiers')) {
|
||||
function dw_skin_tone_modifiers(): array {
|
||||
return ["\u{1F3FB}", "\u{1F3FC}", "\u{1F3FD}", "\u{1F3FE}", "\u{1F3FF}"]; // light .. dark
|
||||
}
|
||||
}
|
||||
if (!function_exists('dw_strip_tone')) {
|
||||
function dw_strip_tone(string $emoji): string {
|
||||
return preg_replace('/\x{1F3FB}|\x{1F3FC}|\x{1F3FD}|\x{1F3FE}|\x{1F3FF}/u', '', $emoji);
|
||||
}
|
||||
}
|
||||
if (!function_exists('dw_build_tone_variants')) {
|
||||
function dw_build_tone_variants(string $emojiBase): array {
|
||||
$mods = ["\u{1F3FB}", "\u{1F3FC}", "\u{1F3FD}", "\u{1F3FE}", "\u{1F3FF}"];
|
||||
return array_map(fn($m) => $emojiBase.$m, $mods);
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$tier = detectTier();
|
||||
$slug = trim($_GET['slug'] ?? '');
|
||||
|
||||
$data = json_decode(file_get_contents(__DIR__.'/../data/emojis.json'), true);
|
||||
$found = null;
|
||||
foreach ($data['emojis'] as $e) {
|
||||
if (($e['slug'] ?? '') === $slug) { $found = $e; break; }
|
||||
}
|
||||
if (!$found) { http_response_code(404); echo json_encode(['error'=>'not_found']); exit; }
|
||||
|
||||
$out = filterEmoji($found, $tier, false);
|
||||
// Add tone support & modifiers for single emoji response
|
||||
$out['supports_skin_tone'] = dw_supports_skin_tone($found) || dw_supports_skin_tone($out);
|
||||
if (!isset($out['skin_tone_modifiers'])) {
|
||||
$out['skin_tone_modifiers'] = dw_skin_tone_modifiers();
|
||||
}
|
||||
if (!empty($out['supports_skin_tone']) && !empty($out['emoji'])) {
|
||||
$base = dw_strip_tone($out['emoji']);
|
||||
if ($base !== '') {
|
||||
$out['emoji_base'] = $base;
|
||||
$out['variants'] = dw_build_tone_variants($base);
|
||||
}
|
||||
}
|
||||
|
||||
$etag = '"'.sha1(json_encode([$slug,$tier,$out])).'"';
|
||||
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($out, JSON_UNESCAPED_UNICODE);
|
||||
231
api/emojis.php
Normal file
231
api/emojis.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
// api/emojis.php (route: /api/emojis?q=...&category=...&subcategory=...&page=1&limit=50)
|
||||
require_once __DIR__.'/../helpers/auth.php';
|
||||
require_once __DIR__.'/../helpers/filters.php';
|
||||
|
||||
// ---- Skin tone helpers (Fitzpatrick modifiers) ----
|
||||
if (!function_exists('dw_supports_skin_tone')) {
|
||||
function dw_supports_skin_tone(array $emoji): bool {
|
||||
// Respect explicit flag if present in source data
|
||||
if (isset($emoji['supports_skin_tone'])) {
|
||||
return (bool)$emoji['supports_skin_tone'];
|
||||
}
|
||||
$cat = strtolower(trim($emoji['category'] ?? ''));
|
||||
$sub = strtolower(trim($emoji['subcategory'] ?? ''));
|
||||
$name = strtolower(trim($emoji['name'] ?? ''));
|
||||
// 1) category must be People & Body
|
||||
if ($cat !== 'people & body') return false;
|
||||
// 2) Exclude faces: many face emojis are not tone-modifiable
|
||||
if (preg_match('~\bface\b~', $name) || preg_match('~\bface\b~', $sub)) return false;
|
||||
// 3) Allowlist of tone-capable subcategories (based on Unicode charts)
|
||||
$allowSubs = [
|
||||
'hand-fingers-open','hand-fingers-partial','hand-fingers-closed','hand-single-finger',
|
||||
'hands','hand-prop','handshake','body-parts','person','person-gesture','person-activity','person-resting'
|
||||
];
|
||||
if (in_array($sub, $allowSubs, true) || (function_exists('str_starts_with') ? str_starts_with($sub, 'hand') : strpos($sub, 'hand') === 0)) return true;
|
||||
// Fallback by keywords mentioning body parts / gestures
|
||||
$keys = [];
|
||||
foreach (['keywords','keywords_en','keywords_id'] as $k) {
|
||||
if (!empty($emoji[$k]) && is_array($emoji[$k])) $keys = array_merge($keys, $emoji[$k]);
|
||||
}
|
||||
$hay = strtolower(' '.implode(' ', $keys).' ');
|
||||
return (bool)preg_match('~\b(hand|hands|wave|clap|thumb|finger|palm|point|ear|nose|leg|foot|person|man|woman|boy|girl|salute)\b~', $hay);
|
||||
}
|
||||
}
|
||||
if (!function_exists('dw_skin_tone_modifiers')) {
|
||||
function dw_skin_tone_modifiers(): array {
|
||||
return ["\u{1F3FB}", "\u{1F3FC}", "\u{1F3FD}", "\u{1F3FE}", "\u{1F3FF}"]; // light .. dark
|
||||
}
|
||||
}
|
||||
if (!function_exists('dw_strip_tone')) {
|
||||
function dw_strip_tone(string $emoji): string {
|
||||
return preg_replace('/\x{1F3FB}|\x{1F3FC}|\x{1F3FD}|\x{1F3FE}|\x{1F3FF}/u', '', $emoji);
|
||||
}
|
||||
}
|
||||
if (!function_exists('dw_build_tone_variants')) {
|
||||
function dw_build_tone_variants(string $emojiBase): array {
|
||||
$mods = ["\u{1F3FB}", "\u{1F3FC}", "\u{1F3FD}", "\u{1F3FE}", "\u{1F3FF}"];
|
||||
return array_map(fn($m) => $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);
|
||||
0
api/error_log
Normal file
0
api/error_log
Normal file
0
api/index.php
Normal file
0
api/index.php
Normal file
Reference in New Issue
Block a user