كيفية بناء بوت دردشة Rocket.Chat باستخدام TypeScript
في هذا الدليل العملي ستتعلم كيفية إنشاء بوت خاص بك لمنصة Rocket.Chat باستخدام TypeScript، ثم تشغيله واختباره على بيئة محلية. هذه الفكرة مناسبة جداً إذا كنت تريد بناء بوت للإشراف، أو الرد على الأوامر، أو تنفيذ مهام تلقائية داخل قنوات الدردشة.
سنمر معاً على إعداد خادم Rocket.Chat محلياً، وإنشاء حساب للبوت، ثم تجهيز مشروع Node.js وكتابة الكود الأساسي، وأخيراً بناء نظام أوامر قابل للتوسعة.
لماذا تختار بناء بوت على Rocket.Chat؟
منصة Rocket.Chat تمنحك مرونة كبيرة، خصوصاً إذا كنت تعتمد على استضافة ذاتية وتحتاج إلى تحكم كامل في بيئة العمل. ومن أبرز المزايا:
- إمكانية أتمتة المهام المتكررة داخل القنوات.
- بناء أوامر مخصصة تناسب فريقك أو مجتمعك.
- إضافة وظائف إشراف، تنبيه، وربط مع خدمات خارجية.
- الاستفادة من
SDKرسمي يسهل الاتصال بالخادم وإدارة الرسائل.
إعداد خادم Rocket.Chat محلياً
أول خطوة هي تشغيل نسخة محلية من Rocket.Chat حتى تتمكن من اختبار البوت بأمان دون التأثير على بيئة إنتاج حقيقية. يفضَّل استخدام Docker لأنه يختصر الوقت ويتكفل بتشغيل الخدمات الضرورية مثل MongoDB.
المتطلبات الأساسية قبل البدء
- تثبيت
Dockerعلى جهازك. - وجود مجلد خاص بمشروع الخادم المحلي.
- الوصول إلى ملف إعداد
docker-composeالخاص ببيئة التطوير.
إذا كنت تعمل على نظام Windows فقد تحتاج إلى تفعيل خاصية المحاكاة الافتراضية من BIOS حتى يعمل Docker Desktop بشكل صحيح.
إنشاء ملف .env للخادم
داخل مجلد Rocket.Chat أنشئ ملفاً باسم .env وضع فيه القيم التالية:
COMPOSE_FILE=docker-compose.dev.yml
PORT=3000
ROOT_URL=http://localhost:3000
ROCKETCHAT_VERSION=latest
تشغيل الحاويات باستخدام Docker Compose
افتح الطرفية داخل نفس المجلد ثم نفّذ الأمر التالي:
docker-compose up -d
عند نجاح العملية ستظهر رسائل تؤكد إنشاء الصور والخدمات المطلوبة.

بعد ذلك افتح المتصفح وانتقل إلى localhost:3000، وستظهر لك واجهة الإعداد الأولى الخاصة بـ Rocket.Chat.
إكمال معالج الإعداد الأولي
سيطلب منك النظام إنشاء حساب المدير Admin. بما أن هذه بيئة محلية، يمكنك استخدام بيانات بسيطة مخصصة للتجربة. بعد إدخال بيانات الحساب، انتقل إلى الخطوات التالية في المعالج.

في شاشة معلومات المؤسسة يمكنك ترك الحقول فارغة إن أردت، لأنها ليست ضرورية لهذا الشرح.
أما في صفحة إعدادات الخادم، فاحرص على تعطيل خيار التفعيل التلقائي للمصادقة الثنائية 2FA في البيئة المحلية حتى لا تفقد القدرة على الدخول إلى الخادم.

في الخطوة الأخيرة يمكنك اختيار تشغيل الخادم بشكل مستقل عبر خيار Keep standalone دون الاشتراك في الخدمات المدفوعة. بعد إنهاء الإعداد ستدخل إلى مساحة العمل، وستجد القناة الافتراضية general جاهزة للاستخدام.

إنشاء حساب بوت داخل Rocket.Chat
لكي يتمكن الكود من الاتصال بالخادم، نحتاج إلى إنشاء مستخدم مخصص للبوت.
خطوات إنشاء المستخدم
- من القائمة الجانبية افتح
Administration. - ثم انتقل إلى
Users. - اضغط على زر
+New. - أدخل اسم البوت واسم المستخدم والبريد وكلمة المرور.
إعدادات مهمة عند إنشاء البوت
- اترك خيار
Require password changeمعطلاً. - اترك خيار
Set random password and send by emailمعطلاً. - اترك خيار
Send welcome emailمعطلاً. - اختر الدور
botمن قائمةRoles.

