# Дигитална Планета — локален AI асистент за Facebook Messenger

Desktop приложение (Windows), което работи с **вече отворен и логнат Messenger** в
Chrome/Edge и помага на оператора да отговаря на входящи съобщения от кандидати
за ваучерни обучения по дигитални умения.

Основният workflow:

```
Messenger → прочитане на последното входящо → AI анализ → генериран отговор
          → поставяне в полето за писане → ОПЕРАТОРЪТ натиска Enter
```

> **В първата версия (MVP) НЯМА автоматично изпращане.** Приложението поставя
> текста в Messenger и спира. Enter натиска човекът (ТЗ §1, §26, §33).

---

## 1. Състояние и acceptance criteria (ТЗ §32)

| Критерий | Статус | Къде е реализиран |
|---|---|---|
| стартира на Windows | ✅ | `app/main.py`, `BUILD-EXE.bat` |
| открива Messenger | ✅ | `app/browser/browser_session.py` (CDP / persistent) |
| разпознава активния чат | ✅ | `MessengerController.detect_active_chat` |
| прочита последното входящо съобщение | ✅ | `app/browser/message_reader.py` |
| не обработва същото съобщение два пъти | ✅ | `message_fingerprint` + `add_incoming_once` |
| AI генерира отговор | ✅ | `app/ai/ai_service.py` + `providers.py` |
| отговорът се показва в desktop UI | ✅ | `app/ui/main_window.py`, `conversation_panel.py` |
| операторът може да го редактира | ✅ | `ReplyPanel.reply_edit` |
| „Постави в Messenger“ го поставя в правилния чат | ✅ | `insert_reply` + `assert_chat_unchanged` |
| НЕ се изпраща автоматично | ✅ | `send_reply` изисква SEMI_AUTO + флаг + whitelist |
| има локална история | ✅ | SQLite + `AIReplyRepository`, `logs/dialogs.log` |
| може да се маркира „Human takeover“ | ✅ | `Conversation.human_takeover` + бутон в UI |

Проверки: **83 автоматични теста** (`pytest`, от които 1 с реален headless
Chromium върху „Messenger-подобен“ DOM) + офлайн self-check
(`python -m app.main --selftest`).

---

## 2. Технологичен стек

* **Python 3.12+**
* **Playwright** (единствен инструмент за browser automation — без Selenium)
* **PySide6** (desktop UI)
* **SQLAlchemy 2.0 + SQLite** (лесно преминаване към PostgreSQL)
* **pydantic / pydantic-settings / python-dotenv** (конфигурация)
* **httpx + openai** (AI provider-и), **cryptography** (+ `keyring` по избор) за ключове
* **FastAPI + uvicorn** — опционален локален status API (`--api`)

---

## 3. Структура на проекта

