Работаем с webhook на примере Telegram [PHP/Python/Node.js]

DrZzz

Client
Регистрация
19.03.2021
Сообщения
59
Реакции
225
Баллы
33
101102


На момент написания статьи появился ещё один способ реализовать работу с вебхуками, настоятельно рекомендую ознакомиться

В данной статье мы рассмотрим, как работать с вебхуками в ZennoPoster (в данном примере с TG) и как реализовать скрипт, который будет принимать запросы. В статье представлены способы поднятия скрипта и для хостинга, и для VPS. Рассмотрим реализацию на PHP, Node.js и Python.

Что это такое ваш «вебхук»:
Webhook – это автоматически сгенерированный HTTP-запрос, созданный на основе каких-то данных. Он запускается предопределенным событием или действием в исходной системе и передается системе, с которой исходная система пытается установить связь.

Webhook работает быстрее, чем опрос или API. Вместе с этим для разработчиков он является менее трудоемким с точки зрения работы. Применительно к приложениям, Webhook – это не что иное, как SMS-уведомления, которые мы получаем во время использования приложения. Например, при покупке некого товара в Интернете продавец присылает вам уведомление по SMS.

Аналогично, каждый раз, когда в исходной системе происходит некоторое событие/действие, система принимающей стороны уведомляется через Webhook.

Источник: https://dzen.ru/a/YuwNNFsDd1B9M8zg

Как будет работать webhook в наших шаблонах:
  1. Telegram передает на наш сервер данные в виде json
  2. Наш сервер сохраняет данные* в нашу базу данных
  3. Из базы данных мы можем запросить json с помощью ZennoPoster и использовать его в своих целях
* Наш сервер может сохранять не весь массив данных, а только какую-то часть, в таком случае мы должны указать в коде, что требуется сохранить, в пункте 4 я приведу примеры под такую реализацию.
Для сайта/IP адреса нам нужен SSL сертификат, подойдет и самоподписной, и которые предоставляют хостинги бесплатно.

webhook.png



1. Выбираем и настраиваем платформу
Хостинг для сайтов

На самом деле подойдет любой, который поддерживает PHP/Python/Node.js и есть возможность создать бд. Создаем отдельный каталог или же можем работать в корневом каталоге сайта.

VPS/VDS
В данном случае потребуется вручную поднимать phpMyAdmin, nginx (либо apache) и устанавливать самоподписные сертификаты. Я беру под такие задачи сервер с параметрами: AMD Epic 1 ядро, 2 Гб ОЗУ, 30 GB NVMe, ОС выбрал Ubuntu 18.04.
В таких случаях я обычно пользуюсь приведенными ниже гайдами:

2. phpMyAdmin
Первым делом нам требуется скачать архив, который расположен в самом низу этой статьи. Внутри архива есть папка «webhook», в которой расположены скрипты под разные ЯП – python, node.js и php. Также имеется файл zenno.sql, который потребуется импортировать в phpMyAdmin.
Этот способ подходит и для хостинга, и для VPS, т.к. используется phpMyAdmin в обоих случаях.
  1. Входим в pma, выбираем базу данных, в которую будет импортироваться таблица
  2. Нажимаем «Импорт»
  3. Выбираем файл zenno.sql и нажимаем «Вперёд»
3. Устанавливаем файлы для работы вебхука
Для хостинга

Самым простым и быстрым способом является установить файлы из папки php в желаемый каталог на хостинге. Хостинги без каких-либо настроек сразу смогут работать с этими файлами. Чтобы все заработало нам остается указать необходимые данные от базы данных в connect.php.

Для VPS/VDS
PHP

Для запуска PHP скрипта нам требуется загрузить файлы в директорию, которая была указана в конфигурации nginx/apache. В файле connect.php требуется указать данные от базы данных.

Python
Для установки и запуска Python скрипта, нам потребуется установить необходимые пакеты:
sudo pip install configparser && sudo pip install flask && sudo pip install mysql-connector-python
После установки и заполнения данных от бд в config.ini, мы можем запустить наш скрипт, командой python main.py

Node.js
Для Node.js скрипта, потребуется установить пакеты и сам Node.js:
sudo apt install nodejs && sudo apt install npm && sudo npm i express && sudo npm i body-parser && sudo npm i mysql
После установки и заполнения данных от бд в main.js, мы можем запустить наш скрипт, командой node main.js

