mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
More docs
This commit is contained in:
parent
3931253a88
commit
f97367b3ee
16 changed files with 415 additions and 100 deletions
|
|
@ -20,28 +20,76 @@ class StorageKey:
|
|||
|
||||
|
||||
class BaseStorage(ABC):
|
||||
"""
|
||||
Base class for all FSM storages
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
@asynccontextmanager
|
||||
async def lock(self, bot: Bot, key: StorageKey) -> AsyncGenerator[None, None]:
|
||||
"""
|
||||
Isolate events with lock.
|
||||
Will be used as context manager
|
||||
|
||||
:param bot: instance of the current bot
|
||||
:param key: storage key
|
||||
:return: An async generator
|
||||
"""
|
||||
yield None
|
||||
|
||||
@abstractmethod
|
||||
async def set_state(self, bot: Bot, key: StorageKey, state: StateType = None) -> None:
|
||||
"""
|
||||
Set state for specified key
|
||||
|
||||
:param bot: instance of the current bot
|
||||
:param key: storage key
|
||||
:param state: new state
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_state(self, bot: Bot, key: StorageKey) -> Optional[str]:
|
||||
"""
|
||||
Get key state
|
||||
|
||||
:param bot: instance of the current bot
|
||||
:param key: storage key
|
||||
:return: current state
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def set_data(self, bot: Bot, key: StorageKey, data: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Write data (replace)
|
||||
|
||||
:param bot: instance of the current bot
|
||||
:param key: storage key
|
||||
:param data: new data
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_data(self, bot: Bot, key: StorageKey) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current data for key
|
||||
|
||||
:param bot: instance of the current bot
|
||||
:param key: storage key
|
||||
:return: current data
|
||||
"""
|
||||
pass
|
||||
|
||||
async def update_data(self, bot: Bot, key: StorageKey, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Update date in the storage for key (like dict.update)
|
||||
|
||||
:param bot: instance of the current bot
|
||||
:param key: storage key
|
||||
:param data: partial data
|
||||
:return: new data
|
||||
"""
|
||||
current_data = await self.get_data(bot=bot, key=key)
|
||||
current_data.update(data)
|
||||
await self.set_data(bot=bot, key=key, data=current_data)
|
||||
|
|
@ -49,4 +97,7 @@ class BaseStorage(ABC):
|
|||
|
||||
@abstractmethod
|
||||
async def close(self) -> None: # pragma: no cover
|
||||
"""
|
||||
Close storage (database connection, file or etc.)
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@ class MemoryStorageRecord:
|
|||
|
||||
|
||||
class MemoryStorage(BaseStorage):
|
||||
"""
|
||||
Default FSM storage, stores all data in :class:`dict` and loss everything on shutdown
|
||||
|
||||
.. warning::
|
||||
|
||||
Is not recommended using in production in due to you will lose all data
|
||||
when your bot restarts
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.storage: DefaultDict[StorageKey, MemoryStorageRecord] = defaultdict(
|
||||
MemoryStorageRecord
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ class KeyBuilder(ABC):
|
|||
|
||||
@abstractmethod
|
||||
def build(self, key: StorageKey, part: Literal["data", "state", "lock"]) -> str:
|
||||
"""
|
||||
This method should be implemented in subclasses
|
||||
|
||||
:param key: contextual key
|
||||
:param part: part of the record
|
||||
:return: key to be used in Redis queries
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -30,9 +37,21 @@ class DefaultKeyBuilder(KeyBuilder):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, prefix: str = "fsm", with_bot_id: bool = False, with_destiny: bool = False
|
||||
self,
|
||||
*,
|
||||
prefix: str = "fsm",
|
||||
separator: str = ":",
|
||||
with_bot_id: bool = False,
|
||||
with_destiny: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
:param prefix: prefix for all records
|
||||
:param separator: separator
|
||||
:param with_bot_id: include Bot id in the key
|
||||
:param with_destiny: include destiny key
|
||||
"""
|
||||
self.prefix = prefix
|
||||
self.separator = separator
|
||||
self.with_bot_id = with_bot_id
|
||||
self.with_destiny = with_destiny
|
||||
|
||||
|
|
@ -44,10 +63,14 @@ class DefaultKeyBuilder(KeyBuilder):
|
|||
if self.with_destiny:
|
||||
parts.append(key.destiny)
|
||||
parts.append(part)
|
||||
return ":".join(parts)
|
||||
return self.separator.join(parts)
|
||||
|
||||
|
||||
class RedisStorage(BaseStorage):
|
||||
"""
|
||||
Redis storage required :code:`aioredis` package installed (:code:`pip install aioredis`)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
redis: Redis,
|
||||
|
|
@ -56,6 +79,13 @@ class RedisStorage(BaseStorage):
|
|||
data_ttl: Optional[int] = None,
|
||||
lock_kwargs: Optional[Dict[str, Any]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
:param redis: Instance of Redis connection
|
||||
:param key_builder: builder that helps to convert contextual key to string
|
||||
:param state_ttl: TTL for state records
|
||||
:param data_ttl: TTL for data records
|
||||
:param lock_kwargs: Custom arguments for Redis lock
|
||||
"""
|
||||
if key_builder is None:
|
||||
key_builder = DefaultKeyBuilder()
|
||||
if lock_kwargs is None:
|
||||
|
|
@ -70,6 +100,14 @@ class RedisStorage(BaseStorage):
|
|||
def from_url(
|
||||
cls, url: str, connection_kwargs: Optional[Dict[str, Any]] = None, **kwargs: Any
|
||||
) -> "RedisStorage":
|
||||
"""
|
||||
Create an instance of :class:`RedisStorage` with specifying the connection string
|
||||
|
||||
:param url: for example :code:`redis://user:password@host:port/db`
|
||||
:param connection_kwargs: see :code:`aioredis` docs
|
||||
:param kwargs: arguments to be passed to :class:`RedisStorage`
|
||||
:return: an instance of :class:`RedisStorage`
|
||||
"""
|
||||
if connection_kwargs is None:
|
||||
connection_kwargs = {}
|
||||
pool = ConnectionPool.from_url(url, **connection_kwargs)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ MAX_BUTTONS = 100
|
|||
|
||||
|
||||
class KeyboardBuilder(Generic[ButtonType]):
|
||||
"""
|
||||
Generic keyboard builder that helps to adjust your markup with defined shape of lines.
|
||||
|
||||
Works both of InlineKeyboardMarkup and ReplyKeyboardMarkup.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, button_type: Type[ButtonType], markup: Optional[List[List[ButtonType]]] = None
|
||||
) -> None:
|
||||
|
|
@ -257,6 +263,10 @@ def repeat_last(items: Iterable[T]) -> Generator[T, None, None]:
|
|||
|
||||
|
||||
class InlineKeyboardBuilder(KeyboardBuilder[InlineKeyboardButton]):
|
||||
"""
|
||||
Inline keyboard builder inherits all methods from generic builder
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@no_type_check
|
||||
|
|
@ -275,6 +285,7 @@ class InlineKeyboardBuilder(KeyboardBuilder[InlineKeyboardButton]):
|
|||
...
|
||||
|
||||
def as_markup(self, **kwargs: Any) -> InlineKeyboardMarkup:
|
||||
"""Construct an InlineKeyboardMarkup"""
|
||||
...
|
||||
|
||||
def __init__(self, markup: Optional[List[List[InlineKeyboardButton]]] = None) -> None:
|
||||
|
|
@ -290,6 +301,10 @@ class InlineKeyboardBuilder(KeyboardBuilder[InlineKeyboardButton]):
|
|||
|
||||
|
||||
class ReplyKeyboardBuilder(KeyboardBuilder[KeyboardButton]):
|
||||
"""
|
||||
Reply keyboard builder inherits all methods from generic builder
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@no_type_check
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue