دليل احترافي لإنشاء بوت ديسكورد باستخدام JavaScript واستضافته مجانًا
مقدمة: لماذا إنشاء بوت Discord باستخدام JavaScript؟
إذا كنت ترغب في بناء بوت خاص بك على منصة Discord دون تثبيت أدوات معقدة على جهازك، فهذه المقالة تقدم لك مسارًا عمليًا وواضحًا. ستتعلم كيفية استخدام JavaScript مع Node.js لإنشاء بوت يعمل سحابيًا بالكامل، مع إمكانية استضافته مجانًا وتشغيله على مدار الوقت.
يعتمد هذا الدليل على مجموعة أدوات مهمة، مثل Discord API، ومكتبة discord.js، ومنصة Replit للتطوير السحابي. والهدف هنا ليس مجرد نقل خطوات تقنية، بل تقديم شرح عربي احترافي يسهل تطبيقه ويضيف قيمة حقيقية للمطور والمستخدم.

كيفية إنشاء حساب بوت على Discord
قبل كتابة أي سطر برمجي، يجب أولًا إنشاء تطبيق وبوت رسمي داخل منصة Discord. هذه الخطوة تمنحك رمز الوصول Token الذي يستخدمه البرنامج لتسجيل الدخول وتشغيل البوت.
خطوات إنشاء البوت
- سجّل الدخول إلى موقع
Discord. - انتقل إلى صفحة التطبيقات
Developer Portal. - اضغط على زر
New Application. - اكتب اسمًا مناسبًا للتطبيق ثم اضغط
Create. - افتح تبويب
Botثم اخترAdd Bot. - أكّد العملية بالضغط على
Yes, do it!.



من الأفضل الإبقاء على الإعدادات الافتراضية، مثل تفعيل Public Bot وتعطيل Require OAuth2 Code Grant إذا لم تكن بحاجة إلى إعدادات متقدمة.
بعد إنشاء البوت، انسخ Token الخاص به. هذا الرمز بمنزلة كلمة مرور للبوت، لذا يجب عدم مشاركته مع أي شخص. وإذا تم تسريبه، يمكنك توليد رمز جديد فورًا.

كيفية دعوة البوت إلى السيرفر
إنشاء البوت وحده لا يكفي، بل يجب إضافته إلى السيرفر الذي تريد أن يعمل بداخله.
توليد رابط الدعوة
- انتقل إلى تبويب
OAuth2. - ضمن قسم
Scopesاخترbot. - حدّد الصلاحيات المناسبة لعمل البوت.
- انسخ الرابط الناتج، ثم افتحه في المتصفح.
- اختر السيرفر المطلوب، ثم اضغط
Authorize.


