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; } ?>