Помогите с реализацией телеграм бота-парсера на aiogram

Всем привет! Я начал писать телеграм бота - парсера на пайтоне с использованием aiogram3. Суть ботак такова: юзер нажимает на кнопку и вводит ключевые слова. Дальше бот распознает всё это(потому что в будущем там будет не 1 сайт) и запускает функцию - парсер. Эта функция парсит, и после заврешения отправляет екзел файл в чат. И всё кажется легко, но осталось только реализация комнады /stop_pars, её суть в том, что когда ее вводят в чат, парсинг останавливается, и коректно завершает свою работу с дальнейшей отправкой екзел файла. Я попробовал сделать глобальные флажки, но когда работает парсер, он перекрывает основной поток, и /stop_pars не работает и не принимает значение от юзера, и вообще любая команда не работает. Помогите пожалуйста, как лечить то это? Сегодня весь день за кодом просидел, так ничего и не получилось. вот код:

from aiogram import F, Bot, Dispatcher, types
import asyncio
from aiogram.filters import CommandStart, Command
from config import TOKEN
from aiogram.types import CallbackQuery, Message
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
import requests
from bs4 import BeautifulSoup
from openpyxl import Workbook
from aiogram.types import FSInputFile


# вы можете добавить свой токен в config.py. Сделанно это для безопасности
bot = Bot(token=TOKEN)
dp = Dispatcher()

# ответ для команда /help
helping = 'Вот команды которые может выполнять бот? \n/start-Запуск бота и выбор парсингов \n/pars-Выбор сервиса который будем парсить \n/stop_pars-Остановка парсинга и отправка exel файла \n/help-Вызвать это меню \n Рекомендации по сайтам: \n ЭТПГПБ: сайт не даёт нормально парсить по запросу где ключевых слов больше двух. Для достижения лучшего результата лучше всего использовать 1 ключевое слово'

#### клавиатура бота ####
keyboard_23buttons = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text='1)Росэлторг', callback_data='button_1'), InlineKeyboardButton(
        text='2)Сбер АСТ', callback_data='button_2'), InlineKeyboardButton(text='3)УТП Сбер АСТ', callback_data='button_3')],
    [InlineKeyboardButton(text='4)Фабрикант', callback_data='button_4'), InlineKeyboardButton(
        text='5)ЭТП РАД', callback_data='button_5'), InlineKeyboardButton(text='6)ТЭК торг', callback_data='button_6')],
    [InlineKeyboardButton(text='7)ЭТПГПБ', callback_data='button_7'), InlineKeyboardButton(
        text='8)Тендерплан', callback_data='button_8'), InlineKeyboardButton(text='9)Ростендер', callback_data='button_9')],
    [InlineKeyboardButton(text='10)Закупки360', callback_data='button_10'), InlineKeyboardButton(
        text='11)Синапс', callback_data='button_11'), InlineKeyboardButton(text='12)Контур', callback_data='button_12')],
    [InlineKeyboardButton(text='13)Стар про', callback_data='button_13'), InlineKeyboardButton(
        text='14)Bicotender', callback_data='button_14'), InlineKeyboardButton(text='15)Tenderguru', callback_data='button_15')],
    [InlineKeyboardButton(text='16)Myseldon', callback_data='button_16'), InlineKeyboardButton(
        text='17)CБИС', callback_data='button_17'), InlineKeyboardButton(text='18)ЕИС закупки', callback_data='button_18')],
    [InlineKeyboardButton(text='19)РТС тендер', callback_data='button_19'), InlineKeyboardButton(
        text='20)B2B center', callback_data='button_20'), InlineKeyboardButton(text='21)Workspace', callback_data='button_21')],
    [InlineKeyboardButton(text='22)Заказ РФ «Татарстан»', callback_data='button_22'), InlineKeyboardButton(
        text='23)Контур закупки', callback_data='button_23')]
])

###### функции парсеров ######
is_parsing = 0
name_file = None


async def exel_to_you(message: types.Message, file_path):
    global name_file
    while True:
        if name_file is not None:
            file = FSInputFile(file_path)
            # file_path = 'gos_etpgpb_data.xlsx'...
            await bot.send_document(chat_id=message.chat.id, document=file)
            break  # Выход из цикла после отправки
        else:
            pass


