Перейти к основному содержимому
Версия: 7.0

Мессенджеры

Messenger — системный модуль, содержащий классы, свойства, действия и формы для обмена сообщениями с внешними мессенджерами: учётные записи, через которые подключается приложение, чаты, в которых участвует учётная запись, хранимые сообщения, а также абстрактные действия отправки и синхронизации чатов, которые реализует модуль мессенджера. Модули Telegram, Viber, Whatsapp и Slack подключают одноимённые мессенджеры: приложение подключает нужные ему (REQUIRE Telegram), и вместе с каждым из них подключается Messenger (а с ним Reflection и Utils). Модуль Skype из той же папки рассчитан на платформу ботов Skype, которую Microsoft закрыла в 2025 году, и здесь не описывается.

Мессенджеры​

Messenger — класс мессенджеров. Каждый модуль мессенджера расширяет его статическим объектом своего мессенджера — Messenger.telegram, Messenger.viber, Messenger.whatsapp, Messenger.slack — и добавляет свои реализации в описанные ниже абстрактные свойства и действия. Приём каждый модуль реализует самостоятельно.

МодульНастройки учётной записиПриёмОтправка
Telegramtoken[Account] — токен ботапо адресу доставки, регистрируемому при создании учётной записи, если не задано skipSetWebHookTelegram[Account]. Личные чаты и группытекст и файлы
Vibertoken[Account] — токен аутентификации ботапо адресу доставки, регистрируемому при создании учётной записи. Личный чат создаётся, когда пользователь подписывается или пишет, и удаляется, когда он отписываетсятекст
WhatsappaccessToken[Account] — временный токен доступа, phoneNumberId[Account] — идентификатор номера телефона отправителя. Общий для приложения verifyToken[] должен совпадать с токеном подтверждения, введённым вместе с адресомпо адресу доставки webhookUrl[], вводимому вручную в настройках разработчика Meta. Личные чатытекст и файлы. sendTemplateMessage[Chat] отправляет шаблон hello_world, не сохраняя сообщение
Slacktoken[Account] — пользовательский токен доступа к APIпо запросу: synchronizeChats[Account] загружает пользователей и каналы, а receiveMessages[Chat] — сообщения чата, пришедшие после последнего сохранённоготекст

Учётные записи, чаты и сообщения​

Account хранит настройки подключения к одному мессенджеру.

СвойствоЧто хранит
name[Account]название учётной записи
messenger[Account]мессенджер как объект Messenger. captionMessenger[Account] возвращает его заголовок
token[Account]токен API мессенджера

account[LONG] находит учётную запись по идентификатору её объекта: адреса доставки Telegram и Viber передают его параметром botid.

Chat — переписка учётной записи с одним пользователем, группой или каналом.

СвойствоЧто хранит
account[Chat]учётная запись. При её удалении удаляются и её чаты
id[Chat]идентификатор чата в мессенджере
chatType[Chat]вид чата как объект ChatType: ChatType.private, ChatType.group или ChatType.channel. captionChatType[Chat] возвращает его заголовок
name[Chat], title[Chat]имя (имя пользователя или канала) и заголовок чата. Заголовок показывается для мессенджеров, у которых задано showTitle[Messenger]

chatIdAccount[Account, STRING] находит чат по учётной записи и идентификатору. Чат создаётся, когда приходит его первое сообщение (в Viber — также когда пользователь подписывается). Мессенджер, который перечисляет свои чаты (Slack, у него задано isSynchronize[Messenger]), загружает их действием synchronizeChats[Account] — оно выполняется при создании учётной записи и по кнопке synchronizeChatsAction[Account] формы.

Message — сохранённое сообщение чата.

СвойствоЧто хранит
chat[Message]чат
message[Message]текст
file[Message]файл, отправленный из приложения действием sendFile[Chat, NAMEDFILE]. Принимающие действия входящие файлы не сохраняют. openFile[Message] открывает его
from[Message]отправитель: в Telegram — имя пользователя, а без него имя; в Viber — имя подписавшегося пользователя или отправителя сообщения; в Whatsapp — номер телефона; в Slack — имя пользователя, найденного среди загруженных, или YOU, если он не найден. YOU также помечает сообщение, отправленное из приложения
dateTime[Message]момент сохранения сообщения. Для сообщения, принятого Slack, — время сообщения в мессенджере

Отправка​