```
facebook-messenger-ai/
├── app/
│   ├── main.py                     # входна точка: UI, --selftest, --api
│   ├── browser/
│   │   ├── browser_session.py      # CDP свързване / persistent профил
│   │   ├── messenger_controller.py # READ / INSERT / SEND + защита срещу сменен чат
│   │   ├── message_reader.py       # четене на съобщенията от DOM
│   │   ├── selectors.py            # fallback селектори (role/aria/contenteditable)
│   │   └── types.py                # ChatMessage, ChatIdentity
│   ├── ai/
│   │   ├── ai_service.py           # оркестрация: класификация → safety → AI → контрол
│   │   ├── providers.py            # AIProvider, OpenAI, DeepSeek, Anthropic, Mock
│   │   ├── prompt_builder.py       # system prompt (ТЗ §13) + вход (ТЗ §12)
│   │   ├── intent_classifier.py    # Intent enum + правила на български (ТЗ §11)
│   │   └── safety_rules.py         # whitelist, human takeover, забранени обещания
│   ├── knowledge/
│   │   ├── knowledge_service.py    # четене/търсене/редакция + sync към DB
│   │   ├── knowledge.json          # фактите (единствен източник на истина)
│   │   └── faq.json                # въпроси/отговори с intent и keywords
│   ├── database/
│   │   ├── db.py                   # engine, session_scope, PRAGMA-и
│   │   ├── models.py               # conversations / messages / ai_replies / knowledge_items
│   │   └── repositories.py         # целият SQL + de-dup логиката
│   ├── services/
│   │   ├── conversation_service.py # разговори, кандидатски данни, контекст
│   │   ├── reply_service.py        # READ → AI → INSERT (+ take over)
│   │   ├── monitoring_service.py   # цикъл на проверка (2–3 с, без агресивен polling)
│   │   └── export_service.py       # Export CSV (ТЗ §29)
│   ├── ui/
│   │   ├── main_window.py          # горна лента, панели, дневник
│   │   ├── conversation_panel.py   # списък разговори + AI предложение
│   │   ├── settings_window.py      # настройки (ТЗ §22)
│   │   ├── knowledge_window.py     # редактор на базата знания
│   │   └── async_bridge.py         # Qt ↔ asyncio мост
│   ├── config/
│   │   ├── settings.py             # env/.env + data/settings.json
│   │   ├── secrets.py              # Credential Manager / криптиран файл (ТЗ §30)
│   │   └── constants.py            # пътища, enums, режими
│   ├── utils/
│   │   ├── logging_setup.py        # logs/app.log + logs/dialogs.log (ТЗ §28)
│   │   ├── fingerprint.py          # hash(chat_id + text + timestamp) (ТЗ §9)
│   │   └── async_runner.py         # фонов asyncio loop
│   └── api/status_server.py        # локален FastAPI (по избор)
├── data/            # messenger_ai.db, settings.json, exports/ (не се качват в git)
├── logs/            # app.log, dialogs.log
├── packaging/       # PyInstaller spec (onedir/onefile) + Inno Setup скрипт
├── scripts/         # start-chrome-debug.bat, run.bat
├── tests/           # 80 теста (pytest)
├── requirements.txt / requirements-dev.txt
└── .env.example
```

---

## 4. Инсталация

### Windows (работна машина) — най-бърз старт за оператора

1. Разархивирайте архива (не работете вътре в .zip файла).
2. Двоен клик на **`0-START.bat`** — той прави всичко: проверява Python,
   инсталира пакетите и Chromium (при първо пускане), иска API ключа и
   стартира Chrome + приложението.
3. В приложението: **Свържи** → **Генерирай** → **Постави в Messenger** →
   натискате **Enter** сами.

Отделни стъпки (ако е нужна само една): `1-INSTALL.bat`,
`4-SET-API-KEY.bat`, `2-START.bat`, `3-SELFTEST.bat`.
Помощни файлове: `HOW-TO-START.txt` (подробно упътване с чести проблеми),
`BUILD-EXE.bat` (прави .exe за оператора без Python).

### Windows — ръчна инсталация (за разработка)

```bat
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
playwright install chromium
copy .env.example .env
```

Попълнете в `.env` поне `OPENAI_API_KEY` (или изберете друг `AI_PROVIDER`), после:

```bat
scripts\start-chrome-debug.bat     :: стартира Chrome с debugging port + Messenger
scripts\run.bat                    :: стартира приложението
```

### Linux / macOS (разработка)

```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest                       # 80 теста
python -m app.main --selftest          # офлайн проверка на ядрото
python -m app.main                     # UI (изисква графична среда)
```

---

## 5. Свързване към Messenger (ТЗ §5)

### Вариант A (по подразбиране, препоръчан): CDP към вече логнатия Chrome

1. Затворете всички прозорци на Chrome.
2. Стартирайте го с отворен debugging port:
   ```bat
   "C:\Program Files\Google\Chrome\Application\chrome.exe" ^
     --remote-debugging-port=9222 ^
     --user-data-dir="%LOCALAPPDATA%\DPAI\chrome-profile"
   ```
   (или просто `scripts\start-chrome-debug.bat`)
3. Влезте в Messenger в този прозорец.
4. В приложението: **Свържи**. Горната лента трябва да покаже
   `Messenger: CONNECTED`.

