كيفية بناء بوت دردشة Rocket.Chat باستخدام TypeScript

دقائق القراءة: 8
واجهة توضيحية لبناء بوت دردشة في Rocket.Chat باستخدام TypeScript

إذا كنت تريد إنشاء بوت مخصص لخادم Rocket.Chat لإدارة المحادثات أو تنفيذ أوامر تلقائية، فهذا الدليل العملي يشرح لك الخطوات كاملة بدءاً من تشغيل الخادم محلياً، مروراً بإنشاء حساب للبوت، وصولاً إلى كتابة الكود باستخدام TypeScript وبناء نظام أوامر قابل للتوسع.

الهدف هنا ليس مجرد تشغيل مثال بسيط، بل إنشاء أساس تقني منظم يمكنك الاعتماد عليه لاحقاً في مشاريع أكبر أو في بيئة إنتاج فعلية.

إعداد خادم Rocket.Chat محلياً

قبل كتابة البوت، تحتاج أولاً إلى تشغيل نسخة محلية من Rocket.Chat حتى تتمكن من اختبار الوظائف دون التأثير على أي بيئة حية. أسهل طريقة هي استخدام Docker مع إعداد جاهز يتولى تشغيل كل من Rocket.Chat وMongoDB معاً.

إذا لم يكن Docker مثبتاً لديك، فقم بتثبيته أولاً بحسب نظام التشغيل الذي تستخدمه. في بعض الأجهزة، قد تحتاج أيضاً إلى تفعيل hardware virtualization من إعدادات BIOS.

إنشاء ملف .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

عند نجاح التشغيل، من المفترض أن تظهر رسائل تؤكد إنشاء الحاويات المطلوبة.

مخرجات الطرفية بعد تنفيذ docker-compose up -d وتشغيل حاويات Rocket.Chat وMongoDB

الآن افتح المتصفح وانتقل إلى localhost:3000. ستظهر لك واجهة الإعداد الأولى الخاصة بمنصة Rocket.Chat.

إكمال معالج الإعداد الأولي

أول شاشة ستعرض عليك إنشاء حساب المدير Admin. هذا الحساب يمنحك صلاحيات كاملة لإدارة الخادم وإعداد المستخدمين والقنوات.

شاشة إدخال بيانات حساب المدير في Rocket.Chat أثناء الإعداد الأولي

بعد ذلك ستجد شاشة معلومات المؤسسة. هذه الخطوة اختيارية، ويمكنك تركها فارغة أثناء العمل المحلي.

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

شاشة إعداد معلومات الخادم في Rocket.Chat مع خيار تعطيل المصادقة الثنائية التلقائية

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

واجهة Rocket.Chat بعد الانتهاء من الإعداد الأولي وظهور قناة general

إنشاء حساب بوت داخل Rocket.Chat

بعد تشغيل الخادم، تحتاج إلى إنشاء مستخدم مخصص للبوت حتى يتمكن الكود من تسجيل الدخول والتفاعل مع الرسائل.

من القائمة الجانبية، افتح Administration ثم انتقل إلى Users واضغط على +New لإنشاء مستخدم جديد.

أثناء إدخال بيانات الحساب، راعِ النقاط التالية:

  • أوقف خيار Require password change.
  • أوقف خيار Set random password and send by email.
  • أوقف خيار Send welcome email.
  • اختر الدور bot من قائمة Roles.

واجهة إنشاء مستخدم بوت جديد في Rocket.Chat مع تعيين الدور bot

بعد الحفظ، دوّن اسم المستخدم وكلمة المرور، لأنك ستحتاج إليهما داخل ملف الإعدادات الخاص بالمشروع.

تهيئة مشروع البوت باستخدام TypeScript

أنشئ مجلداً جديداً وفارغاً لمشروع البوت. بعدها ابدأ بتهيئة مشروع Node.js، سواء باستخدام npm init أو عبر إنشاء ملف package.json يدوياً.

إضافة أوامر التشغيل في package.json