sendMessage[Chat, STRING] отправляет в чат текст, а sendFile[Chat, NAMEDFILE] — файл. Оба — абстрактные действия условной формы: модуль мессенджера добавляет реализацию для чатов своих учётных записей. Отправленное сообщение сохраняется как Message с YOU в from[Message]. Затем реализация применяет изменения текущей сессии вместе с остальными накопленными в ней изменениями, независимо от того, сообщил ли мессенджер об ошибке. Неудавшийся запрос (нет соединения или в ответе статус ошибки) завершает действие ошибкой. Ошибку, которую мессенджер сообщает в успешном ответе, в локальное свойство sendMessageResult[] записывают Telegram, Viber и Slack, а Whatsapp ответ не проверяет. Действие формы sendMessage[Chat] отправляет введённый текст newMessage[Chat] и, если это прошло без ошибки, вложенный файл newAttachment[Chat], а затем показывает ошибку, если она есть. Viber и Slack файлы не отправляют: у них sendFile[Chat, NAMEDFILE] только записывает предупреждение в sendMessageResult[].

Приём​

Telegram, Viber и Whatsapp доставляют входящие сообщения сами: мессенджер отправляет HTTP-запрос веб-серверу приложения по адресу доставки, зарегистрированному для учётной записи. Адрес складывается из webServerUrl[] модуля Reflection и имени действия, принимающего запрос: <webServerUrl>/exec/webHookTelegram?botid=<идентификатор учётной записи> для Telegram, то же с webHookViber для Viber и <webServerUrl>/exec/webHookWhatsapp для Whatsapp, который определяет учётную запись по идентификатору номера телефона в запросе (phoneNumberId[Account]). Принимающие действия webHookTelegram[JSONFILE], webHookViber[JSONFILE] и webHookWhatsapp[JSONFILE] объявлены с @@noauth, поэтому мессенджер вызывает их из внешней системы без аутентификации. Каждое из них читает сообщение из тела запроса, находит или создаёт чат, сохраняет Message и применяет изменения, а Whatsapp вместо этого отвечает на запрос подтверждения адреса (запрос без тела), если его токен совпадает с verifyToken[]. Веб-сервер должен быть доступен мессенджеру по этому адресу по HTTPS, которого мессенджеры требуют. Без такого адреса учётная запись Telegram может только отправлять — с отключённой регистрацией, как описано ниже, — а учётную запись Viber создать нельзя вовсе.

Telegram и Viber регистрируют адрес доставки сами при создании учётной записи (setWebHookTelegram[Account], setWebHookViber[Account], выполняемые событием на новой учётной записи): если мессенджер отклоняет регистрацию, показывается его сообщение, а создание отменяется. Для учётной записи Telegram, которая только отправляет, регистрацию отключает реализация абстрактного свойства skipSetWebHookTelegram[Account]. Для Whatsapp адрес webhookUrl[] вводится в собственных настройках мессенджера вместе с токеном подтверждения, с которым должен совпадать verifyToken[].

Slack вместо этого принимает своими запросами и адреса доставки не требует: receiveMessages[Chat] запрашивает у мессенджера сообщения чата, пришедшие после последнего сохранённого. Оно выполняется по кнопке формы и перед отправкой текста.

Формы​

Форма messengers, размещённая в папке навигатора notification, показывает учётные записи, чаты выбранной учётной записи с полями нового сообщения и вложения, а также сообщения выбранного чата. Форма перечитывает данные каждые 20 секунд, показывая сообщения, доставленные приложению за это время. Сообщения Slack загружает только receiveMessages[Chat]. Форма messenger — форма редактирования учётной записи. Модули мессенджеров добавляют в эти формы свои настройки и действия.

Примеры​

MODULE Notifications;

REQUIRE Telegram;

CLASS Order 'Заказ';
number 'Номер' = DATA STRING[20] (Order);

// учётная запись только отправляет: признак ставится на её форме до сохранения,
// и адрес доставки для неё не регистрируется
sendOnly 'Только отправка' = DATA BOOLEAN (Account);
skipSetWebHookTelegram(Account a) += sendOnly(a);
EXTEND FORM messenger PROPERTIES(a) sendOnly;

// уведомление во все чаты Telegram с именем operator: такой чат появляется, когда этот пользователь пишет учётной записи,
// либо создаётся вручную с учётной записью, идентификатором чата Telegram в id[Chat] и именем в name[Chat]
notifyOperator 'Уведомить оператора' (Order o) {
FOR isTelegram(account(Chat c)) AND name(c) = 'operator' DO
sendMessage(c, 'Заказ ' + number(o) + ' готов');
}