This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Шаблоны
Шаблоны позволяют генерировать как однострочные команды, так и целые блоки текста.
Эти механизмы являются основой построения взаимодействий SHM.
Шаблоны рендерятся локально (сервером SHM), результат рендера может быть отправлен/выполнен с помощью Транспорта.
Шаблоны служат:
Введение
Синтаксис: {{ ОБЪЕКТ.ДАННЫЕ }}
Например, с помощью объекта user можно получить доступ к данным пользователя:
user.id - идентификатор пользователя
user.login - логин пользователя
user.balance - баланс пользователя
Используя эти функции мы можем написать такой шаблон:
Уважаемый клиент.
Ваш логин: {{ user.login }}
Ваш баланс: {{ user.balance }} руб.
В шаблонах поддерживаются условия и циклы, примеры использования вы можете увидеть здесь: Прогноз оплаты
Больше информации о шаблонизаторе Вы можете узнать здесь
Объекты и функции
Ниже приведен список методов для работы с SHM через шаблоны.
Достуность тех или иных методов зависит от контекста применения шаблона.
Подробнее о доступности методов описано здесь.
Пользователь
| Метод |
Описание |
| user.id() |
Идентификатор пользователя (получить/установить) |
| user.switch( USER_ID ) |
Переключение пользователя на указанного (смена контекста) |
| user.login |
Логин пользователя |
| user.balance |
Баланс пользователя |
| user.credit |
Кредитный лимит пользователя |
| user.dogovor |
Договор пользователя |
| user.full_name |
ФИО пользователя |
| user.settings |
Получить settings пользователя |
| user.get_bonus |
Получить кол-во бонусов |
| user.income_percent |
Получить процент партнерских бонусов |
| user.add_bonus( КОЛ-ВО, КОММЕНТ ) |
Начисление бонусов |
| user.set_settings({ 'foo' => 1 }) |
Сохранить в settings пользователя произвольные данные |
| user.services |
Ссылка на услуги пользователя |
| user.gen_session.id |
Специальная функция для генерации идентификатора сессии |
| user.set_new_passwd |
Смена пароля пользователя. Вернет новый пароль |
| user.pays. |
Ссылка на платежи пользователя |
| user.delete |
Удаление пользователя (с нулевым балансом, без услуг) |
| user.list_for_api( 'admin', 1) |
Получить всех пользователей |
Услуги пользователя
| Метод |
Описание |
| us.id() |
Идентификатор пользовательской услуги (получить/установить) |
| us.name |
Имя пользовательской услуги |
| us.created |
Дата создания пользовательской услуги |
| us.expire |
Дата истечения пользовательской услуги |
| us.status |
Статус пользовательской услуги |
| us.settings |
Получить параметры пользовательской услуги |
| us.set_settings({ 'foo' => 1 }) |
Сохранить в settings услуги пользователя произвольные данные |
| us.set(FIELD, VALUE) |
Установка поля FIELD в значение VALUE. Пример: us.set('next', 123) |
| us.child_by_category(CATEGORY). |
Ссылка на дочернюю услугу определенной категории |
| us.finish( 'money_back', 1) |
Завершение услуги с возвратом средств (биллинг продлит или заблокирует услугу в зависимости от наличия средств) |
| us.block |
Принудительная блокировка услуги пользователя |
| us.activate |
Активация услуги пользователя после блокировки |
| us.delete |
Удаление заблокированной услуги пользователя |
| us.gen_store_pass |
Специальная функция для генерации и сохранения пароля в settings |
| us.parent. |
Ссылка на родительскую услугу пользователя |
| us.top_parent. |
Ссылка на самую верхнюю услугу пользователя |
| us.service. |
Ссылка на каталог услуг |
| us.withdraw. |
Ссылка на списание услуги |
| us.list_for_api() |
Получение списка услуг пользователя |
Каталог услуг
| Метод |
Описание |
| service.id() |
Идентификатор пользовательской услуги (получить/установить) |
| service.name |
Название услуги |
| service.cost |
Базовая стоимость услуги |
| service.period |
Период услуги |
| service.category |
Категория услуги |
| service.server |
Ссылка на сервер услуги |
| service.id( N ).name |
Получение имени услуги c идентификатором N |
| service.id( N ). |
Получение произвольного поля услуги c идентификатором N |
| service.api_price_list() |
Возвращает массив услуг из каталога |
| service.settings |
Получить settings услуги |
| service.set_settings({ 'foo' => 1 }) |
Сохранить в settings услуги произвольные данные |
| service.withdraw. |
Ссылка на объект списания |
| service.create_for_api( 'service_id', N, 'check_allow_to_order',1 ) |
Регистрирует услугу клиенту с идентификатором N |
| service.list_for_api() |
Получение списка услуг из каталога |
Платежи
| Метод |
Описание |
| pay.id() |
Получить/установить id платежа |
| pay.date |
Дата и время платежа |
| pay.money |
Cумма платежа |
| pay.pay_system_id |
Имя платежной системы |
| pay.comment |
Данные платежа |
| pay.last |
Получить ссылку на последний платеж |
| pay.forecast |
Возвращает JSON прогноза оплат услуг |
| pay.paysystems |
Получить список платежных систем |
| pay.list_for_api() |
Получение списка платежей |
Бонусы
| Метод |
Описание |
| bonus.list_for_api() |
Получение списка бонусов |
Списания
| Метод |
Описание |
| wd.id() |
Получить/установить id списания |
| wd.create_date |
Дата создания списания |
| wd.withdraw_date |
Дата списания списания |
| wd.cost |
Сумма |
| wd.discount |
Скидка |
| wd.bonus |
Кол-во бонусов |
| wd.months |
Период услуги |
| wd.total |
Итоговая стоимость |
| wd.service_id |
идентификатор каталога услуг |
| wd.user_service_id |
идентификатор услуги пользователя |
| wd.qnt |
Кол-во единиц товара |
| wd.list_for_api() |
Получение списка списаний |
Сервера
| Метод |
Описание |
| server.id() |
Получить/установить id сервера |
| server.name |
Имя сервера |
| server.host |
Host сервера |
| server.transport |
Транспорт сервера |
| server.settings |
Получение settings текущего сервера |
| server.set_settings({ 'foo' => 1 }) |
Сохранить в settings сервера произвольные данные |
| server.group. |
Ссылка на группу сервера |
| server.servers_by_group_id( N ) |
Получение списка серверов из группы N |
| server.list_for_api() |
Получение списка серверов |
Группы серверов
| Метод |
Описание |
| sg.id() |
Получить/установить id группы серверов |
| sg.name |
Имя группы серверов |
| sg.type |
Способ выбора серверов (random,by-one,evenly) |
| sg.transport |
Транспорт группы (local,ssh,http…) |
| sg.settings |
settings группы серверов |
| sg.set_settings({ 'foo' => 1 }) |
Сохранить в settings группы серверов произвольные данные |
| sg.list_for_api() |
Получение списка групп серверов |
Шаблоны
| Метод |
Описание |
| tpl.id |
Получить/установить id шаблона |
| tpl.id( NAME ).parse( 'usi', 123 ) |
Выполнить шаблон с именем NAME для пользовательской услуги с идентификаторм 123 |
| tpl.data |
Данные шаблона |
| tpl.settings |
Получить settings шаблона |
Хранилище
| Метод |
Описание |
| storage.save( NAME, DATA ) |
Сохранить данные DATA в хранилище с ключом NAME |
| storage.load( NAME ) |
Получить данные из хранилища с ключом NAME |
| storage.del( NAME ) |
Удалить данные из хранилища с ключом NAME |
| storage.list_for_api() |
Получение списка данных |
Конфигурация
| Метод |
Описание |
| config.NAME |
Получить данные NAME из конфигурации |
Telegram
| Метод |
Описание |
| telegram.bot(TEMPLATE, CMD, [ARGS]) |
Выполнить CMD с аргументами ARGS из шаблона TEMPLATE |
Задачи
| Метод |
Описание |
| task |
Работа с задачами |
Вспомогательные ф-ии
| Метод |
Описание |
| params |
Аргументы вызова шаблона (http query string) |
| event_name |
Переменная содержит название текущего события |
| toJson() |
Функция преобразования объекта в JSON |
| toQueryString() |
Функция преобразования объектов в Query string |
| ref() |
Функция для преобразования данных в массив |
| list_for_api() |
Функция для получения списков данных из объекта |
| filter() |
Метод для точечной выборки данных |
| misc |
Вспомогательные ф-ии |
Примеры
1 - Вспомогательные функции и методы
misc
Объект misc предоставляет доступ к вспомогательным функциям из модуля Core::Utils. Эти функции помогают работать с датами, строками, JSON, файлами и другими полезными операциями прямо в шаблонах.
Работа с датами и временем
now()
Возвращает текущую дату и время в формате “YYYY-MM-DD HH:MM:SS”.
Синтаксис:
{{ misc.now }}
Пример:
Текущее время: {{ misc.now }}
Результат: Текущее время: 2025-10-20 15:30:45
Преобразует Unix timestamp в строку с датой.
Параметры:
timestamp - Unix timestamp (необязательный, по умолчанию текущее время)
format - формат даты (необязательный, по умолчанию “%Y-%m-%d %H:%M:%S”)
Синтаксис:
{{ misc.utime_to_string(1698756000) }}
{{ misc.utime_to_string(1698756000, "%d.%m.%Y") }}
string_to_utime(date_string)
Преобразует строку с датой в Unix timestamp.
Параметры:
date_string - строка с датой в формате “YYYY-MM-DD HH:MM:SS”
Синтаксис:
{{ misc.string_to_utime("2025-01-01 12:00:00") }}
parse_date(date_string)
Разбирает строку с датой и возвращает хеш с компонентами.
Параметры:
date_string - строка с датой (необязательный, по умолчанию текущая дата)
Синтаксис:
{{ SET date_parts = misc.parse_date("2025-12-31 23:59:59") }}
Год: {{ date_parts.year }}, Месяц: {{ date_parts.month }}
add_date_time(date, options)
Добавляет указанное количество времени к дате.
Параметры:
date - исходная дата (необязательный, по умолчанию текущая дата)
options - хеш с параметрами: year, month, day, hour, min, sec
Синтаксис:
{{ misc.add_date_time(misc.now, { day = 7 }) }}
{{ misc.add_date_time("2025-01-01 00:00:00", { month = 1, day = 15 }) }}
start_of_month(date)
Возвращает начало месяца для указанной даты.
Синтаксис:
{{ misc.start_of_month("2025-03-15 14:30:00") }}
Результат: 2025-03-01 00:00:00
start_of_day(date)
Возвращает начало дня для указанной даты.
Синтаксис:
{{ misc.start_of_day("2025-03-15 14:30:00") }}
Результат: 2025-03-15 00:00:00
end_of_month(date)
Возвращает конец месяца для указанной даты.
Синтаксис:
{{ misc.end_of_month("2025-02-15 14:30:00") }}
Результат: 2025-02-28 23:59:59
days_in_months(date)
Возвращает количество дней в месяце для указанной даты.
Синтаксис:
{{ misc.days_in_months("2025-02-15") }}
Результат: 28
Форматирует разность между указанной датой и текущим временем в читаемом виде.
Параметры:
target_date - целевая дата в формате “YYYY-MM-DD HH:MM:SS”
Синтаксис:
{{ misc.format_time_diff("2025-12-31 23:59:59") }}
Результат: 2 месяца, 11 дней, 8 часов
Работа с JSON
encode_json(data, options)
Преобразует данные в JSON строку с UTF-8 кодировкой.
Параметры:
data - данные для преобразования
options - хеш с параметрами (pretty - для красивого форматирования)
Синтаксис:
{{ misc.encode_json({ name = "Test", value = 123 }) }}
{{ misc.encode_json(data, { pretty = 1 }) }}
encode_json_perl(data, options)
Преобразует данные в JSON строку с внутренней кодировкой Perl (для совместимости с кириллицей).
Синтаксис:
{{ misc.encode_json_perl({ name = "Тест", value = 123 }) }}
decode_json(json_string)
Преобразует JSON строку в структуру данных.
Синтаксис:
{{ SET data = misc.decode_json('{"name":"Test","value":123}') }}
{{ data.name }}
Работа со строками и кодированием
html_escape(string)
Экранирует HTML символы в строке.
Синтаксис:
{{ misc.html_escape('<script>alert("XSS")</script>') }}
Результат: <script>alert("XSS")</script>
encode_base64(string)
Кодирует строку в Base64.
Синтаксис:
{{ misc.encode_base64("Hello World") }}
decode_base64(encoded_string)
Декодирует строку из Base64.
Синтаксис:
{{ misc.decode_base64("SGVsbG8gV29ybGQ=") }}
encode_base64url(string)
Кодирует строку в Base64 URL-safe формат.
Синтаксис:
{{ misc.encode_base64url("Hello World!") }}
decode_base64url(encoded_string)
Декодирует строку из Base64 URL-safe формата.
Синтаксис:
{{ misc.decode_base64url("SGVsbG8gV29ybGQh") }}
to_query_string(hash)
Преобразует хеш в query string для URL.
Синтаксис:
{{ misc.to_query_string({ name = "test", value = "123" }) }}
Результат: name=test&value=123
Генерация данных
passgen(length)
Генерирует случайный пароль.
Параметры:
length - длина пароля (необязательный, по умолчанию 10)
Синтаксис:
{{ misc.passgen(12) }}
{{ misc.passgen() }}
uuid_gen()
Генерирует UUID.
Синтаксис:
{{ misc.uuid_gen() }}
Результат: 123e4567-e89b-12d3-a456-426614174000
get_random_value(value)
Возвращает случайное значение из массива или само значение, если это не массив.
Синтаксис:
{{ misc.get_random_value(["red", "green", "blue"]) }}
{{ misc.get_random_value("single_value") }}
Работа с массивами и хешами
hash_merge(hash1, hash2, ...)
Объединяет несколько хешей в один.
Синтаксис:
{{ SET merged = misc.hash_merge(hash1, hash2, hash3) }}
uniq_by_key(array, key)
Возвращает уникальные элементы массива по указанному ключу.
Синтаксис:
{{ SET unique_items = misc.uniq_by_key(items, "id") }}
Валидация
is_email(email)
Проверяет, является ли строка корректным email адресом.
Синтаксис:
{{ IF misc.is_email("test@example.com") }}
Корректный email
{{ END }}
is_host(hostname)
Проверяет, является ли строка корректным hostname или IP адресом.
Синтаксис:
{{ IF misc.is_host("example.com") }}
Корректный хост
{{ END }}
is_ip_allowed(ip, networks)
Проверяет, входит ли IP адрес в список разрешенных сетей.
Синтаксис:
{{ IF misc.is_ip_allowed("192.168.1.1", ["192.168.1.0/24", "10.0.0.0/8"]) }}
IP разрешен
{{ END }}
Работа с периодами
parse_period(period)
Разбирает строку периода в формате “месяцы.дни.часы”.
Параметры:
period - строка в формате “M.DD.HH”
Синтаксис:
{{ SET period = misc.parse_period("3.15.12") }}
Месяцы: {{ period.months }}, Дни: {{ period.days }}, Часы: {{ period.hours }}
Полезные функции
dots_str_to_sql(string)
Преобразует строку с точками в SQL запрос для JSON полей.
Синтаксис:
{{ SET sql_info = misc.dots_str_to_sql("settings.api.key") }}
Поле: {{ sql_info.field }}, Запрос: {{ sql_info.query }}
get_user_ip()
Возвращает IP адрес пользователя.
Синтаксис:
{{ misc.get_user_ip() }}
Примеры использования
Работа с датами
Сегодня: {{ misc.now }}
Завтра: {{ misc.add_date_time(misc.now, { day = 1 }) }}
Начало месяца: {{ misc.start_of_month(misc.now) }}
Генерация паролей и UUID
Новый пароль: {{ misc.passgen(16) }}
UUID для записи: {{ misc.uuid_gen() }}
Работа с JSON
{{ SET user_data = { name = "Иван", age = 30 } }}
JSON: {{ misc.encode_json_perl(user_data, { pretty = 1 }) }}
Валидация данных
{{ IF misc.is_email(user.email) }}
Email корректен: {{ user.email }}
{{ ELSE }}
Некорректный email
{{ END }}
Важные замечания
Кодировка и локализация
- Используйте
encode_json_perl() для данных с кириллицей в шаблонах
encode_json() подходит для API с UTF-8 кодировкой
- Функция
format_time_diff() возвращает текст на русском языке
Безопасность
- Всегда используйте
html_escape() для пользовательских данных перед выводом в HTML
- Функции валидации (
is_email, is_host) обеспечивают базовую проверку формата
Производительность
- UUID генерация читает системный файл
/proc/sys/kernel/random/uuid
Совместимость
- Все функции даты работают с форматом “YYYY-MM-DD HH:MM:SS”
- Base64 функции совместимы со стандартом RFC 4648
- JSON функции поддерживают вложенные структуры данных
2 - Использование методов
Ряд методов, описанных в разделе Шаблоны не будут работать в ряде случаев.
Работа с идентификаторами
Практически все методы поддерживают работу с идентификаторами (id).
Например, user.id вернет идентификатор текущего пользователя, а us.id вернет идентификатор текущей пользовательской услуги.
Если метод не возвращает свой id, то значит он небыл инициализирован, и следует “добираться” до метода либо через услугу пользователя, либо
через явное указание идентификатора: id( N ). Например, в ряде случаев, server.id не вернет свой идентификатор, однако: us.service.server.id - вернет.
Помимо получения идентификатора, его можно и установить, пример:
user.id(123) - вернет объект для пользователя с идентификатором 123.
user.login - вернет login текущего пользователя
user.id(123).login - вернет login пользователя 123
us.name - вернет название текущей услуги пользователя (если определена)
us.id(99).name - вернет название услуги пользователя с идентификатором 99
Контекст применения шаблонов
| Метод |
Контекст |
| us.* |
События |
| us.id( N ).* |
Везде |
| us.withdraw |
Для платных услуг |
| us.service.server |
Для услуг, у которых определен сервер (server_id в settings услуги пользователя) |
| server.* |
События, Задачи, INIT сервера |
| server.id( N ) |
Везде |
| task |
События, Задачи |
3 - Платежные системы
Описание
SHM имеет встроенный модуль paysystems, позволяющий получать список доступных платежных систем.
Получить список платежных систем можно:
- Через Web:
/shm/v1/user/pay/paysystems
- Из шаблонов:
pay.paysystems( АРГУМЕНТЫ )
Аргументы
| Параметр |
Описание |
| user_id |
Идентификатор пользователя |
| amount |
Сумма платежа. По-умолчанию в неё записывается значение total из “Прогноз оплаты” |
| pp |
Proposed Payment (Предлагаемый платеж). Установите этот параметр в 1, если хотите, чтобы сумма платежа (amount) была установлена в ссылку shm_url |
| paysystem |
Укажите платежную систему, если хотите получить ссылку только для одной конкретной платежной системы |
Описание полей
Пример ответа:
{
"TZ": "Europe/Moscow",
"data": [
{
"amount": 123,
"forecast": 0,
"name": "yookassa",
"paysystem": "yookassa",
"shm_url": "/shm/pay_systems/yookassa.cgi?action=create&user_id=1&ts=1706539331&amount=",
"user_id": 1,
"weight": 10
},
{
"amount": 123,
"forecast": 0,
"name": "ЮMoney",
"paysystem": "yoomoney",
"shm_url": "/shm/pay_systems/yoomoney.cgi?action=create&user_id=1&ts=1706539331&amount=",
"user_id": 1,
"weight": 0
}
],
"date": "Mon Jan 29 17:42:11 2024",
"items": 0,
"limit": 25,
"offset": 0,
"version": "0.10.0"
}
Где:
paysystem - Платежная система
name - Произвольное название платежной системы (для отображения)
amount - Сумма платежа. По-умолчанию заполняется из “Прогноз оплаты”, если он положительный, или из переданного аргумента amount. В остальных случаях - пустой.
forecast - “Прогноз оплаты”
weight - “вес”. Используется для сортировки платежных систем
shm_url - Ссылка для выставления счета в платежной системе. Если флаг pp не установлен, то amount в ссылке остается пустой. Это сделано для удобства использования ссылки.
4 - Фильтры
filter()
Фильтры используются для точечной выборки данных из БД
Например, мы можем получить список пользователей с положительным балансом так:
{{ FOR u IN user.filter( balance = isPositive ).items }}
...
{{ END }}
Синтаксис фильтрации
Важные правила синтаксиса
Кавычки для ключей:
- Кавычки НЕ обязательны для обычных ключей:
status = 1, balance = gt(0)
- Кавычки ОБЯЗАТЕЛЬНЫ только для ключей, содержащие спец. символы:
'-or', '-and', =, !=, a.b …
Запятые:
- В однострочном синтаксисе запятые обязательны:
filter( status = 1, balance = gt(0) )
- В многострочном синтаксисе запятые НЕ нужны при разделении переносом строки
Основные правила записи
Простые значения можно записывать напрямую без использования специальных функций:
{{ FOR u IN user.filter( status = 1 ).items }}
Пользователь {{ u.login }} со статусом 1
{{ END }}
Это эквивалентно использованию eq():
{{ FOR u IN user.filter( status = eq(1) ).items }}
То же самое, что и выше
{{ END }}
Строковые значения также можно записывать напрямую:
{{ FOR u IN user.filter( role = "admin" ).items }}
Администраторы
{{ END }}
Когда использовать функции сравнения
Функции сравнения (eq(), ne(), gt(), etc.) нужны в следующих случаях:
1. Для явного указания типа сравнения:
{{ FOR u IN user.filter( balance = isPositive ).items }}
Пользователи с положительнрым балансом
{{ END }}
2. Для операторов отличных от равенства:
{{ FOR u IN user.filter( age = gt(18) ).items }}
Пользователи старше 18 лет
{{ END }}
3. Для текстовых полей, когда нужно точное совпадение:
{{ FOR u IN user.filter( login = eq("admin") ).items }}
Точно "admin", а не LIKE 'admin' (LIKE регистронезависимый)
{{ END }}
4. В JSON полях для лучшей читаемости:
{{ FOR u IN user.filter( settings = { status = ne(0) } ).items }}
Пользователи с активными настройками
{{ END }}
Краткие формы записи
| Полная форма |
Краткая форма |
Описание |
status = eq(1) |
status = 1 |
Равенство |
enabled = eq(true) |
enabled = true |
Boolean значения |
role = eq("admin") |
role = "admin" |
Строки (для точного сравнения) |
name = eq("") |
name = "" |
Пустая строка |
Специальные случаи
Текстовые поля с LIKE:
# Автоматический LIKE поиск
{{ FOR u IN user.filter( name = "%Петр%" ).items }}
# Точное сравнение
{{ FOR u IN user.filter( name = eq("Петр") ).items }}
Boolean значения:
# Все эти записи эквивалентны
{{ FOR u IN user.filter( enabled = true ).items }}
{{ FOR u IN user.filter( enabled = eq(true) ).items }}
{{ FOR u IN user.filter( enabled = 1 ).items }}
{{ FOR u IN user.filter( enabled = eq(1) ).items }}
NULL значения:
# Простая запись
{{ FOR u IN user.filter( manager_id = null ).items }}
{{ FOR u IN user.filter( manager_id = { '!=' = null } ).items }}
# С функцией
{{ FOR u IN user.filter( manager_id = isNull ).items }}
{{ FOR u IN user.filter( manager_id = isNotNull ).items }}
Многострочный синтаксис
В многострочных фильтрах действуют особые правила:
1. Запятые не требуются при разделении ключ-значение переносом строки:
{{ FOR u IN user.filter(
balance = gt(0)
status = ne(0)
description = isNotEmpty
).items }}
Фильтр без запятых
{{ END }}
2. Сравнение однострочного и многострочного синтаксиса:
# Однострочный (с запятыми)
{{ FOR u IN user.filter( status = 1, balance = gt(0), role = "admin" ).items }}
# Многострочный (без запятых)
{{ FOR u IN user.filter(
status = 1
balance = gt(0)
role = "admin"
).items }}
Принципы работы фильтрации
Логический оператор “И” (AND)
По умолчанию все условия, перечисленные через запятую, объединяются логическим оператором “И”:
{{ FOR u IN user.filter(
balance = gt(0)
status = 1
created = ge('2024-01-01')
).items }}
Пользователь {{ u.login }} соответствует ВСЕМ условиям одновременно
{{ END }}
Это эквивалентно SQL: WHERE balance > 0 AND status = 1 AND created >= '2024-01-01'
Логический оператор “ИЛИ” (OR)
Для реализации логики “ИЛИ” есть несколько способов:
Способ 1: Использование специального ключа -or
{{ FOR u IN user.filter( '-or' = { balance = 0, login = "test" } ).items }}
Пользователь {{ u.login }} с нулевым балансом ИЛИ логином "test"
{{ END }}
Можно комбинировать -or с обычными условиями:
{{ FOR u IN user.filter(
status = 1
'-or' = {
balance = gt(1000)
settings = { vip = true }
}
).items }}
Активный пользователь с балансом больше 1000 ИЛИ VIP статусом
{{ END }}
Способ 2: Объединение результатов нескольких фильтров
{{ SET positive_users = user.filter( balance = gt(0) ).items }}
{{ SET premium_users = user.filter( status = eq(2) ).items }}
{{ FOR u IN positive_users.merge(premium_users).unique }}
Пользователь {{ u.login }} имеет положительный баланс ИЛИ премиум статус
{{ END }}
Способ 3: Массивы значений для одного поля
{{ FOR u IN us.filter( status = [STATUS_ACTIVE,STATUS_BLOCK,STATUS_WAIT_FOR_PAY] ).items }}
Услуги пользователя с указанными статусами
{{ END }}
Автоматическое использование LIKE для текстовых полей
Для текстовых полей в базе данных автоматически применяется оператор LIKE при использовании символов подстановки %. Это позволяет выполнять гибкий поиск по шаблонам.
Принцип работы
Когда вы указываете значение для текстового поля, система автоматически:
- Определяет тип поля в структуре таблицы
- Если поле имеет текстовый тип (
text, varchar, etc.) И значение содержит символы %, применяет оператор LIKE
- Если символов
% нет, используется точное сравнение =
Примеры LIKE поиска
Поиск по подстроке в имени:
{{ FOR u IN user.filter(full_name = "%Иван%").items }}
Найден пользователь {{ u.login }} с именем содержащим "Иван"
{{ END }}
Генерирует SQL: WHERE full_name LIKE '%Иван%'
Поиск по началу логина:
{{ FOR u IN user.filter(login = "admin%").items }}
Найден пользователь {{ u.login }} начинающийся с "admin"
{{ END }}
Генерирует SQL: WHERE login LIKE 'admin%'
Поиск по окончанию email:
{{ FOR u IN user.filter(email = "%@company.com").items }}
Пользователи с корпоративным email
{{ END }}
Генерирует SQL: WHERE email LIKE '%@company.com'
Точное совпадение для текстовых полей
Если вам нужно точное совпадение, НЕ используйте символы %:
{{ FOR u IN user.filter(login = "admin").items }}
Пользователь с точным логином "admin"
{{ END }}
Генерирует SQL: WHERE login LIKE 'admin'
Альтернативно, можно использовать оператор eq():
{{ FOR u IN user.filter(login = eq("admin")).items }}
Пользователь с точным логином "admin"
{{ END }}
Генерирует SQL: WHERE login = 'admin'
Кастомные LIKE паттерны
Для более сложных паттернов поиска используйте явное указание LIKE операторов:
{{ FOR u IN user.filter(email = { '-like' = "%@gmail.com" }).items }}
Пользователи с email на Gmail
{{ END }}
{{ FOR u IN user.filter(login = { '-not_like' = "test%" }).items }}
Пользователи, логин которых НЕ начинается с "test"
{{ END }}
Смешанные условия с LIKE
{{ FOR u IN user.filter(
full_name = "%Петр%"
email = "%@company.com"
status = eq(1)
).items }}
Активные пользователи с именем содержащим "Петр" и корпоративным email
{{ END }}
В этом примере:
full_name LIKE '%Петр%' (LIKE из-за символов %)
email LIKE '%@company.com' (LIKE из-за символов %)
status = 1 (точное сравнение для числового поля)
Важно: Символы % нужно добавлять самостоятельно для активации LIKE поиска!
Работа с JSON полями
Важно: Для работы с JSON полями используйте формат json_поле = { 'ключ' = значение }, а НЕ 'json_поле.ключ' = значение.
Для фильтрации по полям внутри JSON используйте хеш с ключами JSON полей:
{{ FOR u IN user.filter(settings = { notifications = true }).items }}
Пользователь {{ u.login }} с включенными уведомлениями
{{ END }}
Для вложенных JSON структур:
{{ FOR u IN user.filter( profile = { 'preferences.theme' = 'dark' }).items }}
Пользователь {{ u.login }} с темной темой
{{ END }}
Проверка существования JSON поля
Для проверки существования поля в JSON используйте имя поля в точечной нотации:
{{ FOR u IN user.filter(settings = 'notifications').items }}
Пользователь {{ u.login }} у которого есть поле notifications в settings
{{ END }}
Для вложенных полей:
{{ FOR u IN user.filter(settings = 'profile.avatar').items }}
Пользователь {{ u.login }} у которого есть поле profile.avatar в settings
{{ END }}
Проверка отсутствия JSON поля
Для проверки отсутствия поля в JSON используйте префикс !:
{{ FOR u IN user.filter(settings = '!notifications').items }}
Пользователь {{ u.login }} у которого НЕТ поля notifications в settings
{{ END }}
Для вложенных полей:
{{ FOR u IN user.filter(settings = '!profile.avatar').items }}
Пользователь {{ u.login }} у которого НЕТ поля profile.avatar в settings
{{ END }}
Проверка значений в JSON полях
Для проверки конкретных значений используйте хеш с точечной нотацией:
{{ FOR u IN user.filter(settings = { notifications = true, theme = 'dark' }).items }}
Пользователь {{ u.login }} с темной темой и включенными уведомлениями
{{ END }}
С операторами сравнения:
{{ FOR u IN user.filter(settings = { max_items = gt(10), priority = le(5) }).items }}
Пользователь {{ u.login }} с max_items > 10 и priority <= 5 в настройках
{{ END }}
Комбинирование JSON условий
{{ FOR u IN user.filter(
settings = 'api_key'
settings = { notifications = true, auto_backup = ne(false) }
).items }}
Пользователь {{ u.login }} с API ключом, уведомлениями и автобэкапом
{{ END }}
Проверка JSON полей с специальными функциями
{{ FOR u IN user.filter(settings = { api_key = isNotNull }).items }}
Пользователь {{ u.login }} с настроенным API ключом
{{ END }}
{{ FOR u IN user.filter(profile = { avatar = isEmpty }).items }}
Пользователь {{ u.login }} без аватара в профиле
{{ END }}
Практические примеры синтаксиса
Сравнение различных подходов
Числовые значения:
# Простая запись (рекомендуется для равенства)
{{ FOR u IN user.filter( user_id = 123 ).items }}
# Явная запись (когда нужна ясность)
{{ FOR u IN user.filter( user_id = eq(123) ).items }}
# Операторы сравнения (обязательно через функции)
{{ FOR u IN user.filter( user_id = gt(1000) ).items }}
Строковые значения:
# Точное совпадение
{{ FOR u IN us.filter( status = STATUS_ACTIVE ).items }}
# LIKE поиск (с символами %)
{{ FOR u IN us.filter( status = "%ACTIVE%" ).items }}
# Явное точное совпадение (рекомендуется)
{{ FOR u IN us.filter( status = eq(STATUS_ACTIVE) ).items }}
Boolean значения:
# Простая запись (рекомендуется)
{{ FOR u IN user.filter( enabled = true ).items }}
{{ FOR u IN user.filter( deleted = false ).items }}
# Числовая запись (также работает)
{{ FOR u IN user.filter( enabled = 1 ).items }}
{{ FOR u IN user.filter( deleted = 0 ).items }}
JSON поля:
# Простые значения в JSON
{{ FOR u IN user.filter( settings = { theme = 'dark' } ).items }}
# Функции сравнения в JSON
{{ FOR u IN user.filter( settings = { max_items = gt(10) } ).items }}
# Специальные функции в JSON
{{ FOR u IN user.filter( settings = { api_key = isNotNull } ).items }}
Рекомендации по выбору синтаксиса
Используйте простую запись когда:
- Проверяете равенство:
block = 1
- Работаете с boolean:
enabled = true
- Нужно совпадение строки без учета регистра:
role = "admin"
- Нужно совпадение части строки:
role = "adm%"
Используйте функции когда:
- Нужны операторы сравнения:
age = gt(18)
- Работаете с NULL:
field = isNull
- Нужны специальные проверки:
name = isEmpty
- Нужно точное совпадение строки:
role = eq("admin")
Специальные функции для фильтрации
Проверка на NULL и пустые значения
isNull
Проверяет, что поле равно NULL:
{{ FOR u IN user.filter( avatar = isNull ).items }}
Пользователь {{ u.login }} без аватара
{{ END }}
isNotNull
Проверяет, что поле не равно NULL:
{{ FOR u IN user.filter( avatar = isNotNull ).items }}
Пользователь {{ u.login }} с аватаром
{{ END }}
isEmpty
Проверяет, что поле пустое (NULL или пустая строка):
{{ FOR u IN user.filter( description = isEmpty ).items }}
Пользователь {{ u.login }} без описания
{{ END }}
isNotEmpty
Проверяет, что поле не пустое:
{{ FOR u IN user.filter( description = isNotEmpty ).items }}
Пользователь {{ u.login }} с описанием
{{ END }}
Числовые сравнения
lt(значение) (<)
Меньше указанного значения:
{{ FOR u IN user.filter( balance = lt(100) ).items }}
Пользователь {{ u.login }} с балансом меньше 100
{{ END }}
gt(значение) (>)
Больше указанного значения:
{{ FOR u IN user.filter( balance = gt(1000) ).items }}
Пользователь {{ u.login }} с балансом больше 1000
{{ END }}
le(значение) (<=)
Меньше или равно указанному значению:
{{ FOR u IN user.filter( balance = le(0) ).items }}
Пользователь {{ u.login }} с нулевым или отрицательным балансом
{{ END }}
ge(значение) (>=)
Больше или равно указанному значению:
{{ FOR u IN user.filter( balance = ge(500) ).items }}
Пользователь {{ u.login }} с балансом от 500
{{ END }}
eq(значение) (==)
Равно указанному значению:
{{ FOR u IN user.filter( status = eq(1) ).items }}
Активный пользователь {{ u.login }}
{{ END }}
ne(значение) (!=)
Не равно указанному значению:
{{ FOR u IN user.filter( status = ne(0) ).items }}
Пользователь {{ u.login }} (не заблокирован)
{{ END }}
between(мин, макс) ([..])
Значение в диапазоне (включительно):
{{ FOR u IN user.filter( balance = between(100, 1000) ).items }}
Пользователь {{ u.login }} с балансом от 100 до 1000
{{ END }}
Проверка знака числа
isPositive (>0)
Проверяет, что значение больше нуля (положительное):
{{ FOR u IN user.filter( balance = isPositive ).items }}
Пользователь {{ u.login }} с положительным балансом
{{ END }}
isNegative (<0)
Проверяет, что значение меньше нуля (отрицательное):
{{ FOR u IN user.filter( balance = isNegative ).items }}
Пользователь {{ u.login }} с отрицательным балансом
{{ END }}
isNonNegative (>=0)
Проверяет, что значение больше или равно нулю (неотрицательное):
{{ FOR u IN user.filter( balance = isNonNegative ).items }}
Пользователь {{ u.login }} с неотрицательным балансом
{{ END }}
isNonPositive (<=0)
Проверяет, что значение меньше или равно нулю (неположительное):
{{ FOR u IN user.filter( balance = isNonPositive ).items }}
Пользователь {{ u.login }} с неположительным балансом
{{ END }}
Сложные примеры фильтрации
Комбинирование разных типов условий
{{ FOR u IN user.filter(
balance = gt(0)
status = ne(0)
description = isNotEmpty
settings = { notifications = true }
created = between('2024-01-01', '2024-12-31')
).items }}
Активный пользователь {{ u.login }} с положительным балансом, описанием,
включенными уведомлениями, созданный в 2024 году
{{ END }}
Фильтрация с использованием JSON и специальных функций
{{ FOR s IN service.filter(
enabled = true
price = ge(100)
config = { auto_renewal = false }
description = isNotEmpty
).items }}
Активная услуга {{ s.name }} стоимостью от 100 без автопродления
{{ END }}
Комбинирование AND и OR условий
{{ FOR u IN user.filter(
status = eq(1)
balance = ge(0)
'-or' = {
profile = { type = 'premium' }
created = ge('2024-01-01')
settings = { notifications = true }
}
).items }}
Активный пользователь с неотрицательным балансом И (премиум профиль ИЛИ создан в 2024 ИЛИ включены уведомления)
{{ END }}
Сложная JSON фильтрация
{{ FOR u IN user.filter(
settings = 'api_key'
settings = '!deprecated_features'
settings = {
max_sessions = gt(1)
auto_logout = true
'security.two_factor' = ne(false)
}
profile = { 'preferences.language' = ['ru', 'en'] }
).items }}
Пользователь {{ u.login }} с API ключом, без устаревших функций,
с настройками безопасности и поддержкой русского или английского языка
{{ END }}
Специальные значения
Также доступны специальные константы:
null - значение NULL
true - логическое истина (1)
false - логическое ложь (0)
{{ FOR s IN service.filter( enabled = true ).items }}
Активная услуга {{ s.name }}
{{ END }}
5 - Функции
toJson()
Преобразует объект в JSON.
Данную ф-ию удобно использовать для отладки запросов, когда не очевидно, что вернет тот или иной метод, для просмотра полей и т.п.
Пример 1:
Смотрим, что вернет метод user.list_for_api:
{{ toJson( user.list_for_api ) }}
Результат:
{
"balance": 123.45,
"block": 0,
"bonus": 0,
"can_overdraft": 0,
"created": "2024-01-08 15:18:16",
"credit": 0,
"discount": 0,
"full_name": "Admin",
"last_login": "2024-01-22 20:52:53",
"login": "admin",
"user_id": 1
}
Пример 2:
Строим JSON объект. Удобно для использования в Telegram bot-е, в HTTP запросах и т.п.:
{{
toJson(
a = user.id
b = 2
c = [3,4,5]
)
}}
Результат:
{"a":1,"b":2,"c":[3,4,5]}
toQueryString()
Преобразование объекта в Query String:
Пример:
{{
toQueryString(
a = user.id
b = 2
c = "hello world"
)
}}
Результат:
a=1&b=2&c=hello%20world
list_for_api()
Метод для получения списка данных объекта.
Без аргументов выдаст первые 25 строк данных.
Аргументы:
| Параметр |
Описание |
| admin |
Установка этого параметра в 1 позволяет получить данные всех клиентов |
| limit |
Кол-во отдаваемых данных. По-умолчанию: 25. (0 - без лимитов) |
| offset |
Индекс начала смещения списка. По-умолчанию: 0 |
| filter |
Используется для поиска данных по определенным полям |
| sort_field |
Поле для сортировки (по-умолчанию ключевое поле) |
| sort_direction |
Порядок сортировки: asc, desc (по-умолчанию desc) |
Пример 1:
Выведем всех пользователей:
{{ arr = ref(user.list_for_api('admin', 1)) }}
{{ FOR item IN arr }}
User id: {{ item.user_id }}, Login: {{ item.login }}, Balance: {{ item.balance }}
{{ END }}
Результат:
User id: 1, Login: admin, Balance: 0
User id: 22, Login: danuk, Balance: 224.44
User id: 34, Login: Dima, Balance: 0
User id: 117, Login: xims, Balance: 200
Пример 2:
Выведем список всех услуг с категорией начинающиеся на web, и период которых равен 1 месяцу:
{{ arr = ref(service.list_for_api('filter', { 'category' => 'web%', period => 1 } )) }}
{{ FOR item IN arr }}
Service id: {{ item.service_id }}, Name: {{ item.name }}, Cost: {{ item.cost }}
{{ END }}
Результат:
Service id: 111, Name: Web хостинг, Cost: 0
Service id: 110, Name: Тариф X-MAX, Cost: 300
Service id: 5, Name: Web хостинг LITE, Cost: 0
Сортировка
Существует два типа сортировки:
- Сортировка на уровне запросов
- Сортировка на уровне шаблона
При использовании пагинации важно сортировать результаты на уровне запросов.
Сортировка на уровне запросов
Сортировка на уровне запросов осуществляется непосредственно в БД.
Пример выдачи результатов отсортированных по возрастанию:
{{ arr = ref(service.list_for_api('sort_direction','asc')) }}
Пример выдачи результатов отсортированных по возрастанию по полю name:
{{ arr = ref(service.list_for_api('sort_direction','asc', 'sort_field','name')) }}
Сортировка в шаблонах
Сортировка в шаблонах используется для сортировки полученных данных.
Для сортировки используете функции: sort (для алфавитной сортировки), nsort (для числовой сортировки) и reverse (обратный порядок данных).
Пример сортировки по полю category:
{{ arr = ref(service.list_for_api().sort('cateogry')) }}
Пример сортировки по полю cost, в убывающем порядке:
{{ arr = ref(service.list_for_api().nsort('cost').reverse) }}
6 - Автоматизации
6.1 - Подсчет доходов за месяц
Ниже приведен пример шаблона для подсчета поступления средств за Январь 2024 года:
{{ sum = user.pays.filter( date = '2024-01-%' ).sum( all_users = 1 ).money }}
Итого за Январь: {{ sum }} руб.
6.2 - Рассылки
SHM умеет делать рассылки по всем пользователям.
Для того, чтобы сделать рассылку, необходимо:
- В кабинете Администратора выбрать пункт меню: “Задачи -> Текущие задачи”, далее нажать кнопку “ADD”.
- Выберите для кого сделать рассылку: для одного пользователя или для всех (при тестировании используйте одного конкретного пользователя)
- Выберите группу серверов, которая будет использоваться для рассылки
- Выберите необходимый шаблон
- Создайте задачу
- Контролируйте исполнение задчи в “Текущие задачи”
В случае, если после рендера шаблон получается пустой, то такое сообщение отправлено не будет.
Этот свойство используется для выборочной отправки сообщений.
Примеры шаблонов
Шаблон для всех клиентов
Уважаемый {{ user.full_name }}!
Это тестовое сообщение.
Ваш личный кабинет находится по адресу: {{ config.cli.url }}
Шаблон для пользователей Telegram
Если рассылку делать с помощью транспорта Телеграм, то данная проверка в
шаблоне смысла не имеет, так как транспорт Телеграм сам проверяет это поле.
{{ IF user.settings.telegram.login }}
Вы получили это сообщение потому, что у Вас есть Telegram.
Ваш Telegram логин: {{ user.settings.telegram.login }}
{{ END }}
Шаблон для услуг в определенной категории
Данный шаблон будет отправлен только клиентам, у которых есть услуги в категории test.
Категорию услуг можно указать и через маску, например так: te%.
{{ IF user.services.list_for_api('category','test') }}
Уважаемый {{ user.full_name }}!
Вы получили это сообщение потому, что у Вас есть услуга в категории test.
{{ END }}
Информация об услугах в определенной категории
Показываем сообщение для каждой услуги в категории test.
{{ user_services = ref(user.services.list_for_api('category','test')) }}
{{ IF user_services }}
Услуги в категории test найдены в количестве: {{ user_services.size }} штук.
{{ FOR item IN user_services }}
Услуга: {{ item.name }}
Категория: {{ item.category }}
Статус: {{ item.status }}
Действует до: {{ item.expire }}
Цена: {{ item.withdraws.cost }}
Кол-во: {{ item.withdraws.qnt }}
Cкидка: {{ item.withdraws.discount }}%
Итого: {{ item.cost }}
{{ END }}
{{ END }}
Активная услуга в определенной категории
Перебираем (ищем) все услуги пользователя с категорией test, и проверяем, что статус активный.
Как только активная услуга будет найдена, то пишем сообщение и прекращаем перебор (LAST).
Таким образом, будет выведено только одно сообщение, даже если услуг несколько.
Если нужно выводить сообщения для каждой услуги, необходимо убрать LAST.
{{ FOR item IN ref(user.services.list_for_api('category','test')) }}
{{ IF item.status == 'ACTIVE' }}
Вы видите это сообщение, потому что у вас есть активная услуга в категории test
{{ LAST }}
{{ END }}
{{ END }}
Уведомление клиентам без услуг в определенной категории
{{ IF ref(user.services.list_for_api('category','test')).empty }}
Вы видите это сообщение, потому что у вас нет ниодной услуги в категории test
{{ END }}
Подробнее о создании и настройке шаблонов можно прочитать здесь
6.3 - Удаление услуг при блокировке
Для удаления услуг при блокировке создайте шаблон вида:
{{ IF us.status == 'BLOCK' }}
{{ us.delete }}
{{ END }}
и добавьте его к событию CHANGED для нужной категории услуг.
Каждый раз, когда услуга будет переходить в статус BLOCK, SHM будет удалять её.
6.4 - Массовое начисление бонусов
Бывают случаи, когда необходимо начислить бонусы всем клиентам. Здесь приведены примеры, как это сделать.
Массовое начисление бонусов клиентам с указанной активной услугой:
Следующий код начислит всем клиентам с активной услугой 5 по 100 бонусов:
{{ FOR u IN user.items }}
{{ us_list = u.us.filter( service_id = 5, status = 'ACTIVE' ).items }}
{{ IF us_list.size }}
{{ u.add_bonus( 100, 'Акция' ) }}
{{ END }}
{{ END }}
6.5 - Массовое обновление тарифов
Бывают случаи, когда нужно обновить тарифы всем клиентам. Здесь приведены примеры, как это сделать.
Обновление стоимости текущей услуги (тарифа)
Просто изменить стоимость услуги в Каталоге не достаточно.
Необходимо для таких услуг пользователя установить “следующую” услугу в “текущую”:
{{ FOR u IN user.items }}
{{ FOR us IN u.us.items }}
{{ us.set(next = us.service_id) }}
{{ END }}
{{ END }}
Массовая смена услуг (тарифов)
Например, мы хотим сменить (со следующего учетного периода) услугу 5 на 6, делается это так:
{{ FOR u IN user.items }}
{{ FOR us IN u.us.filter( service_id = 5 ).items }}
{{ us.set(next = 6) }}
{{ END }}
{{ END }}
7 - Уведомления клиентам
Для отправки уведомлений клиентам создайте соответствующее событие и привяжите к нему шаблон.
Уведомления будут отправлены с помощью выбранного транспорта. Например, Вы можете отправить уведомление EMAIL, и/или Telegram (настраивается в событии).
7.1 - Прогноз оплаты
Описание
SHM имеет встроенный модуль forecast, позволяющий получать информацию о прогнозе оплаты услуг.
forecast находит все услуги (кроме уже заблокированных), дата истечения которых менее чем 3 дня (days), и ещё неоплаченные услуги, и вычисляет стоимость их продления.
Получить прогноз оплаты можно следующими способами:
- Через Web для текущего авторизованного пользователя:
/shm/v1/user/pay/forecast
- Через Web для пользователя с ID 123:
/shm/v1/user/pay/forecast?user_id=123 (нужно быть авторизованным под администратором)
- Из шаблонов:
user.pays.forecast
Настройки
| Параметр |
Описание |
| days |
Кол-во дней для прогноза (3 дня по-умолчанию) |
| blocked |
Учитывать заблокированные услуги (0 - по-умолчанию НЕТ) |
Пример команды в шаблонах:
{{ forecast = user.pays.forecast(days = 10, blocked = 1) }}
Пример шаблона
Метод user.pays.forecast возвращает JSON вида:
{
"items": [
{
"name": "Регистрация домена в зоне .RU",
"service_id": 11,
"user_service_id": 2949,
"usi": 2949,
"cost": 590,
"discount": 0,
"months": 12,
"qnt": 1,
"total": 590,
"expire": "2017-07-29 12:39:46",
"status": "ACTIVE",
"next": {
"name": "Продление домена в зоне .RU",
"service_id": 12,
"cost": 890,
"discount": 0,
"months": 12,
"qnt": 1,
"total": 890
}
}
],
"balance": -21.56,
"bonuses": 100,
"dept": 21.56,
"total": 768.44
}
Мы можем построить шаблон письма о прогнозе опаты услуг следующим образом:
Уважаемый {{ user.full_name }}
{{ forecast = user.pays.forecast }}
{{ ap = user.make_autopayment( forecast.total ) }}
{{ IF ap == 1 }}
Выполнен автоплатеж в размере: {{ forecast.total }}.
Ваши услуги будут продлены автоматически.
{{ ELSE }}
Уведомляем Вас о сроках действия услуг:
{{ FOR item IN ref(forecast.items) }}
- Услуга: {{ item.name }}
{{ IF item.expire }}
Истекает: {{ item.expire }}
{{ IF item.service_id != item.next.service_id }}
Следующая услуга: {{ item.next.name }}
Стоимость: {{ item.next.total }}
{{ ELSE }}
Стоимость продления: {{ item.next.total }}
{{ END }}
{{ ELSE }}
Стоимость: {{ item.total }}
{{ END }}
{{ END }}
{{ IF forecast.dept }}
Погашение задолженности: {{ forecast.dept }}
{{ END }}
Итого к оплате: {{ forecast.total }} руб.
{{ END }}
Подробнее о создании и настройке шаблонов можно прочитать здесь
7.2 - Уведомление о зачислении платежа
Описание
Создайте шаблон со следующим содержимым и привяжите его к событию PAYMENT:
{{ IF pay.money == 0 }}
Ошибка, платеж не прошел
{{ ELSE }}
Зачислен платеж на сумму: {{ pay.money }} руб.
Ваш баланс: {{ user.balance }} руб.
{{ END }}
8 - HTTP (API)
SHM позволяет использовать шаблоны для внешнего использования
Поддерживаемые методы:
HTTP адрес
Доступ к шаблону с именем my_template можно получить следующим способами:
Для пользователей
Доступ с авторизацией:
/shm/v1/template/my_template?format=html&foo=1&bar=hello
Пример для curl
curl -s -u 'admin:admin' http://127.0.0.1:8081/shm/v1/template/my_template
Более детально о вариантах аутентификации можно почитать здесь
Для публичного использования
/shm/v1/public/my_template?format=html&foo=1&bar=hello
Для того, чтобы шаблон работал без авторизации необходимо прописать в его settings параметр:
allow_public: true
В публичных шаблонах переменная user не устанавливается автоматически, но её можно установить самостоятельно:
-
Указать идентификатор вручную (статически):
в шаблоне: {{ user = user.switch( 123 ) }}
-
Указать идентификатор через HTTP запрос:
/shm/v1/public/my_template?uid=123
в шаблоне: {{ user = user.switch( request.params.uid ) }}
Пример для curl
curl -s http://127.0.0.1:8081/shm/v1/public/my_template?uid=123
Пример шаблона для проверки существования пользователя:
{{ IF user.switch( request.params.uid ).id }}
...
{{ END }}
Аргументы
В строку запроса можно добавить любые аргументы. Внутри самого шаблона к ним можно обратиться через переменную request.params
{{ foo = request.params.foo }}
Ниже приведен список специальных аргументов:
plain (text/plain)
html (text/html)
json (application/json)
other (application/octet-stream)
qrcode
user_id - зарезервирован
В случае работы с шаблоном из под прав администратора можно явно указать идентификатор пользователя
HTTP заголовки
В шаблонах можно читать заголовки. Их удобно использовать для различных проверок
{{ headers = request.headers }}
Пример шаблона для проверки заголовка x-webhook-secret:
{{ IF request.headers.x-webhook-secret == 'something-very-very-secret' }}
...
{{ END }}
Примеры
8.1 - Вывод данных в формате HTML
Пример шаблона (my_template) для формирования данных в HTML:
<table border=1>
{{ FOR u IN ref( user.list_for_api('admin',1,'limit',0)) }}
<tr>
<td>{{ u.user_id }}</td>
<td>{{ u.login }}</td>
<td>{{ u.balance }}</td>
</tr>
{{ END }}
</table>
Пример вызова шаблона для curl:
curl -s http://127.0.0.1:8081/shm/v1/public/my_template?format=html
Необходимо прописать в settings шаблона параметр: allow_public: true
Результат работы HTML шаблона лучше всего смотреть в браузере:
http://127.0.0.1:8081/shm/v1/public/my_template?format=html
8.2 - Вывод данных в формате JSON
Для формирования JSON объекта удобно использовать следующий синтаксис:
Пример шаблона (my_template) для формирования данных в JSON:
{{ u = user.id( request.params.uid ) }}
{{
toJson({
user_id = u.id
login = u.login
balance = u.balance
last_payment = u.pays.last
forecast = u.pays.forecast.total
})
}}
Пример вызова шаблона для curl:
curl -s http://127.0.0.1:8081/shm/v1/public/my_template?format=json&uid=123
Необходимо прописать в settings шаблона параметр: allow_public: true
Пример результата:
{
"user_id": "123",
"login": "danuk",
"balance": -21.56,
"last_payment": {
"comment": null,
"date": "2016-01-04 20:33:35",
"id": 2,
"money": 455,
"pay_system_id": "manual",
"user_id": 123
},
"forecast": 1013.45
}
9 - Выполнение скриптов
Шаблоны позволяют генерировать как однострочные команды, так и целые блоки текста.
Эти механизмы являются основой построения взаимодействий SHM.
10 - Telegram bot
Шаблон для Telegram Bot-а
Это двух-уровневый шаблон. Сначала используются теги <% ... %> для нахождения
нужной секции шаблона, соответствующей команды. После нахождения нужной секции
шаблонизатор будет использовать теги вида: {{ ... }}.
Для сопоставления команды пользователя/бота используется внутренняя переменная cmd.
Так, при наборе команды /balance будет найдена секция: <% CASE '/balance' %>.
Команда USER_NOT_FOUND является встроенной в SHM, и вызывается автоматичеки, когда SHM не может найти у себя этого пользователя.
В каждой секции мы можем писать реальные команды Telegram. Например, команда sendMessage отправляет сообщение в Telegram.
Вы можете использовать эту команду в соответсвии с документацией Telegram.
Для отправки команд в API Telegram используйте метод tg_api.
Для того, чтобы лучше понять, как строятся всевозможные кнопочки, читайте документацию Telegram. В этом шаблоне всего-лишь описаны вызовы этих методов.
Пример шаблона:
<% SWITCH cmd %>
<% CASE 'USER_NOT_FOUND' %>
{{ tg_api( shmRegister = { callback_data = "/menu" } ) }}
<% CASE ['/start', '/menu'] %>
{{ tg_api( sendMessage = {
text = "Я Ваш тестовый Telegram Bot"
reply_markup = {
inline_keyboard = [
[
{
text = "Баланс"
callback_data = "/balance"
}
]
]
}
}
)
}}
<% CASE '/balance' %>
{{ tg_api( deleteMessage = { message_id = message.message_id } ) }}
{{ tg_api( sendMessage = {
text = "Баланс: " _ user.balance
reply_markup = {
inline_keyboard = [
[
{
text = "Назад"
callback_data = "/menu"
}
]
]
}
}
)
}}
<% CASE %>
{{ tg_api( sendMessage = { text = 'Я не знаю команды: ' _ cmd } ) }}
<% END %>
Стандартные методы Telegram
В SHM можно использовать любые методы Telegram.
Полный перечень доступных методов и синтаксис их использования
можно посмотреть на официальном сайте документации Telegram https://core.telegram.org/bots/api#available-methods
chat_id - заполняется автоматически, но при необходимости его можно указать
Встроенные переменные SHM
cmd - специальная переменная SHM в которую записывается значение, полученное из Telegram, в зависимости от типа:
message.text для команд введеных пользователем, и callback_query.data для callback_data. Если команда пользователя начинается
с символа /, то такая команда будет усечена до первого слова.
args - массив аргументов команды, разделитель - пробел.
start_args - именованный массив аргументов для команды start
message - полное сообщение от Telegram (json объект)
Примеры парсинга cmd и args:
| Источник |
Команда |
cmd |
args |
| Пользователь |
/test 1 2 3 |
/test |
[1,2,3] |
| Пользователь |
test 1 2 3 |
test 1 2 3 |
[1,2,3] |
| callback_data |
/test 1 2 3 |
/test |
[1,2,3] |
| callback_data |
test 1 2 3 |
test |
[1,2,3] |
args - это массив. Для получения значений из него используйте синтаксис: args.N, где N - индекс элемента.
Например, получить первый элемент массива можно так: args.0
Передача дополнительных параметров при старте бота
При старте бота может понадобится передача каких-либо данных, например для отслеживания рекламных акций (utm метрики),
или для создания партнерской ссылки.
ВНИМАНИЕ: суммарная длина параметров не может превышать 64 символа
Генерация партнерской ссылки
Для генерации партнерской ссылки можно использовать следующий шаблон:
https://t.me/myshm_bot?start={{ toBase64Url(toQueryString( pid = user.id )) }}
где myshm_bot - имя бота (замените на свой)
Генерация ссылки с произвольными параметрами
https://t.me/myshm_bot?start={{ toBase64Url(toQueryString(
utm_source = 'google'
utm_medium = 'telegram'
foo = 'bar'
))
}}
где myshm_bot - имя бота (замените на свой)
Переменные utm_ автоматически сохраняются в settings пользователя
Чтение переданных параметров
Вы можете прочитать переданный при старте бота параметр с помощью специальной переменной: start_args, пример:
Источник: {{ start_args.utm_source }}
Встроенные методы SHM
shmRegister
Метод позволяет зарегистрировать нового клиента
"shmRegister": {
"callback_data": "/menu",
"error": "ОШИБКА"
}
shmServiceOrder
Метод для регистрации новых услуг. Пример использования:
"shmServiceOrder": {
"service_id": "{{ args.0 }}",
"expire": "2014-09-25 10:11:12",
"parent": 123,
"check_exists": 1,
"check_exists_unpaid": 1,
"check_category": "test-%",
"callback_data": "/menu",
"cb_not_enough_money": "/pay",
"error": "ОШИБКА"
}
где: service_id - ID услуги,
expire - установка произвольной даты истечения услуги (опционально),
parent - ID родительской услуги (опционально),
check_exists - проверка существования услуги (опционально),
check_exists_unpaid - проверка существования неоплаченной услуги (опционально),
check_category - проверка существования услуги по категории (опционально),
callback_data - команда для случая успешного заказа услуги,
cb_not_enough_money - команда для случая нехватки средств для активации услуги.
Если используется один из флагов: check_exists, check_exists_unpaid, check_category и услуга найдена,
то регистрация новой услуги не осуществляется, а метод вернет первую найденную услугу (callback_data или cb_not_enough_money).
shmServiceDelete
Метод для удаления услуг пользователя. Пример использования:
"shmServiceDelete": {
"usi": "{{ args.0 }}",
"callback_data": "/menu",
"error": "ОШИБКА"
}
где: usi - ID услуги пользователя
uploadDocumentFromStorage
Метод загружает данные из Storage и отправляет их в виде файла
"uploadDocumentFromStorage": {
"name": "{{ args.0 }}",
"filename": "{{ args.0 }}.conf"
}
uploadPhotoFromStorage
Метод загружает данные из Storage и отправляет их в виде картинки (QR code)
"uploadPhotoFromStorage": {
"name": "{{ args.0 }}",
"format": "qr_code_png"
}
shmRedirectCallback
Метод вызывает указанную команду (cmd)
"shmRedirectCallback": {
"callback_data": "/help"
}
Приём платежей
Для приёма платежей в Telegram удобно использовать дополнительный шаблон (web_app).
Шаблон используется для возможности ввода произвольной суммы и выбора настроенных платежных систем SHM.
- Настройте одну или несколько платежных систем
- Скачайте шаблон и сохраните в SHM под названием
tg_payments_webapp
- Сделайте шаблон публичным: пропишите в его
settings параметр: allow_public: true
- В Шаблоне своего бота используйте конструкцию вида:
<% CASE '/payment' %>
{{ tg_payment_webapp="$config.api.url/shm/v1/public/tg_payments_webapp?format=html&user_id=$user.id&profile=$tpl.id" }}
{{ tg_api(
sendMessage = {
text = "Оплата покупки",
reply_markup = {
inline_keyboard = [
[{
text = "Оплатить..."
web_app = { url = tg_payment_webapp }
}]
]
}
}
)
}}
Дополнительно можно передавать:
email, например: &email={{ user.settings.email }}
ack_email, например: &ack_email=1 - появится поле ввода email
10.1 - Пагинация
Пример шаблона для постраничного вывода списка услуг
{{
limit = 5
data = []
}}
<% SWITCH cmd %>
<% CASE '/list' %>
{{
offset = args.0 || 0
items = ref( user.services.list_for_api('limit',limit, 'offset',offset) )
}}
{{ FOR item IN items }}
{{ data.push(
[{
"text" = 'Услуга: ' _ item.name _ ' ID: ' _ item.user_service_id
"callback_data" = '/service ' _ item.user_service_id
}]
)
}}
{{ END }}
{{ data.push(
[{
"text" = 'Еще...'
"callback_data" = '/list ' _ (limit + offset)
}]
) IF items.size == limit
}}
{
"sendMessage": {
"text": "Список услуг",
"reply_markup" : {
"inline_keyboard": {{ toJson( data ) }}
}
}
}
<% END %>