Skip to content

Плейсхолдеры

Назначение: Динамическая подстановка значений в параметры действий сценариев.

Механика работы:

Все атрибуты в params автоматически обрабатываются плейсхолдерами перед выполнением действия. Это означает:

  • Все значения в params — строки, числа, массивы, объекты — проходят через обработку плейсхолдеров
  • Все уровни вложенности — плейсхолдеры работают на любом уровне структуры params (в строках, элементах массивов, значениях объектов)
  • Автоматическая обработка — происходит для каждого шага сценария перед выполнением действия
  • Источник данных — система использует накопленные данные события и результаты предыдущих действий как источник значений для подстановки

Синтаксис плейсхолдеров:

Простая замена:

yaml
params:
  text: "Привет, {username}!"
  chat_id: "{user_id}"

С модификаторами:

yaml
params:
  text: "Привет, {username|fallback:Гость}!"
  price: "{amount|*0.9|format:currency}"

Литеральные значения в кавычках:

yaml
params:
  text: "{'hello'|upper}"                      # HELLO (без передачи через словарь)
  duration: "{'1d 2w'|seconds}"                # 1296000 (литеральное время)
  calculation: "{'100'|+50}"                   # 150 (литеральная арифметика)
  date_shift: "{'2024-12-25'|shift:+1 day}"   # 2024-12-26 (литеральная дата)
  # Двойные кавычки для строк с одинарными кавычками
  text: "{"it's working"}"                     # it's working
  # Экранированные кавычки
  text: "{'it\'s working'}"                    # it's working

Вложенные плейсхолдеры:

yaml
params:
  text: "Ответ: {status|equals:{expected}|value:OK|fallback:BAD}"

⚠️ Важно: Кавычки в YAML для плейсхолдеров с regex

Проблема: YAML по-разному обрабатывает escape-последовательности в одинарных и двойных кавычках, что критично для regex паттернов с обратными слешами.

Решение: Используйте двойные кавычки для regex паттернов с обратными слешами (\s, \d, \w и т.д.).

Примеры:

yaml
# ❌ ОШИБКА - в одинарных кавычках \\s не экранируется правильно
params:
  value: '{event_text|regex:^([^\\s]+)|lower}'  # \\s становится \s (обратный слеш + s), а не пробел!

# ✅ ПРАВИЛЬНО - двойные кавычки правильно экранируют обратные слеши
params:
  value: "{event_text|regex:^([^\\s]+)|lower}"  # \\s становится \s (пробел в regex)

# ✅ ПРАВИЛЬНО - для regex без обратных слешей можно использовать одинарные кавычки
params:
  tenant_id: '{user_state|regex:tenant_(\d+)}'  # Работает, так как \d не требует двойного экранирования

# ✅ ПРАВИЛЬНО - для простых regex с одним обратным слешем тоже двойные кавычки
params:
  value: "{event_text|regex:\\s+([^\\s]+)$|lower}"  # \\s для пробела, \\s для пробела

# ✅ ОБЫЧНЫЕ ПЛЕЙСХОЛДЕРЫ - без разницы
params:
  text: "Привет, {username}!"     # Работает
  text: 'Привет, {username}!'    # Тоже работает

Когда использовать двойные кавычки:

  • Regex паттерны с обратными слешами{field|regex:pattern} где pattern содержит \s, \d, \w, \n, \t и т.д.
  • Важно: В двойных кавычках нужно использовать двойное экранирование: \\s для пробела, \\d для цифры
  • Пример: "{event_text|regex:^([^\\s]+)|lower}"\\s становится \s (пробел в regex)

Когда можно использовать одинарные кавычки:

  • Regex паттерны без обратных слешей — простые паттерны без \s, \d, \w и т.д.
  • Обычные плейсхолдеры{username}, {user_id}
  • Простые модификаторы{name|upper}, {price|format:currency}
  • Текстовые значения — без специальных символов

Правило: Если в regex паттерне есть обратные слеши (\s, \d, \w и т.д.) — всегда используйте двойные кавычки с двойным экранированием (\\s, \\d, \\w).