Приложението **не създава нов профил и не иска нов login** — работи с вече
отворената сесия. При `Stop`/затваряне то **не затваря** вашия Chrome.

### Вариант B (резервен): persistent профил

`BROWSER_MODE=persistent` — Playwright стартира отделен профил
(`USER_DATA_DIR`), който пази сесията между пусканията. Полезно, ако CDP е
затворен от политика на машината.

---

## 6. Режими на работа (ТЗ §4)

| Режим | Какво прави | По подразбиране |
|---|---|---|
| `MANUAL` | AI генерира предложение само в приложението; в Messenger не се пише | — |
| `ASSIST` | поставя отговора в полето за писане, **без Enter** | ✅ |
| `SEMI_AUTO` | позволява автоматично изпращане само за whitelist intent-и | изключен |

Whitelist за semi-auto (ТЗ §27): `INFO`, `ONLINE`, `HOURS`, `LEVELS`,
`CERTIFICATE`, `PRICE`.
Никога автоматично: `ELIGIBILITY`, `PIK_PROBLEM`, `KEP_PROBLEM`,
`SMS_PROBLEM`, `EMAIL_PROBLEM`, `HUMAN`, `UNKNOWN`.

В MVP `send_reply` изисква **три** условия: режим `SEMI_AUTO`, настройка
`ALLOW_SEMI_AUTO_SEND=true` и intent от whitelist-а. Затова автоматично
изпращане на практика е невъзможно, докато човек не го включи съзнателно.

---

## 7. Защити

1. **Срещу грешен чат (ТЗ §8).** Преди генериране се запомня идентичността
   (`thread id` от URL + име). Непосредствено преди поставяне се проверява пак;
   при разлика се хвърля `ChatChangedError` и **текстът не се поставя**.
   В лога се вижда „Очакван … / Текущ …“.
2. **Срещу двойно отговаряне (ТЗ §9).** Всеки входящ текст се хешира
   (`sha256(chat_id + text + timestamp)`, без оглед на главни/малки букви и
   интервали) и се пази в `messages.message_hash`. Повторение → `skip`.
3. **Safety правила (ТЗ §13, §19).** Human takeover при: неясна допустимост,
   отказан ваучер, технически проблем (ПИК/КЕП/SMS/e-mail), искане за човек,
   жалба/правен казус, `confidence < 0.75` без точен шаблон.
   Отговорът тогава е стандартният текст от ТЗ §19.
4. **Контрол на AI отговора.** Забранени обещания („ще бъдете одобрен“,
   „ще издадем ваучер“) → ескалация към човек. Изречения, които искат ПИК,
   парола или PIN, се премахват автоматично.
5. **Тайни (ТЗ §30).** API ключът не се hardcode-ва, не влиза в
   `data/settings.json` и не се логва. Приоритет: Windows Credential Manager →
   криптиран `data/secrets.enc` (Fernet) → `.env`.

---

## 8. AI слой

Вход (ТЗ §12): `incoming_message`, `conversation_context`, `candidate_data`, `knowledge`.
Изход: `intent`, `confidence`, `reply`, `needs_human`.

Ред: локална класификация (правила, без разход) → safety → търсене в knowledge
base → AI provider → пост-контрол → (при нужда) детерминистичен шаблон.

**Provider-и:** `openai`, `deepseek` (OpenAI-съвместим endpoint),
`anthropic`, `mock` (офлайн, за тестове и демо).

Нов provider се добавя само в `app/ai/providers.py` + фабриката
`create_provider()`; останалият код не се променя (ТЗ §23).

### Кога се използва шаблон вместо AI

За intent-и с точен отговор в knowledge base (`INFO`, `EMPLOYED`, `LEVELS`,
`CERTIFICATE`, …) приложението има детерминистичен отговор. Ако AI е недостъпен
(няма ключ/мрежа), се връща този шаблон и в UI пише
„AI недостъпен — използван шаблон“. Това е и причината ниската *правилова*
увереност да не води до ескалация, когато има точен шаблон (ТЗ §19 се спазва
за всички случаи без шаблон).

