# Telegram inbound pipeline — current state (pre `feature/telegram-and-authonomy`)

> Snapshot kreiran 2026-04-28 kao baseline pre Faze 1+ izmena u branch-u `feature/telegram-and-authonomy`.
> Ovaj dokument NE opisuje target arhitekturu — opisuje stanje koda na commit-u `db0bb2f` (`main`) iz koga branch polazi.

## High-level flow

```
Telegram getUpdates (polling)
        │
        ▼
Niwa_AI_Admin_Autonomy_Telegram::poll_updates()
        │
        ├─ getUpdates POST (timeout=poll_timeout, limit=poll_limit, allowed_updates=[message,edited_message,callback_query])
        │
        └─ vraca raw $updates[]
                │
                ▼
Niwa_AI_Admin_Autonomy::process_telegram_updates()
        │
        ├─ za svaki update:
        │     extract_message_payload($update) → $message (asocijativni niz)
        │     handle_incoming_message($message)
        │
        ▼
handle_incoming_message
        │
        ├─ if (!success) → ignored (drop)
        ├─ get_user_role(chat_id):
        │     - owner_chat_id match → 'admin'
        │     - stock_managers list match → 'stock_manager'
        │     - else null  ← ★ SILENT DROP TAČKA
        ├─ if (null === user_role) → ignored (drop, korisnik ne dobija nista)
        ├─ stock_manager: clear document_file_id ako nije csv/xlsx
        ├─ duplicate update check
        ├─ rate limit
        ├─ ack callback_query
        ├─ should_queue_founder_message? → enqueue u actor runtime
        └─ begin typing feedback
```

## `extract_message_payload` — sta sad vraca

Polja iz `class-admin-autonomy-telegram.php:212-381`:

| Polje | Tip | Napomena |
|---|---|---|
| `success` | bool | false ako payload nije validan |
| `update_id` | int | iz `update.update_id` |
| `chat_id` | string | iz `message.chat.id` |
| `from_id` | string | iz `message.from.id` |
| `language_code` | string | iz `message.from.language_code` |
| `message_id` | int | iz `message.message_id` |
| `text` | string | iz `message.text` ili (fallback) `message.caption` ★ overload |
| `photo_file_id` | string | najveca verzija iz `message.photo[]`; fallback iz `message.document` ako MIME pocinje sa `image/` |
| `voice_file_id` | string | iz `message.voice` → `message.audio` → `message.video_note` |
| `voice_duration` | int | |
| `media_only` | bool | true SAMO kad nema text-a i ima photo |
| `document_file_id` | string | iz `message.document` ako ekstenzija ∈ {md, zip, skill, csv, xlsx} |
| `document_filename` | string | |
| `voice_only`, `document_only`, `media_only` | bool | mutually exclusive flagovi |
| `callback_query_id`, `callback_data`, `callback_origin_message_id` | various | za inline button reply |

## Slabe tačke (ono što plan v2 popravlja)

### 1. Caption overload-uje text
Linija 240-242:
```php
$text = isset( $message['text'] ) ? trim( (string) $message['text'] ) : '';
if ( '' === $text && ! empty( $message['caption'] ) ) {
    $text = trim( (string) $message['caption'] );
}
```
Posledica: kad korisnik posalje sliku sa caption-om „postavi ovu sliku za featured", payload ide kao tekst poruka (`media_only=false`), dispatcher zaobilazi `resolve_media_only_turn` granu, i agent reaguje na caption ali ne prepoznaje da je slika prilozena u kontekstu rute.

### 2. Document MIME filter za sliku
Linija 251-256: `document` se prepoznaje kao slika SAMO ako `mime_type` pocinje sa `image/`. Telegram desktop ponekad salje `application/octet-stream` za .heic, .tiff, ili kad klijent nije naveo MIME — slika se gubi.

### 3. Document whitelist
Linija 261-271: document prolazi kao "skill import" SAMO ako ekstenzija ∈ {md, zip, skill, csv, xlsx}. Sve drugo (PDF, TXT, JSON, DOCX) — drop.

### 4. Nema retry-ja na getFile/download
`resolve_file_url` (linija 1219-1261) i `Image_Utils::download_to_temp` rade jedan POST + jedan fallback. Transient mrezna greska = izgubljena slika.

### 5. Nema buffer-a za `media_group_id`
Album od 3 slike = 3 odvojena update-a → 3 odvojena turn-a, agent dobija jednu po jednu sliku bez konteksta da su poslate zajedno.

### 6. Nema reply-to-media
Ako korisnik odgovori sa textom na ranije poslatu sliku, payload ne sadrzi `reply_to_message.photo` — agent nema pristup tom resursu.

### 7. Silent drop za neautorizovane
Linija 1083-1089 u `class-admin-autonomy.php`:
```php
$user_role = $this->telegram->get_user_role( $message['chat_id'] );
if ( null === $user_role ) {
    return array( 'success' => true, 'ignored' => true );
}
```
Korisnik nema feedback. Admin ne vidi ko je pisao botu. Nema mehanizma da se neautorizovan korisnik samostalno predstavi sa svojim chat_id-om.

### 8. Nema text fragment debouncing-a
Telegram klijent koji posalje veliku poruku (>4000 char) je razbije na 2-3 update-a u brzom nizu. Trenutno svaki ide kao odvojen turn.

### 9. Forwarded poruke nemaju metadata flag
`message.forward_origin` se ignorise — agent ne zna da je sadrzaj proslednjen.

## Postojeci konzumeri payload-a

`extract_message_payload` se poziva iz **samo jedne tacke**: `class-admin-autonomy.php:831` u petlji `process_telegram_updates`. Rezultat ide kroz `handle_incoming_message` → (ako prodje sve gate-ove) → `enqueue_message` u actor runtime → kasnije `execute_founder_message` → `process_command`.

Polja payload-a se citaju u:
- `class-admin-autonomy.php` (build_message_context ~1397, should_queue_founder_message ~1479, log_turn_debug)
- `class-admin-autonomy-control-plane.php` (~319 — koristi `photo_file_id`)
- `class-admin-autonomy-subagent-manager.php` (~1272, 1332, 1436 — `resolve_file_url` + ingest)

## Granice ovog dokumenta

- Ne pokriva slanje (`send_message`, `send_photo`, `send_document`) — to je odvojeno.
- Ne pokriva agent loop / tool execution.
- Ne pokriva email i daily report mehanizme.
- Verzija plugina u trenutku snapshot-a: 0.12.12-dev.
- Branch: `feature/lead-form-generator` (parent) → `feature/telegram-and-authonomy` (cilj).
