asyncio.sleep() вполне хватит.
Бот должен знать своё состояние ("покой", "сбор игроков", "игра идёт", ну и так далее).
Один обработчик (начало игры) проверяет, что сейчас состояние "покой", отправляет приглашение, ставит состояние "сбор игроков" и ждёт заданное время. По итогам ожидания проверяет список игроков. Если нужного числа игроков в списке нет, ставит состояние "покой", иначе "игра идёт".
Второй обработчик (игрок присоединился) проверяет, что сейчас состояние "сбор игроков". Если да, добавляет отправителя в список игроков.
Как хранить состояние, список игроков и другую игровую инфу, зависит от того, может ли параллельно идти несколько игровых сессий. Если не может, то достаточно простых глобальных переменных. Если может... всё сложнее.