بعد الحفظ، دوّن اسم المستخدم وكلمة المرور لأننا سنستخدمهما داخل ملف .env في المشروع البرمجي.
تهيئة مشروع البوت باستخدام TypeScript
الآن ننتقل إلى كتابة الكود. أنشئ مجلداً جديداً وفارغاً للمشروع، ثم ابدأ بتهيئة مشروع Node.js.
إعداد ملف package.json
يمكنك إنشاؤه باستخدام npm init أو إضافته يدوياً. تأكد من وجود قسم scripts بالشكل التالي:
"scripts": {
"prebuild": "rm -rf ./prod",
"build": "tsc",
"start": "node ./prod/bot.js"
}
تثبيت الاعتماديات
ابدأ بتثبيت الحزم الخاصة بالتطوير:
npm install --save-dev typescript @types/node
ثم ثبّت الاعتماديات الأساسية للمشروع:
npm install @rocket.chat/sdk dotenv
إعداد ملف tsconfig.json
إذا كانت أداة TypeScript مثبّتة عالمياً، يمكنك استخدام الأمر tsc --init لإنشاء الملف، ثم تعديله بالقيم التالية:
{
"compilerOptions": {
"target": "ES5",
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./prod",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"noImplicitAny": false
}
}
إعداد ملف .gitignore
إذا كنت تستخدم git، فأضف الملفات والمجلدات التالية إلى .gitignore:
/node_modules/
/prod/
.env
بهذه الطريقة لن ترفع الملفات المترجمة أو الأسرار الحساسة إلى المستودع.
إعداد متغيرات البيئة
أنشئ ملف .env داخل مشروع البوت، ثم أضف القيم التالية:
ROCKETCHAT_URL="localhost:3000"
ROCKETCHAT_USER="tutorial-bot"
ROCKETCHAT_PASSWORD="********"
ROCKETCHAT_USE_SSL=""
كتابة الكود الأساسي للبوت
أنشئ مجلداً باسم src، ثم داخله ملفاً باسم bot.ts. بعد ذلك ابدأ بإضافة الكود الأساسي.

الاستيراد وقراءة الإعدادات
import { api, driver } from "@rocket.chat/sdk";
import dotenv from "dotenv";
بما أن Node.js لا يحمّل متغيرات البيئة تلقائياً، نحتاج إلى استدعاء الدالة config() من الحزمة dotenv:
dotenv.config();
ثم نفك القيم المطلوبة من process.env:
const {
ROCKETCHAT_URL,
ROCKETCHAT_USER,
ROCKETCHAT_PASSWORD,
ROCKETCHAT_USE_SSL,
} = process.env;
التحقق من القيم الإلزامية
قبل الاتصال بالخادم، من المهم التأكد من وجود المتغيرات اللازمة:
if (!ROCKETCHAT_URL || !ROCKETCHAT_USER || !ROCKETCHAT_PASSWORD) {
console.error("Missing required environment variables.");
process.exit(1);
}
لاحظ أن الشرط يجب أن يتحقق من غياب كلمة المرور باستخدام !ROCKETCHAT_PASSWORD حتى يعمل الفحص كما ينبغي.
استخدام دالة IIFE مع async/await
بما أن الاتصال بالخادم غير متزامن، فمن العملي استخدام دالة تُنفَّذ فوراً:
(async () => {
// Nothing here yet
})();
إعداد الاتصال بالخادم
داخل هذه الدالة أضف الخطوات التالية:
const ssl = !!ROCKETCHAT_USE_SSL;
await driver.connect({ host: ROCKETCHAT_URL, useSsl: ssl });
await driver.login({
username: ROCKETCHAT_USER,
password: ROCKETCHAT_PASSWORD,
});
await api.login({
username: ROCKETCHAT_USER,
password: ROCKETCHAT_PASSWORD,
});
await driver.joinRooms(["general"]);
await driver.subscribeToMessages();
await driver.sendToRoom("I am alive!", "general");
يقوم هذا الجزء بالمهام التالية:
- تحديد ما إذا كان الاتصال سيستخدم
SSL. - ربط البوت بخادم
Rocket.Chat. - تسجيل الدخول باستخدام حساب البوت.
- الانضمام إلى القناة
general. - الاستماع إلى الرسائل الجديدة.
- إرسال رسالة تؤكد أن البوت متصل ويعمل.
بناء المشروع وتشغيله
بعد حفظ الملف، نفّذ الأمرين التاليين:
npm run build
npm run start
إذا نجحت العملية فستظهر رسالة البوت داخل القناة.

