Yangilanishlarni olish
Bot uchun har bir voqea β Update. Ularni ikki usuldan biri bilan olasiz: getUpdates (bot o'zi so'raydi) yoki webhook (Zamin sizning serveringizga yuboradi).
Yangilanish turlari
| Tur | Qachon keladi |
|---|---|
message | Foydalanuvchi matn, fayl, kontakt yoki joylashuv yubordi. Start bosilganda /start (yoki /start PARAM) matni keladi. Guruhda β privacy mode ruxsat bergan xabarlar va xizmat xabarlari; kanal izohlari chatida β izohlar. |
edited_message | Foydalanuvchi o'z matnli xabarini tahrirladi (48 soat ichida). |
callback_query | Foydalanuvchi bot xabaridagi inline tugmani bosdi (shaxsiy chat yoki guruh). |
my_chat_member | Foydalanuvchi botni boshladi, blokladi/to'xtatdi yoki blokdan chiqarib qayta boshladi; guruh yoki kanalda β botni qo'shishdi, admin qilishdi, chiqarishdi. |
chat_member | Guruhda odam qo'shildi, chiqdi, chiqarildi yoki ban qilindi (faqat shu voqealarda). Faqat admin-botlarga va faqat allowed_updates'da aniq ko'rsatilsa. |
channel_post | Bot admin bo'lgan kanalda yangi post. |
edited_channel_post | Kanal posti tahrirlandi. |
Guruh va kanallardagi yangilanishlar: Guruhlar va kanallar.
Foydalanuvchi o'z xabarini o'chirsa, bot xabardor qilinmaydi.
my_chat_member
Shaxsiy chatda:
| Voqea | old_chat_member.status | new_chat_member.status |
|---|---|---|
| Birinchi marta Start (yoki birinchi xabar) | left | member |
| Foydalanuvchi botni blokladi yoki "To'xtatish"ni bosdi | member | kicked |
| Blokdan chiqarib, qayta Start bosdi | kicked | member |
Guruh va kanalda: qo'shildi β left β member (kanalda β administrator), admin qilindi β member β administrator (huquqlar bilan), chiqarildi β β kicked, o'zi chiqdi (leaveChat) β β left. Misollar.
{
"update_id": 18,
"my_chat_member": {
"chat": {
"id": 4810235517,
"type": "private",
"first_name": "Ali",
"last_name": "Valiyev",
"username": "ali"
},
"from": {
"id": 4810235517,
"is_bot": false,
"first_name": "Ali",
"last_name": "Valiyev",
"username": "ali",
"language_code": "uz"
},
"date": 1791106200,
"old_chat_member": {
"user": {
"id": 1000001,
"is_bot": true,
"first_name": "Ob-havo",
"username": "ObHavoBot",
"can_join_groups": true,
"can_read_all_group_messages": false,
"supports_inline_queries": false
},
"status": "member"
},
"new_chat_member": {
"user": {
"id": 1000001,
"is_bot": true,
"first_name": "Ob-havo",
"username": "ObHavoBot",
"can_join_groups": true,
"can_read_all_group_messages": false,
"supports_inline_queries": false
},
"status": "kicked"
}
}
}getUpdates β long polling
getUpdates navbatdagi yangilanishlarni qaytaradi. Navbat bo'sh va timeout > 0 bo'lsa, server javobni yangilanish kelguncha yoki timeout tugaguncha ushlab turadi. Bu usul eng oddiy: ochiq port, domen yoki sertifikat kerak emas.
| Parametr | Ma'nosi |
|---|---|
offset | Oxirgi qayta ishlangan update_id + 1. Server update_id < offset bo'lgan barcha yangilanishlarni tasdiqlangan deb o'chiradi. Manfiy qiymat β oxirgi |offset| ta yangilanish. |
limit | 1β100, standart 100. |
timeout | Kutish, soniya. 0 (standart) β darhol javob. Maksimum 50 (kattaroq qiymat 50 ga qisqartiriladi). Tavsiya: 25β50. |
allowed_updates | Kerakli turlar ro'yxati. Bot uchun saqlanadi: ro'yxatda yo'q turdagi yangilanishlar umuman navbatga qo'yilmaydi. [] β barcha turlar (chat_member'dan tashqari β uni faqat aniq ko'rsatsangiz olasiz). Berilmasa β avvalgi sozlama qoladi. |
To'g'ri tsikl
offset'siz (yoki saqlangan qiymat bilan) getUpdates chaqiring.- Har bir yangilanishni qayta ishlang va
offset = update_id + 1qiling. - Keyingi chaqiruvda shu
offset'ni yuboring β oldingilar o'chadi.
Bot qayta ishga tushganda offset yo'qolsa, tasdiqlanmagan yangilanishlar qayta keladi. Muhim botlarda offset'ni faylga yoki bazaga saqlang.
import os
import time
import requests
TOKEN = os.environ["ZAMIN_BOT_TOKEN"]
API = f"https://api.zamin.app/bot{TOKEN}"
def handle(update):
message = update.get("message")
if message and "text" in message:
requests.post(
f"{API}/sendMessage",
json={"chat_id": message["chat"]["id"], "text": message["text"]},
timeout=15,
)
def main():
# Webhook o'rnatilgan bo'lsa, getUpdates 409 qaytaradi: avval o'chiramiz
requests.post(f"{API}/deleteWebhook", timeout=15)
offset = None
while True:
params = {"timeout": 30, "allowed_updates": ["message", "callback_query", "my_chat_member"]}
if offset is not None:
params["offset"] = offset
try:
r = requests.post(f"{API}/getUpdates", json=params, timeout=40)
body = r.json()
except (requests.RequestException, ValueError) as e:
print("Tarmoq xatosi:", e)
time.sleep(3)
continue
if not body.get("ok"):
if body.get("error_code") == 401:
raise SystemExit("Token noto'g'ri yoki bekor qilingan")
time.sleep((body.get("parameters") or {}).get("retry_after", 3))
continue
for update in body["result"]:
offset = update["update_id"] + 1
try:
handle(update)
except Exception as e: # bitta yangilanish butun botni to'xtatmasin
print("Xato:", update["update_id"], e)
if __name__ == "__main__":
main()Bir vaqtda faqat bittasi
- Webhook o'rnatilgan bo'lsa, getUpdates
409 Conflict: can't use getUpdates method while webhook is activeβ¦qaytaradi. Avval deleteWebhook. - setWebhook ochiq long poll'ni darhol
409bilan yopadi. - Bir vaqtda faqat bitta getUpdates kutishi mumkin: yangi chaqiruv eskisini
409 Conflict: terminated by other getUpdates requestβ¦bilan tugatadi. Bu xato ko'rsangiz β botingizning ikkinchi nusxasi ishlayapti.
Navbat: 24 soat / 1000 ta
Olinmagan (yoki webhookka yetkazilmagan) yangilanishlar 24 soat saqlanadi; bitta bot uchun ko'pi bilan 1000 ta β undan oshsa, eng eskilari o'chiriladi. Bot uzoq vaqt o'chiq tursa, eski yangilanishlar yo'qoladi.
Webhooklar
setWebhook bilan HTTPS manzil bersangiz, har bir yangilanish shu manzilga POST qilinadi. Tanasi β bitta Update JSON obyekti.
POST /zamin/webhook HTTP/1.1
Host: bot.example.com
Content-Type: application/json
User-Agent: ZaminBotAPI/1.0
X-Zamin-Bot-Api-Secret-Token: my-long-random-secret
{"update_id":17,"message":{"message_id":13,"from":{β¦},"chat":{β¦},"date":1791106200,"text":"/start"}}Talablar
- Faqat https://. Sertifikat ishonchli markaz tomonidan berilgan bo'lishi kerak (masalan, Let's Encrypt); o'z-o'zidan imzolangan sertifikat ishlamaydi.
- Portlar: 443, 80, 88, 8443 (port ko'rsatilmasa β 443).
- URL'da login/parol bo'lmasin; URL β€2048 belgi.
Maxfiy sarlavha
setWebhook'da secret_token (1β256 belgi, A-Z a-z 0-9 _ -) bersangiz, har bir so'rov X-Zamin-Bot-Api-Secret-Token sarlavhasi bilan keladi. Sarlavha mos kelmagan so'rovlarni rad eting β webhook manzilingizni topgan har kim soxta yangilanish yubora oladi. Solishtirishni doimiy vaqtli funksiya bilan qiling (Python'da hmac.compare_digest).
Yetkazish va qayta urinish
- Har qanday 2xx status β yetkazildi. Javob tanasi o'qilmaydi (Telegram'dagi kabi javobda metod chaqirish ishlamaydi).
- Kutish vaqti β 10 soniya. Yo'naltirishlarga (3xx) ergashilmaydi: ular xato hisoblanadi.
- Yangilanishlar bot uchun ketma-ket va tartib bilan yetkaziladi: bittasi yetkazilmaguncha keyingisi yuborilmaydi.
max_connectionssaqlanadi, lekin hozircha parallellik yo'q. - Xato bo'lsa (2xx emas, ulanish xatosi yoki timeout), o'sha yangilanish qayta yuboriladi: kutish 1, 2, 4, 8 β¦ soniyagacha ikki baravar oshadi va ko'pi bilan 10 daqiqa bo'ladi. Urinishlar yangilanish 24 soatdan eskirguncha davom etadi.
- Oxirgi xato getWebhookInfo'da:
last_error_message,last_error_date.
SSRF himoyasi: qaysi manzillar rad etiladi
Webhook manzilini istalgan kishi tanlashi mumkin, shuning uchun server ichki tarmoqqa so'rov yubormasligi uchun manzilni o'zi tekshiradi. Host nomi server tomonidan resolve qilinadi va setWebhook paytida ham, har bir yetkazishdan oldin ham tekshiriladi. Tekshirilgan IP manzilga to'g'ridan-to'g'ri ulaniladi (DNS rebinding'dan himoya; SNI va Host sarlavhasi asl nom bilan qoladi). Quyidagilar rad etiladi:
| Toifa | Misollar |
|---|---|
| Xususiy tarmoqlar | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, IPv6 ULA fc00::/7 |
| Loopback va "this network" | 127.0.0.0/8, 0.0.0.0/8, ::1, :: |
| Link-local va metadata | 169.254.0.0/16 (jumladan 169.254.169.254), fe80::/10 |
| CGNAT | 100.64.0.0/10 |
| Multicast va zaxira | 224.0.0.0/4, 240.0.0.0/4, ff00::/8 |
| Hujjat/test tarmoqlari | 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 198.18.0.0/15, 2001:db8::/32, 192.0.0.0/24 |
| IPv4 joylangan IPv6 shakllari | IPv4-mapped (::ffff:β¦), IPv4-compatible, NAT64 (64:ff9b::/96, 64:ff9b:1::/48), 6to4 (2002::/16, 192.88.99.0/24), Teredo (2001::/32), 100::/64, fec0::/10 |
| Zamin'ning o'zi | zamin.app va *.zamin.app, Zamin serverining IP manzillari |
| Lokal nomlar | localhost, *.localhost, localhost.localdomain, ip6-localhost, ip6-loopback, *.local, *.internal |
DNS javoblaridan birortasi ham ommaviy bo'lmagan manzil bo'lsa, butun manzil rad etiladi. Host nomida nuqta bo'lishi va faqat a-z 0-9 . - belgilaridan iborat bo'lishi kerak.
Webhook yoki getUpdates?
| getUpdates | Webhook | |
|---|---|---|
| Sozlash | Hech narsa kerak emas | Domen, HTTPS, ochiq port |
| Kechikish | Juda kichik (long poll) | Juda kichik |
| Bir nechta server | Faqat bitta nusxa | Yuk taqsimlagich ortida bir nechta nusxa |
| Mos keladi | Ishlab chiqish, kichik botlar, uy serveri | Production, serverless, katta botlar |
To'liq misollar: long polling echo bot va Flask webhook.