ការអភិវឌ្ឍបូត
Webhook
របៀបឲ្យម៉ាស៊ីនមេផ្ញើ update ថ្មីៗទៅអាសយដ្ឋាន HTTPS របស់បូតដោយផ្ទាល់ ជំនួសឲ្យ getUpdates — ការកំណត់ ការផ្ទៀងផ្ទាត់ ការព្យាយាមម្តងទៀត និងការឆ្លើយតាមការឆ្លើយតប។
getUpdates គឺជាវិធីដែលបូតសួរម៉ាស៊ីនមេជាបន្តបន្ទាប់។ ផ្ទុយទៅវិញ webhook គឺជាវិធីដែលពេលមាន update ថ្មី ម៉ាស៊ីនមេ Pabal ផ្ញើវាភ្លាមៗទៅអាសយដ្ឋាន HTTPS របស់បូត។ បើបូតដំណើរការលើម៉ាស៊ីនមេដែលបើកជានិច្ច (cloud ឬម៉ាស៊ីនមេក្នុងស្ថាប័ន) webhook គឺសមស្របជាង។
| getUpdates (long polling) | Webhook | |
|---|---|---|
| អ្នកណាចាប់ផ្តើមមុន | បូតសួរម៉ាស៊ីនមេ | ម៉ាស៊ីនមេផ្ញើទៅបូត |
| អ្វីដែលបូតត្រូវការ | គ្រាន់តែអ៊ីនធឺណិតចេញក្រៅ | អាសយដ្ឋាន HTTPS សាធារណៈ |
| សមស្របសម្រាប់ | កុំព្យូទ័រផ្ទាល់ខ្លួន ពេលកំពុងអភិវឌ្ឍ នៅក្រោយជញ្ជាំងភ្លើង | ម៉ាស៊ីនមេដែលបើកជានិច្ច serverless បូតច្រើន |
| ការប្រើទាំងពីរក្នុងពេលតែមួយ | មិនបាន — បើ webhook ត្រូវបានកំណត់ getUpdates នឹងទទួលបាន 409 Conflict | |
របៀបដែល webhook ដំណើរការ
ការពន្យល់ដ្យាក្រាម
- ខ្សែពីរ៖ ខាងឆ្វេងជាម៉ាស៊ីនមេ Pabal ហើយខាងស្តាំជាអាសយដ្ឋាន webhook ដែលអ្នកដំណើរការ។ បន្ទាត់ជាប់ពណ៌ខៀវ ជាសំណើដែលម៉ាស៊ីនមេផ្ញើ ពណ៌ប្រផេះ ជាការឆ្លើយតបធម្មតារបស់ webhook ហើយបន្ទាត់ដាច់ៗពណ៌ក្រហម ជាការបរាជ័យ។
- ការឆ្លើយតប 2xx មានន័យថា "បានទទួលហើយ" (②)។ មុនពេលនោះ update នៅតែស្ថិតនៅលើម៉ាស៊ីនមេ។ តួនៃការឆ្លើយតបអាចទុកទទេបាន។
- បើបរាជ័យ វាផ្ញើ update ដដែលម្តងទៀត (④ → ⑤)៖ ការឆ្លើយតបដែលមិនមែន 2xx ការតភ្ជាប់មិនបាន និងការមិនឆ្លើយក្នុងរយៈពេល 30 វិនាទី សុទ្ធតែជាការបរាជ័យ ហើយចន្លោះពេលចាប់ពី 1 វិនាទី កើនឡើងទ្វេដងម្តងៗ រហូតដល់អតិបរមា 60 វិនាទី។ មូលហេតុ និងពេលវេលានៃការបរាជ័យ ត្រូវបានកត់ទុកក្នុង
getWebhookInfo។ - លំដាប់ត្រូវបានរក្សា៖ ដោយសារ update របស់បូតមួយត្រូវបានផ្ញើម្តងមួយ 7 ត្រូវរង់ចាំរហូតដល់ 6 ជោគជ័យ។ ដូច្នេះ បើ webhook បរាជ័យយូរ update ដែលនៅពីក្រោយនឹងគរកើនឡើង (
pending_update_count)។ - អាចឆ្លើយតាមការឆ្លើយតបបាន (⑥)៖ បើដាក់
methodនិងប៉ារ៉ាម៉ែត្រក្នុងតួនៃការឆ្លើយតប 200 ម៉ាស៊ីនមេនឹងដំណើរការ method នោះជំនួសបូត។ វាលឿន ព្រោះមិនចាំបាច់ផ្ញើសំណើម្តងទៀត។
លក្ខខណ្ឌនៃអាសយដ្ឋាន webhook
- ត្រូវតែជាអាសយដ្ឋាន https://។ វិញ្ញាបនបត្រត្រូវតែទទួលពីស្ថាប័នផ្តល់វិញ្ញាបនបត្រសាធារណៈ (ដូចជា Let's Encrypt) ហើយការបង្ហោះវិញ្ញាបនបត្រដែលចុះហត្ថលេខាដោយខ្លួនឯង (
certificate) មិនត្រូវបានគាំទ្រទេ។ - ត្រូវតែជាអាសយដ្ឋានសាធារណៈដែលអាចទៅដល់ពីអ៊ីនធឺណិត។ ម៉ាស៊ីនមេមិនផ្ញើទៅអាសយដ្ឋានខាងក្នុង ដូចជា loopback (
127.0.0.1,::1) បណ្តាញឯកជន (10.,172.16–31.,192.168.) link-local (169.254., metadata របស់ cloud) និង CGNAT (100.64/10) ទេ។ ទោះបី domain ចង្អុលទៅអាសយដ្ឋានបែបនោះ ក៏ត្រូវបដិសេធដែរ ហើយវាពិនិត្យម្តងទៀតរាល់ពេលផ្ញើ។ - គ្មានការកំណត់លើច្រក (port) ទេ (មិនចាំបាច់ជា 443)។ មិនអាចដាក់ឈ្មោះអ្នកប្រើ និងពាក្យសម្ងាត់ (
https://user:pw@…) ក្នុងអាសយដ្ឋានបានទេ។ - វាមិនធ្វើតាមការបញ្ជូនបន្ត (redirect, 3xx) ទេ ហើយចាត់ទុកជាការបរាជ័យ។ សូមដាក់អាសយដ្ឋានចុងក្រោយ។
- ត្រូវតែឆ្លើយតបក្នុងរយៈពេល 30 វិនាទី។ សម្រាប់ការងារដែលចំណាយពេលយូរ សូមឆ្លើយ 200 ជាមុនសិន ហើយដំណើរការនៅពេលក្រោយ។
ការកំណត់
- ដំណើរការកម្មវិធីដែលនឹងទទួល webhook។ ឧបមាថាអ្នកដំណើរការឧទាហរណ៍មួយខាងក្រោម នៅលើម៉ាស៊ីនមេបូត ត្រង់
127.0.0.1:8081។ - ដាក់ HTTPS នៅខាងមុខ។ ឧទាហរណ៍ ជាមួយ Caddy មានតែពីរបន្ទាត់នេះ ក៏ទទួលបានវិញ្ញាបនបត្រដោយស្វ័យប្រវត្តិ។
bot.example.com { reverse_proxy 127.0.0.1:8081 } - បង្កើតតូខឹនសម្ងាត់មួយ។ 1 ដល់ 256 តួ ជាអក្សរឡាតាំង លេខ
_និង-។ ដោយសារម៉ាស៊ីនមេដាក់តម្លៃនេះក្នុង header នៃរាល់សំណើ អ្នកអាចពិនិត្យថាសំណើពិតជាមកពីម៉ាស៊ីនមេ Pabal មែន។export WEBHOOK_SECRET=$(openssl rand -hex 32) - ហៅ setWebhook។
curl -s "https://pabal.me/bot$BOT_TOKEN/setWebhook" -H 'Content-Type: application/json' -d "{ \"url\": \"https://bot.example.com/pabal-webhook\", \"secret_token\": \"$WEBHOOK_SECRET\", \"allowed_updates\": [\"message\", \"callback_query\"], \"drop_pending_updates\": true }" # {"ok":true,"result":true} - ពិនិត្យស្ថានភាព។ ផ្ញើសារទៅបូតពីកម្មវិធី ហើយមើល
getWebhookInfo។curl -s "https://pabal.me/bot$BOT_TOKEN/getWebhookInfo"{ "ok": true, "result": { "url": "https://bot.example.com/pabal-webhook", "has_custom_certificate": false, "pending_update_count": 0, "ip_address": "198.51.100.7", "max_connections": 40, "allowed_updates": ["message", "callback_query"] } }បើ
pending_update_countស្មើ 0 មានន័យថាកំពុងទទួលបានល្អ។last_error_dateនិងlast_error_messageជាកំណត់ត្រានៃការបរាជ័យចុងក្រោយ ដូច្នេះវានៅតែមាន ទោះបីបានស្តារឡើងវិញហើយក៏ដោយ — សូមមើលពេលវេលា ដើម្បីវិនិច្ឆ័យ។
ប៉ារ៉ាម៉ែត្រ setWebhook
| ប៉ារ៉ាម៉ែត្រ | ប្រភេទ | ចាំបាច់ | ការពិពណ៌នា |
|---|---|---|---|
url | String | ចាំបាច់ | អាសយដ្ឋាន webhook។ បើជាខ្សែអក្សរទទេ វានឹងលុប webhook (ដូច deleteWebhook)។ |
secret_token | String | ស្រេចចិត្ត | 1 ដល់ 256 តួ A-Z a-z 0-9 _ -។ ត្រូវបានផ្ញើក្នុង header X-Telegram-Bot-Api-Secret-Token នៃរាល់សំណើ។ |
allowed_updates | Array of String | ស្រេចចិត្ត | ប្រភេទដែលត្រូវទទួល៖ message, edited_message, callback_query។ បើទុកទទេ ទទួលទាំងអស់។ ប្រភេទដែលមិនមាននៅក្នុងបញ្ជី នឹងមិនត្រូវផ្ញើទេ ហើយត្រូវបោះចោល។ |
drop_pending_updates | Boolean | ស្រេចចិត្ត | បើ true វាបោះចោល update ទាំងអស់ដែលមិនទាន់បញ្ជូន ហើយចាប់ផ្តើមថ្មី។ |
max_connections | Integer | ស្រេចចិត្ត | 1 ដល់ 100 លំនាំដើម 40។ តម្លៃនេះត្រូវបានទទួល និងរក្សាទុក ប៉ុន្តែ Pabal ផ្ញើម្តងមួយសម្រាប់បូតនីមួយៗ ដើម្បីរក្សាលំដាប់។ |
certificate | InputFile | មិនគាំទ្រ | មិនទទួលវិញ្ញាបនបត្រដែលចុះហត្ថលេខាដោយខ្លួនឯងទេ (400)។ សូមប្រើវិញ្ញាបនបត្រសាធារណៈ។ |
ip_address | String | មិនអើពើ | ទទួល ប៉ុន្តែមិនប្រើ។ ម៉ាស៊ីនមេស្វែងរក domain រាល់ពេលផ្ញើ។ |
ការកំណត់ webhook ត្រូវបានរក្សាទុកនៅលើម៉ាស៊ីនមេ ដូច្នេះវានៅដដែល ទោះបីម៉ាស៊ីនមេចាប់ផ្តើមឡើងវិញ ហើយវាចាប់ផ្តើមផ្ញើម្តងទៀតភ្លាមៗពេលបើក។ ប៉ុន្តែ update ដែលមិនទាន់បញ្ជូន ស្ថិតនៅក្នុងអង្គចងចាំ ដូច្នេះវានឹងបាត់ពេលចាប់ផ្តើមឡើងវិញ។
សំណើដែលម៉ាស៊ីនមេផ្ញើ
POST /pabal-webhook HTTP/1.1
Host: bot.example.com
Content-Type: application/json
X-Telegram-Bot-Api-Secret-Token: 3f1c…(តម្លៃដែលបានផ្តល់ទៅ setWebhook)
{"update_id":12,"message":{"message_id":3,"from":{"id":100001,"is_bot":false,"first_name":"សុខា"},"chat":{"id":100001,"first_name":"សុខា","type":"private"},"date":1789805661,"text":"សួស្តី"}}
- តួគឺជា object Update ដែលដូចគ្នាទាំងស្រុងនឹងធាតុមួយនៃលទ្ធផល
getUpdates។ - សូមពិនិត្យតូខឹនសម្ងាត់ជានិច្ច។ បើគ្មាន header ឬតម្លៃខុស សូមបដិសេធដោយ 401។ ធ្វើការប្រៀបធៀបដោយប្រើអនុគមន៍ដែលចំណាយពេលថេរ (
hmac.compare_digest,crypto.timingSafeEqual)។ - update ដដែលអាចមកដល់ពីរដង (ករណីការតភ្ជាប់ដាច់ មុនពេលការឆ្លើយតបទៅដល់ម៉ាស៊ីនមេ)។ ដើម្បីសុវត្ថិភាព សូមរំលង update ដែលបានដំណើរការរួចហើយ ដោយប្រើ
update_id។
ការឆ្លើយតាមការឆ្លើយតប
បើអ្នកដាក់ការហៅ Bot API មួយក្នុងតួនៃការឆ្លើយតប 200 ម៉ាស៊ីនមេនឹងដំណើរការការហៅនោះជំនួសបូត។ ដាក់ឈ្មោះ method ក្នុង method ហើយដាក់ប៉ារ៉ាម៉ែត្រផ្សេងទៀតជាមួយគ្នា។
{"method": "sendMessage", "chat_id": 100001, "text": "ជម្រាបសួរ!"}
- ទម្រង់តួអាចជា JSON, form ឬ
multipart/form-dataក៏បាន (aiogram ផ្ញើជា multipart)។ ទំហំរហូតដល់ 1MB។ - លទ្ធផល ឬកំហុសនៃការហៅនេះ មិនត្រឡប់ទៅបូតវិញទេ (កត់ទុកតែក្នុង log របស់ម៉ាស៊ីនមេ)។ បើត្រូវការលទ្ធផល សូមផ្ញើសំណើដាច់ដោយឡែកដូចធម្មតា។
- បើគ្មានអ្វីត្រូវឆ្លើយ គ្រាន់តែផ្តល់ 200 ដោយគ្មានតួ។
ឧទាហរណ៍
ឧទាហរណ៍ទាំងបួន សុទ្ធតែពិនិត្យតូខឹនសម្ងាត់ ហើយពេលទទួលបានសារជាអក្សរ វាដាក់ sendMessage ក្នុងការឆ្លើយតប ដើម្បីនិយាយតាម។ យើងសន្មតថាមាន HTTPS (ឧ. Caddy) នៅខាងមុខ ហើយទទួលនៅ 127.0.0.1:8081។
# webhook.py — ប្រើតែបណ្ណាល័យស្តង់ដារ
# ដំណើរការ៖ WEBHOOK_SECRET='…' python3 webhook.py
import hmac
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
SECRET = os.environ["WEBHOOK_SECRET"]
class Webhook(BaseHTTPRequestHandler):
def do_POST(self):
got = self.headers.get("X-Telegram-Bot-Api-Secret-Token", "")
if not hmac.compare_digest(got, SECRET):
self.send_response(401)
self.end_headers()
return
update = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
message = update.get("message")
answer = b""
if message and "text" in message:
answer = json.dumps({"method": "sendMessage",
"chat_id": message["chat"]["id"],
"text": message["text"]}).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(answer)))
self.end_headers()
self.wfile.write(answer)
HTTPServer(("127.0.0.1", 8081), Webhook).serve_forever()// webhook.mjs — Node.js 18 ឡើងទៅ ដោយមិនប្រើបណ្ណាល័យ
// ដំណើរការ៖ WEBHOOK_SECRET='…' node webhook.mjs
import http from 'node:http';
import { timingSafeEqual } from 'node:crypto';
const SECRET = Buffer.from(process.env.WEBHOOK_SECRET);
http.createServer(async (req, res) => {
const got = Buffer.from(req.headers['x-telegram-bot-api-secret-token'] ?? '');
if (req.method !== 'POST' || got.length !== SECRET.length || !timingSafeEqual(got, SECRET)) {
res.writeHead(401).end();
return;
}
let body = '';
for await (const chunk of req) body += chunk;
const message = JSON.parse(body).message;
if (message?.text) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ method: 'sendMessage', chat_id: message.chat.id, text: message.text }));
} else {
res.writeHead(200).end();
}
}).listen(8081, '127.0.0.1');# webhook_aiogram.py — pip install aiogram
# កម្មវិធីនេះធ្វើការកំណត់ webhook (setWebhook) ដោយខ្លួនឯងផងដែរ
import os
from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from aiohttp import web
URL = "https://bot.example.com/pabal-webhook"
SECRET = os.environ["WEBHOOK_SECRET"]
dp = Dispatcher()
@dp.message()
async def echo(message):
if message.text:
return message.answer(message.text) # return៖ ផ្ញើដោយដាក់ក្នុងការឆ្លើយតប webhook
async def on_startup(bot: Bot):
await bot.set_webhook(URL, secret_token=SECRET)
def main():
session = AiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me"))
bot = Bot(os.environ["BOT_TOKEN"], session=session)
dp.startup.register(on_startup)
app = web.Application()
SimpleRequestHandler(dispatcher=dp, bot=bot, secret_token=SECRET).register(app, path="/pabal-webhook")
setup_application(app, dp, bot=bot)
web.run_app(app, host="127.0.0.1", port=8081)
main()# webhook_ptb.py — pip install "python-telegram-bot[webhooks]"
# កម្មវិធីនេះធ្វើការកំណត់ webhook (setWebhook) ដោយខ្លួនឯងផងដែរ
import os
from telegram import Update
from telegram.ext import Application, ContextTypes, MessageHandler, filters
SERVER = "https://pabal.me"
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(update.message.text)
app = (Application.builder().token(os.environ["BOT_TOKEN"])
.base_url(f"{SERVER}/bot").base_file_url(f"{SERVER}/file/bot").build())
app.add_handler(MessageHandler(filters.TEXT, echo))
app.run_webhook(listen="127.0.0.1", port=8081, url_path="pabal-webhook",
webhook_url="https://bot.example.com/pabal-webhook",
secret_token=os.environ["WEBHOOK_SECRET"])មើលស្ថានភាព — getWebhookInfo
| វាល | អត្ថន័យ |
|---|---|
url | អាសយដ្ឋានដែលបានកំណត់។ បើគ្មាន webhook ជាខ្សែអក្សរទទេ |
pending_update_count | ចំនួន update ដែលមិនទាន់បញ្ជូន ហើយកំពុងរង់ចាំ |
ip_address | IP នៃអាសយដ្ឋានដែលបានផ្ញើចុងក្រោយ |
last_error_date, last_error_message | ពេលវេលា (វិនាទី Unix) និងមូលហេតុនៃការបរាជ័យចុងក្រោយ។ បើមិនធ្លាប់បរាជ័យ វាមិនមានទេ។ វានៅតែមាន ទោះបីបានស្តារឡើងវិញហើយ |
max_connections, allowed_updates | តម្លៃដែលបានផ្តល់ទៅ setWebhook |
has_custom_certificate | false ជានិច្ច |
សារដែលបង្ហាញក្នុង last_error_message
| សារ | មូលហេតុ |
|---|---|
Connection refused | គ្មានអ្វីកំពុងស្តាប់នៅអាសយដ្ឋាន និងច្រកនោះទេ។ កម្មវិធី webhook ឬ proxy ត្រូវបានបិទ |
Connection timed out | ជញ្ជាំងភ្លើងរារាំង ឬមិនអាចទៅដល់អាសយដ្ឋាន |
Read timeout expired | មិនឆ្លើយតបក្នុងរយៈពេល 30 វិនាទី |
Failed to resolve host: Name or service not known | រកឈ្មោះ domain មិនឃើញ (DNS) |
SSL error {…} | បញ្ហាវិញ្ញាបនបត្រ — ផុតកំណត់ ចុះហត្ថលេខាដោយខ្លួនឯង ឬឈ្មោះមិនត្រូវគ្នា |
Wrong response from the webhook: 502 Bad Gateway | ការឆ្លើយតបដែលមិនមែន 2xx។ លេខនោះជាលេខកូដស្ថានភាពដែល webhook បានផ្តល់ |
IP address 10.0.0.5 is reserved | domain ចង្អុលទៅអាសយដ្ឋានខាងក្នុង (មិនផ្ញើទេ) |
ត្រឡប់ទៅ getUpdates វិញ
curl -s "https://pabal.me/bot$BOT_TOKEN/deleteWebhook"
# ដើម្បីបោះចោល update ដែលកំពុងរង់ចាំផងដែរ៖ deleteWebhook?drop_pending_updates=true
update ដែល webhook បានទទួលរួចហើយ (បានឆ្លើយ 2xx) នឹងមិនមកតាម getUpdates ម្តងទៀតទេ។ មានតែ update ដែលមិនទាន់បញ្ជូនប៉ុណ្ណោះ ដែលអ្នកទទួលបន្តតាម getUpdates។
ការសាកល្បងពេលកំពុងអភិវឌ្ឍ
- Tunnel៖ បើប្រើឧបករណ៍ដែលភ្ជាប់អាសយដ្ឋាន HTTPS សាធារណៈទៅ
127.0.0.1:8081នៃកុំព្យូទ័ររបស់អ្នក (cloudflared, ngrok ។ល។) អ្នកអាចសាកល្បងជាមួយម៉ាស៊ីនមេ Pabal ផលិតកម្មបានផងដែរ។ - ម៉ាស៊ីនមេ Pabal ផ្ទាល់ខ្លួន៖ បើអ្នកបានដំណើរការម៉ាស៊ីនមេ Pabal សម្រាប់ការអភិវឌ្ឍដោយខ្លួនឯង សូមបើកវាជាមួយ
TELEGRAM_WEBHOOK_ALLOW_LOCAL=true។ ម៉ាស៊ីនមេនោះនឹងផ្ញើទៅអាសយដ្ឋានក្នុងម៉ាស៊ីន ដូចជាhttp://127.0.0.1:8081/…ផងដែរ។ កុំបើកវានៅលើម៉ាស៊ីនមេផលិតកម្មជាដាច់ខាត — វាអនុញ្ញាតឲ្យបូតមួយផ្ញើសំណើទៅបណ្តាញខាងក្នុងរបស់ម៉ាស៊ីនមេ (មូលដ្ឋានទិន្នន័យ metadata របស់ cloud)។
បញ្ជីត្រួតពិនិត្យសម្រាប់ដំណើរការពិត
- បានកំណត់តូខឹនសម្ងាត់ ហើយពិនិត្យវានៅគ្រប់សំណើ។
- ឆ្លើយក្នុងរយៈពេល 30 វិនាទី — ការងារដែលចំណាយពេលយូរ បញ្ជូនទៅជួរ (queue) ហើយឆ្លើយ 200 ភ្លាម។
- ច្រោះ update ស្ទួនចេញ ដោយប្រើ
update_id។ - បើ webhook ឈប់យូរ update ដែលនៅពីក្រោយនឹងគរកើនឡើង ហើយបាត់ពេលម៉ាស៊ីនមេចាប់ផ្តើមឡើងវិញ — ត្រួតពិនិត្យកម្មវិធី webhook (
pending_update_countនិងlast_error_dateនៃgetWebhookInfo)។ - បើតូខឹនលេចធ្លាយ ធ្វើ
/revokeហើយហៅsetWebhookម្តងទៀតជាមួយតូខឹនថ្មី។ ប្តូរតូខឹនសម្ងាត់ជាមួយគ្នាផងដែរ។