مُجيب آلي على رسائل Instagram DM مبنيٌّ دون أدوات طرف ثالث — إعداد webhook، التحقق من HMAC، التوجيه القائم على القواعد، ردود الذكاء الاصطناعي بـ GPT-4o، آلة حالة المحادثة بـ Redis
ما الذي ستبنيه: مُجيبٌ آلي لرسائل 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 — اختياري)
  • Redis (للخطوة 7 الخاصة بحالة المحادثة — اختياري)
Shell — project setup
mkdir instagram-auto-responder && cd instagram-auto-responder npm init -y npm install express dotenv openai ioredis cat > .env << EOF IG_VERIFY_TOKEN=your_verify_secret_here IG_APP_SECRET=your_app_secret_here IG_PAGE_TOKEN=your_page_access_token_here IG_BUSINESS_ID=your_instagram_business_account_id OPENAI_API_KEY=your_openai_key_here REDIS_URL=redis://localhost:6379 PORT=3000 EOF
إعداد تطبيق Facebook وربط Instagram
  1. اذهب إلى developers.facebook.com ← Create App ← اختر نوع Business
  2. أضف منتج Instagram إلى تطبيقك
  3. تحت Instagram ← Settings، اربط حساب Instagram الاحترافي الخاص بك
  4. Graph API Explorer ← اختر تطبيقك ← اختر صفحة Facebook الخاصة بك ← أنشئ رمزاً بالصلاحيات: instagram_manage_messages + pages_messaging + pages_read_engagement
  5. انسخ الرمز المُولَّد بوصفه IG_PAGE_TOKEN
  6. استعلم بـ GET /me?fields=instagram_business_account — الـ instagram_business_account.id المُعاد هو IG_BUSINESS_ID الخاص بك
  7. انسخ App Secret من Settings ← Basic بوصفه IG_APP_SECRET
App Review للإنتاج: أثناء التطوير، تعمل واجهة API مع الحسابات المُضافة بوصفها Test Users في إعدادات تطبيقك. للإنتاج (الردّ على عملاء حقيقيين لم يُضافوا مستخدمين اختباريين)، يجب عليك التقديم لـ App Review مع صلاحية instagram_manage_messages. حضّر تسجيلاً للشاشة يُظهر بوتك ووصفاً واضحاً لحالة الاستخدام. عادةً تستغرق المراجعة من 5 إلى 10 أيام عمل.
نقطة نهاية التحقق من 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.

التحقق من توقيع 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'); function verifyInstagramSignature(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 };
معالج أحداث 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 processing const body = req.body; if (body.object !== 'instagram') return; // critical check — not "page" like Messenger for (const entry of body.entry) { for (const event of (entry.messaging || [])) { const igsid = event.sender.id; // Instagram-Scoped ID — store as string try { // Route by event type if (event.message?.reply_to?.story) { // User replied to your story via DM await routeMessage(igsid, event.message.text, 'story_reply', event.message.reply_to.story); } else if (event.referral?.source === 'STORY_MENTION') { // User mentioned your account in their story await routeMessage(igsid, null, 'story_mention', event.referral.story); } else if (event.message?.text) { // Standard text DM await routeMessage(igsid, event.message.text, 'text'); } else if (event.message?.attachments) { // Image, video, audio, or sticker sent in DM await routeMessage(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); } } } });
محرّك المُجيب الآلي القائم على القواعد