احرص على منح البوت أقل قدر ممكن من الصلاحيات، خاصة إذ كنت لا تحتاج إلى صلاحية Administrator. هذه الممارسة تعزز الأمان وتقلل المخاطر.
إعداد بيئة التطوير باستخدام Replit وdiscord.js
يمكنك تطوير البوت محليًا على جهازك، لكن استخدام Replit يجعل التجربة أسهل، لأنه يوفر محررًا سحابيًا، وتثبيتًا تلقائيًا للحزم، وإمكانية تشغيل المشروع مباشرة من المتصفح.
خطوات البدء
- انتقل إلى منصة
Replit. - أنشئ مشروعًا جديدًا واختر بيئة
Node.js. - افتح الملف
main.jsأوindex.jsبحسب بنية المشروع. - أضف استيراد مكتبة
discord.js.
بمجرد الضغط على زر التشغيل، ستقوم المنصة غالبًا بتثبيت التبعيات تلقائيًا.
كتابة أول بوت Discord بلغة JavaScript
تعتمد مكتبة discord.js على مفهوم الأحداث Events. أي إن البوت يستمع إلى مواقف معينة، مثل بدء التشغيل أو وصول رسالة جديدة، ثم يتفاعل معها.
كود بوت بسيط للرد على رسالة ping
const Discord = require("discord.js");
const client = new Discord.Client();
client.on("ready", () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on("message", msg => {
if (msg.content === "ping") {
msg.reply("pong");
}
});
client.login(process.env.TOKEN);
تخزين رمز البوت داخل ملف .env
حتى لا تضع بيانات حساسة داخل الشيفرة مباشرة، أنشئ ملفًا باسم .env وأضف داخله السطر التالي:
TOKEN=[paste token here]
يستخدم هذا الملف لتخزين متغيرات البيئة Environment Variables. وفي Replit يعد هذا الأسلوب أكثر أمانًا من كتابة الرمز مباشرة داخل الكود.
كيف يعمل هذا الكود؟
require("discord.js"): يستورد المكتبة المسؤولة عن التعامل معDiscord API.new Discord.Client(): ينشئ اتصال البوت مع منصة ديسكورد.client.on("ready"): ينفذ كودًا عند نجاح تشغيل البوت.client.on("message"): يستمع إلى الرسائل الجديدة في القنوات.msg.reply("pong"): يرسل ردًا مباشرًا على المستخدم.client.login(process.env.TOKEN): يسجّل دخول البوت عبر الرمز المحفوظ.
تشغيل البوت واختباره
بعد تشغيل المشروع من داخل Replit، انتقل إلى السيرفر في Discord واكتب ping. إذا كان كل شيء صحيحًا، فسيقوم البوت بالرد بكلمة pong.

تطوير البوت: جلب اقتباسات تحفيزية عبر API
الآن سننتقل من بوت بسيط إلى بوت أكثر فائدة. سنضيف أمرًا باسم $inspire ليجلب اقتباسًا تحفيزيًا عشوائيًا من خدمة خارجية.
الكود بعد إضافة node-fetch
const Discord = require("discord.js");
const fetch = require("node-fetch");
const client = new Discord.Client();
function getQuote() {
return fetch("https://zenquotes.io/api/random")
.then(res => {
return res.json();
})
.then(data => {
return data[0]["q"] + " -" + data[0]["a"];
});
}
client.on("ready", () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on("message", msg => {
if (msg.author.bot) return;
if (msg.content === "$inspire") {
getQuote().then(quote => msg.channel.send(quote));
}
});
client.login(process.env.TOKEN);
فائدة هذه الإضافة
- استخدام
node-fetchلإجراء طلبHTTP. - جلب بيانات من خدمة
zenquotes.io. - تحويل الاستجابة إلى صيغة
JSON. - إرسال الاقتباس إلى القناة عند كتابة الأمر
$inspire.
التحقق من msg.author.bot يمنع البوت من الرد على نفسه أو على بوتات أخرى، وهي خطوة مهمة لتجنب الحلقات غير المنتهية.
إضافة رسائل تشجيعية عند اكتشاف كلمات حزينة
يمكن جعل البوت أكثر تفاعلًا من خلال تحليل محتوى الرسائل والرد برسائل تشجيعية عندما يلاحظ كلمات سلبية أو حزينة.
تعريف الكلمات الحزينة والرسائل التشجيعية
const sadWords = ["sad", "depressed", "unhappy", "angry", "miserable"];
const encouragements = [
"Cheer up!",
"Hang in there.",
"You are a great person / bot!"
];
تحديث حدث الرسائل
client.on("message", msg => {
if (msg.content === "$inspire") {
getQuote().then(quote => msg.channel.send(quote));
}
if (sadWords.some(word => msg.content.includes(word))) {
const encouragement = encouragements[Math.floor(Math.random() * encouragements.length)];
msg.reply(encouragement);
}
});
في هذا الجزء يتم فحص الرسالة باستخدام some() لمعرفة ما إذا كانت تحتوي على أي كلمة من القائمة sadWords. وعند تحقق الشرط، يختار البوت رسالة عشوائية من المصفوفة encouragements.
تخزين الرسائل المضافة من المستخدمين باستخدام قاعدة بيانات Replit
إذا أردت أن تسمح للمستخدمين بإضافة رسائل تشجيعية جديدة مباشرة من داخل الدردشة، فستحتاج إلى تخزين هذه البيانات في قاعدة بسيطة. هنا تأتي فائدة حزمة @replit/database.
ربط قاعدة البيانات
const Database = require("@replit/database");
const db = new Database();
تهيئة الرسائل الافتراضية
db.get("encouragements").then(encouragements => {
if (!encouragements || encouragements.length < 1) {
db.set("encouragements", starterEncouragements);
}
});
هنا يتم فحص وجود مفتاح encouragements داخل قاعدة البيانات. وإذا لم يكن موجودًا، تُخزن الرسائل الافتراضية تلقائيًا.
دوال إضافة الرسائل وحذفها
function updateEncouragements(encouragingMessage) {
db.get("encouragements").then(encouragements => {
encouragements.push([encouragingMessage]);
db.set("encouragements", encouragements);
});
}
function deleteEncouragment(index) {
db.get("encouragements").then(encouragements => {
if (encouragements.length > index) {
encouragements.splice(index, 1);
db.set("encouragements", encouragements);
}
});
}
وظيفة updateEncouragements() تضيف رسالة جديدة إلى القائمة، بينما تقوم deleteEncouragment() بحذف عنصر بناءً على رقمه.
إضافة أوامر $new و$del و$list
بعد توصيل قاعدة البيانات، يمكن تطوير البوت ليستقبل أوامر جديدة من المستخدمين.
تحديث منطق الرسائل
client.on("message", msg => {
if (msg.content === "$inspire") {
getQuote().then(quote => msg.channel.send(quote));
}
if (sadWords.some(word => msg.content.includes(word))) {
db.get("encouragements").then(encouragements => {
const encouragement = encouragements[Math.floor(Math.random() * encouragements.length)];
msg.reply(encouragement);
});
}
if (msg.content.startsWith("$new")) {
encouragingMessage = msg.content.split("$new ")[1];
updateEncouragements(encouragingMessage);
msg.channel.send("New encouraging message added.");
}
if (msg.content.startsWith("$del")) {
index = parseInt(msg.content.split("$del ")[1]);
deleteEncouragment(index);
msg.channel.send("Encouraging message deleted.");
}
}
شرح الأوامر
$new: يضيف رسالة تشجيعية جديدة بعد الأمر.$del: يحذف رسالة حسب رقم الفهرس.- يمكن استخدام
split()لاستخراج الجزء المطلوب من نص الرسالة.
مثلًا، عند كتابة $new أنت قادر على تجاوز هذه المرحلة، سيقوم البوت بحفظ هذه العبارة لاستخدامها لاحقًا.
النسخة النهائية من البوت مع التحكم في التفاعل
في النسخة النهائية سنضيف ميزتين مهمتين:
- عرض قائمة الرسائل التشجيعية بالأمر
$list. - تشغيل أو إيقاف الرد على الكلمات الحزينة بالأمر
$responding.
const Discord = require("discord.js");
const fetch = require("node-fetch");
const Database = require("@replit/database");
const db = new Database();
const client = new Discord.Client();
const sadWords = ["sad", "depressed", "unhappy", "angry", "miserable"];
const starterEncouragements = [
"Cheer up!",
"Hang in there.",
"You are a great person / bot!"
];
db.get("encouragements").then(encouragements => {
console.log(encouragements);
if (!encouragements || encouragements.length < 1) {
db.set("encouragements", starterEncouragements);
}
});
db.get("responding").then(value => {
if (value == null) {
db.set("responding", true);
}
});
function getQuote() {
return fetch("https://zenquotes.io/api/random")
.then(res => {
return res.json();
})
.then(data => {
return data[0]["q"] + " -" + data[0]["a"];
});
}
function updateEncouragements(encouragingMessage) {
db.get("encouragements").then(encouragements => {
encouragements.push([encouragingMessage]);
db.set("encouragements", encouragements);
});
}
function deleteEncouragment(index) {
db.get("encouragements").then(encouragements => {
if (encouragements.length > index) {
encouragements.splice(index, 1);
db.set("encouragements", encouragements);
}
});
}
client.on("ready", () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on("message", msg => {
if (msg.content === "$inspire") {
getQuote().then(quote => msg.channel.send(quote));
}
db.get("responding").then(responding => {
if (responding && sadWords.some(word => msg.content.includes(word))) {
db.get("encouragements").then(encouragements => {
const encouragement = encouragements[Math.floor(Math.random() * encouragements.length)];
msg.reply(encouragement);
});
}
});
if (msg.content.startsWith("$new")) {
encouragingMessage = msg.content.split("$new ")[1];
updateEncouragements(encouragingMessage);
msg.channel.send("New encouraging message added.");
}
if (msg.content.startsWith("$del")) {
index = parseInt(msg.content.split("$del ")[1]);
deleteEncouragment(index);
msg.channel.send("Encouraging message deleted.");
}
if (msg.content.startsWith("$list")) {
db.get("encouragements").then(encouragements => {
msg.channel.send(encouragements);
});
}
if (msg.content.startsWith("$responding")) {
value = msg.content.split("$responding ")[1];
if (value.toLowerCase() == "true") {
db.set("responding", true);
msg.channel.send("Responding is on.");
} else {
db.set("responding", false);
msg.channel.send("Responding is off.");
}
}
});
client.login(process.env.TOKEN);
أهمية مفتاح responding
يسمح هذا المفتاح بالتحكم في سلوك البوت دون تعديل الشيفرة. فإذا أرسل المستخدم الأمر $responding true سيُفعّل الردود، وإذا أرسل $responding false ستتوقف.
تشغيل البوت باستمرار مجانًا
من أكبر التحديات في الاستضافة المجانية أن المشروع قد يتوقف عند إغلاق التبويب أو بعد فترة من عدم النشاط. ولحل هذه المشكلة، يمكن الجمع بين خادم ويب بسيط وخدمة خارجية للمراقبة.
الطريقة الأولى: خطة مدفوعة في Replit
إذا كنت تريد حلًا مباشرًا وسهلًا، يمكنك استخدام خيار Always On ضمن الخطط المدفوعة في Replit. هذه الطريقة مناسبة لمن يريد استقرارًا أعلى دون إعدادات إضافية.

الطريقة الثانية: إنشاء خادم ويب واستخدام Uptime Robot
في الخطة المجانية، يمكن إبقاء المشروع نشطًا من خلال إنشاء خادم ويب بسيط يتلقى طلبات دورية كل عدة دقائق.
إنشاء الملف server.js
const express = require("express");
const server = express();
server.all("/", (req, res) => {
res.send("Bot is running!");
});
function keepAlive() {
server.listen(3000, () => {
console.log("Server is ready.");
});
}
module.exports = keepAlive;
استيراد الخادم داخل الملف الرئيسي
const keepAlive = require("./server");
ثم أضف الاستدعاء التالي قبل تسجيل دخول البوت:
keepAlive()
بعد التشغيل، سيظهر لك رابط خاص بخادم الويب، وهذا الرابط ستحتاج إليه في الخطوة التالية.

إعداد Uptime Robot للحفاظ على تشغيل البوت
خدمة Uptime Robot مجانية وتسمح بإرسال طلبات دورية إلى رابط الخادم، وبذلك يظل المشروع مستيقظًا ولا يدخل في وضع السكون.
خطوات الإعداد
- أنشئ حسابًا مجانيًا على
https://uptimerobot.com/. - بعد تسجيل الدخول، اضغط
Add New Monitor. - اختر نوع المراقبة
HTTP(s). - ضع اسمًا مناسبًا للخدمة.
- ألصق رابط الخادم الخاص بمشروعك.
- احفظ الإعداد بالضغط على
Create Monitor.


أفضل ممارسات لتحسين جودة البوت والمقال تقنيًا
- احفظ جميع البيانات الحساسة مثل
Tokenداخل.env. - لا تمنح البوت صلاحيات أكبر من حاجته الفعلية.
- اختبر الأوامر واحدة تلو الأخرى قبل إضافة ميزات جديدة.
- استخدم أسماء أوامر واضحة وسهلة التذكر مثل
$listو$new. - إذا كان مشروعك سيتوسع، ففكر لاحقًا في تنظيم الكود داخل ملفات متعددة.
الخلاصة التقنية
إنشاء بوت Discord باستخدام JavaScript لم يعد مهمة معقدة، خصوصًا مع وجود مكتبات مثل discord.js ومنصات سحابية مثل Replit. القيمة الحقيقية لا تكمن فقط في تشغيل البوت، بل في فهم بنية الأحداث، وإدارة الأوامر، والتعامل مع API وقواعد البيانات البسيطة. إذا أتقنت هذا النموذج، فستكون قادرًا على تطوير بوتات أكثر تقدمًا تشمل أنظمة ترحيب، وإدارة أعضاء، وردودًا ذكية، وتكاملات خارجية أوسع. من الناحية التقنية، هذا المشروع يعد نقطة انطلاق ممتازة لأي مطور يريد الدخول إلى عالم الأتمتة المجتمعية داخل Discord.