Как вывести данные из объекта корутины?
я изучаю API мобильной игры Clash of Clans (coc.py), и веду своего бота в телеграмме в целях удалённого просмотра профиля человека. Я столкнулся с проблемой вывода данных из объекта корутины при выводе данных в свою память. За это отвечает строка: userClashData = await clash_api.get_player(). Насколько я понял - эти данные выводятся только с использованием асинхронных функций, но какой бы я способ не использовал - мне выдаёт RuntimeError. Прошу прощение, если моя ошибка очень тупая, или легко решаемая, но кому не сложно, подскажите пожалуйста, в чём я мог накосякить.
Исходный обработчик:
@bot.message_handler(commands=['clashProfile', 'clashprofile', 'clashPr'])
async def Commands_CheckUserClashProfile(message):
global uid
uid_link = message.from_user.id
user = opt_database.data_select(str(uid_link))
if user is None:
bot.send_message(message.chat.id, '''❌ Профиль Пользователя не найден! Уважаемый Пользователь?, пропишитесь через команду /reg Ник''')
else:
try:
uid = user[0]
banner = user[10]
messData = opt_chats.command_divide(message.text)
messData_tag = messData[1]
user2 = opt_database.data_select(messData_tag)
if user2 is None:
bot.reply_to(message, '❌ Похоже, этот участник не зарегестрирован в боте!')
else:
u2id = user2[0]
u2name = user2[1]
userClashTag_link = cur.execute('SELECT clashTag FROM clashdata WHERE uid = ?;', [u2id])
userClashTag = list(userClashTag_link.fetchone())
if userClashTag is None or userClashTag == '':
bot.reply_to(message, '❌ Этот участник не имеет своего тега в боте!')
else:
try:
userClashData = await clash_api.get_player()
clash_profile_img = render.clash_profile(banner, int(userClashData.trophies), userClashData)
bot.send_photo(message.chat.id, clash_profile_img, caption=f'''? {u2name} \nСтатистика игрока из игры Clash of Clans''')
render.output_cache_clear()
except (coc.NotFound, coc.ClashOfClansException):
bot.reply_to(message, '❌ Данные человека не найдены!')
render.output_cache_clear()
except IndexError:
...
Вывод из интерпретатора:
C:\Users\Admin\PycharmProjects\RecedivePrivate\venv\lib\site-packages\telebot\util.py:91: RuntimeWarning: coroutine 'Commands_CheckUserClashProfile' was never awaited
task(*args, **kwargs) RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Ответы (2 шт):
В коде await clash_api.get_player() является асинхронной функцией. Однако, в обработчиках команд Telegram бота, которые используют telebot, не можете использовать ключевое слово 'await' напрямую внутри функции. Вместо этого, вы можете использовать метод asyncio.create_task(), по идее ошибка должна уйти
import asyncio
# ...
@bot.message_handler(commands=['clashProfile', 'clashprofile', 'clashPr'])
async def Commands_CheckUserClashProfile(message):
# ...
try:
# ...
userClashData = await asyncio.create_task(clash_api.get_player()) # Используйте asyncio.create_task()
clash_profile_img = render.clash_profile(banner, int(userClashData.trophies), userClashData)
bot.send_photo(message.chat.id, clash_profile_img, caption=f'''? {u2name} \nСтатистика игрока из игры Clash of Clans''')
render.output_cache_clear()
# ...
except IndexError:
...
Насколько я понимаю, библиотека которую вы используете для работы с API Telegram не является асинхронной.
Думаю, вам будет проще всего перейти на асинхронный аналог, к примеру aiogram. Ошибка возникает из-за того, что ваша функция является асинхронной, но при написании команды в боте, она пытается запустится синхронно.
Вы можете попытаться сделать функцию async def Commands_CheckUserClashProfile синхронной, если ваш код уже слишком большой, а функции API будут вызываться не так часто, то для вас может подойти и такой подход:
import coc
import asyncio
async def async_get_player_data(player_tag):
async with coc.Client() as coc_client:
await coc_client.login("email", "password")
return await coc_client.get_player(player_tag)
def get_player_data(player_tag):
loop = asyncio.get_event_loop()
player_data = loop.run_until_complete(async_get_player_data(player_tag))
return player_data
def your_function():
print(get_player_data('802PRV0UV')) # этот код может быть внутри вашей функции
your_function()
В вашем примере получится как-то так:
async def async_get_player_data(player_tag):
async with coc.Client() as coc_client:
await coc_client.login("email", "password")
return await coc_client.get_player(player_tag)
def get_player_data(player_tag):
loop = asyncio.get_event_loop()
player_data = loop.run_until_complete(async_get_player_data(player_tag))
return player_data
@bot.message_handler(commands=['clashProfile', 'clashprofile', 'clashPr'])
def Commands_CheckUserClashProfile(message):
global uid
uid_link = message.from_user.id
user = opt_database.data_select(str(uid_link))
if user is None:
bot.send_message(message.chat.id, '''❌ Профиль Пользователя не найден! Уважаемый Пользователь?, пропишитесь через команду /reg Ник''')
else:
try:
uid = user[0]
banner = user[10]
messData = opt_chats.command_divide(message.text)
messData_tag = messData[1]
user2 = opt_database.data_select(messData_tag)
if user2 is None:
bot.reply_to(message, '❌ Похоже, этот участник не зарегестрирован в боте!')
else:
u2id = user2[0]
u2name = user2[1]
userClashTag_link = cur.execute('SELECT clashTag FROM clashdata WHERE uid = ?;', [u2id])
userClashTag = list(userClashTag_link.fetchone())
if userClashTag is None or userClashTag == '':
bot.reply_to(message, '❌ Этот участник не имеет своего тега в боте!')
else:
try:
userClashData = get_player_data(userClashTag)
clash_profile_img = render.clash_profile(banner, int(userClashData.trophies), userClashData)
bot.send_photo(message.chat.id, clash_profile_img, caption=f'''? {u2name} \nСтатистика игрока из игры Clash of Clans''')
render.output_cache_clear()
except (coc.NotFound, coc.ClashOfClansException):
bot.reply_to(message, '❌ Данные человека не найдены!')
render.output_cache_clear()
except IndexError:
...
Единственная проблема: это постоянный вход в учетную запись COC, убирать её с синхронным способом будет довольно сложно, поэтому советую изучить aiogram, и если ваш обработчик будет сделан используя эту библиотеку, то вам не придется делать функцию синхронной, и столько мучиться с asyncio.