Skip to content

Commit e1663b5

Browse files
committed
chore: Upload to GitHub
0 parents  commit e1663b5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3162
-0
lines changed

.gitignore

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
MANIFEST
27+
28+
# PyInstaller
29+
# Usually these files are written by a python script from a template
30+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
31+
*.manifest
32+
*.spec
33+
34+
# Installer logs
35+
pip-log.txt
36+
pip-delete-this-directory.txt
37+
38+
# Unit test / coverage reports
39+
htmlcov/
40+
.tox/
41+
.coverage
42+
.coverage.*
43+
.cache
44+
nosetests.xml
45+
coverage.xml
46+
*.cover
47+
.hypothesis/
48+
.pytest_cache/
49+
50+
# Translations
51+
*.mo
52+
*.pot
53+
54+
# Django stuff:
55+
*.log
56+
local_settings.py
57+
db.sqlite3
58+
59+
# Flask stuff:
60+
instance/
61+
.webassets-cache
62+
63+
# Scrapy stuff:
64+
.scrapy
65+
66+
# Sphinx documentation
67+
docs/_build/
68+
69+
# PyBuilder
70+
target/
71+
72+
# Jupyter Notebook
73+
.ipynb_checkpoints
74+
75+
# pyenv
76+
.python-version
77+
78+
# celery beat schedule file
79+
celerybeat-schedule
80+
81+
# SageMath parsed files
82+
*.sage.py
83+
84+
# Environments
85+
.env
86+
.venv
87+
env/
88+
venv/
89+
ENV/
90+
env.bak/
91+
venv.bak/
92+
93+
# Spyder project settings
94+
.spyderproject
95+
.spyproject
96+
97+
# Rope project settings
98+
.ropeproject
99+
100+
# mkdocs documentation
101+
/site
102+
103+
# mypy
104+
.mypy_cache/
105+
106+
.idea/
107+
.vscode/
108+
109+
static/

LICENSE.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
Лицензия на программное обеспечение
2+
Дата 10 июня 2021г.
3+
4+
Настоящая лицензия регламентирует использование следующего Программного
5+
обеспечения:
6+
* Библиотека Pybotx - сборник подпрограмм или объектов, используемых для
7+
разработки ботов, обеспечивающий интеграцию с ПО «Система коммуникаций
8+
Express».
9+
* Авторский программный модуль (АПМ) (Asyncbox) - программная платформа для
10+
создания ботов для Системы коммуникаций Express.
11+
* Библиотека FSM – сборник подпрограмм или объектов, используемых для
12+
разработки ботов, для представления и управления потоком выполнения команд
13+
ботов.
14+
* Модуль Proxier – модуль ПО, позволяющий перенаправлять запросы на локально
15+
развернутого бота.
16+
* ToDo Бот - индивидуальная разработка ООО «Анлимитед Продакшен» для
17+
демонстрации процесса и результата разработки ботов на платформе.
18+
* Библиотека Pybotx Widgets – библиотека переиспользуемых компонентов для
19+
взаимодействия с пользователями.
20+
21+
Далее по тексту «Программное обеспечение» Правообладателем Программного
22+
обеспечения является ООО «Анлимитед Продакшен» (ИНН: 7707374451, ОГРН:
23+
5167746251240) Данная неисключительная лицензия разрешает лицам, получившим
24+
законным способом копию программного кода Программного обеспечения и
25+
сопутствующей документации, безвозмездно использовать Программное обеспечение
26+
без ограничений, включая неограниченное право на использование, копирование,
27+
доработку, адаптацию, публикацию, распространение, сублицензирование, при
28+
соблюдении следующих условий:
29+
30+
Уведомление о правообладателе должны быть включены во все копии или значимые
31+
части Программных продуктов, в том числе созданных с использованием
32+
Программного обеспечения. Неисключительная лицензия ограничена «правом на
33+
использование», ни одно из положений настоящей лицензии не означает передачи
34+
кому бы то ни было исключительного права на Платформу, Библиотеки, Блоки и
35+
любые элементы Программного обеспечения. Запрещается вносить изменения в
36+
Программное обеспечение, переводить или создавать производные продукты,
37+
основанные на Программном обеспечении и его элементах с целью регистрации
38+
исключительных прав на программный код Программного обеспечения, а также
39+
интегрировать Программное обеспечение или его элементы в другие результаты
40+
интеллектуальной деятельности с последующей регистрацией исключительных прав на
41+
Программное обеспечение в составе результатов интеллектуальной деятельности.
42+
43+
Неисключительная лицензия предоставляется на срок действия авторских прав на
44+
территории всего мира. ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК
45+
ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ
46+
ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И
47+
ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ
48+
ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО
49+
ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ
50+
СИТУАЦИИ, ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ
51+
ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. В ТОМ ЧИСЛЕ, НЕ ГАРАНТИРУЕТ (ВКЛЮЧАЯ, НО
52+
НЕ ОГРАНИЧИВАЯСЬ): ПРИГОДНОСТЬ ДЛЯ КОНКРЕТНЫХ ЦЕЛЕЙ, ТОЧНОСТЬ, ПОЛНОТУ,
53+
ПРОИЗВОДИТЕЛЬНОСТЬ, СИСТЕМНУЮ ИНТЕГРАЦИЮ, БЕСПЕРЕБОЙНОЕ ФУНКЦИОНИРОВАНИЕ,
54+
ОТСУТСТВИЕ ОШИБОК, ИСПРАВЛЕНИЕ НЕПОЛАДОК, ЗАКОННОСТЬ ИСПОЛЬЗОВАНИЯ НА ЛЮБЫХ
55+
ТЕРРИТОРИЯХ ЗА ПРЕДЕЛАМИ РОССИЙСКОЙ ФЕДЕРАЦИИ.