ردود سريعة وحتمية للأسئلة الشائعة — بلا تكلفة استدلال للذكاء الاصطناعي، وردّ فوري. أنماط 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 }, ]; function matchRule(text) { if (!text) return null; for (const rule of RULES) { if (rule.patterns.some(p => p.test(text))) { return rule.response; } } return null; // no rule matched → use GPT-4o } module.exports = { matchRule, RULES };
GPT-4o للردود الذكية
Node.js — GPT-4o integration with typing indicator
aiReply.js
const OpenAI = require('openai'); const oai = new OpenAI({ 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 function getAIReply(igsid, userMessage, conversationHistory) { // Send typing indicator first — makes the bot feel human await sendTypingIndicator(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 function sendTypingIndicator(igsid) { await fetch( `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 };
آلة حالة المحادثة بـ Redis
Node.js + Redis — conversation state per IGSID
conversationState.js
const { Redis } = require('ioredis'); const redis = new Redis(process.env.REDIS_URL); const TTL = 3600; // 1 hour — reset state if user goes quiet async function getConversation(igsid) { const data = await redis.get(`ig:conv:${igsid}`); return data ? JSON.parse(data) : { state: 'ACTIVE', history: [] }; } async function saveConversation(igsid, conversation) { await redis.setex(`ig:conv:${igsid}`, TTL, JSON.stringify(conversation)); } async function addToHistory(igsid, role, content) { const conv = await getConversation(igsid); conv.history.push({ role, content, ts: Date.now() }); // Keep last 20 messages to prevent unbounded growth if (conv.history.length > 20) conv.history = conv.history.slice(-20); await saveConversation(igsid, conv); return conv; } async function setState(igsid, state) { const conv = await getConversation(igsid); conv.state = state; await saveConversation(igsid, conv); } module.exports = { getConversation, saveConversation, addToHistory, setState };
Instagram Send API ودالة routeMessage الكاملة
Node.js — complete routeMessage function
autoResponder.js
const { matchRule } = require('./rules'); const { getAIReply, sendTypingIndicator } = require('./aiReply'); const { getConversation, addToHistory, setState } = require('./conversationState'); const GRAPH = 'https://graph.facebook.com/v21.0'; const BIZ_ID = process.env.IG_BUSINESS_ID; const TOKEN = process.env.IG_PAGE_TOKEN; async function sendDM(igsid, text) { const res = await fetch(`${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 function routeMessage(igsid, text, eventType, extra) { const conv = await getConversation(igsid); // If user is in HANDOFF state, don't auto-reply — human handles it if (conv.state === 'HANDOFF') { console.log(`User ${igsid} is in HANDOFF — skipping auto-reply`); return; } // Story mention — thank them automatically if (eventType === 'story_mention') { await sendDM(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 context if (eventType === 'story_reply' && !text) { await sendDM(igsid, "Hey! Thanks for reacting to our story 😍 Anything you'd like to know?"); return; } // Media sent (image/video) — acknowledge if (eventType === 'media') { await sendDM(igsid, "Thanks for sharing! 📸 How can we help you today?"); return; } // Save inbound message to history await addToHistory(igsid, 'user', text); // 1. Try rule match first (fast, free) const ruleResponse = matchRule(text); if (ruleResponse === 'HANDOFF') { await handleEscalation(igsid); return; } if (ruleResponse) { await sendDM(igsid, ruleResponse); await addToHistory(igsid, 'assistant', ruleResponse); return; } // 2. Fall back to GPT-4o for anything else const updatedConv = await getConversation(igsid); const aiReply = await getAIReply(igsid, text, updatedConv.history); if (aiReply.includes('[HANDOFF]')) { await handleEscalation(igsid); return; } await sendDM(igsid, aiReply); await addToHistory(igsid, 'assistant', aiReply); } module.exports = { routeMessage, sendDM };
التصعيد البشري: عندما لا يستطيع البوت المساعدة
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') { await routeMessage(igsid, message.body, 'text'); } else if (event === 'story.reply') { await routeMessage(igsid, message.body, 'story_reply', story); } else if (event === 'story.mention') { await routeMessage(igsid, null, 'story_mention', story); } }); // Total handler: 12 lines vs 80+ lines of raw webhook parsing and verification

أسئلة شائعة

هل بناء مُجيب آلي لـ 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+.

لا تحتاج بطاقة ائتمان · 50$ شهرياً بعد التجربة · Instagram + Messenger + WhatsApp