DKay
DKay

Reputation: 121

Aiogram -- set state for a exact user

I'm writing a bot in python and aiogram. The point is that the administrator accepts (or rejects) user requests. Accordingly, when the administrator clicks on the button in his chat, I need to change the user's state (his uid is known). I didn't find how to do it anywhere.

I'm looking for something like

dp.set_state(uid, User.accepted))

Thanks!

Upvotes: 3

Views: 8429

Answers (4)

Vlad Ponomarev
Vlad Ponomarev

Reputation: 39

For new Aiogram 3.1.0

As Aiogram 3.1.0 has slightly different methods of accessing storage and context, I would like to share what I came up with:

from aiogram.fsm.storage.base import StorageKey
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext


class UserState(StatesGroup):
    # any other states
    EXPIRED = State()


bot = Bot("your_token", parse_mode=ParseMode.HTML)
storage = MemoryStorage()  # for clarity it is created explicitly
dp = Dispatcher(storage=storage)
# here goes the desired userId and chatId (they are same in private chat)
new_user_storage_key = StorageKey(bot.id, user_id, user_id)
new_user_context = FSMContext(storage=storage, key=new_user_storage_key)
await new_user_context.set_state(UserState.EXPIRED)  # your specified state

User's own storage (MemoryRecord) is now accessed by StorageKey instead of just user_id, that's why it is necessary to create it first, after that fill it with values (IDs) and then pass it to the FSMcontext. After the context is created, calling set_state(...) and set_data(...) will set state/data specifically for the desired user_id.

Actually there is no need to create a storage explicitly as shown in the previous code snippet, as it will be created automatically, so you can just access it by dispatcher.storage property like here:

# the same imports
# the same UserState class

bot = Bot("your_token", parse_mode=ParseMode.HTML)
dp = Dispatcher()  # we don't create storage manually this time
# here goes the desired userId and chatId (they are same in private chat)
new_user_storage_key = StorageKey(bot.id, user_id, user_id)
new_user_context = FSMContext(storage=dp.storage, key=new_user_storage_key)
await new_user_context.set_state(UserState.EXPIRED)  # your specified state

Hope it helps! :)

Upvotes: 3

Anvarjon Khojimatov
Anvarjon Khojimatov

Reputation: 1

state = dp.current_state(chat=chat_id, user=user_id) 
await state.set_state(User.accepted)

dp - an object of the Dispatcher class
chat_id - chat id, which must be equal to the user id if this is a correspondence with a user
User.accepted - the state we want to bring the user to

Upvotes: 0

Hamed
Hamed

Reputation: 143

from aiogram.dispatcher import FSMContext

@dp.message_handler(state='*')
async def example_handler(message: types.Message, state: FSMContext):
    new_state = FSMContext(storage, chat_id, user_id)

then set_state() and set_data() on the new_state.

storage is the FSM storage.

Upvotes: 3

I had the same problem

Found method set() in base class State:

class State:
    ...
    async def set(self):
        state = Dispatcher.get_current().current_state()
        await state.set_state(self.state)

So I created new class from State and overrode method this way:

    async def set(self, user=None):
        """Option to set state for concrete user"""
        state = Dispatcher.get_current().current_state(user=user)
        await state.set_state(self.state)

Usage:

@dp.message_handler(state='*')
async def example_handler(message: Message):
    await SomeStateGroup.SomeState.set(user=message.from_user.id)

If you want some mailing stuff, collect user ids and use that hint.

Upvotes: 2

Related Questions