Updated Satori scripts.
This commit is contained in:
parent
2a18242d08
commit
5a0fbc3890
9 changed files with 227 additions and 97 deletions
|
@ -6,7 +6,6 @@ define('STR_CONFIG', $config['msz-config-path']);
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
if(!is_file(STR_CONFIG)) {
|
if(!is_file(STR_CONFIG)) {
|
||||||
http_response_code(500);
|
|
||||||
echo '{"error":101}';
|
echo '{"error":101}';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +14,6 @@ $config = parse_ini_file(STR_CONFIG);
|
||||||
|
|
||||||
if(empty($config['driver']) || empty($config['unix_socket'])
|
if(empty($config['driver']) || empty($config['unix_socket'])
|
||||||
|| empty($config['username']) || empty($config['database'])) {
|
|| empty($config['username']) || empty($config['database'])) {
|
||||||
http_response_code(500);
|
|
||||||
echo '{"error":102}';
|
echo '{"error":102}';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +34,6 @@ try {
|
||||||
. ', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\'',
|
. ', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\'',
|
||||||
]);
|
]);
|
||||||
} catch(PDOException $ex) {
|
} catch(PDOException $ex) {
|
||||||
http_response_code(500);
|
|
||||||
echo '{"error":103}';
|
echo '{"error":103}';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
180
public/booru.php
Normal file
180
public/booru.php
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
<?php
|
||||||
|
ini_set('display_errors', 'on');
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
define('BOORUS', [
|
||||||
|
'danbooru',
|
||||||
|
'gelbooru',
|
||||||
|
'yandere',
|
||||||
|
]);
|
||||||
|
|
||||||
|
define('BOORU_INFOS', [
|
||||||
|
'danbooru' => [
|
||||||
|
'title' => 'Danbooru',
|
||||||
|
'type' => 'danbooru',
|
||||||
|
'randomUrlFormat' => 'https://danbooru.donmai.us/posts.json?limit=20&tags=order:random+limit:20+%s',
|
||||||
|
'postUrlFormat' => 'https://danbooru.donmai.us/posts/%s',
|
||||||
|
'implicitTags' => [
|
||||||
|
'misaka_mikoto' => ['-shokuhou_misaki'],
|
||||||
|
'shokuhou_misaki' => ['-misaka_mikoto'],
|
||||||
|
'kagari_(rewrite)' => ['-screencap'],
|
||||||
|
'kasuga_ayumu' => ['-rating:explicit'],
|
||||||
|
'osaka_(azumanga_daioh)' => ['-rating:explicit'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'gelbooru' => [
|
||||||
|
'title' => 'Gelbooru',
|
||||||
|
'type' => 'gelbooru',
|
||||||
|
'randomUrlFormat' => 'https://gelbooru.com/index.php?page=dapi&s=post&q=index&json=1&limit=20&tags=sort:random+%s',
|
||||||
|
'postUrlFormat' => 'https://gelbooru.com/index.php?page=post&s=view&id=%s',
|
||||||
|
'implicitTags' => [
|
||||||
|
'misaka_mikoto' => ['-shokuhou_misaki'],
|
||||||
|
'shokuhou_misaki' => ['-misaka_mikoto'],
|
||||||
|
'kagari_(rewrite)' => ['-screencap'],
|
||||||
|
'kasuga_ayumu' => ['-rating:explicit'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'yandere' => [
|
||||||
|
'title' => 'Yande.re',
|
||||||
|
'type' => 'yandere',
|
||||||
|
'randomUrlFormat' => 'https://yande.re/post.json?api_version=2&limit=20&tags=order:random+%s',
|
||||||
|
'postUrlFormat' => 'https://yande.re/post/show/%s',
|
||||||
|
'implicitTags' => [
|
||||||
|
'misaka_mikoto' => ['-shokuhou_misaki'],
|
||||||
|
'shokuhou_misaki' => ['-misaka_mikoto'],
|
||||||
|
'kagari_(rewrite)' => ['-cap'],
|
||||||
|
'kasuga_ayumu' => ['-rating:explicit'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$config = parse_ini_file(__DIR__ . '/../config/flashii.ini');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$booru = (string)filter_input(INPUT_GET, 'b');
|
||||||
|
$tags = (string)filter_input(INPUT_GET, 't');
|
||||||
|
$tags = array_filter(explode(' ', trim($tags)));
|
||||||
|
|
||||||
|
$randomSource = empty($booru);
|
||||||
|
|
||||||
|
if($randomSource)
|
||||||
|
$booru = BOORUS[array_rand(BOORUS)];
|
||||||
|
elseif(!in_array($booru, BOORUS))
|
||||||
|
die('{"error":"booru"}');
|
||||||
|
|
||||||
|
if($booru === 'danbooru' && $randomSource)
|
||||||
|
$booru = BOORUS[1 + (array_rand(BOORUS) - 1)];
|
||||||
|
|
||||||
|
if(!array_key_exists($booru, BOORU_INFOS))
|
||||||
|
die('{"error":"booru-info"}');
|
||||||
|
|
||||||
|
$booruInfo = BOORU_INFOS[$booru];
|
||||||
|
|
||||||
|
if(empty($booruInfo['randomUrlFormat']))
|
||||||
|
die('{"error":"random"}');
|
||||||
|
if(empty($booruInfo['postUrlFormat']))
|
||||||
|
die('{"error":"post"}');
|
||||||
|
if(empty($booruInfo['type']))
|
||||||
|
die('{"error":"type"}');
|
||||||
|
|
||||||
|
$isDanbooru = $booruInfo['type'] === 'danbooru';
|
||||||
|
$isGelbooru = $booruInfo['type'] === 'gelbooru';
|
||||||
|
$isYandere = $booruInfo['type'] === 'yandere';
|
||||||
|
|
||||||
|
if(!empty($booruInfo['implicitTags'])) {
|
||||||
|
$originalTags = $tags;
|
||||||
|
foreach($booruInfo['implicitTags'] as $targetTag => $addTags) {
|
||||||
|
if(!in_array($targetTag, $originalTags))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach($addTags as $tag)
|
||||||
|
if(!in_array($tag, $tags))
|
||||||
|
$tags[] = $tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$tagString = implode('+', array_map(fn($x) => urlencode($x), $tags));
|
||||||
|
|
||||||
|
$curl = curl_init(sprintf($booruInfo['randomUrlFormat'], $tagString));
|
||||||
|
|
||||||
|
$headers = ['Accept: application/json'];
|
||||||
|
if($isDanbooru)
|
||||||
|
$headers[] = 'Authorization: Basic ' . base64_encode($config['danbooru-token']);
|
||||||
|
|
||||||
|
curl_setopt_array($curl, [
|
||||||
|
CURLOPT_AUTOREFERER => true,
|
||||||
|
CURLOPT_CERTINFO => false,
|
||||||
|
CURLOPT_FAILONERROR => false,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_MAXREDIRS => 5,
|
||||||
|
CURLOPT_PATH_AS_IS => true,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_TCP_FASTOPEN => true,
|
||||||
|
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
||||||
|
CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 2,
|
||||||
|
CURLOPT_TIMEOUT => 5,
|
||||||
|
CURLOPT_USERAGENT => 'Satori/20230202 (+https://fii.moe/satori)',
|
||||||
|
CURLOPT_HTTPHEADER => $headers,
|
||||||
|
]);
|
||||||
|
$response = json_decode(curl_exec($curl));
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
$posts = [];
|
||||||
|
|
||||||
|
// gelbooru and yandere block remote embedding, so not even going to bother with a file_url for those
|
||||||
|
|
||||||
|
if($isDanbooru) {
|
||||||
|
if(!empty($response) && is_array($response)) {
|
||||||
|
foreach($response as $raw) {
|
||||||
|
$posts[] = $post = new stdClass;
|
||||||
|
$post->id = $raw->id;
|
||||||
|
$post->rating = $raw->rating;
|
||||||
|
if(!empty($raw->file_url))
|
||||||
|
$post->file_url = $raw->file_url;
|
||||||
|
$post->post_url = sprintf($booruInfo['postUrlFormat'], $raw->id);
|
||||||
|
$post->tags = explode(' ', $raw->tag_string);
|
||||||
|
$post->tags_general = explode(' ', $raw->tag_string_general);
|
||||||
|
$post->tags_character = explode(' ', $raw->tag_string_character);
|
||||||
|
$post->tags_copyright = explode(' ', $raw->tag_string_copyright);
|
||||||
|
$post->tags_artist = explode(' ', $raw->tag_string_artist);
|
||||||
|
$post->tags_meta = explode(' ', $raw->tag_string_meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif($isGelbooru) {
|
||||||
|
if(!empty($response) && !empty($response->post) && is_array($response->post)) {
|
||||||
|
foreach($response->post as $raw) {
|
||||||
|
$posts[] = $post = new stdClass;
|
||||||
|
$post->id = $raw->id;
|
||||||
|
$post->rating = $raw->rating[0];
|
||||||
|
//if(!empty($raw->file_url))
|
||||||
|
// $post->file_url = $raw->file_url;
|
||||||
|
$post->post_url = sprintf($booruInfo['postUrlFormat'], $raw->id);
|
||||||
|
$post->tags = explode(' ', $raw->tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif($isYandere) {
|
||||||
|
if(!empty($response) && !empty($response->posts) && is_array($response->posts)) {
|
||||||
|
foreach($response->posts as $raw) {
|
||||||
|
$posts[] = $post = new stdClass;
|
||||||
|
$post->id = $raw->id;
|
||||||
|
$post->rating = $raw->rating;
|
||||||
|
//if(!empty($raw->file_url))
|
||||||
|
// $post->file_url = $raw->file_url;
|
||||||
|
$post->post_url = sprintf($booruInfo['postUrlFormat'], $raw->id);
|
||||||
|
$post->tags = explode(' ', $raw->tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
die('{"error":"support"}');
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'source' => [
|
||||||
|
'name' => $booru,
|
||||||
|
'type' => $booruInfo['type'],
|
||||||
|
'title' => $booruInfo['title'] ?? $booru,
|
||||||
|
],
|
||||||
|
'tags' => $tags,
|
||||||
|
'posts' => $posts,
|
||||||
|
]);
|
|
@ -13,10 +13,8 @@ try {
|
||||||
if($data)
|
if($data)
|
||||||
echo json_encode($data);
|
echo json_encode($data);
|
||||||
else {
|
else {
|
||||||
http_response_code(404);
|
|
||||||
echo '{"error":105}';
|
echo '{"error":105}';
|
||||||
}
|
}
|
||||||
} catch(PDOException $ex) {
|
} catch(PDOException $ex) {
|
||||||
http_response_code(500);
|
|
||||||
echo '{"error":104}';
|
echo '{"error":104}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
$config = parse_ini_file(__DIR__ . '/../config/flashii.ini');
|
|
||||||
|
|
||||||
chdir($config['msz-path']);
|
|
||||||
require_once 'misuzu.php';
|
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
|
|
||||||
$userId = (int)filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
|
|
||||||
|
|
||||||
if($userId < 1) {
|
|
||||||
echo '[]';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$userInfo = \Misuzu\Users\User::byId($userId);
|
|
||||||
} catch(\Misuzu\Users\UserNotFoundException $ex) {
|
|
||||||
http_response_code(404);
|
|
||||||
echo '[]';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fields = $userInfo->profileFields();
|
|
||||||
$discord = '';
|
|
||||||
foreach($fields as $field)
|
|
||||||
if($field->field_key === 'discord') {
|
|
||||||
$discord = $field->field_value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$regAddr = $userInfo->getRegisterRemoteAddress();
|
|
||||||
$lastAddr = $userInfo->getLastRemoteAddress();
|
|
||||||
|
|
||||||
if(strpos($regAddr, ':') === false)
|
|
||||||
$regAddr = '::ffff:' . $regAddr;
|
|
||||||
if(strpos($lastAddr, ':') === false)
|
|
||||||
$lastAddr = '::ffff:' . $lastAddr;
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
'id' => $userInfo->getId(),
|
|
||||||
'name' => $userInfo->getUsername(),
|
|
||||||
'email' => $userInfo->getEmailAddress(),
|
|
||||||
'reg_addr' => $regAddr,
|
|
||||||
'last_addr' => $lastAddr,
|
|
||||||
'asscord' => $discord,
|
|
||||||
]);
|
|
|
@ -27,10 +27,10 @@ try {
|
||||||
ON f.`forum_id` = p.`forum_id`
|
ON f.`forum_id` = p.`forum_id`
|
||||||
WHERE `post_id` > :post_id
|
WHERE `post_id` > :post_id
|
||||||
AND `post_deleted` IS NULL
|
AND `post_deleted` IS NULL
|
||||||
AND `post_created` > NOW() - INTERVAL 2 DAY
|
AND `post_created` > NOW() - INTERVAL 7 DAY
|
||||||
AND p.`forum_id` IN (2, 7, 24, 6, 5, 4, 16, 20, 8, 19, 10, 11, 13, 21, 15, 14, 27, 29, 28, 18, 23)
|
AND p.`forum_id` IN (2, 7, 24, 6, 5, 4, 16, 20, 8, 19, 10, 11, 13, 21, 15, 14, 27, 29, 28, 18, 23)
|
||||||
ORDER BY `post_id`
|
ORDER BY `post_id`
|
||||||
LIMIT 10
|
LIMIT 6
|
||||||
');
|
');
|
||||||
$fetch->bindValue('post_id', $startId);
|
$fetch->bindValue('post_id', $startId);
|
||||||
if($fetch->execute())
|
if($fetch->execute())
|
||||||
|
@ -38,6 +38,5 @@ try {
|
||||||
else
|
else
|
||||||
echo '[]';
|
echo '[]';
|
||||||
} catch(PDOException $ex) {
|
} catch(PDOException $ex) {
|
||||||
http_response_code(500);
|
|
||||||
echo '{"error":104}';
|
echo '{"error":104}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ try {
|
||||||
SELECT `user_id`, `username`
|
SELECT `user_id`, `username`
|
||||||
FROM `msz_users`
|
FROM `msz_users`
|
||||||
WHERE `user_id` > :user_id
|
WHERE `user_id` > :user_id
|
||||||
AND `user_created` > NOW() - INTERVAL 2 DAY
|
AND `user_created` > NOW() - INTERVAL 7 DAY
|
||||||
ORDER BY `user_id`
|
ORDER BY `user_id`
|
||||||
LIMIT 10
|
LIMIT 10
|
||||||
');
|
');
|
||||||
|
@ -18,6 +18,5 @@ try {
|
||||||
else
|
else
|
||||||
echo '[]';
|
echo '[]';
|
||||||
} catch(PDOException $ex) {
|
} catch(PDOException $ex) {
|
||||||
http_response_code(500);
|
|
||||||
echo '{"error":104}';
|
echo '{"error":104}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
$config = parse_ini_file(__DIR__ . '/../config/flashii.ini');
|
|
||||||
|
|
||||||
define('TETRIO_TOKEN', $config['tetrio-token']);
|
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
|
|
||||||
if(empty($_GET['q'])) {
|
|
||||||
echo '{"success":false,"errors":[{"location":"body","param":"query","value":"","msg":"query missing"}]}';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$curl = curl_init($config['tetrio-user-reverse']);
|
|
||||||
curl_setopt_array($curl, [
|
|
||||||
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0',
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_POST => true,
|
|
||||||
CURLOPT_POSTFIELDS => json_encode(['query' => (string)filter_input(INPUT_GET, 'q', FILTER_SANITIZE_STRING)]),
|
|
||||||
CURLOPT_HTTPHEADER => [
|
|
||||||
'Authorization: Bearer ' . TETRIO_TOKEN,
|
|
||||||
'Accept: application/json',
|
|
||||||
'Content-Type: application/json',
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
$out = curl_exec($curl);
|
|
||||||
curl_close($curl);
|
|
||||||
|
|
||||||
echo $out;
|
|
|
@ -1,7 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
ini_set('display_errors', 'on');
|
|
||||||
error_reporting(-1);
|
|
||||||
|
|
||||||
define('TL_FORMAT', strtolower($_GET['fmt'] ?? 'json'));
|
define('TL_FORMAT', strtolower($_GET['fmt'] ?? 'json'));
|
||||||
|
|
||||||
if (isset($_SERVER['HTTP_USER_AGENT'], $_GET['pretty']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Mozilla/') !== false)
|
if (isset($_SERVER['HTTP_USER_AGENT'], $_GET['pretty']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Mozilla/') !== false)
|
||||||
|
@ -59,11 +56,10 @@ function out($arr, $mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
define('TRANSLATE_URL', 'https://translate.google.com/translate_a/single?ie=UTF-8&oe=UTF-8&multires=1&client=gtx&sl=%s&tl=%s&dt=t&q=%s');
|
define('TRANSLATE_URL', 'https://translate.google.com/translate_a/single?ie=UTF-8&oe=UTF-8&multires=1&client=gtx&sl=%s&tl=%s&dt=t&q=%s');
|
||||||
define('CURL_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36');
|
define('CURL_USER_AGENT', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0');
|
||||||
define('MAX_TRANSL_LENGTH', 630);
|
define('MAX_TRANSL_LENGTH', 630);
|
||||||
|
|
||||||
function curl_req($url)
|
function curl_req($url) {
|
||||||
{
|
|
||||||
$curl = curl_init($url);
|
$curl = curl_init($url);
|
||||||
|
|
||||||
curl_setopt_array($curl, [
|
curl_setopt_array($curl, [
|
||||||
|
@ -81,14 +77,12 @@ class Language {
|
||||||
public $code;
|
public $code;
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
public function __construct($code, $name)
|
public function __construct($code, $name) {
|
||||||
{
|
|
||||||
$this->code = $code;
|
$this->code = $code;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function fromCode($code)
|
public static function fromCode($code) {
|
||||||
{
|
|
||||||
return new Language($code, locale_get_display_name($code));
|
return new Language($code, locale_get_display_name($code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,8 +105,7 @@ $languages = [
|
||||||
|
|
||||||
$mode = $_GET['m'] ?? null;
|
$mode = $_GET['m'] ?? null;
|
||||||
|
|
||||||
switch ($mode)
|
switch ($mode) {
|
||||||
{
|
|
||||||
case 'list':
|
case 'list':
|
||||||
$out = [];
|
$out = [];
|
||||||
|
|
||||||
|
|
39
public/word-define.php
Normal file
39
public/word-define.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
$config = parse_ini_file(__DIR__ . '/../config/flashii.ini');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
$word = (string)filter_input(INPUT_GET, 'word');
|
||||||
|
if(empty($word))
|
||||||
|
die('{"error":"word"}');
|
||||||
|
|
||||||
|
$response = json_decode(file_get_contents(sprintf(
|
||||||
|
'https://api.wordnik.com/v4/word.json/%s/definitions?limit=5&includeRelated=false&sourceDictionaries=ahd-5%%2Cwordnet&useCanonical=true&includeTags=false&api_key=%s',
|
||||||
|
rawurlencode($word),
|
||||||
|
$config['wordnik-token']
|
||||||
|
)));
|
||||||
|
|
||||||
|
if(empty($response))
|
||||||
|
die('{"error":"response"}');
|
||||||
|
|
||||||
|
$defs = [];
|
||||||
|
|
||||||
|
foreach($response as $def) {
|
||||||
|
if(empty($def->text))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$defs[] = [
|
||||||
|
'word' => $def->word ?? '',
|
||||||
|
'pos' => $def->partOfSpeech ?? '',
|
||||||
|
'attr' => $def->attributionText ?? '',
|
||||||
|
'attr_url' => $def->attributionUrl ?? '',
|
||||||
|
'dict' => $def->sourceDictionary ?? '',
|
||||||
|
'text' => strtr($def->text, [
|
||||||
|
'<em>' => '[i]',
|
||||||
|
'</em>' => '[/i]',
|
||||||
|
]),
|
||||||
|
'url' => $def->wordnikUrl ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($defs);
|
Reference in a new issue