Как сделать чтобы цикл while true не прерывал работу всего бота aiogram

Рейтинг: 0Ответов: 4Опубликовано: 08.03.2023
from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
from aiogram.utils import executor
import parseLevel
import parseAct
import datetime
import asyncio
import threading

BOT_TOKEN = 'TOKEN'
import time

counter_act = 0

counter1 = 0
counter2 = 0
counter3 = 0
counter4 = 0
counter5 = 0
parserLvl = parseLevel.WordsParser()

count_stop = 0
now = datetime.datetime.now()

tim = f'{now.hour}:{now.minute}'

bot = Bot(BOT_TOKEN)
dp = Dispatcher(bot)

CHANGE_KEYBOARD = ReplyKeyboardMarkup(resize_keyboard=True).add(
    KeyboardButton("/Змінити рівень або сферу діяльності")
)

LEVEL_KEYBOARD = ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True).add(
    KeyboardButton('/level A1'),
    KeyboardButton('/level A2'),
    KeyboardButton('/level B1'),
    KeyboardButton('/level B2'),
    KeyboardButton('/level C1/2'),
)

ACTIVITY_KEYBOARD = ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True).add(
    KeyboardButton('/Сфера медицини'),
    KeyboardButton('/Сфера ІТ'),
    KeyboardButton('/Сфера економіки'),
    KeyboardButton('/Сфера політики'),
    KeyboardButton('/Сфера мистецтво')
)

MAIN_KEYBOARD = ReplyKeyboardMarkup(resize_keyboard=True).add(
    KeyboardButton('/0брати за рівнем'),
    KeyboardButton('/Обрати за сферою діяльності')
)


@dp.message_handler(commands='start')
async def start_handler(message: types.Message):
    global count_stop
    await bot.send_message(message.from_user.id,
                           "Привіт, це бот для вивчення аглійських слів, тут можно обрати свою сферу або свій рівень англійскої мови(А1,В2.....). Обери нижче! \n Після того як оберете тип слів що вам треба вивчати вам буде приходити 5 слів кожний день о 12:00 день.",
                           reply_markup=MAIN_KEYBOARD)
    count_stop+=1


@dp.message_handler(commands='0брати')
async def choose_level_handler(message: types.Message):
    await bot.send_message(message.from_user.id, "Оберіть рівень.", reply_markup=LEVEL_KEYBOARD)


@dp.message_handler(commands='Обрати')
async def choose_activity_handler(message: types.Message):
    await bot.send_message(message.from_user.id, "Оберіть свою сферу", reply_markup=ACTIVITY_KEYBOARD)

@dp.message_handler(commands='Змінити')
async def stop(message: types.Message):
    await bot.send_message(message.from_user.id,
                           "Привіт, це бот для вивчення аглійських слів, тут можно обрати свою сферу або свій рівень англійскої мови(А1,В2.....). Обери нижче! \n Після того як оберете тип слів що вам треба вивчати вам буде приходити 5 слів кожний день о 12:00 день.",
                           reply_markup=MAIN_KEYBOARD)



@dp.message_handler(commands='level')
async def level_handler(message: types.Message):
    level = message.text.split(' ')[1]  # Получаем уровень из текста сообщения


    if level == 'A1':
        a1_thread = threading.Thread(target=a1(message))
        a1_thread.start()
        a1_thread.join()
    elif level == 'A2':
        a2_thread = threading.Thread(target=a2(message))
        a2_thread.start()
        a2_thread.join()
    elif level == 'B1':
        b1_thread = threading.Thread(target=b1(message))
        b1_thread.start()
        b1_thread.join()
    elif level == 'B2':
        b2_thread = threading.Thread(target=b2(message))
        b2_thread.start()
        b2_thread.join()
    elif level == 'C1/2':
        c1_thread = threading.Thread(target=c1(message))
        c1_thread.start()
        c1_thread.join()

    await bot.send_message(message.from_user.id,
                           f"Ви обрали рівень {level}, тепер вам буде приходить кожен день по 5 слів рівня{level}",
                           reply_markup=CHANGE_KEYBOARD)