داخل قسم 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، فاحرص على تجاهل الملفات والمجلدات التي لا يجب رفعها إلى المستودع، مثل الوحدات المثبتة والملفات المترجمة والبيانات السرية:

/node_modules/
/prod/
.env

حفظ بيانات الاتصال في .env

أنشئ الآن ملف .env داخل مشروع البوت، وأضف القيم التالية مع استبدال بيانات الاعتماد بما يناسبك:

ROCKETCHAT_URL="localhost:3000"
ROCKETCHAT_USER="tutorial-bot"
ROCKETCHAT_PASSWORD="********"
ROCKETCHAT_USE_SSL=""

كتابة الكود الأساسي لبوت Rocket.Chat

داخل المشروع، أنشئ مجلداً باسم src ثم ملفاً باسم bot.ts. سيكون هذا الملف هو نقطة الدخول الرئيسية لتشغيل البوت.

هيكل ملفات مشروع بوت Rocket.Chat باستخدام TypeScript

الاستيراد وتهيئة المتغيرات البيئية

import { api, driver } from "@rocket.chat/sdk";
import dotenv from "dotenv";

لأن Node.js لا يحمّل متغيرات البيئة تلقائياً، يجب تفعيلها عبر 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);
}

إنشاء الدالة الرئيسية غير المتزامنة

بما أن الاتصال بالخادم وتسجيل الدخول يتطلبان استخدام async/await، فيمكنك الاستفادة من تعبير التنفيذ الفوري IIFE:

(async () => {
  // سيتم وضع منطق التشغيل هنا
})();

الاتصال بالخادم وتسجيل الدخول

داخل هذه الدالة، حدّد أولاً ما إذا كان الاتصال سيستخدم SSL:

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,
});

الانضمام إلى الغرفة والاشتراك في الرسائل

بعد نجاح الاتصال، اجعل البوت ينضم إلى قناة general ويبدأ الاستماع إلى الرسائل:

await driver.joinRooms(["general"]);
await driver.subscribeToMessages();

وللتأكد من أن البوت يعمل فعلاً، أرسل رسالة تأكيد عند بدء التشغيل:

await driver.sendToRoom("I am alive!", "general");

بناء المشروع وتشغيله

شغّل الأوامر التالية من الطرفية:

npm run build
npm run start

إذا سار كل شيء كما يجب، سترى رسالة البوت داخل الغرفة.

رسالة البوت داخل Rocket.Chat بعد نجاح تشغيله وإرساله رسالة I am alive

بناء نظام لمعالجة الأوامر

حتى الآن، البوت متصل بالخادم ويستمع للرسائل، لكنه لا يعرف كيف يتعامل معها. لذلك سنضيف طبقة منظمة لمعالجة الأوامر.

ربط الاستجابة للرسائل

بعد سطر subscribeToMessages() أضف السطر التالي:

driver.reactToMessages();

لكن هذه الدالة تحتاج إلى callback، والأفضل هنا هو فصل منطق المعالجة في ملف مستقل لسهولة التوسعة والصيانة.

إنشاء ملفات الأوامر

داخل src أنشئ مجلداً باسم commands، ثم أضف ملفين:

  • CommandHandler.ts
  • CommandList.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> => {
  // المنطق هنا
};

إضافة التحقق الأساسي من الرسائل

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(" ");

هنا يتم استخراج اسم الغرفة، ثم تقسيم الرسالة إلى جزأين: بادئة الأمر واسم الأمر نفسه. على سبيل المثال، في الرسالة !fCC ping ستكون البادئة !fCC والأمر ping.

التنقل بين الأوامر المتاحة

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
  );
}

المنطق هنا بسيط وفعال:

  • إذا لم تبدأ الرسالة بالبادئة الصحيحة، يتجاهلها البوت.
  • إذا وُجدت البادئة، يبحث البوت عن الأمر داخل قائمة الأوامر.
  • إذا عثر عليه، يشغّل الدالة الخاصة به.
  • إذا لم يعثر عليه، يرسل رسالة توضيحية للمستخدم.