README.md

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# pybotx-fsm
2+
3+
Конечный автомат (Finite state machine) для ботов на базе библиотеки
4+
[pybotx](https://github.com/ExpressApp/pybotx).
5+
6+
7+
## Возможности
8+
9+
* Лёгкое создание графа состояний и их переключений.
10+
* Передача данных в следующее состояние при явном вызове перехода.
11+
12+
13+
## Подготовка к установке
14+
15+
Для работы библиотеки необходим Redis, который уже встроен в последние версии
16+
[коробки](https://github.com/ExpressApp/async-box).
17+
18+
19+
## Установка
20+
21+
Добавьте эту строку в зависимости проекта в `pyproject.toml`:
22+
23+
```toml
24+
botx-fsm = { git = "https://github.com/ExpressApp/pybotx-fsm", rev = "0.1.4" }
25+
```
26+
27+
## Работа с графом состояний
28+
29+
1. Добавьте экземпляр автомата в мидлваре для того, чтобы бот мог использовать его:
30+
31+
```python
32+
# bot.py
33+
34+
bot.add_middleware(
35+
FSMMiddleware,
36+
storage=redis_storage,
37+
fsm_instances=your_fsm_instance,
38+
)
39+
```
40+
41+
2. Создайте `enum` для возможных состояний автомата:
42+
43+
```python
44+
from enum import Enum, auto
45+
from botx_fsm import FSM
46+
47+
48+
class ProcessStates(Enum):
49+
state1 = auto()
50+
state2 = auto()
51+
52+
fsm = FSM(ProcessStates)
53+
```
54+
55+
3. Создайте обработчик конкретного состояния:
56+
57+
```python
58+
from botx_fsm import FlowError
59+
60+
61+
@fsm.on(ProcessStates.state1, on_success=ProcessStates.state2)
62+
async def process_state(message: Message, bot: Bot) -> None:
63+
if message.body == "to_state2":
64+
await bot.answer_message("going to state2", message)
65+
return # No exceptions, going to `on_success` argument state
66+
67+
await bot.answer_message("wrong text, try again", message)
68+
raise FlowError # State haven't changed
69+
```
70+
71+
4. Передайте управление обработчику состояний из любого обработчика сообщений:
72+
73+
```python
74+
# bot.py
75+
@collector.handler(command="/start-process")
76+
async def start_process_fsm(message: Message, bot: Bot) -> None:
77+
await bot.answer_message("started process", message)
78+
await fsm.change_state(message, ProcessStates.state1)
79+
```
80+
81+
**Примечание:** В `example/bot` находятся примеры нескольких процессов,
82+
созданных через pybotx-fsm.
83+
84+
85+
## Продвинутая работа с библиотекой
86+
87+
1. В `FlowError` можно передать состояние из enum-а, тогда бот автоматически
88+
перейдёт в него. Также можно выбросить `FlowError(clear=True)`, чтобы выйти
89+
из машины состояний (управление будет передано обработчикам сообщений).
90+
91+
2. Помимо аргумента `on_success` для `@fsm.on` можно использовать `on_failure`.
92+
Тогда выбрасывание `FlowError` без аргументов будет переводить в это
93+
состояние.
94+
95+
Также есть состояние `unset`, которое позволяет выйти из
96+
выполнения конечного автомата:
97+
98+
```python
99+
from botx_fsm import unset
100+
101+
102+
@fsm.on(ProcessStates.state2, on_success=unset, on_failure=ProcessStates.state1)
103+
async def process_state(message: Message, bot: Bot) -> None:
104+
...
105+
```
106+
107+
3. Чтобы передать данные в следующее состояние, необходимо явно вызвать процесс
108+
перехода и распаковать данные в следующем состоянии.
109+
110+
```python
111+
from botx_fsm import StateExtractor
112+
113+
114+
@collector.hidden(command="/start-process", name="start-process")
115+
async def start_process(message: Message, bot: Bot) -> None:
116+
await fsm.change_state(message, ProcessStates.state1, foo="bar")
117+
118+
119+
@fsm.on(ProcessStates.state2, on_success=unset)
120+
async def get_additional_data(
121+
message: Message, bot: Bot, foo: str = Depends(StateExtractor.foo)
122+
) -> None:
123+
...
124+
```

botx_fsm/__init__.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from botx_fsm.di_extractors import StateExtractor
2+
from botx_fsm.exceptions import FlowError
3+
from botx_fsm.fsm import FSM
4+
from botx_fsm.markers import unset
5+
from botx_fsm.middleware import FSMMiddleware
6+
from botx_fsm.models import Key
7+
8+
__all__ = ( # noqa: WPS410
9+
"FlowError", "FSM", "unset", "FSMMiddleware", "Key", 'StateExtractor'
10+
)

botx_fsm/di_extractors.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Callable
2+
3+
from botx import Message, Bot
4+
5+
6+
def _extractor(key: str) -> Callable[[Message], Bot]:
7+
def decorator(message: Message):
8+
try:
9+
return getattr(message.state, key)
10+
except AttributeError:
11+
return getattr(message.bot.state, key)
12+
13+
return decorator
14+
15+
16+
class _StateExtractor:
17+
def __getattribute__(self, item: str) -> Callable[[Message], Bot]:
18+
return _extractor(item)
19+
20+
21+
StateExtractor = _StateExtractor()

botx_fsm/exceptions.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from enum import Enum
2+
from typing import Union
3+
4+
from botx_fsm.markers import FSMStateMarker, undefined
5+
from botx_fsm.typing import EnumT
6+
7+
8+
class FlowError(Exception):
9+
def __init__(
10+
self,
11+
new_state: Union[EnumT, FSMStateMarker] = undefined,
12+
*,
13+
clear: bool = False,
14+
) -> None:
15+
if not isinstance(new_state, Enum) and new_state is not undefined:
16+
raise RuntimeError("new state should be enum member")
17+
18+
self.new_state = new_state
19+
self.clear = clear
20+
21+
def new_state_defined(self) -> bool:
22+
return self.new_state is not undefined and not self.clear
23+
24+
def __repr__(self) -> str:
25+
new_state_str = str(self.new_state)
26+
if self.new_state is undefined:
27+
new_state_str = "<will not change>"
28+
if self.clear:
29+
new_state_str = "<will be cleared>"
30+
31+
return f"FlowError(new_state: {new_state_str})"

0 commit comments

Comments
 (0)