Доступ к вложенным данным:

yaml
params:
  # Точечная нотация для объектов
  text: "Пользователь: {user.profile.name|fallback:Неизвестный}"
  text: "Сообщение: {message.text}"
  
  # Индексы для массивов
  text: "Первый файл: {attachment[0].file_id}"
  text: "Последний пользователь: {users[-1].name}"
  
  # Комбинированный доступ
  text: "Событие: {event.attachment[0].type}"
  text: "Данные: {response.data.items[0].value}"

Доступные данные:

Все данные события и предыдущих действий доступны для использования в плейсхолдерах:

Данные события:

  • user_id — ID пользователя
  • username — имя пользователя
  • chat_id — ID чата
  • event_text — текст сообщения
  • callback_data — данные callback кнопки
  • event_date — время события
  • tenant_id — ID тенанта
  • message_id — ID сообщения
  • reply_to_message_id — ID сообщения, на которое отвечаем
  • forward_from — данные о пересылке
  • И любые другие поля — в зависимости от типа события

Конфигурация тенанта:

  • _config — словарь с конфигурацией тенанта (атрибуты из config.yaml)
    • _config.openrouter_token — OpenRouter API ключ для тенанта (если установлен)
    • Другие атрибуты — в зависимости от конфигурации тенанта
    • Автоматически доступен во всех сценариях тенанта

Данные предыдущих действий:

  • last_message_id — ID последнего отправленного сообщения
  • response_data — любые данные, возвращённые предыдущими действиями
  • Другие поля — в зависимости от действий в сценарии

Доступ к вложенным элементам:

Универсальный доступ к данным! Поддержка точечной нотации для объектов и индексов для массивов.

СинтаксисОписаниеПримерРезультат
Объекты
object.fieldДоступ к полю объекта{message.text}Привет мир
object.field.subfieldВложенные поля{user.profile.name}Иван Петров
Массивы
array[index]Доступ к элементу массива{attachment[0].file_id}file_1 (первый файл)
array[-index]Отрицательный индекс{attachment[-1].file_id}file_3 (последний файл)
array[index].fieldПоле элемента массива{users[1].name}Боб (имя второго пользователя)
array[index][index]Вложенные массивы{matrix[0][1]}2 (элемент матрицы)
Комбинированный
object.array[index].fieldСмешанный доступ{event.attachment[0].file_id}file_1

Примеры использования:

yaml
params:
  # Объекты
  text: "Сообщение: {message.text}"
  text: "Пользователь: {user.profile.name|fallback:Неизвестный}"
  
  # Массивы
  text: "Первый файл: {attachment[0].file_id}"
  text: "Последний пользователь: {users[-1].name|upper}"
  text: "Файл: {attachment[0].file_id}, размер: {attachment[0].size}"
  
  # Комбинированный доступ
  text: "Событие: {event.attachment[0].type}"
  text: "Данные: {response.data.items[0].value}"
  
  # Конфигурация тенанта
  text: "Токен: {_config.openrouter_token|fallback:Не установлен}"
  
  # Безопасный доступ
  text: "{attachment[10].file_id|fallback:Файл не найден}"

Получение информации о файле

Практический пример: получение file_id и type для пересылки файлов

Создайте сценарий, который покажет file_id и type всех вложений из события:

yaml
file_info:
  description: "Получение file_id и type вложений для последующей пересылки"
  trigger:
    - event_type: "message"
      event_text: "/file"

  step:
    - action: "send_message"
      params:
        text: |
          📎 Информация о файле:
          
          file_id: <code>{event_attachment[0].file_id|fallback:{reply_attachment[0].file_id|fallback:Файл не найден}}</code>
          type: <code>{event_attachment[0].type|fallback:{reply_attachment[0].type|fallback:Тип не найден}}</code>

Обработка ошибок:

  • При обращении к несуществующему полю/индексу возвращается None
  • Можно использовать модификатор fallback для значения по умолчанию
  • Отрицательные индексы работают как в Python: -1 = последний элемент
  • Поддерживается неограниченная глубина вложенности

