ساخت ربات تلگرام با Laravel؛ آموزش Webhook تا اجرای واقعی
آموزش ساخت ربات تلگرام با Laravel از تنظیم Bot Token و Webhook تا ثبت Update، احراز هویت secret_token، Queue، رفع خطاهای ۴۱۹ و ۴۲۹ و معرفی SDK Avaztek Telegram.
ربات تلگرام قرار است پس از دریافت /start خوشامد بگوید. در لپتاپ با Long Polling همهچیز کار میکند، ولی بعد از Deploy روی سرور، پیامها گاهی دوبار پردازش میشوند یا پاسخ چند ثانیه بعد میرسد. برای ساخت ربات پایدار با Laravel باید فقط به ارسال sendMessage فکر نکنیم؛ نحوه دریافت Update، اعتبارسنجی Webhook، صف، جلوگیری از پردازش تکراری و بازیابی خطا نیز بخشی از برنامه هستند.
این آموزش، مسیر ساخت ربات تلگرام با Laravel از Webhook تا اجرا را با نمونهکد و چکلیست عملی توضیح میدهد. فرض میکنیم پروژه Laravel و HTTPS معتبر دارید و ربات را از BotFather ساختهاید. توکن واقعی را در متن، Git یا لاگ قرار ندهید. درباره موانع اتصال از دیتاسنتر ایران قبلاً در راهنمای اتصال ربات تلگرام از سرور ایران صحبت کردهایم؛ این مطلب بر پیادهسازی Laravel تمرکز دارد.
نقشه راه: از ساخت ربات تا دریافت نخستین Update
- ساخت Bot با
@BotFatherو دریافت توکن محرمانه؛ - آمادهکردن URL عمومی HTTPS با گواهی معتبر؛
- ساخت Route دریافت Webhook در Laravel؛
- ثبت یک Secret جداگانه برای تأیید سربرگ Webhook؛
- ذخیره شناسه یکتای Update پیش از پردازش؛
- ارسال کار به Queue و پاسخ سریع به Telegram؛
- اجرای Worker، ثبت وضعیت و آزمون پیام و خطا.
در این مقاله مسیر مستقیم Telegram Bot API را آموزش میدهیم. اگر چند ربات و چند پروژه دارید، بخش پایانی الگوی اتصال به هسته مرکزی Avaztek Telegram را نیز بررسی میکند. این دو معماری جای هم نیستند؛ هر کدام هزینه و مسئولیت متفاوتی دارند.
Webhook یا getUpdates؛ برای Laravel کدام مناسبتر است؟
در روش Long Polling، برنامه با getUpdates مرتب رویدادها را از Telegram میگیرد. برای آزمایش محلی ساده است، اما به فرایندی پایدار و مدیریت Offset نیاز دارد. در روش Webhook، Telegram پس از رسیدن رویداد، درخواست HTTPS را به آدرس شما میفرستد. برای سرویس وب با دامنه عمومی معمولاً انتخاب مناسبی است، مشروط بر اینکه مسیر HTTPS از سمت Telegram در دسترس باشد.
| معیار | Long Polling | Webhook |
|---|---|---|
| نقطه دریافت | برنامه از تلگرام درخواست میفرستد | تلگرام به URL شما درخواست میدهد |
| مناسبتر برای | تست و اجرای Worker کنترلشده | برنامه وب و پردازش رویدادمحور |
| نیاز HTTPS عمومی | خیر | بله، طبق شرایط Bot API |
| وضعیت پردازش | نیاز به مدیریت Offset | نیاز به پاسخ سریع و Idempotency |
| مشکل رایج | اجرای چند Poller و تکرار دریافت | Timeout مقصد و تحویل دوباره رویداد |
برای یک ربات نمیتوانید از روش Webhook و getUpdates بهصورت همزمان برای دریافت معمول Update استفاده کنید. هنگام تغییر روش، وضعیت قبلی را کنترل کنید. منبع اصلی جزئیات و پارامترها مستندات رسمی setWebhook در Telegram Bot API است.
گام اول: تنظیمات محرمانه Laravel را آماده کنید
توکن BotFather هویت ربات شماست. آن را در کنترلر، فایل JavaScript، خطای عمومی یا repository قرار ندهید. در فایل محیطی سمت سرور، دو Secret مستقل قرار دهید: Bot Token برای ارتباط خروجی با Telegram و Webhook Secret برای تأیید درخواست ورودی. مقدار مثالها ساختگی است.
TELEGRAM_BOT_TOKEN=replace-with-private-token
TELEGRAM_WEBHOOK_SECRET=replace-with-random-safe-secret
در config/services.php تنظیمها را اضافه کنید؛ بهجای فراخوانی مستقیم env در Runtime از Config استفاده کنید تا config:cache درست کار کند:
'telegram_bot' => [
'token' => env('TELEGRAM_BOT_TOKEN'),
'webhook_secret' => env('TELEGRAM_WEBHOOK_SECRET'),
],
پس از تغییر مقدارها، کش پیکربندی را طبق روند Deploy پروژه بازسازی کنید. اگر توکن لو رفت، از BotFather توکن تازه بگیرید و تمام مسیرهای استفادهکننده را بهروزرسانی کنید. برای Webhook Secret از رشته امن با کاراکترهای مجاز Telegram استفاده کنید؛ این رشته با Bot Token یکسان نباشد.
گام دوم: Route امن برای Webhook بسازید
در نسخههایی از Laravel که فایل routes/api.php فعال است، این مسیر معمولاً از نشست وب و حفاظت CSRF مربوط به فرم مرورگر جداست. در پروژهای که API Route فعال نشده، ابتدا محل ثبت Route و Middlewareها را بررسی کنید. قرار دادن مسیر Webhook عمومی داخل گروه Web بدون تنظیم دقیق CSRF ممکن است خطای ۴۱۹ ایجاد کند.
// routes/api.php (when API routes are enabled)
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TelegramWebhookController;
Route::post('/telegram/webhook', TelegramWebhookController::class)
->middleware('throttle:120,1');
آدرس نمونه با Prefix پیشفرض API برابر https://example.com/api/telegram/webhook است؛ اگر Prefix پروژه تغییر کرده، URL نهایی را از php artisan route:list بگیرید. برای این مسیر به احراز هویت مرورگر نیاز ندارید، اما اعتبارسنجی Secret سربرگ Webhook را نباید حذف کنید.
گام سوم: Secret و شناسه Update را بررسی کنید
Telegram هنگام تنظیم secret_token میتواند مقدار آن را در سربرگ X-Telegram-Bot-Api-Secret-Token به مقصد بفرستد. این مقدار را با hash_equals با Secret تنظیمشده مقایسه کنید. این سازوکار امضای رمزنگاریشده بدنه نیست؛ به همین دلیل HTTPS، محرمانهماندن Secret و کنترل دسترسی شبکه همچنان مهماند.
برای جلوگیری از پردازش دوباره، شناسه update_id را در دیتابیس با ایندکس یکتا ذخیره کنید. اگر درخواست بعد از ثبت رویداد دوباره تحویل شود، فقط یک رکورد برای آن Update وجود خواهد داشت. با این حال، ذخیرهسازی بهتنهایی تضمین نمیکند کار پردازش هم دقیقاً یکبار اجرا شود؛ اثرهای جانبی مانند ارسال پیام یا ثبت پرداخت باید Idempotent باشند.
// Migration concept for durable ingress tracking:
Schema::create('telegram_updates', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('update_id')->unique();
$table->json('payload');
$table->string('status', 20)->default('pending');
$table->timestamps();
});
این ساختار برای یک Bot نمونه است. اگر چند ربات را در یک جدول نگه میدارید، باید کلید یکتا را با شناسه ربات ترکیب کنید تا Updateهای مستقل اشتباه یکی فرض نشوند. در سناریوی حریم خصوصی، نگهداری Payload خام را با سیاست داده و دوره حذف سازگار کنید.
گام چهارم: Webhook Controller را کوتاه و قابل بازیابی بنویسید
مسیر ورودی باید پس از اعتبارسنجی و ثبت پایدار، سریع پاسخ موفق بدهد. اجرای عملیات سنگین یا تماس طولانی با سرویس خارجی در همان درخواست، احتمال Timeout و تحویل مجدد رویداد را بیشتر میکند. نمونه زیر ساختار پایه کنترلر را نشان میدهد. کلاس Job و جدول ثبت رویداد در مراحل جدا پیادهسازی میشوند.
namespace App\Http\Controllers;
use App\Jobs\ProcessTelegramUpdate;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class TelegramWebhookController
{
public function __invoke(Request $request): JsonResponse
{
$expected = (string) config('services.telegram_bot.webhook_secret');
$received = (string) $request->header('X-Telegram-Bot-Api-Secret-Token', '');
if ($expected === '' || ! hash_equals($expected, $received)) {
return response()->json(['ok' => false], 403);
}
$data = $request->validate([
'update_id' => ['required', 'integer', 'min:0'],
]);
$updateId = (int) $data['update_id'];
$saved = DB::table('telegram_updates')->insertOrIgnore([
'update_id' => $updateId,
'payload' => json_encode($request->all(), JSON_THROW_ON_ERROR),
'status' => 'pending',
'created_at' => now(),
'updated_at' => now(),
]);
if ($saved === 1) {
ProcessTelegramUpdate::dispatch($updateId)->onQueue('telegram');
}
return response()->json(['ok' => true]);
}
}
محدودیت مهم نمونه: اگر ذخیره رویداد موفق شود ولی ارسال Job به صف شکست بخورد، رکورد در وضعیت pending باقی میماند. در محیط واقعی باید یک پردازشگر دورهای برای بازیابی همین رکوردهای Pending و تشخیص Jobهای گیرکرده داشته باشید. پاسخ موفق به Telegram تنها به معنی دریافت پایدار است، نه پایان پردازش منطق ربات.
گام پنجم: پردازش پیام و ارسال پاسخ را به Queue بسپارید
Job یک Update ذخیرهشده را میخواند، نوع پیام را تشخیص میدهد و تصمیم تجاری برنامه را اجرا میکند. نمونه زیر برای پیام /start طراحی شده است. قبل از ارسال پیام واقعی باید شرایط تکرار، وضعیت رکورد و مجوز Chat را مدیریت کنید؛ مثال تنها قطعه آموزشی از متد ارسال است.
use Illuminate\Support\Facades\Http;
$token = (string) config('services.telegram_bot.token');
if ($token === '') {
throw new \RuntimeException('Telegram bot token is not configured');
}
$response = Http::asForm()
->connectTimeout(5)
->timeout(10)
->post("https://api.telegram.org/bot{$token}/sendMessage", [
'chat_id' => $chatId,
'text' => 'سلام! پیام شما دریافت شد.',
]);
$response->throw();
if ($response->json('ok') !== true) {
throw new \RuntimeException('Telegram rejected the message');
}
برخلاف نمونههای سطحی، پاسخ HTTP موفق را با وضعیت منطقی Bot API اشتباه نگیرید. از محتوای خطا و مقدار retry_after در صورت وجود برای زمانبندی تلاش بعدی استفاده کنید. مهمتر: در ثبت Exception، URL حاوی Bot Token را به لاگ عمومی نفرستید؛ مسیر Telegram Bot API بهصورت پیشفرض توکن را داخل URL دارد. برای جلوگیری از افشای آن، فیلتر Redaction و سطح دسترسی گزارش خطا را جداگانه تنظیم کنید.
چگونه از پیام خوشامد تکراری جلوگیری کنیم؟
اگر Worker پس از ارسال موفق پیام ولی پیش از ثبت وضعیت completed متوقف شود، ممکن است Job در Retry دوباره پیام بفرستد. ایندکس یکتای Update فقط ورود تکراری را کنترل میکند؛ اجرای اثر خارجی دقیقاً یکبار تضمین نشده است. برای کارهای حساس باید وضعیت عملیات و شناسه یکتای کسبوکار را جدا ذخیره کنید و سیاست جبران یا تأیید نتیجه داشته باشید. برای پرداخت یا تغییر اطلاعات کاربر، تکیه بر یک شرط ساده if کافی نیست.
گام ششم: setWebhook و بررسی وضعیت را انجام دهید
در مستندات رسمی Telegram، متد setWebhook آدرس HTTPS مقصد و secret_token را دریافت میکند. دستور آموزشی زیر باید فقط در محیط امن اجرا شود. برای جلوگیری از ثبت Token در تاریخچه Shell، به جای قراردادن مقدار واقعی داخل خط فرمان، آن را در محیط محدود عملیاتی مدیریت کنید؛ در مثال از متغیرهای موجود استفاده شده است.
# Run on a trusted operator machine with environment variables set.
curl -sS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook" \
--data-urlencode "url=https://example.com/api/telegram/webhook" \
--data-urlencode "secret_token=${TELEGRAM_WEBHOOK_SECRET}"
curl -sS "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getWebhookInfo"
توجه: خود دستور بالا Bot Token را در آرگومانهای پردازش قرار میدهد؛ روی ماشین چندکاربره ممکن است این موضوع قابل مشاهده باشد. برای Production، بهتر است عملیات ثبت Webhook با HTTP Client سمت سرور و Secret Manager انجام شود. همچنین خروجی خام فرمان ممکن است جزئیات عملیاتی داشته باشد؛ آن را بدون پاکسازی در تیکت عمومی قرار ندهید. در getWebhookInfo به URL ثبتشده، تعداد Updateهای معوق و خطای تحویل اخیر توجه کنید.
گام هفتم: Worker و مانیتورینگ اجرای ربات را راهاندازی کنید
وجود رکورد Pending بدون Worker فعال باعث میشود Webhook ظاهراً سالم باشد اما ربات پاسخی ندهد. Worker صف باید با Supervisor یا سازوکار مدیریت فرایند مناسب اجرا و Restart شود. نمونه فرمان زیر برای آزمایش کنترلشده است؛ نام Queue را با تنظیمات واقعی پروژه تطبیق دهید:
php artisan queue:work --queue=telegram --tries=3 --timeout=30
مقدار Timeout باید از زمان انتظار Driver صف و محدودیت Job کمتر باشد تا اجرای همزمان ناخواسته ایجاد نشود. نرخ شکست، عمر قدیمیترین رویداد Pending، تعداد Jobهای Failed و فاصله زمان دریافت تا پاسخ را مانیتور کنید. اگر هر رویداد پیام جدیدی ایجاد میکند، Rate Limit Telegram و محدودیت ارسال گروهی را در طراحی در نظر بگیرید.
جدول عیبیابی Webhook لاراول
| علامت | علت محتمل | بررسی کمخطر |
|---|---|---|
| خطای ۴۱۹ | Webhook داخل مسیر تحت حفاظت CSRF مرورگر ثبت شده | مسیر و Middleware را با route:list بررسی کنید |
| خطای ۴۰۳ | Secret ورودی با مقدار ثبتشده هماهنگ نیست | مقدار Secret پیکربندی را بدون نمایش آن مقایسه کنید |
| HTTP 200 بدون پاسخ ربات | Worker متوقف یا Pending در صف مانده | وضعیت صف و رکوردهای ثبتشده را ببینید |
| پیام تکراری | Retry و اثر جانبی بدون Idempotency | کلید Update و وضعیت ارسال خارجی را بررسی کنید |
| Timeout در getWebhookInfo | مسیر شبکه یا دسترسی Telegram Bot API | DNS، TCP و TLS مقصد مجاز را از سرور بسنجید |
| خطای ۴۲۹ هنگام ارسال | محدودیت نرخ Bot API | پارامتر retry_after و نرخ ارسال را بررسی کنید |
اگر سرور در ایران قرار دارد، باید ارتباط ورودی Telegram به Webhook و ارتباط خروجی Laravel به Bot API را جدا بررسی کنید. ممکن است یکی سالم و دیگری دچار محدودیت باشد. دریافت موفق Header ثابت نمیکند پیام خروجی از سرور به Telegram خواهد رسید.
چه زمانی به جای Webhook مستقیم از Telegram Core استفاده کنیم؟
برای یک ربات ساده، دریافت مستقیم Webhook در Laravel معمولاً کافی است. اما وقتی چند ربات، چند شرکت یا چند برنامه باید Updateها را دریافت کنند، نگهداری Tokenها، مسیر تحویل، Retry و سابقه خطا میان پروژهها پیچیده میشود. در چنین محیطی میتوان دریافت Telegram را در یک هسته مرکزی انجام داد و منطق ربات را داخل برنامههای مصرفکننده نگه داشت.
سامانه Avaztek Telegram طبق معرفی رسمی خود، برای همین معماری طراحی شده است: دریافت و اعتبارسنجی Webhook، ثبت رویداد، جداکردن Bot و شرکت، صف تحویل، Retry و ارسال رویداد به پروژه مقصد با امضای قابل اعتبارسنجی. برخلاف Secret Header خود Telegram، امضای تحویل به پروژه مصرفکننده مربوط به لایه مرکزی Avaztek Telegram است و باید طبق قرارداد SDK بررسی شود.
در معماری مرکزی کدام بخش در Laravel میماند؟
پاسخدادن به /start، ثبت سفارش، مدیریت کاربر و هر تصمیم تجاری داخل برنامه Laravel باقی میماند؛ دریافت Update خام و مدیریت مسیر تحویل میتواند به Telegram Core سپرده شود. این جداسازی مانع جابهجایی منطق کسبوکار به هسته ارتباطی میشود. درباره مراحل اتصال و قرارداد امضا، مستندات SDK Laravel سامانه Avaztek Telegram را بخوانید؛ این مقاله جایگزین جزئیات امضا یا نسخهبندی SDK نیست.
برای بررسی اتصال ربات و همکاری فنی، به فرم درخواست اتصال Telegram Core مراجعه کنید و تعداد رباتها، نوع رویداد، نسخه Laravel، محل استقرار و مشکلات ارتباطی را توضیح دهید. Bot Token را در فرم نفرستید. سامانه برای درخواست بررسی فنی و ارتباط طراحی شده است و قابلیتهایی مانند تحویل امضاشده باید بر اساس راهنمای همان سرویس پیکربندی شوند.
چکلیست قبل از اجرای ربات در Production
- توکن ربات و Webhook Secret در سمت سرور محرمانهاند و قابل چرخش هستند.
- HTTPS معتبر است و URL ثبتشده با Route واقعی Laravel یکسان است.
- Webhook فقط پس از اعتبارسنجی و ثبت پایدار، پاسخ موفق میدهد.
update_idبرای هر Bot یکتاست و دوبارهپردازش به اثر خارجی ناخواسته تبدیل نمیشود.- Queue Worker تحت نظارت اجرا میشود و Pendingهای رهاشده روش بازیابی دارند.
- لاگ Exception هیچ Bot Token یا داده حساس کاربر را فاش نمیکند.
- خطای ۴۲۹، Retry محدود و پیامهای Failed قابل مشاهدهاند.
- برای رویداد تست، دریافت، ذخیره، اجرای Job و پاسخ نهایی جداگانه تأیید شدهاند.
پرسشهای متداول درباره ساخت ربات تلگرام با Laravel
آیا برای ربات تلگرام با Laravel حتماً Webhook لازم است؟
خیر. میتوان با getUpdates و Long Polling هم رویداد دریافت کرد. Webhook برای برنامهای با URL عمومی و دسترسپذیری ورودی مناسب است؛ هر دو روش به مدیریت تکرار و خطا نیاز دارند.
چرا تلگرام به Webhook لاراول خطای ۴۱۹ میدهد؟
احتمال دارد مسیر دریافت داخل گروه Web قرار گرفته باشد و Middleware مربوط به CSRF درخواست بدون نشست مرورگر را رد کند. در نسخهای که API Route فعال است، مسیر اختصاصی API با کنترل Secret رویکرد مناسبتری است.
secret_token تلگرام با امضای HMAC یکی است؟
خیر. Telegram در صورت تنظیم secret_token آن را در سربرگ مشخص قرار میدهد تا مقصد مقدار را مقایسه کند. امضای HMAC بدنه پیام، سازوکاری جداست که ممکن است در تحویل رویداد میان دو سامانه داخلی استفاده شود.
چگونه از دوبار پاسخدادن ربات جلوگیری کنیم؟
رویداد ورودی را با کلید یکتا در پایگاهداده ثبت کنید و برای عملیات خروجی، وضعیت و شناسه مستقل داشته باشید. ارسال دوباره Job یا قطع ارتباط پس از عملیات خارجی ممکن است هنوز پیام تکراری بسازد؛ بنابراین Idempotency فقط با یک شرط روی Controller کامل نمیشود.
چرا Webhook دریافت میشود ولی ربات پاسخ نمیدهد؟
ممکن است Queue Worker فعال نباشد، Job شکست خورده باشد یا ارتباط خروجی به Telegram Bot API مشکل داشته باشد. جدول Updateها، وضعیت Worker و پاسخ متد ارسال را جدا بررسی کنید.
آیا Avaztek Telegram کد منطق ربات را هم اجرا میکند؟
در معماری معرفیشده، Telegram Core مسئول دریافت، اعتبارسنجی، ثبت و تحویل رویداد است؛ منطق تجاری ربات داخل برنامه مصرفکننده مانند Laravel باقی میماند. برای جزئیات اتصال، راهنمای رسمی SDK را ببینید.
جمعبندی: Webhook مطمئن یعنی مسیر کامل پیام قابل پیگیری باشد
برای ساخت ربات تلگرام با Laravel، ثبت یک URL تنها شروع کار است. امنیت Secret، دریافت پایدار Update، Queue، Idempotency و رصد خطاها تعیین میکنند برنامه در شرایط واقعی چگونه رفتار کند. برای یک ربات ساده میتوان همین مسیر مستقیم را پیاده کرد؛ برای چند پروژه با نیاز تحویل متمرکز، Avaztek Telegram گزینهای برای بررسی فنی است. مشخصات پروژه را در فرم تماس سرویس تلگرام بفرستید تا روش اتصال مناسب بررسی شود.
نظرات و گفتوگو
نظرها پس از تایید مدیر منتشر میشوند. پاسخهای رسمی مدیر با نشان مشخص نمایش داده میشوند.