# KASPER® MESSENGER — Технический контекст v2
## После Этапа 2 | Для передачи новому Клоду

---

## ⚡ БЫСТРЫЙ СТАРТ ДЛЯ НОВОГО КЛОДА

```
Ты продолжаешь разработку мессенджера Kasper®.
Прочитай этот документ ПОЛНОСТЬЮ перед любыми действиями.
Файлы на сервере: https://fortest.msoloviev.com/
Главный файл: Kasper.html (~3988 строк)
API: api.php (~511 строк)
Нам нужен Этап 3.
НЕ переписывай с нуля. Редактируй существующие файлы.
```

---

## 📁 ФАЙЛЫ НА СЕРВЕРЕ

| Файл | Назначение |
|------|-----------|
| `Kasper.html` | Всё приложение (SPA, ~3988 строк) |
| `api.php` | MySQL REST API (~511 строк) |
| `db_config.php` | Настройки MySQL (не менять) |
| `sms_proxy.php` | Прокси SMS.ru (отправитель from=Kasper) |
| `schema.sql` | Схема БД (уже применена) |

---

## 🗄️ БАЗА ДАННЫХ MYSQL

**БД:** `kolobok_db`

### Таблицы:
```sql
users         -- id, phone, name, login, avatar, visible, my_status, updated_at
messages      -- id, chat_id, sender_id, text, encrypted, attachments,
              --   reply_to_id, reply_to_txt, reply_to_nm, reactions,
              --   deleted_for, deleted_all, status, edited_at, original_text, created_at
groups        -- id, name, avatar, created_by, created_at
group_members -- group_id, user_id, role(admin/member), joined_at
sms_rate      -- phone, sent_at
auth_log      -- id, phone, ip, action, success, created_at
```

### Все API эндпоинты в api.php:

**Пользователи:**
- `GET  action=get_users` — все пользователи (без фильтра visible)
- `GET  action=get_user&phone=` или `&id=` — найти пользователя
- `POST action=register` — регистрация
- `POST action=check_phone` — есть ли такой номер
- `POST action=check_login` — занят ли логин
- `POST action=update_profile` — обновить профиль
- `POST action=delete_account` — удалить аккаунт
- `POST action=ping` — отметить онлайн (обновляет updated_at)
- `POST action=get_online` — кто онлайн (updated_at < 30 сек)

**Сообщения:**
- `POST action=get_messages` — получить сообщения чата (НЕ помечает прочитанным!)
- `POST action=send_message` — отправить сообщение
- `POST action=mark_read` — явно пометить прочитанным (только при открытии чата)
- `POST action=delete_message` — удалить сообщение
- `POST action=react` — реакция на сообщение
- `POST action=edit_message` — редактировать (только отправитель, только 5 минут)

**Группы:**
- `POST action=create_group` — создать группу (сохраняет в MySQL)
- `POST action=get_user_groups` — группы пользователя
- `POST action=add_group_member` — добавить участника (только admin)
- `POST action=remove_group_member` — удалить участника (admin) или выйти (сам)
- `POST action=update_group` — обновить название/аватар (только admin)
- `POST action=delete_group` — удалить группу (только admin)

**SMS:**
- `POST action=sms_rate_check` — антиспам проверка

---

## 📱 СОСТОЯНИЕ ПРИЛОЖЕНИЯ

### Название: Kasper® | Версия: 1.1.0 | Разработчик: rits.ru
### SMS отправитель: `from=Kasper` (одобрен в SMS.ru для Т2)
### API ключ SMS.ru: `921A0CB4-2AB8-BAC6-12C8-47DC3E392188` (только в sms_proxy.php)

---

## ✅ ЧТО РАБОТАЕТ

### Auth:
- Регистрация по SMS (SMS.ru, отправитель Kasper, оператор Т2)
- Вход по SMS
- 2FA PIN (6 цифр, SHA-256 hash) — **ЕСТЬ БАГ, Этап 3**
- Восстановление сессии при перезапуске

### Чаты / Сообщения:
- Сообщения в MySQL — видны на всех устройствах
- Шифрование AES-256-GCM (shared key деривируется из chat_id)
- Polling активного чата: **800мс**
- Фоновый polling всех чатов: **4 сек**
- Входящие появляются в списке **автоматически** (без входа в чат)
- Прочитанное: ТОЛЬКО при явном открытии чата (mark_read)
- Реакции (emoji chips, хранятся в MySQL)
- Ответ на сообщение (reply)
- Удаление (у себя / у всех)
- **Редактирование сообщений** (5 минут, ✏️ в меню) — Этап 2
- Вложения: фото, видео, документы до 50MB

### Группы:
- Создание → сохраняется в MySQL (все участники видят)
- Синхронизация при запуске (`syncGroupsFromServer()`)
- **Администратор** = создатель группы (роль в group_members)
- Администратор может: сменить аватар, удалить участника, добавить участника
- **Добавить участника** в существующую группу (кнопка в info, только для admin)
- **Удалить участника** (✕ в списке, только admin)
- Выйти из группы
- Удалить группу (только admin)
- Клик на участника → **переход в личный чат**
- Бейдж «Администратор» рядом с именем создателя

### UI / Визуал:
- Логотип kasper.png встроен как base64 (в шапке, на welcome)
- Шапка: оранжевая, плоская снизу
- Белая область с закруглёнными верхними углами (18px) поверх оранжевого фона
- Нижняя панель: 6 иконок (Чаты, Звонки, Видео, Почта, Профиль, Настройки)
  - Позиция: fixed, bottom:20px, left/right:max(10px, calc(50%-230px))
  - border-radius: 22px, box-shadow
  - Звонки и Видео: серые (в разработке)
- Счётчик непрочитанных на кнопке «Чаты»
- Онлайн-статус: зелёная точка = онлайн, серая = офлайн
  - Всегда видна (не скрыта когда офлайн)
- Непрочитанный чат: оранжевый левый бордер + легкий оранжевый фон

### Вкладка «Все»:
- Мои контакты: клик → открыть чат
- Другие пользователи: клик → просмотр профиля (sheet с кнопкой Добавить)
- Онлайн-статус в строке под именем («В сети» / «Не в сети»)

### Настройки:
- Цвет фона чата (8 вариантов включая тёмный и ночной)
- Цвет исходящих сообщений (6 вариантов)
- Звук уведомлений (вкл/выкл) — громкий, 3 ноты
- Видимость в поиске
- 2FA PIN (установить/изменить/удалить)
- Удаление аккаунта
- Выход

---

## ❌ ЧТО НЕ РАБОТАЕТ (следующие этапы)

### Этап 3 (следующий):
1. **2FA bug** — PIN устанавливается, но при следующем входе экран PIN не появляется. Нужно проверить `_orig_verLoginOtp` в Stage 2 блоке — возможно вторичное переопределение ломает цепочку
2. **Поиск в чате** — иконка 🔍 рядом с ℹ️, поиск по `_mStore`
3. **Голосовые сообщения** — MediaRecorder API, opus/webm
4. **Свайп медиафайлов** — touch events в image viewer
5. **Push уведомления** — Service Worker + Notification API

### Этап 4:
1. **Email (Входящие/Исходящие/Лиды)** — IMAP сборщик, MySQL таблица emails
2. **Redis** — кэш online-статусов, последних сообщений

### Этап 5:
1. Анимации (появление/удаление сообщений, реакции)
2. Адаптация планшет/ПК

---

## 🏗️ АРХИТЕКТУРА JS

### Ключевые глобальные переменные:
```javascript
G = {
  user,         // текущий пользователь
  contacts,     // массив контактов
  groups,       // массив групп (синхронизируются с MySQL)
  chats,        // legacy (почти не используется)
  settings,     // настройки
  chatId,       // текущий открытый чат
  chatType,     // 'direct' | 'group'
  chatPeer,     // объект контакта или группы
  replyTo,      // {id, name, text} для reply
  _editingMsgId,// id редактируемого сообщения
  pendingAtch,  // вложения перед отправкой
  selGroupMems, // выбранные для новой группы
}
STORE_PFX = 'KLB_'
USERS_API = '/api.php'

// MySQL Messaging (window._mStore — глобальный)
_mStore = {}      // {chatId: Map(id -> localMsg)}
_lastId = {}      // {chatId: lastServerId}
_pollCid          // chatId активного чата
_pollTmr          // 800ms интервал
_bgPollTimer      // 4s фоновый интервал
_onlineUsers      // {userId: lastSeenMs}
```

