Fix broken time formatting, remove word field

pull/124/head
stijndcl 2022-07-26 21:48:50 +02:00
parent cbd3030565
commit ea4181eac0
9 changed files with 22085 additions and 12 deletions

View File

@ -1,7 +1,9 @@
from typing import Optional from typing import Optional
from database.enums import TempStorageKey
from database.mongo_types import MongoCollection from database.mongo_types import MongoCollection
from database.schemas.mongo import WordleGame from database.schemas.mongo import WordleGame
from database.utils.datetime import today_only_date
__all__ = ["get_active_wordle_game", "make_wordle_guess", "start_new_wordle_game"] __all__ = ["get_active_wordle_game", "make_wordle_guess", "start_new_wordle_game"]
@ -11,9 +13,9 @@ async def get_active_wordle_game(collection: MongoCollection, user_id: int) -> O
return await collection.find_one({"user_id": user_id}) return await collection.find_one({"user_id": user_id})
async def start_new_wordle_game(collection: MongoCollection, user_id: int, word: str) -> WordleGame: async def start_new_wordle_game(collection: MongoCollection, user_id: int) -> WordleGame:
"""Start a new game""" """Start a new game"""
game = WordleGame(user_id=user_id, word=word) game = WordleGame(user_id=user_id)
await collection.insert_one(game.dict(by_alias=True)) await collection.insert_one(game.dict(by_alias=True))
return game return game
@ -21,3 +23,27 @@ async def start_new_wordle_game(collection: MongoCollection, user_id: int, word:
async def make_wordle_guess(collection: MongoCollection, user_id: int, guess: str): async def make_wordle_guess(collection: MongoCollection, user_id: int, guess: str):
"""Make a guess in your current game""" """Make a guess in your current game"""
await collection.update_one({"user_id": user_id}, {"$push": {"guesses": guess}}) await collection.update_one({"user_id": user_id}, {"$push": {"guesses": guess}})
async def get_daily_word(collection: MongoCollection) -> Optional[str]:
"""Get the word of today"""
result = await collection.find_one({"key": TempStorageKey.WORDLE_WORD, "day": today_only_date()})
if result is None:
return None
return result["word"]
async def set_daily_word(collection: MongoCollection, word: str):
"""Set the word of today
This does NOT overwrite the existing word if there is one, so that it can safely run
on startup every time
"""
current_word = await get_daily_word(collection)
if current_word is not None:
return
await collection.update_one(
{"key": TempStorageKey.WORDLE_WORD}, {"day": today_only_date(), "word": word}, upsert=True
)

View File

@ -1,6 +1,6 @@
import enum import enum
__all__ = ["TaskType"] __all__ = ["TaskType", "TempStorageKey"]
# There is a bug in typeshed that causes an incorrect PyCharm warning # There is a bug in typeshed that causes an incorrect PyCharm warning
@ -11,3 +11,10 @@ class TaskType(enum.IntEnum):
BIRTHDAYS = enum.auto() BIRTHDAYS = enum.auto()
UFORA_ANNOUNCEMENTS = enum.auto() UFORA_ANNOUNCEMENTS = enum.auto()
@enum.unique
class TempStorageKey(str, enum.Enum):
"""Enum for keys to distinguish the TemporaryStorage rows"""
WORDLE_WORD = "wordle_word"

View File

@ -6,7 +6,9 @@ from bson import ObjectId
from overrides import overrides from overrides import overrides
from pydantic import BaseModel, Field, conlist from pydantic import BaseModel, Field, conlist
__all__ = ["MongoBase", "WordleGame"] __all__ = ["MongoBase", "TemporaryStorage", "WordleGame"]
from database.utils.datetime import today_only_date
class PyObjectId(str): class PyObjectId(str):
@ -55,6 +57,17 @@ class MongoCollection(MongoBase, ABC):
raise NotImplementedError raise NotImplementedError
class TemporaryStorage(MongoCollection):
"""Collection for lots of random things that don't belong in a full-blown collection"""
key: str
@staticmethod
@overrides
def collection() -> str:
return "temporary"
class WordleStats(BaseModel): class WordleStats(BaseModel):
"""Model that holds stats about a player's Wordle performance""" """Model that holds stats about a player's Wordle performance"""
@ -80,9 +93,9 @@ class GameStats(MongoCollection):
class WordleGame(MongoCollection): class WordleGame(MongoCollection):
"""Collection that holds people's active Wordle games""" """Collection that holds people's active Wordle games"""
user_id: int day: datetime.date = Field(default_factory=lambda: today_only_date())
word: str
guesses: conlist(str, min_items=0, max_items=6) = Field(default_factory=list) guesses: conlist(str, min_items=0, max_items=6) = Field(default_factory=list)
user_id: int
@staticmethod @staticmethod
@overrides @overrides

View File

@ -1,5 +1,14 @@
import datetime
import zoneinfo import zoneinfo
__all__ = ["LOCAL_TIMEZONE"] __all__ = ["LOCAL_TIMEZONE", "today_only_date"]
LOCAL_TIMEZONE = zoneinfo.ZoneInfo("Europe/Brussels") LOCAL_TIMEZONE = zoneinfo.ZoneInfo("Europe/Brussels")
def today_only_date() -> datetime.datetime:
"""Mongo can't handle datetime.date, so we need datetime
We do, however, only care about the date, so remove all the rest
"""
return datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)