ربط المعالج بالملف الرئيسي

عد الآن إلى ملف bot.ts وأضف الاستيراد التالي:

import { CommandHandler } from "./commands/CommandHandler";

ثم مرّر المعالج إلى الدالة:

driver.reactToMessages(CommandHandler);

تعريف بنية الأوامر باستخدام interface

من مزايا TypeScript أنك تستطيع فرض شكل موحّد لكل أمر داخل البوت. هذا يجعل الكود أكثر وضوحاً ويمنع الأخطاء الناتجة عن إضافة أوامر ناقصة البنية.

داخل مجلد 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

الآن أصبح كل شيء جاهزاً لإضافة أول أمر فعلي للبوت.

داخل المجلد 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);
  }
};

هذه البنية تعني أن الأمر اسمه ping، وعند استدعائه يرسل البوت الرد Pong!.

إضافة الأمر إلى القائمة

افتح ملف CommandList.ts وعدّله ليصبح بالشكل التالي:

import { CommandInt } from "../interfaces/CommandInt";
import { ping } from "./ping";

export const CommandList: CommandInt[] = [ping];

إضافة النوع CommandInt[] تمنحك مستوى إضافياً من الأمان، لأن TypeScript سيتحقق من أن جميع العناصر داخل القائمة تتبع البنية المطلوبة فعلاً.

اختبار البوت والأوامر

أعد بناء المشروع ثم شغّله من جديد:

npm run build
npm run start

بعد ذلك، افتح غرفة المحادثة وأرسل الأمر التالي:

!fCC ping

إذا كان كل شيء مضبوطاً، فسيظهر رد البوت مباشرة:

اختبار أمر ping في بوت Rocket.Chat وظهور الرد Pong

أما إذا أرسلت أمراً غير موجود مثل:

!fCC pong

فسيخبرك البوت أن الأمر غير صالح:

رسالة توضيحية من بوت Rocket.Chat عند إدخال أمر غير صالح

أفضل ممارسات لتطوير بوت قابل للتوسع

بعد نجاح النسخة الأولية، من المفيد اعتماد بعض الممارسات التي تجعل مشروعك أكثر احترافية:

  • افصل كل أمر في ملف مستقل ليسهل اختباره وتطويره.
  • استخدم متغيرات البيئة لكل البيانات الحساسة مثل كلمات المرور وروابط الخوادم.
  • أضف سجلات logging واضحة لتتبّع الأخطاء وسلوك البوت.
  • تحقق من صلاحيات المستخدم قبل تنفيذ الأوامر الإدارية.
  • فكّر في إضافة أوامر مساعدة مثل help وabout لتحسين تجربة الاستخدام.

أفكار لتطوير البوت لاحقاً

بعد بناء الهيكل الأساسي، يمكنك التوسع في العديد من الاتجاهات:

  1. إضافة أوامر إدارة للمشرفين.
  2. إنشاء أوامر تربط البوت بواجهات API خارجية.
  3. تنفيذ نظام ردود تلقائية للكلمات المفتاحية.
  4. إضافة سجل للعقوبات أو التنبيهات داخل المجتمعات الكبيرة.
  5. ربط البوت بقاعدة بيانات لحفظ الإعدادات وسجل التفاعلات.

الخلاصة التقنية

بناء بوت على منصة Rocket.Chat باستخدام TypeScript يمنحك مزيجاً ممتازاً من المرونة والتنظيم والأمان البرمجي. الفائدة الحقيقية لا تكمن فقط في تشغيل البوت، بل في تصميم بنية أوامر نظيفة وقابلة للتوسع منذ البداية. إذا بدأت بهذا الأساس الصحيح، فسيصبح من السهل لاحقاً تحويل البوت من مشروع تجريبي بسيط إلى أداة عملية تدير مجتمعاً كاملاً بكفاءة.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *