Как вывести данные из объекта корутины?

Рейтинг: 0Ответов: 2Опубликовано: 08.06.2023

я изучаю 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

Ответы

▲ 0

В коде 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:
        ...
▲ 0

Насколько я понимаю, библиотека которую вы используете для работы с 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.