def find(self, chat_id: str, message_id: str) -> Optional[QuestionMessage]:
     response = self.table.get_item(Key={
         "pk": f"CHAT#{chat_id}",
         "sk": f"MESSAGE#{message_id}"
     })
     if response.get("Item") is None:
         return None
     return QuestionMessage(**response["Item"])
 def get_current_active_question(self,
                                 chat_id: str) -> Optional[QuestionMessage]:
     response = self.table.query(
         IndexName="CurrentActiveQuestion",
         KeyConditionExpression=Key("pk").eq(f"CHAT#{chat_id}"),
         Limit=1,
     )
     if len(response.get("Items")) == 0:
         return None
     return QuestionMessage(**response["Items"][0])
Beispiel #3
0
    def ask(self, question: Question) -> QuestionMessage:
        if question.type == Question.QuestionType.open:
            answering_instructions = "Reply to this message to answer!"
            reply_markup = None
            callback_infos = None
        elif question.type == Question.QuestionType.mcq:
            answering_instructions = "Press one of the buttons below!"
            keyboard: List[List[InlineKeyboardButton]] = []
            answers: List[str] = [question.correct_answer] + question.other_answers
            shuffle(answers)
            callback_infos = []
            for answer in answers:
                callback_data = {
                    "chat_id": self.chat_id,
                    "question_id": question.id,
                    "answer": answer,
                }
                callback_infos.append(callback_data)
                callback_id = self.callback_repository.create(
                    chat_id=self.chat_id, callback_data=callback_data
                )
                keyboard.append(
                    [InlineKeyboardButton(answer, callback_data=callback_id)]
                )
            reply_markup = InlineKeyboardMarkup(keyboard)
        else:
            raise ValueError("Question type was neither open or mcq!")
        message_text = f"""
        <b>Question:</b> {question.question}

        {answering_instructions}
        """
        message: Message = self.bot.send_message(
            text=message_text,
            parse_mode="HTML",
            chat_id=self.chat_id,
            reply_markup=reply_markup,
        )
        question_message = QuestionMessage(
            message_id=message.message_id,
            chat_id=message.chat_id,
            question_id=question.id,
            question_data=question,
            sent_at=message.date,
            step_function_execution_arn=self.step_function_execution_arn,
            callback_infos_json=json.dumps(callback_infos)
            if question.type == question.QuestionType.mcq
            else None,
        )
        self.question_message_repository.create(question_message)
        return question_message
 def create(self, question_message: QuestionMessage):
     if question_message.step_function_execution_arn is None:
         raise ValueError(
             "Step Function Execution ARN must not be None when creating new QuestionMessage"
         )
     self.table.put_item(
         Item={
             "pk":
             f"CHAT#{question_message.chat_id}",
             "sk":
             f"MESSAGE#{question_message.message_id}",
             **json.loads(question_message.json(exclude_none=True)),
             "gsi_current_active_question":
             question_message.step_function_execution_arn,
         },
         ConditionExpression="attribute_not_exists(pk)",
     )
 def get_questions_in_group(self, chat_id: str) -> List[QuestionMessage]:
     response = self.table.query(
         KeyConditionExpression=Key("pk").eq(f"CHAT#{chat_id}")
         & Key("sk").begins_with("MESSAGE#"))
     return [QuestionMessage(**item) for item in response.get("Items", [])]
Beispiel #6
0
 def attempt(
     self,
     chat_id: str,
     message_id: str,
     answer: str,
     answer_time: int,
     user_display_name: str,
     no_retries: bool,
 ) -> Union[Tuple[bool, int, str], Tuple[bool, Set[str], bool, bool]]:
     try:
         no_retry_condition_clause = (
             "AND (attribute_not_exists(wrong_users) OR NOT contains(wrong_users, :user_display_name))"
             if no_retries else "")
         response = self.table.update_item(
             Key={
                 "pk": f"CHAT#{chat_id}",
                 "sk": f"MESSAGE#{message_id}",
             },
             UpdateExpression=
             "SET solved_at = :answer_time REMOVE step_function_execution_arn, gsi_current_active_question",
             ConditionExpression=
             f"question_data.correct_answer = :attempted_answer AND attribute_not_exists(solved_at) {no_retry_condition_clause}",
             ExpressionAttributeValues={
                 ":answer_time":
                 int(answer_time),
                 ":attempted_answer":
                 answer.lower(),
                 **({
                     ":user_display_name": user_display_name
                 } if no_retries else dict()),
             },
             ReturnValues="ALL_OLD",
         )
         return (
             True,
             int(answer_time - response["Attributes"]["sent_at"]),
             response["Attributes"]["question_data"]["correct_answer"],
         )
     except ClientError as ex:
         if ex.response["Error"][
                 "Code"] == "ConditionalCheckFailedException":
             # wrong answer OR they got beaten
             response = self.table.update_item(
                 Key={
                     "pk": f"CHAT#{chat_id}",
                     "sk": f"MESSAGE#{message_id}",
                 },
                 UpdateExpression="ADD wrong_users :w",
                 ExpressionAttributeValues={":w": {user_display_name}},
                 ReturnValues="ALL_OLD",
             )
             if "Attributes" not in response:
                 logging.warn("For some reason, attributes are empty")
                 response2 = self.table.get_item(
                     Key={
                         "pk": f"CHAT#{chat_id}",
                         "sk": f"MESSAGE#{message_id}",
                     },
                     ConsistentRead=True,
                 )
                 response["Attributes"] = response2["Item"]
                 if response2["Item"] is None:
                     return False, set(), False, False
             question_message = QuestionMessage(**response["Attributes"])
             return (
                 False,
                 {user_display_name}.union(
                     response.get("Attributes",
                                  dict()).get("wrong_users", {})),
                 user_display_name
                 in response.get("Attributes",
                                 dict()).get("wrong_users", {}),
                 question_message.solved_at is not None,
             )
         else:
             raise ex