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

utime_to_string(timestamp, format)

Преобразует 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

format_time_diff(target_date)

Форматирует разность между указанной датой и текущим временем в читаемом виде.

Параметры:

  • 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>') }}

Результат: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

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 при использовании символов подстановки %. Это позволяет выполнять гибкий поиск по шаблонам.

Принцип работы

Когда вы указываете значение для текстового поля, система автоматически:

  1. Определяет тип поля в структуре таблицы
  2. Если поле имеет текстовый тип (text, varchar, etc.) И значение содержит символы %, применяет оператор LIKE
  3. Если символов % нет, используется точное сравнение =

Примеры 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 позволяет использовать шаблоны для внешнего использования

Поддерживаемые методы:

  • GET
  • POST

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 }}

Ниже приведен список специальных аргументов:

format - формат отдаваемого контента (MIME types)

  • 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.

  1. Настройте одну или несколько платежных систем
  2. Скачайте шаблон и сохраните в SHM под названием tg_payments_webapp
  3. Сделайте шаблон публичным: пропишите в его settings параметр: allow_public: true
  4. В Шаблоне своего бота используйте конструкцию вида:
<% 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 %>