Я бы вообще изобрёл велосипед, если честно.
У тебя есть список или база активных (неслучившихся) событий. Пусть это будет пара ID-метка времени, остальная инфа нам без надобности.
При запуске бота выбираем из списка ближайшее событие, и вычисляем время ожидания (с небольшим запасом) и спим в корутине. В случае, если ожидание прервано раньше - повторяем то же самое, чтобы либо "доспать", либо переключиться на ожидание более близкого события.
Если же ожидание закончилось успешно, обрабатываем событие и снова повторяем поиск ближайшего события.
Если список событий изменился, прерываем текущее ожидание, чтобы корутина ожидания могла адаптироваться к изменениям.
Кода - строк на пятьдесят, не больше, и выносится в отдельный класс на ура. Как мне кажется, если бот не супер-нагружен (в том смысл, что список хранимых событий изменяется не каждую минуту, и событтия тоже срабатывают не так часто), то этого будет вполне достаточно.
А когда он будет так нагружен - проще будет раз в минуту выбирать то, что должно сработать сейчас.