Конфигурация nginx
В основном я проксирую порт 8081, на котором запущены скрипты, будь то python или же node.js. Сам же nginx с SSL размещается на порте 8443 и указываю путь до сертификатов. Часть конфигурационного файла:
Код:
Развернуть Свернуть Копировать
server {
    listen 8443 ssl http2 default_server;
    listen [::]:8443 ssl http2 default_server;
    server_name zennolab.com;
  
    ssl_certificate "/root/certificate.crt";
    ssl_certificate_key "/root/privkey.key";

    location / {
        proxy_pass http://localhost:8081;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

4. Разбор кода
PHP
connect.php
– в данном файле указываются данные для подключения к бд. Код с комментариями:
PHP:
Развернуть Свернуть Копировать
<?php
header('Access-Control-Allow-Origin: *');
  
    //Адерс бд, если стоит все на одном сервере, то localhost
    $server = "server001.hosting.reg.ru";
  
    //Пользователь бд, должен иметь права к удаленном подключению, если бд находится не на localhost
    $username = "u1_zenno";
  
    //Пароль от пользователя
    $password = "zenno";
  
    //Название базы данных
    $database = "u1_user";
  
    // Подключение к базе данный через MySQLi
    $mysqli = new mysqli($server, $username, $password, $database);
  ?>
webhook.php – файл получает тело запроса, которое отправляется с помощью метод POST, далее мы берем данные из connect.php для подключения к базе данных и выполняем команду SQL, в поле json содержится тело запроса. Код с комментариями:
PHP:
Развернуть Свернуть Копировать
<?php
    header('Access-Control-Allow-Origin: *');
    header('Content-type: application/json');

    //Получаем тело запроса
    $telegram = file_get_contents('php://input');

    //Подключаемся к базе данных по указанным данным из connect.php
    require_once("connect.php");

    //Отправляем SQL запрос в котором будет содержаться тело нашего запроса
    $result_query_insert = $mysqli->query("INSERT INTO `zenno` (json) VALUES ('".$telegram."')");
            $result_query_insert->close();
            $mysqli->close();
?>

Python
config.ini
– в конфигурационном файле указываются данные для подключения к бд. Код с комментариями:
INI:
Развернуть Свернуть Копировать
[db]
;Адерс бд, если стоит все на одном сервере, то localhost
host=server001.hosting.reg.ru
;Пользователь бд, должен иметь права к удаленном подключению, если бд находится не на localhost
user=u1_zenno
;Пароль от пользователя
pass=zenno
;Название базы данных
dbname=u1_user
main.py – алгоритм примерно тот же, что и на php. Только в данном случае нам требуется запустить сначала flask, а уже после этого получать запросы, которые к нам приходят. Код с комментариями:
Python:
Развернуть Свернуть Копировать
# -*- coding: utf-8 -*-
from flask import Flask, request
from mysql.connector import connect, Error
import configparser
import json

app = Flask(__name__)

#Читаем файл config.ini для дальнейшего использования в коде
config = configparser.ConfigParser()
config.read("config.ini", encoding='utf-8')

#Запрашиваем необходимые нам строки из config.ini
dbHost = config["db"]["host"]
dbUser = config["db"]["user"]
dbPass = config["db"]["pass"]
dbName = config["db"]["dbname"]

#Если POST запрос был отправлен на адрес /, то в таком случае начинает работать функция webhook
@app.route('/', methods=['POST'])
def webhook():
    try:
        with connect(host=dbHost,user=dbUser,password=dbPass,database=dbName) as connection:
            #Сначала мы получаем тело запроса - request.json, далее его преобразуем в json
            #После этого переводим его в строку (str), чтобы можно было корректно отправить запрос в бд
            sqlQuery = str(json.dumps(request.json))
            #Кодируем все символы в UTF-8, чтобы не возникло проблем с запросом к бд
            sqlQuery = sqlQuery.encode().decode('unicode_escape')
          
            #Сам запрос к бд
            #На самом деле его можно сократить до "INSERT INTO `zenno` (`json`) VALUES ('" + sqlQuery + "')"
            #Так как наша таблица автоматически ставит и id, и status
            createDbQuery = "INSERT INTO `zenno` (`id`, `json`, `status`) VALUES (NULL, '" + sqlQuery + "', 0)"
            #Дополнительный запрос без которого не происходило сохранение данных строкой выше
            commitQuery = "COMMIT"
            with connection.cursor() as cursor:
                #Отправляем запросы
                cursor.execute(createDbQuery)
                cursor.execute(commitQuery)
              
    except Error as e:
        print(e)
  
    return 'ok'

#Запускаем сервер на порте 8081
app.run(host='0.0.0.0', port=8081)

Node.js
main.js
– алгоритм схож с кодом python, только в данном случае данные для подключения указаны прямо в коде. Код с комментариями:
JavaScript:
Развернуть Свернуть Копировать
const express = require("express")
const bodyParser = require("body-parser")
const mysql = require('mysql');

const app = express()

//Создаем подключение к базе данных, по указанным данным
var connection = mysql.createConnection({
    host: 'server001.hosting.reg.ru', //Адерс бд, если стоит все на одном сервере, то localhost
    user: 'u1_zenno', //Пользователь бд, должен иметь права к удаленном подключению, если бд находится не на localhost
    password: 'zenno', //Пароль от пользователя
    database: 'u1_user' //Название базы данных
});

app.use(bodyParser.json())

//Если POST запрос был отправлен на адрес /, то в таком случае начинает работать код ниже
app.post("/", (req, res) => {
    connection.connect(); //Подключаемся к базе данных из var connection
    connection.query("INSERT INTO `zenno` (`id`, `json`, `status`) VALUES (NULL, '" + JSON.stringify(req.body) + "', 0)", function (error, results, fields) {
      if (error) throw error; // Отправляем SQL запрос, содержащий тело запроса, но его перед этим переводим в строку с помощью JSON.stringify
    });
    connection.end(); //Закрываем подключение к бд
    res.status(200).end() //Отправляем статус 200
})

//Запускаем сервер на порте 8081 и оповещаем об этом в консоли
const server = app.listen(8081, function () {
    console.log('Server listening on port ' + server.address().port)
})

Bonus
Также есть бонусный код, который извлекает id чата, ник и дату, реализованный на PHP. Для этого требуется импортировать новую таблицу zenno2.sql из той же папки. Код с комментариями:
PHP:
Развернуть Свернуть Копировать
<?php
    header('Access-Control-Allow-Origin: *');
    header('Content-type: application/json');

    //Получаем тело запроса
    $telegram = file_get_contents('php://input');
  
    //Извлекаем из переменной telegram, значение id из chat
    //Аналогично и для других строк
    $chatId = $telegram["message"]["chat"]["id"];
    $chatUser = $telegram["message"]["chat"]["username"];
    $chatDate = $telegram["message"]["date"];

    //Подключаемся к базе данных по указанным данным из connect.php
    require_once("connect.php");

    //Отправляем SQL запрос в котором будет содержаться тело нашего запроса и другие значения, которые мы указали выше
    $result_query_insert = $mysqli->query("INSERT INTO `zenno` (json, chatId, chatUser, chatDate) VALUES ('".$telegram."', '".$chatId."', '".$chatUser."', '".$chatDate."')");
            $result_query_insert->close();
            $mysqli->close();
?>

5. Работа в ZennoPoster
Входные настройки и алгоритм работы проекта:
Сначала регистрируем webhook, указав токен бота и ссылку до вебхука. Для работы в других режимах потребуется указать данные от базы данных.

101104


zenno.png

Подробно рассмотрен проект в видео и рассказано о том, как использовать это в своих проектах:


Минимальная допустимая версия для запуска - 7.1.3.0 (5.47.0)
 
Номер конкурса статей
  1. Восемнадцатый конкурс статей
Тема статьи
  1. Нестандартные хаки
  2. Соц. сети

Вложения

Последнее редактирование модератором:
Ну, ооочень крутая тема! Спасибо тебе, мил человек! Даешь аналогичную тему по вебсокетам! :bi:
 
  • Спасибо
Реакции: DrZzz
Мне интересно одно, почему именно вебхук и чем он проигрывает вебсокет. Ради интереса залез в интернет почитать что это за звери такие и по описанию вроде вебсокет должен выигрывать, так как он (вебсокет) более напрямую работает с зенкой чем вебхук.
 
И хотелось бы узнать, а как подключить вебхук к комментариям вк в группе ?
 
И хотелось бы узнать, а как подключить вебхук к комментариям вк в группе ?
Это доступно в моем варианте, ниже закреплено)

Мне интересно одно, почему именно вебхук и чем он проигрывает вебсокет. Ради интереса залез в интернет почитать что это за звери такие и по описанию вроде вебсокет должен выигрывать, так как он (вебсокет) более напрямую работает с зенкой чем вебхук.
Вебхук лови запросы, зенка таким не обладает, только можно сделать GET/POST запрос, насколько мне помнятся на какой то бирже биткойна можно подключить только Вебхук (запросы не работаю)
 
Это доступно в моем варианте, ниже закреплено)
Да я читал и первую и вторую статью) Но в комментариях было указано что такой метод будет медленно работать. А мне нужен бот который сможет моментально отвечать человеку типа "напиши свой знак зодиака" и получи гороскоп на сегодня и т.д. в комментариях под определенной записью.
 