Модификаторы:

МодификаторОписаниеПримерРезультат
Арифметические операции
+valueСложение{price|+100}1500 (если price=1400)
-valueВычитание{price|-50}1350 (если price=1400)
*valueУмножение{price|*0.9}1260 (если price=1400)
/valueДеление{seconds|/3600}2.5 (если seconds=9000)
%valueОстаток от деления{number|%7}3 (если number=10)
Регистр
upperВерхний регистр{name|upper}ИВАН (если name="Иван")
lowerНижний регистр{name|lower}иван (если name="Иван")
titleЗаглавные буквы каждого слова{name|title}Иван Петров (если name="иван петров")
capitalizeПервая заглавная буква{name|capitalize}Иван (если name="иван")
case:typeПреобразование регистра{name|case:upper}ИВАН (если name="Иван")
Списки
tagsПреобразование в теги{users|tags}@user1 @user2
listМаркированный список{items|list}• item1\n• item2
commaЧерез запятую{items|comma}item1, item2
expandРазворачивание массива массивов на один уровень (только в массивах){keyboard|expand}[[a, b], [c]][a, b], [c] (в массиве)
keysИзвлечение ключей из объекта (словаря) в массив{storage_values|keys}["group1", "group2"] (если storage_values={"group1": {...}, "group2": {...}})
Преобразования
codeОборачивание значения в code блок{field|code}<code>value</code>
Форматирование строк
truncate:lengthОбрезка текста{text|truncate:50}Очень длинный текст...
Операции с датами и временем
shift:±интервалСдвиг даты на интервал (PostgreSQL: +1 day, -2 hours, +1 year 2 months). Поддержка всех форматов дат, включая ISO с таймзоной, корректная обработка месяцев/лет{created|shift:+1 day}2024-12-26 (если created="2024-12-25")
secondsПреобразование временных строк в секунды (формат: Xw Yd Zh Km Ms){duration|seconds}9000 (если duration="2h 30m")
to_dateПриведение даты к началу дня (00:00:00), возвращает ISO формат{created|to_date}2024-12-25 00:00:00
to_hourПриведение даты к началу часа (минуты и секунды = 0), возвращает ISO формат{created|to_hour}2024-12-25 15:00:00 (если created="2024-12-25 15:30:45")
to_minuteПриведение даты к началу минуты (секунды = 0), возвращает ISO формат{created|to_minute}2024-12-25 15:30:00 (если created="2024-12-25 15:30:45")
to_secondПриведение даты к началу секунды (микросекунды = 0), возвращает ISO формат{created|to_second}2024-12-25 15:30:45
to_weekПриведение даты к началу недели (понедельник 00:00:00), возвращает ISO формат{created|to_week}2024-12-23 00:00:00 (если created="2024-12-25 15:30:45")
to_monthПриведение даты к началу месяца (1 число, 00:00:00), возвращает ISO формат{created|to_month}2024-12-01 00:00:00 (если created="2024-12-25 15:30:45")
to_yearПриведение даты к началу года (1 января, 00:00:00), возвращает ISO формат{created|to_year}2024-01-01 00:00:00 (если created="2024-12-25 15:30:45")
Форматирование дат и времени
format:timestampПреобразование в Unix timestamp{date|format:timestamp}1703512200
format:dateФормат даты (dd.mm.yyyy){timestamp|format:date}25.12.2024
format:timeФормат времени (HH:MM){timestamp|format:time}14:30
format:time_fullФормат времени с секундами (HH:MM:SS){timestamp|format:time_full}14:30:45
format:datetimeПолный формат (dd.mm.yyyy HH:MM){timestamp|format:datetime}25.12.2024 14:30
format:datetime_fullПолный формат с секундами (dd.mm.yyyy HH:MM:SS){timestamp|format:datetime_full}25.12.2024 14:30:45
format:pg_dateФормат даты для PostgreSQL (YYYY-MM-DD){timestamp|format:pg_date}2024-12-25
format:pg_datetimeФормат даты и времени для PostgreSQL (YYYY-MM-DD HH:MM:SS){timestamp|format:pg_datetime}2024-12-25 14:30:45
Форматирование чисел
format:currencyФорматирование валюты{amount|format:currency}1000.00 ₽
format:percentФорматирование процентов{value|format:percent}25.5%
format:numberФорматирование чисел{value|format:number}1234.56
Условные
equals:valueПроверка равенства (строковое сравнение){status|equals:active}true или false
in_list:itemsПроверка вхождения в список{role|in_list:admin,moderator}true или false
trueПроверка истинности (рекомендуется для boolean){is_active|true}true или false
existsПроверка существования значения (не None и не пустая строка){field|exists}true или false
is_nullПроверка на null (None, пустая строка или строка "null"){field|is_null}true или false
value:resultВозврат значения при истинности{status|equals:active|value:Активен}Активен или null
Служебные
fallback:valueЗначение по умолчанию{username|fallback:Гость}Иван или Гость
lengthПодсчёт длины{text|length}15 (количество символов)
lengthПодсчёт длины массива{array|length}3 (количество элементов)
regex:patternИзвлечение по regex{text|regex:(\d+)}123 (первое число)
Асинхронные действия
readyПроверка готовности async действия{_async_action.ai_req_1|ready}true если завершено, false если выполняется
not_readyПроверка что действие ещё выполняется{_async_action.ai_req_1|not_ready}true если выполняется, false если готово