### Ключевые функции:
```javascript
// Навигация
go(screenId, 'fwd'|'bk')
openSh(id) / closeSh(id)
showConfirm(title, sub, btnLabel, onConfirm, danger)

// Чаты
openDC(cid)              // открыть личный чат
openGC(gid)              // открыть групповой чат
backFromChat()           // вернуться из чата
loadMsgsFromServer(chatId)  // загрузить и рендерить, затем mark_read
pollMessages()           // один опрос (800мс)
bgPollAllChats()         // фоновый опрос всех чатов (4с)
markChatRead(chatId)     // явная пометка прочитанным

// Сообщения
doSend()                 // отправить (учитывает _editingMsgId)
startEdit(id)            // начать редактирование
submitEdit()             // сохранить редактирование
dMsg(id, forAll)         // удалить сообщение
addReact(emoji, id)      // реакция
startReply(id)           // ответить
msgMenu(e, id, mn)       // контекстное меню (показывает Edit если < 5 мин)

// Группы
mkGroup()                // создать группу (→ MySQL)
syncGroupsFromServer()   // синхронизировать с MySQL при запуске
renderGroupInfo()        // инфо группы (admin controls)
kickMember(uid)          // удалить участника (admin only)
openAddMemberSheet()     // sheet добавления участника
doAddMember(uid)         // добавить участника → MySQL
onGroupAvChange(e)       // сменить аватар группы (admin only)
openMemberDM(uid)        // перейти в личный чат с участником

// API
apiPost(action, data)    // POST к api.php
apiGet(params)           // GET к api.php
loadMsgsFromServer(cid)
apiFetchMsgs(cid, sinceId, limit, markAsRead)
serverRegister(user)
serverFindByPhone(phone)
loadServerUsers()        // вкладка Все

// Шифрование
encChatMsg(text, chatId) // шифровать (shared key)
decChatMsg(obj, chatId)  // расшифровать
getChatEncKey(chatId)    // получить ключ из chatId
encryptStr(str)          // для localStorage
decryptStr(str)
```

### Chat ID format:
```
Личный: "dm_<min(id1,id2)>_<max(id1,id2)>"
Группа: "g_<groupId>"
```

### Структура локального сообщения:
```javascript
{
  id,          // number (server id) или 'tmp...' (optimistic)
  sid,         // sender_id
  sn,          // sender_name
  text,        // зашифрованный base64
  _p,          // расшифрованный plaintext (кэш)
  e,           // encrypted: true/false
  atch,        // [{name, type, size, data}] или null
  ts,          // ISO timestamp
  df,          // deleted_for: [userId]
  da,          // deleted_all: bool
  status,      // 'sent'|'delivered'|'read'
  replyTo,     // {id, name, text} или null
  reactions,   // {emoji: [userId]}
  edited,      // true если редактировалось
}
```

---

## 🎨 ДИЗАЙН

```
┌──────────────────────────────┐
│  🐱 Kasper®        👥  +     │  ← Оранжевая шапка #E55A00 (плоская)
│  ██████████████████████████  │  ← Оранжевый фон 22px
│ ╭────────────────────────╮   │  ← Белый контент border-radius:18px 18px 0 0
│ │ Сообщения Контакты Все │   │    margin-top:-18px
│ │ ──────────────────── │ │   │
│ │   список чатов       │ │   │
│ │                      │ │   │
│ ╰────────────────────────╯   │
│                              │
│  ╭──────────────────────╮    │  ← .bnav6
│  │ 💬  📞  🎬  ✉️  👤  ⚙️│    │    position:fixed, bottom:20px
│  ╰──────────────────────╯    │    border-radius:22px
│                              │    left/right: max(10px, calc(50%-230px))
└──────────────────────────────┘
```

### CSS переменные:
```css
--or: #E55A00       /* Kasper orange */
--or-d: #C44A00
--or-l: #FFF0E8
--green: #34C759    /* онлайн, прочитано */
--red: #FF3B30
--bg: #EFEFF4
--bg2: #FFFFFF
```

---

## ⚠️ КРИТИЧЕСКИЕ ПРАВИЛА

1. **НИКОГДА не переписывать с нуля** — только редактировать
2. **Проверять баланс скобок** после JS изменений (должен быть 0):
   ```python
   import re
   with open('Kasper.html','r') as f: c=f.read()
   m=re.search(r'<script>(.*?)</script>',c,re.DOTALL)
   d=sum(1 if ch=='{' else -1 if ch=='}' else 0 for ch in m.group(1))
   print(d)  # 0 = OK
   ```
3. **Сложные замены — через Python** (str_replace ломается на спецсимволах)
4. **apiFetchMsgs всегда с markAsRead=false** в polling — только `markChatRead()` явно
5. **_mStore = window._mStore** — глобальный объект, нужен для renderMain
6. **Number(id)** при сравнении ID — не `===` (string vs number)
7. **get_messages через POST** — PHP guard блокирует GET