# Парсим сайт https://gos.etpgpb.ru/
async def gos_etpgb(bot: Bot, message: types.Message):
    global is_parsing
    global name_file
    headers = {
        "User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Mobile Safari/537.36"
    }

    wb = Workbook()
    ws = wb.active  # async def gos_etpgb(bot: Bot, message: types.Message):

    with open("txt/etpgpb.txt", "r", encoding="utf-8") as file:  # windows-1251
        ws.append(["Ключевое слово", "Наименование закупки", "Ссылка", "Статус",
                   "Прием заявок до", "Регион", "НМЦ", "Тип", "Дата публикации"])
        words = file.readlines()
        for word in words:
            page_number = 0
            word = word.strip()
            if not word:
                continue

            # Отправка запроса на сайт с использованием слова
            for i in range(1, 11):
                page_number += 1
                if is_parsing > 0:
                    print('dsf')
                    break
                else:
                    pass
                try:
                    gosetgb = requests.get(
                        f"https://gos.etpgpb.ru/44/catalog/procedure?q={word}&simple-search=%D0%9D%D0%B0%D0%B9%D1%82%D0%B8&page={page_number}&limit=100", headers=headers).text
                    soup = BeautifulSoup(gosetgb, "lxml")

                    links = soup.find_all(
                        "td", class_="row-procedure_name")  # Парсим ссылки
                    statuses = soup.find_all(
                        "td", class_="row-status")  # Парсим статус
                    # Парсим "Приём заявок до"
                    zayavs = soup.find_all(
                        "td", class_="row-request_end_give_datetime sortable")
                    regions = soup.find_all(
                        "td", class_="row-placer_region_id")  # Парсим регионы
                    nmcs = soup.find_all(
                        "td", class_="row-contract_start_price sortable")  # Парсим НМЦ
                    types = soup.find_all(
                        "span", class_="label label-info")  # Парсим типы
                    publication_date = soup.find_all(
                        "td", class_="row-publication_datetime sortable")  # Парсим дату публикации

                    for link, status, zayav, region, nmc, type_, date in zip(links, statuses, zayavs, regions, nmcs, types, publication_date):
                        good_name = link.find("a", href=True)
                        part_of_link = good_name["href"]
                        full_link = "https://gos.etpgpb.ru" + part_of_link
                        good_name = good_name.text.strip()
                        status_text = status.text.strip()
                        zayav_text = zayav.text.strip()
                        region_text = region.text.strip()
                        nmc_text = nmc.text.strip()
                        type_text = type_.text.strip()
                        date_text = date.text.strip()
                    # Записываем строку в Excel
                        ws.append([word, good_name, full_link, status_text,
                                   zayav_text, region_text, nmc_text, type_text, date_text])
                    print(
                        f"{word} | Успешно спарсил {page_number} страницу с сайта https://gos.etpgpb.ru/")
                    name_file = "gos_etpgpb_data.xlsx"
                    wb.save(f'gos_etpgpb_data.xlsx')

                    print(f"{word} | Спарсил данные, сохраняю в файл: {name_file}")
                    file_path = "gos_etpgpb_data.xlsx"

                except Exception as e:
                    print(
                        f"Ошибка при парсинге страницы {page_number} для слова '{word}': {e}")
                    continue
    await exel_to_you(message, file_path)

#### функции обработки команд ####


@dp.message(CommandStart())  # команда старт
async def cmd_start(message: Message):
    await message.answer('Привет! Что будем парсить? Для выбора введи /pars, или /help для вызова меню бота')


@dp.message(Command('help'))  # команда хелп
async def help(message: Message):
    await message.answer(helping)


@dp.message(Command('pars'))  # команда парс, выбор кнопок
async def pars(message: Message):
    await message.answer('Выберите сайт для парса:', reply_markup=keyboard_23buttons)
######## Обработка парсов и начало парсинга###########


@dp.callback_query(F.data == 'button_7')
async def twenty_seven(callback: CallbackQuery):
    await callback.answer('Введите ключи')
    await callback.message.answer('Пожалуйста, введите ключи. Например - /keys_1 молоко, масло, кефир(парс сайта Росэлторг)')


@dp.message(Command("keys_7"))
async def process_keys(message: types.Message):
    keys = message.text.strip()  # Получаем текст от пользователя
    with open("txt/etpgpb.txt", "w", encoding="utf-8") as words_file:  # Используем контекстный менеджер
        words_file.write(keys)
    with open("txt/etpgpb.txt", "r", encoding="utf-8") as file:
        content = file.read()
    with open("txt/etpgpb.txt", "w", encoding="utf-8") as file:
        file.write(" ".join(content.split()[1:]))
    await message.answer("Парсинг начат!")
    global is_parsing
    is_parsing = 0
    await gos_etpgb(bot, message)


@dp.callback_query(F.data == 'button_1')
async def twenty_one(callback: CallbackQuery):
    await callback.answer('1')
    await callback.message.answer('негры все пидорасы ебаные блять')


@dp.message(Command("keys_1"))
async def process_keys(message: types.Message):
    await message.answer("Парсинг кал!")

    ######## оастановка парсинга#######


@dp.message(Command('stop_pars'))
async def stop_pars(message: Message):
    global is_parsing
    if message.text:
        print("123")
        await message.answer("Парсинг завершен")
        is_parsing += 1
# await message.answer_document(file)


async def main():
    await dp.start_polling(bot)

if __name__ == '__main__':
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print('bot finished work')

    asyncio.run(main())

Ответы (0 шт):