View File

@ -0,0 +1,27 @@
from typing import Optional
from discord import app_commands
from discord.ext import commands
from didier import Didier
class Games(commands.Cog):
"""Cog for various games"""
client: Didier
def __init__(self, client: Didier):
self.client = client
@app_commands.command(name="wordle", description="Play Wordle!")
async def wordle(self, ctx: commands.Context, guess: Optional[str] = None):
"""View your active Wordle game
If an argument is provided, make a guess instead
"""
async def setup(client: Didier):
"""Load the cog"""
await client.add_cog(Games(client))

View File

@ -8,6 +8,8 @@ import settings
from database import enums from database import enums
from database.crud.birthdays import get_birthdays_on_day from database.crud.birthdays import get_birthdays_on_day
from database.crud.ufora_announcements import remove_old_announcements from database.crud.ufora_announcements import remove_old_announcements
from database.crud.wordle import set_daily_word
from database.schemas.mongo import TemporaryStorage
from didier import Didier from didier import Didier
from didier.data.embeds.ufora.announcements import fetch_ufora_announcements from didier.data.embeds.ufora.announcements import fetch_ufora_announcements
from didier.decorators.tasks import timed_task from didier.decorators.tasks import timed_task
@ -46,7 +48,14 @@ class Tasks(commands.Cog):
self.pull_ufora_announcements.start() self.pull_ufora_announcements.start()
self.remove_old_ufora_announcements.start() self.remove_old_ufora_announcements.start()
self._tasks = {"birthdays": self.check_birthdays, "ufora": self.pull_ufora_announcements} # Start other tasks
self.reset_wordle_word.start()
self._tasks = {
"birthdays": self.check_birthdays,
"ufora": self.pull_ufora_announcements,
"wordle": self.reset_wordle_word,
}
@commands.group(name="Tasks", aliases=["Task"], case_insensitive=True, invoke_without_command=True) @commands.group(name="Tasks", aliases=["Task"], case_insensitive=True, invoke_without_command=True)
@commands.check(is_owner) @commands.check(is_owner)
@ -113,6 +122,17 @@ class Tasks(commands.Cog):
async with self.client.postgres_session as session: async with self.client.postgres_session as session:
await remove_old_announcements(session) await remove_old_announcements(session)
@tasks.loop(time=DAILY_RESET_TIME)
async def reset_wordle_word(self):
"""Reset the daily Wordle word"""
db = self.client.mongo_db
collection = db[TemporaryStorage.collection()]
await set_daily_word(collection, random.choice(self.client.wordle_words))
@reset_wordle_word.before_loop
async def _before_reset_wordle_word(self):
await self.client.wait_until_ready()
@check_birthdays.error @check_birthdays.error
@pull_ufora_announcements.error @pull_ufora_announcements.error
@remove_old_ufora_announcements.error @remove_old_ufora_announcements.error
@ -123,5 +143,10 @@ class Tasks(commands.Cog):
async def setup(client: Didier): async def setup(client: Didier):
"""Load the cog""" """Load the cog
await client.add_cog(Tasks(client))
Initially reset the Wordle word
"""
cog = Tasks(client)
await client.add_cog(cog)
await cog.reset_wordle_word()

