В этом шаге вы примете сообщение из очереди уведомлений канала и ответите на него. Очередь работает без публичного адреса у приложения — подойдёт ноутбук.
Что понадобится
- переменные
GRATIO_URL,GRATIO_ID,GRATIO_TOKENиз Шаг 2. Первый запрос; - телефон, с которого вы напишете на подключённый номер.
Как работает очередь
Приложение Gratio
│ receiveNotification ────────────►│ ждёт до receiveTimeout секунд
│◄──────── {receiptId, body} ──────│ самое старое неподтверждённое
│ │
│ обработать body │
│ │
│ deleteNotification/{receiptId} ─►│ подтвердить
│◄──────── {"result": true} ───────│
│ │
│ receiveNotification ────────────►│ следующее…
Пока уведомление не удалено, оно приходит снова — даже после перезапуска приложения. Так ничего не теряется.
1. Напишите на подключённый номер
С личного телефона отправьте на номер канала: «Здравствуйте!».
2. Заберите уведомление
receiveTimeout — сколько секунд ждать, если очередь пуста (до 60):
curl
curl "$GRATIO_URL/waInstance$GRATIO_ID/receiveNotification/$GRATIO_TOKEN?receiveTimeout=20"
Python
import os
import requests
BASE = f"{os.environ['GRATIO_URL']}/waInstance{os.environ['GRATIO_ID']}"
TOKEN = os.environ["GRATIO_TOKEN"]
url = f"{BASE}/receiveNotification/{TOKEN}"
params = {"receiveTimeout": "20"}
response = requests.get(url, params=params, timeout=90)
print(response.status_code, response.json())
Node.js
const { GRATIO_URL, GRATIO_ID, GRATIO_TOKEN } = process.env;
const base = `${GRATIO_URL}/waInstance${GRATIO_ID}`;
const query = new URLSearchParams({ receiveTimeout: "20" });
const url = `${base}/receiveNotification/${GRATIO_TOKEN}?${query}`;
const response = await fetch(url);
console.log(response.status, await response.json());
PHP
<?php
$base = getenv('GRATIO_URL') . '/waInstance' . getenv('GRATIO_ID');
$url = $base . '/receiveNotification/' . getenv('GRATIO_TOKEN');
$url .= '?' . http_build_query(['receiveTimeout' => '20']);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 90,
]);
$response = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), PHP_EOL;
print_r($response);
Вы увидите:
{
"receiptId": 1,
"body": {
"typeWebhook": "incomingMessageReceived",
"instanceData": {"idInstance": 7100000001, "wid": "79990000001@c.us", "typeInstance": "v3", "channel": "max"},
"timestamp": 1791823658,
"idMessage": "117428955282341903",
"eventId": "msg:8695836:117428955282341903",
"senderData": {
"chatId": "8695836", "chatName": "Мария", "chatType": "user",
"sender": "8695836", "senderName": "Мария", "senderType": "user",
"senderContactName": "", "senderPhoneNumber": 0
},
"messageData": {"typeMessage": "textMessage", "textMessageData": {"textMessage": "Здравствуйте!"}}
}
}
receiptId — номер в очереди, body — само уведомление. senderData.chatId — куда отвечать. Пустая очередь — ответ null: напишите ещё раз и повторите запрос.
3. Подтвердите уведомление
Передайте receiptId в конце пути:
curl
curl -X DELETE "$GRATIO_URL/waInstance$GRATIO_ID/deleteNotification/$GRATIO_TOKEN/1"
Python
import os
import requests
BASE = f"{os.environ['GRATIO_URL']}/waInstance{os.environ['GRATIO_ID']}"
TOKEN = os.environ["GRATIO_TOKEN"]
url = f"{BASE}/deleteNotification/{TOKEN}/1"
response = requests.delete(url, timeout=60)
print(response.status_code, response.json())
Node.js
const { GRATIO_URL, GRATIO_ID, GRATIO_TOKEN } = process.env;
const base = `${GRATIO_URL}/waInstance${GRATIO_ID}`;
const url = `${base}/deleteNotification/${GRATIO_TOKEN}/1`;
const response = await fetch(url, { method: "DELETE" });
console.log(response.status, await response.json());
PHP
<?php
$base = getenv('GRATIO_URL') . '/waInstance' . getenv('GRATIO_ID');
$url = $base . '/deleteNotification/' . getenv('GRATIO_TOKEN') . '/1';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$response = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), PHP_EOL;
print_r($response);
Вы увидите: {"result": true}. Следующий receiveNotification отдаст уже другое уведомление.
4. Ответьте
chatId — из senderData.chatId пришедшего сообщения:
curl
curl -X POST "$GRATIO_URL/waInstance$GRATIO_ID/sendMessage/$GRATIO_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"chatId": "8695836",
"message": "Здравствуйте! Чем помочь?"
}'
Python
import os
import requests
BASE = f"{os.environ['GRATIO_URL']}/waInstance{os.environ['GRATIO_ID']}"
TOKEN = os.environ["GRATIO_TOKEN"]
url = f"{BASE}/sendMessage/{TOKEN}"
payload = {"chatId": "8695836", "message": "Здравствуйте! Чем помочь?"}
response = requests.post(url, json=payload, timeout=60)
print(response.status_code, response.json())
Node.js
const { GRATIO_URL, GRATIO_ID, GRATIO_TOKEN } = process.env;
const base = `${GRATIO_URL}/waInstance${GRATIO_ID}`;
const url = `${base}/sendMessage/${GRATIO_TOKEN}`;
const payload = { chatId: "8695836", message: "Здравствуйте! Чем помочь?" };
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
console.log(response.status, await response.json());
PHP
<?php
$base = getenv('GRATIO_URL') . '/waInstance' . getenv('GRATIO_ID');
$url = $base . '/sendMessage/' . getenv('GRATIO_TOKEN');
$payload = ['chatId' => '8695836', 'message' => 'Здравствуйте! Чем помочь?'];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$response = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), PHP_EOL;
print_r($response);
Вы увидите ответ на телефоне. В очередь придут ещё два уведомления о вашем сообщении: outgoingAPIMessageReceived и outgoingMessageStatus со статусом sent, — их тоже нужно забрать и удалить.
Готовый цикл
Цикл забирает уведомления, отвечает на текст и подтверждает каждое уведомление.
Python
import os
import requests
BASE = f"{os.environ['GRATIO_URL']}/waInstance{os.environ['GRATIO_ID']}"
TOKEN = os.environ["GRATIO_TOKEN"]
def handle(body: dict) -> None:
if body["typeWebhook"] != "incomingMessageReceived":
return
data = body["messageData"]
if data["typeMessage"] != "textMessage":
return
text = data["textMessageData"]["textMessage"]
requests.post(f"{BASE}/sendMessage/{TOKEN}", timeout=60, json={
"chatId": body["senderData"]["chatId"],
"message": f"Получили: {text}",
"clientMessageId": f"echo-{body['idMessage']}",
})
while True:
url = f"{BASE}/receiveNotification/{TOKEN}"
item = requests.get(url, params={"receiveTimeout": 50}, timeout=70).json()
if item is None:
continue
try:
handle(item["body"])
finally:
requests.delete(f"{BASE}/deleteNotification/{TOKEN}/{item['receiptId']}", timeout=30)
Node.js
const { GRATIO_URL, GRATIO_ID, GRATIO_TOKEN } = process.env;
const base = `${GRATIO_URL}/waInstance${GRATIO_ID}`;
async function handle(body) {
if (body.typeWebhook !== "incomingMessageReceived") return;
const data = body.messageData;
if (data.typeMessage !== "textMessage") return;
await fetch(`${base}/sendMessage/${GRATIO_TOKEN}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chatId: body.senderData.chatId,
message: `Получили: ${data.textMessageData.textMessage}`,
clientMessageId: `echo-${body.idMessage}`,
}),
});
}
for (;;) {
const r = await fetch(`${base}/receiveNotification/${GRATIO_TOKEN}?receiveTimeout=50`);
const item = await r.json();
if (!item) continue;
try {
await handle(item.body);
} finally {
await fetch(`${base}/deleteNotification/${GRATIO_TOKEN}/${item.receiptId}`, { method: "DELETE" });
}
}
clientMessageId защищает от дубля: если цикл упадёт после отправки и обработает то же уведомление снова, второго ответа не будет. Подробно — в статье Надёжная отправка без дублей.
Если не получилось
| Ответ | Причина | Что сделать |
|---|---|---|
null |
уведомлений нет | напишите на номер ещё раз; увеличьте receiveTimeout |
400 webhook_set |
у канала задан вебхук — очередь читает он | снимите вебхук: setSettings с "webhookUrl": "" |
| одно и то же уведомление снова и снова | оно не удалено | вызовите deleteNotification с его receiptId |
Что дальше
Очередь удобна для начала и для приложений без публичного адреса. Сервер с HTTPS может получать уведомления сразу — вебхуком.
Шаг 4. Уведомления на свой серверПринять уведомления вебхуком — обработчик на Python или Node.js, публичный адрес, настройка канала и проверка.