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.items() Получить всех пользователей

Услуги пользователя

Метод Описание
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.items() Получение списка услуг пользователя

Каталог услуг

Метод Описание
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.reg( service_id = N, check_allow_to_order = 1 ) Регистрирует услугу клиенту с идентификатором N
service.items() Получение списка услуг из каталога

Платежи

Метод Описание
pay.id() Получить/установить id платежа
pay.date Дата и время платежа
pay.money Cумма платежа
pay.pay_system_id Имя платежной системы
pay.comment Данные платежа
pay.last Получить ссылку на последний платеж
pay.forecast Возвращает JSON прогноза оплат услуг
pay.paysystems Получить список платежных систем
pay.items() Получение списка платежей

Бонусы

Метод Описание
bonus.items() Получение списка бонусов

Списания

Метод Описание
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.items() Получение списка списаний

Сервера

Метод Описание
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.items() Получение списка серверов

Группы серверов

Метод Описание
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.items() Получение списка групп серверов

Шаблоны

Метод Описание
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.items() Получение списка данных

Конфигурация

Метод Описание
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() Метод для точечной выборки данных (для items)
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.items:

{{ toJson( user.items.first ) }}

Результат:

{
  "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() УСТАРЕЛ. Используйте метод items()

Метод для получения списка данных объекта.

Без аргументов выдаст первые 25 строк данных.

Аргументы:

Параметр Описание
admin Установка этого параметра в 1 позволяет получить данные всех клиентов
limit Кол-во отдаваемых данных. По-умолчанию: 25. (0 - без лимитов)
offset Индекс начала смещения списка. По-умолчанию: 0
filter Используется для поиска данных по определенным полям
sort_field Поле для сортировки (по-умолчанию ключевое поле)
sort_direction Порядок сортировки: asc, desc (по-умолчанию desc)

Пример 1:

Выведем всех пользователей:

{{ arr = user.items }}
{{ 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 = service.filter( category = 'web%', period => 1 ).items }}
{{ 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

Сортировка

Существует два типа сортировки:

  • Сортировка на уровне запросов
  • Сортировка на уровне шаблона

При использовании пагинации важно сортировать результаты на уровне запросов.

Сортировка на уровне запросов

Сортировка на уровне запросов осуществляется непосредственно в БД.

Пример выдачи результатов отсортированных по возрастанию по полю name:

{{ arr = service.sort('name').items }}

Пример выдачи результатов отсортированных по убыванию по полю name:

{{ arr = service.rsort('name').items }}

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.filter( category = 'test' ).items }}
Уважаемый {{ user.full_name }}!
Вы получили это сообщение потому, что у Вас есть услуга в категории test.
{{ END }}

Информация об услугах в определенной категории

Показываем сообщение для каждой услуги в категории test.

{{ user_services = user.services.filter( category = 'test' ).items }}
{{ 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 user.services.filter( category = 'test' ).items }}

{{ IF item.status == 'ACTIVE' }}
Вы видите это сообщение, потому что у вас есть активная услуга в категории test
{{ LAST }}
{{ END }}

{{ END }}

Уведомление клиентам без услуг в определенной категории

{{ IF user.services.filter( category = 'test' ).items.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 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 user.items }}
<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 = user.services.limit( limit, offset ).items;
}}
{{ 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 %>