# Оглавление
## 🇺🇸 English
- [BriefSurvey](#briefsurvey)
- [Description](#description)
- [Installation](#installation)
- [From GitHub Repository](#from-github-repository)
- [Download Locally and Install](#download-locally-and-install)
- [Quick Start](#quick-start)
- [(case 1) Dynamic create a brief](#case-1-dynamic-crate-a-brief)
- [(case 2)](#case-2)
- [1. Define your questions using Pydantic models](#1-define-your-questions-using-pydantic-models)
- [2. Define a result model](#2-define-a-result-model)
- [3. Create a function to save results](#3-create-a-function-to-save-results)
- [4. Initialize and register the survey](#4-initialize-and-register-the-survey)
- [5. Launch the survey in Telegram with the command](#5-launch-the-survey-in-telegram-with-the-command)
- [Important](#important)
- [Customizing Messages and Buttons in brief_survey](#customizing-messages-and-buttons-in-brief_survey)
- [What can be customized (English)](#what-can-be-customized-english)
- [InfoMessages — system messages](#infomessages-—-system-messages)
- [InfoButtons — button labels](#infobuttons-—-button-labels)
- [Example usage](#example-usage)
## 🇷🇺 Русский
- [BriefSurvey](#briefsurvey)
- [Описание](#описание)
- [Установка](#установка)
- [Github Repo](#github-repo)
- [Скачать локально и установить](#скачать-локально-и-установить)
- [Быстрый старт](#быстрый-старт)
- [(1 вариант) Динамическое добавление вопросов](#1-вариант-динамическое-добавление-вопросов)
- [(2 вариант)](#2-вариант)
- [1. Определите вопросы](#1-определите-вопросы)
- [2. Определите модель результата](#2-определите-модель-результата)
- [3. Создайте функцию для сохранения результатов](#3-создайте-функцию-для-сохранения-результатов)
- [4. Инициализируйте и зарегистрируйте опросник](#4-инициализируйте-и-зарегистрируйте-опросник)
- [5. Запускайте команду в Telegram](#5запускайте-команду-в-telegram)
- [Важно](#важно)
- [Настройка сообщений и кнопок в brief_survey](#настройка-сообщений-и-кнопок-в-brief_survey)
- [Что можно настраивать](#что-можно-настраивать)
- [InfoMessages — системные сообщения](#infomessages-—-системные-сообщения)
- [InfoButtons — тексты кнопок](#infobuttons-—-тексты-кнопок)
- [Пример использования](#пример-использования)
# 🇺🇸 English
## BriefSurvey
Universal Dynamic Survey for Telegram Bots with `aiogram` version 3 `aiogram_dialog` and Pydantic
### Description
BriefSurvey is a module for quick and flexible creation of dialog-based surveys in Telegram using aiogram v3 and aiogram_dialog.
- Questions are defined using Pydantic models to enforce strong typing and validation.
- Final answers are automatically serialized back into a Pydantic result model.
- Easy to extend and customize.
- Supports different question types: text, number, single-choice, multiple-choice.
- Simple integration and handler registration.
- Auto-validation questions by names.Questions with names like "age", "weight" validate and send error messages automatically without validator enter.
---
## Installation
### Via pip
```bash
pip install brief-survey
```
### From GitHub Repository
```bash
pip install git+https://github.com/Fugguri/brief_survey.git
```
### Download Locally and Install
```bash
git clone https://github.com/Fugguri/brief_survey.git
pip install -e brief_survey
```
## Quick Start
### (case 1) Dynamic crate a brief
```python
from brief_survey import BriefSurvey
from brief_survey.validators.person import age
async def save_handler(user_id: int, result: any):
# dynamic access to survey result fields by question name.
name = result.name
age = result.age
gender = result.gender
return
survey = BriefSurvey(
save_handler=save_handler,
start_command='start_brief' # customizable start command for the survey
)
# Customizable error messages
survey.info_messages.invalid_input = "Invalid data received, please try again."
# Customizable button text at the end of the survey
survey.buttons.finish_text = "Finish survey"
survey.add_question(
text="What is your name?",
question_type="text",
name="name",
media_path='storage/media/img.png' # you can send media with text for any question (optional)
)
survey.add_question(
text="Your age?",
question_type="number",
name="age",
validator=age # You can use validators from validators path of this lib.
)
survey.add_question(
text="Select your gender",
question_type="choice",
name="gender",
choices=["Male", "Female"],
next_questions={
'Male': "favorite_car",
'Female': "favorite_color",
}
)
survey.add_question(
text="Favorite car brand?",
question_type="choice",
name="favorite_car",
choices=["MBW", "Mercedes"],
next_question='photo' # mandatory parameter for queries depending on choice. If not set, proceeds to next survey question
)
survey.add_question(
text="Favorite color?",
question_type="choice",
name="favorite_color",
choices=["White", "Pink", "Black"],
next_question='photo' # mandatory parameter for queries depending on choice. If not set, proceeds to next survey question
)
survey.add_question(
text="Upload your photo",
question_type="photo",
name="photo"
)
```
### (case 2) 1. Define your questions using Pydantic models:
```python
from brief_survey import QuestionBase, ChoiceQuestion, MultiChoiceQuestion
questions = [
QuestionBase(
name="name",
text="What is your name?",
type="text",
validator=lambda x: bool(x.strip()),
),
ChoiceQuestion(
name="gender",
text="Select your gender",
type="choice",
choices=[("1", "Male"), ("2", "Female")],
),
MultiChoiceQuestion(
name="profession",
text="Select your profession",
type="multi_choice",
choices=[
("1", "Athlete"),
("2", "Entrepreneur"),
("3", "Worker"),
],
),
]
```
### 2. Define a result model:
``` python
from pydantic import BaseModel
from typing import Optional
class SurveyResult(BaseModel):
name: Optional[str]
gender: Optional[str]
profession: Optional[list[str]]
```
### 3. Create a function to save results:
``` python
async def save_handler(user_id: int, result: SurveyResult):
# Save logic, e.g., store in database
print(f"User {user_id} survey result: {result}")
```
### 4. Initialize and register the survey:
``` python
from brief_survey import BriefSurvey
survey = BriefSurvey(
questions=questions,
save_handler=save_handler,
result_model=SurveyResult,
)
# In your main bot file with Dispatcher dp
survey.register_handlers(
dp=dp,
command_start='start_survey', # optional
text='Start survey', # optional
callback_data="start_survey" # optional
)
```
### 5. Launch the survey in Telegram with the command:
/start_survey
## Important
If you have global handlers in your bot, filter states explicitly using StateFilter to avoid conflicts that can break the survey after the first message:
``` python
from aiogram.filters import StateFilter
dp.message.register(handle, StateFilter(None)) # Only outside states
dp.callback_query.register(handle_callback, StateFilter(None))
```
# Customizing Messages and Buttons in brief_survey
The **brief_survey** library provides `InfoMessages` and `InfoButtons` classes that allow you to easily customize system messages and button texts used in the survey dialogs.
## What can be customized (English)
### InfoMessages — system messages
| Field | Description | Default example |
|-------------------------|-------------------------------------------------|---------------------------------|
| `invalid_input` | Message shown when user input is invalid | `"Please enter valid data."` |
| `save_success` | Message confirming data saved successfully | `"Thank you! Data saved successfully."` |
| `save_fail` | Message shown if saving data failed | `"An error occurred during saving. Please try again later."` |
| `finish_text` | Text displayed at survey completion | `"Data received."` |
| `question_not_found` | Message shown if question is not found | `"Error: question not found."` |
| `pre_save_message` | Message shown before saving data | `"Saving..."` |
| `start_message` | Message shown at start of survey (optional) | `None` |
| `forced_exit_message` | Message shown when survey is forcefully exited | `"Survey exited. Entered data does not allow continuation."` |
### InfoButtons — button labels
| Field | Description | Default example |
|------------------------|-------------------------------------------------|--------------------------------|
| `finish_text` | Text on the button to finish the survey | `"Finish"` |
| `multi_select_confirm` | Text for confirming multi-select choice | `"Confirm selection"` |
| `start_again` | Text on the button to restart the survey | `"Start again"` |
## Example usage
``` python
Setting system messages
survey.info_messages.invalid_input = "Invalid data received, please try again."
survey.info_messages.save_success = "Thank you! Your responses have been saved."
survey.info_messages.save_fail = "Saving failed, please try again later."
survey.info_messages.finish_text = "Thank you for participating!"
survey.info_messages.question_not_found = "Question not found."
survey.info_messages.pre_save_message = "Saving data..."
survey.info_messages.start_message = "Let's start the survey!"
survey.info_messages.forced_exit_message = "Survey terminated due to an error."
Setting button texts
survey.buttons.finish_text = "Finish survey"
survey.buttons.multi_select_confirm = "Confirm"
survey.buttons.start_again = "Restart"
```
# 🇷🇺 Русский
## BriefSurvey
Универсальный динамический опросник для Telegram-ботов на базе `aiogram_dialog` с поддержкой Pydantic-моделей вопросов и результатов.
---
## Описание
BriefSurvey — это модуль для быстрой и гибкой реализации диалоговых опросников в Telegram с помощью `aiogram` 3-й версии и `aiogram_dialog`.
- Вопросы описываются Pydantic-моделями для строгой типизации и валидации.
- Итоговые ответы автоматически сериализуются обратно в Pydantic-модель результата.
- Легко расширяется и настраивается.
- Позволяет реализовать опросник с разными типами вопросов: текст, число, выбор одного или нескольких вариантов.
- Простое подключение и регистрация обработчиков.
- Автоматическая валидация по имени вопроса. Поля типа "age", "weight" проходят автоматическую валидацию и присылают сообщение об ошибке, без указания валидаторов .
---
## Установка
### Github Repo
```bash
pip install git+https://github.com/Fugguri/brief_survey.git
```
### Скачать локально и установить
```bash
git clone https://github.com/Fugguri/brief_survey.git
pip install -e brief_survey
```
## Быстрый старт
### (1 вариант) Динамическое добавление вопросов
```python
from brief_survey import BriefSurvey
async def save_handler(user_id: int, result: any):
#динамическое обращение к полям результата опроса по имени вопроса.
name = result.mame
age = result.age
gender = result.gender
return
survey = BriefSurvey(
save_handler=save_handler,
start_command='start_brief' # Можно настраивать команду начала опроса
)
#Можно настраивать сообщения об ошибках
survey.info_messages.invalid_input = "Получены неверные данные, попробуйте еще раз"
#Можно настраивать сообщени и кнопку в конце опроса
survey.info_messages.invalid_input = "Получены неверные данные, попробуйте еще раз"
survey.buttons.finish_text = "Завершить опрос"
# Если необходимо можете отправить сообщение перед началом опроса
survey.info_messages.start_message = 'Пройдите небольшой опрос перед началом работы с ботом.'
from brief_survey.validators.person import age
survey.add_question(
text="Как вас зовут?",
question_type="text",
name="name",
media_path='storage/media/img.png'# Можете отправлять фотографии вместе с вопросом
)
survey.add_question(
text="Ваш возраст?",
question_type="number",
name="age",
validator=age # Вы можете использовать готовые валидаторы из раздела validators
)
survey.add_question(
text="Выберите пол",
question_type="choice",
name="gender",
choices=["Мужской", "Женский"],
next_questions={
'Мужской': "favorite_car",
'Женский': "favorite_color",
}
)
survey.add_question(
text="Любимая марка автомобиля?",
question_type="choice",
name="favorite_car",
choices=["MBW", "Mercedes"],
next_question='photo' # Обязательный параметр для вариантов зависящих от выбора. Если не указать, пойдет дальше по опросу
)
survey.add_question(
text="Любимый цвет?",
question_type="choice",
name="favorite_car",
choices=["Белый", "Розовый", "Черный"],
next_question='photo' # Обязательный параметр для вариантов зависящих от выбора. Если не указать, пойдет дальше по опросу
)
survey.add_question(
text="Загрузите ваше фото",
question_type="photo",
name="photo"
)
````
### (2 вариант)
1. Определите вопросы (используйте модели из основного модуля):
```python
from brief_survey import QuestionBase, ChoiceQuestion, MultiChoiceQuestion
questions = [
QuestionBase(
name="name",
text="Как вас зовут?",
type="text",
validator=lambda x: bool(x.strip()),
),
ChoiceQuestion(
name="gender",
text="Выберите пол",
type="choice",
choices=[("1", "Мужской"), ("2", "Женский")],
),
MultiChoiceQuestion(
name="gender",
text="Выберите род деятельности",
type="multi_choice",
choices=[("1", "Спортсмен"),
("2", "Предприниматель"),
("3", "Простой работник")
],
)
]
```
### 2. Определите модель результата:
``` python
from pydantic import BaseModel
from typing import Optional
class SurveyResult(BaseModel):
name: Optional[str]
gender: Optional[str]
```
### 3. Создайте функцию для сохранения результатов:
```python
async def save_handler(user_id: int, result: SurveyResult):
# Логика сохранения, например, в базу
print(f"Результат опроса пользователя {user_id}: {result}")
```
### 4. Инициализируйте и зарегистрируйте опросник:
``` python
from brief_survey import BriefSurvey
survey = BriefSurvey(
questions=questions,
save_handler=save_handler,
result_model=SurveyResult,
)
# в основном файле с ботом (Dispatcher dp) регистрация в Dispatcher
survey.register_handlers(dp=dp,
command_start='start_survey', #опционально
text='Начать опрос', #опционально
callback_data="start_survey" #опционально
)
```
### 5.Запускайте команду в Telegram:
/start_survey
## Важно
Если у вас есть глобальный handler, фильтруйте state вручную, при помощи StateFilter.
Неясные конфликты и после первого сообщения опросник перестает работать.
``` python
from aiogram.filters import StateFilter
dp.message.register(handle,StateFilter(None)) # только вне состояний!
dp.callback_query.register(handle_callback,StateFilter(None))
```
## Настройка сообщений и кнопок в brief_survey
В библиотеке **brief_survey** доступны классы `InfoMessages` и `InfoButtons`, которые позволяют легко настраивать тексты системных сообщений и кнопок, используемых в диалогах опросника.
### Что можно настраивать
#### InfoMessages — системные сообщения
| Поле | Описание | Пример по умолчанию |
|-------------------------|-----------------------------------------------------|-----------------------------------------------------|
| `invalid_input` | Сообщение при неверном вводе | `"Пожалуйста, введите корректные данные."` |
| `save_success` | Сообщение об успешном сохранении | `"Спасибо! Данные успешно сохранены."` |
| `save_fail` | Сообщение об ошибке при сохранении | `"Произошла ошибка при сохранении. Попробуйте позже."` |
| `finish_text` | Текст при завершении опроса | `"Данные приняты."` |
| `question_not_found` | Сообщение при ошибке отсутствия вопроса | `"Ошибка: вопрос не найден."` |
| `pre_save_message` | Сообщение перед отправкой данных на сохранение | `"Сохраняю"` |
| `start_message` | Сообщение при начале опроса (опционально) | `None` |
| `forced_exit_message` | Сообщение при принудительном выходе из опроса | `"Выход из опроса.Введенные данные не позволяют продолжить опрос"` |
#### InfoButtons — тексты кнопок
| Поле | Описание | Пример по умолчанию |
|------------------------|---------------------------------------------------|----------------------------------|
| `finish_text` | Надпись на кнопке завершения опроса | `"Завершить"` |
| `multi_select_confirm` | Текст кнопки для подтверждения выбора (множественный выбор) | `"Подтвердить выбор"` |
| `start_again` | Текст кнопки для перезапуска опроса | `"Начать сначала"` |
### Пример использования
``` python Настройка сообщений об ошибках и событий
survey.info_messages.invalid_input = "Получены неверные данные, попробуйте еще раз"
survey.info_messages.save_success = "Спасибо! Ваши ответы сохранены."
survey.info_messages.save_fail = "Ошибка при сохранении, попробуйте позже."
survey.info_messages.finish_text = "Спасибо за участие!"
survey.info_messages.question_not_found = "Вопрос не найден."
survey.info_messages.pre_save_message = "Данные сохраняются..."
survey.info_messages.start_message = "Начинаем опрос!"
survey.info_messages.forced_exit_message = "Опрос прерван из-за ошибки."
Настройка текстов кнопок
survey.buttons.finish_text = "Завершить опрос"
survey.buttons.multi_select_confirm = "Подтвердить"
survey.buttons.start_again = "Начать заново"
```
# ToDo
- add media list handler
- add 2 type logging
- check same questions name in list
- add survey database saver. To save complete survey_to database. And call by his id
# for any errors send me a telegram message to [@fugguri](https://t/me/fugguri).
# ☕️bye me a coffe appreciated
Raw data
{
"_id": null,
"home_page": "https://github.com/Fugguri/brief_survey",
"name": "brief-survey",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "aiogram3, aiogram, aiogram_dialog, brief",
"author": "Fugguri",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/1b/a8/01b6a1e697e6dbdf1de0295f5d9d25552144f1c1a7cf3b6ee7c09339107e/brief_survey-0.2.8.2.tar.gz",
"platform": null,
"description": "# \u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435\n\n## \ud83c\uddfa\ud83c\uddf8 English\n\n- [BriefSurvey](#briefsurvey)\n- [Description](#description)\n- [Installation](#installation)\n - [From GitHub Repository](#from-github-repository)\n - [Download Locally and Install](#download-locally-and-install)\n- [Quick Start](#quick-start)\n - [(case 1) Dynamic create a brief](#case-1-dynamic-crate-a-brief)\n - [(case 2)](#case-2)\n - [1. Define your questions using Pydantic models](#1-define-your-questions-using-pydantic-models)\n - [2. Define a result model](#2-define-a-result-model)\n - [3. Create a function to save results](#3-create-a-function-to-save-results)\n - [4. Initialize and register the survey](#4-initialize-and-register-the-survey)\n - [5. Launch the survey in Telegram with the command](#5-launch-the-survey-in-telegram-with-the-command)\n- [Important](#important)\n- [Customizing Messages and Buttons in brief_survey](#customizing-messages-and-buttons-in-brief_survey)\n - [What can be customized (English)](#what-can-be-customized-english)\n - [InfoMessages \u2014 system messages](#infomessages-\u2014-system-messages)\n - [InfoButtons \u2014 button labels](#infobuttons-\u2014-button-labels)\n - [Example usage](#example-usage)\n\n## \ud83c\uddf7\ud83c\uddfa \u0420\u0443\u0441\u0441\u043a\u0438\u0439\n\n- [BriefSurvey](#briefsurvey)\n- [\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435](#\u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435)\n- [\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430](#\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430)\n - [Github Repo](#github-repo)\n - [\u0421\u043a\u0430\u0447\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c](#\u0441\u043a\u0430\u0447\u0430\u0442\u044c-\u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e-\u0438-\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c)\n- [\u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442](#\u0431\u044b\u0441\u0442\u0440\u044b\u0439-\u0441\u0442\u0430\u0440\u0442)\n - [(1 \u0432\u0430\u0440\u0438\u0430\u043d\u0442) \u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432](#1-\u0432\u0430\u0440\u0438\u0430\u043d\u0442-\u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435-\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435-\u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432)\n - [(2 \u0432\u0430\u0440\u0438\u0430\u043d\u0442)](#2-\u0432\u0430\u0440\u0438\u0430\u043d\u0442)\n - [1. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b](#1-\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435-\u0432\u043e\u043f\u0440\u043e\u0441\u044b)\n - [2. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430](#2-\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435-\u043c\u043e\u0434\u0435\u043b\u044c-\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430)\n - [3. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432](#3-\u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435-\u0444\u0443\u043d\u043a\u0446\u0438\u044e-\u0434\u043b\u044f-\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f-\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432)\n - [4. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 \u0438 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a](#4-\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435-\u0438-\u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435-\u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a)\n - [5. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0432 Telegram](#5\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0439\u0442\u0435-\u043a\u043e\u043c\u0430\u043d\u0434\u0443-\u0432-telegram)\n- [\u0412\u0430\u0436\u043d\u043e](#\u0432\u0430\u0436\u043d\u043e)\n- [\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438 \u043a\u043d\u043e\u043f\u043e\u043a \u0432 brief_survey](#\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430-\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439-\u0438-\u043a\u043d\u043e\u043f\u043e\u043a-\u0432-brief_survey)\n - [\u0427\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c](#\u0447\u0442\u043e-\u043c\u043e\u0436\u043d\u043e-\u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c)\n - [InfoMessages \u2014 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f](#infomessages-\u2014-\u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435-\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f)\n - [InfoButtons \u2014 \u0442\u0435\u043a\u0441\u0442\u044b \u043a\u043d\u043e\u043f\u043e\u043a](#infobuttons-\u2014-\u0442\u0435\u043a\u0441\u0442\u044b-\u043a\u043d\u043e\u043f\u043e\u043a)\n - [\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f](#\u043f\u0440\u0438\u043c\u0435\u0440-\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f)\n\n# \ud83c\uddfa\ud83c\uddf8 English\n\n## BriefSurvey\nUniversal Dynamic Survey for Telegram Bots with `aiogram` version 3 `aiogram_dialog` and Pydantic\n### Description\nBriefSurvey is a module for quick and flexible creation of dialog-based surveys in Telegram using aiogram v3 and aiogram_dialog.\n\n- Questions are defined using Pydantic models to enforce strong typing and validation.\n- Final answers are automatically serialized back into a Pydantic result model.\n- Easy to extend and customize.\n- Supports different question types: text, number, single-choice, multiple-choice.\n- Simple integration and handler registration.\n- Auto-validation questions by names.Questions with names like \"age\", \"weight\" validate and send error messages automatically without validator enter.\n\n\n---\n## Installation\n### Via pip \n\n```bash\npip install brief-survey\n```\n### From GitHub Repository\n\n```bash\npip install git+https://github.com/Fugguri/brief_survey.git \n```\n### Download Locally and Install\n\n```bash\ngit clone https://github.com/Fugguri/brief_survey.git\npip install -e brief_survey \n```\n\n\n## Quick Start\n### (case 1) Dynamic crate a brief \n```python\nfrom brief_survey import BriefSurvey\nfrom brief_survey.validators.person import age\nasync def save_handler(user_id: int, result: any):\n # dynamic access to survey result fields by question name.\n name = result.name\n age = result.age\n gender = result.gender\n\n return\n\nsurvey = BriefSurvey(\n save_handler=save_handler,\n start_command='start_brief' # customizable start command for the survey\n)\n\n# Customizable error messages\nsurvey.info_messages.invalid_input = \"Invalid data received, please try again.\"\n# Customizable button text at the end of the survey\nsurvey.buttons.finish_text = \"Finish survey\"\n\nsurvey.add_question(\n text=\"What is your name?\",\n question_type=\"text\",\n name=\"name\",\n media_path='storage/media/img.png' # you can send media with text for any question (optional)\n)\n\nsurvey.add_question(\n text=\"Your age?\",\n question_type=\"number\",\n name=\"age\",\n validator=age # You can use validators from validators path of this lib.\n)\n\n\nsurvey.add_question(\n text=\"Select your gender\",\n question_type=\"choice\",\n name=\"gender\",\n choices=[\"Male\", \"Female\"],\n next_questions={\n 'Male': \"favorite_car\",\n 'Female': \"favorite_color\",\n }\n)\n\nsurvey.add_question(\n text=\"Favorite car brand?\",\n question_type=\"choice\",\n name=\"favorite_car\",\n choices=[\"MBW\", \"Mercedes\"],\n next_question='photo' # mandatory parameter for queries depending on choice. If not set, proceeds to next survey question\n)\n\nsurvey.add_question(\n text=\"Favorite color?\",\n question_type=\"choice\",\n name=\"favorite_color\",\n choices=[\"White\", \"Pink\", \"Black\"],\n next_question='photo' # mandatory parameter for queries depending on choice. If not set, proceeds to next survey question\n)\n\nsurvey.add_question(\n text=\"Upload your photo\",\n question_type=\"photo\",\n name=\"photo\"\n)\n\n```\n### (case 2) 1. Define your questions using Pydantic models:\n\n```python\nfrom brief_survey import QuestionBase, ChoiceQuestion, MultiChoiceQuestion\n\nquestions = [\n QuestionBase(\n name=\"name\",\n text=\"What is your name?\",\n type=\"text\",\n validator=lambda x: bool(x.strip()),\n ),\n ChoiceQuestion(\n name=\"gender\",\n text=\"Select your gender\",\n type=\"choice\",\n choices=[(\"1\", \"Male\"), (\"2\", \"Female\")],\n ),\n MultiChoiceQuestion(\n name=\"profession\",\n text=\"Select your profession\",\n type=\"multi_choice\",\n choices=[\n (\"1\", \"Athlete\"),\n (\"2\", \"Entrepreneur\"),\n (\"3\", \"Worker\"),\n ],\n ),\n]\n\n```\n### 2. Define a result model:\n``` python\nfrom pydantic import BaseModel\nfrom typing import Optional\n\nclass SurveyResult(BaseModel):\n name: Optional[str]\n gender: Optional[str]\n profession: Optional[list[str]]\n```\n### 3. Create a function to save results:\n``` python\nasync def save_handler(user_id: int, result: SurveyResult):\n # Save logic, e.g., store in database\n print(f\"User {user_id} survey result: {result}\")\n\n```\n### 4. Initialize and register the survey:\n``` python\nfrom brief_survey import BriefSurvey\n\nsurvey = BriefSurvey(\n questions=questions,\n save_handler=save_handler,\n result_model=SurveyResult,\n)\n\n# In your main bot file with Dispatcher dp\nsurvey.register_handlers(\n dp=dp,\n command_start='start_survey', # optional\n text='Start survey', # optional\n callback_data=\"start_survey\" # optional\n)\n```\n### 5. Launch the survey in Telegram with the command:\n /start_survey\n\n\n\n## Important\nIf you have global handlers in your bot, filter states explicitly using StateFilter to avoid conflicts that can break the survey after the first message:\n``` python\nfrom aiogram.filters import StateFilter\n\ndp.message.register(handle, StateFilter(None)) # Only outside states\ndp.callback_query.register(handle_callback, StateFilter(None))\n```\n# Customizing Messages and Buttons in brief_survey\n\nThe **brief_survey** library provides `InfoMessages` and `InfoButtons` classes that allow you to easily customize system messages and button texts used in the survey dialogs.\n\n## What can be customized (English)\n\n### InfoMessages \u2014 system messages\n\n| Field | Description | Default example |\n|-------------------------|-------------------------------------------------|---------------------------------|\n| `invalid_input` | Message shown when user input is invalid | `\"Please enter valid data.\"` |\n| `save_success` | Message confirming data saved successfully | `\"Thank you! Data saved successfully.\"` |\n| `save_fail` | Message shown if saving data failed | `\"An error occurred during saving. Please try again later.\"` |\n| `finish_text` | Text displayed at survey completion | `\"Data received.\"` |\n| `question_not_found` | Message shown if question is not found | `\"Error: question not found.\"` |\n| `pre_save_message` | Message shown before saving data | `\"Saving...\"` |\n| `start_message` | Message shown at start of survey (optional) | `None` |\n| `forced_exit_message` | Message shown when survey is forcefully exited | `\"Survey exited. Entered data does not allow continuation.\"` |\n\n### InfoButtons \u2014 button labels\n\n| Field | Description | Default example |\n|------------------------|-------------------------------------------------|--------------------------------|\n| `finish_text` | Text on the button to finish the survey | `\"Finish\"` |\n| `multi_select_confirm` | Text for confirming multi-select choice | `\"Confirm selection\"` |\n| `start_again` | Text on the button to restart the survey | `\"Start again\"` |\n\n## Example usage\n``` python \nSetting system messages\nsurvey.info_messages.invalid_input = \"Invalid data received, please try again.\"\nsurvey.info_messages.save_success = \"Thank you! Your responses have been saved.\"\nsurvey.info_messages.save_fail = \"Saving failed, please try again later.\"\nsurvey.info_messages.finish_text = \"Thank you for participating!\"\nsurvey.info_messages.question_not_found = \"Question not found.\"\nsurvey.info_messages.pre_save_message = \"Saving data...\"\nsurvey.info_messages.start_message = \"Let's start the survey!\"\nsurvey.info_messages.forced_exit_message = \"Survey terminated due to an error.\"\n\nSetting button texts\nsurvey.buttons.finish_text = \"Finish survey\"\nsurvey.buttons.multi_select_confirm = \"Confirm\"\nsurvey.buttons.start_again = \"Restart\"\n```\n\n\n\n# \ud83c\uddf7\ud83c\uddfa \u0420\u0443\u0441\u0441\u043a\u0438\u0439\n\n## BriefSurvey\n\n\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a \u0434\u043b\u044f Telegram-\u0431\u043e\u0442\u043e\u0432 \u043d\u0430 \u0431\u0430\u0437\u0435 `aiogram_dialog` \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 Pydantic-\u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.\n\n---\n\n## \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\n\nBriefSurvey \u2014 \u044d\u0442\u043e \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0438 \u0433\u0438\u0431\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\u044b\u0445 \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a\u043e\u0432 \u0432 Telegram \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e `aiogram` 3-\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0438 `aiogram_dialog`.\n\n- \u0412\u043e\u043f\u0440\u043e\u0441\u044b \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f Pydantic-\u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u0433\u043e\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438.\n- \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 Pydantic-\u043c\u043e\u0434\u0435\u043b\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430.\n- \u041b\u0435\u0433\u043a\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442\u0441\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f.\n- \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0442\u0438\u043f\u0430\u043c\u0438 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432: \u0442\u0435\u043a\u0441\u0442, \u0447\u0438\u0441\u043b\u043e, \u0432\u044b\u0431\u043e\u0440 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432.\n- \u041f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432.\n- \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0432\u043e\u043f\u0440\u043e\u0441\u0430. \u041f\u043e\u043b\u044f \u0442\u0438\u043f\u0430 \"age\", \"weight\" \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438 \u043f\u0440\u0438\u0441\u044b\u043b\u0430\u044e\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435, \u0431\u0435\u0437 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u043e\u0432 .\n\n---\n\n## \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\n### Github Repo\n```bash\npip install git+https://github.com/Fugguri/brief_survey.git \n```\n### \u0421\u043a\u0430\u0447\u0430\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\n```bash\ngit clone https://github.com/Fugguri/brief_survey.git\npip install -e brief_survey \n```\n\n\n## \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\n### (1 \u0432\u0430\u0440\u0438\u0430\u043d\u0442) \u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432\n```python\n\nfrom brief_survey import BriefSurvey\nasync def save_handler(user_id: int, result: any):\n #\u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u043f\u043e\u043b\u044f\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043e\u043f\u0440\u043e\u0441\u0430 \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0432\u043e\u043f\u0440\u043e\u0441\u0430. \n name = result.mame\n age = result.age\n gender = result.gender \n return \nsurvey = BriefSurvey(\n save_handler=save_handler,\n start_command='start_brief' # \u041c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u043f\u0440\u043e\u0441\u0430\n)\n\n#\u041c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445\nsurvey.info_messages.invalid_input = \"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437\"\n\n#\u041c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438 \u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u0432 \u043a\u043e\u043d\u0446\u0435 \u043e\u043f\u0440\u043e\u0441\u0430\nsurvey.info_messages.invalid_input = \"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437\"\nsurvey.buttons.finish_text = \"\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043e\u043f\u0440\u043e\u0441\" \n# \u0415\u0441\u043b\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u043e\u043f\u0440\u043e\u0441\u0430\nsurvey.info_messages.start_message = '\u041f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043e\u043f\u0440\u043e\u0441 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u043e\u0442\u043e\u043c.'\nfrom brief_survey.validators.person import age \nsurvey.add_question(\n text=\"\u041a\u0430\u043a \u0432\u0430\u0441 \u0437\u043e\u0432\u0443\u0442?\",\n question_type=\"text\",\n name=\"name\",\n media_path='storage/media/img.png'# \u041c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u043c\n)\nsurvey.add_question(\n text=\"\u0412\u0430\u0448 \u0432\u043e\u0437\u0440\u0430\u0441\u0442?\",\n question_type=\"number\",\n name=\"age\",\n validator=age # \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \u0438\u0437 \u0440\u0430\u0437\u0434\u0435\u043b\u0430 validators\n)\nsurvey.add_question(\n text=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\",\n question_type=\"choice\",\n name=\"gender\",\n choices=[\"\u041c\u0443\u0436\u0441\u043a\u043e\u0439\", \"\u0416\u0435\u043d\u0441\u043a\u0438\u0439\"],\n \n next_questions={\n '\u041c\u0443\u0436\u0441\u043a\u043e\u0439': \"favorite_car\",\n '\u0416\u0435\u043d\u0441\u043a\u0438\u0439': \"favorite_color\",\n }\n \n)\nsurvey.add_question(\n text=\"\u041b\u044e\u0431\u0438\u043c\u0430\u044f \u043c\u0430\u0440\u043a\u0430 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044f?\",\n question_type=\"choice\",\n name=\"favorite_car\",\n choices=[\"MBW\", \"Mercedes\"],\n next_question='photo' # \u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0434\u043b\u044f \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0437\u0430\u0432\u0438\u0441\u044f\u0449\u0438\u0445 \u043e\u0442 \u0432\u044b\u0431\u043e\u0440\u0430. \u0415\u0441\u043b\u0438 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043f\u043e\u0439\u0434\u0435\u0442 \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u043e \u043e\u043f\u0440\u043e\u0441\u0443\n\n)\nsurvey.add_question(\n text=\"\u041b\u044e\u0431\u0438\u043c\u044b\u0439 \u0446\u0432\u0435\u0442?\",\n question_type=\"choice\",\n name=\"favorite_car\",\n choices=[\"\u0411\u0435\u043b\u044b\u0439\", \"\u0420\u043e\u0437\u043e\u0432\u044b\u0439\", \"\u0427\u0435\u0440\u043d\u044b\u0439\"],\n next_question='photo' # \u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0434\u043b\u044f \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0437\u0430\u0432\u0438\u0441\u044f\u0449\u0438\u0445 \u043e\u0442 \u0432\u044b\u0431\u043e\u0440\u0430. \u0415\u0441\u043b\u0438 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043f\u043e\u0439\u0434\u0435\u0442 \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u043e \u043e\u043f\u0440\u043e\u0441\u0443\n)\n\nsurvey.add_question(\n text=\"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0432\u0430\u0448\u0435 \u0444\u043e\u0442\u043e\",\n question_type=\"photo\",\n name=\"photo\"\n)\n\n````\n\n### (2 \u0432\u0430\u0440\u0438\u0430\u043d\u0442) \n1. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f):\n\n```python\nfrom brief_survey import QuestionBase, ChoiceQuestion, MultiChoiceQuestion\n\nquestions = [\n QuestionBase(\n name=\"name\",\n text=\"\u041a\u0430\u043a \u0432\u0430\u0441 \u0437\u043e\u0432\u0443\u0442?\",\n type=\"text\",\n validator=lambda x: bool(x.strip()),\n ),\n ChoiceQuestion(\n name=\"gender\",\n text=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\",\n type=\"choice\",\n choices=[(\"1\", \"\u041c\u0443\u0436\u0441\u043a\u043e\u0439\"), (\"2\", \"\u0416\u0435\u043d\u0441\u043a\u0438\u0439\")],\n ),\n MultiChoiceQuestion(\n name=\"gender\",\n text=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u043e\u0434 \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438\",\n type=\"multi_choice\",\n choices=[(\"1\", \"\u0421\u043f\u043e\u0440\u0442\u0441\u043c\u0435\u043d\"), \n (\"2\", \"\u041f\u0440\u0435\u0434\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\"),\n (\"3\", \"\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u043d\u0438\u043a\")\n ],\n )\n]\n\n\n\n```\n### 2. \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430:\n``` python\nfrom pydantic import BaseModel\nfrom typing import Optional\n\n\nclass SurveyResult(BaseModel):\n name: Optional[str]\n gender: Optional[str]\n```\n### 3. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432:\n```python\n\nasync def save_handler(user_id: int, result: SurveyResult):\n # \u041b\u043e\u0433\u0438\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0431\u0430\u0437\u0443\n print(f\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u043f\u0440\u043e\u0441\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f {user_id}: {result}\")\n```\n### 4. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 \u0438 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a:\n``` python\nfrom brief_survey import BriefSurvey\n\nsurvey = BriefSurvey(\n questions=questions,\n save_handler=save_handler,\n result_model=SurveyResult,\n)\n\n# \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0441 \u0431\u043e\u0442\u043e\u043c (Dispatcher dp) \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0432 Dispatcher\nsurvey.register_handlers(dp=dp,\n command_start='start_survey', #\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\n text='\u041d\u0430\u0447\u0430\u0442\u044c \u043e\u043f\u0440\u043e\u0441', #\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\n callback_data=\"start_survey\" #\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\n )\n```\n### 5.\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0432 Telegram:\n\n/start_survey\n\n## \u0412\u0430\u0436\u043d\u043e\n\n\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 handler, \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0439\u0442\u0435 state \u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 StateFilter.\n\u041d\u0435\u044f\u0441\u043d\u044b\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u044b \u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.\n\n``` python\nfrom aiogram.filters import StateFilter\ndp.message.register(handle,StateFilter(None)) # \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439!\ndp.callback_query.register(handle_callback,StateFilter(None))\n```\n## \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438 \u043a\u043d\u043e\u043f\u043e\u043a \u0432 brief_survey\n\n\u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 **brief_survey** \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u043b\u0430\u0441\u0441\u044b `InfoMessages` \u0438 `InfoButtons`, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u044b \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438 \u043a\u043d\u043e\u043f\u043e\u043a, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0432 \u0434\u0438\u0430\u043b\u043e\u0433\u0430\u0445 \u043e\u043f\u0440\u043e\u0441\u043d\u0438\u043a\u0430.\n\n### \u0427\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c\n\n#### InfoMessages \u2014 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\n\n| \u041f\u043e\u043b\u0435 | \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 | \u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e |\n|-------------------------|-----------------------------------------------------|-----------------------------------------------------|\n| `invalid_input` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043d\u0435\u0432\u0435\u0440\u043d\u043e\u043c \u0432\u0432\u043e\u0434\u0435 | `\"\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.\"` |\n| `save_success` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 | `\"\u0421\u043f\u0430\u0441\u0438\u0431\u043e! \u0414\u0430\u043d\u043d\u044b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b.\"` |\n| `save_fail` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 | `\"\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.\"` |\n| `finish_text` | \u0422\u0435\u043a\u0441\u0442 \u043f\u0440\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u043e\u043f\u0440\u043e\u0441\u0430 | `\"\u0414\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u044f\u0442\u044b.\"` |\n| `question_not_found` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0432\u043e\u043f\u0440\u043e\u0441\u0430 | `\"\u041e\u0448\u0438\u0431\u043a\u0430: \u0432\u043e\u043f\u0440\u043e\u0441 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d.\"` |\n| `pre_save_message` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 | `\"\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u044e\"` |\n| `start_message` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043d\u0430\u0447\u0430\u043b\u0435 \u043e\u043f\u0440\u043e\u0441\u0430 (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e) | `None` |\n| `forced_exit_message` | \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u043c \u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437 \u043e\u043f\u0440\u043e\u0441\u0430 | `\"\u0412\u044b\u0445\u043e\u0434 \u0438\u0437 \u043e\u043f\u0440\u043e\u0441\u0430.\u0412\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u043e\u043f\u0440\u043e\u0441\"` |\n\n#### InfoButtons \u2014 \u0442\u0435\u043a\u0441\u0442\u044b \u043a\u043d\u043e\u043f\u043e\u043a\n\n| \u041f\u043e\u043b\u0435 | \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 | \u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e |\n|------------------------|---------------------------------------------------|----------------------------------|\n| `finish_text` | \u041d\u0430\u0434\u043f\u0438\u0441\u044c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043e\u043f\u0440\u043e\u0441\u0430 | `\"\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c\"` |\n| `multi_select_confirm` | \u0422\u0435\u043a\u0441\u0442 \u043a\u043d\u043e\u043f\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0432\u044b\u0431\u043e\u0440\u0430 (\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440) | `\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0432\u044b\u0431\u043e\u0440\"` |\n| `start_again` | \u0422\u0435\u043a\u0441\u0442 \u043a\u043d\u043e\u043f\u043a\u0438 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u043f\u0440\u043e\u0441\u0430 | `\"\u041d\u0430\u0447\u0430\u0442\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430\"` |\n\n### \u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n``` python \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445 \u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\nsurvey.info_messages.invalid_input = \"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437\"\nsurvey.info_messages.save_success = \"\u0421\u043f\u0430\u0441\u0438\u0431\u043e! \u0412\u0430\u0448\u0438 \u043e\u0442\u0432\u0435\u0442\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b.\"\nsurvey.info_messages.save_fail = \"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0437\u0436\u0435.\"\nsurvey.info_messages.finish_text = \"\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0443\u0447\u0430\u0441\u0442\u0438\u0435!\"\nsurvey.info_messages.question_not_found = \"\u0412\u043e\u043f\u0440\u043e\u0441 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d.\"\nsurvey.info_messages.pre_save_message = \"\u0414\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f...\"\nsurvey.info_messages.start_message = \"\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043e\u043f\u0440\u043e\u0441!\"\nsurvey.info_messages.forced_exit_message = \"\u041e\u043f\u0440\u043e\u0441 \u043f\u0440\u0435\u0440\u0432\u0430\u043d \u0438\u0437-\u0437\u0430 \u043e\u0448\u0438\u0431\u043a\u0438.\"\n\n\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0442\u0435\u043a\u0441\u0442\u043e\u0432 \u043a\u043d\u043e\u043f\u043e\u043a\nsurvey.buttons.finish_text = \"\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043e\u043f\u0440\u043e\u0441\"\nsurvey.buttons.multi_select_confirm = \"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\"\nsurvey.buttons.start_again = \"\u041d\u0430\u0447\u0430\u0442\u044c \u0437\u0430\u043d\u043e\u0432\u043e\"\n```\n\n\n\n# ToDo\n- add media list handler\n- add 2 type logging \n- check same questions name in list\n- add survey database saver. To save complete survey_to database. And call by his id\n# for any errors send me a telegram message to [@fugguri](https://t/me/fugguri).\n# \u2615\ufe0fbye me a coffe appreciated \n",
"bugtrack_url": null,
"license": null,
"summary": "Dynamic survey/dialog for aiogram3 with aiogram_dialog and Pydantic support",
"version": "0.2.8.2",
"project_urls": {
"Homepage": "https://github.com/Fugguri/brief_survey",
"github": "https://github.com/Fugguri/brief_survey",
"pypi": "https://pypi.org/project/brief-survey/"
},
"split_keywords": [
"aiogram3",
" aiogram",
" aiogram_dialog",
" brief"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "fe9263b150d5ca0600dc889ac2096184ac282d18f1c3cf4b37eef16263848a65",
"md5": "e7ce7816b97b8646d14e98312b4cfc75",
"sha256": "5b0ffae141dc86e1e3e70590f7f0d2faf8eb6f14569c65a0eaf84f734188c62d"
},
"downloads": -1,
"filename": "brief_survey-0.2.8.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e7ce7816b97b8646d14e98312b4cfc75",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 30534,
"upload_time": "2025-08-13T10:35:57",
"upload_time_iso_8601": "2025-08-13T10:35:57.262823Z",
"url": "https://files.pythonhosted.org/packages/fe/92/63b150d5ca0600dc889ac2096184ac282d18f1c3cf4b37eef16263848a65/brief_survey-0.2.8.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1ba801b6a1e697e6dbdf1de0295f5d9d25552144f1c1a7cf3b6ee7c09339107e",
"md5": "a53fedc90b75ac328e5c5c2a0f7c2ca4",
"sha256": "da7193e8bef72f1fb9bb0b4abf8c27fcd7e26767b4afb83e12a31ac138c63061"
},
"downloads": -1,
"filename": "brief_survey-0.2.8.2.tar.gz",
"has_sig": false,
"md5_digest": "a53fedc90b75ac328e5c5c2a0f7c2ca4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 30727,
"upload_time": "2025-08-13T10:35:58",
"upload_time_iso_8601": "2025-08-13T10:35:58.287458Z",
"url": "https://files.pythonhosted.org/packages/1b/a8/01b6a1e697e6dbdf1de0295f5d9d25552144f1c1a7cf3b6ee7c09339107e/brief_survey-0.2.8.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-13 10:35:58",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Fugguri",
"github_project": "brief_survey",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "aiogram",
"specs": [
[
"==",
"3.21.0"
]
]
},
{
"name": "aiogram_dialog",
"specs": [
[
"==",
"2.3.1"
]
]
},
{
"name": "phonenumbers",
"specs": [
[
"==",
"9.0.10"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"2.11.7"
]
]
},
{
"name": "setuptools",
"specs": [
[
"==",
"68.0.0"
]
]
}
],
"lcname": "brief-survey"
}