async def run_func_every_hour():
while True:
await func() # вызов твоей функции, если она async
# если она синхронная - вызов не должен быть длительным!
await asyncio.sleep(3600.0)
hourly_task = asyncio.create_task(run_func_every_hour())
hourly_task.cancel()
.with self.connection:
Есть рабочий цикл (event loop), в котором программа крутится всё время. Весь асинхронный код выполняется как часть этого цикла, а asyncio определяет, какую часть кода (корутину) выполнить.
При старте ты запускаешь этот цикл и передаёшь ему главную корутину. Большинство методов типа bot.run() делают именно это - запускают цикл и отдают ему для выполнения тело бота.
Тело бота занимается сетевым вводом-выводом и мониторит происходящее. По наступлении события (сообщения, например), бот дёргает соответствующий обработчик-корутину, который ты зарегистрировал до старта. Пока обработчик работает, цикл ждёт - но при await-вызове другой корутины он "уступает ей место", пока она не завершится, позволяя циклу продолжить работу.
Т.е. для написания бота на базе библиотеки нужно сделать две вещи:
1. Разобраться, как запустить тело бота. Бот может иметь готовый синхронный метод, который надо дёргать без await. Этот метод запустить event loop и будет его крутить пока бот не завершит работу. Или же бот может иметь асинхронное тело, которое надо вызвать в рамках уже запущенной корутины в уже работающем event loop. Какой из двух вариантов выбрать - смотри сам, но нужно выбрать один.
2. Ну и разобраться, как подключить обработчики к боту.
Всё, что между этими двумя пунктами - задача библиотеки, для нас это чёрный ящик.