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

دقائق القراءة: 7
واجهة توضيحية لبناء بوت دردشة 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

عند نجاح العملية ستظهر رسائل تؤكد إنشاء الصور والخدمات المطلوبة.

مخرجات أمر 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

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

خطوات إنشاء المستخدم

  1. من القائمة الجانبية افتح Administration.
  2. ثم انتقل إلى Users.
  3. اضغط على زر +New.
  4. أدخل اسم البوت واسم المستخدم والبريد وكلمة المرور.

إعدادات مهمة عند إنشاء البوت

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

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

بعد الحفظ، دوّن اسم المستخدم وكلمة المرور لأننا سنستخدمهما داخل ملف .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. بعد ذلك ابدأ بإضافة الكود الأساسي.

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

الاستيراد وقراءة الإعدادات

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

إذا نجحت العملية فستظهر رسالة البوت داخل القناة.

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

بناء معالج الأوامر Command Handler

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

ربط البوت بمعالج الرسائل

بعد السطر الخاص بالاشتراك في الرسائل، أضف السطر التالي:

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> => {
  // 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!.

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

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

!fCC pong

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

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

أفضل ممارسات لتحسين بنية البوت

إذا كنت تنوي تطوير هذا المشروع ليعمل في بيئة حقيقية، فهذه بعض التوصيات المهمة:

  • فصل الأوامر في ملفات مستقلة لتسهيل الصيانة.
  • استخدام ملفات إعداد منفصلة بين التطوير والإنتاج.
  • إضافة سجلات logging أوضح لمتابعة الأخطاء.
  • التحقق من صلاحيات المستخدم قبل تنفيذ الأوامر الحساسة.
  • إضافة اختبارات تلقائية للأوامر الأساسية.
  • عدم حفظ كلمات المرور مباشرة داخل المستودع، والاكتفاء بملف .env.

أفكار لتوسعة البوت لاحقاً

  • أمر للمساعدة مثل help يعرض قائمة الأوامر.
  • أمر للإشراف مثل حذف الرسائل أو تنبيه الأعضاء.
  • ربط البوت مع واجهات API خارجية.
  • إرسال تنبيهات دورية لقنوات محددة.
  • تنفيذ أوامر تعتمد على قواعد بيانات أو سجلات داخلية.

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

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

اترك تعليقاً

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