View File

@ -27,6 +27,7 @@ class Didier(commands.Bot):
error_channel: discord.abc.Messageable error_channel: discord.abc.Messageable
initial_extensions: tuple[str, ...] = () initial_extensions: tuple[str, ...] = ()
http_session: ClientSession http_session: ClientSession
wordle_words: tuple[str] = tuple()
def __init__(self): def __init__(self):
activity = discord.Activity(type=discord.ActivityType.playing, name=settings.DISCORD_STATUS_MESSAGE) activity = discord.Activity(type=discord.ActivityType.playing, name=settings.DISCORD_STATUS_MESSAGE)
@ -60,6 +61,9 @@ class Didier(commands.Bot):
This hook is called once the bot is initialised This hook is called once the bot is initialised
""" """
# Load the Wordle dictionary
self._load_wordle_words()
# Load extensions # Load extensions
await self._load_initial_extensions() await self._load_initial_extensions()
await self._load_directory_extensions("didier/cogs") await self._load_directory_extensions("didier/cogs")
@ -101,6 +105,16 @@ class Didier(commands.Bot):
elif os.path.isdir(new_path := f"{path}/{file}"): elif os.path.isdir(new_path := f"{path}/{file}"):
await self._load_directory_extensions(new_path) await self._load_directory_extensions(new_path)
def _load_wordle_words(self):
"""Load the dictionary of Wordle words"""
words = []
with open("files/dictionaries/words-english-wordle.txt", "r") as fp:
for line in fp:
words.append(line.strip())
self.wordle_words = tuple(words)
async def resolve_message(self, reference: discord.MessageReference) -> discord.Message: async def resolve_message(self, reference: discord.MessageReference) -> discord.Message:
"""Fetch a message from a reference""" """Fetch a message from a reference"""
# Message is in the cache, return it # Message is in the cache, return it

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ async def wordle_collection(mongodb: MongoDatabase) -> MongoCollection:
@pytest.fixture @pytest.fixture
async def wordle_game(wordle_collection: MongoCollection, test_user_id: int) -> WordleGame: async def wordle_game(wordle_collection: MongoCollection, test_user_id: int) -> WordleGame:
"""Fixture to create a new game""" """Fixture to create a new game"""
game = WordleGame(user_id=test_user_id, word="test") game = WordleGame(user_id=test_user_id)
await wordle_collection.insert_one(game.dict(by_alias=True)) await wordle_collection.insert_one(game.dict(by_alias=True))
yield game yield game
@ -24,7 +24,7 @@ async def test_start_new_game(wordle_collection: MongoCollection, test_user_id:
result = await wordle_collection.find_one({"user_id": test_user_id}) result = await wordle_collection.find_one({"user_id": test_user_id})
assert result is None assert result is None
await crud.start_new_wordle_game(wordle_collection, test_user_id, "test") await crud.start_new_wordle_game(wordle_collection, test_user_id)
result = await wordle_collection.find_one({"user_id": test_user_id}) result = await wordle_collection.find_one({"user_id": test_user_id})
assert result is not None assert result is not None