---

## 9. База знания (ТЗ §14)

* `app/knowledge/knowledge.json` — фактите: обучение, нива и часове, статуси,
  заявление, техническа помощ, правила, шаблони на отговори.
* `app/knowledge/faq.json` — въпроси/отговори с `intent` и `keywords`
  (използват се за търсене и за контекста на AI).
* Редакция през UI: **Knowledge Base** → таб *FAQ* (добавяне/редакция/изтриване)
  и таб *Факти* (полета от `knowledge.json`).
* Записите се огледално пазят и в таблица `knowledge_items`.

Примери от ТЗ §15–§18 (първи отговор, статус „заето лице“, нива, сертификат) са
част от шаблоните и се покриват от тестове (`tests/test_ai_layer.py`).

---

## 10. UI (ТЗ §20, §21)

Снимки на реалния интерфейс (генерират се с `python scripts/make_screenshots.py`):

| Файл | Какво показва |
|---|---|
| `docs/screenshots/01-dashboard.png` | главен прозорец: статуси, разговори, AI предложение, бутони, дневник |
| `docs/screenshots/02-settings.png` | настройки: AI provider/ключ/модел, браузър, мониторинг |
| `docs/screenshots/03-knowledge-base.png` | редактор на базата знания (FAQ + факти) |

* Горна лента: `Messenger: CONNECTED | Mode: ASSIST | AI: READY` и бутони
  **Свържи / Start Monitoring / Stop / Settings / Knowledge Base / Logs / Export CSV**.
* Ляв панел: разговори от локалната история (име, последно съобщение, статус,
  маркер `ЧОВЕК`). Списъкът се води от базата — **не се scraping-ват контакти**
  (ТЗ §33).
* Десен панел: входящо съобщение, AI предложение (редактируемо), intent и
  увереност, забележки от safety слоя, „Human takeover“ и бутони
  **Генерирай / Постави в Messenger / Изпрати / Редактирай / Предай на човек**.
* Долу: дневник на действията (същият формат като `logs/dialogs.log`).

---

## 11. Логове и експорт (ТЗ §28, §29)

`logs/app.log` — технически; `logs/dialogs.log` — четим дневник:

```
2026-09-17 15:42
CHAT: Йорданка Иванова
IN: Заето лице
INTENT: EMPLOYED
AI CONFIDENCE: 0.97
REPLY INSERTED: YES
SENT: NO
------------------------------------------------------------
```

`Export CSV` записва `data/exports/messenger-ai-export-<дата>.csv` с колони:
`date; chat_name; incoming_message; intent; ai_reply; final_reply; sent; human_takeover`
(UTF-8 с BOM и разделител `;` — отваря се коректно в Excel на Windows).

---

## 12. Тестове

```bash
python -m pytest                 # 83 теста: без мрежа; интеграционният иска chromium
python -m app.main --selftest    # 10 проверки на ядрото (CLI)
```

За интеграционния тест с реален браузър (по избор):
`playwright install chromium` — без него тестът се пропуска автоматично.

Покритие по слоеве:

* `tests/test_database.py` — de-dup, история, candidate flags, AI reply mark-ове
* `tests/test_ai_layer.py` — intent-и, safety, текстовете от ТЗ §15–§19, fallback
* `tests/test_browser_layer.py` — парсване на DOM, fallback селектори,
  chat guard, insert без Enter, semi-auto ограничения, monitoring tick
* `tests/test_browser_integration.py` — реален headless Chromium върху
  „Messenger-подобен“ DOM: четене на съобщения/посока/час, поставяне без Enter,
  блокиране при сменен чат
* `tests/test_services.py` — CSV експорт, настройки, криптирани ключове, лог формат
* `tests/test_ui_smoke.py` — сглобяване на UI (offscreen), показване на
  предложение, панели, диалози

На Linux за UI тестовете: `apt-get install -y libegl1 libgl1 libxkbcommon0 libdbus-1-3 libfontconfig1`.

---

## 13. `.exe` и инсталатор (ТЗ §31)