Примеры использования:

yaml
params:
  # Простые операции
  result: "{value|+100|format:currency}"
  
  # Условная логика
  status: "{user_status|equals:active|value:Активен|fallback:Неактивен}"
  
  # Цепочка модификаторов
  formatted: "{price|*0.9|format:currency|fallback:0.00 ₽}"
  
  # Извлечение по regex (для обратных слешей — двойные кавычки!)
  duration: "{event_text|regex:(\\d+)\\s*min|fallback:0}"
  
  # Точечная нотация
  name: "{user.profile.name|fallback:Неизвестный}"
  
  # Временные значения
  duration_seconds: "{duration|seconds}"
  duration_minutes: "{duration|seconds|/60}"
  duration_hours: "{duration|seconds|/3600}"
  formatted_time: "{duration|seconds|format:number}"
  
  # Сдвиг дат
  tomorrow: "{created|shift:+1 day}"
  next_month: "{created|shift:+1 month|format:date}"
  complex_shift: "{created|shift:+1 year 2 months|format:datetime}"
  
  # Приведение к началу периода
  start_of_day: "{created|to_date}"
  start_of_week: "{created|to_week}"
  formatted_month_start: "{created|to_month|format:date}"
  
  # Boolean поля (различать True/False/None)
  is_active: "{is_active|equals:True|value:✅ Включено|fallback:{is_active|equals:False|value:❌ Выключено|fallback:❓ Неизвестно}}"
  is_polling: "{is_polling|equals:True|value:✅ Активен|fallback:{is_polling|equals:False|value:❌ Неактивен|fallback:❓ Неизвестно}}"
  
  # Строковые поля (используйте equals:value)
  status: "{user_status|equals:active|value:Активен|fallback:Неактивен}"
  
  # Упрощённый вариант (если None не ожидается)
  is_simple: "{is_active|true|value:✅ Включено|fallback:❌ Выключено}"
  
  # Проверка существования значения (в условиях)
  # В условиях: {field|exists} == True или {field|exists} == False
  
  # Проверка на null (в условиях)
  # В условиях: {field|is_null} == True или {field|is_null} == False

Пример использования exists в условиях:

yaml
step:
  - action: "validate"
    params:
      condition: "{response_value.feedback|exists} == True"
    transition:
      - action_result: "success"
        transition_action: "continue"  # Значение существует
      - action_result: "failed"
        transition_action: "jump_to_scenario"
        transition_value: "create_feedback"  # Значение не существует

Важно: boolean и строковые поля

Для boolean полей (True/False):

  • Рекомендуется: {is_active|true|value:✅ Включено|fallback:❌ Выключено}
  • Не работает: {is_active|equals:true|value:✅ Включено} (сравнение строк)

