Ajax
JavaScript
Programming
Node.JS
API
February 2018 5

Надоевшая почта или как отправлять сообщения с сайта в Telegram через Node.js (Express)

From Sandbox
logicSchema

После нескольких писем, отправленных с сайта себе на почту, понял что это достаточно неудобно, не современно (возможно), как минимум не прикольно. Задался целью отказаться от использования smtp для формы в пользу api Телеграма.

Так как мое приложение работает на ноде, подумал почему бы не прокачать форму. Общая логика до боли проста. При отправке формы делается запрос к api приложения, где хранится токен бота, обрабатываются данные и далее делается запрос к api телеграмма, который отправляет сообщение в чат.

Но давайте обо всем по порядку.

Для начала, естественно, необходимо создать бота, который будет получать данные из формы и отправлять вам. По сути, он является всего лишь посредником между вами и api телеграмма.

Итак, стучимся к родителю всех ботов, а именно к @BotFather и просим его создать нам одного (вводим /newbot). Вводим имя, ник и получаем токен бота. Как раз он нам и нужен. Заметьте, что ник бота должен быть <your>_bot или <Your>Bot.

@BotFather

Создали, хорошо, но надо оживить его. Ищем его в поиске по нику и пишем /start. Все, теперь можем обращаться к нему через api.

@BotFather

Далее необходимо создать группу, куда бот будет кидать сообщения, не забываем добавить его в чат.

createChat


addMembers


Вводим /join @ник_бота в созданном чате, потому что бывает, что не добавляется в логи запись о приглашении бота в группу.

Идем в браузер и в адресной строке вводим:

https://api.telegram.org/botXXXXXXXXXXXXXXXXXXXXXXX/getUpdates

где XXXXXXXXXXXXXXXXXXXXXXX — токен бота, который любезно дал вам @BotFather.

Если все прошло успешно, то получим примерно такую простыню из букв, где необходимо найти объект «chat»:{«id:XXXXXXXXXX…}. Обычно id группового чата начинается с минуса.

getUpdates

Отлично, получили токен бота и id чата, куда будут приходить сообщения.
Теперь давайте приступим к приложению.

Front


Начнем сначала с фронта.

Я использовал для работы Node обёртку Express, который в свою очередь умеет рендерить файлы различных шаблонизаторов. Решил воспользоваться Pug. Он достаточно прост в освоении, поэтому если впервые сталкиваетесь с ним, труда познакомится с ним не возникнет. Для примера не стал использовать сборщики, поэтому скрипты подключаются по старинке.
Структура приложения сгенерирована с помощью Express Generator.

Разметка формы


views/layout.pug:

doctype html
html
  head
    title= title
    link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css")
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    block content

views/index.pug:

extends layout

