Использование мессенджеров (Viber, Telegram, WhatsApp) заметно упростило и разнообразило общение людей, позволив все время находится “на связи”, и не быть привязанными к компьютеру. В то же время пользователи мессенджеров получили куда больший функционал чем обычные SMS-сообщения, использование которых у операторов мобильной связи стала куда большей роскошью, нежели интернет трафик.
А что, если приспособить отправку сообщений в мессенджер из VBA кода, привязав к окончанию работы макроса, получению Email или настроить на периодическую отправку сообщений со статусом каких-либо показателей (% выполнения плана продаж, список незакрытых сделок и т.д.)? Не найдя подходящего решения в интернет для VBA, я решил написать свое собственное с использованием Telegram бота, о чем и хочу поделиться в данном посте. Обмолвлюсь сразу же – описанный ниже функционал НЕ ПОЗВОЛЯЕТ вести интерактивный диалог с ботом, а лишь отправлять сообщения пользователю в одностороннем порядке.
И так, приступим. Что первое необходимо сделать чтобы начать тестировать функционал отправки сообщений в Telegram через VBA? Предполагаю, что сам мессенджер у Вас уже установлен и поэтому данный этап пропустим )). Опишем следующую этапность действий:
- Создадим Telegram бота
- Напишем VBA-класс для отправки сообщений ботом
- Используем класс в нашем коде
- Создание Telegram бота
Сначала, необходимо настроить бота, через которого Вы будете отправлять сообщения. Для этого a) Найти в Telegram пользователя @BotFather и начните диалог с помощью кнопки Start
b) Отправьте боту команду /newbot, которая отвечает за создание новых ботов
c) Задайте имя своего нового бота согласно инструкциям и подсказкам
d) Получите токен для вашего бота в ответном письме @BotFather (см. рис. выше) e) Найдите своего бота в мессенджере и начните переписку
f) Получите ID чата (вашей переписки с ботом). Для этого в окне браузера введите следующую команду:
https://api.telegram.org/bot<Bot_token>/getUpdates
где <Bot_token> – это токен, полученный от @BotFather.
В нашем тестовом примере токен был 781500114:AAECOTrby1zuQ0afGozyRlvua5CMGezT9IA Таким образом, получим следующую строку:
https://api.telegram.org/bot781500114:AAECOTrby1zuQ0afGozyRlvua5CMGezT9IA/getUpdates
При переходе по указанному адресу, браузер выдаст ответ в котором содержится необходимая информация – id чата
g) Сохраните данные о токене и chat ID для дальнейшего использования в скрипте
- Написание класса для отправки сообщений ботом
Задумка написания класса для отправки ботом сообщений была следующая: указываем имя бота, указываем имя получателя, текст сообщения и отправляем месседж. Для этого имя бота и получателя зададим как свойства класса, для того, чтобы можно было обращаться к ним не по токену и id, а по именам.
Private ms_current_chatbot As String
Private ms_current_user As String
Public Property Let ChatBot(ByVal Value As String): ms_current_chatbot = Value: End Property
Public Property Get ChatBot() As String: ChatBot = ms_current_chatbot: End Property
Public Property Let UserName(ByVal Value As String): ms_current_user = Value: End Property
Public Property Get UserName() As String: ChatBot = ms_current_user: End Property
Таким образом мы определили два свойства: ChatBot, UserName с помощью которых мы будем задавать нашему классу два входящих параметра: имя нашего бота и имя получателя. Сами значения токена и id будем получать на основе заданных имен отдельными функциями внутри класса (они не будут видны извне):
Private Function getChatToken() As String
'********************************
'Purpose: Function retrieves token for bot
'Result: getChatToken gets value of token
'********************************
If Len(ms_current_chatbot) > 0 Then
Select Case ms_current_chatbot
'---- other cases can be added below here ----
Case "OfficeScripts_test": getChatToken = "781500114:AAECOTrby1zuQ0afGozyRlvua5CMGezT9IA"
Case Else: getChatToken = vbNullString
End Select
Else: getChatToken = vbNullString
End If
End Function
Private Function getChatID() As String
'********************************
'Purpose: Function retrieves user chat id
'Result: getChatID gets value of chat id
'********************************
If Len(ms_current_user) > 0 Then
Select Case ms_current_user
'---- other cases can be added below here ----
Case "Aleksandr": getChatID = "xxxxxxxxx" 'Substitute xxxxxxxxx with your real ID
Case Else: getChatID = vbNullString
End Select
Else: getChatID = vbNullString
End If
End Function
Осталось описать главную часть нашего класса – функцию отправки сообщения. Для того чтобы отправить сообщение, необходимо сформировать строку определенного формата и отправить ее на сервер Telegram. Часть строки будет всегда неизменной, и поэтому ее логично задать константой, а другие части зададим переменными, из которых и будем формировать строку.
Постоянную часть определим константой TELEGRAM_URL:
Private Const TELEGRAM_URL As String = “https://api.telegram.org/bot"
А прочие части будем строки (URL) формировать на лету следующим образом:
'//Getting URL string
URL = TELEGRAM_URL & getChatToken & "/sendMessage?" & _
"chat_id=" & getChatID & "&" & _
"text=" & BodyText
Для отправки сообщения будем использовать библиотеку MSXML2, которая должна будет инициализироваться при определении класса. Сохраним ее в переменную mobj_httpRequest , которой и будем передавать нашу строку URL.
Есть, однако, один нюанс, который нельзя не озвучить: Строка URL должна быть совместима с WEB стандартами (не содержать пробелов, спец.символов и т.д.) и для этого ее необходимо конвертировать в нужный нам стандарт. Для этого я воспользовался функцией URLEncode пользователя parkone на GitHub и конвертировал сообщение перед отправкой с помощью этой функции.
Public Function sendMessage(ByVal BodyText As String) As Boolean
'********************************
'Purpose: Function sends telegram message to user
'Arguments: BodyText - message text
'Result: True - message was sent successfully
' False - message wasn't sent
'********************************
Dim URL As String
Dim iCount As Integer
If Not mobj_httpRequest Is Nothing Then
If Len(ms_current_chatbot) > 0 And Len(ms_current_user) > 0 Then
'//Converting chars to url format
BodyText = URLEncode(BodyText)
'//Getting URL string
URL = TELEGRAM_URL & getChatToken & "/sendMessage?" & _
"chat_id=" & getChatID & "&" & _
"text=" & BodyText
mobj_httpRequest.Open "POST", URL, True
mobj_httpRequest.Send
On Error Resume Next
iCount = 0
Do Until mobj_httpRequest.Status = 200 And mobj_httpRequest.ReadyState = 4
iCount = iCount + 1
If iCount = MAX_TRY_COUNT Then
Exit Do
End If
Loop
On Error GoTo 0
If iCount < MAX_TRY_COUNT Then
sendMessage = True
Else: sendMessage = False
End If
Else
'//Chat not specified
If Not SILENT_MODE Then
MsgBox "Chat or User haven't been set. Please specify chat & user firstly", vbExclamation + vbOKOnly
End If
sendMessage = False
End If
Else
'//MSXML object is not initialized
If Not SILENT_MODE Then
MsgBox "Http connection was lost", vbExclamation + vbOKOnly
End If
sendMessage = False
End If
End Function
В случае, если при отправке сообщения произошел сбой в интернет соединении, недоступность сервера и т.д. и наш запрос не был доставлен, в функции sendMessage имеется цикл, который будет повторяться несколько раз (до установленного Вами значения количества попыток), пока не отправит сообщение. Данные строки выделены подсветкой выше.
- Использование класса
В самом начале, когда нами создавался класс для отправки сообщений Telegram, ему было присвоено имя по умолчанию, которое лучше изменить на что-то более значащее и понятное. Я присвоил классу имя clTelegramNotifier. Использовать его в коде довольно просто, если изначально был четко определен желаемый алгоритм работы, о чем было написано в начале второго раздела. Мы хотели задать имя получателя, имя бота, указать какой текст мы хотим отправить и получить наше сообщение на телефоне, что мы в итоге и сделали.
Sub test_telegram()
Dim Telegram As clTelegramNotifier
Dim text As String
Set Telegram = New clTelegramNotifier
If Telegram.Initialized Then
Telegram.ChatBot = "OfficeScripts_test"
Telegram.UserName = "Aleksandr"
text = [b2].Value2
If Telegram.sendMessage(text) Then
Debug.Print "OK"
End If
End If
Set Telegram = Nothing
End Sub
Данную процедуру в рамках тестирования я поместил на кнопку на листе и передавал ей как параметр текст из ячейки [B2]. Результат видно на рисунке ниже. А сам класс можно скачать в конце данного поста.