---

## 🐛 ИЗВЕСТНЫЕ БАГИ

| Баг | Статус | Этап |
|-----|--------|------|
| 2FA PIN не запрашивается при входе | ❌ | Этап 3 |
| Нет поиска в чате | ❌ | Этап 3 |
| Нет голосовых сообщений | ❌ | Этап 3 |
| Нет свайпа медиафайлов | ❌ | Этап 3 |
| Почта только заглушка | ❌ | Этап 4 |

---

## 📋 ПЛАН ЭТАПОВ

### ✅ Этап 1 — ВЫПОЛНЕН
- Ребрендинг Kasper® + логотип kasper.png
- Визуал: шапка, белая зона с округлыми углами, нижняя панель 6 иконок
- Производительность: polling 800мс, фоновый 4с
- Онлайн статус (серая/зелёная точка)
- Счётчик непрочитанных на кнопке Чаты
- SMS from=Kasper
- Громче звук (3 ноты + компрессор)
- iOS AudioContext unlock на первом касании
- Прочитанное только при явном открытии чата
- Вкладка Все: клик на контакт → чат, клик на незнакомца → профиль

### ✅ Этап 2 — ВЫПОЛНЕН
- Группы хранятся в MySQL (таблицы groups + group_members)
- Синхронизация групп при запуске (`syncGroupsFromServer`)
- Все участники видят группу в своём списке чатов
- **Администратор** = создатель (role='admin' в group_members)
- Бейдж «Администратор» в списке участников
- Только admin может: сменить аватар, добавить/удалить участника, удалить группу
- Кнопка «Добавить участника» (только для admin)
- Клик на участника → переход в личный чат
- **Редактирование сообщений** (5 минут, показывает «(изм.)»)
- ✏️ в контекстном меню с таймером
- `action=edit_message` в api.php

### 🔄 Этап 3 — СЛЕДУЮЩИЙ
1. **2FA fix** — проверить chain of overrides для `verLoginOtp`. Баг: при входе `showPinVerify` не вызывается. Нужно найти последний `window.verLoginOtp` в файле и убедиться что он проверяет `userHas2FA(u.id)` перед `_completeLogin`
2. **Поиск в чате** — добавить иконку 🔍 в шапку чата рядом с ℹ️. При клике — поле поиска, ищет по `_mStore` текущего чата, прокручивает и подсвечивает найденные
3. **Голосовые сообщения** — кнопка 🎙️ (показывается когда поле ввода пустое), `MediaRecorder`, запись в webm/opus, отправка как attachment с type='audio/webm'
4. **Свайп медиафайлов** — в просмотрщике (sh-iv) добавить touch events: swipe left/right переключает между изображениями чата. Массив всех изображений из `_mStore[G.chatId]`

### 🔄 Этап 4
1. **Email — полная реализация** (IMAP + таблица emails + Входящие/Исходящие + Compose + Reply)
2. **Redis** — конфиг файл, кэш online и последних сообщений

### 🔄 Этап 5
- Push notifications (Service Worker)
- Анимации (CSS transitions для сообщений, реакций)
- Адаптация планшет/ПК (sidebar на широких экранах)

---

## 🔐 БЕЗОПАСНОСТЬ

- Данные пользователей: AES-256-GCM + PBKDF2 (150k итераций)
- Сообщения: shared key из chatId (одинаков для обоих)
- PIN 2FA: SHA-256 с солью 'KLB_PIN_SALT_v1_'
- API ключ SMS.ru: только в sms_proxy.php
- Сессия: userId + телефон в localStorage

---

## 📊 ЧТО СКАЗАТЬ НОВОМУ КЛОДУ ДЛЯ ЭТАПА 3

```
Продолжаем разработку Kasper®. Читай KASPER_CONTEXT_v2.md полностью.
Файлы на сервере: fortest.msoloviev.com
Kasper.html — ~3988 строк

Нужен Этап 3:
1. Исправить 2FA — найти последний window.verLoginOtp и убедиться в вызове showPinVerify
2. Поиск в чате — иконка в шапке, поиск по _mStore
3. Голосовые сообщения — MediaRecorder, кнопка 🎙️
4. Свайп медиафайлов в просмотрщике

Правила: не переписывай с нуля, проверяй brace balance=0,
apiFetchMsgs всегда markAsRead=false, Number() при сравнении ID.
```

---

*Kasper® Messenger | rits.ru | v1.1.0*
*Документ обновлён после Этапа 2*