@dp.message_handler(commands='Сфера')
async def act_handler(message: types.Message):
    act = message.text.split(' ')[1]  # Получаем сферу из текста сообщения
    global counter_act

    sfera(message, act)
    await bot.send_message(message.from_user.id,
                               f"Ви обрали сферу {act}, тепер вам буде приходить кожен день по 5 слів зі сфери {act}",
                               reply_markup=CHANGE_KEYBOARD)



def sfera(message : types.Message, act):
    global count_stop
    global counter1
    list = []
    if act == 'ІТ':
        obj = parseAct.ParseAct()
        list = obj.parse_IT()
    if act == 'медицини':
        obj = parseAct.ParseAct()
        list = obj.parse_med()
    if act == 'економіки':
        obj = parseAct.ParseAct()
        list = obj.parse_economic()
    if act == 'політики':
        obj = parseAct.ParseAct()
        list = obj.parse_politice()
    if act == 'мистецтво':
        obj = parseAct.ParseAct()
        list = obj.parse_art()
    while True:
        now = datetime.datetime.now()
        tim = now.strftime('%H:%M')

        if tim == '12:00':
            if counter1+5 <= len(list):
                bot.send_message(message.from_user.id, list[counter1])
                bot.send_message(message.from_user.id, list[counter1 + 1])
                bot.send_message(message.from_user.id, list[counter1 + 2])
                bot.send_message(message.from_user.id, list[counter1 + 3])
                bot.send_message(message.from_user.id, list[counter1 + 4])
                time.sleep(60)
                counter1 += 5
        if message.text == '/Змінити рівень або сферу діяльності':
            print(1)
            break


def a1(message: types.Message):
    global count_stop
    global counter1
    list = parserLvl.parse_words('A1')
    while True:
        now = datetime.datetime.now()
        tim = now.strftime('%H:%M')
        print(1)
        if tim == '20:59':
            bot.send_message(message.from_user.id, list[counter1])
            bot.send_message(message.from_user.id, list[counter1 + 1])
            bot.send_message(message.from_user.id, list[counter1 + 2])
            bot.send_message(message.from_user.id, list[counter1 + 3])
            bot.send_message(message.from_user.id, list[counter1 + 4])
            time.sleep(60)
            counter1 += 5
        if message.text == '/Змінити рівень або сферу діяльності':
            print(2)
            break


def a2(message: types.Message):
    global count_stop
    global counter2
    list = parserLvl.parse_words('A2')
    while True:
        now = datetime.datetime.now()
        tim = now.strftime('%H:%M')
        if tim == '12:00':
            bot.send_message(message.from_user.id, list[counter2])
            bot.send_message(message.from_user.id, list[counter2 + 1])
            bot.send_message(message.from_user.id, list[counter2 + 2])
            bot.send_message(message.from_user.id, list[counter2 + 3])
            bot.send_message(message.from_user.id, list[counter2 + 4])
            time.sleep(60)
            counter2 += 5
        if message.text == '/Змінити рівень або сферу діяльності':
            print(1)
            break


def b1(message: types.Message):
    global count_stop
    global counter1
    list = parserLvl.parse_words('B1')
    while True:
        now = datetime.datetime.now()
        tim = now.strftime('%H:%M')
        if tim == '12:00':
            bot.send_message(message.from_user.id, list[counter3])
            bot.send_message(message.from_user.id, list[counter3 + 1])
            bot.send_message(message.from_user.id, list[counter3 + 2])
            bot.send_message(message.from_user.id, list[counter3 + 3])
            bot.send_message(message.from_user.id, list[counter3 + 4])
            time.sleep(60)
            counter1 += 5
        if message.text == '/Змінити рівень або сферу діяльності':
            print(1)
            break


def b2(message: types.Message):
    global count_stop
    global counter4
    list = parserLvl.parse_words('B2')
    while True:
        now = datetime.datetime.now()
        tim = now.strftime('%H:%M')
        if tim == '12:00':
            bot.send_message(message.from_user.id, list[counter4])
            bot.send_message(message.from_user.id, list[counter4 + 1])
            bot.send_message(message.from_user.id, list[counter4 + 2])
            bot.send_message(message.from_user.id, list[counter4 + 3])
            bot.send_message(message.from_user.id, list[counter4 + 4])
            time.sleep(60)
            counter4 += 5
        if message.text == '/Змінити рівень або сферу діяльності':
            print(1)
            break


