# 🔀 نظام التحويل إلى Extensions الداخلية

نظام يسمح للمساعدة الذكية (Gemini) بتحويل المكالمة الحية إلى موظف حقيقي عبر extension داخلي في Zadarma، تلقائياً وبأمان.

---

## 🎯 السيناريو المستهدف

> المتصل: *"je voudrais parler au manager"*
>
> 🤖 المساعدة: *"Je vous transfère au manager, un instant s'il vous plaît."*
>
> 📞 الخط ينتقل تلقائياً إلى **Extension 104** (مدير Bobex على جواله/سطح المكتب).

---

## 🗂️ Extensions المتاحة

| Extension | الدور | كيف تُستدعى |
|---|---|---|
| **101** | Technicien (دعم تقني) | *"je veux un technicien"*, *"un problème technique"* |
| **102** | Support client | *"j'ai une question sur mon dossier"* |
| **103** | Commercial | *"je veux un devis"*, *"vos tarifs"* |
| **104** | **Manager / Directeur** | *"le manager"*, *"le directeur"*, *"le responsable"* |

> 📌 يمكن تعديل الخرائط/الأدوار في `src/bridge/prompts.ts` (قسم *TRANSFERT VERS UNE EXTENSION INTERNE*) ولا حاجة لتعديل الكود.

---

## 🏗️ كيف يعمل تقنياً

```
┌─────────┐     ┌──────────┐     ┌────────┐     ┌──────────┐     ┌────────┐
│ Caller  │────▶│ Zadarma  │────▶│Asterisk│────▶│  Bridge  │────▶│ Gemini │
└─────────┘     └──────────┘     │ (sip-  │     │(sip-     │     │  Live  │
                                 │asterisk)│◀───│ bridge)  │◀────┤  API   │
                                 └────┬───┘     └────┬─────┘     └────────┘
                                      │              │
                                      │  AMI         │
                                      │  Redirect    │ toolCall
                                      ▼              │ "transfer_to_extension"
                                ┌──────────┐         │
                                │ Manager  │         │
                                │ ext 104  │◀────────┘
                                └──────────┘
```

### الخطوات بالتفصيل

1. **Asterisk dialplan** يلتقط القناة الحية (`${CHANNEL}`) ويرسلها للـ Bridge عبر `POST /api/calls/register`
2. **Bridge** يخزّن `channel` في الـ `ActiveCall` مع UUID
3. **Gemini** عند سماع طلب التحويل، يستدعي function tool: `transfer_to_extension(extension="104", role="manager")`
4. **Bridge** يستقبل toolCall ويردّ على Gemini فوراً بالنجاح (لتنطق إعلان التحويل)
5. **Bridge** ينتظر ~2.2 ثانية (وقت الإعلان) ثم يتصل بـ **Asterisk Manager Interface (AMI)** على `asterisk:5038`
6. **AMI Redirect** يحوّل القناة من `AudioSocket(bridge:9092)` إلى `[internal-extensions] exten 104`
7. **Asterisk dialplan** ينفّذ `Dial(PJSIP/565160-104@565160-100,30)` → الـ extension 104 يبدأ يرن
8. **المستخدم 104** (المدير) يرد → المكالمة مرتبطة بالعميل مباشرة

---

## 📁 الملفات المعنية

| الملف | الدور |
|---|---|
| `asterisk/manager.conf` | تفعيل AMI على المنفذ 5038 + user `bridge` |
| `asterisk/extensions-fixed.conf` | السياق `[internal-extensions]` لكل extension + التقاط `${CHANNEL}` |
| `docker-compose.yml` | bind-mount لـ `manager.conf` + متغيرات `AMI_*` |
| `src/bridge/ami-client.ts` | TCP client لـ AMI (login + Redirect + reconnect) |
| `src/bridge/gemini-bridge.ts` | إعلان `functionDeclarations` + توليد حدث `toolCall` |
| `src/bridge/index.ts` | `handleTransferToolCall()` + التقاط `channel` من API |
| `src/bridge/prompts.ts` | قواعد متى/كيف يستخدم Gemini الـ tool |

---

## ⚙️ متغيرات البيئة

```env
AMI_HOST=asterisk
AMI_PORT=5038
AMI_USER=bridge
AMI_PASSWORD=bridge_ami_pw_change_me   # ⚠️ غيّرها قبل الـ production
```

> 💡 منفذ 5038 **ليس** مفتوحاً على الإنترنت — `expose: ["5038"]` يفتحه فقط على شبكة `sip-network` الداخلية بين containers.

---

## 🔒 الأمان

- ✅ **Whitelist صارمة**: Bridge يقبل فقط `101`, `102`, `103`, `104` (في `ALLOWED_TRANSFER_EXTENSIONS`). أي محاولة من Gemini لتحويل لرقم خارجي تُرفض فوراً.
- ✅ **القناة محفوظة بالـ UUID**: لا يمكن لـ Gemini تحويل قناة عميل آخر — `channel` مرتبط بـ `call.id` الحالي فقط.
- ✅ **AMI داخلي فقط**: المنفذ غير متاح خارج Docker network.
- ✅ **بيانات Postgres لا تُمرَّر**: Gemini لا تعرف أرقام extensions العملاء الآخرين.

