Для чего это надо? Я бы использовал только для уведомлений. Некоторые, возможно, захотят и управление реализовывать через команды боту. Минимально для работы нужно реализовать два метода: отправка сообщения и прием.
Всю базовую информацию по созданию ботов в Telegram можно прочитать в тысяче статей по всему интернету, но если парой слов, то для создания бота вам нужно написать @BotFather и получить ключ API.
Требуется создать транспорт для общения с серверами Telegram. Для этого в дереве проекта переходим в раздел Транспорты > SSL и создаем выходной транспорт. Имя можно выбрать любое, но в библиотеке, приложенной к статье, используется имя по умолчанию Telegram. Требуется именно SSL, а не просто сокет.
В транспорте нельзя указывать протокол HTTPS, но известно, что для обмена через HTTPS используется порт 443. Адрес у транспорта указываем следующий:
api.telegram.org:443
Для отправки используется метод sendMessage Telegram API. В качестве аргументов ему требуется передать chat_id и текст. Аргументов может быть и больше, но в базовом варианте, когда передаем только текст, этого достаточно.
В коде ниже есть 4 переменных.
| Имя | Тип | Описание |
| Transport | Строка | Имя транспорта в “Транспорты > SSL” |
| APIKey | Строка | Ключ, полученный от @BotFather |
| ChatID | Целочисленный | Уникальный идентификатор переписки |
| Message | Строка | Текст сообщения |
tr = SYS.Transport.SSL.nodeAt("out_" + Transport); req = SYS.XMLNode("POST"); req.setAttr("URI", "/bot" + APIKey + "/sendMessage"); req.childAdd("cnt").setAttr("name", "chat_id").setText(ChatID.toString()); req.childAdd("cnt").setAttr("name", "text").setText(Message); req.childAdd("cnt").setAttr("name", "disable_notification").setText("true"); tr.messIO(req,"HTTP");
Для получения сообщений используется метод getUpdates Telegram API. Ответы от сервера приходят в формате JSON. Данный формат не поддерживается “из коробки”, однако сообществом разработана библиотека, которая преобразует JSON строку в объект, с которым удобно работать в JavaLikeCalc.
В коде ниже используются 3 переменных.
| Имя | Тип | Описание |
| Transport | Строка | Имя транспорта в “Транспорты > SSL” |
| APIKey | Строка | Ключ, полученный от @BotFather |
| updateId | Целочисленный | ID последнего полученного сообщения, при первом запуске значение 0 |
jsonLib = SYS.DAQ.JavaLikeCalc.lib_Json; tr = SYS.Transport.SSL.nodeAt("out_" + Transport); messagesCount = 1; while(messagesCount){ updateId += 1; req = SYS.XMLNode("POST"); req.setAttr("URI", "/bot" + APIKey + "/getUpdates"); req.childAdd("cnt").setAttr("name", "offset").setText(updateId.toString()); tr.messIO(req,"HTTP"); deser_err = ""; outputObject = jsonLib.deserialize(req.text(), deser_err); messagesCount = outputObject.result.length; for (var i = 0; i < outputObject.result.length; i++){ item = outputObject.result[i]; updateId = max(updateId, item.update_id); username = item.message.from.username; chatID = item.message.from.id; messageText = item.message.text; } }
Данный код будет загружать все доступные сообщения. Между вызовами требуется хранить последнее значение updateId, так как этот параметр позволяет пропускать уже обработанные сообщения
Данные примеры кода стоит оформить в виде библиотеки в Сбор данных → Вычислитель на Java-подобном языке → Библиотека. Так будет гораздо удобнее использовать код повторно.
Обычно телеграм бот нужен для оповещений об аварийных ситуациях. И оповещать приходится группу сотрудников.
Где брать список абонентов, которым рассылать оповещения? Логично было бы использовать список пользователей OpenSCADA и добавлять их в дополнительную группу. Так будет проще управлять списком получателей.
Где хранить ID пользователя Telegram? Логично, что в описании пользователя OpenSCADA. В поле “Описание” можно хранить все, что необходимо. Если таких параметров конфигурации требуется больше чем один, то не проблема. По одному в строку в формате <TYPE>:<VALUE>. В моем случае в данном поле хранятся телефон, адрес почты и ID пользователя Telegram.
В коде ниже есть 4 переменных.
| Имя | Тип | Описание |
| Transport | Строка | Имя транспорта в “Транспорты > SSL” |
| Group | Строка | Имя группы пользователей для оповещения |
| APIKey | Строка | Ключ, полученный от @BotFather |
| Message | Строка | Сообщение |
TelegramLib = SYS.DAQ.JavaLikeCalc.lib_Telegram; users = SYS.Security.nodeList("usr_"); for (var j = 0; j < users.length; j++){ user = SYS.Security.nodeAt(users[j]); // Пропуск пользователей, которым не разрешены уведомления if (user.groups().indexOf(Group) == -1) continue; // Обработка сохраненных транспортов для уведомлений transports = user.cfg("LONGDESCR"); for(offset = 0; (transport = transports.parse(0, "\n", offset)).length; ){ tmp = transport.split(":"); TransportType = tmp[0]; TransportValue = tmp[1]; if (TransportType == "telegram"){ TelegramLib.SendMessage(Transport, APIKey, TransportValue, Message); break; } } }
Выше есть код для массовой отправки сообщений, но добавлять в каждый обработчик датчика код для отправки - плохая идея. Обработчик должен формировать сообщение в журнал, а отдельный виртуальный контроллер отправлять сообщения из журнала по какому-то каналу связи.
При помощи функции SYS.Archive.messGet запрашиваем все сообщения за определенный период, которые подходят под указанную категорию. Категорией может быть просто звездочка для всех сообщений или немного сложнее. Можно и регулярным выражением воспользоваться при необходимости.
В коде ниже есть 7 переменных.
| Имя | Тип | Описание |
| TelegramTransport | Строка | Имя транспорта в “Транспорты > SSL” |
| TelegramAPIKey | Строка | Ключ, полученный от @BotFather |
| TelegramGroup | Строка | Имя группы пользователей для оповещения через Telegram |
| MessagesLevel | Строка | Минимальный уровень сообщений из журнала |
| MessagesCat | Строка | Категория сообщений из журнала |
| TimeBegin | Целый | Время начала |
| utmLast | Целый | Микросекунды последнего сообщения |
using Special.FLibSYS; TelegramLib = SYS.DAQ.JavaLikeCalc.lib_Telegram; if (f_start){ TimeBegin = SYS.time(); } // Запрос сообщений по шаблону TimeFinish = SYS.time(); messages = SYS.Archive.messGet(TimeBegin, TimeFinish, MessagesCat, MessagesLevel); // Обновляем время для следующей итерации TimeBegin = TimeFinish; utmLast = -1; for (var i = 0; i < messages.length; i++){ message = messages[i]; tm = message.tm; if ((TimeBegin == tm) && (utmLast > message.utm)) continue; utmLast = message.utm; // Сохраняем микросекунды для следующей итерации TimeStr = tmFStr(tm,"%d.%m.%Y %H:%M:%S"); messageText = TimeStr + ": " + message.mess; TelegramLib.SendAll(TelegramTransport, TelegramGroup, TelegramAPIKey, messageText); }