كيف تبني مُجيباً آلياً لرسائل Instagram DM دون أدوات طرف ثالث
13 مايو 2026
·
22 دقيقة قراءة
ما الذي ستبنيه: مُجيبٌ آلي لرسائل Instagram DM جاهز للإنتاج، يتعامل مع الرسائل النصية وردود الستوري وإشارات الستوري — مع ردود سريعة قائمة على القواعد للأسئلة الشائعة، و GPT-4o لكل ما عدا ذلك، و Redis لحالة المحادثة، وتصعيد بشري عند الحاجة. واجهة برمجة رسمية فقط. صفر منصات طرف ثالث.
لماذا تبني نظامك الخاص بدلاً من استخدام أداة طرف ثالث؟
تُعدّ ManyChat و Interakt والمنصات المشابهة الخيار الصحيح إذا كنت تحتاج شيئاً يعمل في غضون فترة بعد الظهر دون كتابة كود. وهي الخيار الخاطئ عندما تحتاج إلى:
منطق عمل مخصص. ربط بوت DM الخاص بك بأنظمتك الداخلية — قاعدة بياناتك، CRM الخاص بك، متجرك على Shopify — يتطلب كوداً. الأدوات بدون كود لا تستطيع تنفيذ تكاملات API اعتباطية.
ملكية البيانات. محادثات عملائك ملكٌ لك. على ManyChat، تعيش في قاعدة بيانات ManyChat. على نظامك الخاص، تعيش حيث تريد.
لا رسوم لكل جهة اتصال. تتقاضى ManyChat رسوماً شهرية بناءً على عدد جهات الاتصال. الكود الخاص بك ليس له تكلفة لكل جهة اتصال — فقط استضافة خادمك.
لا مخاطر منصّية. أدوات الطرف الثالث قد تغيّر الأسعار أو تُضيف حدوداً أو تُلغي ميزات. الكود الخاص بك لا يتغير إلا حين تُغيّره أنت.
المتطلبات المسبقة
حساب Instagram احترافي (Business أو Creator) مرتبط بصفحة Facebook تديرها
Node.js 18+ مُثبَّت محلياً
عنوان URL عام بـ HTTPS للتطوير المحلي — استخدم ngrok: ngrok http 3000
مفتاح OpenAI API (للخطوة 6 الخاصة بـ GPT-4o — اختياري)
اذهب إلى developers.facebook.com ← Create App ← اختر نوع Business
أضف منتج Instagram إلى تطبيقك
تحت Instagram ← Settings، اربط حساب Instagram الاحترافي الخاص بك
Graph API Explorer ← اختر تطبيقك ← اختر صفحة Facebook الخاصة بك ← أنشئ رمزاً بالصلاحيات: instagram_manage_messages + pages_messaging + pages_read_engagement
انسخ الرمز المُولَّد بوصفه IG_PAGE_TOKEN
استعلم بـ GET /me?fields=instagram_business_account — الـ instagram_business_account.id المُعاد هو IG_BUSINESS_ID الخاص بك
انسخ App Secret من Settings ← Basic بوصفه IG_APP_SECRET
App Review للإنتاج: أثناء التطوير، تعمل واجهة API مع الحسابات المُضافة بوصفها Test Users في إعدادات تطبيقك. للإنتاج (الردّ على عملاء حقيقيين لم يُضافوا مستخدمين اختباريين)، يجب عليك التقديم لـ App Review مع صلاحية instagram_manage_messages. حضّر تسجيلاً للشاشة يُظهر بوتك ووصفاً واضحاً لحالة الاستخدام. عادةً تستغرق المراجعة من 5 إلى 10 أيام عمل.
2
نقطة نهاية التحقق من webhook
Node.js + Express — webhook verification
index.js
require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const app = express();
// CRITICAL: raw body needed for HMAC verification — must come BEFORE express.json()
app.use('/webhook', express.raw({ type: 'application/json' }));
app.use(express.json());
// GET /webhook — Instagram challenge handshake (same pattern as Messenger)
app.get('/webhook', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.IG_VERIFY_TOKEN) {
console.log('✓ Instagram webhook verified');
return res.status(200).send(challenge);
}
res.sendStatus(403);
});
app.listen(process.env.PORT || 3000, () => console.log('🤖 Instagram auto-responder running'));
بعد تشغيل خادمك، اذهب إلى تطبيق Facebook الخاص بك ← Instagram ← Webhooks ← Add Callback URL. أدخل عنوان ngrok الخاص بك متبوعاً بـ /webhook ورمز التحقق الخاص بك. اشترك في: messages و messaging_referrals.
3
التحقق من توقيع HMAC-SHA256 — لا تتجاوز هذه الخطوة أبداً
كل طلب POST من Instagram يتضمن X-Hub-Signature-256. بدون التحقق منه، يستطيع أي شخص يعرف عنوان URL الخاص بك تزوير الأحداث. توفر وسيطة express.raw() (التي أعددناها في الخطوة 2) الـ Buffer الخام اللازم لحساب HMAC.
Node.js — HMAC signature middleware
verifySignature.js
const crypto = require('crypto');
functionverifyInstagramSignature(req, res, next) {
const signature = req.headers['x-hub-signature-256'];
if (!signature) return res.sendStatus(401);
const hash = crypto
.createHmac('sha256', process.env.IG_APP_SECRET)
.update(req.body) // req.body is raw Buffer from express.raw()
.digest('hex');
const expected = Buffer.from(signature.split('=')[1], 'hex');
const computed = Buffer.from(hash, 'hex');
if (expected.length !== computed.length ||
!crypto.timingSafeEqual(expected, computed)) {
return res.sendStatus(401);
}
// Parse body after verification — it was raw Buffer before
req.body = JSON.parse(req.body.toString('utf8'));
next();
}
module.exports = { verifyInstagramSignature };
4
معالج أحداث webhook الكامل عبر POST
Node.js — full Instagram event handler
webhookHandler.js
const { verifyInstagramSignature } = require('./verifySignature');
const { routeMessage } = require('./autoResponder');
app.post('/webhook', verifyInstagramSignature, async (req, res) => {
res.sendStatus(200); // respond immediately — always before processingconst body = req.body;
if (body.object !== 'instagram') return; // critical check — not "page" like Messengerfor (const entry of body.entry) {
for (const event of (entry.messaging || [])) {
const igsid = event.sender.id; // Instagram-Scoped ID — store as stringtry {
// Route by event typeif (event.message?.reply_to?.story) {
// User replied to your story via DMawaitrouteMessage(igsid, event.message.text, 'story_reply', event.message.reply_to.story);
} else if (event.referral?.source === 'STORY_MENTION') {
// User mentioned your account in their storyawaitrouteMessage(igsid, null, 'story_mention', event.referral.story);
} else if (event.message?.text) {
// Standard text DMawaitrouteMessage(igsid, event.message.text, 'text');
} else if (event.message?.attachments) {
// Image, video, audio, or sticker sent in DMawaitrouteMessage(igsid, null, 'media', event.message.attachments[0]);
} else if (event.reaction) {
// Emoji reaction — usually no response needed
console.log(`Reaction from ${igsid}: ${event.reaction.reaction} (${event.reaction.action})`);
}
} catch (err) {
console.error(`Error handling event for ${igsid}:`, err.message);
}
}
}
});
5
محرّك المُجيب الآلي القائم على القواعد
ردود سريعة وحتمية للأسئلة الشائعة — بلا تكلفة استدلال للذكاء الاصطناعي، وردّ فوري. أنماط Regex تطابق نوايا محددة؛ يرتبط كل نمط بردّ مُعدّ سلفاً. GPT-4o يتولى كل ما لا يطابق.
Node.js — rule-based responder + GPT-4o fallback
autoResponder.js
const RULES = [
{
patterns: [/^(hi|hello|hey|hola|yo|sup)[\s!]*$/i, /^good (morning|afternoon|evening)/i],
response: "Hey! 👋 Thanks for reaching out. How can we help you today?",
},
{
patterns: [/price|how much|cost|pricing/i],
response: "Great question! Our pricing is on our website 💛\nCheck it out: https://yoursite.com/pricing\n\nAny other questions?",
},
{
patterns: [/ship.{0,20}(to|time|free|worldwide)/i, /delivery|deliver/i],
response: "We ship worldwide! 🌍\n• USA: 3–5 business days\n• International: 7–14 days\nFree shipping on orders over $50 🎉",
},
{
patterns: [/return|refund|exchange/i],
response: "We accept returns within 30 days of delivery 📦\nItems must be unused and in original packaging.\nStart here: https://yoursite.com/returns",
},
{
patterns: [/hours|open|when.{0,10}(open|close|available)/i],
response: "Our support team is available Mon–Fri, 9am–6pm EST 🕐\nFor urgent questions, reply here and we'll get back to you ASAP!",
},
{
patterns: [/(human|person|agent|someone|representative)/i, /talk to.{0,10}(human|person|real)/i],
response: "HANDOFF", // special signal — triggers escalation flow
},
];
functionmatchRule(text) {
if (!text) returnnull;
for (const rule of RULES) {
if (rule.patterns.some(p => p.test(text))) {
return rule.response;
}
}
returnnull; // no rule matched → use GPT-4o
}
module.exports = { matchRule, RULES };
6
GPT-4o للردود الذكية
Node.js — GPT-4o integration with typing indicator
aiReply.js
const OpenAI = require('openai');
const oai = newOpenAI({ apiKey: process.env.OPENAI_API_KEY });
const IG_SYSTEM_PROMPT = `You are a helpful assistant for @yourbrand on Instagram.
Answer questions about our products, services, shipping, and returns.
Keep replies SHORT — Instagram DM users are on mobile. Max 150 words.
Use plain text (no markdown). Warm, friendly, conversational tone.
If you're unsure or the question is complex, say: [HANDOFF]
Never make up information about products you don't know.`;
async functiongetAIReply(igsid, userMessage, conversationHistory) {
// Send typing indicator first — makes the bot feel humanawaitsendTypingIndicator(igsid);
const messages = [
{ role: 'system', content: IG_SYSTEM_PROMPT },
...conversationHistory.slice(-8), // last 8 exchanges for context
{ role: 'user', content: userMessage },
];
const completion = await oai.chat.completions.create({
model: 'gpt-4o',
messages,
max_tokens: 200,
temperature: 0.7,
});
return completion.choices[0].message.content;
}
async functionsendTypingIndicator(igsid) {
awaitfetch(
`https://graph.facebook.com/v21.0/${process.env.IG_BUSINESS_ID}/messages?access_token=${process.env.IG_PAGE_TOKEN}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ recipient: { id: igsid }, sender_action: 'typing_on' }),
}
);
}
module.exports = { getAIReply, sendTypingIndicator };
const { matchRule } = require('./rules');
const { getAIReply, sendTypingIndicator } = require('./aiReply');
const { getConversation, addToHistory, setState } = require('./conversationState');
constGRAPH = 'https://graph.facebook.com/v21.0';
constBIZ_ID = process.env.IG_BUSINESS_ID;
constTOKEN = process.env.IG_PAGE_TOKEN;
async functionsendDM(igsid, text) {
const res = awaitfetch(`${GRAPH}/${BIZ_ID}/messages?access_token=${TOKEN}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ recipient: { id: igsid }, message: { text } }),
});
if (!res.ok) console.error('Send failed:', await res.json());
}
async functionrouteMessage(igsid, text, eventType, extra) {
const conv = awaitgetConversation(igsid);
// If user is in HANDOFF state, don't auto-reply — human handles itif (conv.state === 'HANDOFF') {
console.log(`User ${igsid} is in HANDOFF — skipping auto-reply`);
return;
}
// Story mention — thank them automaticallyif (eventType === 'story_mention') {
awaitsendDM(igsid, "We saw you mentioned us — thank you so much! 🙏✨ We might repost it. Cool with you? Just reply 'YES' 😊");
return;
}
// Story reply — acknowledge the story contextif (eventType === 'story_reply' && !text) {
awaitsendDM(igsid, "Hey! Thanks for reacting to our story 😍 Anything you'd like to know?");
return;
}
// Media sent (image/video) — acknowledgeif (eventType === 'media') {
awaitsendDM(igsid, "Thanks for sharing! 📸 How can we help you today?");
return;
}
// Save inbound message to historyawaitaddToHistory(igsid, 'user', text);
// 1. Try rule match first (fast, free)const ruleResponse = matchRule(text);
if (ruleResponse === 'HANDOFF') {
awaithandleEscalation(igsid);
return;
}
if (ruleResponse) {
awaitsendDM(igsid, ruleResponse);
awaitaddToHistory(igsid, 'assistant', ruleResponse);
return;
}
// 2. Fall back to GPT-4o for anything elseconst updatedConv = awaitgetConversation(igsid);
const aiReply = awaitgetAIReply(igsid, text, updatedConv.history);
if (aiReply.includes('[HANDOFF]')) {
awaithandleEscalation(igsid);
return;
}
awaitsendDM(igsid, aiReply);
awaitaddToHistory(igsid, 'assistant', aiReply);
}
module.exports = { routeMessage, sendDM };
9
التصعيد البشري: عندما لا يستطيع البوت المساعدة
Node.js — human escalation with Slack notification
escalation.js
const { sendDM } = require('./autoResponder');
const { setState } = require('./conversationState');
async functionhandleEscalation(igsid) {
// Tell the user a human is on the wayawaitsendDM(igsid,
"Let me connect you with a team member who can help! 👋\n" +
"Someone will reply to you within 1 business hour during Mon–Fri 9am–6pm EST.\n" +
"Anything else I can answer in the meantime? 😊"
);
// Set conversation state to HANDOFF — bot stops auto-replyingawaitsetState(igsid, 'HANDOFF');
// Notify your team via Slackif (process.env.SLACK_WEBHOOK_URL) {
awaitfetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `🔔 Instagram DM escalation needed\nIGSID: ${igsid}\nCheck Instagram DMs to respond.`
}),
});
}
console.log(`Escalated IGSID ${igsid} to human agent`);
}
module.exports = { handleEscalation };
10
SocialHook: تخطَّ الخطوات من 2 إلى 4 بالكامل
الخطوات 2 و 3 و 4 — التحقق من webhook، التحقق من توقيع HMAC، تحليل الحمولة الخام — هي بنية تحتية مكررة يُعيد بناءها كل تكامل Instagram من الصفر. تتولى SocialHook كل ذلك. اربط حساب Instagram الخاص بك وسيصل كل حدث إلى نقطة نهايتك مُتحقَّقاً منه ومُحلَّلاً مسبقاً. تتصل دالة routeMessage() من الخطوات 5 إلى 9 مباشرةً:
Node.js — minimal handler with SocialHook (no boilerplate)
minimal.js
// SocialHook version: no HMAC, no raw body, no object:instagram check// Every event arrives pre-verified and pre-classified
app.post('/webhook', express.json(), async (req, res) => {
res.sendStatus(200);
const { event, from: igsid, message, story } = req.body;
// event: "message.received" | "story.reply" | "story.mention"// message.body: the text content// story.storage_url: already downloaded (CDN expiry handled)if (event === 'message.received') {
awaitrouteMessage(igsid, message.body, 'text');
} else if (event === 'story.reply') {
awaitrouteMessage(igsid, message.body, 'story_reply', story);
} else if (event === 'story.mention') {
awaitrouteMessage(igsid, null, 'story_mention', story);
}
});
// Total handler: 12 lines vs 80+ lines of raw webhook parsing and verification
FAQ
أسئلة شائعة
هل بناء مُجيب آلي لـ Instagram دون ManyChat يخالف شروط خدمة Instagram؟
لا — البناء باستخدام واجهة Meta Instagram Messaging API الرسمية متوافق تماماً. ما يخالف الشروط: أتمتة المتصفح، وكشط رموز الجلسة، وواجهات الكشط غير الرسمية، والأدوات التي تحاكي مستخدماً مسجَّلاً للدخول لإرسال رسائل DM على نطاق واسع. واجهة Messaging API الرسمية هي الطريقة التي صمّمتها واعتمدتها Meta لكي تؤتمت الشركات رسائل Instagram DM. وهي تتطلب حساب Instagram احترافي، و App Review، ومنح صلاحيات صريحة.
لماذا يجب استخدام express.raw() بدلاً من express.json() للتحقق من HMAC؟
يتطلب التحقق من توقيع HMAC-SHA256 البايتات الخام لجسم الطلب — تماماً كما تم استلامها عبر الشبكة. express.json() يحلل الجسم إلى كائن JavaScript، الذي لم يعد قادراً على إنتاج البايتات الأصلية لحساب HMAC. express.raw({ type: 'application/json' }) يمنحك Buffer بالجسم الخام. احسب HMAC على ذلك Buffer، تحقق منه مقابل ترويسة التوقيع، ثم حلّله يدوياً بـ JSON.parse(req.body.toString()).
كيف أتعامل مع ردود ستوري Instagram بشكل مختلف عن رسائل DM العادية؟
تحقق من event.message.reply_to.story في معالج webhook الخاص بك. عند وجوده، يكون حدث الرسالة ردَّ ستوري — رد المستخدم على ستوري عبر DM. نص الرد يوجد في event.message.text. عنوان URL لوسائط الستوري يوجد في event.message.reply_to.story.url (نزّله فوراً — عناوين CDN تنتهي صلاحيتها في غضون ساعات). يستطيع المُجيب الآلي الإقرار بسياق الستوري بشكل مختلف عن رسالة DM البدئية.
كيف أوقف الردود التلقائية حين يتولى إنسان محادثةً ما؟
استخدم حالة Redis لكل IGSID. عند تشغيل التصعيد (يطلب المستخدم إنساناً، أو يُدرج الذكاء الاصطناعي [HANDOFF])، اضبط حالة المحادثة على "HANDOFF" في Redis. في أعلى دالة routeMessage() الخاصة بك، تحقق من هذه الحالة — إذا كانت "HANDOFF"، فتجاوز الرد التلقائي تماماً وعُد. يردّ موظفك البشري يدوياً عبر تطبيق Instagram. أعد ضبط الحالة إلى "ACTIVE" حين يضع البشري علامة على أن المحادثة قد حُسمت، أو تلقائياً بعد انتهاء صلاحية TTL في Redis.
كيف أتجنب الردود التلقائية المكررة للرسالة نفسها؟
قد تُعيد Instagram محاولة تسليم webhook إذا كان خادمك بطيئاً أو أعاد حالة غير 200. استخدم معرّف الرسالة (event.message.mid) مفتاحاً لإزالة التكرار — خزّنه في Redis بـ TTL قصير (5 دقائق) عند معالجته لأول مرة. في بداية معالجك، تحقق مما إذا كان الـ mid موجوداً بالفعل في Redis: إن كان كذلك، فتجاوز المعالجة وأعد 200. هذا يمنع الردود المكررة الناتجة عن إعادات webhook.
هل أحتاج إلى خادم منفصل لكل حساب Instagram؟
لا — خادم واحد يتعامل مع عدة حسابات Instagram. تصل أحداث الحسابات المختلفة إلى عنوان webhook نفسه، مُميَّزةً بـ entry[0].id (معرّف Instagram Business Account). يبحث معالجك عن Page Access Token الصحيح وسياق العميل من قاعدة بيانات باستخدام ذلك المعرّف. راجع دليل webhook متعدد الحسابات للمعمارية الكاملة للتوجيه.
أنت تكتب قواعد العمل. ونحن نتولى طبقة الـ webhook.
اربط حساب Instagram الخاص بك بـ SocialHook. كل DM، وكل ردّ ستوري، وكل إشارة ستوري تصل إلى نقطة نهايتك متحقَّقاً منها وموحَّدة الشكل. محرّك القواعد الخاص بك، وتكامل GPT-4o، وآلة حالة Redis تتصل مباشرةً. 12 سطراً بدلاً من 80+.