Для строковых полей:

  • Используйте: {status|equals:active|value:Активен|fallback:Неактивен}
  • Модификатор true не подходит для строковых значений

Практические примеры

Пример 1: Персонализированное приветствие

yaml
step:
  - action: "send_message"
    params:
      text: |
        👋 Привет, {username|fallback:Гость}!
        
        📊 Ваша статистика:
        • ID: {user_id}
        • Чаты: {chat_count|fallback:0}
        • Последний вход: {last_login|format:datetime|fallback:Неизвестно}

Пример 2: Динамическое меню с условиями

yaml
step:
  - action: "send_message"
    params:
      text: |
        🎛️ Главное меню
        
        Статус: {user_status|equals:active|value:✅ Активен|fallback:❌ Неактивен}
        Баланс: {balance|format:currency|fallback:0.00 ₽}
      inline:
        - [{"📊 Статистика": "stats"}, {"💰 Баланс": "balance"}]
        - [{"⚙️ Настройки": "settings"}]

Пример 3: Обработка ошибок с fallback

yaml
step:
  - action: "send_message"
    params:
      text: |
        ❌ Ошибка обработки
        
        Код ошибки: {error_code|fallback:НЕИЗВЕСТЕН}
        Сообщение: {error_message|fallback:Неизвестная ошибка}
        Время: {timestamp|format:datetime}
      inline:
        - [{"🔄 Повторить": "retry"}, {"🏠 Главное меню": "main"}]

Пример 4: Математические расчёты

yaml
step:
  - action: "send_message"
    params:
      text: |
        💰 Расчёт стоимости
        
        Цена: {base_price|format:currency}
        Скидка: {discount_percent|fallback:0}%
        Итого: {base_price|*{discount_percent|/100}|*{base_price|-1}|+{base_price}|format:currency}

Пример 5: Извлечение данных из текста

yaml
step:
  - action: "send_message"
    params:
      text: |
        📝 Обработка заказа
        
        Номер заказа: "{event_text|regex:order\\s*(\\d+)|fallback:Не найден}"
        Сумма: "{event_text|regex:amount\\s*(\\d+)|format:currency|fallback:0.00 ₽}"
        Время доставки: "{event_text|regex:(\\d+)\\s*min|fallback:30}" минут

Пример 6: Динамическая клавиатура с модификатором expand

yaml
step:
  # Получаем список тенантов
  - action: "get_tenants_list"
    params: {}
  
  # Строим динамическую клавиатуру из публичных тенантов
  - action: "build_keyboard"
    params:
      items: "{public_tenant_ids}"
      keyboard_type: "inline"
      text_template: "Tenant $value$"
      callback_template: "select_tenant_$value$"
      buttons_per_row: 2
  
  # Отправляем сообщение с динамической клавиатурой и статичными кнопками
  - action: "send_message"
    params:
      text: "Выберите тенант:"
      inline:
        - "{keyboard|expand}"              # Разворачиваем динамическую клавиатуру
        - [{"🔙 Назад": "main_menu"}]      # Статичная кнопка

Как работает expand:

  • Модификатор expand разворачивает массив массивов на один уровень только при использовании в массиве
  • В примере выше {keyboard|expand} в массиве inline разворачивает [[{...}, {...}], [{...}]] в [{...}, {...}], [{...}]
  • Это позволяет комбинировать динамически сгенерированные строки клавиатуры со статичными строками
  • Важно: expand работает только в контексте массива. В строке или словаре значение остаётся без изменений

Пример без expand (неправильно):

yaml
inline:
  - "{keyboard}"                           # ❌ Получится [[[...]], [...]] - 3 уровня вложенности!
  - [{"🔙 Назад": "main_menu"}]

Пример с expand (правильно):

yaml
inline:
  - "{keyboard|expand}"                     # ✅ Получится [[...], [...]] - 2 уровня вложенности
  - [{"🔙 Назад": "main_menu"}]

Coreness — Create. Automate. Scale.