Приложението се превръща в `.exe` **на Windows** — PyInstaller не прави
cross-compile от Linux, затова build-ът се прави еднократно на Windows машина
(или на машината на оператора, ако има Python).

```bat
BUILD-EXE.bat          :: еднократно → dist\DigitalPlanetMessengerAI\DigitalPlanetMessengerAI.exe
```

По избор — истински инсталатор `Setup.exe` с [Inno Setup 6](https://jrsoftware.org/isdl.php):

```bat
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" packaging\installer.iss
:: → dist\DigitalPlanetMessengerAI-Setup.exe
```
Инсталаторът слага програмата в `%LOCALAPPDATA%\DigitalPlanetMessengerAI`
(без администратор), прави пряк път в Start Menu и деинсталатор.
`BUILD-EXE.bat` го създава автоматично, ако намери Inno Setup.

**Какво получава операторът**

* `DigitalPlanetMessengerAI.exe` + папка `_internal` — **Python не е нужен**;
* `.env` с API ключа (копие на `.env.example`);
* при първо стартиране до `.exe` се създават `data\` (база) и `logs\`, а
  `app\knowledge\*.json` се копират там, за да са **редактируеми през UI**
  (вж. `app/config/bootstrap.py`).

**Варианти**

* `packaging/messenger-ai.spec` — **onedir** (по подразбиране, по-бърз старт);
* `packaging/messenger-ai-onefile.spec` — **един .exe** файл, по-бавен старт,
  удобен за предаване.

Размерът на `dist` е голям (PySide6 + драйверът на Playwright, ~300–500 MB).
`.exe` не е цифрово подписан → Windows SmartScreen ще предупреди при първо
стартиране (*More info → Run anyway*).

---

## 14. Диагностика

| Симптом | Причина / решение |
|---|---|
| `Няма браузър с отворен debugging port` | стартирайте `scripts\start-chrome-debug.bat` и влезте в Messenger |
| „Свързано, но няма вход в Messenger“ | влезте в профила в отворения Chrome и натиснете „Свържи“ |
| `Не намирам полето за писане` | разговорът не е отворен или Messenger е сменил DOM-а → добавете селектор в `app/browser/selectors.py` |
| „Активният разговор се смени“ | защитата е сработила — генерирайте отново в правилния чат |
| `AI: NO KEY` | няма ключ за избрания provider (Settings → API Key или `.env`) |
| Отговорите идват от шаблон | AI недостъпен — вижте `logs/app.log` |
| Празен списък с разговори | още няма обработени съобщения (историята се пълни при работа) |

---

## 15. Какво НЕ прави тази версия (ТЗ §33)

* ❌ автоматично изпращане (включително в SEMI_AUTO — изключено по подразбиране)
* ❌ CRM, мултиакаунт, масови съобщения
* ❌ scraping на контакти, автоматично започване на нови разговори
* ❌ автоматично приемане/потвърждаване на каквото и да е в Messenger

## 16. Следващи стъпки

1. Semi-auto пилот: включване само за whitelist intent-и при наблюдение.
2. PostgreSQL: смяна само на `DATABASE_URL`.
3. Памет на разговора: обобщения на дълги нишки в контекста.
4. Разширяване на knowledge base с реалните случаи от `logs/dialogs.log`.
5. Windows Credential Manager като основно хранилище за ключове (вече поддържано
   автоматично, ако е инсталиран `keyring`).

---

## 17. Бележки по реализацията

* Пътищата в ТЗ са спазени; добавени са няколко помощни модула:
  `browser/types.py`, `ai/providers.py`, `utils/*`, `services/export_service.py`,
  `ui/knowledge_window.py`, `ui/async_bridge.py`, `api/status_server.py`.
* DOM се чете само за **отворения** разговор. Посоката на съобщенията се
  определя геометрично (ляво/дясно спрямо центъра на контейнера), а не по
  minified класове; всички селектори са с fallback и `role`/`aria` приоритет.
* Playwright и PySide6 се импортират лениво: `--selftest` и `pytest` работят и
  без тях/без графична среда.
