{
 "name": "وكيل الشكاوى — اسأل أبو خليفة",
 "nodes": [
  {
   "parameters": {
    "updates": [
     "messages"
    ],
    "options": {}
   },
   "id": "3231c0a6-c4c1-4da8-adac-05fe23b250d9",
   "name": "رسالة واتساب",
   "type": "n8n-nodes-base.whatsAppTrigger",
   "typeVersion": 1,
   "position": [
    -660,
    0
   ],
   "credentials": {
    "whatsAppTriggerApi": {
     "id": "",
     "name": "WhatsApp OAuth (App ID + App Secret)"
    }
   }
  },
  {
   "parameters": {
    "jsCode": "// رقم الزبون ونص رسالته من واتساب. تنبيهات (وصلت/انقرت) نتجاهلها. الصورة والصوت: الوكيل ما يشوفها، بس يعرف إن الزبون أرسلها\nconst v = $input.first().json; const m = v.messages?.[0];\nif (!m) return [];\nconst kinds = { image: 'صورة', audio: 'رسالة صوتية', video: 'فيديو', document: 'ملف', sticker: 'ملصق' };\nlet text = '';\nif (m.type === 'text') text = m.text.body;\nelse text = '[أرسل الزبون ' + (kinds[m.type] || 'شي') + ']' + (m[m.type]?.caption ? ' ' + m[m.type].caption : '');\nreturn [{ json: { customer: m.from, customer_name: v.contacts?.[0]?.profile?.name || '', text, now: '', phone_number_id: v.metadata.phone_number_id } }];"
   },
   "id": "26cc108f-1a21-4c83-b716-e3c05bfec00a",
   "name": "تجهيز الرسالة",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    -440,
    0
   ]
  },
  {
   "parameters": {
    "assignments": {
     "assignments": [
      {
       "id": "4a2670ab-29ab-47c1-b193-7bc8931d3833",
       "name": "store",
       "type": "string",
       "value": "متجر أناقة للعبايات"
      },
      {
       "id": "a1071113-5483-47f1-88fc-355dabc9cffc",
       "name": "policy",
       "type": "string",
       "value": "- الإرجاع خلال 7 أيام من الاستلام، والقطعة ما انلبست وفيها التاغ.\n- المبلغ يرجع لنفس طريقة الدفع خلال 5 إلى 10 أيام عمل بعد ما توصلنا القطعة.\n- القطعة التالفة أو الغلط: نبدلها أو نرجع المبلغ كامل والشحن علينا، بعد ما يرسل الزبون صورة.\n- التوصيل: الرياض يوم إلى يومين، وباقي المدن 3 إلى 5 أيام عمل.\n- العبايات المفصّلة بالمقاس ما ترجع إلا إذا فيها عيب."
      },
      {
       "id": "7e8ce5dc-b998-4327-8e7c-884f2040fdc5",
       "name": "staff_hours",
       "type": "string",
       "value": "9-22"
      },
      {
       "id": "f85af969-38c7-4e11-8301-b210038562d5",
       "name": "human_phone",
       "type": "string",
       "value": "0500000000"
      }
     ]
    },
    "includeOtherFields": false,
    "options": {}
   },
   "id": "1184d08e-afeb-4959-9edd-5f211c05aa4e",
   "name": "الإعدادات",
   "type": "n8n-nodes-base.set",
   "typeVersion": 3.4,
   "position": [
    -220,
    0
   ]
  },
  {
   "parameters": {
    "promptType": "define",
    "text": "={{ $('تجهيز الرسالة').first().json.text }}",
    "hasOutputParser": true,
    "options": {
     "systemMessage": "=أنت موظف خدمة العملاء في «{{ $('الإعدادات').first().json.store }}» على واتساب. الزبون غالباً زعلان. ردك بلهجة سعودية بيضاء هادية ومحترمة، 2 إلى 4 أسطر، وبالإنجليزي إذا كتب بالإنجليزي.\n\nسياسة المتجر (هذا كل اللي تعرفه، لا تخترع غيره):\n{{ $('الإعدادات').first().json.policy }}\n\nالقواعد:\n1. ابدأ بجملة وحدة فيها اعتذار أو تفهّم صادق، بدون مبالغة ولا تكرار.\n2. لا توعد بشي مو مكتوب في السياسة: لا تقول «رجعنا المبلغ» ولا «بنعوضك» ولا تعطي خصم أو كوبون. قل الخطوة الجاية حسب السياسة.\n3. إذا تحتاج معلومة (رقم الطلب، صورة القطعة، المدينة)، اطلبها بوضوح.\n4. لا تجادل ولا تلوم الزبون. إذا شتم أو هدد، رد بهدوء وركّز على حل مشكلته.\n5. لا تكتب رقم التذكرة ولا مواعيد الموظفين، النظام يضيفها تحت ردك.\n5ب. إذا الرسالة «[أرسل الزبون صورة]» أو صوت أو ملف: أنت ما تشوفه. قل إنه وصل وإن الموظف بيشوفه، وكمل الطلب إذا ناقص شي.\n6. صنّف الشكوى:\n   category: return (يبي يرجع أو يبدل) | damaged (تالف أو ناقص) | wrong_item (مقاس أو لون أو قطعة غلط) | late (تأخير أو ما وصل) | refund_status (يسأل عن فلوس إرجاع) | double_charge (انخصم مرتين أو خصم بدون طلب) | other\n   priority: urgent (فلوس انخصمت غلط، أو تهديد بشكوى رسمية أو تشهير، أو خطر على السلامة) | high (تالف، غلط، تأخير تعدّى موعد التوصيل، أو ما استلم وهو مكتوب «تم التسليم»، أو زبون معصّب جداً) | normal (إرجاع عادي، سؤال، تأخير داخل الموعد)\n7. summary: سطر واحد للموظف: وش المشكلة ووش المطلوب. order_number: رقم الطلب إذا الزبون كتبه، وإلا خلّه فاضي \"\".\n\nأعد الرد فقط بالصيغة المطلوبة (JSON)."
    }
   },
   "id": "c12b313d-e95a-4dd7-9541-071d2a363a1c",
   "name": "موظف خدمة العملاء",
   "type": "@n8n/n8n-nodes-langchain.agent",
   "typeVersion": 3.1,
   "position": [
    0,
    0
   ],
   "retryOnFail": true,
   "maxTries": 2,
   "waitBetweenTries": 1000,
   "onError": "continueErrorOutput"
  },
  {
   "parameters": {
    "model": {
     "__rl": true,
     "mode": "id",
     "value": "claude-haiku-5-5"
    },
    "options": {
     "maxTokensToSample": 1500
    }
   },
   "id": "1f4e73da-8dac-40b0-885a-34982e2c3f3f",
   "name": "Claude Haiku",
   "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
   "typeVersion": 1.6,
   "position": [
    -80,
    220
   ],
   "credentials": {
    "anthropicApi": {
     "id": "",
     "name": "Anthropic API"
    }
   }
  },
  {
   "parameters": {
    "sessionIdType": "customKey",
    "sessionKey": "={{ $('تجهيز الرسالة').first().json.customer }}",
    "contextWindowLength": 10
   },
   "id": "f5e1309d-871f-49cc-8e58-634521689f61",
   "name": "ذاكرة المحادثة",
   "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
   "typeVersion": 1.3,
   "position": [
    60,
    220
   ]
  },
  {
   "parameters": {
    "schemaType": "fromJson",
    "jsonSchemaExample": "{\"category\": \"late\", \"priority\": \"high\", \"order_number\": \"4512\", \"summary\": \"طلب 4512 متأخر أسبوع، الزبونة تبي تعرف وين الشحنة\", \"reply\": \"معك حق، أسبوع تأخير كثير ونعتذر لك. بنتابع شحنة الطلب 4512 ونرد عليك بمكانها بالضبط.\"}"
   },
   "id": "e76da92d-7cec-4d60-970c-8034a314dbad",
   "name": "صيغة الرد",
   "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
   "typeVersion": 1.3,
   "position": [
    200,
    220
   ]
  },
  {
   "parameters": {
    "jsCode": "// تذكرة + أولوية: الكود يرفع الأولوية إذا فيه كلمات خطيرة، ويضيف رقم التذكرة ومواعيد الموظفين تحت الرد\nconst ai = $input.first().json.output;\nconst m = $('تجهيز الرسالة').first().json;\nconst S = $('الإعدادات').first().json;\nconst now = m.now ? DateTime.fromISO(m.now, { zone: 'Asia/Riyadh' }) : $now.setZone('Asia/Riyadh');\n// تذكرة وحدة لكل زبون في اليوم (الرسايل اللي بعدها تنضاف لنفس الرقم)\nconst ticket = 'T' + now.toFormat('LLdd') + '-' + String(m.customer).slice(-4);\nlet priority = ['urgent', 'high', 'normal'].includes(ai.priority) ? ai.priority : 'high';\nconst t = (m.text || '').replace(/[أإآ]/g, 'ا').replace(/ة/g, 'ه').replace(/ى/g, 'ي');\nconst URGENT = [\n  /انخصم.{0,25}(مرتين|بدون)|خصمو?ا? .{0,10}مرتين|سحبتو|charged twice|double charged/i,\n  /وزاره التجاره|شكوي رسميه|بشتكي|اشتكي عليكم|محامي|lawyer|police/i,\n  /(بشهر|اشهر) (فيكم|بكم)|بفضح|افضحكم/i,\n  /(انزل|بنزل|انشر|بنشر|بحط|احط).{0,30}(سناب|تيك ?توك|تويتر|اكس|انستا)/i];\nconst ANGRY = /نصاب|نصب|حرامي|زباله|كذاب|غشاش|زفت|اسوا|تافه|حقير|scam|fraud/i;\nconst why = [];\nif (URGENT.some(r => r.test(t)) && priority !== 'urgent') { priority = 'urgent'; why.push('كلمات تستدعي الاستعجال'); }\nif (ANGRY.test(t) && priority === 'normal') { priority = 'high'; why.push('زبون معصّب'); }\n// ساعات الموظفين: \"9-22\" (وتقبل الأرقام العربية، والدوام اللي يعدّي نص الليل مثل \"18-2\")\nconst [a, b] = String(S.staff_hours).replace(/[٠-٩]/g, d => '٠١٢٣٤٥٦٧٨٩'.indexOf(d)).split('-').map(Number);\nconst open = a < b ? (now.hour >= a && now.hour < b) : (now.hour >= a || now.hour < b);\nconst en = !/[؀-ۿ]/.test(m.text || '');\nconst tail = en\n  ? (open ? `\\nYour ticket: ${ticket}. Our team will follow up shortly.` : `\\nYour ticket: ${ticket}. Our team starts at ${a}:00 and will follow up then.`)\n  : (open ? `\\nرقم تذكرتك: ${ticket}، والموظف بيتابعها.` : `\\nرقم تذكرتك: ${ticket}، والموظفين يبدون من الساعة ${a}:00 ويتابعونها.`);\nconst icon = { urgent: '🔴', high: '🟠', normal: '🟢' }[priority];\nconst PR = { urgent: 'عاجلة', high: 'مهمة', normal: 'عادية' };\nconst CAT = { return: 'إرجاع أو تبديل', damaged: 'تالف أو ناقص', wrong_item: 'قطعة غلط', late: 'تأخير', refund_status: 'يسأل عن المبلغ', double_charge: 'خصم مكرر', other: 'ثاني' };\nconst alert = `${icon} تذكرة ${ticket} (${PR[priority]})\\nالنوع: ${CAT[ai.category] || ai.category}${ai.order_number ? ' · طلب ' + ai.order_number : ''}\\n${ai.summary}\\nالزبون: ${m.customer}${why.length ? '\\n(' + why.join('، ') + ')' : ''}`;\nreturn [{ json: { ticket, priority, category: ai.category || 'other', order_number: ai.order_number || '', summary: ai.summary || '',\n  reply: (ai.reply || '').trim() + tail, customer: m.customer, text: m.text, created: now.toFormat(\"yyyy-LL-dd HH:mm\"),\n  alert_owner: priority !== 'normal', alert } }];"
   },
   "id": "032d5a38-1f1f-47ea-8801-01961ac68110",
   "name": "تذكرة وأولوية",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    240,
    0
   ]
  },
  {
   "parameters": {
    "jsCode": "// إذا الذكاء ما رد صح: رد احتياطي فيه رقم تذكرة ورقمك، وتنبيه لك\nconst m = $('تجهيز الرسالة').first().json; const S = $('الإعدادات').first().json;\nconst now = m.now ? DateTime.fromISO(m.now, { zone: 'Asia/Riyadh' }) : $now.setZone('Asia/Riyadh');\nconst ticket = 'T' + now.toFormat('LLdd') + '-' + String(m.customer).slice(-4);\nreturn [{ json: { ticket, priority: 'high', category: 'other', order_number: '', summary: 'الوكيل ما قدر يرد، راجع الرسالة بنفسك',\n  reply: 'وصلتنا رسالتك ونعتذر عن أي إزعاج. الموظف بيرد عليك بأقرب وقت، أو اتصل على ' + S.human_phone + '\\nرقم تذكرتك: ' + ticket,\n  customer: m.customer, text: m.text, created: now.toFormat('yyyy-LL-dd HH:mm'), alert_owner: true,\n  alert: '🟠 تذكرة ' + ticket + ': رسالة ما قدر الوكيل يرد عليها\\nالزبون: ' + m.customer + '\\n' + m.text } }];"
   },
   "id": "42ca9987-2de6-405a-b556-4ae8875245f5",
   "name": "رد احتياطي",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    240,
    380
   ]
  },
  {
   "parameters": {
    "operation": "append",
    "documentId": {
     "__rl": true,
     "mode": "url",
     "value": "حط هنا رابط جدول Google Sheets"
    },
    "sheetName": {
     "__rl": true,
     "mode": "list",
     "value": "gid=0",
     "cachedResultName": "Sheet1"
    },
    "columns": {
     "mappingMode": "defineBelow",
     "value": {
      "ticket": "={{ $json['ticket'] }}",
      "created": "={{ $json['created'] }}",
      "priority": "={{ $json['priority'] }}",
      "category": "={{ $json['category'] }}",
      "order_number": "={{ $json['order_number'] }}",
      "customer": "={{ $json['customer'] }}",
      "summary": "={{ $json['summary'] }}",
      "text": "={{ $json['text'] }}"
     },
     "matchingColumns": [],
     "schema": [
      {
       "id": "ticket",
       "displayName": "ticket",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "created",
       "displayName": "created",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "priority",
       "displayName": "priority",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "category",
       "displayName": "category",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "order_number",
       "displayName": "order_number",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "customer",
       "displayName": "customer",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "summary",
       "displayName": "summary",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      },
      {
       "id": "text",
       "displayName": "text",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "string",
       "canBeUsedToMatch": true
      }
     ]
    },
    "options": {}
   },
   "id": "4f68713a-94f8-422c-974e-89a048f337c8",
   "name": "سجّل التذكرة (Google Sheets)",
   "type": "n8n-nodes-base.googleSheets",
   "typeVersion": 4.7,
   "position": [
    680,
    0
   ],
   "credentials": {
    "googleSheetsOAuth2Api": {
     "id": "",
     "name": "Google Sheets"
    }
   },
   "onError": "continueRegularOutput"
  },
  {
   "parameters": {},
   "id": "114c9eef-4652-4ccd-9f91-b7f22d264999",
   "name": "التذكرة",
   "type": "n8n-nodes-base.noOp",
   "typeVersion": 1,
   "position": [
    460,
    0
   ]
  },
  {
   "parameters": {
    "resource": "message",
    "operation": "send",
    "phoneNumberId": "={{ $('تجهيز الرسالة').first().json.phone_number_id }}",
    "recipientPhoneNumber": "={{ $('تجهيز الرسالة').first().json.customer }}",
    "textBody": "={{ $('التذكرة').first().json.reply }}",
    "additionalFields": {}
   },
   "id": "dee9097c-a0e0-431a-8af6-2e80f09b73d8",
   "name": "الرد على الزبون (واتساب)",
   "type": "n8n-nodes-base.whatsApp",
   "typeVersion": 1.1,
   "position": [
    900,
    0
   ],
   "credentials": {
    "whatsAppApi": {
     "id": "",
     "name": "WhatsApp (Access token + Business Account ID)"
    }
   }
  },
  {
   "parameters": {
    "conditions": {
     "options": {
      "caseSensitive": true,
      "leftValue": "",
      "typeValidation": "loose",
      "version": 2
     },
     "conditions": [
      {
       "id": "1133f1fd-531a-4b7e-8cbd-ac2d7675b850",
       "leftValue": "={{ $('التذكرة').first().json.alert_owner }}",
       "rightValue": "",
       "operator": {
        "type": "boolean",
        "operation": "true",
        "singleValue": true
       }
      }
     ],
     "combinator": "and"
    },
    "options": {}
   },
   "id": "205e3a8d-37bc-4cce-8452-16dec1e7e682",
   "name": "مستعجلة؟",
   "type": "n8n-nodes-base.if",
   "typeVersion": 2.2,
   "position": [
    1120,
    0
   ]
  },
  {
   "parameters": {
    "chatId": "حط هنا رقم المحادثة (Chat ID)",
    "text": "={{ $('التذكرة').first().json.alert }}",
    "additionalFields": {
     "appendAttribution": false
    }
   },
   "id": "5af5dda9-38bd-45c9-838c-9edfdcea29ce",
   "name": "نبّهني على تيليقرام",
   "type": "n8n-nodes-base.telegram",
   "typeVersion": 1.2,
   "position": [
    1340,
    -60
   ],
   "credentials": {
    "telegramApi": {
     "id": "",
     "name": "Telegram bot"
    }
   }
  }
 ],
 "connections": {
  "رسالة واتساب": {
   "main": [
    [
     {
      "node": "تجهيز الرسالة",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "تجهيز الرسالة": {
   "main": [
    [
     {
      "node": "الإعدادات",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "الإعدادات": {
   "main": [
    [
     {
      "node": "موظف خدمة العملاء",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Claude Haiku": {
   "ai_languageModel": [
    [
     {
      "node": "موظف خدمة العملاء",
      "type": "ai_languageModel",
      "index": 0
     }
    ]
   ]
  },
  "ذاكرة المحادثة": {
   "ai_memory": [
    [
     {
      "node": "موظف خدمة العملاء",
      "type": "ai_memory",
      "index": 0
     }
    ]
   ]
  },
  "صيغة الرد": {
   "ai_outputParser": [
    [
     {
      "node": "موظف خدمة العملاء",
      "type": "ai_outputParser",
      "index": 0
     }
    ]
   ]
  },
  "موظف خدمة العملاء": {
   "main": [
    [
     {
      "node": "تذكرة وأولوية",
      "type": "main",
      "index": 0
     }
    ],
    [
     {
      "node": "رد احتياطي",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "تذكرة وأولوية": {
   "main": [
    [
     {
      "node": "التذكرة",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "رد احتياطي": {
   "main": [
    [
     {
      "node": "التذكرة",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "التذكرة": {
   "main": [
    [
     {
      "node": "سجّل التذكرة (Google Sheets)",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "سجّل التذكرة (Google Sheets)": {
   "main": [
    [
     {
      "node": "الرد على الزبون (واتساب)",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "الرد على الزبون (واتساب)": {
   "main": [
    [
     {
      "node": "مستعجلة؟",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "مستعجلة؟": {
   "main": [
    [
     {
      "node": "نبّهني على تيليقرام",
      "type": "main",
      "index": 0
     }
    ]
   ]
  }
 },
 "settings": {
  "executionOrder": "v1",
  "timezone": "Asia/Riyadh"
 },
 "pinData": {}
}