Как написать Telegram bot на VBA?

Предыстория

У моего заказчика случился один инцидент. Ну, как инцидент, обычная житейская история. У него в компании есть девушка (или женщина, все относительно), которая следит за днями рождений сотрудников, собирает деньги на подарок и т.д., короче, Шура – профсоюзная активистка из «Служебного романа» (помните?: «… если сегодня кто-нибудь еще родится или умрет, я останусь без обеда…»), только помоложе и без профсоюза.

И вот однажды подходит эта «Шура» к одному из сотрудников и просит его помочь ей поднять на этаж пиццу.

  • - Какую пиццу?
  • - У меня сегодня День рождения, я заказала на всех пиццу…
  • - (Ой-йо-мойо(!!???)) ну… это… я тебя поздравляю, пошли за пиццей… Коллеги! у «Шуры» сегодня День рождения!

Тут, конечно, началось… в спешке, пока «Шура» и «Шурик» исчезли в направлении пиццы, торопливые сборы ассигнаций, в ближайший цветочный ларёк направлялся гонец, и воздухе витала мысль: «Просрали полимеры день рождения девушки, которая о каждом из нас не забывала»…

Дабы не попадать в следующий раз впросак, заказчик попросил меня написать для их корпоративного Telegram чата некую программку, которая бы утром поздравляла тех, у кого день рождения (в компании около 200 человек), а за три дня до дня рождения персонально направляла каждому сотруднику группы/отдела, в котором работает будущий именинник, telegram-сообщение о грядущем событии.

«Деньги в руки – будут звуки». Что в результате получилось? Есть чат, в котором сотрудники обсуждают разные вопросы, в нем каждое утро в 9-00 появляется средних размеров сообщение на тему «10 знаменитых людей, родившихся в этот день». Если в «этот день» родился кто-то из компании заказчика, то этот кто-то оказывается в компании знаменитостей. Такой вот каламбур. Также индивидуально некоторые сотрудники получают сообщение от бота «Шура – профсоюзная активистка» о ДР за три дня до «грустного праздника».

Вот, такая преамбула.

Для реализации поставленной задачи я использовал API Telegram, c# и серверную инфраструктуру заказчика на платформе Microsoft.

Шаг за шагом

После создания заказной разработки на c# и .Net мне пришла в голову мысль: а можно ли запилить Telegram бот на VBA? На странице «Bot Code Examples» VBA среди множества языков нет, но ведь это не означает, что на нем нельзя написать бот для «телеги».

Попробую в этом посте заполнить брешь. Сначала нужно сделать то, что делается для любого языка программирования – создать и прописать бота.

  1. Для создания бота в Telegram найдите крестного отца всех telegram ботов - BotFather и дайте ему команду: «/newbot». В ответ отец всех telegram ботов попросит дать имя новому боту. Далее, нужно указать логин бота (username), заканчивающееся на bot. Вот бот готов. В доказательство свершившегося BotFather выдаст access token для доступа в Telegram API приблизительно такого вида: «1234567890:ABCDEFGabcdfgQWERTYUIOPasdfghjklZXC».
  2. Нужно новорожденного бота пригласить в свою группу.
  3. Все, бот может отправлять сообщения в эту группу.

Общая часть закончена. Теперь специфика Visual Basic for Application.

Попробуем отправить сообщение на VBA:

Sub SendAMessage2TheTelegram()
    Dim xmlhttp As New MSXML2.XMLHTTP60, url As String, response As String
    Dim msg As String
    Dim BotToken As String, ChatID As String
    ChatID = "0987654321"
    BotToken = "1234567890:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    msg = "Hello from VBA Excel"
    url = "https://api.telegram.org/bot" & BotToken & "/sendMessage?chat_id=" & ChatID & "&text=" & msg
    xmlhttp.Open "POST", url, False
    xmlhttp.Send
    ' В ответ придет JSON с информацией об успехе выполнения команды
    response = xmlhttp.ResponseText
    Debug.Print response
End Sub
                                

Конкретно этот код работает. Проблема возникнет, если вы попытаетесь отправить сообщение на русском языке. В этом случае от Telegram вы получите ответ: «Bad Request: strings must be encoded in UTF-8».

У VBA, к сожалению, нет встроенной функции перевода в требуемый формат. Что же делать? Что же делать? Надо выпить. Можно придумать свой собственный велосипед на тему: «URL Encoding», - в соответствии с RFC-3986, можно найти на просторах интернета множество чужих велосипедов разной степени глючности и «доработать напильником», но я бы предложил велосипед не изобретать, а воспользоваться функцией JavaScript encodeURIComponent(), она заменяет «неправильные» символы процентными (escape-) последовательностями, представляющими кодировку символа UTF-8.

Public Function EncodeUTF8(str As String)
    Dim ScriptEngine As Object
    Set ScriptEngine = CreateObject("ScriptControl")
    ScriptEngine.Language = "JScript"
    EncodeUTF8 = ScriptEngine.Run("encodeURIComponent", str)
End Function
                                

Это окошко в возможности JavaScript меня уже неоднократно выручало.

Соответственно, функция SendAMessage2TheTelegram будет выглядеть так:

Sub SendAMessage2TheTelegram()
    Dim xmlhttp As New MSXML2.XMLHTTP60, url As String, response As String
    Dim msg As String
    Dim BotToken As String, ChatID As String
    ChatID = "0987654321"
    BotToken = "1234567890:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    msg = "Привет из VBA Excel - Послать сообщение в Telegram - это просто!"
    msg = EncodeUTF8(msg)
    url = "https://api.telegram.org/bot" + BotToken + "/sendMessage?chat_id=" & ChatID & "&text=" & msg
    xmlhttp.Open "POST", url, False
    xmlhttp.Send
    ' В ответ придет JSON с информацией об успехе выполнения команды
    response = xmlhttp.ResponseText
    Debug.Print response
End Sub
                                

Теперь сообщения с русскими буквами прекрасно перевариваются Telegram API. Отдельный вопрос: как узнать id чата, если речь идет о группе или даже конкретной персоне, а не о канале?

Для этого нужно отправить какое-либо сообщение боту из чата, в который вы планируете отправлять сообщения бота.

Например, такое: «/myid @UserNameOfMybot».

После чего посмотреть на историю сообщений бота с помощью команды, которую можно отправить из, например, Google Chrome:

https://api.telegram.org/bot1234567890:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/getUpdates
                            

Посмотреть на последние строчки истории и найти собственное сообщение в структуре JSON. Там должен быть и такой фрагмент: "chat":{"id": 123456789. 123456789- на месте этих цифр должен быть ID чата для переменной ChatID.

Изначально я думал создать на c# com компонент для VBA, с большим набором функций по работе с Telegram. Но нужен ли такой com-компонент? – я пока не решил. Так что этот вопрос тоже можно обсудить.