GloryBot/classes/pycord_challenge.py

147 lines
4.2 KiB
Python
Raw Normal View History

2024-06-02 22:51:07 +03:00
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}}
)