• Как запустить два параллельных процесса бота: бесконечную функцию, проверяющую имеются ли обновления, и обработчики событий и сообщений?

    AnastasiiaEg
    @AnastasiiaEg Автор вопроса
    Vindicar, спасибо большое за помощь! вывела get_price() в отдельный поток, вроде работает все:)
    Написано
  • Как запустить два параллельных процесса бота: бесконечную функцию, проверяющую имеются ли обновления, и обработчики событий и сообщений?

    AnastasiiaEg
    @AnastasiiaEg Автор вопроса
    а, ой, поняла

    async def start_checking_price(bot):
        while True:
            results = await price_check.check_and_update_prices()
            for user_id in results:
                try:
                    await bot.send_message(user_id, results[user_id])
                except Exception as e:
                    print(f"Не удалось отправить сообщение: {e}")
            await asyncio.sleep(1600)
    
    
    async def main():
        try:
            bot = Bot(token=TOKEN)
            dp = Dispatcher(storage=MemoryStorage())
            dp.include_router(router)
    
            task_polling = asyncio.create_task(dp.start_polling(bot))
            task_price_checking = asyncio.create_task(start_checking_price(bot))
    
            await asyncio.gather(task_polling, task_price_checking)
    
        except Exception as e:
            print(f"Ошибка при запуске: {e}")
    
    if __name__ == '__main__':
        try:
            asyncio.run(main())
        except KeyboardInterrupt:
            print('Бот выключен')
        except Exception as e:
            print(f"Ошибка во время работы: {e}")


    check_and_update_prices() выглядит так:
    async def check_and_update_prices():
        notifications = {}
        async with aiosqlite.connect('users_products.db') as conn:
            cursor = await conn.cursor()
            await cursor.execute("SELECT user_id, URL, product_name, current_price, product_availability FROM products_users")
            products = await cursor.fetchall()
    
            for user_id, url, product_name, old_price, old_availability in products:
                domain_url = urlparse(url).netloc
                new_price, availability = await get_price(url, domain_url, old_price)
                notification_message = None
    
                if availability != old_availability or (new_price and int(new_price) != int(old_price)):
                    await manipulation_db.add_or_update_product(user_id, url, product_name, new_price, availability)
    
                    if availability == 0:
                        notification_message = (f'Вашего товара {product_name} больше нет в наличии. Можете отписаться от '
                                                f'отслеживания или подождать наличия (я уведомлю об этом).{url}')
                    elif availability == 1 and new_price:
                        if int(new_price) == int(old_price):
                            notification_message = f'Ваш товар {product_name} снова в наличии! Цена не изменилась.{url}'
                        else:
                            diff = int(old_price) - int(new_price)
                            notification_message = (f'Ваш товар {product_name} снова в наличии и цена изменилась!\n'
                                                    f'Новая цена: {new_price} ({diff}).\n{url}')
                            print(int(old_price), '->', int(new_price))
                    elif new_price and int(new_price) != int(old_price):
                        diff = int(old_price) - int(new_price)
                        notification_message = (f'Цена на ваш товар {product_name} изменилась!\nНовая цена: {new_price}'
                                                f' ({diff}).\n{url}')
                        print(int(old_price), '->', int(new_price))
    
                    if notification_message:
                        notifications[user_id] = notification_message
    
        return notifications
    
    
    async def get_price(site_url, key, old_price):
        global driver, price, name_product
        driver = None
        try:
            driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
            driver.implicitly_wait(3)
            driver.get(site_url)
            if key in DOMAIN_SELECTOR_SOLD_OUT and driver.find_elements(By.CSS_SELECTOR, DOMAIN_SELECTOR_SOLD_OUT[key]):
                return old_price, 0
            else:
                price_elements = driver.find_elements(By.CSS_SELECTOR, DOMAIN_SELECTOR[key])
                if price_elements:
                    price = price_elements[0].text
                else:
                    price_elements = driver.find_elements(By.CSS_SELECTOR, DOMAIN_SELECTOR_ADD[key])
                    if price_elements:
                        price = price_elements[0].text
                    else:
                        print('Элемент с css-селекторами из базы данных на странице не найдены.')
                        return None, -1
                if price:
                    price = re.sub('[\u00A0|\u2009]', '', price)
                    price = re.sub('[A-Za-zА-Яа-я:]+', '', price)
                    price = re.sub('₽.*₽', '', price).strip()
                    price = re.sub('₽[0-9]+₽*', '', price).strip()
                    if price.endswith('₽'):
                        price = price[:-1]
                    clean_price = price.replace(' ', '')
                    return clean_price, 1
                else:
                    return None, -1
        except Exception as e:
            print(f'Произошла непредвиденная ошибка: {e}')
            return None, -1
        finally:
            if driver:
                driver.quit()
    Написано
  • Как запустить два параллельных процесса бота: бесконечную функцию, проверяющую имеются ли обновления, и обработчики событий и сообщений?

    AnastasiiaEg
    @AnastasiiaEg Автор вопроса
    можно об этом подробней "прикладывай код через специальный тег, сейчас он не читаемый"
    Написано
  • Как запустить два параллельных процесса бота: бесконечную функцию, проверяющую имеются ли обновления, и обработчики событий и сообщений?

    AnastasiiaEg
    @AnastasiiaEg Автор вопроса
    функция start_checking_price запускается, а вот polling, судя по всему, нет
    после выполнения check_and_update_prices() выходит такая шибка:
    Traceback (most recent call last):
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/session/aiohttp.py", line 170, in make_request
    async with session.post(
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiohttp/client.py", line 1194, in __aenter__
    self._resp = await self._coro
    ^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiohttp/client.py", line 504, in _request
    with timer:
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiohttp/helpers.py", line 735, in __exit__
    raise asyncio.TimeoutError from None
    TimeoutError

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File "/Users/anastasiaegorova/coding/website_prices_bot/bot.py", line 33, in main
    await dp.start_polling(my_bot)
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/dispatcher/dispatcher.py", line 551, in start_polling
    await asyncio.gather(*done)
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/dispatcher/dispatcher.py", line 340, in _polling
    user: User = await bot.me()
    ^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/bot.py", line 364, in me
    self._me = await self.get_me()
    ^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/bot.py", line 1823, in get_me
    return await self(call, request_timeout=request_timeout)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/bot.py", line 492, in __call__
    return await self.session(self, method, timeout=request_timeout)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/session/base.py", line 254, in __call__
    return cast(TelegramType, await middleware(bot, method))
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/session/aiohttp.py", line 175, in make_request
    raise TelegramNetworkError(method=method, message="Request timeout error")
    aiogram.exceptions.TelegramNetworkError: HTTP Client says - Request timeout error

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File "/Users/anastasiaegorova/coding/website_prices_bot/bot.py", line 44, in
    asyncio.run(main())
    File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
    ^^^^^^^^^^^^^^^^
    File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/base_events.py", line 664, in run_until_complete
    return future.result()
    ^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/bot.py", line 40, in main
    await my_bot.close()
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/bot.py", line 827, in close
    return await self(call, request_timeout=request_timeout)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/bot.py", line 492, in __call__
    return await self.session(self, method, timeout=request_timeout)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/session/base.py", line 254, in __call__
    return cast(TelegramType, await middleware(bot, method))
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/session/aiohttp.py", line 178, in make_request
    response = self.check_response(
    ^^^^^^^^^^^^^^^^^^^^
    File "/Users/anastasiaegorova/coding/website_prices_bot/.venv/lib/python3.12/site-packages/aiogram/client/session/base.py", line 120, in check_response
    raise TelegramBadRequest(method=method, message=description)
    aiogram.exceptions.TelegramBadRequest: Telegram server says - Bad Request: the bot has already been closed

    check_and_update_prices() выглядит так:
    async def check_and_update_prices():
        notifications = {}
        async with aiosqlite.connect('users_products.db') as conn:
            cursor = await conn.cursor()
            await cursor.execute("SELECT user_id, URL, product_name, current_price, product_availability FROM products_users")
            products = await cursor.fetchall()
    
            for user_id, url, product_name, old_price, old_availability in products:
                domain_url = urlparse(url).netloc
                new_price, availability = await get_price(url, domain_url, old_price)
                notification_message = None
    
                if availability != old_availability or (new_price and int(new_price) != int(old_price)):
                    await manipulation_db.add_or_update_product(user_id, url, product_name, new_price, availability)
    
                    if availability == 0:
                        notification_message = (f'Вашего товара {product_name} больше нет в наличии. Можете отписаться от '
                                                f'отслеживания или подождать наличия (я уведомлю об этом).{url}')
                    elif availability == 1 and new_price:
                        if int(new_price) == int(old_price):
                            notification_message = f'Ваш товар {product_name} снова в наличии! Цена не изменилась.{url}'
                        else:
                            diff = int(old_price) - int(new_price)
                            notification_message = (f'Ваш товар {product_name} снова в наличии и цена изменилась!\n'
                                                    f'Новая цена: {new_price} ({diff}).\n{url}')
                            print(int(old_price), '->', int(new_price))
                    elif new_price and int(new_price) != int(old_price):
                        diff = int(old_price) - int(new_price)
                        notification_message = (f'Цена на ваш товар {product_name} изменилась!\nНовая цена: {new_price}'
                                                f' ({diff}).\n{url}')
                        print(int(old_price), '->', int(new_price))
    
                    if notification_message:
                        notifications[user_id] = notification_message
    
        return notifications
    
    
    async def get_price(site_url, key, old_price):
        global driver, price, name_product
        driver = None
        try:
            driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
            driver.implicitly_wait(3)
            driver.get(site_url)
            if key in DOMAIN_SELECTOR_SOLD_OUT and driver.find_elements(By.CSS_SELECTOR, DOMAIN_SELECTOR_SOLD_OUT[key]):
                return old_price, 0
            else:
                price_elements = driver.find_elements(By.CSS_SELECTOR, DOMAIN_SELECTOR[key])
                if price_elements:
                    price = price_elements[0].text
                else:
                    price_elements = driver.find_elements(By.CSS_SELECTOR, DOMAIN_SELECTOR_ADD[key])
                    if price_elements:
                        price = price_elements[0].text
                    else:
                        print('Элемент с css-селекторами из базы данных на странице не найдены.')
                        return None, -1
                if price:
                    price = re.sub('[\u00A0|\u2009]', '', price)
                    price = re.sub('[A-Za-zА-Яа-я:]+', '', price)
                    price = re.sub('₽.*₽', '', price).strip()
                    price = re.sub('₽[0-9]+₽*', '', price).strip()
                    if price.endswith('₽'):
                        price = price[:-1]
                    clean_price = price.replace(' ', '')
                    return clean_price, 1
                else:
                    return None, -1
        except Exception as e:
            print(f'Произошла непредвиденная ошибка: {e}')
            return None, -1
        finally:
            if driver:
                driver.quit()
    Написано
  • Как запустить два параллельных процесса бота: бесконечную функцию, проверяющую имеются ли обновления, и обработчики событий и сообщений?

    AnastasiiaEg
    @AnastasiiaEg Автор вопроса
    не совсем то, что я хочу, но, наверно, если так ничего и не получится, решу таким путем
    Написано
  • Как запустить два параллельных процесса бота: бесконечную функцию, проверяющую имеются ли обновления, и обработчики событий и сообщений?

    AnastasiiaEg
    @AnastasiiaEg Автор вопроса
    с asyncio.gather пробовала, все равно не реагирует на команды
    async def start_checking_price(bot):
    while True:
    results = await price_check.check_and_update_prices()
    for user_id in results:
    try:
    await bot.send_message(user_id, results[user_id])
    except Exception as e:
    print(f"Не удалось отправить сообщение: {e}")
    await asyncio.sleep(1600)

    async def main():
    try:
    bot = Bot(token=TOKEN)
    dp = Dispatcher(storage=MemoryStorage())
    dp.include_router(router)

    task_polling = asyncio.create_task(dp.start_polling(bot))
    task_price_checking = asyncio.create_task(start_checking_price(bot))

    await asyncio.gather(task_polling, task_price_checking)

    except Exception as e:
    print(f"Ошибка при запуске: {e}")

    if __name__ == '__main__':
    try:
    asyncio.run(main())
    except KeyboardInterrupt:
    print('Бот выключен')
    except Exception as e:
    print(f"Ошибка во время работы: {e}")
    Написано