diff --git a/public/index.php b/public/index.php index 9f22a34..f08ea1f 100644 --- a/public/index.php +++ b/public/index.php @@ -1,18 +1,20 @@ handle($request); (new SapiEmitter())->emit($response); \ No newline at end of file diff --git a/src/AnswerDto.php b/src/AnswerDto.php index 4cfbe6a..f08c51e 100644 --- a/src/AnswerDto.php +++ b/src/AnswerDto.php @@ -1,42 +1,63 @@ userAnswer; + } + + /** + * @return string + */ + public function getImgUrl() { + return $this->imgUrl; + } + + /** + * @return string + */ + public function getText() { + return $this->text; + } + private function __consruct() { } public static function fromParts(string $imgUrl, string $text, ?array $userAnswer = null): AnswerDto { $that = new AnswerDto(); $that->imgUrl = $imgUrl; $that->text = $text; if($userAnswer !== null) { $that->userAnswer = UserAnswerDto::fromArray($userAnswer); } return $that; } public static function fromArray(array $array): AnswerDto { if(isset($array['userAnswer'])) { return self::fromParts($array['imgUrl'], $array['text'], $array['userAnswer']); } else { return self::fromParts($array['imgUrl'], $array['text']); } } public function jsonSerialize() { $result = []; if($this->userAnswer !== null) { $result['userAnswer'] = $this->userAnswer; } $result['imgUrl'] = $this->imgUrl; $result['text'] = $this->text; return $result; } } diff --git a/src/Answers.php b/src/Answers.php index be0228c..72f2788 100644 --- a/src/Answers.php +++ b/src/Answers.php @@ -1,28 +1,97 @@ getAttribute('ParsedBody', []); - $questions = WikiApiDto::fromArray($payload); + $pdo = (new Database())->getPdo(); + $thing = WikiApiDto::fromArray($payload); + $sessionId = $thing->getSessionId(); - // TODO: check that user is a human or a bot + $correct = 0; + $wrong = 0; + + foreach($thing->getQuestionList() as $question) { + $question->getQuestionId(); + + $connotationsStmt = $pdo->prepare('SELECT connotation_ID FROM wcaptcha_challenge_connotation WHERE challenge_ID = ?'); + $connotationsStmt->execute([$sessionId]); + + $questionConnotations = []; + foreach($connotationsStmt->fetchAll() as $connotation) { + $questionConnotations[] = $connotation['connotation_ID']; + } + + foreach($question->getAnswersAvailable() as $answer) { + $img = $answer->getImgUrl(); + $imgId = (int) $answer->getText(); + $answer = $answer->getUserAnswer(); + $selected = isset($answer->selected) ?? null; + unset($answer); + + if($selected === null) { + return new JsonResponse(['human' => false, 'why' => 'Select or not select every image']); + } + $connotationsStmt = $pdo->prepare('SELECT image_ID, connotation_ID, image_connotation_positive FROM wcaptcha_image_connotation WHERE image_ID = ?'); + $connotationsStmt->execute([$imgId]); + foreach($connotationsStmt->fetchAll() as $connotation) { + if($connotation['image_connotation_positive'] === 'positive') { + // Any of these selected connotations is right + if($selected) { + // Has been selected: connotation selected by user is in array of image connotations + if(in_array($connotation['connotation_ID'], $questionConnotations)) { + // Users says so + $correct++; + } else { + $wrong++; + } + } else { + // Not selected: connotation in image has not been selected but it was one in the question + if(in_array($connotation['connotation_ID'], $questionConnotations)) { + $wrong++; + } else { + $correct++; + } + } + } else { + // Any of these selected connotations is wrong + if($selected) { + if(in_array($connotation['connotation_ID'], $questionConnotations)) { + $wrong++; + } else { + $correct++; + } + } else { + if(in_array($connotation['connotation_ID'], $questionConnotations)) { + $correct++; + } else { + $wrong++; + } + } + } + } + + $pdo->beginTransaction(); + $pdo->commit(); + + } + } //return new JsonResponse($questions); // A real system would probably not give this information to the user, but to the server - if((bool) mt_rand(0, 1)) { - return new JsonResponse(['human' => true]); + if($correct < $wrong) { + return new JsonResponse(['human' => true, 'correct' => $correct, 'wrong' => $wrong]); } else { - return new JsonResponse(['human' => false]); + return new JsonResponse(['human' => false, 'correct' => $correct, 'wrong' => $wrong]); } } -} \ No newline at end of file +} diff --git a/src/QuestionDto.php b/src/QuestionDto.php index 5271f60..22f196c 100644 --- a/src/QuestionDto.php +++ b/src/QuestionDto.php @@ -1,66 +1,80 @@ 'IMG', 'INPUT' => 'INPUT', // free text 'OPTIONS' => 'OPTIONS' ]; + /** + * @return AnswerDto[] + */ + public function getAnswersAvailable() { + return $this->answersAvailable; + } + + /** + * @return mixed + */ + public function getQuestionId() { + return $this->questionId; + } + private function __consruct() { } protected $questionText; protected $questionId; protected $questionType; protected $answersAvailable; public static function fromParts(string $id, string $text, string $type, array $answersAvailable): QuestionDto { $that = new QuestionDto(); $that->questionId = $id; $that->questionText = $text; $that->setQuestionType($type); $that->setAnswersAvailable($answersAvailable); return $that; } protected function setQuestionType(string $questionType) { if(!isset(self::QUESTION_TYPES[$questionType])) { throw new InvalidArgumentException("$questionType is not a valid question type"); } $this->questionType = $questionType; } protected function setAnswersAvailable(array $answersAvailable) { foreach($answersAvailable as $item) { if(!($item instanceof AnswerDto)) { throw new InvalidArgumentException("answersAvailable must be instanceof AnswerDto"); } } $this->answersAvailable = $answersAvailable; } public static function fromArray(array $array): QuestionDto { $a = []; foreach($array['answersAvailable'] as $el) { $a[] = AnswerDto::fromArray($el); } return self::fromParts($array['questionId'], $array['questionText'], $array['questionType'], $a); } public function jsonSerialize() { $result = []; $result['questionText'] = $this->questionText; $result['questionId'] = $this->questionId; $result['questionType'] = $this->questionType; $result['answersAvailable'] = $this->answersAvailable; return $result; } } diff --git a/src/Questions.php b/src/Questions.php index 2846aa2..fe55087 100644 --- a/src/Questions.php +++ b/src/Questions.php @@ -1,110 +1,112 @@ getAttribute('ParsedBody', []); if(!isset($payload['language'])) { return new JsonResponse(['error' => 'Add language to request'], 400); } if(!isset($payload['appid'])) { return new JsonResponse(['error' => 'Add appid to request'], 400); } $pdo = (new Database())->getPdo(); $pdo->beginTransaction(); $ip = inet_pton($_SERVER['REMOTE_ADDR']); $s = $pdo->prepare('INSERT INTO wcaptcha_session(session_ip, app_id) VALUES (?, ?)'); $s->execute([$ip, (int) $payload['appid']]); $id = $pdo->lastInsertId(); $pdo->commit(); $questions = []; $challengeRows = $pdo->query('SELECT challenge_ID, challenge_type, challenge_text FROM wcaptcha_challenge ORDER BY RAND() LIMIT 2'); $total = 9; $actual = mt_rand(2, 8); foreach($challengeRows as $challenge) { switch($challenge['challenge_type']) { case 'img': $type = QuestionDto::QUESTION_TYPES['IMG']; break; case 'text': default: $type = QuestionDto::QUESTION_TYPES['INPUT']; break; case 'option': $type = QuestionDto::QUESTION_TYPES['OPTIONS']; break; } $answers = []; $already = []; if($type === QuestionDto::QUESTION_TYPES['IMG']) { // Actual images $imageRows = $pdo->prepare(' SELECT image_ID, image_src FROM wcaptcha_challenge_connotation NATURAL JOIN wcaptcha_image_connotation NATURAL JOIN wcaptcha_image WHERE wcaptcha_challenge_connotation.challenge_ID = ? -AND (image_src LIKE "%.jpg" OR image_src LIKE "%.jpeg" OR image_src LIKE "%.png" OR image_src LIKE "%.gif") GROUP BY image_ID, image_src ORDER BY COUNT(*) DESC LIMIT ? '); $imageRows->execute([$challenge['challenge_ID'], $actual]); foreach($imageRows->fetchAll() as $image) { - $answers[] = AnswerDto::fromParts($image['image_src'], 'No text for you'); + $answers[] = AnswerDto::fromParts($image['image_src'], (string) $image['image_ID']); $already[] = $image['image_ID']; } $actual = count($already); $random = $total - $actual; + if($actual <= 0) { + throw new \LogicException('No answers for challenge ' . $challenge['challenge_ID']); + } + // Random images $questionMarks = []; foreach($already as $ignored) { $questionMarks[] = '?'; } unset($ignored); $stmt = ' SELECT image_ID, image_src FROM wcaptcha_image WHERE image_ID NOT IN (' . implode(', ', $questionMarks) .') -AND (image_src LIKE "%.jpg" OR image_src LIKE "%.jpeg" OR image_src LIKE "%.png" OR image_src LIKE "%.gif") ORDER BY RAND() LIMIT ?'; $params = array_merge($already, [$random]); $imageRows2 = $pdo->prepare($stmt); $imageRows2->execute($params); foreach($imageRows2->fetchAll() as $image) { $answers[] = AnswerDto::fromParts($image['image_src'], 'No text for you'); } } else { throw new \LogicException('Not implemented'); } shuffle($answers); $questions[] = QuestionDto::fromParts($challenge['challenge_ID'], $challenge['challenge_text'], $type, $answers); } return new JsonResponse(WikiApiDto::fromParts($id, $questions), 200); } } diff --git a/src/WikiApiDto.php b/src/WikiApiDto.php index 37c5f2a..d26fde4 100644 --- a/src/WikiApiDto.php +++ b/src/WikiApiDto.php @@ -1,31 +1,45 @@ sessionId = $sessionId; $that->questionList = $questionList; return $that; } public function jsonSerialize() { $result = []; $result['sessionId'] = $this->sessionId; $result['questionList'] = $this->questionList; return $result; } + + /** + * @return QuestionDto[] + */ + public function getQuestionList() { + return $this->questionList; + } + + /** + * @return int + */ + public function getSessionId() { + return $this->sessionId; + } }