Да я читал и первую и вторую статью) Но в комментариях было указано что такой метод будет медленно работать. А мне нужен бот который сможет моментально отвечать человеку типа "напиши свой знак зодиака" и получи гороскоп на сегодня и т.д. в комментариях под определенной записью.
Медленный, ха, в каких комментариях?
Я тестил вк, запросы моментально прилетают (уведомление),остальное от скорости зенки зависит, ну я как понимаю задача у тебя легкая обрабатываться моментально будет
 
Не понял, была же уже статья в конкурсе похожая по теме. И теперь её нет.
походу ее тоже не пропустили :bk:
Автор предыдущей статьи, к сожалению, выложил свой материал исключительно с целью продажи своего бота. Прочитав её, нельзя научится работать с вебхуками. Дорабатывать он её отказался, поэтому тема перенесена во внеконкурсный раздел.

@DrZzz предложил написать полный пошаговый материал на эту тему. Тем самым закрыли пробел :-)

Это доступно в моем варианте, ниже закреплено)
Пожалуйста, не занимайтесь рекламой в чужих темах.
 
Последнее редактирование модератором:
  • Спасибо
Реакции: himin
Хорошая полезная статья с примерами кода для использования. :az:
 
  • Спасибо
Реакции: DrZzz
Мне интересно одно, почему именно вебхук и чем он проигрывает вебсокет. Ради интереса залез в интернет почитать что это за звери такие и по описанию вроде вебсокет должен выигрывать, так как он (вебсокет) более напрямую работает с зенкой чем вебхук.

