بناء بوت تيليجرام باستخدام grammY و TypeScript: من الصفر إلى النشر

بوتات تيليجرام في كل مكان. من خدمة العملاء إلى أتمتة المهام، تُشغّل واجهة Bot API الخاصة بتيليجرام ملايين البوتات. في هذا الدليل، ستبني بوت تيليجرام جاهزاً للإنتاج باستخدام grammY — إطار العمل الحديث والآمن من حيث الأنواع لبناء بوتات تيليجرام بـ TypeScript.
ما ستتعلمه
بنهاية هذا الدليل، ستكون قادراً على:
- إنشاء بوت تيليجرام والحصول على رمز API من BotFather
- إعداد مشروع TypeScript مع grammY
- التعامل مع الأوامر والرسائل واستعلامات الاستدعاء
- بناء لوحات مفاتيح تفاعلية وقوائم
- استخدام الوسيط (Middleware) والمؤلفات (Composers) لتنظيم الكود
- تطبيق إدارة الحالة عبر الجلسات
- إضافة تدفقات المحادثة باستخدام
@grammyjs/conversations - نشر البوت باستخدام الاستطلاع الطويل وخطافات الويب
المتطلبات الأساسية
قبل البدء، تأكد من توفر:
- Node.js 20+ مثبت (
node --version) - معرفة بـ TypeScript (الأنواع، async/await، الوحدات)
- حساب تيليجرام (لإنشاء البوت واختباره)
- فهم أساسي لـ HTTP وواجهات البرمجة (APIs)
- محرر أكواد (يُنصح بـ VS Code)
الخطوة 1: إنشاء البوت عبر BotFather
كل بوت تيليجرام يبدأ مع BotFather — البوت الرسمي لتيليجرام لإدارة البوتات.
- افتح تيليجرام وابحث عن
@BotFather - أرسل
/newbot - اختر اسم العرض (مثال: "بوت grammY التجريبي")
- اختر اسم مستخدم ينتهي بـ
bot(مثال:my_grammy_demo_bot) - انسخ رمز API الذي ستتلقاه — يبدو هكذا:
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
حافظ على سرية الرمز! أي شخص يملك رمز البوت يمكنه التحكم فيه. لا تقم أبداً بإضافته لنظام التحكم بالإصدارات.
الخطوة 2: إعداد المشروع
أنشئ مشروعاً جديداً وثبّت التبعيات:
mkdir telegram-grammy-bot && cd telegram-grammy-bot
npm init -y
npm install grammy dotenv
npm install -D typescript @types/node tsx
npx tsc --initحدّث ملف tsconfig.json لـ TypeScript الحديث:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true
},
"include": ["src"]
}أضف "type": "module" إلى package.json وحدّث السكربتات:
{
"type": "module",
"scripts": {
"dev": "tsx watch src/bot.ts",
"build": "tsc",
"start": "node dist/bot.js"
}
}أنشئ ملف .env لرمز البوت:
BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11الخطوة 3: بوتك الأول
أنشئ ملف src/bot.ts:
import { Bot } from "grammy";
import "dotenv/config";
const token = process.env.BOT_TOKEN;
if (!token) throw new Error("BOT_TOKEN is not set");
const bot = new Bot(token);
// التعامل مع أمر /start
bot.command("start", (ctx) => {
ctx.reply(
`مرحباً ${ctx.from?.first_name}! 👋\nأنا بوت مبني بـ grammY و TypeScript.`
);
});
// التعامل مع أمر /help
bot.command("help", (ctx) => {
ctx.reply(
"الأوامر المتاحة:\n" +
"/start - تشغيل البوت\n" +
"/help - عرض رسالة المساعدة\n" +
"/about - معرفة المزيد عن البوت"
);
});
// إعادة إرسال أي رسالة نصية
bot.on("message:text", (ctx) => {
ctx.reply(`قلت: ${ctx.message.text}`);
});
// تشغيل البوت
bot.start();
console.log("البوت يعمل...");شغّله:
npm run devافتح تيليجرام، ابحث عن بوتك باسم المستخدم، وأرسل /start. يجب أن ترى رسالة ترحيب.
الخطوة 4: لوحات المفاتيح التفاعلية واستعلامات الاستدعاء
تضيف لوحات المفاتيح التفاعلية أزراراً تفاعلية لرسائلك. أنشئ ملف src/keyboards.ts:
import { InlineKeyboard } from "grammy";
export const mainMenu = new InlineKeyboard()
.text("📊 الحالة", "status")
.text("⚙️ الإعدادات", "settings")
.row()
.text("ℹ️ حول", "about")
.text("📞 تواصل", "contact");
export const settingsMenu = new InlineKeyboard()
.text("🌍 اللغة", "lang")
.text("🔔 الإشعارات", "notif")
.row()
.text("« العودة للقائمة", "back_to_menu");الآن تعامل مع استعلامات الاستدعاء في src/bot.ts:
import { Bot } from "grammy";
import { mainMenu, settingsMenu } from "./keyboards.js";
import "dotenv/config";
const token = process.env.BOT_TOKEN;
if (!token) throw new Error("BOT_TOKEN is not set");
const bot = new Bot(token);
bot.command("start", (ctx) => {
ctx.reply("مرحباً! اختر خياراً:", { reply_markup: mainMenu });
});
// التعامل مع نقرات الأزرار
bot.callbackQuery("status", (ctx) => {
ctx.answerCallbackQuery();
ctx.editMessageText("✅ البوت يعمل بسلاسة!\n\nنسبة التشغيل: 99.9%", {
reply_markup: new InlineKeyboard().text("« رجوع", "back_to_menu"),
});
});
bot.callbackQuery("settings", (ctx) => {
ctx.answerCallbackQuery();
ctx.editMessageText("⚙️ الإعدادات:", { reply_markup: settingsMenu });
});
bot.callbackQuery("about", async (ctx) => {
await ctx.answerCallbackQuery();
await ctx.editMessageText(
"هذا البوت مبني بـ grammY + TypeScript.\nالإصدار: 1.0.0",
{ reply_markup: new InlineKeyboard().text("« رجوع", "back_to_menu") }
);
});
bot.callbackQuery("back_to_menu", (ctx) => {
ctx.answerCallbackQuery();
ctx.editMessageText("اختر خياراً:", { reply_markup: mainMenu });
});
bot.start();استدعِ دائماً ctx.answerCallbackQuery() عند التعامل مع استعلامات الاستدعاء. هذا يزيل مؤشر التحميل من زر المستخدم. سيظهر تيليجرام خطأ انتهاء المهلة إذا لم تُجب خلال 30 ثانية.
الخطوة 5: الوسيط والمؤلفات
مع نمو بوتك، تحتاج لتنظيم المعالجات. نمط Composer في grammY يتيح لك تقسيم الكود إلى وحدات.
أنشئ ملف src/handlers/admin.ts:
import { Composer } from "grammy";
import type { MyContext } from "../types.js";
const admin = new Composer<MyContext>();
const ADMIN_IDS = [123456789]; // استبدل بمعرف المستخدم الخاص بك
admin.command("broadcast", async (ctx) => {
if (!ADMIN_IDS.includes(ctx.from?.id ?? 0)) {
return ctx.reply("⛔ غير مصرح لك باستخدام هذا الأمر.");
}
const message = ctx.match;
if (!message) {
return ctx.reply("الاستخدام: /broadcast <الرسالة>");
}
await ctx.reply(`📢 إذاعة: ${message}`);
});
admin.command("stats", async (ctx) => {
if (!ADMIN_IDS.includes(ctx.from?.id ?? 0)) {
return ctx.reply("⛔ غير مصرح.");
}
await ctx.reply("📊 إحصائيات البوت:\n- المستخدمون: 42\n- الرسائل اليوم: 156");
});
export default admin;أنشئ ملف src/types.ts للأنواع المشتركة:
import type { Context, SessionFlavor } from "grammy";
export interface SessionData {
language: string;
notificationsEnabled: boolean;
messageCount: number;
}
export type MyContext = Context & SessionFlavor<SessionData>;سجّل المؤلف في ملف البوت الرئيسي:
import admin from "./handlers/admin.js";
// ... إعداد البوت ...
bot.use(admin);الخطوة 6: إدارة الجلسات
تتيح لك الجلسات تخزين بيانات لكل مستخدم عبر الرسائل. ثبّت إضافة الجلسات:
npm install @grammyjs/storage-fileحدّث src/bot.ts لاستخدام الجلسات:
import { Bot, session } from "grammy";
import type { MyContext, SessionData } from "./types.js";
import "dotenv/config";
const token = process.env.BOT_TOKEN;
if (!token) throw new Error("BOT_TOKEN is not set");
const bot = new Bot<MyContext>(token);
function defaultSession(): SessionData {
return {
language: "ar",
notificationsEnabled: true,
messageCount: 0,
};
}
bot.use(
session({
initial: defaultSession,
})
);
// وسيط لعد الرسائل
bot.use(async (ctx, next) => {
if (ctx.message) {
ctx.session.messageCount++;
}
await next();
});
bot.command("mystats", (ctx) => {
const { language, notificationsEnabled, messageCount } = ctx.session;
ctx.reply(
`📊 إحصائياتك:\n` +
`- اللغة: ${language}\n` +
`- الإشعارات: ${notificationsEnabled ? "مفعّلة" : "معطّلة"}\n` +
`- الرسائل المرسلة: ${messageCount}`
);
});
bot.command("setlang", (ctx) => {
const lang = ctx.match;
if (!lang || !["en", "ar", "fr"].includes(lang)) {
return ctx.reply("الاستخدام: /setlang <en|ar|fr>");
}
ctx.session.language = lang;
ctx.reply(`✅ تم تعيين اللغة إلى: ${lang}`);
});
bot.start();الخطوة 7: تدفقات المحادثة
للتفاعلات متعددة الخطوات (مثل النماذج أو المعالجات)، استخدم إضافة المحادثات:
npm install @grammyjs/conversationsأنشئ ملف src/conversations/feedback.ts:
import type { MyContext } from "../types.js";
import type { Conversation } from "@grammyjs/conversations";
type FeedbackConversation = Conversation<MyContext>;
export async function feedbackFlow(
conversation: FeedbackConversation,
ctx: MyContext
) {
await ctx.reply("📝 أحب أن أسمع رأيك!\n\nما اسمك؟");
const nameCtx = await conversation.waitFor("message:text");
const name = nameCtx.message.text;
await ctx.reply(`شكراً ${name}! كيف تقيّم خدمتنا؟ (1-5)`);
let rating: number;
do {
const ratingCtx = await conversation.waitFor("message:text");
rating = parseInt(ratingCtx.message.text);
if (isNaN(rating) || rating < 1 || rating > 5) {
await ctx.reply("الرجاء إدخال رقم بين 1 و 5.");
}
} while (isNaN(rating) || rating < 1 || rating > 5);
await ctx.reply("أي تعليقات إضافية؟ (أرسل /skip للتخطي)");
const commentCtx = await conversation.waitFor("message:text");
const comment =
commentCtx.message.text === "/skip" ? "بدون تعليق" : commentCtx.message.text;
await ctx.reply(
`✅ تم استلام الملاحظات!\n\n` +
`الاسم: ${name}\n` +
`التقييم: ${"⭐".repeat(rating)}\n` +
`التعليق: ${comment}\n\n` +
`شكراً لملاحظاتك!`
);
}سجّله في البوت:
import { conversations, createConversation } from "@grammyjs/conversations";
import { feedbackFlow } from "./conversations/feedback.js";
// أضف بعد وسيط الجلسات
bot.use(conversations());
bot.use(createConversation(feedbackFlow));
bot.command("feedback", async (ctx) => {
await ctx.conversation.enter("feedbackFlow");
});الخطوة 8: معالجة الأخطاء
بوتات الإنتاج تحتاج معالجة أخطاء قوية:
import { BotError, GrammyError, HttpError } from "grammy";
bot.catch((err: BotError) => {
const ctx = err.ctx;
console.error(`خطأ أثناء معالجة التحديث ${ctx.update.update_id}:`);
const e = err.error;
if (e instanceof GrammyError) {
console.error("خطأ في الطلب:", e.description);
if (e.description.includes("message is not modified")) {
// المستخدم نقر نفس الزر مرتين — آمن للتجاهل
return;
}
} else if (e instanceof HttpError) {
console.error("تعذر الاتصال بتيليجرام:", e);
} else {
console.error("خطأ غير معروف:", e);
}
});الخطوة 9: النشر عبر خطافات الويب (Webhooks)
للإنتاج، خطافات الويب أكثر كفاءة من الاستطلاع الطويل. إليك كيفية إعداد webhook مع grammY و Hono:
npm install hono @hono/node-serverأنشئ ملف src/webhook.ts:
import { Bot, webhookCallback } from "grammy";
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import type { MyContext } from "./types.js";
import "dotenv/config";
const token = process.env.BOT_TOKEN;
if (!token) throw new Error("BOT_TOKEN is not set");
const bot = new Bot<MyContext>(token);
// ... سجّل جميع المعالجات والوسيط والجلسات ...
bot.command("start", (ctx) => ctx.reply("مرحباً من وضع webhook!"));
const app = new Hono();
app.get("/", (c) => c.text("البوت يعمل"));
app.post(`/webhook/${token}`, webhookCallback(bot, "hono"));
const PORT = parseInt(process.env.PORT || "3000");
serve({ fetch: app.fetch, port: PORT }, () => {
console.log(`خادم Webhook يعمل على المنفذ ${PORT}`);
});
// عيّن رابط webhook (شغّل مرة واحدة)
// bot.api.setWebhook(`https://your-domain.com/webhook/${token}`);عيّن webhook عبر استدعاء واجهة تيليجرام:
curl -X POST "https://api.telegram.org/bot<YOUR_TOKEN>/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-domain.com/webhook/<YOUR_TOKEN>"}'الخطوة 10: النشر على خادم VPS
أنشئ ملف Dockerfile للنشر:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
COPY .env .env
EXPOSE 3000
CMD ["node", "dist/webhook.js"]ابنِ وشغّل:
docker build -t grammy-bot .
docker run -d --name grammy-bot -p 3000:3000 grammy-botاستخدم وكيلاً عكسياً مثل Caddy أو nginx للتعامل مع HTTPS:
# Caddyfile
bot.yourdomain.com {
reverse_proxy localhost:3000
}
هيكل المشروع
إليك هيكل المشروع النهائي:
telegram-grammy-bot/
├── src/
│ ├── bot.ts # نقطة دخول البوت (استطلاع طويل)
│ ├── webhook.ts # نقطة دخول webhook
│ ├── types.ts # الأنواع المشتركة
│ ├── keyboards.ts # تعريفات لوحات المفاتيح
│ ├── handlers/
│ │ └── admin.ts # مؤلف أوامر المدير
│ └── conversations/
│ └── feedback.ts # تدفق محادثة الملاحظات
├── .env
├── package.json
├── tsconfig.json
└── Dockerfile
استكشاف الأخطاء وإصلاحها
البوت لا يستجيب؟
- تحقق من صحة الرمز وأنه لم تنتهِ صلاحيته
- تأكد من عدم تشغيل نسخة أخرى من البوت (واحدة فقط يمكنها استخدام الاستطلاع الطويل)
- تأكد أن المستخدم لم يحظر البوت
خطاف الويب لا يعمل؟
- تأكد أن الخادم يملك شهادة SSL صالحة (HTTPS مطلوب)
- تحقق من رابط webhook عبر
https://api.telegram.org/bot<TOKEN>/getWebhookInfo - تأكد أن الخادم متاح من الإنترنت
فقدان بيانات الجلسة؟
- جلسات الذاكرة تُمسح عند إعادة التشغيل. استخدم
@grammyjs/storage-fileأو محول قاعدة بيانات للاستمرارية - تأكد أن وسيط الجلسات مسجّل قبل المعالجات
الخطوات التالية
- أضف تكامل قاعدة البيانات مع Prisma أو Drizzle للتخزين الدائم
- طبّق تعدد اللغات باستخدام
@grammyjs/i18n - أضف تحديد معدل الطلبات مع
@grammyjs/ratelimiter - ابنِ تطبيق ويب باستخدام ميزة Mini Apps من تيليجرام
- أعد المدفوعات عبر نظام الدفع المدمج في تيليجرام
- أضف معالجة الملفات للصور والمستندات والرسائل الصوتية
الخلاصة
لقد بنيت بوت تيليجرام كامل الوظائف باستخدام grammY و TypeScript، يغطي:
- إنشاء البوت وإدارة الرمز
- التعامل مع الأوامر والرسائل
- لوحات المفاتيح التفاعلية
- الكود المعياري مع المؤلفات
- إدارة الحالة عبر الجلسات
- تدفقات المحادثة متعددة الخطوات
- معالجة الأخطاء والنشر في بيئة الإنتاج
نهج grammY القائم على TypeScript يمنحك إكمالاً تلقائياً ممتازاً وأماناً في الأنواع وواجهة برمجة نظيفة. مع نظامه البيئي من الإضافات، يعد الإطار المثالي لبناء بوتات تيليجرام في 2026.
الكود المصدري الكامل لهذا الدليل متاح للمرجعية. ابدأ ببناء بوتك الخاص واستكشف واجهة Bot API الغنية من تيليجرام لإنشاء أتمتة قوية وتجارب تفاعلية.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

بناء أول خادم MCP باستخدام TypeScript: الأدوات والموارد والقوالب
تعلم كيف تبني خادم MCP جاهزاً للإنتاج من الصفر باستخدام TypeScript. هذا الدرس العملي يغطي الأدوات والموارد والقوالب ونقل البيانات عبر stdio والربط مع Claude Desktop و Cursor.

بناء ونشر واجهة برمجية بدون خوادم باستخدام Cloudflare Workers و Hono و D1
تعلّم كيف تبني واجهة برمجية REST جاهزة للإنتاج باستخدام Cloudflare Workers وإطار Hono وقاعدة بيانات D1 — من إعداد المشروع حتى النشر العالمي.

بناء واجهات برمجة تطبيقات آمنة الأنواع من البداية للنهاية مع tRPC و Next.js App Router
تعلم كيفية بناء واجهات برمجة تطبيقات آمنة الأنواع بالكامل مع tRPC و Next.js 15 App Router. يغطي هذا الدليل العملي إعداد الموجه والإجراءات والوسيط وتكامل React Query والاستدعاءات من جانب الخادم — كل ذلك دون كتابة أي مخطط API.