بناء معالج الأوامر Command Handler
حتى الآن أصبح البوت يستقبل الرسائل، لكنه لا ينفّذ أي أوامر. لذلك نحتاج إلى بنية تنظّم معالجة الرسائل وتربطها بالأوامر المناسبة.
ربط البوت بمعالج الرسائل
بعد السطر الخاص بالاشتراك في الرسائل، أضف السطر التالي:
driver.reactToMessages();
لكن هذه الدالة تحتاج إلى callback، لذا سننشئ معالجاً منفصلاً للحفاظ على نظافة الكود وسهولة صيانته.
إنشاء ملفات الأوامر
داخل مجلد src أنشئ مجلداً باسم commands، ثم أضف الملفين التاليين:
CommandHandler.tsCommandList.ts
في ملف CommandList.ts ابدأ بهذا السطر:
export const CommandList = [];
كتابة منطق معالج الأوامر
في ملف CommandHandler.ts أضف الاستيرادات التالية:
import { driver } from "@rocket.chat/sdk";
import { IMessage } from "@rocket.chat/sdk/dist/config/messageInterfaces";
import { CommandList } from "./CommandList";
ثم عرّف الدالة:
export const CommandHandler = async (
err: unknown,
messages: IMessage[]
): Promise<void> => {
// Code will go here.
}
أضف بعدها التحقق الأساسي من الأخطاء والرسائل:
if (err) {
console.error(err);
return;
}
const message = messages[0];
if (!message.msg || !message.rid) {
return;
}
الآن استخرج اسم الغرفة والبادئة واسم الأمر:
const roomName = await driver.getRoomName(message.rid);
const [prefix, commandName] = message.msg.split(" ");
ثم أضف منطق مطابقة الأوامر:
if (prefix === "!fCC") {
for (const Command of CommandList) {
if (commandName === Command.name) {
await Command.command(message, roomName);
return;
}
}
await driver.sendToRoom(
`I am sorry, but \`${commandName}\` is not a valid command.`,
roomName
);
}
هذا يعني أن البوت:
- يتجاهل أي رسالة لا تبدأ بالبادئة
!fCC. - يفحص قائمة الأوامر المتاحة.
- ينفّذ الأمر إذا وجد تطابقاً.
- يرسل رسالة خطأ إذا كان الأمر غير معروف.
ربط المعالج بالملف الرئيسي
ارجع إلى ملف bot.ts، ثم استورد المعالج:
import { CommandHandler } from "./commands/CommandHandler";
واستخدمه بهذه الصورة:
driver.reactToMessages(CommandHandler);
تعريف نوع موحّد للأوامر في TypeScript
من مزايا TypeScript أنك تستطيع فرض بنية واضحة للأوامر باستخدام interface، وهذا يقلل الأخطاء عند التوسع لاحقاً.
إنشاء الواجهة CommandInt
داخل مجلد src أنشئ مجلداً باسم interfaces، ثم أضف الملف CommandInt.ts بالمحتوى التالي:
import { IMessage } from "@rocket.chat/sdk/dist/config/messageInterfaces";
export interface CommandInt {
name: string;
description: string;
command: (message: IMessage, room: string) => Promise<void>
}
بهذا أصبحت أي إضافة جديدة إلى قائمة الأوامر مطالبة بالالتزام بهذا الشكل.
إنشاء أول أمر للبوت: ping
الآن سنبني أمراً بسيطاً لاختبار عمل البوت.
ملف الأمر ping.ts
داخل src/commands أنشئ ملفاً باسم ping.ts ثم أضف:
import { driver } from "@rocket.chat/sdk";
import { CommandInt } from "../interfaces/CommandInt";
export const ping: CommandInt = {
name: "ping",
description: "Pings the bot.",
command: async (message, room) => {
await driver.sendToRoom("Pong!", room);
}
}
إضافة الأمر إلى القائمة
افتح ملف CommandList.ts وعدّله بالشكل التالي:
import { CommandInt } from "../interfaces/CommandInt";
import { ping } from "./ping";
export const CommandList: CommandInt[] = [ping];
هذا يضمن أن كل عنصر في CommandList يطابق بنية CommandInt بشكل صريح.
اختبار الأمر
أعد بناء المشروع وتشغيله:
npm run build
npm run start
ثم داخل غرفة الدردشة أرسل الأمر التالي:
!fCC ping
يفترض أن يرد البوت بالرسالة Pong!.

وإذا أرسلت أمراً غير موجود مثل:
!fCC pong
فسيخبرك البوت أن الأمر غير صالح.

أفضل ممارسات لتحسين بنية البوت
إذا كنت تنوي تطوير هذا المشروع ليعمل في بيئة حقيقية، فهذه بعض التوصيات المهمة:
- فصل الأوامر في ملفات مستقلة لتسهيل الصيانة.
- استخدام ملفات إعداد منفصلة بين التطوير والإنتاج.
- إضافة سجلات
loggingأوضح لمتابعة الأخطاء. - التحقق من صلاحيات المستخدم قبل تنفيذ الأوامر الحساسة.
- إضافة اختبارات تلقائية للأوامر الأساسية.
- عدم حفظ كلمات المرور مباشرة داخل المستودع، والاكتفاء بملف
.env.
أفكار لتوسعة البوت لاحقاً
- أمر للمساعدة مثل
helpيعرض قائمة الأوامر. - أمر للإشراف مثل حذف الرسائل أو تنبيه الأعضاء.
- ربط البوت مع واجهات
APIخارجية. - إرسال تنبيهات دورية لقنوات محددة.
- تنفيذ أوامر تعتمد على قواعد بيانات أو سجلات داخلية.
الخلاصة التقنية
بناء بوت على Rocket.Chat باستخدام TypeScript يعد خياراً ممتازاً لمن يريد مشروعاً منظماً وقابلاً للتوسع. الجمع بين @rocket.chat/sdk وميزة الأنواع في TypeScript يمنحك أساساً قوياً لكتابة أوامر موثوقة وسهلة الصيانة. والأهم أن البدء ببنية بسيطة مثل Command Handler وواجهة CommandInt يجعل إضافة الميزات المستقبلية أكثر وضوحاً وأقل عرضة للأخطاء.