Ничем не проигрывает, это разные методы обмена данными. Я рассказал о вебхуках, т.к. работал с ними в ZennoPoster, а вот с вебсокетами был опыт только на Python и Node.js, но никогда не доводилось его использовать в ZennoPoster. Возможно, к следующему конкурсу смогу закрыть и этот вопрос

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

Как я знаю у них есть Callback API, о котором можно почитать здесь - https://dev.vk.com/api/events/overview
Думаю в данном случае нужно включить wall_reply_new, чтобы получать уведомления о новых комментариях (https://dev.vk.com/method/groups.setCallbackSettings)
 
  • Спасибо
Реакции: Дмитрий202020
1. Выбираем и настраиваем платформу

Если ZP и Sql установлен на хостинге с Windows Server?
В этом случае какой набор необходим?

Webhook работает быстрее, чем опрос или API
Только изучаю, поэтому спрошу.
Получается опрашивать базу в бесконечном цикле с нулевой паузой на обновление менее затратно чем делать это через API запросы в Telegram Update?
 
Последнее редактирование:
Здравствуйте, вроде сделал все по инструкции
в боте пишу любую фразу, он мне только ее возвращает с дилеем примерно минута
если написать что то другое, будет все равно возвращать первую фразу

в чем может быть проблема ?
 

Вложения

  • Screenshot_1.jpg
    Screenshot_1.jpg
    67,9 KB · Просмотры: 274
Если ZP и Sql установлен на хостинге с Windows Server?
В этом случае какой набор необходим?


Только изучаю, поэтому спрошу.
Получается опрашивать базу в бесконечном цикле с нулевой паузой на обновление менее затратно чем делать это через API запросы в Telegram Update?
Не думаю.
Telegram Update -> как я писал в своей статье бот таким методом проработает 2-3 дня, дальше какой то баг, offset становится вообще не похожем на предыдущий + я пытался скопировать актуальный offset и вставить в запрос, в итоге тот же баг. Мне кажется это из-за того что постоянный спам серверов Telegram
 
Не думаю.
Telegram Update -> как я писал в своей статье бот таким методом проработает 2-3 дня, дальше какой то баг, offset становится вообще не похожем на предыдущий + я пытался скопировать актуальный offset и вставить в запрос, в итоге тот же баг. Мне кажется это из-за того что постоянный спам серверов Telegram
То есть Telegram Update не рассчитан на частый зацикл?
 
@ibred напиши в телеграм плз (@sa1rus777), никак написать тебе не могу!
 
если в getWebhookInfo показывает ошибку Wrong response from the webhook: 500 Internal Server Error
проблема в хостинге ?
 
Здравствуйте, вроде сделал все по инструкции
в боте пишу любую фразу, он мне только ее возвращает с дилеем примерно минута
если написать что то другое, будет все равно возвращать первую фразу

в чем может быть проблема ?
1. Способ (решение такое себе) Попробуйте заменить содержимое в файле PHP webhook.php

PHP:
Развернуть Свернуть Копировать
<?php
header('Access-Control-Allow-Origin: *');
header('Content-type: application/json');

//Получить тело запроса
$telegram = file_get_contents('php://input');

if (empty($telegram)) {
  die(); // если запрос пустой, не делать ничего
}

//Подключиться к базе данных, используя указанные данные из connect.php
require_once("connect.php");

//Проверить, есть ли запрос уже в базе данных
$result_query_select = $mysqli->query("SELECT * FROM `zenno` WHERE json = '".$telegram."'");
if ($result_query_select->num_rows == 0) {
    //Отправить запрос SQL, который будет содержать тело нашего запроса
    $result_query_insert = $mysqli->query("INSERT INTO `zenno` (json) VALUES ('".$telegram."')");
    $result_query_insert->close();
}
$result_query_select->close();
$mysqli->close();

// Send a response to let Telegram API know that you received a request
http_response_code(200);
echo "OK";
?>

2. Способ

Добавьте в webhook.php в конец перед "?>"

PHP:
Развернуть Свернуть Копировать
// Send a response to let Telegram API know that you received a request
http_response_code(200);
echo "OK";
 
Последнее редактирование:
1. Способ (решение такое себе) Попробуйте заменить содержимое в файле PHP webhook.php

PHP:
Развернуть Свернуть Копировать
<?php
header('Access-Control-Allow-Origin: *');
header('Content-type: application/json');

//Получить тело запроса
$telegram = file_get_contents('php://input');

if (empty($telegram)) {
  die(); // если запрос пустой, не делать ничего
}

//Подключиться к базе данных, используя указанные данные из connect.php
require_once("connect.php");

//Проверить, есть ли запрос уже в базе данных
$result_query_select = $mysqli->query("SELECT * FROM `zenno` WHERE json = '".$telegram."'");
if ($result_query_select->num_rows == 0) {
    //Отправить запрос SQL, который будет содержать тело нашего запроса
    $result_query_insert = $mysqli->query("INSERT INTO `zenno` (json) VALUES ('".$telegram."')");
    $result_query_insert->close();
}
$result_query_select->close();
$mysqli->close();

// Send a response to let Telegram API know that you received a request
http_response_code(200);
echo "OK";
?>

2. Способ

Добавьте в webhook.php в конец перед "?>"

PHP:
Развернуть Свернуть Копировать
// Send a response to let Telegram API know that you received a request
http_response_code(200);
echo "OK";
Здравствуйте, уже сделал с помощью chatgpt :)
 
  • Спасибо
