GloryBot/classes/pycord_challenge.py
2024-06-02 21:51:07 +02:00

147 lines
4.2 KiB
Python

import logging
from dataclasses import dataclass
from re import IGNORECASE, Match, compile
from typing import List, Union
from bson import ObjectId, Regex
from modules.database import col_challenges
logger = logging.getLogger(__name__)
@dataclass
class PycordChallenge:
"""Dataclass of DB entry of a captcha challenge"""
__slots__ = (
"_id",
"guild",
"enabled",
"archived",
"challenge",
"answers",
)
_id: ObjectId
guild: ObjectId
enabled: bool
archived: bool
challenge: str
answers: List[Regex]
@classmethod
async def create(
cls,
guild: ObjectId,
challenge: str,
answers: List[Regex],
enabled: bool = True,
archived: bool = False,
):
"""Create new challenge entry in a database.
### Args:
* guild (`ObjectId`): Guild's ID
* challenge (`str`): Challenge text
* answers (`List[Regex]`): List of regex patterns that are answers to the challenge
* enabled (`bool`, *optional*): Whether the challenge is enabled. Defaults to `True`.
* archived (`bool`, *optional*): Whether the challenge is archived. Defaults to `False`.
### Returns:
* `PycordChallenge`: The challenge object
"""
challenge_entry = {
"guild": guild,
"enabled": enabled,
"archived": archived,
"challenge": challenge,
"answers": answers,
}
inserted = await col_challenges.insert_one(challenge_entry)
challenge_entry["_id"] = inserted.inserted_id
return cls(**challenge_entry)
def solve(self, expression: str) -> bool:
"""Check if the expression is an answer to the challenge
### Args:
* expression (`str`): Some expression to be matched against the challenge's answers.
### Returns:
* `bool`: `True` if the answer is correct and `False` if not.
"""
return bool(self.match(expression))
def match(self, expression: str) -> Union[Match, None]:
"""Match the expression against challenge's answers
### Args:
* expression (`str`): Some expression to be matched against the challenge's answers.
### Returns:
* `Union[Match, None]`: Regex `Match` if a match is found, otherwise `None`.
"""
for answer in self.answers:
if re_match := compile(answer.pattern, IGNORECASE).match(
expression.strip()
):
return re_match
return None
async def enable(self, unarchive: bool = True) -> None:
"""Enable and unarchive the challenge
### Args:
* unarchive (`bool`, *optional*): Whether to unarchive the challenge as well. Defaults to `True`.
"""
self.enabled = True
self.archived = unarchive if self.archived else False
await col_challenges.update_one(
{"_id": self._id},
{
"$set": {
"enabled": True,
"archived": unarchive if self.archived else False,
}
},
)
async def disable(self) -> None:
"""Disable the challenge"""
self.enabled = False
await col_challenges.update_one({"_id": self._id}, {"$set": {"enabled": False}})
async def archive(self, disable: bool = True) -> None:
"""Archive and disable the challenge
### Args:
* disable (`bool`, *optional*): Whether to disable the challenge as well. Defaults to `True`.
"""
self.enabled = not disable if self.enabled else False
self.archived = True
await col_challenges.update_one(
{"_id": self._id},
{
"$set": {
"enabled": not disable if self.enabled else False,
"archived": True,
}
},
)
async def unarchive(self) -> None:
"""Unarchive the challenge"""
self.archived = False
await col_challenges.update_one(
{"_id": self._id}, {"$set": {"archived": False}}
)