---

## 🧪 الاختبار

### يدوياً عبر Asterisk CLI

```bash
ssh root@51.68.45.142 -p 58293
docker exec -it sip-asterisk asterisk -rvvv
# في CLI:
manager show users
manager show connected
core show channels
```

### اختبار AMI من Bridge container

```bash
docker exec -it sip-bridge sh -c "echo -e 'Action: Login\r\nUsername: bridge\r\nSecret: bridge_ami_pw_change_me\r\n\r\n' | nc asterisk 5038"
```

يجب أن يعود `Response: Success`.

### اختبار حي

1. اتصل على `+3228860449`
2. قل: *"je voudrais parler au manager"*
3. ستسمع: *"Je vous transfère au manager, un instant s'il vous plaît."*
4. بعد ثانيتين، الـ extension 104 (هاتف المدير) يرن
5. عندما يرد، أنت متصل به مباشرة (Gemini خرج من الخط)

### مراقبة Logs أثناء الاختبار

```bash
ssh root@51.68.45.142 -p 58293 \
  "docker logs -f sip-bridge | grep -iE 'tool|transfer|ami|redirect'"
```

يجب رؤية تسلسل مثل:
```
[Bridge] Tool call: transfer_to_extension {"extension":"104","role":"manager"} (call=...)
[Bridge] AMI Redirect issued: PJSIP/565160-100-0000000a -> internal-extensions,104
```

---

## 🔧 إعداد Extensions على لوحة Zadarma

1. اذهب إلى https://my.zadarma.com/mypbx/
2. تحت **Standard virtuel** → **Réglage des numéros internes**
3. تأكد أن extensions `101..104` موجودة. لو غير موجودة:
   - اضغط *Créer le numéro du standard téléphonique virtuel*
   - اربط كل extension بالجهاز/الجوال المطلوب
4. **Extension 104 (المدير)**: حالياً مرتبط بـ Android (مرئي بالصورة)
   - افتح تطبيق Zadarma على هاتف المدير
   - سجل بحساب `565160-104` (كلمة المرور من لوحة Zadarma)
   - مكالمات Bobex التي يتم تحويلها للـ 104 ستظهر كرنين في التطبيق

---

## ➕ إضافة Extension جديدة (مثلاً 105)

1. **Asterisk dialplan** (`asterisk/extensions-fixed.conf`):
   ```
   exten => 105,1,NoOp(Internal transfer to extension 105)
    same => n,Dial(PJSIP/565160-105@565160-100,30)
    same => n,Hangup()
   ```

2. **Bridge whitelist** (`src/bridge/index.ts`):
   ```ts
   const ALLOWED_TRANSFER_EXTENSIONS = new Set(["101", "102", "103", "104", "105"]);
   ```

3. **Gemini tool enum** (`src/bridge/gemini-bridge.ts`):
   ```ts
   extension: { enum: ["101", "102", "103", "104", "105"], ... }
   ```

4. **System prompt** (`src/bridge/prompts.ts`): أضف وصف الدور
5. أعد النشر: `git push` → auto-deploy

---

## 🚨 استكشاف الأخطاء

| العَرَض | السبب المحتمل | الإصلاح |
|---|---|---|
| Gemini تقول "transfert en cours" لكن لا شيء يحدث | AMI غير متصل | افحص `docker logs sip-bridge | grep AMI` |
| `AMI Login failed` | كلمة مرور خاطئة | راجع `AMI_PASSWORD` في `.env` ومطابقتها في `manager.conf` |
| `Transfert impossible: canal d'appel non identifié` | dialplan لا يرسل `channel` | تحقق من بند `--data-urlencode "channel=${CHANNEL}"` |
| القناة تُحوَّل لكن الـ extension 104 لا يرن | الجهاز offline / غير مسجل | افتح Zadarma app على هاتف المدير |
| `Redirect failed` | اسم قناة خاطئ | احصل على القناة الصحيحة: `docker exec sip-asterisk asterisk -rx "core show channels"` |

---

## 📊 السجلات في Postgres

كل عملية تحويل ستظهر في:
- جدول `calls` — `status` يبقى `active` أثناء الـ leg الجديد (محادثة العميل مع 104)
- جدول `transcripts` — يحتوي على الإعلان الذي قالته Gemini قبل التحويل
- بعد انتهاء المكالمة كاملة: `status=completed` مع `duration_seconds` الإجمالي

> 💡 الجزء الذي يقع بعد التحويل (العميل ↔ المدير) **لا يُسجَّل في Postgres** لأن Gemini خرج من الخط. لتسجيل هذا الجزء أيضاً، يلزم إعداد `MixMonitor` في Asterisk قبل التحويل (قابل للإضافة لاحقاً).