Реакции: Strannik64
запрос был простым и банальным

как подключить webhook telegram к data base на хостинге ?
напиши php скрипт для подключения
Ля чат gpt стал очень хорошим инструментом для написания кодов за которые просят от 4к прикольно, сам 24/4 в нем, любой запрос только через него)
 
  • Спасибо
Реакции: Strannik64
1. Способ (решение такое себе) Попробуйте заменить содержимое в файле PHP webhook.php

PHP:
Развернуть Свернуть Копировать
<?php
header('Access-Control-Allow-Origin: *');
header('Content-type: application/json');

//Получить тело запроса
$telegram = file_get_contents('php://input');

if (empty($telegram)) {
  die(); // если запрос пустой, не делать ничего
}

//Подключиться к базе данных, используя указанные данные из connect.php
require_once("connect.php");

//Проверить, есть ли запрос уже в базе данных
$result_query_select = $mysqli->query("SELECT * FROM `zenno` WHERE json = '".$telegram."'");
if ($result_query_select->num_rows == 0) {
    //Отправить запрос SQL, который будет содержать тело нашего запроса
    $result_query_insert = $mysqli->query("INSERT INTO `zenno` (json) VALUES ('".$telegram."')");
    $result_query_insert->close();
}
$result_query_select->close();
$mysqli->close();

// Send a response to let Telegram API know that you received a request
http_response_code(200);
echo "OK";
?>

2. Способ

Добавьте в webhook.php в конец перед "?>"

PHP:
Развернуть Свернуть Копировать
// Send a response to let Telegram API know that you received a request
http_response_code(200);
echo "OK";
111963



Почему то все равно повторяет сообщения. Подскажите где косяк может быть?
 
решил
 
Последнее редактирование:

Кто просматривает тему: (Всего: 0, Пользователи: 0, Гости: 0)