def c1(message: types.Message):
    global count_stop
    global counter5
    list = parserLvl.parse_words('C1')
    while True:
        now = datetime.datetime.now()
        tim = now.strftime('%H:%M')
        if tim == '12:00':
            bot.send_message(message.from_user.id, list[counter5])
            bot.send_message(message.from_user.id, list[counter5 + 1])
            bot.send_message(message.from_user.id, list[counter5 + 2])
            bot.send_message(message.from_user.id, list[counter5 + 3])
            bot.send_message(message.from_user.id, list[counter5 + 4])
            time.sleep(60)
            counter5 += 5
        if message.text == '/Змінити рівень або сферу діяльності':
            print(1)
            break



executor.start_polling(dp, skip_updates=True)

Вот весь код. Помогите решить проблемы. Пытался использовать asyncio и threading. Буду очень рад!

Ответы

▲ 2

Вместо

time.sleep(60)

Напиши

await asyncio.sleep(60)

Также для этого импортитруйте asyncio в начале файла

import asyncio

Ну и нужно чтобы функции в которых ты это используешь также были асинхронными

async def test()
▲ 1

На мой взгляд лучшим решением будет открытие while True в новом потоке, вот пример:

from threading import Thread
from time import sleep

def asyncfunciton():
    while True:
        sleep(100)
th = Thread(target=asyncfunction).start()
#продолжение выполнения другого кода

все что находиться в функции asyncfunction будет выполнено в новом потоке, и никак не будет влиять на основной код

▲ 0

Ваша функция level_handler() создает и запускает новый поток выполнения каждый раз при получении сообщения с командой /level, и в нем вызывается одна из функций a1(), a2(), b1(), b2() или c1(). В каждой из этих функций выполняется бесконечный цикл while True, который никогда не завершается, а значит, и поток выполнения никогда не завершится.

Проблема здесь не в цикле while True, а в том, что вы создаете и запускаете новый поток выполнения каждый раз при получении команды /level, что может привести к накоплению большого количества незавершенных потоков, и ваша программа будет потреблять большое количество ресурсов.

Чтобы избежать этой проблемы, вы можете использовать asyncio и создать асинхронную функцию, которая будет выполняться в бесконечном цикле с помощью asyncio.sleep() и обрабатывать все сообщения, которые приходят от пользователей. Вы можете использовать декоратор @dp.message_handler() для регистрации этой функции как обработчика сообщений.

Вот пример кода, который показывает, как вы можете изменить вашу функцию

level_handler() для использования asyncio

async def level_handler(message: types.Message):
    level = message.text.split(' ')[1]  # Получаем уровень из текста сообщения

    await bot.send_message(message.from_user.id,
                           f"Ви обрали рівень {level}, тепер вам буде приходить кожен день по 5 слів рівня{level}",
                           reply_markup=CHANGE_KEYBOARD)

    while True:
        # Ваш код для обработки сообщений
        await asyncio.sleep(1)  # Подождать 1 секунду перед следующей итерацией цикла

Замените вызовы a1(), a2(), b1(), b2() и c1() на соответствующий код для обработки сообщений в вашем цикле while True.

▲ 0

Моя реализация в aiogram3

import asyncio

from aiogram import Bot, Dispatcher
from aiogram.client.bot import DefaultBotProperties

from handlers import user_commands
from config_reader import config


bot = Bot(config.bot_token, default=DefaultBotProperties(parse_mode='HTML'))


dp = Dispatcher()
dp.include_routers(
    user_commands.router
)

async def periodic_send():
        await bot.send_message(chat_id='XXXXX', text='123')


async def main(bot: Bot):
    while True:
        try:

            updates = await bot.get_updates()
            for update in updates:
                await dp._process_update(update=update, bot=bot)
            await bot.delete_webhook(drop_pending_updates=True)
            await periodic_send()
            await asyncio.sleep(1)
        except Exception as e:
            print(f"Ошибка: {e}")
            await asyncio.sleep(5)

if __name__ == '__main__':
    print('bot starting now')
    asyncio.run(main(bot))