Как реализовать проход по конктерным состояниям, заданным например списком id_states = [1,2,7,10]
Понятно что можно проверять у каждого хендлера if state in id_states:
и впринципе это работает, только вот message.answer берется из текущего состояния, и при переходе на следущее состояние бот ждет ответа от пользователя, а мне нужно чтоб при переходе на нужное состояние, бот присылал описание действия пользователя.
Пример:
1,2 состояние всегда активны,
во 2 состоянии:
await message.answer('Отлично, мы находимся в каком-то подьезде')
await message.answer('сделайте фото 1') #действие для 3 состояния
await state.next() # т.е. переходим к 3 состоянию
в 3м состоянии проверяем if state in id_states:
если нет, то await state.next() # т.е. переходим к 4 состоянию
и.т.д
при переходе на состояние из списка, бот ждет действия от пользователя (т.е. загрузки фото), но пользователь то об этом не знает, у него в чате висит сообщение от бота: 'сделайте фото 1'
Всякий раз, когда ловишь себя на том, что пишешь вот такую фигню, создавай массив или словарь вместо отдельных переменных.
Тогда и вопрос прохода по номерам/ключам снимается.
HardX, ты это серьёзно сейчас? Уж списки-то ты должен знать, раз за ботов берёшься. photostates = [State() for i in range(10)]
И тогда можно работать с любым photostates[i].
Vindicar, или я что-то делаю не так, или не понял суть, но не происходит перехода по photostates[i], ни через States.next() ни через photostates[i].set()
import config
import logging
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher import FSMContext
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from data_base import sqllite_db
storage=MemoryStorage()
from All_executions_bot import FSMAdmin as All_executions
#ans_list = [0: Execution now, 1: Step_Back, 2: number of photo]
need_exec_id = 10 # Number of executions
nxt=1
exec_id = 0
logging.basicConfig(level=logging.INFO)
Bot = Bot(token=config.API_TOKEN)
dp = Dispatcher(Bot, storage=storage)
async def on_startup(_):
print('Бот онлайн')
sqllite_db.sql_start()
async def loading_photo(state,message,ans_list): #Функция загрузки фото с ТГ в БД
global i
global exec_id
global nxt
diction.update(await state.get_data())
if message.content_type == 'text':
if message.text == '0':
await All_executions.previous()
await message.answer(ans_list[1])
else:
await message.answer('Жду фото')
elif message.content_type == 'photo':
try:
if i in range(kolfoto):
diction.update({'photo_SET_'+ans_list[2]+'_'+str(i) : message.photo[i].file_id})
i+=1
await message.photo[-1].download(make_dirs=True, \
destination_file='photos/'+diction['adress']+'/'+diction['podiezd']+'/'+message.photo[i-1].file_id+'.jpg') # скачиваем фото в папку /photos
else:
return
except:
print('error loading part '+ans_list[2])
await message.answer('Что-то пошло не так, попробуйте заново.')
if i == kolfoto:
i=0
print(diction)
exec_id+=1
try:
await sqllite_db.sql_update_test(diction)
except:
print('изменения не внесены в БД')
if exec_id==need_exec_id:
await message.answer('Все фото для подъезда №'+diction['podiezd']+\
' загружены, введите номер подъезда или 0 чтобы ввести новый адрес')
if int(pod_load) in pod:
pod.remove(int(pod_load)) #Удаляем загруженный подьезд из списка
if len(pod)==0:
await message.answer('Все фото для адреса '+diction['adress']+' загружены.')
await state.finish()
adresses.remove(adr)
keyboard = types.InlineKeyboardMarkup()
if len(adresses)>0:
for j in range(len(adresses)):
keyboard.add(types.InlineKeyboardButton(text=adresses[j],\
callback_data=adresses[j])) #забиваем клавиатуру по списку из БД
await message.answer('Доступные адреса:',reply_markup=keyboard) #вывод клавиатуры с адресами
await All_executions.adress.set() #Запуск состояния ADRESS
await message.answer('Выберите адрес: \
(если адрес отсутствует, введите вручную)\nИли введите 0 чтобы выйти')
else:
await All_executions.podiezd.set()
await podiezd_input(message, state)
else:
await message.answer('Загрузите '+str(kolfoto)+' фото:\n'+ans_list[0])
diction.clear()
nxt+=1
#await All_executions.next()
await All_executions.photostate[nxt].set()
await load_photo(message,state)
else:
await message.answer('Неизвестный тип входных данных')
def norm_str(findadr): #Функция нормализации введенного вручную адреса задания
findadr = findadr.capitalize()
x = findadr.rfind(' ')
findadr=findadr[:findadr.index(' ')+1]+findadr[findadr.index(' ')+1].upper()+findadr[findadr.index(' ')+2:]
findadr=findadr[:findadr.rfind(' ')+1]+'ул, '+findadr[x+1:]
return findadr
diction ={}
kolfoto=sqllite_db.sql_start() #количество фото для загрузки
adress=[]
pod=[]
i=0
pod_load=0
id_adr=0
loginin=''
adr=''
@dp.message_handler(commands=['start'])
async def send_welcome(message: types.Message):
await message.answer(f'{message.from_user.first_name},\nВведите | Логин пароль | (через пробел) для начала работы')
#Создание инлайн клавиатуры
@dp.message_handler(state=None)
async def strt_bot(message: types.Message, state: FSMContext):
keyboard = types.InlineKeyboardMarkup()
if message.text != '0':
global loginin
loginin=message.text #ввод логин\пароль
await message.answer('логин = '+loginin)
if loginin in sqllite_db.login:
global adresses
adresses = await sqllite_db.sql_get_adresses(loginin,adress) #запрос в БД для получения списка адресов
if len(adresses)>0:
for j in range(len(adresses)):
keyboard.add(types.InlineKeyboardButton(text=adresses[j],\
callback_data=adresses[j])) #забиваем клавиатуру по списку из БД
await message.answer('Доступные адреса:',reply_markup=keyboard) #вывод клавиатуры с адресами
await All_executions.adress.set() #Запуск состояния ADRESS
await message.answer('Выберите адрес: (если адрес отсутствует, введите вручную)\nИли введите 0 чтобы выйти')
else:
await message.answer('Нет доступных адресов\nВведите | Логин пароль | (через пробел) для начала работы')
else:
await message.answer('Логин пароль = '+loginin+' не имеет задач')
else:
if message.text == '0':
await message.answer('Вы успешно вышли.\nВведите | Логин пароль | (через пробел) для начала работы')
#Нажата инлайн кнопка с адресом
@dp.callback_query_handler(text_startswith='', state=All_executions.adress)
async def button_click(call: types.CallbackQuery, state:FSMContext):
if call.data in adresses:
async with state.proxy() as data:
data['adress']= call.data
await call.message.answer(call.data)
await All_executions.podiezd.set() # Запуск состояния ввода номера подьезда
global adr
adr=call.data #переменная для передачи в запрос к бд
global pod
global id_adr
pod,id_adr = await sqllite_db.sql_get_kol_pod(adr,pod,id_adr) # получаем список подьездов к выбранному адресу и id адреса
await call.message.answer('Адрес найден: '+call.data)
await call.message.answer("Введите № подьезда, в который пойдете или 0 чтобы ввести адрес заново\nПо данному адресу расположено подьездов: "+str(len(pod)))
await call.answer()
else:
await call.answer('Нет в списке заданий')
All_executions.previous()
await call.answer()
#Адрес введен вручную
@dp.message_handler(state=All_executions.adress)
async def hand_input(message: types.Message, state:FSMContext):
async with state.proxy() as data:
if data :
if message.text == '0':
await state.finish()
await strt_bot(message, state)
else:
try:
findadr = message.text
findadr = norm_str(findadr)
except:
findadr = message.text
if findadr in adresses: #Поиск в списке адресов
await message.answer('Адрес найден: '+findadr)
data['adress'] = findadr
await All_executions.podiezd.set()
global adr
global id_adr
adr=findadr #переменная для передачи в запрос к бд
global pod
pod,id_adr = await sqllite_db.sql_get_kol_pod(adr,pod,id_adr) # получаем список подьездов к выбранному адресу
await message.answer("Введите № подьезда, в который пойдете или 0 чтобы ввести адрес заново\nПо данному адресу расположено подьездов: "+str(len(pod)))
else:
await message.answer('Адрес не найден, введите адрес или введите 0 чтобы выйти')
#Ввод номера подьезда
@dp.message_handler(content_types=['photo','text'],state=All_executions.podiezd)
async def podiezd_input(message: types.Message, state:FSMContext):
if len(pod)>0:
if message.content_type == 'text':
if message.text.isdigit() == True:
if int(message.text) in pod:
await All_executions.podiezd.set()
async with state.proxy() as data:
data['podiezd']= message.text
global pod_load
pod_load=message.text
await All_executions.next()
await message.answer("Отлично, мы в подьезде № "+message.text+" \nПо адресу "+data['adress'])
await message.answer('Сделайте '+str(kolfoto)+' фотографии:\n1.Фото объекта (общий вид на здание)')
elif message.text == '0':
await All_executions.previous()
keyboard = types.InlineKeyboardMarkup()
if len(adresses)>0:
for j in range(len(adresses)):
keyboard.add(types.InlineKeyboardButton(text=adresses[j],\
callback_data=adresses[j])) #забиваем клавиатуру по списку из БД
await message.answer('Доступные адреса:',reply_markup=keyboard) #вывод клавиатуры с адресами
await All_executions.adress.set() #Запуск состояния ADRESS
await message.answer('Выберите адрес: \
(если адрес отсутствует, введите вручную)\nИли введите 0 чтобы выйти')
else:
await message.answer('Нет доступных адресов\nВведите новый логин/пароль')
else:
await message.answer('нет такого подьезда, введите номер подьезда')
else:
await message.answer('Только цифры')
elif message.content_type == 'photo':
await message.answer('Введите номер подьезда')
return
else:
await strt_bot(loginin, state)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[1])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['2.Неисправность выступающих конструкций: балконов,козырьков,эркеров,карнизов и т.п.','Введите № подьезда, в который пойдете или 0 чтобы ввести адрес заново','1']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[2])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['3.Повреждение целостности ступеней,перил,пандусов и дверей входной группы','1.Фото объекта (общий вид на здание)','2']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[3])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['4.Повреждение целостности/отсутствие светильников, лампочек на входной группе','2.Неисправность выступающих конструкций: балконов,козырьков,эркеров,карнизов и т.п.','3']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[4])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['5.Повреждение целостности/отсутствие светильников, лампочек в МОП','3.Повреждение целостности ступеней,перил,пандусов и дверей входной группы','4']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[5])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['6.Поверхности пола и стен в местах общего пользования подъездов','4.Повреждение целостности/отсутствие светильников, лампочек на входной группе','5']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[6])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['7.Неисправность конструкций внутренних стен, нарушение окрасочного покрытия стен, потолков подъезда','5.Повреждение целостности/отсутствие светильников, лампочек в МОП','6']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[7])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['8.Неисправности дверей в местах общего пользования (в т.ч. отсутствие пружин, доводчиков)','6.Поверхности пола и стен в местах общего пользования подъездов','7']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[8])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['9.Двери выхода на чердак/кровлю закрыты (отсутствует несанкционированный доступ на чердак/кровлю)','7.Неисправность конструкций внутренних стен, нарушение окрасочного покрытия стен, потолков подъезда','8']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[9])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['10.Акт осмотра выхода на чердак/кровлю на предмет отсутствия несанкционированного доступа составлен','8.Неисправности дверей в местах общего пользования (в т.ч. отсутствие пружин, доводчиков)','9']
await loading_photo(state,message,ans_list)
# Загрузка коллекции фото
@dp.message_handler(content_types=['photo','text'], state=All_executions.photostate[10])
async def load_photo(message: types.Message, state:FSMContext):
ans_list=['Все готово','9.Двери выхода на чердак/кровлю закрыты (отсутствует несанкционированный доступ на чердак/кровлю)','10']
await loading_photo(state,message,ans_list)
#Запуск Бота
executor.start_polling(dp, skip_updates=True, on_startup=on_startup)
HardX, нууу, в этой простыне я копаться не буду. Я даже не вижу где ты создаёшь photostate[].
А вообще неплохо бы понять - последовательность состояний одна и та же для всех пользователей, или своя для каждого? Насколько часто она меняется?
Может, тебе проще отказаться от использования встроенной машины состояний, и реализовать её самостоятельно?
Vindicar, последовательность формируется при запуске бота, для всех пользователей последовательность одна, меняется последовательность при перезапуске бота. (грубо - есть список нужных последовательностей, запустил бота, отработал, отключил бота)
Насколько я понял, photostates = [State() for i in range(10)] состояния вообще не создаются, по крайней мере через print(FSMAdmin.states) я вижу только address и podiezd как состояния
photostates = [State() for i in range(10)]
sequence = [1, 2, 7, 9, 10] # отсчёт состояний с 1. Можно переделать на отсчёт с 0.
def get_next_state(current_state: int) -> State:
try: # определяем где мы в последовательности
idx = sequence.index(current_state)
if idx == len(sequence) - 1:
idx = -1 # в начало
raise ValueError: # нас там нет, этот шаг должен быть пропущен
idx = -1
return photostates[sequence[idx+1] - 1] # если отсчёт номеров состояний с 0, то - 1 нужно убрать.
И тогда каждая функция должна знать свой номер current_state, вызвать get_next_state() чтобы получить следующее состояние, а потом уже это состояние активировать.
Как-то так.