block content
  .wrapper
    .wrapper__bg
        img.wrapper__bg-img(src='/images/bg.jpg' alt='bg')
    form(action="/telegram", method="post" class="form" id='telegramForm' enctype="application/x-www-form-urlencoded")
      .form__container
          .form__blur
          .form__title
              .form__title-line
              h3.form__title-text Связаться со мной
              .form__title-line
          .form__inputs
              input(type="text" name='name' placeholder="Имя" class="form__input" required)
              input(type="email" name='email' placeholder="Email" class="form__input" required)
              textarea(name="text" placeholder="Ваше сообщение" class="form__input form__message" required)
      .form__buttons
          input(type="submit" class="form__submit" value="Отправить")
          .form__clean Очистить
  
  script(src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js")
  script(src="/javascripts/app.js")

Не забываем что в Pug вложенность элементов определяется отступами, как в питоне, так что учитывайте это.

Добавляем стили и вот такая форма у меня получилась.

form

Сообщение будет отправляться без перезагрузки страницы, поэтому вешаем обработчик на форму, собираем данные, преобразуем в json и отправляем их асинхронно себе в api + выводим сообщение о статусе запроса.

public/javascripts/app.js:

const formId = 'telegramForm'
const form = document.getElementById(formId)
//функция для захвата данных из тегов формы и синтеза JSON-обьекта 
function toJSONString(form) {
  var obj = {}
  var elements = form.querySelectorAll('input, select, textarea')
  for (var i = 0; i < elements.length; ++i) {
    var element = elements[i]
    var name = element.name
    var value = element.value
    if (name) {
      obj[ name ] = value
    }
  }
  return JSON.stringify(obj)
}
if (form) {
  form.addEventListener('submit', event => {
    event.preventDefault()
    //получаем данные из формы
    const json = toJSONString(form)
    //создаем соединение
    const formReq = new XMLHttpRequest()
    formReq.open('POST', '/telegram', true)
    ///////////////////////////////////
    /////////////SweetAlert//////////
    ///////////////////////////////////
    //обрабатываем ответ сервера
    formReq.onload = function(oEvent) {
      if (formReq.status === 200) {
        swal({
          title: 'Успешно отправлено!',
          icon: 'success',
          timer: 2000
        })
        document.querySelector('.sa-success').style.display = 'block'
        document.querySelector('.sa-button-container').style.opacity = '0'
      }
      if (formReq.status !== 200) {
        swal({
          title: 'Произошла ошибка!',
          icon: 'error',
          timer: 2000
        })
        document.querySelector('.sa-error').style.display = 'block'
        document.querySelector('.sa-button-container').style.opacity = '0'
      }
    }
    ////////////////////////////
    ////////////////////////////
    formReq.setRequestHeader('Content-Type', 'application/json')
    //отправляем
    formReq.send(json)
  })
}

Back


На стороне сервера для начала нужно отловить запрос со стороны клиента, для этого в роутере пишем:

routes/index.js:

//Я вынес логику обработки данных в отдельный файл
const ctrlTelegram = require('../api/telegramMsg');
router.post('/telegram', ctrlTelegram.sendMsg);

api/telegramMsg.js:

module.exports.sendMsg = (req, res) => {
  //токен и id чата берутся из config.json
  const config = require('../config/config.json');
  let http = require('request')
  let reqBody = req.body
  //каждый элемент обьекта запихиваем в массив
  let fields = [
    '<b>Name</b>: ' + reqBody.name,
    '<b>Email</b>: ' + reqBody.email,
    reqBody.text
  ]
  let msg = ''
  //проходимся по массиву и склеиваем все в одну строку
  fields.forEach(field => {
    msg += field + '\n'
  });
  //кодируем результат в текст, понятный адресной строке
  msg = encodeURI(msg)
  //делаем запрос
  http.post(`https://api.telegram.org/bot${config.telegram.token}/sendMessage?chat_id=${config.telegram.chat}&parse_mode=html&text=${msg}`, function (error, response, body) {  
    //не забываем обработать ответ
    console.log('error:', error); 
    console.log('statusCode:', response && response.statusCode); 
    console.log('body:', body); 
    if(response.statusCode===200){
      res.status(200).json({status: 'ok', message: 'Успешно отправлено!'});
    }
    if(response.statusCode!==200){
      res.status(400).json({status: 'error', message: 'Произошла ошибка!'});
    }
  });

}

Для упрощения процесса запроса установлен пакет 'request'.

npm i request

config/config.json:

{
  "telegram": {
    "token": "bot_token",
    "chat": "chat_id"
  }
}

Итак, что же здесь происходит?


В запросе мы передали json, поэтому на стороне сервера с данными можем работать как с обычным объектом.

Для удобства каждое значение объекта запихиваем в массив.
API телеграмма позволяет передать данные посредством текста в адресной строке, поэтому проходим по массиву и создаём длинную строку. Чтобы можно было передать HTML теги, необходимо закодировать строку в универсальный идентификатор (метод encodeURI()), чтобы не вылезала ошибка.

Теперь можно наконец отправить это всё на сервер телеграмма. Делаем запрос (нажимаем кнопку 'Отправить') и вуаля, сообщение отправлено. Не забываем обработать ответ, а то мало ли что.

После всех манипуляций, ответ приходит на фронт и уведомляет прошло все норм или не очень.
Из-за того, что для примера не использовал сборщик, а библиотечка всплывашки рассчитана на модульную сборку, пришлось немного поколхозить при ее вызове на фронте.

formSent

message

Если посмотреть в логи приложения на сервере, можно увидеть примерно следующее:

image

Поздравляю! Теперь вы знаете как отправлять сообщения с вашего сайта в Telegram.

Я описал только общую концепцию данного процесса, поэтому настоятельно рекомендую ознакомится с исходным кодом данного примера.
+20
13.8k 141
Comments 15