Initial commit: Flutter 无书应用项目
This commit is contained in:
375
ht/p1/api.php
Normal file
375
ht/p1/api.php
Normal file
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
define('SECRET_KEY', 'tzgsc_2026_secret_key');
|
||||
define('KEY_EXPIRE_TIME', 300);
|
||||
define('MAX_REQUESTS_NO_KEY', 10);
|
||||
define('RATE_LIMIT_WINDOW', 60);
|
||||
|
||||
function replace_unicode_escape_sequence($match) {
|
||||
return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
|
||||
}
|
||||
|
||||
function send_json_response($data, $status_code = 200) {
|
||||
http_response_code($status_code);
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
function error_response($message, $status_code = 400) {
|
||||
send_json_response([
|
||||
'success' => false,
|
||||
'message' => $message
|
||||
], $status_code);
|
||||
}
|
||||
|
||||
function generate_new_key($timestamp = null) {
|
||||
if ($timestamp === null) {
|
||||
$timestamp = time();
|
||||
}
|
||||
$data = $timestamp . SECRET_KEY;
|
||||
return hash('sha256', $data);
|
||||
}
|
||||
|
||||
function verify_key($key, &$new_key = null) {
|
||||
if (empty($key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$current_time = time();
|
||||
|
||||
for ($i = 0; $i <= KEY_EXPIRE_TIME; $i++) {
|
||||
$expected_key = generate_new_key($current_time - $i);
|
||||
if (hash_equals($expected_key, $key)) {
|
||||
$new_key = generate_new_key();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_client_ip() {
|
||||
$ip = '';
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
|
||||
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
return trim($ip);
|
||||
}
|
||||
|
||||
function check_rate_limit($ip) {
|
||||
$limit_file = "data/rate_limit/" . md5($ip) . ".json";
|
||||
|
||||
if (!is_dir("data/rate_limit")) {
|
||||
mkdir("data/rate_limit", 0755, true);
|
||||
}
|
||||
|
||||
$current_time = time();
|
||||
$requests = [];
|
||||
|
||||
if (file_exists($limit_file)) {
|
||||
$requests = json_decode(file_get_contents($limit_file), true) ?: [];
|
||||
}
|
||||
|
||||
$requests = array_filter($requests, function($time) use ($current_time) {
|
||||
return ($current_time - $time) < RATE_LIMIT_WINDOW;
|
||||
});
|
||||
|
||||
return count($requests);
|
||||
}
|
||||
|
||||
function record_request($ip) {
|
||||
$limit_file = "data/rate_limit/" . md5($ip) . ".json";
|
||||
|
||||
if (!is_dir("data/rate_limit")) {
|
||||
mkdir("data/rate_limit", 0755, true);
|
||||
}
|
||||
|
||||
$current_time = time();
|
||||
$requests = [];
|
||||
|
||||
if (file_exists($limit_file)) {
|
||||
$requests = json_decode(file_get_contents($limit_file), true) ?: [];
|
||||
}
|
||||
|
||||
$requests[] = $current_time;
|
||||
|
||||
$requests = array_filter($requests, function($time) use ($current_time) {
|
||||
return ($current_time - $time) < RATE_LIMIT_WINDOW;
|
||||
});
|
||||
|
||||
file_put_contents($limit_file, json_encode(array_values($requests)));
|
||||
}
|
||||
|
||||
function get_key_param() {
|
||||
if (isset($_GET['key'])) {
|
||||
return $_GET['key'];
|
||||
}
|
||||
if (isset($_POST['key'])) {
|
||||
return $_POST['key'];
|
||||
}
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
if (isset($input['key'])) {
|
||||
return $input['key'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$key = get_key_param();
|
||||
$ip = get_client_ip();
|
||||
$new_key = null;
|
||||
|
||||
if (!verify_key($key, $new_key)) {
|
||||
$request_count = check_rate_limit($ip);
|
||||
|
||||
if ($request_count >= MAX_REQUESTS_NO_KEY) {
|
||||
error_response('请求过于频繁,请稍后再试', 429);
|
||||
}
|
||||
|
||||
record_request($ip);
|
||||
}
|
||||
|
||||
if ($method === 'GET') {
|
||||
$id = isset($_GET['id']) ? $_GET['id'] : '';
|
||||
$msg = isset($_GET['msg']) ? $_GET['msg'] : '';
|
||||
|
||||
if (empty($id)) {
|
||||
error_response('缺少必要参数: id');
|
||||
}
|
||||
|
||||
if (empty($msg)) {
|
||||
$result = get_question($id);
|
||||
if ($new_key) {
|
||||
$result['new_key'] = $new_key;
|
||||
}
|
||||
send_json_response($result);
|
||||
} else {
|
||||
$result = submit_answer($id, $msg);
|
||||
if ($new_key) {
|
||||
$result['new_key'] = $new_key;
|
||||
}
|
||||
send_json_response($result);
|
||||
}
|
||||
} elseif ($method === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$id = isset($input['id']) ? $input['id'] : '';
|
||||
$msg = isset($input['msg']) ? $input['msg'] : '';
|
||||
|
||||
if (empty($id)) {
|
||||
error_response('缺少必要参数: id');
|
||||
}
|
||||
|
||||
if (empty($msg)) {
|
||||
$result = get_question($id);
|
||||
if ($new_key) {
|
||||
$result['new_key'] = $new_key;
|
||||
}
|
||||
send_json_response($result);
|
||||
} else {
|
||||
$result = submit_answer($id, $msg);
|
||||
if ($new_key) {
|
||||
$result['new_key'] = $new_key;
|
||||
}
|
||||
send_json_response($result);
|
||||
}
|
||||
} else {
|
||||
error_response('不支持的请求方法', 405);
|
||||
}
|
||||
|
||||
function get_question($id) {
|
||||
$data = get_curl("https://hanyu.baidu.com/hanyu/ajax/pingce_data");
|
||||
$data = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $data);
|
||||
|
||||
$cj = cj($data);
|
||||
|
||||
$s = preg_match_all('/{"question_content":"(.*?)","type":{"person":"(.*?)","type":"(.*?)","grade":"(.*?)","dynasty":"(.*?)"},"option_answers":\[(.*?)\]}/', $data, $t);
|
||||
|
||||
if ($s == 0) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => '抱歉,获取出现错误。'
|
||||
];
|
||||
}
|
||||
|
||||
$tm = $t[1][0];
|
||||
$z = $t[2][0];
|
||||
$l = $t[3][0];
|
||||
$n = $t[4][0];
|
||||
$nd = $t[5][0];
|
||||
|
||||
preg_match_all('/{"answer_content":"(.*?)","is_standard_answer":(.*?)}/', $t[6][0], $d);
|
||||
|
||||
$options = [];
|
||||
$correct_answer = null;
|
||||
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$d1 = $d[1][$i];
|
||||
$p = $d[2][$i];
|
||||
$option_num = $i + 1;
|
||||
|
||||
$options[] = [
|
||||
'num' => $option_num,
|
||||
'text' => $d1
|
||||
];
|
||||
|
||||
if ($p == "1") {
|
||||
$correct_answer = $option_num;
|
||||
}
|
||||
}
|
||||
|
||||
$question_data = [
|
||||
'id' => $id,
|
||||
'question' => $tm,
|
||||
'options' => $options,
|
||||
'author' => $z,
|
||||
'type' => $l,
|
||||
'grade' => $n,
|
||||
'dynasty' => $nd,
|
||||
'correct_answer' => $correct_answer
|
||||
];
|
||||
|
||||
file_put_contents("data/tzgsc/" . $id . ".json", json_encode($question_data, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'id' => $id,
|
||||
'question' => $tm,
|
||||
'options' => $options,
|
||||
'author' => $z,
|
||||
'type' => $l,
|
||||
'grade' => $n,
|
||||
'dynasty' => $nd
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
function submit_answer($id, $msg) {
|
||||
$data_file = "data/tzgsc/" . $id . ".json";
|
||||
|
||||
if (!file_exists($data_file)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => '题目数据不存在,请先获取题目'
|
||||
];
|
||||
}
|
||||
|
||||
$question_data = json_decode(file_get_contents($data_file), true);
|
||||
|
||||
if ($msg === "提示") {
|
||||
return [
|
||||
'success' => true,
|
||||
'type' => 'hint',
|
||||
'message' => '这是首描写' . $question_data['type'] . '的诗,你在' . $question_data['grade'] . '学过它。'
|
||||
];
|
||||
}
|
||||
|
||||
if ($msg == $question_data['correct_answer']) {
|
||||
$next_id = $id + 1;
|
||||
$next_question = get_question($next_id);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'type' => 'correct',
|
||||
'message' => '恭喜你,回答正确。请继续下一题',
|
||||
'next_question' => $next_question['success'] ? $next_question['data'] : null
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => true,
|
||||
'type' => 'wrong',
|
||||
'message' => '抱歉,答案不对哦。你可以回复提示获取该题的部分信息哦。'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function cj($data) {
|
||||
if (!$data) return;
|
||||
|
||||
$s = preg_match_all('/{"question_content":"(.*?)","type":(.*?)}]}/', $data, $d);
|
||||
if ($s == 0) return;
|
||||
|
||||
$json_file = "data/tzgsc.json";
|
||||
|
||||
$existing = [];
|
||||
if (file_exists($json_file)) {
|
||||
$content = file_get_contents($json_file);
|
||||
$existing = json_decode($content, true);
|
||||
if (!is_array($existing)) {
|
||||
$existing = [];
|
||||
}
|
||||
}
|
||||
|
||||
$existing_questions = [];
|
||||
foreach ($existing as $item) {
|
||||
if (isset($item['question_content'])) {
|
||||
$existing_questions[$item['question_content']] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $s; $i++) {
|
||||
$d1 = $d[1][$i];
|
||||
$d2 = $d[2][$i];
|
||||
|
||||
if (!isset($existing_questions[$d1])) {
|
||||
$new_item = [
|
||||
'question_content' => $d1,
|
||||
'type' => json_decode('{' . $d2, true)
|
||||
];
|
||||
if ($new_item['type']) {
|
||||
$existing[] = $new_item;
|
||||
$existing_questions[$d1] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($json_file, json_encode($existing, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
function get_curl($url, $post = 0, $referer = 1, $cookie = 0, $header = 0, $ua = 0, $nobaody = 0) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$httpheader[] = "Accept:application/json";
|
||||
$httpheader[] = "Accept-Encoding:gzip,deflate,sdch";
|
||||
$httpheader[] = "Accept-Language:zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection:close";
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
if ($post) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||
}
|
||||
if ($header) {
|
||||
curl_setopt($ch, CURLOPT_HEADER, TRUE);
|
||||
}
|
||||
if ($cookie) {
|
||||
curl_setopt($ch, CURLOPT_DICTAPP_MID, $cookie);
|
||||
}
|
||||
if ($referer) {
|
||||
if ($referer == 1) {
|
||||
curl_setopt($ch, CURLOPT_REFERER, 'http://m.qzone.com/infocenter?g_f=');
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_REFERER, $referer);
|
||||
}
|
||||
}
|
||||
if ($ua) {
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, $ua);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; U; Android 4.4.1; zh-cn) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.5 Mobile Safari/533.1');
|
||||
}
|
||||
if ($nobaody) {
|
||||
curl_setopt($ch, CURLOPT_NOBODY, 1);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
$ret = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $ret;
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user