mirror of https://github.com/stijndcl/didier
Fix broken time formatting, remove word field
parent
cbd3030565
commit
ea4181eac0
|
@ -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
|
||||||
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue