Lanka Документація Ланки

Скрипти

Скрипт рахує і змінює змінні. Його виконує крок «Скрипт».

Крок «Скрипт» у редакторі

Під полем скрипта є «Тестовий запуск»: він показує, що зміниться, і нічого не зберігає.

Синтаксис

Кожен рядок присвоює значення: змінна = вираз. Рядок, що починається з #, — коментар.

# Без префікса змінна живе, поки триває розмова
options = filter(app.slots, item.status == "free")
labels = map(options, item.time)

# user.* зберігається за відвідувачем назавжди
user.score = default(user.score, 0) + 10
user.level = user.score > 100 ? 2 : 1

# app.* — спільні дані бота
app.total_plays = default(app.total_plays, 0) + 1
  • Значення бувають такі: числа, рядки в лапках "…", true, false, null, списки [a, b] і об'єкти {ключ: значення}.
  • Рядок у зворотних лапках підставляє значення: `${item.name} ×${item.qty}` дає Кеди ×2. Усередині ${…} будь-який вираз. Порожнє значення підставляється як нічого.
  • Вираз може тривати на кількох рядках, поки не закрито дужку.
  • Коментар займає цілий рядок. Коментар у кінці рядка з кодом не працює.
  • Елемент списку — items[0] або items.0. Розмір списку чи об'єкта — list.size.
  • Змінна, якій ще нічого не присвоїли, дорівнює null. Запасне значення дає default(x, запасне).
  • == порівнює строго: 1 == "1" хибне.

Простори імен

Префікс перед назвою змінної каже, де вона живе і скільки.

Змінна Що це
name або session.name Змінні розмови. Ім'я без префікса пише й читає сюди. Зникають, коли діалог завершується.
user.name Стан окремого відвідувача, назавжди. Читання й запис. Сюди кроки зберігають відповіді: ввід, вибір, опитування.
app.name Спільні дані для всіх відвідувачів. Читання й запис. Лічильники, каталог, розклад.
temp.name Змінні одного скрипта. Живуть, поки він виконується, і ніде не зберігаються.
platform.* Дані Telegram про людину й повідомлення, яке зараз обробляється. Лише читання. Див. нижче.
initiator.* Хто запустив цю ланку через «Сповіщення» чи «Анонс»: user_id, name, username. Лише читання. У звичайній ланці порожньо.
secret.* Секрети бота з «Налаштування бота → Секрети»: ключі API, паролі. Лише читання і лише всередині запитів: fetch, page_meta, save_image.
  • Змінити поле всередині об'єкта напряму не можна: user.cart.qty = 1 не спрацює. Зберіть об'єкт заново або змініть елемент списку через update_where.
  • Два чати можуть змінювати app.* одночасно. Зміни обох не загубляться.
  • Імена, що починаються з _, зарезервовані.
  • Порада: те, що людина ввела, зберігайте в user.*, а те, що порахував скрипт, — у змінних розмови.

Оператори

Що Оператори
Арифметика + - * / %
Порівняння > < >= <= == !=
Логіка && || !
Умова в одному виразі user.x > 5 ? "high" : "low"
Склеювання рядків "Привіт, " + platform.first_name

+ склеює, якщо хоч один бік — рядок. Інакше обидва боки мають бути числами.

Функції

Математика

Функція Що робить
min(a, b, ...) max(a, b, ...) Мінімум і максимум
abs(x) Модуль числа
floor(x) ceil(x) round(x, decimals?) Округлення вниз, вгору і до найближчого, з потрібною кількістю знаків
sqrt(x) pow(x, y) Корінь і степінь
clamp(val, min, max) Обмежити число діапазоном

Рядки

Функція Що робить
len(s) Довжина рядка або списку
upper(s) lower(s) trim(s) Великі літери, малі літери, без пробілів на краях
contains(s, sub) Чи містить рядок підрядок
starts_with(s, prefix) ends_with(s, suffix) Чи починається або закінчується рядок цим текстом
substr(s, start, len?) Вирізати підрядок
replace(s, from, to) Замінити всі входження
split(s, sep) Розбити рядок на список за роздільником

Списки

Функція Що робить
count(list) Кількість елементів
sort(list) unique(list) Відсортований список і список без повторів
join(list, ", ") Список → рядок
append(list, item) Додати елемент у кінець списку
sort_by(list, "key", "desc") Відсортувати за полем; "asc" за зростанням, "desc" за спаданням
take(list, n) Перші n елементів
slice(list, start, count) Відрізок списку від позиції start (рахунок з 0)
last(list, n) Останні n елементів, у тому ж порядку
sum(list) Сума чисел
nth(list, i) Елемент за номером; за межами списку — null

Списки з item

У цих функціях другий аргумент — вираз, у якому item означає поточний елемент.

Функція Що робить
filter(list, item.status == "free") Лишити елементи, для яких вираз правдивий
map(list, item.time) Зібрати значення з кожного елемента
flat_map(list, item.slots) Як map, але списки з кожного елемента зливаються в один
find(list, item.id == 5) Перший елемент, що підходить, або null
update_where(list, item.id == 5, "status", "busy") Змінити поле в елементах, що підходять

Ці виклики не можна вкладати в умову один одного.

Дати

Дати — це рядки ISO, що починаються з YYYY-MM-DD.

Функція Що робить
add_days(date, n) Зсунути дату на n днів; результат має вигляд "yyyy-MM-dd"
get_iso_day(date) Номер дня тижня: 1 — понеділок … 7 — неділя
format(date, "dd.MM") Форматування токенами date-fns: dd, MM, yyyy, HH:mm

Перетворення й перевірки

Функція Що робить
num(x) str(x) bool(x) У число, рядок чи булеве значення; num дає помилку, якщо x — не число
to_num(x, fallback) Число, або fallback, якщо x — не число
is_num(x) is_str(x) is_bool(x) is_null(x) Повертають true або false; is_null правдиве й для змінної, якої ще немає
default(x, fallback) fallback, якщо x — null; 0 і "" лишаються як є

Запити до інших сервісів

fetch звертається до іншого сервісу через інтернет.

temp.resp = fetch("https://api.example.com/items", {method: "POST", body: {q: 1}, cache: 60, timeout: 10})
user.saved = temp.resp.status == 200 ? temp.resp.body.id : null
  • fetch займає весь правий бік рядка. Результат треба записати в temp.x (зникне після скрипта) або в session.x (лишиться на розмову).
  • method, headers і body — як у звичайному HTTP-запиті. Об'єкт у body надсилається як JSON, рядок — як є.
  • timeout — скільки секунд чекати відповідь: 5, якщо не задано, щонайбільше 30.
  • cache — скільки секунд зберігати успішну відповідь, щонайбільше година. Кеш спільний для всіх, хто робить такий самий запит. Він годиться для розкладу чи каталогу, але не для бронювання.
  • Результат — {status, body, error}. body — розібраний JSON або текст. Якщо запит не дійшов, status дорівнює null, а error пояснює чому.
  • Невдалий запит не вважається помилкою скрипта і не веде в гілку «+ Крок помилки». Перевірте temp.resp.status == 200 і розведіть гілки кроком «Умова».
  • Поки запит виконується, людина бачить «друкує…». Сусідні запити, що не залежать один від одного, виконуються паралельно, до 6 на скрипт.

Секрети

Ключ API не пишіть у скрипт відкритим текстом: збережіть його як секрет і беріть у запиті як secret.API_KEY. Докладно — на сторінці Секрети.

Дані сторінки і картинки

page_meta і save_image працюють за тими самими правилами, що й fetch.

temp.page = page_meta(link)
temp.photo = save_image(temp.page.image)
app.catalog = append(app.catalog, {id: "p1", name: temp.page.title, image: temp.photo.url})
  • page_meta(url) читає, що сторінка каже про себе: {status, title, description, image, images, url, site, type, extra, error}. У images — до 10 картинок сторінки, головна перша.
  • save_image(url) копіює картинку до медіа бота: {url, urls, error}. Можна передати список, до 10 картинок, і до 20 МБ кожна. Тестовий запуск картинку не зберігає.

Змінні платформи

Лише для читання.

Змінна Що це
platform.user_id Telegram ID людини (число)
platform.first_name Ім'я
platform.last_name Прізвище
platform.full_name Ім'я й прізвище; будь-яке з них може бути відсутнє
platform.username @username
platform.language_code Код мови (uk, en, …)
platform.is_premium Чи має людина Telegram Premium (true / false)
platform.first_seen Коли людина вперше написала боту (ISO 8601)
platform.last_seen Коли людина востаннє щось робила в боті (ISO 8601)
platform.message_text Текст повідомлення, яке зараз обробляється
platform.start_param Дані з посилання на ланку після дефіса; є лише у відповідь на /start, а розмова, яку воно відкрило, зберігає їх у session.start_param
platform.now Поточний момент у форматі ISO 8601

Ліміти

Що Скільки
Довжина виразу 1024 символи
Кількість інструкцій (fetch на кількох рядках — одна) 50
Довжина скрипта 4096 символів
Довжина списку 500
Елементів в одному {…} чи […] 50
Очікування fetch 5 с, якщо не задано; щонайбільше 30 с
Відповідь fetch 1 МіБ
Кеш fetch 1 година