aiogram icon indicating copy to clipboard operation
aiogram copied to clipboard

FSM Context States are not working when forwarding multiple answers at once

Open HadiH2o opened this issue 2 years ago • 3 comments

Checklist

  • [X] I am sure the error is coming from aiogram code
  • [X] I have searched in the issue tracker for similar bug reports, including closed ones

Context

like i said in the title when you forward multiple messages at once to complete multiple states it stuck in the first state

Imagine we have 3 States(). first_name, last_name, age when you send /start to the bot, the bot asks you to send your first_name and when you gave the answer it asks for your last_name and when you answered that too it askes for your age

now imagine you started the bot and then forwarded your first_name, last_name, age at once the bot should take each message for each state but it takes all of those answers (first_name, last_name, age) as first_name and asks for your last name 3 times.

image

Expected behavior

The bot should take each message for each state

Current behavior

the bot take the first message and get stucked at that state and The bot accept the first message as

Steps to reproduce

well unfortunately i dont know how to fix it

Code example

import asyncio
import logging

from aiogram import Bot, Router, Dispatcher
from aiogram.dispatcher.fsm.context import FSMContext
from aiogram.dispatcher.fsm.state import StatesGroup, State
from aiogram.types import Message

bot = Bot('token')

router = Router()


class States(StatesGroup):
    first_name = State()
    last_name = State()
    age = State()


@router.message(commands=['start'])
async def start(message: Message, state: FSMContext):
    await message.answer('send your first name')
    await state.set_state(States.first_name)


@router.message(state=States.first_name)
async def first_name(message: Message, state: FSMContext):
    await message.answer("send your last name")
    await state.update_data(first_name=message.text)
    await state.set_state(States.last_name)


@router.message(state=States.last_name)
async def last_name(message: Message, state: FSMContext):
    await message.answer("send your age")
    await state.update_data(last_name=message.text)
    await state.set_state(States.age)


@router.message(state=States.age)
async def age(message: Message, state: FSMContext):
    await state.update_data(age=message.text)
    await state.clear()
    await message.answer("done :check:")


async def main() -> None:
    logging.basicConfig(filename='logs.log', filemode='w', level=logging.DEBUG,
                        format="%(asctime)s %(name)-30s %(levelname)-8s %(message)s",
                        datefmt='%Y-%m-%d %H:%M:%S')

    dp = Dispatcher()
    dp.include_router(router)

    await dp.start_polling(bot, allowed_updates=["message"])


if __name__ == "__main__":
    print("started")
    asyncio.run(main())

Logs

No response

Additional information

i tested it in private chats and supergroups and the problem was there

HadiH2o avatar Jul 09 '22 19:07 HadiH2o

You're working with async application. It's not protected from conditions race by default.

While your code is awaiting for set_state operation, aiogram starts processing the next update. This is default asyncio behaviour. If you wanna change this behaviour - create middleware that locks processing for every user.

Olegt0rr avatar Jul 10 '22 07:07 Olegt0rr

Maybe we should add a built-in optional flag for sequential processing of updates? /cc @JrooTJunior

evgfilim1 avatar Jul 12 '22 09:07 evgfilim1

Flag may add performance issues. Per-user locking middleware is enough

Olegt0rr avatar Jul 12 '22 09:07 Olegt0rr

Use events isolation mode in v3

JrooTJunior avatar Aug 04 '23 18:08 JrooTJunior